Tags: , , | Categories: Articles Posted by RTomlinson on 7/30/2009 12:12 AM | Comments (13)

Originally this post was going to be titled "Part 2: Integrating Rhino Security" but upon reflection this wasn't what I was trying to acheive at all. This has been covered enough to get you started and if you need to know how to integrate Rhino Security into your ASP.NET WebForms project then read this post by Billy McCafferty. What I do want to discuss is the concept of Rhino Security, its simplicity, adaptability and extensibility and why it is "the shit".

Brief Overview Of Rhino Security Architecture

Rhino security is an authorization framework that is easily integrated into existing .Net applications to provide a security framework that covers a wide range of authorization scenarios from the simple to the complex. In essence, this boils down to permitting or denying a user, or group of users, from carrying out an operation.

Ayende implements operations using the following notation:

  • /Account/Financial/View
  • /Account/Financial/Edit
  • /Account/Financial/Delete
  • /Case/Assign
  • /Employee

The major benefit of this implementation is that the interpretation of the operations is completely up to the developer. I'll give you an example to show you what I mean. In part 1 I outlined that in our application there is a requirement to implement page level permissions. In order to implement this (I will explain the actual page level permissions implementation in the next post) I prefix all page level operations with '/Page/' and append this with the URL of that given page, such as:

  • /Page/Configuration/RolesPermissions/Default
  • /Page/Admin/Users/AddUser

You can start to see that the operations format gives a huge amount of flexibility and scope for the developer to implement page level, object level and feature level permissions without dictating implementation.

Part 1 also described the requirement that permissions must be based on both users and groups. Rhino Security supports this notion by associating permissions with both users and users groups (see schema below).

Rhino Security Schema

Finally Rhino Security has the concept of permission levels. It isn't completely clear from the documentation as to the intended use of the level, although it is clear that higher level override permissions of a lower level. However, I see this as a massive benefit as Ayende leaves the thinking and implementation up to the developer. For example in my implementation I give inherited user groups a higher level than their parent. So given the scenario where you may have a 'Developer' role (users group) which would have associated permissions. You may have another 'Senior Developer' role that is a child of the 'Developer' role. In my implementation the 'Senior Developer' permissions set should have a greater weighting than it's parent as it is more specific. The 'Developer' would therefore have a level of 1 and the 'Senior Developer' will have a level of 2. Again...just to reiterate this is simply my implementation and by no means a restriction of the framework.

Authorization with Rhino Security

Rather than describe the Repositories and Services to get started with Rhino Security as they are better described here, I will run you through a use case and describe the process.

Say, for example, that we have two users, one is called Bill and he is a Senior Developer and another user called Tom and he is a Junior Developer. Our application must only allow Senior Developers to add new companies to the system.

Given that Rhino Security is integrated into our system, whereby we already have a User table to which Bill and Tom are registered in, the first thing we must do is create our UsersGroups. The way I'm going to do this is to create a parent UsersGroup for Developers, to which Junior and Senior developer roles will inherit from.

_authRepository = ResolveType.Of<IAuthorizationRepository>();

_authRepository.CreateUsersGroup("Developer");
_authRepository.CreateChildUserGroupOf("Developer", "Junior Developer");
_authRepository.CreateChildUserGroupOf("Developer", "Senior Developer");

Now that we have created the UsersGroups we can add both Bill and Tom to the relevant roles:

IUser userA = _userRepository.GetUser("Bill");
IUser userB = _userRepository.GetUser("Tom");


_authRepository.AssociateUserWith(userA, "Senior Developer"); _authRepository.AssociateUserWith(userB, "Junior Developer");

We can now create the operations that we wish to be authorized against. In this use case we are wanting to restrict access at an object level and will therefore use the convention of /ObjectName/Operation. Therefore we will use /Company/Add.

_authRepository.CreateOperation("/Company/Add");

The IPermissionsBuilderService tie's all of this together to associate the UsersGroups with a particular operation and at a specified level. As you can see from the example below this is implemented using a fluent interface.

private void GrantPermission(string operation, bool allowed, UsersGroup usersgroup)
{
    if (allowed)
    {
        _permissionBuilderService
            .Allow(operation)
            .For(_usersGroup)
            .OnEverything()
            .Level(usersgroup.GroupLevel())
            .Save();
    }
    else
    {
        _permissionBuilderService
            .Deny(operation)
            .For(_usersGroup)
            .OnEverything()
            .Level(usersgroup.GroupLevel())
            .Save();
     }
}

GrantPermission("/Company/Add", false, _authRepository.GetUsersGroupByName("Developer"));
GrantPermission("/Company/Add", true, _authRepository.GetUsersGroupByName("Senior Developer"));

If you are wondering what the GroupLevel() method is or does, this is not part of the UsersGroup object out-of-the-box, I wrote this extension method to implement permission levels, so that all permissions are registered above those of their parents. In our use case, the Senior Developer role is a child group of the Developer UsersGroup and as a result the Senior Developer permissions will be at a level above it's parent. This may make more sense if I show you the extension method.

public static int GroupLevel(this UsersGroup usersGroup)
{
    return usersGroup.AllParents.Count + 1;
}

