feat: added secret folder rbac'

This commit is contained in:
Sheen Capadngan
2024-08-01 22:24:35 +08:00
parent 520c068ac4
commit 09928efba3
9 changed files with 187 additions and 22 deletions
@@ -21,6 +21,7 @@ export enum ProjectPermissionSub {
IpAllowList = "ip-allowlist",
Workspace = "workspace",
Secrets = "secrets",
SecretFolders = "secret-folders",
SecretRollback = "secret-rollback",
SecretApproval = "secret-approval",
SecretRotation = "secret-rotation",
@@ -36,6 +36,7 @@ export const formSchema = z.object({
permissions: z
.object({
secrets: z.record(multiEnvPermissionSchema).optional(),
"secret-folders": generalPermissionSchema.optional(),
member: generalPermissionSchema,
groups: generalPermissionSchema,
identity: generalPermissionSchema,
@@ -158,7 +159,7 @@ export const formRolePermission2API = (formVal: TFormSchema["permissions"]) => {
Object.entries(formVal || {}).forEach(([rule, actions]) => {
if (rule === "secrets") {
multiEnvForm2Api(permissions, JSON.parse(JSON.stringify(actions || {})), rule);
} else {
} else if (actions) {
Object.entries(actions).forEach(([action, isAllowed]) => {
if (isAllowed) {
permissions.push({ subject: rule, action });
@@ -0,0 +1,71 @@
import { Control, UseFormSetValue, useWatch } from "react-hook-form";
import { Select, SelectItem, Td, Tr } from "@app/components/v2";
import { ProjectPermissionSub } from "@app/context";
import { TFormSchema } from "@app/views/Project/RolePage/components/RolePermissionsSection/ProjectRoleModifySection.utils";
type Props = {
isEditable: boolean;
setValue: UseFormSetValue<TFormSchema>;
control: Control<TFormSchema>;
};
enum Permission {
SameAsSecrets = "same-as-secrets",
ReadOnly = "read-only"
}
export const RowPermissionSecretFoldersRow = ({ isEditable, setValue, control }: Props) => {
const formName = ProjectPermissionSub.SecretFolders;
const rule = useWatch({
control,
name: `permissions.${formName}`
});
const selectedPermissionCategory =
rule !== undefined ? Permission.ReadOnly : Permission.SameAsSecrets;
const handlePermissionChange = (val: Permission) => {
if (!val) return;
switch (val) {
case Permission.SameAsSecrets: {
setValue(`permissions.${formName}`, undefined, { shouldDirty: true });
break;
}
// Read-only
default:
setValue(
`permissions.${formName}`,
{
read: true,
edit: false,
create: false,
delete: false
},
{
shouldDirty: true
}
);
break;
}
};
return (
<Tr>
<Td />
<Td>Secret Folders</Td>
<Td>
<Select
value={selectedPermissionCategory}
className="w-40 bg-mineshaft-600"
dropdownContainerClassName="border border-mineshaft-600 bg-mineshaft-800"
onValueChange={handlePermissionChange}
isDisabled={!isEditable}
>
<SelectItem value={Permission.SameAsSecrets}>Same as Secrets</SelectItem>
<SelectItem value={Permission.ReadOnly}>Read Only</SelectItem>
</Select>
</Td>
</Tr>
);
};
@@ -13,6 +13,7 @@ import {
} from "@app/views/Project/RolePage/components/RolePermissionsSection/ProjectRoleModifySection.utils";
import { RolePermissionRow } from "./RolePermissionRow";
import { RowPermissionSecretFoldersRow } from "./RolePermissionSecretFoldersRow";
import { RowPermissionSecretsRow } from "./RolePermissionSecretsRow";
const SINGLE_PERMISSION_LIST = [
@@ -177,6 +178,11 @@ export const RolePermissionsSection = ({ roleSlug }: Props) => {
getValue={getValues}
control={control}
/>
<RowPermissionSecretFoldersRow
isEditable={isCustomRole}
setValue={setValue}
control={control}
/>
{SINGLE_PERMISSION_LIST.map((permission) => {
return (
<RolePermissionRow
@@ -44,7 +44,12 @@ import {
Tooltip,
UpgradePlanModal
} from "@app/components/v2";
import { ProjectPermissionActions, ProjectPermissionSub, useSubscription } from "@app/context";
import {
ProjectPermissionActions,
ProjectPermissionSub,
useProjectPermission,
useSubscription
} from "@app/context";
import { usePopUp } from "@app/hooks";
import { useCreateFolder, useDeleteSecretBatch, useMoveSecrets } from "@app/hooks/api";
import { fetchProjectSecrets } from "@app/hooks/api/secrets/queries";
@@ -121,6 +126,12 @@ export const ActionBar = ({
const { reset: resetSelectedSecret } = useSelectedSecretActions();
const isMultiSelectActive = Boolean(Object.keys(selectedSecrets).length);
const { permission } = useProjectPermission();
const shouldCheckFolderPermission = permission.rules.some((rule) =>
(rule.subject as ProjectPermissionSub[]).includes(ProjectPermissionSub.SecretFolders)
);
const debouncedOnSearch = debounce(onSearchChange, 500);
const handleFolderCreate = async (folderName: string) => {
@@ -411,7 +422,12 @@ export const ActionBar = ({
<div className="flex flex-col space-y-1 p-1.5">
<ProjectPermissionCan
I={ProjectPermissionActions.Create}
a={subject(ProjectPermissionSub.Secrets, { environment, secretPath })}
a={subject(
shouldCheckFolderPermission
? ProjectPermissionSub.SecretFolders
: ProjectPermissionSub.Secrets,
{ environment, secretPath }
)}
>
{(isAllowed) => (
<Button
@@ -6,7 +6,7 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { createNotification } from "@app/components/notifications";
import { ProjectPermissionCan } from "@app/components/permissions";
import { DeleteActionModal, IconButton, Modal, ModalContent } from "@app/components/v2";
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/context";
import { ProjectPermissionActions, ProjectPermissionSub, useProjectPermission } from "@app/context";
import { usePopUp } from "@app/hooks";
import { useDeleteFolder, useUpdateFolder } from "@app/hooks/api";
import { TSecretFolder } from "@app/hooks/api/secretFolders/types";
@@ -36,6 +36,11 @@ export const FolderListView = ({
"deleteFolder"
] as const);
const router = useRouter();
const { permission } = useProjectPermission();
const shouldCheckFolderPermission = permission.rules.some((rule) =>
(rule.subject as ProjectPermissionSub[]).includes(ProjectPermissionSub.SecretFolders)
);
const { mutateAsync: updateFolder } = useUpdateFolder();
const { mutateAsync: deleteFolder } = useDeleteFolder();
@@ -128,7 +133,12 @@ export const FolderListView = ({
<div className="flex items-center space-x-4 border-l border-mineshaft-600 px-3 py-3">
<ProjectPermissionCan
I={ProjectPermissionActions.Edit}
a={subject(ProjectPermissionSub.Secrets, { environment, secretPath })}
a={subject(
shouldCheckFolderPermission
? ProjectPermissionSub.SecretFolders
: ProjectPermissionSub.Secrets,
{ environment, secretPath }
)}
renderTooltip
allowedLabel="Edit"
>
@@ -147,7 +157,12 @@ export const FolderListView = ({
</ProjectPermissionCan>
<ProjectPermissionCan
I={ProjectPermissionActions.Delete}
a={subject(ProjectPermissionSub.Secrets, { environment, secretPath })}
a={subject(
shouldCheckFolderPermission
? ProjectPermissionSub.SecretFolders
: ProjectPermissionSub.Secrets,
{ environment, secretPath }
)}
renderTooltip
allowedLabel="Delete"
>