Fix framework pages permissions handling
Signed-off-by: Émile Ré <nemile.re@gmail.com>
This commit is contained in:
@@ -29,8 +29,6 @@ import { promisifyMutation } from "@probo/helpers";
|
||||
import type { FrameworkGraphControlNodeQuery } from "/__generated__/core/FrameworkGraphControlNodeQuery.graphql";
|
||||
import { frameworkControlNodeQuery } from "/hooks/graph/FrameworkGraph";
|
||||
import type { FrameworkDetailPageFragment$data } from "/__generated__/core/FrameworkDetailPageFragment.graphql";
|
||||
import { use } from "react";
|
||||
import { PermissionsContext } from "/providers/PermissionsContext";
|
||||
|
||||
const attachStateOfApplicabilityMutation = graphql`
|
||||
mutation FrameworkControlPageAttachStateOfApplicabilityMutation(
|
||||
@@ -227,11 +225,6 @@ export default function FrameworkControlPage({ queryRef }: Props) {
|
||||
const organizationId = useOrganizationId();
|
||||
const confirm = useConfirm();
|
||||
const navigate = useNavigate();
|
||||
const { isAuthorized } = use(PermissionsContext);
|
||||
const [detachStateOfApplicability, isDetachingStateOfApplicability] =
|
||||
useMutation(detachStateOfApplicabilityMutation);
|
||||
const [attachStateOfApplicability, isAttachingStateOfApplicability] =
|
||||
useMutation(attachStateOfApplicabilityMutation);
|
||||
const [detachMeasure, isDetachingMeasure] = useMutation(
|
||||
detachMeasureMutation,
|
||||
);
|
||||
@@ -253,6 +246,11 @@ export default function FrameworkControlPage({ queryRef }: Props) {
|
||||
attachSnapshotMutation,
|
||||
);
|
||||
const [deleteControl] = useMutation(deleteControlMutation);
|
||||
|
||||
const [attachStateOfApplicability, isAttachingStateOfApplicability] =
|
||||
useMutation(attachStateOfApplicabilityMutation);
|
||||
const [detachStateOfApplicability, isDetachingStateOfApplicability] =
|
||||
useMutation(detachStateOfApplicabilityMutation);
|
||||
const [attachObligation, isAttachingObligation] = useMutation(
|
||||
attachObligationMutation,
|
||||
);
|
||||
@@ -260,61 +258,33 @@ export default function FrameworkControlPage({ queryRef }: Props) {
|
||||
detachObligationMutation,
|
||||
);
|
||||
|
||||
const canLinkStateOfApplicability = isAuthorized(
|
||||
"Control",
|
||||
"createStateOfApplicabilityControlMapping",
|
||||
);
|
||||
const canUnlinkStateOfApplicability = isAuthorized(
|
||||
"Control",
|
||||
"deleteStateOfApplicabilityControlMapping",
|
||||
);
|
||||
const canLinkStateOfApplicability =
|
||||
control.canCreateStateOfApplicabilityMapping;
|
||||
const canUnlinkStateOfApplicability =
|
||||
control.canDeleteStateOfApplicabilityMapping;
|
||||
const statesOfApplicabilityReadOnly =
|
||||
!canLinkStateOfApplicability && !canUnlinkStateOfApplicability;
|
||||
|
||||
const canLinkMeasure = isAuthorized(
|
||||
"Control",
|
||||
"createControlMeasureMapping",
|
||||
);
|
||||
const canUnlinkMeasure = isAuthorized(
|
||||
"Control",
|
||||
"deleteControlMeasureMapping",
|
||||
);
|
||||
const canLinkMeasure = control.canCreateMeasureMapping;
|
||||
const canUnlinkMeasure = control.canDeleteMeasureMapping;
|
||||
const measuresReadOnly = !canLinkMeasure && !canUnlinkMeasure;
|
||||
|
||||
const canLinkDocument = isAuthorized(
|
||||
"Control",
|
||||
"createControlDocumentMapping",
|
||||
);
|
||||
const canUnlinkDocument = isAuthorized(
|
||||
"Control",
|
||||
"deleteControlDocumentMapping",
|
||||
);
|
||||
const canLinkDocument = control.canCreateDocumentMapping;
|
||||
const canUnlinkDocument = control.canDeleteDocumentMapping;
|
||||
const documentsReadOnly = !canLinkDocument && !canUnlinkDocument;
|
||||
|
||||
const canLinkAudit = isAuthorized("Control", "createControlAuditMapping");
|
||||
const canUnlinkAudit = isAuthorized("Control", "deleteControlAuditMapping");
|
||||
const canLinkAudit = control.canCreateAuditMapping;
|
||||
const canUnlinkAudit = control.canDeleteAuditMapping;
|
||||
const auditsReadOnly = !canLinkAudit && !canUnlinkAudit;
|
||||
|
||||
const canLinkObligation = isAuthorized(
|
||||
"Control",
|
||||
"createControlObligationMapping",
|
||||
);
|
||||
const canUnlinkObligation = isAuthorized(
|
||||
"Control",
|
||||
"deleteControlObligationMapping",
|
||||
);
|
||||
const obligationsReadOnly = !canLinkObligation && !canUnlinkObligation;
|
||||
|
||||
const canLinkSnapshot = isAuthorized(
|
||||
"Control",
|
||||
"createControlSnapshotMapping",
|
||||
);
|
||||
const canUnlinkSnapshot = isAuthorized(
|
||||
"Control",
|
||||
"deleteControlSnapshotMapping",
|
||||
);
|
||||
const canLinkSnapshot = control.canCreateSnapshotMapping;
|
||||
const canUnlinkSnapshot = control.canDeleteSnapshotMapping;
|
||||
const snapshotsReadOnly = !canLinkSnapshot && !canUnlinkSnapshot;
|
||||
|
||||
const canLinkObligation = control.canCreateObligationMapping;
|
||||
const canUnlinkObligation = control.canDeleteObligationMapping;
|
||||
const obligationsReadOnly = !canLinkObligation && !canUnlinkObligation;
|
||||
|
||||
const withErrorHandling =
|
||||
<T extends MutationParameters>(
|
||||
mutationFn: (config: UseMutationConfig<T>) => void,
|
||||
@@ -382,7 +352,7 @@ export default function FrameworkControlPage({ queryRef }: Props) {
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
{isAuthorized("Control", "updateControl") && (
|
||||
{control.canUpdate && (
|
||||
<FrameworkControlDialog
|
||||
frameworkId={framework.id}
|
||||
connectionId={connectionId}
|
||||
@@ -393,7 +363,7 @@ export default function FrameworkControlPage({ queryRef }: Props) {
|
||||
</Button>
|
||||
</FrameworkControlDialog>
|
||||
)}
|
||||
{isAuthorized("Control", "deleteControl") && (
|
||||
{control.canDelete && (
|
||||
<ActionDropdown variant="secondary">
|
||||
<DropdownItem
|
||||
icon={IconTrashCan}
|
||||
|
||||
@@ -31,8 +31,6 @@ import type { FrameworkDetailPageExportFrameworkMutation } from "/__generated__/
|
||||
import { FrameworkFormDialog } from "./dialogs/FrameworkFormDialog";
|
||||
import { FrameworkControlDialog } from "./dialogs/FrameworkControlDialog";
|
||||
import { useMutationWithToasts } from "/hooks/useMutationWithToasts";
|
||||
import { use } from "react";
|
||||
import { PermissionsContext } from "/providers/PermissionsContext";
|
||||
|
||||
const frameworkDetailFragment = graphql`
|
||||
fragment FrameworkDetailPageFragment on Framework {
|
||||
@@ -41,6 +39,10 @@ const frameworkDetailFragment = graphql`
|
||||
description
|
||||
lightLogoURL
|
||||
darkLogoURL
|
||||
canExport: permission(action: "core:franework:export")
|
||||
canUpdate: permission(action: "core:framework:update")
|
||||
canDelete: permission(action: "core:framework:delete")
|
||||
canCreateControl: permission(action: "core:control:create")
|
||||
organization {
|
||||
name
|
||||
}
|
||||
@@ -88,10 +90,15 @@ type Props = {
|
||||
};
|
||||
|
||||
export default function FrameworkDetailPage(props: Props) {
|
||||
const { queryRef } = props;
|
||||
|
||||
const { __ } = useTranslate();
|
||||
const { controlId } = useParams<{ controlId?: string }>();
|
||||
const organizationId = useOrganizationId();
|
||||
const data = usePreloadedQuery(frameworkNodeQuery, props.queryRef);
|
||||
const data = usePreloadedQuery<FrameworkGraphNodeQuery>(
|
||||
frameworkNodeQuery,
|
||||
queryRef,
|
||||
);
|
||||
const framework = useFragment<FrameworkDetailPageFragment$key>(
|
||||
frameworkDetailFragment,
|
||||
data.node,
|
||||
@@ -106,7 +113,6 @@ export default function FrameworkDetailPage(props: Props) {
|
||||
framework,
|
||||
ConnectionHandler.getConnectionID(organizationId, connectionListKey)!,
|
||||
);
|
||||
const { isAuthorized } = use(PermissionsContext);
|
||||
const [generateFrameworkStateOfApplicability] =
|
||||
useMutationWithToasts<FrameworkDetailPageGenerateFrameworkStateOfApplicabilityMutation>(
|
||||
generateFrameworkStateOfApplicabilityMutation,
|
||||
@@ -155,7 +161,7 @@ export default function FrameworkDetailPage(props: Props) {
|
||||
</>
|
||||
}
|
||||
>
|
||||
{isAuthorized("Framework", "updateFramework") && (
|
||||
{framework.canUpdate && (
|
||||
<FrameworkFormDialog
|
||||
organizationId={organizationId}
|
||||
framework={framework}
|
||||
@@ -202,7 +208,7 @@ export default function FrameworkDetailPage(props: Props) {
|
||||
>
|
||||
{__("Export Framework")}
|
||||
</DropdownItem>
|
||||
{isAuthorized("Framework", "deleteFramework") && (
|
||||
{framework.canDelete && (
|
||||
<DropdownItem
|
||||
icon={IconTrashCan}
|
||||
variant="danger"
|
||||
@@ -231,7 +237,7 @@ export default function FrameworkDetailPage(props: Props) {
|
||||
active={selectedControl?.id === control.id}
|
||||
/>
|
||||
))}
|
||||
{isAuthorized("Organization", "createControl") && (
|
||||
{framework.canCreateControl && (
|
||||
<FrameworkControlDialog
|
||||
frameworkId={framework.id}
|
||||
connectionId={connectionId}
|
||||
|
||||
@@ -27,9 +27,8 @@ import {
|
||||
import { Link } from "react-router";
|
||||
import type { FrameworksPageCardFragment$key } from "/__generated__/core/FrameworksPageCardFragment.graphql";
|
||||
import { useMutationWithToasts } from "/hooks/useMutationWithToasts";
|
||||
import { useState, type ChangeEventHandler, use } from "react";
|
||||
import { useState, type ChangeEventHandler } from "react";
|
||||
import { FrameworkFormDialog } from "./dialogs/FrameworkFormDialog";
|
||||
import { PermissionsContext } from "/providers/PermissionsContext";
|
||||
|
||||
type Props = {
|
||||
queryRef: PreloadedQuery<FrameworkGraphListQuery>;
|
||||
@@ -53,7 +52,6 @@ const importFrameworkMutation = graphql`
|
||||
|
||||
export default function FrameworksPage(props: Props) {
|
||||
const { __ } = useTranslate();
|
||||
const { isAuthorized } = use(PermissionsContext);
|
||||
usePageTitle(__("Frameworks"));
|
||||
const data = usePreloadedQuery(frameworksQuery, props.queryRef);
|
||||
const connectionId = data.organization.frameworks!.__id;
|
||||
@@ -120,9 +118,9 @@ export default function FrameworksPage(props: Props) {
|
||||
|
||||
const isLoading = isUploading || isImporting;
|
||||
|
||||
const hasAnyAction =
|
||||
isAuthorized("Framework", "updateFramework") ||
|
||||
isAuthorized("Framework", "deleteFramework");
|
||||
const hasAnyAction = frameworks.some(
|
||||
({ canUpdate, canDelete }) => canUpdate || canDelete,
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
@@ -135,7 +133,7 @@ export default function FrameworksPage(props: Props) {
|
||||
title={__("Frameworks")}
|
||||
description={__("Manage your compliance frameworks")}
|
||||
>
|
||||
{isAuthorized("Organization", "createFramework") && (
|
||||
{data.organization.canCreateFramework && (
|
||||
<>
|
||||
<FileButton
|
||||
variant="secondary"
|
||||
@@ -175,6 +173,8 @@ const frameworkCardFragment = graphql`
|
||||
description
|
||||
lightLogoURL
|
||||
darkLogoURL
|
||||
canUpdate: permission(action: "core:framework:update")
|
||||
canDelete: permission(action: "core:framework:delete")
|
||||
}
|
||||
`;
|
||||
|
||||
@@ -187,7 +187,6 @@ type FrameworkCardProps = {
|
||||
|
||||
function FrameworkCard(props: FrameworkCardProps) {
|
||||
const framework = useFragment(frameworkCardFragment, props.framework);
|
||||
const { isAuthorized } = use(PermissionsContext);
|
||||
const deleteFramework = useDeleteFrameworkMutation(
|
||||
framework,
|
||||
props.connectionId,
|
||||
@@ -210,7 +209,7 @@ function FrameworkCard(props: FrameworkCardProps) {
|
||||
/>
|
||||
{props.hasAnyAction && (
|
||||
<ActionDropdown className="z-10 relative">
|
||||
{isAuthorized("Framework", "updateFramework") && (
|
||||
{framework.canUpdate && (
|
||||
<DropdownItem
|
||||
icon={IconPencil}
|
||||
onClick={() => {
|
||||
@@ -220,7 +219,7 @@ function FrameworkCard(props: FrameworkCardProps) {
|
||||
{__("Edit")}
|
||||
</DropdownItem>
|
||||
)}
|
||||
{isAuthorized("Framework", "deleteFramework") && (
|
||||
{framework.canDelete && (
|
||||
<DropdownItem
|
||||
icon={IconTrashCan}
|
||||
onClick={() => deleteFramework()}
|
||||
|
||||
Reference in New Issue
Block a user