feat: added kms encryption and decryption secret bridge

This commit is contained in:
=
2024-07-30 23:03:56 +05:30
parent b563c4030b
commit 8eab27d752
3 changed files with 130 additions and 86 deletions
@@ -145,7 +145,8 @@ export const fnSecretsV2FromImports = async ({
secretDAL, secretDAL,
secretImportDAL, secretImportDAL,
depth = 0, depth = 0,
cyclicDetector = new Set() cyclicDetector = new Set(),
decryptor
}: { }: {
allowedImports: (Omit<TSecretImports, "importEnv"> & { allowedImports: (Omit<TSecretImports, "importEnv"> & {
importEnv: { id: string; slug: string; name: string }; importEnv: { id: string; slug: string; name: string };
@@ -155,6 +156,7 @@ export const fnSecretsV2FromImports = async ({
secretImportDAL: Pick<TSecretImportDALFactory, "findByFolderIds">; secretImportDAL: Pick<TSecretImportDALFactory, "findByFolderIds">;
depth?: number; depth?: number;
cyclicDetector?: Set<string>; cyclicDetector?: Set<string>;
decryptor: (value?: Buffer | null) => string | undefined;
}) => { }) => {
// avoid going more than a depth // avoid going more than a depth
if (depth >= LEVEL_BREAK) return []; if (depth >= LEVEL_BREAK) return [];
@@ -203,7 +205,8 @@ export const fnSecretsV2FromImports = async ({
folderDAL, folderDAL,
secretDAL, secretDAL,
depth: depth + 1, depth: depth + 1,
cyclicDetector cyclicDetector,
decryptor
}); });
} }
const secretsFromdeeperImportGroupedByFolderId = groupBy(secretsFromDeeperImports, (i) => i.importFolderId); const secretsFromdeeperImportGroupedByFolderId = groupBy(secretsFromDeeperImports, (i) => i.importFolderId);
@@ -224,8 +227,8 @@ export const fnSecretsV2FromImports = async ({
.map((item) => ({ .map((item) => ({
...item, ...item,
secretKey: item.key, secretKey: item.key,
secretValue: item.encryptedValue?.toString(), secretValue: decryptor(item.encryptedValue),
secretComment: item.encryptedComment?.toString(), secretComment: decryptor(item.encryptedComment),
environment: importEnv.slug, environment: importEnv.slug,
workspace: "", // This field should not be used, it's only here to keep the older Python SDK versions backwards compatible with the new Postgres backend. workspace: "", // This field should not be used, it's only here to keep the older Python SDK versions backwards compatible with the new Postgres backend.
_id: item.id // The old Python SDK depends on the _id field being returned. We return this to keep the older Python SDK versions backwards compatible with the new Postgres backend. _id: item.id // The old Python SDK depends on the _id field being returned. We return this to keep the older Python SDK versions backwards compatible with the new Postgres backend.
@@ -365,7 +365,7 @@ export const recursivelyGetSecretPaths = async ({
type TInterpolateSecretArg = { type TInterpolateSecretArg = {
projectId: string; projectId: string;
decryptSecret: (encryptedValue?: Buffer | null) => string; decryptSecret: (encryptedValue?: Buffer | null) => string | undefined;
secretDAL: Pick<TSecretV2BridgeDALFactory, "findByFolderId">; secretDAL: Pick<TSecretV2BridgeDALFactory, "findByFolderId">;
folderDAL: Pick<TSecretFolderDALFactory, "findBySecretPath">; folderDAL: Pick<TSecretFolderDALFactory, "findBySecretPath">;
}; };
@@ -44,10 +44,16 @@ import {
} from "./secret-v2-bridge-types"; } from "./secret-v2-bridge-types";
import { TSecretVersionV2DALFactory } from "./secret-version-dal"; import { TSecretVersionV2DALFactory } from "./secret-version-dal";
import { TSecretVersionV2TagDALFactory } from "./secret-version-tag-dal"; import { TSecretVersionV2TagDALFactory } from "./secret-version-tag-dal";
import { TKmsServiceFactory } from "../kms/kms-service";
import { TSecretSnapshotServiceFactory } from "@app/ee/services/secret-snapshot/secret-snapshot-service";
type TSecretV2BridgeServiceFactoryDep = { type TSecretV2BridgeServiceFactoryDep = {
secretDAL: TSecretV2BridgeDALFactory; secretDAL: TSecretV2BridgeDALFactory;
secretVersionDAL: TSecretVersionV2DALFactory; secretVersionDAL: TSecretVersionV2DALFactory;
kmsService: Pick<
TKmsServiceFactory,
"getProjectSecretManagerKmsDataKey" | "encryptWithInputKey" | "decryptWithInputKey"
>;
secretVersionTagDAL: Pick<TSecretVersionV2TagDALFactory, "insertMany">; secretVersionTagDAL: Pick<TSecretVersionV2TagDALFactory, "insertMany">;
secretTagDAL: TSecretTagDALFactory; secretTagDAL: TSecretTagDALFactory;
permissionService: Pick<TPermissionServiceFactory, "getProjectPermission">; permissionService: Pick<TPermissionServiceFactory, "getProjectPermission">;
@@ -64,10 +70,22 @@ type TSecretV2BridgeServiceFactoryDep = {
TSecretApprovalRequestSecretDALFactory, TSecretApprovalRequestSecretDALFactory,
"insertMany" | "insertApprovalSecretTags" "insertMany" | "insertApprovalSecretTags"
>; >;
snapshotService: Pick<TSecretSnapshotServiceFactory, "performSnapshot">;
}; };
export type TSecretV2BridgeServiceFactory = ReturnType<typeof secretV2BridgeServiceFactory>; export type TSecretV2BridgeServiceFactory = ReturnType<typeof secretV2BridgeServiceFactory>;
const encryptionHelper = {
encryptValue: (encryptor: Awaited<ReturnType<TKmsServiceFactory["encryptWithKmsKey"]>>, value?: string) => {
if (typeof value === "undefined") return;
return encryptor({ plainText: Buffer.from(value) }).cipherTextBlob;
},
decryptValue: (decryptor: Awaited<ReturnType<TKmsServiceFactory["decryptWithInputKey"]>>, value?: Buffer | null) => {
if (!value) return;
return decryptor({ cipherTextBlob: value }).toString();
}
};
/* /*
* This service is a bridge from our old architecture towards the new architecture * This service is a bridge from our old architecture towards the new architecture
*/ */
@@ -84,7 +102,8 @@ export const secretV2BridgeServiceFactory = ({
secretVersionTagDAL, secretVersionTagDAL,
secretApprovalPolicyService, secretApprovalPolicyService,
secretApprovalRequestDAL, secretApprovalRequestDAL,
secretApprovalRequestSecretDAL secretApprovalRequestSecretDAL,
kmsService
}: TSecretV2BridgeServiceFactoryDep) => { }: TSecretV2BridgeServiceFactoryDep) => {
const createSecret = async ({ const createSecret = async ({
actor, actor,
@@ -141,6 +160,9 @@ export const secretV2BridgeServiceFactory = ({
const { secretName, type, ...el } = inputSecret; const { secretName, type, ...el } = inputSecret;
const references = getAllNestedSecretReferences(inputSecret.secretValue); const references = getAllNestedSecretReferences(inputSecret.secretValue);
const secretManagerDataKey = await kmsService.getProjectSecretManagerKmsDataKey(projectId);
const secretManagerEncryptor = await kmsService.encryptWithInputKey({ key: secretManagerDataKey });
const secret = await secretDAL.transaction((tx) => const secret = await secretDAL.transaction((tx) =>
fnSecretBulkInsert({ fnSecretBulkInsert({
folderId, folderId,
@@ -149,8 +171,8 @@ export const secretV2BridgeServiceFactory = ({
version: 1, version: 1,
type, type,
reminderRepeatDays: el.secretReminderRepeatDays, reminderRepeatDays: el.secretReminderRepeatDays,
encryptedComment: el.secretComment ? Buffer.from(el.secretComment) : undefined, encryptedComment: encryptionHelper.encryptValue(secretManagerEncryptor, el.secretComment),
encryptedValue: el.secretValue ? Buffer.from(el.secretValue) : undefined, encryptedValue: encryptionHelper.encryptValue(secretManagerEncryptor, el.secretValue),
reminderNote: el.secretReminderNote, reminderNote: el.secretReminderNote,
skipMultilineEncoding: el.skipMultilineEncoding, skipMultilineEncoding: el.skipMultilineEncoding,
key: secretName, key: secretName,
@@ -167,7 +189,7 @@ export const secretV2BridgeServiceFactory = ({
}) })
); );
// await snapshotService.performSnapshot(folderId); await snapshotService.performSnapshot(folderId);
await secretQueueService.syncSecrets({ await secretQueueService.syncSecrets({
secretPath, secretPath,
actorId, actorId,
@@ -264,6 +286,16 @@ export const secretV2BridgeServiceFactory = ({
const { secretName, secretValue, secretComment } = inputSecret; const { secretName, secretValue, secretComment } = inputSecret;
const secretManagerDataKey = await kmsService.getProjectSecretManagerKmsDataKey(projectId);
const secretManagerEncryptor = await kmsService.encryptWithInputKey({ key: secretManagerDataKey });
const encryptedValue =
typeof secretValue !== "undefined"
? {
encryptedValue: encryptionHelper.encryptValue(secretManagerEncryptor, secretValue) as Buffer,
references: getAllNestedSecretReferences(secretValue)
}
: {};
const updatedSecret = await secretDAL.transaction(async (tx) => const updatedSecret = await secretDAL.transaction(async (tx) =>
fnSecretBulkUpdate({ fnSecretBulkUpdate({
folderId, folderId,
@@ -272,20 +304,12 @@ export const secretV2BridgeServiceFactory = ({
filter: { id: secretId }, filter: { id: secretId },
data: { data: {
reminderRepeatDays: inputSecret.secretReminderRepeatDays, reminderRepeatDays: inputSecret.secretReminderRepeatDays,
encryptedComment: secretComment ? Buffer.from(secretComment) : undefined, encryptedComment: encryptionHelper.encryptValue(secretManagerEncryptor, secretComment),
reminderNote: inputSecret.secretReminderNote, reminderNote: inputSecret.secretReminderNote,
skipMultilineEncoding: inputSecret.skipMultilineEncoding, skipMultilineEncoding: inputSecret.skipMultilineEncoding,
key: inputSecret.newSecretName || secretName, key: inputSecret.newSecretName || secretName,
tags: inputSecret.tagIds, tags: inputSecret.tagIds,
...(secretValue ...encryptedValue
? {
encryptedValue: Buffer.from(secretValue),
references: getAllNestedSecretReferences(secretValue)
}
: {
encryptedValue: undefined,
references: undefined
})
} }
} }
], ],
@@ -305,7 +329,7 @@ export const secretV2BridgeServiceFactory = ({
projectId projectId
}); });
// await snapshotService.performSnapshot(folderId); await snapshotService.performSnapshot(folderId);
await secretQueueService.syncSecrets({ await secretQueueService.syncSecrets({
actor, actor,
actorId, actorId,
@@ -315,9 +339,8 @@ export const secretV2BridgeServiceFactory = ({
}); });
return reshapeBridgeSecret(projectId, environment, secretPath, { return reshapeBridgeSecret(projectId, environment, secretPath, {
...updatedSecret[0], ...updatedSecret[0],
// TODO(akhilmhdh-sev2): fix this value: inputSecret.secretValue,
value: updatedSecret[0].encryptedValue?.toString(), comment: inputSecret.secretComment
comment: updatedSecret[0].encryptedComment?.toString()
}); });
}; };
@@ -372,7 +395,7 @@ export const secretV2BridgeServiceFactory = ({
}) })
); );
// await snapshotService.performSnapshot(folderId); await snapshotService.performSnapshot(folderId);
await secretQueueService.syncSecrets({ await secretQueueService.syncSecrets({
actor, actor,
actorId, actorId,
@@ -380,11 +403,13 @@ export const secretV2BridgeServiceFactory = ({
projectId, projectId,
environmentSlug: folder.environment.slug environmentSlug: folder.environment.slug
}); });
const secretManagerDataKey = await kmsService.getProjectSecretManagerKmsDataKey(projectId);
const secretManagerDecryptor = await kmsService.decryptWithInputKey({ key: secretManagerDataKey });
return reshapeBridgeSecret(projectId, environment, secretPath, { return reshapeBridgeSecret(projectId, environment, secretPath, {
...deletedSecret[0], ...deletedSecret[0],
// TODO(akhilmhdh-sev2): fix this value: encryptionHelper.decryptValue(secretManagerDecryptor, deletedSecret[0].encryptedValue),
value: deletedSecret[0].encryptedValue?.toString(), comment: encryptionHelper.decryptValue(secretManagerDecryptor, deletedSecret[0].encryptedComment)
comment: deletedSecret[0].encryptedComment?.toString()
}); });
}; };
@@ -448,6 +473,9 @@ export const secretV2BridgeServiceFactory = ({
actorId actorId
); );
const secretManagerDataKey = await kmsService.getProjectSecretManagerKmsDataKey(projectId);
const secretManagerDecryptor = await kmsService.decryptWithInputKey({ key: secretManagerDataKey });
if (includeImports) { if (includeImports) {
const secretImports = await secretImportDAL.findByFolderIds(paths.map((p) => p.folderId)); const secretImports = await secretImportDAL.findByFolderIds(paths.map((p) => p.folderId));
const allowedImports = secretImports.filter(({ importEnv, importPath, isReplication }) => const allowedImports = secretImports.filter(({ importEnv, importPath, isReplication }) =>
@@ -467,16 +495,16 @@ export const secretV2BridgeServiceFactory = ({
allowedImports, allowedImports,
secretDAL, secretDAL,
folderDAL, folderDAL,
secretImportDAL secretImportDAL,
decryptor: (value) => encryptionHelper.decryptValue(secretManagerDecryptor, value)
}); });
return { return {
secrets: secrets.map((secret) => secrets: secrets.map((secret) =>
reshapeBridgeSecret(projectId, environment, groupedPaths[secret.folderId][0].path, { reshapeBridgeSecret(projectId, environment, groupedPaths[secret.folderId][0].path, {
...secret, ...secret,
// TODO(akhilmhdh-sev2): decryption missiong value: encryptionHelper.decryptValue(secretManagerDecryptor, secret.encryptedValue),
value: secret.encryptedValue?.toString(), comment: encryptionHelper.decryptValue(secretManagerDecryptor, secret.encryptedComment)
comment: secret.encryptedComment?.toString()
}) })
), ),
imports: importedSecrets imports: importedSecrets
@@ -487,9 +515,8 @@ export const secretV2BridgeServiceFactory = ({
secrets: secrets.map((secret) => secrets: secrets.map((secret) =>
reshapeBridgeSecret(projectId, environment, groupedPaths[secret.folderId][0].path, { reshapeBridgeSecret(projectId, environment, groupedPaths[secret.folderId][0].path, {
...secret, ...secret,
// TODO(akhilmhdh-sev2): decrypt this value: encryptionHelper.decryptValue(secretManagerDecryptor, secret.encryptedValue),
value: secret.encryptedValue?.toString(), comment: encryptionHelper.decryptValue(secretManagerDecryptor, secret.encryptedComment)
comment: secret.encryptedComment?.toString()
}) })
) )
}; };
@@ -536,6 +563,9 @@ export const secretV2BridgeServiceFactory = ({
secretType = SecretType.Shared; secretType = SecretType.Shared;
} }
const secretManagerDataKey = await kmsService.getProjectSecretManagerKmsDataKey(projectId);
const secretManagerDecryptor = await kmsService.decryptWithInputKey({ key: secretManagerDataKey });
const secret = await (version === undefined const secret = await (version === undefined
? secretDAL.findOneWithTags({ ? secretDAL.findOneWithTags({
folderId, folderId,
@@ -551,13 +581,13 @@ export const secretV2BridgeServiceFactory = ({
key: secretName key: secretName
}) })
.then((el) => SecretsV2Schema.parse({ ...el, id: el.secretId }))); .then((el) => SecretsV2Schema.parse({ ...el, id: el.secretId })));
// TODO(akhilmhdh-sev2): resolve this decryptSecret
const interpolateInlineSecretReference = interpolateSecrets({ const interpolateInlineSecretReference = interpolateSecrets({
projectId, projectId,
decryptSecret: () => "", decryptSecret: (encryptedValue) => encryptionHelper.decryptValue(secretManagerDecryptor, encryptedValue),
secretDAL, secretDAL,
folderDAL folderDAL
}); });
// now if secret is not found // now if secret is not found
// then search for imported secrets // then search for imported secrets
// here we consider the import order also thus starting from bottom // here we consider the import order also thus starting from bottom
@@ -579,13 +609,15 @@ export const secretV2BridgeServiceFactory = ({
allowedImports, allowedImports,
secretDAL, secretDAL,
folderDAL, folderDAL,
secretImportDAL secretImportDAL,
decryptor: (value) => encryptionHelper.decryptValue(secretManagerDecryptor, value)
}); });
for (let i = importedSecrets.length - 1; i >= 0; i -= 1) { for (let i = importedSecrets.length - 1; i >= 0; i -= 1) {
for (let j = 0; j < importedSecrets[i].secrets.length; j += 1) { for (let j = 0; j < importedSecrets[i].secrets.length; j += 1) {
if (secretName === importedSecrets[i].secrets[j].key) { if (secretName === importedSecrets[i].secrets[j].key) {
const importedSecret = importedSecrets[i].secrets[j]; const importedSecret = importedSecrets[i].secrets[j];
let secretValue = importedSecret.encryptedValue ? importedSecret.encryptedValue.toString() : undefined; let secretValue = encryptionHelper.decryptValue(secretManagerDecryptor, importedSecret.encryptedValue);
if (expandSecretReferences && secretValue) { if (expandSecretReferences && secretValue) {
const secretReferenceExpandedString = { const secretReferenceExpandedString = {
[importedSecret.key]: { value: secretValue } [importedSecret.key]: { value: secretValue }
@@ -595,11 +627,10 @@ export const secretV2BridgeServiceFactory = ({
secretValue = secretReferenceExpandedString[importedSecret.key].value; secretValue = secretReferenceExpandedString[importedSecret.key].value;
} }
// TODO(akhilmhdh-sev2): decrypt this
return reshapeBridgeSecret(projectId, importedSecrets[i].environment, importedSecrets[i].secretPath, { return reshapeBridgeSecret(projectId, importedSecrets[i].environment, importedSecrets[i].secretPath, {
...importedSecret, ...importedSecret,
value: secretValue, value: secretValue,
comment: importedSecret.encryptedComment?.toString() comment: encryptionHelper.decryptValue(secretManagerDecryptor, importedSecret.encryptedComment)
}); });
} }
} }
@@ -607,7 +638,7 @@ export const secretV2BridgeServiceFactory = ({
} }
if (!secret) throw new BadRequestError({ message: "Secret not found" }); if (!secret) throw new BadRequestError({ message: "Secret not found" });
let secretValue = secret.encryptedValue ? secret.encryptedValue.toString() : undefined; let secretValue = encryptionHelper.decryptValue(secretManagerDecryptor, secret.encryptedValue);
if (expandSecretReferences && secretValue) { if (expandSecretReferences && secretValue) {
const secretReferenceExpandedString = { const secretReferenceExpandedString = {
[secret.key]: { value: secretValue } [secret.key]: { value: secretValue }
@@ -617,11 +648,10 @@ export const secretV2BridgeServiceFactory = ({
secretValue = secretReferenceExpandedString[secret.key].value; secretValue = secretReferenceExpandedString[secret.key].value;
} }
// TODO(akhilmhdh-sev2): fix this
return reshapeBridgeSecret(projectId, environment, path, { return reshapeBridgeSecret(projectId, environment, path, {
...secret, ...secret,
value: secretValue, value: secretValue,
comment: secret.encryptedComment?.toString() comment: encryptionHelper.decryptValue(secretManagerDecryptor, secret.encryptedComment)
}); });
}; };
@@ -670,12 +700,15 @@ export const secretV2BridgeServiceFactory = ({
const tags = sanitizedTagIds.length ? await secretTagDAL.findManyTagsById(projectId, sanitizedTagIds) : []; const tags = sanitizedTagIds.length ? await secretTagDAL.findManyTagsById(projectId, sanitizedTagIds) : [];
if (tags.length !== sanitizedTagIds.length) throw new BadRequestError({ message: "Tag not found" }); if (tags.length !== sanitizedTagIds.length) throw new BadRequestError({ message: "Tag not found" });
const secretManagerDataKey = await kmsService.getProjectSecretManagerKmsDataKey(projectId);
const secretManagerEncryptor = await kmsService.encryptWithInputKey({ key: secretManagerDataKey });
const newSecrets = await secretDAL.transaction(async (tx) => const newSecrets = await secretDAL.transaction(async (tx) =>
fnSecretBulkInsert({ fnSecretBulkInsert({
inputSecrets: inputSecrets.map((el) => ({ inputSecrets: inputSecrets.map((el) => ({
version: 1, version: 1,
encryptedComment: el.secretComment ? Buffer.from(el.secretComment) : undefined, encryptedComment: encryptionHelper.encryptValue(secretManagerEncryptor, el.secretComment),
encryptedValue: el.secretValue ? Buffer.from(el.secretValue) : undefined, encryptedValue: encryptionHelper.encryptValue(secretManagerEncryptor, el.secretValue),
skipMultilineEncoding: el.skipMultilineEncoding, skipMultilineEncoding: el.skipMultilineEncoding,
key: el.secretKey, key: el.secretKey,
tagIds: el.tagIds, tagIds: el.tagIds,
@@ -691,7 +724,7 @@ export const secretV2BridgeServiceFactory = ({
}) })
); );
// await snapshotService.performSnapshot(folderId); await snapshotService.performSnapshot(folderId);
await secretQueueService.syncSecrets({ await secretQueueService.syncSecrets({
actor, actor,
actorId, actorId,
@@ -700,12 +733,12 @@ export const secretV2BridgeServiceFactory = ({
environmentSlug: folder.environment.slug environmentSlug: folder.environment.slug
}); });
const secretManagerDecryptor = await kmsService.decryptWithInputKey({ key: secretManagerDataKey });
return newSecrets.map((el) => return newSecrets.map((el) =>
reshapeBridgeSecret(projectId, environment, secretPath, { reshapeBridgeSecret(projectId, environment, secretPath, {
...el, ...el,
// TODO(akhilmhdh-sev2): decryption missiong value: encryptionHelper.decryptValue(secretManagerDecryptor, el.encryptedValue),
value: el.encryptedValue?.toString(), comment: encryptionHelper.decryptValue(secretManagerDecryptor, el.encryptedComment)
comment: el.encryptedComment?.toString()
}) })
); );
}; };
@@ -770,38 +803,41 @@ export const secretV2BridgeServiceFactory = ({
const tags = sanitizedTagIds.length ? await secretTagDAL.findManyTagsById(projectId, sanitizedTagIds) : []; const tags = sanitizedTagIds.length ? await secretTagDAL.findManyTagsById(projectId, sanitizedTagIds) : [];
if (tags.length !== sanitizedTagIds.length) throw new BadRequestError({ message: "Tag not found" }); if (tags.length !== sanitizedTagIds.length) throw new BadRequestError({ message: "Tag not found" });
const secretManagerDataKey = await kmsService.getProjectSecretManagerKmsDataKey(projectId);
const secretManagerEncryptor = await kmsService.encryptWithInputKey({ key: secretManagerDataKey });
const secrets = await secretDAL.transaction(async (tx) => const secrets = await secretDAL.transaction(async (tx) =>
fnSecretBulkUpdate({ fnSecretBulkUpdate({
folderId, folderId,
tx, tx,
inputSecrets: inputSecrets.map((el) => ({ inputSecrets: inputSecrets.map((el) => {
filter: { key: el.secretKey, type: SecretType.Shared }, const encryptedValue =
data: { typeof el.secretValue !== "undefined"
reminderRepeatDays: el.secretReminderRepeatDays,
encryptedComment: el.secretComment ? Buffer.from(el.secretComment) : undefined,
reminderNote: el.secretReminderNote,
skipMultilineEncoding: el.skipMultilineEncoding,
key: el.newSecretName || el.secretKey,
tags: el.tagIds,
...(el.secretValue
? { ? {
encryptedValue: Buffer.from(el.secretValue), encryptedValue: encryptionHelper.encryptValue(secretManagerEncryptor, el.secretValue) as Buffer,
references: getAllNestedSecretReferences(el.secretValue) references: getAllNestedSecretReferences(el.secretValue)
} }
: { : {};
encryptedValue: undefined, return {
references: undefined filter: { key: el.secretKey, type: SecretType.Shared },
}) data: {
} reminderRepeatDays: el.secretReminderRepeatDays,
})), encryptedComment: encryptionHelper.encryptValue(secretManagerEncryptor, el.secretComment),
reminderNote: el.secretReminderNote,
skipMultilineEncoding: el.skipMultilineEncoding,
key: el.newSecretName || el.secretKey,
tags: el.tagIds,
...encryptedValue
}
};
}),
secretDAL, secretDAL,
secretVersionDAL, secretVersionDAL,
secretTagDAL, secretTagDAL,
secretVersionTagDAL secretVersionTagDAL
}) })
); );
await snapshotService.performSnapshot(folderId);
// await snapshotService.performSnapshot(folderId);
await secretQueueService.syncSecrets({ await secretQueueService.syncSecrets({
actor, actor,
actorId, actorId,
@@ -810,12 +846,12 @@ export const secretV2BridgeServiceFactory = ({
environmentSlug: folder.environment.slug environmentSlug: folder.environment.slug
}); });
const secretManagerDecryptor = await kmsService.decryptWithInputKey({ key: secretManagerDataKey });
return secrets.map((el) => return secrets.map((el) =>
reshapeBridgeSecret(projectId, environment, secretPath, { reshapeBridgeSecret(projectId, environment, secretPath, {
...el, ...el,
// TODO(akhilmhdh-sev2): decryption missiong value: encryptionHelper.decryptValue(secretManagerDecryptor, el.encryptedValue),
value: el.encryptedValue?.toString(), comment: encryptionHelper.decryptValue(secretManagerDecryptor, el.encryptedComment)
comment: el.encryptedComment?.toString()
}) })
); );
}; };
@@ -884,12 +920,13 @@ export const secretV2BridgeServiceFactory = ({
environmentSlug: folder.environment.slug environmentSlug: folder.environment.slug
}); });
const secretManagerDataKey = await kmsService.getProjectSecretManagerKmsDataKey(projectId);
const secretManagerDecryptor = await kmsService.decryptWithInputKey({ key: secretManagerDataKey });
return secretsDeleted.map((el) => return secretsDeleted.map((el) =>
reshapeBridgeSecret(projectId, environment, secretPath, { reshapeBridgeSecret(projectId, environment, secretPath, {
...el, ...el,
// TODO(akhilmhdh-sev2): decryption missiong value: encryptionHelper.decryptValue(secretManagerDecryptor, el.encryptedValue),
value: el.encryptedValue?.toString(), comment: encryptionHelper.decryptValue(secretManagerDecryptor, el.encryptedComment)
comment: el.encryptedComment?.toString()
}) })
); );
}; };
@@ -943,15 +980,20 @@ export const secretV2BridgeServiceFactory = ({
if (!hasRole(ProjectMembershipRole.Admin)) if (!hasRole(ProjectMembershipRole.Admin))
throw new BadRequestError({ message: "Only admins are allowed to take this action" }); throw new BadRequestError({ message: "Only admins are allowed to take this action" });
const secretManagerDataKey = await kmsService.getProjectSecretManagerKmsDataKey(projectId);
const secretManagerDecryptor = await kmsService.decryptWithInputKey({ key: secretManagerDataKey });
await secretDAL.transaction(async (tx) => { await secretDAL.transaction(async (tx) => {
const secrets = await secretDAL.findAllProjectSecretValues(projectId, tx); const secrets = await secretDAL.findAllProjectSecretValues(projectId, tx);
// TODO(akhilmhdh-sev2): decryption missing
await secretDAL.upsertSecretReferences( await secretDAL.upsertSecretReferences(
secrets secrets
.filter((el) => Boolean(el.encryptedValue)) .filter((el) => Boolean(el.encryptedValue))
.map(({ id, encryptedValue }) => ({ .map(({ id, encryptedValue }) => ({
secretId: id, secretId: id,
references: getAllNestedSecretReferences(encryptedValue?.toString("utf8")) references: encryptedValue
? getAllNestedSecretReferences(
encryptionHelper.decryptValue(secretManagerDecryptor, encryptedValue) as string
)
: []
})), })),
tx tx
); );
@@ -1028,10 +1070,11 @@ export const secretV2BridgeServiceFactory = ({
}); });
} }
const secretManagerDataKey = await kmsService.getProjectSecretManagerKmsDataKey(projectId);
const secretManagerDecryptor = await kmsService.decryptWithInputKey({ key: secretManagerDataKey });
const decryptedSourceSecrets = sourceSecrets.map((secret) => ({ const decryptedSourceSecrets = sourceSecrets.map((secret) => ({
...secret, ...secret,
// TODO(akhilmhdh-sev2): decryption missiong value: encryptionHelper.decryptValue(secretManagerDecryptor, secret.encryptedValue)
value: secret.encryptedValue?.toString()
})); }));
let isSourceUpdated = false; let isSourceUpdated = false;
@@ -1050,8 +1093,7 @@ export const secretV2BridgeServiceFactory = ({
const decryptedDestinationSecrets = destinationSecretsFromDB.map((secret) => { const decryptedDestinationSecrets = destinationSecretsFromDB.map((secret) => {
return { return {
...secret, ...secret,
// TODO(akhilmhdh-sev2): decryption missiong value: encryptionHelper.decryptValue(secretManagerDecryptor, secret.encryptedValue)
value: secret.encryptedValue?.toString()
}; };
}); });
@@ -1154,8 +1196,8 @@ export const secretV2BridgeServiceFactory = ({
type: doc.type, type: doc.type,
metadata: doc.metadata, metadata: doc.metadata,
key: doc.key, key: doc.key,
encryptedValue: doc.encryptedValue ? Buffer.from(doc.encryptedValue) : undefined, encryptedValue: doc.encryptedValue,
encryptedComment: doc.encryptedComment ? Buffer.from(doc.encryptedComment) : undefined, encryptedComment: doc.encryptedComment,
skipMultilineEncoding: doc.skipMultilineEncoding, skipMultilineEncoding: doc.skipMultilineEncoding,
reminderNote: doc.reminderNote, reminderNote: doc.reminderNote,
reminderRepeatDays: doc.reminderRepeatDays, reminderRepeatDays: doc.reminderRepeatDays,
@@ -1188,8 +1230,7 @@ export const secretV2BridgeServiceFactory = ({
...(doc.encryptedValue ...(doc.encryptedValue
? { ? {
encryptedValue: doc.encryptedValue, encryptedValue: doc.encryptedValue,
// TODO(akhilmhdh-sev2): fix decryption references: doc.value ? getAllNestedSecretReferences(doc.value) : []
references: getAllNestedSecretReferences(doc.encryptedValue.toString())
} }
: { : {
encryptedValue: undefined, encryptedValue: undefined,
@@ -1275,7 +1316,7 @@ export const secretV2BridgeServiceFactory = ({
}); });
if (isDestinationUpdated) { if (isDestinationUpdated) {
// await snapshotService.performSnapshot(destinationFolder.id); await snapshotService.performSnapshot(destinationFolder.id);
await secretQueueService.syncSecrets({ await secretQueueService.syncSecrets({
projectId, projectId,
secretPath: destinationFolder.path, secretPath: destinationFolder.path,
@@ -1286,7 +1327,7 @@ export const secretV2BridgeServiceFactory = ({
} }
if (isSourceUpdated) { if (isSourceUpdated) {
// await snapshotService.performSnapshot(sourceFolder.id); await snapshotService.performSnapshot(sourceFolder.id);
await secretQueueService.syncSecrets({ await secretQueueService.syncSecrets({
projectId, projectId,
secretPath: sourceFolder.path, secretPath: sourceFolder.path,