Add PKI sync AWS Certificate Manager

This commit is contained in:
Carlos Monastyrski
2025-10-01 18:43:35 -03:00
parent c03af5daf2
commit 95df76de5a
47 changed files with 1466 additions and 72 deletions
@@ -0,0 +1,65 @@
import { z } from "zod";
import { PkiSync } from "@app/hooks/api/pkiSyncs";
import { BasePkiSyncSchema } from "./base-pki-sync-schema";
const AwsCertificateManagerSyncOptionsSchema = z.object({
canImportCertificates: z.boolean().default(false),
canRemoveCertificates: z.boolean().default(false),
certificateNameSchema: z
.string()
.optional()
.refine(
(val) => {
// For AWS Certificate Manager, {{certificateId}} is always required if certificateNameSchema is provided
if (!val) return true;
if (!val.includes("{{certificateId}}")) {
return false;
}
const allowedOptionalPlaceholders = ["{{environment}}"];
const allowedPlaceholdersRegexPart = ["{{certificateId}}", ...allowedOptionalPlaceholders]
.map((p) => p.replace(/[-/\\^$*+?.()|[\]{}]/g, "\\$&"))
.join("|");
const allowedContentRegex = new RegExp(
`^([a-zA-Z0-9_\\-/]|${allowedPlaceholdersRegexPart})*$`
);
return allowedContentRegex.test(val);
},
{
message:
"Certificate name schema must include {{certificateId}} placeholder for AWS Certificate Manager."
}
)
});
export const AwsCertificateManagerPkiSyncDestinationSchema = BasePkiSyncSchema(
AwsCertificateManagerSyncOptionsSchema
).merge(
z.object({
destination: z.literal(PkiSync.AwsCertificateManager),
destinationConfig: z.object({
region: z.string().min(1, "AWS region is required")
})
})
);
export const UpdateAwsCertificateManagerPkiSyncDestinationSchema =
AwsCertificateManagerPkiSyncDestinationSchema.partial().merge(
z.object({
name: z
.string()
.trim()
.min(1, "Name is required")
.max(255, "Name must be less than 255 characters"),
destination: z.literal(PkiSync.AwsCertificateManager),
connection: z.object({
id: z.string().uuid("Invalid connection ID format"),
name: z.string().max(255, "Connection name must be less than 255 characters")
})
})
);
@@ -0,0 +1,30 @@
import { z } from "zod";
import { PkiSync } from "@app/hooks/api/pkiSyncs";
import { BasePkiSyncSchema } from "./base-pki-sync-schema";
export const AzureKeyVaultPkiSyncDestinationSchema = BasePkiSyncSchema().merge(
z.object({
destination: z.literal(PkiSync.AzureKeyVault),
destinationConfig: z.object({
vaultBaseUrl: z.string().url("Valid URL is required")
})
})
);
export const UpdateAzureKeyVaultPkiSyncDestinationSchema =
AzureKeyVaultPkiSyncDestinationSchema.partial().merge(
z.object({
name: z
.string()
.trim()
.min(1, "Name is required")
.max(255, "Name must be less than 255 characters"),
destination: z.literal(PkiSync.AzureKeyVault),
connection: z.object({
id: z.string().uuid("Invalid connection ID format"),
name: z.string().max(255, "Connection name must be less than 255 characters")
})
})
);
@@ -0,0 +1,63 @@
import { AnyZodObject, z } from "zod";
export const BasePkiSyncSchema = <T extends AnyZodObject | undefined = undefined>(
additionalSyncOptions?: T
) => {
const baseSyncOptionsSchema = z.object({
canImportCertificates: z.boolean().default(false),
canRemoveCertificates: z.boolean().default(false),
certificateNameSchema: z
.string()
.optional()
.refine(
(val) => {
if (!val) return true;
const allowedOptionalPlaceholders = ["{{environment}}"];
const allowedPlaceholdersRegexPart = ["{{certificateId}}", ...allowedOptionalPlaceholders]
.map((p) => p.replace(/[-/\\^$*+?.()|[\]{}]/g, "\\$&")) // Escape regex special characters
.join("|");
const allowedContentRegex = new RegExp(
`^([a-zA-Z0-9_\\-/]|${allowedPlaceholdersRegexPart})*$`
);
const contentIsValid = allowedContentRegex.test(val);
if (val.trim()) {
const certificateIdRegex = /\{\{certificateId\}\}/;
const certificateIdIsPresent = certificateIdRegex.test(val);
return contentIsValid && certificateIdIsPresent;
}
return contentIsValid;
},
{
message:
"Certificate name schema must include exactly one {{certificateId}} placeholder. It can also include {{environment}} placeholders. Only alphanumeric characters (a-z, A-Z, 0-9), dashes (-), underscores (_), and slashes (/) are allowed besides the placeholders."
}
)
});
const syncOptionsSchema = additionalSyncOptions
? baseSyncOptionsSchema.merge(additionalSyncOptions)
: (baseSyncOptionsSchema as T extends AnyZodObject
? z.ZodObject<z.objectUtil.MergeShapes<typeof baseSyncOptionsSchema.shape, T["shape"]>>
: typeof baseSyncOptionsSchema);
return z.object({
name: z
.string()
.trim()
.min(1, "Name is required")
.max(255, "Name must be less than 255 characters"),
description: z.string().optional(),
isAutoSyncEnabled: z.boolean().default(true),
subscriberId: z.string().min(1, "PKI Subscriber is required"),
connection: z.object({
id: z.string().uuid("Invalid connection ID format"),
name: z.string().max(255, "Connection name must be less than 255 characters")
}),
syncOptions: syncOptionsSchema
});
};
@@ -0,0 +1,28 @@
import { z } from "zod";
import {
AwsCertificateManagerPkiSyncDestinationSchema,
UpdateAwsCertificateManagerPkiSyncDestinationSchema
} from "./aws-certificate-manager-pki-sync-destination-schema";
import {
AzureKeyVaultPkiSyncDestinationSchema,
UpdateAzureKeyVaultPkiSyncDestinationSchema
} from "./azure-key-vault-pki-sync-destination-schema";
const PkiSyncUnionSchema = z.discriminatedUnion("destination", [
AzureKeyVaultPkiSyncDestinationSchema,
AwsCertificateManagerPkiSyncDestinationSchema
]);
const UpdatePkiSyncUnionSchema = z.discriminatedUnion("destination", [
UpdateAzureKeyVaultPkiSyncDestinationSchema,
UpdateAwsCertificateManagerPkiSyncDestinationSchema
]);
export const PkiSyncFormSchema = PkiSyncUnionSchema;
export const UpdatePkiSyncFormSchema = UpdatePkiSyncUnionSchema;
export type TPkiSyncForm = z.infer<typeof PkiSyncFormSchema>;
export type TUpdatePkiSyncForm = z.infer<typeof UpdatePkiSyncFormSchema>;