mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-06 22:27:22 +00:00
feat: add support for removing instance admin permission from identity
This commit is contained in:
@@ -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({
|
server.route({
|
||||||
method: "POST",
|
method: "POST",
|
||||||
url: "/bootstrap",
|
url: "/bootstrap",
|
||||||
|
|||||||
@@ -437,6 +437,22 @@ export const superAdminServiceFactory = ({
|
|||||||
return user;
|
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 getIdentities = async ({ offset, limit, searchTerm }: TAdminGetIdentitiesDTO) => {
|
||||||
const identities = await identityDAL.getIdentitiesByFilter({
|
const identities = await identityDAL.getIdentitiesByFilter({
|
||||||
limit,
|
limit,
|
||||||
@@ -554,6 +570,7 @@ export const superAdminServiceFactory = ({
|
|||||||
getAdminSlackConfig,
|
getAdminSlackConfig,
|
||||||
updateRootEncryptionStrategy,
|
updateRootEncryptionStrategy,
|
||||||
getConfiguredEncryptionStrategies,
|
getConfiguredEncryptionStrategies,
|
||||||
grantServerAdminAccessToUser
|
grantServerAdminAccessToUser,
|
||||||
|
deleteIdentitySuperAdminAccess
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
export {
|
export {
|
||||||
useAdminDeleteUser,
|
useAdminDeleteUser,
|
||||||
useAdminGrantServerAdminAccess,
|
useAdminGrantServerAdminAccess,
|
||||||
|
useAdminRemoveIdentitySuperAdminAccess,
|
||||||
useCreateAdminUser,
|
useCreateAdminUser,
|
||||||
useUpdateAdminSlackConfig,
|
useUpdateAdminSlackConfig,
|
||||||
useUpdateServerConfig,
|
useUpdateServerConfig,
|
||||||
|
|||||||
@@ -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 = () => {
|
export const useAdminGrantServerAdminAccess = () => {
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
return useMutation({
|
return useMutation({
|
||||||
|
|||||||
@@ -1,10 +1,16 @@
|
|||||||
import { useState } from "react";
|
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 { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
|
|
||||||
|
import { createNotification } from "@app/components/notifications";
|
||||||
import {
|
import {
|
||||||
Badge,
|
Badge,
|
||||||
Button,
|
Button,
|
||||||
|
DeleteActionModal,
|
||||||
|
DropdownMenu,
|
||||||
|
DropdownMenuContent,
|
||||||
|
DropdownMenuItem,
|
||||||
|
DropdownMenuTrigger,
|
||||||
EmptyState,
|
EmptyState,
|
||||||
Input,
|
Input,
|
||||||
Table,
|
Table,
|
||||||
@@ -16,10 +22,22 @@ import {
|
|||||||
THead,
|
THead,
|
||||||
Tr
|
Tr
|
||||||
} from "@app/components/v2";
|
} 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 { 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 [searchIdentityFilter, setSearchIdentityFilter] = useState("");
|
||||||
const [debouncedSearchTerm] = useDebounce(searchIdentityFilter, 500);
|
const [debouncedSearchTerm] = useDebounce(searchIdentityFilter, 500);
|
||||||
|
|
||||||
@@ -49,6 +67,7 @@ const IdentityPanelTable = () => {
|
|||||||
<THead>
|
<THead>
|
||||||
<Tr>
|
<Tr>
|
||||||
<Th>Name</Th>
|
<Th>Name</Th>
|
||||||
|
<Th className="w-5" />
|
||||||
</Tr>
|
</Tr>
|
||||||
</THead>
|
</THead>
|
||||||
<TBody>
|
<TBody>
|
||||||
@@ -65,6 +84,31 @@ const IdentityPanelTable = () => {
|
|||||||
</Badge>
|
</Badge>
|
||||||
)}
|
)}
|
||||||
</Td>
|
</Td>
|
||||||
|
<Td>
|
||||||
|
{isInstanceAdmin && (
|
||||||
|
<div className="flex justify-end">
|
||||||
|
<DropdownMenu>
|
||||||
|
<DropdownMenuTrigger asChild className="rounded-lg">
|
||||||
|
<div className="hover:text-primary-400 data-[state=open]:text-primary-400">
|
||||||
|
<FontAwesomeIcon size="sm" icon={faEllipsis} />
|
||||||
|
</div>
|
||||||
|
</DropdownMenuTrigger>
|
||||||
|
<DropdownMenuContent align="start" className="p-1">
|
||||||
|
{isInstanceAdmin && (
|
||||||
|
<DropdownMenuItem
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
handlePopUpOpen("removeServerAdmin", { name, id });
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Remove Server Admin
|
||||||
|
</DropdownMenuItem>
|
||||||
|
)}
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</Td>
|
||||||
</Tr>
|
</Tr>
|
||||||
))
|
))
|
||||||
)}
|
)}
|
||||||
@@ -89,11 +133,49 @@ const IdentityPanelTable = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const IdentityPanel = () => (
|
export const IdentityPanel = () => {
|
||||||
<div className="mb-6 rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4">
|
const { handlePopUpToggle, popUp, handlePopUpOpen, handlePopUpClose } = usePopUp([
|
||||||
<div className="mb-4">
|
"removeServerAdmin"
|
||||||
<p className="text-xl font-semibold text-mineshaft-100">Identities</p>
|
] 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 (
|
||||||
|
<div className="mb-6 rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4">
|
||||||
|
<div className="mb-4">
|
||||||
|
<p className="text-xl font-semibold text-mineshaft-100">Identities</p>
|
||||||
|
</div>
|
||||||
|
<IdentityPanelTable handlePopUpOpen={handlePopUpOpen} />
|
||||||
|
<DeleteActionModal
|
||||||
|
isOpen={popUp.removeServerAdmin.isOpen}
|
||||||
|
title={`Are you sure want to remove Server Admin permissions from ${
|
||||||
|
(popUp?.removeServerAdmin?.data as { name: string })?.name || ""
|
||||||
|
}?`}
|
||||||
|
subTitle=""
|
||||||
|
onChange={(isOpen) => handlePopUpToggle("removeServerAdmin", isOpen)}
|
||||||
|
deleteKey="confirm"
|
||||||
|
onDeleteApproved={handleRemoveServerAdmin}
|
||||||
|
buttonText="Remove Access"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<IdentityPanelTable />
|
);
|
||||||
</div>
|
};
|
||||||
);
|
|
||||||
|
|||||||
Reference in New Issue
Block a user