mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-10 14:28:35 +00:00
Check and sync status
This commit is contained in:
@@ -4,6 +4,7 @@ import { TDbClient } from "@app/db";
|
|||||||
import { TableName } from "@app/db/schemas";
|
import { TableName } from "@app/db/schemas";
|
||||||
import { DatabaseError } from "@app/lib/errors";
|
import { DatabaseError } from "@app/lib/errors";
|
||||||
import { ormify, selectAllTableCols, sqlNestRelationships } from "@app/lib/knex";
|
import { ormify, selectAllTableCols, sqlNestRelationships } from "@app/lib/knex";
|
||||||
|
import { CertificateRequestStatus } from "@app/services/certificate-request/certificate-request-types";
|
||||||
|
|
||||||
export type TPkiAcmeOrderDALFactory = ReturnType<typeof pkiAcmeOrderDALFactory>;
|
export type TPkiAcmeOrderDALFactory = ReturnType<typeof pkiAcmeOrderDALFactory>;
|
||||||
|
|
||||||
@@ -19,6 +20,42 @@ export const pkiAcmeOrderDALFactory = (db: TDbClient) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const findWithCertificateRequestForSync = async (id: string, tx?: Knex) => {
|
||||||
|
try {
|
||||||
|
const order = await (tx || db)(TableName.PkiAcmeOrder)
|
||||||
|
.leftJoin(
|
||||||
|
TableName.CertificateRequests,
|
||||||
|
`${TableName.PkiAcmeOrder}.id`,
|
||||||
|
`${TableName.CertificateRequests}.certificateId`
|
||||||
|
)
|
||||||
|
.select(
|
||||||
|
selectAllTableCols(TableName.PkiAcmeOrder),
|
||||||
|
db.ref("id").withSchema(TableName.CertificateRequests).as("certificateRequestId"),
|
||||||
|
db.ref("status").withSchema(TableName.CertificateRequests).as("certificateRequestStatus")
|
||||||
|
)
|
||||||
|
.forUpdate(TableName.PkiAcmeOrder)
|
||||||
|
.where(`${TableName.PkiAcmeOrder}.id`, id)
|
||||||
|
.first();
|
||||||
|
if (!order) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
...order,
|
||||||
|
certificateRequest:
|
||||||
|
order.certificateRequestId && order.certificateRequestStatus
|
||||||
|
? {
|
||||||
|
id: order.certificateRequestId,
|
||||||
|
status: order.certificateRequestStatus as CertificateRequestStatus,
|
||||||
|
// The certificate id for async certificate request is the same as the order id
|
||||||
|
certificateId: order.id
|
||||||
|
}
|
||||||
|
: undefined
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
throw new DatabaseError({ error, name: "Find PKI ACME order by id with certificate request" });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const findByAccountAndOrderIdWithAuthorizations = async (accountId: string, orderId: string, tx?: Knex) => {
|
const findByAccountAndOrderIdWithAuthorizations = async (accountId: string, orderId: string, tx?: Knex) => {
|
||||||
try {
|
try {
|
||||||
const rows = await (tx || db)(TableName.PkiAcmeOrder)
|
const rows = await (tx || db)(TableName.PkiAcmeOrder)
|
||||||
@@ -72,6 +109,7 @@ export const pkiAcmeOrderDALFactory = (db: TDbClient) => {
|
|||||||
return {
|
return {
|
||||||
...pkiAcmeOrderOrm,
|
...pkiAcmeOrderOrm,
|
||||||
findByIdForFinalization,
|
findByIdForFinalization,
|
||||||
|
findWithCertificateRequestForSync,
|
||||||
findByAccountAndOrderIdWithAuthorizations,
|
findByAccountAndOrderIdWithAuthorizations,
|
||||||
listByAccountId
|
listByAccountId
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -104,6 +104,7 @@ import {
|
|||||||
TRawJwsPayload,
|
TRawJwsPayload,
|
||||||
TRespondToAcmeChallengeResponse
|
TRespondToAcmeChallengeResponse
|
||||||
} from "./pki-acme-types";
|
} from "./pki-acme-types";
|
||||||
|
import { TPkiAcmeOrders } from "@app/db/schemas";
|
||||||
|
|
||||||
type TPkiAcmeServiceFactoryDep = {
|
type TPkiAcmeServiceFactoryDep = {
|
||||||
projectDAL: Pick<TProjectDALFactory, "findOne" | "updateById" | "transaction" | "findById">;
|
projectDAL: Pick<TProjectDALFactory, "findOne" | "updateById" | "transaction" | "findById">;
|
||||||
@@ -117,11 +118,13 @@ type TPkiAcmeServiceFactoryDep = {
|
|||||||
>;
|
>;
|
||||||
acmeOrderDAL: Pick<
|
acmeOrderDAL: Pick<
|
||||||
TPkiAcmeOrderDALFactory,
|
TPkiAcmeOrderDALFactory,
|
||||||
|
| "findById"
|
||||||
| "create"
|
| "create"
|
||||||
| "transaction"
|
| "transaction"
|
||||||
| "updateById"
|
| "updateById"
|
||||||
| "findByAccountAndOrderIdWithAuthorizations"
|
| "findByAccountAndOrderIdWithAuthorizations"
|
||||||
| "findByIdForFinalization"
|
| "findByIdForFinalization"
|
||||||
|
| "findWithCertificateRequestForSync"
|
||||||
| "listByAccountId"
|
| "listByAccountId"
|
||||||
>;
|
>;
|
||||||
acmeAuthDAL: Pick<TPkiAcmeAuthDALFactory, "create" | "findByAccountIdAndAuthIdWithChallenges">;
|
acmeAuthDAL: Pick<TPkiAcmeAuthDALFactory, "create" | "findByAccountIdAndAuthIdWithChallenges">;
|
||||||
@@ -370,6 +373,50 @@ export const pkiAcmeServiceFactory = ({
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const checkAndSyncAcmeOrderStatus = async ({ orderId }: { orderId: string }): Promise<TPkiAcmeOrders> => {
|
||||||
|
const order = await acmeOrderDAL.findById(orderId);
|
||||||
|
if (!order) {
|
||||||
|
throw new NotFoundError({ message: "ACME order not found" });
|
||||||
|
}
|
||||||
|
if (order.status !== AcmeOrderStatus.Ready) {
|
||||||
|
return order;
|
||||||
|
}
|
||||||
|
return acmeOrderDAL.transaction(async (tx) => {
|
||||||
|
// Lock the order for syncing with async cert request
|
||||||
|
const orderWithCertificateRequest = await acmeOrderDAL.findWithCertificateRequestForSync(orderId, tx);
|
||||||
|
if (!orderWithCertificateRequest) {
|
||||||
|
throw new NotFoundError({ message: "ACME order not found" });
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
orderWithCertificateRequest.status !== AcmeOrderStatus.Ready ||
|
||||||
|
!orderWithCertificateRequest.certificateRequest
|
||||||
|
) {
|
||||||
|
return orderWithCertificateRequest;
|
||||||
|
}
|
||||||
|
let newStatus: AcmeOrderStatus | undefined;
|
||||||
|
let newCertificateId: string | undefined;
|
||||||
|
switch (orderWithCertificateRequest.certificateRequest.status) {
|
||||||
|
case CertificateRequestStatus.PENDING:
|
||||||
|
break;
|
||||||
|
case CertificateRequestStatus.ISSUED:
|
||||||
|
newStatus = AcmeOrderStatus.Valid;
|
||||||
|
newCertificateId = orderWithCertificateRequest.certificateRequest.certificateId;
|
||||||
|
break;
|
||||||
|
case CertificateRequestStatus.FAILED:
|
||||||
|
newStatus = AcmeOrderStatus.Invalid;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new AcmeServerInternalError({
|
||||||
|
message: `Invalid certificate request status: ${orderWithCertificateRequest.certificateRequest.status as string}`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (newStatus) {
|
||||||
|
return acmeOrderDAL.updateById(orderId, { status: newStatus, certificateId: newCertificateId }, tx);
|
||||||
|
}
|
||||||
|
return orderWithCertificateRequest;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const getAcmeDirectory = async (profileId: string): Promise<TGetAcmeDirectoryResponse> => {
|
const getAcmeDirectory = async (profileId: string): Promise<TGetAcmeDirectoryResponse> => {
|
||||||
const profile = await validateAcmeProfile(profileId);
|
const profile = await validateAcmeProfile(profileId);
|
||||||
return {
|
return {
|
||||||
@@ -737,9 +784,11 @@ export const pkiAcmeServiceFactory = ({
|
|||||||
if (!order) {
|
if (!order) {
|
||||||
throw new NotFoundError({ message: "ACME order not found" });
|
throw new NotFoundError({ message: "ACME order not found" });
|
||||||
}
|
}
|
||||||
|
// Sync order first in case if there is a certificate request that needs to be processed
|
||||||
|
const syncedOrder = await checkAndSyncAcmeOrderStatus({ orderId });
|
||||||
return {
|
return {
|
||||||
status: 200,
|
status: 200,
|
||||||
body: buildAcmeOrderResource({ profileId, order }),
|
body: buildAcmeOrderResource({ profileId, order: syncedOrder }),
|
||||||
headers: {
|
headers: {
|
||||||
Location: buildUrl(profileId, `/orders/${orderId}`),
|
Location: buildUrl(profileId, `/orders/${orderId}`),
|
||||||
Link: `<${buildUrl(profileId, "/directory")}>;rel="index"`
|
Link: `<${buildUrl(profileId, "/directory")}>;rel="index"`
|
||||||
@@ -1001,7 +1050,7 @@ export const pkiAcmeServiceFactory = ({
|
|||||||
}
|
}
|
||||||
if (certIssuanceJobData) {
|
if (certIssuanceJobData) {
|
||||||
// TODO: ideally, this should be done inside the transaction, but the pg-boss queue doesn't support external transactions
|
// TODO: ideally, this should be done inside the transaction, but the pg-boss queue doesn't support external transactions
|
||||||
// as it seems to be.
|
// as it seems to be. we need to commit the transaction before queuing the job, otherwise the job will fail (not found error).
|
||||||
await certificateIssuanceQueue.queueCertificateIssuance(certIssuanceJobData);
|
await certificateIssuanceQueue.queueCertificateIssuance(certIssuanceJobData);
|
||||||
}
|
}
|
||||||
order = updatedOrder;
|
order = updatedOrder;
|
||||||
@@ -1049,14 +1098,16 @@ export const pkiAcmeServiceFactory = ({
|
|||||||
if (!order) {
|
if (!order) {
|
||||||
throw new NotFoundError({ message: "ACME order not found" });
|
throw new NotFoundError({ message: "ACME order not found" });
|
||||||
}
|
}
|
||||||
if (order.status !== AcmeOrderStatus.Valid) {
|
// Sync order first in case if there is a certificate request that needs to be processed
|
||||||
|
const syncedOrder = await checkAndSyncAcmeOrderStatus({ orderId });
|
||||||
|
if (syncedOrder.status !== AcmeOrderStatus.Valid) {
|
||||||
throw new AcmeOrderNotReadyError({ message: "ACME order is not valid" });
|
throw new AcmeOrderNotReadyError({ message: "ACME order is not valid" });
|
||||||
}
|
}
|
||||||
if (!order.certificateId) {
|
if (!syncedOrder.certificateId) {
|
||||||
throw new NotFoundError({ message: "The certificate for this ACME order no longer exists" });
|
throw new NotFoundError({ message: "The certificate for this ACME order no longer exists" });
|
||||||
}
|
}
|
||||||
|
|
||||||
const certBody = await certificateBodyDAL.findOne({ certId: order.certificateId });
|
const certBody = await certificateBodyDAL.findOne({ certId: syncedOrder.certificateId });
|
||||||
const certificateManagerKeyId = await getProjectKmsCertificateKeyId({
|
const certificateManagerKeyId = await getProjectKmsCertificateKeyId({
|
||||||
projectId: profile.projectId,
|
projectId: profile.projectId,
|
||||||
projectDAL,
|
projectDAL,
|
||||||
|
|||||||
Reference in New Issue
Block a user