From 90be28b87a44942bbb515cf958efe789f48740aa Mon Sep 17 00:00:00 2001 From: Daniel Hougaard <62331820+DanielHougaard@users.noreply.github.com> Date: Wed, 20 Mar 2024 20:25:48 +0100 Subject: [PATCH] Feat: Import indicator --- .../src/hooks/api/secretImports/queries.tsx | 108 +++++++++++++++++- .../views/SecretMainPage/SecretMainPage.tsx | 4 +- .../SecretOverviewPage/SecretOverviewPage.tsx | 9 +- .../SecretOverviewFolderRow.tsx | 6 +- 4 files changed, 117 insertions(+), 10 deletions(-) diff --git a/frontend/src/hooks/api/secretImports/queries.tsx b/frontend/src/hooks/api/secretImports/queries.tsx index 1714f45ff..94321e9e2 100644 --- a/frontend/src/hooks/api/secretImports/queries.tsx +++ b/frontend/src/hooks/api/secretImports/queries.tsx @@ -11,7 +11,7 @@ import { TGetImportedFoldersByEnvDTO, TGetImportedSecrets, TGetSecretImports, - TImportedSecretFolder, + TGetSecretImportsAllEnvs, TImportedSecrets, TSecretImport, TuseGetImportedFoldersByEnv @@ -27,7 +27,9 @@ export const secretImportKeys = { }: Omit<TGetImportedSecrets, "decryptFileKey">) => [{ environment, path, projectId }, "secrets-import-sec"] as const, getImportedFoldersByEnv: ({ environment, projectId, path }: TGetImportedFoldersByEnvDTO) => - [{ environment, projectId, path }, "imported-folders"] as const + [{ environment, projectId, path }, "imported-folders"] as const, + getImportedFoldersAllEnvs: ({ projectId, path, environment }: TGetImportedFoldersByEnvDTO) => + [{ projectId, path, environment }, "imported-folders-all-envs"] as const }; const fetchSecretImport = async ({ projectId, environment, path = "/" }: TGetSecretImports) => { @@ -90,7 +92,7 @@ const fetchImportedFolders = async ({ environment, path }: TGetImportedFoldersByEnvDTO) => { - const { data } = await apiRequest.get<{ secretImports: TImportedSecretFolder[] }>( + const { data } = await apiRequest.get<{ secretImports: TSecretImport[] }>( "/api/v1/secret-imports", { params: { @@ -103,7 +105,7 @@ const fetchImportedFolders = async ({ return data.secretImports; }; -export const useGetImportedSecrets = ({ +export const useGetImportedSecretsSingleEnv = ({ environment, decryptFileKey, path, @@ -188,6 +190,98 @@ export const useGetImportedSecrets = ({ ) }); +export const useGetImportedSecretsAllEnvs = ({ + projectId, + environments, + path = "/", + decryptFileKey +}: TGetSecretImportsAllEnvs) => { + const secretImports = useQueries({ + queries: environments.map((env) => ({ + queryKey: secretImportKeys.getImportedFoldersAllEnvs({ + environment: env, + projectId, + path + }), + queryFn: () => fetchImportedSecrets(projectId, env, path).catch(() => []), + enabled: Boolean(projectId) && Boolean(env), + // eslint-disable-next-line react-hooks/rules-of-hooks + select: useCallback( + (data: TImportedSecrets[]) => { + const PRIVATE_KEY = localStorage.getItem("PRIVATE_KEY") as string; + const latestKey = decryptFileKey; + const key = decryptAssymmetric({ + ciphertext: latestKey.encryptedKey, + nonce: latestKey.nonce, + publicKey: latestKey.sender.publicKey, + privateKey: PRIVATE_KEY + }); + + return data.map((el) => ({ + environment: el.environment, + secretPath: el.secretPath, + environmentInfo: el.environmentInfo, + folderId: el.folderId, + secrets: el.secrets.map((encSecret) => { + const secretKey = decryptSymmetric({ + ciphertext: encSecret.secretKeyCiphertext, + iv: encSecret.secretKeyIV, + tag: encSecret.secretKeyTag, + key + }); + + const secretValue = decryptSymmetric({ + ciphertext: encSecret.secretValueCiphertext, + iv: encSecret.secretValueIV, + tag: encSecret.secretValueTag, + key + }); + + const secretComment = decryptSymmetric({ + ciphertext: encSecret.secretCommentCiphertext, + iv: encSecret.secretCommentIV, + tag: encSecret.secretCommentTag, + key + }); + + return { + id: encSecret.id, + env: encSecret.environment, + key: secretKey, + value: secretValue, + tags: encSecret.tags, + comment: secretComment, + createdAt: encSecret.createdAt, + updatedAt: encSecret.updatedAt, + version: encSecret.version + }; + }) + })); + }, + [decryptFileKey] + ) + })) + }); + + const isImportedSecretPresentInEnv = useCallback( + (secPath: string, envSlug: string) => { + const selectedEnvIndex = environments.indexOf(envSlug); + + if (selectedEnvIndex !== -1) { + const isPresent = secretImports?.[selectedEnvIndex]?.data?.find( + ({ secretPath }) => secretPath === secPath + ); + + return Boolean(isPresent); + } + return false; + }, + [(secretImports || []).map((response) => response.data)] + ); + + return { secretImports, isImportedSecretPresentInEnv }; +}; + export const useGetImportedFoldersByEnv = ({ projectId, environments, @@ -195,14 +289,16 @@ export const useGetImportedFoldersByEnv = ({ }: TuseGetImportedFoldersByEnv) => { const queryParams = new URLSearchParams(window.location.search); + const currentPath = path; + const importedFolders = useQueries({ queries: environments.map((env) => ({ queryKey: secretImportKeys.getImportedFoldersByEnv({ projectId, environment: env, - path + path: currentPath }), - queryFn: async () => fetchImportedFolders({ projectId, environment: env, path }), + queryFn: async () => fetchImportedFolders({ projectId, environment: env, path: currentPath }), enabled: Boolean(projectId) && Boolean(env) })) }); diff --git a/frontend/src/views/SecretMainPage/SecretMainPage.tsx b/frontend/src/views/SecretMainPage/SecretMainPage.tsx index 312b676b7..a06ceda9d 100644 --- a/frontend/src/views/SecretMainPage/SecretMainPage.tsx +++ b/frontend/src/views/SecretMainPage/SecretMainPage.tsx @@ -17,7 +17,7 @@ import { } from "@app/context"; import { usePopUp } from "@app/hooks"; import { - useGetImportedSecrets, + useGetImportedSecretsSingleEnv, useGetProjectFolders, useGetProjectSecrets, useGetSecretApprovalPolicyOfABoard, @@ -124,7 +124,7 @@ export const SecretMainPage = () => { }); // fetch imported secrets to show user the overriden ones - const { data: importedSecrets } = useGetImportedSecrets({ + const { data: importedSecrets } = useGetImportedSecretsSingleEnv({ projectId: workspaceId, environment, decryptFileKey: decryptFileKey!, diff --git a/frontend/src/views/SecretOverviewPage/SecretOverviewPage.tsx b/frontend/src/views/SecretOverviewPage/SecretOverviewPage.tsx index 7eda5c203..02741cc9f 100644 --- a/frontend/src/views/SecretOverviewPage/SecretOverviewPage.tsx +++ b/frontend/src/views/SecretOverviewPage/SecretOverviewPage.tsx @@ -56,6 +56,7 @@ import { useDeleteSecretV3, useGetFoldersByEnv, useGetImportedFoldersByEnv, + useGetImportedSecretsAllEnvs, useGetProjectSecretsAllEnv, useGetUserWsKey, useUpdateSecretV3 @@ -134,7 +135,12 @@ export const SecretOverviewPage = () => { const { isImportedFolderPresentInEnv } = useGetImportedFoldersByEnv({ projectId: workspaceId, - path: secretPath, + environments: userAvailableEnvs.map(({ slug }) => slug) + }); + + const { isImportedSecretPresentInEnv } = useGetImportedSecretsAllEnvs({ + projectId: workspaceId, + decryptFileKey: latestFileKey!, environments: userAvailableEnvs.map(({ slug }) => slug) }); @@ -657,6 +663,7 @@ export const SecretOverviewPage = () => { filteredSecretNames.map((key, index) => ( <SecretOverviewTableRow secretPath={secretPath} + isImportedSecretPresentInEnv={isImportedSecretPresentInEnv} onSecretCreate={handleSecretCreate} onSecretDelete={handleSecretDelete} onSecretUpdate={handleSecretUpdate} diff --git a/frontend/src/views/SecretOverviewPage/components/SecretOverviewFolderRow/SecretOverviewFolderRow.tsx b/frontend/src/views/SecretOverviewPage/components/SecretOverviewFolderRow/SecretOverviewFolderRow.tsx index ca506f7cb..fd225c9e1 100644 --- a/frontend/src/views/SecretOverviewPage/components/SecretOverviewFolderRow/SecretOverviewFolderRow.tsx +++ b/frontend/src/views/SecretOverviewPage/components/SecretOverviewFolderRow/SecretOverviewFolderRow.tsx @@ -40,7 +40,11 @@ export const SecretOverviewFolderRow = ({ isPresent || isImportPresent ? "text-green-600" : "text-red-600" )} > - <Tooltip isDisabled={!isImportPresent} content="Folder is imported"> + <Tooltip + center + isDisabled={!isImportPresent} + content="Folder is imported from another environment" + > <div className="flex justify-center"> <FontAwesomeIcon // eslint-disable-next-line no-nested-ternary