Add certificate id on CA certificate responses

This commit is contained in:
Carlos Monastyrski
2025-12-18 12:23:20 -03:00
parent 7e0868a1ee
commit d624214f45
4 changed files with 152 additions and 9 deletions

View File

@@ -93,12 +93,13 @@ export const registerInternalCertificateAuthorityRouter = async (server: Fastify
200: z.object({
certificate: z.string().trim().describe(CERTIFICATE_AUTHORITIES.RENEW_CA_CERT.certificate),
certificateChain: z.string().trim().describe(CERTIFICATE_AUTHORITIES.RENEW_CA_CERT.certificateChain),
serialNumber: z.string().trim().describe(CERTIFICATE_AUTHORITIES.RENEW_CA_CERT.serialNumber)
serialNumber: z.string().trim().describe(CERTIFICATE_AUTHORITIES.RENEW_CA_CERT.serialNumber),
certId: z.string().describe("Certificate ID")
})
}
},
handler: async (req) => {
const { certificate, certificateChain, serialNumber, ca } =
const { certificate, certificateChain, serialNumber, certId, ca } =
await server.services.internalCertificateAuthority.renewCaCert({
caId: req.params.caId,
actor: req.permission.type,
@@ -123,7 +124,8 @@ export const registerInternalCertificateAuthorityRouter = async (server: Fastify
return {
certificate,
certificateChain,
serialNumber
serialNumber,
certId
};
}
});
@@ -159,7 +161,8 @@ export const registerInternalCertificateAuthorityRouter = async (server: Fastify
.string()
.trim()
.describe(CERTIFICATE_AUTHORITIES.GENERATE_CA_CERTIFICATE.certificateChain),
serialNumber: z.string().trim().describe(CERTIFICATE_AUTHORITIES.GENERATE_CA_CERTIFICATE.serialNumber)
serialNumber: z.string().trim().describe(CERTIFICATE_AUTHORITIES.GENERATE_CA_CERTIFICATE.serialNumber),
certId: z.string().describe("Certificate ID")
})
}
},
@@ -169,6 +172,7 @@ export const registerInternalCertificateAuthorityRouter = async (server: Fastify
let certificate: string;
let certificateChain: string;
let serialNumber: string;
let certId: string;
let ca: { id: string; dn: string; projectId: string };
if (parentCaId) {
@@ -187,6 +191,7 @@ export const registerInternalCertificateAuthorityRouter = async (server: Fastify
certificate = intermediateCert.certificate;
certificateChain = intermediateCert.certificateChain;
serialNumber = intermediateCert.serialNumber;
certId = intermediateCert.certId;
ca = await server.services.internalCertificateAuthority.getCaById({
caId: req.params.caId,
@@ -210,6 +215,7 @@ export const registerInternalCertificateAuthorityRouter = async (server: Fastify
certificate = rootCert.certificate;
certificateChain = rootCert.certificateChain;
serialNumber = rootCert.serialNumber;
certId = rootCert.certId;
ca = rootCert.ca;
}
@@ -230,7 +236,8 @@ export const registerInternalCertificateAuthorityRouter = async (server: Fastify
return {
certificate,
certificateChain,
serialNumber
serialNumber,
certId
};
}
});
@@ -255,6 +262,7 @@ export const registerInternalCertificateAuthorityRouter = async (server: Fastify
certificate: z.string().describe(CERTIFICATE_AUTHORITIES.GET_CA_CERTS.certificate),
certificateChain: z.string().describe(CERTIFICATE_AUTHORITIES.GET_CA_CERTS.certificateChain),
serialNumber: z.string().describe(CERTIFICATE_AUTHORITIES.GET_CA_CERTS.serialNumber),
certId: z.string().describe("Certificate ID"),
version: z.number().describe(CERTIFICATE_AUTHORITIES.GET_CA_CERTS.version)
})
)
@@ -303,12 +311,13 @@ export const registerInternalCertificateAuthorityRouter = async (server: Fastify
200: z.object({
certificate: z.string().describe(CERTIFICATE_AUTHORITIES.GET_CERT.certificate),
certificateChain: z.string().describe(CERTIFICATE_AUTHORITIES.GET_CERT.certificateChain),
serialNumber: z.string().describe(CERTIFICATE_AUTHORITIES.GET_CERT.serialNumber)
serialNumber: z.string().describe(CERTIFICATE_AUTHORITIES.GET_CERT.serialNumber),
certId: z.string().describe("Certificate ID")
})
}
},
handler: async (req) => {
const { certificate, certificateChain, serialNumber, ca } =
const { certificate, certificateChain, serialNumber, certId, ca } =
await server.services.internalCertificateAuthority.getCaCert({
caId: req.params.caId,
actor: req.permission.type,
@@ -332,7 +341,64 @@ export const registerInternalCertificateAuthorityRouter = async (server: Fastify
return {
certificate,
certificateChain,
serialNumber
serialNumber,
certId
};
}
});
server.route({
method: "GET",
url: "/:caId/certificate/:certId",
config: {
rateLimit: readLimit
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
schema: {
hide: false,
tags: [ApiDocsTags.PkiCertificateAuthorities],
description: "Get a specific CA certificate by ID",
params: z.object({
caId: z.string().trim().describe(CERTIFICATE_AUTHORITIES.GET_CERT.caId),
certId: z.string().trim().describe("Certificate ID to retrieve")
}),
response: {
200: z.object({
certificate: z.string().describe(CERTIFICATE_AUTHORITIES.GET_CERT.certificate),
certificateChain: z.string().describe(CERTIFICATE_AUTHORITIES.GET_CERT.certificateChain),
serialNumber: z.string().describe(CERTIFICATE_AUTHORITIES.GET_CERT.serialNumber),
certId: z.string().describe("Certificate ID")
})
}
},
handler: async (req) => {
const { certificate, certificateChain, serialNumber, certId, ca } =
await server.services.internalCertificateAuthority.getCaCertByIdWithAuth({
caId: req.params.caId,
certId: req.params.certId,
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId
});
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
projectId: ca.projectId,
event: {
type: EventType.GET_CA_CERT,
metadata: {
caId: ca.id,
dn: ca.dn
}
}
});
return {
certificate,
certificateChain,
serialNumber,
certId
};
}
});

