mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-09-22 13:39:35 +00:00
Merge pull request #2584 from akhilmhdh/fix/upgrade-v1-to-v2
feat: added auto ghost user creation and fixed ghost user creation in v1
This commit is contained in:
@@ -493,6 +493,9 @@ export const registerRoutes = async (
|
||||
authDAL,
|
||||
userDAL
|
||||
});
|
||||
|
||||
const projectBotService = projectBotServiceFactory({ permissionService, projectBotDAL, projectDAL });
|
||||
|
||||
const orgService = orgServiceFactory({
|
||||
userAliasDAL,
|
||||
identityMetadataDAL,
|
||||
@@ -515,7 +518,8 @@ export const registerRoutes = async (
|
||||
userDAL,
|
||||
groupDAL,
|
||||
orgBotDAL,
|
||||
oidcConfigDAL
|
||||
oidcConfigDAL,
|
||||
projectBotService
|
||||
});
|
||||
const signupService = authSignupServiceFactory({
|
||||
tokenService,
|
||||
@@ -574,7 +578,6 @@ export const registerRoutes = async (
|
||||
secretScanningDAL,
|
||||
secretScanningQueue
|
||||
});
|
||||
const projectBotService = projectBotServiceFactory({ permissionService, projectBotDAL, projectDAL });
|
||||
|
||||
const projectMembershipService = projectMembershipServiceFactory({
|
||||
projectMembershipDAL,
|
||||
@@ -838,7 +841,10 @@ export const registerRoutes = async (
|
||||
integrationAuthDAL,
|
||||
snapshotDAL,
|
||||
snapshotSecretV2BridgeDAL,
|
||||
secretApprovalRequestDAL
|
||||
secretApprovalRequestDAL,
|
||||
projectKeyDAL,
|
||||
projectUserMembershipRoleDAL,
|
||||
orgService
|
||||
});
|
||||
const secretImportService = secretImportServiceFactory({
|
||||
licenseService,
|
||||
|
||||
@@ -41,8 +41,9 @@ import { TAuthTokenServiceFactory } from "../auth-token/auth-token-service";
|
||||
import { TokenType } from "../auth-token/auth-token-types";
|
||||
import { TIdentityMetadataDALFactory } from "../identity/identity-metadata-dal";
|
||||
import { TProjectDALFactory } from "../project/project-dal";
|
||||
import { assignWorkspaceKeysToMembers } from "../project/project-fns";
|
||||
import { assignWorkspaceKeysToMembers, createProjectKey } from "../project/project-fns";
|
||||
import { TProjectBotDALFactory } from "../project-bot/project-bot-dal";
|
||||
import { TProjectBotServiceFactory } from "../project-bot/project-bot-service";
|
||||
import { TProjectKeyDALFactory } from "../project-key/project-key-dal";
|
||||
import { TProjectMembershipDALFactory } from "../project-membership/project-membership-dal";
|
||||
import { TProjectUserMembershipRoleDALFactory } from "../project-membership/project-user-membership-role-dal";
|
||||
@@ -80,7 +81,7 @@ type TOrgServiceFactoryDep = {
|
||||
TProjectMembershipDALFactory,
|
||||
"findProjectMembershipsByUserId" | "delete" | "create" | "find" | "insertMany" | "transaction"
|
||||
>;
|
||||
projectKeyDAL: Pick<TProjectKeyDALFactory, "find" | "delete" | "insertMany" | "findLatestProjectKey">;
|
||||
projectKeyDAL: Pick<TProjectKeyDALFactory, "find" | "delete" | "insertMany" | "findLatestProjectKey" | "create">;
|
||||
orgMembershipDAL: Pick<TOrgMembershipDALFactory, "findOrgMembershipById" | "findOne" | "findById">;
|
||||
incidentContactDAL: TIncidentContactsDALFactory;
|
||||
samlConfigDAL: Pick<TSamlConfigDALFactory, "findOne" | "findEnforceableSamlCfg">;
|
||||
@@ -94,8 +95,9 @@ type TOrgServiceFactoryDep = {
|
||||
>;
|
||||
projectUserAdditionalPrivilegeDAL: Pick<TProjectUserAdditionalPrivilegeDALFactory, "delete">;
|
||||
projectRoleDAL: Pick<TProjectRoleDALFactory, "find">;
|
||||
projectBotDAL: Pick<TProjectBotDALFactory, "findOne">;
|
||||
projectUserMembershipRoleDAL: Pick<TProjectUserMembershipRoleDALFactory, "insertMany">;
|
||||
projectBotDAL: Pick<TProjectBotDALFactory, "findOne" | "updateById">;
|
||||
projectUserMembershipRoleDAL: Pick<TProjectUserMembershipRoleDALFactory, "insertMany" | "create">;
|
||||
projectBotService: Pick<TProjectBotServiceFactory, "getBotKey">;
|
||||
};
|
||||
|
||||
export type TOrgServiceFactory = ReturnType<typeof orgServiceFactory>;
|
||||
@@ -122,7 +124,8 @@ export const orgServiceFactory = ({
|
||||
oidcConfigDAL,
|
||||
projectBotDAL,
|
||||
projectUserMembershipRoleDAL,
|
||||
identityMetadataDAL
|
||||
identityMetadataDAL,
|
||||
projectBotService
|
||||
}: TOrgServiceFactoryDep) => {
|
||||
/*
|
||||
* Get organization details by the organization id
|
||||
@@ -718,20 +721,67 @@ export const orgServiceFactory = ({
|
||||
|
||||
const customRolesGroupBySlug = groupBy(customRoles, ({ slug }) => slug);
|
||||
|
||||
const ghostUser = await projectDAL.findProjectGhostUser(projectId, tx);
|
||||
if (!ghostUser) {
|
||||
throw new NotFoundError({
|
||||
name: "InviteUser",
|
||||
message: "Failed to find project owner"
|
||||
});
|
||||
}
|
||||
// this will auto generate bot
|
||||
const { botKey, bot: autoGeneratedBot } = await projectBotService.getBotKey(projectId, true);
|
||||
|
||||
const ghostUserLatestKey = await projectKeyDAL.findLatestProjectKey(ghostUser.id, projectId, tx);
|
||||
if (!ghostUserLatestKey) {
|
||||
throw new NotFoundError({
|
||||
name: "InviteUser",
|
||||
message: "Failed to find project owner's latest key"
|
||||
const ghostUser = await projectDAL.findProjectGhostUser(projectId, tx);
|
||||
let ghostUserId = ghostUser?.id;
|
||||
|
||||
// backfill missing ghost user
|
||||
if (!ghostUserId) {
|
||||
const newGhostUser = await addGhostUser(project.orgId, tx);
|
||||
const projectMembership = await projectMembershipDAL.create(
|
||||
{
|
||||
userId: newGhostUser.user.id,
|
||||
projectId: project.id
|
||||
},
|
||||
tx
|
||||
);
|
||||
await projectUserMembershipRoleDAL.create(
|
||||
{ projectMembershipId: projectMembership.id, role: ProjectMembershipRole.Admin },
|
||||
tx
|
||||
);
|
||||
|
||||
const { key: encryptedProjectKey, iv: encryptedProjectKeyIv } = createProjectKey({
|
||||
publicKey: newGhostUser.keys.publicKey,
|
||||
privateKey: newGhostUser.keys.plainPrivateKey,
|
||||
plainProjectKey: botKey
|
||||
});
|
||||
|
||||
// 4. Save the project key for the ghost user.
|
||||
await projectKeyDAL.create(
|
||||
{
|
||||
projectId: project.id,
|
||||
receiverId: newGhostUser.user.id,
|
||||
encryptedKey: encryptedProjectKey,
|
||||
nonce: encryptedProjectKeyIv,
|
||||
senderId: newGhostUser.user.id
|
||||
},
|
||||
tx
|
||||
);
|
||||
|
||||
const { iv, tag, ciphertext, encoding, algorithm } = infisicalSymmetricEncypt(
|
||||
newGhostUser.keys.plainPrivateKey
|
||||
);
|
||||
if (autoGeneratedBot) {
|
||||
await projectBotDAL.updateById(
|
||||
autoGeneratedBot.id,
|
||||
{
|
||||
tag,
|
||||
iv,
|
||||
encryptedProjectKey,
|
||||
encryptedProjectKeyNonce: encryptedProjectKeyIv,
|
||||
encryptedPrivateKey: ciphertext,
|
||||
isActive: true,
|
||||
publicKey: newGhostUser.keys.publicKey,
|
||||
senderId: newGhostUser.user.id,
|
||||
algorithm,
|
||||
keyEncoding: encoding
|
||||
},
|
||||
tx
|
||||
);
|
||||
}
|
||||
ghostUserId = newGhostUser.user.id;
|
||||
}
|
||||
|
||||
const bot = await projectBotDAL.findOne({ projectId }, tx);
|
||||
@@ -742,6 +792,14 @@ export const orgServiceFactory = ({
|
||||
});
|
||||
}
|
||||
|
||||
const ghostUserLatestKey = await projectKeyDAL.findLatestProjectKey(ghostUserId, projectId, tx);
|
||||
if (!ghostUserLatestKey) {
|
||||
throw new NotFoundError({
|
||||
name: "InviteUser",
|
||||
message: "Failed to find project owner's latest key"
|
||||
});
|
||||
}
|
||||
|
||||
const botPrivateKey = infisicalSymmetricDecrypt({
|
||||
keyEncoding: bot.keyEncoding as SecretKeyEncoding,
|
||||
iv: bot.iv,
|
||||
@@ -785,7 +843,7 @@ export const orgServiceFactory = ({
|
||||
newWsMembers.map((el) => ({
|
||||
encryptedKey: el.workspaceEncryptedKey,
|
||||
nonce: el.workspaceEncryptedNonce,
|
||||
senderId: ghostUser.id,
|
||||
senderId: ghostUserId,
|
||||
receiverId: el.orgMembershipId,
|
||||
projectId
|
||||
})),
|
||||
|
||||
@@ -24,14 +24,14 @@ export const getBotKeyFnFactory = (
|
||||
projectBotDAL: TProjectBotDALFactory,
|
||||
projectDAL: Pick<TProjectDALFactory, "findById">
|
||||
) => {
|
||||
const getBotKeyFn = async (projectId: string) => {
|
||||
const getBotKeyFn = async (projectId: string, shouldGetBotKey?: boolean) => {
|
||||
const project = await projectDAL.findById(projectId);
|
||||
if (!project)
|
||||
throw new NotFoundError({
|
||||
message: "Project not found during bot lookup. Are you sure you are using the correct project ID?"
|
||||
});
|
||||
|
||||
if (project.version === 3) {
|
||||
if (project.version === 3 && !shouldGetBotKey) {
|
||||
return { project, shouldUseSecretV2Bridge: true };
|
||||
}
|
||||
|
||||
@@ -65,8 +65,9 @@ export const getBotKeyFnFactory = (
|
||||
const { iv, tag, ciphertext, encoding, algorithm } = infisicalSymmetricEncypt(botKey.privateKey);
|
||||
const encryptedWorkspaceKey = encryptAsymmetric(workspaceKey, botKey.publicKey, userPrivateKey);
|
||||
|
||||
let botId;
|
||||
if (!bot) {
|
||||
await projectBotDAL.create({
|
||||
const newBot = await projectBotDAL.create({
|
||||
name: "Infisical Bot (Ghost)",
|
||||
projectId,
|
||||
isActive: true,
|
||||
@@ -80,8 +81,9 @@ export const getBotKeyFnFactory = (
|
||||
encryptedProjectKeyNonce: encryptedWorkspaceKey.nonce,
|
||||
senderId: projectV1Keys.userId
|
||||
});
|
||||
botId = newBot.id;
|
||||
} else {
|
||||
await projectBotDAL.updateById(bot.id, {
|
||||
const updatedBot = await projectBotDAL.updateById(bot.id, {
|
||||
isActive: true,
|
||||
tag,
|
||||
iv,
|
||||
@@ -93,8 +95,10 @@ export const getBotKeyFnFactory = (
|
||||
encryptedProjectKeyNonce: encryptedWorkspaceKey.nonce,
|
||||
senderId: projectV1Keys.userId
|
||||
});
|
||||
botId = updatedBot.id;
|
||||
}
|
||||
return { botKey: workspaceKey, project, shouldUseSecretV2Bridge: false };
|
||||
|
||||
return { botKey: workspaceKey, project, shouldUseSecretV2Bridge: false, bot: { id: botId } };
|
||||
}
|
||||
|
||||
const botPrivateKey = getBotPrivateKey({ bot });
|
||||
@@ -104,7 +108,7 @@ export const getBotKeyFnFactory = (
|
||||
nonce: bot.encryptedProjectKeyNonce,
|
||||
publicKey: bot.sender.publicKey
|
||||
});
|
||||
return { botKey, project, shouldUseSecretV2Bridge: false };
|
||||
return { botKey, project, shouldUseSecretV2Bridge: false, bot: { id: bot.id } };
|
||||
};
|
||||
|
||||
return getBotKeyFn;
|
||||
|
||||
@@ -27,8 +27,8 @@ export const projectBotServiceFactory = ({
|
||||
}: TProjectBotServiceFactoryDep) => {
|
||||
const getBotKeyFn = getBotKeyFnFactory(projectBotDAL, projectDAL);
|
||||
|
||||
const getBotKey = async (projectId: string) => {
|
||||
return getBotKeyFn(projectId);
|
||||
const getBotKey = async (projectId: string, shouldGetBotKey?: boolean) => {
|
||||
return getBotKeyFn(projectId, shouldGetBotKey);
|
||||
};
|
||||
|
||||
const findBotByProjectId = async ({
|
||||
|
||||
@@ -17,6 +17,7 @@ import { TSnapshotSecretV2DALFactory } from "@app/ee/services/secret-snapshot/sn
|
||||
import { KeyStorePrefixes, KeyStoreTtls, TKeyStoreFactory } from "@app/keystore/keystore";
|
||||
import { getConfig } from "@app/lib/config/env";
|
||||
import { decryptSymmetric128BitHexKeyUTF8 } from "@app/lib/crypto";
|
||||
import { infisicalSymmetricEncypt } from "@app/lib/crypto/encryption";
|
||||
import { daysToMillisecond, secondsToMillis } from "@app/lib/dates";
|
||||
import { BadRequestError, NotFoundError } from "@app/lib/errors";
|
||||
import { getTimeDifferenceInSeconds, groupBy, isSamePath, unique } from "@app/lib/fn";
|
||||
@@ -37,10 +38,14 @@ import { syncIntegrationSecrets } from "../integration-auth/integration-sync-sec
|
||||
import { TKmsServiceFactory } from "../kms/kms-service";
|
||||
import { KmsDataKey } from "../kms/kms-types";
|
||||
import { TOrgDALFactory } from "../org/org-dal";
|
||||
import { TOrgServiceFactory } from "../org/org-service";
|
||||
import { TProjectDALFactory } from "../project/project-dal";
|
||||
import { createProjectKey } from "../project/project-fns";
|
||||
import { TProjectBotServiceFactory } from "../project-bot/project-bot-service";
|
||||
import { TProjectEnvDALFactory } from "../project-env/project-env-dal";
|
||||
import { TProjectKeyDALFactory } from "../project-key/project-key-dal";
|
||||
import { TProjectMembershipDALFactory } from "../project-membership/project-membership-dal";
|
||||
import { TProjectUserMembershipRoleDALFactory } from "../project-membership/project-user-membership-role-dal";
|
||||
import { TSecretFolderDALFactory } from "../secret-folder/secret-folder-dal";
|
||||
import { TSecretImportDALFactory } from "../secret-import/secret-import-dal";
|
||||
import { fnSecretsV2FromImports } from "../secret-import/secret-import-fns";
|
||||
@@ -77,7 +82,8 @@ type TSecretQueueFactoryDep = {
|
||||
projectEnvDAL: Pick<TProjectEnvDALFactory, "findOne" | "find">;
|
||||
projectDAL: TProjectDALFactory;
|
||||
projectBotDAL: TProjectBotDALFactory;
|
||||
projectMembershipDAL: Pick<TProjectMembershipDALFactory, "findAllProjectMembers">;
|
||||
projectKeyDAL: Pick<TProjectKeyDALFactory, "create">;
|
||||
projectMembershipDAL: Pick<TProjectMembershipDALFactory, "findAllProjectMembers" | "create">;
|
||||
smtpService: TSmtpService;
|
||||
orgDAL: Pick<TOrgDALFactory, "findOrgByProjectId">;
|
||||
secretVersionDAL: TSecretVersionDALFactory;
|
||||
@@ -95,6 +101,8 @@ type TSecretQueueFactoryDep = {
|
||||
snapshotSecretV2BridgeDAL: Pick<TSnapshotSecretV2DALFactory, "insertMany" | "batchInsert">;
|
||||
keyStore: Pick<TKeyStoreFactory, "acquireLock" | "setItemWithExpiry" | "getItem">;
|
||||
auditLogService: Pick<TAuditLogServiceFactory, "createAuditLog">;
|
||||
orgService: Pick<TOrgServiceFactory, "addGhostUser">;
|
||||
projectUserMembershipRoleDAL: Pick<TProjectUserMembershipRoleDALFactory, "create">;
|
||||
};
|
||||
|
||||
export type TGetSecrets = {
|
||||
@@ -111,6 +119,8 @@ type TIntegrationSecret = Record<
|
||||
string,
|
||||
{ value: string; comment?: string; skipMultilineEncoding?: boolean | null | undefined }
|
||||
>;
|
||||
|
||||
// TODO(akhilmhdh): split this into multiple queue
|
||||
export const secretQueueFactory = ({
|
||||
queueService,
|
||||
integrationDAL,
|
||||
@@ -141,7 +151,10 @@ export const secretQueueFactory = ({
|
||||
snapshotSecretV2BridgeDAL,
|
||||
secretApprovalRequestDAL,
|
||||
keyStore,
|
||||
auditLogService
|
||||
auditLogService,
|
||||
orgService,
|
||||
projectUserMembershipRoleDAL,
|
||||
projectKeyDAL
|
||||
}: TSecretQueueFactoryDep) => {
|
||||
const removeSecretReminder = async (dto: TRemoveSecretReminderDTO) => {
|
||||
const appCfg = getConfig();
|
||||
@@ -1028,11 +1041,13 @@ export const secretQueueFactory = ({
|
||||
const {
|
||||
botKey,
|
||||
shouldUseSecretV2Bridge: isProjectUpgradedToV3,
|
||||
project
|
||||
project,
|
||||
bot
|
||||
} = await projectBotService.getBotKey(projectId);
|
||||
if (isProjectUpgradedToV3 || project.upgradeStatus === ProjectUpgradeStatus.InProgress) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!botKey) throw new NotFoundError({ message: "Project bot not found" });
|
||||
await projectDAL.updateById(projectId, { upgradeStatus: ProjectUpgradeStatus.InProgress });
|
||||
|
||||
@@ -1044,6 +1059,57 @@ export const secretQueueFactory = ({
|
||||
const folders = await folderDAL.findByProjectId(projectId);
|
||||
// except secret version and snapshot migrate rest of everything first in a transaction
|
||||
await secretDAL.transaction(async (tx) => {
|
||||
// if project v1 create the project ghost user
|
||||
if (project.version === ProjectVersion.V1) {
|
||||
const ghostUser = await orgService.addGhostUser(project.orgId, tx);
|
||||
const projectMembership = await projectMembershipDAL.create(
|
||||
{
|
||||
userId: ghostUser.user.id,
|
||||
projectId: project.id
|
||||
},
|
||||
tx
|
||||
);
|
||||
await projectUserMembershipRoleDAL.create(
|
||||
{ projectMembershipId: projectMembership.id, role: ProjectMembershipRole.Admin },
|
||||
tx
|
||||
);
|
||||
|
||||
const { key: encryptedProjectKey, iv: encryptedProjectKeyIv } = createProjectKey({
|
||||
publicKey: ghostUser.keys.publicKey,
|
||||
privateKey: ghostUser.keys.plainPrivateKey,
|
||||
plainProjectKey: botKey
|
||||
});
|
||||
|
||||
// 4. Save the project key for the ghost user.
|
||||
await projectKeyDAL.create(
|
||||
{
|
||||
projectId: project.id,
|
||||
receiverId: ghostUser.user.id,
|
||||
encryptedKey: encryptedProjectKey,
|
||||
nonce: encryptedProjectKeyIv,
|
||||
senderId: ghostUser.user.id
|
||||
},
|
||||
tx
|
||||
);
|
||||
const { iv, tag, ciphertext, encoding, algorithm } = infisicalSymmetricEncypt(ghostUser.keys.plainPrivateKey);
|
||||
await projectBotDAL.updateById(
|
||||
bot.id,
|
||||
{
|
||||
tag,
|
||||
iv,
|
||||
encryptedProjectKey,
|
||||
encryptedProjectKeyNonce: encryptedProjectKeyIv,
|
||||
encryptedPrivateKey: ciphertext,
|
||||
isActive: true,
|
||||
publicKey: ghostUser.keys.publicKey,
|
||||
senderId: ghostUser.user.id,
|
||||
algorithm,
|
||||
keyEncoding: encoding
|
||||
},
|
||||
tx
|
||||
);
|
||||
}
|
||||
|
||||
for (const folder of folders) {
|
||||
const folderId = folder.id;
|
||||
/*
|
||||
|
||||
Reference in New Issue
Block a user