Wednesday, December 03, 2008
« What books should I buy for CSLA .NET? | Main | CSLA .NET 3.6 RC1 available »

One of the topic areas I get asked about frequently is authorization. Specifically role-based authorization as supported by .NET, and how to make that work in the "real world".

I get asked about this because CSLA .NET (for Windows and Silverlight) follow the standard role-based .NET model. In fact, CSLA .NET rests directly on the existing .NET infrastructure.

So what's the problem? Why doesn't the role-based model work in the real world?

First off, it is important to realize that it does work for some scenarios. It isn't bad for course-grained models where users are authorized at the page or form level. ASP.NET directly uses this model for its authorization, and many people are happy with that.

But it doesn't match the requirements of a lot of organizations in my experience. Many organizations have a slightly more complex structure that provides better administrative control and manageability.

image

Whether a user can get to a page/form, or can view a property or edit a property is often controlled by a permission, not a role. In other words, users are in roles, and a role is essentially a permission set: a list of permissions the role has (or doesn't have).

This doesn't map real well into the .NET IPrincipal interface, which only exposes an IsInRole() method. Finding out if the user is in a role isn't particularly useful, because the application really needs to call some sort of HasPermission() method.

In my view the answer is relatively simple.

The first step is understanding that there are two concerns here: the administrative issues, and the runtime issues.

At administration time the concepts of "user", "role" and "permission" are all important. Admins will associate permissions with roles, and roles with users. This gives them the kind of control and manageability they require.

At runtime, when the user is actually using the application, the roles are entirely meaningless. However, if you consider that IsInRole() can be thought of as "HasPermission()", then there's a solution. When you load the .NET principal with a list of "roles", you really load it with a list of permissions. So when your application asks "IsInRole()", it does it like this:

bool result = currentPrincipal.IsInRole(requiredPermission);

Notice that I am "misusing" the IsInRole() method by passing in the name of a permission, not the name of a role. But that's ok, assuming that I've loaded my principal object with a list of permissions instead of a list of roles. Remember, the IsInRole() method typically does nothing more than determine whether the string parameter value is in a list of known values. It doesn't really matter if that list of values are "roles" or "permissions".

And since, at runtime, no one cares about roles at all, there's no sense loading them into memory. This means the list of "roles" can instead be a list of "permissions".

The great thing is that many people store their users, roles and permissions in some sort of relational store (like SQL Server). In that case it is a simple JOIN statement to retrieve all permissions for a user, merging all the user's roles together to get that list, and not returning the actual role values at all (because they are only useful at admin time).


Wednesday, December 03, 2008 12:55:02 PM (Central Standard Time, UTC-06:00)
Rocky -

I think it's healthy to do a little re-examination of authorization, but I'd like to throw one more wrinkle in here -- many of the auth requirements I've seen are dependent on current object context.

As an example, I'll use an imaginary form of the project tracker app. Imagine that you've got the current role of "Project Manager", and that role might let you create a project.

To edit a project, however, you have to be the project manager *for the given project*. There are all sorts of variations for how you might be assigned that context-specific role, but let's just say that there's an admin screen somewhere where a PM is assigned to a particular project.

Now, given the limited functionality in the project tracker app, you're probably just about covered here -- whenever you call for a Project object, you get it and then check auth on that object after it's instantiated. This is a minor inconvenience, since it must happen outside the current static Auth methods, but it's not unworkable.

If you scale up the problem, though, in scope and scale, you can see some stress points. Let's say you're talking about a CRM system where you have to have some permission to view contacts, and that permission takes the form of CanViewCustomer(CustomerID). Now, you start to run into scaling issues because your DB queries are either bringing back all sorts of records that you're instantiating and then rejecting because the user isn't supposed to see them, or you have to start to incorporate this auth scheme into your data access.

Search would be another place where stuff starts to get messy - you don't want to show the user search results that the user cannot access (yes, I know that requirements will vary here, but I've seen this example, so I'm going to toss it out here).

This is one of the areas where I was a little disappointed in MS when they rolled out Zermatt - they left a pretty big hunk of the problem on the table, IMO. I think that a custom Principal could be modified relatively easily such that it carried an Access Control List (ACL) for roles, and this would go a long way toward helping with this problem, but it would be really, really nice to see this sort of concept become more globally available - ie, the ACLs are carried down into the DB such that I'm not bringing back thousands of records that I expect to immediately throw away.

I'd be interested to know how many other people out there have seen variations on this problem.

Wednesday, December 03, 2008 1:14:43 PM (Central Standard Time, UTC-06:00)
I am not a big fan of the role based authorization concepts put out by Microsoft to date. Simple role based systems like they propose suffer from 2 major flaws: 1. They don’t consider a user across different applications (so if I am a Manager in Active Directory, then I have to be considered a Manager in all Role based applications and 2) they do not allow for granular entity level access (like enabling/disabling buttons, etc) very easily.
Rich
Wednesday, December 03, 2008 1:20:31 PM (Central Standard Time, UTC-06:00)
I think I have a similar problem: I need authorization per instance instead of per object. I have a flickr-like web-application, where the access to images is granted per user. That means that the user that uploads the image decides individually which other users can view/comment/delete the image. Something like this would be nice:

result = currentPrincipal.IsInRole(requiredPermission, image);
JohnM
Wednesday, December 03, 2008 1:20:34 PM (Central Standard Time, UTC-06:00)
This is interesting, I am currently developing a security platform, for a large enterprise. We struggle not only with "Role Explosion" but also the problems of using context in an intelligent way. Ultimatly you want to decouple the security "policy" from the code as much as possible. Standards exist to help solve these problems, the two we are using are the RBAC standard, and the eXtensible Access Control Markup Language (XACML) which allow you to merge the two into a pretty pragmatic and usful authorization engine.

The XCAML standard allows for you to basically build policies which are rule-sets, collection of rules for access control, along with instance specific references through a generic policy. XACML is a full blown standard and has many implementations out there (Sun, IBM, and Oracle) to name a few, microsoft has yet to leverage this yet, maybe in the next 5 years.
Thursday, December 04, 2008 1:36:23 AM (Central Standard Time, UTC-06:00)
this violates the "principle of least surprise". If I have a code statement that says "I am checking whether a principal is in a given role" but in reality I am checking whether the principal has a given permission! Confusing... would be better to extend the IPrincipal interface
Gabriel Schenker
Thursday, December 04, 2008 8:20:52 AM (Central Standard Time, UTC-06:00)
Gabriel, I don't disagree that this is an imperfect solution. However, extending IPrincipal isn't terribly practical in all cases.

1. All code interacting with the principal becomes complicated (casting is required)

2. Your custom method won't work with ASP.NET

3. Your custom method won't work with CAS

4. Your custom method won't work with the .NET security attributes

On the positive side, I did enhance CSLA a few years ago so it is possible for you to write a bit of code so CSLA can use your custom method. So it is true that extending your principal with a HasPermission() method can work very nicely with CSLA - assuming you are willing to live with the drawbacks I've listed above.
Thursday, December 04, 2008 3:48:06 PM (Central Standard Time, UTC-06:00)
A similar issue I've run into is that many business and military clients tend to think of roles along with an organizational structure. For example, it's not just "User XYZ is a Manager"- it's "User XYZ is a Manager of Store #123" or even more complex- "User XYZ is a Manager of the Eastern Region, which contains Store #123". Developers want to do things like:

Roles.IsInRole("Manager",requestedStoreId) 'Indicates whether the current user is a manager of a given store
FormList.GetForUserRole(userName, "Manager") 'Returns forms for stores for which the user is a manager

Anybody else seen this? Does it indicate some misunderstanding of role-based security?
Daniel
Friday, December 05, 2008 1:20:22 AM (Central Standard Time, UTC-06:00)
You can minimise the casting code by returning a CustomPrinciple (your extended Principal with for example permissions) from Security context...

Public Module SecurityContext

Public Property User() As CustomPrincipal
Get
If HttpContext.Current Is Nothing Then
Return CType(Thread.CurrentPrincipal, CustomPrincipal
Else
Return CType(HttpContext.Current.User, CustomPrincipal
End If
End Get
Set(ByVal value As CustomPrincipal
If HttpContext.Current IsNot Nothing Then
HttpContext.Current.User = value
End If
Thread.CurrentPrincipal = value
End Set
End Property

End Module

Then in your application you can use...

If not YourDomain.SecurityContext.User.HasPermission("AccessSecurityDetails") Then Response.Redirect("login.aspx")

Ian Gibson
Tuesday, December 09, 2008 2:33:32 PM (Central Standard Time, UTC-06:00)
This is interesting topic indeed, me and my team have recently released Generic Security Subsystem that's completely based on CSLA .NET and that deals with this problem domain around authorization mechanisms that involve roles as Rocky mentioned.

Our goal was to decouple security system authorization authority out of business logic (which resides on CSLA .NET as well) in a way that all authorization decisions are determined outside of business logic and that security subsystem is unaware of actual business objects at all. That being said, the same security subsystem can be used for variety of business solutions (even those that doesn't use CSLA .NET).

Briefly what security subsystem supports is registration of business objects and their instances as Resources or User Groups. Resources can be Document, Folder and other BOs, User Groups are User, User Role, Department and so on.
Additionally security subsystem mainains relations between various User Groups and also relations between various Resources meaning that Department, User Role and User are in relation as User Groups, as well as Folder and Document are in relation as Resources. This is what business logic feeds into security subsystem.

What left are Permissions and they're defined by business logic, but maintained by security subsystem. Therefore, business logic will specify that permission "Annotate Document" will exist but it's gonna be mainained by security subsystem through User Group Permission Sets where all permissions are specified for particular User Group and Resource.

Then there's complex calculation done through inheritance both on User Groups and Resources based on their relations. Now business logic only requires something like this:

Resource.CanAccess(CurrentUserID, (int)DocumentAccessPointCode.UpdateCommonAttributes)

Where Resource is object within Generic Security Subsystem and DocumentAccessPointCode.UpdateCommonAttributes specifies access point - part of resource that user is trying to access. From that point on, security subsystem calculates all permissions that this user accumulates through all User Groups he's member of (directly or indirectly) through relations that business logic defined and all required permissions that particular access point demands (directly or indirectly) through inheritance relations that business logic defined.

As far as IPrincipal model goes, here's how we get CurrentUserID from above:

((DocManExpressUserPrincipal)Thread.CurrentPrincipal).DocManExpressUserIdentity.SafeUserID;

So yes Rocky, you need to cast a lot when you've got specific IPrincipal, but specific IPrincipal implementation here was done because of totally different goal and that is to broad Windows authentication so that something between Windows authenticaiton and CSLA authentication can be supported.

What's acomplished is similar authorization strategy as Windows ACL model uses but with more typed permissions and object model related toward business problem domain.

Once again, even though Generic Security Subsystem is decoupled off the business logic, it uses CSLA .NET and it's got own data access routines and even it's own storage.

--- Milan Simic
Milan Simic
Tuesday, December 30, 2008 8:26:51 PM (Central Standard Time, UTC-06:00)
You guys all present great points. I am working on a web-based Business App, that requires Permission based Security. In my years in the industry, I've worked mostly on Roled-based Security but now require a rights-based system for the new app.

Initially, my thoughts were around using Role based security then applying a trimming or granular level based on the user's initial role. So the system is a hybrid of Role (initially) and then Rights (as permitted by an Admin).

An admin or owner of the business could assign his roles but then go across the roles to include addition permissions for the user. Using CMS roles as an example, a user can be in an editor role who can create/view/edit/delete content pages but can also be given workflow rights. The editor could be allowed to approve content or even publish content.

Conversely, an editor of the CMS can effectively be downgraded to a simple read-only user of the CMS by having his/her rights reduced.

From a code perspective, you are still using the ASP.NET role/membership provider (or custom), but using a custom principal to achieve the granularity required. The custom principal can implement a HasRight method, which is connected to an XML or SQL table with user-mapped rights.

In fact, I've been thinking about implementing this HasRight Method in a base Controller class or a base web forms class.
Thoughts around this idea?
Andrew Olowu
Monday, January 19, 2009 7:37:27 PM (Central Standard Time, UTC-06:00)
Dear Rocky
I have used c#2005 experts objects in my real projects . I think your csla.net be a good Framework which I love very much .
I now have an aporia that i want to use Jquery and csla.net in my next projects. Would you give me some message that how can I join them as a whole body ?
Also ,so far as Data binding is concerned, I want to use HTML controls instead of asp.net controls which run at server side. I attempt to reduce the burden in server side which can let my applicatuon run easier .
I don't know what I said is appropriate or not, would you do me a favor
giving me some idea about upper?
thinks

Yours
LZ

LZ
Comments are closed.