mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-09-22 13:39:35 +00:00
feat: completed migration script for project split revert
This commit is contained in:
@@ -1,8 +1,275 @@
|
|||||||
|
import slugify from "@sindresorhus/slugify";
|
||||||
import { Knex } from "knex";
|
import { Knex } from "knex";
|
||||||
|
import { v4 as uuidV4 } from "uuid";
|
||||||
|
|
||||||
|
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
||||||
|
|
||||||
import { ProjectType, TableName } from "../schemas";
|
import { ProjectType, TableName } from "../schemas";
|
||||||
|
|
||||||
|
/* eslint-disable no-await-in-loop,@typescript-eslint/ban-ts-comment */
|
||||||
|
const newProject = async (knex: Knex, projectId: string, projectType: ProjectType) => {
|
||||||
|
const newProjectId = uuidV4();
|
||||||
|
const project = await knex(TableName.Project).where("id", projectId).first();
|
||||||
|
await knex(TableName.Project).insert({
|
||||||
|
...project,
|
||||||
|
type: projectType,
|
||||||
|
// @ts-ignore id is required
|
||||||
|
id: newProjectId,
|
||||||
|
slug: slugify(`${project?.name}-${alphaNumericNanoId(4)}`)
|
||||||
|
});
|
||||||
|
|
||||||
|
const customRoleMapping: Record<string, string> = {};
|
||||||
|
const projectCustomRoles = await knex(TableName.ProjectRoles).where("projectId", projectId);
|
||||||
|
if (projectCustomRoles.length) {
|
||||||
|
await knex.batchInsert(
|
||||||
|
TableName.ProjectRoles,
|
||||||
|
projectCustomRoles.map((el) => {
|
||||||
|
const id = uuidV4();
|
||||||
|
customRoleMapping[el.id] = id;
|
||||||
|
return {
|
||||||
|
...el,
|
||||||
|
id,
|
||||||
|
projectId: newProjectId,
|
||||||
|
permissions: el.permissions ? JSON.stringify(el.permissions) : el.permissions
|
||||||
|
};
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const groupMembershipMapping: Record<string, string> = {};
|
||||||
|
const groupMemberships = await knex(TableName.GroupProjectMembership).where("projectId", projectId);
|
||||||
|
if (groupMemberships.length) {
|
||||||
|
await knex.batchInsert(
|
||||||
|
TableName.GroupProjectMembership,
|
||||||
|
groupMemberships.map((el) => {
|
||||||
|
const id = uuidV4();
|
||||||
|
groupMembershipMapping[el.id] = id;
|
||||||
|
return { ...el, id, projectId: newProjectId };
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const groupMembershipRoles = await knex(TableName.GroupProjectMembershipRole).whereIn(
|
||||||
|
"projectMembershipId",
|
||||||
|
groupMemberships.map((el) => el.id)
|
||||||
|
);
|
||||||
|
if (groupMembershipRoles.length) {
|
||||||
|
await knex.batchInsert(
|
||||||
|
TableName.GroupProjectMembershipRole,
|
||||||
|
groupMembershipRoles.map((el) => {
|
||||||
|
const id = uuidV4();
|
||||||
|
const projectMembershipId = groupMembershipMapping[el.projectMembershipId];
|
||||||
|
const customRoleId = el.customRoleId ? customRoleMapping[el.customRoleId] : el.customRoleId;
|
||||||
|
return { ...el, id, projectMembershipId, customRoleId };
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const identityProjectMembershipMapping: Record<string, string> = {};
|
||||||
|
const identities = await knex(TableName.IdentityProjectMembership).where("projectId", projectId);
|
||||||
|
if (identities.length) {
|
||||||
|
await knex.batchInsert(
|
||||||
|
TableName.IdentityProjectMembership,
|
||||||
|
identities.map((el) => {
|
||||||
|
const id = uuidV4();
|
||||||
|
identityProjectMembershipMapping[el.id] = id;
|
||||||
|
return { ...el, id, projectId: newProjectId };
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const identitiesRoles = await knex(TableName.IdentityProjectMembershipRole).whereIn(
|
||||||
|
"projectMembershipId",
|
||||||
|
identities.map((el) => el.id)
|
||||||
|
);
|
||||||
|
if (identitiesRoles.length) {
|
||||||
|
await knex.batchInsert(
|
||||||
|
TableName.IdentityProjectMembershipRole,
|
||||||
|
identitiesRoles.map((el) => {
|
||||||
|
const id = uuidV4();
|
||||||
|
const projectMembershipId = identityProjectMembershipMapping[el.projectMembershipId];
|
||||||
|
const customRoleId = el.customRoleId ? customRoleMapping[el.customRoleId] : el.customRoleId;
|
||||||
|
return { ...el, id, projectMembershipId, customRoleId };
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const projectMembershipMapping: Record<string, string> = {};
|
||||||
|
const projectUserMembers = await knex(TableName.ProjectMembership).where("projectId", projectId);
|
||||||
|
if (projectUserMembers.length) {
|
||||||
|
await knex.batchInsert(
|
||||||
|
TableName.ProjectMembership,
|
||||||
|
projectUserMembers.map((el) => {
|
||||||
|
const id = uuidV4();
|
||||||
|
projectMembershipMapping[el.id] = id;
|
||||||
|
return { ...el, id, projectId: newProjectId };
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const membershipRoles = await knex(TableName.ProjectUserMembershipRole).whereIn(
|
||||||
|
"projectMembershipId",
|
||||||
|
projectUserMembers.map((el) => el.id)
|
||||||
|
);
|
||||||
|
if (membershipRoles.length) {
|
||||||
|
await knex.batchInsert(
|
||||||
|
TableName.ProjectUserMembershipRole,
|
||||||
|
membershipRoles.map((el) => {
|
||||||
|
const id = uuidV4();
|
||||||
|
const projectMembershipId = projectMembershipMapping[el.projectMembershipId];
|
||||||
|
const customRoleId = el.customRoleId ? customRoleMapping[el.customRoleId] : el.customRoleId;
|
||||||
|
return { ...el, id, projectMembershipId, customRoleId };
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const kmsKeys = await knex(TableName.KmsKey).where("projectId", projectId).andWhere("isReserved", true);
|
||||||
|
if (kmsKeys.length) {
|
||||||
|
await knex.batchInsert(
|
||||||
|
TableName.KmsKey,
|
||||||
|
kmsKeys.map((el) => {
|
||||||
|
const id = uuidV4();
|
||||||
|
const slug = slugify(alphaNumericNanoId(8).toLowerCase());
|
||||||
|
return { ...el, id, slug, projectId: newProjectId };
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const projectBot = await knex(TableName.ProjectBot).where("projectId", projectId).first();
|
||||||
|
if (projectBot) {
|
||||||
|
const newProjectBot = { ...projectBot, id: uuidV4(), projectId: newProjectId };
|
||||||
|
await knex(TableName.ProjectBot).insert(newProjectBot);
|
||||||
|
}
|
||||||
|
|
||||||
|
const projectKeys = await knex(TableName.ProjectKeys).where("projectId", projectId);
|
||||||
|
if (projectKeys.length) {
|
||||||
|
await knex.batchInsert(
|
||||||
|
TableName.ProjectKeys,
|
||||||
|
projectKeys.map((el) => {
|
||||||
|
const id = uuidV4();
|
||||||
|
return { ...el, id, projectId: newProjectId };
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const projectGateways = await knex(TableName.ProjectGateway).where("projectId", projectId);
|
||||||
|
if (projectGateways.length) {
|
||||||
|
await knex.batchInsert(
|
||||||
|
TableName.ProjectGateway,
|
||||||
|
projectGateways.map((el) => {
|
||||||
|
const id = uuidV4();
|
||||||
|
return { ...el, id, projectId: newProjectId };
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const projectSlackConfigs = await knex(TableName.ProjectSlackConfigs).where("projectId", projectId);
|
||||||
|
if (projectSlackConfigs.length) {
|
||||||
|
await knex.batchInsert(
|
||||||
|
TableName.ProjectSlackConfigs,
|
||||||
|
projectSlackConfigs.map((el) => {
|
||||||
|
const id = uuidV4();
|
||||||
|
return { ...el, id, projectId: newProjectId };
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const projectMicrosoftTeamsConfigs = await knex(TableName.ProjectMicrosoftTeamsConfigs).where("projectId", projectId);
|
||||||
|
if (projectMicrosoftTeamsConfigs.length) {
|
||||||
|
await knex.batchInsert(
|
||||||
|
TableName.ProjectMicrosoftTeamsConfigs,
|
||||||
|
projectMicrosoftTeamsConfigs.map((el) => {
|
||||||
|
const id = uuidV4();
|
||||||
|
return { ...el, id, projectId: newProjectId };
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const trustedIps = await knex(TableName.TrustedIps).where("projectId", projectId);
|
||||||
|
if (trustedIps.length) {
|
||||||
|
await knex.batchInsert(
|
||||||
|
TableName.TrustedIps,
|
||||||
|
trustedIps.map((el) => {
|
||||||
|
const id = uuidV4();
|
||||||
|
return { ...el, id, projectId: newProjectId };
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return newProjectId;
|
||||||
|
};
|
||||||
|
|
||||||
|
const kickOutSecretManagerProject = async (knex: Knex, oldProjectId: string) => {
|
||||||
|
const enviroments = await knex(TableName.Environment).where("projectId", oldProjectId).returning("id").first();
|
||||||
|
if (enviroments) {
|
||||||
|
const newProjectId = await newProject(knex, oldProjectId, ProjectType.SecretManager);
|
||||||
|
await knex(TableName.IntegrationAuth).where("projectId", oldProjectId).update("projectId", newProjectId);
|
||||||
|
await knex(TableName.ProjectBot).where("projectId", oldProjectId).update("projectId", newProjectId);
|
||||||
|
await knex(TableName.SecretBlindIndex).where("projectId", oldProjectId).update("projectId", newProjectId);
|
||||||
|
await knex(TableName.SecretSync).where("projectId", oldProjectId).update("projectId", newProjectId);
|
||||||
|
await knex(TableName.SecretTag).where("projectId", oldProjectId).update("projectId", newProjectId);
|
||||||
|
await knex(TableName.SecretReminderRecipients).where("projectId", oldProjectId).update("projectId", newProjectId);
|
||||||
|
await knex(TableName.ServiceToken).where("projectId", oldProjectId).update("projectId", newProjectId);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const kickOutCertManagerProject = async (knex: Knex, oldProjectId: string) => {
|
||||||
|
const cas = await knex(TableName.CertificateAuthority).where("projectId", oldProjectId).returning("id").first();
|
||||||
|
if (cas) {
|
||||||
|
const newProjectId = await newProject(knex, oldProjectId, ProjectType.CertificateManager);
|
||||||
|
await knex(TableName.CertificateAuthority).where("projectId", oldProjectId).update("projectId", newProjectId);
|
||||||
|
await knex(TableName.Certificate).where("projectId", oldProjectId).update("projectId", newProjectId);
|
||||||
|
await knex(TableName.PkiSubscriber).where("projectId", oldProjectId).update("projectId", newProjectId);
|
||||||
|
await knex(TableName.PkiCollection).where("projectId", oldProjectId).update("projectId", newProjectId);
|
||||||
|
await knex(TableName.PkiAlert).where("projectId", oldProjectId).update("projectId", newProjectId);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const kickOutSecretScanningProject = async (knex: Knex, oldProjectId: string) => {
|
||||||
|
const cas = await knex(TableName.SecretScanningConfig).where("projectId", oldProjectId).returning("id").first();
|
||||||
|
if (cas) {
|
||||||
|
const newProjectId = await newProject(knex, oldProjectId, ProjectType.SecretScanning);
|
||||||
|
await knex(TableName.SecretScanningConfig).where("projectId", oldProjectId).update("projectId", newProjectId);
|
||||||
|
await knex(TableName.SecretScanningDataSource).where("projectId", oldProjectId).update("projectId", newProjectId);
|
||||||
|
await knex(TableName.SecretScanningFinding).where("projectId", oldProjectId).update("projectId", newProjectId);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const kickOutKmsProject = async (knex: Knex, oldProjectId: string) => {
|
||||||
|
const kmsKeys = await knex(TableName.KmsKey)
|
||||||
|
.where("projectId", oldProjectId)
|
||||||
|
.andWhere("isReserved", false)
|
||||||
|
.returning("id")
|
||||||
|
.first();
|
||||||
|
if (kmsKeys) {
|
||||||
|
const newProjectId = await newProject(knex, oldProjectId, ProjectType.KMS);
|
||||||
|
await knex(TableName.KmsKey)
|
||||||
|
.where("projectId", oldProjectId)
|
||||||
|
.andWhere("isReserved", false)
|
||||||
|
.update("projectId", newProjectId);
|
||||||
|
await knex(TableName.KmipClient).where("projectId", oldProjectId).update("projectId", newProjectId);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const kickOutSshProject = async (knex: Knex, oldProjectId: string) => {
|
||||||
|
const hosts = await knex(TableName.ProjectSshConfig).where("projectId", oldProjectId).returning("id").first();
|
||||||
|
if (hosts) {
|
||||||
|
const newProjectId = await newProject(knex, oldProjectId, ProjectType.SSH);
|
||||||
|
await knex(TableName.SshHost).where("projectId", oldProjectId).update("projectId", newProjectId);
|
||||||
|
await knex(TableName.ProjectSshConfig).where("projectId", oldProjectId).update("projectId", newProjectId);
|
||||||
|
await knex(TableName.SshCertificateAuthority).where("projectId", oldProjectId).update("projectId", newProjectId);
|
||||||
|
await knex(TableName.SecretScanningFinding).where("projectId", oldProjectId).update("projectId", newProjectId);
|
||||||
|
await knex(TableName.SshHostGroup).where("projectId", oldProjectId).update("projectId", newProjectId);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const BATCH_SIZE = 1000;
|
||||||
export async function up(knex: Knex): Promise<void> {
|
export async function up(knex: Knex): Promise<void> {
|
||||||
|
const hasTemplateTypeColumn = await knex.schema.hasColumn(TableName.ProjectTemplates, "type");
|
||||||
|
if (hasTemplateTypeColumn) {
|
||||||
|
await knex.schema.alterTable(TableName.ProjectTemplates, (t) => {
|
||||||
|
t.string("type").notNullable().defaultTo(ProjectType.SecretManager).alter();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const hasTypeColumn = await knex.schema.hasColumn(TableName.Project, "type");
|
const hasTypeColumn = await knex.schema.hasColumn(TableName.Project, "type");
|
||||||
const hasDefaultTypeColumn = await knex.schema.hasColumn(TableName.Project, "defaultProduct");
|
const hasDefaultTypeColumn = await knex.schema.hasColumn(TableName.Project, "defaultProduct");
|
||||||
if (hasTypeColumn && hasDefaultTypeColumn) {
|
if (hasTypeColumn && hasDefaultTypeColumn) {
|
||||||
@@ -16,13 +283,27 @@ export async function up(knex: Knex): Promise<void> {
|
|||||||
t.string("type").notNullable().alter();
|
t.string("type").notNullable().alter();
|
||||||
t.string("defaultProduct").nullable().alter();
|
t.string("defaultProduct").nullable().alter();
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
const hasTemplateTypeColumn = await knex.schema.hasColumn(TableName.ProjectTemplates, "type");
|
let projectsToBeSplit;
|
||||||
if (hasTemplateTypeColumn) {
|
do {
|
||||||
await knex.schema.alterTable(TableName.ProjectTemplates, (t) => {
|
// eslint-disable-next-line no-await-in-loop
|
||||||
t.string("type").notNullable().defaultTo(ProjectType.SecretManager).alter();
|
projectsToBeSplit = await knex(TableName.Project)
|
||||||
});
|
.whereNotNull("defaultProduct")
|
||||||
|
.limit(BATCH_SIZE)
|
||||||
|
.select("id", "defaultProduct");
|
||||||
|
if (projectsToBeSplit.length) {
|
||||||
|
const ids: string[] = [];
|
||||||
|
for (const { id, defaultProduct } of projectsToBeSplit) {
|
||||||
|
if (defaultProduct !== ProjectType.SecretManager) await kickOutSecretManagerProject(knex, id);
|
||||||
|
if (defaultProduct !== ProjectType.CertificateManager) await kickOutCertManagerProject(knex, id);
|
||||||
|
if (defaultProduct !== ProjectType.KMS) await kickOutKmsProject(knex, id);
|
||||||
|
if (defaultProduct !== ProjectType.SSH) await kickOutSshProject(knex, id);
|
||||||
|
if (defaultProduct !== ProjectType.SecretScanning) await kickOutSecretScanningProject(knex, id);
|
||||||
|
ids.push(id);
|
||||||
|
}
|
||||||
|
await knex(TableName.Project).whereIn("id", ids).update("defaultProduct", null);
|
||||||
|
}
|
||||||
|
} while (projectsToBeTyped.length > 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user