feat: updated frontend project permission logic

This commit is contained in:
=
2024-10-18 00:33:35 +05:30
parent 181821f8f5
commit b6dc6ffc01
29 changed files with 663 additions and 274 deletions
@@ -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
@@ -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: ["*"]
})
)
)
@@ -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>
@@ -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;
}
@@ -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,
{