feat: all the services are now working with secrets v2 architecture

This commit is contained in:
=
2024-07-30 23:17:41 +05:30
parent 643d13b0ec
commit 36ac1f47ca
18 changed files with 562 additions and 320 deletions
@@ -115,6 +115,22 @@ export async function up(knex: Knex): Promise<void> {
t.timestamps(true, true, true); t.timestamps(true, true, true);
}); });
} }
if (await knex.schema.hasTable(TableName.IntegrationAuth)) {
const hasEncryptedAccess = await knex.schema.hasColumn(TableName.IntegrationAuth, "encryptedAccess");
const hasEncryptedAccessId = await knex.schema.hasColumn(TableName.IntegrationAuth, "encryptedAccessId");
const hasEncryptedRefresh = await knex.schema.hasColumn(TableName.IntegrationAuth, "encryptedRefresh");
const hasEncryptedAwsIamAssumRole = await knex.schema.hasColumn(
TableName.IntegrationAuth,
"encryptedAwsAssumeIamRoleArn"
);
await knex.schema.alterTable(TableName.IntegrationAuth, (t) => {
if (!hasEncryptedAccess) t.binary("encryptedAccess");
if (!hasEncryptedAccessId) t.binary("encryptedAccessId");
if (!hasEncryptedRefresh) t.binary("encryptedRefresh");
if (!hasEncryptedAwsIamAssumRole) t.binary("hasEncryptedAwsIamAssumRole");
});
}
} }
export async function down(knex: Knex): Promise<void> { export async function down(knex: Knex): Promise<void> {
@@ -131,4 +147,20 @@ export async function down(knex: Knex): Promise<void> {
await dropOnUpdateTrigger(knex, TableName.SecretVersionV2); await dropOnUpdateTrigger(knex, TableName.SecretVersionV2);
await knex.schema.dropTableIfExists(TableName.SecretVersionV2Tag); await knex.schema.dropTableIfExists(TableName.SecretVersionV2Tag);
await knex.schema.dropTableIfExists(TableName.SecretVersionV2); await knex.schema.dropTableIfExists(TableName.SecretVersionV2);
if (await knex.schema.hasTable(TableName.IntegrationAuth)) {
const hasEncryptedAccess = await knex.schema.hasColumn(TableName.IntegrationAuth, "encryptedAccess");
const hasEncryptedAccessId = await knex.schema.hasColumn(TableName.IntegrationAuth, "encryptedAccessId");
const hasEncryptedRefresh = await knex.schema.hasColumn(TableName.IntegrationAuth, "encryptedRefresh");
const hasEncryptedAwsIamAssumRole = await knex.schema.hasColumn(
TableName.IntegrationAuth,
"encryptedAwsAssumeIamRoleArn"
);
await knex.schema.alterTable(TableName.IntegrationAuth, (t) => {
if (hasEncryptedAccess) t.dropColumn("encryptedAccess");
if (hasEncryptedAccessId) t.dropColumn("encryptedAccessId");
if (hasEncryptedRefresh) t.dropColumn("encryptedRefresh");
if (hasEncryptedAwsIamAssumRole) t.dropColumn("hasEncryptedAwsIamAssumRole");
});
}
} }
+7 -1
View File
@@ -5,6 +5,8 @@
import { z } from "zod"; import { z } from "zod";
import { zodBuffer } from "@app/lib/zod";
import { TImmutableDBKeys } from "./models"; import { TImmutableDBKeys } from "./models";
export const IntegrationAuthsSchema = z.object({ export const IntegrationAuthsSchema = z.object({
@@ -32,7 +34,11 @@ export const IntegrationAuthsSchema = z.object({
updatedAt: z.date(), updatedAt: z.date(),
awsAssumeIamRoleArnCipherText: z.string().nullable().optional(), awsAssumeIamRoleArnCipherText: z.string().nullable().optional(),
awsAssumeIamRoleArnIV: z.string().nullable().optional(), awsAssumeIamRoleArnIV: z.string().nullable().optional(),
awsAssumeIamRoleArnTag: z.string().nullable().optional() awsAssumeIamRoleArnTag: z.string().nullable().optional(),
encryptedAccess: zodBuffer.nullable().optional(),
encryptedAccessId: zodBuffer.nullable().optional(),
encryptedRefresh: zodBuffer.nullable().optional(),
hasEncryptedAwsIamAssumRole: zodBuffer.nullable().optional()
}); });
export type TIntegrationAuths = z.infer<typeof IntegrationAuthsSchema>; export type TIntegrationAuths = z.infer<typeof IntegrationAuthsSchema>;
@@ -16,6 +16,7 @@ export type TSecretApprovalRequestSecretDALFactory = ReturnType<typeof secretApp
export const secretApprovalRequestSecretDALFactory = (db: TDbClient) => { export const secretApprovalRequestSecretDALFactory = (db: TDbClient) => {
const secretApprovalRequestSecretOrm = ormify(db, TableName.SecretApprovalRequestSecret); const secretApprovalRequestSecretOrm = ormify(db, TableName.SecretApprovalRequestSecret);
const secretApprovalRequestSecretTagOrm = ormify(db, TableName.SecretApprovalRequestSecretTag); const secretApprovalRequestSecretTagOrm = ormify(db, TableName.SecretApprovalRequestSecretTag);
const secretApprovalRequestSecretV2TagOrm = ormify(db, TableName.SecretApprovalRequestSecretTagV2);
const secretApprovalRequestSecretV2Orm = ormify(db, TableName.SecretApprovalRequestSecretV2); const secretApprovalRequestSecretV2Orm = ormify(db, TableName.SecretApprovalRequestSecretV2);
const bulkUpdateNoVersionIncrement = async (data: TSecretApprovalRequestsSecrets[], tx?: Knex) => { const bulkUpdateNoVersionIncrement = async (data: TSecretApprovalRequestsSecrets[], tx?: Knex) => {
@@ -359,6 +360,7 @@ export const secretApprovalRequestSecretDALFactory = (db: TDbClient) => {
findByRequestId, findByRequestId,
findByRequestIdBridgeSecretV2, findByRequestIdBridgeSecretV2,
bulkUpdateNoVersionIncrement, bulkUpdateNoVersionIncrement,
insertApprovalSecretTags: secretApprovalRequestSecretTagOrm.insertMany insertApprovalSecretTags: secretApprovalRequestSecretTagOrm.insertMany,
insertApprovalSecretV2Tags: secretApprovalRequestSecretV2TagOrm.insertMany
}; };
}; };
@@ -15,6 +15,7 @@ import { groupBy, pick, unique } from "@app/lib/fn";
import { alphaNumericNanoId } from "@app/lib/nanoid"; 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 { 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";
@@ -28,13 +29,6 @@ import {
fnSecretBulkUpdate, fnSecretBulkUpdate,
getAllNestedSecretReferences getAllNestedSecretReferences
} from "@app/services/secret/secret-fns"; } from "@app/services/secret/secret-fns";
import {
fnSecretBulkInsert as fnSecretV2BridgeBulkInsert,
fnSecretBulkUpdate as fnSecretV2BridgeBulkUpdate,
fnSecretBulkDelete as fnSecretV2BridgeBulkDelete,
getAllNestedSecretReferences as getAllNestedSecretReferencesV2Bridge,
secretEncryptionHelper
} from "@app/services/secret-v2-bridge/secret-v2-bridge-fns";
import { TSecretQueueFactory } from "@app/services/secret/secret-queue"; import { TSecretQueueFactory } from "@app/services/secret/secret-queue";
import { SecretOperations } from "@app/services/secret/secret-types"; import { SecretOperations } from "@app/services/secret/secret-types";
import { TSecretVersionDALFactory } from "@app/services/secret/secret-version-dal"; import { TSecretVersionDALFactory } from "@app/services/secret/secret-version-dal";
@@ -44,6 +38,16 @@ import { TSecretFolderDALFactory } from "@app/services/secret-folder/secret-fold
import { TSecretTagDALFactory } from "@app/services/secret-tag/secret-tag-dal"; import { TSecretTagDALFactory } from "@app/services/secret-tag/secret-tag-dal";
import { SmtpTemplates, TSmtpService } from "@app/services/smtp/smtp-service"; import { SmtpTemplates, TSmtpService } from "@app/services/smtp/smtp-service";
import { TUserDALFactory } from "@app/services/user/user-dal"; import { TUserDALFactory } from "@app/services/user/user-dal";
import { TSecretV2BridgeDALFactory } from "@app/services/secret-v2-bridge/secret-v2-bridge-dal";
import {
fnSecretBulkDelete as fnSecretV2BridgeBulkDelete,
fnSecretBulkInsert as fnSecretV2BridgeBulkInsert,
fnSecretBulkUpdate as fnSecretV2BridgeBulkUpdate,
getAllNestedSecretReferences as getAllNestedSecretReferencesV2Bridge,
secretEncryptionHelper
} from "@app/services/secret-v2-bridge/secret-v2-bridge-fns";
import { TSecretVersionV2DALFactory } from "@app/services/secret-v2-bridge/secret-version-dal";
import { TSecretVersionV2TagDALFactory } from "@app/services/secret-v2-bridge/secret-version-tag-dal";
import { TPermissionServiceFactory } from "../permission/permission-service"; import { TPermissionServiceFactory } from "../permission/permission-service";
import { ProjectPermissionActions, ProjectPermissionSub } from "../permission/project-permission"; import { ProjectPermissionActions, ProjectPermissionSub } from "../permission/project-permission";
@@ -63,10 +67,6 @@ import {
TSecretApprovalDetailsDTO, TSecretApprovalDetailsDTO,
TStatusChangeDTO TStatusChangeDTO
} from "./secret-approval-request-types"; } from "./secret-approval-request-types";
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
import { TSecretV2BridgeDALFactory } from "@app/services/secret-v2-bridge/secret-v2-bridge-dal";
import { TSecretVersionV2DALFactory } from "@app/services/secret-v2-bridge/secret-version-dal";
import { TSecretVersionV2TagDALFactory } from "@app/services/secret-v2-bridge/secret-version-tag-dal";
type TSecretApprovalRequestServiceFactoryDep = { type TSecretApprovalRequestServiceFactoryDep = {
permissionService: Pick<TPermissionServiceFactory, "getProjectPermission">; permissionService: Pick<TPermissionServiceFactory, "getProjectPermission">;
@@ -730,7 +730,7 @@ export const secretApprovalRequestServiceFactory = ({
}); });
} }
await snapshotService.performSnapshot(folderId, shouldUseSecretV2Bridge); await snapshotService.performSnapshot(folderId);
const [folder] = await folderDAL.findSecretPathByFolderIds(projectId, [folderId]); const [folder] = await folderDAL.findSecretPathByFolderIds(projectId, [folderId]);
if (!folder) throw new BadRequestError({ message: "Folder not found" }); if (!folder) throw new BadRequestError({ message: "Folder not found" });
await secretQueueService.syncSecrets({ await secretQueueService.syncSecrets({
@@ -10,15 +10,11 @@ import { logger } from "@app/lib/logger";
import { alphaNumericNanoId } from "@app/lib/nanoid"; import { alphaNumericNanoId } from "@app/lib/nanoid";
import { QueueName, TQueueServiceFactory } from "@app/queue"; import { QueueName, TQueueServiceFactory } from "@app/queue";
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 { KmsDataKey } from "@app/services/kms/kms-types";
import { TProjectBotServiceFactory } from "@app/services/project-bot/project-bot-service"; import { TProjectBotServiceFactory } from "@app/services/project-bot/project-bot-service";
import { TSecretDALFactory } from "@app/services/secret/secret-dal"; import { TSecretDALFactory } from "@app/services/secret/secret-dal";
import { fnSecretBulkInsert, fnSecretBulkUpdate } from "@app/services/secret/secret-fns"; import { fnSecretBulkInsert, fnSecretBulkUpdate } from "@app/services/secret/secret-fns";
import {
fnSecretBulkInsert as fnSecretV2BridgeBulkInsert,
fnSecretBulkUpdate as fnSecretV2BridgeBulkUpdate,
getAllNestedSecretReferences,
getAllNestedSecretReferences as getAllNestedSecretReferencesV2Bridge
} from "@app/services/secret-v2-bridge/secret-v2-bridge-fns";
import { TSecretQueueFactory, uniqueSecretQueueKey } from "@app/services/secret/secret-queue"; import { TSecretQueueFactory, uniqueSecretQueueKey } from "@app/services/secret/secret-queue";
import { SecretOperations } from "@app/services/secret/secret-types"; import { SecretOperations } from "@app/services/secret/secret-types";
import { TSecretVersionDALFactory } from "@app/services/secret/secret-version-dal"; import { TSecretVersionDALFactory } from "@app/services/secret/secret-version-dal";
@@ -28,13 +24,17 @@ import { ReservedFolders } from "@app/services/secret-folder/secret-folder-types
import { TSecretImportDALFactory } from "@app/services/secret-import/secret-import-dal"; import { TSecretImportDALFactory } from "@app/services/secret-import/secret-import-dal";
import { fnSecretsFromImports, fnSecretsV2FromImports } from "@app/services/secret-import/secret-import-fns"; import { fnSecretsFromImports, fnSecretsV2FromImports } from "@app/services/secret-import/secret-import-fns";
import { TSecretTagDALFactory } from "@app/services/secret-tag/secret-tag-dal"; import { TSecretTagDALFactory } from "@app/services/secret-tag/secret-tag-dal";
import { MAX_REPLICATION_DEPTH } from "./secret-replication-constants";
import { TSecretV2BridgeDALFactory } from "@app/services/secret-v2-bridge/secret-v2-bridge-dal"; import { TSecretV2BridgeDALFactory } from "@app/services/secret-v2-bridge/secret-v2-bridge-dal";
import {
fnSecretBulkInsert as fnSecretV2BridgeBulkInsert,
fnSecretBulkUpdate as fnSecretV2BridgeBulkUpdate,
getAllNestedSecretReferences,
getAllNestedSecretReferences as getAllNestedSecretReferencesV2Bridge
} from "@app/services/secret-v2-bridge/secret-v2-bridge-fns";
import { TSecretVersionV2DALFactory } from "@app/services/secret-v2-bridge/secret-version-dal"; import { TSecretVersionV2DALFactory } from "@app/services/secret-v2-bridge/secret-version-dal";
import { TSecretVersionV2TagDALFactory } from "@app/services/secret-v2-bridge/secret-version-tag-dal"; import { TSecretVersionV2TagDALFactory } from "@app/services/secret-v2-bridge/secret-version-tag-dal";
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
import { KmsDataKey } from "@app/services/kms/kms-types"; import { MAX_REPLICATION_DEPTH } from "./secret-replication-constants";
type TSecretReplicationServiceFactoryDep = { type TSecretReplicationServiceFactoryDep = {
secretDAL: Pick< secretDAL: Pick<
@@ -17,9 +17,13 @@ import { BadRequestError } from "@app/lib/errors";
import { logger } from "@app/lib/logger"; import { logger } from "@app/lib/logger";
import { alphaNumericNanoId } from "@app/lib/nanoid"; import { alphaNumericNanoId } from "@app/lib/nanoid";
import { QueueJobs, QueueName, TQueueServiceFactory } from "@app/queue"; import { QueueJobs, QueueName, TQueueServiceFactory } from "@app/queue";
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
import { KmsDataKey } from "@app/services/kms/kms-types";
import { TProjectBotServiceFactory } from "@app/services/project-bot/project-bot-service"; import { TProjectBotServiceFactory } from "@app/services/project-bot/project-bot-service";
import { TSecretDALFactory } from "@app/services/secret/secret-dal"; import { TSecretDALFactory } from "@app/services/secret/secret-dal";
import { TSecretVersionDALFactory } from "@app/services/secret/secret-version-dal"; import { TSecretVersionDALFactory } from "@app/services/secret/secret-version-dal";
import { TSecretV2BridgeDALFactory } from "@app/services/secret-v2-bridge/secret-v2-bridge-dal";
import { TSecretVersionV2DALFactory } from "@app/services/secret-v2-bridge/secret-version-dal";
import { TTelemetryServiceFactory } from "@app/services/telemetry/telemetry-service"; import { TTelemetryServiceFactory } from "@app/services/telemetry/telemetry-service";
import { PostHogEventTypes } from "@app/services/telemetry/telemetry-types"; import { PostHogEventTypes } from "@app/services/telemetry/telemetry-types";
@@ -39,10 +43,6 @@ import {
secretRotationPreSetFn secretRotationPreSetFn
} from "./secret-rotation-queue-fn"; } from "./secret-rotation-queue-fn";
import { TSecretRotationData, TSecretRotationDbFn, TSecretRotationEncData } from "./secret-rotation-queue-types"; import { TSecretRotationData, TSecretRotationDbFn, TSecretRotationEncData } from "./secret-rotation-queue-types";
import { TSecretV2BridgeDALFactory } from "@app/services/secret-v2-bridge/secret-v2-bridge-dal";
import { TSecretVersionV2DALFactory } from "@app/services/secret-v2-bridge/secret-version-dal";
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
import { KmsDataKey } from "@app/services/kms/kms-types";
export type TSecretRotationQueueFactory = ReturnType<typeof secretRotationQueueFactory>; export type TSecretRotationQueueFactory = ReturnType<typeof secretRotationQueueFactory>;
@@ -10,6 +10,9 @@ import { TSecretVersionTagDALFactory } from "@app/services/secret/secret-version
import { TSecretFolderDALFactory } from "@app/services/secret-folder/secret-folder-dal"; import { TSecretFolderDALFactory } from "@app/services/secret-folder/secret-folder-dal";
import { TSecretFolderVersionDALFactory } from "@app/services/secret-folder/secret-folder-version-dal"; import { TSecretFolderVersionDALFactory } from "@app/services/secret-folder/secret-folder-version-dal";
import { TSecretTagDALFactory } from "@app/services/secret-tag/secret-tag-dal"; import { TSecretTagDALFactory } from "@app/services/secret-tag/secret-tag-dal";
import { TSecretV2BridgeDALFactory } from "@app/services/secret-v2-bridge/secret-v2-bridge-dal";
import { TSecretVersionV2DALFactory } from "@app/services/secret-v2-bridge/secret-version-dal";
import { TSecretVersionV2TagDALFactory } from "@app/services/secret-v2-bridge/secret-version-tag-dal";
import { TLicenseServiceFactory } from "../license/license-service"; import { TLicenseServiceFactory } from "../license/license-service";
import { TPermissionServiceFactory } from "../permission/permission-service"; import { TPermissionServiceFactory } from "../permission/permission-service";
@@ -23,11 +26,8 @@ import {
import { TSnapshotDALFactory } from "./snapshot-dal"; import { TSnapshotDALFactory } from "./snapshot-dal";
import { TSnapshotFolderDALFactory } from "./snapshot-folder-dal"; import { TSnapshotFolderDALFactory } from "./snapshot-folder-dal";
import { TSnapshotSecretDALFactory } from "./snapshot-secret-dal"; import { TSnapshotSecretDALFactory } from "./snapshot-secret-dal";
import { getFullFolderPath } from "./snapshot-service-fns";
import { TSecretVersionV2DALFactory } from "@app/services/secret-v2-bridge/secret-version-dal";
import { TSnapshotSecretV2DALFactory } from "./snapshot-secret-v2-dal"; import { TSnapshotSecretV2DALFactory } from "./snapshot-secret-v2-dal";
import { TSecretV2BridgeDALFactory } from "@app/services/secret-v2-bridge/secret-v2-bridge-dal"; import { getFullFolderPath } from "./snapshot-service-fns";
import { TSecretVersionV2TagDALFactory } from "@app/services/secret-v2-bridge/secret-version-tag-dal";
type TSecretSnapshotServiceFactoryDep = { type TSecretSnapshotServiceFactoryDep = {
snapshotDAL: TSnapshotDALFactory; snapshotDAL: TSnapshotDALFactory;
@@ -167,14 +167,15 @@ export const secretSnapshotServiceFactory = ({
return snapshotDetails; return snapshotDetails;
}; };
const performSnapshot = async (folderId: string, shouldUseSecretV2Bridge: boolean) => { const performSnapshot = async (folderId: string) => {
try { try {
if (!licenseService.isValidLicense) throw new InternalServerError({ message: "Invalid license" }); if (!licenseService.isValidLicense) throw new InternalServerError({ message: "Invalid license" });
const folder = await folderDAL.findById(folderId);
if (!folder) throw new BadRequestError({ message: "Folder not found" });
const shouldUseSecretV2Bridge = folder.projectVersion === 3;
if (shouldUseSecretV2Bridge) { if (shouldUseSecretV2Bridge) {
const snapshot = await snapshotDAL.transaction(async (tx) => { const snapshot = await snapshotDAL.transaction(async (tx) => {
const folder = await folderDAL.findById(folderId, tx);
if (!folder) throw new BadRequestError({ message: "Folder not found" });
const secretVersions = await secretVersionV2BridgeDAL.findLatestVersionByFolderId(folderId, tx); const secretVersions = await secretVersionV2BridgeDAL.findLatestVersionByFolderId(folderId, tx);
const folderVersions = await folderVersionDAL.findLatestVersionByFolderId(folderId, tx); const folderVersions = await folderVersionDAL.findLatestVersionByFolderId(folderId, tx);
const newSnapshot = await snapshotDAL.create( const newSnapshot = await snapshotDAL.create(
@@ -208,9 +209,6 @@ export const secretSnapshotServiceFactory = ({
} }
const snapshot = await snapshotDAL.transaction(async (tx) => { const snapshot = await snapshotDAL.transaction(async (tx) => {
const folder = await folderDAL.findById(folderId, tx);
if (!folder) throw new BadRequestError({ message: "Folder not found" });
const secretVersions = await secretVersionDAL.findLatestVersionByFolderId(folderId, tx); const secretVersions = await secretVersionDAL.findLatestVersionByFolderId(folderId, tx);
const folderVersions = await folderVersionDAL.findLatestVersionByFolderId(folderId, tx); const folderVersions = await folderVersionDAL.findLatestVersionByFolderId(folderId, tx);
const newSnapshot = await snapshotDAL.create( const newSnapshot = await snapshotDAL.create(
+1
View File
@@ -6,3 +6,4 @@ export * from "./array";
export * from "./dates"; export * from "./dates";
export * from "./object"; export * from "./object";
export * from "./string"; export * from "./string";
export * from "./undefined";
+3
View File
@@ -0,0 +1,3 @@
export const executeIfDefined = <T, R>(func: (input: T) => R, input: T | undefined): R | undefined => {
return input === undefined ? undefined : func(input);
};
@@ -11,6 +11,8 @@ import { BadRequestError } from "@app/lib/errors";
import { TProjectPermission } from "@app/lib/types"; import { TProjectPermission } from "@app/lib/types";
import { TIntegrationDALFactory } from "../integration/integration-dal"; import { TIntegrationDALFactory } from "../integration/integration-dal";
import { TKmsServiceFactory } from "../kms/kms-service";
import { KmsDataKey } from "../kms/kms-types";
import { TProjectBotDALFactory } from "../project-bot/project-bot-dal"; import { TProjectBotDALFactory } from "../project-bot/project-bot-dal";
import { TProjectBotServiceFactory } from "../project-bot/project-bot-service"; import { TProjectBotServiceFactory } from "../project-bot/project-bot-service";
import { getApps } from "./integration-app-list"; import { getApps } from "./integration-app-list";
@@ -55,6 +57,7 @@ type TIntegrationAuthServiceFactoryDep = {
projectBotService: Pick<TProjectBotServiceFactory, "getBotKey">; projectBotService: Pick<TProjectBotServiceFactory, "getBotKey">;
projectBotDAL: Pick<TProjectBotDALFactory, "findOne">; projectBotDAL: Pick<TProjectBotDALFactory, "findOne">;
permissionService: Pick<TPermissionServiceFactory, "getProjectPermission">; permissionService: Pick<TPermissionServiceFactory, "getProjectPermission">;
kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey">;
}; };
export type TIntegrationAuthServiceFactory = ReturnType<typeof integrationAuthServiceFactory>; export type TIntegrationAuthServiceFactory = ReturnType<typeof integrationAuthServiceFactory>;
@@ -64,7 +67,8 @@ export const integrationAuthServiceFactory = ({
integrationAuthDAL, integrationAuthDAL,
integrationDAL, integrationDAL,
projectBotDAL, projectBotDAL,
projectBotService projectBotService,
kmsService
}: TIntegrationAuthServiceFactoryDep) => { }: TIntegrationAuthServiceFactoryDep) => {
const listIntegrationAuthByProjectId = async ({ const listIntegrationAuthByProjectId = async ({
actorId, actorId,
@@ -145,18 +149,38 @@ export const integrationAuthServiceFactory = ({
}; };
} }
const key = await projectBotService.getBotKey(projectId); const { shouldUseSecretV2Bridge, botKey } = await projectBotService.getBotKey(projectId);
if (tokenExchange.refreshToken) { if (shouldUseSecretV2Bridge) {
const refreshEncToken = encryptSymmetric128BitHexKeyUTF8(tokenExchange.refreshToken, key); const { encryptor: secretManagerEncryptor } = await kmsService.createCipherPairWithDataKey({
updateDoc.refreshIV = refreshEncToken.iv; type: KmsDataKey.SecretManager,
updateDoc.refreshTag = refreshEncToken.tag; projectId
updateDoc.refreshCiphertext = refreshEncToken.ciphertext; });
} if (tokenExchange.refreshToken) {
if (tokenExchange.accessToken) { const refreshEncToken = secretManagerEncryptor({
const accessEncToken = encryptSymmetric128BitHexKeyUTF8(tokenExchange.accessToken, key); plainText: Buffer.from(tokenExchange.refreshToken)
updateDoc.accessIV = accessEncToken.iv; }).cipherTextBlob;
updateDoc.accessTag = accessEncToken.tag; updateDoc.encryptedRefresh = refreshEncToken;
updateDoc.accessCiphertext = accessEncToken.ciphertext; }
if (tokenExchange.accessToken) {
const accessToken = secretManagerEncryptor({
plainText: Buffer.from(tokenExchange.accessToken)
}).cipherTextBlob;
updateDoc.encryptedAccess = accessToken;
}
} else {
if (!botKey) throw new BadRequestError({ message: "Bot key not found" });
if (tokenExchange.refreshToken) {
const refreshEncToken = encryptSymmetric128BitHexKeyUTF8(tokenExchange.refreshToken, botKey);
updateDoc.refreshIV = refreshEncToken.iv;
updateDoc.refreshTag = refreshEncToken.tag;
updateDoc.refreshCiphertext = refreshEncToken.ciphertext;
}
if (tokenExchange.accessToken) {
const accessEncToken = encryptSymmetric128BitHexKeyUTF8(tokenExchange.accessToken, botKey);
updateDoc.accessIV = accessEncToken.iv;
updateDoc.accessTag = accessEncToken.tag;
updateDoc.accessCiphertext = accessEncToken.ciphertext;
}
} }
return integrationAuthDAL.transaction(async (tx) => { return integrationAuthDAL.transaction(async (tx) => {
const doc = await integrationAuthDAL.findOne({ projectId, integration }, tx); const doc = await integrationAuthDAL.findOne({ projectId, integration }, tx);
@@ -212,109 +236,210 @@ export const integrationAuthServiceFactory = ({
: {}) : {})
}; };
const key = await projectBotService.getBotKey(projectId); const { shouldUseSecretV2Bridge, botKey } = await projectBotService.getBotKey(projectId);
if (refreshToken) { if (shouldUseSecretV2Bridge) {
const tokenDetails = await exchangeRefresh( const { encryptor: secretManagerEncryptor } = await kmsService.createCipherPairWithDataKey({
integration, type: KmsDataKey.SecretManager,
refreshToken, projectId
url, });
updateDoc.metadata as Record<string, string> if (refreshToken) {
); const tokenDetails = await exchangeRefresh(
const refreshEncToken = encryptSymmetric128BitHexKeyUTF8(tokenDetails.refreshToken, key); integration,
updateDoc.refreshIV = refreshEncToken.iv; refreshToken,
updateDoc.refreshTag = refreshEncToken.tag; url,
updateDoc.refreshCiphertext = refreshEncToken.ciphertext; updateDoc.metadata as Record<string, string>
const accessEncToken = encryptSymmetric128BitHexKeyUTF8(tokenDetails.accessToken, key); );
updateDoc.accessIV = accessEncToken.iv; const refreshEncToken = secretManagerEncryptor({
updateDoc.accessTag = accessEncToken.tag; plainText: Buffer.from(tokenDetails.refreshToken)
updateDoc.accessCiphertext = accessEncToken.ciphertext; }).cipherTextBlob;
updateDoc.accessExpiresAt = tokenDetails.accessExpiresAt; updateDoc.encryptedRefresh = refreshEncToken;
}
if (!refreshToken && (accessId || accessToken || awsAssumeIamRoleArn)) { const accessEncToken = secretManagerEncryptor({
if (accessToken) { plainText: Buffer.from(tokenDetails.accessToken)
const accessEncToken = encryptSymmetric128BitHexKeyUTF8(accessToken, key); }).cipherTextBlob;
updateDoc.encryptedAccess = accessEncToken;
updateDoc.accessExpiresAt = tokenDetails.accessExpiresAt;
}
if (!refreshToken && (accessId || accessToken || awsAssumeIamRoleArn)) {
if (accessToken) {
const accessEncToken = secretManagerEncryptor({
plainText: Buffer.from(accessToken)
}).cipherTextBlob;
updateDoc.encryptedAccess = accessEncToken;
}
if (accessId) {
const accessEncToken = secretManagerEncryptor({
plainText: Buffer.from(accessId)
}).cipherTextBlob;
updateDoc.encryptedAccessId = accessEncToken;
}
if (awsAssumeIamRoleArn) {
const awsAssumeIamRoleArnEncrypted = secretManagerEncryptor({
plainText: Buffer.from(awsAssumeIamRoleArn)
}).cipherTextBlob;
updateDoc.hasEncryptedAwsIamAssumRole = awsAssumeIamRoleArnEncrypted;
}
}
} else {
if (!botKey) throw new BadRequestError({ message: "Bot key not found" });
if (refreshToken) {
const tokenDetails = await exchangeRefresh(
integration,
refreshToken,
url,
updateDoc.metadata as Record<string, string>
);
const refreshEncToken = encryptSymmetric128BitHexKeyUTF8(tokenDetails.refreshToken, botKey);
updateDoc.refreshIV = refreshEncToken.iv;
updateDoc.refreshTag = refreshEncToken.tag;
updateDoc.refreshCiphertext = refreshEncToken.ciphertext;
const accessEncToken = encryptSymmetric128BitHexKeyUTF8(tokenDetails.accessToken, botKey);
updateDoc.accessIV = accessEncToken.iv; updateDoc.accessIV = accessEncToken.iv;
updateDoc.accessTag = accessEncToken.tag; updateDoc.accessTag = accessEncToken.tag;
updateDoc.accessCiphertext = accessEncToken.ciphertext; updateDoc.accessCiphertext = accessEncToken.ciphertext;
updateDoc.accessExpiresAt = tokenDetails.accessExpiresAt;
} }
if (accessId) {
const accessEncToken = encryptSymmetric128BitHexKeyUTF8(accessId, key); if (!refreshToken && (accessId || accessToken || awsAssumeIamRoleArn)) {
updateDoc.accessIdIV = accessEncToken.iv; if (accessToken) {
updateDoc.accessIdTag = accessEncToken.tag; const accessEncToken = encryptSymmetric128BitHexKeyUTF8(accessToken, botKey);
updateDoc.accessIdCiphertext = accessEncToken.ciphertext; updateDoc.accessIV = accessEncToken.iv;
} updateDoc.accessTag = accessEncToken.tag;
if (awsAssumeIamRoleArn) { updateDoc.accessCiphertext = accessEncToken.ciphertext;
const awsAssumeIamRoleArnEnc = encryptSymmetric128BitHexKeyUTF8(awsAssumeIamRoleArn, key); }
updateDoc.awsAssumeIamRoleArnCipherText = awsAssumeIamRoleArnEnc.ciphertext; if (accessId) {
updateDoc.awsAssumeIamRoleArnIV = awsAssumeIamRoleArnEnc.iv; const accessEncToken = encryptSymmetric128BitHexKeyUTF8(accessId, botKey);
updateDoc.awsAssumeIamRoleArnTag = awsAssumeIamRoleArnEnc.tag; updateDoc.accessIdIV = accessEncToken.iv;
updateDoc.accessIdTag = accessEncToken.tag;
updateDoc.accessIdCiphertext = accessEncToken.ciphertext;
}
if (awsAssumeIamRoleArn) {
const awsAssumeIamRoleArnEnc = encryptSymmetric128BitHexKeyUTF8(awsAssumeIamRoleArn, botKey);
updateDoc.awsAssumeIamRoleArnCipherText = awsAssumeIamRoleArnEnc.ciphertext;
updateDoc.awsAssumeIamRoleArnIV = awsAssumeIamRoleArnEnc.iv;
updateDoc.awsAssumeIamRoleArnTag = awsAssumeIamRoleArnEnc.tag;
}
} }
} }
return integrationAuthDAL.create(updateDoc); return integrationAuthDAL.create(updateDoc);
}; };
// helper function // helper function
const getIntegrationAccessToken = async (integrationAuth: TIntegrationAuths, botKey: string) => { const getIntegrationAccessToken = async (
integrationAuth: TIntegrationAuths,
shouldUseSecretV2Bridge: boolean,
botKey?: string
) => {
let accessToken: string | undefined; let accessToken: string | undefined;
let accessId: string | undefined; let accessId: string | undefined;
// this means its not access token based // this means its not access token based
if ( if (
integrationAuth.integration === Integrations.AWS_SECRET_MANAGER && integrationAuth.integration === Integrations.AWS_SECRET_MANAGER &&
integrationAuth.awsAssumeIamRoleArnCipherText (shouldUseSecretV2Bridge
? integrationAuth.hasEncryptedAwsIamAssumRole
: integrationAuth.awsAssumeIamRoleArnCipherText)
) { ) {
return { accessToken: "", accessId: "" }; return { accessToken: "", accessId: "" };
} }
if (shouldUseSecretV2Bridge) {
const { decryptor: secretManagerDecryptor, encryptor: secretManagerEncryptor } =
await kmsService.createCipherPairWithDataKey({
type: KmsDataKey.SecretManager,
projectId: integrationAuth.projectId
});
if (integrationAuth.encryptedAccess) {
accessToken = secretManagerDecryptor({ cipherTextBlob: integrationAuth.encryptedAccess }).toString();
}
if (integrationAuth.accessTag && integrationAuth.accessIV && integrationAuth.accessCiphertext) { if (integrationAuth.encryptedRefresh) {
accessToken = decryptSymmetric128BitHexKeyUTF8({ const refreshToken = secretManagerDecryptor({ cipherTextBlob: integrationAuth.encryptedRefresh }).toString();
ciphertext: integrationAuth.accessCiphertext,
iv: integrationAuth.accessIV,
tag: integrationAuth.accessTag,
key: botKey
});
}
if (integrationAuth.refreshCiphertext && integrationAuth.refreshIV && integrationAuth.refreshTag) { if (integrationAuth.accessExpiresAt && integrationAuth.accessExpiresAt < new Date()) {
const refreshToken = decryptSymmetric128BitHexKeyUTF8({ // refer above it contains same logic except not saving
key: botKey, const tokenDetails = await exchangeRefresh(
ciphertext: integrationAuth.refreshCiphertext, integrationAuth.integration,
iv: integrationAuth.refreshIV, refreshToken,
tag: integrationAuth.refreshTag integrationAuth?.url,
}); integrationAuth.metadata as Record<string, string>
);
const encryptedRefresh = secretManagerEncryptor({
plainText: Buffer.from(tokenDetails.refreshToken)
}).cipherTextBlob;
const encryptedAccess = secretManagerEncryptor({
plainText: Buffer.from(tokenDetails.accessToken)
}).cipherTextBlob;
accessToken = tokenDetails.accessToken;
await integrationAuthDAL.updateById(integrationAuth.id, {
accessExpiresAt: tokenDetails.accessExpiresAt,
encryptedRefresh,
encryptedAccess
});
}
}
if (!accessToken) throw new BadRequestError({ message: "Missing access token" });
if (integrationAuth.accessExpiresAt && integrationAuth.accessExpiresAt < new Date()) { if (integrationAuth.encryptedAccessId) {
// refer above it contains same logic except not saving accessId = secretManagerDecryptor({
const tokenDetails = await exchangeRefresh( cipherTextBlob: integrationAuth.encryptedAccessId
integrationAuth.integration, }).toString();
refreshToken, }
integrationAuth?.url,
integrationAuth.metadata as Record<string, string> // the old bot key is else
); } else {
const refreshEncToken = encryptSymmetric128BitHexKeyUTF8(tokenDetails.refreshToken, botKey); if (!botKey) throw new BadRequestError({ message: "bot key is missing" });
const accessEncToken = encryptSymmetric128BitHexKeyUTF8(tokenDetails.accessToken, botKey); if (integrationAuth.accessTag && integrationAuth.accessIV && integrationAuth.accessCiphertext) {
accessToken = tokenDetails.accessToken; accessToken = decryptSymmetric128BitHexKeyUTF8({
await integrationAuthDAL.updateById(integrationAuth.id, { ciphertext: integrationAuth.accessCiphertext,
refreshIV: refreshEncToken.iv, iv: integrationAuth.accessIV,
refreshTag: refreshEncToken.tag, tag: integrationAuth.accessTag,
refreshCiphertext: refreshEncToken.ciphertext, key: botKey
accessExpiresAt: tokenDetails.accessExpiresAt, });
accessIV: accessEncToken.iv, }
accessTag: accessEncToken.tag,
accessCiphertext: accessEncToken.ciphertext if (integrationAuth.refreshCiphertext && integrationAuth.refreshIV && integrationAuth.refreshTag) {
const refreshToken = decryptSymmetric128BitHexKeyUTF8({
key: botKey,
ciphertext: integrationAuth.refreshCiphertext,
iv: integrationAuth.refreshIV,
tag: integrationAuth.refreshTag
});
if (integrationAuth.accessExpiresAt && integrationAuth.accessExpiresAt < new Date()) {
// refer above it contains same logic except not saving
const tokenDetails = await exchangeRefresh(
integrationAuth.integration,
refreshToken,
integrationAuth?.url,
integrationAuth.metadata as Record<string, string>
);
const refreshEncToken = encryptSymmetric128BitHexKeyUTF8(tokenDetails.refreshToken, botKey);
const accessEncToken = encryptSymmetric128BitHexKeyUTF8(tokenDetails.accessToken, botKey);
accessToken = tokenDetails.accessToken;
await integrationAuthDAL.updateById(integrationAuth.id, {
refreshIV: refreshEncToken.iv,
refreshTag: refreshEncToken.tag,
refreshCiphertext: refreshEncToken.ciphertext,
accessExpiresAt: tokenDetails.accessExpiresAt,
accessIV: accessEncToken.iv,
accessTag: accessEncToken.tag,
accessCiphertext: accessEncToken.ciphertext
});
}
}
if (!accessToken) throw new BadRequestError({ message: "Missing access token" });
if (integrationAuth.accessIdTag && integrationAuth.accessIdIV && integrationAuth.accessIdCiphertext) {
accessId = decryptSymmetric128BitHexKeyUTF8({
key: botKey,
ciphertext: integrationAuth.accessIdCiphertext,
iv: integrationAuth.accessIdIV,
tag: integrationAuth.accessIdTag
}); });
} }
} }
if (!accessToken) throw new BadRequestError({ message: "Missing access token" });
if (integrationAuth.accessIdTag && integrationAuth.accessIdIV && integrationAuth.accessIdCiphertext) {
accessId = decryptSymmetric128BitHexKeyUTF8({
key: botKey,
ciphertext: integrationAuth.accessIdCiphertext,
iv: integrationAuth.accessIdIV,
tag: integrationAuth.accessIdTag
});
}
return { accessId, accessToken }; return { accessId, accessToken };
}; };
@@ -339,8 +464,8 @@ export const integrationAuthServiceFactory = ({
); );
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
const botKey = await projectBotService.getBotKey(integrationAuth.projectId); const { botKey, shouldUseSecretV2Bridge } = await projectBotService.getBotKey(integrationAuth.projectId);
const { accessToken, accessId } = await getIntegrationAccessToken(integrationAuth, botKey); const { accessToken, accessId } = await getIntegrationAccessToken(integrationAuth, shouldUseSecretV2Bridge, botKey);
const apps = await getApps({ const apps = await getApps({
integration: integrationAuth.integration, integration: integrationAuth.integration,
accessToken, accessToken,
@@ -371,8 +496,8 @@ export const integrationAuthServiceFactory = ({
); );
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
const botKey = await projectBotService.getBotKey(integrationAuth.projectId); const { shouldUseSecretV2Bridge, botKey } = await projectBotService.getBotKey(integrationAuth.projectId);
const { accessToken } = await getIntegrationAccessToken(integrationAuth, botKey); const { accessToken } = await getIntegrationAccessToken(integrationAuth, shouldUseSecretV2Bridge, botKey);
const teams = await getTeams({ const teams = await getTeams({
integration: integrationAuth.integration, integration: integrationAuth.integration,
accessToken, accessToken,
@@ -400,8 +525,8 @@ export const integrationAuthServiceFactory = ({
actorOrgId actorOrgId
); );
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
const botKey = await projectBotService.getBotKey(integrationAuth.projectId); const { shouldUseSecretV2Bridge, botKey } = await projectBotService.getBotKey(integrationAuth.projectId);
const { accessToken } = await getIntegrationAccessToken(integrationAuth, botKey); const { accessToken } = await getIntegrationAccessToken(integrationAuth, shouldUseSecretV2Bridge, botKey);
if (appId) { if (appId) {
const { data } = await request.get<TVercelBranches[]>( const { data } = await request.get<TVercelBranches[]>(
@@ -441,8 +566,8 @@ export const integrationAuthServiceFactory = ({
actorOrgId actorOrgId
); );
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
const botKey = await projectBotService.getBotKey(integrationAuth.projectId); const { shouldUseSecretV2Bridge, botKey } = await projectBotService.getBotKey(integrationAuth.projectId);
const { accessToken } = await getIntegrationAccessToken(integrationAuth, botKey); const { accessToken } = await getIntegrationAccessToken(integrationAuth, shouldUseSecretV2Bridge, botKey);
if (accountId) { if (accountId) {
const { data } = await request.get<TChecklyGroups[]>(`${IntegrationUrls.CHECKLY_API_URL}/v1/check-groups`, { const { data } = await request.get<TChecklyGroups[]>(`${IntegrationUrls.CHECKLY_API_URL}/v1/check-groups`, {
headers: { headers: {
@@ -468,8 +593,8 @@ export const integrationAuthServiceFactory = ({
actorOrgId actorOrgId
); );
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
const botKey = await projectBotService.getBotKey(integrationAuth.projectId); const { shouldUseSecretV2Bridge, botKey } = await projectBotService.getBotKey(integrationAuth.projectId);
const { accessToken } = await getIntegrationAccessToken(integrationAuth, botKey); const { accessToken } = await getIntegrationAccessToken(integrationAuth, shouldUseSecretV2Bridge, botKey);
const octokit = new Octokit({ const octokit = new Octokit({
auth: accessToken auth: accessToken
@@ -505,8 +630,8 @@ export const integrationAuthServiceFactory = ({
actorOrgId actorOrgId
); );
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
const botKey = await projectBotService.getBotKey(integrationAuth.projectId); const { shouldUseSecretV2Bridge, botKey } = await projectBotService.getBotKey(integrationAuth.projectId);
const { accessToken } = await getIntegrationAccessToken(integrationAuth, botKey); const { accessToken } = await getIntegrationAccessToken(integrationAuth, shouldUseSecretV2Bridge, botKey);
const octokit = new Octokit({ const octokit = new Octokit({
auth: accessToken auth: accessToken
@@ -537,8 +662,8 @@ export const integrationAuthServiceFactory = ({
actorOrgId actorOrgId
); );
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
const botKey = await projectBotService.getBotKey(integrationAuth.projectId); const { shouldUseSecretV2Bridge, botKey } = await projectBotService.getBotKey(integrationAuth.projectId);
const { accessToken } = await getIntegrationAccessToken(integrationAuth, botKey); const { accessToken } = await getIntegrationAccessToken(integrationAuth, shouldUseSecretV2Bridge, botKey);
const { data } = await request.get<{ results: Array<{ id: string; name: string }> }>( const { data } = await request.get<{ results: Array<{ id: string; name: string }> }>(
`${IntegrationUrls.QOVERY_API_URL}/organization`, `${IntegrationUrls.QOVERY_API_URL}/organization`,
{ {
@@ -571,8 +696,8 @@ export const integrationAuthServiceFactory = ({
actorOrgId actorOrgId
); );
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
const botKey = await projectBotService.getBotKey(integrationAuth.projectId); const { shouldUseSecretV2Bridge, botKey } = await projectBotService.getBotKey(integrationAuth.projectId);
const { accessId, accessToken } = await getIntegrationAccessToken(integrationAuth, botKey); const { accessId, accessToken } = await getIntegrationAccessToken(integrationAuth, shouldUseSecretV2Bridge, botKey);
const kms = new AWS.KMS({ const kms = new AWS.KMS({
region, region,
@@ -629,8 +754,8 @@ export const integrationAuthServiceFactory = ({
actorOrgId actorOrgId
); );
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
const botKey = await projectBotService.getBotKey(integrationAuth.projectId); const { shouldUseSecretV2Bridge, botKey } = await projectBotService.getBotKey(integrationAuth.projectId);
const { accessToken } = await getIntegrationAccessToken(integrationAuth, botKey); const { accessToken } = await getIntegrationAccessToken(integrationAuth, shouldUseSecretV2Bridge, botKey);
if (orgId) { if (orgId) {
const { data } = await request.get<{ results: Array<{ id: string; name: string }> }>( const { data } = await request.get<{ results: Array<{ id: string; name: string }> }>(
`${IntegrationUrls.QOVERY_API_URL}/organization/${orgId}/project`, `${IntegrationUrls.QOVERY_API_URL}/organization/${orgId}/project`,
@@ -665,8 +790,8 @@ export const integrationAuthServiceFactory = ({
actorOrgId actorOrgId
); );
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
const botKey = await projectBotService.getBotKey(integrationAuth.projectId); const { shouldUseSecretV2Bridge, botKey } = await projectBotService.getBotKey(integrationAuth.projectId);
const { accessToken } = await getIntegrationAccessToken(integrationAuth, botKey); const { accessToken } = await getIntegrationAccessToken(integrationAuth, shouldUseSecretV2Bridge, botKey);
if (projectId && projectId !== "none") { if (projectId && projectId !== "none") {
// TODO: fix // TODO: fix
const { data } = await request.get<{ results: { id: string; name: string }[] }>( const { data } = await request.get<{ results: { id: string; name: string }[] }>(
@@ -706,8 +831,8 @@ export const integrationAuthServiceFactory = ({
actorOrgId actorOrgId
); );
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
const botKey = await projectBotService.getBotKey(integrationAuth.projectId); const { shouldUseSecretV2Bridge, botKey } = await projectBotService.getBotKey(integrationAuth.projectId);
const { accessToken } = await getIntegrationAccessToken(integrationAuth, botKey); const { accessToken } = await getIntegrationAccessToken(integrationAuth, shouldUseSecretV2Bridge, botKey);
if (environmentId) { if (environmentId) {
const { data } = await request.get<{ results: { id: string; name: string }[] }>( const { data } = await request.get<{ results: { id: string; name: string }[] }>(
`${IntegrationUrls.QOVERY_API_URL}/environment/${environmentId}/application`, `${IntegrationUrls.QOVERY_API_URL}/environment/${environmentId}/application`,
@@ -746,8 +871,8 @@ export const integrationAuthServiceFactory = ({
actorOrgId actorOrgId
); );
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
const botKey = await projectBotService.getBotKey(integrationAuth.projectId); const { shouldUseSecretV2Bridge, botKey } = await projectBotService.getBotKey(integrationAuth.projectId);
const { accessToken } = await getIntegrationAccessToken(integrationAuth, botKey); const { accessToken } = await getIntegrationAccessToken(integrationAuth, shouldUseSecretV2Bridge, botKey);
if (environmentId) { if (environmentId) {
const { data } = await request.get<{ results: { id: string; name: string }[] }>( const { data } = await request.get<{ results: { id: string; name: string }[] }>(
`${IntegrationUrls.QOVERY_API_URL}/environment/${environmentId}/container`, `${IntegrationUrls.QOVERY_API_URL}/environment/${environmentId}/container`,
@@ -786,8 +911,8 @@ export const integrationAuthServiceFactory = ({
actorOrgId actorOrgId
); );
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
const botKey = await projectBotService.getBotKey(integrationAuth.projectId); const { shouldUseSecretV2Bridge, botKey } = await projectBotService.getBotKey(integrationAuth.projectId);
const { accessToken } = await getIntegrationAccessToken(integrationAuth, botKey); const { accessToken } = await getIntegrationAccessToken(integrationAuth, shouldUseSecretV2Bridge, botKey);
if (environmentId) { if (environmentId) {
const { data } = await request.get<{ results: { id: string; name: string }[] }>( const { data } = await request.get<{ results: { id: string; name: string }[] }>(
`${IntegrationUrls.QOVERY_API_URL}/environment/${environmentId}/job`, `${IntegrationUrls.QOVERY_API_URL}/environment/${environmentId}/job`,
@@ -825,8 +950,8 @@ export const integrationAuthServiceFactory = ({
actorOrgId actorOrgId
); );
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
const botKey = await projectBotService.getBotKey(integrationAuth.projectId); const { shouldUseSecretV2Bridge, botKey } = await projectBotService.getBotKey(integrationAuth.projectId);
const { accessToken } = await getIntegrationAccessToken(integrationAuth, botKey); const { accessToken } = await getIntegrationAccessToken(integrationAuth, shouldUseSecretV2Bridge, botKey);
const { data } = await request.get<THerokuPipelineCoupling[]>( const { data } = await request.get<THerokuPipelineCoupling[]>(
`${IntegrationUrls.HEROKU_API_URL}/pipeline-couplings`, `${IntegrationUrls.HEROKU_API_URL}/pipeline-couplings`,
@@ -865,8 +990,8 @@ export const integrationAuthServiceFactory = ({
actorOrgId actorOrgId
); );
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
const botKey = await projectBotService.getBotKey(integrationAuth.projectId); const { shouldUseSecretV2Bridge, botKey } = await projectBotService.getBotKey(integrationAuth.projectId);
const { accessToken } = await getIntegrationAccessToken(integrationAuth, botKey); const { accessToken } = await getIntegrationAccessToken(integrationAuth, shouldUseSecretV2Bridge, botKey);
if (appId) { if (appId) {
const query = ` const query = `
query GetEnvironments($projectId: String!, $after: String, $before: String, $first: Int, $isEphemeral: Boolean, $last: Int) { query GetEnvironments($projectId: String!, $after: String, $before: String, $first: Int, $isEphemeral: Boolean, $last: Int) {
@@ -933,8 +1058,8 @@ export const integrationAuthServiceFactory = ({
actorOrgId actorOrgId
); );
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
const botKey = await projectBotService.getBotKey(integrationAuth.projectId); const { shouldUseSecretV2Bridge, botKey } = await projectBotService.getBotKey(integrationAuth.projectId);
const { accessToken } = await getIntegrationAccessToken(integrationAuth, botKey); const { accessToken } = await getIntegrationAccessToken(integrationAuth, shouldUseSecretV2Bridge, botKey);
if (appId && appId !== "") { if (appId && appId !== "") {
const query = ` const query = `
@@ -1007,8 +1132,8 @@ export const integrationAuthServiceFactory = ({
actorOrgId actorOrgId
); );
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
const botKey = await projectBotService.getBotKey(integrationAuth.projectId); const { shouldUseSecretV2Bridge, botKey } = await projectBotService.getBotKey(integrationAuth.projectId);
const { accessToken } = await getIntegrationAccessToken(integrationAuth, botKey); const { accessToken } = await getIntegrationAccessToken(integrationAuth, shouldUseSecretV2Bridge, botKey);
const workspaces: TBitbucketWorkspace[] = []; const workspaces: TBitbucketWorkspace[] = [];
let hasNextPage = true; let hasNextPage = true;
let workspaceUrl = `${IntegrationUrls.BITBUCKET_API_URL}/2.0/workspaces`; let workspaceUrl = `${IntegrationUrls.BITBUCKET_API_URL}/2.0/workspaces`;
@@ -1056,8 +1181,8 @@ export const integrationAuthServiceFactory = ({
actorOrgId actorOrgId
); );
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
const botKey = await projectBotService.getBotKey(integrationAuth.projectId); const { shouldUseSecretV2Bridge, botKey } = await projectBotService.getBotKey(integrationAuth.projectId);
const { accessToken } = await getIntegrationAccessToken(integrationAuth, botKey); const { accessToken } = await getIntegrationAccessToken(integrationAuth, shouldUseSecretV2Bridge, botKey);
const secretGroups: { name: string; groupId: string }[] = []; const secretGroups: { name: string; groupId: string }[] = [];
if (appId) { if (appId) {
@@ -1124,8 +1249,8 @@ export const integrationAuthServiceFactory = ({
actorOrgId actorOrgId
); );
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
const botKey = await projectBotService.getBotKey(integrationAuth.projectId); const { shouldUseSecretV2Bridge, botKey } = await projectBotService.getBotKey(integrationAuth.projectId);
const { accessToken } = await getIntegrationAccessToken(integrationAuth, botKey); const { accessToken } = await getIntegrationAccessToken(integrationAuth, shouldUseSecretV2Bridge, botKey);
if (appId) { if (appId) {
const { const {
data: { buildType } data: { buildType }
@@ -312,12 +312,14 @@ export const secretFolderDALFactory = (db: TDbClient) => {
const folder = await (tx || db.replicaNode())(TableName.SecretFolder) const folder = await (tx || db.replicaNode())(TableName.SecretFolder)
.where({ [`${TableName.SecretFolder}.id` as "id"]: id }) .where({ [`${TableName.SecretFolder}.id` as "id"]: id })
.join(TableName.Environment, `${TableName.SecretFolder}.envId`, `${TableName.Environment}.id`) .join(TableName.Environment, `${TableName.SecretFolder}.envId`, `${TableName.Environment}.id`)
.join(TableName.Project, `${TableName.Environment}.projectId`, `${TableName.Project}.id`)
.select(selectAllTableCols(TableName.SecretFolder)) .select(selectAllTableCols(TableName.SecretFolder))
.select( .select(
db.ref("id").withSchema(TableName.Environment).as("envId"), db.ref("id").withSchema(TableName.Environment).as("envId"),
db.ref("slug").withSchema(TableName.Environment).as("envSlug"), db.ref("slug").withSchema(TableName.Environment).as("envSlug"),
db.ref("name").withSchema(TableName.Environment).as("envName"), db.ref("name").withSchema(TableName.Environment).as("envName"),
db.ref("projectId").withSchema(TableName.Environment) db.ref("projectId").withSchema(TableName.Environment),
db.ref("version").withSchema(TableName.Project).as("projectVersion")
) )
.first(); .first();
if (folder) { if (folder) {
@@ -9,6 +9,8 @@ import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services
import { getReplicationFolderName } from "@app/ee/services/secret-replication/secret-replication-service"; import { getReplicationFolderName } from "@app/ee/services/secret-replication/secret-replication-service";
import { BadRequestError } from "@app/lib/errors"; import { BadRequestError } from "@app/lib/errors";
import { TKmsServiceFactory } from "../kms/kms-service";
import { KmsDataKey } from "../kms/kms-types";
import { TProjectDALFactory } from "../project/project-dal"; import { TProjectDALFactory } from "../project/project-dal";
import { TProjectBotServiceFactory } from "../project-bot/project-bot-service"; import { TProjectBotServiceFactory } from "../project-bot/project-bot-service";
import { TProjectEnvDALFactory } from "../project-env/project-env-dal"; import { TProjectEnvDALFactory } from "../project-env/project-env-dal";
@@ -16,8 +18,9 @@ import { TSecretDALFactory } from "../secret/secret-dal";
import { decryptSecretRaw } from "../secret/secret-fns"; import { decryptSecretRaw } from "../secret/secret-fns";
import { TSecretQueueFactory } from "../secret/secret-queue"; import { TSecretQueueFactory } from "../secret/secret-queue";
import { TSecretFolderDALFactory } from "../secret-folder/secret-folder-dal"; import { TSecretFolderDALFactory } from "../secret-folder/secret-folder-dal";
import { TSecretV2BridgeDALFactory } from "../secret-v2-bridge/secret-v2-bridge-dal";
import { TSecretImportDALFactory } from "./secret-import-dal"; import { TSecretImportDALFactory } from "./secret-import-dal";
import { fnSecretsFromImports } from "./secret-import-fns"; import { fnSecretsFromImports, fnSecretsV2FromImports } from "./secret-import-fns";
import { import {
TCreateSecretImportDTO, TCreateSecretImportDTO,
TDeleteSecretImportDTO, TDeleteSecretImportDTO,
@@ -31,12 +34,14 @@ type TSecretImportServiceFactoryDep = {
secretImportDAL: TSecretImportDALFactory; secretImportDAL: TSecretImportDALFactory;
folderDAL: TSecretFolderDALFactory; folderDAL: TSecretFolderDALFactory;
secretDAL: Pick<TSecretDALFactory, "find">; secretDAL: Pick<TSecretDALFactory, "find">;
secretV2BridgeDAL: Pick<TSecretV2BridgeDALFactory, "find">;
projectBotService: Pick<TProjectBotServiceFactory, "getBotKey">; projectBotService: Pick<TProjectBotServiceFactory, "getBotKey">;
projectDAL: Pick<TProjectDALFactory, "checkProjectUpgradeStatus">; projectDAL: Pick<TProjectDALFactory, "checkProjectUpgradeStatus">;
projectEnvDAL: TProjectEnvDALFactory; projectEnvDAL: TProjectEnvDALFactory;
permissionService: Pick<TPermissionServiceFactory, "getProjectPermission">; permissionService: Pick<TPermissionServiceFactory, "getProjectPermission">;
secretQueueService: Pick<TSecretQueueFactory, "syncSecrets" | "replicateSecrets">; secretQueueService: Pick<TSecretQueueFactory, "syncSecrets" | "replicateSecrets">;
licenseService: Pick<TLicenseServiceFactory, "getPlan">; licenseService: Pick<TLicenseServiceFactory, "getPlan">;
kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey">;
}; };
const ERR_SEC_IMP_NOT_FOUND = new BadRequestError({ message: "Secret import not found" }); const ERR_SEC_IMP_NOT_FOUND = new BadRequestError({ message: "Secret import not found" });
@@ -52,7 +57,9 @@ export const secretImportServiceFactory = ({
secretDAL, secretDAL,
secretQueueService, secretQueueService,
licenseService, licenseService,
projectBotService projectBotService,
secretV2BridgeDAL,
kmsService
}: TSecretImportServiceFactoryDep) => { }: TSecretImportServiceFactoryDep) => {
const createImport = async ({ const createImport = async ({
environment, environment,
@@ -489,7 +496,23 @@ export const secretImportServiceFactory = ({
) )
); );
const botKey = await projectBotService.getBotKey(projectId); const { botKey, shouldUseSecretV2Bridge } = await projectBotService.getBotKey(projectId);
if (shouldUseSecretV2Bridge) {
const { encryptor: secretManagerEncryptor } = await kmsService.createCipherPairWithDataKey({
type: KmsDataKey.SecretManager,
projectId
});
const importedSecrets = await fnSecretsV2FromImports({
allowedImports,
folderDAL,
secretDAL: secretV2BridgeDAL,
secretImportDAL,
decryptor: (value) =>
value ? secretManagerEncryptor({ plainText: value }).cipherTextBlob.toString() : undefined
});
return importedSecrets;
}
if (!botKey) throw new BadRequestError({ message: "Project bot not found", name: "bot_not_found_error" }); if (!botKey) throw new BadRequestError({ message: "Project bot not found", name: "bot_not_found_error" });
const importedSecrets = await fnSecretsFromImports({ allowedImports, folderDAL, secretDAL, secretImportDAL }); const importedSecrets = await fnSecretsFromImports({ allowedImports, folderDAL, secretDAL, secretImportDAL });
@@ -4,11 +4,11 @@ import { TableName, TSecretFolders, TSecretsV2 } from "@app/db/schemas";
import { groupBy } from "@app/lib/fn"; import { groupBy } from "@app/lib/fn";
import { logger } from "@app/lib/logger"; import { logger } from "@app/lib/logger";
import { TKmsServiceFactory } from "../kms/kms-service";
import { TProjectEnvDALFactory } from "../project-env/project-env-dal"; import { TProjectEnvDALFactory } from "../project-env/project-env-dal";
import { TSecretFolderDALFactory } from "../secret-folder/secret-folder-dal"; import { TSecretFolderDALFactory } from "../secret-folder/secret-folder-dal";
import { TSecretV2BridgeDALFactory } from "./secret-v2-bridge-dal"; import { TSecretV2BridgeDALFactory } from "./secret-v2-bridge-dal";
import { TFnSecretBulkDelete, TFnSecretBulkInsert, TFnSecretBulkUpdate } from "./secret-v2-bridge-types"; import { TFnSecretBulkDelete, TFnSecretBulkInsert, TFnSecretBulkUpdate } from "./secret-v2-bridge-types";
import { TKmsServiceFactory } from "../kms/kms-service";
const INTERPOLATION_SYNTAX_REG = /\${([^}]+)}/g; const INTERPOLATION_SYNTAX_REG = /\${([^}]+)}/g;
@@ -14,6 +14,7 @@ import { alphaNumericNanoId } from "@app/lib/nanoid";
import { ActorType } from "../auth/auth-type"; import { ActorType } from "../auth/auth-type";
import { TKmsServiceFactory } from "../kms/kms-service"; import { TKmsServiceFactory } from "../kms/kms-service";
import { KmsDataKey } from "../kms/kms-types";
import { TProjectEnvDALFactory } from "../project-env/project-env-dal"; import { TProjectEnvDALFactory } from "../project-env/project-env-dal";
import { TSecretQueueFactory } from "../secret/secret-queue"; import { TSecretQueueFactory } from "../secret/secret-queue";
import { TSecretFolderDALFactory } from "../secret-folder/secret-folder-dal"; import { TSecretFolderDALFactory } from "../secret-folder/secret-folder-dal";
@@ -22,7 +23,6 @@ import { fnSecretsV2FromImports } from "../secret-import/secret-import-fns";
import { TSecretTagDALFactory } from "../secret-tag/secret-tag-dal"; import { TSecretTagDALFactory } from "../secret-tag/secret-tag-dal";
import { TSecretV2BridgeDALFactory } from "./secret-v2-bridge-dal"; import { TSecretV2BridgeDALFactory } from "./secret-v2-bridge-dal";
import { import {
secretEncryptionHelper,
fnSecretBulkDelete, fnSecretBulkDelete,
fnSecretBulkInsert, fnSecretBulkInsert,
fnSecretBulkUpdate, fnSecretBulkUpdate,
@@ -51,10 +51,7 @@ import { TSecretVersionV2TagDALFactory } from "./secret-version-tag-dal";
type TSecretV2BridgeServiceFactoryDep = { type TSecretV2BridgeServiceFactoryDep = {
secretDAL: TSecretV2BridgeDALFactory; secretDAL: TSecretV2BridgeDALFactory;
secretVersionDAL: TSecretVersionV2DALFactory; secretVersionDAL: TSecretVersionV2DALFactory;
kmsService: Pick< kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey">;
TKmsServiceFactory,
"getProjectSecretManagerKmsDataKey" | "encryptWithInputKey" | "decryptWithInputKey"
>;
secretVersionTagDAL: Pick<TSecretVersionV2TagDALFactory, "insertMany">; secretVersionTagDAL: Pick<TSecretVersionV2TagDALFactory, "insertMany">;
secretTagDAL: TSecretTagDALFactory; secretTagDAL: TSecretTagDALFactory;
permissionService: Pick<TPermissionServiceFactory, "getProjectPermission">; permissionService: Pick<TPermissionServiceFactory, "getProjectPermission">;
@@ -69,7 +66,7 @@ type TSecretV2BridgeServiceFactoryDep = {
secretApprovalRequestDAL: Pick<TSecretApprovalRequestDALFactory, "create" | "transaction">; secretApprovalRequestDAL: Pick<TSecretApprovalRequestDALFactory, "create" | "transaction">;
secretApprovalRequestSecretDAL: Pick< secretApprovalRequestSecretDAL: Pick<
TSecretApprovalRequestSecretDALFactory, TSecretApprovalRequestSecretDALFactory,
"insertMany" | "insertApprovalSecretTags" "insertV2Bridge" | "insertApprovalSecretV2Tags"
>; >;
snapshotService: Pick<TSecretSnapshotServiceFactory, "performSnapshot">; snapshotService: Pick<TSecretSnapshotServiceFactory, "performSnapshot">;
}; };
@@ -150,8 +147,10 @@ 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 { encryptor: secretManagerEncryptor } = await kmsService.createCipherPairWithDataKey({
const secretManagerEncryptor = await kmsService.encryptWithInputKey({ key: secretManagerDataKey }); type: KmsDataKey.SecretManager,
projectId
});
const secret = await secretDAL.transaction((tx) => const secret = await secretDAL.transaction((tx) =>
fnSecretBulkInsert({ fnSecretBulkInsert({
@@ -161,8 +160,12 @@ export const secretV2BridgeServiceFactory = ({
version: 1, version: 1,
type, type,
reminderRepeatDays: el.secretReminderRepeatDays, reminderRepeatDays: el.secretReminderRepeatDays,
encryptedComment: secretEncryptionHelper.encryptValue(secretManagerEncryptor, el.secretComment), encryptedComment: el.secretComment
encryptedValue: secretEncryptionHelper.encryptValue(secretManagerEncryptor, el.secretValue), ? secretManagerEncryptor({ plainText: Buffer.from(el.secretComment) }).cipherTextBlob
: undefined,
encryptedValue: el.secretValue
? secretManagerEncryptor({ plainText: Buffer.from(el.secretValue) }).cipherTextBlob
: undefined,
reminderNote: el.secretReminderNote, reminderNote: el.secretReminderNote,
skipMultilineEncoding: el.skipMultilineEncoding, skipMultilineEncoding: el.skipMultilineEncoding,
key: secretName, key: secretName,
@@ -179,7 +182,7 @@ export const secretV2BridgeServiceFactory = ({
}) })
); );
await snapshotService.performSnapshot(folderId, true); await snapshotService.performSnapshot(folderId);
await secretQueueService.syncSecrets({ await secretQueueService.syncSecrets({
secretPath, secretPath,
actorId, actorId,
@@ -274,14 +277,16 @@ export const secretV2BridgeServiceFactory = ({
const tags = inputSecret.tagIds ? await secretTagDAL.find({ projectId, $in: { id: inputSecret.tagIds } }) : []; const tags = inputSecret.tagIds ? await secretTagDAL.find({ projectId, $in: { id: inputSecret.tagIds } }) : [];
if ((inputSecret.tagIds || []).length !== tags.length) throw new BadRequestError({ message: "Tag not found" }); if ((inputSecret.tagIds || []).length !== tags.length) throw new BadRequestError({ message: "Tag not found" });
const { secretName, secretValue, secretComment } = inputSecret; const { secretName, secretValue } = inputSecret;
const secretManagerDataKey = await kmsService.getProjectSecretManagerKmsDataKey(projectId); const { encryptor: secretManagerEncryptor } = await kmsService.createCipherPairWithDataKey({
const secretManagerEncryptor = await kmsService.encryptWithInputKey({ key: secretManagerDataKey }); type: KmsDataKey.SecretManager,
projectId
});
const encryptedValue = const encryptedValue =
typeof secretValue !== "undefined" typeof secretValue !== "undefined"
? { ? {
encryptedValue: secretEncryptionHelper.encryptValue(secretManagerEncryptor, secretValue) as Buffer, encryptedValue: secretManagerEncryptor({ plainText: Buffer.from(secretValue) }).cipherTextBlob,
references: getAllNestedSecretReferences(secretValue) references: getAllNestedSecretReferences(secretValue)
} }
: {}; : {};
@@ -294,7 +299,9 @@ export const secretV2BridgeServiceFactory = ({
filter: { id: secretId }, filter: { id: secretId },
data: { data: {
reminderRepeatDays: inputSecret.secretReminderRepeatDays, reminderRepeatDays: inputSecret.secretReminderRepeatDays,
encryptedComment: secretEncryptionHelper.encryptValue(secretManagerEncryptor, secretComment), encryptedComment: inputSecret.secretComment
? secretManagerEncryptor({ plainText: Buffer.from(inputSecret.secretComment) }).cipherTextBlob
: undefined,
reminderNote: inputSecret.secretReminderNote, reminderNote: inputSecret.secretReminderNote,
skipMultilineEncoding: inputSecret.skipMultilineEncoding, skipMultilineEncoding: inputSecret.skipMultilineEncoding,
key: inputSecret.newSecretName || secretName, key: inputSecret.newSecretName || secretName,
@@ -319,7 +326,7 @@ export const secretV2BridgeServiceFactory = ({
projectId projectId
}); });
await snapshotService.performSnapshot(folderId, true); await snapshotService.performSnapshot(folderId);
await secretQueueService.syncSecrets({ await secretQueueService.syncSecrets({
actor, actor,
actorId, actorId,
@@ -385,7 +392,7 @@ export const secretV2BridgeServiceFactory = ({
}) })
); );
await snapshotService.performSnapshot(folderId, true); await snapshotService.performSnapshot(folderId);
await secretQueueService.syncSecrets({ await secretQueueService.syncSecrets({
actor, actor,
actorId, actorId,
@@ -394,12 +401,18 @@ export const secretV2BridgeServiceFactory = ({
environmentSlug: folder.environment.slug environmentSlug: folder.environment.slug
}); });
const secretManagerDataKey = await kmsService.getProjectSecretManagerKmsDataKey(projectId); const { decryptor: secretManagerDecryptor } = await kmsService.createCipherPairWithDataKey({
const secretManagerDecryptor = await kmsService.decryptWithInputKey({ key: secretManagerDataKey }); type: KmsDataKey.SecretManager,
projectId
});
return reshapeBridgeSecret(projectId, environment, secretPath, { return reshapeBridgeSecret(projectId, environment, secretPath, {
...deletedSecret[0], ...deletedSecret[0],
value: secretEncryptionHelper.decryptValue(secretManagerDecryptor, deletedSecret[0].encryptedValue), value: deletedSecret[0].encryptedValue
comment: secretEncryptionHelper.decryptValue(secretManagerDecryptor, deletedSecret[0].encryptedComment) ? secretManagerDecryptor({ cipherTextBlob: deletedSecret[0].encryptedValue }).toString()
: undefined,
comment: deletedSecret[0].encryptedComment
? secretManagerDecryptor({ cipherTextBlob: deletedSecret[0].encryptedComment }).toString()
: undefined
}); });
}; };
@@ -463,8 +476,10 @@ export const secretV2BridgeServiceFactory = ({
actorId actorId
); );
const secretManagerDataKey = await kmsService.getProjectSecretManagerKmsDataKey(projectId); const { decryptor: secretManagerDecryptor } = await kmsService.createCipherPairWithDataKey({
const secretManagerDecryptor = await kmsService.decryptWithInputKey({ key: secretManagerDataKey }); type: KmsDataKey.SecretManager,
projectId
});
if (includeImports) { if (includeImports) {
const secretImports = await secretImportDAL.findByFolderIds(paths.map((p) => p.folderId)); const secretImports = await secretImportDAL.findByFolderIds(paths.map((p) => p.folderId));
@@ -486,15 +501,19 @@ export const secretV2BridgeServiceFactory = ({
secretDAL, secretDAL,
folderDAL, folderDAL,
secretImportDAL, secretImportDAL,
decryptor: (value) => secretEncryptionHelper.decryptValue(secretManagerDecryptor, value) decryptor: (value) => (value ? secretManagerDecryptor({ cipherTextBlob: value }).toString() : undefined)
}); });
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,
value: secretEncryptionHelper.decryptValue(secretManagerDecryptor, secret.encryptedValue), value: secret.encryptedValue
comment: secretEncryptionHelper.decryptValue(secretManagerDecryptor, secret.encryptedComment) ? secretManagerDecryptor({ cipherTextBlob: secret.encryptedValue }).toString()
: undefined,
comment: secret.encryptedComment
? secretManagerDecryptor({ cipherTextBlob: secret.encryptedComment }).toString()
: undefined
}) })
), ),
imports: importedSecrets imports: importedSecrets
@@ -505,8 +524,12 @@ 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,
value: secretEncryptionHelper.decryptValue(secretManagerDecryptor, secret.encryptedValue), value: secret.encryptedValue
comment: secretEncryptionHelper.decryptValue(secretManagerDecryptor, secret.encryptedComment) ? secretManagerDecryptor({ cipherTextBlob: secret.encryptedValue }).toString()
: undefined,
comment: secret.encryptedComment
? secretManagerDecryptor({ cipherTextBlob: secret.encryptedComment }).toString()
: undefined
}) })
) )
}; };
@@ -553,8 +576,10 @@ export const secretV2BridgeServiceFactory = ({
secretType = SecretType.Shared; secretType = SecretType.Shared;
} }
const secretManagerDataKey = await kmsService.getProjectSecretManagerKmsDataKey(projectId); const { decryptor: secretManagerDecryptor } = await kmsService.createCipherPairWithDataKey({
const secretManagerDecryptor = await kmsService.decryptWithInputKey({ key: secretManagerDataKey }); type: KmsDataKey.SecretManager,
projectId
});
const secret = await (version === undefined const secret = await (version === undefined
? secretDAL.findOneWithTags({ ? secretDAL.findOneWithTags({
@@ -571,9 +596,10 @@ export const secretV2BridgeServiceFactory = ({
key: secretName key: secretName
}) })
.then((el) => SecretsV2Schema.parse({ ...el, id: el.secretId }))); .then((el) => SecretsV2Schema.parse({ ...el, id: el.secretId })));
const interpolateInlineSecretReference = interpolateSecrets({ const interpolateInlineSecretReference = interpolateSecrets({
projectId, projectId,
decryptSecret: (encryptedValue) => secretEncryptionHelper.decryptValue(secretManagerDecryptor, encryptedValue), decryptSecret: (value) => (value ? secretManagerDecryptor({ cipherTextBlob: value }).toString() : undefined),
secretDAL, secretDAL,
folderDAL folderDAL
}); });
@@ -600,17 +626,17 @@ export const secretV2BridgeServiceFactory = ({
secretDAL, secretDAL,
folderDAL, folderDAL,
secretImportDAL, secretImportDAL,
decryptor: (value) => secretEncryptionHelper.decryptValue(secretManagerDecryptor, value) decryptor: (value) => (value ? secretManagerDecryptor({ cipherTextBlob: value }).toString() : undefined)
}); });
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 = secretEncryptionHelper.decryptValue( let secretValue = importedSecret.encryptedValue
secretManagerDecryptor, ? secretManagerDecryptor({ cipherTextBlob: importedSecret.encryptedValue }).toString()
importedSecret.encryptedValue : undefined;
);
if (expandSecretReferences && secretValue) { if (expandSecretReferences && secretValue) {
const secretReferenceExpandedString = { const secretReferenceExpandedString = {
[importedSecret.key]: { value: secretValue } [importedSecret.key]: { value: secretValue }
@@ -623,7 +649,9 @@ export const secretV2BridgeServiceFactory = ({
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: secretEncryptionHelper.decryptValue(secretManagerDecryptor, importedSecret.encryptedComment) comment: importedSecret.encryptedComment
? secretManagerDecryptor({ cipherTextBlob: importedSecret.encryptedComment }).toString()
: undefined
}); });
} }
} }
@@ -631,7 +659,9 @@ export const secretV2BridgeServiceFactory = ({
} }
if (!secret) throw new BadRequestError({ message: "Secret not found" }); if (!secret) throw new BadRequestError({ message: "Secret not found" });
let secretValue = secretEncryptionHelper.decryptValue(secretManagerDecryptor, secret.encryptedValue); let secretValue = secret.encryptedValue
? secretManagerDecryptor({ cipherTextBlob: secret.encryptedValue }).toString()
: undefined;
if (expandSecretReferences && secretValue) { if (expandSecretReferences && secretValue) {
const secretReferenceExpandedString = { const secretReferenceExpandedString = {
[secret.key]: { value: secretValue } [secret.key]: { value: secretValue }
@@ -644,7 +674,9 @@ export const secretV2BridgeServiceFactory = ({
return reshapeBridgeSecret(projectId, environment, path, { return reshapeBridgeSecret(projectId, environment, path, {
...secret, ...secret,
value: secretValue, value: secretValue,
comment: secretEncryptionHelper.decryptValue(secretManagerDecryptor, secret.encryptedComment) comment: secret.encryptedComment
? secretManagerDecryptor({ cipherTextBlob: secret.encryptedComment }).toString()
: undefined
}); });
}; };
@@ -693,15 +725,19 @@ 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 { encryptor: secretManagerEncryptor, decryptor: secretManagerDecryptor } =
const secretManagerEncryptor = await kmsService.encryptWithInputKey({ key: secretManagerDataKey }); await kmsService.createCipherPairWithDataKey({ type: KmsDataKey.SecretManager, projectId });
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: secretEncryptionHelper.encryptValue(secretManagerEncryptor, el.secretComment), encryptedComment: el.secretComment
encryptedValue: secretEncryptionHelper.encryptValue(secretManagerEncryptor, el.secretValue), ? secretManagerEncryptor({ plainText: Buffer.from(el.secretComment) }).cipherTextBlob
: undefined,
encryptedValue: el.secretValue
? secretManagerEncryptor({ plainText: Buffer.from(el.secretValue) }).cipherTextBlob
: undefined,
skipMultilineEncoding: el.skipMultilineEncoding, skipMultilineEncoding: el.skipMultilineEncoding,
key: el.secretKey, key: el.secretKey,
tagIds: el.tagIds, tagIds: el.tagIds,
@@ -717,7 +753,7 @@ export const secretV2BridgeServiceFactory = ({
}) })
); );
await snapshotService.performSnapshot(folderId, true); await snapshotService.performSnapshot(folderId);
await secretQueueService.syncSecrets({ await secretQueueService.syncSecrets({
actor, actor,
actorId, actorId,
@@ -726,12 +762,13 @@ 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,
value: secretEncryptionHelper.decryptValue(secretManagerDecryptor, el.encryptedValue), value: el.encryptedValue ? secretManagerDecryptor({ cipherTextBlob: el.encryptedValue }).toString() : undefined,
comment: secretEncryptionHelper.decryptValue(secretManagerDecryptor, el.encryptedComment) comment: el.encryptedComment
? secretManagerDecryptor({ cipherTextBlob: el.encryptedComment }).toString()
: undefined
}) })
); );
}; };
@@ -799,8 +836,8 @@ 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 { encryptor: secretManagerEncryptor, decryptor: secretManagerDecryptor } =
const secretManagerEncryptor = await kmsService.encryptWithInputKey({ key: secretManagerDataKey }); await kmsService.createCipherPairWithDataKey({ type: KmsDataKey.SecretManager, projectId });
const secrets = await secretDAL.transaction(async (tx) => const secrets = await secretDAL.transaction(async (tx) =>
fnSecretBulkUpdate({ fnSecretBulkUpdate({
@@ -811,7 +848,7 @@ export const secretV2BridgeServiceFactory = ({
const encryptedValue = const encryptedValue =
typeof el.secretValue !== "undefined" typeof el.secretValue !== "undefined"
? { ? {
encryptedValue: secretEncryptionHelper.encryptValue(secretManagerEncryptor, el.secretValue) as Buffer, encryptedValue: secretManagerEncryptor({ plainText: Buffer.from(el.secretValue) }).cipherTextBlob,
references: getAllNestedSecretReferences(el.secretValue) references: getAllNestedSecretReferences(el.secretValue)
} }
: {}; : {};
@@ -819,7 +856,9 @@ export const secretV2BridgeServiceFactory = ({
filter: { id: originalSecret.id, type: SecretType.Shared }, filter: { id: originalSecret.id, type: SecretType.Shared },
data: { data: {
reminderRepeatDays: el.secretReminderRepeatDays, reminderRepeatDays: el.secretReminderRepeatDays,
encryptedComment: secretEncryptionHelper.encryptValue(secretManagerEncryptor, el.secretComment), encryptedComment: el.secretComment
? secretManagerEncryptor({ plainText: Buffer.from(el.secretComment) }).cipherTextBlob
: undefined,
reminderNote: el.secretReminderNote, reminderNote: el.secretReminderNote,
skipMultilineEncoding: el.skipMultilineEncoding, skipMultilineEncoding: el.skipMultilineEncoding,
key: el.newSecretName || el.secretKey, key: el.newSecretName || el.secretKey,
@@ -834,7 +873,7 @@ export const secretV2BridgeServiceFactory = ({
secretVersionTagDAL secretVersionTagDAL
}) })
); );
await snapshotService.performSnapshot(folderId, true); await snapshotService.performSnapshot(folderId);
await secretQueueService.syncSecrets({ await secretQueueService.syncSecrets({
actor, actor,
actorId, actorId,
@@ -843,12 +882,13 @@ 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,
value: secretEncryptionHelper.decryptValue(secretManagerDecryptor, el.encryptedValue), value: el.encryptedValue ? secretManagerDecryptor({ cipherTextBlob: el.encryptedValue }).toString() : undefined,
comment: secretEncryptionHelper.decryptValue(secretManagerDecryptor, el.encryptedComment) comment: el.encryptedComment
? secretManagerDecryptor({ cipherTextBlob: el.encryptedComment }).toString()
: undefined
}) })
); );
}; };
@@ -917,13 +957,17 @@ export const secretV2BridgeServiceFactory = ({
environmentSlug: folder.environment.slug environmentSlug: folder.environment.slug
}); });
const secretManagerDataKey = await kmsService.getProjectSecretManagerKmsDataKey(projectId); const { decryptor: secretManagerDecryptor } = await kmsService.createCipherPairWithDataKey({
const secretManagerDecryptor = await kmsService.decryptWithInputKey({ key: secretManagerDataKey }); type: KmsDataKey.SecretManager,
projectId
});
return secretsDeleted.map((el) => return secretsDeleted.map((el) =>
reshapeBridgeSecret(projectId, environment, secretPath, { reshapeBridgeSecret(projectId, environment, secretPath, {
...el, ...el,
value: secretEncryptionHelper.decryptValue(secretManagerDecryptor, el.encryptedValue), value: el.encryptedValue ? secretManagerDecryptor({ cipherTextBlob: el.encryptedValue }).toString() : undefined,
comment: secretEncryptionHelper.decryptValue(secretManagerDecryptor, el.encryptedComment) comment: el.encryptedComment
? secretManagerDecryptor({ cipherTextBlob: el.encryptedComment }).toString()
: undefined
}) })
); );
}; };
@@ -977,8 +1021,10 @@ 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 { decryptor: secretManagerDecryptor } = await kmsService.createCipherPairWithDataKey({
const secretManagerDecryptor = await kmsService.decryptWithInputKey({ key: secretManagerDataKey }); type: KmsDataKey.SecretManager,
projectId
});
await secretDAL.transaction(async (tx) => { await secretDAL.transaction(async (tx) => {
const secrets = await secretDAL.findAllProjectSecretValues(projectId, tx); const secrets = await secretDAL.findAllProjectSecretValues(projectId, tx);
await secretDAL.upsertSecretReferences( await secretDAL.upsertSecretReferences(
@@ -987,9 +1033,7 @@ export const secretV2BridgeServiceFactory = ({
.map(({ id, encryptedValue }) => ({ .map(({ id, encryptedValue }) => ({
secretId: id, secretId: id,
references: encryptedValue references: encryptedValue
? getAllNestedSecretReferences( ? getAllNestedSecretReferences(secretManagerDecryptor({ cipherTextBlob: encryptedValue }).toString())
secretEncryptionHelper.decryptValue(secretManagerDecryptor, encryptedValue) as string
)
: [] : []
})), })),
tx tx
@@ -1067,11 +1111,15 @@ export const secretV2BridgeServiceFactory = ({
}); });
} }
const secretManagerDataKey = await kmsService.getProjectSecretManagerKmsDataKey(projectId); const { decryptor: secretManagerDecryptor } = await kmsService.createCipherPairWithDataKey({
const secretManagerDecryptor = await kmsService.decryptWithInputKey({ key: secretManagerDataKey }); type: KmsDataKey.SecretManager,
projectId
});
const decryptedSourceSecrets = sourceSecrets.map((secret) => ({ const decryptedSourceSecrets = sourceSecrets.map((secret) => ({
...secret, ...secret,
value: secretEncryptionHelper.decryptValue(secretManagerDecryptor, secret.encryptedValue) value: secret.encryptedValue
? secretManagerDecryptor({ cipherTextBlob: secret.encryptedValue }).toString()
: undefined
})); }));
let isSourceUpdated = false; let isSourceUpdated = false;
@@ -1090,7 +1138,9 @@ export const secretV2BridgeServiceFactory = ({
const decryptedDestinationSecrets = destinationSecretsFromDB.map((secret) => { const decryptedDestinationSecrets = destinationSecretsFromDB.map((secret) => {
return { return {
...secret, ...secret,
value: secretEncryptionHelper.decryptValue(secretManagerDecryptor, secret.encryptedValue) value: secret.encryptedValue
? secretManagerDecryptor({ cipherTextBlob: secret.encryptedValue }).toString()
: undefined
}; };
}); });
@@ -1151,33 +1201,25 @@ export const secretV2BridgeServiceFactory = ({
tx tx
); );
// TODO(akhilmhdh-sev2): fix this const commits = locallyCreatedSecrets.concat(locallyUpdatedSecrets).map((doc) => {
// const commits = locallyCreatedSecrets.concat(locallyUpdatedSecrets).map((doc) => { const { operation } = doc;
// const { operation } = doc; const localSecret = destinationSecretsGroupedByKey[doc.key]?.[0];
// const localSecret = destinationSecretsGroupedByKey[doc.key]?.[0];
// return {
// return { op: operation,
// op: operation, requestId: approvalRequestDoc.id,
// requestId: approvalRequestDoc.id, metadata: doc.metadata,
// metadata: doc.metadata, key: doc.key,
// secretKeyIV: doc.secretKeyIV, encryptedValue: doc.encryptedValue,
// secretKeyTag: doc.secretKeyTag, encryptedComment: doc.encryptedComment,
// secretKeyCiphertext: doc.secretKeyCiphertext, skipMultilineEncoding: doc.skipMultilineEncoding,
// secretValueIV: doc.secretValueIV, // except create operation other two needs the secret id and version id
// secretValueTag: doc.secretValueTag, ...(operation !== SecretOperations.Create
// secretValueCiphertext: doc.secretValueCiphertext, ? { secretId: localSecret.id, secretVersion: latestSecretVersions[localSecret.id].id }
// secretBlindIndex: doc.secretBlindIndex, : {})
// secretCommentIV: doc.secretCommentIV, };
// secretCommentTag: doc.secretCommentTag, });
// secretCommentCiphertext: doc.secretCommentCiphertext, await secretApprovalRequestSecretDAL.insertV2Bridge(commits, tx);
// skipMultilineEncoding: doc.skipMultilineEncoding,
// // except create operation other two needs the secret id and version id
// ...(operation !== SecretOperations.Create
// ? { secretId: localSecret.id, secretVersion: latestSecretVersions[localSecret.id].id }
// : {})
// };
// });
await secretApprovalRequestSecretDAL.insertMany([], tx);
} else { } else {
// apply changes directly // apply changes directly
if (locallyCreatedSecrets.length) { if (locallyCreatedSecrets.length) {
@@ -1268,34 +1310,24 @@ export const secretV2BridgeServiceFactory = ({
tx tx
); );
// TODO(akhilmhdh-sev2): finish this const commits = locallyDeletedSecrets.map((doc) => {
// const commits = locallyDeletedSecrets.map((doc) => { const { operation } = doc;
// const { operation } = doc; const localSecret = sourceSecretsGroupByKey[doc.key]?.[0];
// const localSecret = sourceSecretsGroupByKey[doc.key]?.[0];
//
// return {
// op: operation,
// keyEncoding: doc.keyEncoding,
// algorithm: doc.algorithm,
// requestId: approvalRequestDoc.id,
// metadata: doc.metadata,
// secretKeyIV: doc.secretKeyIV,
// secretKeyTag: doc.secretKeyTag,
// secretKeyCiphertext: doc.secretKeyCiphertext,
// secretValueIV: doc.secretValueIV,
// secretValueTag: doc.secretValueTag,
// secretValueCiphertext: doc.secretValueCiphertext,
// secretBlindIndex: doc.secretBlindIndex,
// secretCommentIV: doc.secretCommentIV,
// secretCommentTag: doc.secretCommentTag,
// secretCommentCiphertext: doc.secretCommentCiphertext,
// skipMultilineEncoding: doc.skipMultilineEncoding,
// secretId: localSecret.id,
// secretVersion: latestSecretVersions[localSecret.id].id
// };
// });
await secretApprovalRequestSecretDAL.insertMany([], tx); return {
op: operation,
requestId: approvalRequestDoc.id,
metadata: doc.metadata,
key: doc.key,
encryptedComment: doc.encryptedComment,
encryptedValue: doc.encryptedValue,
skipMultilineEncoding: doc.skipMultilineEncoding,
secretId: localSecret.id,
secretVersion: latestSecretVersions[localSecret.id].id
};
});
await secretApprovalRequestSecretDAL.insertV2Bridge(commits, tx);
} else { } else {
// if no secret approval policy is present, we delete directly. // if no secret approval policy is present, we delete directly.
await secretDAL.delete( await secretDAL.delete(
@@ -1313,7 +1345,7 @@ export const secretV2BridgeServiceFactory = ({
}); });
if (isDestinationUpdated) { if (isDestinationUpdated) {
await snapshotService.performSnapshot(destinationFolder.id, true); await snapshotService.performSnapshot(destinationFolder.id);
await secretQueueService.syncSecrets({ await secretQueueService.syncSecrets({
projectId, projectId,
secretPath: destinationFolder.path, secretPath: destinationFolder.path,
@@ -1324,7 +1356,7 @@ export const secretV2BridgeServiceFactory = ({
} }
if (isSourceUpdated) { if (isSourceUpdated) {
await snapshotService.performSnapshot(sourceFolder.id, true); await snapshotService.performSnapshot(sourceFolder.id);
await secretQueueService.syncSecrets({ await secretQueueService.syncSecrets({
projectId, projectId,
secretPath: sourceFolder.path, secretPath: sourceFolder.path,
+6 -6
View File
@@ -22,8 +22,14 @@ import {
import { BadRequestError } from "@app/lib/errors"; import { BadRequestError } from "@app/lib/errors";
import { groupBy, unique } from "@app/lib/fn"; import { groupBy, unique } from "@app/lib/fn";
import { logger } from "@app/lib/logger"; import { logger } from "@app/lib/logger";
import {
fnSecretBulkInsert as fnSecretV2BridgeBulkInsert,
fnSecretBulkUpdate as fnSecretV2BridgeBulkUpdate,
getAllNestedSecretReferences as getAllNestedSecretReferencesV2Bridge
} from "@app/services/secret-v2-bridge/secret-v2-bridge-fns";
import { ActorAuthMethod, ActorType } from "../auth/auth-type"; import { ActorAuthMethod, ActorType } from "../auth/auth-type";
import { KmsDataKey } from "../kms/kms-types";
import { getBotKeyFnFactory } from "../project-bot/project-bot-fns"; import { getBotKeyFnFactory } from "../project-bot/project-bot-fns";
import { TProjectEnvDALFactory } from "../project-env/project-env-dal"; import { TProjectEnvDALFactory } from "../project-env/project-env-dal";
import { TSecretFolderDALFactory } from "../secret-folder/secret-folder-dal"; import { TSecretFolderDALFactory } from "../secret-folder/secret-folder-dal";
@@ -39,12 +45,6 @@ import {
TUpdateManySecretsRawFn, TUpdateManySecretsRawFn,
TUpdateManySecretsRawFnFactory TUpdateManySecretsRawFnFactory
} from "./secret-types"; } from "./secret-types";
import { KmsDataKey } from "../kms/kms-types";
import {
fnSecretBulkInsert as fnSecretV2BridgeBulkInsert,
fnSecretBulkUpdate as fnSecretV2BridgeBulkUpdate,
getAllNestedSecretReferences as getAllNestedSecretReferencesV2Bridge
} from "@app/services/secret-v2-bridge/secret-v2-bridge-fns";
export const generateSecretBlindIndexBySalt = async (secretName: string, secretBlindIndexDoc: TSecretBlindIndexes) => { export const generateSecretBlindIndexBySalt = async (secretName: string, secretBlindIndexDoc: TSecretBlindIndexes) => {
const appCfg = getConfig(); const appCfg = getConfig();
+33 -15
View File
@@ -18,6 +18,8 @@ import { TSecretTagDALFactory } from "@app/services/secret-tag/secret-tag-dal";
import { TIntegrationDALFactory } from "../integration/integration-dal"; import { TIntegrationDALFactory } from "../integration/integration-dal";
import { TIntegrationAuthServiceFactory } from "../integration-auth/integration-auth-service"; import { TIntegrationAuthServiceFactory } from "../integration-auth/integration-auth-service";
import { syncIntegrationSecrets } from "../integration-auth/integration-sync-secret"; import { syncIntegrationSecrets } from "../integration-auth/integration-sync-secret";
import { TKmsServiceFactory } from "../kms/kms-service";
import { KmsDataKey } from "../kms/kms-types";
import { TOrgDALFactory } from "../org/org-dal"; import { TOrgDALFactory } from "../org/org-dal";
import { TProjectDALFactory } from "../project/project-dal"; import { TProjectDALFactory } from "../project/project-dal";
import { TProjectBotServiceFactory } from "../project-bot/project-bot-service"; import { TProjectBotServiceFactory } from "../project-bot/project-bot-service";
@@ -25,6 +27,9 @@ import { TProjectEnvDALFactory } from "../project-env/project-env-dal";
import { TProjectMembershipDALFactory } from "../project-membership/project-membership-dal"; import { TProjectMembershipDALFactory } from "../project-membership/project-membership-dal";
import { TSecretFolderDALFactory } from "../secret-folder/secret-folder-dal"; import { TSecretFolderDALFactory } from "../secret-folder/secret-folder-dal";
import { TSecretImportDALFactory } from "../secret-import/secret-import-dal"; import { TSecretImportDALFactory } from "../secret-import/secret-import-dal";
import { TSecretV2BridgeDALFactory } from "../secret-v2-bridge/secret-v2-bridge-dal";
import { TSecretVersionV2DALFactory } from "../secret-v2-bridge/secret-version-dal";
import { TSecretVersionV2TagDALFactory } from "../secret-v2-bridge/secret-version-tag-dal";
import { SmtpTemplates, TSmtpService } from "../smtp/smtp-service"; import { SmtpTemplates, TSmtpService } from "../smtp/smtp-service";
import { TWebhookDALFactory } from "../webhook/webhook-dal"; import { TWebhookDALFactory } from "../webhook/webhook-dal";
import { fnTriggerWebhook } from "../webhook/webhook-fns"; import { fnTriggerWebhook } from "../webhook/webhook-fns";
@@ -36,10 +41,6 @@ import {
TRemoveSecretReminderDTO, TRemoveSecretReminderDTO,
TSyncSecretsDTO TSyncSecretsDTO
} from "./secret-types"; } from "./secret-types";
import { TKmsServiceFactory } from "../kms/kms-service";
import { TSecretV2BridgeDALFactory } from "../secret-v2-bridge/secret-v2-bridge-dal";
import { TSecretVersionV2DALFactory } from "../secret-v2-bridge/secret-version-dal";
import { TSecretVersionV2TagDALFactory } from "../secret-v2-bridge/secret-version-tag-dal";
export type TSecretQueueFactory = ReturnType<typeof secretQueueFactory>; export type TSecretQueueFactory = ReturnType<typeof secretQueueFactory>;
type TSecretQueueFactoryDep = { type TSecretQueueFactoryDep = {
@@ -557,6 +558,10 @@ export const secretQueueFactory = ({
} }
const { shouldUseSecretV2Bridge, botKey } = await projectBotService.getBotKey(projectId); const { shouldUseSecretV2Bridge, botKey } = await projectBotService.getBotKey(projectId);
const { decryptor: secretManagerDecryptor } = await kmsService.createCipherPairWithDataKey({
type: KmsDataKey.SecretManager,
projectId
});
let referencedFolderIds; let referencedFolderIds;
if (shouldUseSecretV2Bridge) { if (shouldUseSecretV2Bridge) {
const secretReferences = await secretV2BridgeDAL.findReferencedSecretReferences( const secretReferences = await secretV2BridgeDAL.findReferencedSecretReferences(
@@ -623,31 +628,44 @@ export const secretQueueFactory = ({
projectId: integration.projectId projectId: integration.projectId
}; };
const { accessToken, accessId } = await integrationAuthService.getIntegrationAccessToken(integrationAuth, botKey); const { accessToken, accessId } = await integrationAuthService.getIntegrationAccessToken(
const awsAssumeRoleArn = integrationAuth,
shouldUseSecretV2Bridge,
botKey
);
let awsAssumeRoleArn = null;
if (shouldUseSecretV2Bridge) {
if (integrationAuth.awsAssumeIamRoleArnCipherText) {
awsAssumeRoleArn = secretManagerDecryptor({
cipherTextBlob: Buffer.from(integrationAuth.awsAssumeIamRoleArnCipherText)
}).toString();
}
} else if (
integrationAuth.awsAssumeIamRoleArnTag && integrationAuth.awsAssumeIamRoleArnTag &&
integrationAuth.awsAssumeIamRoleArnIV && integrationAuth.awsAssumeIamRoleArnIV &&
integrationAuth.awsAssumeIamRoleArnCipherText integrationAuth.awsAssumeIamRoleArnCipherText
? decryptSymmetric128BitHexKeyUTF8({ ) {
ciphertext: integrationAuth.awsAssumeIamRoleArnCipherText, awsAssumeRoleArn = decryptSymmetric128BitHexKeyUTF8({
iv: integrationAuth.awsAssumeIamRoleArnIV, ciphertext: integrationAuth.awsAssumeIamRoleArnCipherText,
tag: integrationAuth.awsAssumeIamRoleArnTag, iv: integrationAuth.awsAssumeIamRoleArnIV,
key: botKey tag: integrationAuth.awsAssumeIamRoleArnTag,
}) key: botKey as string
: null; });
}
const secrets = shouldUseSecretV2Bridge const secrets = shouldUseSecretV2Bridge
? await getIntegrationSecretsV2({ ? await getIntegrationSecretsV2({
environment, environment,
projectId, projectId,
folderId: folder.id, folderId: folder.id,
depth: 1 depth: 1,
decryptor: (value) => (value ? secretManagerDecryptor({ cipherTextBlob: value }).toString() : "")
}) })
: await getIntegrationSecrets({ : await getIntegrationSecrets({
environment, environment,
projectId, projectId,
folderId: folder.id, folderId: folder.id,
key: botKey, key: botKey as string,
depth: 1 depth: 1
}); });
const suffixedSecrets: typeof secrets = {}; const suffixedSecrets: typeof secrets = {};
@@ -14,6 +14,7 @@ import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services
import { TSecretApprovalPolicyServiceFactory } from "@app/ee/services/secret-approval-policy/secret-approval-policy-service"; import { TSecretApprovalPolicyServiceFactory } from "@app/ee/services/secret-approval-policy/secret-approval-policy-service";
import { TSecretApprovalRequestDALFactory } from "@app/ee/services/secret-approval-request/secret-approval-request-dal"; import { TSecretApprovalRequestDALFactory } from "@app/ee/services/secret-approval-request/secret-approval-request-dal";
import { TSecretApprovalRequestSecretDALFactory } from "@app/ee/services/secret-approval-request/secret-approval-request-secret-dal"; import { TSecretApprovalRequestSecretDALFactory } from "@app/ee/services/secret-approval-request/secret-approval-request-secret-dal";
import { TSecretApprovalRequestServiceFactory } from "@app/ee/services/secret-approval-request/secret-approval-request-service";
import { TSecretSnapshotServiceFactory } from "@app/ee/services/secret-snapshot/secret-snapshot-service"; import { TSecretSnapshotServiceFactory } from "@app/ee/services/secret-snapshot/secret-snapshot-service";
import { getConfig } from "@app/lib/config/env"; import { getConfig } from "@app/lib/config/env";
import { import {
@@ -73,7 +74,6 @@ import {
} from "./secret-types"; } from "./secret-types";
import { TSecretVersionDALFactory } from "./secret-version-dal"; import { TSecretVersionDALFactory } from "./secret-version-dal";
import { TSecretVersionTagDALFactory } from "./secret-version-tag-dal"; import { TSecretVersionTagDALFactory } from "./secret-version-tag-dal";
import { TSecretApprovalRequestServiceFactory } from "@app/ee/services/secret-approval-request/secret-approval-request-service";
type TSecretServiceFactoryDep = { type TSecretServiceFactoryDep = {
secretDAL: TSecretDALFactory; secretDAL: TSecretDALFactory;
+1 -1
View File
@@ -12,10 +12,10 @@ import { TSecretFolderDALFactory } from "@app/services/secret-folder/secret-fold
import { TSecretTagDALFactory } from "@app/services/secret-tag/secret-tag-dal"; import { TSecretTagDALFactory } from "@app/services/secret-tag/secret-tag-dal";
import { ActorType } from "../auth/auth-type"; import { ActorType } from "../auth/auth-type";
import { TKmsServiceFactory } from "../kms/kms-service";
import { TSecretV2BridgeDALFactory } from "../secret-v2-bridge/secret-v2-bridge-dal"; import { TSecretV2BridgeDALFactory } from "../secret-v2-bridge/secret-v2-bridge-dal";
import { TSecretVersionV2DALFactory } from "../secret-v2-bridge/secret-version-dal"; import { TSecretVersionV2DALFactory } from "../secret-v2-bridge/secret-version-dal";
import { TSecretVersionV2TagDALFactory } from "../secret-v2-bridge/secret-version-tag-dal"; import { TSecretVersionV2TagDALFactory } from "../secret-v2-bridge/secret-version-tag-dal";
import { TKmsServiceFactory } from "../kms/kms-service";
type TPartialSecret = Pick<TSecrets, "id" | "secretReminderRepeatDays" | "secretReminderNote">; type TPartialSecret = Pick<TSecrets, "id" | "secretReminderRepeatDays" | "secretReminderNote">;