misc: added support for removing instance admin access of users

This commit is contained in:
Sheen Capadngan
2025-03-19 02:13:24 +08:00
parent d91add2e7b
commit 015a193330
5 changed files with 155 additions and 40 deletions
@@ -435,6 +435,42 @@ export const registerAdminRouter = async (server: FastifyZodProvider) => {
} }
}); });
server.route({
method: "DELETE",
url: "/user-management/users/:userId/admin-access",
config: {
rateLimit: writeLimit
},
schema: {
params: z.object({
userId: z.string()
}),
response: {
200: z.object({
user: UsersSchema.pick({
username: true,
firstName: true,
lastName: true,
email: true,
id: true
})
})
}
},
onRequest: (req, res, done) => {
verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN])(req, res, () => {
verifySuperAdmin(req, res, done);
});
},
handler: async (req) => {
const user = await server.services.superAdmin.deleteUserSuperAdminAccess(req.params.userId);
return {
user
};
}
});
server.route({ server.route({
method: "POST", method: "POST",
url: "/bootstrap", url: "/bootstrap",
@@ -453,6 +453,17 @@ export const superAdminServiceFactory = ({
return identity; return identity;
}; };
const deleteUserSuperAdminAccess = async (userId: string) => {
const user = await userDAL.findById(userId);
if (!user) {
throw new NotFoundError({ name: "User", message: "User not found" });
}
const updatedUser = userDAL.updateById(userId, { superAdmin: false });
return updatedUser;
};
const getIdentities = async ({ offset, limit, searchTerm }: TAdminGetIdentitiesDTO) => { const getIdentities = async ({ offset, limit, searchTerm }: TAdminGetIdentitiesDTO) => {
const identities = await identityDAL.getIdentitiesByFilter({ const identities = await identityDAL.getIdentitiesByFilter({
limit, limit,
@@ -571,6 +582,7 @@ export const superAdminServiceFactory = ({
updateRootEncryptionStrategy, updateRootEncryptionStrategy,
getConfiguredEncryptionStrategies, getConfiguredEncryptionStrategies,
grantServerAdminAccessToUser, grantServerAdminAccessToUser,
deleteIdentitySuperAdminAccess deleteIdentitySuperAdminAccess,
deleteUserSuperAdminAccess
}; };
}; };
+1
View File
@@ -3,6 +3,7 @@ export {
useAdminGrantServerAdminAccess, useAdminGrantServerAdminAccess,
useAdminRemoveIdentitySuperAdminAccess, useAdminRemoveIdentitySuperAdminAccess,
useCreateAdminUser, useCreateAdminUser,
useRemoveUserServerAdminAccess,
useUpdateAdminSlackConfig, useUpdateAdminSlackConfig,
useUpdateServerConfig, useUpdateServerConfig,
useUpdateServerEncryptionStrategy useUpdateServerEncryptionStrategy
+16
View File
@@ -88,6 +88,22 @@ export const useAdminRemoveIdentitySuperAdminAccess = () => {
}); });
}; };
export const useRemoveUserServerAdminAccess = () => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: async (userId: string) => {
await apiRequest.delete(`/api/v1/admin/user-management/users/${userId}/admin-access`);
return {};
},
onSuccess: () => {
queryClient.invalidateQueries({
queryKey: [adminStandaloneKeys.getUsers]
});
}
});
};
export const useAdminGrantServerAdminAccess = () => { export const useAdminGrantServerAdminAccess = () => {
const queryClient = useQueryClient(); const queryClient = useQueryClient();
return useMutation({ return useMutation({
@@ -33,22 +33,26 @@ import {
THead, THead,
Tr Tr
} from "@app/components/v2"; } from "@app/components/v2";
import { useSubscription, useUser } from "@app/context"; import { useSubscription } from "@app/context";
import { useDebounce, usePopUp } from "@app/hooks"; import { useDebounce, usePopUp } from "@app/hooks";
import { import {
useAdminDeleteUser, useAdminDeleteUser,
useAdminGetUsers, useAdminGetUsers,
useAdminGrantServerAdminAccess useAdminGrantServerAdminAccess,
useRemoveUserServerAdminAccess
} from "@app/hooks/api"; } from "@app/hooks/api";
import { UsePopUpState } from "@app/hooks/usePopUp"; import { UsePopUpState } from "@app/hooks/usePopUp";
const addServerAdminUpgradePlanMessage = "Granting another user Server Admin permissions"; const addServerAdminUpgradePlanMessage = "Granting another user Server Admin permissions";
const removeServerAdminUpgradePlanMessage = "Removing Server Admin permissions from user";
const UserPanelTable = ({ const UserPanelTable = ({
handlePopUpOpen handlePopUpOpen
}: { }: {
handlePopUpOpen: ( handlePopUpOpen: (
popUpName: keyof UsePopUpState<["removeUser", "upgradePlan", "upgradeToServerAdmin"]>, popUpName: keyof UsePopUpState<
["removeUser", "upgradePlan", "upgradeToServerAdmin", "removeServerAdmin"]
>,
data?: { data?: {
username: string; username: string;
id: string; id: string;
@@ -58,8 +62,6 @@ const UserPanelTable = ({
}) => { }) => {
const [searchUserFilter, setSearchUserFilter] = useState(""); const [searchUserFilter, setSearchUserFilter] = useState("");
const [adminsOnly, setAdminsOnly] = useState(false); const [adminsOnly, setAdminsOnly] = useState(false);
const { user } = useUser();
const userId = user?.id || "";
const [debouncedSearchTerm] = useDebounce(searchUserFilter, 500); const [debouncedSearchTerm] = useDebounce(searchUserFilter, 500);
const { subscription } = useSubscription(); const { subscription } = useSubscription();
@@ -143,45 +145,61 @@ const UserPanelTable = ({
</Td> </Td>
<Td className="w-5/12">{email}</Td> <Td className="w-5/12">{email}</Td>
<Td> <Td>
{userId !== id && ( <div className="flex justify-end">
<div className="flex justify-end"> <DropdownMenu>
<DropdownMenu> <DropdownMenuTrigger asChild className="rounded-lg">
<DropdownMenuTrigger asChild className="rounded-lg"> <div className="hover:text-primary-400 data-[state=open]:text-primary-400">
<div className="hover:text-primary-400 data-[state=open]:text-primary-400"> <FontAwesomeIcon size="sm" icon={faEllipsis} />
<FontAwesomeIcon size="sm" icon={faEllipsis} /> </div>
</div> </DropdownMenuTrigger>
</DropdownMenuTrigger> <DropdownMenuContent align="start" className="p-1">
<DropdownMenuContent align="start" className="p-1"> <DropdownMenuItem
onClick={(e) => {
e.stopPropagation();
handlePopUpOpen("removeUser", { username, id });
}}
>
Remove User
</DropdownMenuItem>
{!superAdmin && (
<DropdownMenuItem <DropdownMenuItem
onClick={(e) => { onClick={(e) => {
e.stopPropagation(); e.stopPropagation();
handlePopUpOpen("removeUser", { username, id }); if (!subscription?.instanceUserManagement) {
handlePopUpOpen("upgradePlan", {
username,
id,
message: addServerAdminUpgradePlanMessage
});
return;
}
handlePopUpOpen("upgradeToServerAdmin", { username, id });
}} }}
> >
Remove User Make User Server Admin
</DropdownMenuItem> </DropdownMenuItem>
{!superAdmin && ( )}
<DropdownMenuItem {superAdmin && (
onClick={(e) => { <DropdownMenuItem
e.stopPropagation(); onClick={(e) => {
if (!subscription?.instanceUserManagement) { e.stopPropagation();
handlePopUpOpen("upgradePlan", { if (!subscription?.instanceUserManagement) {
username, handlePopUpOpen("upgradePlan", {
id, username,
message: addServerAdminUpgradePlanMessage id,
}); message: removeServerAdminUpgradePlanMessage
return; });
} return;
handlePopUpOpen("upgradeToServerAdmin", { username, id }); }
}} handlePopUpOpen("removeServerAdmin", { username, id });
> }}
Make User Server Admin >
</DropdownMenuItem> Remove Server Admin
)} </DropdownMenuItem>
</DropdownMenuContent> )}
</DropdownMenu> </DropdownMenuContent>
</div> </DropdownMenu>
)} </div>
</Td> </Td>
</Tr> </Tr>
); );
@@ -212,11 +230,13 @@ export const UserPanel = () => {
const { handlePopUpToggle, popUp, handlePopUpOpen, handlePopUpClose } = usePopUp([ const { handlePopUpToggle, popUp, handlePopUpOpen, handlePopUpClose } = usePopUp([
"removeUser", "removeUser",
"upgradePlan", "upgradePlan",
"upgradeToServerAdmin" "upgradeToServerAdmin",
"removeServerAdmin"
] as const); ] as const);
const { mutateAsync: deleteUser } = useAdminDeleteUser(); const { mutateAsync: deleteUser } = useAdminDeleteUser();
const { mutateAsync: grantAdminAccess } = useAdminGrantServerAdminAccess(); const { mutateAsync: grantAdminAccess } = useAdminGrantServerAdminAccess();
const { mutateAsync: removeAdminAccess } = useRemoveUserServerAdminAccess();
const handleRemoveUser = async () => { const handleRemoveUser = async () => {
const { id } = popUp?.removeUser?.data as { id: string; username: string }; const { id } = popUp?.removeUser?.data as { id: string; username: string };
@@ -256,6 +276,25 @@ export const UserPanel = () => {
handlePopUpClose("upgradeToServerAdmin"); handlePopUpClose("upgradeToServerAdmin");
}; };
const handleRemoveServerAdminAccess = async () => {
const { id } = popUp?.removeServerAdmin?.data as { id: string; username: string };
try {
await removeAdminAccess(id);
createNotification({
type: "success",
text: "Successfully removed server admin access from user"
});
} catch {
createNotification({
type: "error",
text: "Error removing server admin access from user"
});
}
handlePopUpClose("removeServerAdmin");
};
return ( return (
<div className="mb-6 rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4"> <div className="mb-6 rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4">
<div className="mb-4"> <div className="mb-4">
@@ -282,6 +321,17 @@ export const UserPanel = () => {
onDeleteApproved={handleGrantServerAdminAccess} onDeleteApproved={handleGrantServerAdminAccess}
buttonText="Grant Access" buttonText="Grant Access"
/> />
<DeleteActionModal
isOpen={popUp.removeServerAdmin.isOpen}
title={`Are you sure want to remove Server Admin permissions from ${
(popUp?.removeServerAdmin?.data as { id: string; username: string })?.username || ""
}?`}
subTitle=""
onChange={(isOpen) => handlePopUpToggle("removeServerAdmin", isOpen)}
deleteKey="confirm"
onDeleteApproved={handleRemoveServerAdminAccess}
buttonText="Remove Access"
/>
<UpgradePlanModal <UpgradePlanModal
isOpen={popUp.upgradePlan.isOpen} isOpen={popUp.upgradePlan.isOpen}
onOpenChange={(isOpen) => handlePopUpToggle("upgradePlan", isOpen)} onOpenChange={(isOpen) => handlePopUpToggle("upgradePlan", isOpen)}