Business requires a very complicated access control system where users are granted/denied permissions based on multiple criteria at runtime. Just to give you an idea look at following rules for some resources for example.
- Can view the list
- Can view the detail
- Can view the detail page but can not see the email address/phone number field
- can edit the full doc
- can edit only the phone number but can not set it to null as new value.
- can remove the record altogether
- can print the page only if not still a trainee
I hope you got the idea. There are about 100+ resources with each having between 20 to 90 similar such intertwined rules.
Considering the sheer size and complexity of the system, my team has decided to build a hierarchical role based access control system and below is our idea of how we are planning to tackle the problem.
-
use a simple class for each role
-
use inverted chain of command and add a ref for the roles current role is inherited from
-
current role class handles permissions unique to the current class only. and then continues all the way up (in real life downwards) to compute the full permission graph.
-
use decorators to handle the runtime calculations for cross cutting concerns for example limiting the delete permission if user is a trainee or he is still on probation or limit the access when user has resigned and just completing his last days at the company.
To put it into an example, a user is a manager but in probation duration and he wants to access the details of a client. here is the pseudo code
CountryManagerPermnissionsSpec()
{
immediate_subordinates = ["RegionalManager", "ManagerHR"];
accessible_resources = [
"Finance": ["LIST", "DETAIL", "TRANSACTION_HISTORY", "REPORTS"]
]
}
// class to limit the permissions when user is still under training
TraineeUserPermissionsSpec()
{
blocked_activites = ["delete", "bulk delete", "direct client message"];
}
Skipping out the implementation detail, we can compute the current users permission as below
new acl
acl->addRole(ContryManager)
acl->addRole(Trainee)
can_see_data = UserCanViewDataSpec(user, acl)
Now my question is, this leads us to a lot of tiny but testable classes, which is not a problem for us but is this the right approach towards handling such kind of a problem ? Is there any better alternate ?
I don't think it maters but just in case, current system is being built in php.
Aucun commentaire:
Enregistrer un commentaire