From c6f51cbd106353e934469b81ded04082722e5de6 Mon Sep 17 00:00:00 2001 From: Fang-Pen Lin Date: Wed, 29 Oct 2025 20:31:41 -0700 Subject: [PATCH] Implement get acme order --- .../services/pki-acme/pki-acme-order-dal.ts | 81 +++++++------------ .../ee/services/pki-acme/pki-acme-service.ts | 31 ++++--- .../ee/services/pki-acme/pki-acme-types.ts | 2 + 3 files changed, 52 insertions(+), 62 deletions(-) diff --git a/backend/src/ee/services/pki-acme/pki-acme-order-dal.ts b/backend/src/ee/services/pki-acme/pki-acme-order-dal.ts index fabaaf2a1..2c7ea6aee 100644 --- a/backend/src/ee/services/pki-acme/pki-acme-order-dal.ts +++ b/backend/src/ee/services/pki-acme/pki-acme-order-dal.ts @@ -1,10 +1,10 @@ import { Knex } from "knex"; 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 { DatabaseError } from "@app/lib/errors"; -import { ormify } from "@app/lib/knex"; +import { ormify, selectAllTableCols } from "@app/lib/knex"; export type TPkiAcmeOrderDALFactory = ReturnType; @@ -51,54 +51,35 @@ export const pkiAcmeOrderDALFactory = (db: TDbClient) => { } }; - const findByAccountId = async (accountId: string, tx?: Knex) => { + const findByIdWithAuthorizations = async (id: string, tx?: Knex) => { 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) { - throw new DatabaseError({ error, name: "Find PKI ACME orders by account 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" }); + throw new DatabaseError({ error, name: "Find PKI ACME order by id" }); } }; @@ -107,10 +88,6 @@ export const pkiAcmeOrderDALFactory = (db: TDbClient) => { create, updateById, findById, - findByAccountId, - findByStatus, - findByAccountIdAndStatus, - deleteById, - deleteByAccountId + findByIdWithAuthorizations }; }; diff --git a/backend/src/ee/services/pki-acme/pki-acme-service.ts b/backend/src/ee/services/pki-acme/pki-acme-service.ts index ba9197b33..9b6f9d4e5 100644 --- a/backend/src/ee/services/pki-acme/pki-acme-service.ts +++ b/backend/src/ee/services/pki-acme/pki-acme-service.ts @@ -7,7 +7,9 @@ import { AcmeAccountDoesNotExistError, AcmeBadPublicKeyError, AcmeMalformedError, - AcmeServerInternalError + AcmeServerInternalError, + AcmeUnauthorizedError, + AcmeUnsupportedIdentifierError } from "./pki-acme-errors"; import { TPkiAcmeAccounts } from "@app/db/schemas/pki-acme-accounts"; @@ -54,7 +56,7 @@ import { type TPkiAcmeServiceFactoryDep = { certificateProfileDAL: Pick; acmeAccountDAL: Pick; - acmeOrderDAL: Pick; + acmeOrderDAL: Pick; acmeAuthDAL: Pick; acmeOrderAuthDAL: Pick; }; @@ -170,7 +172,7 @@ export const pkiAcmeServiceFactory = ({ } const accountId = extractAccountIdFromKid(protectedHeader.kid, profileId); 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); if (!account) { @@ -294,7 +296,7 @@ export const pkiAcmeServiceFactory = ({ tx ); } 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 ({ profileId, + accountId, orderId }: { profileId: string; + accountId: string; orderId: string; }): Promise> => { - const profile = await validateAcmeProfile(profileId); - // FIXME: Implement ACME get order + const order = await acmeOrderDAL.findByIdWithAuthorizations(orderId); + if (!order || order.accountId !== accountId) { + throw new NotFoundError({ message: "ACME order not found" }); + } return { status: 200, body: { - status: "pending", - expires: new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString(), - identifiers: [], - authorizations: [], + status: order.status, + expires: order.expiresAt.toISOString(), + identifiers: order.authorizations.map((auth: TPkiAcmeAuths) => ({ + 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`) }, headers: { Location: buildUrl(`/api/v1/pki/acme/profiles/${profileId}/orders/${orderId}`) } diff --git a/backend/src/ee/services/pki-acme/pki-acme-types.ts b/backend/src/ee/services/pki-acme/pki-acme-types.ts index 827e28c7e..e9fa4ffa0 100644 --- a/backend/src/ee/services/pki-acme/pki-acme-types.ts +++ b/backend/src/ee/services/pki-acme/pki-acme-types.ts @@ -109,9 +109,11 @@ export type TPkiAcmeServiceFactory = { }) => Promise>; getAcmeOrder: ({ profileId, + accountId, orderId }: { profileId: string; + accountId: string; orderId: string; }) => Promise>; finalizeAcmeOrder: ({