Fix auditor access to people

Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
This commit is contained in:
Sacha Al Himdani
2026-02-23 13:46:36 +01:00
parent 71b01bf20e
commit 86e4c2eada
3 changed files with 38 additions and 30 deletions

View File

@@ -1,12 +1,15 @@
import { getAssignableRoles } from "@probo/helpers";
import { useTranslate } from "@probo/i18n"; import { useTranslate } from "@probo/i18n";
import { Tbody, Td, Th, Thead, Tr } from "@probo/ui"; import { Tbody, Td, Th, Thead, Tr } from "@probo/ui";
import type { ComponentProps } from "react"; import type { ComponentProps } from "react";
import { use } from "react";
import { ConnectionHandler, graphql, usePaginationFragment } from "react-relay"; import { ConnectionHandler, graphql, usePaginationFragment } from "react-relay";
import type { PeopleListFragment$key } from "#/__generated__/iam/PeopleListFragment.graphql"; import type { PeopleListFragment$key } from "#/__generated__/iam/PeopleListFragment.graphql";
import type { PeopleListFragment_RefetchQuery } from "#/__generated__/iam/PeopleListFragment_RefetchQuery.graphql"; import type { PeopleListFragment_RefetchQuery } from "#/__generated__/iam/PeopleListFragment_RefetchQuery.graphql";
import { type Order, SortableTable, SortableTh } from "#/components/SortableTable"; import { type Order, SortableTable, SortableTh } from "#/components/SortableTable";
import { useOrganizationId } from "#/hooks/useOrganizationId"; import { useOrganizationId } from "#/hooks/useOrganizationId";
import { CurrentUser } from "#/providers/CurrentUser";
import { PeopleListItem } from "./PeopleListItem"; import { PeopleListItem } from "./PeopleListItem";
@@ -50,6 +53,8 @@ export function PeopleList(props: {
const organizationId = useOrganizationId(); const organizationId = useOrganizationId();
const { __ } = useTranslate(); const { __ } = useTranslate();
const { role } = use(CurrentUser);
const canManageRoles = getAssignableRoles(role).length > 0;
const peoplePagination = usePaginationFragment< const peoplePagination = usePaginationFragment<
PeopleListFragment_RefetchQuery, PeopleListFragment_RefetchQuery,
@@ -85,7 +90,7 @@ export function PeopleList(props: {
<SortableTh field="FULL_NAME" onOrderChange={handleOrderChange}>{__("Name")}</SortableTh> <SortableTh field="FULL_NAME" onOrderChange={handleOrderChange}>{__("Name")}</SortableTh>
<SortableTh field="STATE">{__("Status")}</SortableTh> <SortableTh field="STATE">{__("Status")}</SortableTh>
<SortableTh field="EMAIL_ADDRESS" onOrderChange={handleOrderChange}>{__("Email")}</SortableTh> <SortableTh field="EMAIL_ADDRESS" onOrderChange={handleOrderChange}>{__("Email")}</SortableTh>
<SortableTh field="ROLE" onOrderChange={handleOrderChange}>{__("Role")}</SortableTh> {canManageRoles && <SortableTh field="ROLE" onOrderChange={handleOrderChange}>{__("Role")}</SortableTh>}
<SortableTh field="CREATED_AT" onOrderChange={handleOrderChange}>{__("Created on")}</SortableTh> <SortableTh field="CREATED_AT" onOrderChange={handleOrderChange}>{__("Created on")}</SortableTh>
<Th></Th> <Th></Th>
</Tr> </Tr>

View File

@@ -213,36 +213,38 @@ export function PeopleListItem(props: {
<Badge variant="info">{profile.source}</Badge> <Badge variant="info">{profile.source}</Badge>
</div> </div>
</Td> </Td>
<Td {availableRoles.length > 0 && (
noLink <Td
className={clsx( noLink
"pr-4", className={clsx(
isRemoving && "opacity-60 pointer-events-none", "pr-4",
isInactive && "opacity-50", isRemoving && "opacity-60 pointer-events-none",
)} isInactive && "opacity-50",
> )}
<Select
disabled={!profile.membership.canUpdate || isUpdatingRole}
value={profile.membership.role}
onValueChange={role => void handleUpdateRole(role)}
> >
{availableRoles.includes("OWNER") && ( <Select
<Option value="OWNER">{__("Owner")}</Option> disabled={!profile.membership.canUpdate || isUpdatingRole}
)} value={profile.membership.role}
{availableRoles.includes("ADMIN") && ( onValueChange={role => void handleUpdateRole(role)}
<Option value="ADMIN">{__("Admin")}</Option> >
)} {availableRoles.includes("OWNER") && (
{availableRoles.includes("VIEWER") && ( <Option value="OWNER">{__("Owner")}</Option>
<Option value="VIEWER">{__("Viewer")}</Option> )}
)} {availableRoles.includes("ADMIN") && (
{availableRoles.includes("AUDITOR") && ( <Option value="ADMIN">{__("Admin")}</Option>
<Option value="AUDITOR">{__("Auditor")}</Option> )}
)} {availableRoles.includes("VIEWER") && (
{availableRoles.includes("EMPLOYEE") && ( <Option value="VIEWER">{__("Viewer")}</Option>
<Option value="EMPLOYEE">{__("Employee")}</Option> )}
)} {availableRoles.includes("AUDITOR") && (
</Select> <Option value="AUDITOR">{__("Auditor")}</Option>
</Td> )}
{availableRoles.includes("EMPLOYEE") && (
<Option value="EMPLOYEE">{__("Employee")}</Option>
)}
</Select>
</Td>
)}
<Td className={clsx( <Td className={clsx(
isRemoving && "opacity-60 pointer-events-none", isRemoving && "opacity-60 pointer-events-none",
isInactive && "opacity-50", isInactive && "opacity-50",

View File

@@ -318,6 +318,7 @@ var IAMViewerPolicy = policy.NewPolicy(
policy.Allow( policy.Allow(
ActionOrganizationGet, ActionOrganizationGet,
ActionMembershipList, ActionMembershipList,
ActionInvitationList,
). ).
WithSID("org-viewer-access"). WithSID("org-viewer-access").
When(policy.Equals("principal.organization_id", "resource.organization_id")), When(policy.Equals("principal.organization_id", "resource.organization_id")),