At this point we pretty much have our use case covered. In summary, Bill and Tom are Developers, Bill is a Senior Dev and Tom a Junior. Bill, as a Senior Dev, is permitted to add companies to the application, Tom as a Junior Dev is not permitted as his parent role prevents him from doing so.

Given that the plumbing is now sorted we can enforce authorization using the bool IAuthorizationService.IsAllowed(IUser user, string operation) method.

// Returns true
bool canBillAddCompany = _authRepository.IsAllowed(userA, "/Company/Add");
// Returns false
bool canTomAddCompany = _authRepository.IsAllowed(userB, "/Company/Add");

 

Conclusion

In reality what I have covered here will be hidden behind some kind of UI or service. This will be covered in later posts. In my next post I will be detailing how to implement a dynamic permissions discovery framework on top of Rhino Security to automate the registration of permission.

 

Share or Bookmark this post…
  • del.icio.us
  • Digg
  • DotNetKicks
  • Facebook
  • Slashdot
  • StumbleUpon
  • Reddit
  • Technorati
  • TwitThis

Comments

trackback
DotNetKicks.com on 8/13/2009 2:31 AM An Enterprise Authorization Framework: Part 2 - Why Rhino Security is

You've been kicked (a good thing) - Trackback from DotNetKicks.com
sirrocco
sirrocco Romania on 8/13/2009 3:42 AM Mate, thanks a bunch for this presentation Smile - i've been looking for something like this but just didn't find anything nicely explained .

Keep it up and looking forward to your new post.
DannyT
DannyT United Kingdom on 9/3/2009 2:12 AM Thanks for the post, nice explanation.

Would be very interested in your thoughts about the "UI or service" you mention.
anon
anon United States on 11/19/2009 12:54 PM So, why is Rhino Security "shit" ?? You're only saying good things about it..
RTomlinson
RTomlinson United Kingdom on 1/24/2010 8:36 AM Danny - I will post a sample project up for you to see my implementation. Have a final post to write that will tie everything up.

beto
beto United States on 2/3/2010 10:18 AM Are you going to post how to work with entity references, entity types and entity groups . I would really be interested in this.
RTomlinson
RTomlinson United Kingdom on 2/3/2010 7:47 PM @beto: I would like to write a post on entities and entity groups with applied permissions but I'm thinking that this may be too in depth for an overview of Rhino Security. I am putting together an example application for this series and that may help you understand entity types and grouping of entities.
beto
beto United States on 2/10/2010 6:48 AM I would definitely would like to see the example application.  Also, would you be able to have grandchildren roles, i.e. Developer > Jr Developer > Associate Developer, or is it just one level that you can only create children from parent roles, and you can't create children of children roles.  I guess i just need an explanation of how the UsersGroupHierarchy table would work?
Degree451
Degree451 United States on 2/14/2010 5:48 PM Do you know how to remove a permission from a specific user after its been saved? Once I allow or deny a permission I don't see any methods to undo/remove those permissions and since deny permissions take precedence over allow once you've denied you can never go back to allowing.! My UI is just like the Visual Studio Team Explorer security dialog for team projects so it allows the end users to allow or deny operations to specific users/groups.
Paul
Paul United States on 2/24/2010 9:38 AM Ryan,

Very nice series.  I have had a very hard time finding any real documentation besides yours, so it's much appreciated.

One thing that I'm not clear on, that I hoped you might have some ideas on, is how to do instance-level rights on objects.  

So, for example, associate a given user or group with a particular object instance (and operations on that instance).  Is it possible to do that easily?  I am a bit vague still on how this all fits together in a real app, so the only means by which I could think of was to manually create operations with the object's Id in it when the object is created, and then manage permissions around those, but that seemed like it'd get pretty bloated pretty quickly.

Thanks,
Paul
RTomlinson
RTomlinson United Kingdom on 4/14/2010 8:49 PM All,

Apologies for not responding to any of your posts. I have neglected to blog recently. I am still putting together a sample app for this security framework with the little free time that I actually have.

@Paul - Hopefully the sample app will help you out with how it all fits together. Instance level security, as you said, is much more complex.

The main issue being tracking object instances. The instance could generate a GUID that could be stored along with its state to your database. In larger systems expect a lot of data to be generated along with a pretty hefty performance decrease.

It's also heavily dependant on your requirements for operations, i.e. whether your operations are different on an instance level compared to an object level.

Paul
Paul United States on 4/14/2010 10:20 PM thanks for responding, Ryan.

that doesn't sound very encouraging for my particular project, unfortunately.  Yes, the requirement is in fact for users to be able to 'own' instances of objects and have different rights for being the owner vice not being an owner.  

This doesn't strike me as all that unusual a requirement, though.  Consider if you will a regular forum app; a user 'owns' a post and can usually edit or delete an individual post instance.  Moderators have rights over posts as a class, and so on.

Unless I'm reading you wrong, it feels like a gap in the framework if it doesn't account for that kind of thing w/o a 'hefty performance decrease'.
Sharyn Atcher
Sharyn Atcher United States on 5/16/2010 1:59 AM Very well written post, do you have an rss feed I can subscribe to?

Add comment


(Will show your Gravatar icon)

  Country flag

biuquote
  • Comment
  • Preview
Loading