Here’s a prototype of a role-based access control (RBAC) setup in Anvil. The core of this setup are:
- data tables: Roles(RoleName), RolesPermissions(Role, Resource, Permission) and Permissions (PermissionName, Rule)
- module miracle-acl (for now as copied into app)
- function
user_is_authorized()
to check whether a user is authorised to access a given resource (e.g. a table) with a certain permission - functions
get_client_readable_acl()
,get_client_writable_acl()
andget_client_writable_cascade_acl()
that return rows from a given table, according to given permission and user.
A grant is defined by role, resource and permission: e.g. the grant (‘admin’, ‘projects’, ‘writeAsAdmin’) gives admins (users associated with this role) ‘writeAsAdmin’ permission to access the Projects table. Grants are saved in table RolesPermissions.
Each permission has its rule. A rule defines an if condition for a by-row search. For instance, since ‘writeAsAdmin’ permission normally gives the right to access everything (all rows in a given table), the pair is (‘writeAsAdmin’, ‘True’). For writeAsUser permission the normal behaviour is that a user can read her own projects, so the pair is (‘writeAsUser’ , ‘LoggedUser == item[‘Owner’]’). LoggedUser denotes a current user, item is a row from a given table (projects in our case). Owner is a column in projects table linked to users.
In the demo app we also have a table ProjectGroups which represents groups of users. Project group is a group of users that work on projects together, and project group leader can access projects which belong to his group. The permission-rule pair is thus (writeAsGroupLeader, ‘LoggedUser[‘LeaderOfGroup’] == item[‘ProjectGroup’]’). LeaderOfGroup and ProjectGroup are columns in users and projects tables, respectively, and are linked to ProjectGroups table.
The main form in the demo app displays a list of projects. Choose a user from a dropdown and then click the button to check which projects are accessible to this user with a given permission (writeAsUser, writeAsAdmin, writeAsGroupLeader), i.e. if that was a permission needed to access the rows in projects table. Users are:
Demo function getProjectsWithWrite()
firstly establishes whether a chosen user is authorised for a clicked permission (depending from her role) and then gets the rows from projects table according to this permission.
The development process of an app with RBAC would thus be:
- set the Permissions table - permissions and the rules in this table are tightly related to the data structure of your application and other resources
- prepare functions to access data in app tables (like the demo function
getProjectsWithWrite()
); these functions should enforce (define) the permissions needed to access resources - function
user_is_authorized()
could be used to hide certain parts of GUI if a user is not authorised to have access to that resource.
In the demo app, I’m not using the Users service, however the Users table from this service can have a link to Roles table, so everything could be nicely integrated.
Assigning the roles to users is then a task for the app administrator on a daily basis …
Caution: this is only a prototype and if you spot mistakes or if you have suggestions, please comment. Current (logged in) user and permissions needed to access resources should be controlled on the server, not on the client as in the demo app! Modules are probably a better place than Server modules for the core functions.
https://anvil.works/build#clone:EIBSB22UKB434YFD=YLM2QTEAMLHW3LIVBKJCZAPS