mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-15 05:19:49 +00:00
feat: added secret approval policy in replication
This commit is contained in:
+64
-40
@@ -16,8 +16,15 @@ import { TProjectDALFactory } from "@app/services/project/project-dal";
|
||||
import { TProjectBotServiceFactory } from "@app/services/project-bot/project-bot-service";
|
||||
import { TSecretDALFactory } from "@app/services/secret/secret-dal";
|
||||
import { getAllNestedSecretReferences } from "@app/services/secret/secret-fns";
|
||||
import {
|
||||
fnSecretBlindIndexCheck,
|
||||
fnSecretBlindIndexCheckV2,
|
||||
fnSecretBulkDelete,
|
||||
fnSecretBulkInsert,
|
||||
fnSecretBulkUpdate
|
||||
} from "@app/services/secret/secret-fns";
|
||||
import { TSecretQueueFactory } from "@app/services/secret/secret-queue";
|
||||
import { TSecretServiceFactory } from "@app/services/secret/secret-service";
|
||||
import { SecretOperations } from "@app/services/secret/secret-types";
|
||||
import { TSecretVersionDALFactory } from "@app/services/secret/secret-version-dal";
|
||||
import { TSecretVersionTagDALFactory } from "@app/services/secret/secret-version-tag-dal";
|
||||
import { TSecretBlindIndexDALFactory } from "@app/services/secret-blind-index/secret-blind-index-dal";
|
||||
@@ -32,7 +39,6 @@ import { TSecretApprovalRequestReviewerDALFactory } from "./secret-approval-requ
|
||||
import { TSecretApprovalRequestSecretDALFactory } from "./secret-approval-request-secret-dal";
|
||||
import {
|
||||
ApprovalStatus,
|
||||
CommitType,
|
||||
RequestState,
|
||||
TApprovalRequestCountDTO,
|
||||
TGenerateSecretApprovalRequestDTO,
|
||||
@@ -48,7 +54,7 @@ type TSecretApprovalRequestServiceFactoryDep = {
|
||||
secretApprovalRequestDAL: TSecretApprovalRequestDALFactory;
|
||||
secretApprovalRequestSecretDAL: TSecretApprovalRequestSecretDALFactory;
|
||||
secretApprovalRequestReviewerDAL: TSecretApprovalRequestReviewerDALFactory;
|
||||
folderDAL: Pick<TSecretFolderDALFactory, "findBySecretPath" | "findById" | "findSecretPathByFolderIds">;
|
||||
folderDAL: Pick<TSecretFolderDALFactory, "findBySecretPath" | "findSecretPathByFolderIds">;
|
||||
secretDAL: TSecretDALFactory;
|
||||
secretTagDAL: Pick<TSecretTagDALFactory, "findManyTagsById" | "saveTagsToSecret" | "deleteTagsManySecret">;
|
||||
secretBlindIndexDAL: Pick<TSecretBlindIndexDALFactory, "findOne">;
|
||||
@@ -56,16 +62,7 @@ type TSecretApprovalRequestServiceFactoryDep = {
|
||||
secretVersionDAL: Pick<TSecretVersionDALFactory, "findLatestVersionMany" | "insertMany">;
|
||||
secretVersionTagDAL: Pick<TSecretVersionTagDALFactory, "insertMany">;
|
||||
projectDAL: Pick<TProjectDALFactory, "checkProjectUpgradeStatus">;
|
||||
projectBotService: Pick<TProjectBotServiceFactory, "getBotKey">;
|
||||
secretService: Pick<
|
||||
TSecretServiceFactory,
|
||||
| "fnSecretBulkInsert"
|
||||
| "fnSecretBulkUpdate"
|
||||
| "fnSecretBlindIndexCheck"
|
||||
| "fnSecretBulkDelete"
|
||||
| "fnSecretBlindIndexCheckV2"
|
||||
>;
|
||||
secretQueueService: Pick<TSecretQueueFactory, "syncSecrets">;
|
||||
secretQueueService: Pick<TSecretQueueFactory, "syncSecrets" | "removeSecretReminder">;
|
||||
};
|
||||
|
||||
export type TSecretApprovalRequestServiceFactory = ReturnType<typeof secretApprovalRequestServiceFactory>;
|
||||
@@ -82,7 +79,6 @@ export const secretApprovalRequestServiceFactory = ({
|
||||
projectDAL,
|
||||
permissionService,
|
||||
snapshotService,
|
||||
secretService,
|
||||
secretVersionDAL,
|
||||
secretQueueService,
|
||||
projectBotService
|
||||
@@ -302,11 +298,12 @@ export const secretApprovalRequestServiceFactory = ({
|
||||
const secretApprovalSecrets = await secretApprovalRequestSecretDAL.findByRequestId(secretApprovalRequest.id);
|
||||
if (!secretApprovalSecrets) throw new BadRequestError({ message: "No secrets found" });
|
||||
|
||||
const conflicts: Array<{ secretId: string; op: CommitType }> = [];
|
||||
let secretCreationCommits = secretApprovalSecrets.filter(({ op }) => op === CommitType.Create);
|
||||
const conflicts: Array<{ secretId: string; op: SecretOperations }> = [];
|
||||
let secretCreationCommits = secretApprovalSecrets.filter(({ op }) => op === SecretOperations.Create);
|
||||
if (secretCreationCommits.length) {
|
||||
const { secsGroupedByBlindIndex: conflictGroupByBlindIndex } = await secretService.fnSecretBlindIndexCheckV2({
|
||||
const { secsGroupedByBlindIndex: conflictGroupByBlindIndex } = await fnSecretBlindIndexCheckV2({
|
||||
folderId,
|
||||
secretDAL,
|
||||
inputSecrets: secretCreationCommits.map(({ secretBlindIndex }) => {
|
||||
if (!secretBlindIndex) {
|
||||
throw new BadRequestError({
|
||||
@@ -319,17 +316,19 @@ export const secretApprovalRequestServiceFactory = ({
|
||||
secretCreationCommits
|
||||
.filter(({ secretBlindIndex }) => conflictGroupByBlindIndex[secretBlindIndex || ""])
|
||||
.forEach((el) => {
|
||||
conflicts.push({ op: CommitType.Create, secretId: el.id });
|
||||
conflicts.push({ op: SecretOperations.Create, secretId: el.id });
|
||||
});
|
||||
secretCreationCommits = secretCreationCommits.filter(
|
||||
({ secretBlindIndex }) => !conflictGroupByBlindIndex[secretBlindIndex || ""]
|
||||
);
|
||||
}
|
||||
|
||||
let secretUpdationCommits = secretApprovalSecrets.filter(({ op }) => op === CommitType.Update);
|
||||
let secretUpdationCommits = secretApprovalSecrets.filter(({ op }) => op === SecretOperations.Update);
|
||||
if (secretUpdationCommits.length) {
|
||||
const { secsGroupedByBlindIndex: conflictGroupByBlindIndex } = await secretService.fnSecretBlindIndexCheckV2({
|
||||
const { secsGroupedByBlindIndex: conflictGroupByBlindIndex } = await fnSecretBlindIndexCheckV2({
|
||||
folderId,
|
||||
secretDAL,
|
||||
userId: "",
|
||||
inputSecrets: secretUpdationCommits
|
||||
.filter(({ secretBlindIndex, secret }) => secret && secret.secretBlindIndex !== secretBlindIndex)
|
||||
.map(({ secretBlindIndex }) => {
|
||||
@@ -347,7 +346,7 @@ export const secretApprovalRequestServiceFactory = ({
|
||||
(secretBlindIndex && conflictGroupByBlindIndex[secretBlindIndex]) || !secretId
|
||||
)
|
||||
.forEach((el) => {
|
||||
conflicts.push({ op: CommitType.Update, secretId: el.id });
|
||||
conflicts.push({ op: SecretOperations.Update, secretId: el.id });
|
||||
});
|
||||
|
||||
secretUpdationCommits = secretUpdationCommits.filter(
|
||||
@@ -356,11 +355,11 @@ export const secretApprovalRequestServiceFactory = ({
|
||||
);
|
||||
}
|
||||
|
||||
const secretDeletionCommits = secretApprovalSecrets.filter(({ op }) => op === CommitType.Delete);
|
||||
const botKey = await projectBotService.getBotKey(projectId).catch(() => null);
|
||||
const secretDeletionCommits = secretApprovalSecrets.filter(({ op }) => op === SecretOperations.Delete);
|
||||
|
||||
const mergeStatus = await secretApprovalRequestDAL.transaction(async (tx) => {
|
||||
const newSecrets = secretCreationCommits.length
|
||||
? await secretService.fnSecretBulkInsert({
|
||||
? await fnSecretBulkInsert({
|
||||
tx,
|
||||
folderId,
|
||||
inputSecrets: secretCreationCommits.map((el) => ({
|
||||
@@ -380,7 +379,8 @@ export const secretApprovalRequestServiceFactory = ({
|
||||
"secretReminderRepeatDays",
|
||||
"algorithm",
|
||||
"keyEncoding",
|
||||
"secretBlindIndex"
|
||||
"secretBlindIndex",
|
||||
"isReplicated"
|
||||
]),
|
||||
tags: el?.tags.map(({ id }) => id),
|
||||
version: 1,
|
||||
@@ -403,7 +403,7 @@ export const secretApprovalRequestServiceFactory = ({
|
||||
})
|
||||
: [];
|
||||
const updatedSecrets = secretUpdationCommits.length
|
||||
? await secretService.fnSecretBulkUpdate({
|
||||
? await fnSecretBulkUpdate({
|
||||
folderId,
|
||||
projectId,
|
||||
tx,
|
||||
@@ -425,6 +425,7 @@ export const secretApprovalRequestServiceFactory = ({
|
||||
"secretKeyTag",
|
||||
"secretKeyIV",
|
||||
"metadata",
|
||||
"isReplicated",
|
||||
"skipMultilineEncoding",
|
||||
"secretReminderNote",
|
||||
"secretReminderRepeatDays",
|
||||
@@ -449,11 +450,13 @@ export const secretApprovalRequestServiceFactory = ({
|
||||
})
|
||||
: [];
|
||||
const deletedSecret = secretDeletionCommits.length
|
||||
? await secretService.fnSecretBulkDelete({
|
||||
? await fnSecretBulkDelete({
|
||||
projectId,
|
||||
folderId,
|
||||
tx,
|
||||
actorId: "",
|
||||
secretDAL,
|
||||
secretQueueService,
|
||||
inputSecrets: secretDeletionCommits.map(({ secretBlindIndex }) => {
|
||||
if (!secretBlindIndex) {
|
||||
throw new BadRequestError({
|
||||
@@ -480,12 +483,33 @@ export const secretApprovalRequestServiceFactory = ({
|
||||
};
|
||||
});
|
||||
await snapshotService.performSnapshot(folderId);
|
||||
const folder = await folderDAL.findById(folderId);
|
||||
const [folder] = await folderDAL.findSecretPathByFolderIds(projectId, [folderId]);
|
||||
// TODO(akhilmhdh-pg): change query to do secret path from folder
|
||||
await secretQueueService.syncSecrets({
|
||||
projectId,
|
||||
secretPath: "/",
|
||||
environment: folder?.environment.envSlug as string
|
||||
secretPath: folder.path,
|
||||
environmentSlug: folder.environmentSlug,
|
||||
folderId: folder.id,
|
||||
membershipId: membership.id,
|
||||
environmentId: folder.envId,
|
||||
secrets: mergeStatus.secrets.created
|
||||
.map(({ id, version }) => ({
|
||||
operation: SecretOperations.Create,
|
||||
version,
|
||||
id
|
||||
}))
|
||||
.concat(
|
||||
mergeStatus.secrets.updated.map(({ id, version }) => ({
|
||||
operation: SecretOperations.Update,
|
||||
version,
|
||||
id
|
||||
})),
|
||||
mergeStatus.secrets.deleted.map(({ id, version }) => ({
|
||||
operation: SecretOperations.Delete,
|
||||
version,
|
||||
id
|
||||
}))
|
||||
)
|
||||
});
|
||||
return mergeStatus;
|
||||
};
|
||||
@@ -533,9 +557,9 @@ export const secretApprovalRequestServiceFactory = ({
|
||||
const commits: Omit<TSecretApprovalRequestsSecretsInsert, "requestId">[] = [];
|
||||
const commitTagIds: Record<string, string[]> = {};
|
||||
// for created secret approval change
|
||||
const createdSecrets = data[CommitType.Create];
|
||||
const createdSecrets = data[SecretOperations.Create];
|
||||
if (createdSecrets && createdSecrets?.length) {
|
||||
const { keyName2BlindIndex } = await secretService.fnSecretBlindIndexCheck({
|
||||
const { keyName2BlindIndex } = await fnSecretBlindIndexCheck({
|
||||
inputSecrets: createdSecrets,
|
||||
folderId,
|
||||
isNew: true,
|
||||
@@ -546,7 +570,7 @@ export const secretApprovalRequestServiceFactory = ({
|
||||
commits.push(
|
||||
...createdSecrets.map(({ secretName, ...el }) => ({
|
||||
...el,
|
||||
op: CommitType.Create as const,
|
||||
op: SecretOperations.Create as const,
|
||||
version: 1,
|
||||
secretBlindIndex: keyName2BlindIndex[secretName],
|
||||
algorithm: SecretEncryptionAlgo.AES_256_GCM,
|
||||
@@ -558,12 +582,12 @@ export const secretApprovalRequestServiceFactory = ({
|
||||
});
|
||||
}
|
||||
// not secret approval for update operations
|
||||
const updatedSecrets = data[CommitType.Update];
|
||||
const updatedSecrets = data[SecretOperations.Update];
|
||||
if (updatedSecrets && updatedSecrets?.length) {
|
||||
// get all blind index
|
||||
// Find all those secrets
|
||||
// if not throw not found
|
||||
const { keyName2BlindIndex, secrets: secretsToBeUpdated } = await secretService.fnSecretBlindIndexCheck({
|
||||
const { keyName2BlindIndex, secrets: secretsToBeUpdated } = await fnSecretBlindIndexCheck({
|
||||
inputSecrets: updatedSecrets,
|
||||
folderId,
|
||||
isNew: false,
|
||||
@@ -574,7 +598,7 @@ export const secretApprovalRequestServiceFactory = ({
|
||||
// now find any secret that needs to update its name
|
||||
// same process as above
|
||||
const nameUpdatedSecrets = updatedSecrets.filter(({ newSecretName }) => Boolean(newSecretName));
|
||||
const { keyName2BlindIndex: newKeyName2BlindIndex } = await secretService.fnSecretBlindIndexCheck({
|
||||
const { keyName2BlindIndex: newKeyName2BlindIndex } = await fnSecretBlindIndexCheck({
|
||||
inputSecrets: nameUpdatedSecrets,
|
||||
folderId,
|
||||
isNew: true,
|
||||
@@ -599,7 +623,7 @@ export const secretApprovalRequestServiceFactory = ({
|
||||
return {
|
||||
...latestSecretVersions[secretId],
|
||||
...el,
|
||||
op: CommitType.Update as const,
|
||||
op: SecretOperations.Update as const,
|
||||
secret: secretId,
|
||||
secretVersion: latestSecretVersions[secretId].id,
|
||||
secretBlindIndex,
|
||||
@@ -609,12 +633,12 @@ export const secretApprovalRequestServiceFactory = ({
|
||||
);
|
||||
}
|
||||
// deleted secrets
|
||||
const deletedSecrets = data[CommitType.Delete];
|
||||
const deletedSecrets = data[SecretOperations.Delete];
|
||||
if (deletedSecrets && deletedSecrets.length) {
|
||||
// get all blind index
|
||||
// Find all those secrets
|
||||
// if not throw not found
|
||||
const { keyName2BlindIndex, secrets } = await secretService.fnSecretBlindIndexCheck({
|
||||
const { keyName2BlindIndex, secrets } = await fnSecretBlindIndexCheck({
|
||||
inputSecrets: deletedSecrets,
|
||||
folderId,
|
||||
isNew: false,
|
||||
@@ -635,7 +659,7 @@ export const secretApprovalRequestServiceFactory = ({
|
||||
if (!latestSecretVersions[secretId].secretBlindIndex)
|
||||
throw new BadRequestError({ message: "Failed to find secret blind index" });
|
||||
return {
|
||||
op: CommitType.Delete as const,
|
||||
op: SecretOperations.Delete as const,
|
||||
...latestSecretVersions[secretId],
|
||||
secretBlindIndex: latestSecretVersions[secretId].secretBlindIndex as string,
|
||||
secret: secretId,
|
||||
|
||||
@@ -1,11 +1,6 @@
|
||||
import { TImmutableDBKeys, TSecretApprovalPolicies, TSecretApprovalRequestsSecrets } from "@app/db/schemas";
|
||||
import { TProjectPermission } from "@app/lib/types";
|
||||
|
||||
export enum CommitType {
|
||||
Create = "create",
|
||||
Update = "update",
|
||||
Delete = "delete"
|
||||
}
|
||||
import { SecretOperations } from "@app/services/secret/secret-types";
|
||||
|
||||
export enum RequestState {
|
||||
Open = "open",
|
||||
@@ -18,14 +13,14 @@ export enum ApprovalStatus {
|
||||
REJECTED = "rejected"
|
||||
}
|
||||
|
||||
type TApprovalCreateSecret = Omit<
|
||||
export type TApprovalCreateSecret = Omit<
|
||||
TSecretApprovalRequestsSecrets,
|
||||
TImmutableDBKeys | "version" | "algorithm" | "keyEncoding" | "requestId" | "op" | "secretVersion" | "secretBlindIndex"
|
||||
> & {
|
||||
secretName: string;
|
||||
tagIds?: string[];
|
||||
};
|
||||
type TApprovalUpdateSecret = Partial<TApprovalCreateSecret> & {
|
||||
export type TApprovalUpdateSecret = Partial<TApprovalCreateSecret> & {
|
||||
secretName: string;
|
||||
newSecretName?: string;
|
||||
tagIds?: string[];
|
||||
@@ -36,9 +31,9 @@ export type TGenerateSecretApprovalRequestDTO = {
|
||||
secretPath: string;
|
||||
policy: TSecretApprovalPolicies;
|
||||
data: {
|
||||
[CommitType.Create]?: TApprovalCreateSecret[];
|
||||
[CommitType.Update]?: TApprovalUpdateSecret[];
|
||||
[CommitType.Delete]?: { secretName: string }[];
|
||||
[SecretOperations.Create]?: TApprovalCreateSecret[];
|
||||
[SecretOperations.Update]?: TApprovalUpdateSecret[];
|
||||
[SecretOperations.Delete]?: { secretName: string }[];
|
||||
};
|
||||
} & TProjectPermission;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user