CREDENTIAL TYPE MANAGEMENT WORKS

This commit is contained in:
2026-02-14 15:15:49 -06:00
parent b7637334a6
commit cdae4d47a4
46 changed files with 7621 additions and 41 deletions
+111
View File
@@ -0,0 +1,111 @@
import { prisma } from "../constants";
import { CredentialTypeController } from "../controllers/CredentialTypeController";
import { CredentialTypeField } from "../modules/credentials/credentialTypeDefs";
import GenericError from "../Errors/GenericError";
export const credentialTypes = {
/**
* Fetch Credential Type
*
* Fetch a credential type by its ID or name and return a CredentialTypeController instance.
*
* @param identifier - The credential type ID or name to fetch
* @returns {Promise<CredentialTypeController>} - The credential type controller
*/
async fetch(identifier: string): Promise<CredentialTypeController> {
const credentialType = await prisma.credentialType.findFirst({
where: {
OR: [{ id: identifier }, { name: identifier }],
},
include: {
credentials: true,
},
});
if (!credentialType) {
throw new GenericError({
message: "Credential type not found",
name: "CredentialTypeNotFound",
cause: `No credential type exists with identifier '${identifier}'`,
status: 404,
});
}
return new CredentialTypeController(credentialType);
},
/**
* Fetch All Credential Types
*
* Fetch all credential types in the system.
*
* @returns {Promise<CredentialTypeController[]>} - Array of credential type controllers
*/
async fetchAll(): Promise<CredentialTypeController[]> {
const credentialTypesList = await prisma.credentialType.findMany({
include: {
credentials: true,
},
});
return credentialTypesList.map((ct) => new CredentialTypeController(ct));
},
/**
* Create Credential Type
*
* Create a new credential type with its field definitions.
*
* @param data - The credential type data to create
* @returns {Promise<CredentialTypeController>} - The created credential type controller
*/
async create(data: {
name: string;
permissionScope: string;
fields: CredentialTypeField[];
icon?: string;
}): Promise<CredentialTypeController> {
// Check if a credential type with this name already exists
const existing = await prisma.credentialType.findFirst({
where: { name: data.name },
});
if (existing) {
throw new GenericError({
message: "Credential type name already exists",
name: "CredentialTypeAlreadyExists",
cause: `A credential type with name '${data.name}' already exists`,
status: 400,
});
}
const credentialType = await prisma.credentialType.create({
data: {
name: data.name,
permissionScope: data.permissionScope,
fields: data.fields as any,
icon: data.icon,
},
include: {
credentials: true,
},
});
return new CredentialTypeController(credentialType);
},
/**
* Delete Credential Type
*
* Delete a credential type by its ID.
* This will cascade delete all credentials of this type.
*
* @param id - The credential type ID to delete
* @returns {Promise<void>}
*/
async delete(id: string): Promise<void> {
await prisma.credentialType.delete({
where: { id },
});
},
};
+158
View File
@@ -0,0 +1,158 @@
import { prisma } from "../constants";
import { CredentialController } from "../controllers/CredentialController";
import { fieldValidator } from "../modules/credentials/fieldValidator";
import {
CredentialField,
CredentialTypeField,
} from "../modules/credentials/credentialTypeDefs";
import { generateSecureValue } from "../modules/credentials/generateSecureValue";
import GenericError from "../Errors/GenericError";
export const credentials = {
/**
* Fetch Credential
*
* Fetch a credential by its ID and return a CredentialController instance.
*
* @param id - The credential ID to fetch
* @returns {Promise<CredentialController>} - The credential controller
*/
async fetch(id: string): Promise<CredentialController> {
const credential = await prisma.credential.findFirst({
where: { id },
include: {
type: true,
company: true,
securevalues: true,
},
});
if (!credential) {
throw new GenericError({
message: "Credential not found",
name: "CredentialNotFound",
cause: `No credential exists with ID '${id}'`,
status: 404,
});
}
return new CredentialController(credential);
},
/**
* Fetch Credentials by Company
*
* Fetch all credentials associated with a specific company.
*
* @param companyId - The company ID to fetch credentials for
* @returns {Promise<CredentialController[]>} - Array of credential controllers
*/
async fetchByCompany(companyId: string): Promise<CredentialController[]> {
const credentialsList = await prisma.credential.findMany({
where: { companyId },
include: {
type: true,
company: true,
securevalues: true,
},
});
return credentialsList.map((cred) => new CredentialController(cred));
},
/**
* Create Credential
*
* Create a new credential with validated fields.
* This method processes all incoming field values, validating them against
* the credential type, encrypting secure fields, and inserting everything
* into the database atomically.
*
* @param data - The credential data to create
* @returns {Promise<CredentialController>} - The created credential controller
*/
async create(data: {
name: string;
typeId: string;
companyId: string;
fields: CredentialField[];
}): Promise<CredentialController> {
// Fetch the credential type to get acceptable fields
const credentialType = await prisma.credentialType.findFirst({
where: { id: data.typeId },
});
if (!credentialType) {
throw new GenericError({
message: "Credential type not found",
name: "CredentialTypeNotFound",
cause: `No credential type exists with ID '${data.typeId}'`,
status: 404,
});
}
// Validate the fields against acceptable fields
const acceptableFields = JSON.parse(credentialType.fields! as string).map(
(f: { id: string; name: string; secure: boolean }) => ({
id: f.id,
name: f.name,
secure: f.secure,
}),
) as CredentialTypeField[];
const validatedFields = await fieldValidator(data.fields, acceptableFields);
// Separate secure and non-secure fields
const secureFields = validatedFields.filter((f) => f.secure);
const nonSecureFields = validatedFields.filter((f) => !f.secure);
// Build fields object for non-secure fields
const fieldsObject: Record<string, any> = {};
nonSecureFields.forEach((field) => {
fieldsObject[field.fieldId] = field.value;
});
// Encrypt secure field values
const secureValueData = secureFields.map((field) => {
const { encrypted, hash } = generateSecureValue(field.value);
return {
name: field.fieldId,
content: encrypted,
hash,
};
});
// Create credential and all secure values in a transaction
const credential = await prisma.credential.create({
data: {
name: data.name,
typeId: data.typeId,
companyId: data.companyId,
fields: fieldsObject,
securevalues: {
create: secureValueData,
},
},
include: {
type: true,
company: true,
securevalues: true,
},
});
return new CredentialController(credential);
},
/**
* Delete Credential
*
* Delete a credential by its ID.
*
* @param id - The credential ID to delete
* @returns {Promise<void>}
*/
async delete(id: string): Promise<void> {
await prisma.credential.delete({
where: { id },
});
},
};