Handle errors

This commit is contained in:
Fang-Pen Lin
2025-10-31 21:43:53 -07:00
parent 47ee70c6a0
commit e5db20d062
4 changed files with 71 additions and 29 deletions

View File

@@ -18,7 +18,8 @@ export const PkiAcmeOrdersSchema = z.object({
updatedAt: z.date(),
csr: z.string().nullable().optional(),
certificate: z.string().nullable().optional(),
certificateChain: z.string().nullable().optional()
certificateChain: z.string().nullable().optional(),
error: z.string().nullable().optional()
});
export type TPkiAcmeOrders = z.infer<typeof PkiAcmeOrdersSchema>;

View File

@@ -528,3 +528,24 @@ export class AcmeOrderNotReadyError extends AcmeError {
this.name = "AcmeOrderNotReadyError";
}
}
export class AcmeBadCSRError extends AcmeError {
constructor({
detail = "The CSR is unacceptable",
error,
message
}: {
detail?: string;
error?: unknown;
message?: string;
} = {}) {
super({
type: AcmeErrorType.BadCsr,
detail,
status: 400,
error,
message
});
this.name = "AcmeBadCSRError";
}
}

View File

@@ -2,7 +2,7 @@ import { TPkiAcmeAccounts } from "@app/db/schemas/pki-acme-accounts";
import { TPkiAcmeAuths } from "@app/db/schemas/pki-acme-auths";
import { getConfig } from "@app/lib/config/env";
import { crypto } from "@app/lib/crypto/cryptography";
import { NotFoundError } from "@app/lib/errors";
import { BadRequestError, NotFoundError } from "@app/lib/errors";
import { logger } from "@app/lib/logger";
import { TCertificateProfileDALFactory } from "@app/services/certificate-profile/certificate-profile-dal";
@@ -25,6 +25,7 @@ import { TPkiAcmeAuthDALFactory } from "./pki-acme-auth-dal";
import { TPkiAcmeChallengeDALFactory } from "./pki-acme-challenge-dal";
import {
AcmeAccountDoesNotExistError,
AcmeBadCSRError,
AcmeBadPublicKeyError,
AcmeError,
AcmeMalformedError,
@@ -520,32 +521,51 @@ export const pkiAcmeServiceFactory = ({
const { csr } = payload;
// TODO: validate the CSR and return badCSR error if it's invalid
// TODO: this should be the same transaction?
const { certificate, certificateChain, certificateId } = await certificateV3Service.signCertificateFromProfile({
actor: ActorType.ACME_ACCOUNT,
actorId: accountId,
actorAuthMethod: null,
actorOrgId,
profileId,
csr,
notBefore: order.notBefore ? new Date(order.notBefore) : undefined,
notAfter: order.notAfter ? new Date(order.notAfter) : undefined,
validity: {
// TODO: read config from the profile to get the expiration time instead
ttl: (24 * 60 * 60 * 1000).toString()
},
enrollmentType: EnrollmentType.ACME
});
// TODO: associate the certificate with the order
await acmeOrderDAL.updateById(
orderId,
{
status: AcmeOrderStatus.Valid,
csr,
certificateChain,
certificate
},
tx
);
try {
const { certificate, certificateChain, certificateId } =
await certificateV3Service.signCertificateFromProfile({
actor: ActorType.ACME_ACCOUNT,
actorId: accountId,
actorAuthMethod: null,
actorOrgId,
profileId,
csr,
notBefore: order.notBefore ? new Date(order.notBefore) : undefined,
notAfter: order.notAfter ? new Date(order.notAfter) : undefined,
validity: {
// TODO: read config from the profile to get the expiration time instead
ttl: (24 * 60 * 60 * 1000).toString()
},
enrollmentType: EnrollmentType.ACME
});
// TODO: associate the certificate with the order
await acmeOrderDAL.updateById(
orderId,
{
status: AcmeOrderStatus.Valid,
csr,
certificateChain,
certificate
},
tx
);
} catch (error) {
await acmeOrderDAL.updateById(
orderId,
{
csr,
status: AcmeOrderStatus.Invalid,
error: error instanceof Error ? error.message : "Unknown error"
},
tx
);
// TODO: log the error
// TODO: audit log the error
if (error instanceof BadRequestError) {
throw new AcmeBadCSRError({ detail: `Invalid CSR: ${error.message}` });
}
throw new AcmeServerInternalError({ detail: "Failed to sign certificate" });
}
return await acmeOrderDAL.findByAccountAndOrderIdWithAuthorizations(accountId, orderId, tx);
});
} else if (order.status !== AcmeOrderStatus.Valid) {

View File

@@ -74,7 +74,7 @@ export const certificateProfileDALFactory = (db: TDbClient) => {
.join(TableName.Project, `${TableName.PkiCertificateProfile}.projectId`, `${TableName.Project}.id`)
.select(selectAllTableCols(TableName.PkiCertificateProfile))
.select(db.ref("orgId").withSchema(TableName.Project).as("ownerOrgId"))
.where({ id })
.where(`${TableName.PkiCertificateProfile}.id`, id)
.first()) as (TCertificateProfile & { ownerOrgId: string }) | undefined;
return certificateProfile;
} catch (error) {