View File

@@ -273,6 +273,7 @@ export const getCaCertChains = async ({
certificate: caCertObj.toString("pem"),
certificateChain: decryptedChain.toString("utf-8"),
serialNumber: caCertObj.serialNumber,
certId: caCert.id,
version: caCert.version
};
})

View File

@@ -62,6 +62,7 @@ import {
TCreateCaDTO,
TDeleteCaDTO,
TGenerateRootCaCertificateDTO,
TGetCaCertByIdDTO,
TGetCaCertDTO,
TGetCaCertificateTemplatesDTO,
TGetCaCertsDTO,
@@ -556,6 +557,7 @@ export const internalCertificateAuthorityServiceFactory = ({
let certificate = "";
let certificateChain = "";
let newCertId = "";
switch (ca.internalCa.type) {
case InternalCaType.ROOT: {
@@ -611,6 +613,8 @@ export const internalCertificateAuthorityServiceFactory = ({
tx
);
newCertId = newCaCert.id;
await internalCertificateAuthorityDAL.update(
{
caId: ca.id
@@ -753,6 +757,8 @@ export const internalCertificateAuthorityServiceFactory = ({
tx
);
newCertId = newCaCert.id;
await internalCertificateAuthorityDAL.update(
{
caId: ca.id
@@ -780,6 +786,7 @@ export const internalCertificateAuthorityServiceFactory = ({
certificate,
certificateChain,
serialNumber,
certId: newCertId,
ca: {
...ca,
...ca.internalCa
@@ -854,6 +861,7 @@ export const internalCertificateAuthorityServiceFactory = ({
certificate: caCert,
certificateChain: caCertChain,
serialNumber,
certId: ca.internalCa.activeCaCertId,
ca: expandInternalCa(ca)
};
};
@@ -891,6 +899,61 @@ export const internalCertificateAuthorityServiceFactory = ({
return caCertObj;
};
const getCaCertByIdWithAuth = async ({
caId,
certId,
actorId,
actorAuthMethod,
actor,
actorOrgId
}: TGetCaCertByIdDTO) => {
const ca = await certificateAuthorityDAL.findByIdWithAssociatedCa(caId);
if (!ca.internalCa) throw new NotFoundError({ message: `CA with ID '${caId}' not found` });
const { permission } = await permissionService.getProjectPermission({
actor,
actorId,
projectId: ca.projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.CertificateManager
});
ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionCertificateAuthorityActions.Read,
subject(ProjectPermissionSub.CertificateAuthorities, { name: ca.name })
);
const caCert = await certificateAuthorityCertDAL.findOne({
caId,
id: certId
});
if (!caCert) {
throw new NotFoundError({ message: `Certificate with ID '${certId}' not found for CA with ID '${caId}'` });
}
const {
caCert: certificate,
caCertChain: certificateChain,
serialNumber
} = await getCaCertChain({
caCertId: certId,
certificateAuthorityDAL,
certificateAuthorityCertDAL,
projectDAL,
kmsService
});
return {
certificate,
certificateChain,
serialNumber,
certId,
ca: expandInternalCa(ca)
};
};
/**
* Issue certificate to be imported back in for intermediate CA
*/
@@ -1296,10 +1359,16 @@ export const internalCertificateAuthorityServiceFactory = ({
parentCaId
});
const updatedCa = await certificateAuthorityDAL.findByIdWithAssociatedCa(caId, tx);
if (!updatedCa.internalCa?.activeCaCertId) {
throw new Error("Failed to get certificate ID after import");
}
return {
certificate: signedResult.certificate,
certificateChain: signedResult.certificateChain,
serialNumber: signedResult.serialNumber
serialNumber: signedResult.serialNumber,
certId: updatedCa.internalCa.activeCaCertId
};
};
@@ -2275,6 +2344,7 @@ export const internalCertificateAuthorityServiceFactory = ({
certificate: cert.toString("pem"),
certificateChain: "",
serialNumber,
certId: caCert.id,
ca: expandInternalCa(updatedCa)
};
});
@@ -2290,6 +2360,7 @@ export const internalCertificateAuthorityServiceFactory = ({
getCaCerts,
getCaCert,
getCaCertById,
getCaCertByIdWithAuth,
signIntermediate,
importCertToCa,
generateIntermediateCaCertificate,

View File

@@ -114,6 +114,11 @@ export type TGetCaCertDTO = {
caId: string;
} & Omit<TProjectPermission, "projectId">;
export type TGetCaCertByIdDTO = {
caId: string;
certId: string;
} & Omit<TProjectPermission, "projectId">;
export type TSignIntermediateDTO = {
isInternal?: boolean;
} & (