Add PKI Syncs docs and a few improvements on the router
@@ -50,6 +50,7 @@ export enum ApiDocsTags {
|
|||||||
IdentitySpecificPrivilegesV2 = "Identity Specific Privileges V2",
|
IdentitySpecificPrivilegesV2 = "Identity Specific Privileges V2",
|
||||||
AppConnections = "App Connections",
|
AppConnections = "App Connections",
|
||||||
SecretSyncs = "Secret Syncs",
|
SecretSyncs = "Secret Syncs",
|
||||||
|
PkiSyncs = "PKI Syncs",
|
||||||
Integrations = "Integrations",
|
Integrations = "Integrations",
|
||||||
ServiceTokens = "Service Tokens",
|
ServiceTokens = "Service Tokens",
|
||||||
AuditLogs = "Audit Logs",
|
AuditLogs = "Audit Logs",
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ import { registerPasswordRouter } from "./password-router";
|
|||||||
import { registerPkiAlertRouter } from "./pki-alert-router";
|
import { registerPkiAlertRouter } from "./pki-alert-router";
|
||||||
import { registerPkiCollectionRouter } from "./pki-collection-router";
|
import { registerPkiCollectionRouter } from "./pki-collection-router";
|
||||||
import { registerPkiSubscriberRouter } from "./pki-subscriber-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 { registerProjectEnvRouter } from "./project-env-router";
|
||||||
import { registerProjectKeyRouter } from "./project-key-router";
|
import { registerProjectKeyRouter } from "./project-key-router";
|
||||||
import { registerProjectMembershipRouter } from "./project-membership-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(registerIntegrationAuthRouter, { prefix: "/integration-auth" });
|
||||||
await server.register(registerWebhookRouter, { prefix: "/webhooks" });
|
await server.register(registerWebhookRouter, { prefix: "/webhooks" });
|
||||||
await server.register(registerIdentityRouter, { prefix: "/identities" });
|
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(
|
await server.register(
|
||||||
async (secretSharingRouter) => {
|
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 { request } from "@app/lib/config/request";
|
||||||
import { logger } from "@app/lib/logger";
|
import { logger } from "@app/lib/logger";
|
||||||
import { TAppConnectionDALFactory } from "@app/services/app-connection/app-connection-dal";
|
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 { getAzureConnectionAccessToken } from "@app/services/app-connection/azure-key-vault";
|
||||||
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
||||||
import { TCertificateMap } from "@app/services/pki-sync/pki-sync-types";
|
import { TCertificateMap } from "@app/services/pki-sync/pki-sync-types";
|
||||||
|
|
||||||
|
import { PkiSync } from "../pki-sync-enums";
|
||||||
import { PkiSyncError } from "../pki-sync-errors";
|
import { PkiSyncError } from "../pki-sync-errors";
|
||||||
import { GetAzureKeyVaultCertificate, TAzureKeyVaultPkiSyncWithCredentials } from "./azure-key-vault-pki-sync-types";
|
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 = {
|
type TAzureKeyVaultPkiSyncFactoryDeps = {
|
||||||
appConnectionDAL: Pick<TAppConnectionDALFactory, "findById" | "updateById">;
|
appConnectionDAL: Pick<TAppConnectionDALFactory, "findById" | "updateById">;
|
||||||
kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey">;
|
kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey">;
|
||||||
@@ -56,7 +66,6 @@ export const azureKeyVaultPkiSyncFactory = ({ kmsService, appConnectionDAL }: TA
|
|||||||
lastSlashIndex = getAzureKeyVaultCertificate.id.lastIndexOf("/");
|
lastSlashIndex = getAzureKeyVaultCertificate.id.lastIndexOf("/");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the certificate details
|
|
||||||
const azureKeyVaultCertificate = await request.get<GetAzureKeyVaultCertificate>(
|
const azureKeyVaultCertificate = await request.get<GetAzureKeyVaultCertificate>(
|
||||||
`${getAzureKeyVaultCertificate.id}?api-version=7.4`,
|
`${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 = "";
|
let certPem = "";
|
||||||
if (azureKeyVaultCertificate.data.cer) {
|
if (azureKeyVaultCertificate.data.cer) {
|
||||||
try {
|
try {
|
||||||
@@ -75,7 +83,6 @@ export const azureKeyVaultPkiSyncFactory = ({ kmsService, appConnectionDAL }: TA
|
|||||||
const base64Cert = azureKeyVaultCertificate.data.cer;
|
const base64Cert = azureKeyVaultCertificate.data.cer;
|
||||||
certPem = `-----BEGIN CERTIFICATE-----\n${base64Cert.match(/.{1,64}/g)?.join("\n")}\n-----END CERTIFICATE-----`;
|
certPem = `-----BEGIN CERTIFICATE-----\n${base64Cert.match(/.{1,64}/g)?.join("\n")}\n-----END CERTIFICATE-----`;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// If conversion fails, assume it's already in PEM format
|
|
||||||
certPem = azureKeyVaultCertificate.data.cer;
|
certPem = azureKeyVaultCertificate.data.cer;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -259,7 +266,6 @@ export const azureKeyVaultPkiSyncFactory = ({ kmsService, appConnectionDAL }: TA
|
|||||||
{ certificateKey: key, syncId: pkiSync.id },
|
{ certificateKey: key, syncId: pkiSync.id },
|
||||||
"Certificate exists in deleted but recoverable state in Azure Key Vault - skipping upload"
|
"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" };
|
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("app").withSchema(TableName.AppConnection).as("appConnectionApp"),
|
||||||
db.ref("encryptedCredentials").withSchema(TableName.AppConnection).as("appConnectionEncryptedCredentials"),
|
db.ref("encryptedCredentials").withSchema(TableName.AppConnection).as("appConnectionEncryptedCredentials"),
|
||||||
db.ref("orgId").withSchema(TableName.AppConnection).as("appConnectionOrgId"),
|
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("method").withSchema(TableName.AppConnection).as("appConnectionMethod"),
|
||||||
db.ref("description").withSchema(TableName.AppConnection).as("appConnectionDescription"),
|
db.ref("description").withSchema(TableName.AppConnection).as("appConnectionDescription"),
|
||||||
db.ref("version").withSchema(TableName.AppConnection).as("appConnectionVersion"),
|
db.ref("version").withSchema(TableName.AppConnection).as("appConnectionVersion"),
|
||||||
@@ -47,6 +48,7 @@ const expandPkiSync = (pkiSync: Awaited<ReturnType<typeof basePkiSyncQuery>>[num
|
|||||||
appConnectionApp,
|
appConnectionApp,
|
||||||
appConnectionEncryptedCredentials,
|
appConnectionEncryptedCredentials,
|
||||||
appConnectionOrgId,
|
appConnectionOrgId,
|
||||||
|
appConnectionProjectId,
|
||||||
appConnectionMethod,
|
appConnectionMethod,
|
||||||
appConnectionDescription,
|
appConnectionDescription,
|
||||||
appConnectionVersion,
|
appConnectionVersion,
|
||||||
@@ -70,6 +72,7 @@ const expandPkiSync = (pkiSync: Awaited<ReturnType<typeof basePkiSyncQuery>>[num
|
|||||||
app: appConnectionApp,
|
app: appConnectionApp,
|
||||||
encryptedCredentials: appConnectionEncryptedCredentials,
|
encryptedCredentials: appConnectionEncryptedCredentials,
|
||||||
orgId: appConnectionOrgId,
|
orgId: appConnectionOrgId,
|
||||||
|
projectId: appConnectionProjectId,
|
||||||
method: appConnectionMethod,
|
method: appConnectionMethod,
|
||||||
description: appConnectionDescription,
|
description: appConnectionDescription,
|
||||||
version: appConnectionVersion,
|
version: appConnectionVersion,
|
||||||
|
|||||||
@@ -5,11 +5,16 @@ import { BadRequestError } from "@app/lib/errors";
|
|||||||
import { TAppConnectionDALFactory } from "@app/services/app-connection/app-connection-dal";
|
import { TAppConnectionDALFactory } from "@app/services/app-connection/app-connection-dal";
|
||||||
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
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 { PkiSync } from "./pki-sync-enums";
|
||||||
import { TCertificateMap, TPkiSyncWithCredentials } from "./pki-sync-types";
|
import { TCertificateMap, TPkiSyncWithCredentials } from "./pki-sync-types";
|
||||||
|
|
||||||
const ENTERPRISE_PKI_SYNCS: PkiSync[] = [];
|
const ENTERPRISE_PKI_SYNCS: PkiSync[] = [];
|
||||||
|
|
||||||
|
const PKI_SYNC_LIST_OPTIONS = {
|
||||||
|
[PkiSync.AzureKeyVault]: AZURE_KEY_VAULT_PKI_SYNC_LIST_OPTION
|
||||||
|
};
|
||||||
|
|
||||||
export const enterprisePkiSyncCheck = async (
|
export const enterprisePkiSyncCheck = async (
|
||||||
licenseService: Pick<TLicenseServiceFactory, "getPlan">,
|
licenseService: Pick<TLicenseServiceFactory, "getPlan">,
|
||||||
orgId: string,
|
orgId: string,
|
||||||
@@ -26,7 +31,7 @@ export const enterprisePkiSyncCheck = async (
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const listPkiSyncOptions = () => {
|
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> => {
|
export const matchesSchema = <T extends ZodSchema>(schema: T, data: unknown): data is z.infer<T> => {
|
||||||
|
|||||||
@@ -216,7 +216,6 @@ export const pkiSyncQueueFactory = ({
|
|||||||
encryptedCertificate
|
encryptedCertificate
|
||||||
});
|
});
|
||||||
|
|
||||||
// Create certificate secret record with encrypted private key (if available)
|
|
||||||
if (certData.privateKey) {
|
if (certData.privateKey) {
|
||||||
const { cipherTextBlob: encryptedPrivateKey } = await kmsEncryptor({
|
const { cipherTextBlob: encryptedPrivateKey } = await kmsEncryptor({
|
||||||
plainText: Buffer.from(certData.privateKey)
|
plainText: Buffer.from(certData.privateKey)
|
||||||
@@ -231,7 +230,6 @@ export const pkiSyncQueueFactory = ({
|
|||||||
logger.info(`Successfully created certificate ${certData.name} with ID ${createdCert.id}`);
|
logger.info(`Successfully created certificate ${certData.name} with ID ${createdCert.id}`);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(`Failed to create certificate ${certData.name}: ${String(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) {
|
for (const certificate of certificates) {
|
||||||
try {
|
try {
|
||||||
// Only sync certificates issued by Infisical (not imported ones)
|
// Only sync certificates issued by Infisical (not imported ones)
|
||||||
// Imported certificates don't have caId and certificateTemplateId
|
|
||||||
if (!certificate.caId) {
|
if (!certificate.caId) {
|
||||||
logger.debug(
|
logger.debug(
|
||||||
{ certificateId: certificate.id, subscriberId },
|
{ certificateId: certificate.id, subscriberId },
|
||||||
@@ -503,15 +500,14 @@ export const pkiSyncQueueFactory = ({
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const {
|
const {
|
||||||
connection: { orgId, encryptedCredentials },
|
connection: { orgId, encryptedCredentials, projectId: appConnectionProjectId }
|
||||||
projectId
|
|
||||||
} = pkiSync;
|
} = pkiSync;
|
||||||
|
|
||||||
const credentials = await decryptAppConnectionCredentials({
|
const credentials = await decryptAppConnectionCredentials({
|
||||||
orgId,
|
orgId,
|
||||||
encryptedCredentials,
|
encryptedCredentials,
|
||||||
kmsService,
|
kmsService,
|
||||||
projectId
|
projectId: appConnectionProjectId
|
||||||
});
|
});
|
||||||
|
|
||||||
const pkiSyncWithCredentials = {
|
const pkiSyncWithCredentials = {
|
||||||
@@ -564,7 +560,6 @@ export const pkiSyncQueueFactory = ({
|
|||||||
if (err instanceof PkiSyncError && !err.shouldRetry) {
|
if (err instanceof PkiSyncError && !err.shouldRetry) {
|
||||||
isFinalAttempt = true;
|
isFinalAttempt = true;
|
||||||
} else {
|
} else {
|
||||||
// re-throw so job fails
|
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
@@ -630,15 +625,14 @@ export const pkiSyncQueueFactory = ({
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const {
|
const {
|
||||||
connection: { orgId, encryptedCredentials },
|
connection: { orgId, encryptedCredentials, projectId: appConnectionProjectId }
|
||||||
projectId
|
|
||||||
} = pkiSync;
|
} = pkiSync;
|
||||||
|
|
||||||
const credentials = await decryptAppConnectionCredentials({
|
const credentials = await decryptAppConnectionCredentials({
|
||||||
orgId,
|
orgId,
|
||||||
encryptedCredentials,
|
encryptedCredentials,
|
||||||
kmsService,
|
kmsService,
|
||||||
projectId
|
projectId: appConnectionProjectId
|
||||||
});
|
});
|
||||||
|
|
||||||
await $importCertificates({
|
await $importCertificates({
|
||||||
@@ -673,7 +667,6 @@ export const pkiSyncQueueFactory = ({
|
|||||||
if (err instanceof PkiSyncError && !err.shouldRetry) {
|
if (err instanceof PkiSyncError && !err.shouldRetry) {
|
||||||
isFinalAttempt = true;
|
isFinalAttempt = true;
|
||||||
} else {
|
} else {
|
||||||
// re-throw so job fails
|
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
@@ -746,14 +739,14 @@ export const pkiSyncQueueFactory = ({
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const {
|
const {
|
||||||
connection: { orgId, encryptedCredentials }
|
connection: { orgId, encryptedCredentials, projectId: appConnectionProjectId }
|
||||||
} = pkiSync;
|
} = pkiSync;
|
||||||
|
|
||||||
const credentials = await decryptAppConnectionCredentials({
|
const credentials = await decryptAppConnectionCredentials({
|
||||||
orgId,
|
orgId,
|
||||||
encryptedCredentials,
|
encryptedCredentials,
|
||||||
kmsService,
|
kmsService,
|
||||||
projectId: pkiSync.projectId
|
projectId: appConnectionProjectId
|
||||||
});
|
});
|
||||||
|
|
||||||
const certificateMap = await $getInfisicalCertificates(pkiSync);
|
const certificateMap = await $getInfisicalCertificates(pkiSync);
|
||||||
@@ -797,7 +790,6 @@ export const pkiSyncQueueFactory = ({
|
|||||||
if (err instanceof PkiSyncError && !err.shouldRetry) {
|
if (err instanceof PkiSyncError && !err.shouldRetry) {
|
||||||
isFinalAttempt = true;
|
isFinalAttempt = true;
|
||||||
} else {
|
} else {
|
||||||
// re-throw so job fails
|
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
@@ -880,7 +872,6 @@ export const pkiSyncQueueFactory = ({
|
|||||||
errorMessage = lastRemoveMessage || null;
|
errorMessage = lastRemoveMessage || null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Log notification for now - actual email sending would require SMTP configuration
|
|
||||||
if (projectAdmins.length > 0) {
|
if (projectAdmins.length > 0) {
|
||||||
logger.info(
|
logger.info(
|
||||||
`PKI Sync ${action} failure notification would be sent to ${projectAdmins.length} admin(s) for sync "${name}" in project "${project.name}". Error: ${errorMessage}`
|
`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)
|
appConnectionApp: z.string().max(255)
|
||||||
});
|
});
|
||||||
|
|
||||||
// Schema for PKI sync details (includes app connection info)
|
|
||||||
export const PkiSyncDetailsSchema = PkiSyncSchema.extend({
|
export const PkiSyncDetailsSchema = PkiSyncSchema.extend({
|
||||||
appConnectionName: z.string().max(255),
|
appConnectionName: z.string().max(255),
|
||||||
appConnectionApp: 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
|
// Validates permission to connect and app is valid for sync destination
|
||||||
await appConnectionService.connectAppConnectionById(destinationApp, connectionId, actor);
|
await appConnectionService.connectAppConnectionById(destinationApp, connectionId, actor);
|
||||||
|
|
||||||
|
const defaultSyncOptions = {
|
||||||
|
canImportCertificates: false,
|
||||||
|
canRemoveCertificates: true,
|
||||||
|
...syncOptions
|
||||||
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const pkiSync = await pkiSyncDAL.create({
|
const pkiSync = await pkiSyncDAL.create({
|
||||||
name,
|
name,
|
||||||
@@ -110,7 +116,7 @@ export const pkiSyncServiceFactory = ({
|
|||||||
destination,
|
destination,
|
||||||
isAutoSyncEnabled,
|
isAutoSyncEnabled,
|
||||||
destinationConfig,
|
destinationConfig,
|
||||||
syncOptions,
|
syncOptions: defaultSyncOptions,
|
||||||
subscriberId,
|
subscriberId,
|
||||||
connectionId,
|
connectionId,
|
||||||
projectId,
|
projectId,
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
---
|
||||||
|
title: "Create"
|
||||||
|
openapi: "POST /api/v1/pki-syncs"
|
||||||
|
---
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
---
|
||||||
|
title: "Delete"
|
||||||
|
openapi: "DELETE /api/v1/pki-syncs/{pkiSyncId}"
|
||||||
|
---
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
---
|
||||||
|
title: "Get by ID"
|
||||||
|
openapi: "GET /api/v1/pki-syncs/{pkiSyncId}"
|
||||||
|
---
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
---
|
||||||
|
title: "Import Certificates"
|
||||||
|
openapi: "POST /api/v1/pki-syncs/{pkiSyncId}/import"
|
||||||
|
---
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
---
|
||||||
|
title: "List"
|
||||||
|
openapi: "GET /api/v1/pki-syncs"
|
||||||
|
---
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
---
|
||||||
|
title: "Remove Certificates"
|
||||||
|
openapi: "POST /api/v1/pki-syncs/{pkiSyncId}/remove"
|
||||||
|
---
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
---
|
||||||
|
title: "Sync Certificates"
|
||||||
|
openapi: "POST /api/v1/pki-syncs/{pkiSyncId}/sync"
|
||||||
|
---
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
---
|
||||||
|
title: "Update"
|
||||||
|
openapi: "PATCH /api/v1/pki-syncs/{pkiSyncId}"
|
||||||
|
---
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
---
|
||||||
|
title: "List PKI Syncs"
|
||||||
|
openapi: "GET /api/v1/pki-syncs"
|
||||||
|
---
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
---
|
||||||
|
title: "Options"
|
||||||
|
openapi: "GET /api/v1/pki-syncs/options"
|
||||||
|
---
|
||||||
@@ -711,6 +711,19 @@
|
|||||||
"documentation/platform/pki/pki-issuer",
|
"documentation/platform/pki/pki-issuer",
|
||||||
"documentation/platform/pki/integration-guides/gloo-mesh"
|
"documentation/platform/pki/integration-guides/gloo-mesh"
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"group": "Certificate Syncs",
|
||||||
|
"pages": [
|
||||||
|
"documentation/platform/pki/certificate-syncs",
|
||||||
|
"documentation/platform/pki/certificate-syncs/overview",
|
||||||
|
{
|
||||||
|
"group": "Syncs",
|
||||||
|
"pages": [
|
||||||
|
"documentation/platform/pki/certificate-syncs/azure-key-vault"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -2475,6 +2488,26 @@
|
|||||||
"api-reference/endpoints/pki-alerts/update",
|
"api-reference/endpoints/pki-alerts/update",
|
||||||
"api-reference/endpoints/pki-alerts/delete"
|
"api-reference/endpoints/pki-alerts/delete"
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"group": "Certificate Syncs",
|
||||||
|
"pages": [
|
||||||
|
"api-reference/endpoints/certificate-syncs/list",
|
||||||
|
"api-reference/endpoints/certificate-syncs/options",
|
||||||
|
{
|
||||||
|
"group": "Azure Key Vault",
|
||||||
|
"pages": [
|
||||||
|
"api-reference/endpoints/certificate-syncs/azure-key-vault/list",
|
||||||
|
"api-reference/endpoints/certificate-syncs/azure-key-vault/get-by-id",
|
||||||
|
"api-reference/endpoints/certificate-syncs/azure-key-vault/create",
|
||||||
|
"api-reference/endpoints/certificate-syncs/azure-key-vault/update",
|
||||||
|
"api-reference/endpoints/certificate-syncs/azure-key-vault/delete",
|
||||||
|
"api-reference/endpoints/certificate-syncs/azure-key-vault/sync-certificates",
|
||||||
|
"api-reference/endpoints/certificate-syncs/azure-key-vault/import-certificates",
|
||||||
|
"api-reference/endpoints/certificate-syncs/azure-key-vault/remove-certificates"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@@ -2614,9 +2647,9 @@
|
|||||||
"href": "https://infisical.com"
|
"href": "https://infisical.com"
|
||||||
},
|
},
|
||||||
"api": {
|
"api": {
|
||||||
"openapi": "https://app.infisical.com/api/docs/json",
|
"openapi": "https://5e8f77f30103.ngrok-free.app/api/docs/json",
|
||||||
"mdx": {
|
"mdx": {
|
||||||
"server": ["https://app.infisical.com"]
|
"server": ["https://5e8f77f30103.ngrok-free.app"]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"appearance": {
|
"appearance": {
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
---
|
||||||
|
sidebarTitle: "Explore Options"
|
||||||
|
description: "Browse and search through all available certificate syncs for Infisical PKI."
|
||||||
|
---
|
||||||
|
|
||||||
|
import { CertificateSyncsBrowser } from "/snippets/CertificateSyncsBrowser.jsx";
|
||||||
|
|
||||||
|
<CertificateSyncsBrowser />
|
||||||
@@ -0,0 +1,136 @@
|
|||||||
|
---
|
||||||
|
title: "Azure Key Vault"
|
||||||
|
description: "Learn how to configure an Azure Key Vault Certificate Sync for Infisical PKI."
|
||||||
|
---
|
||||||
|
|
||||||
|
**Prerequisites:**
|
||||||
|
|
||||||
|
- Set up and configure a [Certificate Authority](/documentation/platform/pki/overview)
|
||||||
|
- Create an [Azure Key Vault Connection](/integrations/app-connections/azure-key-vault)
|
||||||
|
- Ensure your network security policies allow incoming requests from Infisical to this certificate sync provider, if network restrictions apply.
|
||||||
|
|
||||||
|
<Note>
|
||||||
|
The Azure Key Vault Certificate Sync requires the following certificate permissions to be set on the user / service principal
|
||||||
|
for Infisical to sync certificates to Azure Key Vault: `certificates/list`, `certificates/get`, `certificates/import`, `certificates/delete`.
|
||||||
|
|
||||||
|
Any role with these permissions would work such as the **Key Vault Certificates Officer** role.
|
||||||
|
</Note>
|
||||||
|
|
||||||
|
<Note>
|
||||||
|
Certificates synced to Azure Key Vault will be stored as certificate objects, preserving both the certificate and private key components.
|
||||||
|
</Note>
|
||||||
|
|
||||||
|
<Tabs>
|
||||||
|
<Tab title="Infisical UI">
|
||||||
|
1. Navigate to **Project** > **Integrations** and select the **Certificate Syncs** tab. Click on the **Add Sync** button.
|
||||||
|

|
||||||
|
|
||||||
|
2. Select the **Azure Key Vault** option.
|
||||||
|

|
||||||
|
|
||||||
|
3. Configure the **Source** from where certificates should be retrieved, then click **Next**.
|
||||||
|

|
||||||
|
|
||||||
|
- **PKI Subscriber**: The PKI subscriber to retrieve certificates from.
|
||||||
|
|
||||||
|
4. Configure the **Destination** to where certificates should be deployed, then click **Next**.
|
||||||
|

|
||||||
|
|
||||||
|
- **Azure Connection**: The Azure Connection to authenticate with.
|
||||||
|
- **Vault Base URL**: The URL of your Azure Key Vault.
|
||||||
|
<p class="height:1px" />
|
||||||
|
|
||||||
|
5. Configure the **Sync Options** to specify how certificates should be synced, then click **Next**.
|
||||||
|

|
||||||
|
|
||||||
|
- **Auto-Sync Enabled**: If enabled, certificates will automatically be synced from the source PKI subscriber when changes occur. Disable to enforce manual syncing only.
|
||||||
|
- **Enable Certificate Removal**: If enabled, Infisical will remove expired certificates from the destination during sync operations. Disable this option if you intend to manage certificate cleanup manually.
|
||||||
|
|
||||||
|
6. Configure the **Details** of your Azure Key Vault Certificate Sync, then click **Next**.
|
||||||
|

|
||||||
|
|
||||||
|
- **Name**: The name of your sync. Must be slug-friendly.
|
||||||
|
- **Description**: An optional description for your sync.
|
||||||
|
|
||||||
|
7. Review your Azure Key Vault Certificate Sync configuration, then click **Create Sync**.
|
||||||
|

|
||||||
|
|
||||||
|
8. If enabled, your Azure Key Vault Certificate Sync will begin syncing your certificates to the destination endpoint.
|
||||||
|

|
||||||
|
|
||||||
|
</Tab>
|
||||||
|
<Tab title="API">
|
||||||
|
To create an **Azure Key Vault Certificate Sync**, make an API request to the [Create Azure Key Vault Certificate Sync](/api-reference/endpoints/certificate-syncs/azure-key-vault/create) API endpoint.
|
||||||
|
|
||||||
|
### Sample request
|
||||||
|
|
||||||
|
```bash Request
|
||||||
|
curl --request POST \
|
||||||
|
--url https://app.infisical.com/api/v1/pki-syncs \
|
||||||
|
--header 'Content-Type: application/json' \
|
||||||
|
--data '{
|
||||||
|
"name": "my-key-vault-cert-sync",
|
||||||
|
"projectId": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
|
||||||
|
"description": "an example certificate sync",
|
||||||
|
"connectionId": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
|
||||||
|
"subscriberId": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
|
||||||
|
"destination": "azure-key-vault",
|
||||||
|
"isAutoSyncEnabled": true,
|
||||||
|
"syncOptions": {
|
||||||
|
"canRemoveCertificates": true
|
||||||
|
},
|
||||||
|
"destinationConfig": {
|
||||||
|
"vaultBaseUrl": "https://my-key-vault.vault.azure.net"
|
||||||
|
}
|
||||||
|
}'
|
||||||
|
```
|
||||||
|
|
||||||
|
### Sample response
|
||||||
|
|
||||||
|
```json Response
|
||||||
|
{
|
||||||
|
"pkiSync": {
|
||||||
|
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
|
||||||
|
"name": "my-key-vault-cert-sync",
|
||||||
|
"description": "an example certificate sync",
|
||||||
|
"destination": "azure-key-vault",
|
||||||
|
"isAutoSyncEnabled": true,
|
||||||
|
"destinationConfig": {
|
||||||
|
"vaultBaseUrl": "https://my-key-vault.vault.azure.net"
|
||||||
|
},
|
||||||
|
"syncOptions": {
|
||||||
|
"canRemoveCertificates": true
|
||||||
|
},
|
||||||
|
"projectId": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
|
||||||
|
"subscriberId": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
|
||||||
|
"connectionId": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
|
||||||
|
"createdAt": "2023-01-01T00:00:00.000Z",
|
||||||
|
"updatedAt": "2023-01-01T00:00:00.000Z"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
</Tab>
|
||||||
|
</Tabs>
|
||||||
|
|
||||||
|
## Certificate Management
|
||||||
|
|
||||||
|
Your Azure Key Vault Certificate Sync will:
|
||||||
|
|
||||||
|
- **Automatic Deployment**: Deploy new certificates issued by your PKI subscriber to Azure Key Vault
|
||||||
|
- **Certificate Updates**: Update certificates in Azure Key Vault when renewals occur
|
||||||
|
- **Expiration Handling**: Optionally remove expired certificates from Azure Key Vault (if enabled)
|
||||||
|
- **Format Preservation**: Maintain certificate format and metadata during sync operations
|
||||||
|
|
||||||
|
<Note>
|
||||||
|
Azure Key Vault Certificate Syncs support both automatic and manual synchronization modes. When auto-sync is enabled, certificates are automatically deployed as they are issued or renewed.
|
||||||
|
</Note>
|
||||||
|
|
||||||
|
## Manual Certificate Import
|
||||||
|
|
||||||
|
You can manually import existing certificates from your PKI subscriber to Azure Key Vault using the import certificates functionality. This is useful for:
|
||||||
|
|
||||||
|
- Initial setup when you have existing certificates to migrate
|
||||||
|
- One-time imports of specific certificates
|
||||||
|
- Testing certificate sync configurations
|
||||||
|
|
||||||
|
To manually import certificates, use the [Import Certificates](/api-reference/endpoints/certificate-syncs/azure-key-vault/import) API endpoint or the manual import option in the Infisical UI.
|
||||||
@@ -0,0 +1,118 @@
|
|||||||
|
---
|
||||||
|
sidebarTitle: "Overview"
|
||||||
|
description: "Learn how to sync certificates from Infisical PKI to third-party services."
|
||||||
|
---
|
||||||
|
|
||||||
|
Certificate Syncs enable you to sync certificates from Infisical PKI to third-party services using [App Connections](/integrations/app-connections/overview).
|
||||||
|
|
||||||
|
<Note>
|
||||||
|
Certificate Syncs are designed to automatically deploy certificates issued by your Certificate Authority to external services, ensuring your certificates are always up-to-date across your infrastructure.
|
||||||
|
</Note>
|
||||||
|
|
||||||
|
## Concept
|
||||||
|
|
||||||
|
Certificate Syncs are a project-level resource used to sync certificates, via an [App Connection](/integrations/app-connections/overview), from a particular PKI subscriber (source)
|
||||||
|
to a third-party service (destination). When new certificates are issued or existing certificates are renewed, changes will automatically be propagated to the destination, ensuring
|
||||||
|
your certificates are always current.
|
||||||
|
|
||||||
|
<br />
|
||||||
|
|
||||||
|
<div align="center">
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
%%{init: {'flowchart': {'curve': 'linear'} } }%%
|
||||||
|
graph LR
|
||||||
|
A[App Connection]
|
||||||
|
B[Certificate Sync]
|
||||||
|
C[Certificate 1]
|
||||||
|
D[Certificate 2]
|
||||||
|
E[Certificate 3]
|
||||||
|
F[Third-Party Service]
|
||||||
|
G[Certificate 1]
|
||||||
|
H[Certificate 2]
|
||||||
|
I[Certificate 3]
|
||||||
|
J[PKI Subscriber]
|
||||||
|
|
||||||
|
B --> A
|
||||||
|
C --> J
|
||||||
|
D --> J
|
||||||
|
E --> J
|
||||||
|
A --> F
|
||||||
|
F --> G
|
||||||
|
F --> H
|
||||||
|
F --> I
|
||||||
|
J --> B
|
||||||
|
|
||||||
|
classDef default fill:#ffffff,stroke:#666,stroke-width:2px,rx:10px,color:black
|
||||||
|
classDef connection fill:#FFF2B2,stroke:#E6C34A,stroke-width:2px,color:black,rx:15px
|
||||||
|
classDef certificate fill:#E6F4FF,stroke:#0096D6,stroke-width:2px,color:black,rx:15px
|
||||||
|
classDef sync fill:#F4FFE6,stroke:#96D600,stroke-width:2px,color:black,rx:15px
|
||||||
|
classDef service fill:#E6E6FF,stroke:#6B4E96,stroke-width:2px,color:black,rx:15px
|
||||||
|
classDef subscriber fill:#FFE6E6,stroke:#D63F3F,stroke-width:2px,color:black,rx:15px
|
||||||
|
|
||||||
|
class A connection
|
||||||
|
class B sync
|
||||||
|
class C,D,E,G,H,I certificate
|
||||||
|
class F service
|
||||||
|
class J subscriber
|
||||||
|
```
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
## Workflow
|
||||||
|
|
||||||
|
Configuring a Certificate Sync requires three components: a <strong>source</strong> PKI subscriber to retrieve certificates from,
|
||||||
|
a <strong>destination</strong> endpoint to deploy certificates to, and <strong>configuration options</strong> to determine how your certificates
|
||||||
|
should be synced. Follow these steps to start syncing:
|
||||||
|
|
||||||
|
<Note>
|
||||||
|
For step-by-step guides on syncing to a particular third-party service, refer to the Certificate Syncs section in the Navigation Bar.
|
||||||
|
</Note>
|
||||||
|
|
||||||
|
1. <strong>Create App Connection:</strong> If you have not already done so, create an [App Connection](/integrations/app-connections/overview)
|
||||||
|
via the UI or API for the third-party service you intend to sync certificates to.
|
||||||
|
|
||||||
|
2. <strong>Create Certificate Sync:</strong> Configure a Certificate Sync in the desired project by specifying the following parameters via the UI or API:
|
||||||
|
- <strong>Source:</strong> The PKI subscriber you wish to retrieve certificates from.
|
||||||
|
- <strong>Destination:</strong> The App Connection to utilize and the destination endpoint to deploy certificates to. These can vary between services.
|
||||||
|
- <strong>Options:</strong> Customize how certificates should be synced, such as whether or not certificates should be removed from the destination when they expire.
|
||||||
|
|
||||||
|
<Note>
|
||||||
|
Certificate Syncs are the source of truth for connected third-party services. Any certificate,
|
||||||
|
including associated data, not present or managed by Infisical before syncing will be
|
||||||
|
overwritten, and changes made directly in the connected service outside of Infisical may also
|
||||||
|
be overwritten by future syncs.
|
||||||
|
</Note>
|
||||||
|
|
||||||
|
<Info>
|
||||||
|
Some third-party services do not support removing expired certificates automatically.
|
||||||
|
</Info>
|
||||||
|
|
||||||
|
3. <strong>Utilize Sync:</strong> Any new certificates issued or renewals from the source PKI subscriber will now automatically be propagated to the destination endpoint.
|
||||||
|
|
||||||
|
<Note>
|
||||||
|
Infisical is continuously expanding its Certificate Sync third-party service support. If the service you need isn't available,
|
||||||
|
contact us at [email protected] to make a request.
|
||||||
|
</Note>
|
||||||
|
|
||||||
|
## Certificate Management
|
||||||
|
|
||||||
|
Certificate Syncs handle the full lifecycle of certificate management:
|
||||||
|
|
||||||
|
- **Automatic Deployment**: New certificates are automatically deployed to configured destinations
|
||||||
|
- **Renewal Propagation**: Certificate renewals are seamlessly pushed to all connected services
|
||||||
|
- **Expiration Handling**: Expired certificates can be automatically removed from destinations (service-dependent)
|
||||||
|
- **Certificate Validation**: Certificates are validated before deployment to ensure integrity
|
||||||
|
|
||||||
|
<div align="center">
|
||||||
|
```mermaid
|
||||||
|
graph LR
|
||||||
|
A[Certificate Issued] -->|Deploy| B[Destination Service]
|
||||||
|
C[Certificate Renewed] -->|Update| B
|
||||||
|
D[Certificate Expired] -->|Remove| B
|
||||||
|
style B fill:#F4FFE6,stroke:#96D600,stroke-width:2px,color:black,rx:15px
|
||||||
|
style A fill:#E6F4FF,stroke:#0096D6,stroke-width:2px,color:black,rx:15px
|
||||||
|
style C fill:#E6F4FF,stroke:#0096D6,stroke-width:2px,color:black,rx:15px
|
||||||
|
style D fill:#FFE6E6,stroke:#D63F3F,stroke-width:2px,color:black,rx:15px
|
||||||
|
```
|
||||||
|
</div>
|
||||||
|
After Width: | Height: | Size: 470 KiB |
|
After Width: | Height: | Size: 206 KiB |
|
After Width: | Height: | Size: 525 KiB |
|
After Width: | Height: | Size: 522 KiB |
|
After Width: | Height: | Size: 520 KiB |
|
After Width: | Height: | Size: 544 KiB |
|
After Width: | Height: | Size: 498 KiB |
|
After Width: | Height: | Size: 807 KiB |
|
After Width: | Height: | Size: 782 KiB |
@@ -23,7 +23,8 @@ export const DeletePkiSyncModal = ({ isOpen, onOpenChange, pkiSync, onComplete }
|
|||||||
try {
|
try {
|
||||||
await deleteSync.mutateAsync({
|
await deleteSync.mutateAsync({
|
||||||
syncId,
|
syncId,
|
||||||
projectId
|
projectId,
|
||||||
|
destination
|
||||||
});
|
});
|
||||||
|
|
||||||
createNotification({
|
createNotification({
|
||||||
|
|||||||
@@ -24,7 +24,8 @@ const Content = ({ pkiSync, onComplete }: ContentProps) => {
|
|||||||
try {
|
try {
|
||||||
await triggerImportCertificates.mutateAsync({
|
await triggerImportCertificates.mutateAsync({
|
||||||
syncId,
|
syncId,
|
||||||
projectId
|
projectId,
|
||||||
|
destination
|
||||||
});
|
});
|
||||||
|
|
||||||
createNotification({
|
createNotification({
|
||||||
|
|||||||
@@ -24,7 +24,8 @@ const Content = ({ pkiSync, onComplete }: ContentProps) => {
|
|||||||
try {
|
try {
|
||||||
await triggerRemoveCertificates.mutateAsync({
|
await triggerRemoveCertificates.mutateAsync({
|
||||||
syncId,
|
syncId,
|
||||||
projectId
|
projectId,
|
||||||
|
destination
|
||||||
});
|
});
|
||||||
|
|
||||||
createNotification({
|
createNotification({
|
||||||
|
|||||||
@@ -10,13 +10,7 @@ import { createNotification } from "@app/components/notifications";
|
|||||||
import { Button, FormControl, Switch } from "@app/components/v2";
|
import { Button, FormControl, Switch } from "@app/components/v2";
|
||||||
import { useProject } from "@app/context";
|
import { useProject } from "@app/context";
|
||||||
import { PKI_SYNC_MAP } from "@app/helpers/pkiSyncs";
|
import { PKI_SYNC_MAP } from "@app/helpers/pkiSyncs";
|
||||||
import {
|
import { PkiSync, TPkiSync, useCreatePkiSync, usePkiSyncOption } from "@app/hooks/api/pkiSyncs";
|
||||||
PkiSync,
|
|
||||||
TCreatePkiSyncDTO,
|
|
||||||
TPkiSync,
|
|
||||||
useCreatePkiSync,
|
|
||||||
usePkiSyncOption
|
|
||||||
} from "@app/hooks/api/pkiSyncs";
|
|
||||||
|
|
||||||
import { PkiSyncDestinationFields } from "./PkiSyncDestinationFields";
|
import { PkiSyncDestinationFields } from "./PkiSyncDestinationFields";
|
||||||
import { PkiSyncDetailsFields } from "./PkiSyncDetailsFields";
|
import { PkiSyncDetailsFields } from "./PkiSyncDetailsFields";
|
||||||
@@ -69,10 +63,7 @@ export const CreatePkiSyncForm = ({ destination, onComplete, onCancel }: Props)
|
|||||||
...formData,
|
...formData,
|
||||||
connectionId: connection.id,
|
connectionId: connection.id,
|
||||||
projectId: currentProject.id,
|
projectId: currentProject.id,
|
||||||
destinationConfig: {
|
destinationConfig
|
||||||
destination,
|
|
||||||
config: destinationConfig
|
|
||||||
} as unknown as TCreatePkiSyncDTO["destinationConfig"]
|
|
||||||
});
|
});
|
||||||
|
|
||||||
createNotification({
|
createNotification({
|
||||||
|
|||||||
@@ -43,7 +43,8 @@ export const EditPkiSyncForm = ({ pkiSync, fields, onComplete }: Props) => {
|
|||||||
syncId: pkiSync.id,
|
syncId: pkiSync.id,
|
||||||
...formData,
|
...formData,
|
||||||
connectionId: connection.id,
|
connectionId: connection.id,
|
||||||
projectId: pkiSync.projectId
|
projectId: pkiSync.projectId,
|
||||||
|
destination: pkiSync.destination
|
||||||
});
|
});
|
||||||
|
|
||||||
createNotification({
|
createNotification({
|
||||||
|
|||||||
@@ -12,6 +12,8 @@ export const PkiSyncOptionsFields = () => {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<p className="mb-4 text-sm text-bunker-300">Configure how certificates should be synced.</p>
|
<p className="mb-4 text-sm text-bunker-300">Configure how certificates should be synced.</p>
|
||||||
|
{/*
|
||||||
|
TODO: Re-enable this when we have a way to import certificates
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name="syncOptions.canImportCertificates"
|
name="syncOptions.canImportCertificates"
|
||||||
@@ -48,6 +50,7 @@ export const PkiSyncOptionsFields = () => {
|
|||||||
</FormControl>
|
</FormControl>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
*/}
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name="syncOptions.canRemoveCertificates"
|
name="syncOptions.canRemoveCertificates"
|
||||||
|
|||||||
@@ -70,6 +70,7 @@ export const PkiSyncReviewFields = () => {
|
|||||||
<GenericFieldLabel label="Upload Certificates">
|
<GenericFieldLabel label="Upload Certificates">
|
||||||
<Badge variant="success">Always Enabled</Badge>
|
<Badge variant="success">Always Enabled</Badge>
|
||||||
</GenericFieldLabel>
|
</GenericFieldLabel>
|
||||||
|
{/* Hidden for now - Import certificates functionality disabled
|
||||||
{syncOptions?.canImportCertificates !== undefined && (
|
{syncOptions?.canImportCertificates !== undefined && (
|
||||||
<GenericFieldLabel label="Import Certificates">
|
<GenericFieldLabel label="Import Certificates">
|
||||||
<Badge variant={syncOptions.canImportCertificates ? "success" : "danger"}>
|
<Badge variant={syncOptions.canImportCertificates ? "success" : "danger"}>
|
||||||
@@ -77,6 +78,7 @@ export const PkiSyncReviewFields = () => {
|
|||||||
</Badge>
|
</Badge>
|
||||||
</GenericFieldLabel>
|
</GenericFieldLabel>
|
||||||
)}
|
)}
|
||||||
|
*/}
|
||||||
{syncOptions?.canRemoveCertificates !== undefined && (
|
{syncOptions?.canRemoveCertificates !== undefined && (
|
||||||
<GenericFieldLabel label="Remove Certificates">
|
<GenericFieldLabel label="Remove Certificates">
|
||||||
<Badge variant={syncOptions.canRemoveCertificates ? "success" : "danger"}>
|
<Badge variant={syncOptions.canRemoveCertificates ? "success" : "danger"}>
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ export const PkiSyncFormSchema = z.object({
|
|||||||
vaultBaseUrl: z.string().url("Valid URL is required")
|
vaultBaseUrl: z.string().url("Valid URL is required")
|
||||||
}),
|
}),
|
||||||
syncOptions: z.object({
|
syncOptions: z.object({
|
||||||
canImportCertificates: z.boolean().default(true),
|
canImportCertificates: z.boolean().default(false),
|
||||||
canRemoveCertificates: z.boolean().default(true)
|
canRemoveCertificates: z.boolean().default(true)
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -15,8 +15,11 @@ import {
|
|||||||
export const useCreatePkiSync = () => {
|
export const useCreatePkiSync = () => {
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
return useMutation({
|
return useMutation({
|
||||||
mutationFn: async (params: TCreatePkiSyncDTO) => {
|
mutationFn: async ({ destination, ...params }: TCreatePkiSyncDTO) => {
|
||||||
const { data } = await apiRequest.post<TPkiSyncResponse>("/api/v1/pki-syncs", params);
|
const { data } = await apiRequest.post<TPkiSyncResponse>(
|
||||||
|
`/api/v1/pki-syncs/${destination}`,
|
||||||
|
params
|
||||||
|
);
|
||||||
|
|
||||||
return data.pkiSync;
|
return data.pkiSync;
|
||||||
},
|
},
|
||||||
@@ -28,9 +31,9 @@ export const useCreatePkiSync = () => {
|
|||||||
export const useUpdatePkiSync = () => {
|
export const useUpdatePkiSync = () => {
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
return useMutation({
|
return useMutation({
|
||||||
mutationFn: async ({ syncId, projectId, ...params }: TUpdatePkiSyncDTO) => {
|
mutationFn: async ({ syncId, projectId, destination, ...params }: TUpdatePkiSyncDTO) => {
|
||||||
const { data } = await apiRequest.patch<TPkiSyncResponse>(
|
const { data } = await apiRequest.patch<TPkiSyncResponse>(
|
||||||
`/api/v1/pki-syncs/${syncId}`,
|
`/api/v1/pki-syncs/${destination}/${syncId}`,
|
||||||
params,
|
params,
|
||||||
{ params: { projectId } }
|
{ params: { projectId } }
|
||||||
);
|
);
|
||||||
@@ -47,8 +50,8 @@ export const useUpdatePkiSync = () => {
|
|||||||
export const useDeletePkiSync = () => {
|
export const useDeletePkiSync = () => {
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
return useMutation({
|
return useMutation({
|
||||||
mutationFn: async ({ syncId, projectId }: TDeletePkiSyncDTO) => {
|
mutationFn: async ({ syncId, projectId, destination }: TDeletePkiSyncDTO) => {
|
||||||
const { data } = await apiRequest.delete(`/api/v1/pki-syncs/${syncId}`, {
|
const { data } = await apiRequest.delete(`/api/v1/pki-syncs/${destination}/${syncId}`, {
|
||||||
params: { projectId }
|
params: { projectId }
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -64,9 +67,9 @@ export const useDeletePkiSync = () => {
|
|||||||
export const useTriggerPkiSyncSyncCertificates = () => {
|
export const useTriggerPkiSyncSyncCertificates = () => {
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
return useMutation({
|
return useMutation({
|
||||||
mutationFn: async ({ syncId, projectId }: TTriggerPkiSyncSyncCertificatesDTO) => {
|
mutationFn: async ({ syncId, projectId, destination }: TTriggerPkiSyncSyncCertificatesDTO) => {
|
||||||
const { data } = await apiRequest.post(
|
const { data } = await apiRequest.post(
|
||||||
`/api/v1/pki-syncs/${syncId}/sync`,
|
`/api/v1/pki-syncs/${destination}/${syncId}/sync`,
|
||||||
{},
|
{},
|
||||||
{
|
{
|
||||||
params: { projectId }
|
params: { projectId }
|
||||||
@@ -85,9 +88,13 @@ export const useTriggerPkiSyncSyncCertificates = () => {
|
|||||||
export const useTriggerPkiSyncImportCertificates = () => {
|
export const useTriggerPkiSyncImportCertificates = () => {
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
return useMutation({
|
return useMutation({
|
||||||
mutationFn: async ({ syncId, projectId }: TTriggerPkiSyncImportCertificatesDTO) => {
|
mutationFn: async ({
|
||||||
|
syncId,
|
||||||
|
projectId,
|
||||||
|
destination
|
||||||
|
}: TTriggerPkiSyncImportCertificatesDTO) => {
|
||||||
const { data } = await apiRequest.post(
|
const { data } = await apiRequest.post(
|
||||||
`/api/v1/pki-syncs/${syncId}/import`,
|
`/api/v1/pki-syncs/${destination}/${syncId}/import`,
|
||||||
{},
|
{},
|
||||||
{
|
{
|
||||||
params: { projectId }
|
params: { projectId }
|
||||||
@@ -106,9 +113,13 @@ export const useTriggerPkiSyncImportCertificates = () => {
|
|||||||
export const useTriggerPkiSyncRemoveCertificates = () => {
|
export const useTriggerPkiSyncRemoveCertificates = () => {
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
return useMutation({
|
return useMutation({
|
||||||
mutationFn: async ({ syncId, projectId }: TTriggerPkiSyncRemoveCertificatesDTO) => {
|
mutationFn: async ({
|
||||||
|
syncId,
|
||||||
|
projectId,
|
||||||
|
destination
|
||||||
|
}: TTriggerPkiSyncRemoveCertificatesDTO) => {
|
||||||
const { data } = await apiRequest.post(
|
const { data } = await apiRequest.post(
|
||||||
`/api/v1/pki-syncs/${syncId}/remove`,
|
`/api/v1/pki-syncs/${destination}/${syncId}/remove`,
|
||||||
{},
|
{},
|
||||||
{
|
{
|
||||||
params: { projectId }
|
params: { projectId }
|
||||||
|
|||||||
@@ -29,29 +29,34 @@ export type TCreatePkiSyncDTO = DiscriminativePick<
|
|||||||
| "isAutoSyncEnabled"
|
| "isAutoSyncEnabled"
|
||||||
> & { subscriberId?: string; projectId: string };
|
> & { subscriberId?: string; projectId: string };
|
||||||
|
|
||||||
export type TUpdatePkiSyncDTO = Partial<Omit<TCreatePkiSyncDTO, "destination" | "projectId">> & {
|
export type TUpdatePkiSyncDTO = Partial<Omit<TCreatePkiSyncDTO, "projectId">> & {
|
||||||
syncId: string;
|
syncId: string;
|
||||||
projectId: string;
|
projectId: string;
|
||||||
|
destination: PkiSync;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TDeletePkiSyncDTO = {
|
export type TDeletePkiSyncDTO = {
|
||||||
syncId: string;
|
syncId: string;
|
||||||
projectId: string;
|
projectId: string;
|
||||||
|
destination: PkiSync;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TTriggerPkiSyncSyncCertificatesDTO = {
|
export type TTriggerPkiSyncSyncCertificatesDTO = {
|
||||||
syncId: string;
|
syncId: string;
|
||||||
projectId: string;
|
projectId: string;
|
||||||
|
destination: PkiSync;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TTriggerPkiSyncImportCertificatesDTO = {
|
export type TTriggerPkiSyncImportCertificatesDTO = {
|
||||||
syncId: string;
|
syncId: string;
|
||||||
projectId: string;
|
projectId: string;
|
||||||
|
destination: PkiSync;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TTriggerPkiSyncRemoveCertificatesDTO = {
|
export type TTriggerPkiSyncRemoveCertificatesDTO = {
|
||||||
syncId: string;
|
syncId: string;
|
||||||
projectId: string;
|
projectId: string;
|
||||||
|
destination: PkiSync;
|
||||||
};
|
};
|
||||||
|
|
||||||
export * from "./common";
|
export * from "./common";
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ import { ProjectPermissionSub } from "@app/context";
|
|||||||
import { ProjectPermissionPkiSyncActions } from "@app/context/ProjectPermissionContext/types";
|
import { ProjectPermissionPkiSyncActions } from "@app/context/ProjectPermissionContext/types";
|
||||||
import { PKI_SYNC_MAP } from "@app/helpers/pkiSyncs";
|
import { PKI_SYNC_MAP } from "@app/helpers/pkiSyncs";
|
||||||
import { useToggle } from "@app/hooks";
|
import { useToggle } from "@app/hooks";
|
||||||
import { PkiSyncData, PkiSyncStatus, usePkiSyncOption } from "@app/hooks/api/pkiSyncs";
|
import { PkiSyncData, PkiSyncStatus } from "@app/hooks/api/pkiSyncs";
|
||||||
|
|
||||||
import { PkiSyncDestinationCol } from "./PkiSyncDestinationCol";
|
import { PkiSyncDestinationCol } from "./PkiSyncDestinationCol";
|
||||||
import { PkiSyncTableCell } from "./PkiSyncTableCell";
|
import { PkiSyncTableCell } from "./PkiSyncTableCell";
|
||||||
@@ -80,8 +80,6 @@ export const PkiSyncRow = ({
|
|||||||
projectId
|
projectId
|
||||||
} = pkiSync;
|
} = pkiSync;
|
||||||
|
|
||||||
const { syncOption } = usePkiSyncOption(destination);
|
|
||||||
|
|
||||||
const destinationName = PKI_SYNC_MAP[destination].name;
|
const destinationName = PKI_SYNC_MAP[destination].name;
|
||||||
|
|
||||||
const [isIdCopied, setIsIdCopied] = useToggle(false);
|
const [isIdCopied, setIsIdCopied] = useToggle(false);
|
||||||
@@ -296,38 +294,36 @@ export const PkiSyncRow = ({
|
|||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
)}
|
)}
|
||||||
</ProjectPermissionCan>
|
</ProjectPermissionCan>
|
||||||
{syncOption?.canImportCertificates && (
|
<ProjectPermissionCan
|
||||||
<ProjectPermissionCan
|
I={ProjectPermissionPkiSyncActions.ImportCertificates}
|
||||||
I={ProjectPermissionPkiSyncActions.ImportCertificates}
|
a={permissionSubject}
|
||||||
a={permissionSubject}
|
>
|
||||||
>
|
{(isAllowed: boolean) => (
|
||||||
{(isAllowed: boolean) => (
|
<DropdownMenuItem
|
||||||
<DropdownMenuItem
|
icon={<FontAwesomeIcon icon={faDownload} />}
|
||||||
icon={<FontAwesomeIcon icon={faDownload} />}
|
onClick={(e) => {
|
||||||
onClick={(e) => {
|
e.stopPropagation();
|
||||||
e.stopPropagation();
|
onTriggerImportCertificates(pkiSync);
|
||||||
onTriggerImportCertificates(pkiSync);
|
}}
|
||||||
}}
|
isDisabled={!isAllowed}
|
||||||
isDisabled={!isAllowed}
|
>
|
||||||
|
<Tooltip
|
||||||
|
position="left"
|
||||||
|
sideOffset={42}
|
||||||
|
content={`Import certificates from this ${destinationName} destination into Infisical.`}
|
||||||
>
|
>
|
||||||
<Tooltip
|
<div className="flex h-full w-full items-center justify-between gap-1">
|
||||||
position="left"
|
<span>Import Certificates</span>
|
||||||
sideOffset={42}
|
<FontAwesomeIcon
|
||||||
content={`Import certificates from this ${destinationName} destination into Infisical.`}
|
className="text-bunker-300"
|
||||||
>
|
size="sm"
|
||||||
<div className="flex h-full w-full items-center justify-between gap-1">
|
icon={faInfoCircle}
|
||||||
<span>Import Certificates</span>
|
/>
|
||||||
<FontAwesomeIcon
|
</div>
|
||||||
className="text-bunker-300"
|
</Tooltip>
|
||||||
size="sm"
|
</DropdownMenuItem>
|
||||||
icon={faInfoCircle}
|
)}
|
||||||
/>
|
</ProjectPermissionCan>
|
||||||
</div>
|
|
||||||
</Tooltip>
|
|
||||||
</DropdownMenuItem>
|
|
||||||
)}
|
|
||||||
</ProjectPermissionCan>
|
|
||||||
)}
|
|
||||||
<ProjectPermissionCan
|
<ProjectPermissionCan
|
||||||
I={ProjectPermissionPkiSyncActions.RemoveCertificates}
|
I={ProjectPermissionPkiSyncActions.RemoveCertificates}
|
||||||
a={permissionSubject}
|
a={permissionSubject}
|
||||||
|
|||||||
@@ -230,6 +230,7 @@ export const PkiSyncsTable = ({ pkiSyncs }: Props) => {
|
|||||||
await updateSync.mutateAsync({
|
await updateSync.mutateAsync({
|
||||||
syncId: pkiSync.id,
|
syncId: pkiSync.id,
|
||||||
projectId: pkiSync.projectId,
|
projectId: pkiSync.projectId,
|
||||||
|
destination: pkiSync.destination,
|
||||||
isAutoSyncEnabled
|
isAutoSyncEnabled
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -251,7 +252,8 @@ export const PkiSyncsTable = ({ pkiSyncs }: Props) => {
|
|||||||
try {
|
try {
|
||||||
await triggerSync.mutateAsync({
|
await triggerSync.mutateAsync({
|
||||||
syncId: pkiSync.id,
|
syncId: pkiSync.id,
|
||||||
projectId: pkiSync.projectId
|
projectId: pkiSync.projectId,
|
||||||
|
destination: pkiSync.destination
|
||||||
});
|
});
|
||||||
|
|
||||||
createNotification({
|
createNotification({
|
||||||
|
|||||||
@@ -42,7 +42,6 @@ import { PKI_SYNC_MAP } from "@app/helpers/pkiSyncs";
|
|||||||
import { usePopUp, useToggle } from "@app/hooks";
|
import { usePopUp, useToggle } from "@app/hooks";
|
||||||
import {
|
import {
|
||||||
TPkiSync,
|
TPkiSync,
|
||||||
usePkiSyncOption,
|
|
||||||
useTriggerPkiSyncSyncCertificates,
|
useTriggerPkiSyncSyncCertificates,
|
||||||
useUpdatePkiSync
|
useUpdatePkiSync
|
||||||
} from "@app/hooks/api/pkiSyncs";
|
} from "@app/hooks/api/pkiSyncs";
|
||||||
@@ -69,7 +68,6 @@ export const PkiSyncActionTriggers = ({ pkiSync }: Props) => {
|
|||||||
const updatePkiSyncMutation = useUpdatePkiSync();
|
const updatePkiSyncMutation = useUpdatePkiSync();
|
||||||
|
|
||||||
const destinationName = PKI_SYNC_MAP[destination].name;
|
const destinationName = PKI_SYNC_MAP[destination].name;
|
||||||
const { syncOption } = usePkiSyncOption(destination);
|
|
||||||
|
|
||||||
const handleCopyId = useCallback(() => {
|
const handleCopyId = useCallback(() => {
|
||||||
setIsIdCopied.on();
|
setIsIdCopied.on();
|
||||||
@@ -88,7 +86,8 @@ export const PkiSyncActionTriggers = ({ pkiSync }: Props) => {
|
|||||||
try {
|
try {
|
||||||
await triggerSyncMutation.mutateAsync({
|
await triggerSyncMutation.mutateAsync({
|
||||||
syncId: id,
|
syncId: id,
|
||||||
projectId
|
projectId,
|
||||||
|
destination
|
||||||
});
|
});
|
||||||
createNotification({
|
createNotification({
|
||||||
text: "PKI sync job queued successfully",
|
text: "PKI sync job queued successfully",
|
||||||
@@ -108,6 +107,7 @@ export const PkiSyncActionTriggers = ({ pkiSync }: Props) => {
|
|||||||
await updatePkiSyncMutation.mutateAsync({
|
await updatePkiSyncMutation.mutateAsync({
|
||||||
syncId: id,
|
syncId: id,
|
||||||
projectId,
|
projectId,
|
||||||
|
destination,
|
||||||
isAutoSyncEnabled: !pkiSync.isAutoSyncEnabled
|
isAutoSyncEnabled: !pkiSync.isAutoSyncEnabled
|
||||||
});
|
});
|
||||||
createNotification({
|
createNotification({
|
||||||
@@ -192,65 +192,61 @@ export const PkiSyncActionTriggers = ({ pkiSync }: Props) => {
|
|||||||
Copy Sync ID
|
Copy Sync ID
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
|
|
||||||
{syncOption?.canImportCertificates && (
|
<ProjectPermissionCan
|
||||||
<ProjectPermissionCan
|
I={ProjectPermissionPkiSyncActions.ImportCertificates}
|
||||||
I={ProjectPermissionPkiSyncActions.ImportCertificates}
|
a={permissionSubject}
|
||||||
a={permissionSubject}
|
>
|
||||||
>
|
{(isAllowed: boolean) => (
|
||||||
{(isAllowed: boolean) => (
|
<DropdownMenuItem
|
||||||
<DropdownMenuItem
|
icon={<FontAwesomeIcon icon={faDownload} />}
|
||||||
icon={<FontAwesomeIcon icon={faDownload} />}
|
onClick={() => handlePopUpOpen("importCertificates")}
|
||||||
onClick={() => handlePopUpOpen("importCertificates")}
|
isDisabled={!isAllowed}
|
||||||
isDisabled={!isAllowed}
|
>
|
||||||
|
<Tooltip
|
||||||
|
position="left"
|
||||||
|
sideOffset={42}
|
||||||
|
content={`Import certificates from this ${destinationName} destination into Infisical.`}
|
||||||
>
|
>
|
||||||
<Tooltip
|
<div className="flex h-full w-full items-center justify-between gap-1">
|
||||||
position="left"
|
<span>Import Certificates</span>
|
||||||
sideOffset={42}
|
<FontAwesomeIcon
|
||||||
content={`Import certificates from this ${destinationName} destination into Infisical.`}
|
className="text-bunker-300"
|
||||||
>
|
size="sm"
|
||||||
<div className="flex h-full w-full items-center justify-between gap-1">
|
icon={faInfoCircle}
|
||||||
<span>Import Certificates</span>
|
/>
|
||||||
<FontAwesomeIcon
|
</div>
|
||||||
className="text-bunker-300"
|
</Tooltip>
|
||||||
size="sm"
|
</DropdownMenuItem>
|
||||||
icon={faInfoCircle}
|
)}
|
||||||
/>
|
</ProjectPermissionCan>
|
||||||
</div>
|
|
||||||
</Tooltip>
|
|
||||||
</DropdownMenuItem>
|
|
||||||
)}
|
|
||||||
</ProjectPermissionCan>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{syncOption?.canRemoveCertificates && (
|
<ProjectPermissionCan
|
||||||
<ProjectPermissionCan
|
I={ProjectPermissionPkiSyncActions.RemoveCertificates}
|
||||||
I={ProjectPermissionPkiSyncActions.RemoveCertificates}
|
a={permissionSubject}
|
||||||
a={permissionSubject}
|
>
|
||||||
>
|
{(isAllowed: boolean) => (
|
||||||
{(isAllowed: boolean) => (
|
<DropdownMenuItem
|
||||||
<DropdownMenuItem
|
icon={<FontAwesomeIcon icon={faEraser} />}
|
||||||
icon={<FontAwesomeIcon icon={faEraser} />}
|
onClick={() => handlePopUpOpen("removeCertificates")}
|
||||||
onClick={() => handlePopUpOpen("removeCertificates")}
|
isDisabled={!isAllowed}
|
||||||
isDisabled={!isAllowed}
|
>
|
||||||
|
<Tooltip
|
||||||
|
position="left"
|
||||||
|
sideOffset={42}
|
||||||
|
content={`Remove certificates synced by Infisical from this ${destinationName} destination.`}
|
||||||
>
|
>
|
||||||
<Tooltip
|
<div className="flex h-full w-full items-center justify-between gap-1">
|
||||||
position="left"
|
<span>Remove Certificates</span>
|
||||||
sideOffset={42}
|
<FontAwesomeIcon
|
||||||
content={`Remove certificates synced by Infisical from this ${destinationName} destination.`}
|
className="text-bunker-300"
|
||||||
>
|
size="sm"
|
||||||
<div className="flex h-full w-full items-center justify-between gap-1">
|
icon={faInfoCircle}
|
||||||
<span>Remove Certificates</span>
|
/>
|
||||||
<FontAwesomeIcon
|
</div>
|
||||||
className="text-bunker-300"
|
</Tooltip>
|
||||||
size="sm"
|
</DropdownMenuItem>
|
||||||
icon={faInfoCircle}
|
)}
|
||||||
/>
|
</ProjectPermissionCan>
|
||||||
</div>
|
|
||||||
</Tooltip>
|
|
||||||
</DropdownMenuItem>
|
|
||||||
)}
|
|
||||||
</ProjectPermissionCan>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<ProjectPermissionCan I={ProjectPermissionPkiSyncActions.Edit} a={permissionSubject}>
|
<ProjectPermissionCan I={ProjectPermissionPkiSyncActions.Edit} a={permissionSubject}>
|
||||||
{(isAllowed: boolean) => (
|
{(isAllowed: boolean) => (
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ type Props = {
|
|||||||
|
|
||||||
export const PkiSyncOptionsSection = ({ pkiSync, onEditOptions }: Props) => {
|
export const PkiSyncOptionsSection = ({ pkiSync, onEditOptions }: Props) => {
|
||||||
const {
|
const {
|
||||||
syncOptions: { canImportCertificates, canRemoveCertificates }
|
syncOptions: { canRemoveCertificates }
|
||||||
} = pkiSync;
|
} = pkiSync;
|
||||||
|
|
||||||
const permissionSubject = subject(ProjectPermissionSub.PkiSyncs, {
|
const permissionSubject = subject(ProjectPermissionSub.PkiSyncs, {
|
||||||
@@ -44,11 +44,13 @@ export const PkiSyncOptionsSection = ({ pkiSync, onEditOptions }: Props) => {
|
|||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
|
{/* Hidden for now - Import certificates functionality disabled
|
||||||
<GenericFieldLabel label="Certificate Import">
|
<GenericFieldLabel label="Certificate Import">
|
||||||
<Badge variant={canImportCertificates ? "success" : "danger"}>
|
<Badge variant={canImportCertificates ? "success" : "danger"}>
|
||||||
{canImportCertificates ? "Enabled" : "Disabled"}
|
{canImportCertificates ? "Enabled" : "Disabled"}
|
||||||
</Badge>
|
</Badge>
|
||||||
</GenericFieldLabel>
|
</GenericFieldLabel>
|
||||||
|
*/}
|
||||||
<GenericFieldLabel label="Certificate Removal">
|
<GenericFieldLabel label="Certificate Removal">
|
||||||
<Badge variant={canRemoveCertificates ? "success" : "danger"}>
|
<Badge variant={canRemoveCertificates ? "success" : "danger"}>
|
||||||
{canRemoveCertificates ? "Enabled" : "Disabled"}
|
{canRemoveCertificates ? "Enabled" : "Disabled"}
|
||||||
|
|||||||