|
| 1 | +--- |
| 2 | +title: "canAccessWithPermissions" |
| 3 | +--- |
| 4 | + |
| 5 | +`canAccessWithPermissions` is a helper function that facilitates the implementation of [Access Control](./Permissions.md#access-control) policies based on an underlying list of user roles and permissions. |
| 6 | + |
| 7 | +It is a builder block to implement the `authProvider.canAccess()` method, which is called by ra-core to check whether the current user has the right to perform a given action on a given resource or record. |
| 8 | + |
| 9 | +This feature requires a valid [Enterprise Edition](https://marmelab.com/ra-enterprise/) subscription. |
| 10 | + |
| 11 | +## Installation |
| 12 | + |
| 13 | +```bash |
| 14 | +npm install --save @react-admin/ra-core-ee |
| 15 | +# or |
| 16 | +yarn add @react-admin/ra-core-ee |
| 17 | +``` |
| 18 | + |
| 19 | +## Usage |
| 20 | + |
| 21 | +`canAccessWithPermissions` is a pure function that you can call from your `authProvider.canAccess()` implementation. |
| 22 | + |
| 23 | +```tsx |
| 24 | +import { canAccessWithPermissions } from '@react-admin/ra-core-ee'; |
| 25 | + |
| 26 | +const authProvider = { |
| 27 | + // ... |
| 28 | + canAccess: async ({ action, resource, record }) => { |
| 29 | + const permissions = myGetPermissionsFunction(); |
| 30 | + return canAccessWithPermissions({ |
| 31 | + permissions, |
| 32 | + action, |
| 33 | + resource, |
| 34 | + record, |
| 35 | + }); |
| 36 | + } |
| 37 | + // ... |
| 38 | +}; |
| 39 | +``` |
| 40 | + |
| 41 | +The `permissions` parameter must be an array of permissions. A *permission* is an object that represents access to a subset of the application. It is defined by a `resource` (usually a noun) and an `action` (usually a verb), with sometimes an additional `record`. |
| 42 | + |
| 43 | +Here are a few examples of permissions: |
| 44 | + |
| 45 | +- `{ action: "*", resource: "*" }`: allow everything |
| 46 | +- `{ action: "read", resource: "*" }`: allow read actions on all resources |
| 47 | +- `{ action: "read", resource: ["companies", "people"] }`: allow read actions on a subset of resources |
| 48 | +- `{ action: ["read", "create", "edit", "export"], resource: "companies" }`: allow all actions except delete on companies |
| 49 | +- `{ action: ["write"], resource: "game.score", record: { "id": "123" } }`: allow write action on the score of the game with id 123 |
| 50 | + |
| 51 | +:::tip |
| 52 | +When the `record` field is omitted, the permission is valid for all records. |
| 53 | +::: |
| 54 | + |
| 55 | +In most cases, the permissions are derived from user roles, which are fetched at login and stored in memory or in localStorage. Check the [`getPermissionsFromRoles`](./getPermissionsFromRoles.md) function to merge the permissions from multiple roles into a single flat array of permissions. |
| 56 | + |
| 57 | +## Parameters |
| 58 | + |
| 59 | +This function takes an object as argument with the following fields: |
| 60 | + |
| 61 | +| Name | Optional | Type | Description |
| 62 | +| - | - | - | - | |
| 63 | +| `permissions` | Required | `Array<Permission>` | An array of permissions for the current user |
| 64 | +| `action` | Required | `string` | The action for which to check users has the execution right |
| 65 | +| `resource` | Required | `string` | The resource for which to check users has the execution right |
| 66 | +| `record` | Required | `string` | The record for which to check users has the execution right |
| 67 | + |
| 68 | +`canAccessWithPermissions` expects the `permissions` to be a flat array of permissions. It is your responsibility to fetch these permissions (usually during login). If the permissions are spread into several role definitions, you can merge them into a single array using the [`getPermissionsFromRoles`](./getPermissionsFromRoles.md) function. |
| 69 | + |
| 70 | +## Building RBAC |
| 71 | + |
| 72 | +The following example shows how to implement Role-based Access Control (RBAC) in `authProvider.canAccess()` using `canAccessWithPermissions` and `getPermissionsFromRoles`. The role permissions are defined in the code, and the user roles are returned by the authentication endpoint. Additional user permissions can also be returned by the authentication endpoint. |
| 73 | + |
| 74 | +The `authProvider` stores the permissions in `localStorage`, so that returning users can access their permissions without having to log in again. |
| 75 | + |
| 76 | +```tsx |
| 77 | +// in roleDefinitions.ts |
| 78 | +export const roleDefinitions = { |
| 79 | + admin: [ |
| 80 | + { action: '*', resource: '*' } |
| 81 | + ], |
| 82 | + reader: [ |
| 83 | + { action: ['list', 'show', 'export'], resource: '*' }, |
| 84 | + { action: 'read', resource: 'posts.*' }, |
| 85 | + { action: 'read', resource: 'comments.*' }, |
| 86 | + ], |
| 87 | + accounting: [ |
| 88 | + { action: '*', resource: 'sales' }, |
| 89 | + ], |
| 90 | +}; |
| 91 | + |
| 92 | +// in authProvider.ts |
| 93 | +import { canAccessWithPermissions, getPermissionsFromRoles } from '@react-admin/ra-core-ee'; |
| 94 | +import { roleDefinitions } from './roleDefinitions'; |
| 95 | + |
| 96 | +const authProvider = { |
| 97 | + login: async ({ username, password }) => { |
| 98 | + const request = new Request('https://mydomain.com/authenticate', { |
| 99 | + method: 'POST', |
| 100 | + body: JSON.stringify({ username, password }), |
| 101 | + headers: new Headers({ 'Content-Type': 'application/json' }), |
| 102 | + }); |
| 103 | + const response = await fetch(request); |
| 104 | + if (response.status < 200 || response.status >= 300) { |
| 105 | + throw new Error(response.statusText); |
| 106 | + } |
| 107 | + const { user: { roles, permissions }} = await response.json(); |
| 108 | + // merge the permissions from the roles with the extra permissions |
| 109 | + const permissions = getPermissionsFromRoles({ |
| 110 | + roleDefinitions, |
| 111 | + userPermissions, |
| 112 | + userRoles |
| 113 | + }); |
| 114 | + localStorage.setItem('permissions', JSON.stringify(permissions)); |
| 115 | + }, |
| 116 | + canAccess: async ({ action, resource, record }) => { |
| 117 | + const permissions = JSON.parse(localStorage.getItem('permissions')); |
| 118 | + return canAccessWithPermissions({ |
| 119 | + permissions, |
| 120 | + action, |
| 121 | + resource, |
| 122 | + record, |
| 123 | + }); |
| 124 | + } |
| 125 | + // ... |
| 126 | +}; |
| 127 | +``` |
0 commit comments