mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-04 01:26:09 +00:00
feature: project role templates
This commit is contained in:
Generated
+4
-4
@@ -78,7 +78,7 @@
|
|||||||
"react-day-picker": "^9.4.3",
|
"react-day-picker": "^9.4.3",
|
||||||
"react-dom": "^18.3.1",
|
"react-dom": "^18.3.1",
|
||||||
"react-helmet": "^6.1.0",
|
"react-helmet": "^6.1.0",
|
||||||
"react-hook-form": "^7.54.0",
|
"react-hook-form": "^7.56.3",
|
||||||
"react-i18next": "^15.2.0",
|
"react-i18next": "^15.2.0",
|
||||||
"react-icons": "^5.4.0",
|
"react-icons": "^5.4.0",
|
||||||
"react-markdown": "^10.0.1",
|
"react-markdown": "^10.0.1",
|
||||||
@@ -11484,9 +11484,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/react-hook-form": {
|
"node_modules/react-hook-form": {
|
||||||
"version": "7.54.0",
|
"version": "7.56.3",
|
||||||
"resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.54.0.tgz",
|
"resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.56.3.tgz",
|
||||||
"integrity": "sha512-PS05+UQy/IdSbJNojBypxAo9wllhHgGmyr8/dyGQcPoiMf3e7Dfb9PWYVRco55bLbxH9S+1yDDJeTdlYCSxO3A==",
|
"integrity": "sha512-IK18V6GVbab4TAo1/cz3kqajxbDPGofdF0w7VHdCo0Nt8PrPlOZcuuDq9YYIV1BtjcX78x0XsldbQRQnQXWXmw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18.0.0"
|
"node": ">=18.0.0"
|
||||||
|
|||||||
@@ -82,7 +82,7 @@
|
|||||||
"react-day-picker": "^9.4.3",
|
"react-day-picker": "^9.4.3",
|
||||||
"react-dom": "^18.3.1",
|
"react-dom": "^18.3.1",
|
||||||
"react-helmet": "^6.1.0",
|
"react-helmet": "^6.1.0",
|
||||||
"react-hook-form": "^7.54.0",
|
"react-hook-form": "^7.56.3",
|
||||||
"react-i18next": "^15.2.0",
|
"react-i18next": "^15.2.0",
|
||||||
"react-icons": "^5.4.0",
|
"react-icons": "^5.4.0",
|
||||||
"react-markdown": "^10.0.1",
|
"react-markdown": "^10.0.1",
|
||||||
|
|||||||
+4
-19
@@ -1,5 +1,5 @@
|
|||||||
import { Controller, FormProvider, useForm } from "react-hook-form";
|
import { Controller, FormProvider, useForm } from "react-hook-form";
|
||||||
import { faChevronLeft, faPlus, faSave } from "@fortawesome/free-solid-svg-icons";
|
import { faChevronLeft, faSave } from "@fortawesome/free-solid-svg-icons";
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import { twMerge } from "tailwind-merge";
|
import { twMerge } from "tailwind-merge";
|
||||||
@@ -9,12 +9,11 @@ import { createNotification } from "@app/components/notifications";
|
|||||||
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 { isCustomProjectRole } from "@app/helpers/roles";
|
import { isCustomProjectRole } from "@app/helpers/roles";
|
||||||
import { usePopUp } from "@app/hooks";
|
|
||||||
import { TProjectTemplate, useUpdateProjectTemplate } from "@app/hooks/api/projectTemplates";
|
import { TProjectTemplate, useUpdateProjectTemplate } from "@app/hooks/api/projectTemplates";
|
||||||
import { slugSchema } from "@app/lib/schemas";
|
import { slugSchema } from "@app/lib/schemas";
|
||||||
|
import { AddPoliciesButton } from "@app/pages/project/RoleDetailsBySlugPage/components/AddPoliciesButton";
|
||||||
import { GeneralPermissionPolicies } from "@app/pages/project/RoleDetailsBySlugPage/components/GeneralPermissionPolicies";
|
import { GeneralPermissionPolicies } from "@app/pages/project/RoleDetailsBySlugPage/components/GeneralPermissionPolicies";
|
||||||
import { PermissionEmptyState } from "@app/pages/project/RoleDetailsBySlugPage/components/PermissionEmptyState";
|
import { PermissionEmptyState } from "@app/pages/project/RoleDetailsBySlugPage/components/PermissionEmptyState";
|
||||||
import { PolicySelectionModal } from "@app/pages/project/RoleDetailsBySlugPage/components/PolicySelectionModal";
|
|
||||||
import {
|
import {
|
||||||
formRolePermission2API,
|
formRolePermission2API,
|
||||||
PROJECT_PERMISSION_OBJECT,
|
PROJECT_PERMISSION_OBJECT,
|
||||||
@@ -44,8 +43,6 @@ export const ProjectTemplateEditRoleForm = ({
|
|||||||
role,
|
role,
|
||||||
isDisabled
|
isDisabled
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
const { popUp, handlePopUpToggle } = usePopUp(["addPolicy"] as const);
|
|
||||||
|
|
||||||
const formMethods = useForm<TFormSchema>({
|
const formMethods = useForm<TFormSchema>({
|
||||||
values: role ? { ...role, permissions: rolePermission2Form(role.permissions) } : undefined,
|
values: role ? { ...role, permissions: rolePermission2Form(role.permissions) } : undefined,
|
||||||
resolver: zodResolver(formSchema)
|
resolver: zodResolver(formSchema)
|
||||||
@@ -120,7 +117,7 @@ export const ProjectTemplateEditRoleForm = ({
|
|||||||
variant="outline_bg"
|
variant="outline_bg"
|
||||||
type="submit"
|
type="submit"
|
||||||
className={twMerge(
|
className={twMerge(
|
||||||
"h-10 rounded-r-none border border-primary",
|
"mr-4 h-10 border border-primary",
|
||||||
isDirty && "bg-primary text-black"
|
isDirty && "bg-primary text-black"
|
||||||
)}
|
)}
|
||||||
isDisabled={isSubmitting || !isDirty || isDisabled}
|
isDisabled={isSubmitting || !isDirty || isDisabled}
|
||||||
@@ -129,19 +126,7 @@ export const ProjectTemplateEditRoleForm = ({
|
|||||||
>
|
>
|
||||||
Save
|
Save
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<AddPoliciesButton isDisabled={isDisabled} />
|
||||||
className="h-10 rounded-l-none"
|
|
||||||
variant="outline_bg"
|
|
||||||
leftIcon={<FontAwesomeIcon icon={faPlus} />}
|
|
||||||
isDisabled={isDisabled}
|
|
||||||
onClick={() => handlePopUpToggle("addPolicy")}
|
|
||||||
>
|
|
||||||
Add Policies
|
|
||||||
</Button>
|
|
||||||
<PolicySelectionModal
|
|
||||||
isOpen={popUp.addPolicy.isOpen}
|
|
||||||
onOpenChange={(isOpen) => handlePopUpToggle("addPolicy", isOpen)}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
+4
-24
@@ -1,12 +1,6 @@
|
|||||||
import { Controller, FormProvider, useForm } from "react-hook-form";
|
import { Controller, FormProvider, useForm } from "react-hook-form";
|
||||||
import { subject } from "@casl/ability";
|
import { subject } from "@casl/ability";
|
||||||
import {
|
import { faCaretDown, faChevronLeft, faClock, faSave } from "@fortawesome/free-solid-svg-icons";
|
||||||
faCaretDown,
|
|
||||||
faChevronLeft,
|
|
||||||
faClock,
|
|
||||||
faPlus,
|
|
||||||
faSave
|
|
||||||
} from "@fortawesome/free-solid-svg-icons";
|
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import { format, formatDistance } from "date-fns";
|
import { format, formatDistance } from "date-fns";
|
||||||
@@ -33,16 +27,15 @@ import {
|
|||||||
useProjectPermission,
|
useProjectPermission,
|
||||||
useWorkspace
|
useWorkspace
|
||||||
} from "@app/context";
|
} from "@app/context";
|
||||||
import { usePopUp } from "@app/hooks";
|
|
||||||
import {
|
import {
|
||||||
useCreateIdentityProjectAdditionalPrivilege,
|
useCreateIdentityProjectAdditionalPrivilege,
|
||||||
useGetIdentityProjectPrivilegeDetails,
|
useGetIdentityProjectPrivilegeDetails,
|
||||||
useUpdateIdentityProjectAdditionalPrivilege
|
useUpdateIdentityProjectAdditionalPrivilege
|
||||||
} from "@app/hooks/api";
|
} from "@app/hooks/api";
|
||||||
import { IdentityProjectAdditionalPrivilegeTemporaryMode } from "@app/hooks/api/identityProjectAdditionalPrivilege/types";
|
import { IdentityProjectAdditionalPrivilegeTemporaryMode } from "@app/hooks/api/identityProjectAdditionalPrivilege/types";
|
||||||
|
import { AddPoliciesButton } from "@app/pages/project/RoleDetailsBySlugPage/components/AddPoliciesButton";
|
||||||
import { GeneralPermissionPolicies } from "@app/pages/project/RoleDetailsBySlugPage/components/GeneralPermissionPolicies";
|
import { GeneralPermissionPolicies } from "@app/pages/project/RoleDetailsBySlugPage/components/GeneralPermissionPolicies";
|
||||||
import { PermissionEmptyState } from "@app/pages/project/RoleDetailsBySlugPage/components/PermissionEmptyState";
|
import { PermissionEmptyState } from "@app/pages/project/RoleDetailsBySlugPage/components/PermissionEmptyState";
|
||||||
import { PolicySelectionModal } from "@app/pages/project/RoleDetailsBySlugPage/components/PolicySelectionModal";
|
|
||||||
import {
|
import {
|
||||||
formRolePermission2API,
|
formRolePermission2API,
|
||||||
PROJECT_PERMISSION_OBJECT,
|
PROJECT_PERMISSION_OBJECT,
|
||||||
@@ -97,7 +90,6 @@ export const IdentityProjectAdditionalPrivilegeModifySection = ({
|
|||||||
ProjectPermissionIdentityActions.Edit,
|
ProjectPermissionIdentityActions.Edit,
|
||||||
subject(ProjectPermissionSub.Identity, { identityId })
|
subject(ProjectPermissionSub.Identity, { identityId })
|
||||||
);
|
);
|
||||||
const { popUp, handlePopUpToggle } = usePopUp(["addPolicy"] as const);
|
|
||||||
|
|
||||||
const form = useForm<TFormSchema>({
|
const form = useForm<TFormSchema>({
|
||||||
values: privilegeDetails
|
values: privilegeDetails
|
||||||
@@ -224,7 +216,7 @@ export const IdentityProjectAdditionalPrivilegeModifySection = ({
|
|||||||
variant="outline_bg"
|
variant="outline_bg"
|
||||||
type="submit"
|
type="submit"
|
||||||
className={twMerge(
|
className={twMerge(
|
||||||
"h-10 rounded-r-none border border-primary",
|
"mr-4 h-10 border border-primary",
|
||||||
isDirty && "bg-primary text-black"
|
isDirty && "bg-primary text-black"
|
||||||
)}
|
)}
|
||||||
isDisabled={isSubmitting || !isDirty || isDisabled}
|
isDisabled={isSubmitting || !isDirty || isDisabled}
|
||||||
@@ -233,15 +225,7 @@ export const IdentityProjectAdditionalPrivilegeModifySection = ({
|
|||||||
>
|
>
|
||||||
Save
|
Save
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<AddPoliciesButton isDisabled={isDisabled} />
|
||||||
isDisabled={isDisabled}
|
|
||||||
className="h-10 rounded-l-none"
|
|
||||||
variant="outline_bg"
|
|
||||||
leftIcon={<FontAwesomeIcon icon={faPlus} />}
|
|
||||||
onClick={() => handlePopUpToggle("addPolicy")}
|
|
||||||
>
|
|
||||||
Add Policies
|
|
||||||
</Button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -382,10 +366,6 @@ export const IdentityProjectAdditionalPrivilegeModifySection = ({
|
|||||||
)
|
)
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<PolicySelectionModal
|
|
||||||
isOpen={popUp.addPolicy.isOpen}
|
|
||||||
onOpenChange={(isOpen) => handlePopUpToggle("addPolicy", isOpen)}
|
|
||||||
/>
|
|
||||||
</FormProvider>
|
</FormProvider>
|
||||||
</form>
|
</form>
|
||||||
);
|
);
|
||||||
|
|||||||
+4
-25
@@ -1,11 +1,5 @@
|
|||||||
import { Controller, FormProvider, useForm } from "react-hook-form";
|
import { Controller, FormProvider, useForm } from "react-hook-form";
|
||||||
import {
|
import { faCaretDown, faChevronLeft, faClock, faSave } from "@fortawesome/free-solid-svg-icons";
|
||||||
faCaretDown,
|
|
||||||
faChevronLeft,
|
|
||||||
faClock,
|
|
||||||
faPlus,
|
|
||||||
faSave
|
|
||||||
} from "@fortawesome/free-solid-svg-icons";
|
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import { format, formatDistance } from "date-fns";
|
import { format, formatDistance } from "date-fns";
|
||||||
@@ -32,16 +26,15 @@ import {
|
|||||||
useProjectPermission,
|
useProjectPermission,
|
||||||
useWorkspace
|
useWorkspace
|
||||||
} from "@app/context";
|
} from "@app/context";
|
||||||
import { usePopUp } from "@app/hooks";
|
|
||||||
import {
|
import {
|
||||||
useCreateProjectUserAdditionalPrivilege,
|
useCreateProjectUserAdditionalPrivilege,
|
||||||
useGetProjectUserPrivilegeDetails,
|
useGetProjectUserPrivilegeDetails,
|
||||||
useUpdateProjectUserAdditionalPrivilege
|
useUpdateProjectUserAdditionalPrivilege
|
||||||
} from "@app/hooks/api";
|
} from "@app/hooks/api";
|
||||||
import { ProjectUserAdditionalPrivilegeTemporaryMode } from "@app/hooks/api/projectUserAdditionalPrivilege/types";
|
import { ProjectUserAdditionalPrivilegeTemporaryMode } from "@app/hooks/api/projectUserAdditionalPrivilege/types";
|
||||||
|
import { AddPoliciesButton } from "@app/pages/project/RoleDetailsBySlugPage/components/AddPoliciesButton";
|
||||||
import { GeneralPermissionPolicies } from "@app/pages/project/RoleDetailsBySlugPage/components/GeneralPermissionPolicies";
|
import { GeneralPermissionPolicies } from "@app/pages/project/RoleDetailsBySlugPage/components/GeneralPermissionPolicies";
|
||||||
import { PermissionEmptyState } from "@app/pages/project/RoleDetailsBySlugPage/components/PermissionEmptyState";
|
import { PermissionEmptyState } from "@app/pages/project/RoleDetailsBySlugPage/components/PermissionEmptyState";
|
||||||
import { PolicySelectionModal } from "@app/pages/project/RoleDetailsBySlugPage/components/PolicySelectionModal";
|
|
||||||
import {
|
import {
|
||||||
formRolePermission2API,
|
formRolePermission2API,
|
||||||
PROJECT_PERMISSION_OBJECT,
|
PROJECT_PERMISSION_OBJECT,
|
||||||
@@ -83,8 +76,6 @@ export const MembershipProjectAdditionalPrivilegeModifySection = ({
|
|||||||
projectMembershipId,
|
projectMembershipId,
|
||||||
isDisabled
|
isDisabled
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
const { popUp, handlePopUpToggle } = usePopUp(["addPolicy"] as const);
|
|
||||||
|
|
||||||
const isCreate = !privilegeId;
|
const isCreate = !privilegeId;
|
||||||
const { currentWorkspace } = useWorkspace();
|
const { currentWorkspace } = useWorkspace();
|
||||||
const projectId = currentWorkspace?.id || "";
|
const projectId = currentWorkspace?.id || "";
|
||||||
@@ -221,7 +212,7 @@ export const MembershipProjectAdditionalPrivilegeModifySection = ({
|
|||||||
variant="outline_bg"
|
variant="outline_bg"
|
||||||
type="submit"
|
type="submit"
|
||||||
className={twMerge(
|
className={twMerge(
|
||||||
"h-10 rounded-r-none border border-primary",
|
"mr-4 h-10 border border-primary",
|
||||||
isDirty && "bg-primary text-black"
|
isDirty && "bg-primary text-black"
|
||||||
)}
|
)}
|
||||||
isDisabled={isSubmitting || !isDirty || isDisabled}
|
isDisabled={isSubmitting || !isDirty || isDisabled}
|
||||||
@@ -230,15 +221,7 @@ export const MembershipProjectAdditionalPrivilegeModifySection = ({
|
|||||||
>
|
>
|
||||||
Save
|
Save
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<AddPoliciesButton isDisabled={isDisabled} />
|
||||||
isDisabled={isDisabled}
|
|
||||||
className="h-10 rounded-l-none"
|
|
||||||
variant="outline_bg"
|
|
||||||
leftIcon={<FontAwesomeIcon icon={faPlus} />}
|
|
||||||
onClick={() => handlePopUpToggle("addPolicy")}
|
|
||||||
>
|
|
||||||
Add Policies
|
|
||||||
</Button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -377,10 +360,6 @@ export const MembershipProjectAdditionalPrivilegeModifySection = ({
|
|||||||
</GeneralPermissionPolicies>
|
</GeneralPermissionPolicies>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
<PolicySelectionModal
|
|
||||||
isOpen={popUp.addPolicy.isOpen}
|
|
||||||
onOpenChange={(isOpen) => handlePopUpToggle("addPolicy", isOpen)}
|
|
||||||
/>
|
|
||||||
</FormProvider>
|
</FormProvider>
|
||||||
</form>
|
</form>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -0,0 +1,78 @@
|
|||||||
|
import { faAngleDown, faLayerGroup, faPlus } from "@fortawesome/free-solid-svg-icons";
|
||||||
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
|
|
||||||
|
import {
|
||||||
|
Button,
|
||||||
|
DropdownMenu,
|
||||||
|
DropdownMenuContent,
|
||||||
|
DropdownMenuTrigger,
|
||||||
|
IconButton
|
||||||
|
} from "@app/components/v2";
|
||||||
|
import { usePopUp } from "@app/hooks";
|
||||||
|
import { PolicySelectionModal } from "@app/pages/project/RoleDetailsBySlugPage/components/PolicySelectionModal";
|
||||||
|
import { PolicyTemplateModal } from "@app/pages/project/RoleDetailsBySlugPage/components/PolicyTemplateModal";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
isDisabled?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const AddPoliciesButton = ({ isDisabled }: Props) => {
|
||||||
|
const { popUp, handlePopUpToggle, handlePopUpOpen, handlePopUpClose } = usePopUp([
|
||||||
|
"addPolicy",
|
||||||
|
"addPolicyOptions",
|
||||||
|
"applyTemplate"
|
||||||
|
] as const);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Button
|
||||||
|
className="h-10 rounded-r-none"
|
||||||
|
variant="outline_bg"
|
||||||
|
leftIcon={<FontAwesomeIcon icon={faPlus} />}
|
||||||
|
isDisabled={isDisabled}
|
||||||
|
onClick={() => handlePopUpToggle("addPolicy")}
|
||||||
|
>
|
||||||
|
Add Policies
|
||||||
|
</Button>
|
||||||
|
<DropdownMenu
|
||||||
|
open={popUp.addPolicyOptions.isOpen}
|
||||||
|
onOpenChange={(isOpen) => handlePopUpToggle("addPolicyOptions", isOpen)}
|
||||||
|
>
|
||||||
|
<DropdownMenuTrigger asChild>
|
||||||
|
<IconButton
|
||||||
|
ariaLabel="add-policies-options"
|
||||||
|
variant="outline_bg"
|
||||||
|
className="rounded-l-none bg-mineshaft-600 p-3"
|
||||||
|
>
|
||||||
|
<FontAwesomeIcon icon={faAngleDown} />
|
||||||
|
</IconButton>
|
||||||
|
</DropdownMenuTrigger>
|
||||||
|
<DropdownMenuContent sideOffset={2} align="end">
|
||||||
|
<div className="flex flex-col space-y-1 p-1.5">
|
||||||
|
<Button
|
||||||
|
leftIcon={<FontAwesomeIcon icon={faLayerGroup} className="pr-2" />}
|
||||||
|
onClick={() => {
|
||||||
|
handlePopUpOpen("applyTemplate");
|
||||||
|
handlePopUpClose("addPolicyOptions");
|
||||||
|
}}
|
||||||
|
isDisabled={isDisabled}
|
||||||
|
variant="outline_bg"
|
||||||
|
className="h-10 text-left"
|
||||||
|
isFullWidth
|
||||||
|
>
|
||||||
|
Add From Template
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
|
<PolicySelectionModal
|
||||||
|
isOpen={popUp.addPolicy.isOpen}
|
||||||
|
onOpenChange={(isOpen) => handlePopUpToggle("addPolicy", isOpen)}
|
||||||
|
/>
|
||||||
|
<PolicyTemplateModal
|
||||||
|
isOpen={popUp.applyTemplate.isOpen}
|
||||||
|
onOpenChange={(isOpen) => handlePopUpToggle("applyTemplate", isOpen)}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
+49
-20
@@ -1,5 +1,5 @@
|
|||||||
import { cloneElement, useState } from "react";
|
import { cloneElement, ReactNode, useState } from "react";
|
||||||
import { Controller, useFieldArray, useFormContext } from "react-hook-form";
|
import { Controller, useFieldArray, useFormContext, useWatch } from "react-hook-form";
|
||||||
import {
|
import {
|
||||||
faChevronDown,
|
faChevronDown,
|
||||||
faChevronRight,
|
faChevronRight,
|
||||||
@@ -29,6 +29,39 @@ type Props<T extends ProjectPermissionSub> = {
|
|||||||
isDisabled?: boolean;
|
isDisabled?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type ActionProps = {
|
||||||
|
value: string;
|
||||||
|
subject: ProjectPermissionSub;
|
||||||
|
rootIndex: number;
|
||||||
|
label: ReactNode;
|
||||||
|
isDisabled?: boolean;
|
||||||
|
control: any;
|
||||||
|
};
|
||||||
|
|
||||||
|
const ActionCheckbox = ({ value, subject, isDisabled, rootIndex, label, control }: ActionProps) => {
|
||||||
|
// scott: using Controller caused discrepency between field value and actual value, this is a hacky fix
|
||||||
|
const fieldValue = useWatch({ control, name: `permissions.${subject}.${rootIndex}.${value}` });
|
||||||
|
const { setValue } = useFormContext();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex items-center justify-center">
|
||||||
|
<Checkbox
|
||||||
|
isDisabled={isDisabled}
|
||||||
|
isChecked={Boolean(fieldValue)}
|
||||||
|
onCheckedChange={(isChecked) =>
|
||||||
|
setValue(`permissions.${subject}.${rootIndex}.${value}`, isChecked, {
|
||||||
|
shouldDirty: true,
|
||||||
|
shouldTouch: true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
id={`permissions.${subject}.${rootIndex}.${String(value)}`}
|
||||||
|
>
|
||||||
|
{label}
|
||||||
|
</Checkbox>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export const GeneralPermissionPolicies = <T extends keyof NonNullable<TFormSchema["permissions"]>>({
|
export const GeneralPermissionPolicies = <T extends keyof NonNullable<TFormSchema["permissions"]>>({
|
||||||
subject,
|
subject,
|
||||||
actions,
|
actions,
|
||||||
@@ -41,11 +74,18 @@ export const GeneralPermissionPolicies = <T extends keyof NonNullable<TFormSchem
|
|||||||
control,
|
control,
|
||||||
name: `permissions.${subject}`
|
name: `permissions.${subject}`
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// scott: this is a hacky work-around to resolve bug of fields not updating UI when removed
|
||||||
|
const watchFields: any[] = useWatch({
|
||||||
|
control,
|
||||||
|
name: `permissions.${subject}`
|
||||||
|
});
|
||||||
|
|
||||||
const [isOpen, setIsOpen] = useToggle();
|
const [isOpen, setIsOpen] = useToggle();
|
||||||
const [draggedItem, setDraggedItem] = useState<number | null>(null);
|
const [draggedItem, setDraggedItem] = useState<number | null>(null);
|
||||||
const [dragOverItem, setDragOverItem] = useState<number | null>(null);
|
const [dragOverItem, setDragOverItem] = useState<number | null>(null);
|
||||||
|
|
||||||
if (!fields.length) return <div />;
|
if (!watchFields?.length) return <div />;
|
||||||
|
|
||||||
const handleDragStart = (_: React.DragEvent, index: number) => {
|
const handleDragStart = (_: React.DragEvent, index: number) => {
|
||||||
setDraggedItem(index);
|
setDraggedItem(index);
|
||||||
@@ -194,25 +234,14 @@ export const GeneralPermissionPolicies = <T extends keyof NonNullable<TFormSchem
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Controller
|
<ActionCheckbox
|
||||||
key={`${el.id}-${index + 1}`}
|
key={`${el.id}-${index + 1}`}
|
||||||
name={`permissions.${subject}.${rootIndex}.${value}` as any}
|
value={value}
|
||||||
|
label={label}
|
||||||
|
rootIndex={rootIndex}
|
||||||
control={control}
|
control={control}
|
||||||
defaultValue={false}
|
subject={subject}
|
||||||
render={({ field }) => {
|
isDisabled={isDisabled}
|
||||||
return (
|
|
||||||
<div className="flex items-center justify-center">
|
|
||||||
<Checkbox
|
|
||||||
isDisabled={isDisabled}
|
|
||||||
isChecked={Boolean(field.value)}
|
|
||||||
onCheckedChange={field.onChange}
|
|
||||||
id={`permissions.${subject}.${rootIndex}.${String(value)}`}
|
|
||||||
>
|
|
||||||
{label}
|
|
||||||
</Checkbox>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
|||||||
@@ -0,0 +1,202 @@
|
|||||||
|
import { useState } from "react";
|
||||||
|
import { useFormContext } from "react-hook-form";
|
||||||
|
|
||||||
|
import { createNotification } from "@app/components/notifications";
|
||||||
|
import {
|
||||||
|
Accordion,
|
||||||
|
AccordionContent,
|
||||||
|
AccordionItem,
|
||||||
|
AccordionTrigger,
|
||||||
|
Button,
|
||||||
|
Modal,
|
||||||
|
ModalClose,
|
||||||
|
ModalContent
|
||||||
|
} from "@app/components/v2";
|
||||||
|
import { ProjectPermissionSub } from "@app/context";
|
||||||
|
import { useGetProjectTypeFromRoute } from "@app/hooks";
|
||||||
|
import { ProjectType } from "@app/hooks/api/workspace/types";
|
||||||
|
|
||||||
|
import {
|
||||||
|
PROJECT_PERMISSION_OBJECT,
|
||||||
|
RoleTemplate,
|
||||||
|
RoleTemplates,
|
||||||
|
TFormSchema
|
||||||
|
} from "./ProjectRoleModifySection.utils";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
isOpen: boolean;
|
||||||
|
onOpenChange: (isOpen: boolean) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
type ContentProps = {
|
||||||
|
onClose: () => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
const Content = ({ onClose }: ContentProps) => {
|
||||||
|
const rootForm = useFormContext<TFormSchema>();
|
||||||
|
const projectType = useGetProjectTypeFromRoute();
|
||||||
|
|
||||||
|
const [selectedTemplate, setSelectedTemplate] = useState<RoleTemplate>();
|
||||||
|
const [conflictingSubjects, setConflictingSubjects] = useState<ProjectPermissionSub[]>([]);
|
||||||
|
const [showConflictingSubjects, setShowConflictingSubjects] = useState(false);
|
||||||
|
|
||||||
|
const templates = RoleTemplates[projectType ?? ProjectType.SecretManager];
|
||||||
|
|
||||||
|
const onSubmit = (skipConflicting = false) => {
|
||||||
|
if (!selectedTemplate) {
|
||||||
|
createNotification({ type: "error", text: "Please select a template" });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
selectedTemplate.permissions.forEach(({ subject, actions }) => {
|
||||||
|
if (skipConflicting && conflictingSubjects.includes(subject)) return;
|
||||||
|
|
||||||
|
rootForm.setValue(
|
||||||
|
`permissions.${subject}`,
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
|
// @ts-ignore-error akhilmhdh: this is because of ts collision with both
|
||||||
|
[Object.fromEntries(actions.map((action) => [action, true]))],
|
||||||
|
{
|
||||||
|
shouldDirty: true,
|
||||||
|
shouldTouch: true,
|
||||||
|
shouldValidate: true
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
onClose();
|
||||||
|
};
|
||||||
|
|
||||||
|
const onApply = () => {
|
||||||
|
if (!selectedTemplate) {
|
||||||
|
createNotification({ type: "error", text: "Please select a template" });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const conflictingPolicies: ProjectPermissionSub[] = [];
|
||||||
|
|
||||||
|
selectedTemplate.permissions.forEach(({ subject }) => {
|
||||||
|
const rootPolicyValue = rootForm.getValues("permissions")?.[subject];
|
||||||
|
|
||||||
|
if (rootPolicyValue?.length) {
|
||||||
|
conflictingPolicies.push(subject);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (conflictingPolicies.length) {
|
||||||
|
setConflictingSubjects(conflictingPolicies);
|
||||||
|
setShowConflictingSubjects(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
onSubmit();
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Modal isOpen={showConflictingSubjects} onOpenChange={setShowConflictingSubjects}>
|
||||||
|
<ModalContent
|
||||||
|
title="Conflicting Policies"
|
||||||
|
subTitle="The following resources already have policies assigned to them."
|
||||||
|
>
|
||||||
|
<div className="grid grid-cols-2 gap-2 text-sm">
|
||||||
|
{conflictingSubjects.map((subject) => (
|
||||||
|
<div key={subject}>
|
||||||
|
<span className="text-mineshaft-200">
|
||||||
|
{PROJECT_PERMISSION_OBJECT[subject].title}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<div className="mt-8 flex space-x-4">
|
||||||
|
<ModalClose asChild>
|
||||||
|
<Button colorSchema="danger" onClick={() => onSubmit()}>
|
||||||
|
Overwrite Existing
|
||||||
|
</Button>
|
||||||
|
</ModalClose>
|
||||||
|
<ModalClose asChild>
|
||||||
|
<Button colorSchema="secondary" onClick={() => onSubmit(true)}>
|
||||||
|
Skip Conflicting
|
||||||
|
</Button>
|
||||||
|
</ModalClose>
|
||||||
|
</div>
|
||||||
|
</ModalContent>
|
||||||
|
</Modal>
|
||||||
|
<Accordion
|
||||||
|
type="single"
|
||||||
|
value={selectedTemplate?.id}
|
||||||
|
onValueChange={(value) =>
|
||||||
|
setSelectedTemplate(templates.find((template) => template.id === value))
|
||||||
|
}
|
||||||
|
collapsible
|
||||||
|
className="w-full border-collapse"
|
||||||
|
>
|
||||||
|
{templates.map(({ name, description, permissions, id }) => (
|
||||||
|
<AccordionItem
|
||||||
|
key={id}
|
||||||
|
value={id}
|
||||||
|
className="m-0 border border-mineshaft-600 first:rounded-t last:rounded-b data-[state=open]:border-primary/40 data-[state=open]:bg-mineshaft-600/30"
|
||||||
|
>
|
||||||
|
<AccordionTrigger className="w-full justify-start p-4 py-8 text-mineshaft-100 hover:bg-mineshaft-700 hover:text-mineshaft-100 data-[state=open]:bg-primary/[3%] data-[state=open]:text-mineshaft-100">
|
||||||
|
<div className="mr-auto flex flex-col py-2 text-left">
|
||||||
|
<span>{name}</span>
|
||||||
|
<span className="text-sm leading-3 text-mineshaft-400">{description}</span>
|
||||||
|
</div>
|
||||||
|
</AccordionTrigger>
|
||||||
|
<AccordionContent className="border-t border-mineshaft-600">
|
||||||
|
<div className="thin-scrollbar max-h-[20rem] overflow-y-auto">
|
||||||
|
<span className="text-mineshaft-300">Grants the following permissions:</span>
|
||||||
|
<div className="grid grid-cols-2 gap-4 py-2">
|
||||||
|
{permissions
|
||||||
|
.map((permission) => ({
|
||||||
|
...permission,
|
||||||
|
object: PROJECT_PERMISSION_OBJECT[permission.subject]
|
||||||
|
}))
|
||||||
|
.sort((a, b) => a.object.title.localeCompare(b.object.title))
|
||||||
|
.map(({ subject, actions, object }) => {
|
||||||
|
return (
|
||||||
|
<div key={subject}>
|
||||||
|
<span className="text-mineshaft-200">{object.title}</span>
|
||||||
|
<ul className="mt-1 flex list-disc flex-col gap-1 pl-4">
|
||||||
|
{actions.map((action) => (
|
||||||
|
<li key={action}>
|
||||||
|
{object.actions.find((a) => a.value === action)?.label}
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</AccordionContent>
|
||||||
|
</AccordionItem>
|
||||||
|
))}
|
||||||
|
</Accordion>
|
||||||
|
<div className="mt-8 flex space-x-4">
|
||||||
|
<Button isDisabled={!selectedTemplate} onClick={onApply}>
|
||||||
|
Apply Template
|
||||||
|
</Button>
|
||||||
|
<ModalClose asChild>
|
||||||
|
<Button colorSchema="secondary" variant="plain">
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
</ModalClose>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const PolicyTemplateModal = ({ isOpen, onOpenChange }: Props) => {
|
||||||
|
return (
|
||||||
|
<Modal isOpen={isOpen} onOpenChange={onOpenChange}>
|
||||||
|
<ModalContent
|
||||||
|
title="Policy Templates"
|
||||||
|
subTitle="Select a template with prepopulated policies to get started. You can always add more policies later."
|
||||||
|
className="max-w-3xl"
|
||||||
|
>
|
||||||
|
<Content onClose={() => onOpenChange(false)} />
|
||||||
|
</ModalContent>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
};
|
||||||
+346
-9
@@ -877,19 +877,19 @@ export const PROJECT_PERMISSION_OBJECT: TProjectPermissionObject = {
|
|||||||
title: "Dynamic Secrets",
|
title: "Dynamic Secrets",
|
||||||
actions: [
|
actions: [
|
||||||
{
|
{
|
||||||
label: "Read root credentials",
|
label: "Read Root Credentials",
|
||||||
value: ProjectPermissionDynamicSecretActions.ReadRootCredential
|
value: ProjectPermissionDynamicSecretActions.ReadRootCredential
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "Create root credentials",
|
label: "Create Root Credentials",
|
||||||
value: ProjectPermissionDynamicSecretActions.CreateRootCredential
|
value: ProjectPermissionDynamicSecretActions.CreateRootCredential
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "Modify root credentials",
|
label: "Modify Root Credentials",
|
||||||
value: ProjectPermissionDynamicSecretActions.EditRootCredential
|
value: ProjectPermissionDynamicSecretActions.EditRootCredential
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "Remove root credentials",
|
label: "Remove Root Credentials",
|
||||||
value: ProjectPermissionDynamicSecretActions.DeleteRootCredential
|
value: ProjectPermissionDynamicSecretActions.DeleteRootCredential
|
||||||
},
|
},
|
||||||
{ label: "Manage Leases", value: ProjectPermissionDynamicSecretActions.Lease }
|
{ label: "Manage Leases", value: ProjectPermissionDynamicSecretActions.Lease }
|
||||||
@@ -1174,23 +1174,23 @@ export const PROJECT_PERMISSION_OBJECT: TProjectPermissionObject = {
|
|||||||
title: "KMIP",
|
title: "KMIP",
|
||||||
actions: [
|
actions: [
|
||||||
{
|
{
|
||||||
label: "Read clients",
|
label: "Read Clients",
|
||||||
value: ProjectPermissionKmipActions.ReadClients
|
value: ProjectPermissionKmipActions.ReadClients
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "Create clients",
|
label: "Create Clients",
|
||||||
value: ProjectPermissionKmipActions.CreateClients
|
value: ProjectPermissionKmipActions.CreateClients
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "Modify clients",
|
label: "Modify Clients",
|
||||||
value: ProjectPermissionKmipActions.UpdateClients
|
value: ProjectPermissionKmipActions.UpdateClients
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "Delete clients",
|
label: "Delete Clients",
|
||||||
value: ProjectPermissionKmipActions.DeleteClients
|
value: ProjectPermissionKmipActions.DeleteClients
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "Generate client certificates",
|
label: "Generate Client Certificates",
|
||||||
value: ProjectPermissionKmipActions.GenerateClientCertificates
|
value: ProjectPermissionKmipActions.GenerateClientCertificates
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -1280,3 +1280,340 @@ export const ProjectTypePermissionSubjects: Record<
|
|||||||
...SecretsManagerPermissionSubjects()
|
...SecretsManagerPermissionSubjects()
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type RoleTemplate = {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
description: string;
|
||||||
|
permissions: { subject: ProjectPermissionSub; actions: string[] }[];
|
||||||
|
};
|
||||||
|
|
||||||
|
const projectManagerTemplate = (
|
||||||
|
additionalPermissions: RoleTemplate["permissions"] = []
|
||||||
|
): RoleTemplate => ({
|
||||||
|
id: "project-manager",
|
||||||
|
name: "Project Management Policies",
|
||||||
|
description: "Grants access to manage project members and settings",
|
||||||
|
permissions: [
|
||||||
|
{
|
||||||
|
subject: ProjectPermissionSub.AuditLogs,
|
||||||
|
actions: Object.values(ProjectPermissionActions)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
subject: ProjectPermissionSub.Groups,
|
||||||
|
actions: Object.values(ProjectPermissionGroupActions)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
subject: ProjectPermissionSub.Member,
|
||||||
|
actions: Object.values(ProjectPermissionMemberActions)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
subject: ProjectPermissionSub.Identity,
|
||||||
|
actions: Object.values(ProjectPermissionIdentityActions)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
subject: ProjectPermissionSub.Project,
|
||||||
|
actions: [ProjectPermissionActions.Edit, ProjectPermissionActions.Delete]
|
||||||
|
},
|
||||||
|
{ subject: ProjectPermissionSub.Role, actions: Object.values(ProjectPermissionActions) },
|
||||||
|
{
|
||||||
|
subject: ProjectPermissionSub.Settings,
|
||||||
|
actions: [ProjectPermissionActions.Read, ProjectPermissionActions.Edit]
|
||||||
|
},
|
||||||
|
...additionalPermissions
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
export const RoleTemplates: Record<ProjectType, RoleTemplate[]> = {
|
||||||
|
[ProjectType.SSH]: [
|
||||||
|
{
|
||||||
|
id: "ssh-viewer",
|
||||||
|
name: "SSH Viewing Policies",
|
||||||
|
description: "Grants read access to SSH certificates and hosts",
|
||||||
|
permissions: [
|
||||||
|
{
|
||||||
|
subject: ProjectPermissionSub.SshCertificateAuthorities,
|
||||||
|
actions: [ProjectPermissionActions.Read]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
subject: ProjectPermissionSub.SshCertificates,
|
||||||
|
actions: [ProjectPermissionActions.Read]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
subject: ProjectPermissionSub.SshCertificateTemplates,
|
||||||
|
actions: [ProjectPermissionActions.Read]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
subject: ProjectPermissionSub.SshHosts,
|
||||||
|
actions: [ProjectPermissionSshHostActions.Read]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
subject: ProjectPermissionSub.SshHostGroups,
|
||||||
|
actions: [ProjectPermissionActions.Read]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "ssh-cert-editor",
|
||||||
|
name: "SSH Certificate Editing Policies",
|
||||||
|
description: "Grants read and edit access to SSH certificates",
|
||||||
|
permissions: [
|
||||||
|
{
|
||||||
|
subject: ProjectPermissionSub.SshCertificateAuthorities,
|
||||||
|
actions: Object.values(ProjectPermissionActions)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
subject: ProjectPermissionSub.SshCertificates,
|
||||||
|
actions: Object.values(ProjectPermissionActions)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
subject: ProjectPermissionSub.SshCertificateTemplates,
|
||||||
|
actions: Object.values(ProjectPermissionActions)
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "ssh-host-editor",
|
||||||
|
name: "SSH Host Editing Policies",
|
||||||
|
description: "Grants read and edit access to SSH hosts",
|
||||||
|
permissions: [
|
||||||
|
{
|
||||||
|
subject: ProjectPermissionSub.SshHosts,
|
||||||
|
actions: Object.values(ProjectPermissionSshHostActions)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
subject: ProjectPermissionSub.SshHostGroups,
|
||||||
|
actions: Object.values(ProjectPermissionActions)
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
projectManagerTemplate()
|
||||||
|
],
|
||||||
|
[ProjectType.KMS]: [
|
||||||
|
{
|
||||||
|
id: "kms-viewer",
|
||||||
|
name: "KMS Viewing Policies",
|
||||||
|
description: "Grants read access to KMS keys and KMIP clients",
|
||||||
|
permissions: [
|
||||||
|
{
|
||||||
|
subject: ProjectPermissionSub.Cmek,
|
||||||
|
actions: [ProjectPermissionCmekActions.Read]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
subject: ProjectPermissionSub.Kmip,
|
||||||
|
actions: [ProjectPermissionKmipActions.ReadClients]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "key-editor",
|
||||||
|
name: "KMS Key Editing Policies",
|
||||||
|
description: "Grants read and edit access to KMS keys",
|
||||||
|
permissions: [
|
||||||
|
{
|
||||||
|
subject: ProjectPermissionSub.Cmek,
|
||||||
|
actions: Object.values(ProjectPermissionCmekActions)
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "kmip-editor",
|
||||||
|
name: "KMIP Client Editing Policies",
|
||||||
|
description: "Grants read and edit access to KMIP clients",
|
||||||
|
permissions: [
|
||||||
|
{
|
||||||
|
subject: ProjectPermissionSub.Kmip,
|
||||||
|
actions: Object.values(ProjectPermissionKmipActions)
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
projectManagerTemplate()
|
||||||
|
],
|
||||||
|
[ProjectType.CertificateManager]: [
|
||||||
|
{
|
||||||
|
id: "cert-viewer",
|
||||||
|
name: "Certificate Viewing Policies",
|
||||||
|
description: "Grants read access to certificates and related resources",
|
||||||
|
permissions: [
|
||||||
|
{
|
||||||
|
subject: ProjectPermissionSub.PkiCollections,
|
||||||
|
actions: [ProjectPermissionActions.Read]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
subject: ProjectPermissionSub.PkiAlerts,
|
||||||
|
actions: [ProjectPermissionActions.Read]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
subject: ProjectPermissionSub.CertificateAuthorities,
|
||||||
|
actions: [ProjectPermissionActions.Read]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
subject: ProjectPermissionSub.CertificateTemplates,
|
||||||
|
actions: [ProjectPermissionActions.Read]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
subject: ProjectPermissionSub.Certificates,
|
||||||
|
actions: [
|
||||||
|
ProjectPermissionCertificateActions.Read,
|
||||||
|
ProjectPermissionCertificateActions.ReadPrivateKey
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "cert-editor",
|
||||||
|
name: "Certificate Editing Policies",
|
||||||
|
description: "Grants read and edit access to certificates and related resources",
|
||||||
|
permissions: [
|
||||||
|
{
|
||||||
|
subject: ProjectPermissionSub.PkiCollections,
|
||||||
|
actions: Object.values(ProjectPermissionActions)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
subject: ProjectPermissionSub.PkiAlerts,
|
||||||
|
actions: Object.values(ProjectPermissionActions)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
subject: ProjectPermissionSub.CertificateAuthorities,
|
||||||
|
actions: Object.values(ProjectPermissionActions)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
subject: ProjectPermissionSub.CertificateTemplates,
|
||||||
|
actions: Object.values(ProjectPermissionActions)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
subject: ProjectPermissionSub.Certificates,
|
||||||
|
actions: Object.values(ProjectPermissionCertificateActions)
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
projectManagerTemplate()
|
||||||
|
],
|
||||||
|
[ProjectType.SecretManager]: [
|
||||||
|
{
|
||||||
|
id: "secret-viewer",
|
||||||
|
name: "Secret Viewing Policies",
|
||||||
|
description: "Grants read access to secrets and related resources",
|
||||||
|
permissions: [
|
||||||
|
{
|
||||||
|
subject: ProjectPermissionSub.SecretRollback,
|
||||||
|
actions: [ProjectPermissionActions.Read]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
subject: ProjectPermissionSub.SecretImports,
|
||||||
|
actions: [ProjectPermissionActions.Read]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
subject: ProjectPermissionSub.Secrets,
|
||||||
|
actions: [
|
||||||
|
ProjectPermissionSecretActions.DescribeSecret,
|
||||||
|
ProjectPermissionSecretActions.ReadValue
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
subject: ProjectPermissionSub.DynamicSecrets,
|
||||||
|
actions: [ProjectPermissionDynamicSecretActions.ReadRootCredential]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
subject: ProjectPermissionSub.Environments,
|
||||||
|
actions: [ProjectPermissionActions.Read]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
subject: ProjectPermissionSub.Tags,
|
||||||
|
actions: [ProjectPermissionActions.Read]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
subject: ProjectPermissionSub.SecretRotation,
|
||||||
|
actions: [ProjectPermissionSecretRotationActions.Read]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
subject: ProjectPermissionSub.Integrations,
|
||||||
|
actions: [ProjectPermissionActions.Read]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
subject: ProjectPermissionSub.SecretSyncs,
|
||||||
|
actions: [ProjectPermissionSecretSyncActions.Read]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "secret-editor",
|
||||||
|
name: "Secret Editing Policies",
|
||||||
|
description: "Grants read and edit access to secrets and related resources",
|
||||||
|
permissions: [
|
||||||
|
{
|
||||||
|
subject: ProjectPermissionSub.Environments,
|
||||||
|
actions: Object.values(ProjectPermissionActions)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
subject: ProjectPermissionSub.DynamicSecrets,
|
||||||
|
actions: Object.values(ProjectPermissionDynamicSecretActions)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
subject: ProjectPermissionSub.Secrets,
|
||||||
|
actions: [
|
||||||
|
ProjectPermissionSecretActions.DescribeSecret,
|
||||||
|
ProjectPermissionSecretActions.ReadValue,
|
||||||
|
ProjectPermissionSecretActions.Edit,
|
||||||
|
ProjectPermissionSecretActions.Create,
|
||||||
|
ProjectPermissionSecretActions.Delete
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
subject: ProjectPermissionSub.SecretRollback,
|
||||||
|
actions: [ProjectPermissionActions.Read, ProjectPermissionActions.Create]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
subject: ProjectPermissionSub.Tags,
|
||||||
|
actions: Object.values(ProjectPermissionActions)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
subject: ProjectPermissionSub.SecretImports,
|
||||||
|
actions: Object.values(ProjectPermissionActions)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
subject: ProjectPermissionSub.SecretRotation,
|
||||||
|
actions: Object.values(ProjectPermissionSecretRotationActions)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
subject: ProjectPermissionSub.SecretFolders,
|
||||||
|
actions: [
|
||||||
|
ProjectPermissionActions.Create,
|
||||||
|
ProjectPermissionActions.Edit,
|
||||||
|
ProjectPermissionActions.Delete
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
subject: ProjectPermissionSub.Integrations,
|
||||||
|
actions: Object.values(ProjectPermissionActions)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
subject: ProjectPermissionSub.SecretSyncs,
|
||||||
|
actions: Object.values(ProjectPermissionSecretSyncActions)
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
projectManagerTemplate([
|
||||||
|
{
|
||||||
|
subject: ProjectPermissionSub.IpAllowList,
|
||||||
|
actions: Object.values(ProjectPermissionActions)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
subject: ProjectPermissionSub.Kms,
|
||||||
|
actions: [ProjectPermissionActions.Edit]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
subject: ProjectPermissionSub.SecretApproval,
|
||||||
|
actions: Object.values(ProjectPermissionActions)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
subject: ProjectPermissionSub.ServiceTokens,
|
||||||
|
actions: Object.values(ProjectPermissionActions)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
subject: ProjectPermissionSub.Webhooks,
|
||||||
|
actions: Object.values(ProjectPermissionActions)
|
||||||
|
}
|
||||||
|
])
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|||||||
+6
-21
@@ -1,7 +1,7 @@
|
|||||||
import { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
import { FormProvider, useForm } from "react-hook-form";
|
import { FormProvider, useForm } from "react-hook-form";
|
||||||
import { MongoAbility, MongoQuery, RawRuleOf } from "@casl/ability";
|
import { MongoAbility, MongoQuery, RawRuleOf } from "@casl/ability";
|
||||||
import { faPlus, faSave } from "@fortawesome/free-solid-svg-icons";
|
import { faSave } from "@fortawesome/free-solid-svg-icons";
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import { twMerge } from "tailwind-merge";
|
import { twMerge } from "tailwind-merge";
|
||||||
@@ -12,17 +12,16 @@ import { Button } from "@app/components/v2";
|
|||||||
import { ProjectPermissionSub, useWorkspace } from "@app/context";
|
import { ProjectPermissionSub, useWorkspace } from "@app/context";
|
||||||
import { ProjectPermissionSet } from "@app/context/ProjectPermissionContext";
|
import { ProjectPermissionSet } from "@app/context/ProjectPermissionContext";
|
||||||
import { evaluatePermissionsAbility } from "@app/helpers/permissions";
|
import { evaluatePermissionsAbility } from "@app/helpers/permissions";
|
||||||
import { usePopUp } from "@app/hooks";
|
|
||||||
import { useGetProjectRoleBySlug, useUpdateProjectRole } from "@app/hooks/api";
|
import { useGetProjectRoleBySlug, useUpdateProjectRole } from "@app/hooks/api";
|
||||||
import { ProjectMembershipRole } from "@app/hooks/api/roles/types";
|
import { ProjectMembershipRole } from "@app/hooks/api/roles/types";
|
||||||
import { ProjectType } from "@app/hooks/api/workspace/types";
|
import { ProjectType } from "@app/hooks/api/workspace/types";
|
||||||
import { PolicySelectionModal } from "@app/pages/project/RoleDetailsBySlugPage/components/PolicySelectionModal";
|
import { AddPoliciesButton } from "@app/pages/project/RoleDetailsBySlugPage/components/AddPoliciesButton";
|
||||||
|
import { GeneralPermissionPolicies } from "@app/pages/project/RoleDetailsBySlugPage/components/GeneralPermissionPolicies";
|
||||||
|
import { PermissionEmptyState } from "@app/pages/project/RoleDetailsBySlugPage/components/PermissionEmptyState";
|
||||||
|
|
||||||
import { DynamicSecretPermissionConditions } from "./DynamicSecretPermissionConditions";
|
import { DynamicSecretPermissionConditions } from "./DynamicSecretPermissionConditions";
|
||||||
import { GeneralPermissionConditions } from "./GeneralPermissionConditions";
|
import { GeneralPermissionConditions } from "./GeneralPermissionConditions";
|
||||||
import { GeneralPermissionPolicies } from "./GeneralPermissionPolicies";
|
|
||||||
import { IdentityManagementPermissionConditions } from "./IdentityManagementPermissionConditions";
|
import { IdentityManagementPermissionConditions } from "./IdentityManagementPermissionConditions";
|
||||||
import { PermissionEmptyState } from "./PermissionEmptyState";
|
|
||||||
import {
|
import {
|
||||||
formRolePermission2API,
|
formRolePermission2API,
|
||||||
isConditionalSubjects,
|
isConditionalSubjects,
|
||||||
@@ -86,8 +85,6 @@ export const RolePermissionsSection = ({ roleSlug, isDisabled }: Props) => {
|
|||||||
|
|
||||||
const { mutateAsync: updateRole } = useUpdateProjectRole();
|
const { mutateAsync: updateRole } = useUpdateProjectRole();
|
||||||
|
|
||||||
const { popUp, handlePopUpToggle } = usePopUp(["addPolicy"] as const);
|
|
||||||
|
|
||||||
const onSubmit = async (el: TFormSchema) => {
|
const onSubmit = async (el: TFormSchema) => {
|
||||||
try {
|
try {
|
||||||
if (!projectId || !role?.id) return;
|
if (!projectId || !role?.id) return;
|
||||||
@@ -151,7 +148,7 @@ export const RolePermissionsSection = ({ roleSlug, isDisabled }: Props) => {
|
|||||||
variant="outline_bg"
|
variant="outline_bg"
|
||||||
type="submit"
|
type="submit"
|
||||||
className={twMerge(
|
className={twMerge(
|
||||||
"h-10 rounded-r-none border border-primary",
|
"mr-4 h-10 border border-primary",
|
||||||
isDirty && "bg-primary text-black"
|
isDirty && "bg-primary text-black"
|
||||||
)}
|
)}
|
||||||
isDisabled={isSubmitting || !isDirty}
|
isDisabled={isSubmitting || !isDirty}
|
||||||
@@ -160,15 +157,7 @@ export const RolePermissionsSection = ({ roleSlug, isDisabled }: Props) => {
|
|||||||
>
|
>
|
||||||
Save
|
Save
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<AddPoliciesButton isDisabled={isDisabled} />
|
||||||
isDisabled={isDisabled}
|
|
||||||
className="h-10 rounded-l-none"
|
|
||||||
variant="outline_bg"
|
|
||||||
leftIcon={<FontAwesomeIcon icon={faPlus} />}
|
|
||||||
onClick={() => handlePopUpToggle("addPolicy")}
|
|
||||||
>
|
|
||||||
Add Policy
|
|
||||||
</Button>
|
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
@@ -190,10 +179,6 @@ export const RolePermissionsSection = ({ roleSlug, isDisabled }: Props) => {
|
|||||||
</GeneralPermissionPolicies>
|
</GeneralPermissionPolicies>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
<PolicySelectionModal
|
|
||||||
isOpen={popUp.addPolicy.isOpen}
|
|
||||||
onOpenChange={(isOpen) => handlePopUpToggle("addPolicy", isOpen)}
|
|
||||||
/>
|
|
||||||
</FormProvider>
|
</FormProvider>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user