2

Permissioning/Authorization (not Authentication) is a cross-cutting concern, I think.

In an Onion Architecture or Hexagonal Architecture, where should permissioning be performed? Examples of permissioning required would be:

  • Filtering data returned to the front end (UI, API, or otherwise)
  • Validating that a business operation can be performed at all

Ideally, via the Single Responsibility Principle, the code that performs the business operations and returns data shouldn't need to be aware of a user's permissions at all. The implementations of that functionality should know how to perform business operations or query a repository or domain service - that's it.

Would a wrapper/facade implementing the same interface as the class performing the business operation or returning the data be the place to put this permissioning? Or is there a better way?

Also, if the best practice is to permission by activity, not by role, is it still valid to say permissioning should be performed by a service whose purpose is simply to return data?

Jeff
  • 32,948
  • 13
  • 96
  • 198

2 Answers2

2

One could argue that access checking should always be as close to the code that performs the operation as possible to reduce the chance that someone can find a side-channel that bypasses access checking. That said, if you can use a wrapper class such that you guarantee that in the production system access checking will always be in place, I think it is fine.

Validating that a business operation can be performed at all

I find that it is very natural to put access checks that determine if an operation can be performed or not in a wrapper. The wrapper code is typically simple glue that understands the arguments that are being passed to the protected function and converts those into a form appropriate for making authorization decisions.

Filtering data returned to the front end (UI, API, or otherwise)

By this I am assuming that you mean filtering rows out of a query's response based on the permissions of the caller. For example, if a department manager makes a query for everyone's salary, the manager would be returned only the salaries of people who report to him/her as they don't have permission to access other people's salaries.

For this type of filtering I have never found a way of implementing it as a crosscutting concern. I have either baked filtering into the business logic or fallen back on a model that simply refuses to allow the query to execute due to lack of permission.

The problem that I've faced is that, to enable filtering, the security code must look at the data that is returned and be able to associate permissions with it. It seems a fair amount of work to do this in the simple case and downright hairy in the complex case (imagine a data set being returned that is a join of several database operations).

That said, I'm not against the content filtering. I just haven't seen a good solution for it.

Neil Smithline
  • 1,462
  • 9
  • 19
0

Permissions by activity is where you have a permission and activities that are being checked by the authorisation API. This is the same as Roles and Permissions and the authorisation is done by checking the permissions against business object for which we have specified that Role is authorized. This gives flexibility, because the only thing fixed is the permissions, but we can have different roles - defined in DB independent from the permissions completely.

One way to separate the authorisation logic completely from the services and and Business layer that returns the object for display is to use custom authorisation attributes. In these attributes you can specify which permissions the user needs to have in order to execute action in MVC controller.

Retrieving the business objects for which the user has a permission and using them as an input when calling a service or repository is a good way to separate the Authorisation logic from the service logic. Example of that will be- I am user x and have access to customers y,t,g and l - give me all order of my customers.

Lyubomir Velchev
  • 902
  • 2
  • 11
  • 29