Overview
XB UI needs to be able to check permissions and alter the UI accordingly. We need some basic utilities that will allow this to be handled consistently throughout the app.
There are 3 types of checks:
Config entities
drupalSettings.permissions => [
'globalRegions' => bool,
'sections' => bool,
'codeComponents' => bool,
'contentTemplates' => bool,
],
Creating new content entities
TBD, #3529895: Provide the client with `create` operation access information similar to #3516657.
Still to be defined, but potentially something like:
drupalSettings.permissions => [
"contentEntityCreateOperations' => {
"entity_type": {
"bundle": label/FALSE,
}
"xb_page" : {
"xb_page": "Page" // The singular label for "New Page"
}
"node": {
"article": "Article", // The singular label for "New Article"
"blogpost": FALSE,
}
],
],
Existing content entities
See #3516657: Update `ApiContentControllers::list()` to expose available content entity operations in `meta` and \Drupal\experience_builder\XbUriDefinitions[link])
Calling /xb/api/v0/content/{entity_type} for the list of entities, returns a set of allowed links for each content entity:
'entity_id': {
'id': 1,
'title': "Page 1",
"status" => TRUE,
"path" => '/page-1',
...,
"links": {
"edit-form": "/xb/xb_page/1",
"delete-form': "/xb/xb_page/1",
"https://drupal.org/project/experience_builder#link-rel-duplicate": "/xb/api/v0/content/xb_page"
"https://drupal.org/project/experience_builder#link-rel-set-as-homepage": "/xb/api/v0/content/xb_page"
}
}
...
Proposed resolution
Communicate permissions
Provide a list of permissions to the FE within drupalSettings.xb.permissions or similar.
type Permissions = string[];
interface DrupalSettings {
xb: {
...
permissions: Permissions;
}
}
Utility functions
Create utility functions to help manage and check permissions:
- Check if a user has an individual permission
const hasPermission = (permission: string, userPermissions: Permissions): boolean => { return userPermissions.includes(permission); }; - Check if a user has multiple permissions
const hasPermissions = (requiredPermissions: Permissions, userPermissions: Permissions): boolean => { return requiredPermissions.every(permission => userPermissions.includes(permission)); }; - Check if a user has one or more of a given list of permissions
const hasAnyPermission = (requiredPermissions: Permissions, userPermissions: Permissions): boolean => { return requiredPermissions.some(permission => userPermissions.includes(permission)); };
React Component
A reusable React component that can be used to conditionally render UI elements based on permissions:
import React, { ReactNode } from 'react';
interface PermissionCheckProps {
hasPermission?: string;
hasAnyPermission?: Permissions;
hasPermissions?: Permissions;
denied?: ReactNode;
children: ReactNode;
userPermissions: Permissions;
}
const PermissionCheck: React.FC<PermissionCheckProps> = ({
hasPermission,
hasAnyPermission,
hasPermissions,
denied = <div>You don’t have permission</div>,
children,
userPermissions
}) => {
const isAllowed =
(hasPermission && hasPermission(userPermissions)) ||
(hasAnyPermission && hasAnyPermission(hasAnyPermission, userPermissions)) ||
(hasPermissions && hasPermissions(hasPermissions, userPermissions));
return <>{isAllowed ? children : denied}</>;
};
export default PermissionCheck;
Example of how it would be used:
<PermissionCheck hasPermission="canAdd" denied={(<button disabled title="You do not have permission to add">Add</button>)}>
<button>Add</button>
</PermissionCheck>
Important Note
Backend Enforcement: Always enforce permissions on the backend as frontend checks can be bypassed.
Out of scope
Real-time Updates: Real-time updates to permissions don't need to be supported; users must reload the page to get updated permissions.
User interface changes
Design Considerations:
We need to come up with a set of common rules/UX guidelines to cover the following and when they should be used
- UI Element Visibility: Show or remove UI elements based on permissions.
- UI Element State: Enable or disable UI (visuals for disabled states) elements based on permissions.
- Custom UI: Display custom UI in place of the regular UI when a permission is missing.
- Custom dialog: Display custom dialog when a user performs an action they don't have permission to do.
| Comment | File | Size | Author |
|---|---|---|---|
| #16 | page-perms-delete-only.gif | 29.02 KB | wim leers |
| #16 | page-perms-none.gif | 20.02 KB | wim leers |
| #16 | page-perms-all.gif | 49.69 KB | wim leers |
Issue fork experience_builder-3516641
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
Comments
Comment #2
wim leersAs long as we agree on what the server will pass to the client in #3516648: Update `ExperienceBuilderController` to pass current user's XB high-level permissions to the client, work on this could begin in parallel. But that probably makes little sense?
Comment #3
mglaman#3516648: Update `ExperienceBuilderController` to pass current user's XB high-level permissions to the client is finished, can the updated and agreed upon format be updated here? Are we using raw permissions from Drupal or should it be createPage, etc because that is not what was done in the other ticket.
Comment #4
penyaskito@mglaman That includes the config-related permissions, which are all-or-nothing so are just flags. But we might want to still postpone on #3516657: Update `ApiContentControllers::list()` to expose available content entity operations in `meta` for the content related ones.
Comment #5
wim leers#4++
Given that this could otherwise result in information disclosure vulnerabilities (not access bypass because the server side pieces are taken care of, at least once #3494915: Support entity-level + field-level access checking in auto-save — i.e. in `experience_builder.api.api.(layout.post|auto-save.post)` + #3516432: Update all XB routes to respect content entity update/field edit access of edited XB field are done), going ahead and tagging this a blocker for #3515932: Milestone 1.0.0-beta1: Enable creation of non-throwaway sites.
Comment #6
wim leersAdding #4's + #5's as related issues, to improve discoverability.
Comment #7
penyaskito#3516657: Update `ApiContentControllers::list()` to expose available content entity operations in `meta` landed.
Comment #8
penyaskitoAdded the different scenarios of permissions that need to be exposed to the client UI.
Comment #9
penyaskitojust formatting
Comment #10
jessebaker commentedIn the interests of keeping things as simple as possible on the FE, I was hoping to get something like the following
JSON:
Because then in the UI checking if we should display a button or not is as simple as, for example:
JSX:
Comment #12
penyaskitoAmazing 😍
NW per @larowlan review and I think I spotted a wrong permission check, but this looks amazing already and covers more scope than I expected.
Comment #14
penyaskitoComment #15
jessebaker commentedFinally got to the bottom of a number of issues that were causing a lot of friction in writing the Playwright tests for this work. All resolved now and ready for another round of review.
The summary on the MR is the most up to date place for all info on this work.
Comment #16
wim leersDid thorough manual testing. No problems found.
Tip for fast manual testing: change
to
etc. to test any combination of things.
Found two bugs, both in the same area:
<hr>should be omitted.Comment #17
jessebaker commentedI'm not sure it's possible to actually get into those situations (yet?!) but good catch. At the moment, you can't even access XB if you don't have permission to edit XB pages so if you can access XB then you will always have at least one option in the menu.
Anyway I've added some more robustness around the UI when people have no permissions or can delete things but don't have any other items in the menu which should keep this looking tidy in the future no matter what configuration of permissions people end up with!
Comment #18
wim leersIs that true? What if I can create/edit articles?
In any case, it won't be true anymore very soon, because #3529924: Add access check for using Experience Builder at all: if >=1 content entity type with an XB field can be created or edited. will definitely allow you to load XB if you cannot use
Pages at all, assuming you have an editable XB field on article nodes.Thanks for adding that robustness — I won't test again, I bet you tested it thoroughly 😄👍
Comment #19
wim leers/me typed "u", return, cmd+space, click "Save" … and didn't spot that it didn't select 😅
Comment #21
wim leers🤩🤩🤩🤩 And … the XB UI actually starts to come fully alive! 🧟♂️
Comment #22
wim leersAFAICT this is a small edge case we missed here: #3535132: Show descriptive error message instead of generic 403 for unauthorised actions in the navigator.
Comment #23
wim leersOne more missed edge case: #3535142: Edit option incorrectly visible in Layers panel for users without "administer code components" permission.