Complexities of Role based UI Access
In some projects I came across that implemented roles (for example, super admin, admin, regular user etc.), I found a lot of codes based on if-else conditions making the readability complex due to ever-changing requirements of a project. The problem gets more complex whenever there is a case of mixed access, that is multiple roles can access multiple pages both common and exclusive to one another:
Let's say in a hospital management system we have doctors accessing certain pages (ex. Patient History, Generate Prescription, View Report), lab technicians some pages which doctors do not need access and some which they do (Upload Report, View Report) and patients who have access to certain pages(Patient History, View Report, View Prescription) , Admin for the sake of this article all pages of a certain Hospital and Super Admin - all pages for all hospitals.
While reviewing codes for these kind of role implementation, I have seen stuff written with if/else logics to prevent access to the page in a router/controller.
Pseudo codes for illustration purposes):
If role != 'admin' or role != 'doctor'
return
{'code':401,
'error':'generate prescription is inaccessible'} ;
And then as project requirements grows the conditions in the if/else start growing and becomes not manageable.
So here is a suggestion which might be worth considering.
Implement a role table. Here table can be considered a matrix with dynamically expandable rows and columns where columns represent the roles and the rows represent the UI pages/controls. Illustration below:
Role vs UI Table
Note that it probably would not be a good idea to use a Relational Database Table although it is looking like one above, simply because the columns are not static.
This is more of a matrix implementation than anything else.
However, the key here is we need an elegant lookup table and the if/else codes mentioned aboive can be changed in a generic fashion likewise:
// assume Role = 'doctor'
// assmue Page = 'generate prescription'
If Accessible(Role, Page) == 'no'
return
{'code':401,
'error': Page + ' is inaccessible'}
Note that no matter how may roles or pages you create, the code in router/controller remains intact and need not be touched.
All you have to do is update the Role vs UI table.
There is one thing left and that is - what if a user such as super admin has access to multiple hospitals page and an admin only has access to pages of a certain hospital. Or while a doctor is tied to a certain hospital, a patient can access pages in multiple hospitals that he/she has visited.
We can simply solve it by a "Belongs To" table. Here is the concept in a nutshell:
(User ID | Role) vs Belongs To Table
[user1 | admin, hospital 1]
[user2 | admin, hospital 2]
[user3 | doctor, hospital 1]
[user4 | doctor, hospital 2]
[user5 | labtech, hospital 1]
[user6 | labtech, hospital 2]
[user7 | patient, Any]
[user8 | super, Any]
Here we are simply having a lookup table of combined key using user and role vs which hospital that user belongs to based on that role.
Super admin and patient can belong to any hospital.
Therefore, before applying the Accessible method, we just do a simple check with Belongs To, i.e
hospital = BelongsTo(userId, Role)
If hospital == 'any' or hospital == currentHospital
then return check_accessibility();
Whatever discussed in this article is a guideline to the beginners (or sometimes mid level devs) to think in a more organized way to deal with roles and accessibility. Definitely a real world project demands more but we have to have the right mindset first and start writing elegant codes.