From b37f780c4c9f89688f87539278a59b0db65426be Mon Sep 17 00:00:00 2001 From: = Date: Thu, 25 Jul 2024 22:25:59 +0530 Subject: [PATCH] feat: added auto bot creator when bot is missing by taking the user old server encrypted private key --- .../services/project-bot/project-bot-dal.ts | 42 +++++++++++- .../services/project-bot/project-bot-fns.ts | 66 +++++++++++++++++-- .../project-bot/project-bot-service.ts | 2 +- 3 files changed, 102 insertions(+), 8 deletions(-) diff --git a/backend/src/services/project-bot/project-bot-dal.ts b/backend/src/services/project-bot/project-bot-dal.ts index e25ebbd09..81c177d21 100644 --- a/backend/src/services/project-bot/project-bot-dal.ts +++ b/backend/src/services/project-bot/project-bot-dal.ts @@ -1,7 +1,7 @@ import { Knex } from "knex"; import { TDbClient } from "@app/db"; -import { TableName, TProjectBots } from "@app/db/schemas"; +import { TableName, TProjectBots, TUserEncryptionKeys } from "@app/db/schemas"; import { DatabaseError } from "@app/lib/errors"; import { ormify, selectAllTableCols } from "@app/lib/knex"; @@ -41,5 +41,43 @@ export const projectBotDALFactory = (db: TDbClient) => { } }; - return { ...projectBotOrm, findOne, findProjectByBotId }; + const findProjectUserWorkspaceKey = async (projectId: string) => { + try { + const doc = await db + .replicaNode()(TableName.ProjectMembership) + .where(`${TableName.ProjectMembership}.projectId` as "projectId", projectId) + .where(`${TableName.Users}.isGhost` as "isGhost", false) + .join(TableName.Users, `${TableName.ProjectMembership}.userId`, `${TableName.Users}.id`) + .join(TableName.ProjectKeys, `${TableName.ProjectMembership}.userId`, `${TableName.ProjectKeys}.receiverId`) + .join( + TableName.UserEncryptionKey, + `${TableName.UserEncryptionKey}.userId`, + `${TableName.Users}.id` + ) + .join( + db(TableName.UserEncryptionKey).as("senderUserEncryption"), + `${TableName.ProjectKeys}.senderId`, + `senderUserEncryption.userId` + ) + .whereNotNull(`${TableName.UserEncryptionKey}.serverEncryptedPrivateKey`) + .whereNotNull(`${TableName.UserEncryptionKey}.serverEncryptedPrivateKeyIV`) + .whereNotNull(`${TableName.UserEncryptionKey}.serverEncryptedPrivateKeyTag`) + .select( + db.ref("serverEncryptedPrivateKey").withSchema(TableName.UserEncryptionKey), + db.ref("serverEncryptedPrivateKeyTag").withSchema(TableName.UserEncryptionKey), + db.ref("serverEncryptedPrivateKeyIV").withSchema(TableName.UserEncryptionKey), + db.ref("serverEncryptedPrivateKeyEncoding").withSchema(TableName.UserEncryptionKey), + db.ref("encryptedKey").withSchema(TableName.ProjectKeys).as("projectEncryptedKey"), + db.ref("nonce").withSchema(TableName.ProjectKeys).as("projectKeyNonce"), + db.ref("publicKey").withSchema("senderUserEncryption").as("senderPublicKey"), + db.ref("id").withSchema(TableName.Users).as("userId") + ) + .first(); + return doc; + } catch (error) { + throw new DatabaseError({ error, name: "Find all project members" }); + } + }; + + return { ...projectBotOrm, findOne, findProjectByBotId, findProjectUserWorkspaceKey }; }; diff --git a/backend/src/services/project-bot/project-bot-fns.ts b/backend/src/services/project-bot/project-bot-fns.ts index 9269c2936..59d0493ac 100644 --- a/backend/src/services/project-bot/project-bot-fns.ts +++ b/backend/src/services/project-bot/project-bot-fns.ts @@ -1,5 +1,11 @@ import { SecretKeyEncoding } from "@app/db/schemas"; -import { decryptAsymmetric, infisicalSymmetricDecrypt } from "@app/lib/crypto/encryption"; +import { + decryptAsymmetric, + encryptAsymmetric, + generateAsymmetricKeyPair, + infisicalSymmetricDecrypt, + infisicalSymmetricEncypt +} from "@app/lib/crypto/encryption"; import { BadRequestError } from "@app/lib/errors"; import { TProjectBotDALFactory } from "@app/services/project-bot/project-bot-dal"; @@ -27,11 +33,61 @@ export const getBotKeyFnFactory = ( } const bot = await projectBotDAL.findOne({ projectId: project.id }); + if (!bot || !bot.isActive || !bot.encryptedProjectKey || !bot.encryptedProjectKeyNonce) { + // trying to set bot automatically + const projectV1Keys = await projectBotDAL.findProjectUserWorkspaceKey(projectId); + if (!projectV1Keys) + throw new BadRequestError({ message: "Bot not found. [no-private-key]. Please ask admin user to login" }); - if (!bot) throw new BadRequestError({ message: "Failed to find bot key", name: "bot_not_found_error" }); - if (!bot.isActive) throw new BadRequestError({ message: "Bot is not active", name: "bot_not_found_error" }); - if (!bot.encryptedProjectKeyNonce || !bot.encryptedProjectKey) - throw new BadRequestError({ message: "Encryption key missing", name: "bot_not_found_error" }); + let userPrivateKey = ""; + if ( + projectV1Keys?.serverEncryptedPrivateKey && + projectV1Keys.serverEncryptedPrivateKeyIV && + projectV1Keys.serverEncryptedPrivateKeyTag && + projectV1Keys.serverEncryptedPrivateKeyEncoding + ) { + userPrivateKey = infisicalSymmetricDecrypt({ + iv: projectV1Keys.serverEncryptedPrivateKeyIV, + tag: projectV1Keys.serverEncryptedPrivateKeyTag, + ciphertext: projectV1Keys.serverEncryptedPrivateKey, + keyEncoding: projectV1Keys.serverEncryptedPrivateKeyEncoding as SecretKeyEncoding + }); + } + const workspaceKey = decryptAsymmetric({ + ciphertext: projectV1Keys.projectEncryptedKey, + nonce: projectV1Keys.projectKeyNonce, + publicKey: projectV1Keys.senderPublicKey, + privateKey: userPrivateKey + }); + const botKey = generateAsymmetricKeyPair(); + const { iv, tag, ciphertext, encoding, algorithm } = infisicalSymmetricEncypt(botKey.privateKey); + const encryptedWorkspaceKey = encryptAsymmetric(workspaceKey, botKey.publicKey, userPrivateKey); + + if (!bot) { + await projectBotDAL.create({ + name: "Infisical Bot (Ghost)", + projectId, + tag, + iv, + encryptedPrivateKey: ciphertext, + isActive: true, + publicKey: botKey.publicKey, + algorithm, + keyEncoding: encoding, + encryptedProjectKey: encryptedWorkspaceKey.ciphertext, + encryptedProjectKeyNonce: encryptedWorkspaceKey.nonce, + senderId: projectV1Keys.userId + }); + } else { + await projectBotDAL.updateById(bot.id, { + isActive: true, + encryptedProjectKey: encryptedWorkspaceKey.ciphertext, + encryptedProjectKeyNonce: encryptedWorkspaceKey.nonce, + senderId: projectV1Keys.userId + }); + } + return { botKey: workspaceKey, project, shouldUseSecretV2Bridge: false }; + } const botPrivateKey = getBotPrivateKey({ bot }); diff --git a/backend/src/services/project-bot/project-bot-service.ts b/backend/src/services/project-bot/project-bot-service.ts index ce7782a80..1df29e6b7 100644 --- a/backend/src/services/project-bot/project-bot-service.ts +++ b/backend/src/services/project-bot/project-bot-service.ts @@ -60,7 +60,7 @@ export const projectBotServiceFactory = ({ const project = await projectDAL.findById(projectId, tx); - if (project.version === ProjectVersion.V2) { + if (project.version === ProjectVersion.V2 || project.version === ProjectVersion.V3) { throw new BadRequestError({ message: "Failed to create bot, project is upgraded." }); }