Implement get acme order

This commit is contained in:
Fang-Pen Lin
2025-11-07 09:18:34 -08:00
parent ea4b36b0ae
commit c6f51cbd10
3 changed files with 52 additions and 62 deletions
@@ -1,10 +1,10 @@
import { Knex } from "knex"; import { Knex } from "knex";
import { TDbClient } from "@app/db"; import { TDbClient } from "@app/db";
import { TableName } from "@app/db/schemas"; import { TableName, TPkiAcmeAuths } from "@app/db/schemas";
import { TPkiAcmeOrdersInsert, TPkiAcmeOrdersUpdate } from "@app/db/schemas/pki-acme-orders"; import { TPkiAcmeOrdersInsert, TPkiAcmeOrdersUpdate } from "@app/db/schemas/pki-acme-orders";
import { DatabaseError } from "@app/lib/errors"; import { DatabaseError } from "@app/lib/errors";
import { ormify } from "@app/lib/knex"; import { ormify, selectAllTableCols } from "@app/lib/knex";
export type TPkiAcmeOrderDALFactory = ReturnType<typeof pkiAcmeOrderDALFactory>; export type TPkiAcmeOrderDALFactory = ReturnType<typeof pkiAcmeOrderDALFactory>;
@@ -51,54 +51,35 @@ export const pkiAcmeOrderDALFactory = (db: TDbClient) => {
} }
}; };
const findByAccountId = async (accountId: string, tx?: Knex) => { const findByIdWithAuthorizations = async (id: string, tx?: Knex) => {
try { try {
const orders = await (tx || db)(TableName.PkiAcmeOrder).where({ accountId }); const order = await (tx || db)(TableName.PkiAcmeOrder)
.join(TableName.PkiAcmeOrderAuth, `${TableName.PkiAcmeOrderAuth}.orderId`, `${TableName.PkiAcmeOrder}.id`)
.join(TableName.PkiAcmeAuth, `${TableName.PkiAcmeOrderAuth}.authId`, `${TableName.PkiAcmeAuth}.id`)
.select(
selectAllTableCols(TableName.PkiAcmeOrder),
db.ref("id").withSchema(TableName.PkiAcmeAuth).as("authId"),
db.ref("identifierType").withSchema(TableName.PkiAcmeAuth).as("identifierType"),
db.ref("identifierValue").withSchema(TableName.PkiAcmeAuth).as("identifierValue"),
db.ref("expiresAt").withSchema(TableName.PkiAcmeAuth).as("expiresAt")
)
.where(`${TableName.PkiAcmeOrder}.id`, id)
.first();
return orders; if (!order) {
return null;
}
return {
...order,
authorizations: order.authorizations.map((auth: TPkiAcmeAuths) => ({
id: auth.id,
identifierType: auth.identifierType,
identifierValue: auth.identifierValue,
expiresAt: auth.expiresAt
}))
};
} catch (error) { } catch (error) {
throw new DatabaseError({ error, name: "Find PKI ACME orders by account id" }); throw new DatabaseError({ error, name: "Find PKI ACME order by id" });
}
};
const findByStatus = async (status: string, tx?: Knex) => {
try {
const orders = await (tx || db)(TableName.PkiAcmeOrder).where({ status });
return orders;
} catch (error) {
throw new DatabaseError({ error, name: "Find PKI ACME orders by status" });
}
};
const findByAccountIdAndStatus = async (accountId: string, status: string, tx?: Knex) => {
try {
const orders = await (tx || db)(TableName.PkiAcmeOrder).where({ accountId, status });
return orders;
} catch (error) {
throw new DatabaseError({ error, name: "Find PKI ACME orders by account id and status" });
}
};
const deleteById = async (id: string, tx?: Knex) => {
try {
const result = await (tx || db)(TableName.PkiAcmeOrder).where({ id }).delete().returning("*");
const [order] = result;
return order || null;
} catch (error) {
throw new DatabaseError({ error, name: "Delete PKI ACME order by id" });
}
};
const deleteByAccountId = async (accountId: string, tx?: Knex) => {
try {
const result = await (tx || db)(TableName.PkiAcmeOrder).where({ accountId }).delete().returning("*");
return result;
} catch (error) {
throw new DatabaseError({ error, name: "Delete PKI ACME orders by account id" });
} }
}; };
@@ -107,10 +88,6 @@ export const pkiAcmeOrderDALFactory = (db: TDbClient) => {
create, create,
updateById, updateById,
findById, findById,
findByAccountId, findByIdWithAuthorizations
findByStatus,
findByAccountIdAndStatus,
deleteById,
deleteByAccountId
}; };
}; };
@@ -7,7 +7,9 @@ import {
AcmeAccountDoesNotExistError, AcmeAccountDoesNotExistError,
AcmeBadPublicKeyError, AcmeBadPublicKeyError,
AcmeMalformedError, AcmeMalformedError,
AcmeServerInternalError AcmeServerInternalError,
AcmeUnauthorizedError,
AcmeUnsupportedIdentifierError
} from "./pki-acme-errors"; } from "./pki-acme-errors";
import { TPkiAcmeAccounts } from "@app/db/schemas/pki-acme-accounts"; import { TPkiAcmeAccounts } from "@app/db/schemas/pki-acme-accounts";
@@ -54,7 +56,7 @@ import {
type TPkiAcmeServiceFactoryDep = { type TPkiAcmeServiceFactoryDep = {
certificateProfileDAL: Pick<TCertificateProfileDALFactory, "findById">; certificateProfileDAL: Pick<TCertificateProfileDALFactory, "findById">;
acmeAccountDAL: Pick<TPkiAcmeAccountDALFactory, "findByProjectIdAndAccountId" | "findByPublicKey" | "create">; acmeAccountDAL: Pick<TPkiAcmeAccountDALFactory, "findByProjectIdAndAccountId" | "findByPublicKey" | "create">;
acmeOrderDAL: Pick<TPkiAcmeOrderDALFactory, "create" | "transaction">; acmeOrderDAL: Pick<TPkiAcmeOrderDALFactory, "create" | "transaction" | "findByIdWithAuthorizations">;
acmeAuthDAL: Pick<TPkiAcmeAuthDALFactory, "create">; acmeAuthDAL: Pick<TPkiAcmeAuthDALFactory, "create">;
acmeOrderAuthDAL: Pick<TPkiAcmeOrderAuthDALFactory, "insertMany">; acmeOrderAuthDAL: Pick<TPkiAcmeOrderAuthDALFactory, "insertMany">;
}; };
@@ -170,7 +172,7 @@ export const pkiAcmeServiceFactory = ({
} }
const accountId = extractAccountIdFromKid(protectedHeader.kid, profileId); const accountId = extractAccountIdFromKid(protectedHeader.kid, profileId);
if (expectedAccountId && accountId !== expectedAccountId) { if (expectedAccountId && accountId !== expectedAccountId) {
throw new AcmeAccountDoesNotExistError({ message: "ACME account ID mismatch" }); throw new NotFoundError({ message: "ACME resource not found" });
} }
const account = await acmeAccountDAL.findByProjectIdAndAccountId(profile.id, accountId); const account = await acmeAccountDAL.findByProjectIdAndAccountId(profile.id, accountId);
if (!account) { if (!account) {
@@ -294,7 +296,7 @@ export const pkiAcmeServiceFactory = ({
tx tx
); );
} else { } else {
throw new AcmeMalformedError({ detail: "Only DNS identifiers are supported" }); throw new AcmeUnsupportedIdentifierError({ detail: "Only DNS identifiers are supported" });
} }
}) })
); );
@@ -373,20 +375,29 @@ export const pkiAcmeServiceFactory = ({
const getAcmeOrder = async ({ const getAcmeOrder = async ({
profileId, profileId,
accountId,
orderId orderId
}: { }: {
profileId: string; profileId: string;
accountId: string;
orderId: string; orderId: string;
}): Promise<TAcmeResponse<TGetAcmeOrderResponse>> => { }): Promise<TAcmeResponse<TGetAcmeOrderResponse>> => {
const profile = await validateAcmeProfile(profileId); const order = await acmeOrderDAL.findByIdWithAuthorizations(orderId);
// FIXME: Implement ACME get order if (!order || order.accountId !== accountId) {
throw new NotFoundError({ message: "ACME order not found" });
}
return { return {
status: 200, status: 200,
body: { body: {
status: "pending", status: order.status,
expires: new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString(), expires: order.expiresAt.toISOString(),
identifiers: [], identifiers: order.authorizations.map((auth: TPkiAcmeAuths) => ({
authorizations: [], type: auth.identifierType,
value: auth.identifierValue
})),
authorizations: order.authorizations.map((auth: TPkiAcmeAuths) =>
buildUrl(`/api/v1/pki/acme/profiles/${profileId}/authorizations/${auth.id}`)
),
finalize: buildUrl(`/api/v1/pki/acme/profiles/${profileId}/orders/${orderId}/finalize`) finalize: buildUrl(`/api/v1/pki/acme/profiles/${profileId}/orders/${orderId}/finalize`)
}, },
headers: { Location: buildUrl(`/api/v1/pki/acme/profiles/${profileId}/orders/${orderId}`) } headers: { Location: buildUrl(`/api/v1/pki/acme/profiles/${profileId}/orders/${orderId}`) }
@@ -109,9 +109,11 @@ export type TPkiAcmeServiceFactory = {
}) => Promise<TAcmeResponse<TListAcmeOrdersResponse>>; }) => Promise<TAcmeResponse<TListAcmeOrdersResponse>>;
getAcmeOrder: ({ getAcmeOrder: ({
profileId, profileId,
accountId,
orderId orderId
}: { }: {
profileId: string; profileId: string;
accountId: string;
orderId: string; orderId: string;
}) => Promise<TAcmeResponse<TGetAcmeOrderResponse>>; }) => Promise<TAcmeResponse<TGetAcmeOrderResponse>>;
finalizeAcmeOrder: ({ finalizeAcmeOrder: ({