Ghost user and migration finished!

This commit is contained in:
Daniel Hougaard
2024-02-22 05:00:25 +01:00
parent 0cfab8ab6b
commit 8333250b0b
24 changed files with 810 additions and 123 deletions
@@ -56,7 +56,6 @@ const encryptSecrets = async ({
publicKey: wsKey.sender.publicKey,
privateKey: PRIVATE_KEY
});
} else {
// case: a (shared) key does not exist for the workspace
randomBytes = crypto.randomBytes(16).toString("hex");
@@ -116,7 +115,6 @@ const encryptSecrets = async ({
return result;
});
} catch (error) {
console.log("Error while encrypting secrets");
}
+2 -2
View File
@@ -8,9 +8,9 @@ const encKeyKeys = {
getUserWorkspaceKey: (workspaceID: string) => ["workspace-key-pair", { workspaceID }] as const
};
export const fetchUserWsKey = async (workspaceID: string) => {
export const fetchUserWsKey = async (projectId: string) => {
const { data } = await apiRequest.get<UserWsKeyPair>(
`/api/v2/workspace/${workspaceID}/encrypted-key`
`/api/v2/workspace/${projectId}/encrypted-key`
);
return data;
+1 -1
View File
@@ -51,7 +51,7 @@ export const useAddUserToWsNonE2EE = () => {
return useMutation<{}, {}, AddUserToWsDTONonE2EE>({
mutationFn: async ({ projectId, emails }) => {
const { data } = await apiRequest.post(`/api/v3/projects/${projectId}/memberships`, {
const { data } = await apiRequest.post(`/api/v2/workspace/${projectId}/memberships`, {
emails
});
return data;
+2 -1
View File
@@ -21,5 +21,6 @@ export {
useToggleAutoCapitalization,
useUpdateIdentityWorkspaceRole,
useUpdateUserWorkspaceRole,
useUpdateWsEnvironment
useUpdateWsEnvironment,
useUpgradeProject
} from "./queries";
+16 -1
View File
@@ -61,6 +61,21 @@ export const fetchWorkspaceSecrets = async (workspaceId: string) => {
return secrets;
};
export const useUpgradeProject = () => {
const queryClient = useQueryClient();
return useMutation<{}, {}, { projectId: string; privateKey: string }>({
mutationFn: ({ projectId, privateKey }) => {
return apiRequest.post(`/api/v2/workspace/${projectId}/upgrade`, {
userPrivateKey: privateKey
});
},
onSuccess: () => {
queryClient.invalidateQueries(workspaceKeys.getAllUserWorkspace);
}
});
};
const fetchUserWorkspaces = async () => {
const { data } = await apiRequest.get<{ workspaces: Workspace[] }>("/api/v1/workspace");
return data.workspaces;
@@ -160,7 +175,7 @@ export const createWorkspace = ({
organizationId,
projectName
}: CreateWorkspaceDTO): Promise<{ data: { project: Workspace } }> => {
return apiRequest.post("/api/v3/projects", { projectName, organizationId });
return apiRequest.post("/api/v2/workspace", { projectName, organizationId });
};
export const useCreateWorkspace = () => {
+1 -1
View File
@@ -3,7 +3,7 @@ export type Workspace = {
id: string;
name: string;
orgId: string;
e2ee: boolean;
version: "v1" | "v2";
autoCapitalization: boolean;
environments: WorkspaceEnv[];
slug: string;
+16 -41
View File
@@ -4,7 +4,6 @@
/* eslint-disable vars-on-top */
/* eslint-disable no-var */
/* eslint-disable func-names */
import crypto from "crypto";
import { useEffect } from "react";
import { Controller, useForm } from "react-hook-form";
@@ -34,7 +33,6 @@ import * as yup from "yup";
import { useNotificationContext } from "@app/components/context/Notifications/NotificationProvider";
import { OrgPermissionCan } from "@app/components/permissions";
import { tempLocalStorage } from "@app/components/utilities/checks/tempLocalStorage";
import { encryptAssymmetric } from "@app/components/utilities/cryptography/crypto";
import {
Button,
Checkbox,
@@ -62,16 +60,14 @@ import {
import { usePopUp } from "@app/hooks";
import {
fetchOrgUsers,
useAddUserToWsE2EE,
useAddUserToWsNonE2EE,
useCreateWorkspace,
useGetOrgTrialUrl,
useGetSecretApprovalRequestCount,
useGetUserAction,
useLogoutUser,
useRegisterUserAction,
useUploadWsKey
useRegisterUserAction
} from "@app/hooks/api";
import { fetchUserWsKey } from "@app/hooks/api/keys/queries";
import { CreateOrgModal } from "@app/views/Org/components";
interface LayoutProps {
@@ -129,8 +125,8 @@ export const AppLayout = ({ children }: LayoutProps) => {
: true;
const createWs = useCreateWorkspace();
const uploadWsKey = useUploadWsKey();
const addWsUser = useAddUserToWsE2EE();
const addUsersToProject = useAddUserToWsNonE2EE();
const infisicalPlatformVersion = process.env.NEXT_PUBLIC_INFISICAL_PLATFORM_VERSION;
const { popUp, handlePopUpOpen, handlePopUpClose, handlePopUpToggle } = usePopUp([
@@ -220,53 +216,32 @@ export const AppLayout = ({ children }: LayoutProps) => {
const onCreateProject = async ({ name, addMembers }: TAddProjectFormData) => {
// type check
if (!currentOrg?.id) return;
if (!currentOrg) return;
if (!user) return;
try {
const {
data: {
project: { id: newWorkspaceId }
project: { id: newProjectId }
}
} = await createWs.mutateAsync({
organizationId: currentOrg?.id,
organizationId: currentOrg.id,
projectName: name
});
const randomBytes = crypto.randomBytes(16).toString("hex");
const PRIVATE_KEY = String(localStorage.getItem("PRIVATE_KEY"));
const { ciphertext, nonce } = encryptAssymmetric({
plaintext: randomBytes,
publicKey: user.publicKey,
privateKey: PRIVATE_KEY
});
await uploadWsKey.mutateAsync({
encryptedKey: ciphertext,
nonce,
userId: user?.id,
workspaceId: newWorkspaceId
});
if (addMembers) {
// not using hooks because need at this point only
const orgUsers = await fetchOrgUsers(currentOrg.id);
const decryptKey = await fetchUserWsKey(newWorkspaceId);
await addWsUser.mutateAsync({
workspaceId: newWorkspaceId,
decryptKey,
userPrivateKey: PRIVATE_KEY,
members: orgUsers
.filter(
({ status, user: orgUser }) => status === "accepted" && user.email !== orgUser.email
)
.map(({ user: orgUser, id: orgMembershipId }) => ({
userPublicKey: orgUser.publicKey,
orgMembershipId
}))
await addUsersToProject.mutateAsync({
emails: orgUsers
.map((member) => member.user.email)
.filter((email) => email !== user.email),
projectId: newProjectId
});
}
createNotification({ text: "Workspace created", type: "success" });
handlePopUpClose("addNewWs");
router.push(`/project/${newWorkspaceId}/secrets/overview`);
router.push(`/project/${newProjectId}/secrets/overview`);
} catch (err) {
console.error(err);
createNotification({ text: "Failed to create workspace", type: "error" });
@@ -117,18 +117,23 @@ export const MemberListTab = () => {
if (!orgUser) return;
try {
if (currentWorkspace.e2ee) {
if (currentWorkspace.version === "v1") {
await addUserToWorkspace({
workspaceId,
userPrivateKey,
decryptKey: wsKey,
members: [{ orgMembershipId, userPublicKey: orgUser.user.publicKey }]
});
} else {
} else if (currentWorkspace.version === "v2") {
await addUserToWorkspaceNonE2EE({
projectId: workspaceId,
emails: [orgUser.user.email]
});
} else {
createNotification({
text: "Failed to add user to project, unknown project type",
type: "error"
});
}
createNotification({
text: "Successfully added user to the project",
@@ -1,4 +1,4 @@
import { useEffect, useRef, useState } from "react";
import { useCallback, useEffect, useRef, useState } from "react";
import { useTranslation } from "react-i18next";
import Link from "next/link";
import { useRouter } from "next/router";
@@ -14,6 +14,8 @@ import { useNotificationContext } from "@app/components/context/Notifications/No
import NavHeader from "@app/components/navigation/NavHeader";
import { PermissionDeniedBanner } from "@app/components/permissions";
import {
Alert,
AlertDescription,
Button,
EmptyState,
IconButton,
@@ -37,7 +39,8 @@ import {
useGetFoldersByEnv,
useGetProjectSecretsAllEnv,
useGetUserWsKey,
useUpdateSecretV3
useUpdateSecretV3,
useUpgradeProject
} from "@app/hooks/api";
import { FolderBreadCrumbs } from "./components/FolderBreadCrumbs";
@@ -104,6 +107,7 @@ export const SecretOverviewPage = () => {
environments: userAvailableEnvs.map(({ slug }) => slug)
});
const upgradeProject = useUpgradeProject();
const { mutateAsync: createSecretV3 } = useCreateSecretV3();
const { mutateAsync: updateSecretV3 } = useUpdateSecretV3();
const { mutateAsync: deleteSecretV3 } = useDeleteSecretV3();
@@ -197,6 +201,24 @@ export const SecretOverviewPage = () => {
}
};
const onUpgradeProject = useCallback(async () => {
const PRIVATE_KEY = localStorage.getItem("PRIVATE_KEY");
if (!PRIVATE_KEY) {
createNotification({
type: "error",
text: "Private key not found"
});
return;
}
await upgradeProject.mutateAsync({
projectId: workspaceId,
privateKey: PRIVATE_KEY
});
}, []);
const handleResetSearch = () => setSearchFilter("");
const handleFolderClick = (path: string) => {
@@ -315,6 +337,23 @@ export const SecretOverviewPage = () => {
.
</p>
</div>
{currentWorkspace?.version === "v1" && (
<div className="mt-8">
<Alert variant="danger">
<AlertDescription className="prose">
Upgrade your project. More filler text More filler text More filler text More filler
text More filler text More filler text More filler text More filler text More filler
text More filler text More filler text More filler text{" "}
</AlertDescription>
<div className="mt-2">
<Button isLoading={upgradeProject.isLoading} onClick={onUpgradeProject}>
Upgrade
</Button>
</div>
</Alert>
</div>
)}
<div className="mt-8 flex items-center justify-between">
<FolderBreadCrumbs secretPath={secretPath} onResetSearch={handleResetSearch} />
<div className="w-80">