mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-07 01:27:41 +00:00
feat: completed migration backend logic
This commit is contained in:
@@ -354,12 +354,66 @@ export const secretApprovalRequestSecretDALFactory = (db: TDbClient) => {
|
|||||||
throw new DatabaseError({ error, name: "FindByRequestId" });
|
throw new DatabaseError({ error, name: "FindByRequestId" });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
// special query for migration to v2 secret
|
||||||
|
const findByProjectId = async (projectId: string, tx?: Knex) => {
|
||||||
|
try {
|
||||||
|
const docs = await (tx || db)(TableName.SecretApprovalRequestSecret)
|
||||||
|
.join(
|
||||||
|
TableName.SecretApprovalRequest,
|
||||||
|
`${TableName.SecretApprovalRequest}.id`,
|
||||||
|
`${TableName.SecretApprovalRequestSecret}.requestId`
|
||||||
|
)
|
||||||
|
.join(TableName.SecretFolder, `${TableName.SecretApprovalRequest}.folderId`, `${TableName.SecretFolder}.id`)
|
||||||
|
.join(TableName.Environment, `${TableName.SecretFolder}.envId`, `${TableName.Environment}.id`)
|
||||||
|
.leftJoin(
|
||||||
|
TableName.SecretApprovalRequestSecretTag,
|
||||||
|
`${TableName.SecretApprovalRequestSecret}.id`,
|
||||||
|
`${TableName.SecretApprovalRequestSecretTag}.secretId`
|
||||||
|
)
|
||||||
|
.where({ projectId })
|
||||||
|
.select(selectAllTableCols(TableName.SecretApprovalRequestSecret))
|
||||||
|
.select(
|
||||||
|
db.ref("id").withSchema(TableName.SecretApprovalRequestSecretTag).as("secretApprovalTagId"),
|
||||||
|
db.ref("secretId").withSchema(TableName.SecretApprovalRequestSecretTag).as("secretApprovalTagSecretId"),
|
||||||
|
db.ref("tagId").withSchema(TableName.SecretApprovalRequestSecretTag).as("secretApprovalTagSecretTagId"),
|
||||||
|
db.ref("createdAt").withSchema(TableName.SecretApprovalRequestSecretTag).as("secretApprovalTagCreatedAt"),
|
||||||
|
db.ref("updatedAt").withSchema(TableName.SecretApprovalRequestSecretTag).as("secretApprovalTagUpdatedAt")
|
||||||
|
);
|
||||||
|
const formatedDoc = sqlNestRelationships({
|
||||||
|
data: docs,
|
||||||
|
key: "id",
|
||||||
|
parentMapper: (data) => SecretApprovalRequestsSecretsSchema.parse(data),
|
||||||
|
childrenMapper: [
|
||||||
|
{
|
||||||
|
key: "secretApprovalTagId",
|
||||||
|
label: "tags" as const,
|
||||||
|
mapper: ({
|
||||||
|
secretApprovalTagSecretId,
|
||||||
|
secretApprovalTagId,
|
||||||
|
secretApprovalTagUpdatedAt,
|
||||||
|
secretApprovalTagCreatedAt
|
||||||
|
}) => ({
|
||||||
|
secretApprovalTagSecretId,
|
||||||
|
secretApprovalTagId,
|
||||||
|
secretApprovalTagUpdatedAt,
|
||||||
|
secretApprovalTagCreatedAt
|
||||||
|
})
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
return formatedDoc;
|
||||||
|
} catch (error) {
|
||||||
|
throw new DatabaseError({ error, name: "FindByRequestId" });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...secretApprovalRequestSecretOrm,
|
...secretApprovalRequestSecretOrm,
|
||||||
insertV2Bridge: secretApprovalRequestSecretV2Orm.insertMany,
|
insertV2Bridge: secretApprovalRequestSecretV2Orm.insertMany,
|
||||||
findByRequestId,
|
findByRequestId,
|
||||||
findByRequestIdBridgeSecretV2,
|
findByRequestIdBridgeSecretV2,
|
||||||
bulkUpdateNoVersionIncrement,
|
bulkUpdateNoVersionIncrement,
|
||||||
|
findByProjectId,
|
||||||
insertApprovalSecretTags: secretApprovalRequestSecretTagOrm.insertMany,
|
insertApprovalSecretTags: secretApprovalRequestSecretTagOrm.insertMany,
|
||||||
insertApprovalSecretV2Tags: secretApprovalRequestSecretV2TagOrm.insertMany
|
insertApprovalSecretV2Tags: secretApprovalRequestSecretV2TagOrm.insertMany
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
/* eslint-disable no-await-in-loop */
|
/* eslint-disable no-await-in-loop */
|
||||||
import { Knex } from "knex";
|
import { Knex } from "knex";
|
||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
import { TDbClient } from "@app/db";
|
import { TDbClient } from "@app/db";
|
||||||
import {
|
import {
|
||||||
@@ -719,6 +720,71 @@ export const snapshotDALFactory = (db: TDbClient) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// special query for migration for secret v2
|
||||||
|
const findNSecretV1SnapshotByFolderId = async (folderId: string, n = 15, tx?: Knex) => {
|
||||||
|
try {
|
||||||
|
const data = await (tx || db.replicaNode())(TableName.Snapshot)
|
||||||
|
.leftJoin(TableName.SnapshotSecret, `${TableName.Snapshot}.id`, `${TableName.SnapshotSecret}.snapshotId`)
|
||||||
|
.leftJoin(
|
||||||
|
TableName.SecretVersion,
|
||||||
|
`${TableName.SnapshotSecret}.secretVersionId`,
|
||||||
|
`${TableName.SecretVersion}.id`
|
||||||
|
)
|
||||||
|
.leftJoin(
|
||||||
|
TableName.SecretVersionTag,
|
||||||
|
`${TableName.SecretVersionTag}.${TableName.SecretVersion}Id`,
|
||||||
|
`${TableName.SecretVersion}.id`
|
||||||
|
)
|
||||||
|
.select(selectAllTableCols(TableName.SecretVersion))
|
||||||
|
.select(
|
||||||
|
db.ref("id").withSchema(TableName.Snapshot).as("snapshotId"),
|
||||||
|
db.ref("createdAt").withSchema(TableName.Snapshot).as("snapshotCreatedAt"),
|
||||||
|
db.ref("updatedAt").withSchema(TableName.Snapshot).as("snapshotUpdatedAt"),
|
||||||
|
db.ref("envId").withSchema(TableName.SnapshotSecret).as("snapshotEnvId"),
|
||||||
|
db.ref("id").withSchema(TableName.SecretVersionTag).as("secretVersionTagId"),
|
||||||
|
db.ref("secret_versionsId").withSchema(TableName.SecretVersionTag).as("secretVersionTagSecretId"),
|
||||||
|
db.ref("secret_versionsId").withSchema(TableName.SecretVersionTag).as("secretVersionTagSecretTagId"),
|
||||||
|
db.raw(
|
||||||
|
`DENSE_RANK() OVER (partition by ${TableName.Snapshot}."id" ORDER BY ${TableName.SecretVersion}."createdAt") as rank`
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.orderBy(`${TableName.Snapshot}.createdAt`, "desc")
|
||||||
|
.where(`${TableName.Snapshot}.folderId`, folderId)
|
||||||
|
.andWhere("rank", "<", n);
|
||||||
|
|
||||||
|
return sqlNestRelationships({
|
||||||
|
data,
|
||||||
|
key: "snapshotId",
|
||||||
|
parentMapper: ({ snapshotId: id, snapshotCreatedAt: createdAt, snapshotUpdatedAt: updatedAt }) => ({
|
||||||
|
id,
|
||||||
|
folderId,
|
||||||
|
createdAt,
|
||||||
|
updatedAt
|
||||||
|
}),
|
||||||
|
childrenMapper: [
|
||||||
|
{
|
||||||
|
key: "id",
|
||||||
|
label: "secretVersions" as const,
|
||||||
|
mapper: (el) => SecretVersionsSchema.extend({ snapshotEnvId: z.string() }).parse(el),
|
||||||
|
childrenMapper: [
|
||||||
|
{
|
||||||
|
key: "secretVersionTagId",
|
||||||
|
label: "tags" as const,
|
||||||
|
mapper: ({ secretVersionTagId, secretVersionTagSecretId, secretVersionTagSecretTagId }) => ({
|
||||||
|
id: secretVersionTagId,
|
||||||
|
secretVersionId: secretVersionTagSecretId,
|
||||||
|
secretTagId: secretVersionTagSecretTagId
|
||||||
|
})
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
throw new DatabaseError({ error, name: "FindSecretSnapshotDataById" });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...secretSnapshotOrm,
|
...secretSnapshotOrm,
|
||||||
findById,
|
findById,
|
||||||
@@ -728,6 +794,7 @@ export const snapshotDALFactory = (db: TDbClient) => {
|
|||||||
countOfSnapshotsByFolderId,
|
countOfSnapshotsByFolderId,
|
||||||
findSecretSnapshotDataById,
|
findSecretSnapshotDataById,
|
||||||
findSecretSnapshotV2DataById,
|
findSecretSnapshotV2DataById,
|
||||||
pruneExcessSnapshots
|
pruneExcessSnapshots,
|
||||||
|
findNSecretV1SnapshotByFolderId
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -721,7 +721,10 @@ export const registerRoutes = async (
|
|||||||
secretV2BridgeDAL,
|
secretV2BridgeDAL,
|
||||||
secretVersionTagV2BridgeDAL,
|
secretVersionTagV2BridgeDAL,
|
||||||
secretRotationDAL,
|
secretRotationDAL,
|
||||||
integrationAuthDAL
|
integrationAuthDAL,
|
||||||
|
snapshotDAL,
|
||||||
|
secretApprovalRequestSecretDAL,
|
||||||
|
snapshotSecretV2BridgeDAL
|
||||||
});
|
});
|
||||||
const secretImportService = secretImportServiceFactory({
|
const secretImportService = secretImportServiceFactory({
|
||||||
licenseService,
|
licenseService,
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
/* eslint-disable no-await-in-loop */
|
/* eslint-disable no-await-in-loop */
|
||||||
import { AxiosError } from "axios";
|
import { AxiosError } from "axios";
|
||||||
|
|
||||||
import { ProjectUpgradeStatus, ProjectVersion } from "@app/db/schemas";
|
import { ProjectUpgradeStatus, ProjectVersion, TSecretSnapshotSecretsV2, TSecretVersionsV2 } from "@app/db/schemas";
|
||||||
|
import { TSecretApprovalRequestSecretDALFactory } from "@app/ee/services/secret-approval-request/secret-approval-request-secret-dal";
|
||||||
import { TSecretRotationDALFactory } from "@app/ee/services/secret-rotation/secret-rotation-dal";
|
import { TSecretRotationDALFactory } from "@app/ee/services/secret-rotation/secret-rotation-dal";
|
||||||
|
import { TSnapshotDALFactory } from "@app/ee/services/secret-snapshot/snapshot-dal";
|
||||||
|
import { TSnapshotSecretV2DALFactory } from "@app/ee/services/secret-snapshot/snapshot-secret-v2-dal";
|
||||||
import { getConfig } from "@app/lib/config/env";
|
import { getConfig } from "@app/lib/config/env";
|
||||||
import { decryptSymmetric128BitHexKeyUTF8 } from "@app/lib/crypto";
|
import { decryptSymmetric128BitHexKeyUTF8 } from "@app/lib/crypto";
|
||||||
import { daysToMillisecond, secondsToMillis } from "@app/lib/dates";
|
import { daysToMillisecond, secondsToMillis } from "@app/lib/dates";
|
||||||
@@ -73,6 +76,12 @@ type TSecretQueueFactoryDep = {
|
|||||||
secretVersionV2BridgeDAL: Pick<TSecretVersionV2DALFactory, "insertMany" | "findLatestVersionMany">;
|
secretVersionV2BridgeDAL: Pick<TSecretVersionV2DALFactory, "insertMany" | "findLatestVersionMany">;
|
||||||
secretVersionTagV2BridgeDAL: Pick<TSecretVersionV2TagDALFactory, "insertMany">;
|
secretVersionTagV2BridgeDAL: Pick<TSecretVersionV2TagDALFactory, "insertMany">;
|
||||||
secretRotationDAL: Pick<TSecretRotationDALFactory, "secretOutputV2InsertMany" | "find">;
|
secretRotationDAL: Pick<TSecretRotationDALFactory, "secretOutputV2InsertMany" | "find">;
|
||||||
|
secretApprovalRequestSecretDAL: Pick<
|
||||||
|
TSecretApprovalRequestSecretDALFactory,
|
||||||
|
"findByProjectId" | "insertV2Bridge" | "insertApprovalSecretV2Tags"
|
||||||
|
>;
|
||||||
|
snapshotDAL: Pick<TSnapshotDALFactory, "findNSecretV1SnapshotByFolderId">;
|
||||||
|
snapshotSecretV2BridgeDAL: Pick<TSnapshotSecretV2DALFactory, "insertMany">;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TGetSecrets = {
|
export type TGetSecrets = {
|
||||||
@@ -113,7 +122,10 @@ export const secretQueueFactory = ({
|
|||||||
secretVersionV2BridgeDAL,
|
secretVersionV2BridgeDAL,
|
||||||
kmsService,
|
kmsService,
|
||||||
secretVersionTagV2BridgeDAL,
|
secretVersionTagV2BridgeDAL,
|
||||||
secretRotationDAL
|
secretRotationDAL,
|
||||||
|
secretApprovalRequestSecretDAL,
|
||||||
|
snapshotDAL,
|
||||||
|
snapshotSecretV2BridgeDAL
|
||||||
}: TSecretQueueFactoryDep) => {
|
}: TSecretQueueFactoryDep) => {
|
||||||
const removeSecretReminder = async (dto: TRemoveSecretReminderDTO) => {
|
const removeSecretReminder = async (dto: TRemoveSecretReminderDTO) => {
|
||||||
const appCfg = getConfig();
|
const appCfg = getConfig();
|
||||||
@@ -869,7 +881,81 @@ export const secretQueueFactory = ({
|
|||||||
}),
|
}),
|
||||||
tx
|
tx
|
||||||
);
|
);
|
||||||
await secretV2BridgeDAL.upsertSecretReferences(secretReferences);
|
await secretV2BridgeDAL.upsertSecretReferences(secretReferences, tx);
|
||||||
|
}
|
||||||
|
|
||||||
|
const snapshots = await snapshotDAL.findNSecretV1SnapshotByFolderId(folderId, 10, tx);
|
||||||
|
const projectV3SecretVersions: Record<string, TSecretVersionsV2> = {};
|
||||||
|
const projectV3SecretVersionTags: { secret_versions_v2Id: string; secret_tagsId: string }[] = [];
|
||||||
|
const projectV3SnapshotSecrets: Omit<TSecretSnapshotSecretsV2, "id">[] = [];
|
||||||
|
snapshots.forEach(({ secretVersions = [], ...snapshot }) => {
|
||||||
|
secretVersions.forEach((el) => {
|
||||||
|
projectV3SnapshotSecrets.push({
|
||||||
|
secretVersionId: el.id,
|
||||||
|
snapshotId: snapshot.id,
|
||||||
|
createdAt: snapshot.createdAt,
|
||||||
|
updatedAt: snapshot.updatedAt,
|
||||||
|
envId: el.snapshotEnvId
|
||||||
|
});
|
||||||
|
if (projectV3SecretVersions[el.id]) return;
|
||||||
|
|
||||||
|
const key = decryptSymmetric128BitHexKeyUTF8({
|
||||||
|
ciphertext: el.secretKeyCiphertext,
|
||||||
|
iv: el.secretKeyIV,
|
||||||
|
tag: el.secretKeyTag,
|
||||||
|
key: botKey
|
||||||
|
});
|
||||||
|
const value = decryptSymmetric128BitHexKeyUTF8({
|
||||||
|
ciphertext: el.secretValueCiphertext,
|
||||||
|
iv: el.secretValueIV,
|
||||||
|
tag: el.secretValueTag,
|
||||||
|
key: botKey
|
||||||
|
});
|
||||||
|
const comment =
|
||||||
|
el.secretCommentCiphertext && el.secretCommentTag && el.secretCommentIV
|
||||||
|
? decryptSymmetric128BitHexKeyUTF8({
|
||||||
|
ciphertext: el.secretCommentCiphertext,
|
||||||
|
iv: el.secretCommentIV,
|
||||||
|
tag: el.secretCommentTag,
|
||||||
|
key: botKey
|
||||||
|
})
|
||||||
|
: "";
|
||||||
|
const encryptedValue = secretManagerEncryptor({ plainText: Buffer.from(value) }).cipherTextBlob;
|
||||||
|
|
||||||
|
const encryptedComment = comment
|
||||||
|
? secretManagerEncryptor({ plainText: Buffer.from(comment) }).cipherTextBlob
|
||||||
|
: null;
|
||||||
|
projectV3SecretVersions[el.id] = {
|
||||||
|
id: el.id,
|
||||||
|
createdAt: el.createdAt,
|
||||||
|
updatedAt: el.updatedAt,
|
||||||
|
skipMultilineEncoding: el.skipMultilineEncoding,
|
||||||
|
encryptedComment,
|
||||||
|
encryptedValue,
|
||||||
|
key,
|
||||||
|
version: el.version,
|
||||||
|
type: el.type,
|
||||||
|
userId: el.userId,
|
||||||
|
folderId: el.folderId,
|
||||||
|
metadata: el.metadata,
|
||||||
|
reminderNote: el.secretReminderNote,
|
||||||
|
reminderRepeatDays: el.secretReminderRepeatDays,
|
||||||
|
secretId: el.secretId,
|
||||||
|
envId: el.envId
|
||||||
|
};
|
||||||
|
el.tags.forEach(({ secretTagId }) => {
|
||||||
|
projectV3SecretVersionTags.push({ secret_tagsId: secretTagId, secret_versions_v2Id: el.id });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
if (projectV3SecretVersionTags.length) {
|
||||||
|
await secretVersionV2BridgeDAL.insertMany(Object.values(projectV3SecretVersions), tx);
|
||||||
|
}
|
||||||
|
if (projectV3SecretVersionTags.length) {
|
||||||
|
await secretVersionTagV2BridgeDAL.insertMany(projectV3SecretVersionTags, tx);
|
||||||
|
}
|
||||||
|
if (projectV3SnapshotSecrets.length) {
|
||||||
|
await snapshotSecretV2BridgeDAL.insertMany(projectV3SnapshotSecrets, tx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
@@ -959,7 +1045,6 @@ export const secretQueueFactory = ({
|
|||||||
* Secret Rotation Secret Migration
|
* Secret Rotation Secret Migration
|
||||||
* Saving the new encrypted colum
|
* Saving the new encrypted colum
|
||||||
* */
|
* */
|
||||||
|
|
||||||
const projectV1SecretRotations = await secretRotationDAL.find({ projectId }, tx);
|
const projectV1SecretRotations = await secretRotationDAL.find({ projectId }, tx);
|
||||||
await secretRotationDAL.secretOutputV2InsertMany(
|
await secretRotationDAL.secretOutputV2InsertMany(
|
||||||
projectV1SecretRotations.flatMap((el) =>
|
projectV1SecretRotations.flatMap((el) =>
|
||||||
@@ -967,6 +1052,69 @@ export const secretQueueFactory = ({
|
|||||||
),
|
),
|
||||||
tx
|
tx
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* approvals
|
||||||
|
* */
|
||||||
|
const projectV1ApprovalSecrets = await secretApprovalRequestSecretDAL.findByProjectId(projectId);
|
||||||
|
if (projectV1ApprovalSecrets.length) {
|
||||||
|
await secretApprovalRequestSecretDAL.insertV2Bridge(
|
||||||
|
projectV1ApprovalSecrets.map((el) => {
|
||||||
|
const key = decryptSymmetric128BitHexKeyUTF8({
|
||||||
|
ciphertext: el.secretKeyCiphertext,
|
||||||
|
iv: el.secretKeyIV,
|
||||||
|
tag: el.secretKeyTag,
|
||||||
|
key: botKey
|
||||||
|
});
|
||||||
|
const value = decryptSymmetric128BitHexKeyUTF8({
|
||||||
|
ciphertext: el.secretValueCiphertext,
|
||||||
|
iv: el.secretValueIV,
|
||||||
|
tag: el.secretValueTag,
|
||||||
|
key: botKey
|
||||||
|
});
|
||||||
|
const comment =
|
||||||
|
el.secretCommentCiphertext && el.secretCommentTag && el.secretCommentIV
|
||||||
|
? decryptSymmetric128BitHexKeyUTF8({
|
||||||
|
ciphertext: el.secretCommentCiphertext,
|
||||||
|
iv: el.secretCommentIV,
|
||||||
|
tag: el.secretCommentTag,
|
||||||
|
key: botKey
|
||||||
|
})
|
||||||
|
: "";
|
||||||
|
const encryptedValue = secretManagerEncryptor({ plainText: Buffer.from(value) }).cipherTextBlob;
|
||||||
|
const encryptedComment = comment
|
||||||
|
? secretManagerEncryptor({ plainText: Buffer.from(comment) }).cipherTextBlob
|
||||||
|
: null;
|
||||||
|
return {
|
||||||
|
id: el.id,
|
||||||
|
createdAt: el.createdAt,
|
||||||
|
updatedAt: el.updatedAt,
|
||||||
|
skipMultilineEncoding: el.skipMultilineEncoding,
|
||||||
|
encryptedComment,
|
||||||
|
encryptedValue,
|
||||||
|
key,
|
||||||
|
version: el.version,
|
||||||
|
metadata: el.metadata,
|
||||||
|
reminderNote: el.secretReminderNote,
|
||||||
|
reminderRepeatDays: el.secretReminderRepeatDays,
|
||||||
|
requestId: el.requestId,
|
||||||
|
op: el.op,
|
||||||
|
secretId: el.secretId,
|
||||||
|
secretVersion: el.secretVersion
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
tx
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const projectV1SecretApprovalSecretTags = projectV1ApprovalSecrets.flatMap((el) =>
|
||||||
|
el.tags.map((tag) => ({
|
||||||
|
secretId: tag.secretApprovalTagSecretId,
|
||||||
|
tagId: tag.secretApprovalTagId
|
||||||
|
}))
|
||||||
|
);
|
||||||
|
if (projectV1SecretApprovalSecretTags.length) {
|
||||||
|
await secretApprovalRequestSecretDAL.insertApprovalSecretV2Tags(projectV1SecretApprovalSecretTags, tx);
|
||||||
|
}
|
||||||
await projectDAL.updateById(projectId, { upgradeStatus: null, version: ProjectVersion.V3 }, tx);
|
await projectDAL.updateById(projectId, { upgradeStatus: null, version: ProjectVersion.V3 }, tx);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user