diff --git a/backend/src/controllers/v1/organizationController.ts b/backend/src/controllers/v1/organizationController.ts index 6de5a7c63..f738891a2 100644 --- a/backend/src/controllers/v1/organizationController.ts +++ b/backend/src/controllers/v1/organizationController.ts @@ -260,22 +260,6 @@ export const createOrganizationPortalSession = async ( } }; -/** - * Return organization subscriptions - * @param req - * @param res - * @returns - */ -export const getOrganizationSubscriptions = async ( - req: Request, - res: Response -) => { - return res.status(200).send({ - subscriptions: [] - }); -}; - - /** * Given a org id, return the projects each member of the org belongs to * @param req diff --git a/backend/src/routes/v1/organization.ts b/backend/src/routes/v1/organization.ts index 1c6b5b7e4..78672093d 100644 --- a/backend/src/routes/v1/organization.ts +++ b/backend/src/routes/v1/organization.ts @@ -148,20 +148,6 @@ router.post( organizationController.createOrganizationPortalSession ); -router.get( - "/:organizationId/subscriptions", // TODO endpoint: deprecate - requireAuth({ - acceptedAuthModes: [AuthMode.JWT], - }), - requireOrganizationAuth({ - acceptedRoles: [OWNER, ADMIN, MEMBER], - acceptedStatuses: [ACCEPTED], - }), - param("organizationId").exists().trim(), - validateRequest, - organizationController.getOrganizationSubscriptions -); - router.get( "/:organizationId/workspace-memberships", requireAuth({ diff --git a/frontend/src/components/basic/dialog/AddServiceTokenDialog.tsx b/frontend/src/components/basic/dialog/AddServiceTokenDialog.tsx deleted file mode 100644 index 7e7c389d8..000000000 --- a/frontend/src/components/basic/dialog/AddServiceTokenDialog.tsx +++ /dev/null @@ -1,271 +0,0 @@ -import crypto from "crypto"; - -import { Fragment, useState } from "react"; -import { useTranslation } from "react-i18next"; -import { faCheck, faCopy } from "@fortawesome/free-solid-svg-icons"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { Dialog, Transition } from "@headlessui/react"; - -import addServiceToken from "@app/pages/api/serviceToken/addServiceToken"; -import getLatestFileKey from "@app/pages/api/workspace/getLatestFileKey"; - -import { decryptAssymmetric, encryptSymmetric } from "../../utilities/cryptography/crypto"; -import Button from "../buttons/Button"; -import InputField from "../InputField"; -import ListBox from "../Listbox"; - -const expiryMapping = { - "1 day": 86400, - "7 days": 604800, - "1 month": 2592000, - "6 months": 15552000, - "12 months": 31104000 -}; - -type Props = { - isOpen: boolean; - closeModal: () => void; - workspaceId: string; - workspaceName: string; - serviceTokens: any[]; - environments: Array<{ name: string; slug: string }>; - setServiceTokens: (arg: any[]) => void; -}; - -const AddServiceTokenDialog = ({ - isOpen, - closeModal, - workspaceId, - workspaceName, - serviceTokens, - environments, - setServiceTokens -}: Props) => { - const [serviceToken, setServiceToken] = useState(""); - const [serviceTokenName, setServiceTokenName] = useState(""); - const [selectedServiceTokenEnv, setSelectedServiceTokenEnv] = useState(environments?.[0]); - const [serviceTokenExpiresIn, setServiceTokenExpiresIn] = useState("1 day"); - const [serviceTokenCopied, setServiceTokenCopied] = useState(false); - const { t } = useTranslation(); - - const generateServiceToken = async () => { - const latestFileKey = await getLatestFileKey({ workspaceId }); - - const key = decryptAssymmetric({ - ciphertext: latestFileKey.latestKey.encryptedKey, - nonce: latestFileKey.latestKey.nonce, - publicKey: latestFileKey.latestKey.sender.publicKey, - privateKey: localStorage.getItem("PRIVATE_KEY") as string - }); - - const randomBytes = crypto.randomBytes(16).toString("hex"); - const { ciphertext, iv, tag } = encryptSymmetric({ - plaintext: key, - key: randomBytes - }); - - console.log( - 1234, - selectedServiceTokenEnv, - environments, - selectedServiceTokenEnv?.slug ? selectedServiceTokenEnv.slug : environments[0]?.slug - ); - const newServiceToken = await addServiceToken({ - name: serviceTokenName, - workspaceId, - environment: selectedServiceTokenEnv?.slug - ? selectedServiceTokenEnv.slug - : environments[0]?.slug, - expiresIn: expiryMapping[serviceTokenExpiresIn as keyof typeof expiryMapping], - encryptedKey: ciphertext, - iv, - tag - }); - - setServiceTokens(serviceTokens.concat([newServiceToken.serviceTokenData])); - setServiceToken(`${newServiceToken.serviceToken}.${randomBytes}`); - }; - - function copyToClipboard() { - // Get the text field - const copyText = document.getElementById("serviceToken") as HTMLInputElement; - - // Select the text field - copyText.select(); - copyText.setSelectionRange(0, 99999); // For mobile devices - - // Copy the text inside the text field - navigator.clipboard.writeText(copyText.value); - - setServiceTokenCopied(true); - setTimeout(() => setServiceTokenCopied(false), 2000); - // Alert the copied text - // alert("Copied the text: " + copyText.value); - } - - const closeAddServiceTokenModal = () => { - closeModal(); - setServiceTokenName(""); - setServiceToken(""); - }; - - return ( -
- - - -
- - -
-
- - {serviceToken === "" ? ( - - - {t("section.token.add-dialog.title", { - target: workspaceName - })} - -
-
-

- {t("section.token.add-dialog.description")} -

-
-
-
- -
-
- name)} - onChange={(envName) => - setSelectedServiceTokenEnv( - environments.find(({ name }) => envName === name) || { - name: "unknown", - slug: "unknown" - } - ) - } - isFull - text={`${t("common.environment")}: `} - /> -
-
- -
-
-
-
-
-
- ) : ( - - - {t("section.token.add-dialog.copy-service-token")} - -
-
-

- {t("section.token.add-dialog.copy-service-token-description")} -

-
-
-
-
- -
- {serviceToken} -
-
- - - {t("common.click-to-copy")} - -
-
-
-
-
-
- )} -
-
-
-
-
-
- ); -}; - -export default AddServiceTokenDialog; diff --git a/frontend/src/components/basic/table/EnvironmentsTable.tsx b/frontend/src/components/basic/table/EnvironmentsTable.tsx deleted file mode 100644 index b9c94534a..000000000 --- a/frontend/src/components/basic/table/EnvironmentsTable.tsx +++ /dev/null @@ -1,184 +0,0 @@ -import { useEffect, useState } from "react"; -import { faPencil, faPlus, faX } from "@fortawesome/free-solid-svg-icons"; -import { plans } from "public/data/frequentConstants"; - -import { usePopUp } from "../../../hooks/usePopUp"; -import getOrganizationSubscriptions from "../../../pages/api/organization/GetOrgSubscription"; -import Button from "../buttons/Button"; -import { AddUpdateEnvironmentDialog } from "../dialog/AddUpdateEnvironmentDialog"; -import DeleteActionModal from "../dialog/DeleteActionModal"; -import UpgradePlanModal from "../dialog/UpgradePlan"; - -type Env = { name: string; slug: string }; - -type Props = { - data: Env[]; - onCreateEnv: (arg0: Env) => Promise; - onUpdateEnv: (oldSlug: string, arg0: Env) => Promise; - onDeleteEnv: (slug: string) => Promise; -}; - -const EnvironmentTable = ({ data = [], onCreateEnv, onDeleteEnv, onUpdateEnv }: Props) => { - const { popUp, handlePopUpOpen, handlePopUpClose } = usePopUp([ - "createUpdateEnv", - "deleteEnv", - "upgradePlan" - ] as const); - const [plan, setPlan] = useState(""); - const host = window.location.origin; - - useEffect(() => { - // on initial load - run auth check - (async () => { - const orgId = localStorage.getItem("orgData.id") as string; - const subscriptions = await getOrganizationSubscriptions({ - orgId - }); - if (subscriptions) { - setPlan(subscriptions.data[0].plan.product) - } - })(); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); - - const onEnvCreateCB = async (env: Env) => { - try { - await onCreateEnv(env); - handlePopUpClose("createUpdateEnv"); - } catch (error) { - console.error(error); - } - }; - - const onEnvUpdateCB = async (env: Env) => { - try { - await onUpdateEnv((popUp.createUpdateEnv?.data as Pick)?.slug, env); - handlePopUpClose("createUpdateEnv"); - } catch (error) { - console.error(error); - } - }; - - const onEnvDeleteCB = async () => { - try { - await onDeleteEnv((popUp.deleteEnv?.data as Pick)?.slug); - handlePopUpClose("deleteEnv"); - } catch (error) { - console.error(error); - } - }; - - return ( - <> -
-
-

Project Environments

-

- Choose which environments will show up in your dashboard like development, staging, - production -

-

- Note: the text in slugs shows how these environmant should be accessed in CLI. -

-
-
-
-
-
-
- - - - - - - - - {data?.length > 0 ? ( - data.map(({ name, slug }) => ( - - - - - - )) - ) : ( - - - - )} - -
NameSlug -
{name}{slug} -
-
-
-
-
- No environments found -
- handlePopUpClose("deleteEnv")} - onSubmit={onEnvDeleteCB} - /> - handlePopUpClose("createUpdateEnv")} - onCreateSubmit={onEnvCreateCB} - onEditSubmit={onEnvUpdateCB} - /> - handlePopUpClose("upgradePlan")} - text="You can add custom environments if you switch to Infisical's Team plan." - /> -
- - ); -}; - -export default EnvironmentTable; diff --git a/frontend/src/components/basic/table/ServiceTokenTable.tsx b/frontend/src/components/basic/table/ServiceTokenTable.tsx deleted file mode 100644 index 9c02c059c..000000000 --- a/frontend/src/components/basic/table/ServiceTokenTable.tsx +++ /dev/null @@ -1,98 +0,0 @@ -import { faX } from "@fortawesome/free-solid-svg-icons"; - -import { useNotificationContext } from "@app/components/context/Notifications/NotificationProvider"; - -import deleteServiceToken from "../../../pages/api/serviceToken/deleteServiceToken"; -import guidGenerator from "../../utilities/randomId"; -import Button from "../buttons/Button"; - -interface TokenProps { - _id: string; - name: string; - environment: string; - expiresAt: string; -} - -interface ServiceTokensProps { - data: TokenProps[]; - workspaceName: string; - setServiceTokens: (value: TokenProps[]) => void; -} - -/** - * This is the component that we utilize for the service token table - * #TODO: add the possibility of choosing and doing operations on multiple users. - * @param {object} obj - * @param {any[]} obj.data - current state of the service token table - * @param {string} obj.workspaceName - name of the current project - * @param {function} obj.setServiceTokens - updating the state of the service token table - * @returns - */ -const ServiceTokenTable = ({ data, workspaceName, setServiceTokens }: ServiceTokensProps) => { - const { createNotification } = useNotificationContext(); - - return ( -
-
- - - - - - - - - - - {data?.length > 0 ? ( - data?.map((row) => ( - - - - - - - - )) - ) : ( - - - - )} - -
TOKEN NAMEPROJECTENVIRONMENTVAILD UNTIL -
- {row.name} - - {workspaceName} - - {row.environment} - - {new Date(row.expiresAt).toUTCString()} - -
-
-
- No service tokens yet -
-
- ); -}; - -export default ServiceTokenTable; diff --git a/frontend/src/components/basic/table/UserTable.tsx b/frontend/src/components/basic/table/UserTable.tsx deleted file mode 100644 index f029ae4f0..000000000 --- a/frontend/src/components/basic/table/UserTable.tsx +++ /dev/null @@ -1,234 +0,0 @@ -import { useEffect, useState } from "react"; -import { useRouter } from "next/router"; -import { faX } from "@fortawesome/free-solid-svg-icons"; - -import changeUserRoleInOrganization from "@app/pages/api/organization/changeUserRoleInOrganization"; -import deleteUserFromOrganization from "@app/pages/api/organization/deleteUserFromOrganization"; -import getOrganizationProjectMemberships from "@app/pages/api/organization/GetOrgProjectMemberships"; -import deleteUserFromWorkspace from "@app/pages/api/workspace/deleteUserFromWorkspace"; -import getLatestFileKey from "@app/pages/api/workspace/getLatestFileKey"; -import uploadKeys from "@app/pages/api/workspace/uploadKeys"; - -import { decryptAssymmetric, encryptAssymmetric } from "../../utilities/cryptography/crypto"; -import guidGenerator from "../../utilities/randomId"; -import Button from "../buttons/Button"; -import Listbox from "../Listbox"; - -// const roles = ['admin', 'user']; -// TODO: Set type for this -type Props = { - userData: any[]; - changeData: (users: any[]) => void; - myUser: string; - filter: string; - resendInvite: (email: string) => void; - isOrg: boolean; -}; - -/** - * This is the component that we utilize for the user table - in future, can reuse it for some other purposes too. - * #TODO: add the possibility of choosing and doing operations on multiple users. - * @param {*} props - * @returns - */ -const UserTable = ({ userData, changeData, myUser, filter, resendInvite, isOrg }: Props) => { - const [roleSelected, setRoleSelected] = useState( - Array(userData?.length).fill(userData.map((user) => user.role)) - ); - const router = useRouter(); - const [myRole, setMyRole] = useState("member"); - const [userProjectMemberships, setUserProjectMemberships] = useState([]); - - const workspaceId = router.query.id as string; - // Delete the row in the table (e.g. a user) - // #TODO: Add a pop-up that warns you that the user is going to be deleted. - const handleDelete = (membershipId: string, index: number) => { - // setUserIdToBeDeleted(userId); - // onClick(); - if (isOrg) { - deleteUserFromOrganization(membershipId); - } else { - deleteUserFromWorkspace(membershipId); - } - changeData(userData.filter((v, i) => i !== index)); - setRoleSelected([ - ...roleSelected.slice(0, index), - ...roleSelected.slice(index + 1, userData?.length) - ]); - }; - - // Update the role of a certain user - const handleRoleUpdate = (index: number, e: string) => { - changeUserRoleInOrganization(String(localStorage.getItem("orgData.id")), userData[index].membershipId, e); - changeData([ - ...userData.slice(0, index), - ...[ - { - key: userData[index].key, - firstName: userData[index].firstName, - lastName: userData[index].lastName, - email: userData[index].email, - role: e, - status: userData[index].status, - userId: userData[index].userId, - membershipId: userData[index].membershipId, - publicKey: userData[index].publicKey - } - ], - ...userData.slice(index + 1, userData?.length) - ]); - }; - - useEffect(() => { - setMyRole(userData.filter((user) => user.email === myUser)[0]?.role); - (async () => { - const result = await getOrganizationProjectMemberships({ orgId: String(localStorage.getItem("orgData.id"))}) - setUserProjectMemberships(result); - })(); - }, [userData, myUser]); - - const grantAccess = async (id: string, publicKey: string) => { - const result = await getLatestFileKey({ workspaceId }); - - const PRIVATE_KEY = localStorage.getItem("PRIVATE_KEY") as string; - - // assymmetrically decrypt symmetric key with local private key - const key = decryptAssymmetric({ - ciphertext: result.latestKey.encryptedKey, - nonce: result.latestKey.nonce, - publicKey: result.latestKey.sender.publicKey, - privateKey: PRIVATE_KEY - }); - - const { ciphertext, nonce } = encryptAssymmetric({ - plaintext: key, - publicKey, - privateKey: PRIVATE_KEY - }); - - uploadKeys(workspaceId, id, ciphertext, nonce); - router.reload(); - }; - - const deleteMembershipAndResendInvite = (email: string) => { - // deleteUserFromWorkspace(membershipId); - resendInvite(email); - }; - - return ( -
-
- - - - - - - - - - - {userData?.filter( - (user) => - user.firstName?.toLowerCase().includes(filter) || - user.lastName?.toLowerCase().includes(filter) || - user.email?.toLowerCase().includes(filter) - ).length > 0 && - userData - ?.filter( - (user) => - user.firstName?.toLowerCase().includes(filter) || - user.lastName?.toLowerCase().includes(filter) || - user.email?.toLowerCase().includes(filter) - ) - .map((row, index) => ( - - - - - - - - ))} - -
NAMEEMAILROLEPROJECTS -
- {row.firstName} {row.lastName} - - {row.email} - -
- {row.status === "accepted" && - ((myRole === "admin" && row.role !== "owner") || myRole === "owner") && - (myUser !== row.email) ? ( - handleRoleUpdate(index, e)} - data={ - myRole === "owner" ? ["owner", "admin", "member"] : ["admin", "member"] - } - /> - ) : ( - row.status !== "invited" && - row.status !== "verified" && ( - { - throw new Error("Function not implemented."); - }} - data={null} - /> - ) - )} - {(row.status === "invited" || row.status === "verified") && ( -
-
- )} - {row.status === "completed" && myUser !== row.email && ( -
-
- )} -
-
-
- {userProjectMemberships[row.userId] - ? userProjectMemberships[row.userId]?.map((project: any) => ( -
- {project.name} -
- )) - : This user isn't part of any projects yet.} -
-
- {myUser !== row.email && - // row.role !== "admin" && - myRole !== "member" ? ( -
-
- ) : ( -
- )} -
-
- ); -}; - -export default UserTable; diff --git a/frontend/src/components/dashboard/CompareSecretsModal.tsx b/frontend/src/components/dashboard/CompareSecretsModal.tsx deleted file mode 100644 index bf0575e09..000000000 --- a/frontend/src/components/dashboard/CompareSecretsModal.tsx +++ /dev/null @@ -1,88 +0,0 @@ -import { SetStateAction, useEffect, useState } from "react"; -import Image from "next/image"; - -import { WorkspaceEnv } from "@app/hooks/api/types"; - -import getSecretsForProject from "../utilities/secrets/getSecretsForProject"; -import { Modal, ModalContent } from "../v2"; - -interface Secrets { - label: string; - secret: string; -} - -interface CompareSecretsModalProps { - compareModal: boolean; - setCompareModal: React.Dispatch>; - selectedEnv: WorkspaceEnv; - workspaceEnvs: WorkspaceEnv[]; - workspaceId: string; - currentSecret: { - key: string; - value: string; - }; -} - -const CompareSecretsModal = ({ - compareModal, - setCompareModal, - selectedEnv, - workspaceEnvs, - workspaceId, - currentSecret -}: CompareSecretsModalProps) => { - const [secrets, setSecrets] = useState([]); - - const getEnvSecrets = async () => { - const workspaceEnvironments = workspaceEnvs?.filter((env) => env !== selectedEnv); - const newSecrets = await Promise.all( - workspaceEnvironments.map(async (env) => { - // #TODO: optimize this query somehow... - const allSecrets = await getSecretsForProject({ env: env.slug, workspaceId }); - const secret = - allSecrets.find((item) => item.key === currentSecret.key)?.value ?? "Not found"; - return { label: env.name, secret }; - }) - ); - setSecrets([{ label: selectedEnv.name, secret: currentSecret.value }, ...newSecrets]); - }; - - useEffect(() => { - if (compareModal) { - (async () => { - await getEnvSecrets(); - })(); - } - }, [compareModal]); - - return ( - - e.preventDefault()}> -
- {secrets.length === 0 ? ( -
- infisical loading indicator -
- ) : ( - secrets.map((item) => ( -
-

{item.label}

- -
- )) - )} -
-
-
- ); -}; -export default CompareSecretsModal; diff --git a/frontend/src/components/dashboard/SideBar.tsx b/frontend/src/components/dashboard/SideBar.tsx deleted file mode 100644 index 48a080a3a..000000000 --- a/frontend/src/components/dashboard/SideBar.tsx +++ /dev/null @@ -1,222 +0,0 @@ -/* eslint-disable react/no-unused-prop-types */ -import { useState } from "react"; -import { useTranslation } from "react-i18next"; -import Image from "next/image"; -import { faXmark } from "@fortawesome/free-solid-svg-icons"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; - -import SecretVersionList from "@app/ee/components/SecretVersionList"; -import { WorkspaceEnv } from "@app/hooks/api/types"; - -import Button from "../basic/buttons/Button"; -import Toggle from "../basic/Toggle"; -import CommentField from "./CommentField"; -import CompareSecretsModal from "./CompareSecretsModal"; -import DashboardInputField from "./DashboardInputField"; -import { DeleteActionButton } from "./DeleteActionButton"; -import GenerateSecretMenu from "./GenerateSecretMenu"; - -interface SecretProps { - key: string; - value: string | undefined; - valueOverride: string | undefined; - pos: number; - id: string; - comment: string; -} - -export interface DeleteRowFunctionProps { - ids: string[]; - secretName: string; -} - -interface SideBarProps { - toggleSidebar: (value: string) => void; - data: SecretProps[]; - modifyKey: (value: string, id: string) => void; - modifyValue: (value: string, id: string) => void; - modifyValueOverride: (value: string | undefined, id: string) => void; - modifyComment: (value: string, id: string) => void; - buttonReady: boolean; - savePush: () => void; - sharedToHide: string[]; - setSharedToHide: (values: string[]) => void; - deleteRow: (props: DeleteRowFunctionProps) => void; - workspaceEnvs: WorkspaceEnv[]; - selectedEnv: WorkspaceEnv; - workspaceId: string; -} - -/** - * @param {object} obj - * @param {function} obj.toggleSidebar - function that opens or closes the sidebar - * @param {SecretProps[]} obj.data - data of a certain key valeu pair - * @param {function} obj.modifyKey - function that modifies the secret key - * @param {function} obj.modifyValue - function that modifies the secret value - * @param {function} obj.modifyValueOverride - function that modifies the secret value if it is an override - * @param {boolean} obj.buttonReady - is the button for saving chagnes active - * @param {function} obj.savePush - save changes andp ush secrets - * @param {function} obj.deleteRow - a function to delete a certain keyPair - * @returns the sidebar with 'secret's settings' - */ -const SideBar = ({ - toggleSidebar, - data, - modifyKey, - modifyValue, - modifyValueOverride, - modifyComment, - buttonReady, - savePush, - deleteRow, - workspaceEnvs, - selectedEnv, - workspaceId -}: SideBarProps) => { - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const [isLoading, setIsLoading] = useState(false); - const [overrideEnabled, setOverrideEnabled] = useState(data[0]?.valueOverride !== undefined); - const [compareModal, setCompareModal] = useState(false); - const { t } = useTranslation(); - - return ( -
- {isLoading ? ( -
- infisical loading indicator -
- ) : ( -
-
-

{t("dashboard.sidebar.secret")}

-
null} - role="button" - tabIndex={0} - className="p-1" - onClick={() => toggleSidebar("None")} - > - -
-
-
-

{t("dashboard.sidebar.key")}

-
- -
-
- {data[0]?.value || data[0]?.value === "" ? ( -
-

{t("dashboard.sidebar.value")}

-
- -
-
- -
-
- ) : ( -
- - {t("common.note")}: - - {t("dashboard.sidebar.personal-explanation")} -
- )} -
- {(data[0]?.value || data[0]?.value === "") && ( -
-

{t("dashboard.sidebar.override")}

- -
- )} -
-
- -
-
- -
-
-
- - -
- )} -
-
-
-
-
-
-
- ); -}; - -export default SideBar; diff --git a/frontend/src/components/utilities/secrets/getSecretsForProject.ts b/frontend/src/components/utilities/secrets/getSecretsForProject.ts deleted file mode 100644 index de1a75369..000000000 --- a/frontend/src/components/utilities/secrets/getSecretsForProject.ts +++ /dev/null @@ -1,160 +0,0 @@ -import { Tag } from "public/data/frequentInterfaces"; - -import getSecrets from "@app/pages/api/files/GetSecrets"; -import getLatestFileKey from "@app/pages/api/workspace/getLatestFileKey"; - -import { decryptAssymmetric, decryptSymmetric } from "../cryptography/crypto"; - -interface EncryptedSecretProps { - _id: string; - createdAt: string; - environment: string; - secretCommentCiphertext: string; - secretCommentIV: string; - secretCommentTag: string; - secretKeyCiphertext: string; - secretKeyIV: string; - secretKeyTag: string; - secretValueCiphertext: string; - secretValueIV: string; - secretValueTag: string; - type: "personal" | "shared"; - tags: Tag[]; -} - -interface SecretProps { - key: string; - value: string | undefined; - type: "personal" | "shared"; - comment: string; - id: string; - tags: Tag[]; -} - -interface FunctionProps { - env: string; - setIsKeyAvailable?: any; - setData?: any; - workspaceId: string; -} - -/** - * Gets the secrets for a certain project - * @param {object} obj - * @param {string} obj.env - environment for which we are getting secrets - * @param {boolean} obj.isKeyAvailable - if a person is able to create new key pairs - * @param {function} obj.setData - state function that manages the state of secrets in the dashboard - * @param {string} obj.workspaceId - id of a workspace for which we are getting secrets - */ -const getSecretsForProject = async ({ - env, - setIsKeyAvailable, - setData, - workspaceId -}: FunctionProps) => { - try { - let encryptedSecrets; - try { - encryptedSecrets = await getSecrets(workspaceId, env); - } catch (error) { - console.log("ERROR: Not able to access the latest version of secrets"); - } - - const latestKey = await getLatestFileKey({ workspaceId }); - // This is called isKeyAvailable but what it really means is if a person is able to create new key pairs - if (typeof setIsKeyAvailable === "function") { - setIsKeyAvailable(!latestKey ? encryptedSecrets.length === 0 : true); - } - - const PRIVATE_KEY = localStorage.getItem("PRIVATE_KEY") as string; - - const tempDecryptedSecrets: SecretProps[] = []; - if (latestKey) { - // assymmetrically decrypt symmetric key with local private key - const key = decryptAssymmetric({ - ciphertext: latestKey.latestKey.encryptedKey, - nonce: latestKey.latestKey.nonce, - publicKey: latestKey.latestKey.sender.publicKey, - privateKey: PRIVATE_KEY - }); - - // decrypt secret keys, values, and comments - encryptedSecrets.forEach((secret: EncryptedSecretProps) => { - const plainTextKey = decryptSymmetric({ - ciphertext: secret.secretKeyCiphertext, - iv: secret.secretKeyIV, - tag: secret.secretKeyTag, - key - }); - - let plainTextValue; - if (secret.secretValueCiphertext !== undefined) { - plainTextValue = decryptSymmetric({ - ciphertext: secret.secretValueCiphertext, - iv: secret.secretValueIV, - tag: secret.secretValueTag, - key - }); - } else { - plainTextValue = undefined; - } - - let plainTextComment; - if (secret.secretCommentCiphertext) { - plainTextComment = decryptSymmetric({ - ciphertext: secret.secretCommentCiphertext, - iv: secret.secretCommentIV, - tag: secret.secretCommentTag, - key - }); - } else { - plainTextComment = ""; - } - - tempDecryptedSecrets.push({ - id: secret._id, - key: plainTextKey, - value: plainTextValue, - type: secret.type, - comment: plainTextComment, - tags: secret.tags - }); - }); - } - - const secretKeys = [...new Set(tempDecryptedSecrets.map((secret) => secret.key))]; - - const result = secretKeys.map((key, index) => ({ - id: tempDecryptedSecrets.filter((secret) => secret.key === key && secret.type === "shared")[0] - ?.id, - idOverride: tempDecryptedSecrets.filter( - (secret) => secret.key === key && secret.type === "personal" - )[0]?.id, - pos: index, - key, - value: tempDecryptedSecrets.filter( - (secret) => secret.key === key && secret.type === "shared" - )[0]?.value, - valueOverride: tempDecryptedSecrets.filter( - (secret) => secret.key === key && secret.type === "personal" - )[0]?.value, - comment: tempDecryptedSecrets.filter( - (secret) => secret.key === key && secret.type === "shared" - )[0]?.comment, - tags: tempDecryptedSecrets.filter( - (secret) => secret.key === key && secret.type === "shared" - )[0]?.tags - })); - - if (typeof setData === "function") { - setData(result); - } - - return result; - } catch (error) { - console.log("Something went wrong during accessing or decripting secrets."); - } - return []; -}; - -export default getSecretsForProject; diff --git a/frontend/src/context/OrganizationContext/OrganizationContext.tsx b/frontend/src/context/OrganizationContext/OrganizationContext.tsx index 69fd7fe38..0c0878fa1 100644 --- a/frontend/src/context/OrganizationContext/OrganizationContext.tsx +++ b/frontend/src/context/OrganizationContext/OrganizationContext.tsx @@ -1,6 +1,6 @@ import { createContext, ReactNode, useContext, useMemo } from "react"; -import { useGetOrganization } from "@app/hooks/api"; +import { useGetOrganizations } from "@app/hooks/api"; import { Organization } from "@app/hooks/api/types"; @@ -17,8 +17,8 @@ type Props = { }; export const OrgProvider = ({ children }: Props): JSX.Element => { - const { data: userOrgs, isLoading } = useGetOrganization(); - + const { data: userOrgs, isLoading } = useGetOrganizations(); + // const currentWsOrgID = currentWorkspace?.organization; const currentWsOrgID = localStorage.getItem("orgData.id"); diff --git a/frontend/src/helpers/project.ts b/frontend/src/helpers/project.ts index 30bd996d9..3b3dddf5a 100644 --- a/frontend/src/helpers/project.ts +++ b/frontend/src/helpers/project.ts @@ -2,9 +2,9 @@ import crypto from "crypto"; import { encryptAssymmetric } from "@app/components/utilities/cryptography/crypto"; import encryptSecrets from "@app/components/utilities/secrets/encryptSecrets"; -import addSecrets from "@app/pages/api/files/AddSecrets"; +import { createSecret } from "@app/hooks/api/secrets/queries"; +import { createWorkspace } from "@app/hooks/api/workspace/queries"; import getUser from "@app/pages/api/user/getUser"; -import createWorkspace from "@app/pages/api/workspace/createWorkspace"; import uploadKeys from "@app/pages/api/workspace/uploadKeys"; const secretsToBeAdded = [ @@ -95,10 +95,12 @@ const initProjectHelper = async ({ try { // create new project - project = await createWorkspace({ + const { data: { workspace } } = await createWorkspace({ workspaceName: projectName, organizationId }); + + project = workspace; // create and upload new (encrypted) project key const randomBytes = crypto.randomBytes(16).toString("hex"); @@ -116,19 +118,33 @@ const initProjectHelper = async ({ await uploadKeys(project._id, user._id, ciphertext, nonce); + const workspaceId = project._id; + // encrypt and upload secrets to new project const secrets = await encryptSecrets({ secretsToEncrypt: secretsToBeAdded, - workspaceId: project._id, + workspaceId, env: "dev" }); - - await addSecrets({ - secrets: secrets ?? [], - env: "dev", - workspaceId: project._id + + secrets?.forEach((secret) => { + createSecret({ + workspaceId, + environment: secret.environment, + type: secret.type, + secretKey: secret.secretName, + secretKeyCiphertext: secret.secretKeyCiphertext, + secretKeyIV: secret.secretKeyIV, + secretKeyTag: secret.secretKeyTag, + secretValueCiphertext: secret.secretValueCiphertext, + secretValueIV: secret.secretValueIV, + secretValueTag: secret.secretValueTag, + secretCommentCiphertext: secret.secretCommentCiphertext, + secretCommentIV: secret.secretCommentIV, + secretCommentTag: secret.secretCommentTag, + secretPath: "/" + }); }); - } catch (err) { console.error("Failed to init project in organization", err); } diff --git a/frontend/src/hooks/api/bots/queries.tsx b/frontend/src/hooks/api/bots/queries.tsx index 1c28c9640..3e35a58f0 100644 --- a/frontend/src/hooks/api/bots/queries.tsx +++ b/frontend/src/hooks/api/bots/queries.tsx @@ -8,29 +8,26 @@ const queryKeys = { getBot: (workspaceId: string) => [{ workspaceId }, "bot"] as const }; -const fetchWorkspaceBot = async (workspaceId: string) => { - const { data } = await apiRequest.get<{ bot: TBot }>(`/api/v1/bot/${workspaceId}`); - return data.bot; -}; - export const useGetWorkspaceBot = (workspaceId: string) => useQuery({ queryKey: queryKeys.getBot(workspaceId), - queryFn: () => fetchWorkspaceBot(workspaceId), + queryFn: async () => { + const { data: { bot } } = await apiRequest.get<{ bot: TBot }>(`/api/v1/bot/${workspaceId}`); + return bot; + }, enabled: Boolean(workspaceId) }); -// mutation - export const useUpdateBotActiveStatus = () => { const queryClient = useQueryClient(); return useMutation<{}, {}, TSetBotActiveStatusDto>({ - mutationFn: ({ botId, isActive, botKey }) => - apiRequest.patch(`/api/v1/bot/${botId}/active`, { + mutationFn: ({ botId, isActive, botKey }) => { + return apiRequest.patch(`/api/v1/bot/${botId}/active`, { isActive, botKey - }), + }); + }, onSuccess: (_, { workspaceId }) => { queryClient.invalidateQueries(queryKeys.getBot(workspaceId)); } diff --git a/frontend/src/hooks/api/organization/index.ts b/frontend/src/hooks/api/organization/index.ts index ded8c3f29..7134d0501 100644 --- a/frontend/src/hooks/api/organization/index.ts +++ b/frontend/src/hooks/api/organization/index.ts @@ -4,7 +4,7 @@ export { useCreateCustomerPortalSession, useDeleteOrgPmtMethod, useDeleteOrgTaxId, - useGetOrganization, + useGetOrganizations, useGetOrgBillingDetails, useGetOrgInvoices, useGetOrgLicenses, diff --git a/frontend/src/hooks/api/organization/queries.tsx b/frontend/src/hooks/api/organization/queries.tsx index 66065c6ef..f7bd3e973 100644 --- a/frontend/src/hooks/api/organization/queries.tsx +++ b/frontend/src/hooks/api/organization/queries.tsx @@ -12,10 +12,11 @@ import { PmtMethod, ProductsTable, RenameOrgDTO, - TaxID} from "./types"; + TaxID +} from "./types"; const organizationKeys = { - getUserOrganization: ["organization"] as const, + getUserOrganizations: ["organization"] as const, getOrgPlanBillingInfo: (orgId: string) => [{ orgId }, "organization-plan-billing"] as const, getOrgPlanTable: (orgId: string) => [{ orgId }, "organization-plan-table"] as const, getOrgPlansTable: (orgId: string, billingCycle: "monthly" | "yearly") => [{ orgId, billingCycle }, "organization-plans-table"] as const, @@ -26,13 +27,12 @@ const organizationKeys = { getOrgLicenses: (orgId: string) => [{ orgId }, "organization-licenses"] as const }; -export const useGetOrganization = () => { +export const useGetOrganizations = () => { return useQuery({ - queryKey: organizationKeys.getUserOrganization, + queryKey: organizationKeys.getUserOrganizations, queryFn: async () => { - const { data } = await apiRequest.get<{ organizations: Organization[] }>("/api/v1/organization"); - - return data.organizations; + const { data: { organizations } } = await apiRequest.get<{ organizations: Organization[] }>("/api/v1/organization"); + return organizations; } }); } @@ -44,7 +44,7 @@ export const useRenameOrg = () => { mutationFn: ({ newOrgName, orgId }) => apiRequest.patch(`/api/v1/organization/${orgId}/name`, { name: newOrgName }), onSuccess: () => { - queryClient.invalidateQueries(organizationKeys.getUserOrganization); + queryClient.invalidateQueries(organizationKeys.getUserOrganizations); } }); }; diff --git a/frontend/src/hooks/api/secrets/queries.tsx b/frontend/src/hooks/api/secrets/queries.tsx index 6f65a8edd..e1c3e10bf 100644 --- a/frontend/src/hooks/api/secrets/queries.tsx +++ b/frontend/src/hooks/api/secrets/queries.tsx @@ -11,13 +11,13 @@ import { apiRequest } from "@app/config/request"; import { secretSnapshotKeys } from "../secretSnapshots/queries"; import { BatchSecretDTO, + CreateSecretDTO, DecryptedSecret, EncryptedSecret, EncryptedSecretVersion, GetProjectSecretsDTO, GetSecretVersionsDTO, - TGetProjectSecretsAllEnvDTO -} from "./types"; + TGetProjectSecretsAllEnvDTO} from "./types"; export const secretKeys = { // this is also used in secretSnapshot part @@ -324,3 +324,24 @@ export const useBatchSecretsOp = () => { } }); }; + +export const createSecret = async (dto: CreateSecretDTO) => { + const { data } = await apiRequest.post(`/api/v3/secrets/${dto.secretKey}`, dto); + return data; +} + +export const useCreateSecret = () => { + const queryClient = useQueryClient(); + + return useMutation<{}, {}, CreateSecretDTO>({ + mutationFn: async (dto) => { + const data = createSecret(dto); + return data; + }, + onSuccess: (_, dto) => { + queryClient.invalidateQueries( + secretKeys.getProjectSecret(dto.workspaceId, dto.environment) + ); + } + }); +}; \ No newline at end of file diff --git a/frontend/src/hooks/api/secrets/types.ts b/frontend/src/hooks/api/secrets/types.ts index c317da2c9..964bac6c2 100644 --- a/frontend/src/hooks/api/secrets/types.ts +++ b/frontend/src/hooks/api/secrets/types.ts @@ -147,3 +147,22 @@ export type TDeleteSecretsV3DTO = { secretPath: string; secretName: string; }; + +// --- v3 + +export type CreateSecretDTO = { + workspaceId: string; + environment: string; + type: "shared" | "personal"; + secretKey: string; + secretKeyCiphertext: string; + secretKeyIV: string; + secretKeyTag: string; + secretValueCiphertext: string; + secretValueIV: string; + secretValueTag: string; + secretCommentCiphertext: string; + secretCommentIV: string; + secretCommentTag: string; + secretPath: string; +} \ No newline at end of file diff --git a/frontend/src/hooks/api/serviceTokens/queries.tsx b/frontend/src/hooks/api/serviceTokens/queries.tsx index 8cabdb9b6..4a0241cdf 100644 --- a/frontend/src/hooks/api/serviceTokens/queries.tsx +++ b/frontend/src/hooks/api/serviceTokens/queries.tsx @@ -23,12 +23,13 @@ const fetchWorkspaceServiceTokens = async (workspaceID: string) => { type UseGetWorkspaceServiceTokensProps = { workspaceID: string }; -export const useGetUserWsServiceTokens = ({ workspaceID }: UseGetWorkspaceServiceTokensProps) => - useQuery({ +export const useGetUserWsServiceTokens = ({ workspaceID }: UseGetWorkspaceServiceTokensProps) => { + return useQuery({ queryKey: serviceTokenKeys.getAllWorkspaceServiceToken(workspaceID), queryFn: () => fetchWorkspaceServiceTokens(workspaceID), enabled: Boolean(workspaceID) }); +} // mutation export const useCreateServiceToken = () => { @@ -36,6 +37,7 @@ export const useCreateServiceToken = () => { return useMutation({ mutationFn: async (body) => { + console.log("useCreateServiceToken"); const { data } = await apiRequest.post("/api/v2/service-token/", body); data.serviceToken += `.${body.randomBytes}`; return data; @@ -51,6 +53,7 @@ export const useDeleteServiceToken = () => { return useMutation({ mutationFn: async (serviceTokenId) => { + console.log("useDeleteServiceToken"); const { data } = await apiRequest.delete(`/api/v2/service-token/${serviceTokenId}`); return data; }, diff --git a/frontend/src/hooks/api/workspace/queries.tsx b/frontend/src/hooks/api/workspace/queries.tsx index 9833c06fd..860ee7757 100644 --- a/frontend/src/hooks/api/workspace/queries.tsx +++ b/frontend/src/hooks/api/workspace/queries.tsx @@ -166,13 +166,21 @@ export const useGetWorkspaceIntegrations = (workspaceId: string) => enabled: Boolean(workspaceId) }); -// mutation +export const createWorkspace = ({ + organizationId, + workspaceName +}: CreateWorkspaceDTO): Promise<{ data: { workspace: Workspace } }> => { + return apiRequest.post("/api/v1/workspace", { workspaceName, organizationId }); +} + export const useCreateWorkspace = () => { const queryClient = useQueryClient(); return useMutation<{ data: { workspace: Workspace } }, {}, CreateWorkspaceDTO>({ - mutationFn: async ({ organizationId, workspaceName }) => - apiRequest.post("/api/v1/workspace", { workspaceName, organizationId }), + mutationFn: async ({ organizationId, workspaceName }) => createWorkspace({ + organizationId, + workspaceName + }), onSuccess: () => { queryClient.invalidateQueries(workspaceKeys.getAllUserWorkspace); } @@ -183,8 +191,9 @@ export const useRenameWorkspace = () => { const queryClient = useQueryClient(); return useMutation<{}, {}, RenameWorkspaceDTO>({ - mutationFn: ({ workspaceID, newWorkspaceName }) => - apiRequest.post(`/api/v1/workspace/${workspaceID}/name`, { name: newWorkspaceName }), + mutationFn: ({ workspaceID, newWorkspaceName }) => { + return apiRequest.post(`/api/v1/workspace/${workspaceID}/name`, { name: newWorkspaceName }); + }, onSuccess: () => { queryClient.invalidateQueries(workspaceKeys.getAllUserWorkspace); } @@ -220,11 +229,12 @@ export const useCreateWsEnvironment = () => { const queryClient = useQueryClient(); return useMutation<{}, {}, CreateEnvironmentDTO>({ - mutationFn: ({ workspaceID, environmentName, environmentSlug }) => - apiRequest.post(`/api/v2/workspace/${workspaceID}/environments`, { + mutationFn: ({ workspaceID, environmentName, environmentSlug }) => { + return apiRequest.post(`/api/v2/workspace/${workspaceID}/environments`, { environmentName, environmentSlug - }), + }); + }, onSuccess: () => { queryClient.invalidateQueries(workspaceKeys.getAllUserWorkspace); } @@ -235,12 +245,13 @@ export const useUpdateWsEnvironment = () => { const queryClient = useQueryClient(); return useMutation<{}, {}, UpdateEnvironmentDTO>({ - mutationFn: ({ workspaceID, environmentName, environmentSlug, oldEnvironmentSlug }) => - apiRequest.put(`/api/v2/workspace/${workspaceID}/environments`, { + mutationFn: ({ workspaceID, environmentName, environmentSlug, oldEnvironmentSlug }) => { + return apiRequest.put(`/api/v2/workspace/${workspaceID}/environments`, { environmentName, environmentSlug, oldEnvironmentSlug - }), + }); + }, onSuccess: () => { queryClient.invalidateQueries(workspaceKeys.getAllUserWorkspace); } @@ -251,10 +262,11 @@ export const useDeleteWsEnvironment = () => { const queryClient = useQueryClient(); return useMutation<{}, {}, DeleteEnvironmentDTO>({ - mutationFn: ({ workspaceID, environmentSlug }) => - apiRequest.delete(`/api/v2/workspace/${workspaceID}/environments`, { + mutationFn: ({ workspaceID, environmentSlug }) => { + return apiRequest.delete(`/api/v2/workspace/${workspaceID}/environments`, { data: { environmentSlug } - }), + }); + }, onSuccess: () => { queryClient.invalidateQueries(workspaceKeys.getAllUserWorkspace); } diff --git a/frontend/src/pages/api/bot/getBot.ts b/frontend/src/pages/api/bot/getBot.ts deleted file mode 100644 index 2579db67f..000000000 --- a/frontend/src/pages/api/bot/getBot.ts +++ /dev/null @@ -1,27 +0,0 @@ -import SecurityClient from "@app/components/utilities/SecurityClient"; - -interface Props { - workspaceId: string; -} - -/** - * This function fetches the bot for a project - * @param {Object} obj - * @param {String} obj.workspaceId - * @returns - */ -const getBot = async ({ workspaceId }: Props) => - SecurityClient.fetchCall(`/api/v1/bot/${workspaceId}`, { - method: "GET", - headers: { - "Content-Type": "application/json" - } - }).then(async (res) => { - if (res && res.status === 200) { - return (await res.json()).bot; - } - console.log("Failed to get bot for project"); - return undefined; - }); - -export default getBot; diff --git a/frontend/src/pages/api/bot/setBotActiveStatus.ts b/frontend/src/pages/api/bot/setBotActiveStatus.ts deleted file mode 100644 index 68852b50c..000000000 --- a/frontend/src/pages/api/bot/setBotActiveStatus.ts +++ /dev/null @@ -1,42 +0,0 @@ -import SecurityClient from "@app/components/utilities/SecurityClient"; - -interface BotKey { - encryptedKey: string; - nonce: string; -} - -interface Props { - botId: string; - isActive: boolean; - botKey?: BotKey; -} - -/** - * This function sets the active status of a bot and shares a copy of - * the project key (encrypted under the bot's public key) with the - * project's bot - * @param {Object} obj - * @param {String} obj.botId - * @param {String} obj.isActive - * @param {Object} obj.botKey - * @returns - */ -const setBotActiveStatus = async ({ botId, isActive, botKey }: Props) => - SecurityClient.fetchCall(`/api/v1/bot/${botId}/active`, { - method: "PATCH", - headers: { - "Content-Type": "application/json" - }, - body: JSON.stringify({ - isActive, - botKey - }) - }).then(async (res) => { - if (res && res.status === 200) { - return res.json(); - } - console.log("Failed to get bot for project"); - return undefined; - }); - -export default setBotActiveStatus; diff --git a/frontend/src/pages/api/environments/createEnvironment.ts b/frontend/src/pages/api/environments/createEnvironment.ts deleted file mode 100644 index 52e7562c9..000000000 --- a/frontend/src/pages/api/environments/createEnvironment.ts +++ /dev/null @@ -1,28 +0,0 @@ -import SecurityClient from "@app/components/utilities/SecurityClient"; - -type NewEnvironmentInfo = { - environmentSlug: string; - environmentName: string; -}; - -/** - * This route deletes a specified workspace. - * @param {*} workspaceId - * @returns - */ -const createEnvironment = (workspaceId: string, newEnv: NewEnvironmentInfo) => - SecurityClient.fetchCall(`/api/v2/workspace/${workspaceId}/environments`, { - method: "POST", - headers: { - "Content-Type": "application/json" - }, - body: JSON.stringify(newEnv) - }).then(async (res) => { - if (res && res.status === 200) { - return res; - } - console.log("Failed to create environment"); - return undefined; - }); - -export default createEnvironment; diff --git a/frontend/src/pages/api/environments/deleteEnvironment.ts b/frontend/src/pages/api/environments/deleteEnvironment.ts deleted file mode 100644 index d94f411f7..000000000 --- a/frontend/src/pages/api/environments/deleteEnvironment.ts +++ /dev/null @@ -1,22 +0,0 @@ -import SecurityClient from "@app/components/utilities/SecurityClient"; -/** - * This route deletes a specified env. - * @param {*} workspaceId - * @returns - */ -const deleteEnvironment = (workspaceId: string, environmentSlug: string) => - SecurityClient.fetchCall(`/api/v2/workspace/${workspaceId}/environments`, { - method: "DELETE", - headers: { - "Content-Type": "application/json" - }, - body: JSON.stringify({ environmentSlug }) - }).then(async (res) => { - if (res && res.status === 200) { - return res; - } - console.log("Failed to delete environment"); - return undefined; - }); - -export default deleteEnvironment; diff --git a/frontend/src/pages/api/environments/updateEnvironment.ts b/frontend/src/pages/api/environments/updateEnvironment.ts deleted file mode 100644 index 2e671de98..000000000 --- a/frontend/src/pages/api/environments/updateEnvironment.ts +++ /dev/null @@ -1,29 +0,0 @@ -import SecurityClient from "@app/components/utilities/SecurityClient"; - -type EnvironmentInfo = { - oldEnvironmentSlug: string; - environmentSlug: string; - environmentName: string; -}; - -/** - * This route updates a specified environment. - * @param {*} workspaceId - * @returns - */ -const updateEnvironment = (workspaceId: string, env: EnvironmentInfo) => - SecurityClient.fetchCall(`/api/v2/workspace/${workspaceId}/environments`, { - method: "PUT", - headers: { - "Content-Type": "application/json" - }, - body: JSON.stringify(env) - }).then(async (res) => { - if (res && res.status === 200) { - return res; - } - console.log("Failed to update environment"); - return undefined; - }); - -export default updateEnvironment; diff --git a/frontend/src/pages/api/files/AddSecrets.ts b/frontend/src/pages/api/files/AddSecrets.ts deleted file mode 100644 index fe23aa3a0..000000000 --- a/frontend/src/pages/api/files/AddSecrets.ts +++ /dev/null @@ -1,54 +0,0 @@ -import SecurityClient from "@app/components/utilities/SecurityClient"; - -interface EncryptedSecretProps { - id: string; - createdAt: string; - environment: string; - secretCommentCiphertext: string; - secretCommentIV: string; - secretCommentTag: string; - secretKeyCiphertext: string; - secretKeyIV: string; - secretKeyTag: string; - secretValueCiphertext: string; - secretValueIV: string; - secretValueTag: string; - type: "personal" | "shared"; -} - -/** - * This function adds secrets to a certain project - * @param {object} obj - * @param {EncryptedSecretProps} obj.secrets - the ids of secrets that we want to add - * @param {string} obj.env - the environment to which we are adding secrets - * @param {string} obj.workspaceId - the project to which we are adding secrets - * @returns - */ -const addSecrets = async ({ - secrets, - env, - workspaceId -}: { - secrets: EncryptedSecretProps[]; - env: string; - workspaceId: string; -}) => - SecurityClient.fetchCall("/api/v2/secrets", { - method: "POST", - headers: { - "Content-Type": "application/json" - }, - body: JSON.stringify({ - environment: env, - workspaceId, - secrets - }) - }).then(async (res) => { - if (res && res.status === 200) { - return res.json(); - } - console.log("Failed to add certain project secrets"); - return undefined; - }); - -export default addSecrets; diff --git a/frontend/src/pages/api/files/DeleteSecrets.ts b/frontend/src/pages/api/files/DeleteSecrets.ts deleted file mode 100644 index 6ea22b8e4..000000000 --- a/frontend/src/pages/api/files/DeleteSecrets.ts +++ /dev/null @@ -1,25 +0,0 @@ -import SecurityClient from "@app/components/utilities/SecurityClient"; - -/** - * This function deletes certain secrets from a certain project - * @param {string[]} secretIds - the ids of secrets that we want to be deleted - * @returns - */ -const deleteSecrets = async ({ secretIds }: { secretIds: string[] }) => - SecurityClient.fetchCall("/api/v2/secrets", { - method: "DELETE", - headers: { - "Content-Type": "application/json" - }, - body: JSON.stringify({ - secretIds - }) - }).then(async (res) => { - if (res && res.status === 200) { - return res.json(); - } - console.log("Failed to delete certain project secrets"); - return undefined; - }); - -export default deleteSecrets; diff --git a/frontend/src/pages/api/files/GetSecrets.ts b/frontend/src/pages/api/files/GetSecrets.ts deleted file mode 100644 index d8e8caa40..000000000 --- a/frontend/src/pages/api/files/GetSecrets.ts +++ /dev/null @@ -1,29 +0,0 @@ -import SecurityClient from "@app/components/utilities/SecurityClient"; - -/** - * This function fetches the encrypted secrets for a certain project - * @param {string} workspaceId - project is for which a user is trying to get secrets - * @param {string} env - environment of a project for which a user is trying ot get secrets - * @returns - */ -const getSecrets = async (workspaceId: string, env: string) => - SecurityClient.fetchCall( - `/api/v2/secrets?${new URLSearchParams({ - environment: env, - workspaceId - })}`, - { - method: "GET", - headers: { - "Content-Type": "application/json" - } - } - ).then(async (res) => { - if (res && res.status === 200) { - return (await res.json()).secrets; - } - console.log("Failed to get project secrets"); - return undefined; - }); - -export default getSecrets; diff --git a/frontend/src/pages/api/files/UpdateSecrets.ts b/frontend/src/pages/api/files/UpdateSecrets.ts deleted file mode 100644 index 03b8a18fb..000000000 --- a/frontend/src/pages/api/files/UpdateSecrets.ts +++ /dev/null @@ -1,42 +0,0 @@ -import SecurityClient from "@app/components/utilities/SecurityClient"; - -interface EncryptedSecretProps { - id: string; - createdAt: string; - environment: string; - secretCommentCiphertext: string; - secretCommentIV: string; - secretCommentTag: string; - secretKeyCiphertext: string; - secretKeyIV: string; - secretKeyTag: string; - secretValueCiphertext: string; - secretValueIV: string; - secretValueTag: string; - type: "personal" | "shared"; -} - -/** - * This function updates certain secrets in a certain project - * @param {object} obj - * @param {EncryptedSecretProps[]} obj.secrets - the ids of secrets that we want to update - * @returns - */ -const updateSecrets = async ({ secrets }: { secrets: EncryptedSecretProps[] }) => - SecurityClient.fetchCall("/api/v2/secrets", { - method: "PATCH", - headers: { - "Content-Type": "application/json" - }, - body: JSON.stringify({ - secrets - }) - }).then(async (res) => { - if (res && res.status === 200) { - return res.json(); - } - console.log("Failed to update certain project secrets"); - return undefined; - }); - -export default updateSecrets; diff --git a/frontend/src/pages/api/files/UploadSecrets.ts b/frontend/src/pages/api/files/UploadSecrets.ts deleted file mode 100644 index b23f4e5bb..000000000 --- a/frontend/src/pages/api/files/UploadSecrets.ts +++ /dev/null @@ -1,39 +0,0 @@ -import SecurityClient from "@app/components/utilities/SecurityClient"; - -interface Props { - workspaceId: string; - secrets: any; - keys: string; - environment: string; -} - -/** - * This function uploads the encrypted .env file - * @param {object} obj - * @param {string} obj.workspaceId - * @param {} obj.secrets - * @param {} obj.keys - * @param {string} obj.environment - * @returns - */ -const uploadSecrets = async ({ workspaceId, secrets, keys, environment }: Props) => - SecurityClient.fetchCall(`/api/v2/workspace/${workspaceId}/secrets`, { - method: "POST", - headers: { - "Content-Type": "application/json" - }, - body: JSON.stringify({ - secrets, - keys, - environment, - channel: "web" - }) - }).then(async (res) => { - if (res && res.status === 200) { - return res; - } - console.log("Failed to push secrets"); - return undefined; - }); - -export default uploadSecrets; diff --git a/frontend/src/pages/api/files/batchSecrets.ts b/frontend/src/pages/api/files/batchSecrets.ts deleted file mode 100644 index 69b88a451..000000000 --- a/frontend/src/pages/api/files/batchSecrets.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { apiRequest } from "@app/config/request"; - -interface RequestType { - method: string; - secret: { - type: string; - secretKeyCiphertext: string; - secretKeyIV: string; - secretKeyTag: string; - secretValueCiphertext: string; - secretValueIV: string; - secretValueTag: string; - secretCommentCiphertext: string; - secretCommentIV: string; - secretCommentTag: string; - tags: string[]; - } -} - -const batchSecrets = async ({ - workspaceId, - environment, - requests -}: { - workspaceId: string; - environment: string; - requests: RequestType[]; -}) => { - const { data } = await apiRequest.post("/api/v2/secrets/batch", { - workspaceId, - environment, - requests - }); - - return data; -} - -export default batchSecrets; \ No newline at end of file diff --git a/frontend/src/pages/api/organization/GetOrgSubscription.ts b/frontend/src/pages/api/organization/GetOrgSubscription.ts deleted file mode 100644 index 9cef7f10a..000000000 --- a/frontend/src/pages/api/organization/GetOrgSubscription.ts +++ /dev/null @@ -1,23 +0,0 @@ -import SecurityClient from "@app/components/utilities/SecurityClient"; - -/** - * This route lets us get the current subscription of an org. - * @param {*} req - * @param {*} res - * @returns - */ -const getOrganizationSubscriptions = (req: { orgId: string }) => - SecurityClient.fetchCall(`/api/v1/organization/${req.orgId}/subscriptions`, { - method: "GET", - headers: { - "Content-Type": "application/json" - } - }).then(async (res) => { - if (res && res.status === 200) { - return (await res.json()).subscriptions; - } - console.log("Failed to get org subscriptions"); - return undefined; - }); - -export default getOrganizationSubscriptions; diff --git a/frontend/src/pages/api/organization/getOrgs.ts b/frontend/src/pages/api/organization/getOrgs.ts index da92206ec..09cd0f022 100644 --- a/frontend/src/pages/api/organization/getOrgs.ts +++ b/frontend/src/pages/api/organization/getOrgs.ts @@ -4,18 +4,20 @@ import SecurityClient from "@app/components/utilities/SecurityClient"; * This route lets us get the all the orgs of a certain user. * @returns */ -const getOrganizations = () => - SecurityClient.fetchCall("/api/v1/organization", { +const getOrganizations = () => { + return SecurityClient.fetchCall("/api/v1/organization", { method: "GET", headers: { "Content-Type": "application/json" } }).then(async (res) => { if (res?.status === 200) { - return (await res.json()).organizations; + const {organizations} = await res.json(); + return organizations; } console.log("Failed to get orgs of a user"); return undefined; }); +} export default getOrganizations; diff --git a/frontend/src/pages/api/serviceToken/addServiceToken.ts b/frontend/src/pages/api/serviceToken/addServiceToken.ts deleted file mode 100644 index f3ecc661d..000000000 --- a/frontend/src/pages/api/serviceToken/addServiceToken.ts +++ /dev/null @@ -1,56 +0,0 @@ -import SecurityClient from "@app/components/utilities/SecurityClient"; - -interface Props { - name: string; - workspaceId: string; - environment: string; - expiresIn: number; - encryptedKey: string; - iv: string; - tag: string; -} - -/** - * This route adds a service token for a specific user in a project - * @param {object} obj - * @param {string} obj.name - name of the service token - * @param {string} obj.workspaceId - workspace for which we are issuing the token - * @param {string} obj.environment - environment for which we are issuing the token - * @param {string} obj.expiresIn - how soon the service token expires in ms - * @param {string} obj.encryptedKey - encrypted project key through random symmetric encryption - * @param {string} obj.iv - obtained through symmetric encryption - * @param {string} obj.tag - obtained through symmetric encryption - * @returns - */ -const addServiceToken = ({ - name, - workspaceId, - environment, - expiresIn, - encryptedKey, - iv, - tag -}: Props) => - SecurityClient.fetchCall("/api/v2/service-token/", { - method: "POST", - headers: { - "Content-Type": "application/json" - }, - body: JSON.stringify({ - name, - workspaceId, - environment, - expiresIn, - encryptedKey, - iv, - tag - }) - }).then(async (res) => { - if (res && res.status === 200) { - return res.json(); - } - console.log("Failed to add service tokens"); - return undefined; - }); - -export default addServiceToken; diff --git a/frontend/src/pages/api/serviceToken/deleteServiceToken.ts b/frontend/src/pages/api/serviceToken/deleteServiceToken.ts deleted file mode 100644 index e2d4b97ec..000000000 --- a/frontend/src/pages/api/serviceToken/deleteServiceToken.ts +++ /dev/null @@ -1,27 +0,0 @@ -import SecurityClient from "@app/components/utilities/SecurityClient"; - -interface Props { - serviceTokenId: string; -} - -/** - * This route revokes a specific service token - * @param {object} obj - * @param {string} obj.serviceTokenId - id of a cervice token that we want to delete - * @returns - */ -const deleteServiceToken = ({ serviceTokenId }: Props) => - SecurityClient.fetchCall(`/api/v2/service-token/${serviceTokenId}`, { - method: "DELETE", - headers: { - "Content-Type": "application/json" - } - }).then(async (res) => { - if (res && res.status === 200) { - return res.json(); - } - console.log("Failed to delete a service token"); - return undefined; - }); - -export default deleteServiceToken; diff --git a/frontend/src/pages/api/serviceToken/getServiceTokens.ts b/frontend/src/pages/api/serviceToken/getServiceTokens.ts deleted file mode 100644 index 38f02cb52..000000000 --- a/frontend/src/pages/api/serviceToken/getServiceTokens.ts +++ /dev/null @@ -1,22 +0,0 @@ -import SecurityClient from "@app/components/utilities/SecurityClient"; - -/** - * This route gets service tokens for a specific user in a project - * @param {*} param0 - * @returns - */ -const getServiceTokens = ({ workspaceId }: { workspaceId: string }) => - SecurityClient.fetchCall(`/api/v2/workspace/${workspaceId}/service-token-data`, { - method: "GET", - headers: { - "Content-Type": "application/json" - } - }).then(async (res) => { - if (res && res.status === 200) { - return (await res.json()).serviceTokenData; - } - console.log("Failed to get service tokens"); - return undefined; - }); - -export default getServiceTokens; diff --git a/frontend/src/pages/api/workspace/createWorkspace.ts b/frontend/src/pages/api/workspace/createWorkspace.ts deleted file mode 100644 index f241a4fe8..000000000 --- a/frontend/src/pages/api/workspace/createWorkspace.ts +++ /dev/null @@ -1,33 +0,0 @@ -import SecurityClient from "@app/components/utilities/SecurityClient"; - -/** - * This route creates a new workspace for a user within a certain organization. - * @param {string} workspaceName - project Name - * @param {string} organizationId - org ID - * @returns - */ -const createWorkspace = ({ - workspaceName, - organizationId -}: { - workspaceName: string; - organizationId: string; -}) => - SecurityClient.fetchCall("/api/v1/workspace", { - method: "POST", - headers: { - "Content-Type": "application/json" - }, - body: JSON.stringify({ - workspaceName, - organizationId - }) - }).then(async (res) => { - if (res?.status === 200) { - return (await res.json()).workspace; - } - console.log("Failed to create a project"); - return undefined; - }); - -export default createWorkspace; diff --git a/frontend/src/pages/api/workspace/renameWorkspace.ts b/frontend/src/pages/api/workspace/renameWorkspace.ts deleted file mode 100644 index 7e910a42a..000000000 --- a/frontend/src/pages/api/workspace/renameWorkspace.ts +++ /dev/null @@ -1,26 +0,0 @@ -import SecurityClient from "@app/components/utilities/SecurityClient"; - -/** - * This route lets us rename a certain workspace. - * @param {*} req - * @param {*} res - * @returns - */ -const renameWorkspace = (workspaceId: string, newWorkspaceName: string) => - SecurityClient.fetchCall(`/api/v1/workspace/${workspaceId}/name`, { - method: "POST", - headers: { - "Content-Type": "application/json" - }, - body: JSON.stringify({ - name: newWorkspaceName - }) - }).then(async (res) => { - if (res && res.status === 200) { - return res; - } - console.log("Failed to rename a project"); - return undefined; - }); - -export default renameWorkspace; diff --git a/frontend/src/views/Settings/ProjectSettingsPage/components/E2EESection/E2EESection.tsx b/frontend/src/views/Settings/ProjectSettingsPage/components/E2EESection/E2EESection.tsx index 58e88e593..8e765f3ff 100644 --- a/frontend/src/views/Settings/ProjectSettingsPage/components/E2EESection/E2EESection.tsx +++ b/frontend/src/views/Settings/ProjectSettingsPage/components/E2EESection/E2EESection.tsx @@ -1,30 +1,17 @@ -import { useEffect, useState } from "react"; - import { decryptAssymmetric, encryptAssymmetric } from "@app/components/utilities/cryptography/crypto"; import { Checkbox } from "@app/components/v2"; import { useWorkspace } from "@app/context"; +import { useGetWorkspaceBot, useUpdateBotActiveStatus } from "@app/hooks/api"; -import getBot from "../../../../../pages/api/bot/getBot"; -import setBotActiveStatus from "../../../../../pages/api/bot/setBotActiveStatus"; import getLatestFileKey from "../../../../../pages/api/workspace/getLatestFileKey"; export const E2EESection = () => { const { currentWorkspace } = useWorkspace(); - const [bot, setBot] = useState(null); - - useEffect(() => { - (async () => { - if (currentWorkspace) { - // get project bot - setBot(await getBot({ - workspaceId: currentWorkspace._id - })); - } - })(); - }, [currentWorkspace]); + const { data: bot } = useGetWorkspaceBot(currentWorkspace?._id ?? ""); + const { mutateAsync: updateBotActiveStatus } = useUpdateBotActiveStatus(); /** * Activate bot for project by performing the following steps: @@ -69,22 +56,20 @@ export const E2EESection = () => { encryptedKey: ciphertext, nonce }; - - const botx = await setBotActiveStatus({ - botId: bot._id, + + await updateBotActiveStatus({ + workspaceId: currentWorkspace._id, + botKey, isActive: true, - botKey + botId: bot._id }); - - setBot(botx.bot); } else { // bot is active -> deactivate bot - const botx = await setBotActiveStatus({ + await updateBotActiveStatus({ + isActive: false, botId: bot._id, - isActive: false + workspaceId: currentWorkspace._id }); - - setBot(botx.bot); } } } catch (err) {