mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-11 11:29:21 +00:00
Add PKI Syncs docs and a few improvements on the router
This commit is contained in:
@@ -50,6 +50,7 @@ export enum ApiDocsTags {
|
||||
IdentitySpecificPrivilegesV2 = "Identity Specific Privileges V2",
|
||||
AppConnections = "App Connections",
|
||||
SecretSyncs = "Secret Syncs",
|
||||
PkiSyncs = "PKI Syncs",
|
||||
Integrations = "Integrations",
|
||||
ServiceTokens = "Service Tokens",
|
||||
AuditLogs = "Audit Logs",
|
||||
|
||||
@@ -48,7 +48,7 @@ import { registerPasswordRouter } from "./password-router";
|
||||
import { registerPkiAlertRouter } from "./pki-alert-router";
|
||||
import { registerPkiCollectionRouter } from "./pki-collection-router";
|
||||
import { registerPkiSubscriberRouter } from "./pki-subscriber-router";
|
||||
import { registerPkiSyncRouter } from "./pki-sync-router";
|
||||
import { PKI_SYNC_REGISTER_ROUTER_MAP, registerPkiSyncRouter } from "./pki-sync-routers";
|
||||
import { registerProjectEnvRouter } from "./project-env-router";
|
||||
import { registerProjectKeyRouter } from "./project-key-router";
|
||||
import { registerProjectMembershipRouter } from "./project-membership-router";
|
||||
@@ -157,7 +157,16 @@ export const registerV1Routes = async (server: FastifyZodProvider) => {
|
||||
await server.register(registerIntegrationAuthRouter, { prefix: "/integration-auth" });
|
||||
await server.register(registerWebhookRouter, { prefix: "/webhooks" });
|
||||
await server.register(registerIdentityRouter, { prefix: "/identities" });
|
||||
await server.register(registerPkiSyncRouter, { prefix: "/pki-syncs" });
|
||||
await server.register(
|
||||
async (pkiSyncRouter) => {
|
||||
// register generic pki sync endpoints
|
||||
await pkiSyncRouter.register(registerPkiSyncRouter);
|
||||
for await (const [destination, router] of Object.entries(PKI_SYNC_REGISTER_ROUTER_MAP)) {
|
||||
await pkiSyncRouter.register(router, { prefix: `/${destination}` });
|
||||
}
|
||||
},
|
||||
{ prefix: "/pki-syncs" }
|
||||
);
|
||||
|
||||
await server.register(
|
||||
async (secretSharingRouter) => {
|
||||
|
||||
@@ -1,540 +0,0 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
||||
import { logger } from "@app/lib/logger";
|
||||
import { readLimit } from "@app/server/config/rateLimiter";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
import { AzureKeyVaultPkiSyncConfigSchema } from "@app/services/pki-sync/azure-key-vault/azure-key-vault-pki-sync-types";
|
||||
import { PkiSync } from "@app/services/pki-sync/pki-sync-enums";
|
||||
import { PkiSyncDetailsSchema, PkiSyncListItemSchema, PkiSyncSchema } from "@app/services/pki-sync/pki-sync-schemas";
|
||||
import { TCreatePkiSyncDTO, TUpdatePkiSyncDTO } from "@app/services/pki-sync/pki-sync-types";
|
||||
|
||||
const CreatePkiSyncRequestBodySchema = z.object({
|
||||
name: z.string().trim().min(1).max(64),
|
||||
description: z.string().optional(),
|
||||
destination: z.nativeEnum(PkiSync),
|
||||
isAutoSyncEnabled: z.boolean().default(true),
|
||||
destinationConfig: z
|
||||
.discriminatedUnion("destination", [
|
||||
z.object({
|
||||
destination: z.literal(PkiSync.AzureKeyVault),
|
||||
config: AzureKeyVaultPkiSyncConfigSchema
|
||||
})
|
||||
])
|
||||
.transform(({ config }) => config),
|
||||
syncOptions: z.record(z.unknown()).default({}),
|
||||
subscriberId: z.string().optional(),
|
||||
connectionId: z.string(),
|
||||
projectId: z.string().trim().min(1)
|
||||
});
|
||||
|
||||
const UpdatePkiSyncRequestBodySchema = z.object({
|
||||
name: z.string().trim().min(1).max(64).optional(),
|
||||
description: z.string().optional(),
|
||||
isAutoSyncEnabled: z.boolean().optional(),
|
||||
destinationConfig: z.record(z.unknown()).optional(),
|
||||
syncOptions: z.record(z.unknown()).optional(),
|
||||
subscriberId: z.string().optional(),
|
||||
connectionId: z.string().optional()
|
||||
});
|
||||
|
||||
export const registerPkiSyncRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
method: "GET",
|
||||
url: "/options",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
schema: {
|
||||
description: "Get PKI sync options",
|
||||
security: [
|
||||
{
|
||||
bearerAuth: []
|
||||
}
|
||||
],
|
||||
response: {
|
||||
200: {
|
||||
description: "PKI sync options retrieved successfully",
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: z.object({
|
||||
pkiSyncOptions: z.array(
|
||||
z.object({
|
||||
name: z.string(),
|
||||
destination: z.nativeEnum(PkiSync),
|
||||
canImportCertificates: z.boolean(),
|
||||
canRemoveCertificates: z.boolean(),
|
||||
enterprise: z.boolean().optional()
|
||||
})
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.SERVICE_TOKEN]),
|
||||
handler: async () => {
|
||||
const pkiSyncOptions = [
|
||||
{
|
||||
name: "Azure Key Vault",
|
||||
destination: PkiSync.AzureKeyVault,
|
||||
canImportCertificates: true,
|
||||
canRemoveCertificates: true,
|
||||
enterprise: false
|
||||
}
|
||||
];
|
||||
|
||||
return { pkiSyncOptions };
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
method: "POST",
|
||||
url: "/",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
schema: {
|
||||
description: "Create PKI sync",
|
||||
security: [
|
||||
{
|
||||
bearerAuth: []
|
||||
}
|
||||
],
|
||||
requestBody: {
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: CreatePkiSyncRequestBodySchema
|
||||
}
|
||||
}
|
||||
},
|
||||
response: {
|
||||
200: {
|
||||
description: "PKI sync created successfully",
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: z.object({
|
||||
pkiSync: PkiSyncSchema
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.SERVICE_TOKEN]),
|
||||
handler: async (req) => {
|
||||
const requestBody = CreatePkiSyncRequestBodySchema.parse(req.body);
|
||||
const createData: Omit<TCreatePkiSyncDTO, "auditLogInfo"> = requestBody;
|
||||
|
||||
try {
|
||||
const pkiSync = await server.services.pkiSync.createPkiSync(createData, req.permission);
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
...req.auditLogInfo,
|
||||
projectId: createData.projectId,
|
||||
event: {
|
||||
type: EventType.CREATE_PKI_SYNC,
|
||||
metadata: {
|
||||
pkiSyncId: pkiSync.id,
|
||||
name: pkiSync.name,
|
||||
destination: pkiSync.destination
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return { pkiSync };
|
||||
} catch (error) {
|
||||
logger.error("Failed to create PKI sync");
|
||||
logger.error(error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
method: "GET",
|
||||
url: "/",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
schema: {
|
||||
description: "List PKI syncs",
|
||||
security: [
|
||||
{
|
||||
bearerAuth: []
|
||||
}
|
||||
],
|
||||
querystring: z.object({
|
||||
projectId: z.string().trim().min(1)
|
||||
}),
|
||||
response: {
|
||||
200: {
|
||||
description: "PKI syncs retrieved successfully",
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: z.object({
|
||||
pkiSyncs: z.array(PkiSyncListItemSchema)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.SERVICE_TOKEN]),
|
||||
handler: async (req) => {
|
||||
const pkiSyncs = await server.services.pkiSync.listPkiSyncsByProjectId(
|
||||
{
|
||||
projectId: req.query.projectId
|
||||
},
|
||||
req.permission
|
||||
);
|
||||
|
||||
return { pkiSyncs };
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
method: "GET",
|
||||
url: "/:pkiSyncId",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
schema: {
|
||||
description: "Get PKI sync by ID",
|
||||
security: [
|
||||
{
|
||||
bearerAuth: []
|
||||
}
|
||||
],
|
||||
params: z.object({
|
||||
pkiSyncId: z.string()
|
||||
}),
|
||||
querystring: z.object({
|
||||
projectId: z.string().trim().min(1)
|
||||
}),
|
||||
response: {
|
||||
200: {
|
||||
description: "PKI sync retrieved successfully",
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: z.object({
|
||||
pkiSync: PkiSyncDetailsSchema
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.SERVICE_TOKEN]),
|
||||
handler: async (req) => {
|
||||
const pkiSync = await server.services.pkiSync.findPkiSyncById(
|
||||
{
|
||||
id: req.params.pkiSyncId,
|
||||
projectId: req.query.projectId
|
||||
},
|
||||
req.permission
|
||||
);
|
||||
|
||||
return { pkiSync };
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
method: "PATCH",
|
||||
url: "/:pkiSyncId",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
schema: {
|
||||
description: "Update PKI sync",
|
||||
security: [
|
||||
{
|
||||
bearerAuth: []
|
||||
}
|
||||
],
|
||||
params: z.object({
|
||||
pkiSyncId: z.string()
|
||||
}),
|
||||
querystring: z.object({
|
||||
projectId: z.string().trim().min(1)
|
||||
}),
|
||||
requestBody: {
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: UpdatePkiSyncRequestBodySchema
|
||||
}
|
||||
}
|
||||
},
|
||||
response: {
|
||||
200: {
|
||||
description: "PKI sync updated successfully",
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: z.object({
|
||||
pkiSync: PkiSyncSchema
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.SERVICE_TOKEN]),
|
||||
handler: async (req) => {
|
||||
const requestBody = UpdatePkiSyncRequestBodySchema.parse(req.body);
|
||||
const updateData: Omit<TUpdatePkiSyncDTO, "auditLogInfo"> = {
|
||||
id: req.params.pkiSyncId,
|
||||
projectId: req.query.projectId,
|
||||
...requestBody
|
||||
};
|
||||
|
||||
try {
|
||||
const pkiSync = await server.services.pkiSync.updatePkiSync(updateData, req.permission);
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
...req.auditLogInfo,
|
||||
projectId: req.query.projectId,
|
||||
event: {
|
||||
type: EventType.UPDATE_PKI_SYNC,
|
||||
metadata: {
|
||||
pkiSyncId: pkiSync.id,
|
||||
name: pkiSync.name
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return { pkiSync };
|
||||
} catch (error) {
|
||||
logger.error("Failed to update PKI sync");
|
||||
logger.error(error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
method: "DELETE",
|
||||
url: "/:pkiSyncId",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
schema: {
|
||||
description: "Delete PKI sync",
|
||||
security: [
|
||||
{
|
||||
bearerAuth: []
|
||||
}
|
||||
],
|
||||
params: z.object({
|
||||
pkiSyncId: z.string()
|
||||
}),
|
||||
querystring: z.object({
|
||||
projectId: z.string().trim().min(1)
|
||||
}),
|
||||
response: {
|
||||
200: {
|
||||
description: "PKI sync deleted successfully",
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: z.object({
|
||||
pkiSync: z.object({
|
||||
id: z.string(),
|
||||
name: z.string(),
|
||||
destination: z.nativeEnum(PkiSync)
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.SERVICE_TOKEN]),
|
||||
handler: async (req) => {
|
||||
try {
|
||||
const pkiSync = await server.services.pkiSync.deletePkiSync(
|
||||
{
|
||||
id: req.params.pkiSyncId,
|
||||
projectId: req.query.projectId
|
||||
},
|
||||
req.permission
|
||||
);
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
...req.auditLogInfo,
|
||||
projectId: req.query.projectId,
|
||||
event: {
|
||||
type: EventType.DELETE_PKI_SYNC,
|
||||
metadata: {
|
||||
pkiSyncId: pkiSync.id,
|
||||
name: pkiSync.name,
|
||||
destination: pkiSync.destination
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return { pkiSync };
|
||||
} catch (error) {
|
||||
logger.error("Failed to delete PKI sync");
|
||||
logger.error(error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
method: "POST",
|
||||
url: "/:pkiSyncId/sync",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
schema: {
|
||||
description: "Trigger PKI sync",
|
||||
security: [
|
||||
{
|
||||
bearerAuth: []
|
||||
}
|
||||
],
|
||||
params: z.object({
|
||||
pkiSyncId: z.string()
|
||||
}),
|
||||
querystring: z.object({
|
||||
projectId: z.string().trim().min(1)
|
||||
}),
|
||||
response: {
|
||||
200: {
|
||||
description: "PKI sync triggered successfully",
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: z.object({
|
||||
message: z.string()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.SERVICE_TOKEN]),
|
||||
handler: async (req) => {
|
||||
try {
|
||||
const result = await server.services.pkiSync.triggerPkiSyncSyncCertificatesById(
|
||||
{
|
||||
id: req.params.pkiSyncId,
|
||||
projectId: req.query.projectId
|
||||
},
|
||||
req.permission
|
||||
);
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
logger.error("Failed to trigger PKI sync");
|
||||
logger.error(error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
method: "POST",
|
||||
url: "/:pkiSyncId/import",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
schema: {
|
||||
description: "Import certificates from PKI sync destination",
|
||||
security: [
|
||||
{
|
||||
bearerAuth: []
|
||||
}
|
||||
],
|
||||
params: z.object({
|
||||
pkiSyncId: z.string()
|
||||
}),
|
||||
querystring: z.object({
|
||||
projectId: z.string().trim().min(1)
|
||||
}),
|
||||
response: {
|
||||
200: {
|
||||
description: "PKI sync import triggered successfully",
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: z.object({
|
||||
message: z.string()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.SERVICE_TOKEN]),
|
||||
handler: async (req) => {
|
||||
try {
|
||||
const result = await server.services.pkiSync.triggerPkiSyncImportCertificatesById(
|
||||
{
|
||||
id: req.params.pkiSyncId,
|
||||
projectId: req.query.projectId
|
||||
},
|
||||
req.permission
|
||||
);
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
logger.error("Failed to trigger PKI sync import certificates");
|
||||
logger.error(error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
method: "POST",
|
||||
url: "/:pkiSyncId/remove",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
schema: {
|
||||
description: "Remove certificates from PKI sync destination",
|
||||
security: [
|
||||
{
|
||||
bearerAuth: []
|
||||
}
|
||||
],
|
||||
params: z.object({
|
||||
pkiSyncId: z.string()
|
||||
}),
|
||||
querystring: z.object({
|
||||
projectId: z.string().trim().min(1)
|
||||
}),
|
||||
response: {
|
||||
200: {
|
||||
description: "PKI sync remove triggered successfully",
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: z.object({
|
||||
message: z.string()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.SERVICE_TOKEN]),
|
||||
handler: async (req) => {
|
||||
try {
|
||||
const result = await server.services.pkiSync.triggerPkiSyncRemoveCertificatesById(
|
||||
{
|
||||
id: req.params.pkiSyncId,
|
||||
projectId: req.query.projectId
|
||||
},
|
||||
req.permission
|
||||
);
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
logger.error("Failed to trigger PKI sync remove certificates");
|
||||
logger.error(error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,17 @@
|
||||
import {
|
||||
AzureKeyVaultPkiSyncSchema,
|
||||
CreateAzureKeyVaultPkiSyncSchema,
|
||||
UpdateAzureKeyVaultPkiSyncSchema
|
||||
} from "@app/services/pki-sync/azure-key-vault";
|
||||
import { PkiSync } from "@app/services/pki-sync/pki-sync-enums";
|
||||
|
||||
import { registerSyncPkiEndpoints } from "./pki-sync-endpoints";
|
||||
|
||||
export const registerAzureKeyVaultPkiSyncRouter = async (server: FastifyZodProvider) =>
|
||||
registerSyncPkiEndpoints({
|
||||
destination: PkiSync.AzureKeyVault,
|
||||
server,
|
||||
responseSchema: AzureKeyVaultPkiSyncSchema,
|
||||
createSchema: CreateAzureKeyVaultPkiSyncSchema,
|
||||
updateSchema: UpdateAzureKeyVaultPkiSyncSchema
|
||||
});
|
||||
@@ -0,0 +1,9 @@
|
||||
import { PkiSync } from "@app/services/pki-sync/pki-sync-enums";
|
||||
|
||||
import { registerAzureKeyVaultPkiSyncRouter } from "./azure-key-vault-pki-sync-router";
|
||||
|
||||
export * from "./pki-sync-router";
|
||||
|
||||
export const PKI_SYNC_REGISTER_ROUTER_MAP: Record<PkiSync, (server: FastifyZodProvider) => Promise<void>> = {
|
||||
[PkiSync.AzureKeyVault]: registerAzureKeyVaultPkiSyncRouter
|
||||
};
|
||||
@@ -0,0 +1,363 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
||||
import { ApiDocsTags } from "@app/lib/api-docs";
|
||||
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
import { PkiSync } from "@app/services/pki-sync/pki-sync-enums";
|
||||
import { PKI_SYNC_NAME_MAP } from "@app/services/pki-sync/pki-sync-maps";
|
||||
|
||||
export const registerSyncPkiEndpoints = ({
|
||||
server,
|
||||
destination,
|
||||
createSchema,
|
||||
updateSchema,
|
||||
responseSchema
|
||||
}: {
|
||||
destination: PkiSync;
|
||||
server: FastifyZodProvider;
|
||||
createSchema: z.ZodType<{
|
||||
name: string;
|
||||
projectId: string;
|
||||
connectionId: string;
|
||||
destinationConfig: Record<string, unknown>;
|
||||
syncOptions?: Record<string, unknown>;
|
||||
description?: string;
|
||||
isAutoSyncEnabled?: boolean;
|
||||
subscriberId?: string;
|
||||
}>;
|
||||
updateSchema: z.ZodType<{
|
||||
connectionId?: string;
|
||||
name?: string;
|
||||
destinationConfig?: Record<string, unknown>;
|
||||
syncOptions?: Record<string, unknown>;
|
||||
description?: string;
|
||||
isAutoSyncEnabled?: boolean;
|
||||
subscriberId?: string;
|
||||
}>;
|
||||
responseSchema: z.ZodTypeAny;
|
||||
}) => {
|
||||
const destinationName = PKI_SYNC_NAME_MAP[destination];
|
||||
|
||||
server.route({
|
||||
method: "GET",
|
||||
url: `/`,
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
schema: {
|
||||
hide: false,
|
||||
tags: [ApiDocsTags.PkiSyncs],
|
||||
description: `List the ${destinationName} PKI Syncs for the specified project.`,
|
||||
querystring: z.object({
|
||||
projectId: z.string().trim().min(1, "Project ID required")
|
||||
}),
|
||||
response: {
|
||||
200: z.object({ pkiSyncs: responseSchema.array() })
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.SERVICE_TOKEN]),
|
||||
handler: async (req) => {
|
||||
const {
|
||||
query: { projectId }
|
||||
} = req;
|
||||
|
||||
const pkiSyncs = await server.services.pkiSync.listPkiSyncsByProjectId({ projectId }, req.permission);
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
...req.auditLogInfo,
|
||||
projectId,
|
||||
event: {
|
||||
type: EventType.GET_PKI_SYNCS,
|
||||
metadata: {
|
||||
projectId
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return { pkiSyncs };
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
method: "GET",
|
||||
url: "/:pkiSyncId",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
schema: {
|
||||
hide: false,
|
||||
tags: [ApiDocsTags.PkiSyncs],
|
||||
description: `Get the specified ${destinationName} PKI Sync by ID.`,
|
||||
params: z.object({
|
||||
pkiSyncId: z.string()
|
||||
}),
|
||||
querystring: z.object({
|
||||
projectId: z.string().trim().min(1)
|
||||
}),
|
||||
response: {
|
||||
200: z.object({ pkiSync: responseSchema })
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.SERVICE_TOKEN]),
|
||||
handler: async (req) => {
|
||||
const { pkiSyncId } = req.params;
|
||||
const { projectId } = req.query;
|
||||
|
||||
const pkiSync = await server.services.pkiSync.findPkiSyncById({ id: pkiSyncId, projectId }, req.permission);
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
...req.auditLogInfo,
|
||||
projectId: pkiSync.projectId,
|
||||
event: {
|
||||
type: EventType.GET_PKI_SYNC,
|
||||
metadata: {
|
||||
syncId: pkiSyncId,
|
||||
destination
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return { pkiSync };
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
method: "POST",
|
||||
url: "/",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
hide: false,
|
||||
tags: [ApiDocsTags.PkiSyncs],
|
||||
description: `Create a ${destinationName} PKI Sync for the specified project.`,
|
||||
body: createSchema,
|
||||
response: {
|
||||
200: z.object({ pkiSync: responseSchema })
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.SERVICE_TOKEN]),
|
||||
handler: async (req) => {
|
||||
const pkiSync = await server.services.pkiSync.createPkiSync({ ...req.body, destination }, req.permission);
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
...req.auditLogInfo,
|
||||
projectId: pkiSync.projectId,
|
||||
event: {
|
||||
type: EventType.CREATE_PKI_SYNC,
|
||||
metadata: {
|
||||
pkiSyncId: pkiSync.id,
|
||||
name: pkiSync.name,
|
||||
destination
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return { pkiSync };
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
method: "PATCH",
|
||||
url: "/:pkiSyncId",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
hide: false,
|
||||
tags: [ApiDocsTags.PkiSyncs],
|
||||
description: `Update the specified ${destinationName} PKI Sync.`,
|
||||
params: z.object({
|
||||
pkiSyncId: z.string()
|
||||
}),
|
||||
querystring: z.object({
|
||||
projectId: z.string().trim().min(1)
|
||||
}),
|
||||
body: updateSchema,
|
||||
response: {
|
||||
200: z.object({ pkiSync: responseSchema })
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.SERVICE_TOKEN]),
|
||||
handler: async (req) => {
|
||||
const { pkiSyncId } = req.params;
|
||||
const { projectId } = req.query;
|
||||
|
||||
const pkiSync = await server.services.pkiSync.updatePkiSync(
|
||||
{ ...req.body, id: pkiSyncId, projectId },
|
||||
req.permission
|
||||
);
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
...req.auditLogInfo,
|
||||
projectId,
|
||||
event: {
|
||||
type: EventType.UPDATE_PKI_SYNC,
|
||||
metadata: {
|
||||
pkiSyncId,
|
||||
name: pkiSync.name
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return { pkiSync };
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
method: "DELETE",
|
||||
url: `/:pkiSyncId`,
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
hide: false,
|
||||
tags: [ApiDocsTags.PkiSyncs],
|
||||
description: `Delete the specified ${destinationName} PKI Sync.`,
|
||||
params: z.object({
|
||||
pkiSyncId: z.string()
|
||||
}),
|
||||
querystring: z.object({
|
||||
projectId: z.string().trim().min(1)
|
||||
}),
|
||||
response: {
|
||||
200: z.object({ pkiSync: responseSchema })
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.SERVICE_TOKEN]),
|
||||
handler: async (req) => {
|
||||
const { pkiSyncId } = req.params;
|
||||
const { projectId } = req.query;
|
||||
|
||||
const pkiSync = await server.services.pkiSync.deletePkiSync({ id: pkiSyncId, projectId }, req.permission);
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
...req.auditLogInfo,
|
||||
projectId,
|
||||
event: {
|
||||
type: EventType.DELETE_PKI_SYNC,
|
||||
metadata: {
|
||||
pkiSyncId,
|
||||
name: pkiSync.name,
|
||||
destination: pkiSync.destination
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return { pkiSync };
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
method: "POST",
|
||||
url: "/:pkiSyncId/sync",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
hide: false,
|
||||
tags: [ApiDocsTags.PkiSyncs],
|
||||
description: `Trigger a sync for the specified ${destinationName} PKI Sync.`,
|
||||
params: z.object({
|
||||
pkiSyncId: z.string()
|
||||
}),
|
||||
querystring: z.object({
|
||||
projectId: z.string().trim().min(1)
|
||||
}),
|
||||
response: {
|
||||
200: z.object({ message: z.string() })
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.SERVICE_TOKEN]),
|
||||
handler: async (req) => {
|
||||
const { pkiSyncId } = req.params;
|
||||
const { projectId } = req.query;
|
||||
|
||||
const result = await server.services.pkiSync.triggerPkiSyncSyncCertificatesById(
|
||||
{
|
||||
id: pkiSyncId,
|
||||
projectId
|
||||
},
|
||||
req.permission
|
||||
);
|
||||
|
||||
return result;
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
method: "POST",
|
||||
url: "/:pkiSyncId/import",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
hide: false,
|
||||
tags: [ApiDocsTags.PkiSyncs],
|
||||
description: `Import certificates from the specified ${destinationName} PKI Sync destination.`,
|
||||
params: z.object({
|
||||
pkiSyncId: z.string()
|
||||
}),
|
||||
querystring: z.object({
|
||||
projectId: z.string().trim().min(1)
|
||||
}),
|
||||
response: {
|
||||
200: z.object({ message: z.string() })
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.SERVICE_TOKEN]),
|
||||
handler: async (req) => {
|
||||
const { pkiSyncId } = req.params;
|
||||
const { projectId } = req.query;
|
||||
|
||||
const result = await server.services.pkiSync.triggerPkiSyncImportCertificatesById(
|
||||
{
|
||||
id: pkiSyncId,
|
||||
projectId
|
||||
},
|
||||
req.permission
|
||||
);
|
||||
|
||||
return result;
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
method: "POST",
|
||||
url: "/:pkiSyncId/remove",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
hide: false,
|
||||
tags: [ApiDocsTags.PkiSyncs],
|
||||
description: `Remove certificates from the specified ${destinationName} PKI Sync destination.`,
|
||||
params: z.object({
|
||||
pkiSyncId: z.string()
|
||||
}),
|
||||
querystring: z.object({
|
||||
projectId: z.string().trim().min(1)
|
||||
}),
|
||||
response: {
|
||||
200: z.object({ message: z.string() })
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.SERVICE_TOKEN]),
|
||||
handler: async (req) => {
|
||||
const { pkiSyncId } = req.params;
|
||||
const { projectId } = req.query;
|
||||
|
||||
const result = await server.services.pkiSync.triggerPkiSyncRemoveCertificatesById(
|
||||
{
|
||||
id: pkiSyncId,
|
||||
projectId
|
||||
},
|
||||
req.permission
|
||||
);
|
||||
|
||||
return result;
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,174 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
||||
import { ApiDocsTags } from "@app/lib/api-docs";
|
||||
import { readLimit } from "@app/server/config/rateLimiter";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
import { PkiSync } from "@app/services/pki-sync/pki-sync-enums";
|
||||
|
||||
const PkiSyncSchema = z.object({
|
||||
id: z.string().uuid(),
|
||||
name: z.string(),
|
||||
description: z.string().nullable().optional(),
|
||||
destination: z.nativeEnum(PkiSync),
|
||||
isAutoSyncEnabled: z.boolean(),
|
||||
destinationConfig: z.record(z.unknown()),
|
||||
syncOptions: z.record(z.unknown()),
|
||||
projectId: z.string().uuid(),
|
||||
subscriberId: z.string().uuid().nullable().optional(),
|
||||
connectionId: z.string().uuid(),
|
||||
createdAt: z.date(),
|
||||
updatedAt: z.date(),
|
||||
// Sync status fields
|
||||
syncStatus: z.string().nullable().optional(),
|
||||
lastSyncJobId: z.string().nullable().optional(),
|
||||
lastSyncMessage: z.string().nullable().optional(),
|
||||
lastSyncedAt: z.date().nullable().optional(),
|
||||
// Import status fields
|
||||
importStatus: z.string().nullable().optional(),
|
||||
lastImportJobId: z.string().nullable().optional(),
|
||||
lastImportMessage: z.string().nullable().optional(),
|
||||
lastImportedAt: z.date().nullable().optional(),
|
||||
// Remove status fields
|
||||
removeStatus: z.string().nullable().optional(),
|
||||
lastRemoveJobId: z.string().nullable().optional(),
|
||||
lastRemoveMessage: z.string().nullable().optional(),
|
||||
lastRemovedAt: z.date().nullable().optional(),
|
||||
// App connection info
|
||||
appConnectionName: z.string(),
|
||||
appConnectionApp: z.string(),
|
||||
connection: z.object({
|
||||
id: z.string(),
|
||||
name: z.string(),
|
||||
app: z.string(),
|
||||
encryptedCredentials: z.unknown().nullable(),
|
||||
orgId: z.string().uuid(),
|
||||
projectId: z.string().uuid().nullable().optional(),
|
||||
method: z.string(),
|
||||
description: z.string().nullable().optional(),
|
||||
version: z.number(),
|
||||
gatewayId: z.string().uuid().nullable().optional(),
|
||||
createdAt: z.date(),
|
||||
updatedAt: z.date(),
|
||||
isPlatformManagedCredentials: z.boolean().nullable().optional()
|
||||
})
|
||||
});
|
||||
|
||||
const PkiSyncOptionsSchema = z.object({
|
||||
name: z.string(),
|
||||
connection: z.nativeEnum(AppConnection),
|
||||
destination: z.nativeEnum(PkiSync),
|
||||
canImportCertificates: z.boolean(),
|
||||
canRemoveCertificates: z.boolean()
|
||||
});
|
||||
|
||||
export const registerPkiSyncRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
method: "GET",
|
||||
url: "/options",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
schema: {
|
||||
hide: false,
|
||||
tags: [ApiDocsTags.PkiSyncs],
|
||||
description: "List the available PKI Sync Options.",
|
||||
response: {
|
||||
200: z.object({
|
||||
pkiSyncOptions: PkiSyncOptionsSchema.array()
|
||||
})
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.SERVICE_TOKEN]),
|
||||
handler: () => {
|
||||
const pkiSyncOptions = server.services.pkiSync.getPkiSyncOptions();
|
||||
return { pkiSyncOptions };
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
method: "GET",
|
||||
url: "/",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
schema: {
|
||||
hide: false,
|
||||
tags: [ApiDocsTags.PkiSyncs],
|
||||
description: "List all the PKI Syncs for the specified project.",
|
||||
querystring: z.object({
|
||||
projectId: z.string().trim().min(1)
|
||||
}),
|
||||
response: {
|
||||
200: z.object({ pkiSyncs: PkiSyncSchema.array() })
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.SERVICE_TOKEN]),
|
||||
handler: async (req) => {
|
||||
const {
|
||||
query: { projectId },
|
||||
permission
|
||||
} = req;
|
||||
|
||||
const pkiSyncs = await server.services.pkiSync.listPkiSyncsByProjectId({ projectId }, permission);
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
...req.auditLogInfo,
|
||||
projectId,
|
||||
event: {
|
||||
type: EventType.GET_PKI_SYNCS,
|
||||
metadata: {
|
||||
projectId
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return { pkiSyncs };
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
method: "GET",
|
||||
url: "/:pkiSyncId",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
schema: {
|
||||
hide: false,
|
||||
tags: [ApiDocsTags.PkiSyncs],
|
||||
description: "Get a PKI Sync by ID.",
|
||||
params: z.object({
|
||||
pkiSyncId: z.string()
|
||||
}),
|
||||
querystring: z.object({
|
||||
projectId: z.string().trim().min(1)
|
||||
}),
|
||||
response: {
|
||||
200: z.object({ pkiSync: PkiSyncSchema })
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.SERVICE_TOKEN]),
|
||||
handler: async (req) => {
|
||||
const { pkiSyncId } = req.params;
|
||||
const { projectId } = req.query;
|
||||
|
||||
const pkiSync = await server.services.pkiSync.findPkiSyncById({ id: pkiSyncId, projectId }, req.permission);
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
...req.auditLogInfo,
|
||||
projectId: pkiSync.projectId,
|
||||
event: {
|
||||
type: EventType.GET_PKI_SYNC,
|
||||
metadata: {
|
||||
syncId: pkiSyncId,
|
||||
destination: pkiSync.destination
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return { pkiSync };
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -4,13 +4,23 @@ import { AxiosError } from "axios";
|
||||
import { request } from "@app/lib/config/request";
|
||||
import { logger } from "@app/lib/logger";
|
||||
import { TAppConnectionDALFactory } from "@app/services/app-connection/app-connection-dal";
|
||||
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
|
||||
import { getAzureConnectionAccessToken } from "@app/services/app-connection/azure-key-vault";
|
||||
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
||||
import { TCertificateMap } from "@app/services/pki-sync/pki-sync-types";
|
||||
|
||||
import { PkiSync } from "../pki-sync-enums";
|
||||
import { PkiSyncError } from "../pki-sync-errors";
|
||||
import { GetAzureKeyVaultCertificate, TAzureKeyVaultPkiSyncWithCredentials } from "./azure-key-vault-pki-sync-types";
|
||||
|
||||
export const AZURE_KEY_VAULT_PKI_SYNC_LIST_OPTION = {
|
||||
name: "Azure Key Vault" as const,
|
||||
connection: AppConnection.AzureKeyVault,
|
||||
destination: PkiSync.AzureKeyVault,
|
||||
canImportCertificates: false,
|
||||
canRemoveCertificates: true
|
||||
};
|
||||
|
||||
type TAzureKeyVaultPkiSyncFactoryDeps = {
|
||||
appConnectionDAL: Pick<TAppConnectionDALFactory, "findById" | "updateById">;
|
||||
kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey">;
|
||||
@@ -56,7 +66,6 @@ export const azureKeyVaultPkiSyncFactory = ({ kmsService, appConnectionDAL }: TA
|
||||
lastSlashIndex = getAzureKeyVaultCertificate.id.lastIndexOf("/");
|
||||
}
|
||||
|
||||
// Get the certificate details
|
||||
const azureKeyVaultCertificate = await request.get<GetAzureKeyVaultCertificate>(
|
||||
`${getAzureKeyVaultCertificate.id}?api-version=7.4`,
|
||||
{
|
||||
@@ -66,7 +75,6 @@ export const azureKeyVaultPkiSyncFactory = ({ kmsService, appConnectionDAL }: TA
|
||||
}
|
||||
);
|
||||
|
||||
// Convert base64 certificate to PEM format if available
|
||||
let certPem = "";
|
||||
if (azureKeyVaultCertificate.data.cer) {
|
||||
try {
|
||||
@@ -75,7 +83,6 @@ export const azureKeyVaultPkiSyncFactory = ({ kmsService, appConnectionDAL }: TA
|
||||
const base64Cert = azureKeyVaultCertificate.data.cer;
|
||||
certPem = `-----BEGIN CERTIFICATE-----\n${base64Cert.match(/.{1,64}/g)?.join("\n")}\n-----END CERTIFICATE-----`;
|
||||
} catch (error) {
|
||||
// If conversion fails, assume it's already in PEM format
|
||||
certPem = azureKeyVaultCertificate.data.cer;
|
||||
}
|
||||
}
|
||||
@@ -259,7 +266,6 @@ export const azureKeyVaultPkiSyncFactory = ({ kmsService, appConnectionDAL }: TA
|
||||
{ certificateKey: key, syncId: pkiSync.id },
|
||||
"Certificate exists in deleted but recoverable state in Azure Key Vault - skipping upload"
|
||||
);
|
||||
// Return a successful result to avoid failing the entire sync
|
||||
return { key, success: false, skipped: true, reason: "Certificate in deleted but recoverable state" };
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
|
||||
import { PkiSync } from "@app/services/pki-sync/pki-sync-enums";
|
||||
import { PkiSyncSchema } from "@app/services/pki-sync/pki-sync-schemas";
|
||||
|
||||
import { AzureKeyVaultPkiSyncConfigSchema } from "./azure-key-vault-pki-sync-types";
|
||||
|
||||
export const AzureKeyVaultPkiSyncSchema = PkiSyncSchema.extend({
|
||||
destination: z.literal(PkiSync.AzureKeyVault),
|
||||
destinationConfig: AzureKeyVaultPkiSyncConfigSchema
|
||||
});
|
||||
|
||||
export const CreateAzureKeyVaultPkiSyncSchema = z.object({
|
||||
name: z.string().trim().min(1).max(64),
|
||||
description: z.string().optional(),
|
||||
isAutoSyncEnabled: z.boolean().default(true),
|
||||
destinationConfig: AzureKeyVaultPkiSyncConfigSchema,
|
||||
syncOptions: z.record(z.unknown()).optional().default({}),
|
||||
subscriberId: z.string().optional(),
|
||||
connectionId: z.string(),
|
||||
projectId: z.string().trim().min(1)
|
||||
});
|
||||
|
||||
export const UpdateAzureKeyVaultPkiSyncSchema = z.object({
|
||||
name: z.string().trim().min(1).max(64).optional(),
|
||||
description: z.string().optional(),
|
||||
isAutoSyncEnabled: z.boolean().optional(),
|
||||
destinationConfig: AzureKeyVaultPkiSyncConfigSchema.optional(),
|
||||
syncOptions: z.record(z.unknown()).optional(),
|
||||
subscriberId: z.string().optional(),
|
||||
connectionId: z.string().optional()
|
||||
});
|
||||
|
||||
export const AzureKeyVaultPkiSyncListItemSchema = z.object({
|
||||
name: z.literal("Azure Key Vault"),
|
||||
connection: z.literal(AppConnection.AzureKeyVault),
|
||||
destination: z.literal(PkiSync.AzureKeyVault),
|
||||
canImportCertificates: z.literal(false),
|
||||
canRemoveCertificates: z.literal(true)
|
||||
});
|
||||
@@ -0,0 +1,3 @@
|
||||
export * from "./azure-key-vault-pki-sync-fns";
|
||||
export * from "./azure-key-vault-pki-sync-schemas";
|
||||
export * from "./azure-key-vault-pki-sync-types";
|
||||
@@ -21,6 +21,7 @@ const basePkiSyncQuery = ({ filter, db, tx }: { db: TDbClient; filter?: PkiSyncF
|
||||
db.ref("app").withSchema(TableName.AppConnection).as("appConnectionApp"),
|
||||
db.ref("encryptedCredentials").withSchema(TableName.AppConnection).as("appConnectionEncryptedCredentials"),
|
||||
db.ref("orgId").withSchema(TableName.AppConnection).as("appConnectionOrgId"),
|
||||
db.ref("projectId").withSchema(TableName.AppConnection).as("appConnectionProjectId"),
|
||||
db.ref("method").withSchema(TableName.AppConnection).as("appConnectionMethod"),
|
||||
db.ref("description").withSchema(TableName.AppConnection).as("appConnectionDescription"),
|
||||
db.ref("version").withSchema(TableName.AppConnection).as("appConnectionVersion"),
|
||||
@@ -47,6 +48,7 @@ const expandPkiSync = (pkiSync: Awaited<ReturnType<typeof basePkiSyncQuery>>[num
|
||||
appConnectionApp,
|
||||
appConnectionEncryptedCredentials,
|
||||
appConnectionOrgId,
|
||||
appConnectionProjectId,
|
||||
appConnectionMethod,
|
||||
appConnectionDescription,
|
||||
appConnectionVersion,
|
||||
@@ -70,6 +72,7 @@ const expandPkiSync = (pkiSync: Awaited<ReturnType<typeof basePkiSyncQuery>>[num
|
||||
app: appConnectionApp,
|
||||
encryptedCredentials: appConnectionEncryptedCredentials,
|
||||
orgId: appConnectionOrgId,
|
||||
projectId: appConnectionProjectId,
|
||||
method: appConnectionMethod,
|
||||
description: appConnectionDescription,
|
||||
version: appConnectionVersion,
|
||||
|
||||
@@ -5,11 +5,16 @@ import { BadRequestError } from "@app/lib/errors";
|
||||
import { TAppConnectionDALFactory } from "@app/services/app-connection/app-connection-dal";
|
||||
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
||||
|
||||
import { AZURE_KEY_VAULT_PKI_SYNC_LIST_OPTION } from "./azure-key-vault/azure-key-vault-pki-sync-fns";
|
||||
import { PkiSync } from "./pki-sync-enums";
|
||||
import { TCertificateMap, TPkiSyncWithCredentials } from "./pki-sync-types";
|
||||
|
||||
const ENTERPRISE_PKI_SYNCS: PkiSync[] = [];
|
||||
|
||||
const PKI_SYNC_LIST_OPTIONS = {
|
||||
[PkiSync.AzureKeyVault]: AZURE_KEY_VAULT_PKI_SYNC_LIST_OPTION
|
||||
};
|
||||
|
||||
export const enterprisePkiSyncCheck = async (
|
||||
licenseService: Pick<TLicenseServiceFactory, "getPlan">,
|
||||
orgId: string,
|
||||
@@ -26,7 +31,7 @@ export const enterprisePkiSyncCheck = async (
|
||||
};
|
||||
|
||||
export const listPkiSyncOptions = () => {
|
||||
return Object.values(PkiSync);
|
||||
return Object.values(PKI_SYNC_LIST_OPTIONS).sort((a, b) => a.name.localeCompare(b.name));
|
||||
};
|
||||
|
||||
export const matchesSchema = <T extends ZodSchema>(schema: T, data: unknown): data is z.infer<T> => {
|
||||
|
||||
@@ -216,7 +216,6 @@ export const pkiSyncQueueFactory = ({
|
||||
encryptedCertificate
|
||||
});
|
||||
|
||||
// Create certificate secret record with encrypted private key (if available)
|
||||
if (certData.privateKey) {
|
||||
const { cipherTextBlob: encryptedPrivateKey } = await kmsEncryptor({
|
||||
plainText: Buffer.from(certData.privateKey)
|
||||
@@ -231,7 +230,6 @@ export const pkiSyncQueueFactory = ({
|
||||
logger.info(`Successfully created certificate ${certData.name} with ID ${createdCert.id}`);
|
||||
} catch (error) {
|
||||
logger.error(`Failed to create certificate ${certData.name}: ${String(error)}`);
|
||||
// Continue with other certificates even if one fails
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -264,7 +262,6 @@ export const pkiSyncQueueFactory = ({
|
||||
for (const certificate of certificates) {
|
||||
try {
|
||||
// Only sync certificates issued by Infisical (not imported ones)
|
||||
// Imported certificates don't have caId and certificateTemplateId
|
||||
if (!certificate.caId) {
|
||||
logger.debug(
|
||||
{ certificateId: certificate.id, subscriberId },
|
||||
@@ -503,15 +500,14 @@ export const pkiSyncQueueFactory = ({
|
||||
|
||||
try {
|
||||
const {
|
||||
connection: { orgId, encryptedCredentials },
|
||||
projectId
|
||||
connection: { orgId, encryptedCredentials, projectId: appConnectionProjectId }
|
||||
} = pkiSync;
|
||||
|
||||
const credentials = await decryptAppConnectionCredentials({
|
||||
orgId,
|
||||
encryptedCredentials,
|
||||
kmsService,
|
||||
projectId
|
||||
projectId: appConnectionProjectId
|
||||
});
|
||||
|
||||
const pkiSyncWithCredentials = {
|
||||
@@ -564,7 +560,6 @@ export const pkiSyncQueueFactory = ({
|
||||
if (err instanceof PkiSyncError && !err.shouldRetry) {
|
||||
isFinalAttempt = true;
|
||||
} else {
|
||||
// re-throw so job fails
|
||||
throw err;
|
||||
}
|
||||
} finally {
|
||||
@@ -630,15 +625,14 @@ export const pkiSyncQueueFactory = ({
|
||||
|
||||
try {
|
||||
const {
|
||||
connection: { orgId, encryptedCredentials },
|
||||
projectId
|
||||
connection: { orgId, encryptedCredentials, projectId: appConnectionProjectId }
|
||||
} = pkiSync;
|
||||
|
||||
const credentials = await decryptAppConnectionCredentials({
|
||||
orgId,
|
||||
encryptedCredentials,
|
||||
kmsService,
|
||||
projectId
|
||||
projectId: appConnectionProjectId
|
||||
});
|
||||
|
||||
await $importCertificates({
|
||||
@@ -673,7 +667,6 @@ export const pkiSyncQueueFactory = ({
|
||||
if (err instanceof PkiSyncError && !err.shouldRetry) {
|
||||
isFinalAttempt = true;
|
||||
} else {
|
||||
// re-throw so job fails
|
||||
throw err;
|
||||
}
|
||||
} finally {
|
||||
@@ -746,14 +739,14 @@ export const pkiSyncQueueFactory = ({
|
||||
|
||||
try {
|
||||
const {
|
||||
connection: { orgId, encryptedCredentials }
|
||||
connection: { orgId, encryptedCredentials, projectId: appConnectionProjectId }
|
||||
} = pkiSync;
|
||||
|
||||
const credentials = await decryptAppConnectionCredentials({
|
||||
orgId,
|
||||
encryptedCredentials,
|
||||
kmsService,
|
||||
projectId: pkiSync.projectId
|
||||
projectId: appConnectionProjectId
|
||||
});
|
||||
|
||||
const certificateMap = await $getInfisicalCertificates(pkiSync);
|
||||
@@ -797,7 +790,6 @@ export const pkiSyncQueueFactory = ({
|
||||
if (err instanceof PkiSyncError && !err.shouldRetry) {
|
||||
isFinalAttempt = true;
|
||||
} else {
|
||||
// re-throw so job fails
|
||||
throw err;
|
||||
}
|
||||
} finally {
|
||||
@@ -880,7 +872,6 @@ export const pkiSyncQueueFactory = ({
|
||||
errorMessage = lastRemoveMessage || null;
|
||||
}
|
||||
|
||||
// Log notification for now - actual email sending would require SMTP configuration
|
||||
if (projectAdmins.length > 0) {
|
||||
logger.info(
|
||||
`PKI Sync ${action} failure notification would be sent to ${projectAdmins.length} admin(s) for sync "${name}" in project "${project.name}". Error: ${errorMessage}`
|
||||
|
||||
@@ -40,7 +40,6 @@ export const PkiSyncListItemSchema = PkiSyncSchema.extend({
|
||||
appConnectionApp: z.string().max(255)
|
||||
});
|
||||
|
||||
// Schema for PKI sync details (includes app connection info)
|
||||
export const PkiSyncDetailsSchema = PkiSyncSchema.extend({
|
||||
appConnectionName: z.string().max(255),
|
||||
appConnectionApp: z.string().max(255)
|
||||
|
||||
@@ -103,6 +103,12 @@ export const pkiSyncServiceFactory = ({
|
||||
// Validates permission to connect and app is valid for sync destination
|
||||
await appConnectionService.connectAppConnectionById(destinationApp, connectionId, actor);
|
||||
|
||||
const defaultSyncOptions = {
|
||||
canImportCertificates: false,
|
||||
canRemoveCertificates: true,
|
||||
...syncOptions
|
||||
};
|
||||
|
||||
try {
|
||||
const pkiSync = await pkiSyncDAL.create({
|
||||
name,
|
||||
@@ -110,7 +116,7 @@ export const pkiSyncServiceFactory = ({
|
||||
destination,
|
||||
isAutoSyncEnabled,
|
||||
destinationConfig,
|
||||
syncOptions,
|
||||
syncOptions: defaultSyncOptions,
|
||||
subscriberId,
|
||||
connectionId,
|
||||
projectId,
|
||||
|
||||
Reference in New Issue
Block a user