mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-09-22 13:39:35 +00:00
PIT: Add delete commit for cascade deletion
This commit is contained in:
@@ -415,6 +415,7 @@ export const secretSnapshotServiceFactory = ({
|
|||||||
secretVersionId?: string;
|
secretVersionId?: string;
|
||||||
folderVersionId?: string;
|
folderVersionId?: string;
|
||||||
isUpdate?: boolean;
|
isUpdate?: boolean;
|
||||||
|
folderId?: string;
|
||||||
}[] = [];
|
}[] = [];
|
||||||
|
|
||||||
// this will remove all secrets in current folder except rotated secrets which we ignore
|
// this will remove all secrets in current folder except rotated secrets which we ignore
|
||||||
@@ -625,7 +626,8 @@ export const secretSnapshotServiceFactory = ({
|
|||||||
// Folder was only deleted
|
// Folder was only deleted
|
||||||
commitChanges.push({
|
commitChanges.push({
|
||||||
type: CommitType.DELETE,
|
type: CommitType.DELETE,
|
||||||
folderVersionId: deletedInfo.versionId
|
folderVersionId: deletedInfo.versionId,
|
||||||
|
folderId: deletedInfo.id
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ import { TResourceMetadataDALFactory } from "../resource-metadata/resource-metad
|
|||||||
import { TSecretFolderDALFactory } from "../secret-folder/secret-folder-dal";
|
import { TSecretFolderDALFactory } from "../secret-folder/secret-folder-dal";
|
||||||
import { TSecretFolderVersionDALFactory } from "../secret-folder/secret-folder-version-dal";
|
import { TSecretFolderVersionDALFactory } from "../secret-folder/secret-folder-version-dal";
|
||||||
import { TSecretTagDALFactory } from "../secret-tag/secret-tag-dal";
|
import { TSecretTagDALFactory } from "../secret-tag/secret-tag-dal";
|
||||||
import * as secretV2BridgeDal 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 { TUserDALFactory } from "../user/user-dal";
|
import { TUserDALFactory } from "../user/user-dal";
|
||||||
import { TFolderCommitDALFactory } from "./folder-commit-dal";
|
import { TFolderCommitDALFactory } from "./folder-commit-dal";
|
||||||
@@ -68,6 +68,7 @@ type TCreateCommitDTO = {
|
|||||||
secretVersionId?: string;
|
secretVersionId?: string;
|
||||||
folderVersionId?: string;
|
folderVersionId?: string;
|
||||||
isUpdate?: boolean;
|
isUpdate?: boolean;
|
||||||
|
folderId?: string;
|
||||||
}[];
|
}[];
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -143,7 +144,7 @@ type TFolderCommitServiceFactoryDep = {
|
|||||||
folderDAL: TSecretFolderDALFactory;
|
folderDAL: TSecretFolderDALFactory;
|
||||||
folderVersionDAL: TSecretFolderVersionDALFactory;
|
folderVersionDAL: TSecretFolderVersionDALFactory;
|
||||||
secretVersionV2BridgeDAL: TSecretVersionV2DALFactory;
|
secretVersionV2BridgeDAL: TSecretVersionV2DALFactory;
|
||||||
secretV2BridgeDAL: secretV2BridgeDal.TSecretV2BridgeDALFactory;
|
secretV2BridgeDAL: TSecretV2BridgeDALFactory;
|
||||||
projectDAL: Pick<TProjectDALFactory, "findById" | "findProjectByEnvId">;
|
projectDAL: Pick<TProjectDALFactory, "findById" | "findProjectByEnvId">;
|
||||||
folderCommitQueueService?: Pick<
|
folderCommitQueueService?: Pick<
|
||||||
TFolderCommitQueueServiceFactory,
|
TFolderCommitQueueServiceFactory,
|
||||||
@@ -676,6 +677,95 @@ export const folderCommitServiceFactory = ({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const createDeleteCommitForNestedFolders = async ({
|
||||||
|
folderId,
|
||||||
|
actorMetadata,
|
||||||
|
actorType,
|
||||||
|
envId,
|
||||||
|
parentFolderName,
|
||||||
|
step = 1,
|
||||||
|
tx
|
||||||
|
}: {
|
||||||
|
folderId: string;
|
||||||
|
actorMetadata: Record<string, string>;
|
||||||
|
actorType: string;
|
||||||
|
envId: string;
|
||||||
|
parentFolderName: string;
|
||||||
|
step?: number;
|
||||||
|
tx?: Knex;
|
||||||
|
}) => {
|
||||||
|
if (step > 20) {
|
||||||
|
logger.info(`createDeleteCommitForNestedFolders - Max step reached for folder ${folderId}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
logger.info(`Creating delete commit for nested folders ${folderId}`);
|
||||||
|
const folderVersion = await folderVersionDAL.findLatestVersion(folderId, tx);
|
||||||
|
if (!folderVersion) {
|
||||||
|
logger.info(`No folder version found for ${folderId}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const lastFolderCommit = await folderCommitDAL.findLatestCommit(folderId, undefined, tx);
|
||||||
|
if (!lastFolderCommit) {
|
||||||
|
logger.info(`No commit found for folder ${folderId}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const folderState = await reconstructFolderState(lastFolderCommit.id, tx);
|
||||||
|
const changes = folderState.map((resource) => ({
|
||||||
|
type: ChangeType.DELETE,
|
||||||
|
folderId: resource.id,
|
||||||
|
folderName: resource.folderName,
|
||||||
|
secretVersionId: resource.type === ResourceType.SECRET ? resource.versionId : undefined,
|
||||||
|
folderVersionId: resource.type === ResourceType.FOLDER ? resource.versionId : undefined,
|
||||||
|
secretKey: resource.secretKey
|
||||||
|
}));
|
||||||
|
logger.info(`Found ${changes.length} changes for ${folderId}`);
|
||||||
|
|
||||||
|
await Promise.all(
|
||||||
|
changes
|
||||||
|
.filter((change) => change.type === ChangeType.DELETE && change.folderVersionId)
|
||||||
|
.map(async (change) => {
|
||||||
|
await createDeleteCommitForNestedFolders({
|
||||||
|
folderId: change.folderId,
|
||||||
|
actorMetadata,
|
||||||
|
actorType,
|
||||||
|
envId,
|
||||||
|
parentFolderName: folderVersion.name,
|
||||||
|
step: step + 1,
|
||||||
|
tx
|
||||||
|
});
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
const newCommit = await folderCommitDAL.create(
|
||||||
|
{
|
||||||
|
actorMetadata,
|
||||||
|
actorType,
|
||||||
|
message: `Parent folder ${parentFolderName} deleted`,
|
||||||
|
folderId,
|
||||||
|
envId
|
||||||
|
},
|
||||||
|
tx
|
||||||
|
);
|
||||||
|
|
||||||
|
const batchSize = 500;
|
||||||
|
const chunks = chunkArray(changes, batchSize);
|
||||||
|
|
||||||
|
await Promise.all(
|
||||||
|
chunks.map(async (chunk) => {
|
||||||
|
await folderCommitChangesDAL.insertMany(
|
||||||
|
chunk.map((change) => ({
|
||||||
|
folderCommitId: newCommit.id,
|
||||||
|
changeType: CommitType.DELETE,
|
||||||
|
secretVersionId: change.secretVersionId,
|
||||||
|
folderVersionId: change.folderVersionId,
|
||||||
|
isUpdate: false
|
||||||
|
})),
|
||||||
|
tx
|
||||||
|
);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new commit with the provided changes
|
* Creates a new commit with the provided changes
|
||||||
*/
|
*/
|
||||||
@@ -698,6 +788,21 @@ export const folderCommitServiceFactory = ({
|
|||||||
throw new NotFoundError({ message: `Folder with ID ${data.folderId} not found` });
|
throw new NotFoundError({ message: `Folder with ID ${data.folderId} not found` });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await Promise.all(
|
||||||
|
data.changes.map(async (change) => {
|
||||||
|
if (change.type === ChangeType.DELETE && change.folderId) {
|
||||||
|
await createDeleteCommitForNestedFolders({
|
||||||
|
folderId: change.folderId,
|
||||||
|
actorMetadata: metadata,
|
||||||
|
actorType: data.actor.type,
|
||||||
|
envId: folder.envId,
|
||||||
|
parentFolderName: folder.name,
|
||||||
|
tx
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
const newCommit = await folderCommitDAL.create(
|
const newCommit = await folderCommitDAL.create(
|
||||||
{
|
{
|
||||||
actorMetadata: metadata,
|
actorMetadata: metadata,
|
||||||
@@ -1102,7 +1207,8 @@ export const folderCommitServiceFactory = ({
|
|||||||
|
|
||||||
commitChanges.push({
|
commitChanges.push({
|
||||||
type: ChangeType.DELETE,
|
type: ChangeType.DELETE,
|
||||||
folderVersionId: change.versionId
|
folderVersionId: change.versionId,
|
||||||
|
folderId: change.id
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|||||||
@@ -537,6 +537,7 @@ export const secretFolderDALFactory = (db: TDbClient) => {
|
|||||||
try {
|
try {
|
||||||
const folders = await (tx || db.replicaNode())(TableName.SecretFolder)
|
const folders = await (tx || db.replicaNode())(TableName.SecretFolder)
|
||||||
.where({ parentId })
|
.where({ parentId })
|
||||||
|
.andWhere({ isReserved: false })
|
||||||
.select(selectAllTableCols(TableName.SecretFolder));
|
.select(selectAllTableCols(TableName.SecretFolder));
|
||||||
return folders;
|
return folders;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -478,7 +478,8 @@ export const secretFolderServiceFactory = ({
|
|||||||
changes: [
|
changes: [
|
||||||
{
|
{
|
||||||
type: CommitType.DELETE,
|
type: CommitType.DELETE,
|
||||||
folderVersionId: folderVersions[doc.id].id
|
folderVersionId: folderVersions[doc.id].id,
|
||||||
|
folderId: doc.id
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -180,11 +180,24 @@ export const secretFolderVersionDALFactory = (db: TDbClient) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const findLatestVersion = async (folderId: string, tx?: Knex) => {
|
||||||
|
try {
|
||||||
|
const doc = await (tx || db.replicaNode())(TableName.SecretFolderVersion)
|
||||||
|
.where(`${TableName.SecretFolderVersion}.folderId`, folderId)
|
||||||
|
.select(selectAllTableCols(TableName.SecretFolderVersion))
|
||||||
|
.first();
|
||||||
|
return doc;
|
||||||
|
} catch (error) {
|
||||||
|
throw new DatabaseError({ error, name: "findLatestVersion" });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...secretFolderVerOrm,
|
...secretFolderVerOrm,
|
||||||
findLatestFolderVersions,
|
findLatestFolderVersions,
|
||||||
findLatestVersionByFolderId,
|
findLatestVersionByFolderId,
|
||||||
pruneExcessVersions,
|
pruneExcessVersions,
|
||||||
findByIdsWithLatestVersion
|
findByIdsWithLatestVersion,
|
||||||
|
findLatestVersion
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -12,8 +12,8 @@ import {
|
|||||||
} from "@app/context";
|
} from "@app/context";
|
||||||
import {
|
import {
|
||||||
PermissionConditionOperators,
|
PermissionConditionOperators,
|
||||||
ProjectPermissionCommitsActions,
|
|
||||||
ProjectPermissionApprovalActions,
|
ProjectPermissionApprovalActions,
|
||||||
|
ProjectPermissionCommitsActions,
|
||||||
ProjectPermissionDynamicSecretActions,
|
ProjectPermissionDynamicSecretActions,
|
||||||
ProjectPermissionGroupActions,
|
ProjectPermissionGroupActions,
|
||||||
ProjectPermissionIdentityActions,
|
ProjectPermissionIdentityActions,
|
||||||
|
|||||||
Reference in New Issue
Block a user