Delete more deprecated frontend calls

This commit is contained in:
Tuan Dang
2023-08-09 17:55:57 +07:00
parent 2a1665a2c3
commit b47f61f1ad
19 changed files with 121 additions and 289 deletions

View File

@@ -1,7 +1,7 @@
import { Types } from "mongoose";
import { Request, Response } from "express";
import { ISecret, Secret, ServiceTokenData } from "../../models";
import { IAction, SecretVersion, EventType, AuditLog } from "../../ee/models";
import { AuditLog, EventType, IAction, SecretVersion } from "../../ee/models";
import {
ACTION_ADD_SECRETS,
ACTION_DELETE_SECRETS,
@@ -14,7 +14,7 @@ import {
import { BadRequestError, UnauthorizedRequestError } from "../../utils/errors";
import { EventService } from "../../services";
import { eventPushSecrets } from "../../events";
import { EELogService, EESecretService, EEAuditLogService } from "../../ee/services";
import { EEAuditLogService, EELogService, EESecretService } from "../../ee/services";
import { SecretService, TelemetryService } from "../../services";
import { getUserAgentType } from "../../utils/posthog";
import { PERMISSION_WRITE_SECRETS } from "../../variables";

View File

@@ -16,7 +16,7 @@ type ValidEventScope =
| Required<EventScope>
export default class EEAuditLogService {
static async createAuditLog(authData: AuthData, event: Event, eventScope: ValidEventScope, shouldSave: boolean = true) {
static async createAuditLog(authData: AuthData, event: Event, eventScope: ValidEventScope, shouldSave = true) {
const MS_IN_DAY = 24 * 60 * 60 * 1000;

View File

@@ -6,7 +6,7 @@ import { userActionController } from "../../controllers/v1";
import { AuthMode } from "../../variables";
// note: [userAction] will be deprecated in /v2 in favor of [action]
router.post(
router.post( // TODO endpoint: move this into /users/me
"/",
requireAuth({
acceptedAuthModes: [AuthMode.JWT],

View File

@@ -4,12 +4,11 @@ import { faEye, faEyeSlash, faPenToSquare, faPlus, faX } from "@fortawesome/free
import { useNotificationContext } from "@app/components/context/Notifications/NotificationProvider";
import { Select, SelectItem } from "@app/components/v2";
import { useSubscription } from "@app/context";
import { useSubscription, useWorkspace } from "@app/context";
import updateUserProjectPermission from "@app/ee/api/memberships/UpdateUserProjectPermission";
import changeUserRoleInWorkspace from "@app/pages/api/workspace/changeUserRoleInWorkspace";
import deleteUserFromWorkspace from "@app/pages/api/workspace/deleteUserFromWorkspace";
import getLatestFileKey from "@app/pages/api/workspace/getLatestFileKey";
import getProjectInfo from "@app/pages/api/workspace/getProjectInfo";
import uploadKeys from "@app/pages/api/workspace/uploadKeys";
import { decryptAssymmetric, encryptAssymmetric } from "../../utilities/cryptography/crypto";
@@ -39,6 +38,7 @@ type EnvironmentProps = {
* @returns
*/
const ProjectUsersTable = ({ userData, changeData, myUser, filter, isUserListLoading }: Props) => {
const { currentWorkspace } = useWorkspace();
const { subscription } = useSubscription();
const [roleSelected, setRoleSelected] = useState(
Array(userData?.length).fill(userData.map((user) => user.role))
@@ -163,10 +163,11 @@ const ProjectUsersTable = ({ userData, changeData, myUser, filter, isUserListLoa
useEffect(() => {
setMyRole(userData.filter((user) => user.email === myUser)[0]?.role);
(async () => {
const result = await getProjectInfo({ projectId: workspaceId });
setWorkspaceEnvs(result.environments);
if (currentWorkspace) {
setWorkspaceEnvs(currentWorkspace.environments);
}
})();
}, [userData, myUser]);
}, [userData, myUser, currentWorkspace]);
const grantAccess = async (id: string, publicKey: string) => {
const result = await getLatestFileKey({ workspaceId });

View File

@@ -1,5 +1,5 @@
import { fetchUserAction } from "@app/hooks/api/users/queries";
import getOrganizationUsers from "@app/pages/api/organization/GetOrgUsers";
import checkUserAction from "@app/pages/api/userActions/checkUserAction";
interface OnboardingCheckProps {
setTotalOnboardingActionsDone?: (value: number) => void;
@@ -20,25 +20,23 @@ const onboardingCheck = async ({
setUsersInOrg
}: OnboardingCheckProps) => {
let countActions = 0;
const userActionSlack = await checkUserAction({
action: "slack_cta_clicked"
});
const userActionSlack = await fetchUserAction(
"slack_cta_clicked"
);
if (userActionSlack) {
countActions += 1;
}
if (setHasUserClickedSlack) setHasUserClickedSlack(!!userActionSlack);
const userActionSecrets = await checkUserAction({
action: "first_time_secrets_pushed"
});
const userActionSecrets = await fetchUserAction("first_time_secrets_pushed");
if (userActionSecrets) {
countActions += 1;
}
if (setHasUserPushedSecrets) setHasUserPushedSecrets(!!userActionSecrets);
const userActionIntro = await checkUserAction({
action: "intro_cta_clicked"
});
const userActionIntro = await fetchUserAction("intro_cta_clicked");
if (userActionIntro) {
countActions += 1;
}

View File

@@ -3,8 +3,8 @@ import crypto from "crypto";
import { encryptAssymmetric } from "@app/components/utilities/cryptography/crypto";
import encryptSecrets from "@app/components/utilities/secrets/encryptSecrets";
import { createSecret } from "@app/hooks/api/secrets/queries";
import { fetchUserDetails } from "@app/hooks/api/users/queries";
import { createWorkspace } from "@app/hooks/api/workspace/queries";
import getUser from "@app/pages/api/user/getUser";
import uploadKeys from "@app/pages/api/workspace/uploadKeys";
const secretsToBeAdded = [
@@ -108,7 +108,7 @@ const initProjectHelper = async ({
if (!PRIVATE_KEY) throw new Error("Failed to find private key");
const user = await getUser();
const user = await fetchUserDetails();
const { ciphertext, nonce } = encryptAssymmetric({
plaintext: randomBytes,

View File

@@ -3,8 +3,10 @@ export {
useAddUserToOrg,
useAddUserToWs,
useCreateAPIKey,
useCreateMyAction,
useDeleteAPIKey,
useDeleteOrgMembership,
useGetMyActions,
useGetMyAPIKeys,
useGetMyIp,
useGetMySessions,
@@ -14,6 +16,6 @@ export {
useLogoutUser,
useRegisterUserAction,
useRevokeMySessions,
useUpdateMfaEnabled,
useUpdateOrgUserRole,
useUpdateUserAuthProvider
} from "./queries";
useUpdateUserAuthProvider} from "./queries";

View File

@@ -19,7 +19,8 @@ import {
RenameUserDTO,
TokenVersion,
UpdateOrgUserRoleDTO,
User} from "./types";
User
} from "./types";
const userKeys = {
getUser: ["user"] as const,
@@ -27,7 +28,7 @@ const userKeys = {
getOrgUsers: (orgId: string) => [{ orgId }, "user"],
myIp: ["ip"] as const,
myAPIKeys: ["api-keys"] as const,
mySessions: ["sessions"] as const
mySessions: ["sessions"] as const,
};
export const fetchUserDetails = async () => {
@@ -38,7 +39,7 @@ export const fetchUserDetails = async () => {
export const useGetUser = () => useQuery(userKeys.getUser, fetchUserDetails);
const fetchUserAction = async (action: string) => {
export const fetchUserAction = async (action: string) => {
const { data } = await apiRequest.get<{ userAction: string }>("/api/v1/user-action", {
params: {
action
@@ -303,4 +304,27 @@ export const useRevokeMySessions = () => {
queryClient.invalidateQueries(userKeys.mySessions);
}
});
}
export const useUpdateMfaEnabled = () => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: async ({
isMfaEnabled
}: {
isMfaEnabled: boolean;
}) => {
const { data: { user } } = await apiRequest.patch(
"/api/v2/users/me/mfa",
{
isMfaEnabled
}
);
return user;
},
onSuccess() {
queryClient.invalidateQueries(userKeys.getUser);
}
});
}

View File

@@ -9,7 +9,7 @@ export enum AuthProvider {
export type User = {
createdAt: Date;
updatedAt: Date;
email?: string;
email: string;
firstName?: string;
lastName?: string;
authProvider?: AuthProvider;

View File

@@ -1,20 +0,0 @@
import SecurityClient from "@app/components/utilities/SecurityClient";
/**
* This route gets the information about a specific user.
*/
const getUser = () =>
SecurityClient.fetchCall("/api/v1/user", {
method: "GET",
headers: {
"Content-Type": "application/json"
}
}).then(async (res) => {
if (res?.status === 200) {
return (await res.json()).user;
}
console.log("Failed to get user info");
return undefined;
});
export default getUser;

View File

@@ -1,32 +0,0 @@
import SecurityClient from "@app/components/utilities/SecurityClient";
interface Props {
isMfaEnabled: boolean;
}
/**
* Update the user's MFA-enabled status to [isMfaEnabled]
* @param {Object} obj
* @param {Boolean} obj.isMfaEnabled - whether or not MFA status should be set to enabled or not
* @returns {User} user - user with updated MFA-enabled status
*/
const updateMyMfaEnabled = async ({
isMfaEnabled
}: Props) =>
SecurityClient.fetchCall("/api/v2/users/me/mfa", {
method: "PATCH",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
isMfaEnabled,
})
}).then(async (res) => {
if (res && res.status === 200) {
return (await res.json()).user;
}
console.log("Failed to update MFA status");
return undefined;
});
export default updateMyMfaEnabled;

View File

@@ -1,29 +0,0 @@
import SecurityClient from "@app/components/utilities/SecurityClient";
/**
* This route registers a certain action for a user
* @param {*} email
* @param {*} workspaceId
* @returns
*/
const checkUserAction = ({ action }: { action: string }) =>
SecurityClient.fetchCall(
"/api/v1/user-action" +
`?${new URLSearchParams({
action
})}`,
{
method: "GET",
headers: {
"Content-Type": "application/json"
}
}
).then(async (res) => {
if (res && res.status === 200) {
return (await res.json()).userAction;
}
console.log("Failed to check a user action");
return undefined;
});
export default checkUserAction;

View File

@@ -1,25 +0,0 @@
import SecurityClient from "@app/components/utilities/SecurityClient";
/**
* This route registers a certain action for a user
* @param {*} action
* @returns
*/
const registerUserAction = ({ action }: { action: string }) =>
SecurityClient.fetchCall("/api/v1/user-action", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
action
})
}).then(async (res) => {
if (res && res.status === 200) {
return res;
}
console.log("Failed to register a user action");
return undefined;
});
export default registerUserAction;

View File

@@ -1,30 +0,0 @@
import SecurityClient from "@app/components/utilities/SecurityClient";
interface Workspace {
__v: number;
_id: string;
name: string;
organization: string;
environments: Array<{ name: string; slug: string }>;
}
/**
* This route lets us get the workspaces of a certain user
* @returns
*/
const getAWorkspace = (workspaceID: string) =>
SecurityClient.fetchCall(`/api/v1/workspace/${workspaceID}`, {
method: "GET",
headers: {
"Content-Type": "application/json"
}
}).then(async (res) => {
if (res?.status === 200) {
const data = (await res.json()) as unknown as { workspace: Workspace };
return data.workspace;
}
throw new Error("Failed to get workspace");
});
export default getAWorkspace;

View File

@@ -1,22 +0,0 @@
import SecurityClient from "@app/components/utilities/SecurityClient";
/**
* This route lets us get the information of a certain project.
* @param {*} projectId - project ID (we renamed workspaces to projects in the app)
* @returns
*/
const getProjectInfo = ({ projectId }: { projectId: string }) =>
SecurityClient.fetchCall(`/api/v1/workspace/${projectId}`, {
method: "GET",
headers: {
"Content-Type": "application/json"
}
}).then(async (res) => {
if (res?.status === 200) {
return (await res.json()).workspace;
}
console.log("Failed to get project info");
return undefined;
});
export default getProjectInfo;

View File

@@ -1,22 +0,0 @@
import SecurityClient from "@app/components/utilities/SecurityClient";
/**
* This route lets us get the environments that a certain user has acess to in a certain project
* @param {string} workspaceId
* @returns
*/
const getWorkspaceEnvironments = ({ workspaceId }: { workspaceId: string }) =>
SecurityClient.fetchCall(`/api/v2/workspace/${workspaceId}/environments`, {
method: "GET",
headers: {
"Content-Type": "application/json"
}
}).then(async (res) => {
if (res?.status === 200) {
return (await res.json()).accessibleEnvironments;
}
console.log("Failed to get accessible environments");
return undefined;
});
export default getWorkspaceEnvironments;

View File

@@ -36,11 +36,10 @@ import {
} from "@app/components/v2";
import { TabsObject } from "@app/components/v2/Tabs";
import { useSubscription, useUser, useWorkspace } from "@app/context";
import { fetchOrgUsers, useAddUserToWs, useCreateWorkspace, useUploadWsKey } from "@app/hooks/api";
import { fetchOrgUsers, useAddUserToWs, useCreateWorkspace, useRegisterUserAction,useUploadWsKey } from "@app/hooks/api";
import { usePopUp } from "@app/hooks/usePopUp";
import { encryptAssymmetric } from "../../../../components/utilities/cryptography/crypto";
import registerUserAction from "../../../api/userActions/registerUserAction";
const features = [
{
@@ -70,6 +69,7 @@ const LearningItem = ({
userAction,
link
}: ItemProps): JSX.Element => {
const registerUserAction = useRegisterUserAction();
if (link) {
return (
<a
@@ -89,9 +89,9 @@ const LearningItem = ({
tabIndex={0}
onClick={async () => {
if (userAction && userAction !== "first_time_secrets_pushed") {
await registerUserAction({
action: userAction
});
await registerUserAction.mutateAsync(
userAction
);
}
}}
className={`group relative flex h-[5.5rem] w-full items-center justify-between overflow-hidden rounded-md border ${
@@ -130,9 +130,7 @@ const LearningItem = ({
tabIndex={0}
onClick={async () => {
if (userAction) {
await registerUserAction({
action: userAction
});
await registerUserAction.mutateAsync(userAction);
}
}}
className="relative my-1.5 flex h-[5.5rem] w-full cursor-pointer items-center justify-between overflow-hidden rounded-md border border-dashed border-bunker-400 bg-bunker-700 py-2 pl-2 pr-6 shadow-xl duration-200 hover:bg-bunker-500"
@@ -169,6 +167,7 @@ const LearningItemSquare = ({
userAction,
link
}: ItemProps): JSX.Element => {
const registerUserAction = useRegisterUserAction();
return (
<a
target={`${link?.includes("https") ? "_blank" : "_self"}`}
@@ -187,9 +186,7 @@ const LearningItemSquare = ({
tabIndex={0}
onClick={async () => {
if (userAction && userAction !== "first_time_secrets_pushed") {
await registerUserAction({
action: userAction
});
await registerUserAction.mutateAsync(userAction);
}
}}
className={`group relative flex w-full items-center justify-between overflow-hidden rounded-md border ${

View File

@@ -11,14 +11,13 @@ import AddProjectMemberDialog from "@app/components/basic/dialog/AddProjectMembe
import ProjectUsersTable from "@app/components/basic/table/ProjectUsersTable";
import guidGenerator from "@app/components/utilities/randomId";
import { Input } from "@app/components/v2";
import { useGetUser } from "@app/hooks/api";
import {
decryptAssymmetric,
encryptAssymmetric
} from "../../../../components/utilities/cryptography/crypto";
import getOrganizationUsers from "../../../api/organization/GetOrgUsers";
import getUser from "../../../api/user/getUser";
// import DeleteUserDialog from '@app/components/basic/dialog/DeleteUserDialog';
import addUserToWorkspace from "../../../api/workspace/addUserToWorkspace";
import getWorkspaceUsers from "../../../api/workspace/getWorkspaceUsers";
import uploadKeys from "../../../api/workspace/uploadKeys";
@@ -43,6 +42,7 @@ interface MembershipProps {
// #TODO: Update all the workspaceIds
export default function Users() {
const { data: user } = useGetUser();
const [isAddOpen, setIsAddOpen] = useState(false);
// let [isDeleteOpen, setIsDeleteOpen] = useState(false);
// let [userIdToBeDeleted, setUserIdToBeDeleted] = useState(false);
@@ -60,46 +60,47 @@ export default function Users() {
const [orgUserList, setOrgUserList] = useState<any[]>([]);
useEffect(() => {
(async () => {
const user = await getUser();
setPersonalEmail(user.email);
if (user) {
(async () => {
setPersonalEmail(user.email);
// This part quiries the current users of a project
const workspaceUsers = await getWorkspaceUsers({
workspaceId
});
const tempUserList = workspaceUsers.map((membership: MembershipProps) => ({
key: guidGenerator(),
firstName: membership.user?.firstName,
lastName: membership.user?.lastName,
email: membership.user?.email === null ? membership.inviteEmail : membership.user?.email,
role: membership?.role,
status: membership?.status,
userId: membership.user?._id,
membershipId: membership._id,
deniedPermissions: membership.deniedPermissions,
publicKey: membership.user?.publicKey
}));
setUserList(tempUserList);
// This part quiries the current users of a project
const workspaceUsers = await getWorkspaceUsers({
workspaceId
});
const tempUserList = workspaceUsers.map((membership: MembershipProps) => ({
key: guidGenerator(),
firstName: membership.user?.firstName,
lastName: membership.user?.lastName,
email: membership.user?.email === null ? membership.inviteEmail : membership.user?.email,
role: membership?.role,
status: membership?.status,
userId: membership.user?._id,
membershipId: membership._id,
deniedPermissions: membership.deniedPermissions,
publicKey: membership.user?.publicKey
}));
setUserList(tempUserList);
setIsUserListLoading(false);
setIsUserListLoading(false);
// This is needed to know wha users from an org (if any), we are able to add to a certain project
const orgUsers = await getOrganizationUsers({
orgId: String(localStorage.getItem("orgData.id"))
});
setOrgUserList(orgUsers);
setEmail(
orgUsers
?.filter((membership: MembershipProps) => membership.status === "accepted")
.map((membership: MembershipProps) => membership.user.email)
.filter(
(usEmail: string) =>
!tempUserList?.map((user1: UserProps) => user1.email).includes(usEmail)
)[0]
);
})();
}, []);
// This is needed to know wha users from an org (if any), we are able to add to a certain project
const orgUsers = await getOrganizationUsers({
orgId: String(localStorage.getItem("orgData.id"))
});
setOrgUserList(orgUsers);
setEmail(
orgUsers
?.filter((membership: MembershipProps) => membership.status === "accepted")
.map((membership: MembershipProps) => membership.user.email)
.filter(
(usEmail: string) =>
!tempUserList?.map((user1: UserProps) => user1.email).includes(usEmail)
)[0]
);
})();
}
}, [user]);
const closeAddModal = () => {
setIsAddOpen(false);

View File

@@ -1,17 +1,14 @@
import { useEffect, useState } from "react";
import { useNotificationContext } from "@app/components/context/Notifications/NotificationProvider";
import { Checkbox, EmailServiceSetupModal } from "@app/components/v2";
import {
useGetUser,
useUpdateMfaEnabled} from "@app/hooks/api";
import { useFetchServerStatus } from "@app/hooks/api/serverDetails";
import { usePopUp } from "@app/hooks/usePopUp";
import { useGetUser } from "../../../../hooks/api";
import { User } from "../../../../hooks/api/types";
import updateMyMfaEnabled from "../../../../pages/api/user/updateMyMfaEnabled";
export const MFASection = () => {
const [isMfaEnabled, setIsMfaEnabled] = useState(false);
const { data: user } = useGetUser();
const { mutateAsync } = useUpdateMfaEnabled();
const { createNotification } = useNotificationContext();
const { handlePopUpToggle, popUp, handlePopUpOpen } = usePopUp([
"setUpEmail"
@@ -19,22 +16,12 @@ export const MFASection = () => {
const {data: serverDetails } = useFetchServerStatus()
useEffect(() => {
if (user && typeof user.isMfaEnabled !== "undefined") {
setIsMfaEnabled(user.isMfaEnabled);
}
}, [user]);
const toggleMfa = async (state: boolean) => {
try {
const newUser: User = await updateMyMfaEnabled({
const newUser = await mutateAsync({
isMfaEnabled: state
});
if (newUser) {
setIsMfaEnabled(newUser.isMfaEnabled);
}
createNotification({
text: `${newUser.isMfaEnabled ? "Successfully turned on two-factor authentication." : "Successfully turned off two-factor authentication."}`,
type: "success"
@@ -55,20 +42,22 @@ export const MFASection = () => {
<p className="text-xl font-semibold text-mineshaft-100 mb-8">
Two-factor Authentication
</p>
<Checkbox
className="data-[state=checked]:bg-primary"
id="isTwoFAEnabled"
isChecked={isMfaEnabled}
onCheckedChange={(state) => {
if (serverDetails?.emailConfigured){
toggleMfa(state as boolean);
} else {
handlePopUpOpen("setUpEmail");
}
}}
>
Enable 2-factor authentication via your personal email.
</Checkbox>
{user && (
<Checkbox
className="data-[state=checked]:bg-primary"
id="isTwoFAEnabled"
isChecked={user?.isMfaEnabled}
onCheckedChange={(state) => {
if (serverDetails?.emailConfigured){
toggleMfa(state as boolean);
} else {
handlePopUpOpen("setUpEmail");
}
}}
>
Enable 2-factor authentication via your personal email.
</Checkbox>
)}
</div>
</form>
<EmailServiceSetupModal