mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-06 16:27:46 +00:00
feat(ui): finished ui for identity additional privilege
This commit is contained in:
@@ -41,6 +41,18 @@ export type IdentityMembership = {
|
|||||||
temporaryAccessStartTime: string | null;
|
temporaryAccessStartTime: string | null;
|
||||||
temporaryAccessEndTime: string | null;
|
temporaryAccessEndTime: string | null;
|
||||||
}[];
|
}[];
|
||||||
|
additionalPrivileges: {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
description: string | null | undefined;
|
||||||
|
slug: string;
|
||||||
|
temporaryRange: string | null | undefined;
|
||||||
|
temporaryMode: string | null | undefined;
|
||||||
|
temporaryAccessEndTime: string | null | undefined;
|
||||||
|
temporaryAccessStartTime: string | null | undefined;
|
||||||
|
isTemporary: boolean;
|
||||||
|
createdAt: string;
|
||||||
|
}[];
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
export {
|
||||||
|
useCreateIdentityProjectAdditionalPrivilege,
|
||||||
|
useDeleteIdentityProjectAdditionalPrivilege,
|
||||||
|
useUpdateIdentityProjectAdditionalPrivilege
|
||||||
|
} from "./mutation";
|
||||||
|
export { useGetIdentityProjectPrivilegeDetails } from "./queries";
|
||||||
|
export type { TIdentityProjectPrivilege } from "./types";
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
import { packRules } from "@casl/ability/extra";
|
||||||
|
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
||||||
|
|
||||||
|
import { apiRequest } from "@app/config/request";
|
||||||
|
|
||||||
|
import { workspaceKeys } from "../workspace/queries";
|
||||||
|
import {
|
||||||
|
TCreateIdentityProjectPrivilegeDTO,
|
||||||
|
TDeleteIdentityProjectPrivilegeDTO,
|
||||||
|
TIdentityProjectPrivilege,
|
||||||
|
TUpdateIdentityProjectPrivlegeDTO
|
||||||
|
} from "./types";
|
||||||
|
|
||||||
|
export const useCreateIdentityProjectAdditionalPrivilege = () => {
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
|
return useMutation<
|
||||||
|
{ privilege: TIdentityProjectPrivilege },
|
||||||
|
{},
|
||||||
|
TCreateIdentityProjectPrivilegeDTO
|
||||||
|
>({
|
||||||
|
mutationFn: async (dto) => {
|
||||||
|
const { data } = await apiRequest.post("/api/v1/additional-privilege/identity", {
|
||||||
|
...dto,
|
||||||
|
permissions: packRules(dto.permissions)
|
||||||
|
});
|
||||||
|
return data.privilege;
|
||||||
|
},
|
||||||
|
onSuccess: (_, { projectId }) => {
|
||||||
|
queryClient.invalidateQueries(workspaceKeys.getWorkspaceIdentityMemberships(projectId));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useUpdateIdentityProjectAdditionalPrivilege = () => {
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
|
return useMutation<
|
||||||
|
{ privilege: TIdentityProjectPrivilege },
|
||||||
|
{},
|
||||||
|
TUpdateIdentityProjectPrivlegeDTO
|
||||||
|
>({
|
||||||
|
mutationFn: async (dto) => {
|
||||||
|
const { data } = await apiRequest.patch(
|
||||||
|
`/api/v1/additional-privilege/identity/${dto.privilegeId}`,
|
||||||
|
{ ...dto, permissions: dto.permissions ? packRules(dto.permissions) : undefined }
|
||||||
|
);
|
||||||
|
return data.privilege;
|
||||||
|
},
|
||||||
|
onSuccess: (_, { projectId }) => {
|
||||||
|
queryClient.invalidateQueries(workspaceKeys.getWorkspaceIdentityMemberships(projectId));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useDeleteIdentityProjectAdditionalPrivilege = () => {
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
|
return useMutation<
|
||||||
|
{ privilege: TIdentityProjectPrivilege },
|
||||||
|
{},
|
||||||
|
TDeleteIdentityProjectPrivilegeDTO
|
||||||
|
>({
|
||||||
|
mutationFn: async (dto) => {
|
||||||
|
const { data } = await apiRequest.delete(
|
||||||
|
`/api/v1/additional-privilege/identity/${dto.privilegeId}`
|
||||||
|
);
|
||||||
|
return data.privilege;
|
||||||
|
},
|
||||||
|
onSuccess: (_, { projectId }) => {
|
||||||
|
queryClient.invalidateQueries(workspaceKeys.getWorkspaceIdentityMemberships(projectId));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
import { PackRule, unpackRules } from "@casl/ability/extra";
|
||||||
|
import { useQuery } from "@tanstack/react-query";
|
||||||
|
|
||||||
|
import { apiRequest } from "@app/config/request";
|
||||||
|
|
||||||
|
import { TProjectPermission } from "../roles/types";
|
||||||
|
import { TIdentityProjectPrivilege } from "./types";
|
||||||
|
|
||||||
|
export const identitiyProjectPrivilegeKeys = {
|
||||||
|
details: (privilegeId: string) => ["project-user-privilege", { privilegeId }] as const
|
||||||
|
};
|
||||||
|
|
||||||
|
const fetchIdentityProjectPrivilegeDetails = async (privilegeId: string) => {
|
||||||
|
const {
|
||||||
|
data: { privilege }
|
||||||
|
} = await apiRequest.get<{
|
||||||
|
privilege: Omit<TIdentityProjectPrivilege, "permissions"> & { permissions: unknown };
|
||||||
|
}>(`/api/v1/additional-privilege/identity/${privilegeId}`);
|
||||||
|
return {
|
||||||
|
...privilege,
|
||||||
|
permissions: unpackRules(privilege.permissions as PackRule<TProjectPermission>[])
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useGetIdentityProjectPrivilegeDetails = (privilegeId: string) => {
|
||||||
|
return useQuery({
|
||||||
|
enabled: Boolean(privilegeId),
|
||||||
|
queryKey: identitiyProjectPrivilegeKeys.details(privilegeId),
|
||||||
|
queryFn: () => fetchIdentityProjectPrivilegeDetails(privilegeId)
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
import { TProjectPermission } from "../roles/types";
|
||||||
|
|
||||||
|
export enum IdentityProjectAdditionalPrivilegeTemporaryMode {
|
||||||
|
Relative = "relative"
|
||||||
|
}
|
||||||
|
|
||||||
|
export type TIdentityProjectPrivilege = {
|
||||||
|
projectMembershipId: string;
|
||||||
|
slug: string;
|
||||||
|
name: string;
|
||||||
|
isTemporary: boolean;
|
||||||
|
id: string;
|
||||||
|
createdAt: Date;
|
||||||
|
updatedAt: Date;
|
||||||
|
description?: string | null | undefined;
|
||||||
|
temporaryMode?: string | null | undefined;
|
||||||
|
temporaryRange?: string | null | undefined;
|
||||||
|
temporaryAccessStartTime?: string | null | undefined;
|
||||||
|
temporaryAccessEndTime?: Date | null | undefined;
|
||||||
|
permissions?: TProjectPermission[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export type TCreateIdentityProjectPrivilegeDTO = {
|
||||||
|
identityId: string;
|
||||||
|
projectId: string;
|
||||||
|
slug: string;
|
||||||
|
name: string;
|
||||||
|
description?: string;
|
||||||
|
isTemporary?: boolean;
|
||||||
|
temporaryMode?: IdentityProjectAdditionalPrivilegeTemporaryMode;
|
||||||
|
temporaryRange?: string;
|
||||||
|
temporaryAccessStartTime?: string;
|
||||||
|
permissions: TProjectPermission[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export type TUpdateIdentityProjectPrivlegeDTO = {
|
||||||
|
privilegeId: string;
|
||||||
|
projectId: string;
|
||||||
|
} & Partial<Omit<TCreateIdentityProjectPrivilegeDTO, "projectMembershipId" | "projectId">>;
|
||||||
|
|
||||||
|
export type TDeleteIdentityProjectPrivilegeDTO = {
|
||||||
|
privilegeId: string;
|
||||||
|
projectId: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type TGetIdentityProejctPrivilegeDetails = {
|
||||||
|
privilegeId: string;
|
||||||
|
};
|
||||||
@@ -6,6 +6,7 @@ export * from "./bots";
|
|||||||
export * from "./dynamicSecret";
|
export * from "./dynamicSecret";
|
||||||
export * from "./dynamicSecretLease";
|
export * from "./dynamicSecretLease";
|
||||||
export * from "./identities";
|
export * from "./identities";
|
||||||
|
export * from "./identityProjectAdditionalPrivilege";
|
||||||
export * from "./incidentContacts";
|
export * from "./incidentContacts";
|
||||||
export * from "./integrationAuth";
|
export * from "./integrationAuth";
|
||||||
export * from "./integrations";
|
export * from "./integrations";
|
||||||
|
|||||||
+54
-19
@@ -22,8 +22,11 @@ import { useNotificationContext } from "@app/components/context/Notifications/No
|
|||||||
import { Button, FormControl, Input } from "@app/components/v2";
|
import { Button, FormControl, Input } from "@app/components/v2";
|
||||||
import { ProjectPermissionSub } from "@app/context";
|
import { ProjectPermissionSub } from "@app/context";
|
||||||
import {
|
import {
|
||||||
|
useCreateIdentityProjectAdditionalPrivilege,
|
||||||
useCreateProjectUserAdditionalPrivilege,
|
useCreateProjectUserAdditionalPrivilege,
|
||||||
|
useGetIdentityProjectPrivilegeDetails,
|
||||||
useGetProjectUserPrivilegeDetails,
|
useGetProjectUserPrivilegeDetails,
|
||||||
|
useUpdateIdentityProjectAdditionalPrivilege,
|
||||||
useUpdateProjectUserAdditionalPrivilege
|
useUpdateProjectUserAdditionalPrivilege
|
||||||
} from "@app/hooks/api";
|
} from "@app/hooks/api";
|
||||||
|
|
||||||
@@ -32,7 +35,6 @@ import {
|
|||||||
formRolePermission2API,
|
formRolePermission2API,
|
||||||
formSchema,
|
formSchema,
|
||||||
rolePermission2Form,
|
rolePermission2Form,
|
||||||
// rolePermission2Form,
|
|
||||||
TFormSchema
|
TFormSchema
|
||||||
} from "../ProjectRoleListTab/components/ProjectRoleModifySection/ProjectRoleModifySection.utils";
|
} from "../ProjectRoleListTab/components/ProjectRoleModifySection/ProjectRoleModifySection.utils";
|
||||||
import { SecretRollbackPermission } from "../ProjectRoleListTab/components/ProjectRoleModifySection/SecretRollbackPermission";
|
import { SecretRollbackPermission } from "../ProjectRoleListTab/components/ProjectRoleModifySection/SecretRollbackPermission";
|
||||||
@@ -124,14 +126,26 @@ type Props = {
|
|||||||
actorId: string;
|
actorId: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const AdditionalPrivilegeForm = ({ onGoBack, privilegeId, actorId, workspaceId }: Props) => {
|
export const AdditionalPrivilegeForm = ({
|
||||||
|
onGoBack,
|
||||||
|
privilegeId,
|
||||||
|
actorId,
|
||||||
|
workspaceId,
|
||||||
|
isIdentity
|
||||||
|
}: Props) => {
|
||||||
const { createNotification } = useNotificationContext();
|
const { createNotification } = useNotificationContext();
|
||||||
const isNewRole = !privilegeId;
|
const isNewRole = !privilegeId;
|
||||||
|
|
||||||
const { data: projectUserPrivilegeDetails } = useGetProjectUserPrivilegeDetails(
|
const { data: projectUserPrivilegeDetails } = useGetProjectUserPrivilegeDetails(
|
||||||
privilegeId || ""
|
privilegeId && !isIdentity ? privilegeId : ""
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const { data: identityProjectPrivilegeDetails } = useGetIdentityProjectPrivilegeDetails(
|
||||||
|
isIdentity && privilegeId ? privilegeId : ""
|
||||||
|
);
|
||||||
|
|
||||||
|
const privileges = isIdentity ? identityProjectPrivilegeDetails : projectUserPrivilegeDetails;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
handleSubmit,
|
handleSubmit,
|
||||||
register,
|
register,
|
||||||
@@ -141,24 +155,36 @@ export const AdditionalPrivilegeForm = ({ onGoBack, privilegeId, actorId, worksp
|
|||||||
control
|
control
|
||||||
} = useForm<TFormSchema>({
|
} = useForm<TFormSchema>({
|
||||||
resolver: zodResolver(formSchema),
|
resolver: zodResolver(formSchema),
|
||||||
values: projectUserPrivilegeDetails && {
|
values: privileges && {
|
||||||
...projectUserPrivilegeDetails,
|
...privileges,
|
||||||
description: projectUserPrivilegeDetails.description || "",
|
description: privileges.description || "",
|
||||||
permissions: rolePermission2Form(projectUserPrivilegeDetails.permissions)
|
permissions: rolePermission2Form(privileges.permissions)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const createProjectUserAdditionalPrivilege = useCreateProjectUserAdditionalPrivilege();
|
const createProjectUserAdditionalPrivilege = useCreateProjectUserAdditionalPrivilege();
|
||||||
const updateProjectUserAdditionalPrivilege = useUpdateProjectUserAdditionalPrivilege();
|
const updateProjectUserAdditionalPrivilege = useUpdateProjectUserAdditionalPrivilege();
|
||||||
|
|
||||||
|
const createIdentityProjectAdditionalPrivilege = useCreateIdentityProjectAdditionalPrivilege();
|
||||||
|
const updateIdentityProjectAdditionalPrivilege = useUpdateIdentityProjectAdditionalPrivilege();
|
||||||
|
|
||||||
const handleRoleUpdate = async (el: TFormSchema) => {
|
const handleRoleUpdate = async (el: TFormSchema) => {
|
||||||
try {
|
try {
|
||||||
await updateProjectUserAdditionalPrivilege.mutateAsync({
|
if (isIdentity) {
|
||||||
...el,
|
await updateIdentityProjectAdditionalPrivilege.mutateAsync({
|
||||||
permissions: formRolePermission2API(el.permissions),
|
...el,
|
||||||
privilegeId: privilegeId as string,
|
permissions: formRolePermission2API(el.permissions),
|
||||||
workspaceId
|
privilegeId: privilegeId as string,
|
||||||
});
|
projectId: workspaceId
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
await updateProjectUserAdditionalPrivilege.mutateAsync({
|
||||||
|
...el,
|
||||||
|
permissions: formRolePermission2API(el.permissions),
|
||||||
|
privilegeId: privilegeId as string,
|
||||||
|
workspaceId
|
||||||
|
});
|
||||||
|
}
|
||||||
createNotification({ type: "success", text: "Successfully update privilege" });
|
createNotification({ type: "success", text: "Successfully update privilege" });
|
||||||
onGoBack();
|
onGoBack();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -174,12 +200,21 @@ export const AdditionalPrivilegeForm = ({ onGoBack, privilegeId, actorId, worksp
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await createProjectUserAdditionalPrivilege.mutateAsync({
|
if (isIdentity) {
|
||||||
...el,
|
await createIdentityProjectAdditionalPrivilege.mutateAsync({
|
||||||
permissions: formRolePermission2API(el.permissions),
|
...el,
|
||||||
projectMembershipId: actorId,
|
permissions: formRolePermission2API(el.permissions),
|
||||||
workspaceId
|
identityId: actorId,
|
||||||
});
|
projectId: workspaceId
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
await createProjectUserAdditionalPrivilege.mutateAsync({
|
||||||
|
...el,
|
||||||
|
permissions: formRolePermission2API(el.permissions),
|
||||||
|
projectMembershipId: actorId,
|
||||||
|
workspaceId
|
||||||
|
});
|
||||||
|
}
|
||||||
createNotification({ type: "success", text: "Created new privilege" });
|
createNotification({ type: "success", text: "Created new privilege" });
|
||||||
onGoBack();
|
onGoBack();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|||||||
+33
-13
@@ -19,7 +19,10 @@ import {
|
|||||||
} from "@app/components/v2";
|
} from "@app/components/v2";
|
||||||
import { useWorkspace } from "@app/context";
|
import { useWorkspace } from "@app/context";
|
||||||
import { usePopUp } from "@app/hooks";
|
import { usePopUp } from "@app/hooks";
|
||||||
import { useDeleteProjectUserAdditionalPrivilege } from "@app/hooks/api";
|
import {
|
||||||
|
useDeleteIdentityProjectAdditionalPrivilege,
|
||||||
|
useDeleteProjectUserAdditionalPrivilege
|
||||||
|
} from "@app/hooks/api";
|
||||||
import { TWorkspaceUser } from "@app/hooks/api/types";
|
import { TWorkspaceUser } from "@app/hooks/api/types";
|
||||||
|
|
||||||
import { AdditionalPrivilegeForm } from "./AdditionalPrivilegeForm";
|
import { AdditionalPrivilegeForm } from "./AdditionalPrivilegeForm";
|
||||||
@@ -28,40 +31,51 @@ import { AdditionalPrivilegeTemporaryAccess } from "./AdditionalPrivilegeTempora
|
|||||||
type Props = {
|
type Props = {
|
||||||
onGoBack: VoidFunction;
|
onGoBack: VoidFunction;
|
||||||
name: string;
|
name: string;
|
||||||
projectMembershipId: string;
|
isIdentity?: boolean;
|
||||||
|
// isIdentity id - identity id else projectMembershipId
|
||||||
|
actorId: string;
|
||||||
privileges: TWorkspaceUser["additionalPrivileges"];
|
privileges: TWorkspaceUser["additionalPrivileges"];
|
||||||
};
|
};
|
||||||
|
|
||||||
export const AdditionalPrivilegeSection = ({
|
export const AdditionalPrivilegeSection = ({
|
||||||
onGoBack,
|
onGoBack,
|
||||||
privileges = [],
|
privileges = [],
|
||||||
projectMembershipId,
|
actorId,
|
||||||
name
|
name,
|
||||||
|
isIdentity
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
const { popUp, handlePopUpOpen, handlePopUpToggle, handlePopUpClose } = usePopUp([
|
const { popUp, handlePopUpOpen, handlePopUpToggle, handlePopUpClose } = usePopUp([
|
||||||
"modifyPrivilege",
|
"modifyPrivilege",
|
||||||
"deletePrivilege"
|
"deletePrivilege"
|
||||||
] as const);
|
] as const);
|
||||||
const { createNotification } = useNotificationContext();
|
const { createNotification } = useNotificationContext();
|
||||||
const deleteProjectUserAdditionalPrivilege = useDeleteProjectUserAdditionalPrivilege();
|
|
||||||
const { currentWorkspace } = useWorkspace();
|
const { currentWorkspace } = useWorkspace();
|
||||||
const workspaceId = currentWorkspace?.id || "";
|
const workspaceId = currentWorkspace?.id || "";
|
||||||
|
const deleteProjectUserAdditionalPrivilege = useDeleteProjectUserAdditionalPrivilege();
|
||||||
|
const deleteProjectIdentityAdditionalPrivilege = useDeleteIdentityProjectAdditionalPrivilege();
|
||||||
|
|
||||||
const onPrivilegeDelete = async (privilegeId: string) => {
|
const onPrivilegeDelete = async (privilegeId: string) => {
|
||||||
try {
|
try {
|
||||||
await deleteProjectUserAdditionalPrivilege.mutateAsync({
|
if (isIdentity) {
|
||||||
privilegeId,
|
await deleteProjectIdentityAdditionalPrivilege.mutateAsync({
|
||||||
workspaceId
|
privilegeId,
|
||||||
});
|
projectId: workspaceId
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
await deleteProjectUserAdditionalPrivilege.mutateAsync({
|
||||||
|
privilegeId,
|
||||||
|
workspaceId
|
||||||
|
});
|
||||||
|
}
|
||||||
handlePopUpClose("deletePrivilege");
|
handlePopUpClose("deletePrivilege");
|
||||||
createNotification({
|
createNotification({
|
||||||
type: "success",
|
type: "success",
|
||||||
text: "Successfully removed user privilege"
|
text: "Successfully removed privilege"
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
createNotification({
|
createNotification({
|
||||||
type: "error",
|
type: "error",
|
||||||
text: "Failed to delete user privilege"
|
text: "Failed to delete privilege"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -83,7 +97,8 @@ export const AdditionalPrivilegeSection = ({
|
|||||||
onGoBack={() => handlePopUpClose("modifyPrivilege")}
|
onGoBack={() => handlePopUpClose("modifyPrivilege")}
|
||||||
privilegeId={privilegeDetails?.id}
|
privilegeId={privilegeDetails?.id}
|
||||||
workspaceId={workspaceId}
|
workspaceId={workspaceId}
|
||||||
actorId={projectMembershipId}
|
isIdentity={isIdentity}
|
||||||
|
actorId={actorId}
|
||||||
/>
|
/>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
);
|
);
|
||||||
@@ -119,7 +134,11 @@ export const AdditionalPrivilegeSection = ({
|
|||||||
</div>
|
</div>
|
||||||
<div className="mt-6 flex flex-col space-y-4">
|
<div className="mt-6 flex flex-col space-y-4">
|
||||||
{privileges.length === 0 && (
|
{privileges.length === 0 && (
|
||||||
<EmptyState title="User has no additional privileges" iconSize="3x" icon={faUserShield} />
|
<EmptyState
|
||||||
|
title={`${isIdentity ? "Machine identity" : "User"} has no additional privileges`}
|
||||||
|
iconSize="3x"
|
||||||
|
icon={faUserShield}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
{privileges.map(({ id, name: privilegeName, description, slug, ...dto }) => (
|
{privileges.map(({ id, name: privilegeName, description, slug, ...dto }) => (
|
||||||
<div
|
<div
|
||||||
@@ -137,6 +156,7 @@ export const AdditionalPrivilegeSection = ({
|
|||||||
</div>
|
</div>
|
||||||
<div className="flex items-center space-x-4">
|
<div className="flex items-center space-x-4">
|
||||||
<AdditionalPrivilegeTemporaryAccess
|
<AdditionalPrivilegeTemporaryAccess
|
||||||
|
isIdentity={isIdentity}
|
||||||
privilegeId={id}
|
privilegeId={id}
|
||||||
workspaceId={workspaceId}
|
workspaceId={workspaceId}
|
||||||
temporaryConfig={!dto.isTemporary ? { isTemporary: false } : { ...dto }}
|
temporaryConfig={!dto.isTemporary ? { isTemporary: false } : { ...dto }}
|
||||||
|
|||||||
+41
-15
@@ -18,7 +18,11 @@ import {
|
|||||||
Tooltip
|
Tooltip
|
||||||
} from "@app/components/v2";
|
} from "@app/components/v2";
|
||||||
import { usePopUp } from "@app/hooks";
|
import { usePopUp } from "@app/hooks";
|
||||||
import { useUpdateProjectUserAdditionalPrivilege } from "@app/hooks/api";
|
import {
|
||||||
|
useUpdateIdentityProjectAdditionalPrivilege,
|
||||||
|
useUpdateProjectUserAdditionalPrivilege
|
||||||
|
} from "@app/hooks/api";
|
||||||
|
import { IdentityProjectAdditionalPrivilegeTemporaryMode } from "@app/hooks/api/identityProjectAdditionalPrivilege/types";
|
||||||
import { ProjectUserAdditionalPrivilegeTemporaryMode } from "@app/hooks/api/projectUserAdditionalPrivilege/types";
|
import { ProjectUserAdditionalPrivilegeTemporaryMode } from "@app/hooks/api/projectUserAdditionalPrivilege/types";
|
||||||
|
|
||||||
const temporaryRoleFormSchema = z.object({
|
const temporaryRoleFormSchema = z.object({
|
||||||
@@ -30,6 +34,7 @@ type TTemporaryRoleFormSchema = z.infer<typeof temporaryRoleFormSchema>;
|
|||||||
type TTemporaryRoleFormProps = {
|
type TTemporaryRoleFormProps = {
|
||||||
privilegeId: string;
|
privilegeId: string;
|
||||||
workspaceId: string;
|
workspaceId: string;
|
||||||
|
isIdentity?: boolean;
|
||||||
temporaryConfig?: {
|
temporaryConfig?: {
|
||||||
isTemporary?: boolean;
|
isTemporary?: boolean;
|
||||||
temporaryAccessEndTime?: string | null;
|
temporaryAccessEndTime?: string | null;
|
||||||
@@ -41,7 +46,8 @@ type TTemporaryRoleFormProps = {
|
|||||||
export const AdditionalPrivilegeTemporaryAccess = ({
|
export const AdditionalPrivilegeTemporaryAccess = ({
|
||||||
temporaryConfig: defaultValues = {},
|
temporaryConfig: defaultValues = {},
|
||||||
workspaceId,
|
workspaceId,
|
||||||
privilegeId
|
privilegeId,
|
||||||
|
isIdentity
|
||||||
}: TTemporaryRoleFormProps) => {
|
}: TTemporaryRoleFormProps) => {
|
||||||
const { popUp, handlePopUpToggle } = usePopUp(["setTempRole"] as const);
|
const { popUp, handlePopUpToggle } = usePopUp(["setTempRole"] as const);
|
||||||
const { createNotification } = useNotificationContext();
|
const { createNotification } = useNotificationContext();
|
||||||
@@ -56,17 +62,29 @@ export const AdditionalPrivilegeTemporaryAccess = ({
|
|||||||
isTemporaryFieldValue && new Date() > new Date(defaultValues.temporaryAccessEndTime || "");
|
isTemporaryFieldValue && new Date() > new Date(defaultValues.temporaryAccessEndTime || "");
|
||||||
|
|
||||||
const updateProjectUserAdditionalPrivilege = useUpdateProjectUserAdditionalPrivilege();
|
const updateProjectUserAdditionalPrivilege = useUpdateProjectUserAdditionalPrivilege();
|
||||||
|
const updateProjectIdentityAdditionalPrivilege = useUpdateIdentityProjectAdditionalPrivilege();
|
||||||
|
|
||||||
const handleGrantTemporaryAccess = async (el: TTemporaryRoleFormSchema) => {
|
const handleGrantTemporaryAccess = async (el: TTemporaryRoleFormSchema) => {
|
||||||
try {
|
try {
|
||||||
await updateProjectUserAdditionalPrivilege.mutateAsync({
|
if (isIdentity) {
|
||||||
privilegeId: privilegeId as string,
|
await updateProjectIdentityAdditionalPrivilege.mutateAsync({
|
||||||
workspaceId,
|
privilegeId: privilegeId as string,
|
||||||
isTemporary: true,
|
projectId: workspaceId,
|
||||||
temporaryRange: el.temporaryRange,
|
isTemporary: true,
|
||||||
temporaryAccessStartTime: new Date().toISOString(),
|
temporaryRange: el.temporaryRange,
|
||||||
temporaryMode: ProjectUserAdditionalPrivilegeTemporaryMode.Relative
|
temporaryAccessStartTime: new Date().toISOString(),
|
||||||
});
|
temporaryMode: IdentityProjectAdditionalPrivilegeTemporaryMode.Relative
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
await updateProjectUserAdditionalPrivilege.mutateAsync({
|
||||||
|
privilegeId: privilegeId as string,
|
||||||
|
workspaceId,
|
||||||
|
isTemporary: true,
|
||||||
|
temporaryRange: el.temporaryRange,
|
||||||
|
temporaryAccessStartTime: new Date().toISOString(),
|
||||||
|
temporaryMode: ProjectUserAdditionalPrivilegeTemporaryMode.Relative
|
||||||
|
});
|
||||||
|
}
|
||||||
createNotification({ type: "success", text: "Successfully updated access" });
|
createNotification({ type: "success", text: "Successfully updated access" });
|
||||||
handlePopUpToggle("setTempRole");
|
handlePopUpToggle("setTempRole");
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -77,11 +95,19 @@ export const AdditionalPrivilegeTemporaryAccess = ({
|
|||||||
|
|
||||||
const handleRevokeTemporaryAccess = async () => {
|
const handleRevokeTemporaryAccess = async () => {
|
||||||
try {
|
try {
|
||||||
await updateProjectUserAdditionalPrivilege.mutateAsync({
|
if (isIdentity) {
|
||||||
privilegeId: privilegeId as string,
|
await updateProjectIdentityAdditionalPrivilege.mutateAsync({
|
||||||
workspaceId,
|
privilegeId: privilegeId as string,
|
||||||
isTemporary: false
|
projectId: workspaceId,
|
||||||
});
|
isTemporary: false
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
await updateProjectUserAdditionalPrivilege.mutateAsync({
|
||||||
|
privilegeId: privilegeId as string,
|
||||||
|
workspaceId,
|
||||||
|
isTemporary: false
|
||||||
|
});
|
||||||
|
}
|
||||||
createNotification({ type: "success", text: "Successfully updated access" });
|
createNotification({ type: "success", text: "Successfully updated access" });
|
||||||
handlePopUpToggle("setTempRole");
|
handlePopUpToggle("setTempRole");
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|||||||
@@ -1,17 +1,270 @@
|
|||||||
|
import Link from "next/link";
|
||||||
|
import {
|
||||||
|
faArrowUpRightFromSquare,
|
||||||
|
faPlus,
|
||||||
|
faServer,
|
||||||
|
faUserShield,
|
||||||
|
faXmark
|
||||||
|
} from "@fortawesome/free-solid-svg-icons";
|
||||||
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
|
import { format } from "date-fns";
|
||||||
import { motion } from "framer-motion";
|
import { motion } from "framer-motion";
|
||||||
|
import { twMerge } from "tailwind-merge";
|
||||||
|
|
||||||
import { IdentitySection } from "./components";
|
import { useNotificationContext } from "@app/components/context/Notifications/NotificationProvider";
|
||||||
|
import { ProjectPermissionCan } from "@app/components/permissions";
|
||||||
|
import {
|
||||||
|
Button,
|
||||||
|
DeleteActionModal,
|
||||||
|
EmptyState,
|
||||||
|
IconButton,
|
||||||
|
Table,
|
||||||
|
TableContainer,
|
||||||
|
TableSkeleton,
|
||||||
|
TBody,
|
||||||
|
Td,
|
||||||
|
Th,
|
||||||
|
THead,
|
||||||
|
Tr
|
||||||
|
} from "@app/components/v2";
|
||||||
|
import { ProjectPermissionActions, ProjectPermissionSub, useWorkspace } from "@app/context";
|
||||||
|
import { withProjectPermission } from "@app/hoc";
|
||||||
|
import { useDeleteIdentityFromWorkspace, useGetWorkspaceIdentityMemberships } from "@app/hooks/api";
|
||||||
|
import { usePopUp } from "@app/hooks/usePopUp";
|
||||||
|
|
||||||
export const IdentityTab = () => {
|
import { AdditionalPrivilegeSection } from "../AdditionalPrivilegeSection";
|
||||||
return (
|
import { IdentityModal } from "./components/IdentityModal";
|
||||||
<motion.div
|
import { IdentityRoles } from "./components/IdentityRoles";
|
||||||
key="panel-identity"
|
|
||||||
transition={{ duration: 0.15 }}
|
export const IdentityTab = withProjectPermission(
|
||||||
initial={{ opacity: 0, translateX: 30 }}
|
() => {
|
||||||
animate={{ opacity: 1, translateX: 0 }}
|
const { createNotification } = useNotificationContext();
|
||||||
exit={{ opacity: 0, translateX: 30 }}
|
const { currentWorkspace } = useWorkspace();
|
||||||
>
|
|
||||||
<IdentitySection />
|
const workspaceId = currentWorkspace?.id ?? "";
|
||||||
</motion.div>
|
|
||||||
);
|
const { data, isLoading } = useGetWorkspaceIdentityMemberships(currentWorkspace?.id || "");
|
||||||
};
|
const { mutateAsync: deleteMutateAsync } = useDeleteIdentityFromWorkspace();
|
||||||
|
|
||||||
|
const { popUp, handlePopUpOpen, handlePopUpClose, handlePopUpToggle } = usePopUp([
|
||||||
|
"identity",
|
||||||
|
"deleteIdentity",
|
||||||
|
"upgradePlan",
|
||||||
|
"additionalPrivilege"
|
||||||
|
] as const);
|
||||||
|
|
||||||
|
const onRemoveIdentitySubmit = async (identityId: string) => {
|
||||||
|
try {
|
||||||
|
await deleteMutateAsync({
|
||||||
|
identityId,
|
||||||
|
workspaceId
|
||||||
|
});
|
||||||
|
|
||||||
|
createNotification({
|
||||||
|
text: "Successfully removed identity from project",
|
||||||
|
type: "success"
|
||||||
|
});
|
||||||
|
|
||||||
|
handlePopUpClose("deleteIdentity");
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
const error = err as any;
|
||||||
|
const text = error?.response?.data?.message ?? "Failed to remove identity from project";
|
||||||
|
|
||||||
|
createNotification({
|
||||||
|
text,
|
||||||
|
type: "error"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (popUp.additionalPrivilege.isOpen) {
|
||||||
|
const privilegeDetails = popUp?.additionalPrivilege?.data as {
|
||||||
|
name: string;
|
||||||
|
index: number;
|
||||||
|
identityId: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<motion.div
|
||||||
|
key="panel-additional-permission"
|
||||||
|
className="mb-6 rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4"
|
||||||
|
transition={{ duration: 0.15 }}
|
||||||
|
initial={{ opacity: 0, translateX: 30 }}
|
||||||
|
animate={{ opacity: 1, translateX: 0 }}
|
||||||
|
exit={{ opacity: 0, translateX: 30 }}
|
||||||
|
>
|
||||||
|
<AdditionalPrivilegeSection
|
||||||
|
isIdentity
|
||||||
|
onGoBack={() => handlePopUpClose("additionalPrivilege")}
|
||||||
|
privileges={data?.[privilegeDetails.index]?.additionalPrivileges || []}
|
||||||
|
name={privilegeDetails.name}
|
||||||
|
actorId={privilegeDetails.identityId}
|
||||||
|
/>
|
||||||
|
</motion.div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<motion.div
|
||||||
|
key="panel-identity"
|
||||||
|
transition={{ duration: 0.15 }}
|
||||||
|
initial={{ opacity: 0, translateX: 30 }}
|
||||||
|
animate={{ opacity: 1, translateX: 0 }}
|
||||||
|
exit={{ opacity: 0, translateX: 30 }}
|
||||||
|
>
|
||||||
|
<div className="mb-6 rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4">
|
||||||
|
<div className="mb-4 flex items-center justify-between">
|
||||||
|
<p className="text-xl font-semibold text-mineshaft-100">Identities</p>
|
||||||
|
<div className="flex w-full justify-end pr-4">
|
||||||
|
<Link href="https://infisical.com/docs/documentation/platform/identities/overview">
|
||||||
|
<span className="w-max cursor-pointer rounded-md border border-mineshaft-500 bg-mineshaft-600 px-4 py-2 text-mineshaft-200 duration-200 hover:border-primary/40 hover:bg-primary/10 hover:text-white">
|
||||||
|
Documentation{" "}
|
||||||
|
<FontAwesomeIcon
|
||||||
|
icon={faArrowUpRightFromSquare}
|
||||||
|
className="mb-[0.06rem] ml-1 text-xs"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
<ProjectPermissionCan
|
||||||
|
I={ProjectPermissionActions.Create}
|
||||||
|
a={ProjectPermissionSub.Identity}
|
||||||
|
>
|
||||||
|
{(isAllowed) => (
|
||||||
|
<Button
|
||||||
|
colorSchema="primary"
|
||||||
|
type="submit"
|
||||||
|
leftIcon={<FontAwesomeIcon icon={faPlus} />}
|
||||||
|
onClick={() => handlePopUpOpen("identity")}
|
||||||
|
isDisabled={!isAllowed}
|
||||||
|
>
|
||||||
|
Add identity
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</ProjectPermissionCan>
|
||||||
|
</div>
|
||||||
|
<TableContainer>
|
||||||
|
<Table>
|
||||||
|
<THead>
|
||||||
|
<Tr>
|
||||||
|
<Th>Name</Th>
|
||||||
|
<Th>Role</Th>
|
||||||
|
<Th>Added on</Th>
|
||||||
|
<Th className="w-5" />
|
||||||
|
</Tr>
|
||||||
|
</THead>
|
||||||
|
<TBody>
|
||||||
|
{isLoading && <TableSkeleton columns={7} innerKey="project-identities" />}
|
||||||
|
{!isLoading &&
|
||||||
|
data &&
|
||||||
|
data.length > 0 &&
|
||||||
|
data.map(
|
||||||
|
({ identity: { id, name }, roles, createdAt, additionalPrivileges }, index) => {
|
||||||
|
const hasAdditionalPrivilege = Boolean(additionalPrivileges.length);
|
||||||
|
return (
|
||||||
|
<Tr className="h-10" key={`st-v3-${id}`}>
|
||||||
|
<Td>{name}</Td>
|
||||||
|
<Td>
|
||||||
|
<ProjectPermissionCan
|
||||||
|
I={ProjectPermissionActions.Edit}
|
||||||
|
a={ProjectPermissionSub.Identity}
|
||||||
|
>
|
||||||
|
{(isAllowed) => (
|
||||||
|
<IdentityRoles
|
||||||
|
roles={roles}
|
||||||
|
disableEdit={!isAllowed}
|
||||||
|
identityId={id}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</ProjectPermissionCan>
|
||||||
|
</Td>
|
||||||
|
<Td>{format(new Date(createdAt), "yyyy-MM-dd")}</Td>
|
||||||
|
<Td className="flex justify-end">
|
||||||
|
<div className="flex items-center space-x-2">
|
||||||
|
<ProjectPermissionCan
|
||||||
|
I={ProjectPermissionActions.Edit}
|
||||||
|
a={ProjectPermissionSub.Member}
|
||||||
|
allowedLabel="Additional Privilege"
|
||||||
|
>
|
||||||
|
{(isAllowed) => (
|
||||||
|
<IconButton
|
||||||
|
size="lg"
|
||||||
|
variant="plain"
|
||||||
|
ariaLabel="update"
|
||||||
|
className={twMerge(hasAdditionalPrivilege && "text-primary")}
|
||||||
|
isDisabled={!isAllowed}
|
||||||
|
onClick={() =>
|
||||||
|
handlePopUpOpen("additionalPrivilege", {
|
||||||
|
name,
|
||||||
|
index,
|
||||||
|
identityId: id
|
||||||
|
})
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<FontAwesomeIcon icon={faUserShield} />
|
||||||
|
</IconButton>
|
||||||
|
)}
|
||||||
|
</ProjectPermissionCan>
|
||||||
|
<ProjectPermissionCan
|
||||||
|
I={ProjectPermissionActions.Delete}
|
||||||
|
a={ProjectPermissionSub.Identity}
|
||||||
|
>
|
||||||
|
{(isAllowed) => (
|
||||||
|
<IconButton
|
||||||
|
onClick={() => {
|
||||||
|
handlePopUpOpen("deleteIdentity", {
|
||||||
|
identityId: id,
|
||||||
|
name
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
size="lg"
|
||||||
|
colorSchema="danger"
|
||||||
|
variant="plain"
|
||||||
|
ariaLabel="update"
|
||||||
|
className="ml-4"
|
||||||
|
isDisabled={!isAllowed}
|
||||||
|
>
|
||||||
|
<FontAwesomeIcon icon={faXmark} />
|
||||||
|
</IconButton>
|
||||||
|
)}
|
||||||
|
</ProjectPermissionCan>
|
||||||
|
</div>
|
||||||
|
</Td>
|
||||||
|
</Tr>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
)}
|
||||||
|
{!isLoading && data && data?.length === 0 && (
|
||||||
|
<Tr>
|
||||||
|
<Td colSpan={7}>
|
||||||
|
<EmptyState
|
||||||
|
title="No identities have been added to this project"
|
||||||
|
icon={faServer}
|
||||||
|
/>
|
||||||
|
</Td>
|
||||||
|
</Tr>
|
||||||
|
)}
|
||||||
|
</TBody>
|
||||||
|
</Table>
|
||||||
|
</TableContainer>
|
||||||
|
|
||||||
|
<IdentityModal popUp={popUp} handlePopUpToggle={handlePopUpToggle} />
|
||||||
|
<DeleteActionModal
|
||||||
|
isOpen={popUp.deleteIdentity.isOpen}
|
||||||
|
title={`Are you sure want to remove ${(popUp?.deleteIdentity?.data as { name: string })?.name || ""
|
||||||
|
} from the project?`}
|
||||||
|
onChange={(isOpen) => handlePopUpToggle("deleteIdentity", isOpen)}
|
||||||
|
deleteKey="confirm"
|
||||||
|
onDeleteApproved={() =>
|
||||||
|
onRemoveIdentitySubmit(
|
||||||
|
(popUp?.deleteIdentity?.data as { identityId: string })?.identityId
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</motion.div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
{ action: ProjectPermissionActions.Read, subject: ProjectPermissionSub.Identity }
|
||||||
|
);
|
||||||
|
|||||||
-107
@@ -1,107 +0,0 @@
|
|||||||
import Link from "next/link";
|
|
||||||
import { faArrowUpRightFromSquare, faPlus } from "@fortawesome/free-solid-svg-icons";
|
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
|
||||||
|
|
||||||
import { createNotification } from "@app/components/notifications";
|
|
||||||
import { ProjectPermissionCan } from "@app/components/permissions";
|
|
||||||
import { Button, DeleteActionModal } from "@app/components/v2";
|
|
||||||
import { ProjectPermissionActions, ProjectPermissionSub, useWorkspace } from "@app/context";
|
|
||||||
import { withProjectPermission } from "@app/hoc";
|
|
||||||
import { useDeleteIdentityFromWorkspace } from "@app/hooks/api";
|
|
||||||
import { usePopUp } from "@app/hooks/usePopUp";
|
|
||||||
|
|
||||||
import { IdentityModal } from "./IdentityModal";
|
|
||||||
import { IdentityTable } from "./IdentityTable";
|
|
||||||
|
|
||||||
export const IdentitySection = withProjectPermission(
|
|
||||||
() => {
|
|
||||||
|
|
||||||
const { currentWorkspace } = useWorkspace();
|
|
||||||
|
|
||||||
const workspaceId = currentWorkspace?.id ?? "";
|
|
||||||
|
|
||||||
const { mutateAsync: deleteMutateAsync } = useDeleteIdentityFromWorkspace();
|
|
||||||
|
|
||||||
const { popUp, handlePopUpOpen, handlePopUpClose, handlePopUpToggle } = usePopUp([
|
|
||||||
"identity",
|
|
||||||
"deleteIdentity",
|
|
||||||
"upgradePlan"
|
|
||||||
] as const);
|
|
||||||
|
|
||||||
const onRemoveIdentitySubmit = async (identityId: string) => {
|
|
||||||
try {
|
|
||||||
await deleteMutateAsync({
|
|
||||||
identityId,
|
|
||||||
workspaceId
|
|
||||||
});
|
|
||||||
|
|
||||||
createNotification({
|
|
||||||
text: "Successfully removed identity from project",
|
|
||||||
type: "success"
|
|
||||||
});
|
|
||||||
|
|
||||||
handlePopUpClose("deleteIdentity");
|
|
||||||
} catch (err) {
|
|
||||||
console.error(err);
|
|
||||||
const error = err as any;
|
|
||||||
const text = error?.response?.data?.message ?? "Failed to remove identity from project";
|
|
||||||
|
|
||||||
createNotification({
|
|
||||||
text,
|
|
||||||
type: "error"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="mb-6 rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4">
|
|
||||||
<div className="mb-4 flex items-center justify-between">
|
|
||||||
<p className="text-xl font-semibold text-mineshaft-100">Identities</p>
|
|
||||||
<div className="flex w-full justify-end pr-4">
|
|
||||||
<Link href="https://infisical.com/docs/documentation/platform/identities/overview">
|
|
||||||
<span className="w-max cursor-pointer rounded-md border border-mineshaft-500 bg-mineshaft-600 px-4 py-2 text-mineshaft-200 duration-200 hover:border-primary/40 hover:bg-primary/10 hover:text-white">
|
|
||||||
Documentation{" "}
|
|
||||||
<FontAwesomeIcon
|
|
||||||
icon={faArrowUpRightFromSquare}
|
|
||||||
className="mb-[0.06rem] ml-1 text-xs"
|
|
||||||
/>
|
|
||||||
</span>
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
<ProjectPermissionCan
|
|
||||||
I={ProjectPermissionActions.Create}
|
|
||||||
a={ProjectPermissionSub.Identity}
|
|
||||||
>
|
|
||||||
{(isAllowed) => (
|
|
||||||
<Button
|
|
||||||
colorSchema="primary"
|
|
||||||
type="submit"
|
|
||||||
leftIcon={<FontAwesomeIcon icon={faPlus} />}
|
|
||||||
onClick={() => handlePopUpOpen("identity")}
|
|
||||||
isDisabled={!isAllowed}
|
|
||||||
>
|
|
||||||
Add identity
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
</ProjectPermissionCan>
|
|
||||||
</div>
|
|
||||||
<IdentityTable handlePopUpOpen={handlePopUpOpen} />
|
|
||||||
<IdentityModal popUp={popUp} handlePopUpToggle={handlePopUpToggle} />
|
|
||||||
<DeleteActionModal
|
|
||||||
isOpen={popUp.deleteIdentity.isOpen}
|
|
||||||
title={`Are you sure want to remove ${
|
|
||||||
(popUp?.deleteIdentity?.data as { name: string })?.name || ""
|
|
||||||
} from the project?`}
|
|
||||||
onChange={(isOpen) => handlePopUpToggle("deleteIdentity", isOpen)}
|
|
||||||
deleteKey="confirm"
|
|
||||||
onDeleteApproved={() =>
|
|
||||||
onRemoveIdentitySubmit(
|
|
||||||
(popUp?.deleteIdentity?.data as { identityId: string })?.identityId
|
|
||||||
)
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
{ action: ProjectPermissionActions.Read, subject: ProjectPermissionSub.Identity }
|
|
||||||
);
|
|
||||||
-108
@@ -1,108 +0,0 @@
|
|||||||
import { faServer, faXmark } from "@fortawesome/free-solid-svg-icons";
|
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
|
||||||
import { format } from "date-fns";
|
|
||||||
|
|
||||||
import { ProjectPermissionCan } from "@app/components/permissions";
|
|
||||||
import {
|
|
||||||
EmptyState,
|
|
||||||
IconButton,
|
|
||||||
Table,
|
|
||||||
TableContainer,
|
|
||||||
TableSkeleton,
|
|
||||||
TBody,
|
|
||||||
Td,
|
|
||||||
Th,
|
|
||||||
THead,
|
|
||||||
Tr
|
|
||||||
} from "@app/components/v2";
|
|
||||||
import { ProjectPermissionActions, ProjectPermissionSub, useWorkspace } from "@app/context";
|
|
||||||
import { useGetWorkspaceIdentityMemberships } from "@app/hooks/api";
|
|
||||||
import { UsePopUpState } from "@app/hooks/usePopUp";
|
|
||||||
|
|
||||||
import { IdentityRoles } from "./IdentityRoles";
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
handlePopUpOpen: (
|
|
||||||
popUpName: keyof UsePopUpState<["deleteIdentity", "identity"]>,
|
|
||||||
data?: {
|
|
||||||
identityId?: string;
|
|
||||||
name?: string;
|
|
||||||
}
|
|
||||||
) => void;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const IdentityTable = ({ handlePopUpOpen }: Props) => {
|
|
||||||
const { currentWorkspace } = useWorkspace();
|
|
||||||
const { data, isLoading } = useGetWorkspaceIdentityMemberships(currentWorkspace?.id || "");
|
|
||||||
|
|
||||||
return (
|
|
||||||
<TableContainer>
|
|
||||||
<Table>
|
|
||||||
<THead>
|
|
||||||
<Tr>
|
|
||||||
<Th>Name</Th>
|
|
||||||
<Th>Role</Th>
|
|
||||||
<Th>Added on</Th>
|
|
||||||
<Th className="w-5" />
|
|
||||||
</Tr>
|
|
||||||
</THead>
|
|
||||||
<TBody>
|
|
||||||
{isLoading && <TableSkeleton columns={7} innerKey="project-identities" />}
|
|
||||||
{!isLoading &&
|
|
||||||
data &&
|
|
||||||
data.length > 0 &&
|
|
||||||
data.map(({ identity: { id, name }, roles, createdAt }) => {
|
|
||||||
return (
|
|
||||||
<Tr className="h-10" key={`st-v3-${id}`}>
|
|
||||||
<Td>{name}</Td>
|
|
||||||
<Td>
|
|
||||||
<ProjectPermissionCan
|
|
||||||
I={ProjectPermissionActions.Edit}
|
|
||||||
a={ProjectPermissionSub.Identity}
|
|
||||||
>
|
|
||||||
{(isAllowed) => (
|
|
||||||
<IdentityRoles roles={roles} disableEdit={!isAllowed} identityId={id} />
|
|
||||||
)}
|
|
||||||
</ProjectPermissionCan>
|
|
||||||
</Td>
|
|
||||||
<Td>{format(new Date(createdAt), "yyyy-MM-dd")}</Td>
|
|
||||||
<Td className="flex justify-end">
|
|
||||||
<ProjectPermissionCan
|
|
||||||
I={ProjectPermissionActions.Delete}
|
|
||||||
a={ProjectPermissionSub.Identity}
|
|
||||||
>
|
|
||||||
{(isAllowed) => (
|
|
||||||
<IconButton
|
|
||||||
onClick={() => {
|
|
||||||
handlePopUpOpen("deleteIdentity", {
|
|
||||||
identityId: id,
|
|
||||||
name
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
size="lg"
|
|
||||||
colorSchema="danger"
|
|
||||||
variant="plain"
|
|
||||||
ariaLabel="update"
|
|
||||||
className="ml-4"
|
|
||||||
isDisabled={!isAllowed}
|
|
||||||
>
|
|
||||||
<FontAwesomeIcon icon={faXmark} />
|
|
||||||
</IconButton>
|
|
||||||
)}
|
|
||||||
</ProjectPermissionCan>
|
|
||||||
</Td>
|
|
||||||
</Tr>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
{!isLoading && data && data?.length === 0 && (
|
|
||||||
<Tr>
|
|
||||||
<Td colSpan={7}>
|
|
||||||
<EmptyState title="No identities have been added to this project" icon={faServer} />
|
|
||||||
</Td>
|
|
||||||
</Tr>
|
|
||||||
)}
|
|
||||||
</TBody>
|
|
||||||
</Table>
|
|
||||||
</TableContainer>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
-1
@@ -1 +0,0 @@
|
|||||||
export { IdentitySection } from "./IdentitySection";
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
export { IdentitySection } from "./IdentitySection";
|
|
||||||
@@ -215,7 +215,7 @@ export const MemberListTab = () => {
|
|||||||
onGoBack={() => handlePopUpClose("additionalPrivilege")}
|
onGoBack={() => handlePopUpClose("additionalPrivilege")}
|
||||||
privileges={members?.[privilegeDetails.index]?.additionalPrivileges || []}
|
privileges={members?.[privilegeDetails.index]?.additionalPrivileges || []}
|
||||||
name={privilegeDetails.name}
|
name={privilegeDetails.name}
|
||||||
projectMembershipId={privilegeDetails.projectMembershipId}
|
actorId={privilegeDetails.projectMembershipId}
|
||||||
/>
|
/>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user