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.
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.