mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-14 11:29:42 +00:00
Fix tag and metadata insert/update logic on revert/rollback and fix tree checkpoint logic to exclude reserved folders
This commit is contained in:
@@ -153,6 +153,20 @@ describe("folderCommitServiceFactory", () => {
|
||||
getProjectPermission: vi.fn().mockResolvedValue({})
|
||||
};
|
||||
|
||||
const mockSecretTagDAL = {
|
||||
findSecretTagsByVersionId: vi.fn().mockResolvedValue([]),
|
||||
saveTagsToSecretV2: vi.fn().mockResolvedValue([]),
|
||||
findSecretTagsBySecretId: vi.fn().mockResolvedValue([]),
|
||||
deleteTagsToSecretV2: vi.fn().mockResolvedValue([]),
|
||||
saveTagsToSecretVersionV2: vi.fn().mockResolvedValue([])
|
||||
};
|
||||
|
||||
const mockResourceMetadataDAL = {
|
||||
find: vi.fn().mockResolvedValue([]),
|
||||
insertMany: vi.fn().mockResolvedValue([]),
|
||||
delete: vi.fn().mockResolvedValue([])
|
||||
};
|
||||
|
||||
let folderCommitService: TFolderCommitServiceFactory;
|
||||
|
||||
beforeEach(() => {
|
||||
@@ -187,7 +201,9 @@ describe("folderCommitServiceFactory", () => {
|
||||
folderCommitQueueService: mockFolderCommitQueueService,
|
||||
// @ts-expect-error - Mock implementation doesn't need all interface methods for testing
|
||||
permissionService: mockPermissionService,
|
||||
kmsService: mockKmsService
|
||||
kmsService: mockKmsService,
|
||||
secretTagDAL: mockSecretTagDAL,
|
||||
resourceMetadataDAL: mockResourceMetadataDAL
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -2,7 +2,13 @@
|
||||
import { ForbiddenError } from "@casl/ability";
|
||||
import { Knex } from "knex";
|
||||
|
||||
import { ActionProjectType, TSecretFolders, TSecretFolderVersions, TSecretVersionsV2 } from "@app/db/schemas";
|
||||
import {
|
||||
ActionProjectType,
|
||||
TSecretFolders,
|
||||
TSecretFolderVersions,
|
||||
TSecretV2TagJunctionInsert,
|
||||
TSecretVersionsV2
|
||||
} from "@app/db/schemas";
|
||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
|
||||
import { ProjectPermissionCommitsActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
|
||||
import { getConfig } from "@app/lib/config/env";
|
||||
@@ -20,8 +26,10 @@ import { TIdentityDALFactory } from "../identity/identity-dal";
|
||||
import { TKmsServiceFactory } from "../kms/kms-service";
|
||||
import { KmsDataKey } from "../kms/kms-types";
|
||||
import { TProjectDALFactory } from "../project/project-dal";
|
||||
import { TResourceMetadataDALFactory } from "../resource-metadata/resource-metadata-dal";
|
||||
import { TSecretFolderDALFactory } from "../secret-folder/secret-folder-dal";
|
||||
import { TSecretFolderVersionDALFactory } from "../secret-folder/secret-folder-version-dal";
|
||||
import { TSecretTagDALFactory } from "../secret-tag/secret-tag-dal";
|
||||
import * as secretV2BridgeDal from "../secret-v2-bridge/secret-v2-bridge-dal";
|
||||
import { TSecretVersionV2DALFactory } from "../secret-v2-bridge/secret-version-dal";
|
||||
import { TUserDALFactory } from "../user/user-dal";
|
||||
@@ -143,6 +151,15 @@ type TFolderCommitServiceFactoryDep = {
|
||||
>;
|
||||
permissionService?: TPermissionServiceFactory;
|
||||
kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey">;
|
||||
secretTagDAL: Pick<
|
||||
TSecretTagDALFactory,
|
||||
| "findSecretTagsByVersionId"
|
||||
| "saveTagsToSecretV2"
|
||||
| "findSecretTagsBySecretId"
|
||||
| "deleteTagsToSecretV2"
|
||||
| "saveTagsToSecretVersionV2"
|
||||
>;
|
||||
resourceMetadataDAL: Pick<TResourceMetadataDALFactory, "find" | "insertMany" | "delete">;
|
||||
};
|
||||
|
||||
export const folderCommitServiceFactory = ({
|
||||
@@ -161,7 +178,9 @@ export const folderCommitServiceFactory = ({
|
||||
folderTreeCheckpointResourcesDAL,
|
||||
folderCommitQueueService,
|
||||
permissionService,
|
||||
kmsService
|
||||
kmsService,
|
||||
secretTagDAL,
|
||||
resourceMetadataDAL
|
||||
}: TFolderCommitServiceFactoryDep) => {
|
||||
const appCfg = getConfig();
|
||||
|
||||
@@ -738,6 +757,11 @@ export const folderCommitServiceFactory = ({
|
||||
tx?: Knex
|
||||
) => {
|
||||
const commitChanges = [];
|
||||
const folder = await folderDAL.findById(folderId, tx);
|
||||
if (!folder) {
|
||||
return [];
|
||||
}
|
||||
const project = await projectDAL.findById(folder.projectId, tx);
|
||||
|
||||
// Filter only secret changes using discriminated union
|
||||
const secretChanges = changes.filter(
|
||||
@@ -774,12 +798,25 @@ export const folderCommitServiceFactory = ({
|
||||
encryptedValue: secretVersion.encryptedValue,
|
||||
encryptedComment: secretVersion.encryptedComment,
|
||||
userId: secretVersion.userId,
|
||||
metadata: secretVersion.metadata,
|
||||
folderId
|
||||
}
|
||||
];
|
||||
await secretV2BridgeDAL.insertMany(newSecret, tx);
|
||||
|
||||
const metadata: { key: string; value: string }[] =
|
||||
(secretVersion.metadata as { key: string; value: string }[]) || [];
|
||||
if (metadata.length > 0) {
|
||||
await resourceMetadataDAL.insertMany(
|
||||
metadata.map(({ key, value }) => ({
|
||||
key,
|
||||
value,
|
||||
secretId: change.id,
|
||||
orgId: project.orgId
|
||||
})),
|
||||
tx
|
||||
);
|
||||
}
|
||||
|
||||
const newVersion = await secretVersionV2BridgeDAL.create(
|
||||
{
|
||||
folderId,
|
||||
@@ -792,15 +829,29 @@ export const folderCommitServiceFactory = ({
|
||||
reminderNote: secretVersion.reminderNote,
|
||||
reminderRepeatDays: secretVersion.reminderRepeatDays,
|
||||
userId: secretVersion.userId,
|
||||
metadata: secretVersion.metadata,
|
||||
actorType: actorInfo.actorType,
|
||||
envId: secretVersion.envId,
|
||||
metadata: JSON.stringify(metadata),
|
||||
...(actorInfo.actorType === ActorType.IDENTITY && { identityActorId: actorInfo.actorId }),
|
||||
...(actorInfo.actorType === ActorType.USER && { userActorId: actorInfo.actorId })
|
||||
},
|
||||
tx
|
||||
);
|
||||
|
||||
const secretTagsToBeInsert: TSecretV2TagJunctionInsert[] = [];
|
||||
const secretTags = await secretTagDAL.findSecretTagsByVersionId(secretVersion.id, tx);
|
||||
secretTags.forEach((tag) => {
|
||||
secretTagsToBeInsert.push({ secrets_v2Id: change.id, secret_tagsId: tag.secret_tagsId });
|
||||
});
|
||||
await secretTagDAL.saveTagsToSecretV2(secretTagsToBeInsert, tx);
|
||||
await secretTagDAL.saveTagsToSecretVersionV2(
|
||||
secretTagsToBeInsert.map((tag) => ({
|
||||
secret_tagsId: tag.secret_tagsId,
|
||||
secret_versions_v2Id: newVersion.id
|
||||
})),
|
||||
tx
|
||||
);
|
||||
|
||||
commitChanges.push({
|
||||
type: ChangeType.ADD,
|
||||
secretVersionId: newVersion.id
|
||||
@@ -821,12 +872,26 @@ export const folderCommitServiceFactory = ({
|
||||
reminderRepeatDays: secretVersion?.reminderRepeatDays,
|
||||
encryptedValue: secretVersion?.encryptedValue,
|
||||
encryptedComment: secretVersion?.encryptedComment,
|
||||
userId: secretVersion?.userId,
|
||||
metadata: secretVersion?.metadata
|
||||
userId: secretVersion?.userId
|
||||
},
|
||||
tx
|
||||
);
|
||||
|
||||
const metadata: { key: string; value: string }[] =
|
||||
(secretVersion.metadata as { key: string; value: string }[]) || [];
|
||||
await resourceMetadataDAL.delete({ secretId: change.id }, tx);
|
||||
if (metadata.length > 0) {
|
||||
await resourceMetadataDAL.insertMany(
|
||||
metadata.map(({ key, value }) => ({
|
||||
key,
|
||||
value,
|
||||
secretId: change.id,
|
||||
orgId: project.orgId
|
||||
})),
|
||||
tx
|
||||
);
|
||||
}
|
||||
|
||||
const newVersion = await secretVersionV2BridgeDAL.create(
|
||||
{
|
||||
version: nextVersion,
|
||||
@@ -837,7 +902,7 @@ export const folderCommitServiceFactory = ({
|
||||
reminderNote: secretVersion.reminderNote,
|
||||
reminderRepeatDays: secretVersion.reminderRepeatDays,
|
||||
userId: secretVersion.userId,
|
||||
metadata: secretVersion.metadata,
|
||||
metadata: JSON.stringify(metadata),
|
||||
actorType: actorInfo.actorType,
|
||||
envId: secretVersion.envId,
|
||||
folderId,
|
||||
@@ -848,6 +913,32 @@ export const folderCommitServiceFactory = ({
|
||||
tx
|
||||
);
|
||||
|
||||
let secretTagsToBeInsert: TSecretV2TagJunctionInsert[] = [];
|
||||
const secretTagsToBeDelete: string[] = [];
|
||||
const secretTags = await secretTagDAL.findSecretTagsByVersionId(secretVersion.id, tx);
|
||||
secretTags.forEach((tag) => {
|
||||
secretTagsToBeInsert.push({ secrets_v2Id: change.id, secret_tagsId: tag.secret_tagsId });
|
||||
});
|
||||
const currentTags = await secretTagDAL.findSecretTagsBySecretId(change.id, tx);
|
||||
currentTags.forEach((tag) => {
|
||||
if (!secretTagsToBeInsert.find((t) => t.secret_tagsId === tag.secret_tagsId)) {
|
||||
secretTagsToBeDelete.push(tag.secret_tagsId);
|
||||
secretTagsToBeInsert = secretTagsToBeInsert.filter((t) => t.secret_tagsId !== tag.secret_tagsId);
|
||||
}
|
||||
});
|
||||
await secretTagDAL.saveTagsToSecretV2(secretTagsToBeInsert, tx);
|
||||
await secretTagDAL.saveTagsToSecretVersionV2(
|
||||
secretTagsToBeInsert.map((tag) => ({
|
||||
secret_tagsId: tag.secret_tagsId,
|
||||
secret_versions_v2Id: newVersion.id
|
||||
})),
|
||||
tx
|
||||
);
|
||||
await secretTagDAL.deleteTagsToSecretV2(
|
||||
{ $in: { secret_tagsId: secretTagsToBeDelete }, secrets_v2Id: change.id },
|
||||
tx
|
||||
);
|
||||
|
||||
commitChanges.push({
|
||||
type: ChangeType.ADD,
|
||||
isUpdate: true,
|
||||
@@ -1126,7 +1217,7 @@ export const folderCommitServiceFactory = ({
|
||||
actorOrgId,
|
||||
projectId
|
||||
});
|
||||
return folderCommitDAL.findById(commitId, tx);
|
||||
return folderCommitDAL.findById(commitId, tx, projectId);
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -548,6 +548,7 @@ export const secretFolderDALFactory = (db: TDbClient) => {
|
||||
try {
|
||||
const folders = await (tx || db.replicaNode())(TableName.SecretFolder)
|
||||
.where({ envId })
|
||||
.andWhere({ isReserved: false })
|
||||
.select(selectAllTableCols(TableName.SecretFolder));
|
||||
return folders;
|
||||
} catch (error) {
|
||||
|
||||
@@ -11,6 +11,7 @@ export const secretTagDALFactory = (db: TDbClient) => {
|
||||
const secretTagOrm = ormify(db, TableName.SecretTag);
|
||||
const secretJnTagOrm = ormify(db, TableName.JnSecretTag);
|
||||
const secretV2JnTagOrm = ormify(db, TableName.SecretV2JnTag);
|
||||
const secretVersionV2TagOrm = ormify(db, TableName.SecretVersionV2Tag);
|
||||
|
||||
const findManyTagsById = async (projectId: string, ids: string[], tx?: Knex) => {
|
||||
try {
|
||||
@@ -48,14 +49,39 @@ export const secretTagDALFactory = (db: TDbClient) => {
|
||||
}
|
||||
};
|
||||
|
||||
const findSecretTagsByVersionId = async (versionId: string, tx?: Knex) => {
|
||||
try {
|
||||
const tags = await (tx || db.replicaNode())(TableName.SecretVersionV2Tag)
|
||||
.where(`${TableName.SecretVersionV2Tag}.${TableName.SecretVersionV2}Id`, versionId)
|
||||
.select(selectAllTableCols(TableName.SecretVersionV2Tag));
|
||||
return tags;
|
||||
} catch (error) {
|
||||
throw new DatabaseError({ error, name: "Find all by version id" });
|
||||
}
|
||||
};
|
||||
|
||||
const findSecretTagsBySecretId = async (secretId: string, tx?: Knex) => {
|
||||
try {
|
||||
const tags = await (tx || db.replicaNode())(TableName.SecretV2JnTag)
|
||||
.where(`${TableName.SecretV2JnTag}.${TableName.SecretV2}Id`, secretId)
|
||||
.select(selectAllTableCols(TableName.SecretV2JnTag));
|
||||
return tags;
|
||||
} catch (error) {
|
||||
throw new DatabaseError({ error, name: "Find all by secret id" });
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
...secretTagOrm,
|
||||
saveTagsToSecret: secretJnTagOrm.insertMany,
|
||||
deleteTagsToSecret: secretJnTagOrm.delete,
|
||||
saveTagsToSecretV2: secretV2JnTagOrm.batchInsert,
|
||||
deleteTagsToSecretV2: secretV2JnTagOrm.delete,
|
||||
saveTagsToSecretVersionV2: secretVersionV2TagOrm.insertMany,
|
||||
findSecretTagsByProjectId,
|
||||
deleteTagsManySecret,
|
||||
findManyTagsById
|
||||
findManyTagsById,
|
||||
findSecretTagsByVersionId,
|
||||
findSecretTagsBySecretId
|
||||
};
|
||||
};
|
||||
|
||||
@@ -128,7 +128,7 @@ export const fnSecretBulkInsert = async ({
|
||||
userActorId,
|
||||
identityActorId,
|
||||
actorType,
|
||||
metadata: el.metadata ? JSON.stringify(el.metadata) : null,
|
||||
metadata: el.metadata ? JSON.stringify(el.metadata) : [],
|
||||
secretId: newSecretGroupedByKeyName[el.key][0].id
|
||||
})),
|
||||
tx
|
||||
@@ -274,7 +274,7 @@ export const fnSecretBulkUpdate = async ({
|
||||
userId,
|
||||
encryptedComment,
|
||||
version,
|
||||
metadata: metadata ? JSON.stringify(metadata) : null,
|
||||
metadata: metadata ? JSON.stringify(metadata) : [],
|
||||
reminderNote,
|
||||
encryptedValue,
|
||||
reminderRepeatDays,
|
||||
|
||||
@@ -2885,7 +2885,7 @@ export const secretV2BridgeServiceFactory = ({
|
||||
envId: string;
|
||||
projectId: string;
|
||||
}) => {
|
||||
const environment = await projectEnvDAL.findOne({ id: envId });
|
||||
const environment = await projectEnvDAL.findOne({ id: envId, projectId });
|
||||
|
||||
const { permission } = await permissionService.getProjectPermission({
|
||||
actor,
|
||||
|
||||
Reference in New Issue
Block a user