PKI revamp - backend initial changes

This commit is contained in:
Carlos Monastyrski
2025-10-09 00:29:55 -03:00
parent c1833b5cbd
commit c4dcc3dd83
47 changed files with 9624 additions and 26 deletions
@@ -0,0 +1,336 @@
import { Knex } from "knex";
import { TDbClient } from "@app/db";
import { TableName } from "@app/db/schemas";
import { DatabaseError } from "@app/lib/errors";
import { ormify, selectAllTableCols } from "@app/lib/knex";
import {
EnrollmentType,
TCertificateProfileCertificate,
TCertificateProfileInsert,
TCertificateProfileMetrics,
TCertificateProfileUpdate
} from "./certificate-profile-types";
export type TCertificateProfileDALFactory = ReturnType<typeof certificateProfileDALFactory>;
export const certificateProfileDALFactory = (db: TDbClient) => {
const certificateProfileOrm = ormify(db, TableName.CertificateProfile);
const create = async (data: TCertificateProfileInsert, tx?: Knex) => {
try {
const [certificateProfile] = await (tx || db)(TableName.CertificateProfile).insert(data).returning("*");
return certificateProfile;
} catch (error) {
throw new DatabaseError({ error, name: "Create certificate profile" });
}
};
const updateById = async (id: string, data: TCertificateProfileUpdate, tx?: Knex) => {
try {
const [certificateProfile] = await (tx || db)(TableName.CertificateProfile)
.where({ id })
.update(data)
.returning("*");
return certificateProfile;
} catch (error) {
throw new DatabaseError({ error, name: "Update certificate profile" });
}
};
const deleteById = async (id: string, tx?: Knex) => {
try {
const [certificateProfile] = await (tx || db)(TableName.CertificateProfile).where({ id }).del().returning("*");
return certificateProfile;
} catch (error) {
throw new DatabaseError({ error, name: "Delete certificate profile" });
}
};
const findById = async (id: string, tx?: Knex) => {
try {
const certificateProfile = await (tx || db)(TableName.CertificateProfile).where({ id }).first();
return certificateProfile;
} catch (error) {
throw new DatabaseError({ error, name: "Find certificate profile by id" });
}
};
const findByIdWithConfigs = async (id: string, tx?: Knex) => {
try {
const result = await (tx || db)(TableName.CertificateProfile)
.select(
selectAllTableCols(TableName.CertificateProfile),
(tx || db).ref("id").withSchema(TableName.CertificateAuthority).as("caId"),
(tx || db).ref("projectId").withSchema(TableName.CertificateAuthority).as("caProjectId"),
(tx || db).ref("status").withSchema(TableName.CertificateAuthority).as("caStatus"),
(tx || db).ref("name").withSchema(TableName.CertificateAuthority).as("caName"),
(tx || db).ref("id").withSchema(TableName.CertificateTemplateV2).as("templateId"),
(tx || db).ref("projectId").withSchema(TableName.CertificateTemplateV2).as("templateProjectId"),
(tx || db).ref("name").withSchema(TableName.CertificateTemplateV2).as("templateName"),
(tx || db).ref("description").withSchema(TableName.CertificateTemplateV2).as("templateDescription"),
(tx || db).ref("id").withSchema(TableName.EstEnrollmentConfig).as("estConfigId"),
(tx || db)
.ref("disableBootstrapCaValidation")
.withSchema(TableName.EstEnrollmentConfig)
.as("estConfigDisableBootstrapCaValidation"),
(tx || db).ref("hashedPassphrase").withSchema(TableName.EstEnrollmentConfig).as("estConfigHashedPassphrase"),
(tx || db).ref("encryptedCaChain").withSchema(TableName.EstEnrollmentConfig).as("estConfigEncryptedCaChain"),
(tx || db).ref("id").withSchema(TableName.ApiEnrollmentConfig).as("apiConfigId"),
(tx || db).ref("autoRenew").withSchema(TableName.ApiEnrollmentConfig).as("apiConfigAutoRenew"),
(tx || db).ref("autoRenewDays").withSchema(TableName.ApiEnrollmentConfig).as("apiConfigAutoRenewDays")
)
.leftJoin(
TableName.CertificateAuthority,
`${TableName.CertificateProfile}.caId`,
`${TableName.CertificateAuthority}.id`
)
.leftJoin(
TableName.CertificateTemplateV2,
`${TableName.CertificateProfile}.certificateTemplateId`,
`${TableName.CertificateTemplateV2}.id`
)
.leftJoin(
TableName.EstEnrollmentConfig,
`${TableName.CertificateProfile}.estConfigId`,
`${TableName.EstEnrollmentConfig}.id`
)
.leftJoin(
TableName.ApiEnrollmentConfig,
`${TableName.CertificateProfile}.apiConfigId`,
`${TableName.ApiEnrollmentConfig}.id`
)
.where(`${TableName.CertificateProfile}.id`, id)
.first();
return result;
} catch (error) {
throw new DatabaseError({ error, name: "Find certificate profile by id with configs" });
}
};
const findBySlugAndProjectId = async (slug: string, projectId: string, tx?: Knex) => {
try {
const certificateProfile = await (tx || db)(TableName.CertificateProfile).where({ slug, projectId }).first();
return certificateProfile;
} catch (error) {
throw new DatabaseError({ error, name: "Find certificate profile by slug and project id" });
}
};
const findByProjectId = async (
projectId: string,
options: {
offset?: number;
limit?: number;
search?: string;
enrollmentType?: EnrollmentType;
caId?: string;
} = {},
tx?: Knex
) => {
try {
const { offset = 0, limit = 20, search, enrollmentType, caId } = options;
let query = (tx || db)(TableName.CertificateProfile).where(
`${TableName.CertificateProfile}.projectId`,
projectId
);
if (search) {
query = query.where((builder) => {
void builder
.whereILike(`${TableName.CertificateProfile}.name`, `%${search}%`)
.orWhereILike(`${TableName.CertificateProfile}.description`, `%${search}%`)
.orWhereILike(`${TableName.CertificateProfile}.slug`, `%${search}%`);
});
}
if (enrollmentType) {
query = query.where(`${TableName.CertificateProfile}.enrollmentType`, enrollmentType);
}
if (caId) {
query = query.where(`${TableName.CertificateProfile}.caId`, caId);
}
const certificateProfiles = await query
.select(selectAllTableCols(TableName.CertificateProfile))
.orderBy(`${TableName.CertificateProfile}.createdAt`, "desc")
.offset(offset)
.limit(limit);
return certificateProfiles;
} catch (error) {
throw new DatabaseError({ error, name: "Find certificate profiles by project id" });
}
};
const countByProjectId = async (
projectId: string,
options: {
search?: string;
enrollmentType?: EnrollmentType;
caId?: string;
} = {},
tx?: Knex
) => {
try {
const { search, enrollmentType, caId } = options;
let query = (tx || db)(TableName.CertificateProfile).where({ projectId });
if (search) {
query = query.where((builder) => {
void builder
.whereILike("name", `%${search}%`)
.orWhereILike("description", `%${search}%`)
.orWhereILike("slug", `%${search}%`);
});
}
if (enrollmentType) {
query = query.where({ enrollmentType });
}
if (caId) {
query = query.where({ caId });
}
const result = await query.count("*").first();
return parseInt((result as unknown as { count: string }).count || "0", 10);
} catch (error) {
throw new DatabaseError({ error, name: "Count certificate profiles by project id" });
}
};
const findByNameAndProjectId = async (name: string, projectId: string, tx?: Knex) => {
try {
const certificateProfile = await (tx || db)(TableName.CertificateProfile).where({ name, projectId }).first();
return certificateProfile;
} catch (error) {
throw new DatabaseError({ error, name: "Find certificate profile by name and project id" });
}
};
const getCertificatesByProfile = async (
profileId: string,
options: {
offset?: number;
limit?: number;
status?: "active" | "expired" | "revoked";
search?: string;
} = {},
tx?: Knex
): Promise<TCertificateProfileCertificate[]> => {
try {
const { offset = 0, limit = 20, status, search } = options;
const now = new Date();
let query = (tx || db)(TableName.Certificate).where("profileId", profileId);
if (search) {
query = query.where((builder) => {
void builder.whereILike("cn", `%${search}%`).orWhereILike("serialNumber", `%${search}%`);
});
}
if (status) {
switch (status) {
case "active":
query = query.where("notAfter", ">", now).where("isRevoked", false);
break;
case "expired":
query = query.where("notAfter", "<=", now).where("isRevoked", false);
break;
case "revoked":
query = query.where("isRevoked", true);
break;
default:
break;
}
}
const certificates = await query
.select((tx || db).ref("id").withSchema(TableName.Certificate))
.select((tx || db).ref("serialNumber").withSchema(TableName.Certificate))
.select((tx || db).ref("cn").withSchema(TableName.Certificate))
.select((tx || db).ref("status").withSchema(TableName.Certificate))
.select((tx || db).ref("notBefore").withSchema(TableName.Certificate))
.select((tx || db).ref("notAfter").withSchema(TableName.Certificate))
.select((tx || db).ref("isRevoked").withSchema(TableName.Certificate))
.select((tx || db).ref("createdAt").withSchema(TableName.Certificate))
.orderBy("createdAt", "desc")
.offset(offset)
.limit(limit);
return certificates;
} catch (error) {
throw new DatabaseError({ error, name: "Get certificates by profile" });
}
};
const getProfileMetrics = async (
profileId: string,
expiringDays: number = 30,
tx?: Knex
): Promise<TCertificateProfileMetrics> => {
try {
const now = new Date();
const expiringDate = new Date();
expiringDate.setDate(now.getDate() + expiringDays);
const metrics = await (tx || db)(TableName.Certificate)
.where("profileId", profileId)
.select(
db.raw("COUNT(*) as total_certificates"),
db.raw("COUNT(CASE WHEN NOT is_revoked AND not_after > ? THEN 1 END) as active_certificates", [now]),
db.raw("COUNT(CASE WHEN NOT is_revoked AND not_after <= ? THEN 1 END) as expired_certificates", [now]),
db.raw(
"COUNT(CASE WHEN NOT is_revoked AND not_after > ? AND not_after <= ? THEN 1 END) as expiring_certificates",
[now, expiringDate]
),
db.raw("COUNT(CASE WHEN is_revoked THEN 1 END) as revoked_certificates")
)
.first();
return {
profileId,
totalCertificates: parseInt(String((metrics as Record<string, unknown>)?.total_certificates || 0), 10),
activeCertificates: parseInt(String((metrics as Record<string, unknown>)?.active_certificates || 0), 10),
expiredCertificates: parseInt(String((metrics as Record<string, unknown>)?.expired_certificates || 0), 10),
expiringCertificates: parseInt(String((metrics as Record<string, unknown>)?.expiring_certificates || 0), 10),
revokedCertificates: parseInt(String((metrics as Record<string, unknown>)?.revoked_certificates || 0), 10)
};
} catch (error) {
throw new DatabaseError({ error, name: "Get certificate profile metrics" });
}
};
const isProfileInUse = async (profileId: string, tx?: Knex) => {
try {
const doc = await (tx || db)(TableName.Certificate).where("profileId", profileId).count("*").first();
return parseInt((doc as unknown as { count: string }).count || "0", 10);
} catch (error) {
throw new DatabaseError({ error, name: "Check if certificate profile is in use" });
}
};
return {
...certificateProfileOrm,
create,
updateById,
deleteById,
findById,
findByIdWithConfigs,
findBySlugAndProjectId,
findByProjectId,
countByProjectId,
findByNameAndProjectId,
getCertificatesByProfile,
getProfileMetrics,
isProfileInUse
};
};