feat: resolved ui issues related to permission based hiding

This commit is contained in:
=
2024-08-21 23:01:23 +05:30
parent cf3b2ebbca
commit 49a0d3cec6
4 changed files with 83 additions and 50 deletions
@@ -44,6 +44,13 @@ export type ProjectPermissionSet =
ProjectPermissionActions, ProjectPermissionActions,
ProjectPermissionSub.Secrets | (ForcedSubject<ProjectPermissionSub.Secrets> & SubjectFields) ProjectPermissionSub.Secrets | (ForcedSubject<ProjectPermissionSub.Secrets> & SubjectFields)
] ]
| [
ProjectPermissionActions,
(
| ProjectPermissionSub.SecretFolders
| (ForcedSubject<ProjectPermissionSub.SecretFolders> & SubjectFields)
)
]
| [ProjectPermissionActions, ProjectPermissionSub.Role] | [ProjectPermissionActions, ProjectPermissionSub.Role]
| [ProjectPermissionActions, ProjectPermissionSub.Tags] | [ProjectPermissionActions, ProjectPermissionSub.Tags]
| [ProjectPermissionActions, ProjectPermissionSub.Member] | [ProjectPermissionActions, ProjectPermissionSub.Member]
@@ -151,6 +151,7 @@ export const RowPermissionSecretsRow = ({
<Controller <Controller
name={`permissions.${formName}.${slug}.secretPath`} name={`permissions.${formName}.${slug}.secretPath`}
control={control} control={control}
defaultValue="/**"
render={({ field }) => ( render={({ field }) => (
/* eslint-disable-next-line no-template-curly-in-string */ /* eslint-disable-next-line no-template-curly-in-string */
<FormControl helperText="Supports glob path pattern string"> <FormControl helperText="Supports glob path pattern string">
@@ -405,8 +405,21 @@ export const SecretOverviewPage = () => {
const pathSegment = secretPath.split("/").filter(Boolean); const pathSegment = secretPath.split("/").filter(Boolean);
const parentPath = `/${pathSegment.slice(0, -1).join("/")}`; const parentPath = `/${pathSegment.slice(0, -1).join("/")}`;
const folderName = pathSegment.at(-1); const folderName = pathSegment.at(-1);
console.log(folderName, parentPath); const canCreateFolder = permission.rules.some((rule) =>
if (folderName && parentPath) { (rule.subject as ProjectPermissionSub[]).includes(ProjectPermissionSub.SecretFolders)
)
? permission.can(
ProjectPermissionActions.Create,
subject(ProjectPermissionSub.SecretFolders, {
environment: slug,
secretPath: parentPath
})
)
: permission.can(
ProjectPermissionActions.Create,
subject(ProjectPermissionSub.Secrets, { environment: slug, secretPath: parentPath })
);
if (folderName && parentPath && canCreateFolder) {
await createFolder({ await createFolder({
projectId: workspaceId, projectId: workspaceId,
environment: slug, environment: slug,
@@ -584,22 +597,14 @@ export const SecretOverviewPage = () => {
</div> </div>
{userAvailableEnvs.length > 0 && ( {userAvailableEnvs.length > 0 && (
<div> <div>
<ProjectPermissionCan <Button
I={ProjectPermissionActions.Create} variant="outline_bg"
a={subject(ProjectPermissionSub.Secrets, { secretPath })} leftIcon={<FontAwesomeIcon icon={faPlus} />}
onClick={() => handlePopUpOpen("addSecretsInAllEnvs")}
className="h-10 rounded-r-none"
> >
{(isAllowed) => ( Add Secret
<Button </Button>
variant="outline_bg"
leftIcon={<FontAwesomeIcon icon={faPlus} />}
onClick={() => handlePopUpOpen("addSecretsInAllEnvs")}
className="h-10 rounded-r-none"
isDisabled={!isAllowed}
>
Add Secret
</Button>
)}
</ProjectPermissionCan>
<DropdownMenu <DropdownMenu
open={popUp.misc.isOpen} open={popUp.misc.isOpen}
onOpenChange={(isOpen) => handlePopUpToggle("misc", isOpen)} onOpenChange={(isOpen) => handlePopUpToggle("misc", isOpen)}
@@ -1,5 +1,6 @@
import { ClipboardEvent } from "react"; import { ClipboardEvent } from "react";
import { Controller, useForm } from "react-hook-form"; import { Controller, useForm } from "react-hook-form";
import { subject } from "@casl/ability";
import { faWarning } from "@fortawesome/free-solid-svg-icons"; import { faWarning } 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";
@@ -17,7 +18,12 @@ import {
Tooltip Tooltip
} from "@app/components/v2"; } from "@app/components/v2";
import { InfisicalSecretInput } from "@app/components/v2/InfisicalSecretInput"; import { InfisicalSecretInput } from "@app/components/v2/InfisicalSecretInput";
import { useWorkspace } from "@app/context"; import {
ProjectPermissionActions,
ProjectPermissionSub,
useProjectPermission,
useWorkspace
} from "@app/context";
import { getKeyValue } from "@app/helpers/parseEnvVar"; import { getKeyValue } from "@app/helpers/parseEnvVar";
import { useCreateFolder, useCreateSecretV3, useUpdateSecretV3 } from "@app/hooks/api"; import { useCreateFolder, useCreateSecretV3, useUpdateSecretV3 } from "@app/hooks/api";
import { SecretType, SecretV3RawSanitized } from "@app/hooks/api/types"; import { SecretType, SecretV3RawSanitized } from "@app/hooks/api/types";
@@ -62,6 +68,7 @@ export const CreateSecretForm = ({
const newSecretKey = watch("key"); const newSecretKey = watch("key");
const { currentWorkspace } = useWorkspace(); const { currentWorkspace } = useWorkspace();
const { permission } = useProjectPermission();
const workspaceId = currentWorkspace?.id || ""; const workspaceId = currentWorkspace?.id || "";
const environments = currentWorkspace?.environments || []; const environments = currentWorkspace?.environments || [];
@@ -186,39 +193,52 @@ export const CreateSecretForm = ({
/> />
<FormLabel label="Environments" className="mb-2" /> <FormLabel label="Environments" className="mb-2" />
<div className="thin-scrollbar grid max-h-64 grid-cols-3 gap-4 overflow-auto py-2"> <div className="thin-scrollbar grid max-h-64 grid-cols-3 gap-4 overflow-auto py-2">
{environments.map((env) => { {environments
return ( .filter((environmentSlug) =>
<Controller permission.can(
name={`environments.${env.slug}`} ProjectPermissionActions.Create,
key={`secret-input-${env.slug}`} subject(ProjectPermissionSub.Secrets, {
control={control} environment: environmentSlug.slug,
render={({ field }) => ( secretPath
<Checkbox })
isChecked={field.value} )
onCheckedChange={field.onChange} )
id={`secret-input-${env.slug}`} .map((env) => {
className="!justify-start" return (
> <Controller
<span className="flex w-full flex-row items-center justify-start whitespace-pre-wrap"> name={`environments.${env.slug}`}
<span title={env.name} className="truncate"> key={`secret-input-${env.slug}`}
{env.name} control={control}
render={({ field }) => (
<Checkbox
isChecked={field.value}
onCheckedChange={field.onChange}
id={`secret-input-${env.slug}`}
className="!justify-start"
>
<span className="flex w-full flex-row items-center justify-start whitespace-pre-wrap">
<span title={env.name} className="truncate">
{env.name}
</span>
<span>
{getSecretByKey(env.slug, newSecretKey) && (
<Tooltip
className="max-w-[150px]"
content="Secret already exists, and it will be overwritten"
>
<FontAwesomeIcon
icon={faWarning}
className="ml-1 text-yellow-400"
/>
</Tooltip>
)}
</span>
</span> </span>
<span> </Checkbox>
{getSecretByKey(env.slug, newSecretKey) && ( )}
<Tooltip />
className="max-w-[150px]" );
content="Secret already exists, and it will be overwritten" })}
>
<FontAwesomeIcon icon={faWarning} className="ml-1 text-yellow-400" />
</Tooltip>
)}
</span>
</span>
</Checkbox>
)}
/>
);
})}
</div> </div>
<div className="mt-7 flex items-center"> <div className="mt-7 flex items-center">
<Button <Button