feat: testing v2 architecture changes and corrections as needed

This commit is contained in:
=
2024-07-30 23:19:06 +05:30
parent b7b0e60b1d
commit 4a06e3e712
13 changed files with 83 additions and 151 deletions
@@ -128,7 +128,7 @@ export async function up(knex: Knex): Promise<void> {
if (!hasEncryptedAccess) t.binary("encryptedAccess"); if (!hasEncryptedAccess) t.binary("encryptedAccess");
if (!hasEncryptedAccessId) t.binary("encryptedAccessId"); if (!hasEncryptedAccessId) t.binary("encryptedAccessId");
if (!hasEncryptedRefresh) t.binary("encryptedRefresh"); if (!hasEncryptedRefresh) t.binary("encryptedRefresh");
if (!hasEncryptedAwsIamAssumRole) t.binary("hasEncryptedAwsIamAssumRole"); if (!hasEncryptedAwsIamAssumRole) t.binary("encryptedAwsIamAssumRole");
}); });
} }
} }
@@ -160,7 +160,7 @@ export async function down(knex: Knex): Promise<void> {
if (hasEncryptedAccess) t.dropColumn("encryptedAccess"); if (hasEncryptedAccess) t.dropColumn("encryptedAccess");
if (hasEncryptedAccessId) t.dropColumn("encryptedAccessId"); if (hasEncryptedAccessId) t.dropColumn("encryptedAccessId");
if (hasEncryptedRefresh) t.dropColumn("encryptedRefresh"); if (hasEncryptedRefresh) t.dropColumn("encryptedRefresh");
if (hasEncryptedAwsIamAssumRole) t.dropColumn("hasEncryptedAwsIamAssumRole"); if (hasEncryptedAwsIamAssumRole) t.dropColumn("encryptedAwsIamAssumRole");
}); });
} }
} }
+1 -1
View File
@@ -38,7 +38,7 @@ export const IntegrationAuthsSchema = z.object({
encryptedAccess: zodBuffer.nullable().optional(), encryptedAccess: zodBuffer.nullable().optional(),
encryptedAccessId: zodBuffer.nullable().optional(), encryptedAccessId: zodBuffer.nullable().optional(),
encryptedRefresh: zodBuffer.nullable().optional(), encryptedRefresh: zodBuffer.nullable().optional(),
hasEncryptedAwsIamAssumRole: zodBuffer.nullable().optional() encryptedAwsIamAssumRole: zodBuffer.nullable().optional()
}); });
export type TIntegrationAuths = z.infer<typeof IntegrationAuthsSchema>; export type TIntegrationAuths = z.infer<typeof IntegrationAuthsSchema>;
@@ -12,6 +12,7 @@ import { getConfig } from "@app/lib/config/env";
import { decryptSymmetric128BitHexKeyUTF8 } from "@app/lib/crypto"; import { decryptSymmetric128BitHexKeyUTF8 } from "@app/lib/crypto";
import { BadRequestError, UnauthorizedError } from "@app/lib/errors"; import { BadRequestError, UnauthorizedError } from "@app/lib/errors";
import { groupBy, pick, unique } from "@app/lib/fn"; import { groupBy, pick, unique } from "@app/lib/fn";
import { setKnexStringValue } from "@app/lib/knex";
import { alphaNumericNanoId } from "@app/lib/nanoid"; import { alphaNumericNanoId } from "@app/lib/nanoid";
import { EnforcementLevel } from "@app/lib/types"; import { EnforcementLevel } from "@app/lib/types";
import { ActorType } from "@app/services/auth/auth-type"; import { ActorType } from "@app/services/auth/auth-type";
@@ -43,8 +44,7 @@ import {
fnSecretBulkDelete as fnSecretV2BridgeBulkDelete, fnSecretBulkDelete as fnSecretV2BridgeBulkDelete,
fnSecretBulkInsert as fnSecretV2BridgeBulkInsert, fnSecretBulkInsert as fnSecretV2BridgeBulkInsert,
fnSecretBulkUpdate as fnSecretV2BridgeBulkUpdate, fnSecretBulkUpdate as fnSecretV2BridgeBulkUpdate,
getAllNestedSecretReferences as getAllNestedSecretReferencesV2Bridge, getAllNestedSecretReferences as getAllNestedSecretReferencesV2Bridge
secretEncryptionHelper
} from "@app/services/secret-v2-bridge/secret-v2-bridge-fns"; } from "@app/services/secret-v2-bridge/secret-v2-bridge-fns";
import { TSecretVersionV2DALFactory } from "@app/services/secret-v2-bridge/secret-version-dal"; import { TSecretVersionV2DALFactory } from "@app/services/secret-v2-bridge/secret-version-dal";
import { TSecretVersionV2TagDALFactory } from "@app/services/secret-v2-bridge/secret-version-tag-dal"; import { TSecretVersionV2TagDALFactory } from "@app/services/secret-v2-bridge/secret-version-tag-dal";
@@ -1086,8 +1086,14 @@ export const secretApprovalRequestServiceFactory = ({
...createdSecrets.map((createdSecret) => ({ ...createdSecrets.map((createdSecret) => ({
op: SecretOperations.Create, op: SecretOperations.Create,
version: 1, version: 1,
encryptedComment: secretEncryptionHelper.encryptValue(secretManagerEncryptor, createdSecret.secretComment), encryptedComment: setKnexStringValue(
encryptedValue: secretEncryptionHelper.encryptValue(secretManagerEncryptor, createdSecret.secretValue), createdSecret.secretComment,
(value) => secretManagerEncryptor({ plainText: Buffer.from(value) }).cipherTextBlob
),
encryptedValue: setKnexStringValue(
createdSecret.secretValue,
(value) => secretManagerEncryptor({ plainText: Buffer.from(value) }).cipherTextBlob
),
skipMultilineEncoding: createdSecret.skipMultilineEncoding, skipMultilineEncoding: createdSecret.skipMultilineEncoding,
key: createdSecret.secretKey, key: createdSecret.secretKey,
type: SecretType.Shared type: SecretType.Shared
@@ -1152,8 +1158,14 @@ export const secretApprovalRequestServiceFactory = ({
return { return {
...latestSecretVersions[secretId], ...latestSecretVersions[secretId],
key: newSecretName || secretKey, key: newSecretName || secretKey,
encryptedValue: secretEncryptionHelper.encryptValue(secretManagerEncryptor, secretValue) as Buffer, encryptedComment: setKnexStringValue(
encryptedComment: secretEncryptionHelper.encryptValue(secretManagerEncryptor, secretComment) as Buffer, secretComment,
(value) => secretManagerEncryptor({ plainText: Buffer.from(value) }).cipherTextBlob
),
encryptedValue: setKnexStringValue(
secretValue,
(value) => secretManagerEncryptor({ plainText: Buffer.from(value) }).cipherTextBlob
),
reminderRepeatDays, reminderRepeatDays,
reminderNote, reminderNote,
metadata, metadata,
+9
View File
@@ -12,3 +12,12 @@ export const stripUndefinedInWhere = <T extends object>(val: T): Exclude<T, unde
}); });
return copy as Exclude<T, undefined>; return copy as Exclude<T, undefined>;
}; };
// if its undefined its skipped in knex
// if its empty string its set as null
// else pass to the required one
export const setKnexStringValue = <T>(value: string | null | undefined, cb: (arg: string) => T) => {
if (typeof value === "undefined") return;
if (value === "" || value === null) return null;
return cb(value);
};
-1
View File
@@ -694,7 +694,6 @@ export const registerRoutes = async (
integrationAuthDAL, integrationAuthDAL,
integrationDAL, integrationDAL,
permissionService, permissionService,
projectBotDAL,
projectBotService, projectBotService,
kmsService kmsService
}); });
@@ -13,7 +13,6 @@ import { TProjectPermission } from "@app/lib/types";
import { TIntegrationDALFactory } from "../integration/integration-dal"; import { TIntegrationDALFactory } from "../integration/integration-dal";
import { TKmsServiceFactory } from "../kms/kms-service"; import { TKmsServiceFactory } from "../kms/kms-service";
import { KmsDataKey } from "../kms/kms-types"; import { KmsDataKey } from "../kms/kms-types";
import { TProjectBotDALFactory } from "../project-bot/project-bot-dal";
import { TProjectBotServiceFactory } from "../project-bot/project-bot-service"; import { TProjectBotServiceFactory } from "../project-bot/project-bot-service";
import { getApps } from "./integration-app-list"; import { getApps } from "./integration-app-list";
import { TIntegrationAuthDALFactory } from "./integration-auth-dal"; import { TIntegrationAuthDALFactory } from "./integration-auth-dal";
@@ -55,7 +54,6 @@ type TIntegrationAuthServiceFactoryDep = {
integrationAuthDAL: TIntegrationAuthDALFactory; integrationAuthDAL: TIntegrationAuthDALFactory;
integrationDAL: Pick<TIntegrationDALFactory, "delete">; integrationDAL: Pick<TIntegrationDALFactory, "delete">;
projectBotService: Pick<TProjectBotServiceFactory, "getBotKey">; projectBotService: Pick<TProjectBotServiceFactory, "getBotKey">;
projectBotDAL: Pick<TProjectBotDALFactory, "findOne">;
permissionService: Pick<TPermissionServiceFactory, "getProjectPermission">; permissionService: Pick<TPermissionServiceFactory, "getProjectPermission">;
kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey">; kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey">;
}; };
@@ -66,7 +64,6 @@ export const integrationAuthServiceFactory = ({
permissionService, permissionService,
integrationAuthDAL, integrationAuthDAL,
integrationDAL, integrationDAL,
projectBotDAL,
projectBotService, projectBotService,
kmsService kmsService
}: TIntegrationAuthServiceFactoryDep) => { }: TIntegrationAuthServiceFactoryDep) => {
@@ -126,9 +123,6 @@ export const integrationAuthServiceFactory = ({
); );
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.Integrations); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.Integrations);
const bot = await projectBotDAL.findOne({ isActive: true, projectId });
if (!bot) throw new BadRequestError({ message: "Bot must be enabled for oauth2 code token exchange" });
const tokenExchange = await exchangeCode({ integration, code, url }); const tokenExchange = await exchangeCode({ integration, code, url });
const updateDoc: TIntegrationAuthsInsert = { const updateDoc: TIntegrationAuthsInsert = {
projectId, projectId,
@@ -217,9 +211,6 @@ export const integrationAuthServiceFactory = ({
); );
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.Integrations); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.Integrations);
const bot = await projectBotDAL.findOne({ isActive: true, projectId });
if (!bot) throw new BadRequestError({ message: "Bot must be enabled for oauth2 code token exchange" });
const updateDoc: TIntegrationAuthsInsert = { const updateDoc: TIntegrationAuthsInsert = {
projectId, projectId,
namespace, namespace,
@@ -278,7 +269,7 @@ export const integrationAuthServiceFactory = ({
const awsAssumeIamRoleArnEncrypted = secretManagerEncryptor({ const awsAssumeIamRoleArnEncrypted = secretManagerEncryptor({
plainText: Buffer.from(awsAssumeIamRoleArn) plainText: Buffer.from(awsAssumeIamRoleArn)
}).cipherTextBlob; }).cipherTextBlob;
updateDoc.hasEncryptedAwsIamAssumRole = awsAssumeIamRoleArnEncrypted; updateDoc.encryptedAwsIamAssumRole = awsAssumeIamRoleArnEncrypted;
} }
} }
} else { } else {
@@ -338,7 +329,7 @@ export const integrationAuthServiceFactory = ({
if ( if (
integrationAuth.integration === Integrations.AWS_SECRET_MANAGER && integrationAuth.integration === Integrations.AWS_SECRET_MANAGER &&
(shouldUseSecretV2Bridge (shouldUseSecretV2Bridge
? integrationAuth.hasEncryptedAwsIamAssumRole ? integrationAuth.encryptedAwsIamAssumRole
: integrationAuth.awsAssumeIamRoleArnCipherText) : integrationAuth.awsAssumeIamRoleArnCipherText)
) { ) {
return { accessToken: "", accessId: "" }; return { accessToken: "", accessId: "" };
@@ -123,7 +123,11 @@ export const integrationDALFactory = (db: TDbClient) => {
db.ref("keyEncoding").withSchema(TableName.IntegrationAuth).as("keyEncodingAu"), db.ref("keyEncoding").withSchema(TableName.IntegrationAuth).as("keyEncodingAu"),
db.ref("awsAssumeIamRoleArnCipherText").withSchema(TableName.IntegrationAuth), db.ref("awsAssumeIamRoleArnCipherText").withSchema(TableName.IntegrationAuth),
db.ref("awsAssumeIamRoleArnIV").withSchema(TableName.IntegrationAuth), db.ref("awsAssumeIamRoleArnIV").withSchema(TableName.IntegrationAuth),
db.ref("awsAssumeIamRoleArnTag").withSchema(TableName.IntegrationAuth) db.ref("awsAssumeIamRoleArnTag").withSchema(TableName.IntegrationAuth),
db.ref("encryptedRefresh").withSchema(TableName.IntegrationAuth),
db.ref("encryptedAccess").withSchema(TableName.IntegrationAuth),
db.ref("encryptedAccessId").withSchema(TableName.IntegrationAuth),
db.ref("encryptedAwsIamAssumRole").withSchema(TableName.IntegrationAuth)
); );
return docs.map( return docs.map(
({ ({
@@ -152,6 +156,10 @@ export const integrationDALFactory = (db: TDbClient) => {
awsAssumeIamRoleArnIV, awsAssumeIamRoleArnIV,
awsAssumeIamRoleArnCipherText, awsAssumeIamRoleArnCipherText,
awsAssumeIamRoleArnTag, awsAssumeIamRoleArnTag,
encryptedAccess,
encryptedRefresh,
encryptedAccessId,
encryptedAwsIamAssumRole,
...el ...el
}) => ({ }) => ({
...el, ...el,
@@ -183,7 +191,11 @@ export const integrationDALFactory = (db: TDbClient) => {
accessExpiresAt, accessExpiresAt,
awsAssumeIamRoleArnIV, awsAssumeIamRoleArnIV,
awsAssumeIamRoleArnCipherText, awsAssumeIamRoleArnCipherText,
awsAssumeIamRoleArnTag awsAssumeIamRoleArnTag,
encryptedAccess,
encryptedRefresh,
encryptedAccessId,
encryptedAwsIamAssumRole
} }
}) })
); );
@@ -498,7 +498,7 @@ export const secretImportServiceFactory = ({
const { botKey, shouldUseSecretV2Bridge } = await projectBotService.getBotKey(projectId); const { botKey, shouldUseSecretV2Bridge } = await projectBotService.getBotKey(projectId);
if (shouldUseSecretV2Bridge) { if (shouldUseSecretV2Bridge) {
const { encryptor: secretManagerEncryptor } = await kmsService.createCipherPairWithDataKey({ const { decryptor: secretManagerDecryptor } = await kmsService.createCipherPairWithDataKey({
type: KmsDataKey.SecretManager, type: KmsDataKey.SecretManager,
projectId projectId
}); });
@@ -507,8 +507,7 @@ export const secretImportServiceFactory = ({
folderDAL, folderDAL,
secretDAL: secretV2BridgeDAL, secretDAL: secretV2BridgeDAL,
secretImportDAL, secretImportDAL,
decryptor: (value) => decryptor: (value) => (value ? secretManagerDecryptor({ cipherTextBlob: value }).toString() : undefined)
value ? secretManagerEncryptor({ plainText: value }).cipherTextBlob.toString() : undefined
}); });
return importedSecrets; return importedSecrets;
} }
@@ -4,7 +4,6 @@ import { TableName, TSecretFolders, TSecretsV2 } from "@app/db/schemas";
import { groupBy } from "@app/lib/fn"; import { groupBy } from "@app/lib/fn";
import { logger } from "@app/lib/logger"; import { logger } from "@app/lib/logger";
import { TKmsServiceFactory } from "../kms/kms-service";
import { TProjectEnvDALFactory } from "../project-env/project-env-dal"; import { TProjectEnvDALFactory } from "../project-env/project-env-dal";
import { TSecretFolderDALFactory } from "../secret-folder/secret-folder-dal"; import { TSecretFolderDALFactory } from "../secret-folder/secret-folder-dal";
import { TSecretV2BridgeDALFactory } from "./secret-v2-bridge-dal"; import { TSecretV2BridgeDALFactory } from "./secret-v2-bridge-dal";
@@ -204,7 +203,10 @@ export const fnSecretBulkUpdate = async ({
tags !== undefined ? { tags, secretId: newSecrets[i].id } : [] tags !== undefined ? { tags, secretId: newSecrets[i].id } : []
); );
if (secsUpdatedTag.length) { if (secsUpdatedTag.length) {
await secretTagDAL.deleteTagsToSecretV2({ $in: { id: secsUpdatedTag.map(({ secretId }) => secretId) } }, tx); await secretTagDAL.deleteTagsToSecretV2(
{ $in: { secrets_v2Id: secsUpdatedTag.map(({ secretId }) => secretId) } },
tx
);
const newSecretTags = secsUpdatedTag.flatMap(({ tags: secretTags = [], secretId }) => const newSecretTags = secsUpdatedTag.flatMap(({ tags: secretTags = [], secretId }) =>
secretTags.map((tag) => ({ secretTags.map((tag) => ({
[`${TableName.SecretTag}Id` as const]: tag, [`${TableName.SecretTag}Id` as const]: tag,
@@ -552,14 +554,3 @@ export const reshapeBridgeSecret = (
createdAt: secret.createdAt, createdAt: secret.createdAt,
updatedAt: secret.updatedAt updatedAt: secret.updatedAt
}); });
export const secretEncryptionHelper = {
encryptValue: (encryptor: Awaited<ReturnType<TKmsServiceFactory["encryptWithKmsKey"]>>, value?: string) => {
if (typeof value === "undefined") return;
return encryptor({ plainText: Buffer.from(value) }).cipherTextBlob;
},
decryptValue: (decryptor: Awaited<ReturnType<TKmsServiceFactory["decryptWithInputKey"]>>, value?: Buffer | null) => {
if (!value) return;
return decryptor({ cipherTextBlob: value }).toString();
}
};
@@ -9,6 +9,7 @@ import { TSecretApprovalRequestSecretDALFactory } from "@app/ee/services/secret-
import { TSecretSnapshotServiceFactory } from "@app/ee/services/secret-snapshot/secret-snapshot-service"; import { TSecretSnapshotServiceFactory } from "@app/ee/services/secret-snapshot/secret-snapshot-service";
import { BadRequestError, NotFoundError } from "@app/lib/errors"; import { BadRequestError, NotFoundError } from "@app/lib/errors";
import { groupBy } from "@app/lib/fn"; import { groupBy } from "@app/lib/fn";
import { setKnexStringValue } from "@app/lib/knex";
import { logger } from "@app/lib/logger"; import { logger } from "@app/lib/logger";
import { alphaNumericNanoId } from "@app/lib/nanoid"; import { alphaNumericNanoId } from "@app/lib/nanoid";
@@ -160,9 +161,10 @@ export const secretV2BridgeServiceFactory = ({
version: 1, version: 1,
type, type,
reminderRepeatDays: el.secretReminderRepeatDays, reminderRepeatDays: el.secretReminderRepeatDays,
encryptedComment: el.secretComment encryptedComment: setKnexStringValue(
? secretManagerEncryptor({ plainText: Buffer.from(el.secretComment) }).cipherTextBlob el.secretComment,
: undefined, (value) => secretManagerEncryptor({ plainText: Buffer.from(value) }).cipherTextBlob
),
encryptedValue: el.secretValue encryptedValue: el.secretValue
? secretManagerEncryptor({ plainText: Buffer.from(el.secretValue) }).cipherTextBlob ? secretManagerEncryptor({ plainText: Buffer.from(el.secretValue) }).cipherTextBlob
: undefined, : undefined,
@@ -265,7 +267,7 @@ export const secretV2BridgeServiceFactory = ({
if (inputSecret.newSecretName) { if (inputSecret.newSecretName) {
const doesNewNameSecretExist = await secretDAL.findOne({ const doesNewNameSecretExist = await secretDAL.findOne({
key: inputSecret.secretName, key: inputSecret.newSecretName,
type: SecretType.Shared, type: SecretType.Shared,
folderId folderId
}); });
@@ -299,9 +301,10 @@ export const secretV2BridgeServiceFactory = ({
filter: { id: secretId }, filter: { id: secretId },
data: { data: {
reminderRepeatDays: inputSecret.secretReminderRepeatDays, reminderRepeatDays: inputSecret.secretReminderRepeatDays,
encryptedComment: inputSecret.secretComment encryptedComment: setKnexStringValue(
? secretManagerEncryptor({ plainText: Buffer.from(inputSecret.secretComment) }).cipherTextBlob inputSecret.secretComment,
: undefined, (value) => secretManagerEncryptor({ plainText: Buffer.from(value) }).cipherTextBlob
),
reminderNote: inputSecret.secretReminderNote, reminderNote: inputSecret.secretReminderNote,
skipMultilineEncoding: inputSecret.skipMultilineEncoding, skipMultilineEncoding: inputSecret.skipMultilineEncoding,
key: inputSecret.newSecretName || secretName, key: inputSecret.newSecretName || secretName,
@@ -732,9 +735,10 @@ export const secretV2BridgeServiceFactory = ({
fnSecretBulkInsert({ fnSecretBulkInsert({
inputSecrets: inputSecrets.map((el) => ({ inputSecrets: inputSecrets.map((el) => ({
version: 1, version: 1,
encryptedComment: el.secretComment encryptedComment: setKnexStringValue(
? secretManagerEncryptor({ plainText: Buffer.from(el.secretComment) }).cipherTextBlob el.secretComment,
: undefined, (value) => secretManagerEncryptor({ plainText: Buffer.from(value) }).cipherTextBlob
),
encryptedValue: el.secretValue encryptedValue: el.secretValue
? secretManagerEncryptor({ plainText: Buffer.from(el.secretValue) }).cipherTextBlob ? secretManagerEncryptor({ plainText: Buffer.from(el.secretValue) }).cipherTextBlob
: undefined, : undefined,
@@ -821,7 +825,7 @@ export const secretV2BridgeServiceFactory = ({
const secrets = await secretDAL.findBySecretKeys( const secrets = await secretDAL.findBySecretKeys(
folderId, folderId,
secretsWithNewName.map((el) => ({ secretsWithNewName.map((el) => ({
key: el.secretKey, key: el.newSecretName as string,
type: SecretType.Shared type: SecretType.Shared
})) }))
); );
@@ -856,9 +860,10 @@ export const secretV2BridgeServiceFactory = ({
filter: { id: originalSecret.id, type: SecretType.Shared }, filter: { id: originalSecret.id, type: SecretType.Shared },
data: { data: {
reminderRepeatDays: el.secretReminderRepeatDays, reminderRepeatDays: el.secretReminderRepeatDays,
encryptedComment: el.secretComment encryptedComment: setKnexStringValue(
? secretManagerEncryptor({ plainText: Buffer.from(el.secretComment) }).cipherTextBlob el.secretComment,
: undefined, (value) => secretManagerEncryptor({ plainText: Buffer.from(value) }).cipherTextBlob
),
reminderNote: el.secretReminderNote, reminderNote: el.secretReminderNote,
skipMultilineEncoding: el.skipMultilineEncoding, skipMultilineEncoding: el.skipMultilineEncoding,
key: el.newSecretName || el.secretKey, key: el.newSecretName || el.secretKey,
+2 -2
View File
@@ -635,9 +635,9 @@ export const secretQueueFactory = ({
); );
let awsAssumeRoleArn = null; let awsAssumeRoleArn = null;
if (shouldUseSecretV2Bridge) { if (shouldUseSecretV2Bridge) {
if (integrationAuth.awsAssumeIamRoleArnCipherText) { if (integrationAuth.encryptedAwsIamAssumRole) {
awsAssumeRoleArn = secretManagerDecryptor({ awsAssumeRoleArn = secretManagerDecryptor({
cipherTextBlob: Buffer.from(integrationAuth.awsAssumeIamRoleArnCipherText) cipherTextBlob: Buffer.from(integrationAuth.encryptedAwsIamAssumRole)
}).toString(); }).toString();
} }
} else if ( } else if (
@@ -1,50 +1,39 @@
import { useCallback, useEffect } from "react"; import { useCallback, useEffect } from "react";
import { useTranslation } from "react-i18next";
import { createNotification } from "@app/components/notifications"; import { createNotification } from "@app/components/notifications";
import { Button, Modal, ModalContent } from "@app/components/v2";
import { ProjectPermissionActions, ProjectPermissionSub, useWorkspace } from "@app/context"; import { ProjectPermissionActions, ProjectPermissionSub, useWorkspace } from "@app/context";
import { withProjectPermission } from "@app/hoc"; import { withProjectPermission } from "@app/hoc";
import { usePopUp } from "@app/hooks";
import { import {
useDeleteIntegration, useDeleteIntegration,
useDeleteIntegrationAuths, useDeleteIntegrationAuths,
useGetCloudIntegrations, useGetCloudIntegrations,
useGetUserWsKey,
useGetWorkspaceAuthorizations, useGetWorkspaceAuthorizations,
useGetWorkspaceBot, useGetWorkspaceIntegrations
useGetWorkspaceIntegrations,
useUpdateBotActiveStatus
} from "@app/hooks/api"; } from "@app/hooks/api";
import { IntegrationAuth } from "@app/hooks/api/types"; import { IntegrationAuth } from "@app/hooks/api/types";
import { ProjectVersion } from "@app/hooks/api/workspace/types";
import { CloudIntegrationSection } from "./components/CloudIntegrationSection"; import { CloudIntegrationSection } from "./components/CloudIntegrationSection";
import { FrameworkIntegrationSection } from "./components/FrameworkIntegrationSection"; import { FrameworkIntegrationSection } from "./components/FrameworkIntegrationSection";
import { InfrastructureIntegrationSection } from "./components/InfrastructureIntegrationSection/InfrastructureIntegrationSection"; import { InfrastructureIntegrationSection } from "./components/InfrastructureIntegrationSection/InfrastructureIntegrationSection";
import { IntegrationsSection } from "./components/IntegrationsSection"; import { IntegrationsSection } from "./components/IntegrationsSection";
import { generateBotKey, redirectForProviderAuth } from "./IntegrationPage.utils"; import { redirectForProviderAuth } from "./IntegrationPage.utils";
type Props = { type Props = {
frameworkIntegrations: Array<{ name: string; slug: string; image: string; docsLink: string }>; frameworkIntegrations: Array<{ name: string; slug: string; image: string; docsLink: string }>;
infrastructureIntegrations: Array<{ name: string; slug: string; image: string; docsLink: string }>; infrastructureIntegrations: Array<{
name: string;
slug: string;
image: string;
docsLink: string;
}>;
}; };
export const IntegrationsPage = withProjectPermission( export const IntegrationsPage = withProjectPermission(
({ frameworkIntegrations, infrastructureIntegrations }: Props) => { ({ frameworkIntegrations, infrastructureIntegrations }: Props) => {
const { t } = useTranslation();
const { currentWorkspace } = useWorkspace(); const { currentWorkspace } = useWorkspace();
const workspaceId = currentWorkspace?.id || ""; const workspaceId = currentWorkspace?.id || "";
const environments = currentWorkspace?.environments || []; const environments = currentWorkspace?.environments || [];
const { data: latestWsKey } = useGetUserWsKey(workspaceId);
const { popUp, handlePopUpOpen, handlePopUpToggle, handlePopUpClose } = usePopUp([
"activeBot"
] as const);
const { data: cloudIntegrations, isLoading: isCloudIntegrationsLoading } = const { data: cloudIntegrations, isLoading: isCloudIntegrationsLoading } =
useGetCloudIntegrations(); useGetCloudIntegrations();
@@ -70,11 +59,6 @@ export const IntegrationsPage = withProjectPermission(
isFetching: isIntegrationFetching isFetching: isIntegrationFetching
} = useGetWorkspaceIntegrations(workspaceId); } = useGetWorkspaceIntegrations(workspaceId);
const { data: bot } = useGetWorkspaceBot(workspaceId);
// mutation
const { mutateAsync: updateBotActiveStatus, mutate: updateBotActiveStatusSync } =
useUpdateBotActiveStatus();
const { mutateAsync: deleteIntegration } = useDeleteIntegration(); const { mutateAsync: deleteIntegration } = useDeleteIntegration();
const { const {
mutateAsync: deleteIntegrationAuths, mutateAsync: deleteIntegrationAuths,
@@ -95,12 +79,6 @@ export const IntegrationsPage = withProjectPermission(
isIntegrationsAuthorizedEmpty && isIntegrationsAuthorizedEmpty &&
isIntegrationsEmpty isIntegrationsEmpty
) { ) {
if (bot?.id && currentWorkspace?.version === ProjectVersion.V1)
updateBotActiveStatusSync({
isActive: false,
botId: bot.id,
workspaceId
});
resetDeleteIntegrationAuths(); resetDeleteIntegrationAuths();
} }
}, [ }, [
@@ -116,16 +94,6 @@ export const IntegrationsPage = withProjectPermission(
if (!selectedCloudIntegration) return; if (!selectedCloudIntegration) return;
try { try {
if (bot && !bot.isActive && currentWorkspace?.version === ProjectVersion.V1) {
const botKey = generateBotKey(bot.publicKey, latestWsKey!);
await updateBotActiveStatus({
workspaceId,
botKey,
isActive: true,
botId: bot.id
});
}
redirectForProviderAuth(selectedCloudIntegration); redirectForProviderAuth(selectedCloudIntegration);
} catch (error) { } catch (error) {
console.error(error); console.error(error);
@@ -135,19 +103,9 @@ export const IntegrationsPage = withProjectPermission(
// function to strat integration for a provider // function to strat integration for a provider
// confirmation to user passing the bot key for provider to get secret access // confirmation to user passing the bot key for provider to get secret access
const handleProviderIntegrationStart = (provider: string) => { const handleProviderIntegrationStart = (provider: string) => {
if (!bot?.isActive) {
handlePopUpOpen("activeBot", { provider });
return;
}
handleProviderIntegration(provider); handleProviderIntegration(provider);
}; };
const handleUserAcceptBotCondition = () => {
const { provider } = popUp.activeBot?.data as { provider: string };
handleProviderIntegration(provider);
handlePopUpClose("activeBot");
};
const handleIntegrationDelete = async (integrationId: string, cb: () => void) => { const handleIntegrationDelete = async (integrationId: string, cb: () => void) => {
try { try {
await deleteIntegration({ id: integrationId, workspaceId }); await deleteIntegration({ id: integrationId, workspaceId });
@@ -195,7 +153,6 @@ export const IntegrationsPage = withProjectPermission(
integrations={integrations} integrations={integrations}
environments={environments} environments={environments}
onIntegrationDelete={({ id }, cb) => handleIntegrationDelete(id, cb)} onIntegrationDelete={({ id }, cb) => handleIntegrationDelete(id, cb)}
isBotActive={bot?.isActive}
workspaceId={workspaceId} workspaceId={workspaceId}
/> />
<CloudIntegrationSection <CloudIntegrationSection
@@ -205,30 +162,6 @@ export const IntegrationsPage = withProjectPermission(
onIntegrationStart={handleProviderIntegrationStart} onIntegrationStart={handleProviderIntegrationStart}
onIntegrationRevoke={handleIntegrationAuthRevoke} onIntegrationRevoke={handleIntegrationAuthRevoke}
/> />
<Modal
isOpen={popUp.activeBot?.isOpen}
onOpenChange={(isOpen) => handlePopUpToggle("activeBot", isOpen)}
>
<ModalContent
title={t("integrations.grant-access-to-secrets") as string}
footerContent={
<div className="flex items-center space-x-2">
<Button onClick={() => handleUserAcceptBotCondition()}>
{t("integrations.grant-access-button") as string}
</Button>
<Button
onClick={() => handlePopUpClose("activeBot")}
variant="outline_bg"
colorSchema="secondary"
>
Cancel
</Button>
</div>
}
>
{t("integrations.why-infisical-needs-access")}
</ModalContent>
</Modal>
<FrameworkIntegrationSection frameworks={frameworkIntegrations} /> <FrameworkIntegrationSection frameworks={frameworkIntegrations} />
<InfrastructureIntegrationSection integrations={infrastructureIntegrations} /> <InfrastructureIntegrationSection integrations={infrastructureIntegrations} />
</div> </div>
@@ -1,4 +1,3 @@
import Link from "next/link";
import { faCalendarCheck } from "@fortawesome/free-regular-svg-icons"; import { faCalendarCheck } from "@fortawesome/free-regular-svg-icons";
import { faArrowRight, faRefresh, faWarning, faXmark } from "@fortawesome/free-solid-svg-icons"; import { faArrowRight, faRefresh, faWarning, faXmark } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
@@ -7,8 +6,6 @@ import { integrationSlugNameMapping } from "public/data/frequentConstants";
import { ProjectPermissionCan } from "@app/components/permissions"; import { ProjectPermissionCan } from "@app/components/permissions";
import { import {
Alert,
AlertDescription,
Button, Button,
DeleteActionModal, DeleteActionModal,
EmptyState, EmptyState,
@@ -29,7 +26,6 @@ type Props = {
integrations?: TIntegration[]; integrations?: TIntegration[];
isLoading?: boolean; isLoading?: boolean;
onIntegrationDelete: (integration: TIntegration, cb: () => void) => void; onIntegrationDelete: (integration: TIntegration, cb: () => void) => void;
isBotActive: boolean | undefined;
workspaceId: string; workspaceId: string;
}; };
@@ -38,7 +34,6 @@ export const IntegrationsSection = ({
environments = [], environments = [],
isLoading, isLoading,
onIntegrationDelete, onIntegrationDelete,
isBotActive,
workspaceId workspaceId
}: Props) => { }: Props) => {
const { popUp, handlePopUpOpen, handlePopUpClose, handlePopUpToggle } = usePopUp([ const { popUp, handlePopUpOpen, handlePopUpClose, handlePopUpToggle } = usePopUp([
@@ -59,21 +54,7 @@ export const IntegrationsSection = ({
</div> </div>
)} )}
{!isBotActive && Boolean(integrations.length) && ( {!isLoading && !integrations.length && (
<div className="px-6 py-4">
<Alert hideTitle variant="warning">
<AlertDescription>
All the active integrations will be disabled. Disable End-to-End Encryption in{" "}
<Link href={`/project/${workspaceId}/settings`} passHref>
<a className="underline underline-offset-2">project settings </a>
</Link>
to re-enable it.
</AlertDescription>
</Alert>
</div>
)}
{!isLoading && !integrations.length && isBotActive && (
<div className="mx-6"> <div className="mx-6">
<EmptyState <EmptyState
className="rounded-md border border-mineshaft-700 pt-8 pb-4" className="rounded-md border border-mineshaft-700 pt-8 pb-4"
@@ -81,7 +62,7 @@ export const IntegrationsSection = ({
/> />
</div> </div>
)} )}
{!isLoading && isBotActive && ( {!isLoading && (
<div className="flex min-w-max flex-col space-y-4 p-6 pt-0"> <div className="flex min-w-max flex-col space-y-4 p-6 pt-0">
{integrations?.map((integration) => ( {integrations?.map((integration) => (
<div <div