improvement: finish address changes

This commit is contained in:
Scott Wilson
2024-10-02 16:35:53 -07:00
parent fd254fbeec
commit e667c7c988
82 changed files with 2708 additions and 223 deletions
+2
View File
@@ -38,6 +38,7 @@ import { TAuthTokenServiceFactory } from "@app/services/auth-token/auth-token-se
import { TCertificateServiceFactory } from "@app/services/certificate/certificate-service";
import { TCertificateAuthorityServiceFactory } from "@app/services/certificate-authority/certificate-authority-service";
import { TCertificateTemplateServiceFactory } from "@app/services/certificate-template/certificate-template-service";
import { TCmekServiceFactory } from "@app/services/cmek/cmek-service";
import { TExternalMigrationServiceFactory } from "@app/services/external-migration/external-migration-service";
import { TGroupProjectServiceFactory } from "@app/services/group-project/group-project-service";
import { TIdentityServiceFactory } from "@app/services/identity/identity-service";
@@ -182,6 +183,7 @@ declare module "fastify" {
orgAdmin: TOrgAdminServiceFactory;
slack: TSlackServiceFactory;
workflowIntegration: TWorkflowIntegrationServiceFactory;
cmek: TCmekServiceFactory;
migration: TExternalMigrationServiceFactory;
};
// this is exclusive use for middlewares in which we need to inject data
@@ -0,0 +1,44 @@
import { Knex } from "knex";
import { TableName } from "@app/db/schemas";
export async function up(knex: Knex): Promise<void> {
if (await knex.schema.hasTable(TableName.KmsKey)) {
const hasOrgId = await knex.schema.hasColumn(TableName.KmsKey, "orgId");
const hasSlug = await knex.schema.hasColumn(TableName.KmsKey, "slug");
// projectId for CMEK functionality
await knex.schema.alterTable(TableName.KmsKey, (table) => {
table.string("projectId").nullable().references("id").inTable(TableName.Project).onDelete("CASCADE");
if (hasOrgId) {
table.dropUnique(["orgId", "slug"]); // prevents using the same key name in different projects so swapping constraint
table.unique(["orgId", "projectId", "slug"]);
}
if (hasSlug) {
table.renameColumn("slug", "name");
}
});
}
}
export async function down(knex: Knex): Promise<void> {
if (await knex.schema.hasTable(TableName.KmsKey)) {
const hasOrgId = await knex.schema.hasColumn(TableName.KmsKey, "orgId");
const hasName = await knex.schema.hasColumn(TableName.KmsKey, "name");
// remove projectId for CMEK functionality
await knex.schema.alterTable(TableName.KmsKey, (table) => {
if (hasOrgId) {
table.dropUnique(["orgId", "projectId", "slug"]);
table.unique(["orgId", "slug"]);
}
table.dropColumn("projectId");
if (hasName) {
table.renameColumn("name", "slug");
}
});
}
}
+1 -1
View File
@@ -54,7 +54,7 @@ export const getSecretManagerDataKey = async (knex: Knex, projectId: string) =>
} else {
const [kmsDoc] = await knex(TableName.KmsKey)
.insert({
slug: slugify(alphaNumericNanoId(8).toLowerCase()),
name: slugify(alphaNumericNanoId(8).toLowerCase()),
orgId: project.orgId,
isReserved: false
})
+3 -2
View File
@@ -13,9 +13,10 @@ export const KmsKeysSchema = z.object({
isDisabled: z.boolean().default(false).nullable().optional(),
isReserved: z.boolean().default(true).nullable().optional(),
orgId: z.string().uuid(),
slug: z.string(),
name: z.string(),
createdAt: z.date(),
updatedAt: z.date()
updatedAt: z.date(),
projectId: z.string().nullable().optional()
});
export type TKmsKeys = z.infer<typeof KmsKeysSchema>;
+11 -11
View File
@@ -26,7 +26,7 @@ const sanitizedExternalSchemaForGetAll = KmsKeysSchema.pick({
isDisabled: true,
createdAt: true,
updatedAt: true,
slug: true
name: true
})
.extend({
externalKms: ExternalKmsSchema.pick({
@@ -57,7 +57,7 @@ export const registerExternalKmsRouter = async (server: FastifyZodProvider) => {
},
schema: {
body: z.object({
slug: z.string().min(1).trim().toLowerCase(),
name: z.string().min(1).trim().toLowerCase(),
description: z.string().trim().optional(),
provider: ExternalKmsInputSchema
}),
@@ -74,7 +74,7 @@ export const registerExternalKmsRouter = async (server: FastifyZodProvider) => {
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
slug: req.body.slug,
name: req.body.name,
provider: req.body.provider,
description: req.body.description
});
@@ -87,7 +87,7 @@ export const registerExternalKmsRouter = async (server: FastifyZodProvider) => {
metadata: {
kmsId: externalKms.id,
provider: req.body.provider.type,
slug: req.body.slug,
name: req.body.name,
description: req.body.description
}
}
@@ -108,7 +108,7 @@ export const registerExternalKmsRouter = async (server: FastifyZodProvider) => {
id: z.string().trim().min(1)
}),
body: z.object({
slug: z.string().min(1).trim().toLowerCase().optional(),
name: z.string().min(1).trim().toLowerCase().optional(),
description: z.string().trim().optional(),
provider: ExternalKmsInputUpdateSchema
}),
@@ -125,7 +125,7 @@ export const registerExternalKmsRouter = async (server: FastifyZodProvider) => {
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
slug: req.body.slug,
name: req.body.name,
provider: req.body.provider,
description: req.body.description,
id: req.params.id
@@ -139,7 +139,7 @@ export const registerExternalKmsRouter = async (server: FastifyZodProvider) => {
metadata: {
kmsId: externalKms.id,
provider: req.body.provider.type,
slug: req.body.slug,
name: req.body.name,
description: req.body.description
}
}
@@ -182,7 +182,7 @@ export const registerExternalKmsRouter = async (server: FastifyZodProvider) => {
type: EventType.DELETE_KMS,
metadata: {
kmsId: externalKms.id,
slug: externalKms.slug
name: externalKms.name
}
}
});
@@ -224,7 +224,7 @@ export const registerExternalKmsRouter = async (server: FastifyZodProvider) => {
type: EventType.GET_KMS,
metadata: {
kmsId: externalKms.id,
slug: externalKms.slug
name: externalKms.name
}
}
});
@@ -266,7 +266,7 @@ export const registerExternalKmsRouter = async (server: FastifyZodProvider) => {
},
schema: {
params: z.object({
slug: z.string().trim().min(1)
name: z.string().trim().min(1)
}),
response: {
200: z.object({
@@ -281,7 +281,7 @@ export const registerExternalKmsRouter = async (server: FastifyZodProvider) => {
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
slug: req.params.slug
name: req.params.name
});
return { externalKms };
}
+4 -4
View File
@@ -203,7 +203,7 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
200: z.object({
secretManagerKmsKey: z.object({
id: z.string(),
slug: z.string(),
name: z.string(),
isExternal: z.boolean()
})
})
@@ -243,7 +243,7 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
200: z.object({
secretManagerKmsKey: z.object({
id: z.string(),
slug: z.string(),
name: z.string(),
isExternal: z.boolean()
})
})
@@ -268,7 +268,7 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
metadata: {
secretManagerKmsKey: {
id: secretManagerKmsKey.id,
slug: secretManagerKmsKey.slug
name: secretManagerKmsKey.name
}
}
}
@@ -336,7 +336,7 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
200: z.object({
secretManagerKmsKey: z.object({
id: z.string(),
slug: z.string(),
name: z.string(),
isExternal: z.boolean()
})
})
@@ -1,3 +1,4 @@
import { SymmetricEncryption } from "@app/lib/crypto/cipher";
import { TProjectPermission } from "@app/lib/types";
import { ActorType } from "@app/services/auth/auth-type";
import { CaStatus } from "@app/services/certificate-authority/certificate-authority-types";
@@ -182,7 +183,13 @@ export enum EventType {
DELETE_SLACK_INTEGRATION = "delete-slack-integration",
GET_PROJECT_SLACK_CONFIG = "get-project-slack-config",
UPDATE_PROJECT_SLACK_CONFIG = "update-project-slack-config",
INTEGRATION_SYNCED = "integration-synced"
INTEGRATION_SYNCED = "integration-synced",
CREATE_CMEK = "create-cmek",
UPDATE_CMEK = "update-cmek",
DELETE_CMEK = "delete-cmek",
GET_CMEKS = "get-cmeks",
CMEK_ENCRYPT = "cmek-encrypt",
CMEK_DECRYPT = "cmek-decrypt"
}
interface UserActorMetadata {
@@ -1350,7 +1357,7 @@ interface CreateKmsEvent {
metadata: {
kmsId: string;
provider: string;
slug: string;
name: string;
description?: string;
};
}
@@ -1359,7 +1366,7 @@ interface DeleteKmsEvent {
type: EventType.DELETE_KMS;
metadata: {
kmsId: string;
slug: string;
name: string;
};
}
@@ -1368,7 +1375,7 @@ interface UpdateKmsEvent {
metadata: {
kmsId: string;
provider: string;
slug?: string;
name?: string;
description?: string;
};
}
@@ -1377,7 +1384,7 @@ interface GetKmsEvent {
type: EventType.GET_KMS;
metadata: {
kmsId: string;
slug: string;
name: string;
};
}
@@ -1386,7 +1393,7 @@ interface UpdateProjectKmsEvent {
metadata: {
secretManagerKmsKey: {
id: string;
slug: string;
name: string;
};
};
}
@@ -1541,6 +1548,53 @@ interface IntegrationSyncedEvent {
};
}
interface CreateCmekEvent {
type: EventType.CREATE_CMEK;
metadata: {
keyId: string;
name: string;
description?: string;
encryptionAlgorithm: SymmetricEncryption;
};
}
interface DeleteCmekEvent {
type: EventType.DELETE_CMEK;
metadata: {
keyId: string;
};
}
interface UpdateCmekEvent {
type: EventType.UPDATE_CMEK;
metadata: {
keyId: string;
name?: string;
description?: string;
};
}
interface GetCmeksEvent {
type: EventType.GET_CMEKS;
metadata: {
keyIds: string[];
};
}
interface CmekEncryptEvent {
type: EventType.CMEK_ENCRYPT;
metadata: {
keyId: string;
};
}
interface CmekDecryptEvent {
type: EventType.CMEK_DECRYPT;
metadata: {
keyId: string;
};
}
export type Event =
| GetSecretsEvent
| GetSecretEvent
@@ -1680,4 +1734,10 @@ export type Event =
| GetSlackIntegration
| UpdateProjectSlackConfig
| GetProjectSlackConfig
| IntegrationSyncedEvent;
| IntegrationSyncedEvent
| CreateCmekEvent
| UpdateCmekEvent
| DeleteCmekEvent
| GetCmeksEvent
| CmekEncryptEvent
| CmekDecryptEvent;
@@ -30,7 +30,7 @@ export const externalKmsDALFactory = (db: TDbClient) => {
isDisabled: el.isDisabled,
isReserved: el.isReserved,
orgId: el.orgId,
slug: el.slug,
name: el.name,
createdAt: el.createdAt,
updatedAt: el.updatedAt,
externalKms: {
@@ -43,7 +43,7 @@ export const externalKmsServiceFactory = ({
provider,
description,
actor,
slug,
name,
actorId,
actorOrgId,
actorAuthMethod
@@ -64,7 +64,7 @@ export const externalKmsServiceFactory = ({
});
}
const kmsSlug = slug ? slugify(slug) : slugify(alphaNumericNanoId(8).toLowerCase());
const kmsName = name ? slugify(name) : slugify(alphaNumericNanoId(8).toLowerCase());
let sanitizedProviderInput = "";
switch (provider.type) {
@@ -96,7 +96,7 @@ export const externalKmsServiceFactory = ({
{
isReserved: false,
description,
slug: kmsSlug,
name: kmsName,
orgId: actorOrgId
},
tx
@@ -120,7 +120,7 @@ export const externalKmsServiceFactory = ({
description,
actor,
id: kmsId,
slug,
name,
actorId,
actorOrgId,
actorAuthMethod
@@ -142,7 +142,7 @@ export const externalKmsServiceFactory = ({
});
}
const kmsSlug = slug ? slugify(slug) : undefined;
const kmsName = name ? slugify(name) : undefined;
const externalKmsDoc = await externalKmsDAL.findOne({ kmsKeyId: kmsDoc.id });
if (!externalKmsDoc) throw new NotFoundError({ message: "External kms not found" });
@@ -188,7 +188,7 @@ export const externalKmsServiceFactory = ({
kmsDoc.id,
{
description,
slug: kmsSlug
name: kmsName
},
tx
);
@@ -280,14 +280,14 @@ export const externalKmsServiceFactory = ({
}
};
const findBySlug = async ({
const findByName = async ({
actor,
actorId,
actorOrgId,
actorAuthMethod,
slug: kmsSlug
name: kmsName
}: TGetExternalKmsBySlugDTO) => {
const kmsDoc = await kmsDAL.findOne({ slug: kmsSlug, orgId: actorOrgId });
const kmsDoc = await kmsDAL.findOne({ name: kmsName, orgId: actorOrgId });
const { permission } = await permissionService.getOrgPermission(
actor,
actorId,
@@ -327,6 +327,6 @@ export const externalKmsServiceFactory = ({
deleteById,
list,
findById,
findBySlug
findBySlug: findByName
};
};
@@ -3,14 +3,14 @@ import { TOrgPermission } from "@app/lib/types";
import { TExternalKmsInputSchema, TExternalKmsInputUpdateSchema } from "./providers/model";
export type TCreateExternalKmsDTO = {
slug?: string;
name?: string;
description?: string;
provider: TExternalKmsInputSchema;
} & Omit<TOrgPermission, "orgId">;
export type TUpdateExternalKmsDTO = {
id: string;
slug?: string;
name?: string;
description?: string;
provider?: TExternalKmsInputUpdateSchema;
} & Omit<TOrgPermission, "orgId">;
@@ -26,5 +26,5 @@ export type TGetExternalKmsByIdDTO = {
} & Omit<TOrgPermission, "orgId">;
export type TGetExternalKmsBySlugDTO = {
slug: string;
name: string;
} & Omit<TOrgPermission, "orgId">;
@@ -2,13 +2,24 @@ import { CreateKeyCommand, DecryptCommand, DescribeKeyCommand, EncryptCommand, K
import { AssumeRoleCommand, STSClient } from "@aws-sdk/client-sts";
import { randomUUID } from "crypto";
import { getConfig } from "@app/lib/config/env";
import { ExternalKmsAwsSchema, KmsAwsCredentialType, TExternalKmsAwsSchema, TExternalKmsProviderFns } from "./model";
const getAwsKmsClient = async (providerInputs: TExternalKmsAwsSchema) => {
const appCfg = getConfig();
if (providerInputs.credential.type === KmsAwsCredentialType.AssumeRole) {
const awsCredential = providerInputs.credential.data;
const stsClient = new STSClient({
region: providerInputs.awsRegion
region: providerInputs.awsRegion,
credentials:
appCfg.CLIENT_ID_AWS_INTEGRATION && appCfg.CLIENT_SECRET_AWS_INTEGRATION
? {
accessKeyId: appCfg.CLIENT_ID_AWS_INTEGRATION,
secretAccessKey: appCfg.CLIENT_SECRET_AWS_INTEGRATION
}
: undefined
});
const command = new AssumeRoleCommand({
RoleArn: awsCredential.assumeRoleArn,
@@ -40,7 +40,7 @@ export const getDefaultOnPremFeatures = (): TFeatureSet => ({
secretRotation: true,
caCrl: false,
instanceUserManagement: false,
externalKms: false,
externalKms: true,
rateLimits: {
readLimit: 60,
writeLimit: 200,
@@ -14,6 +14,15 @@ export enum ProjectPermissionActions {
Delete = "delete"
}
export enum ProjectPermissionCmekActions {
Read = "read",
Create = "create",
Edit = "edit",
Delete = "delete",
Encrypt = "encrypt",
Decrypt = "decrypt"
}
export enum ProjectPermissionSub {
Role = "role",
Member = "member",
@@ -38,7 +47,8 @@ export enum ProjectPermissionSub {
CertificateTemplates = "certificate-templates",
PkiAlerts = "pki-alerts",
PkiCollections = "pki-collections",
Kms = "kms"
Kms = "kms",
Cmek = "cmek"
}
export type SecretSubjectFields = {
@@ -95,6 +105,7 @@ export type ProjectPermissionSet =
| [ProjectPermissionActions, ProjectPermissionSub.CertificateTemplates]
| [ProjectPermissionActions, ProjectPermissionSub.PkiAlerts]
| [ProjectPermissionActions, ProjectPermissionSub.PkiCollections]
| [ProjectPermissionCmekActions, ProjectPermissionSub.Cmek]
| [ProjectPermissionActions.Delete, ProjectPermissionSub.Project]
| [ProjectPermissionActions.Edit, ProjectPermissionSub.Project]
| [ProjectPermissionActions.Read, ProjectPermissionSub.SecretRollback]
@@ -282,6 +293,12 @@ export const ProjectPermissionSchema = z.discriminatedUnion("subject", [
action: CASL_ACTION_SCHEMA_ENUM([ProjectPermissionActions.Read]).describe(
"Describe what action an entity can take."
)
}),
z.object({
subject: z.literal(ProjectPermissionSub.Cmek).describe("The entity this permission pertains to."),
action: CASL_ACTION_SCHEMA_NATIVE_ENUM(ProjectPermissionCmekActions).describe(
"Describe what action an entity can take."
)
})
]);
@@ -325,6 +342,17 @@ const buildAdminPermissionRules = () => {
can([ProjectPermissionActions.Edit, ProjectPermissionActions.Delete], ProjectPermissionSub.Project);
can([ProjectPermissionActions.Read, ProjectPermissionActions.Create], ProjectPermissionSub.SecretRollback);
can([ProjectPermissionActions.Edit], ProjectPermissionSub.Kms);
can(
[
ProjectPermissionCmekActions.Create,
ProjectPermissionCmekActions.Edit,
ProjectPermissionCmekActions.Delete,
ProjectPermissionCmekActions.Read,
ProjectPermissionCmekActions.Encrypt,
ProjectPermissionCmekActions.Decrypt
],
ProjectPermissionSub.Cmek
);
return rules;
};
@@ -444,6 +472,18 @@ const buildMemberPermissionRules = () => {
can([ProjectPermissionActions.Read], ProjectPermissionSub.PkiAlerts);
can([ProjectPermissionActions.Read], ProjectPermissionSub.PkiCollections);
can(
[
ProjectPermissionCmekActions.Create,
ProjectPermissionCmekActions.Edit,
ProjectPermissionCmekActions.Delete,
ProjectPermissionCmekActions.Read,
ProjectPermissionCmekActions.Encrypt,
ProjectPermissionCmekActions.Decrypt
],
ProjectPermissionSub.Cmek
);
return rules;
};
@@ -470,6 +510,7 @@ const buildViewerPermissionRules = () => {
can(ProjectPermissionActions.Read, ProjectPermissionSub.IpAllowList);
can(ProjectPermissionActions.Read, ProjectPermissionSub.CertificateAuthorities);
can(ProjectPermissionActions.Read, ProjectPermissionSub.Certificates);
can(ProjectPermissionCmekActions.Read, ProjectPermissionSub.Cmek);
return rules;
};
+34
View File
@@ -1347,3 +1347,37 @@ export const PROJECT_ROLE = {
projectSlug: "The slug of the project to list the roles of."
}
};
export const KMS = {
CREATE_KEY: {
projectId: "The ID of the project to create the key in.",
name: "The name of the key to be created. Must be slug-friendly.",
description: "An optional description of the key.",
encryptionAlgorithm: "The algorithm to use when performing cryptographic operations with the key."
},
UPDATE_KEY: {
keyId: "The ID of the key to be updated.",
name: "The updated name of this key. Must be slug-friendly.",
description: "The updated description of this key.",
isDisabled: "The flag to enable or disable this key."
},
DELETE_KEY: {
keyId: "The ID of the key to be deleted."
},
LIST_KEYS: {
projectId: "The ID of the project to list keys from.",
offset: "The offset to start from. If you enter 10, it will start from the 10th key.",
limit: "The number of keys to return.",
orderBy: "The column to order keys by.",
orderDirection: "The direction to order keys in.",
search: "The text string to filter key names by."
},
ENCRYPT: {
keyId: "The ID of the key to encrypt the data with.",
plaintext: "The plaintext to be encrypted (base64 encoded)."
},
DECRYPT: {
keyId: "The ID of the key to decrypt the data with.",
ciphertext: "The ciphertext to be decrypted (base64 encoded)."
}
};
+40
View File
@@ -0,0 +1,40 @@
// Credit: https://github.com/miguelmota/is-base64
export const isBase64 = (
v: string,
opts = { allowEmpty: false, mimeRequired: false, allowMime: true, paddingRequired: false }
) => {
if (opts.allowEmpty === false && v === "") {
return false;
}
let regex = "(?:[A-Za-z0-9+\\/]{4})*(?:[A-Za-z0-9+\\/]{2}==|[A-Za-z0-9+/]{3}=)?";
const mimeRegex = "(data:\\w+\\/[a-zA-Z\\+\\-\\.]+;base64,)";
if (opts.mimeRequired === true) {
regex = mimeRegex + regex;
} else if (opts.allowMime === true) {
regex = `${mimeRegex}?${regex}`;
}
if (opts.paddingRequired === false) {
regex = "(?:[A-Za-z0-9+\\/]{4})*(?:[A-Za-z0-9+\\/]{2}(==)?|[A-Za-z0-9+\\/]{3}=?)?";
}
return new RegExp(`^${regex}$`, "gi").test(v);
};
export const getBase64SizeInBytes = (base64String: string) => {
// Remove data URI scheme if present
const base64 = base64String.replace(/^data:.*?;base64,/, "");
// Remove padding characters
const withoutPadding = base64.replace(/=+$/, "");
// Calculate bits: each base64 character represents 6 bits
const totalBits = withoutPadding.length * 6;
// Convert bits to bytes (8 bits = 1 byte)
const bytes = totalBits / 8;
return bytes;
};
+8
View File
@@ -96,6 +96,7 @@ import { certificateAuthorityServiceFactory } from "@app/services/certificate-au
import { certificateTemplateDALFactory } from "@app/services/certificate-template/certificate-template-dal";
import { certificateTemplateEstConfigDALFactory } from "@app/services/certificate-template/certificate-template-est-config-dal";
import { certificateTemplateServiceFactory } from "@app/services/certificate-template/certificate-template-service";
import { cmekServiceFactory } from "@app/services/cmek/cmek-service";
import { externalMigrationServiceFactory } from "@app/services/external-migration/external-migration-service";
import { groupProjectDALFactory } from "@app/services/group-project/group-project-dal";
import { groupProjectMembershipRoleDALFactory } from "@app/services/group-project/group-project-membership-role-dal";
@@ -1192,6 +1193,12 @@ export const registerRoutes = async (
workflowIntegrationDAL
});
const cmekService = cmekServiceFactory({
kmsDAL,
kmsService,
permissionService
});
const migrationService = externalMigrationServiceFactory({
projectService,
orgService,
@@ -1281,6 +1288,7 @@ export const registerRoutes = async (
secretSharing: secretSharingService,
userEngagement: userEngagementService,
externalKms: externalKmsService,
cmek: cmekService,
orgAdmin: orgAdminService,
slack: slackService,
workflowIntegration: workflowIntegrationService,
+331
View File
@@ -0,0 +1,331 @@
import slugify from "@sindresorhus/slugify";
import { z } from "zod";
import { InternalKmsSchema, KmsKeysSchema } from "@app/db/schemas";
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
import { KMS } from "@app/lib/api-docs";
import { getBase64SizeInBytes, isBase64 } from "@app/lib/base64";
import { SymmetricEncryption } from "@app/lib/crypto/cipher";
import { OrderByDirection } from "@app/lib/types";
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 { CmekOrderBy } from "@app/services/cmek/cmek-types";
const keyNameSchema = z
.string()
.trim()
.min(1)
.max(32)
.toLowerCase()
.refine((v) => slugify(v) === v, {
message: "Name must be slug friendly"
});
const keyDescriptionSchema = z.string().trim().max(500).optional();
const base64Schema = z.string().superRefine((val, ctx) => {
if (!isBase64(val)) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: "plaintext must be base64 encoded"
});
}
if (getBase64SizeInBytes(val) > 6144) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: "data cannot exceed 6144 bytes"
});
}
});
export const registerCmekRouter = async (server: FastifyZodProvider) => {
// create encryption key
server.route({
method: "POST",
url: "/keys",
config: {
rateLimit: writeLimit
},
schema: {
description: "Create KMS key",
body: z.object({
projectId: z.string().describe(KMS.CREATE_KEY.projectId),
name: keyNameSchema.describe(KMS.CREATE_KEY.name),
description: keyDescriptionSchema.describe(KMS.CREATE_KEY.description),
encryptionAlgorithm: z
.nativeEnum(SymmetricEncryption)
.optional()
.default(SymmetricEncryption.AES_GCM_256)
.describe(KMS.CREATE_KEY.encryptionAlgorithm) // eventually will support others
}),
response: {
200: z.object({
key: KmsKeysSchema
})
}
},
onRequest: verifyAuth([AuthMode.JWT]),
handler: async (req) => {
const {
body: { projectId, name, description, encryptionAlgorithm },
permission
} = req;
const cmek = await server.services.cmek.createCmek(
{ orgId: permission.orgId, projectId, name, description, encryptionAlgorithm },
permission
);
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
projectId,
event: {
type: EventType.CREATE_CMEK,
metadata: {
keyId: cmek.id,
name,
description,
encryptionAlgorithm
}
}
});
return { key: cmek };
}
});
// update KMS key
server.route({
method: "PATCH",
url: "/keys/:keyId",
config: {
rateLimit: writeLimit
},
schema: {
description: "Update KMS key",
params: z.object({
keyId: z.string().uuid().describe(KMS.UPDATE_KEY.keyId)
}),
body: z.object({
name: keyNameSchema.optional().describe(KMS.UPDATE_KEY.name),
isDisabled: z.boolean().optional().describe(KMS.UPDATE_KEY.isDisabled),
description: keyDescriptionSchema.describe(KMS.UPDATE_KEY.description)
}),
response: {
200: z.object({
key: KmsKeysSchema
})
}
},
onRequest: verifyAuth([AuthMode.JWT]),
handler: async (req) => {
const {
params: { keyId },
body,
permission
} = req;
const cmek = await server.services.cmek.updateCmekById({ keyId, ...body }, permission);
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
orgId: permission.orgId,
event: {
type: EventType.UPDATE_CMEK,
metadata: {
keyId,
...body
}
}
});
return { key: cmek };
}
});
// delete KMS key
server.route({
method: "DELETE",
url: "/keys/:keyId",
config: {
rateLimit: writeLimit
},
schema: {
description: "Delete KMS key",
params: z.object({
keyId: z.string().uuid().describe(KMS.DELETE_KEY.keyId)
}),
response: {
200: z.object({
key: KmsKeysSchema
})
}
},
onRequest: verifyAuth([AuthMode.JWT]),
handler: async (req) => {
const {
params: { keyId },
permission
} = req;
const cmek = await server.services.cmek.deleteCmekById(keyId, permission);
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
orgId: permission.orgId,
event: {
type: EventType.DELETE_CMEK,
metadata: {
keyId
}
}
});
return { key: cmek };
}
});
// list KMS keys
server.route({
method: "GET",
url: "/keys",
config: {
rateLimit: readLimit
},
schema: {
description: "List KMS keys",
querystring: z.object({
projectId: z.string().describe(KMS.LIST_KEYS.projectId),
offset: z.coerce.number().min(0).optional().default(0).describe(KMS.LIST_KEYS.offset),
limit: z.coerce.number().min(1).max(100).optional().default(100).describe(KMS.LIST_KEYS.limit),
orderBy: z.nativeEnum(CmekOrderBy).optional().default(CmekOrderBy.Name).describe(KMS.LIST_KEYS.orderBy),
orderDirection: z
.nativeEnum(OrderByDirection)
.optional()
.default(OrderByDirection.ASC)
.describe(KMS.LIST_KEYS.orderDirection),
search: z.string().trim().optional().describe(KMS.LIST_KEYS.search)
}),
response: {
200: z.object({
keys: KmsKeysSchema.merge(InternalKmsSchema.pick({ version: true, encryptionAlgorithm: true })).array(),
totalCount: z.number()
})
}
},
onRequest: verifyAuth([AuthMode.JWT]),
handler: async (req) => {
const {
query: { projectId, ...dto },
permission
} = req;
const { cmeks, totalCount } = await server.services.cmek.listCmeksByProjectId({ projectId, ...dto }, permission);
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
projectId,
event: {
type: EventType.GET_CMEKS,
metadata: {
keyIds: cmeks.map((key) => key.id)
}
}
});
return { keys: cmeks, totalCount };
}
});
// encrypt data
server.route({
method: "POST",
url: "/keys/:keyId/encrypt",
config: {
rateLimit: writeLimit
},
schema: {
description: "Encrypt data with KMS key",
params: z.object({
keyId: z.string().uuid().describe(KMS.ENCRYPT.keyId)
}),
body: z.object({
plaintext: base64Schema.describe(KMS.ENCRYPT.plaintext)
}),
response: {
200: z.object({
ciphertext: z.string()
})
}
},
onRequest: verifyAuth([AuthMode.JWT]),
handler: async (req) => {
const {
params: { keyId },
body: { plaintext },
permission
} = req;
const ciphertext = await server.services.cmek.cmekEncrypt({ keyId, plaintext }, permission);
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
orgId: permission.orgId,
event: {
type: EventType.CMEK_ENCRYPT,
metadata: {
keyId
}
}
});
return { ciphertext };
}
});
server.route({
method: "POST",
url: "/keys/:keyId/decrypt",
config: {
rateLimit: writeLimit
},
schema: {
description: "Decrypt data with KMS key",
params: z.object({
keyId: z.string().uuid().describe(KMS.ENCRYPT.keyId)
}),
body: z.object({
ciphertext: base64Schema.describe(KMS.ENCRYPT.plaintext)
}),
response: {
200: z.object({
plaintext: z.string()
})
}
},
onRequest: verifyAuth([AuthMode.JWT]),
handler: async (req) => {
const {
params: { keyId },
body: { ciphertext },
permission
} = req;
const plaintext = await server.services.cmek.cmekDecrypt({ keyId, ciphertext }, permission);
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
orgId: permission.orgId,
event: {
type: EventType.CMEK_DECRYPT,
metadata: {
keyId
}
}
});
return { plaintext };
}
});
};
+2 -1
View File
@@ -1,3 +1,4 @@
import { registerCmekRouter } from "@app/server/routes/v1/cmek-router";
import { registerDashboardRouter } from "@app/server/routes/v1/dashboard-router";
import { registerAdminRouter } from "./admin-router";
@@ -103,6 +104,6 @@ export const registerV1Routes = async (server: FastifyZodProvider) => {
await server.register(registerIdentityRouter, { prefix: "/identities" });
await server.register(registerSecretSharingRouter, { prefix: "/secret-sharing" });
await server.register(registerUserEngagementRouter, { prefix: "/user-engagement" });
await server.register(registerDashboardRouter, { prefix: "/dashboard" });
await server.register(registerCmekRouter, { prefix: "/kms" });
};
+169
View File
@@ -0,0 +1,169 @@
import { ForbiddenError } from "@casl/ability";
import { FastifyRequest } from "fastify";
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
import { ProjectPermissionCmekActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
import { BadRequestError, NotFoundError } from "@app/lib/errors";
import {
TCmekDecryptDTO,
TCmekEncryptDTO,
TCreateCmekDTO,
TListCmeksByProjectIdDTO,
TUpdabteCmekByIdDTO
} from "@app/services/cmek/cmek-types";
import { TKmsKeyDALFactory } from "@app/services/kms/kms-key-dal";
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
type TCmekServiceFactoryDep = {
kmsService: TKmsServiceFactory;
kmsDAL: TKmsKeyDALFactory;
permissionService: TPermissionServiceFactory;
};
export type TCmekServiceFactory = ReturnType<typeof cmekServiceFactory>;
export const cmekServiceFactory = ({ kmsService, kmsDAL, permissionService }: TCmekServiceFactoryDep) => {
const createCmek = async ({ projectId, ...dto }: TCreateCmekDTO, actor: FastifyRequest["permission"]) => {
const { permission } = await permissionService.getProjectPermission(
actor.type,
actor.id,
projectId,
actor.authMethod,
actor.orgId
);
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionCmekActions.Create, ProjectPermissionSub.Cmek);
const cmek = await kmsService.generateKmsKey({
...dto,
projectId,
isReserved: false
});
return cmek;
};
const updateCmekById = async ({ keyId, ...data }: TUpdabteCmekByIdDTO, actor: FastifyRequest["permission"]) => {
const key = await kmsDAL.findById(keyId);
if (!key) throw new NotFoundError({ message: "Key not found" });
if (!key.projectId || key.isReserved) throw new BadRequestError({ message: "Key is not customer managed" });
const { permission } = await permissionService.getProjectPermission(
actor.type,
actor.id,
key.projectId,
actor.authMethod,
actor.orgId
);
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionCmekActions.Edit, ProjectPermissionSub.Cmek);
const cmek = await kmsDAL.updateById(keyId, data);
return cmek;
};
const deleteCmekById = async (keyId: string, actor: FastifyRequest["permission"]) => {
const key = await kmsDAL.findById(keyId);
if (!key) throw new NotFoundError({ message: "Key not found" });
if (!key.projectId || key.isReserved) throw new BadRequestError({ message: "Key is not customer managed" });
const { permission } = await permissionService.getProjectPermission(
actor.type,
actor.id,
key.projectId,
actor.authMethod,
actor.orgId
);
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionCmekActions.Delete, ProjectPermissionSub.Cmek);
const cmek = kmsDAL.deleteById(keyId);
return cmek;
};
const listCmeksByProjectId = async (
{ projectId, ...filters }: TListCmeksByProjectIdDTO,
actor: FastifyRequest["permission"]
) => {
const { permission } = await permissionService.getProjectPermission(
actor.type,
actor.id,
projectId,
actor.authMethod,
actor.orgId
);
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionCmekActions.Read, ProjectPermissionSub.Cmek);
const { keys: cmeks, totalCount } = await kmsDAL.findKmsKeysByProjectId({ projectId, ...filters });
return { cmeks, totalCount };
};
const cmekEncrypt = async ({ keyId, plaintext }: TCmekEncryptDTO, actor: FastifyRequest["permission"]) => {
const key = await kmsDAL.findById(keyId);
if (!key) throw new NotFoundError({ message: "Key not found" });
if (!key.projectId || key.isReserved) throw new BadRequestError({ message: "Key is not customer managed" });
if (key.isDisabled) throw new BadRequestError({ message: "Key is disabled" });
const { permission } = await permissionService.getProjectPermission(
actor.type,
actor.id,
key.projectId,
actor.authMethod,
actor.orgId
);
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionCmekActions.Encrypt, ProjectPermissionSub.Cmek);
const encrypt = await kmsService.encryptWithKmsKey({ kmsId: keyId });
const { cipherTextBlob } = await encrypt({ plainText: Buffer.from(plaintext, "base64") });
return cipherTextBlob.toString("base64");
};
const cmekDecrypt = async ({ keyId, ciphertext }: TCmekDecryptDTO, actor: FastifyRequest["permission"]) => {
const key = await kmsDAL.findById(keyId);
if (!key) throw new NotFoundError({ message: "Key not found" });
if (!key.projectId || key.isReserved) throw new BadRequestError({ message: "Key is not customer managed" });
if (key.isDisabled) throw new BadRequestError({ message: "Key is disabled" });
const { permission } = await permissionService.getProjectPermission(
actor.type,
actor.id,
key.projectId,
actor.authMethod,
actor.orgId
);
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionCmekActions.Decrypt, ProjectPermissionSub.Cmek);
const decrypt = await kmsService.decryptWithKmsKey({ kmsId: keyId });
const plaintextBlob = await decrypt({ cipherTextBlob: Buffer.from(ciphertext, "base64") });
return plaintextBlob.toString("base64");
};
return {
createCmek,
updateCmekById,
deleteCmekById,
listCmeksByProjectId,
cmekEncrypt,
cmekDecrypt
};
};
+40
View File
@@ -0,0 +1,40 @@
import { SymmetricEncryption } from "@app/lib/crypto/cipher";
import { OrderByDirection } from "@app/lib/types";
export type TCreateCmekDTO = {
orgId: string;
projectId: string;
name: string;
description?: string;
encryptionAlgorithm: SymmetricEncryption;
};
export type TUpdabteCmekByIdDTO = {
keyId: string;
name?: string;
isDisabled?: boolean;
description?: string;
};
export type TListCmeksByProjectIdDTO = {
projectId: string;
offset?: number;
limit?: number;
orderBy?: CmekOrderBy;
orderDirection?: OrderByDirection;
search?: string;
};
export type TCmekEncryptDTO = {
keyId: string;
plaintext: string;
};
export type TCmekDecryptDTO = {
keyId: string;
ciphertext: string;
};
export enum CmekOrderBy {
Name = "name"
}
@@ -5,7 +5,7 @@ import { TableName, TIdentityOrgMemberships } from "@app/db/schemas";
import { DatabaseError } from "@app/lib/errors";
import { ormify, selectAllTableCols, sqlNestRelationships } from "@app/lib/knex";
import { OrderByDirection } from "@app/lib/types";
import { TListOrgIdentitiesByOrgIdDTO } from "@app/services/identity/identity-types";
import { OrgIdentityOrderBy, TListOrgIdentitiesByOrgIdDTO } from "@app/services/identity/identity-types";
export type TIdentityOrgDALFactory = ReturnType<typeof identityOrgDALFactory>;
@@ -33,7 +33,7 @@ export const identityOrgDALFactory = (db: TDbClient) => {
{
limit,
offset = 0,
orderBy,
orderBy = OrgIdentityOrderBy.Name,
orderDirection = OrderByDirection.ASC,
search,
...filter
@@ -43,12 +43,16 @@ export const identityOrgDALFactory = (db: TDbClient) => {
) => {
try {
const paginatedFetchIdentity = (tx || db.replicaNode())(TableName.Identity)
.where((queryBuilder) => {
if (limit) {
void queryBuilder.offset(offset).limit(limit);
}
})
.as(TableName.Identity);
.as(TableName.Identity)
.orderBy(`${TableName.Identity}.${orderBy}`, orderDirection);
if (search?.length) {
void paginatedFetchIdentity.whereILike(`${TableName.Identity}.name`, `%${search}%`);
}
if (limit) {
void paginatedFetchIdentity.offset(offset).limit(limit);
}
const query = (tx || db.replicaNode())(TableName.IdentityOrgMembership)
.where(filter)
@@ -78,24 +82,8 @@ export const identityOrgDALFactory = (db: TDbClient) => {
db.ref("id").withSchema(TableName.IdentityMetadata).as("metadataId"),
db.ref("key").withSchema(TableName.IdentityMetadata).as("metadataKey"),
db.ref("value").withSchema(TableName.IdentityMetadata).as("metadataValue")
);
if (orderBy) {
switch (orderBy) {
case "name":
void query.orderBy(`${TableName.Identity}.${orderBy}`, orderDirection);
break;
case "role":
void query.orderBy(`${TableName.IdentityOrgMembership}.${orderBy}`, orderDirection);
break;
default:
// do nothing
}
}
if (search?.length) {
void query.whereILike(`${TableName.Identity}.name`, `%${search}%`);
}
)
.orderBy(`${TableName.Identity}.${orderBy}`, orderDirection);
const docs = await query;
const formattedDocs = sqlNestRelationships({
@@ -41,6 +41,6 @@ export type TListOrgIdentitiesByOrgIdDTO = {
} & TOrgPermission;
export enum OrgIdentityOrderBy {
Name = "name",
Role = "role"
Name = "name"
// Role = "role"
}
+11
View File
@@ -0,0 +1,11 @@
import { SymmetricEncryption } from "@app/lib/crypto/cipher";
export const getByteLengthForAlgorithm = (encryptionAlgorithm: SymmetricEncryption) => {
switch (encryptionAlgorithm) {
case SymmetricEncryption.AES_GCM_128:
return 16;
case SymmetricEncryption.AES_GCM_256:
default:
return 32;
}
};
+49 -2
View File
@@ -1,9 +1,11 @@
import { Knex } from "knex";
import { TDbClient } from "@app/db";
import { KmsKeysSchema, TableName } from "@app/db/schemas";
import { KmsKeysSchema, TableName, TInternalKms, TKmsKeys } from "@app/db/schemas";
import { DatabaseError } from "@app/lib/errors";
import { ormify, selectAllTableCols } from "@app/lib/knex";
import { OrderByDirection } from "@app/lib/types";
import { CmekOrderBy, TListCmeksByProjectIdDTO } from "@app/services/cmek/cmek-types";
export type TKmsKeyDALFactory = ReturnType<typeof kmskeyDALFactory>;
@@ -71,5 +73,50 @@ export const kmskeyDALFactory = (db: TDbClient) => {
}
};
return { ...kmsOrm, findByIdWithAssociatedKms };
const findKmsKeysByProjectId = async (
{
projectId,
offset = 0,
limit,
orderBy = CmekOrderBy.Name,
orderDirection = OrderByDirection.ASC,
search
}: TListCmeksByProjectIdDTO,
tx?: Knex
) => {
try {
const query = (tx || db.replicaNode())(TableName.KmsKey)
.where("projectId", projectId)
.where((qb) => {
if (search) {
void qb.whereILike("name", `%${search}%`);
}
})
.join(TableName.InternalKms, `${TableName.InternalKms}.kmsKeyId`, `${TableName.KmsKey}.id`)
.select<
(TKmsKeys &
Pick<TInternalKms, "version" | "encryptionAlgorithm"> & {
total_count: number;
})[]
>(
selectAllTableCols(TableName.KmsKey),
db.raw(`count(*) OVER() as total_count`),
db.ref("encryptionAlgorithm").withSchema(TableName.InternalKms),
db.ref("version").withSchema(TableName.InternalKms)
)
.orderBy(orderBy, orderDirection);
if (limit) {
void query.limit(limit).offset(offset);
}
const data = await query;
return { keys: data, totalCount: Number(data?.[0]?.total_count ?? 0) };
} catch (error) {
throw new DatabaseError({ error, name: "Find kms keys by project id" });
}
};
return { ...kmsOrm, findByIdWithAssociatedKms, findKmsKeysByProjectId };
};
+46 -15
View File
@@ -17,6 +17,7 @@ import { generateHash } from "@app/lib/crypto/encryption";
import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors";
import { logger } from "@app/lib/logger";
import { alphaNumericNanoId } from "@app/lib/nanoid";
import { getByteLengthForAlgorithm } from "@app/services/kms/kms-fns";
import { TOrgDALFactory } from "../org/org-dal";
import { TProjectDALFactory } from "../project/project-dal";
@@ -66,22 +67,43 @@ export const kmsServiceFactory = ({
}: TKmsServiceFactoryDep) => {
let ROOT_ENCRYPTION_KEY = Buffer.alloc(0);
const $getRootEncryptionKey = (encryptionAlgorithm: SymmetricEncryption) => {
switch (encryptionAlgorithm) {
case SymmetricEncryption.AES_GCM_128:
return ROOT_ENCRYPTION_KEY.subarray(0, 16); // taking first 128bits
case SymmetricEncryption.AES_GCM_256:
default:
return ROOT_ENCRYPTION_KEY;
}
};
/*
* Generate KMS Key
* This function is responsibile for generating the infisical internal KMS for various entities
* Like for secret manager, cert manager or for organization
*/
const generateKmsKey = async ({ orgId, isReserved = true, tx, slug }: TGenerateKMSDTO) => {
const cipher = symmetricCipherService(SymmetricEncryption.AES_GCM_256);
const kmsKeyMaterial = randomSecureBytes(32);
const encryptedKeyMaterial = cipher.encrypt(kmsKeyMaterial, ROOT_ENCRYPTION_KEY);
const sanitizedSlug = slug ? slugify(slug) : slugify(alphaNumericNanoId(8).toLowerCase());
const generateKmsKey = async ({
orgId,
isReserved = true,
tx,
name,
projectId,
encryptionAlgorithm = SymmetricEncryption.AES_GCM_256,
description
}: TGenerateKMSDTO) => {
const cipher = symmetricCipherService(encryptionAlgorithm);
const kmsKeyMaterial = randomSecureBytes(getByteLengthForAlgorithm(encryptionAlgorithm));
const encryptedKeyMaterial = cipher.encrypt(kmsKeyMaterial, $getRootEncryptionKey(encryptionAlgorithm));
const sanitizedName = name ? slugify(name) : slugify(alphaNumericNanoId(8).toLowerCase());
const dbQuery = async (db: Knex) => {
const kmsDoc = await kmsDAL.create(
{
slug: sanitizedSlug,
name: sanitizedName,
orgId,
isReserved
isReserved,
projectId,
description
},
db
);
@@ -90,7 +112,7 @@ export const kmsServiceFactory = ({
{
version: 1,
encryptedKey: encryptedKeyMaterial,
encryptionAlgorithm: SymmetricEncryption.AES_GCM_256,
encryptionAlgorithm,
kmsKeyId: kmsDoc.id
},
db
@@ -286,8 +308,13 @@ export const kmsServiceFactory = ({
}
// internal KMS
const cipher = symmetricCipherService(SymmetricEncryption.AES_GCM_256);
const kmsKey = cipher.decrypt(kmsDoc.internalKms?.encryptedKey as Buffer, ROOT_ENCRYPTION_KEY);
const encryptionAlgorithm =
(kmsDoc.internalKms?.encryptionAlgorithm as SymmetricEncryption) ?? SymmetricEncryption.AES_GCM_256;
const cipher = symmetricCipherService(encryptionAlgorithm);
const kmsKey = cipher.decrypt(
kmsDoc.internalKms?.encryptedKey as Buffer,
$getRootEncryptionKey(encryptionAlgorithm)
);
return ({ cipherTextBlob: versionedCipherTextBlob }: Pick<TDecryptWithKmsDTO, "cipherTextBlob">) => {
const cipherTextBlob = versionedCipherTextBlob.subarray(0, -KMS_VERSION_BLOB_LENGTH);
@@ -347,10 +374,14 @@ export const kmsServiceFactory = ({
}
// internal KMS
// akhilmhdh: as more encryption are added do a check here on kmsDoc.encryptionAlgorithm
const cipher = symmetricCipherService(SymmetricEncryption.AES_GCM_256);
const encryptionAlgorithm =
(kmsDoc.internalKms?.encryptionAlgorithm as SymmetricEncryption) ?? SymmetricEncryption.AES_GCM_256;
const cipher = symmetricCipherService(encryptionAlgorithm);
return ({ plainText }: Pick<TEncryptWithKmsDTO, "plainText">) => {
const kmsKey = cipher.decrypt(kmsDoc.internalKms?.encryptedKey as Buffer, ROOT_ENCRYPTION_KEY);
const kmsKey = cipher.decrypt(
kmsDoc.internalKms?.encryptedKey as Buffer,
$getRootEncryptionKey(encryptionAlgorithm)
);
const encryptedPlainTextBlob = cipher.encrypt(plainText, kmsKey);
// Buffer#1 encrypted text + Buffer#2 version number
@@ -767,8 +798,8 @@ export const kmsServiceFactory = ({
message: "KMS not found"
});
}
const { id, slug, orgId, isExternal } = kms;
return { id, slug, orgId, isExternal };
const { id, name, orgId, isExternal } = kms;
return { id, name, orgId, isExternal };
};
// akhilmhdh: a copy of this is made in migrations/utils/kms
+6 -1
View File
@@ -1,5 +1,7 @@
import { Knex } from "knex";
import { SymmetricEncryption } from "@app/lib/crypto/cipher";
export enum KmsDataKey {
Organization,
SecretManager
@@ -22,8 +24,11 @@ export type TEncryptWithKmsDataKeyDTO =
export type TGenerateKMSDTO = {
orgId: string;
projectId?: string;
encryptionAlgorithm?: SymmetricEncryption;
isReserved?: boolean;
slug?: string;
name?: string;
description?: string;
tx?: Knex;
};