User roles and permissions

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() and get_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:

Capture

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 … :sunglasses:

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

8 Likes

This is a great contribution. Thanks man!

1 Like

Tomaz, thanks for this clean example. I am at the point where I need to think about some serious access control management and this was a nice place to start.

I noticed that the last commit to miracle-acl and its 4 forks is 4 year old. This often means the project is dead because of low quality or because there is something better out there. Are you using this library because you have experience with it or because you happened to find it while playing with this demo?

I started reading the blog you mentioned on your previous post and I am intrigued by the UNIX-style permission model. Have you explored that path at all?

Thanks! This “weekend project” was just an exercise to check whether something like that can be made. I was mostly intrigued by implementing rules (the rows filter) into RBAC model, and how to make that as easy and intuitive as possible.

I guess any kind of RBAC library could be used. I chose miracle-acl because it is sleek and “a pleasure to use”, as described here --> there you can find a list of interesting python libraries in this field. I also considered policy (it seems that it uses a file to set up the grants??), py-fortress (requires an LDAP server), balrog-rbac (interesting and similar to miracle, don’t like its name :grinning:) and yosaipy2 (too heavy to grasp it so quickly, and it includes many things - like two-factor authentication - that are probably not needed in Anvil).

If you check the miracle-acl code, it’s not complicated, it just checks whether a permission exists for the (role, resource) pair, nothing else - it’s precisely what I needed.

I guess that UNIX-style permission system (or DAC, please see this thread) could be easily made with a special numeric field in secured data tables where you could save coded information about permissions (as a sum of powers of two, the rights for owner, group and anybody else) and apply binary checks. And then you also need a list of user groups (each user belongs to one or more groups). I don’t like this approach since its assumption is that every resource has its owner, and this is usually not true for web applications - but it may suite your needs just as well. RBAC, on the other hand, has troubles when resources to guard are individual rows in data tables - binary operations in DAC are usually faster than complex filtering.

1 Like