Skip to content

Commit

Permalink
Document Datagrid access control in Datagrid doc
Browse files Browse the repository at this point in the history
  • Loading branch information
fzaninotto committed Dec 12, 2024
1 parent 4308f7c commit 40b374d
Show file tree
Hide file tree
Showing 2 changed files with 89 additions and 116 deletions.
78 changes: 2 additions & 76 deletions docs/AuthRBAC.md
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ const allProductsButStock = [

## Setup

Define role definitions in your application code, or fetch them from the API.
Define role definitions in your application code, or fetch them from an API.

```jsx
export const roleDefinitions = {
Expand Down Expand Up @@ -365,7 +365,7 @@ Ra-rbac provides alternative components to react-admin base components. These al
- List
- [`<List>`](#list)
- [`<ListActions>`](#listactions)
- [`<Datagrid>`](#datagrid)
- [`<Datagrid>`](./Datagrid.md#access-control)
- Detail
- [`<SimpleShowLayout>`](#simpleshowlayout)
- [`<Tab>`](#tab)
Expand All @@ -375,80 +375,6 @@ Ra-rbac provides alternative components to react-admin base components. These al
- [`<TabbedForm>`](#tabbedform)
- [`<FormTab>`](#formtab)

## `<Datagrid>`

Alternative to react-admin's `<Datagrid>` that adds RBAC control to columns.

- Users must have the `'delete'` permission on the resource to see the `<BulkExportButton>`.
- Users must have the `'read'` permission on a resource column to see it in the export:

```jsx
{ action: "read", resource: `${resource}.${source}` }.
// or
{ action: "read", resource: `${resource}.*` }.
```

Also, the `rowClick` prop is automatically set depending on the user props:

- `"edit"` if the user has the permission to edit the resource
- `"show"` if the user doesn't have the permission to edit the resource but has the permission to show it
- empty otherwise

```tsx
import { canAccessWithPermissions, List, Datagrid } from '@react-admin/ra-rbac';
import {
ImageField,
TextField,
ReferenceField,
NumberField,
} from 'react-admin';

const authProvider = {
// ...
canAccess: async ({ action, record, resource }) =>
canAccessWithPermissions({
permissions: [
{ action: 'list', resource: 'products' },
{ action: 'read', resource: 'products.thumbnail' },
{ action: 'read', resource: 'products.reference' },
{ action: 'read', resource: 'products.category_id' },
{ action: 'read', resource: 'products.width' },
{ action: 'read', resource: 'products.height' },
{ action: 'read', resource: 'products.price' },
{ action: 'read', resource: 'products.description' },
],
action,
record,
resource
}),
};

const ProductList = () => (
<List>
{/* ra-rbac Datagrid */}
<Datagrid>
<ImageField source="thumbnail" />
<TextField source="reference" />
<ReferenceField source="category_id" reference="categories">
<TextField source="name" />
</ReferenceField>
<NumberField source="width" />
<NumberField source="height" />
<NumberField source="price" />
<TextField source="description" />
{/** these two columns are not visible to the user **/}
<NumberField source="stock" />
<NumberField source="sales" />
</Datagrid>
</List>
);
```

**Tip**: Adding the 'read' permission on the resource itself doesn't grant the 'read' permission on the columns. If you want a user to see all possible columns, add the 'read' permission on columns using a wildcard:

```jsx
{ action: "read", resource: "products.*" }.
```

## `<List>`

Expand Down
127 changes: 87 additions & 40 deletions docs/Datagrid.md
Original file line number Diff line number Diff line change
Expand Up @@ -1074,52 +1074,99 @@ Additionally, `<DatagridAG>` is compatible with the [Enterprise version of ag-gr

Check [the `<DatagridAG>` documentation](./DatagridAG.md) for more details.

## Fields And Permissions
## Access Control

You might want to display some fields only to users with specific permissions. Use [the `useCanAccess` hook](./useCanAccess.md) to check whether users have access to a field:
If you need to hide some columns based on a set of permissions, use the `<Datagrid>` component from the `@react-admin/ra-rbac` package.

```diff
-import { Datagrid } from 'react-admin';
+import { Datagrid } from '@react-admin/ra-rbac';
```

This component adds the following [RBAC](./AuthRBAC.md) controls:

- Users must have the `'read'` permission on a resource column to see it in the export:

```jsx
{ action: "read", resource: `${resource}.${source}` }.
// or
{ action: "read", resource: `${resource}.*` }.
```

- Users must have the `'delete'` permission on the resource to see the `<BulkExportButton>`.

- The default `rowClick` depends on the user permissions:
- `"edit"` if the user can access the current resource with the `edit` action
- `"show"` if the user can access the current resource with the `show` action
- empty otherwise

Here is an example of `<Datagrid>` with RBAC:

{% raw %}
```tsx
import { List, Datagrid, TextField, TextInput, ShowButton, useCanAccess } from 'react-admin';
import { canAccessWithPermissions, List, Datagrid } from '@react-admin/ra-rbac';
import {
ImageField,
TextField,
ReferenceField,
NumberField,
} from 'react-admin';

const getUserFilters = (canAccessRole) => ([
<TextInput label="user.list.search" source="q" alwaysOn />,
<TextInput source="name" />,
canAccessRole ? <TextInput source="role" /> : null,
].filter(filter => filter !== null)
);
const authProvider = {
// ...
canAccess: async ({ action, record, resource }) =>
canAccessWithPermissions({
permissions: [
{ action: 'list', resource: 'products' },
{ action: 'read', resource: 'products.thumbnail' },
{ action: 'read', resource: 'products.reference' },
{ action: 'read', resource: 'products.category_id' },
{ action: 'read', resource: 'products.width' },
{ action: 'read', resource: 'products.height' },
{ action: 'read', resource: 'products.price' },
{ action: 'read', resource: 'products.description' },
// { action: 'read', resource: 'products.stock' },
// { action: 'read', resource: 'products.sales' },
// { action: 'delete', resource: 'products' },
{ action: 'show', resource: 'products' },
],
action,
record,
resource
}),
};

export const UserList = ({ permissions, ...props }) => {
const { canAccess, error, isPending } = useCanAccess({
resource: 'users.role',
action: 'read'
});
return (
<List
{...props}
filters={getUserFilters(canAccess)}
sort={{ field: 'name', order: 'ASC' }}
>
<Datagrid>
<TextField source="id" />
const ProductList = () => (
<List>
{/* The datagrid has no bulk actions as the user doesn't have the 'delete' permission */}
<Datagrid>
<ImageField source="thumbnail" />
<TextField source="reference" />
<ReferenceField source="category_id" reference="categories">
<TextField source="name" />
{canAccess ? <TextField source="role" /> : null}
<EditButton />
<ShowButton />
</Datagrid>
</List>
)
};
</ReferenceField>
<NumberField source="width" />
<NumberField source="height" />
<NumberField source="price" />
<TextField source="description" />
{/** These two columns are not visible to the user **/}
<NumberField source="stock" />
<NumberField source="sales" />
</Datagrid>
</List>
);
```
{% endraw %}

Note how the `canAccess` value is passed down to the custom `filters` component to allow Filter customization, too.
**Tip**: Adding the 'read' permission on the resource itself doesn't grant the 'read' permission on the columns. If you want a user to see all possible columns, add the 'read' permission on columns using a wildcard:

```jsx
{ action: "read", resource: "products.*" }.
```

Should you need to check multiple fields, you can leverage [the `useCanAccessResources` hook](./useCanAccess.md#multiple-resources):
Fow simple cases, you can also use [the `useCanAccess` hook](./useCanAccess.md) to check whether users have access to a field:

{% raw %}
```tsx
import { List, Datagrid, TextField, TextInput, ShowButton, useCanAccessResources } from 'react-admin';
import { List, Datagrid, TextField, TextInput, ShowButton, useCanAccess } from 'react-admin';

const getUserFilters = (canAccessRole) => ([
<TextInput label="user.list.search" source="q" alwaysOn />,
Expand All @@ -1129,8 +1176,8 @@ const getUserFilters = (canAccessRole) => ([
);

export const UserList = ({ permissions, ...props }) => {
const { canAccess, error, isPending } = useCanAccessResources({
resources: ['users.id', 'users.name', 'users.role'],
const { canAccess, error, isPending } = useCanAccess({
resource: 'users.role',
action: 'read'
});
return (
Expand All @@ -1140,9 +1187,9 @@ export const UserList = ({ permissions, ...props }) => {
sort={{ field: 'name', order: 'ASC' }}
>
<Datagrid>
{canAccess['users.id'] ? <TextField source="id" /> : null}
{canAccess['users.name'] ? <TextField source="name" /> : null}
{canAccess['users.role'] ? <TextField source="role" /> : null}
<TextField source="id" />
<TextField source="name" />
{canAccess ? <TextField source="role" /> : null}
<EditButton />
<ShowButton />
</Datagrid>
Expand All @@ -1152,7 +1199,7 @@ export const UserList = ({ permissions, ...props }) => {
```
{% endraw %}

**Tip**: The [ra-rbac module](./AuthRBAC.md#datagrid) provides a wrapper for the `<Datagrid>` with built-in permission check for columns.
Note how the `canAccess` value is passed down to the custom `filters` component to allow Filter customization, too.

## Standalone Usage

Expand Down

0 comments on commit 40b374d

Please sign in to comment.