Extract types

This commit is contained in:
Fang-Pen Lin
2025-11-07 09:17:49 -08:00
parent 4875add711
commit 2f4066cafb
4 changed files with 94 additions and 43 deletions
+16 -7
View File
@@ -1,8 +1,6 @@
/* eslint-disable @typescript-eslint/no-floating-promises */ /* eslint-disable @typescript-eslint/no-floating-promises */
import { z } from "zod"; import { z } from "zod";
import { ApiDocsTags } from "@app/lib/api-docs";
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
import { import {
CreateAcmeAccountResponseSchema, CreateAcmeAccountResponseSchema,
CreateAcmeAccountSchema, CreateAcmeAccountSchema,
@@ -25,8 +23,13 @@ import {
RespondToAcmeChallengeResponseSchema, RespondToAcmeChallengeResponseSchema,
RespondToAcmeChallengeSchema RespondToAcmeChallengeSchema
} from "@app/ee/services/pki-acme/pki-acme-schemas"; } from "@app/ee/services/pki-acme/pki-acme-schemas";
import { ApiDocsTags } from "@app/lib/api-docs";
import { getConfig } from "@app/lib/config/env";
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
export const registerPkiAcmeRouter = async (server: FastifyZodProvider) => { export const registerPkiAcmeRouter = async (server: FastifyZodProvider) => {
const appCfg = getConfig();
server.addContentTypeParser("application/jose+json", { parseAs: "string" }, (_, body, done) => { server.addContentTypeParser("application/jose+json", { parseAs: "string" }, (_, body, done) => {
try { try {
const strBody = body instanceof Buffer ? body.toString() : body; const strBody = body instanceof Buffer ? body.toString() : body;
@@ -107,11 +110,21 @@ export const registerPkiAcmeRouter = async (server: FastifyZodProvider) => {
} }
}, },
handler: async (req, res) => { handler: async (req, res) => {
// TODO: check nonce here
// TODO: check signature here
const account = await server.services.pkiAcme.createAcmeAccount(req.params.profileId, req.body); const account = await server.services.pkiAcme.createAcmeAccount(req.params.profileId, req.body);
// TODO: deal with existing account case here // TODO: deal with existing account case here
res.code(201); res.code(201);
res.header(
"Location",
`${appCfg.SITE_URL}/api/v1/pki/acme/profiles/${req.params.profileId}/accounts/${account.accountUrl}`
);
// TODO: DRY
const nonce = await server.services.pkiAcme.getAcmeNewNonce(req.params.profileId); const nonce = await server.services.pkiAcme.getAcmeNewNonce(req.params.profileId);
res.header("Replay-Nonce", nonce); res.header("Replay-Nonce", nonce);
res.header("Cache-Control", "no-store");
return account; return account;
} }
}); });
@@ -227,11 +240,7 @@ export const registerPkiAcmeRouter = async (server: FastifyZodProvider) => {
} }
}, },
handler: async (req) => { handler: async (req) => {
const order = await server.services.pkiAcme.finalizeAcmeOrder( const order = await server.services.pkiAcme.finalizeAcmeOrder(req.params.profileId, req.params.orderId, req.body);
req.params.profileId,
req.params.orderId,
req.body.csr
);
return order; return order;
} }
}); });
@@ -21,23 +21,26 @@ export const GetAcmeNewNonceSchema = z.object({
}) })
}); });
// New Account payload schema
export const CreateAcmeAccountBodySchema = z.object({
contact: z.array(z.string()).optional(),
termsOfServiceAgreed: z.boolean().optional(),
onlyReturnExisting: z.boolean().optional(),
externalAccountBinding: z
.object({
protected: z.string(),
payload: z.string(),
signature: z.string()
})
.optional()
});
// New Account endpoint // New Account endpoint
export const CreateAcmeAccountSchema = z.object({ export const CreateAcmeAccountSchema = z.object({
params: z.object({ params: z.object({
profileId: z.string().uuid() profileId: z.string().uuid()
}), }),
body: z.object({ body: CreateAcmeAccountBodySchema
contact: z.array(z.string()).optional(),
termsOfServiceAgreed: z.boolean().optional(),
onlyReturnExisting: z.boolean().optional(),
externalAccountBinding: z
.object({
protected: z.string(),
payload: z.string(),
signature: z.string()
})
.optional()
})
}); });
export const CreateAcmeAccountResponseSchema = z.object({ export const CreateAcmeAccountResponseSchema = z.object({
@@ -47,21 +50,24 @@ export const CreateAcmeAccountResponseSchema = z.object({
accountUrl: z.string() accountUrl: z.string()
}); });
// New Order payload schema
export const CreateAcmeOrderBodySchema = z.object({
identifiers: z.array(
z.object({
type: z.string(),
value: z.string()
})
),
notBefore: z.string().optional(),
notAfter: z.string().optional()
});
// New Order endpoint // New Order endpoint
export const CreateAcmeOrderSchema = z.object({ export const CreateAcmeOrderSchema = z.object({
params: z.object({ params: z.object({
profileId: z.string().uuid() profileId: z.string().uuid()
}), }),
body: z.object({ body: CreateAcmeOrderBodySchema
identifiers: z.array(
z.object({
type: z.string(),
value: z.string()
})
),
notBefore: z.string().optional(),
notAfter: z.string().optional()
})
}); });
export const CreateAcmeOrderResponseSchema = z.object({ export const CreateAcmeOrderResponseSchema = z.object({
@@ -78,15 +84,18 @@ export const CreateAcmeOrderResponseSchema = z.object({
certificate: z.string().optional() certificate: z.string().optional()
}); });
// Account Deactivation payload schema
export const DeactivateAcmeAccountBodySchema = z.object({
status: z.literal("deactivated")
});
// Account Deactivation endpoint // Account Deactivation endpoint
export const DeactivateAcmeAccountSchema = z.object({ export const DeactivateAcmeAccountSchema = z.object({
params: z.object({ params: z.object({
profileId: z.string().uuid(), profileId: z.string().uuid(),
accountId: z.string() accountId: z.string()
}), }),
body: z.object({ body: DeactivateAcmeAccountBodySchema
status: z.literal("deactivated")
})
}); });
export const DeactivateAcmeAccountResponseSchema = z.object({ export const DeactivateAcmeAccountResponseSchema = z.object({
@@ -127,15 +136,18 @@ export const GetAcmeOrderResponseSchema = z.object({
certificate: z.string().optional() certificate: z.string().optional()
}); });
// Finalize Order payload schema
export const FinalizeAcmeOrderBodySchema = z.object({
csr: z.string()
});
// Finalize Order endpoint // Finalize Order endpoint
export const FinalizeAcmeOrderSchema = z.object({ export const FinalizeAcmeOrderSchema = z.object({
params: z.object({ params: z.object({
profileId: z.string().uuid(), profileId: z.string().uuid(),
orderId: z.string() orderId: z.string()
}), }),
body: z.object({ body: FinalizeAcmeOrderBodySchema
csr: z.string()
})
}); });
export const FinalizeAcmeOrderResponseSchema = z.object({ export const FinalizeAcmeOrderResponseSchema = z.object({
@@ -4,10 +4,14 @@ import { NotFoundError } from "@app/lib/errors";
import { TCertificateProfileDALFactory } from "@app/services/certificate-profile/certificate-profile-dal"; import { TCertificateProfileDALFactory } from "@app/services/certificate-profile/certificate-profile-dal";
import { import {
TCreateAcmeAccountPayload,
TCreateAcmeAccountResponse, TCreateAcmeAccountResponse,
TCreateAcmeOrderPayload,
TCreateAcmeOrderResponse, TCreateAcmeOrderResponse,
TDeactivateAcmeAccountPayload,
TDeactivateAcmeAccountResponse, TDeactivateAcmeAccountResponse,
TDownloadAcmeCertificateDTO, TDownloadAcmeCertificateDTO,
TFinalizeAcmeOrderPayload,
TFinalizeAcmeOrderResponse, TFinalizeAcmeOrderResponse,
TGetAcmeAuthorizationResponse, TGetAcmeAuthorizationResponse,
TGetAcmeDirectoryResponse, TGetAcmeDirectoryResponse,
@@ -49,7 +53,10 @@ export const pkiAcmeServiceFactory = ({ certificateProfileDAL }: TPkiAcmeService
return "FIXME-generate-nonce"; return "FIXME-generate-nonce";
}; };
const createAcmeAccount = async (profileId: string, body: unknown): Promise<TCreateAcmeAccountResponse> => { const createAcmeAccount = async (
profileId: string,
body: TCreateAcmeAccountPayload
): Promise<TCreateAcmeAccountResponse> => {
// FIXME: Implement ACME new account registration // FIXME: Implement ACME new account registration
// Use EAB authentication to find corresponding Infisical machine identity // Use EAB authentication to find corresponding Infisical machine identity
// Check permissions and return account information // Check permissions and return account information
@@ -63,7 +70,10 @@ export const pkiAcmeServiceFactory = ({ certificateProfileDAL }: TPkiAcmeService
}; };
}; };
const createAcmeOrder = async (profileId: string, body: unknown): Promise<TCreateAcmeOrderResponse> => { const createAcmeOrder = async (
profileId: string,
body: TCreateAcmeOrderPayload
): Promise<TCreateAcmeOrderResponse> => {
// FIXME: Implement ACME new order creation // FIXME: Implement ACME new order creation
const orderId = "FIXME-order-id"; const orderId = "FIXME-order-id";
const baseUrl = appCfg.SITE_URL || ""; const baseUrl = appCfg.SITE_URL || "";
@@ -78,7 +88,8 @@ export const pkiAcmeServiceFactory = ({ certificateProfileDAL }: TPkiAcmeService
const deactivateAcmeAccount = async ( const deactivateAcmeAccount = async (
profileId: string, profileId: string,
accountId: string accountId: string,
body?: TDeactivateAcmeAccountPayload
): Promise<TDeactivateAcmeAccountResponse> => { ): Promise<TDeactivateAcmeAccountResponse> => {
// FIXME: Implement ACME account deactivation // FIXME: Implement ACME account deactivation
return { return {
@@ -108,8 +119,9 @@ export const pkiAcmeServiceFactory = ({ certificateProfileDAL }: TPkiAcmeService
const finalizeAcmeOrder = async ( const finalizeAcmeOrder = async (
profileId: string, profileId: string,
orderId: string, orderId: string,
csr: string body: TFinalizeAcmeOrderPayload
): Promise<TFinalizeAcmeOrderResponse> => { ): Promise<TFinalizeAcmeOrderResponse> => {
const { csr } = body;
// FIXME: Implement ACME finalize order // FIXME: Implement ACME finalize order
const baseUrl = appCfg.SITE_URL || ""; const baseUrl = appCfg.SITE_URL || "";
return { return {
@@ -1,9 +1,13 @@
import { z } from "zod"; import { z } from "zod";
import { import {
CreateAcmeAccountBodySchema,
CreateAcmeAccountResponseSchema, CreateAcmeAccountResponseSchema,
CreateAcmeOrderBodySchema,
CreateAcmeOrderResponseSchema, CreateAcmeOrderResponseSchema,
DeactivateAcmeAccountBodySchema,
DeactivateAcmeAccountResponseSchema, DeactivateAcmeAccountResponseSchema,
FinalizeAcmeOrderBodySchema,
FinalizeAcmeOrderResponseSchema, FinalizeAcmeOrderResponseSchema,
GetAcmeAuthorizationResponseSchema, GetAcmeAuthorizationResponseSchema,
GetAcmeDirectoryResponseSchema, GetAcmeDirectoryResponseSchema,
@@ -23,15 +27,29 @@ export type TDownloadAcmeCertificateDTO = string;
export type TGetAcmeAuthorizationResponse = z.infer<typeof GetAcmeAuthorizationResponseSchema>; export type TGetAcmeAuthorizationResponse = z.infer<typeof GetAcmeAuthorizationResponseSchema>;
export type TRespondToAcmeChallengeResponse = z.infer<typeof RespondToAcmeChallengeResponseSchema>; export type TRespondToAcmeChallengeResponse = z.infer<typeof RespondToAcmeChallengeResponseSchema>;
// Payload types
export type TCreateAcmeAccountPayload = z.infer<typeof CreateAcmeAccountBodySchema>;
export type TCreateAcmeOrderPayload = z.infer<typeof CreateAcmeOrderBodySchema>;
export type TDeactivateAcmeAccountPayload = z.infer<typeof DeactivateAcmeAccountBodySchema>;
export type TFinalizeAcmeOrderPayload = z.infer<typeof FinalizeAcmeOrderBodySchema>;
export type TPkiAcmeServiceFactory = { export type TPkiAcmeServiceFactory = {
getAcmeDirectory: (profileId: string) => Promise<TGetAcmeDirectoryResponse>; getAcmeDirectory: (profileId: string) => Promise<TGetAcmeDirectoryResponse>;
getAcmeNewNonce: (profileId: string) => Promise<string>; getAcmeNewNonce: (profileId: string) => Promise<string>;
createAcmeAccount: (profileId: string, body: unknown) => Promise<TCreateAcmeAccountResponse>; createAcmeAccount: (profileId: string, body: TCreateAcmeAccountPayload) => Promise<TCreateAcmeAccountResponse>;
createAcmeOrder: (profileId: string, body: unknown) => Promise<TCreateAcmeOrderResponse>; createAcmeOrder: (profileId: string, body: TCreateAcmeOrderPayload) => Promise<TCreateAcmeOrderResponse>;
deactivateAcmeAccount: (profileId: string, accountId: string) => Promise<TDeactivateAcmeAccountResponse>; deactivateAcmeAccount: (
profileId: string,
accountId: string,
body?: TDeactivateAcmeAccountPayload
) => Promise<TDeactivateAcmeAccountResponse>;
listAcmeOrders: (profileId: string, accountId: string) => Promise<TListAcmeOrdersResponse>; listAcmeOrders: (profileId: string, accountId: string) => Promise<TListAcmeOrdersResponse>;
getAcmeOrder: (profileId: string, orderId: string) => Promise<TGetAcmeOrderResponse>; getAcmeOrder: (profileId: string, orderId: string) => Promise<TGetAcmeOrderResponse>;
finalizeAcmeOrder: (profileId: string, orderId: string, csr: string) => Promise<TFinalizeAcmeOrderResponse>; finalizeAcmeOrder: (
profileId: string,
orderId: string,
body: TFinalizeAcmeOrderPayload
) => Promise<TFinalizeAcmeOrderResponse>;
downloadAcmeCertificate: (profileId: string, orderId: string) => Promise<string>; downloadAcmeCertificate: (profileId: string, orderId: string) => Promise<string>;
getAcmeAuthorization: (profileId: string, authzId: string) => Promise<TGetAcmeAuthorizationResponse>; getAcmeAuthorization: (profileId: string, authzId: string) => Promise<TGetAcmeAuthorizationResponse>;
respondToAcmeChallenge: (profileId: string, authzId: string) => Promise<TRespondToAcmeChallengeResponse>; respondToAcmeChallenge: (profileId: string, authzId: string) => Promise<TRespondToAcmeChallengeResponse>;