mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-16 06:45:28 +00:00
feat: updated frontend project permission logic
This commit is contained in:
@@ -303,7 +303,7 @@ export const SecretOverviewPage = () => {
|
||||
if (
|
||||
permission.can(
|
||||
ProjectPermissionActions.Edit,
|
||||
subject(ProjectPermissionSub.Secrets, { environment: env.slug, secretPath })
|
||||
subject(ProjectPermissionSub.SecretFolders, { environment: env.slug, secretPath })
|
||||
)
|
||||
) {
|
||||
const folder = getFolderByNameAndEnv(oldFolderName, env.slug);
|
||||
@@ -506,20 +506,13 @@ export const SecretOverviewPage = () => {
|
||||
const pathSegment = secretPath.split("/").filter(Boolean);
|
||||
const parentPath = `/${pathSegment.slice(0, -1).join("/")}`;
|
||||
const folderName = pathSegment.at(-1);
|
||||
const canCreateFolder = permission.rules.some((rule) =>
|
||||
(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 })
|
||||
);
|
||||
const canCreateFolder = permission.can(
|
||||
ProjectPermissionActions.Create,
|
||||
subject(ProjectPermissionSub.SecretFolders, {
|
||||
environment: slug,
|
||||
secretPath: parentPath
|
||||
})
|
||||
);
|
||||
if (folderName && parentPath && canCreateFolder) {
|
||||
await createFolder({
|
||||
projectId: workspaceId,
|
||||
@@ -771,7 +764,7 @@ export const SecretOverviewPage = () => {
|
||||
<div className="flex flex-col space-y-1 p-1.5">
|
||||
<ProjectPermissionCan
|
||||
I={ProjectPermissionActions.Create}
|
||||
a={subject(ProjectPermissionSub.Secrets, { secretPath })}
|
||||
a={ProjectPermissionSub.SecretFolders}
|
||||
>
|
||||
{(isAllowed) => (
|
||||
<Button
|
||||
|
||||
+11
-18
@@ -93,23 +93,14 @@ export const CreateSecretForm = ({
|
||||
const pathSegment = secretPath.split("/").filter(Boolean);
|
||||
const parentPath = `/${pathSegment.slice(0, -1).join("/")}`;
|
||||
const folderName = pathSegment.at(-1);
|
||||
const canCreateFolder = permission.rules.some((rule) =>
|
||||
(rule.subject as ProjectPermissionSub[]).includes(ProjectPermissionSub.SecretFolders)
|
||||
)
|
||||
? permission.can(
|
||||
ProjectPermissionActions.Create,
|
||||
subject(ProjectPermissionSub.SecretFolders, {
|
||||
environment: env.slug,
|
||||
secretPath: parentPath
|
||||
})
|
||||
)
|
||||
: permission.can(
|
||||
ProjectPermissionActions.Create,
|
||||
subject(ProjectPermissionSub.Secrets, {
|
||||
environment: env.slug,
|
||||
secretPath: parentPath
|
||||
})
|
||||
);
|
||||
const canCreateFolder = permission.can(
|
||||
ProjectPermissionActions.Create,
|
||||
subject(ProjectPermissionSub.SecretFolders, {
|
||||
environment: env.slug,
|
||||
secretPath: parentPath
|
||||
})
|
||||
);
|
||||
|
||||
if (folderName && parentPath && canCreateFolder) {
|
||||
await createFolder({
|
||||
projectId: workspaceId,
|
||||
@@ -250,7 +241,9 @@ export const CreateSecretForm = ({
|
||||
ProjectPermissionActions.Create,
|
||||
subject(ProjectPermissionSub.Secrets, {
|
||||
environment: environmentSlug.slug,
|
||||
secretPath
|
||||
secretPath,
|
||||
secretName: "*",
|
||||
secretTags: ["*"]
|
||||
})
|
||||
)
|
||||
)
|
||||
|
||||
+11
-7
@@ -1,4 +1,4 @@
|
||||
import { useCallback,useState } from "react";
|
||||
import { useCallback, useState } from "react";
|
||||
import { Controller, useForm } from "react-hook-form";
|
||||
import { subject } from "@casl/ability";
|
||||
import { faCheck, faCopy, faTrash, faXmark } from "@fortawesome/free-solid-svg-icons";
|
||||
@@ -7,7 +7,7 @@ import { twMerge } from "tailwind-merge";
|
||||
|
||||
import { createNotification } from "@app/components/notifications";
|
||||
import { ProjectPermissionCan } from "@app/components/permissions";
|
||||
import { DeleteActionModal,IconButton, Tooltip } from "@app/components/v2";
|
||||
import { DeleteActionModal, IconButton, Tooltip } from "@app/components/v2";
|
||||
import { InfisicalSecretInput } from "@app/components/v2/InfisicalSecretInput";
|
||||
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/context";
|
||||
import { useToggle } from "@app/hooks";
|
||||
@@ -63,8 +63,8 @@ export const SecretEditRow = ({
|
||||
const [isModalOpen, setIsModalOpen] = useState<boolean>(false);
|
||||
|
||||
const toggleModal = useCallback(() => {
|
||||
setIsModalOpen((prev) => !prev)
|
||||
}, [])
|
||||
setIsModalOpen((prev) => !prev);
|
||||
}, []);
|
||||
|
||||
const handleFormReset = () => {
|
||||
reset();
|
||||
@@ -114,7 +114,6 @@ export const SecretEditRow = ({
|
||||
|
||||
return (
|
||||
<div className="group flex w-full cursor-text items-center space-x-2">
|
||||
|
||||
<DeleteActionModal
|
||||
isOpen={isModalOpen}
|
||||
onClose={toggleModal}
|
||||
@@ -151,8 +150,13 @@ export const SecretEditRow = ({
|
||||
{isDirty ? (
|
||||
<>
|
||||
<ProjectPermissionCan
|
||||
I={ProjectPermissionActions.Create}
|
||||
a={subject(ProjectPermissionSub.Secrets, { environment, secretPath })}
|
||||
I={isCreatable ? ProjectPermissionActions.Create : ProjectPermissionActions.Edit}
|
||||
a={subject(ProjectPermissionSub.Secrets, {
|
||||
environment,
|
||||
secretPath,
|
||||
secretName,
|
||||
secretTags: ["*"]
|
||||
})}
|
||||
>
|
||||
{(isAllowed) => (
|
||||
<div>
|
||||
|
||||
+10
-9
@@ -18,7 +18,7 @@ import {
|
||||
} from "@app/context";
|
||||
import { useToggle } from "@app/hooks";
|
||||
import { useUpdateSecretV3 } from "@app/hooks/api";
|
||||
import { SecretType,SecretV3RawSanitized } from "@app/hooks/api/types";
|
||||
import { SecretType, SecretV3RawSanitized } from "@app/hooks/api/types";
|
||||
import { SecretActionType } from "@app/views/SecretMainPage/components/SecretListView/SecretListView.utils";
|
||||
|
||||
type Props = {
|
||||
@@ -42,15 +42,16 @@ function SecretRenameRow({ environments, getSecretByKey, secretKey, secretPath }
|
||||
|
||||
const isReadOnly = environments.some((env) => {
|
||||
const environment = env.slug;
|
||||
const secretDetails = getSecretByKey(environment, secretKey);
|
||||
const secretPermissionSubject = subject(ProjectPermissionSub.Secrets, {
|
||||
environment,
|
||||
secretPath,
|
||||
secretName: secretKey,
|
||||
secretTags: (secretDetails?.tags || []).map((i) => i.slug)
|
||||
});
|
||||
const isSecretInEnvReadOnly =
|
||||
permission.can(
|
||||
ProjectPermissionActions.Read,
|
||||
subject(ProjectPermissionSub.Secrets, { environment, secretPath })
|
||||
) &&
|
||||
permission.cannot(
|
||||
ProjectPermissionActions.Edit,
|
||||
subject(ProjectPermissionSub.Secrets, { environment, secretPath })
|
||||
);
|
||||
permission.can(ProjectPermissionActions.Read, secretPermissionSubject) &&
|
||||
permission.cannot(ProjectPermissionActions.Edit, secretPermissionSubject);
|
||||
if (isSecretInEnvReadOnly) {
|
||||
return true;
|
||||
}
|
||||
|
||||
+38
-24
@@ -49,9 +49,9 @@ export const SelectionPanel = ({
|
||||
"bulkDeleteEntries"
|
||||
] as const);
|
||||
|
||||
const selectedFolderCount = Object.keys(selectedEntries.folder).length
|
||||
const selectedKeysCount = Object.keys(selectedEntries.secret).length
|
||||
const selectedCount = selectedFolderCount + selectedKeysCount
|
||||
const selectedFolderCount = Object.keys(selectedEntries.folder).length;
|
||||
const selectedKeysCount = Object.keys(selectedEntries.secret).length;
|
||||
const selectedCount = selectedFolderCount + selectedKeysCount;
|
||||
|
||||
const { currentWorkspace } = useWorkspace();
|
||||
const workspaceId = currentWorkspace?.id || "";
|
||||
@@ -65,7 +65,12 @@ export const SelectionPanel = ({
|
||||
const shouldShowDelete = userAvailableEnvs.some((env) =>
|
||||
permission.can(
|
||||
ProjectPermissionActions.Delete,
|
||||
subject(ProjectPermissionSub.Secrets, { environment: env.slug, secretPath })
|
||||
subject(ProjectPermissionSub.Secrets, {
|
||||
environment: env.slug,
|
||||
secretPath,
|
||||
secretName: "*",
|
||||
secretTags: ["*"]
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
@@ -77,41 +82,50 @@ export const SelectionPanel = ({
|
||||
return "Do you want to delete the selected secrets across environments?";
|
||||
}
|
||||
return "Do you want to delete the selected folders across environments?";
|
||||
}
|
||||
};
|
||||
|
||||
const handleBulkDelete = async () => {
|
||||
let processedEntries = 0;
|
||||
|
||||
const promises = userAvailableEnvs.map(async (env) => {
|
||||
// additional check: ensure that bulk delete is only executed on envs that user has access to
|
||||
|
||||
if (
|
||||
permission.cannot(
|
||||
permission.can(
|
||||
ProjectPermissionActions.Delete,
|
||||
subject(ProjectPermissionSub.Secrets, { environment: env.slug, secretPath })
|
||||
subject(ProjectPermissionSub.SecretFolders, { environment: env.slug, secretPath })
|
||||
)
|
||||
) {
|
||||
return;
|
||||
await Promise.all(
|
||||
Object.keys(selectedEntries.folder).map(async (folderName) => {
|
||||
const folder = getFolderByNameAndEnv(folderName, env.slug);
|
||||
if (folder) {
|
||||
processedEntries += 1;
|
||||
await deleteFolder({
|
||||
folderId: folder?.id,
|
||||
path: secretPath,
|
||||
environment: env.slug,
|
||||
projectId: workspaceId
|
||||
});
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
await Promise.all(
|
||||
Object.keys(selectedEntries.folder).map(async (folderName) => {
|
||||
const folder = getFolderByNameAndEnv(folderName, env.slug);
|
||||
if (folder) {
|
||||
processedEntries += 1;
|
||||
await deleteFolder({
|
||||
folderId: folder?.id,
|
||||
path: secretPath,
|
||||
environment: env.slug,
|
||||
projectId: workspaceId
|
||||
});
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
const secretsToDelete = Object.keys(selectedEntries.secret).reduce(
|
||||
(accum: TDeleteSecretBatchDTO["secrets"], secretName) => {
|
||||
const entry = getSecretByKey(env.slug, secretName);
|
||||
if (entry) {
|
||||
const canDeleteSecret = permission.can(
|
||||
ProjectPermissionActions.Delete,
|
||||
subject(ProjectPermissionSub.Secrets, {
|
||||
environment: env.slug,
|
||||
secretPath,
|
||||
secretName,
|
||||
secretTags: (entry?.tags || []).map((i) => i.slug)
|
||||
})
|
||||
);
|
||||
|
||||
if (entry && canDeleteSecret) {
|
||||
return [
|
||||
...accum,
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user