Add actor to secret version history

This commit is contained in:
carlosmonastyrski
2025-03-05 17:20:57 -03:00
parent 3badcea95b
commit 30ccfbfc8e
15 changed files with 376 additions and 88 deletions
@@ -0,0 +1,52 @@
import { Knex } from "knex";
import { TableName } from "@app/db/schemas";
export async function up(knex: Knex): Promise<void> {
const hasSecretVersionV2UserActorId = await knex.schema.hasColumn(TableName.SecretVersionV2, "userActorId");
const hasSecretVersionV2IdentityActorId = await knex.schema.hasColumn(TableName.SecretVersionV2, "identityActorId");
const hasSecretVersionV2ActorType = await knex.schema.hasColumn(TableName.SecretVersionV2, "actorType");
if (!hasSecretVersionV2UserActorId) {
await knex.schema.alterTable(TableName.SecretVersionV2, (t) => {
t.uuid("userActorId");
t.foreign("userActorId").references("id").inTable(TableName.Users);
});
}
if (!hasSecretVersionV2IdentityActorId) {
await knex.schema.alterTable(TableName.SecretVersionV2, (t) => {
t.uuid("identityActorId");
t.foreign("identityActorId").references("id").inTable(TableName.Identity);
});
}
if (!hasSecretVersionV2ActorType) {
await knex.schema.alterTable(TableName.SecretVersionV2, (t) => {
t.string("actorType");
});
}
}
export async function down(knex: Knex): Promise<void> {
const hasSecretVersionV2UserActorId = await knex.schema.hasColumn(TableName.SecretVersionV2, "userActorId");
const hasSecretVersionV2IdentityActorId = await knex.schema.hasColumn(TableName.SecretVersionV2, "identityActorId");
const hasSecretVersionV2ActorType = await knex.schema.hasColumn(TableName.SecretVersionV2, "actorType");
if (hasSecretVersionV2UserActorId) {
await knex.schema.alterTable(TableName.SecretVersionV2, (t) => {
t.dropColumn("userActorId");
});
}
if (!hasSecretVersionV2IdentityActorId) {
await knex.schema.alterTable(TableName.SecretVersionV2, (t) => {
t.dropColumn("identityActorId");
});
}
if (!hasSecretVersionV2ActorType) {
await knex.schema.alterTable(TableName.SecretVersionV2, (t) => {
t.dropColumn("actorType");
});
}
}
+4 -1
View File
@@ -25,7 +25,10 @@ export const SecretVersionsV2Schema = z.object({
folderId: z.string().uuid(),
userId: z.string().uuid().nullable().optional(),
createdAt: z.date(),
updatedAt: z.date()
updatedAt: z.date(),
userActorId: z.string().uuid().nullable().optional(),
identityActorId: z.string().uuid().nullable().optional(),
actorType: z.string().nullable().optional()
});
export type TSecretVersionsV2 = z.infer<typeof SecretVersionsV2Schema>;
@@ -757,7 +757,11 @@ export const secretApprovalRequestServiceFactory = ({
secretDAL,
secretVersionDAL,
secretTagDAL,
secretVersionTagDAL
secretVersionTagDAL,
actor: {
type: actor,
actorId
}
})
: [];
const updatedSecrets = secretUpdationCommits.length
@@ -803,7 +807,11 @@ export const secretApprovalRequestServiceFactory = ({
secretDAL,
secretVersionDAL,
secretTagDAL,
secretVersionTagDAL
secretVersionTagDAL,
actor: {
type: actor,
actorId
}
})
: [];
const deletedSecret = secretDeletionCommits.length
@@ -710,6 +710,10 @@ export const secretReplicationServiceFactory = ({
tx,
secretTagDAL,
secretVersionTagDAL,
actor: {
type: actor,
actorId
},
inputSecrets: locallyCreatedSecrets.map((doc) => {
return {
keyEncoding: doc.keyEncoding,
@@ -741,6 +745,10 @@ export const secretReplicationServiceFactory = ({
tx,
secretTagDAL,
secretVersionTagDAL,
actor: {
type: actor,
actorId
},
inputSecrets: locallyUpdatedSecrets.map((doc) => {
return {
filter: {
@@ -370,7 +370,20 @@ export const secretSnapshotServiceFactory = ({
const secrets = await secretV2BridgeDAL.insertMany(
rollbackSnaps.flatMap(({ secretVersions, folderId }) =>
secretVersions.map(
({ latestSecretVersion, version, updatedAt, createdAt, secretId, envId, id, tags, ...el }) => ({
({
latestSecretVersion,
version,
updatedAt,
createdAt,
secretId,
envId,
id,
tags,
userActorId,
identityActorId,
actorType,
...el
}) => ({
...el,
id: secretId,
version: deletedTopLevelSecsGroupById[secretId] ? latestSecretVersion + 1 : latestSecretVersion,
+3 -1
View File
@@ -1039,7 +1039,9 @@ export const registerRoutes = async (
secretApprovalRequestSecretDAL,
kmsService,
snapshotService,
resourceMetadataDAL
resourceMetadataDAL,
userDAL,
identityDAL
});
const secretApprovalRequestService = secretApprovalRequestServiceFactory({
@@ -111,7 +111,15 @@ export const secretRawSchema = z.object({
secretReminderRepeatDays: z.number().nullable().optional(),
skipMultilineEncoding: z.boolean().default(false).nullable().optional(),
createdAt: z.date(),
updatedAt: z.date()
updatedAt: z.date(),
actor: z
.object({
actorId: z.string().nullable(),
actorType: z.string().nullable(),
name: z.string().nullable().optional()
})
.optional()
.nullable()
});
export const ProjectPermissionSchema = z.object({
@@ -772,6 +772,10 @@ export const importDataIntoInfisicalFn = async ({
secretVersionDAL,
secretTagDAL,
secretVersionTagDAL,
actor: {
type: actor,
actorId
},
tx
});
}
@@ -10,6 +10,7 @@ import { ResourceMetadataDTO } from "../resource-metadata/resource-metadata-sche
import { TSecretFolderDALFactory } from "../secret-folder/secret-folder-dal";
import { TSecretV2BridgeDALFactory } from "./secret-v2-bridge-dal";
import { TFnSecretBulkDelete, TFnSecretBulkInsert, TFnSecretBulkUpdate } from "./secret-v2-bridge-types";
import { ActorType } from "../auth/auth-type";
const INTERPOLATION_SYNTAX_REG = /\${([a-zA-Z0-9-_.]+)}/g;
// akhilmhdh: JS regex with global save state in .test
@@ -62,6 +63,7 @@ export const fnSecretBulkInsert = async ({
resourceMetadataDAL,
secretTagDAL,
secretVersionTagDAL,
actor,
tx
}: TFnSecretBulkInsert) => {
const sanitizedInputSecrets = inputSecrets.map(
@@ -90,6 +92,9 @@ export const fnSecretBulkInsert = async ({
})
);
const userActorId = actor && actor.type === ActorType.USER ? actor.actorId : undefined;
const identityActorId = actor && actor.type !== ActorType.USER ? actor.actorId : undefined;
const newSecrets = await secretDAL.insertMany(
sanitizedInputSecrets.map((el) => ({ ...el, folderId })),
tx
@@ -106,6 +111,9 @@ export const fnSecretBulkInsert = async ({
sanitizedInputSecrets.map((el) => ({
...el,
folderId,
userActorId,
identityActorId,
actorType: actor?.type,
secretId: newSecretGroupedByKeyName[el.key][0].id
})),
tx
@@ -157,8 +165,12 @@ export const fnSecretBulkUpdate = async ({
secretVersionDAL,
secretTagDAL,
secretVersionTagDAL,
resourceMetadataDAL
resourceMetadataDAL,
actor
}: TFnSecretBulkUpdate) => {
const userActorId = actor && actor?.type === ActorType.USER ? actor?.actorId : undefined;
const identityActorId = actor && actor?.type !== ActorType.USER ? actor?.actorId : undefined;
const sanitizedInputSecrets = inputSecrets.map(
({
filter,
@@ -216,7 +228,10 @@ export const fnSecretBulkUpdate = async ({
encryptedValue,
reminderRepeatDays,
folderId,
secretId
secretId,
userActorId,
identityActorId,
actorType: actor?.type
})
),
tx
@@ -616,6 +631,11 @@ export const reshapeBridgeSecret = (
secret: Omit<TSecretsV2, "encryptedValue" | "encryptedComment"> & {
value: string;
comment: string;
actor?: {
actorType?: string;
actorId?: string;
name?: string;
};
tags?: {
id: string;
slug: string;
@@ -636,6 +656,7 @@ export const reshapeBridgeSecret = (
_id: secret.id,
id: secret.id,
user: secret.userId,
actor: secret.actor,
tags: secret.tags,
skipMultilineEncoding: secret.skipMultilineEncoding,
secretReminderRepeatDays: secret.reminderRepeatDays,
@@ -62,6 +62,8 @@ import {
} from "./secret-v2-bridge-types";
import { TSecretVersionV2DALFactory } from "./secret-version-dal";
import { TSecretVersionV2TagDALFactory } from "./secret-version-tag-dal";
import { TUserDALFactory } from "../user/user-dal";
import { TIdentityDALFactory } from "../identity/identity-dal";
type TSecretV2BridgeServiceFactoryDep = {
secretDAL: TSecretV2BridgeDALFactory;
@@ -85,6 +87,8 @@ type TSecretV2BridgeServiceFactoryDep = {
>;
snapshotService: Pick<TSecretSnapshotServiceFactory, "performSnapshot">;
resourceMetadataDAL: Pick<TResourceMetadataDALFactory, "insertMany" | "delete">;
userDAL: Pick<TUserDALFactory, "find">;
identityDAL: Pick<TIdentityDALFactory, "find">;
};
export type TSecretV2BridgeServiceFactory = ReturnType<typeof secretV2BridgeServiceFactory>;
@@ -107,7 +111,9 @@ export const secretV2BridgeServiceFactory = ({
secretApprovalRequestDAL,
secretApprovalRequestSecretDAL,
kmsService,
resourceMetadataDAL
resourceMetadataDAL,
userDAL,
identityDAL
}: TSecretV2BridgeServiceFactoryDep) => {
const $validateSecretReferences = async (
projectId: string,
@@ -301,6 +307,10 @@ export const secretV2BridgeServiceFactory = ({
secretVersionDAL,
secretTagDAL,
secretVersionTagDAL,
actor: {
type: actor,
actorId
},
tx
})
);
@@ -483,6 +493,10 @@ export const secretV2BridgeServiceFactory = ({
secretVersionDAL,
secretTagDAL,
secretVersionTagDAL,
actor: {
type: actor,
actorId
},
tx
})
);
@@ -1230,6 +1244,10 @@ export const secretV2BridgeServiceFactory = ({
secretVersionDAL,
secretTagDAL,
secretVersionTagDAL,
actor: {
type: actor,
actorId
},
tx
})
);
@@ -1490,6 +1508,10 @@ export const secretV2BridgeServiceFactory = ({
secretVersionDAL,
secretTagDAL,
secretVersionTagDAL,
actor: {
type: actor,
actorId
},
resourceMetadataDAL
});
updatedSecrets.push(...bulkUpdatedSecrets.map((el) => ({ ...el, secretPath: folder.path })));
@@ -1522,6 +1544,10 @@ export const secretV2BridgeServiceFactory = ({
secretVersionDAL,
secretTagDAL,
secretVersionTagDAL,
actor: {
type: actor,
actorId
},
tx
});
updatedSecrets.push(...bulkInsertedSecrets.map((el) => ({ ...el, secretPath: folder.path })));
@@ -1690,13 +1716,39 @@ export const secretV2BridgeServiceFactory = ({
projectId: folder.projectId
});
const secretVersions = await secretVersionDAL.find({ secretId }, { offset, limit, sort: [["createdAt", "desc"]] });
return secretVersions.map((el) =>
reshapeBridgeSecret(folder.projectId, folder.environment.envSlug, "/", {
const userIds = Array.from(
new Set(secretVersions.map((version) => version.userActorId).filter(Boolean))
) as string[];
const users = userIds.length > 0 ? await userDAL.find({ $in: { id: userIds } }) : [];
const usersById = groupBy(users, (user) => user.id);
const identitiesIds = Array.from(
new Set(secretVersions.map((version) => version.identityActorId).filter(Boolean))
) as string[];
const identities = identitiesIds.length > 0 ? await identityDAL.find({ $in: { id: identitiesIds } }) : [];
const identitiesById = groupBy(identities, (identity) => identity.id);
return secretVersions.map((el) => {
let entityId;
let actorName;
if (el.userActorId) {
actorName = usersById[el.userActorId]?.[0]?.username;
entityId = el.userActorId;
} else if (el.identityActorId) {
actorName = identitiesById[el.identityActorId]?.[0]?.name;
entityId = el.identityActorId;
}
const actorEntity = el.actorType ? { actorType: el.actorType, actorId: entityId, name: actorName } : undefined;
return reshapeBridgeSecret(folder.projectId, folder.environment.envSlug, "/", {
...el,
actor: actorEntity,
value: el.encryptedValue ? secretManagerDecryptor({ cipherTextBlob: el.encryptedValue }).toString() : "",
comment: el.encryptedComment ? secretManagerDecryptor({ cipherTextBlob: el.encryptedComment }).toString() : ""
})
);
});
});
};
// this is a backfilling API for secret references
@@ -1956,6 +2008,10 @@ export const secretV2BridgeServiceFactory = ({
secretTagDAL,
resourceMetadataDAL,
secretVersionTagDAL,
actor: {
type: actor,
actorId
},
inputSecrets: locallyCreatedSecrets.map((doc) => {
return {
type: doc.type,
@@ -1982,6 +2038,10 @@ export const secretV2BridgeServiceFactory = ({
tx,
secretTagDAL,
secretVersionTagDAL,
actor: {
type: actor,
actorId
},
inputSecrets: locallyUpdatedSecrets.map((doc) => {
return {
filter: {
@@ -168,6 +168,10 @@ export type TFnSecretBulkInsert = {
secretVersionDAL: Pick<TSecretVersionV2DALFactory, "insertMany">;
secretTagDAL: Pick<TSecretTagDALFactory, "saveTagsToSecretV2">;
secretVersionTagDAL: Pick<TSecretVersionV2TagDALFactory, "insertMany">;
actor?: {
type: string;
actorId: string;
};
};
type TRequireReferenceIfValue =
@@ -192,6 +196,10 @@ export type TFnSecretBulkUpdate = {
secretVersionDAL: Pick<TSecretVersionV2DALFactory, "insertMany">;
secretTagDAL: Pick<TSecretTagDALFactory, "saveTagsToSecretV2" | "deleteTagsToSecretV2">;
secretVersionTagDAL: Pick<TSecretVersionV2TagDALFactory, "insertMany">;
actor?: {
type: string;
actorId: string;
};
tx?: Knex;
};
+33 -1
View File
@@ -284,6 +284,10 @@ export const secretServiceFactory = ({
secretVersionDAL,
secretTagDAL,
secretVersionTagDAL,
actor: {
type: actor,
actorId
},
tx
})
);
@@ -429,6 +433,10 @@ export const secretServiceFactory = ({
secretVersionDAL,
secretTagDAL,
secretVersionTagDAL,
actor: {
type: actor,
actorId
},
tx
})
);
@@ -822,6 +830,10 @@ export const secretServiceFactory = ({
secretVersionDAL,
secretTagDAL,
secretVersionTagDAL,
actor: {
type: actor,
actorId
},
tx
})
);
@@ -931,7 +943,11 @@ export const secretServiceFactory = ({
secretDAL,
secretVersionDAL,
secretTagDAL,
secretVersionTagDAL
secretVersionTagDAL,
actor: {
type: actor,
actorId
}
})
);
@@ -2404,6 +2420,10 @@ export const secretServiceFactory = ({
secretVersionDAL,
secretTagDAL,
secretVersionTagDAL,
actor: {
type: actor,
actorId
},
tx
})
);
@@ -2514,6 +2534,10 @@ export const secretServiceFactory = ({
secretVersionDAL,
secretTagDAL,
secretVersionTagDAL,
actor: {
type: actor,
actorId
},
tx
})
);
@@ -2848,6 +2872,10 @@ export const secretServiceFactory = ({
secretDAL,
tx,
secretTagDAL,
actor: {
type: actor,
actorId
},
secretVersionTagDAL,
inputSecrets: locallyCreatedSecrets.map((doc) => {
return {
@@ -2879,6 +2907,10 @@ export const secretServiceFactory = ({
tx,
secretTagDAL,
secretVersionTagDAL,
actor: {
type: actor,
actorId
},
inputSecrets: locallyUpdatedSecrets.map((doc) => {
return {
filter: {
@@ -322,6 +322,10 @@ export type TFnSecretBulkInsert = {
secretVersionDAL: Pick<TSecretVersionDALFactory, "insertMany">;
secretTagDAL: Pick<TSecretTagDALFactory, "saveTagsToSecret">;
secretVersionTagDAL: Pick<TSecretVersionTagDALFactory, "insertMany">;
actor?: {
type?: string;
actorId: string;
};
};
export type TFnSecretBulkUpdate = {
@@ -336,6 +340,10 @@ export type TFnSecretBulkUpdate = {
secretTagDAL: Pick<TSecretTagDALFactory, "saveTagsToSecret" | "deleteTagsManySecret">;
secretVersionTagDAL: Pick<TSecretVersionTagDALFactory, "insertMany">;
tx?: Knex;
actor?: {
type?: string;
actorId: string;
};
};
export type TAttachSecretTagsDTO = {