mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-07 16:27:40 +00:00
PIT: remove reminders from commit history
This commit is contained in:
@@ -27,10 +27,8 @@ const secretVersionSchema = z.object({
|
|||||||
secretKey: z.string(),
|
secretKey: z.string(),
|
||||||
secretComment: z.string(),
|
secretComment: z.string(),
|
||||||
skipMultilineEncoding: z.boolean().nullable().optional(),
|
skipMultilineEncoding: z.boolean().nullable().optional(),
|
||||||
secretReminderRepeatDays: z.number().nullable().optional(),
|
|
||||||
tags: z.array(z.string()).nullable().optional(),
|
tags: z.array(z.string()).nullable().optional(),
|
||||||
metadata: z.unknown().nullable().optional(),
|
metadata: z.unknown().nullable().optional(),
|
||||||
secretReminderNote: z.string().nullable().optional(),
|
|
||||||
secretValue: z.string()
|
secretValue: z.string()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -100,11 +100,8 @@ type SecretChange = BaseChange & {
|
|||||||
secretKey?: string;
|
secretKey?: string;
|
||||||
secretComment?: string;
|
secretComment?: string;
|
||||||
skipMultilineEncoding?: boolean | null;
|
skipMultilineEncoding?: boolean | null;
|
||||||
secretReminderRepeatDays?: number | null;
|
|
||||||
secretReminderNote?: string | null;
|
|
||||||
metadata?: unknown;
|
metadata?: unknown;
|
||||||
tags?: string[] | null;
|
tags?: string[] | null;
|
||||||
secretReminderRecipients?: string[] | null;
|
|
||||||
secretValue?: string;
|
secretValue?: string;
|
||||||
}[];
|
}[];
|
||||||
};
|
};
|
||||||
@@ -770,6 +767,89 @@ export const folderCommitServiceFactory = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const compareSecretVersions = async (
|
||||||
|
version1: TSecretVersionsV2 & { tags: { id: string }[] },
|
||||||
|
version2: TSecretVersionsV2 & { tags: { id: string }[] },
|
||||||
|
projectId: string
|
||||||
|
) => {
|
||||||
|
const { decryptor: secretManagerDecryptor } = await kmsService.createCipherPairWithDataKey({
|
||||||
|
type: KmsDataKey.SecretManager,
|
||||||
|
projectId
|
||||||
|
});
|
||||||
|
const objectsEqual = (o1: unknown, o2: unknown): boolean => {
|
||||||
|
if (typeof o1 !== "object" || o1 === null || typeof o2 !== "object" || o2 === null) {
|
||||||
|
return o1 === o2;
|
||||||
|
}
|
||||||
|
|
||||||
|
const obj1 = o1 as Record<string, unknown>;
|
||||||
|
const obj2 = o2 as Record<string, unknown>;
|
||||||
|
return (
|
||||||
|
Object.keys(obj1).length === Object.keys(obj2).length && Object.keys(obj1).every((p) => obj1[p] === obj2[p])
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const arraysEqual = (a1: unknown[], a2: unknown[]) =>
|
||||||
|
a1.length === a2.length && a1.every((obj1) => a2.some((obj2) => objectsEqual(obj1, obj2)));
|
||||||
|
|
||||||
|
const version1Reshaped = {
|
||||||
|
...version1,
|
||||||
|
encryptedValue: version1.encryptedValue
|
||||||
|
? secretManagerDecryptor({ cipherTextBlob: version1.encryptedValue }).toString()
|
||||||
|
: "",
|
||||||
|
encryptedComment: version1.encryptedComment
|
||||||
|
? secretManagerDecryptor({ cipherTextBlob: version1.encryptedComment }).toString()
|
||||||
|
: "",
|
||||||
|
metadata: version1.metadata as { key: string; value: string }[],
|
||||||
|
tags: version1.tags.map((tag) => tag.id)
|
||||||
|
};
|
||||||
|
const version2Reshaped = {
|
||||||
|
...version2,
|
||||||
|
encryptedValue: version2.encryptedValue
|
||||||
|
? secretManagerDecryptor({ cipherTextBlob: version2.encryptedValue }).toString()
|
||||||
|
: "",
|
||||||
|
encryptedComment: version2.encryptedComment
|
||||||
|
? secretManagerDecryptor({ cipherTextBlob: version2.encryptedComment }).toString()
|
||||||
|
: "",
|
||||||
|
metadata: version2.metadata as { key: string; value: string }[],
|
||||||
|
tags: version2.tags.map((tag) => tag.id)
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
version1Reshaped.key === version2Reshaped.key &&
|
||||||
|
version1Reshaped.encryptedValue === version2Reshaped.encryptedValue &&
|
||||||
|
version1Reshaped.encryptedComment === version2Reshaped.encryptedComment &&
|
||||||
|
version1Reshaped.skipMultilineEncoding === version2Reshaped.skipMultilineEncoding &&
|
||||||
|
arraysEqual(version1Reshaped.metadata, version2Reshaped.metadata) &&
|
||||||
|
version1Reshaped.tags.length === version2Reshaped.tags.length &&
|
||||||
|
version1Reshaped.tags.every((tag) => version2Reshaped.tags.includes(tag))
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const filterIgnoredChanges = async (
|
||||||
|
changes: {
|
||||||
|
type: string;
|
||||||
|
secretVersionId?: string;
|
||||||
|
folderVersionId?: string;
|
||||||
|
isUpdate?: boolean;
|
||||||
|
folderId?: string;
|
||||||
|
}[],
|
||||||
|
projectId: string,
|
||||||
|
tx?: Knex
|
||||||
|
) => {
|
||||||
|
let filteredChanges = [...changes];
|
||||||
|
for (const change of changes) {
|
||||||
|
if (change.type === ChangeType.ADD && change.isUpdate && change.secretVersionId) {
|
||||||
|
const secretVersions = await secretVersionV2BridgeDAL.findByIdAndPreviousVersion(change.secretVersionId, tx);
|
||||||
|
const comparison = await compareSecretVersions(secretVersions[0], secretVersions[1], projectId);
|
||||||
|
if (comparison) {
|
||||||
|
filteredChanges = filteredChanges.filter(
|
||||||
|
(filteredChange) => filteredChange.secretVersionId !== change.secretVersionId
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return filteredChanges;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new commit with the provided changes
|
* Creates a new commit with the provided changes
|
||||||
*/
|
*/
|
||||||
@@ -792,6 +872,17 @@ 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` });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const project = await projectDAL.findProjectByEnvId(folder.envId, tx);
|
||||||
|
|
||||||
|
if (!project) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const changes = await filterIgnoredChanges(data.changes, project.id, tx);
|
||||||
|
if (changes.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const newCommit = await folderCommitDAL.create(
|
const newCommit = await folderCommitDAL.create(
|
||||||
{
|
{
|
||||||
actorMetadata: metadata,
|
actorMetadata: metadata,
|
||||||
@@ -804,7 +895,7 @@ export const folderCommitServiceFactory = ({
|
|||||||
);
|
);
|
||||||
|
|
||||||
const batchSize = 500;
|
const batchSize = 500;
|
||||||
const chunks = chunkArray(data.changes, batchSize);
|
const chunks = chunkArray(changes, batchSize);
|
||||||
|
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
chunks.map(async (chunk) => {
|
chunks.map(async (chunk) => {
|
||||||
@@ -822,7 +913,7 @@ export const folderCommitServiceFactory = ({
|
|||||||
);
|
);
|
||||||
|
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
data.changes.map(async (change) => {
|
changes.map(async (change) => {
|
||||||
if (change.type === ChangeType.DELETE && change.folderId) {
|
if (change.type === ChangeType.DELETE && change.folderId) {
|
||||||
await createDeleteCommitForNestedFolders({
|
await createDeleteCommitForNestedFolders({
|
||||||
folderId: change.folderId,
|
folderId: change.folderId,
|
||||||
@@ -1486,7 +1577,9 @@ export const folderCommitServiceFactory = ({
|
|||||||
},
|
},
|
||||||
tx
|
tx
|
||||||
);
|
);
|
||||||
await createFolderCheckpoint({ folderId, folderCommitId: newCommit.id, force: true, tx });
|
if (newCommit) {
|
||||||
|
await createFolderCheckpoint({ folderId, folderCommitId: newCommit.id, force: true, tx });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -288,30 +288,6 @@ export const fnSecretBulkUpdate = async ({
|
|||||||
tx
|
tx
|
||||||
);
|
);
|
||||||
|
|
||||||
const commitChanges = secretVersions
|
|
||||||
.filter(({ type }) => type === SecretType.Shared)
|
|
||||||
.map((sv) => ({
|
|
||||||
type: CommitType.ADD,
|
|
||||||
isUpdate: true,
|
|
||||||
secretVersionId: sv.id
|
|
||||||
}));
|
|
||||||
if (commitChanges.length > 0) {
|
|
||||||
await folderCommitService.createCommit(
|
|
||||||
{
|
|
||||||
actor: {
|
|
||||||
type: actorType || ActorType.PLATFORM,
|
|
||||||
metadata: {
|
|
||||||
id: actor?.actorId
|
|
||||||
}
|
|
||||||
},
|
|
||||||
message: "Secret Updated",
|
|
||||||
folderId,
|
|
||||||
changes: commitChanges
|
|
||||||
},
|
|
||||||
tx
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
await secretDAL.upsertSecretReferences(
|
await secretDAL.upsertSecretReferences(
|
||||||
inputSecrets
|
inputSecrets
|
||||||
.filter(({ data: { references } }) => Boolean(references))
|
.filter(({ data: { references } }) => Boolean(references))
|
||||||
@@ -382,6 +358,31 @@ export const fnSecretBulkUpdate = async ({
|
|||||||
},
|
},
|
||||||
{ tx }
|
{ tx }
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const commitChanges = secretVersions
|
||||||
|
.filter(({ type }) => type === SecretType.Shared)
|
||||||
|
.map((sv) => ({
|
||||||
|
type: CommitType.ADD,
|
||||||
|
isUpdate: true,
|
||||||
|
secretVersionId: sv.id
|
||||||
|
}));
|
||||||
|
if (commitChanges.length > 0) {
|
||||||
|
await folderCommitService.createCommit(
|
||||||
|
{
|
||||||
|
actor: {
|
||||||
|
type: actorType || ActorType.PLATFORM,
|
||||||
|
metadata: {
|
||||||
|
id: actor?.actorId
|
||||||
|
}
|
||||||
|
},
|
||||||
|
message: "Secret Updated",
|
||||||
|
folderId,
|
||||||
|
changes: commitChanges
|
||||||
|
},
|
||||||
|
tx
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return secretsWithTags.map((secret) => ({ ...secret, _id: secret.id }));
|
return secretsWithTags.map((secret) => ({ ...secret, _id: secret.id }));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { Knex } from "knex";
|
|||||||
import { TDbClient } from "@app/db";
|
import { TDbClient } from "@app/db";
|
||||||
import { SecretVersionsV2Schema, TableName, TSecretVersionsV2, TSecretVersionsV2Update } from "@app/db/schemas";
|
import { SecretVersionsV2Schema, TableName, TSecretVersionsV2, TSecretVersionsV2Update } from "@app/db/schemas";
|
||||||
import { BadRequestError, DatabaseError } from "@app/lib/errors";
|
import { BadRequestError, DatabaseError } from "@app/lib/errors";
|
||||||
import { ormify, selectAllTableCols, sqlNestRelationships, TFindOpt } from "@app/lib/knex";
|
import { buildFindFilter, ormify, selectAllTableCols, sqlNestRelationships, TFindOpt } from "@app/lib/knex";
|
||||||
import { logger } from "@app/lib/logger";
|
import { logger } from "@app/lib/logger";
|
||||||
import { QueueName } from "@app/queue";
|
import { QueueName } from "@app/queue";
|
||||||
|
|
||||||
@@ -371,6 +371,79 @@ export const secretVersionV2BridgeDALFactory = (db: TDbClient) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const findByIdAndPreviousVersion = async (secretVersionId: string, tx?: Knex) => {
|
||||||
|
try {
|
||||||
|
const targetSecretVersion = await (tx || db.replicaNode())(TableName.SecretVersionV2)
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
||||||
|
.where(buildFindFilter({ id: secretVersionId }, TableName.SecretVersionV2))
|
||||||
|
.leftJoin(
|
||||||
|
TableName.SecretVersionV2Tag,
|
||||||
|
`${TableName.SecretVersionV2}.id`,
|
||||||
|
`${TableName.SecretVersionV2Tag}.${TableName.SecretVersionV2}Id`
|
||||||
|
)
|
||||||
|
.leftJoin(
|
||||||
|
TableName.SecretTag,
|
||||||
|
`${TableName.SecretVersionV2Tag}.${TableName.SecretTag}Id`,
|
||||||
|
`${TableName.SecretTag}.id`
|
||||||
|
)
|
||||||
|
.select(selectAllTableCols(TableName.SecretVersionV2))
|
||||||
|
.select(db.ref("id").withSchema(TableName.SecretTag).as("tagId"))
|
||||||
|
.select(db.ref("color").withSchema(TableName.SecretTag).as("tagColor"))
|
||||||
|
.select(db.ref("slug").withSchema(TableName.SecretTag).as("tagSlug"))
|
||||||
|
.first();
|
||||||
|
if (targetSecretVersion) {
|
||||||
|
const previousSecretVersion = await (tx || db.replicaNode())(TableName.SecretVersionV2)
|
||||||
|
.where(
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
||||||
|
buildFindFilter(
|
||||||
|
{ version: targetSecretVersion.version - 1, secretId: targetSecretVersion.secretId },
|
||||||
|
TableName.SecretVersionV2
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.leftJoin(
|
||||||
|
TableName.SecretVersionV2Tag,
|
||||||
|
`${TableName.SecretVersionV2}.id`,
|
||||||
|
`${TableName.SecretVersionV2Tag}.${TableName.SecretVersionV2}Id`
|
||||||
|
)
|
||||||
|
.leftJoin(
|
||||||
|
TableName.SecretTag,
|
||||||
|
`${TableName.SecretVersionV2Tag}.${TableName.SecretTag}Id`,
|
||||||
|
`${TableName.SecretTag}.id`
|
||||||
|
)
|
||||||
|
.select(selectAllTableCols(TableName.SecretVersionV2))
|
||||||
|
.select(db.ref("id").withSchema(TableName.SecretTag).as("tagId"))
|
||||||
|
.select(db.ref("color").withSchema(TableName.SecretTag).as("tagColor"))
|
||||||
|
.select(db.ref("slug").withSchema(TableName.SecretTag).as("tagSlug"))
|
||||||
|
.first();
|
||||||
|
if (!previousSecretVersion) return [];
|
||||||
|
const docs = [previousSecretVersion, targetSecretVersion];
|
||||||
|
|
||||||
|
const data = sqlNestRelationships({
|
||||||
|
data: docs,
|
||||||
|
key: "id",
|
||||||
|
parentMapper: (el) => ({ _id: el.id, ...SecretVersionsV2Schema.parse(el) }),
|
||||||
|
childrenMapper: [
|
||||||
|
{
|
||||||
|
key: "tagId",
|
||||||
|
label: "tags" as const,
|
||||||
|
mapper: ({ tagId: id, tagColor: color, tagSlug: slug }) => ({
|
||||||
|
id,
|
||||||
|
color,
|
||||||
|
slug,
|
||||||
|
name: slug
|
||||||
|
})
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
} catch (error) {
|
||||||
|
throw new DatabaseError({ error, name: "FindByIdAndPreviousVersion" });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...secretVersionV2Orm,
|
...secretVersionV2Orm,
|
||||||
pruneExcessVersions,
|
pruneExcessVersions,
|
||||||
@@ -379,6 +452,7 @@ export const secretVersionV2BridgeDALFactory = (db: TDbClient) => {
|
|||||||
findLatestVersionByFolderId,
|
findLatestVersionByFolderId,
|
||||||
findVersionsBySecretIdWithActors,
|
findVersionsBySecretIdWithActors,
|
||||||
findBySecretId,
|
findBySecretId,
|
||||||
findByIdsWithLatestVersion
|
findByIdsWithLatestVersion,
|
||||||
|
findByIdAndPreviousVersion
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3351,10 +3351,8 @@ export const secretServiceFactory = ({
|
|||||||
secretKey: v.secretKey,
|
secretKey: v.secretKey,
|
||||||
secretComment: v.secretComment,
|
secretComment: v.secretComment,
|
||||||
skipMultilineEncoding: v.skipMultilineEncoding,
|
skipMultilineEncoding: v.skipMultilineEncoding,
|
||||||
secretReminderRepeatDays: v.secretReminderRepeatDays,
|
|
||||||
tags: v.tags?.map((tag) => tag.slug),
|
tags: v.tags?.map((tag) => tag.slug),
|
||||||
metadata: v.metadata,
|
metadata: v.metadata,
|
||||||
secretReminderNote: v.secretReminderNote,
|
|
||||||
secretValue: v.secretValue
|
secretValue: v.secretValue
|
||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user