mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-07 16:27:40 +00:00
Refine routers
This commit is contained in:
@@ -1,32 +1,38 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-floating-promises */
|
/* eslint-disable @typescript-eslint/no-floating-promises */
|
||||||
|
import type { TAcmeResponse } from "@app/ee/services/pki-acme/pki-acme-types";
|
||||||
|
import { FastifyReply } from "fastify";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
CreateAcmeAccountResponseSchema,
|
CreateAcmeAccountResponseSchema,
|
||||||
CreateAcmeOrderBodySchema,
|
CreateAcmeOrderBodySchema,
|
||||||
CreateAcmeOrderResponseSchema,
|
CreateAcmeOrderResponseSchema,
|
||||||
|
DeactivateAcmeAccountBodySchema,
|
||||||
DeactivateAcmeAccountResponseSchema,
|
DeactivateAcmeAccountResponseSchema,
|
||||||
DeactivateAcmeAccountSchema,
|
FinalizeAcmeOrderBodySchema,
|
||||||
DownloadAcmeCertificateSchema,
|
|
||||||
FinalizeAcmeOrderResponseSchema,
|
|
||||||
FinalizeAcmeOrderSchema,
|
|
||||||
GetAcmeAuthorizationResponseSchema,
|
GetAcmeAuthorizationResponseSchema,
|
||||||
GetAcmeAuthorizationSchema,
|
|
||||||
GetAcmeDirectoryResponseSchema,
|
GetAcmeDirectoryResponseSchema,
|
||||||
GetAcmeDirectorySchema,
|
|
||||||
GetAcmeNewNonceSchema,
|
|
||||||
GetAcmeOrderResponseSchema,
|
GetAcmeOrderResponseSchema,
|
||||||
GetAcmeOrderSchema,
|
|
||||||
ListAcmeOrdersResponseSchema,
|
ListAcmeOrdersResponseSchema,
|
||||||
ListAcmeOrdersSchema,
|
|
||||||
RawJwsPayloadSchema,
|
RawJwsPayloadSchema,
|
||||||
RespondToAcmeChallengeResponseSchema,
|
RespondToAcmeChallengeResponseSchema
|
||||||
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 { ApiDocsTags } from "@app/lib/api-docs";
|
||||||
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||||
|
|
||||||
export const registerPkiAcmeRouter = async (server: FastifyZodProvider) => {
|
export const registerPkiAcmeRouter = async (server: FastifyZodProvider) => {
|
||||||
|
const sendAcmeResponse = async <T>(res: FastifyReply, profileId: string, response: TAcmeResponse<T>): Promise<T> => {
|
||||||
|
res.code(response.status);
|
||||||
|
for (const [key, value] of Object.entries(response.headers)) {
|
||||||
|
res.header(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
const nonce = await server.services.pkiAcme.getAcmeNewNonce(profileId);
|
||||||
|
res.header("Replay-Nonce", nonce);
|
||||||
|
res.header("Cache-Control", "no-store");
|
||||||
|
return response.body;
|
||||||
|
};
|
||||||
|
|
||||||
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;
|
||||||
@@ -53,7 +59,9 @@ export const registerPkiAcmeRouter = async (server: FastifyZodProvider) => {
|
|||||||
hide: false,
|
hide: false,
|
||||||
tags: [ApiDocsTags.PkiAcme],
|
tags: [ApiDocsTags.PkiAcme],
|
||||||
description: "ACME Directory - provides URLs for the client to make API calls to",
|
description: "ACME Directory - provides URLs for the client to make API calls to",
|
||||||
...GetAcmeDirectorySchema.shape,
|
params: z.object({
|
||||||
|
profileId: z.string().uuid()
|
||||||
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: GetAcmeDirectoryResponseSchema
|
200: GetAcmeDirectoryResponseSchema
|
||||||
}
|
}
|
||||||
@@ -77,15 +85,17 @@ export const registerPkiAcmeRouter = async (server: FastifyZodProvider) => {
|
|||||||
hide: false,
|
hide: false,
|
||||||
tags: [ApiDocsTags.PkiAcme],
|
tags: [ApiDocsTags.PkiAcme],
|
||||||
description: "ACME New Nonce - generate a new nonce and return in Replay-Nonce header",
|
description: "ACME New Nonce - generate a new nonce and return in Replay-Nonce header",
|
||||||
...GetAcmeNewNonceSchema.shape,
|
params: z.object({
|
||||||
|
profileId: z.string().uuid()
|
||||||
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({})
|
200: z.string().length(0)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
handler: async (req, res) => {
|
handler: async (req, res) => {
|
||||||
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);
|
||||||
return {};
|
return "";
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -112,23 +122,16 @@ export const registerPkiAcmeRouter = async (server: FastifyZodProvider) => {
|
|||||||
handler: async (req, res) => {
|
handler: async (req, res) => {
|
||||||
const { payload, protectedHeader } = await server.services.pkiAcme.validateNewAccountJwsPayload(req.body);
|
const { payload, protectedHeader } = await server.services.pkiAcme.validateNewAccountJwsPayload(req.body);
|
||||||
const { alg, jwk } = protectedHeader;
|
const { alg, jwk } = protectedHeader;
|
||||||
const { status, body, headers } = await server.services.pkiAcme.createAcmeAccount({
|
return sendAcmeResponse(
|
||||||
profileId: req.params.profileId,
|
res,
|
||||||
alg,
|
req.params.profileId,
|
||||||
jwk: jwk!,
|
await server.services.pkiAcme.createAcmeAccount({
|
||||||
payload
|
profileId: req.params.profileId,
|
||||||
});
|
alg,
|
||||||
// TODO: DRY
|
jwk: jwk!,
|
||||||
res.code(status);
|
payload
|
||||||
for (const [key, value] of Object.entries(headers)) {
|
})
|
||||||
res.header(key, value);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: DRY
|
|
||||||
const nonce = await server.services.pkiAcme.getAcmeNewNonce(req.params.profileId);
|
|
||||||
res.header("Replay-Nonce", nonce);
|
|
||||||
res.header("Cache-Control", "no-store");
|
|
||||||
return body;
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -155,14 +158,20 @@ export const registerPkiAcmeRouter = async (server: FastifyZodProvider) => {
|
|||||||
// TODO: replace with verify ACME signature here instead
|
// TODO: replace with verify ACME signature here instead
|
||||||
// onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
// onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
handler: async (req, res) => {
|
handler: async (req, res) => {
|
||||||
const { payload, protectedHeader, accountId } = await server.services.pkiAcme.validateExistingAccountJwsPayload(
|
const { payload, accountId } = await server.services.pkiAcme.validateExistingAccountJwsPayload(
|
||||||
req.params.profileId,
|
req.params.profileId,
|
||||||
req.body,
|
req.body,
|
||||||
CreateAcmeOrderBodySchema
|
CreateAcmeOrderBodySchema
|
||||||
);
|
);
|
||||||
const order = await server.services.pkiAcme.createAcmeOrder(req.params.profileId, req.body);
|
return sendAcmeResponse(
|
||||||
res.code(201);
|
res,
|
||||||
return order;
|
req.params.profileId,
|
||||||
|
await server.services.pkiAcme.createAcmeOrder({
|
||||||
|
profileId: req.params.profileId,
|
||||||
|
accountId,
|
||||||
|
payload
|
||||||
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -178,7 +187,11 @@ export const registerPkiAcmeRouter = async (server: FastifyZodProvider) => {
|
|||||||
hide: false,
|
hide: false,
|
||||||
tags: [ApiDocsTags.PkiAcme],
|
tags: [ApiDocsTags.PkiAcme],
|
||||||
description: "ACME Account Deactivation",
|
description: "ACME Account Deactivation",
|
||||||
...DeactivateAcmeAccountSchema.shape,
|
params: z.object({
|
||||||
|
profileId: z.string().uuid(),
|
||||||
|
accountId: z.string()
|
||||||
|
}),
|
||||||
|
body: DeactivateAcmeAccountBodySchema,
|
||||||
response: {
|
response: {
|
||||||
200: DeactivateAcmeAccountResponseSchema
|
200: DeactivateAcmeAccountResponseSchema
|
||||||
}
|
}
|
||||||
@@ -203,7 +216,10 @@ export const registerPkiAcmeRouter = async (server: FastifyZodProvider) => {
|
|||||||
hide: false,
|
hide: false,
|
||||||
tags: [ApiDocsTags.PkiAcme],
|
tags: [ApiDocsTags.PkiAcme],
|
||||||
description: "ACME List Orders - get existing orders from current account",
|
description: "ACME List Orders - get existing orders from current account",
|
||||||
...ListAcmeOrdersSchema.shape,
|
params: z.object({
|
||||||
|
profileId: z.string().uuid(),
|
||||||
|
accountId: z.string()
|
||||||
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: ListAcmeOrdersResponseSchema
|
200: ListAcmeOrdersResponseSchema
|
||||||
}
|
}
|
||||||
@@ -228,7 +244,10 @@ export const registerPkiAcmeRouter = async (server: FastifyZodProvider) => {
|
|||||||
hide: false,
|
hide: false,
|
||||||
tags: [ApiDocsTags.PkiAcme],
|
tags: [ApiDocsTags.PkiAcme],
|
||||||
description: "ACME Get Order - return status and details of the order",
|
description: "ACME Get Order - return status and details of the order",
|
||||||
...GetAcmeOrderSchema.shape,
|
params: z.object({
|
||||||
|
profileId: z.string().uuid(),
|
||||||
|
orderId: z.string().uuid()
|
||||||
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: GetAcmeOrderResponseSchema
|
200: GetAcmeOrderResponseSchema
|
||||||
}
|
}
|
||||||
@@ -253,10 +272,11 @@ export const registerPkiAcmeRouter = async (server: FastifyZodProvider) => {
|
|||||||
hide: false,
|
hide: false,
|
||||||
tags: [ApiDocsTags.PkiAcme],
|
tags: [ApiDocsTags.PkiAcme],
|
||||||
description: "ACME Finalize Order - finalize cert order by providing CSR",
|
description: "ACME Finalize Order - finalize cert order by providing CSR",
|
||||||
...FinalizeAcmeOrderSchema.shape,
|
params: z.object({
|
||||||
response: {
|
profileId: z.string().uuid(),
|
||||||
200: FinalizeAcmeOrderResponseSchema
|
orderId: z.string().uuid()
|
||||||
}
|
}),
|
||||||
|
body: FinalizeAcmeOrderBodySchema
|
||||||
},
|
},
|
||||||
// TODO: replace with verify ACME signature here instead
|
// TODO: replace with verify ACME signature here instead
|
||||||
// onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
// onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
@@ -278,7 +298,10 @@ export const registerPkiAcmeRouter = async (server: FastifyZodProvider) => {
|
|||||||
hide: false,
|
hide: false,
|
||||||
tags: [ApiDocsTags.PkiAcme],
|
tags: [ApiDocsTags.PkiAcme],
|
||||||
description: "ACME Download Certificate - download certificate when ready",
|
description: "ACME Download Certificate - download certificate when ready",
|
||||||
...DownloadAcmeCertificateSchema.shape,
|
params: z.object({
|
||||||
|
profileId: z.string().uuid(),
|
||||||
|
orderId: z.string().uuid()
|
||||||
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.string()
|
200: z.string()
|
||||||
}
|
}
|
||||||
@@ -307,7 +330,10 @@ export const registerPkiAcmeRouter = async (server: FastifyZodProvider) => {
|
|||||||
hide: false,
|
hide: false,
|
||||||
tags: [ApiDocsTags.PkiAcme],
|
tags: [ApiDocsTags.PkiAcme],
|
||||||
description: "ACME Identifier Authorization - get authorization info (challenges)",
|
description: "ACME Identifier Authorization - get authorization info (challenges)",
|
||||||
...GetAcmeAuthorizationSchema.shape,
|
params: z.object({
|
||||||
|
profileId: z.string().uuid(),
|
||||||
|
authzId: z.string().uuid()
|
||||||
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: GetAcmeAuthorizationResponseSchema
|
200: GetAcmeAuthorizationResponseSchema
|
||||||
}
|
}
|
||||||
@@ -332,7 +358,10 @@ export const registerPkiAcmeRouter = async (server: FastifyZodProvider) => {
|
|||||||
hide: false,
|
hide: false,
|
||||||
tags: [ApiDocsTags.PkiAcme],
|
tags: [ApiDocsTags.PkiAcme],
|
||||||
description: "ACME Respond to Challenge - let ACME server know challenge is ready",
|
description: "ACME Respond to Challenge - let ACME server know challenge is ready",
|
||||||
...RespondToAcmeChallengeSchema.shape,
|
params: z.object({
|
||||||
|
profileId: z.string().uuid(),
|
||||||
|
authzId: z.string().uuid()
|
||||||
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: RespondToAcmeChallengeResponseSchema
|
200: RespondToAcmeChallengeResponseSchema
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,26 @@
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
|
export enum AcmeIdentifierType {
|
||||||
|
DNS = "dns"
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum AcmeOrderStatus {
|
||||||
|
Pending = "pending",
|
||||||
|
Processing = "processing",
|
||||||
|
Ready = "ready",
|
||||||
|
Valid = "valid",
|
||||||
|
Invalid = "invalid"
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum AcmeAuthStatus {
|
||||||
|
Pending = "pending",
|
||||||
|
Valid = "valid",
|
||||||
|
Invalid = "invalid",
|
||||||
|
Deactivated = "deactivated",
|
||||||
|
Expired = "expired",
|
||||||
|
Revoked = "revoked"
|
||||||
|
}
|
||||||
|
|
||||||
export const ProtectedHeaderSchema = z
|
export const ProtectedHeaderSchema = z
|
||||||
.object({
|
.object({
|
||||||
alg: z.string(),
|
alg: z.string(),
|
||||||
@@ -20,13 +41,6 @@ export const RawJwsPayloadSchema = z.object({
|
|||||||
signature: z.string()
|
signature: z.string()
|
||||||
});
|
});
|
||||||
|
|
||||||
// Directory endpoint
|
|
||||||
export const GetAcmeDirectorySchema = z.object({
|
|
||||||
params: z.object({
|
|
||||||
profileId: z.string().uuid()
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
export const GetAcmeDirectoryResponseSchema = z.object({
|
export const GetAcmeDirectoryResponseSchema = z.object({
|
||||||
newNonce: z.string(),
|
newNonce: z.string(),
|
||||||
newAccount: z.string(),
|
newAccount: z.string(),
|
||||||
@@ -34,13 +48,6 @@ export const GetAcmeDirectoryResponseSchema = z.object({
|
|||||||
revokeCert: z.string().optional()
|
revokeCert: z.string().optional()
|
||||||
});
|
});
|
||||||
|
|
||||||
// New Nonce endpoint
|
|
||||||
export const GetAcmeNewNonceSchema = z.object({
|
|
||||||
params: z.object({
|
|
||||||
profileId: z.string().uuid()
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
// New Account payload schema
|
// New Account payload schema
|
||||||
export const CreateAcmeAccountBodySchema = z.object({
|
export const CreateAcmeAccountBodySchema = z.object({
|
||||||
contact: z.array(z.string()).optional(),
|
contact: z.array(z.string()).optional(),
|
||||||
@@ -73,22 +80,16 @@ export const CreateAcmeAccountResponseSchema = z.object({
|
|||||||
export const CreateAcmeOrderBodySchema = z.object({
|
export const CreateAcmeOrderBodySchema = z.object({
|
||||||
identifiers: z.array(
|
identifiers: z.array(
|
||||||
z.object({
|
z.object({
|
||||||
type: z.string(),
|
type: z
|
||||||
value: z.string()
|
.string()
|
||||||
|
.regex(/^(?!-)[A-Za-z0-9-]{1,63}(?<!-)(\.(?!-)[A-Za-z0-9-]{1,63}(?<!-))*$/, "Invalid DNS identifier"),
|
||||||
|
value: z.enum(Object.values(AcmeIdentifierType) as [string, ...string[]])
|
||||||
})
|
})
|
||||||
),
|
),
|
||||||
notBefore: z.string().optional(),
|
notBefore: z.string().optional(),
|
||||||
notAfter: z.string().optional()
|
notAfter: z.string().optional()
|
||||||
});
|
});
|
||||||
|
|
||||||
// New Order endpoint
|
|
||||||
export const CreateAcmeOrderSchema = z.object({
|
|
||||||
params: z.object({
|
|
||||||
profileId: z.string().uuid()
|
|
||||||
}),
|
|
||||||
body: CreateAcmeOrderBodySchema
|
|
||||||
});
|
|
||||||
|
|
||||||
export const CreateAcmeOrderResponseSchema = z.object({
|
export const CreateAcmeOrderResponseSchema = z.object({
|
||||||
status: z.string(),
|
status: z.string(),
|
||||||
expires: z.string(),
|
expires: z.string(),
|
||||||
@@ -108,41 +109,17 @@ export const DeactivateAcmeAccountBodySchema = z.object({
|
|||||||
status: z.literal("deactivated")
|
status: z.literal("deactivated")
|
||||||
});
|
});
|
||||||
|
|
||||||
// Account Deactivation endpoint
|
|
||||||
export const DeactivateAcmeAccountSchema = z.object({
|
|
||||||
params: z.object({
|
|
||||||
profileId: z.string().uuid(),
|
|
||||||
accountId: z.string()
|
|
||||||
}),
|
|
||||||
body: DeactivateAcmeAccountBodySchema
|
|
||||||
});
|
|
||||||
|
|
||||||
export const DeactivateAcmeAccountResponseSchema = z.object({
|
export const DeactivateAcmeAccountResponseSchema = z.object({
|
||||||
status: z.string()
|
status: z.string()
|
||||||
});
|
});
|
||||||
|
|
||||||
// List Orders endpoint
|
// List Orders endpoint
|
||||||
export const ListAcmeOrdersSchema = z.object({
|
|
||||||
params: z.object({
|
|
||||||
profileId: z.string().uuid(),
|
|
||||||
accountId: z.string()
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
export const ListAcmeOrdersResponseSchema = z.object({
|
export const ListAcmeOrdersResponseSchema = z.object({
|
||||||
orders: z.array(z.string())
|
orders: z.array(z.string())
|
||||||
});
|
});
|
||||||
|
|
||||||
// Get Order endpoint
|
|
||||||
export const GetAcmeOrderSchema = z.object({
|
|
||||||
params: z.object({
|
|
||||||
profileId: z.string().uuid(),
|
|
||||||
orderId: z.string()
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
export const GetAcmeOrderResponseSchema = z.object({
|
export const GetAcmeOrderResponseSchema = z.object({
|
||||||
status: z.string(),
|
status: z.enum(Object.values(AcmeOrderStatus) as [string, ...string[]]),
|
||||||
expires: z.string().optional(),
|
expires: z.string().optional(),
|
||||||
identifiers: z.array(
|
identifiers: z.array(
|
||||||
z.object({
|
z.object({
|
||||||
@@ -160,17 +137,8 @@ export const FinalizeAcmeOrderBodySchema = z.object({
|
|||||||
csr: z.string()
|
csr: z.string()
|
||||||
});
|
});
|
||||||
|
|
||||||
// Finalize Order endpoint
|
|
||||||
export const FinalizeAcmeOrderSchema = z.object({
|
|
||||||
params: z.object({
|
|
||||||
profileId: z.string().uuid(),
|
|
||||||
orderId: z.string()
|
|
||||||
}),
|
|
||||||
body: FinalizeAcmeOrderBodySchema
|
|
||||||
});
|
|
||||||
|
|
||||||
export const FinalizeAcmeOrderResponseSchema = z.object({
|
export const FinalizeAcmeOrderResponseSchema = z.object({
|
||||||
status: z.string(),
|
status: z.enum(Object.values(AcmeOrderStatus) as [string, ...string[]]),
|
||||||
expires: z.string().optional(),
|
expires: z.string().optional(),
|
||||||
identifiers: z.array(
|
identifiers: z.array(
|
||||||
z.object({
|
z.object({
|
||||||
@@ -183,24 +151,8 @@ export const FinalizeAcmeOrderResponseSchema = z.object({
|
|||||||
certificate: z.string().optional()
|
certificate: z.string().optional()
|
||||||
});
|
});
|
||||||
|
|
||||||
// Download Certificate endpoint
|
|
||||||
export const DownloadAcmeCertificateSchema = z.object({
|
|
||||||
params: z.object({
|
|
||||||
profileId: z.string().uuid(),
|
|
||||||
orderId: z.string()
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
// Get Authorization endpoint
|
|
||||||
export const GetAcmeAuthorizationSchema = z.object({
|
|
||||||
params: z.object({
|
|
||||||
profileId: z.string().uuid(),
|
|
||||||
authzId: z.string()
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
export const GetAcmeAuthorizationResponseSchema = z.object({
|
export const GetAcmeAuthorizationResponseSchema = z.object({
|
||||||
status: z.string(),
|
status: z.enum(Object.values(AcmeAuthStatus) as [string, ...string[]]),
|
||||||
expires: z.string().optional(),
|
expires: z.string().optional(),
|
||||||
identifier: z.object({
|
identifier: z.object({
|
||||||
type: z.string(),
|
type: z.string(),
|
||||||
@@ -217,14 +169,6 @@ export const GetAcmeAuthorizationResponseSchema = z.object({
|
|||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
// Respond to Challenge endpoint
|
|
||||||
export const RespondToAcmeChallengeSchema = z.object({
|
|
||||||
params: z.object({
|
|
||||||
profileId: z.string().uuid(),
|
|
||||||
authzId: z.string()
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
export const RespondToAcmeChallengeResponseSchema = z.object({
|
export const RespondToAcmeChallengeResponseSchema = z.object({
|
||||||
type: z.string(),
|
type: z.string(),
|
||||||
url: z.string(),
|
url: z.string(),
|
||||||
|
|||||||
@@ -19,8 +19,15 @@ import {
|
|||||||
import { errors, flattenedVerify, FlattenedVerifyResult, importJWK, JWSHeaderParameters } from "jose";
|
import { errors, flattenedVerify, FlattenedVerifyResult, importJWK, JWSHeaderParameters } from "jose";
|
||||||
import { z, ZodError } from "zod";
|
import { z, ZodError } from "zod";
|
||||||
import { TPkiAcmeAccountDALFactory } from "./pki-acme-account-dal";
|
import { TPkiAcmeAccountDALFactory } from "./pki-acme-account-dal";
|
||||||
|
import { TPkiAcmeAuthDALFactory } from "./pki-acme-auth-dal";
|
||||||
import { TPkiAcmeOrderDALFactory } from "./pki-acme-order-dal";
|
import { TPkiAcmeOrderDALFactory } from "./pki-acme-order-dal";
|
||||||
import { CreateAcmeAccountBodySchema, ProtectedHeaderSchema } from "./pki-acme-schemas";
|
import {
|
||||||
|
AcmeAuthStatus,
|
||||||
|
AcmeIdentifierType,
|
||||||
|
AcmeOrderStatus,
|
||||||
|
CreateAcmeAccountBodySchema,
|
||||||
|
ProtectedHeaderSchema
|
||||||
|
} from "./pki-acme-schemas";
|
||||||
import {
|
import {
|
||||||
TAcmeResponse,
|
TAcmeResponse,
|
||||||
TAuthenciatedJwsPayload,
|
TAuthenciatedJwsPayload,
|
||||||
@@ -46,12 +53,14 @@ type TPkiAcmeServiceFactoryDep = {
|
|||||||
certificateProfileDAL: Pick<TCertificateProfileDALFactory, "findById">;
|
certificateProfileDAL: Pick<TCertificateProfileDALFactory, "findById">;
|
||||||
acmeAccountDAL: Pick<TPkiAcmeAccountDALFactory, "findById" | "findByPublicKey" | "create">;
|
acmeAccountDAL: Pick<TPkiAcmeAccountDALFactory, "findById" | "findByPublicKey" | "create">;
|
||||||
acmeOrderDAL: Pick<TPkiAcmeOrderDALFactory, "create">;
|
acmeOrderDAL: Pick<TPkiAcmeOrderDALFactory, "create">;
|
||||||
|
acmeAuthDAL: Pick<TPkiAcmeAuthDALFactory, "create">;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const pkiAcmeServiceFactory = ({
|
export const pkiAcmeServiceFactory = ({
|
||||||
certificateProfileDAL,
|
certificateProfileDAL,
|
||||||
acmeAccountDAL,
|
acmeAccountDAL,
|
||||||
acmeOrderDAL
|
acmeOrderDAL,
|
||||||
|
acmeAuthDAL
|
||||||
}: TPkiAcmeServiceFactoryDep): TPkiAcmeServiceFactory => {
|
}: TPkiAcmeServiceFactoryDep): TPkiAcmeServiceFactory => {
|
||||||
const validateAcmeProfile = async (profileId: string): Promise<TCertificateProfileWithConfigs> => {
|
const validateAcmeProfile = async (profileId: string): Promise<TCertificateProfileWithConfigs> => {
|
||||||
const profile = await certificateProfileDAL.findById(profileId);
|
const profile = await certificateProfileDAL.findById(profileId);
|
||||||
@@ -168,11 +177,11 @@ export const pkiAcmeServiceFactory = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const getAcmeDirectory = async (profileId: string): Promise<TGetAcmeDirectoryResponse> => {
|
const getAcmeDirectory = async (profileId: string): Promise<TGetAcmeDirectoryResponse> => {
|
||||||
await validateAcmeProfile(profileId);
|
const profile = await validateAcmeProfile(profileId);
|
||||||
return {
|
return {
|
||||||
newNonce: buildUrl(`/api/v1/pki/acme/profiles/${profileId}/new-nonce`),
|
newNonce: buildUrl(`/api/v1/pki/acme/profiles/${profile.id}/new-nonce`),
|
||||||
newAccount: buildUrl(`/api/v1/pki/acme/profiles/${profileId}/new-account`),
|
newAccount: buildUrl(`/api/v1/pki/acme/profiles/${profile.id}/new-account`),
|
||||||
newOrder: buildUrl(`/api/v1/pki/acme/profiles/${profileId}/new-order`)
|
newOrder: buildUrl(`/api/v1/pki/acme/profiles/${profile.id}/new-order`)
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -244,15 +253,29 @@ export const pkiAcmeServiceFactory = ({
|
|||||||
payload: TCreateAcmeOrderPayload;
|
payload: TCreateAcmeOrderPayload;
|
||||||
}): Promise<TAcmeResponse<TCreateAcmeOrderResponse>> => {
|
}): Promise<TAcmeResponse<TCreateAcmeOrderResponse>> => {
|
||||||
const account = await acmeAccountDAL.findById(profileId, accountId)!;
|
const account = await acmeAccountDAL.findById(profileId, accountId)!;
|
||||||
|
|
||||||
// TODO: check and see if we have existing orders for this account that meet the criteria
|
// TODO: check and see if we have existing orders for this account that meet the criteria
|
||||||
// if we do, return the existing order
|
// if we do, return the existing order
|
||||||
|
|
||||||
orders = await acmeOrderDAL.create({
|
const order = await acmeOrderDAL.create({
|
||||||
profileId,
|
accountId: account.id,
|
||||||
accountId,
|
status: AcmeOrderStatus.Pending
|
||||||
status: "pending"
|
|
||||||
});
|
});
|
||||||
|
payload.identifiers.forEach(async (identifier) => {
|
||||||
|
if (identifier.type === AcmeIdentifierType.DNS) {
|
||||||
|
// TODO: reuse existing authorizations for this identifier if they exist
|
||||||
|
const auth = await acmeAuthDAL.create({
|
||||||
|
accountId: account.id,
|
||||||
|
status: AcmeAuthStatus.Pending,
|
||||||
|
identifierType: identifier.type,
|
||||||
|
identifierValue: identifier.value,
|
||||||
|
// TODO: read config from the profile to get the expiration time instead
|
||||||
|
expiresAt: new Date(Date.now() + 24 * 60 * 60 * 1000)
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
throw new AcmeMalformedError({ detail: "Only DNS identifiers are supported" });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// FIXME: Implement ACME new order creation
|
// FIXME: Implement ACME new order creation
|
||||||
const orderId = "FIXME-order-id";
|
const orderId = "FIXME-order-id";
|
||||||
return {
|
return {
|
||||||
@@ -262,10 +285,10 @@ export const pkiAcmeServiceFactory = ({
|
|||||||
expires: new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString(),
|
expires: new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString(),
|
||||||
identifiers: [],
|
identifiers: [],
|
||||||
authorizations: [],
|
authorizations: [],
|
||||||
finalize: buildUrl(`/api/v1/pki/acme/profiles/${profile.id}/orders/${orderId}/finalize`)
|
finalize: buildUrl(`/api/v1/pki/acme/profiles/${account.profileId}/orders/${orderId}/finalize`)
|
||||||
},
|
},
|
||||||
headers: {
|
headers: {
|
||||||
Location: buildUrl(`/api/v1/pki/acme/profiles/${profile.id}/orders/${orderId}`)
|
Location: buildUrl(`/api/v1/pki/acme/profiles/${account.profileId}/orders/${orderId}`)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -74,6 +74,7 @@ import { pamSessionServiceFactory } from "@app/ee/services/pam-session/pam-sessi
|
|||||||
import { permissionDALFactory } from "@app/ee/services/permission/permission-dal";
|
import { permissionDALFactory } from "@app/ee/services/permission/permission-dal";
|
||||||
import { permissionServiceFactory } from "@app/ee/services/permission/permission-service";
|
import { permissionServiceFactory } from "@app/ee/services/permission/permission-service";
|
||||||
import { pitServiceFactory } from "@app/ee/services/pit/pit-service";
|
import { pitServiceFactory } from "@app/ee/services/pit/pit-service";
|
||||||
|
import { pkiAcmeAuthDALFactory } from "@app/ee/services/pki-acme/pki-acme-auth-dal";
|
||||||
import { pkiAcmeServiceFactory } from "@app/ee/services/pki-acme/pki-acme-service";
|
import { pkiAcmeServiceFactory } from "@app/ee/services/pki-acme/pki-acme-service";
|
||||||
import { projectTemplateDALFactory } from "@app/ee/services/project-template/project-template-dal";
|
import { projectTemplateDALFactory } from "@app/ee/services/project-template/project-template-dal";
|
||||||
import { projectTemplateServiceFactory } from "@app/ee/services/project-template/project-template-service";
|
import { projectTemplateServiceFactory } from "@app/ee/services/project-template/project-template-service";
|
||||||
@@ -350,6 +351,7 @@ import { workflowIntegrationDALFactory } from "@app/services/workflow-integratio
|
|||||||
import { workflowIntegrationServiceFactory } from "@app/services/workflow-integration/workflow-integration-service";
|
import { workflowIntegrationServiceFactory } from "@app/services/workflow-integration/workflow-integration-service";
|
||||||
|
|
||||||
import { pkiAcmeAccountDALFactory } from "@app/ee/services/pki-acme/pki-acme-account-dal";
|
import { pkiAcmeAccountDALFactory } from "@app/ee/services/pki-acme/pki-acme-account-dal";
|
||||||
|
import { pkiAcmeOrderDALFactory } from "@app/ee/services/pki-acme/pki-acme-order-dal";
|
||||||
import { injectAuditLogInfo } from "../plugins/audit-log";
|
import { injectAuditLogInfo } from "../plugins/audit-log";
|
||||||
import { injectAssumePrivilege } from "../plugins/auth/inject-assume-privilege";
|
import { injectAssumePrivilege } from "../plugins/auth/inject-assume-privilege";
|
||||||
import { injectIdentity } from "../plugins/auth/inject-identity";
|
import { injectIdentity } from "../plugins/auth/inject-identity";
|
||||||
@@ -361,7 +363,6 @@ import { initializeOauthConfigSync } from "./v1/sso-router";
|
|||||||
import { registerV2Routes } from "./v2";
|
import { registerV2Routes } from "./v2";
|
||||||
import { registerV3Routes } from "./v3";
|
import { registerV3Routes } from "./v3";
|
||||||
import { registerV4Routes } from "./v4";
|
import { registerV4Routes } from "./v4";
|
||||||
import { pkiAcmeOrderDALFactory } from "@app/ee/services/pki-acme/pki-acme-order-dal";
|
|
||||||
|
|
||||||
const histogram = monitorEventLoopDelay({ resolution: 20 });
|
const histogram = monitorEventLoopDelay({ resolution: 20 });
|
||||||
histogram.enable();
|
histogram.enable();
|
||||||
@@ -1068,7 +1069,7 @@ export const registerRoutes = async (
|
|||||||
const acmeEnrollmentConfigDAL = acmeEnrollmentConfigDALFactory(db);
|
const acmeEnrollmentConfigDAL = acmeEnrollmentConfigDALFactory(db);
|
||||||
const acmeAccountDAL = pkiAcmeAccountDALFactory(db);
|
const acmeAccountDAL = pkiAcmeAccountDALFactory(db);
|
||||||
const acmeOrderDAL = pkiAcmeOrderDALFactory(db);
|
const acmeOrderDAL = pkiAcmeOrderDALFactory(db);
|
||||||
|
const acmeAuthDAL = pkiAcmeAuthDALFactory(db);
|
||||||
const certificateDAL = certificateDALFactory(db);
|
const certificateDAL = certificateDALFactory(db);
|
||||||
const certificateBodyDAL = certificateBodyDALFactory(db);
|
const certificateBodyDAL = certificateBodyDALFactory(db);
|
||||||
const certificateSecretDAL = certificateSecretDALFactory(db);
|
const certificateSecretDAL = certificateSecretDALFactory(db);
|
||||||
@@ -1172,7 +1173,8 @@ export const registerRoutes = async (
|
|||||||
const pkiAcmeService = pkiAcmeServiceFactory({
|
const pkiAcmeService = pkiAcmeServiceFactory({
|
||||||
certificateProfileDAL,
|
certificateProfileDAL,
|
||||||
acmeAccountDAL,
|
acmeAccountDAL,
|
||||||
acmeOrderDAL
|
acmeOrderDAL,
|
||||||
|
acmeAuthDAL
|
||||||
});
|
});
|
||||||
|
|
||||||
const pkiAlertService = pkiAlertServiceFactory({
|
const pkiAlertService = pkiAlertServiceFactory({
|
||||||
|
|||||||
Reference in New Issue
Block a user