diff --git a/backend/src/server/routes/v1/admin-router.ts b/backend/src/server/routes/v1/admin-router.ts index d89672e8a..d0303a33c 100644 --- a/backend/src/server/routes/v1/admin-router.ts +++ b/backend/src/server/routes/v1/admin-router.ts @@ -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({ method: "POST", url: "/bootstrap", diff --git a/backend/src/services/super-admin/super-admin-service.ts b/backend/src/services/super-admin/super-admin-service.ts index 4405ced3b..317348cac 100644 --- a/backend/src/services/super-admin/super-admin-service.ts +++ b/backend/src/services/super-admin/super-admin-service.ts @@ -453,6 +453,17 @@ export const superAdminServiceFactory = ({ 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 identities = await identityDAL.getIdentitiesByFilter({ limit, @@ -571,6 +582,7 @@ export const superAdminServiceFactory = ({ updateRootEncryptionStrategy, getConfiguredEncryptionStrategies, grantServerAdminAccessToUser, - deleteIdentitySuperAdminAccess + deleteIdentitySuperAdminAccess, + deleteUserSuperAdminAccess }; }; diff --git a/frontend/src/hooks/api/admin/index.ts b/frontend/src/hooks/api/admin/index.ts index f4982dc14..d43fbc080 100644 --- a/frontend/src/hooks/api/admin/index.ts +++ b/frontend/src/hooks/api/admin/index.ts @@ -3,6 +3,7 @@ export { useAdminGrantServerAdminAccess, useAdminRemoveIdentitySuperAdminAccess, useCreateAdminUser, + useRemoveUserServerAdminAccess, useUpdateAdminSlackConfig, useUpdateServerConfig, useUpdateServerEncryptionStrategy diff --git a/frontend/src/hooks/api/admin/mutation.ts b/frontend/src/hooks/api/admin/mutation.ts index 754aca5e9..b3e1e37b4 100644 --- a/frontend/src/hooks/api/admin/mutation.ts +++ b/frontend/src/hooks/api/admin/mutation.ts @@ -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 = () => { const queryClient = useQueryClient(); return useMutation({ diff --git a/frontend/src/pages/admin/OverviewPage/components/UserPanel.tsx b/frontend/src/pages/admin/OverviewPage/components/UserPanel.tsx index 84d0ed6f0..9395a9a8a 100644 --- a/frontend/src/pages/admin/OverviewPage/components/UserPanel.tsx +++ b/frontend/src/pages/admin/OverviewPage/components/UserPanel.tsx @@ -33,22 +33,26 @@ import { THead, Tr } from "@app/components/v2"; -import { useSubscription, useUser } from "@app/context"; +import { useSubscription } from "@app/context"; import { useDebounce, usePopUp } from "@app/hooks"; import { useAdminDeleteUser, useAdminGetUsers, - useAdminGrantServerAdminAccess + useAdminGrantServerAdminAccess, + useRemoveUserServerAdminAccess } from "@app/hooks/api"; import { UsePopUpState } from "@app/hooks/usePopUp"; const addServerAdminUpgradePlanMessage = "Granting another user Server Admin permissions"; +const removeServerAdminUpgradePlanMessage = "Removing Server Admin permissions from user"; const UserPanelTable = ({ handlePopUpOpen }: { handlePopUpOpen: ( - popUpName: keyof UsePopUpState<["removeUser", "upgradePlan", "upgradeToServerAdmin"]>, + popUpName: keyof UsePopUpState< + ["removeUser", "upgradePlan", "upgradeToServerAdmin", "removeServerAdmin"] + >, data?: { username: string; id: string; @@ -58,8 +62,6 @@ const UserPanelTable = ({ }) => { const [searchUserFilter, setSearchUserFilter] = useState(""); const [adminsOnly, setAdminsOnly] = useState(false); - const { user } = useUser(); - const userId = user?.id || ""; const [debouncedSearchTerm] = useDebounce(searchUserFilter, 500); const { subscription } = useSubscription(); @@ -143,45 +145,61 @@ const UserPanelTable = ({