diff --git a/backend/src/server/routes/v1/admin-router.ts b/backend/src/server/routes/v1/admin-router.ts
index b0d474be9..2935e2655 100644
--- a/backend/src/server/routes/v1/admin-router.ts
+++ b/backend/src/server/routes/v1/admin-router.ts
@@ -399,6 +399,42 @@ export const registerAdminRouter = async (server: FastifyZodProvider) => {
}
});
+ server.route({
+ method: "DELETE",
+ url: "/identity-management/identities/:identityId/super-admin-access",
+ config: {
+ rateLimit: writeLimit
+ },
+ schema: {
+ params: z.object({
+ identityId: z.string()
+ }),
+ response: {
+ 200: z.object({
+ identity: IdentitiesSchema.pick({
+ name: true,
+ id: true
+ })
+ })
+ }
+ },
+ onRequest: (req, res, done) => {
+ verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN])(req, res, () => {
+ verifySuperAdmin(req, res, done);
+ });
+ },
+ handler: async (req) => {
+ const identity = await server.services.superAdmin.deleteIdentitySuperAdminAccess(
+ req.params.identityId,
+ req.permission.id
+ );
+
+ return {
+ identity
+ };
+ }
+ });
+
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 1f1ed592d..b8fb5a3e3 100644
--- a/backend/src/services/super-admin/super-admin-service.ts
+++ b/backend/src/services/super-admin/super-admin-service.ts
@@ -437,6 +437,22 @@ export const superAdminServiceFactory = ({
return user;
};
+ const deleteIdentitySuperAdminAccess = async (identityId: string, actorId: string) => {
+ const identity = await identityDAL.findById(identityId);
+ if (!identity) {
+ throw new NotFoundError({ name: "Identity", message: "Identity not found" });
+ }
+
+ const currentAdminIdentityIds = (await getServerCfg()).adminIdentityIds ?? [];
+ if (!currentAdminIdentityIds?.includes(identityId)) {
+ throw new BadRequestError({ name: "Identity", message: "Identity does not have super admin access" });
+ }
+
+ await updateServerCfg({ adminIdentityIds: currentAdminIdentityIds.filter((id) => id !== identityId) }, actorId);
+
+ return identity;
+ };
+
const getIdentities = async ({ offset, limit, searchTerm }: TAdminGetIdentitiesDTO) => {
const identities = await identityDAL.getIdentitiesByFilter({
limit,
@@ -554,6 +570,7 @@ export const superAdminServiceFactory = ({
getAdminSlackConfig,
updateRootEncryptionStrategy,
getConfiguredEncryptionStrategies,
- grantServerAdminAccessToUser
+ grantServerAdminAccessToUser,
+ deleteIdentitySuperAdminAccess
};
};
diff --git a/frontend/src/hooks/api/admin/index.ts b/frontend/src/hooks/api/admin/index.ts
index 5eb6c6732..f4982dc14 100644
--- a/frontend/src/hooks/api/admin/index.ts
+++ b/frontend/src/hooks/api/admin/index.ts
@@ -1,6 +1,7 @@
export {
useAdminDeleteUser,
useAdminGrantServerAdminAccess,
+ useAdminRemoveIdentitySuperAdminAccess,
useCreateAdminUser,
useUpdateAdminSlackConfig,
useUpdateServerConfig,
diff --git a/frontend/src/hooks/api/admin/mutation.ts b/frontend/src/hooks/api/admin/mutation.ts
index 901c079a0..754aca5e9 100644
--- a/frontend/src/hooks/api/admin/mutation.ts
+++ b/frontend/src/hooks/api/admin/mutation.ts
@@ -70,6 +70,24 @@ export const useAdminDeleteUser = () => {
});
};
+export const useAdminRemoveIdentitySuperAdminAccess = () => {
+ const queryClient = useQueryClient();
+ return useMutation({
+ mutationFn: async (identityId: string) => {
+ await apiRequest.delete(
+ `/api/v1/admin/identity-management/identities/${identityId}/super-admin-access`
+ );
+
+ return {};
+ },
+ onSuccess: () => {
+ queryClient.invalidateQueries({
+ queryKey: [adminStandaloneKeys.getIdentities]
+ });
+ }
+ });
+};
+
export const useAdminGrantServerAdminAccess = () => {
const queryClient = useQueryClient();
return useMutation({
diff --git a/frontend/src/pages/admin/OverviewPage/components/IdentityPanel.tsx b/frontend/src/pages/admin/OverviewPage/components/IdentityPanel.tsx
index 81ae19d4f..1df9a7cd3 100644
--- a/frontend/src/pages/admin/OverviewPage/components/IdentityPanel.tsx
+++ b/frontend/src/pages/admin/OverviewPage/components/IdentityPanel.tsx
@@ -1,10 +1,16 @@
import { useState } from "react";
-import { faMagnifyingGlass, faServer } from "@fortawesome/free-solid-svg-icons";
+import { faEllipsis, faMagnifyingGlass, faServer } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { createNotification } from "@app/components/notifications";
import {
Badge,
Button,
+ DeleteActionModal,
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuItem,
+ DropdownMenuTrigger,
EmptyState,
Input,
Table,
@@ -16,10 +22,22 @@ import {
THead,
Tr
} from "@app/components/v2";
-import { useDebounce } from "@app/hooks";
+import { useDebounce, usePopUp } from "@app/hooks";
+import { useAdminRemoveIdentitySuperAdminAccess } from "@app/hooks/api/admin";
import { useAdminGetIdentities } from "@app/hooks/api/admin/queries";
+import { UsePopUpState } from "@app/hooks/usePopUp";
-const IdentityPanelTable = () => {
+const IdentityPanelTable = ({
+ handlePopUpOpen
+}: {
+ handlePopUpOpen: (
+ popUpName: keyof UsePopUpState<["removeServerAdmin"]>,
+ data?: {
+ name: string;
+ id: string;
+ }
+ ) => void;
+}) => {
const [searchIdentityFilter, setSearchIdentityFilter] = useState("");
const [debouncedSearchTerm] = useDebounce(searchIdentityFilter, 500);
@@ -49,6 +67,7 @@ const IdentityPanelTable = () => {
Name
+
Identities
+export const IdentityPanel = () => { + const { handlePopUpToggle, popUp, handlePopUpOpen, handlePopUpClose } = usePopUp([ + "removeServerAdmin" + ] as const); + + const { mutate: deleteIdentitySuperAdminAccess } = useAdminRemoveIdentitySuperAdminAccess(); + + const handleRemoveServerAdmin = async () => { + const { id } = popUp?.removeServerAdmin?.data as { id: string; name: string }; + + try { + await deleteIdentitySuperAdminAccess(id); + createNotification({ + type: "success", + text: "Successfully removed server admin permissions" + }); + } catch { + createNotification({ + type: "error", + text: "Error removing server admin permissions" + }); + } + + handlePopUpClose("removeServerAdmin"); + }; + + return ( +Identities
+