Authorization is a difficult topic. The implementation is typically so application/developer specific, that when you ask ten people how they do it, you most likely get ten different answers. I think this is also the reason why .NET does not include a “real” authorization framework besides providing some building blocks (e.g. roles and claims). When working at the claims guide for Microsoft we even shied away from writing a “best practices” chapter on that topic.
Because of that, everybody is building his own little framework around authorization – sometime simple sometimes with humongous complexity. And when you give feedback to such a system, you will most likely hurt somebody’s feelings.
Below are some observations around authorization and patterns I identified while working with our customers.
Fine vs. coarse grained
This is an obvious one. Coarse grained authorization mechanisms are indeed implemented in .NET. Coarse grained basically means that you do authorization at a very early stage in the pipeline. Typically around the question: “is user X allowed to access resource Y”. Details of that resource access (like specific parameters passed in) are typically not known at this early stage. Some examples are:
- The ASP.NET <authorization /> configuration element. It restricts access to URLs or pages (and verbs) based on roles.
- The .NET [PrincipalPermission] attribute restricts access to methods and classes based on roles.
- The WCF service authorization manager restricts access to SOAP actions based on roles (and claims).
- The WIF claims authorization manager has a coarse grained mode where you can restrict access to SOAP actions or URLs/verbs based on claims.
- The ASP.NET MVC/Web API [Authorize] attribute restricts access to controllers or action methods based on roles.
Coarse grained authorization is very useful to specify a general access policy across multiple endpoints in your application. They also typically follow the pattern that you centralize your authorization policy in a single place.
Since this type of authorization typically runs before any deserialization or model binding has happened, you don’t get access to the actual payload. And this is by design, because you don’t really want to do anything expensive to find out the user isn’t authorized anyways. But many authorization decisions are indeed based on the data that you get send. This is where fine grained authorization comes into play.
Fine grained authorization typically happens “inside the method” – either on the façade or in the business logic. There you have full access to the context. This is the space where .NET does not have a lot to offer and where people do their own implementations.
A remarkable exception is the WIF/.NET 4.5 claims authorization manager infrastructure where authorization is abstracted using resource/action pairs. I like that approach, but unfortunately the API did not get much love from Microsoft, and without writing your own little supporting infrastructure you won’t have much fun (something I guess I will revisit in a future post).
Inline vs. Separate
What I really mean here is, is the authorization logic intermingled with the business logic, or can they be maintained, versioned and tested separately.
The big anti-pattern is this:
if (User.IsInRole(“Foo”))
{
// business logic
}
else
{
// other business logic
}
What I really don’t like about this approach is, that the business logic code needs to have intimate knowledge about who is allowed to do what. Both business and authorization logic will separately change over time, mixing them makes the code hard to maintain.
There are numerous variations of that approach, including attributes (like [PrincipalPermission]) or “resource base classes” that implement the security checks.
The approach I’d recommend is keeping those concerns separate. The WIF/.NET 4.5 ClaimsAuthorizationManager uses that approach. There the business logic code is only telling the authorization system that “principal X tries to do Y” – and the authorization system figures out the rest. Ideally the configuration policy is implemented separately from the application code, so you can version them independently.
Going one step further, the authorization data may even be stored on a totally different system, and your application local plumbing only queries that system. Microsoft Authorization Manager (AzMan) used that approach when storing authorization rules in Active Directory. There are also XACML based systems, but this technology never got a lot of traction in the .NET/Windows space.
(The ideas in XACML btw are interesting as they have clean separations of concerns. I’d love to see a claims/rules based system built into Windows. Like AzMan v.Next.)
Obviously there are also a number of in-between implementations.
Finding the right balance…
between all these approaches is hard. And especially when you are trying to write re-usable code, you constantly struggle generalizing things too much and trying to abstract the too specific ones.
Which approach are you using? Am I missing something?
How would you design authorization (let’s say for a controller action in ASP.NET MVC) in which the user is allowed to see a <> of data based on certain rule? For example, how would you handle:
– “a user is allowed to see only his data if he is member of Employee role”
– “a user is allowed to see his data and all of his subordinates if he is in Manager role”
It is not about my opinion here ;)
In my applications every action has a access profile to execute.
Example: AddCustomer needs profile (“ADD_01”), DeleteCostumer needs profile (“DEL_01”).
A role is a group of profiles, Admin should have both ADD_01 and DEL_01, while User only has ADD_01.
Then I use an attribute like [Profile(“ADD_01”)] in each action, that will validate that the user has the role needed, or only the profile.
What do you think?
It is not about *my* opinion here ;)
But looks like the separation I am talking about.
Pingback: Extending Authorization in ASP.NET Web API – Part 1: Basics | www.leastprivilege.com
We broke down everything the application can do into quite fine-grained functions. Furthermore we have roles (bear with me for the terminology) that map to a set of functions. The admin role typically has all functions enabled, while other roles could just have a subset, such as reading some data, or writing specific entities.
In our application, pretty much all kind of entities are identified by a GUID (which I’ll refer to as ID from there on). The presence of such an ID is true as well for users, groups and built-in subjects such as “Everyone”, “Anonymous” or “Authenticated User”.
Therefore, we can compute a set of subject IDs for every user, which includes the user ID (which we call the primary subject), the appropriate built-in subject IDs as well as all the IDs of direct and indirect group memberships (e.g. if a group is member of another group etc.). Note that even when no user is logged on we will at least have “Anonymous” and “Everyone” as subject IDs in that set; this set is available from the user principal object.
The permissions are stored as ACEs (Access Control Entries), which are basically entries consisting of the following information:
* role
* subject ID (“who”)
* object ID (“what”, we use Guid.Empty for “global” permissions)
* kind (allow, deny, audit)
* inheritable (e.g. does this entry propagate to child objects?)
The access entries are stored on their own and completely loosely coupled by the object IDs. If an object or a subject is deleted, there will be stale entries, but that doesn’t matter much.
There is a registry for hierarchy resolvers, which do two things: they can check whether they are resposible for the hierarchy a specific object GUID, and they can return the parent object ID (or null if no parent exists) of a given object ID. This enables the propagation of permissions, which is very important as soon as one starts to deal with object hierarchies.
With all that in place, we can compute the effective functions that any user has, and also determine whether the operations shall be recorded in the generic audit log. The computation conceptually works as follows (there is much that can be cached etc., so performance hasn’t been a problem so far):
* get all subject IDs for the user
* get all access entries of the given subjects on the given object
* find the hierarchy resolver for the object ID (if any), and walk up the hierarchy as long as there are parents, collecting the access entries that are marked as inheritable for the given subjects
* create three sets of functions from the roles in all access entries: allow, deny and audit
A generic “Checked” function, which receives a delegate to perform the operation (and report to the audit), will check if the required function is in the allow but not the deny set and if so try to execute it. Finally, it will check if the function needs to be audited (e.g. in the audit set), and if so report the performed operation to the audit log together with some additional information (time, user, object, success, message), The function will either pass through the result of the execution (if successful), re-throw the raised exception (if allowed but unsuccessful) or throw an UnauthorizedAccessException if not allowed to perform the operation in the first place.
As you can see, the whole setup is very generic and versatile, and all the core authorization code is actually in separate libraries that have no knowledge at all from the business or application logic.
sounds good!
For my last greenfield development, I used AzMan (stored in AD). It was a good decision: I can’t recall how many times the owners have asked for a change that I have actioned by twiddling in Authorization Manager. But now it’s deprecated and we should use claims. We’ll use ADFS 2; the question is how to generate the claims. I can’t use AD groups because I have more operations than the Windows token has capacity. So it looks like I need to develop something. But this is boilerplate stuff. My boss doesn’t want me writing this. If I write a line of code, it should be because no one else can know our requirements well enough. I understand that there are many ways to skin this cat but plenty of us will make do with what we find. As you can tell I don’t have answers to your question and I’m frustrated that there isn’t anything in the box.
Is .Net Sql AzMan the answer to this question? http://netsqlazman.codeplex.com/