Fix tag and metadata insert/update logic on revert/rollback and fix tree checkpoint logic to exclude reserved folders

This commit is contained in:
carlosmonastyrski
2025-05-26 14:31:05 -03:00
parent e5bc609a2a
commit 5fc6fd71ce
14 changed files with 208 additions and 66 deletions
+7 -1
View File
@@ -17,8 +17,10 @@ import { kmsRootConfigDALFactory } from "@app/services/kms/kms-root-config-dal";
import { kmsServiceFactory } from "@app/services/kms/kms-service"; import { kmsServiceFactory } from "@app/services/kms/kms-service";
import { orgDALFactory } from "@app/services/org/org-dal"; import { orgDALFactory } from "@app/services/org/org-dal";
import { projectDALFactory } from "@app/services/project/project-dal"; import { projectDALFactory } from "@app/services/project/project-dal";
import { resourceMetadataDALFactory } from "@app/services/resource-metadata/resource-metadata-dal";
import { secretFolderDALFactory } from "@app/services/secret-folder/secret-folder-dal"; import { secretFolderDALFactory } from "@app/services/secret-folder/secret-folder-dal";
import { secretFolderVersionDALFactory } from "@app/services/secret-folder/secret-folder-version-dal"; import { secretFolderVersionDALFactory } from "@app/services/secret-folder/secret-folder-version-dal";
import { secretTagDALFactory } from "@app/services/secret-tag/secret-tag-dal";
import { secretV2BridgeDALFactory } from "@app/services/secret-v2-bridge/secret-v2-bridge-dal"; import { secretV2BridgeDALFactory } from "@app/services/secret-v2-bridge/secret-v2-bridge-dal";
import { secretVersionV2BridgeDALFactory } from "@app/services/secret-v2-bridge/secret-version-dal"; import { secretVersionV2BridgeDALFactory } from "@app/services/secret-v2-bridge/secret-version-dal";
import { userDALFactory } from "@app/services/user/user-dal"; import { userDALFactory } from "@app/services/user/user-dal";
@@ -86,11 +88,13 @@ export const getMigrationPITServices = async ({
const folderCheckpointResourcesDAL = folderCheckpointResourcesDALFactory(db); const folderCheckpointResourcesDAL = folderCheckpointResourcesDALFactory(db);
const secretV2BridgeDAL = secretV2BridgeDALFactory({ db, keyStore }); const secretV2BridgeDAL = secretV2BridgeDALFactory({ db, keyStore });
const folderTreeCheckpointResourcesDAL = folderTreeCheckpointResourcesDALFactory(db); const folderTreeCheckpointResourcesDAL = folderTreeCheckpointResourcesDALFactory(db);
const secretTagDAL = secretTagDALFactory(db);
const orgDAL = orgDALFactory(db); const orgDAL = orgDALFactory(db);
const kmsRootConfigDAL = kmsRootConfigDALFactory(db); const kmsRootConfigDAL = kmsRootConfigDALFactory(db);
const kmsDAL = kmskeyDALFactory(db); const kmsDAL = kmskeyDALFactory(db);
const internalKmsDAL = internalKmsDALFactory(db); const internalKmsDAL = internalKmsDALFactory(db);
const resourceMetadataDAL = resourceMetadataDALFactory(db);
const hsmModule = initializeHsmModule(envConfig); const hsmModule = initializeHsmModule(envConfig);
hsmModule.initialize(); hsmModule.initialize();
@@ -128,7 +132,9 @@ export const getMigrationPITServices = async ({
folderCheckpointResourcesDAL, folderCheckpointResourcesDAL,
secretV2BridgeDAL, secretV2BridgeDAL,
folderTreeCheckpointResourcesDAL, folderTreeCheckpointResourcesDAL,
kmsService kmsService,
secretTagDAL,
resourceMetadataDAL
}); });
return { folderCommitService }; return { folderCommitService };
+6 -6
View File
@@ -202,7 +202,7 @@ export const registerPITRouter = async (server: FastifyZodProvider) => {
}), }),
querystring: z.object({ querystring: z.object({
folderId: z.string().trim(), folderId: z.string().trim(),
envId: z.string().trim(), environment: z.string().trim(),
deepRollback: booleanSchema.default(false), deepRollback: booleanSchema.default(false),
secretPath: z.string().trim().default("/").transform(removeTrailingSlash), secretPath: z.string().trim().default("/").transform(removeTrailingSlash),
projectId: z.string().trim() projectId: z.string().trim()
@@ -228,7 +228,7 @@ export const registerPITRouter = async (server: FastifyZodProvider) => {
projectId: req.query.projectId, projectId: req.query.projectId,
commitId: req.params.commitId, commitId: req.params.commitId,
folderId: req.query.folderId, folderId: req.query.folderId,
envId: req.query.envId, environment: req.query.environment,
deepRollback: req.query.deepRollback, deepRollback: req.query.deepRollback,
secretPath: req.query.secretPath secretPath: req.query.secretPath
}); });
@@ -243,7 +243,7 @@ export const registerPITRouter = async (server: FastifyZodProvider) => {
folderId: req.query.folderId, folderId: req.query.folderId,
deepRollback: req.query.deepRollback, deepRollback: req.query.deepRollback,
diffsCount: result.length.toString(), diffsCount: result.length.toString(),
env: req.query.envId, environment: req.query.environment,
folderPath: req.query.secretPath folderPath: req.query.secretPath
} }
} }
@@ -268,7 +268,7 @@ export const registerPITRouter = async (server: FastifyZodProvider) => {
folderId: z.string().trim(), folderId: z.string().trim(),
deepRollback: z.boolean().default(false), deepRollback: z.boolean().default(false),
message: z.string().max(256).trim().optional(), message: z.string().max(256).trim().optional(),
envId: z.string().trim(), environment: z.string().trim(),
projectId: z.string().trim() projectId: z.string().trim()
}), }),
response: { response: {
@@ -292,7 +292,7 @@ export const registerPITRouter = async (server: FastifyZodProvider) => {
folderId: req.body.folderId, folderId: req.body.folderId,
deepRollback: req.body.deepRollback, deepRollback: req.body.deepRollback,
message: req.body.message, message: req.body.message,
envId: req.body.envId environment: req.body.environment
}); });
await server.services.auditLog.createAuditLog({ await server.services.auditLog.createAuditLog({
@@ -302,7 +302,7 @@ export const registerPITRouter = async (server: FastifyZodProvider) => {
type: EventType.PIT_ROLLBACK_COMMIT, type: EventType.PIT_ROLLBACK_COMMIT,
metadata: { metadata: {
targetCommitId: req.params.commitId, targetCommitId: req.params.commitId,
envId: req.body.envId, environment: req.body.environment,
folderId: req.body.folderId, folderId: req.body.folderId,
deepRollback: req.body.deepRollback, deepRollback: req.body.deepRollback,
message: req.body.message || "Rollback to previous commit", message: req.body.message || "Rollback to previous commit",
@@ -2959,7 +2959,7 @@ interface PitRollbackCommitEvent {
deepRollback: boolean; deepRollback: boolean;
message: string; message: string;
totalChanges: string; totalChanges: string;
envId: string; environment: string;
}; };
} }
@@ -2988,7 +2988,7 @@ interface PitCompareFolderStatesEvent {
folderId: string; folderId: string;
deepRollback: boolean; deepRollback: boolean;
diffsCount: string; diffsCount: string;
env: string; environment: string;
folderPath: string; folderPath: string;
}; };
} }
+23 -24
View File
@@ -11,6 +11,7 @@ import {
isFolderCommitChange, isFolderCommitChange,
isSecretCommitChange isSecretCommitChange
} from "@app/services/folder-commit-changes/folder-commit-changes-dal"; } from "@app/services/folder-commit-changes/folder-commit-changes-dal";
import { TProjectEnvDALFactory } from "@app/services/project-env/project-env-dal";
import { TSecretServiceFactory } from "@app/services/secret/secret-service"; import { TSecretServiceFactory } from "@app/services/secret/secret-service";
import { TSecretFolderDALFactory } from "@app/services/secret-folder/secret-folder-dal"; import { TSecretFolderDALFactory } from "@app/services/secret-folder/secret-folder-dal";
import { TSecretFolderServiceFactory } from "@app/services/secret-folder/secret-folder-service"; import { TSecretFolderServiceFactory } from "@app/services/secret-folder/secret-folder-service";
@@ -23,6 +24,7 @@ type TPitServiceFactoryDep = {
folderService: Pick<TSecretFolderServiceFactory, "getFolderById" | "getFolderVersions">; folderService: Pick<TSecretFolderServiceFactory, "getFolderById" | "getFolderVersions">;
permissionService: Pick<TPermissionServiceFactory, "getProjectPermission">; permissionService: Pick<TPermissionServiceFactory, "getProjectPermission">;
folderDAL: Pick<TSecretFolderDALFactory, "findSecretPathByFolderIds">; folderDAL: Pick<TSecretFolderDALFactory, "findSecretPathByFolderIds">;
projectEnvDAL: Pick<TProjectEnvDALFactory, "findOne">;
}; };
export type TPitServiceFactory = ReturnType<typeof pitServiceFactory>; export type TPitServiceFactory = ReturnType<typeof pitServiceFactory>;
@@ -32,7 +34,8 @@ export const pitServiceFactory = ({
secretService, secretService,
folderService, folderService,
permissionService, permissionService,
folderDAL folderDAL,
projectEnvDAL
}: TPitServiceFactoryDep) => { }: TPitServiceFactoryDep) => {
const getCommitsCount = async ({ const getCommitsCount = async ({
actor, actor,
@@ -183,7 +186,7 @@ export const pitServiceFactory = ({
projectId, projectId,
commitId, commitId,
folderId, folderId,
envId, environment,
deepRollback, deepRollback,
secretPath secretPath
}: { }: {
@@ -194,7 +197,7 @@ export const pitServiceFactory = ({
projectId: string; projectId: string;
commitId: string; commitId: string;
folderId: string; folderId: string;
envId: string; environment: string;
deepRollback: boolean; deepRollback: boolean;
secretPath: string; secretPath: string;
}) => { }) => {
@@ -216,6 +219,11 @@ export const pitServiceFactory = ({
projectId projectId
}); });
const env = await projectEnvDAL.findOne({
projectId,
slug: environment
});
if (!latestCommit) { if (!latestCommit) {
throw new NotFoundError({ message: "Latest commit not found" }); throw new NotFoundError({ message: "Latest commit not found" });
} }
@@ -224,7 +232,7 @@ export const pitServiceFactory = ({
if (deepRollback) { if (deepRollback) {
diffs = await folderCommitService.deepCompareFolder({ diffs = await folderCommitService.deepCompareFolder({
targetCommitId: targetCommit.id, targetCommitId: targetCommit.id,
envId, envId: env.id,
projectId projectId
}); });
} else { } else {
@@ -268,7 +276,7 @@ export const pitServiceFactory = ({
actor, actor,
actorOrgId, actorOrgId,
actorAuthMethod, actorAuthMethod,
envId, env.id,
projectId, projectId,
diff.folderPath || "" diff.folderPath || ""
); );
@@ -295,7 +303,7 @@ export const pitServiceFactory = ({
folderId, folderId,
deepRollback, deepRollback,
message, message,
envId environment
}: { }: {
actor: ActorType; actor: ActorType;
actorId: string; actorId: string;
@@ -306,7 +314,7 @@ export const pitServiceFactory = ({
folderId: string; folderId: string;
deepRollback: boolean; deepRollback: boolean;
message?: string; message?: string;
envId: string; environment: string;
}) => { }) => {
const { permission: userPermission } = await permissionService.getProjectPermission({ const { permission: userPermission } = await permissionService.getProjectPermission({
actor, actor,
@@ -346,16 +354,21 @@ export const pitServiceFactory = ({
projectId projectId
}); });
if (!targetCommit || targetCommit.folderId !== folderId || targetCommit.envId !== envId) { const env = await projectEnvDAL.findOne({
projectId,
slug: environment
});
if (!targetCommit || targetCommit.folderId !== folderId || targetCommit.envId !== env.id) {
throw new NotFoundError({ message: "Target commit not found" }); throw new NotFoundError({ message: "Target commit not found" });
} }
if (!latestCommit || latestCommit.envId !== envId) { if (!latestCommit || latestCommit.envId !== env.id) {
throw new NotFoundError({ message: "Latest commit not found" }); throw new NotFoundError({ message: "Latest commit not found" });
} }
if (deepRollback) { if (deepRollback) {
await folderCommitService.deepRollbackFolder(commitId, envId, actorId, actor, projectId, message); await folderCommitService.deepRollbackFolder(commitId, env.id, actorId, actor, projectId, message);
return { success: true }; return { success: true };
} }
@@ -426,20 +439,6 @@ export const pitServiceFactory = ({
projectId: string; projectId: string;
commitId: string; commitId: string;
}) => { }) => {
const { permission: userPermission } = await permissionService.getProjectPermission({
actor,
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
});
ForbiddenError.from(userPermission).throwUnlessCan(
ProjectPermissionCommitsActions.Read,
ProjectPermissionSub.Commits
);
const commit = await folderCommitService.getCommitById({ const commit = await folderCommitService.getCommitById({
commitId, commitId,
actor, actor,
@@ -422,7 +422,7 @@ export const secretReplicationServiceFactory = ({
return { return {
op: operation, op: operation,
requestId: approvalRequestDoc.id, requestId: approvalRequestDoc.id,
metadata: doc.metadata, metadata: doc.metadata ? JSON.stringify(doc.metadata) : [],
secretMetadata: JSON.stringify(doc.secretMetadata), secretMetadata: JSON.stringify(doc.secretMetadata),
key: doc.key, key: doc.key,
encryptedValue: doc.encryptedValue, encryptedValue: doc.encryptedValue,
@@ -454,7 +454,7 @@ export const secretReplicationServiceFactory = ({
inputSecrets: locallyCreatedSecrets.map((doc) => { inputSecrets: locallyCreatedSecrets.map((doc) => {
return { return {
type: doc.type, type: doc.type,
metadata: doc.metadata, metadata: doc.metadata ? JSON.stringify(doc.metadata) : [],
key: doc.key, key: doc.key,
encryptedValue: doc.encryptedValue, encryptedValue: doc.encryptedValue,
encryptedComment: doc.encryptedComment, encryptedComment: doc.encryptedComment,
@@ -484,7 +484,7 @@ export const secretReplicationServiceFactory = ({
}, },
data: { data: {
type: doc.type, type: doc.type,
metadata: doc.metadata, metadata: doc.metadata ? JSON.stringify(doc.metadata) : [],
key: doc.key, key: doc.key,
encryptedValue: doc.encryptedValue as Buffer, encryptedValue: doc.encryptedValue as Buffer,
encryptedComment: doc.encryptedComment, encryptedComment: doc.encryptedComment,
+5 -2
View File
@@ -599,7 +599,9 @@ export const registerRoutes = async (
folderTreeCheckpointResourcesDAL, folderTreeCheckpointResourcesDAL,
folderCommitQueueService, folderCommitQueueService,
permissionService, permissionService,
kmsService kmsService,
secretTagDAL,
resourceMetadataDAL
}); });
const scimService = scimServiceFactory({ const scimService = scimServiceFactory({
licenseService, licenseService,
@@ -1527,7 +1529,8 @@ export const registerRoutes = async (
secretService, secretService,
folderService, folderService,
permissionService, permissionService,
folderDAL folderDAL,
projectEnvDAL
}); });
const identityOidcAuthService = identityOidcAuthServiceFactory({ const identityOidcAuthService = identityOidcAuthServiceFactory({
@@ -153,6 +153,20 @@ describe("folderCommitServiceFactory", () => {
getProjectPermission: vi.fn().mockResolvedValue({}) 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; let folderCommitService: TFolderCommitServiceFactory;
beforeEach(() => { beforeEach(() => {
@@ -187,7 +201,9 @@ describe("folderCommitServiceFactory", () => {
folderCommitQueueService: mockFolderCommitQueueService, folderCommitQueueService: mockFolderCommitQueueService,
// @ts-expect-error - Mock implementation doesn't need all interface methods for testing // @ts-expect-error - Mock implementation doesn't need all interface methods for testing
permissionService: mockPermissionService, permissionService: mockPermissionService,
kmsService: mockKmsService kmsService: mockKmsService,
secretTagDAL: mockSecretTagDAL,
resourceMetadataDAL: mockResourceMetadataDAL
}); });
}); });
@@ -2,7 +2,13 @@
import { ForbiddenError } from "@casl/ability"; import { ForbiddenError } from "@casl/ability";
import { Knex } from "knex"; 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 { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
import { ProjectPermissionCommitsActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission"; import { ProjectPermissionCommitsActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
import { getConfig } from "@app/lib/config/env"; import { getConfig } from "@app/lib/config/env";
@@ -20,8 +26,10 @@ import { TIdentityDALFactory } from "../identity/identity-dal";
import { TKmsServiceFactory } from "../kms/kms-service"; import { TKmsServiceFactory } from "../kms/kms-service";
import { KmsDataKey } from "../kms/kms-types"; import { KmsDataKey } from "../kms/kms-types";
import { TProjectDALFactory } from "../project/project-dal"; import { TProjectDALFactory } from "../project/project-dal";
import { TResourceMetadataDALFactory } from "../resource-metadata/resource-metadata-dal";
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 * as secretV2BridgeDal from "../secret-v2-bridge/secret-v2-bridge-dal"; import * as secretV2BridgeDal 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";
@@ -143,6 +151,15 @@ type TFolderCommitServiceFactoryDep = {
>; >;
permissionService?: TPermissionServiceFactory; permissionService?: TPermissionServiceFactory;
kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey">; kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey">;
secretTagDAL: Pick<
TSecretTagDALFactory,
| "findSecretTagsByVersionId"
| "saveTagsToSecretV2"
| "findSecretTagsBySecretId"
| "deleteTagsToSecretV2"
| "saveTagsToSecretVersionV2"
>;
resourceMetadataDAL: Pick<TResourceMetadataDALFactory, "find" | "insertMany" | "delete">;
}; };
export const folderCommitServiceFactory = ({ export const folderCommitServiceFactory = ({
@@ -161,7 +178,9 @@ export const folderCommitServiceFactory = ({
folderTreeCheckpointResourcesDAL, folderTreeCheckpointResourcesDAL,
folderCommitQueueService, folderCommitQueueService,
permissionService, permissionService,
kmsService kmsService,
secretTagDAL,
resourceMetadataDAL
}: TFolderCommitServiceFactoryDep) => { }: TFolderCommitServiceFactoryDep) => {
const appCfg = getConfig(); const appCfg = getConfig();
@@ -738,6 +757,11 @@ export const folderCommitServiceFactory = ({
tx?: Knex tx?: Knex
) => { ) => {
const commitChanges = []; 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 // Filter only secret changes using discriminated union
const secretChanges = changes.filter( const secretChanges = changes.filter(
@@ -774,12 +798,25 @@ export const folderCommitServiceFactory = ({
encryptedValue: secretVersion.encryptedValue, encryptedValue: secretVersion.encryptedValue,
encryptedComment: secretVersion.encryptedComment, encryptedComment: secretVersion.encryptedComment,
userId: secretVersion.userId, userId: secretVersion.userId,
metadata: secretVersion.metadata,
folderId folderId
} }
]; ];
await secretV2BridgeDAL.insertMany(newSecret, tx); 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( const newVersion = await secretVersionV2BridgeDAL.create(
{ {
folderId, folderId,
@@ -792,15 +829,29 @@ export const folderCommitServiceFactory = ({
reminderNote: secretVersion.reminderNote, reminderNote: secretVersion.reminderNote,
reminderRepeatDays: secretVersion.reminderRepeatDays, reminderRepeatDays: secretVersion.reminderRepeatDays,
userId: secretVersion.userId, userId: secretVersion.userId,
metadata: secretVersion.metadata,
actorType: actorInfo.actorType, actorType: actorInfo.actorType,
envId: secretVersion.envId, envId: secretVersion.envId,
metadata: JSON.stringify(metadata),
...(actorInfo.actorType === ActorType.IDENTITY && { identityActorId: actorInfo.actorId }), ...(actorInfo.actorType === ActorType.IDENTITY && { identityActorId: actorInfo.actorId }),
...(actorInfo.actorType === ActorType.USER && { userActorId: actorInfo.actorId }) ...(actorInfo.actorType === ActorType.USER && { userActorId: actorInfo.actorId })
}, },
tx 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({ commitChanges.push({
type: ChangeType.ADD, type: ChangeType.ADD,
secretVersionId: newVersion.id secretVersionId: newVersion.id
@@ -821,12 +872,26 @@ export const folderCommitServiceFactory = ({
reminderRepeatDays: secretVersion?.reminderRepeatDays, reminderRepeatDays: secretVersion?.reminderRepeatDays,
encryptedValue: secretVersion?.encryptedValue, encryptedValue: secretVersion?.encryptedValue,
encryptedComment: secretVersion?.encryptedComment, encryptedComment: secretVersion?.encryptedComment,
userId: secretVersion?.userId, userId: secretVersion?.userId
metadata: secretVersion?.metadata
}, },
tx 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( const newVersion = await secretVersionV2BridgeDAL.create(
{ {
version: nextVersion, version: nextVersion,
@@ -837,7 +902,7 @@ export const folderCommitServiceFactory = ({
reminderNote: secretVersion.reminderNote, reminderNote: secretVersion.reminderNote,
reminderRepeatDays: secretVersion.reminderRepeatDays, reminderRepeatDays: secretVersion.reminderRepeatDays,
userId: secretVersion.userId, userId: secretVersion.userId,
metadata: secretVersion.metadata, metadata: JSON.stringify(metadata),
actorType: actorInfo.actorType, actorType: actorInfo.actorType,
envId: secretVersion.envId, envId: secretVersion.envId,
folderId, folderId,
@@ -848,6 +913,32 @@ export const folderCommitServiceFactory = ({
tx 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({ commitChanges.push({
type: ChangeType.ADD, type: ChangeType.ADD,
isUpdate: true, isUpdate: true,
@@ -1126,7 +1217,7 @@ export const folderCommitServiceFactory = ({
actorOrgId, actorOrgId,
projectId projectId
}); });
return folderCommitDAL.findById(commitId, tx); return folderCommitDAL.findById(commitId, tx, projectId);
}; };
/** /**
@@ -548,6 +548,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({ envId }) .where({ envId })
.andWhere({ isReserved: false })
.select(selectAllTableCols(TableName.SecretFolder)); .select(selectAllTableCols(TableName.SecretFolder));
return folders; return folders;
} catch (error) { } catch (error) {
@@ -11,6 +11,7 @@ export const secretTagDALFactory = (db: TDbClient) => {
const secretTagOrm = ormify(db, TableName.SecretTag); const secretTagOrm = ormify(db, TableName.SecretTag);
const secretJnTagOrm = ormify(db, TableName.JnSecretTag); const secretJnTagOrm = ormify(db, TableName.JnSecretTag);
const secretV2JnTagOrm = ormify(db, TableName.SecretV2JnTag); const secretV2JnTagOrm = ormify(db, TableName.SecretV2JnTag);
const secretVersionV2TagOrm = ormify(db, TableName.SecretVersionV2Tag);
const findManyTagsById = async (projectId: string, ids: string[], tx?: Knex) => { const findManyTagsById = async (projectId: string, ids: string[], tx?: Knex) => {
try { 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 { return {
...secretTagOrm, ...secretTagOrm,
saveTagsToSecret: secretJnTagOrm.insertMany, saveTagsToSecret: secretJnTagOrm.insertMany,
deleteTagsToSecret: secretJnTagOrm.delete, deleteTagsToSecret: secretJnTagOrm.delete,
saveTagsToSecretV2: secretV2JnTagOrm.batchInsert, saveTagsToSecretV2: secretV2JnTagOrm.batchInsert,
deleteTagsToSecretV2: secretV2JnTagOrm.delete, deleteTagsToSecretV2: secretV2JnTagOrm.delete,
saveTagsToSecretVersionV2: secretVersionV2TagOrm.insertMany,
findSecretTagsByProjectId, findSecretTagsByProjectId,
deleteTagsManySecret, deleteTagsManySecret,
findManyTagsById findManyTagsById,
findSecretTagsByVersionId,
findSecretTagsBySecretId
}; };
}; };
@@ -128,7 +128,7 @@ export const fnSecretBulkInsert = async ({
userActorId, userActorId,
identityActorId, identityActorId,
actorType, actorType,
metadata: el.metadata ? JSON.stringify(el.metadata) : null, metadata: el.metadata ? JSON.stringify(el.metadata) : [],
secretId: newSecretGroupedByKeyName[el.key][0].id secretId: newSecretGroupedByKeyName[el.key][0].id
})), })),
tx tx
@@ -274,7 +274,7 @@ export const fnSecretBulkUpdate = async ({
userId, userId,
encryptedComment, encryptedComment,
version, version,
metadata: metadata ? JSON.stringify(metadata) : null, metadata: metadata ? JSON.stringify(metadata) : [],
reminderNote, reminderNote,
encryptedValue, encryptedValue,
reminderRepeatDays, reminderRepeatDays,
@@ -2885,7 +2885,7 @@ export const secretV2BridgeServiceFactory = ({
envId: string; envId: string;
projectId: string; projectId: string;
}) => { }) => {
const environment = await projectEnvDAL.findOne({ id: envId }); const environment = await projectEnvDAL.findOne({ id: envId, projectId });
const { permission } = await permissionService.getProjectPermission({ const { permission } = await permissionService.getProjectPermission({
actor, actor,
@@ -31,16 +31,16 @@ export const commitKeys = {
rollbackPreview: ({ rollbackPreview: ({
folderId, folderId,
commitId, commitId,
envId, envSlug,
projectId, projectId,
deepRollback deepRollback
}: { }: {
folderId: string; folderId: string;
commitId: string; commitId: string;
envId: string; envSlug: string;
projectId: string; projectId: string;
deepRollback: boolean; deepRollback: boolean;
}) => [{ folderId, commitId, envId, projectId, deepRollback }, "rollback-preview"] as const }) => [{ folderId, commitId, envSlug, projectId, deepRollback }, "rollback-preview"] as const
}; };
const fetchFolderCommitsCount = async ({ const fetchFolderCommitsCount = async ({
@@ -111,7 +111,7 @@ export const fetchCommitDetails = async (workspaceId: string, commitId: string)
export const fetchRollbackPreview = async ( export const fetchRollbackPreview = async (
folderId: string, folderId: string,
commitId: string, commitId: string,
envId: string, envSlug: string,
workspaceId: string, workspaceId: string,
deepRollback: boolean, deepRollback: boolean,
secretPath: string secretPath: string
@@ -121,7 +121,7 @@ export const fetchRollbackPreview = async (
{ {
params: { params: {
folderId, folderId,
envId, environment: envSlug,
deepRollback, deepRollback,
secretPath, secretPath,
projectId: workspaceId projectId: workspaceId
@@ -137,7 +137,7 @@ const fetchRollback = async (
workspaceId: string, workspaceId: string,
deepRollback: boolean, deepRollback: boolean,
message?: string, message?: string,
envId?: string envSlug?: string
) => { ) => {
const { data } = await apiRequest.post<{ success: boolean }>( const { data } = await apiRequest.post<{ success: boolean }>(
`/api/v1/pit/commits/${commitId}/rollback`, `/api/v1/pit/commits/${commitId}/rollback`,
@@ -145,7 +145,7 @@ const fetchRollback = async (
folderId, folderId,
deepRollback, deepRollback,
message, message,
envId, environment: envSlug,
projectId: workspaceId projectId: workspaceId
} }
); );
@@ -195,7 +195,7 @@ export const useCommitRollback = ({
deepRollback, deepRollback,
environment, environment,
directory, directory,
envId envSlug
}: { }: {
workspaceId: string; workspaceId: string;
commitId: string; commitId: string;
@@ -203,12 +203,12 @@ export const useCommitRollback = ({
deepRollback: boolean; deepRollback: boolean;
environment: string; environment: string;
directory: string; directory: string;
envId: string; envSlug: string;
}) => { }) => {
const queryClient = useQueryClient(); const queryClient = useQueryClient();
return useMutation({ return useMutation({
mutationFn: (message: string) => mutationFn: (message: string) =>
fetchRollback(folderId, commitId, workspaceId, deepRollback, message, envId), fetchRollback(folderId, commitId, workspaceId, deepRollback, message, envSlug),
onSuccess: () => { onSuccess: () => {
queryClient.invalidateQueries({ queryClient.invalidateQueries({
queryKey: [ queryKey: [
@@ -280,15 +280,15 @@ export const useGetCommitDetails = (workspaceId: string, commitId: string) => {
export const useGetRollbackPreview = ( export const useGetRollbackPreview = (
folderId: string, folderId: string,
commitId: string, commitId: string,
envId: string, envSlug: string,
projectId: string, projectId: string,
deepRollback: boolean, deepRollback: boolean,
secretPath: string secretPath: string
) => { ) => {
return useQuery({ return useQuery({
queryKey: commitKeys.rollbackPreview({ folderId, commitId, envId, projectId, deepRollback }), queryKey: commitKeys.rollbackPreview({ folderId, commitId, envSlug, projectId, deepRollback }),
queryFn: () => queryFn: () =>
fetchRollbackPreview(folderId, commitId, envId, projectId, deepRollback, secretPath), fetchRollbackPreview(folderId, commitId, envSlug, projectId, deepRollback, secretPath),
enabled: Boolean(folderId) && Boolean(commitId) enabled: Boolean(folderId) && Boolean(commitId)
}); });
}; };
@@ -127,13 +127,13 @@ export const RollbackPreviewTab = (): JSX.Element => {
deepRollback, deepRollback,
environment: envSlug, environment: envSlug,
directory: secretPath, directory: secretPath,
envId: currentWorkspace.environments.find((e) => e.slug === envSlug)?.id || "" envSlug
}); });
const { data: rollbackChangesNested, isLoading } = useGetRollbackPreview( const { data: rollbackChangesNested, isLoading } = useGetRollbackPreview(
folderId, folderId,
selectedCommitId, selectedCommitId,
currentWorkspace.environments.find((e) => e.slug === envSlug)?.id || "", envSlug,
currentWorkspace.id, currentWorkspace.id,
deepRollback, deepRollback,
secretPath secretPath