mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-08 09:27:50 +00:00
feat: updated kms service to return only kms details and some more minor changes
This commit is contained in:
@@ -7,6 +7,7 @@ import { getLastMidnightDateISO, removeTrailingSlash } from "@app/lib/fn";
|
|||||||
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||||
import { AuthMode } from "@app/services/auth/auth-type";
|
import { AuthMode } from "@app/services/auth/auth-type";
|
||||||
|
import { KmsType } from "@app/services/kms/kms-types";
|
||||||
|
|
||||||
export const registerProjectRouter = async (server: FastifyZodProvider) => {
|
export const registerProjectRouter = async (server: FastifyZodProvider) => {
|
||||||
server.route({
|
server.route({
|
||||||
@@ -194,7 +195,7 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
|
|||||||
},
|
},
|
||||||
onRequest: verifyAuth([AuthMode.JWT]),
|
onRequest: verifyAuth([AuthMode.JWT]),
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
const kmsKeys = await server.services.project.getProjectKmsKeys({
|
const kmsKey = await server.services.project.getProjectKmsKeys({
|
||||||
actor: req.permission.type,
|
actor: req.permission.type,
|
||||||
actorId: req.permission.id,
|
actorId: req.permission.id,
|
||||||
actorAuthMethod: req.permission.authMethod,
|
actorAuthMethod: req.permission.authMethod,
|
||||||
@@ -202,7 +203,7 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
|
|||||||
projectId: req.params.workspaceId
|
projectId: req.params.workspaceId
|
||||||
});
|
});
|
||||||
|
|
||||||
return kmsKeys;
|
return kmsKey;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -217,7 +218,10 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
|
|||||||
workspaceId: z.string().trim()
|
workspaceId: z.string().trim()
|
||||||
}),
|
}),
|
||||||
body: z.object({
|
body: z.object({
|
||||||
secretManagerKmsKeyId: z.string()
|
kms: z.discriminatedUnion("type", [
|
||||||
|
z.object({ type: z.literal(KmsType.Internal) }),
|
||||||
|
z.object({ type: z.literal(KmsType.External), kmsId: z.string() })
|
||||||
|
])
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { BadRequestError } from "@app/lib/errors";
|
|||||||
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
||||||
import { TKmsKeyDALFactory } from "@app/services/kms/kms-key-dal";
|
import { TKmsKeyDALFactory } from "@app/services/kms/kms-key-dal";
|
||||||
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
||||||
|
import { KmsDataKey } from "@app/services/kms/kms-types";
|
||||||
|
|
||||||
import { TLicenseServiceFactory } from "../license/license-service";
|
import { TLicenseServiceFactory } from "../license/license-service";
|
||||||
import { OrgPermissionActions, OrgPermissionSubjects } from "../permission/org-permission";
|
import { OrgPermissionActions, OrgPermissionSubjects } from "../permission/org-permission";
|
||||||
@@ -23,10 +24,7 @@ import { ExternalKmsAwsSchema, KmsProviders } from "./providers/model";
|
|||||||
|
|
||||||
type TExternalKmsServiceFactoryDep = {
|
type TExternalKmsServiceFactoryDep = {
|
||||||
externalKmsDAL: TExternalKmsDALFactory;
|
externalKmsDAL: TExternalKmsDALFactory;
|
||||||
kmsService: Pick<
|
kmsService: Pick<TKmsServiceFactory, "getOrgKmsKeyId" | "createCipherPairWithDataKey">;
|
||||||
TKmsServiceFactory,
|
|
||||||
"getOrgKmsKeyId" | "decryptWithInputKey" | "encryptWithInputKey" | "getOrgKmsDataKey"
|
|
||||||
>;
|
|
||||||
kmsDAL: Pick<TKmsKeyDALFactory, "create" | "updateById" | "findById" | "deleteById" | "findOne">;
|
kmsDAL: Pick<TKmsKeyDALFactory, "create" | "updateById" | "findById" | "deleteById" | "findOne">;
|
||||||
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">;
|
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">;
|
||||||
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
|
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
|
||||||
@@ -84,12 +82,12 @@ export const externalKmsServiceFactory = ({
|
|||||||
throw new BadRequestError({ message: "external kms provided is invalid" });
|
throw new BadRequestError({ message: "external kms provided is invalid" });
|
||||||
}
|
}
|
||||||
|
|
||||||
const orgKmsDataKey = await kmsService.getOrgKmsDataKey(actorOrgId);
|
const { encryptor: orgDataKeyEncryptor } = await kmsService.createCipherPairWithDataKey({
|
||||||
const kmsEncryptor = await kmsService.encryptWithInputKey({
|
type: KmsDataKey.Organization,
|
||||||
key: orgKmsDataKey
|
orgId: actorOrgId
|
||||||
});
|
});
|
||||||
|
|
||||||
const { cipherTextBlob: encryptedProviderInputs } = kmsEncryptor({
|
const { cipherTextBlob: encryptedProviderInputs } = orgDataKeyEncryptor({
|
||||||
plainText: Buffer.from(sanitizedProviderInput, "utf8")
|
plainText: Buffer.from(sanitizedProviderInput, "utf8")
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -150,13 +148,13 @@ export const externalKmsServiceFactory = ({
|
|||||||
if (!externalKmsDoc) throw new BadRequestError({ message: "External kms not found" });
|
if (!externalKmsDoc) throw new BadRequestError({ message: "External kms not found" });
|
||||||
|
|
||||||
let sanitizedProviderInput = "";
|
let sanitizedProviderInput = "";
|
||||||
if (provider) {
|
const { encryptor: orgDataKeyEncryptor, decryptor: orgDataKeyDecryptor } =
|
||||||
const orgKmsDataKey = await kmsService.getOrgKmsDataKey(kmsDoc.orgId);
|
await kmsService.createCipherPairWithDataKey({
|
||||||
const kmsDecryptor = await kmsService.decryptWithInputKey({
|
type: KmsDataKey.Organization,
|
||||||
key: orgKmsDataKey
|
orgId: actorOrgId
|
||||||
});
|
});
|
||||||
|
if (provider) {
|
||||||
const decryptedProviderInputBlob = kmsDecryptor({
|
const decryptedProviderInputBlob = orgDataKeyDecryptor({
|
||||||
cipherTextBlob: externalKmsDoc.encryptedProviderInputs
|
cipherTextBlob: externalKmsDoc.encryptedProviderInputs
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -179,11 +177,7 @@ export const externalKmsServiceFactory = ({
|
|||||||
|
|
||||||
let encryptedProviderInputs: Buffer | undefined;
|
let encryptedProviderInputs: Buffer | undefined;
|
||||||
if (sanitizedProviderInput) {
|
if (sanitizedProviderInput) {
|
||||||
const orgKmsDataKey = await kmsService.getOrgKmsDataKey(actorOrgId);
|
const { cipherTextBlob } = orgDataKeyEncryptor({
|
||||||
const kmsEncryptor = await kmsService.encryptWithInputKey({
|
|
||||||
key: orgKmsDataKey
|
|
||||||
});
|
|
||||||
const { cipherTextBlob } = kmsEncryptor({
|
|
||||||
plainText: Buffer.from(sanitizedProviderInput, "utf8")
|
plainText: Buffer.from(sanitizedProviderInput, "utf8")
|
||||||
});
|
});
|
||||||
encryptedProviderInputs = cipherTextBlob;
|
encryptedProviderInputs = cipherTextBlob;
|
||||||
@@ -266,12 +260,12 @@ export const externalKmsServiceFactory = ({
|
|||||||
const externalKmsDoc = await externalKmsDAL.findOne({ kmsKeyId: kmsDoc.id });
|
const externalKmsDoc = await externalKmsDAL.findOne({ kmsKeyId: kmsDoc.id });
|
||||||
if (!externalKmsDoc) throw new BadRequestError({ message: "External kms not found" });
|
if (!externalKmsDoc) throw new BadRequestError({ message: "External kms not found" });
|
||||||
|
|
||||||
const orgKmsDataKey = await kmsService.getOrgKmsDataKey(kmsDoc.orgId);
|
const { decryptor: orgDataKeyDecryptor } = await kmsService.createCipherPairWithDataKey({
|
||||||
const kmsDecryptor = await kmsService.decryptWithInputKey({
|
type: KmsDataKey.Organization,
|
||||||
key: orgKmsDataKey
|
orgId: actorOrgId
|
||||||
});
|
});
|
||||||
|
|
||||||
const decryptedProviderInputBlob = kmsDecryptor({
|
const decryptedProviderInputBlob = orgDataKeyDecryptor({
|
||||||
cipherTextBlob: externalKmsDoc.encryptedProviderInputs
|
cipherTextBlob: externalKmsDoc.encryptedProviderInputs
|
||||||
});
|
});
|
||||||
switch (externalKmsDoc.provider) {
|
switch (externalKmsDoc.provider) {
|
||||||
@@ -306,12 +300,12 @@ export const externalKmsServiceFactory = ({
|
|||||||
const externalKmsDoc = await externalKmsDAL.findOne({ kmsKeyId: kmsDoc.id });
|
const externalKmsDoc = await externalKmsDAL.findOne({ kmsKeyId: kmsDoc.id });
|
||||||
if (!externalKmsDoc) throw new BadRequestError({ message: "External kms not found" });
|
if (!externalKmsDoc) throw new BadRequestError({ message: "External kms not found" });
|
||||||
|
|
||||||
const orgKmsDataKey = await kmsService.getOrgKmsDataKey(kmsDoc.orgId);
|
const { decryptor: orgDataKeyDecryptor } = await kmsService.createCipherPairWithDataKey({
|
||||||
const kmsDecryptor = await kmsService.decryptWithInputKey({
|
type: KmsDataKey.Organization,
|
||||||
key: orgKmsDataKey
|
orgId: actorOrgId
|
||||||
});
|
});
|
||||||
|
|
||||||
const decryptedProviderInputBlob = kmsDecryptor({
|
const decryptedProviderInputBlob = orgDataKeyDecryptor({
|
||||||
cipherTextBlob: externalKmsDoc.encryptedProviderInputs
|
cipherTextBlob: externalKmsDoc.encryptedProviderInputs
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
+14
-10
@@ -17,6 +17,7 @@ import { alphaNumericNanoId } from "@app/lib/nanoid";
|
|||||||
import { EnforcementLevel } from "@app/lib/types";
|
import { EnforcementLevel } from "@app/lib/types";
|
||||||
import { ActorType } from "@app/services/auth/auth-type";
|
import { ActorType } from "@app/services/auth/auth-type";
|
||||||
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
||||||
|
import { KmsDataKey } from "@app/services/kms/kms-types";
|
||||||
import { TProjectDALFactory } from "@app/services/project/project-dal";
|
import { TProjectDALFactory } from "@app/services/project/project-dal";
|
||||||
import { TProjectBotServiceFactory } from "@app/services/project-bot/project-bot-service";
|
import { TProjectBotServiceFactory } from "@app/services/project-bot/project-bot-service";
|
||||||
import { TProjectEnvDALFactory } from "@app/services/project-env/project-env-dal";
|
import { TProjectEnvDALFactory } from "@app/services/project-env/project-env-dal";
|
||||||
@@ -89,10 +90,7 @@ type TSecretApprovalRequestServiceFactoryDep = {
|
|||||||
projectEnvDAL: Pick<TProjectEnvDALFactory, "findOne">;
|
projectEnvDAL: Pick<TProjectEnvDALFactory, "findOne">;
|
||||||
projectDAL: Pick<TProjectDALFactory, "checkProjectUpgradeStatus" | "findById" | "findProjectById">;
|
projectDAL: Pick<TProjectDALFactory, "checkProjectUpgradeStatus" | "findById" | "findProjectById">;
|
||||||
secretQueueService: Pick<TSecretQueueFactory, "syncSecrets" | "removeSecretReminder">;
|
secretQueueService: Pick<TSecretQueueFactory, "syncSecrets" | "removeSecretReminder">;
|
||||||
kmsService: Pick<
|
kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey" | "encryptWithInputKey" | "decryptWithInputKey">;
|
||||||
TKmsServiceFactory,
|
|
||||||
"getProjectSecretManagerKmsDataKey" | "encryptWithInputKey" | "decryptWithInputKey"
|
|
||||||
>;
|
|
||||||
secretV2BridgeDAL: Pick<
|
secretV2BridgeDAL: Pick<
|
||||||
TSecretV2BridgeDALFactory,
|
TSecretV2BridgeDALFactory,
|
||||||
"insertMany" | "upsertSecretReferences" | "findBySecretKeys" | "bulkUpdate" | "deleteMany"
|
"insertMany" | "upsertSecretReferences" | "findBySecretKeys" | "bulkUpdate" | "deleteMany"
|
||||||
@@ -214,8 +212,10 @@ export const secretApprovalRequestServiceFactory = ({
|
|||||||
|
|
||||||
let secrets;
|
let secrets;
|
||||||
if (shouldUseSecretV2Bridge) {
|
if (shouldUseSecretV2Bridge) {
|
||||||
const secretManagerDataKey = await kmsService.getProjectSecretManagerKmsDataKey(projectId);
|
const { decryptor: secretManagerDecryptor } = await kmsService.createCipherPairWithDataKey({
|
||||||
const secretManagerDecryptor = await kmsService.decryptWithInputKey({ key: secretManagerDataKey });
|
type: KmsDataKey.SecretManager,
|
||||||
|
projectId
|
||||||
|
});
|
||||||
const encrypedSecrets = await secretApprovalRequestSecretDAL.findByRequestIdBridgeSecretV2(
|
const encrypedSecrets = await secretApprovalRequestSecretDAL.findByRequestIdBridgeSecretV2(
|
||||||
secretApprovalRequest.id
|
secretApprovalRequest.id
|
||||||
);
|
);
|
||||||
@@ -427,8 +427,10 @@ export const secretApprovalRequestServiceFactory = ({
|
|||||||
);
|
);
|
||||||
if (!secretApprovalSecrets) throw new BadRequestError({ message: "No secrets found" });
|
if (!secretApprovalSecrets) throw new BadRequestError({ message: "No secrets found" });
|
||||||
|
|
||||||
const secretManagerDataKey = await kmsService.getProjectSecretManagerKmsDataKey(projectId);
|
const { decryptor: secretManagerDecryptor } = await kmsService.createCipherPairWithDataKey({
|
||||||
const secretManagerDecryptor = await kmsService.decryptWithInputKey({ key: secretManagerDataKey });
|
type: KmsDataKey.SecretManager,
|
||||||
|
projectId
|
||||||
|
});
|
||||||
|
|
||||||
const conflicts: Array<{ secretId: string; op: SecretOperations }> = [];
|
const conflicts: Array<{ secretId: string; op: SecretOperations }> = [];
|
||||||
let secretCreationCommits = secretApprovalSecrets.filter(({ op }) => op === SecretOperations.Create);
|
let secretCreationCommits = secretApprovalSecrets.filter(({ op }) => op === SecretOperations.Create);
|
||||||
@@ -1074,8 +1076,10 @@ export const secretApprovalRequestServiceFactory = ({
|
|||||||
const commits: Omit<TSecretApprovalRequestsSecretsV2Insert, "requestId">[] = [];
|
const commits: Omit<TSecretApprovalRequestsSecretsV2Insert, "requestId">[] = [];
|
||||||
const commitTagIds: Record<string, string[]> = {};
|
const commitTagIds: Record<string, string[]> = {};
|
||||||
|
|
||||||
const secretManagerDataKey = await kmsService.getProjectSecretManagerKmsDataKey(projectId);
|
const { encryptor: secretManagerEncryptor } = await kmsService.createCipherPairWithDataKey({
|
||||||
const secretManagerEncryptor = await kmsService.encryptWithInputKey({ key: secretManagerDataKey });
|
type: KmsDataKey.SecretManager,
|
||||||
|
projectId
|
||||||
|
});
|
||||||
|
|
||||||
// for created secret approval change
|
// for created secret approval change
|
||||||
const createdSecrets = data[SecretOperations.Create];
|
const createdSecrets = data[SecretOperations.Create];
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ export type TKmsKeyDALFactory = ReturnType<typeof kmskeyDALFactory>;
|
|||||||
export const kmskeyDALFactory = (db: TDbClient) => {
|
export const kmskeyDALFactory = (db: TDbClient) => {
|
||||||
const kmsOrm = ormify(db, TableName.KmsKey);
|
const kmsOrm = ormify(db, TableName.KmsKey);
|
||||||
|
|
||||||
|
// akhilmhdh: this function should never be called outside kms service
|
||||||
|
// why: because the encrypted key should never be shared with another service
|
||||||
const findByIdWithAssociatedKms = async (id: string, tx?: Knex) => {
|
const findByIdWithAssociatedKms = async (id: string, tx?: Knex) => {
|
||||||
try {
|
try {
|
||||||
const result = await (tx || db.replicaNode())(TableName.KmsKey)
|
const result = await (tx || db.replicaNode())(TableName.KmsKey)
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import slugify from "@sindresorhus/slugify";
|
import slugify from "@sindresorhus/slugify";
|
||||||
import { Knex } from "knex";
|
import { Knex } from "knex";
|
||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { KmsKeysSchema } from "@app/db/schemas";
|
||||||
import { AwsKmsProviderFactory } from "@app/ee/services/external-kms/providers/aws-kms";
|
import { AwsKmsProviderFactory } from "@app/ee/services/external-kms/providers/aws-kms";
|
||||||
import {
|
import {
|
||||||
ExternalKmsAwsSchema,
|
ExternalKmsAwsSchema,
|
||||||
@@ -23,12 +25,14 @@ import { TKmsKeyDALFactory } from "./kms-key-dal";
|
|||||||
import { TKmsRootConfigDALFactory } from "./kms-root-config-dal";
|
import { TKmsRootConfigDALFactory } from "./kms-root-config-dal";
|
||||||
import {
|
import {
|
||||||
KmsDataKey,
|
KmsDataKey,
|
||||||
|
KmsType,
|
||||||
TDecryptWithKeyDTO,
|
TDecryptWithKeyDTO,
|
||||||
TDecryptWithKmsDTO,
|
TDecryptWithKmsDTO,
|
||||||
TEncryptionWithKeyDTO,
|
TEncryptionWithKeyDTO,
|
||||||
TEncryptWithKmsDataKeyDTO,
|
TEncryptWithKmsDataKeyDTO,
|
||||||
TEncryptWithKmsDTO,
|
TEncryptWithKmsDTO,
|
||||||
TGenerateKMSDTO
|
TGenerateKMSDTO,
|
||||||
|
TUpdateProjectSecretManagerKmsKeyDTO
|
||||||
} from "./kms-types";
|
} from "./kms-types";
|
||||||
|
|
||||||
type TKmsServiceFactoryDep = {
|
type TKmsServiceFactoryDep = {
|
||||||
@@ -42,7 +46,6 @@ type TKmsServiceFactoryDep = {
|
|||||||
|
|
||||||
export type TKmsServiceFactory = ReturnType<typeof kmsServiceFactory>;
|
export type TKmsServiceFactory = ReturnType<typeof kmsServiceFactory>;
|
||||||
|
|
||||||
export const INTERNAL_KMS_KEY_ID = "internal";
|
|
||||||
const KMS_ROOT_CONFIG_UUID = "00000000-0000-0000-0000-000000000000";
|
const KMS_ROOT_CONFIG_UUID = "00000000-0000-0000-0000-000000000000";
|
||||||
|
|
||||||
const KMS_ROOT_CREATION_WAIT_KEY = "wait_till_ready_kms_root_key";
|
const KMS_ROOT_CREATION_WAIT_KEY = "wait_till_ready_kms_root_key";
|
||||||
@@ -51,6 +54,8 @@ const KMS_ROOT_CREATION_WAIT_TIME = 10;
|
|||||||
// akhilmhdh: Don't edit this value. This is measured for blob concatination in kms
|
// akhilmhdh: Don't edit this value. This is measured for blob concatination in kms
|
||||||
const KMS_VERSION = "v01";
|
const KMS_VERSION = "v01";
|
||||||
const KMS_VERSION_BLOB_LENGTH = 3;
|
const KMS_VERSION_BLOB_LENGTH = 3;
|
||||||
|
const KmsSanitizedSchema = KmsKeysSchema.extend({ isExternal: z.boolean() });
|
||||||
|
|
||||||
export const kmsServiceFactory = ({
|
export const kmsServiceFactory = ({
|
||||||
kmsDAL,
|
kmsDAL,
|
||||||
kmsRootConfigDAL,
|
kmsRootConfigDAL,
|
||||||
@@ -61,7 +66,11 @@ export const kmsServiceFactory = ({
|
|||||||
}: TKmsServiceFactoryDep) => {
|
}: TKmsServiceFactoryDep) => {
|
||||||
let ROOT_ENCRYPTION_KEY = Buffer.alloc(0);
|
let ROOT_ENCRYPTION_KEY = Buffer.alloc(0);
|
||||||
|
|
||||||
// this is used symmetric encryption
|
/*
|
||||||
|
* 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 generateKmsKey = async ({ orgId, isReserved = true, tx, slug }: TGenerateKMSDTO) => {
|
||||||
const cipher = symmetricCipherService(SymmetricEncryption.AES_GCM_256);
|
const cipher = symmetricCipherService(SymmetricEncryption.AES_GCM_256);
|
||||||
const kmsKeyMaterial = randomSecureBytes(32);
|
const kmsKeyMaterial = randomSecureBytes(32);
|
||||||
@@ -93,6 +102,11 @@ export const kmsServiceFactory = ({
|
|||||||
return doc;
|
return doc;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Simple encryption service function to do all the encryption tasks in infisical
|
||||||
|
* This can be even later exposed directly as api for encryption as function
|
||||||
|
* The encrypted binary even has everything into it. The IV, the version etc
|
||||||
|
*/
|
||||||
const encryptWithInputKey = async ({ key }: Omit<TEncryptionWithKeyDTO, "plainText">) => {
|
const encryptWithInputKey = async ({ key }: Omit<TEncryptionWithKeyDTO, "plainText">) => {
|
||||||
// akhilmhdh: as more encryption are added do a check here on kmsDoc.encryptionAlgorithm
|
// akhilmhdh: as more encryption are added do a check here on kmsDoc.encryptionAlgorithm
|
||||||
const cipher = symmetricCipherService(SymmetricEncryption.AES_GCM_256);
|
const cipher = symmetricCipherService(SymmetricEncryption.AES_GCM_256);
|
||||||
@@ -105,6 +119,10 @@ export const kmsServiceFactory = ({
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Simple decryption service function to do all the encryption tasks in infisical
|
||||||
|
* This can be even later exposed directly as api for encryption as function
|
||||||
|
*/
|
||||||
const decryptWithInputKey = async ({ key }: Omit<TDecryptWithKeyDTO, "cipherTextBlob">) => {
|
const decryptWithInputKey = async ({ key }: Omit<TDecryptWithKeyDTO, "cipherTextBlob">) => {
|
||||||
const cipher = symmetricCipherService(SymmetricEncryption.AES_GCM_256);
|
const cipher = symmetricCipherService(SymmetricEncryption.AES_GCM_256);
|
||||||
|
|
||||||
@@ -115,6 +133,13 @@ export const kmsServiceFactory = ({
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Function to generate a KMS for an org
|
||||||
|
* We handle concurrent with redis locking and waitReady
|
||||||
|
* What happens is first we check kms is assigned else first we acquire lock and create the kms with connection
|
||||||
|
* In mean time the rest of the request will wait until creation is finished followed by getting the created on
|
||||||
|
* In real time this would be milliseconds
|
||||||
|
*/
|
||||||
const getOrgKmsKeyId = async (orgId: string) => {
|
const getOrgKmsKeyId = async (orgId: string) => {
|
||||||
let org = await orgDAL.findById(orgId);
|
let org = await orgDAL.findById(orgId);
|
||||||
|
|
||||||
@@ -176,7 +201,12 @@ export const kmsServiceFactory = ({
|
|||||||
return org.kmsDefaultKeyId;
|
return org.kmsDefaultKeyId;
|
||||||
};
|
};
|
||||||
|
|
||||||
const decryptWithKmsKey = async ({ kmsId }: Omit<TDecryptWithKmsDTO, "cipherTextBlob">) => {
|
const decryptWithKmsKey = async ({
|
||||||
|
kmsId,
|
||||||
|
depth = 0
|
||||||
|
}: Omit<TDecryptWithKmsDTO, "cipherTextBlob"> & { depth?: number }) => {
|
||||||
|
if (depth > 2) throw new BadRequestError({ message: "KMS depth max limit" });
|
||||||
|
|
||||||
const kmsDoc = await kmsDAL.findByIdWithAssociatedKms(kmsId);
|
const kmsDoc = await kmsDAL.findByIdWithAssociatedKms(kmsId);
|
||||||
if (!kmsDoc) {
|
if (!kmsDoc) {
|
||||||
throw new NotFoundError({ message: "KMS ID not found" });
|
throw new NotFoundError({ message: "KMS ID not found" });
|
||||||
@@ -189,8 +219,12 @@ export const kmsServiceFactory = ({
|
|||||||
throw new Error("Invalid organization KMS");
|
throw new Error("Invalid organization KMS");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The idea is external kms connection info is encrypted by an org default KMS
|
||||||
|
// This could be external kms(in future) but at the end of the day, the end KMS will be an infisical internal one
|
||||||
|
// we put a limit of depth to avoid too many cycles
|
||||||
const orgKmsDecryptor = await decryptWithKmsKey({
|
const orgKmsDecryptor = await decryptWithKmsKey({
|
||||||
kmsId: kmsDoc.orgKms.id
|
kmsId: kmsDoc.orgKms.id,
|
||||||
|
depth: depth + 1
|
||||||
});
|
});
|
||||||
|
|
||||||
const orgKmsDataKey = await orgKmsDecryptor({
|
const orgKmsDataKey = await orgKmsDecryptor({
|
||||||
@@ -303,7 +337,7 @@ export const kmsServiceFactory = ({
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const getOrgKmsDataKey = async (orgId: string) => {
|
const $getOrgKmsDataKey = async (orgId: string) => {
|
||||||
const kmsKeyId = await getOrgKmsKeyId(orgId);
|
const kmsKeyId = await getOrgKmsKeyId(orgId);
|
||||||
let org = await orgDAL.findById(orgId);
|
let org = await orgDAL.findById(orgId);
|
||||||
|
|
||||||
@@ -313,7 +347,7 @@ export const kmsServiceFactory = ({
|
|||||||
|
|
||||||
if (!org.kmsEncryptedDataKey) {
|
if (!org.kmsEncryptedDataKey) {
|
||||||
const lock = await keyStore
|
const lock = await keyStore
|
||||||
.acquireLock([KeyStorePrefixes.KmsOrgDataKeyCreation, orgId], 3000, { retryCount: 3 })
|
.acquireLock([KeyStorePrefixes.KmsOrgDataKeyCreation, orgId], 500, { retryCount: 0 })
|
||||||
.catch(() => null);
|
.catch(() => null);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -448,14 +482,7 @@ export const kmsServiceFactory = ({
|
|||||||
return project.kmsSecretManagerKeyId;
|
return project.kmsSecretManagerKeyId;
|
||||||
};
|
};
|
||||||
|
|
||||||
const getProjectSecretManagerKmsKey = async (projectId: string) => {
|
const $getProjectSecretManagerKmsDataKey = async (projectId: string) => {
|
||||||
const kmsKeyId = await getProjectSecretManagerKmsKeyId(projectId);
|
|
||||||
const kmsKey = await kmsDAL.findByIdWithAssociatedKms(kmsKeyId);
|
|
||||||
|
|
||||||
return kmsKey;
|
|
||||||
};
|
|
||||||
|
|
||||||
const getProjectSecretManagerKmsDataKey = async (projectId: string) => {
|
|
||||||
const kmsKeyId = await getProjectSecretManagerKmsKeyId(projectId);
|
const kmsKeyId = await getProjectSecretManagerKmsKeyId(projectId);
|
||||||
let project = await projectDAL.findById(projectId);
|
let project = await projectDAL.findById(projectId);
|
||||||
|
|
||||||
@@ -528,21 +555,58 @@ export const kmsServiceFactory = ({
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const updateProjectSecretManagerKmsKey = async (projectId: string, kmsId: string) => {
|
const $getDataKey = async (dto: TEncryptWithKmsDataKeyDTO) => {
|
||||||
const currentKms = await getProjectSecretManagerKmsKey(projectId);
|
switch (dto.type) {
|
||||||
|
case KmsDataKey.SecretManager: {
|
||||||
|
return $getProjectSecretManagerKmsDataKey(dto.projectId);
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
return $getOrgKmsDataKey(dto.orgId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
if ((currentKms.isReserved && kmsId === INTERNAL_KMS_KEY_ID) || currentKms.id === kmsId) {
|
// by keeping the decrypted data key in inner scope
|
||||||
return currentKms;
|
// none of the entities outside can interact directly or expose the data key
|
||||||
|
const createCipherPairWithDataKey = async (encryptionContext: TEncryptWithKmsDataKeyDTO) => {
|
||||||
|
const dataKey = await $getDataKey(encryptionContext);
|
||||||
|
const cipher = symmetricCipherService(SymmetricEncryption.AES_GCM_256);
|
||||||
|
|
||||||
|
return {
|
||||||
|
encryptor: ({ plainText }: Pick<TEncryptWithKmsDTO, "plainText">) => {
|
||||||
|
const encryptedPlainTextBlob = cipher.encrypt(plainText, dataKey);
|
||||||
|
|
||||||
|
// Buffer#1 encrypted text + Buffer#2 version number
|
||||||
|
const versionBlob = Buffer.from(KMS_VERSION, "utf8"); // length is 3
|
||||||
|
const cipherTextBlob = Buffer.concat([encryptedPlainTextBlob, versionBlob]);
|
||||||
|
return { cipherTextBlob };
|
||||||
|
},
|
||||||
|
decryptor: ({ cipherTextBlob: versionedCipherTextBlob }: Pick<TDecryptWithKeyDTO, "cipherTextBlob">) => {
|
||||||
|
const cipherTextBlob = versionedCipherTextBlob.subarray(0, -KMS_VERSION_BLOB_LENGTH);
|
||||||
|
const decryptedBlob = cipher.decrypt(cipherTextBlob, dataKey);
|
||||||
|
return decryptedBlob;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateProjectSecretManagerKmsKey = async ({ projectId, kms }: TUpdateProjectSecretManagerKmsKeyDTO) => {
|
||||||
|
const kmsKeyId = await getProjectSecretManagerKmsKeyId(projectId);
|
||||||
|
const currentKms = await kmsDAL.findById(kmsKeyId);
|
||||||
|
|
||||||
|
// case: internal kms -> internal kms. no change needed
|
||||||
|
if (kms.type === KmsType.Internal && currentKms.isReserved) {
|
||||||
|
return KmsSanitizedSchema.parseAsync({ isExternal: false, ...currentKms });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (kmsId !== INTERNAL_KMS_KEY_ID) {
|
if (kms.type === KmsType.External) {
|
||||||
|
// validate kms is scoped in org
|
||||||
|
const { kmsId } = kms;
|
||||||
const project = await projectDAL.findById(projectId);
|
const project = await projectDAL.findById(projectId);
|
||||||
if (!project) {
|
if (!project) {
|
||||||
throw new NotFoundError({
|
throw new NotFoundError({
|
||||||
message: "Project not found."
|
message: "Project not found."
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const kmsDoc = await kmsDAL.findByIdWithAssociatedKms(kmsId);
|
const kmsDoc = await kmsDAL.findByIdWithAssociatedKms(kmsId);
|
||||||
if (!kmsDoc) {
|
if (!kmsDoc) {
|
||||||
throw new NotFoundError({ message: "KMS ID not found." });
|
throw new NotFoundError({ message: "KMS ID not found." });
|
||||||
@@ -555,38 +619,36 @@ export const kmsServiceFactory = ({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const dataKey = await getProjectSecretManagerKmsDataKey(projectId);
|
const dataKey = await $getProjectSecretManagerKmsDataKey(projectId);
|
||||||
|
|
||||||
return kmsDAL.transaction(async (tx) => {
|
return kmsDAL.transaction(async (tx) => {
|
||||||
const project = await projectDAL.findById(projectId, tx);
|
const project = await projectDAL.findById(projectId, tx);
|
||||||
let newKmsId = kmsId;
|
let kmsId;
|
||||||
|
if (kms.type === KmsType.Internal) {
|
||||||
if (newKmsId === INTERNAL_KMS_KEY_ID) {
|
const internalKms = await generateKmsKey({
|
||||||
const key = await generateKmsKey({
|
|
||||||
isReserved: true,
|
isReserved: true,
|
||||||
orgId: project.orgId,
|
orgId: project.orgId,
|
||||||
tx
|
tx
|
||||||
});
|
});
|
||||||
|
kmsId = internalKms.id;
|
||||||
newKmsId = key.id;
|
} else {
|
||||||
|
kmsId = kms.kmsId;
|
||||||
}
|
}
|
||||||
|
|
||||||
const kmsEncryptor = await encryptWithKmsKey({ kmsId: newKmsId }, tx);
|
const kmsEncryptor = await encryptWithKmsKey({ kmsId }, tx);
|
||||||
const { cipherTextBlob } = await kmsEncryptor({ plainText: dataKey });
|
const { cipherTextBlob } = await kmsEncryptor({ plainText: dataKey });
|
||||||
await projectDAL.updateById(
|
await projectDAL.updateById(
|
||||||
projectId,
|
projectId,
|
||||||
{
|
{
|
||||||
kmsSecretManagerKeyId: newKmsId,
|
kmsSecretManagerKeyId: kmsId,
|
||||||
kmsSecretManagerEncryptedDataKey: cipherTextBlob
|
kmsSecretManagerEncryptedDataKey: cipherTextBlob
|
||||||
},
|
},
|
||||||
tx
|
tx
|
||||||
);
|
);
|
||||||
|
|
||||||
if (currentKms.isReserved) {
|
if (currentKms.isReserved) {
|
||||||
await kmsDAL.deleteById(currentKms.id, tx);
|
await kmsDAL.deleteById(currentKms.id, tx);
|
||||||
}
|
}
|
||||||
|
const newKms = await kmsDAL.findById(kmsId, tx);
|
||||||
return kmsDAL.findByIdWithAssociatedKms(newKmsId, tx);
|
return KmsSanitizedSchema.parseAsync({ isExternal: !currentKms.isReserved, ...newKms });
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -598,13 +660,15 @@ export const kmsServiceFactory = ({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const secretManagerDataKey = await getProjectSecretManagerKmsDataKey(projectId);
|
const secretManagerDataKey = await $getProjectSecretManagerKmsDataKey(projectId);
|
||||||
const kmsKeyIdForEncrypt = await getOrgKmsKeyId(project.orgId);
|
const kmsKeyIdForEncrypt = await getOrgKmsKeyId(project.orgId);
|
||||||
const kmsEncryptor = await encryptWithKmsKey({ kmsId: kmsKeyIdForEncrypt });
|
const kmsEncryptor = await encryptWithKmsKey({ kmsId: kmsKeyIdForEncrypt });
|
||||||
const { cipherTextBlob: encryptedSecretManagerDataKey } = await kmsEncryptor({ plainText: secretManagerDataKey });
|
const { cipherTextBlob: encryptedSecretManagerDataKeyWithOrgKms } = await kmsEncryptor({
|
||||||
|
plainText: secretManagerDataKey
|
||||||
|
});
|
||||||
|
|
||||||
// backup format: version.projectId.kmsFunction.kmsId.Base64(encryptedDataKey).verificationHash
|
// backup format: version.projectId.kmsFunction.kmsId.Base64(encryptedDataKey).verificationHash
|
||||||
let secretManagerBackup = `v1.${projectId}.secretManager.${kmsKeyIdForEncrypt}.${encryptedSecretManagerDataKey.toString(
|
let secretManagerBackup = `v1.${projectId}.secretManager.${kmsKeyIdForEncrypt}.${encryptedSecretManagerDataKeyWithOrgKms.toString(
|
||||||
"base64"
|
"base64"
|
||||||
)}`;
|
)}`;
|
||||||
|
|
||||||
@@ -678,39 +742,8 @@ export const kmsServiceFactory = ({
|
|||||||
message: "KMS not found"
|
message: "KMS not found"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
const { id, slug, orgId, isExternal } = kms;
|
||||||
return kms;
|
return { id, slug, orgId, isExternal };
|
||||||
};
|
|
||||||
|
|
||||||
const $getDataKey = async (dto: TEncryptWithKmsDataKeyDTO) => {
|
|
||||||
switch (dto.type) {
|
|
||||||
case KmsDataKey.SecretManager: {
|
|
||||||
return getProjectSecretManagerKmsDataKey(dto.projectId);
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
return getProjectSecretManagerKmsDataKey(dto.orgId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const createCipherPairWithDataKey = async (encryptionContext: TEncryptWithKmsDataKeyDTO) => {
|
|
||||||
const dataKey = await $getDataKey(encryptionContext);
|
|
||||||
const cipher = symmetricCipherService(SymmetricEncryption.AES_GCM_256);
|
|
||||||
return {
|
|
||||||
encryptor: ({ plainText }: Pick<TEncryptWithKmsDTO, "plainText">) => {
|
|
||||||
const encryptedPlainTextBlob = cipher.encrypt(plainText, dataKey);
|
|
||||||
|
|
||||||
// Buffer#1 encrypted text + Buffer#2 version number
|
|
||||||
const versionBlob = Buffer.from(KMS_VERSION, "utf8"); // length is 3
|
|
||||||
const cipherTextBlob = Buffer.concat([encryptedPlainTextBlob, versionBlob]);
|
|
||||||
return { cipherTextBlob };
|
|
||||||
},
|
|
||||||
decryptor: ({ cipherTextBlob: versionedCipherTextBlob }: Pick<TDecryptWithKeyDTO, "cipherTextBlob">) => {
|
|
||||||
const cipherTextBlob = versionedCipherTextBlob.subarray(0, -KMS_VERSION_BLOB_LENGTH);
|
|
||||||
const decryptedBlob = cipher.decrypt(cipherTextBlob, dataKey);
|
|
||||||
return decryptedBlob;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const startService = async () => {
|
const startService = async () => {
|
||||||
@@ -762,14 +795,11 @@ export const kmsServiceFactory = ({
|
|||||||
startService,
|
startService,
|
||||||
generateKmsKey,
|
generateKmsKey,
|
||||||
encryptWithKmsKey,
|
encryptWithKmsKey,
|
||||||
encryptWithInputKey,
|
|
||||||
decryptWithKmsKey,
|
decryptWithKmsKey,
|
||||||
|
encryptWithInputKey,
|
||||||
decryptWithInputKey,
|
decryptWithInputKey,
|
||||||
getOrgKmsKeyId,
|
getOrgKmsKeyId,
|
||||||
getProjectSecretManagerKmsKeyId,
|
getProjectSecretManagerKmsKeyId,
|
||||||
getOrgKmsDataKey,
|
|
||||||
getProjectSecretManagerKmsDataKey,
|
|
||||||
getProjectSecretManagerKmsKey,
|
|
||||||
updateProjectSecretManagerKmsKey,
|
updateProjectSecretManagerKmsKey,
|
||||||
getProjectKeyBackup,
|
getProjectKeyBackup,
|
||||||
loadProjectKeyBackup,
|
loadProjectKeyBackup,
|
||||||
|
|||||||
@@ -1,5 +1,25 @@
|
|||||||
import { Knex } from "knex";
|
import { Knex } from "knex";
|
||||||
|
|
||||||
|
export enum KmsDataKey {
|
||||||
|
Organization,
|
||||||
|
SecretManager
|
||||||
|
// CertificateManager
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum KmsType {
|
||||||
|
External = "external",
|
||||||
|
Internal = "internal"
|
||||||
|
}
|
||||||
|
|
||||||
|
export type TEncryptWithKmsDataKeyDTO =
|
||||||
|
| { type: KmsDataKey.Organization; orgId: string }
|
||||||
|
| { type: KmsDataKey.SecretManager; projectId: string };
|
||||||
|
// akhilmhdh: not implemented yet
|
||||||
|
// | {
|
||||||
|
// type: KmsDataKey.CertificateManager;
|
||||||
|
// projectId: string;
|
||||||
|
// };
|
||||||
|
|
||||||
export type TGenerateKMSDTO = {
|
export type TGenerateKMSDTO = {
|
||||||
orgId: string;
|
orgId: string;
|
||||||
isReserved?: boolean;
|
isReserved?: boolean;
|
||||||
@@ -27,17 +47,7 @@ export type TDecryptWithKeyDTO = {
|
|||||||
cipherTextBlob: Buffer;
|
cipherTextBlob: Buffer;
|
||||||
};
|
};
|
||||||
|
|
||||||
export enum KmsDataKey {
|
export type TUpdateProjectSecretManagerKmsKeyDTO = {
|
||||||
Organization,
|
projectId: string;
|
||||||
SecretManager
|
kms: { type: KmsType.Internal } | { type: KmsType.External; kmsId: string };
|
||||||
// CertificateManager
|
};
|
||||||
}
|
|
||||||
|
|
||||||
export type TEncryptWithKmsDataKeyDTO =
|
|
||||||
| { type: KmsDataKey.Organization; orgId: string }
|
|
||||||
| { type: KmsDataKey.SecretManager; projectId: string };
|
|
||||||
// akhilmhdh: not implemented yet
|
|
||||||
// | {
|
|
||||||
// type: KmsDataKey.CertificateManager;
|
|
||||||
// projectId: string;
|
|
||||||
// };
|
|
||||||
|
|||||||
@@ -650,7 +650,7 @@ export const projectServiceFactory = ({
|
|||||||
|
|
||||||
const updateProjectKmsKey = async ({
|
const updateProjectKmsKey = async ({
|
||||||
projectId,
|
projectId,
|
||||||
secretManagerKmsKeyId,
|
kms,
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
@@ -666,7 +666,10 @@ export const projectServiceFactory = ({
|
|||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Kms);
|
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Kms);
|
||||||
|
|
||||||
const secretManagerKmsKey = await kmsService.updateProjectSecretManagerKmsKey(projectId, secretManagerKmsKeyId);
|
const secretManagerKmsKey = await kmsService.updateProjectSecretManagerKmsKey({
|
||||||
|
projectId,
|
||||||
|
kms
|
||||||
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
secretManagerKmsKey
|
secretManagerKmsKey
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { TProjectPermission } from "@app/lib/types";
|
|||||||
|
|
||||||
import { ActorAuthMethod, ActorType } from "../auth/auth-type";
|
import { ActorAuthMethod, ActorType } from "../auth/auth-type";
|
||||||
import { CaStatus } from "../certificate-authority/certificate-authority-types";
|
import { CaStatus } from "../certificate-authority/certificate-authority-types";
|
||||||
|
import { KmsType } from "../kms/kms-types";
|
||||||
|
|
||||||
export enum ProjectFilterType {
|
export enum ProjectFilterType {
|
||||||
ID = "id",
|
ID = "id",
|
||||||
@@ -106,7 +107,7 @@ export type TListProjectCertsDTO = {
|
|||||||
} & Omit<TProjectPermission, "projectId">;
|
} & Omit<TProjectPermission, "projectId">;
|
||||||
|
|
||||||
export type TUpdateProjectKmsDTO = {
|
export type TUpdateProjectKmsDTO = {
|
||||||
secretManagerKmsKeyId: string;
|
kms: { type: KmsType.Internal } | { type: KmsType.External; kmsId: string };
|
||||||
} & TProjectPermission;
|
} & TProjectPermission;
|
||||||
|
|
||||||
export type TLoadProjectKmsBackupDTO = {
|
export type TLoadProjectKmsBackupDTO = {
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { useMutation, useQueryClient } from "@tanstack/react-query";
|
|||||||
import { apiRequest } from "@app/config/request";
|
import { apiRequest } from "@app/config/request";
|
||||||
|
|
||||||
import { kmsKeys } from "./queries";
|
import { kmsKeys } from "./queries";
|
||||||
import { AddExternalKmsType } from "./types";
|
import { AddExternalKmsType, KmsType } from "./types";
|
||||||
|
|
||||||
export const useAddExternalKms = (orgId: string) => {
|
export const useAddExternalKms = (orgId: string) => {
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
@@ -66,7 +66,9 @@ export const useRemoveExternalKms = (orgId: string) => {
|
|||||||
export const useUpdateProjectKms = (projectId: string) => {
|
export const useUpdateProjectKms = (projectId: string) => {
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
return useMutation({
|
return useMutation({
|
||||||
mutationFn: async (updatedData: { secretManagerKmsKeyId: string }) => {
|
mutationFn: async (
|
||||||
|
updatedData: { type: KmsType.Internal } | { type: KmsType.External; kmsId: string }
|
||||||
|
) => {
|
||||||
const { data } = await apiRequest.patch(`/api/v1/workspace/${projectId}/kms`, updatedData);
|
const { data } = await apiRequest.patch(`/api/v1/workspace/${projectId}/kms`, updatedData);
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
|
|||||||
@@ -29,6 +29,11 @@ export type KmsListEntry = {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export enum KmsType {
|
||||||
|
Internal = "internal",
|
||||||
|
External = "external"
|
||||||
|
}
|
||||||
|
|
||||||
export enum ExternalKmsProvider {
|
export enum ExternalKmsProvider {
|
||||||
AWS = "aws"
|
AWS = "aws"
|
||||||
}
|
}
|
||||||
|
|||||||
+12
-9
@@ -31,7 +31,7 @@ import {
|
|||||||
useUpdateProjectKms
|
useUpdateProjectKms
|
||||||
} from "@app/hooks/api";
|
} from "@app/hooks/api";
|
||||||
import { fetchProjectKmsBackup } from "@app/hooks/api/kms/queries";
|
import { fetchProjectKmsBackup } from "@app/hooks/api/kms/queries";
|
||||||
import { INTERNAL_KMS_KEY_ID } from "@app/hooks/api/kms/types";
|
import { INTERNAL_KMS_KEY_ID, KmsType } from "@app/hooks/api/kms/types";
|
||||||
import { Organization, Workspace } from "@app/hooks/api/types";
|
import { Organization, Workspace } from "@app/hooks/api/types";
|
||||||
|
|
||||||
const formSchema = z.object({
|
const formSchema = z.object({
|
||||||
@@ -207,7 +207,9 @@ export const EncryptionTab = () => {
|
|||||||
const { data: externalKmsList } = useGetExternalKmsList(currentOrg?.id!);
|
const { data: externalKmsList } = useGetExternalKmsList(currentOrg?.id!);
|
||||||
const { data: activeKms } = useGetActiveProjectKms(currentWorkspace?.id!);
|
const { data: activeKms } = useGetActiveProjectKms(currentWorkspace?.id!);
|
||||||
|
|
||||||
const { mutateAsync: updateProjectKms } = useUpdateProjectKms(currentWorkspace?.id!);
|
const { mutateAsync: updateProjectKms, isLoading: isUpdatingProjectKms } = useUpdateProjectKms(
|
||||||
|
currentWorkspace?.id!
|
||||||
|
);
|
||||||
const { popUp, handlePopUpToggle, handlePopUpOpen } = usePopUp([
|
const { popUp, handlePopUpToggle, handlePopUpOpen } = usePopUp([
|
||||||
"createBackupConfirmation",
|
"createBackupConfirmation",
|
||||||
"loadBackup"
|
"loadBackup"
|
||||||
@@ -239,9 +241,11 @@ export const EncryptionTab = () => {
|
|||||||
|
|
||||||
const onUpdateProjectKms = async (data: TForm) => {
|
const onUpdateProjectKms = async (data: TForm) => {
|
||||||
try {
|
try {
|
||||||
await updateProjectKms({
|
await updateProjectKms(
|
||||||
secretManagerKmsKeyId: data.kmsKeyId
|
data.kmsKeyId === INTERNAL_KMS_KEY_ID
|
||||||
});
|
? { type: KmsType.Internal }
|
||||||
|
: { type: KmsType.External, kmsId: data.kmsKeyId }
|
||||||
|
);
|
||||||
|
|
||||||
createNotification({
|
createNotification({
|
||||||
text: "Successfully updated project KMS",
|
text: "Successfully updated project KMS",
|
||||||
@@ -287,10 +291,9 @@ export const EncryptionTab = () => {
|
|||||||
<FormControl errorText={error?.message} isError={Boolean(error)}>
|
<FormControl errorText={error?.message} isError={Boolean(error)}>
|
||||||
<Select
|
<Select
|
||||||
{...field}
|
{...field}
|
||||||
isDisabled={!isAllowed}
|
isDisabled={!isAllowed || isUpdatingProjectKms}
|
||||||
onValueChange={(e) => {
|
onValueChange={onChange}
|
||||||
onChange(e);
|
isLoading={isUpdatingProjectKms}
|
||||||
}}
|
|
||||||
className="w-3/4 bg-mineshaft-600"
|
className="w-3/4 bg-mineshaft-600"
|
||||||
>
|
>
|
||||||
<SelectItem value={INTERNAL_KMS_KEY_ID} key="kms-internal">
|
<SelectItem value={INTERNAL_KMS_KEY_ID} key="kms-internal">
|
||||||
|
|||||||
Reference in New Issue
Block a user