Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Enforcing property-level authorization in domain objects

I am implementing a RESTful service that has a security model requiring authorization at three levels:

  1. Resource level authorization - determining whether the user has access to the resource (entity) at all. For example, does the current user have permission to view customers in general. If not, then everything stops there.
  2. Instance level authorization - determining whether the user has access to a specific instance of a resource (entity). Due to various rules and the state of the entity, the current user may not be granted access to one of more customers within the set of customers. E.g., a customer may be able to view their own information but not the information of another customer.
  3. Property level authorization - determine which properties the user has access to on an instance of the resource (entity). We have many business rules that determine whether a user may see and/or change individual properties of a resource (entity). For example, the current user may be able to see the customer's name but not their address or phone number as well as able to see and add notes.

Implementing resource-level authorization is straight-forward; however, the other two are not. I believe the solution for instance-level authorization will reveal itself with a solution to the (harder, imo) property-level authorization question. The latter issue is complicated by the fact that I need to communicate the authorization decisions, by property, in the response message (ala hypermedia) - in other words, this isn't something I can simply enforce in property setters.

With each request to the service, I must use the current user's information to perform these authorization checks. In the case of a GET request for either a list of resources or an individual resource, I need to tell the API layer which attributes the current user can see (is visible) and whether the attribute is read-only or editable. The API layer will then use this information to create the appropriate response message. For instance, any property that is not visible will not be included in the message. Read-only properties will be flagged so the client application can render the property in the appropriate state for the user.

Solutions like application services, aspects, etc. work great for resource-level authorization and can even be used for instance-level checks, but I am stuck determining how best to model my domain so the business rules and checks enforcing the security constraints are included.

NOTE: Keep in mind that this goes way beyond role-based security in that I am getting the final authorization result based on business rules using the current state of the resource and environment (along with verifying access using the permissions granted to the current user via their roles).

How should I model the domain so I have enforcement of all three types of authorization checks (in a testable way, with DI, etc)?

like image 895
SonOfPirate Avatar asked May 02 '15 12:05

SonOfPirate


2 Answers

Initial Assumptions

I suppose the following architecture:

                                     Stateless Scaling                                          Sharding
                                             .                                                     .
                                             .                                                     .
                                             .                                                     .
                         +=========================================+
                         |  Service Layer                          |
+----------+             |  +-----------+        +---------------+ |                         +------------+
|          |    HTTP     |  |           |        |               | |   Driver, Wire, etc.    |            |
|  Client  | <============> |  RESTful  | <====> |  Data Access  | <=======================> |  Database  |
|          |    JSON     |  |  Service  |  DTO   |     Layer     | |     ORM, Raw, etc.      |            |
|          |             |  |           |        |               | |                         |            |
+----------+             |  +-----------+        +---------------+ |                         +------------+
                         +=========================================+
                                             .                                                     .
                                             .                                                     .
                                             .                                                     .

Initially, let us suppose you are authenticating the Client with the Service Layer and obtain a particular token, that encodes the authentication and authorization information in it.

First of all, what I first think of is to process all the requests and then only filter them depending on authorization. This will make the whole thing much simpler and much easier to maintain. However, of course there might be some requests requiring expensive processing, in which case this approach is absolutely not effective. On the other hand, the heavy-load requests will most probably involve resource level access, which as you have stated is easy to organise, and it is possible to detect and authorise in Service Layer at API or at least Data Access levels.

Further Thoughts

As for instance and property level authorization, I will not even try to put it into the Data Access Layer and would completely isolate it beyond the API level, i.e. starting from Data Access Layer no any layer would even be aware of it. Even if you request a list of 1M objects and want to emit one or two properties from all objects for that particular client, it would be preferable to fetch the whole objects and then only hide the properties.

Another assumption is that your model is a clear DTO, i.e. simply a data container, and all the business logic is implemented in the Service Layer, particularly API level. And say you pass the data across HTTP encoded as JSON. So anyway, somewhere in front of the API layer, you are going to have a small serialization stage, to transform your model into JSON. So this stage is where I think is the ideal place to put the instance and property authorization.

Suggestion

If it comes to the property level authorization, I think there is no reasonable way to isolate the model from security logic. Be it a rule-based, role-based or whatever-based authorisation, the process is going to be verified upon a piece of value from the authentication/authorisation token provided by the Client. So, in the serialisation level, you will get basically two parameters, the token and the model, and accordingly will serialise appropriate properties or the instance as a whole.

When it comes to defining the rules, roles and whatevers per property for the model, it can be done in various ways depending on the available paradigms, i.e. depending on the language the Service Layer will be implemented. The definitions can be done heavily utilising Annotations (Java) or Decorators (Python). For emitting specific properties, Python will come handy with its dynamic typing and hacky features, e.g. Descriptors. In case of Java, you might end up encapsulating properties into a template class, say AuthField<T>.

Summary

Summing all up, I would suggest to put the instance and property authorization in front of the API Layer in the stage of serialisation. Thus basically, the roles/rules will be assigned in the model and the authorisation will be performed in the serialiser, being provided the model and token.

like image 149
bagrat Avatar answered Nov 06 '22 04:11

bagrat


Since a comment was added recently, I thought I'd update this post with my learnings since I originally asked the question...

Simply put, my original logic was flawed and I was trying to do too much in my business entities. Following a CQRS approach helped make the solution clearer.

For state changes, the "write model" uses the business entity and a "command handler"/"domain service" performs authorization checks to make sure the user has the necessary permissions to update the requested object and, where applicable, change specific properties of that object. I still debate whether the property-level checks belong inside the business entity methods or outside (in the handler/service).

In the case of the "read model", a "query handler"/"domain service" checks the resource- and instance-level authorization rules so only objects to which the user has access are returned. The handler/service uses a mapper object that applies the property-level authorization rules to the data when constructing the DTO(s) to return. In other words, the data access layer returns the "projection" (or view) regardless of authorization rules and the mapper ignores any properties to which the current user does not have access.

I dabbled with using dynamic query generation based on the list of fields to which the user has access but found that to be overkill in our case. But, in a more complex solution with larger, more complex entities, I can see that being more viable.

like image 1
SonOfPirate Avatar answered Nov 06 '22 05:11

SonOfPirate