mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-02 18:25:45 +00:00
feat: resolved ui issues related to permission based hiding
This commit is contained in:
@@ -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]
|
||||||
|
|||||||
+1
@@ -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)}
|
||||||
|
|||||||
+53
-33
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user