mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-06 13:27:22 +00:00
requested changes
This commit is contained in:
@@ -1,15 +1,9 @@
|
|||||||
import { ForbiddenError, subject } from "@casl/ability";
|
import { ForbiddenError, subject } from "@casl/ability";
|
||||||
|
|
||||||
import {
|
import { TableName, TSecretTagJunctionInsert, TSecretV2TagJunctionInsert } from "@app/db/schemas";
|
||||||
TableName,
|
|
||||||
TSecretSnapshotFolders,
|
|
||||||
TSecretSnapshotSecretsV2,
|
|
||||||
TSecretTagJunctionInsert,
|
|
||||||
TSecretV2TagJunctionInsert
|
|
||||||
} from "@app/db/schemas";
|
|
||||||
import { decryptSymmetric128BitHexKeyUTF8 } from "@app/lib/crypto";
|
import { decryptSymmetric128BitHexKeyUTF8 } from "@app/lib/crypto";
|
||||||
import { InternalServerError, NotFoundError } from "@app/lib/errors";
|
import { InternalServerError, NotFoundError } from "@app/lib/errors";
|
||||||
import { chunkArray, 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 "@app/services/kms/kms-service";
|
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
||||||
import { KmsDataKey } from "@app/services/kms/kms-types";
|
import { KmsDataKey } from "@app/services/kms/kms-types";
|
||||||
@@ -247,14 +241,8 @@ export const secretSnapshotServiceFactory = ({
|
|||||||
tx
|
tx
|
||||||
);
|
);
|
||||||
|
|
||||||
const chunkedSnapshotSecrets = chunkArray(secretVersions, 2500);
|
const snapshotSecrets = await snapshotSecretV2BridgeDAL.batchInsert(
|
||||||
const chunkedSnapshotFolders = chunkArray(folderVersions, 2500);
|
secretVersions.map(({ id }) => ({
|
||||||
const snapshotSecrets: TSecretSnapshotSecretsV2[] = [];
|
|
||||||
const snapshotFolders: TSecretSnapshotFolders[] = [];
|
|
||||||
|
|
||||||
for await (const chunk of chunkedSnapshotSecrets) {
|
|
||||||
const result = await snapshotSecretV2BridgeDAL.insertMany(
|
|
||||||
chunk.map(({ id }) => ({
|
|
||||||
secretVersionId: id,
|
secretVersionId: id,
|
||||||
envId: folder.environment.envId,
|
envId: folder.environment.envId,
|
||||||
snapshotId: newSnapshot.id
|
snapshotId: newSnapshot.id
|
||||||
@@ -262,12 +250,8 @@ export const secretSnapshotServiceFactory = ({
|
|||||||
tx
|
tx
|
||||||
);
|
);
|
||||||
|
|
||||||
snapshotSecrets.push(...result);
|
const snapshotFolders = await snapshotFolderDAL.batchInsert(
|
||||||
}
|
folderVersions.map(({ id }) => ({
|
||||||
|
|
||||||
for await (const chunk of chunkedSnapshotFolders) {
|
|
||||||
const result = await snapshotFolderDAL.insertMany(
|
|
||||||
chunk.map(({ id }) => ({
|
|
||||||
folderVersionId: id,
|
folderVersionId: id,
|
||||||
envId: folder.environment.envId,
|
envId: folder.environment.envId,
|
||||||
snapshotId: newSnapshot.id
|
snapshotId: newSnapshot.id
|
||||||
@@ -275,9 +259,6 @@ export const secretSnapshotServiceFactory = ({
|
|||||||
tx
|
tx
|
||||||
);
|
);
|
||||||
|
|
||||||
snapshotFolders.push(...result);
|
|
||||||
}
|
|
||||||
|
|
||||||
return { ...newSnapshot, secrets: snapshotSecrets, folder: snapshotFolders };
|
return { ...newSnapshot, secrets: snapshotSecrets, folder: snapshotFolders };
|
||||||
});
|
});
|
||||||
return snapshot;
|
return snapshot;
|
||||||
|
|||||||
@@ -1206,8 +1206,16 @@ export const registerRoutes = async (
|
|||||||
const externalMigrationQueue = externalMigrationQueueFactory({
|
const externalMigrationQueue = externalMigrationQueueFactory({
|
||||||
orgService,
|
orgService,
|
||||||
projectEnvService,
|
projectEnvService,
|
||||||
|
projectDAL,
|
||||||
projectService,
|
projectService,
|
||||||
smtpService,
|
smtpService,
|
||||||
|
kmsService,
|
||||||
|
projectEnvDAL,
|
||||||
|
secretVersionDAL: secretVersionV2BridgeDAL,
|
||||||
|
secretTagDAL,
|
||||||
|
secretVersionTagDAL: secretVersionTagV2BridgeDAL,
|
||||||
|
folderDAL,
|
||||||
|
secretDAL: secretV2BridgeDAL,
|
||||||
queueService,
|
queueService,
|
||||||
secretV2BridgeService
|
secretV2BridgeService
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -4,19 +4,39 @@ import sjcl from "sjcl";
|
|||||||
import tweetnacl from "tweetnacl";
|
import tweetnacl from "tweetnacl";
|
||||||
import tweetnaclUtil from "tweetnacl-util";
|
import tweetnaclUtil from "tweetnacl-util";
|
||||||
|
|
||||||
import { OrgMembershipRole, ProjectMembershipRole } from "@app/db/schemas";
|
import { OrgMembershipRole, ProjectMembershipRole, SecretType } from "@app/db/schemas";
|
||||||
import { BadRequestError } from "@app/lib/errors";
|
import { BadRequestError, NotFoundError } from "@app/lib/errors";
|
||||||
import { chunkArray } from "@app/lib/fn";
|
import { chunkArray } from "@app/lib/fn";
|
||||||
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 { TKmsServiceFactory } from "../kms/kms-service";
|
||||||
|
import { KmsDataKey } from "../kms/kms-types";
|
||||||
import { TOrgServiceFactory } from "../org/org-service";
|
import { TOrgServiceFactory } from "../org/org-service";
|
||||||
|
import { TProjectDALFactory } from "../project/project-dal";
|
||||||
import { TProjectServiceFactory } from "../project/project-service";
|
import { TProjectServiceFactory } from "../project/project-service";
|
||||||
|
import { TProjectEnvDALFactory } from "../project-env/project-env-dal";
|
||||||
import { TProjectEnvServiceFactory } from "../project-env/project-env-service";
|
import { TProjectEnvServiceFactory } from "../project-env/project-env-service";
|
||||||
|
import { TSecretFolderDALFactory } from "../secret-folder/secret-folder-dal";
|
||||||
|
import { TSecretTagDALFactory } from "../secret-tag/secret-tag-dal";
|
||||||
|
import { TSecretV2BridgeDALFactory } from "../secret-v2-bridge/secret-v2-bridge-dal";
|
||||||
|
import { fnSecretBulkInsert, getAllNestedSecretReferences } from "../secret-v2-bridge/secret-v2-bridge-fns";
|
||||||
import type { TSecretV2BridgeServiceFactory } from "../secret-v2-bridge/secret-v2-bridge-service";
|
import type { TSecretV2BridgeServiceFactory } from "../secret-v2-bridge/secret-v2-bridge-service";
|
||||||
|
import { TSecretVersionV2DALFactory } from "../secret-v2-bridge/secret-version-dal";
|
||||||
|
import { TSecretVersionV2TagDALFactory } from "../secret-v2-bridge/secret-version-tag-dal";
|
||||||
import { InfisicalImportData, TEnvKeyExportJSON, TImportInfisicalDataCreate } from "./external-migration-types";
|
import { InfisicalImportData, TEnvKeyExportJSON, TImportInfisicalDataCreate } from "./external-migration-types";
|
||||||
|
|
||||||
export type TImportDataIntoInfisicalDTO = {
|
export type TImportDataIntoInfisicalDTO = {
|
||||||
|
projectDAL: Pick<TProjectDALFactory, "transaction">;
|
||||||
|
projectEnvDAL: Pick<TProjectEnvDALFactory, "find" | "findLastEnvPosition" | "create" | "findOne">;
|
||||||
|
kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey">;
|
||||||
|
|
||||||
|
secretDAL: Pick<TSecretV2BridgeDALFactory, "insertMany" | "upsertSecretReferences" | "findBySecretKeys">;
|
||||||
|
secretVersionDAL: Pick<TSecretVersionV2DALFactory, "insertMany" | "create">;
|
||||||
|
secretTagDAL: Pick<TSecretTagDALFactory, "saveTagsToSecretV2" | "create">;
|
||||||
|
secretVersionTagDAL: Pick<TSecretVersionV2TagDALFactory, "insertMany" | "create">;
|
||||||
|
|
||||||
|
folderDAL: Pick<TSecretFolderDALFactory, "create" | "findBySecretPath">;
|
||||||
projectService: Pick<TProjectServiceFactory, "createProject">;
|
projectService: Pick<TProjectServiceFactory, "createProject">;
|
||||||
orgService: Pick<TOrgServiceFactory, "inviteUserToOrganization">;
|
orgService: Pick<TOrgServiceFactory, "inviteUserToOrganization">;
|
||||||
projectEnvService: Pick<TProjectEnvServiceFactory, "createEnvironment">;
|
projectEnvService: Pick<TProjectEnvServiceFactory, "createEnvironment">;
|
||||||
@@ -91,9 +111,15 @@ export const parseEnvKeyDataFn = async (decryptedJson: string): Promise<Infisica
|
|||||||
|
|
||||||
export const importDataIntoInfisicalFn = async ({
|
export const importDataIntoInfisicalFn = async ({
|
||||||
projectService,
|
projectService,
|
||||||
|
projectEnvDAL,
|
||||||
|
projectDAL,
|
||||||
orgService,
|
orgService,
|
||||||
projectEnvService,
|
secretDAL,
|
||||||
secretV2BridgeService,
|
kmsService,
|
||||||
|
secretVersionDAL,
|
||||||
|
secretTagDAL,
|
||||||
|
secretVersionTagDAL,
|
||||||
|
folderDAL,
|
||||||
input: { data, actor, actorId, actorOrgId, actorAuthMethod }
|
input: { data, actor, actorId, actorOrgId, actorAuthMethod }
|
||||||
}: TImportDataIntoInfisicalDTO) => {
|
}: TImportDataIntoInfisicalDTO) => {
|
||||||
// Import data to infisical
|
// Import data to infisical
|
||||||
@@ -104,6 +130,7 @@ export const importDataIntoInfisicalFn = async ({
|
|||||||
const originalToNewProjectId = new Map<string, string>();
|
const originalToNewProjectId = new Map<string, string>();
|
||||||
const originalToNewEnvironmentId = new Map<string, string>();
|
const originalToNewEnvironmentId = new Map<string, string>();
|
||||||
|
|
||||||
|
await projectDAL.transaction(async (tx) => {
|
||||||
for await (const project of data.projects) {
|
for await (const project of data.projects) {
|
||||||
const newProject = await projectService
|
const newProject = await projectService
|
||||||
.createProject({
|
.createProject({
|
||||||
@@ -112,10 +139,12 @@ export const importDataIntoInfisicalFn = async ({
|
|||||||
actorOrgId,
|
actorOrgId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
workspaceName: project.name,
|
workspaceName: project.name,
|
||||||
createDefaultEnvs: false
|
createDefaultEnvs: false,
|
||||||
|
tx
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch((e) => {
|
||||||
throw new BadRequestError({ message: `Failed to import to project [name:${project.name}` });
|
logger.error(e, `Failed to import to project [name:${project.name}]`);
|
||||||
|
throw new BadRequestError({ message: `Failed to import to project [name:${project.name}]` });
|
||||||
});
|
});
|
||||||
|
|
||||||
originalToNewProjectId.set(project.id, newProject.id);
|
originalToNewProjectId.set(project.id, newProject.id);
|
||||||
@@ -123,6 +152,7 @@ export const importDataIntoInfisicalFn = async ({
|
|||||||
|
|
||||||
// Invite user importing projects
|
// Invite user importing projects
|
||||||
const invites = await orgService.inviteUserToOrganization({
|
const invites = await orgService.inviteUserToOrganization({
|
||||||
|
verifyPermissions: false,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorId,
|
actorId,
|
||||||
actorOrgId,
|
actorOrgId,
|
||||||
@@ -133,7 +163,8 @@ export const importDataIntoInfisicalFn = async ({
|
|||||||
projects: Array.from(originalToNewProjectId.values()).map((project) => ({
|
projects: Array.from(originalToNewProjectId.values()).map((project) => ({
|
||||||
id: project,
|
id: project,
|
||||||
projectRoleSlug: [ProjectMembershipRole.Member]
|
projectRoleSlug: [ProjectMembershipRole.Member]
|
||||||
}))
|
})),
|
||||||
|
tx
|
||||||
});
|
});
|
||||||
if (!invites) {
|
if (!invites) {
|
||||||
throw new BadRequestError({ message: `Failed to invite user to projects: [userId:${actorId}]` });
|
throw new BadRequestError({ message: `Failed to invite user to projects: [userId:${actorId}]` });
|
||||||
@@ -142,30 +173,23 @@ export const importDataIntoInfisicalFn = async ({
|
|||||||
// Import environments
|
// Import environments
|
||||||
if (data.environments) {
|
if (data.environments) {
|
||||||
for await (const environment of data.environments) {
|
for await (const environment of data.environments) {
|
||||||
try {
|
const projectId = originalToNewProjectId.get(environment.projectId)!;
|
||||||
const newEnvironment = await projectEnvService.createEnvironment({
|
const slug = slugify(`${environment.name}-${alphaNumericNanoId(4)}`);
|
||||||
actor,
|
|
||||||
actorId,
|
|
||||||
actorOrgId,
|
|
||||||
actorAuthMethod,
|
|
||||||
name: environment.name,
|
|
||||||
projectId: originalToNewProjectId.get(environment.projectId)!,
|
|
||||||
slug: slugify(`${environment.name}-${alphaNumericNanoId(4)}`)
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!newEnvironment) {
|
const existingEnv = await projectEnvDAL.findOne({ projectId, slug }, tx);
|
||||||
logger.error(`Failed to import environment: [name:${environment.name}]`);
|
|
||||||
|
if (existingEnv) {
|
||||||
throw new BadRequestError({
|
throw new BadRequestError({
|
||||||
message: `Failed to import environment: [name:${environment.name}]`
|
message: `Environment with slug '${slug}' already exist`,
|
||||||
});
|
name: "CreateEnvironment"
|
||||||
}
|
|
||||||
originalToNewEnvironmentId.set(environment.id, newEnvironment.slug);
|
|
||||||
} catch (error) {
|
|
||||||
throw new BadRequestError({
|
|
||||||
message: `Failed to import environment: ${environment.name}]`,
|
|
||||||
name: "EnvKeyMigrationImportEnvironment"
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const lastPos = await projectEnvDAL.findLastEnvPosition(projectId, tx);
|
||||||
|
const doc = await projectEnvDAL.create({ slug, name: environment.name, projectId, position: lastPos + 1 }, tx);
|
||||||
|
await folderDAL.create({ name: "root", parentId: null, envId: doc.id, version: 1 }, tx);
|
||||||
|
|
||||||
|
originalToNewEnvironmentId.set(environment.id, doc.slug);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -191,26 +215,67 @@ export const importDataIntoInfisicalFn = async ({
|
|||||||
// for each of the mappedEnvironmentId
|
// for each of the mappedEnvironmentId
|
||||||
for await (const [envId, secrets] of mappedToEnvironmentId) {
|
for await (const [envId, secrets] of mappedToEnvironmentId) {
|
||||||
const environment = data.environments.find((env) => env.id === envId);
|
const environment = data.environments.find((env) => env.id === envId);
|
||||||
const projectId = environment?.projectId;
|
const projectId = originalToNewProjectId.get(environment?.projectId as string)!;
|
||||||
|
|
||||||
if (!projectId) {
|
if (!projectId) {
|
||||||
throw new BadRequestError({ message: `Failed to import secret, project not found` });
|
throw new BadRequestError({ message: `Failed to import secret, project not found` });
|
||||||
}
|
}
|
||||||
|
|
||||||
const secretBatches = chunkArray(secrets, 2500);
|
const { encryptor: secretManagerEncrypt } = await kmsService.createCipherPairWithDataKey(
|
||||||
|
{
|
||||||
|
type: KmsDataKey.SecretManager,
|
||||||
|
projectId
|
||||||
|
},
|
||||||
|
tx
|
||||||
|
);
|
||||||
|
|
||||||
|
const envSlug = originalToNewEnvironmentId.get(envId)!;
|
||||||
|
const folder = await folderDAL.findBySecretPath(projectId, envSlug, "/", tx);
|
||||||
|
if (!folder)
|
||||||
|
throw new NotFoundError({
|
||||||
|
message: `Folder not found for the given environment slug (${envSlug}) & secret path (/)`,
|
||||||
|
name: "Create secret"
|
||||||
|
});
|
||||||
|
|
||||||
|
const secretsByKeys = await secretDAL.findBySecretKeys(
|
||||||
|
folder.id,
|
||||||
|
secrets.map((el) => ({
|
||||||
|
key: el.secretKey,
|
||||||
|
type: SecretType.Shared
|
||||||
|
})),
|
||||||
|
tx
|
||||||
|
);
|
||||||
|
if (secretsByKeys.length) {
|
||||||
|
throw new BadRequestError({
|
||||||
|
message: `Secret already exist: ${secretsByKeys.map((el) => el.key).join(",")}`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const secretBatches = chunkArray(secrets, 2500);
|
||||||
for await (const secretBatch of secretBatches) {
|
for await (const secretBatch of secretBatches) {
|
||||||
await secretV2BridgeService.createManySecret({
|
await fnSecretBulkInsert({
|
||||||
actorId,
|
inputSecrets: secretBatch.map((el) => {
|
||||||
actor,
|
const references = getAllNestedSecretReferences(el.secretValue);
|
||||||
actorOrgId,
|
|
||||||
environment: originalToNewEnvironmentId.get(envId)!,
|
return {
|
||||||
actorAuthMethod,
|
version: 1,
|
||||||
projectId: originalToNewProjectId.get(projectId)!,
|
encryptedValue: el.secretValue
|
||||||
secretPath: "/",
|
? secretManagerEncrypt({ plainText: Buffer.from(el.secretValue) }).cipherTextBlob
|
||||||
secrets: secretBatch
|
: undefined,
|
||||||
|
key: el.secretKey,
|
||||||
|
references,
|
||||||
|
type: SecretType.Shared
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
folderId: folder.id,
|
||||||
|
secretDAL,
|
||||||
|
secretVersionDAL,
|
||||||
|
secretTagDAL,
|
||||||
|
secretVersionTagDAL,
|
||||||
|
tx
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,22 +3,40 @@ import { infisicalSymmetricDecrypt } from "@app/lib/crypto/encryption";
|
|||||||
import { logger } from "@app/lib/logger";
|
import { logger } from "@app/lib/logger";
|
||||||
import { QueueJobs, QueueName, TQueueServiceFactory } from "@app/queue";
|
import { QueueJobs, QueueName, TQueueServiceFactory } from "@app/queue";
|
||||||
|
|
||||||
|
import { TKmsServiceFactory } from "../kms/kms-service";
|
||||||
import { TOrgServiceFactory } from "../org/org-service";
|
import { TOrgServiceFactory } from "../org/org-service";
|
||||||
|
import { TProjectDALFactory } from "../project/project-dal";
|
||||||
import { TProjectServiceFactory } from "../project/project-service";
|
import { TProjectServiceFactory } from "../project/project-service";
|
||||||
|
import { TProjectEnvDALFactory } from "../project-env/project-env-dal";
|
||||||
import { TProjectEnvServiceFactory } from "../project-env/project-env-service";
|
import { TProjectEnvServiceFactory } from "../project-env/project-env-service";
|
||||||
|
import { TSecretFolderDALFactory } from "../secret-folder/secret-folder-dal";
|
||||||
|
import { TSecretTagDALFactory } from "../secret-tag/secret-tag-dal";
|
||||||
|
import { TSecretV2BridgeDALFactory } from "../secret-v2-bridge/secret-v2-bridge-dal";
|
||||||
import { TSecretV2BridgeServiceFactory } from "../secret-v2-bridge/secret-v2-bridge-service";
|
import { TSecretV2BridgeServiceFactory } from "../secret-v2-bridge/secret-v2-bridge-service";
|
||||||
|
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 { importDataIntoInfisicalFn } from "./external-migration-fns";
|
import { importDataIntoInfisicalFn } from "./external-migration-fns";
|
||||||
import { ExternalPlatforms, TImportInfisicalDataCreate } from "./external-migration-types";
|
import { ExternalPlatforms, TImportInfisicalDataCreate } from "./external-migration-types";
|
||||||
|
|
||||||
export type TExternalMigrationQueueFactoryDep = {
|
export type TExternalMigrationQueueFactoryDep = {
|
||||||
|
smtpService: TSmtpService;
|
||||||
|
queueService: TQueueServiceFactory;
|
||||||
|
|
||||||
|
projectDAL: Pick<TProjectDALFactory, "transaction">;
|
||||||
|
projectEnvDAL: Pick<TProjectEnvDALFactory, "find" | "findLastEnvPosition" | "create" | "findOne">;
|
||||||
|
kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey">;
|
||||||
|
|
||||||
|
secretDAL: Pick<TSecretV2BridgeDALFactory, "insertMany" | "upsertSecretReferences" | "findBySecretKeys">;
|
||||||
|
secretVersionDAL: Pick<TSecretVersionV2DALFactory, "insertMany" | "create">;
|
||||||
|
secretTagDAL: Pick<TSecretTagDALFactory, "saveTagsToSecretV2" | "create">;
|
||||||
|
secretVersionTagDAL: Pick<TSecretVersionV2TagDALFactory, "insertMany" | "create">;
|
||||||
|
|
||||||
|
folderDAL: Pick<TSecretFolderDALFactory, "create" | "findBySecretPath">;
|
||||||
projectService: Pick<TProjectServiceFactory, "createProject">;
|
projectService: Pick<TProjectServiceFactory, "createProject">;
|
||||||
orgService: Pick<TOrgServiceFactory, "inviteUserToOrganization">;
|
orgService: Pick<TOrgServiceFactory, "inviteUserToOrganization">;
|
||||||
projectEnvService: Pick<TProjectEnvServiceFactory, "createEnvironment">;
|
projectEnvService: Pick<TProjectEnvServiceFactory, "createEnvironment">;
|
||||||
secretV2BridgeService: Pick<TSecretV2BridgeServiceFactory, "createManySecret">;
|
secretV2BridgeService: Pick<TSecretV2BridgeServiceFactory, "createManySecret">;
|
||||||
|
|
||||||
smtpService: TSmtpService;
|
|
||||||
queueService: TQueueServiceFactory;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TExternalMigrationQueueFactory = ReturnType<typeof externalMigrationQueueFactory>;
|
export type TExternalMigrationQueueFactory = ReturnType<typeof externalMigrationQueueFactory>;
|
||||||
@@ -28,8 +46,16 @@ export const externalMigrationQueueFactory = ({
|
|||||||
projectService,
|
projectService,
|
||||||
orgService,
|
orgService,
|
||||||
smtpService,
|
smtpService,
|
||||||
|
projectDAL,
|
||||||
projectEnvService,
|
projectEnvService,
|
||||||
secretV2BridgeService
|
secretV2BridgeService,
|
||||||
|
kmsService,
|
||||||
|
projectEnvDAL,
|
||||||
|
secretDAL,
|
||||||
|
secretVersionDAL,
|
||||||
|
secretTagDAL,
|
||||||
|
secretVersionTagDAL,
|
||||||
|
folderDAL
|
||||||
}: TExternalMigrationQueueFactoryDep) => {
|
}: TExternalMigrationQueueFactoryDep) => {
|
||||||
const startImport = async (dto: {
|
const startImport = async (dto: {
|
||||||
actorEmail: string;
|
actorEmail: string;
|
||||||
@@ -76,6 +102,14 @@ export const externalMigrationQueueFactory = ({
|
|||||||
|
|
||||||
await importDataIntoInfisicalFn({
|
await importDataIntoInfisicalFn({
|
||||||
input: decryptedJson,
|
input: decryptedJson,
|
||||||
|
projectDAL,
|
||||||
|
projectEnvDAL,
|
||||||
|
secretDAL,
|
||||||
|
secretVersionDAL,
|
||||||
|
secretTagDAL,
|
||||||
|
secretVersionTagDAL,
|
||||||
|
folderDAL,
|
||||||
|
kmsService,
|
||||||
projectService,
|
projectService,
|
||||||
orgService,
|
orgService,
|
||||||
projectEnvService,
|
projectEnvService,
|
||||||
|
|||||||
@@ -160,8 +160,8 @@ export const kmsServiceFactory = ({
|
|||||||
* In mean time the rest of the request will wait until creation is finished followed by getting the created on
|
* In mean time the rest of the request will wait until creation is finished followed by getting the created on
|
||||||
* In real time this would be milliseconds
|
* In real time this would be milliseconds
|
||||||
*/
|
*/
|
||||||
const getOrgKmsKeyId = async (orgId: string) => {
|
const getOrgKmsKeyId = async (orgId: string, trx?: Knex) => {
|
||||||
let org = await orgDAL.findById(orgId);
|
let org = await orgDAL.findById(orgId, trx);
|
||||||
|
|
||||||
if (!org) {
|
if (!org) {
|
||||||
throw new NotFoundError({ message: "Org not found" });
|
throw new NotFoundError({ message: "Org not found" });
|
||||||
@@ -180,9 +180,9 @@ export const kmsServiceFactory = ({
|
|||||||
waitingCb: () => logger.info("KMS. Waiting for org key to be created")
|
waitingCb: () => logger.info("KMS. Waiting for org key to be created")
|
||||||
});
|
});
|
||||||
|
|
||||||
org = await orgDAL.findById(orgId);
|
org = await orgDAL.findById(orgId, trx);
|
||||||
} else {
|
} else {
|
||||||
const keyId = await orgDAL.transaction(async (tx) => {
|
const keyId = await (trx || orgDAL).transaction(async (tx) => {
|
||||||
org = await orgDAL.findById(orgId, tx);
|
org = await orgDAL.findById(orgId, tx);
|
||||||
if (org.kmsDefaultKeyId) {
|
if (org.kmsDefaultKeyId) {
|
||||||
return org.kmsDefaultKeyId;
|
return org.kmsDefaultKeyId;
|
||||||
@@ -240,11 +240,12 @@ export const kmsServiceFactory = ({
|
|||||||
|
|
||||||
const decryptWithKmsKey = async ({
|
const decryptWithKmsKey = async ({
|
||||||
kmsId,
|
kmsId,
|
||||||
depth = 0
|
depth = 0,
|
||||||
}: Omit<TDecryptWithKmsDTO, "cipherTextBlob"> & { depth?: number }) => {
|
tx
|
||||||
|
}: Omit<TDecryptWithKmsDTO, "cipherTextBlob"> & { depth?: number; tx?: Knex }) => {
|
||||||
if (depth > 2) throw new BadRequestError({ message: "KMS depth max limit" });
|
if (depth > 2) throw new BadRequestError({ message: "KMS depth max limit" });
|
||||||
|
|
||||||
const kmsDoc = await kmsDAL.findByIdWithAssociatedKms(kmsId);
|
const kmsDoc = await kmsDAL.findByIdWithAssociatedKms(kmsId, tx);
|
||||||
if (!kmsDoc) {
|
if (!kmsDoc) {
|
||||||
throw new NotFoundError({ message: "KMS ID not found" });
|
throw new NotFoundError({ message: "KMS ID not found" });
|
||||||
}
|
}
|
||||||
@@ -261,7 +262,8 @@ export const kmsServiceFactory = ({
|
|||||||
// we put a limit of depth to avoid too many cycles
|
// we put a limit of depth to avoid too many cycles
|
||||||
const orgKmsDecryptor = await decryptWithKmsKey({
|
const orgKmsDecryptor = await decryptWithKmsKey({
|
||||||
kmsId: kmsDoc.orgKms.id,
|
kmsId: kmsDoc.orgKms.id,
|
||||||
depth: depth + 1
|
depth: depth + 1,
|
||||||
|
tx
|
||||||
});
|
});
|
||||||
|
|
||||||
const orgKmsDataKey = await orgKmsDecryptor({
|
const orgKmsDataKey = await orgKmsDecryptor({
|
||||||
@@ -375,9 +377,9 @@ export const kmsServiceFactory = ({
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const $getOrgKmsDataKey = async (orgId: string) => {
|
const $getOrgKmsDataKey = async (orgId: string, trx?: Knex) => {
|
||||||
const kmsKeyId = await getOrgKmsKeyId(orgId);
|
const kmsKeyId = await getOrgKmsKeyId(orgId, trx);
|
||||||
let org = await orgDAL.findById(orgId);
|
let org = await orgDAL.findById(orgId, trx);
|
||||||
|
|
||||||
if (!org) {
|
if (!org) {
|
||||||
throw new NotFoundError({ message: "Org not found" });
|
throw new NotFoundError({ message: "Org not found" });
|
||||||
@@ -396,9 +398,9 @@ export const kmsServiceFactory = ({
|
|||||||
waitingCb: () => logger.info("KMS. Waiting for org data key to be created")
|
waitingCb: () => logger.info("KMS. Waiting for org data key to be created")
|
||||||
});
|
});
|
||||||
|
|
||||||
org = await orgDAL.findById(orgId);
|
org = await orgDAL.findById(orgId, trx);
|
||||||
} else {
|
} else {
|
||||||
const orgDataKey = await orgDAL.transaction(async (tx) => {
|
const orgDataKey = await (trx || orgDAL).transaction(async (tx) => {
|
||||||
org = await orgDAL.findById(orgId, tx);
|
org = await orgDAL.findById(orgId, tx);
|
||||||
if (org.kmsEncryptedDataKey) {
|
if (org.kmsEncryptedDataKey) {
|
||||||
return;
|
return;
|
||||||
@@ -455,8 +457,8 @@ export const kmsServiceFactory = ({
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const getProjectSecretManagerKmsKeyId = async (projectId: string) => {
|
const getProjectSecretManagerKmsKeyId = async (projectId: string, trx?: Knex) => {
|
||||||
let project = await projectDAL.findById(projectId);
|
let project = await projectDAL.findById(projectId, trx);
|
||||||
if (!project) {
|
if (!project) {
|
||||||
throw new NotFoundError({ message: "Project not found" });
|
throw new NotFoundError({ message: "Project not found" });
|
||||||
}
|
}
|
||||||
@@ -477,7 +479,7 @@ export const kmsServiceFactory = ({
|
|||||||
|
|
||||||
project = await projectDAL.findById(projectId);
|
project = await projectDAL.findById(projectId);
|
||||||
} else {
|
} else {
|
||||||
const kmsKeyId = await projectDAL.transaction(async (tx) => {
|
const kmsKeyId = await (trx || projectDAL).transaction(async (tx) => {
|
||||||
project = await projectDAL.findById(projectId, tx);
|
project = await projectDAL.findById(projectId, tx);
|
||||||
if (project.kmsSecretManagerKeyId) {
|
if (project.kmsSecretManagerKeyId) {
|
||||||
return project.kmsSecretManagerKeyId;
|
return project.kmsSecretManagerKeyId;
|
||||||
@@ -520,9 +522,9 @@ export const kmsServiceFactory = ({
|
|||||||
return project.kmsSecretManagerKeyId;
|
return project.kmsSecretManagerKeyId;
|
||||||
};
|
};
|
||||||
|
|
||||||
const $getProjectSecretManagerKmsDataKey = async (projectId: string) => {
|
const $getProjectSecretManagerKmsDataKey = async (projectId: string, trx?: Knex) => {
|
||||||
const kmsKeyId = await getProjectSecretManagerKmsKeyId(projectId);
|
const kmsKeyId = await getProjectSecretManagerKmsKeyId(projectId, trx);
|
||||||
let project = await projectDAL.findById(projectId);
|
let project = await projectDAL.findById(projectId, trx);
|
||||||
|
|
||||||
if (!project.kmsSecretManagerEncryptedDataKey) {
|
if (!project.kmsSecretManagerEncryptedDataKey) {
|
||||||
const lock = await keyStore
|
const lock = await keyStore
|
||||||
@@ -538,18 +540,21 @@ export const kmsServiceFactory = ({
|
|||||||
delay: 500
|
delay: 500
|
||||||
});
|
});
|
||||||
|
|
||||||
project = await projectDAL.findById(projectId);
|
project = await projectDAL.findById(projectId, trx);
|
||||||
} else {
|
} else {
|
||||||
const projectDataKey = await projectDAL.transaction(async (tx) => {
|
const projectDataKey = await (trx || projectDAL).transaction(async (tx) => {
|
||||||
project = await projectDAL.findById(projectId, tx);
|
project = await projectDAL.findById(projectId, tx);
|
||||||
if (project.kmsSecretManagerEncryptedDataKey) {
|
if (project.kmsSecretManagerEncryptedDataKey) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const dataKey = randomSecureBytes();
|
const dataKey = randomSecureBytes();
|
||||||
const kmsEncryptor = await encryptWithKmsKey({
|
const kmsEncryptor = await encryptWithKmsKey(
|
||||||
|
{
|
||||||
kmsId: kmsKeyId
|
kmsId: kmsKeyId
|
||||||
});
|
},
|
||||||
|
tx
|
||||||
|
);
|
||||||
|
|
||||||
const { cipherTextBlob } = await kmsEncryptor({
|
const { cipherTextBlob } = await kmsEncryptor({
|
||||||
plainText: dataKey
|
plainText: dataKey
|
||||||
@@ -585,7 +590,8 @@ export const kmsServiceFactory = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const kmsDecryptor = await decryptWithKmsKey({
|
const kmsDecryptor = await decryptWithKmsKey({
|
||||||
kmsId: kmsKeyId
|
kmsId: kmsKeyId,
|
||||||
|
tx: trx
|
||||||
});
|
});
|
||||||
|
|
||||||
return kmsDecryptor({
|
return kmsDecryptor({
|
||||||
@@ -593,13 +599,13 @@ export const kmsServiceFactory = ({
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const $getDataKey = async (dto: TEncryptWithKmsDataKeyDTO) => {
|
const $getDataKey = async (dto: TEncryptWithKmsDataKeyDTO, trx?: Knex) => {
|
||||||
switch (dto.type) {
|
switch (dto.type) {
|
||||||
case KmsDataKey.SecretManager: {
|
case KmsDataKey.SecretManager: {
|
||||||
return $getProjectSecretManagerKmsDataKey(dto.projectId);
|
return $getProjectSecretManagerKmsDataKey(dto.projectId, trx);
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
return $getOrgKmsDataKey(dto.orgId);
|
return $getOrgKmsDataKey(dto.orgId, trx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -607,8 +613,9 @@ export const kmsServiceFactory = ({
|
|||||||
// by keeping the decrypted data key in inner scope
|
// by keeping the decrypted data key in inner scope
|
||||||
// none of the entities outside can interact directly or expose the data key
|
// none of the entities outside can interact directly or expose the data key
|
||||||
// NOTICE: If changing here update migrations/utils/kms
|
// NOTICE: If changing here update migrations/utils/kms
|
||||||
const createCipherPairWithDataKey = async (encryptionContext: TEncryptWithKmsDataKeyDTO) => {
|
const createCipherPairWithDataKey = async (encryptionContext: TEncryptWithKmsDataKeyDTO, trx?: Knex) => {
|
||||||
const dataKey = await $getDataKey(encryptionContext);
|
const dataKey = await $getDataKey(encryptionContext, trx);
|
||||||
|
|
||||||
const cipher = symmetricCipherService(SymmetricEncryption.AES_GCM_256);
|
const cipher = symmetricCipherService(SymmetricEncryption.AES_GCM_256);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { ForbiddenError } from "@casl/ability";
|
import { ForbiddenError, MongoAbility } from "@casl/ability";
|
||||||
import slugify from "@sindresorhus/slugify";
|
import slugify from "@sindresorhus/slugify";
|
||||||
|
import { MongoQuery } from "@ucast/mongo2js";
|
||||||
import crypto from "crypto";
|
import crypto from "crypto";
|
||||||
import jwt from "jsonwebtoken";
|
import jwt from "jsonwebtoken";
|
||||||
import { Knex } from "knex";
|
import { Knex } from "knex";
|
||||||
@@ -19,7 +20,11 @@ import { TProjects } from "@app/db/schemas/projects";
|
|||||||
import { TGroupDALFactory } from "@app/ee/services/group/group-dal";
|
import { TGroupDALFactory } from "@app/ee/services/group/group-dal";
|
||||||
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
|
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
|
||||||
import { TOidcConfigDALFactory } from "@app/ee/services/oidc/oidc-config-dal";
|
import { TOidcConfigDALFactory } from "@app/ee/services/oidc/oidc-config-dal";
|
||||||
import { OrgPermissionActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission";
|
import {
|
||||||
|
OrgPermissionActions,
|
||||||
|
OrgPermissionSet,
|
||||||
|
OrgPermissionSubjects
|
||||||
|
} from "@app/ee/services/permission/org-permission";
|
||||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
|
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
|
||||||
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
|
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
|
||||||
import { TProjectUserAdditionalPrivilegeDALFactory } from "@app/ee/services/project-user-additional-privilege/project-user-additional-privilege-dal";
|
import { TProjectUserAdditionalPrivilegeDALFactory } from "@app/ee/services/project-user-additional-privilege/project-user-additional-privilege-dal";
|
||||||
@@ -487,11 +492,17 @@ export const orgServiceFactory = ({
|
|||||||
organizationRoleSlug,
|
organizationRoleSlug,
|
||||||
projects: invitedProjects,
|
projects: invitedProjects,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
|
tx: trx,
|
||||||
|
verifyPermissions = true
|
||||||
}: TInviteUserToOrgDTO) => {
|
}: TInviteUserToOrgDTO) => {
|
||||||
const appCfg = getConfig();
|
const appCfg = getConfig();
|
||||||
|
|
||||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
|
let permission: MongoAbility<OrgPermissionSet, MongoQuery> | undefined;
|
||||||
|
if (verifyPermissions) {
|
||||||
|
permission = (await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId))
|
||||||
|
.permission;
|
||||||
|
}
|
||||||
|
|
||||||
const org = await orgDAL.findOrgById(orgId);
|
const org = await orgDAL.findOrgById(orgId);
|
||||||
|
|
||||||
@@ -512,12 +523,15 @@ export const orgServiceFactory = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const projectsToInvite = invitedProjects?.length
|
const projectsToInvite = invitedProjects?.length
|
||||||
? await projectDAL.find({
|
? await projectDAL.find(
|
||||||
|
{
|
||||||
orgId,
|
orgId,
|
||||||
$in: {
|
$in: {
|
||||||
id: invitedProjects?.map(({ id }) => id)
|
id: invitedProjects?.map(({ id }) => id)
|
||||||
}
|
}
|
||||||
})
|
},
|
||||||
|
{ tx: trx }
|
||||||
|
)
|
||||||
: [];
|
: [];
|
||||||
if (projectsToInvite.length !== invitedProjects?.length) {
|
if (projectsToInvite.length !== invitedProjects?.length) {
|
||||||
throw new ForbiddenRequestError({
|
throw new ForbiddenRequestError({
|
||||||
@@ -534,7 +548,7 @@ export const orgServiceFactory = ({
|
|||||||
const mailsForOrgInvitation: { email: string; userId: string; firstName: string; lastName: string }[] = [];
|
const mailsForOrgInvitation: { email: string; userId: string; firstName: string; lastName: string }[] = [];
|
||||||
const mailsForProjectInvitation: { email: string[]; projectName: string }[] = [];
|
const mailsForProjectInvitation: { email: string[]; projectName: string }[] = [];
|
||||||
const newProjectMemberships: TProjectMemberships[] = [];
|
const newProjectMemberships: TProjectMemberships[] = [];
|
||||||
await orgDAL.transaction(async (tx) => {
|
await (trx || orgDAL).transaction(async (tx) => {
|
||||||
const users: Pick<TUsers, "id" | "firstName" | "lastName" | "email" | "username">[] = [];
|
const users: Pick<TUsers, "id" | "firstName" | "lastName" | "email" | "username">[] = [];
|
||||||
|
|
||||||
for await (const inviteeEmail of inviteeEmails) {
|
for await (const inviteeEmail of inviteeEmails) {
|
||||||
@@ -620,8 +634,16 @@ export const orgServiceFactory = ({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (verifyPermissions) {
|
||||||
|
if (!permission) {
|
||||||
|
throw new ForbiddenRequestError({
|
||||||
|
name: "InviteUser",
|
||||||
|
message: "Failed to invite user because no permission was found"
|
||||||
|
});
|
||||||
|
}
|
||||||
// as its used by project invite also
|
// as its used by project invite also
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.Member);
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.Member);
|
||||||
|
}
|
||||||
let roleId;
|
let roleId;
|
||||||
const orgRole = isCustomOrgRole ? OrgMembershipRole.Custom : organizationRoleSlug;
|
const orgRole = isCustomOrgRole ? OrgMembershipRole.Custom : organizationRoleSlug;
|
||||||
if (isCustomOrgRole) {
|
if (isCustomOrgRole) {
|
||||||
@@ -662,6 +684,7 @@ export const orgServiceFactory = ({
|
|||||||
// if there exist no project membership we set is as given by the request
|
// if there exist no project membership we set is as given by the request
|
||||||
for await (const project of projectsToInvite) {
|
for await (const project of projectsToInvite) {
|
||||||
const projectId = project.id;
|
const projectId = project.id;
|
||||||
|
if (verifyPermissions) {
|
||||||
const { permission: projectPermission } = await permissionService.getProjectPermission(
|
const { permission: projectPermission } = await permissionService.getProjectPermission(
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
@@ -673,6 +696,7 @@ export const orgServiceFactory = ({
|
|||||||
ProjectPermissionActions.Create,
|
ProjectPermissionActions.Create,
|
||||||
ProjectPermissionSub.Member
|
ProjectPermissionSub.Member
|
||||||
);
|
);
|
||||||
|
}
|
||||||
const existingMembers = await projectMembershipDAL.find(
|
const existingMembers = await projectMembershipDAL.find(
|
||||||
{
|
{
|
||||||
projectId: project.id,
|
projectId: project.id,
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import { Knex } from "knex";
|
||||||
|
|
||||||
import { TOrgPermission } from "@app/lib/types";
|
import { TOrgPermission } from "@app/lib/types";
|
||||||
|
|
||||||
import { ActorAuthMethod, ActorType } from "../auth/auth-type";
|
import { ActorAuthMethod, ActorType } from "../auth/auth-type";
|
||||||
@@ -26,18 +28,15 @@ export type TDeleteOrgMembershipDTO = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type TInviteUserToOrgDTO = {
|
export type TInviteUserToOrgDTO = {
|
||||||
actorId: string;
|
|
||||||
actor: ActorType;
|
|
||||||
orgId: string;
|
|
||||||
actorOrgId: string | undefined;
|
|
||||||
actorAuthMethod: ActorAuthMethod;
|
|
||||||
inviteeEmails: string[];
|
inviteeEmails: string[];
|
||||||
organizationRoleSlug: string;
|
organizationRoleSlug: string;
|
||||||
|
tx?: Knex;
|
||||||
|
verifyPermissions?: boolean;
|
||||||
projects?: {
|
projects?: {
|
||||||
id: string;
|
id: string;
|
||||||
projectRoleSlug?: string[];
|
projectRoleSlug?: string[];
|
||||||
}[];
|
}[];
|
||||||
};
|
} & TOrgPermission;
|
||||||
|
|
||||||
export type TVerifyUserToOrgDTO = {
|
export type TVerifyUserToOrgDTO = {
|
||||||
email: string;
|
email: string;
|
||||||
|
|||||||
@@ -39,7 +39,8 @@ export const projectEnvServiceFactory = ({
|
|||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
position,
|
position,
|
||||||
name,
|
name,
|
||||||
slug
|
slug,
|
||||||
|
tx: trx
|
||||||
}: TCreateEnvDTO) => {
|
}: TCreateEnvDTO) => {
|
||||||
const { permission } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission(
|
||||||
actor,
|
actor,
|
||||||
@@ -83,7 +84,7 @@ export const projectEnvServiceFactory = ({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const env = await projectEnvDAL.transaction(async (tx) => {
|
const env = await (trx || projectEnvDAL).transaction(async (tx) => {
|
||||||
if (position !== undefined) {
|
if (position !== undefined) {
|
||||||
// Check if there's an environment at the specified position
|
// Check if there's an environment at the specified position
|
||||||
const existingEnvWithPosition = await projectEnvDAL.findOne({ projectId, position }, tx);
|
const existingEnvWithPosition = await projectEnvDAL.findOne({ projectId, position }, tx);
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
|
import { Knex } from "knex";
|
||||||
|
|
||||||
import { TProjectPermission } from "@app/lib/types";
|
import { TProjectPermission } from "@app/lib/types";
|
||||||
|
|
||||||
export type TCreateEnvDTO = {
|
export type TCreateEnvDTO = {
|
||||||
name: string;
|
name: string;
|
||||||
slug: string;
|
slug: string;
|
||||||
position?: number;
|
position?: number;
|
||||||
|
tx?: Knex;
|
||||||
} & TProjectPermission;
|
} & TProjectPermission;
|
||||||
|
|
||||||
export type TUpdateEnvDTO = {
|
export type TUpdateEnvDTO = {
|
||||||
|
|||||||
@@ -147,6 +147,7 @@ export const projectServiceFactory = ({
|
|||||||
workspaceName,
|
workspaceName,
|
||||||
slug: projectSlug,
|
slug: projectSlug,
|
||||||
kmsKeyId,
|
kmsKeyId,
|
||||||
|
tx: trx,
|
||||||
createDefaultEnvs = true
|
createDefaultEnvs = true
|
||||||
}: TCreateProjectDTO) => {
|
}: TCreateProjectDTO) => {
|
||||||
const organization = await orgDAL.findOne({ id: actorOrgId });
|
const organization = await orgDAL.findOne({ id: actorOrgId });
|
||||||
@@ -169,7 +170,7 @@ export const projectServiceFactory = ({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const results = await projectDAL.transaction(async (tx) => {
|
const results = await (trx || projectDAL).transaction(async (tx) => {
|
||||||
const ghostUser = await orgService.addGhostUser(organization.id, tx);
|
const ghostUser = await orgService.addGhostUser(organization.id, tx);
|
||||||
|
|
||||||
if (kmsKeyId) {
|
if (kmsKeyId) {
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import { Knex } from "knex";
|
||||||
|
|
||||||
import { TProjectKeys } from "@app/db/schemas";
|
import { TProjectKeys } from "@app/db/schemas";
|
||||||
import { TProjectPermission } from "@app/lib/types";
|
import { TProjectPermission } from "@app/lib/types";
|
||||||
|
|
||||||
@@ -30,6 +32,7 @@ export type TCreateProjectDTO = {
|
|||||||
slug?: string;
|
slug?: string;
|
||||||
kmsKeyId?: string;
|
kmsKeyId?: string;
|
||||||
createDefaultEnvs?: boolean;
|
createDefaultEnvs?: boolean;
|
||||||
|
tx?: Knex;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TDeleteProjectBySlugDTO = {
|
export type TDeleteProjectBySlugDTO = {
|
||||||
|
|||||||
@@ -82,7 +82,10 @@ export const fnSecretBulkInsert = async ({
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
const newSecrets = await secretDAL.insertMany(sanitizedInputSecrets.map((el) => ({ ...el, folderId })));
|
const newSecrets = await secretDAL.insertMany(
|
||||||
|
sanitizedInputSecrets.map((el) => ({ ...el, folderId })),
|
||||||
|
tx
|
||||||
|
);
|
||||||
const newSecretGroupedByKeyName = groupBy(newSecrets, (item) => item.key);
|
const newSecretGroupedByKeyName = groupBy(newSecrets, (item) => item.key);
|
||||||
const newSecretTags = inputSecrets.flatMap(({ tagIds: secretTags = [], key }) =>
|
const newSecretTags = inputSecrets.flatMap(({ tagIds: secretTags = [], key }) =>
|
||||||
secretTags.map((tag) => ({
|
secretTags.map((tag) => ({
|
||||||
|
|||||||
@@ -911,7 +911,8 @@ export const secretV2BridgeServiceFactory = ({
|
|||||||
actorOrgId,
|
actorOrgId,
|
||||||
environment,
|
environment,
|
||||||
projectId,
|
projectId,
|
||||||
secrets: inputSecrets
|
secrets: inputSecrets,
|
||||||
|
tx: trx
|
||||||
}: TCreateManySecretDTO) => {
|
}: TCreateManySecretDTO) => {
|
||||||
const { permission } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission(
|
||||||
actor,
|
actor,
|
||||||
@@ -951,7 +952,7 @@ export const secretV2BridgeServiceFactory = ({
|
|||||||
const { encryptor: secretManagerEncryptor, decryptor: secretManagerDecryptor } =
|
const { encryptor: secretManagerEncryptor, decryptor: secretManagerDecryptor } =
|
||||||
await kmsService.createCipherPairWithDataKey({ type: KmsDataKey.SecretManager, projectId });
|
await kmsService.createCipherPairWithDataKey({ type: KmsDataKey.SecretManager, projectId });
|
||||||
|
|
||||||
const newSecrets = await secretDAL.transaction(async (tx) =>
|
const newSecrets = await (trx || secretDAL).transaction(async (tx) =>
|
||||||
fnSecretBulkInsert({
|
fnSecretBulkInsert({
|
||||||
inputSecrets: inputSecrets.map((el) => {
|
inputSecrets: inputSecrets.map((el) => {
|
||||||
const references = getAllNestedSecretReferences(el.secretValue);
|
const references = getAllNestedSecretReferences(el.secretValue);
|
||||||
|
|||||||
@@ -81,6 +81,7 @@ export type TCreateManySecretDTO = Omit<TProjectPermission, "projectId"> & {
|
|||||||
secretPath: string;
|
secretPath: string;
|
||||||
projectId: string;
|
projectId: string;
|
||||||
environment: string;
|
environment: string;
|
||||||
|
tx?: Knex;
|
||||||
secrets: {
|
secrets: {
|
||||||
secretKey: string;
|
secretKey: string;
|
||||||
secretValue: string;
|
secretValue: string;
|
||||||
|
|||||||
+3
-2
@@ -54,8 +54,9 @@ export const EnvKeyPlatformModal = ({ onClose }: Props) => {
|
|||||||
decryptionKey: data.encryptionKey
|
decryptionKey: data.encryptionKey
|
||||||
});
|
});
|
||||||
createNotification({
|
createNotification({
|
||||||
text: "Data imported successfully.",
|
title: "Import started",
|
||||||
type: "success"
|
text: "Your data is being imported. You will receive an email when the import is complete or if the import fails. This may take up to 10 minutes.",
|
||||||
|
type: "info"
|
||||||
});
|
});
|
||||||
|
|
||||||
onClose();
|
onClose();
|
||||||
|
|||||||
Reference in New Issue
Block a user