Rhino Tools: Rhino Security Guide
In the post I want to discuss basics of Rhino Security which was developed by Ayende. This is a nice security model implementation that could be easily integrated and adapted in many applications and scenarios. Intention of the post is to give good startup for Rhino Security. I will try to follow KISS (Keep It Simple) in my explanations, yes, I know that “It's All Relative” :) and also keep to minimum required infrastructure for the startup (mainly only required). Rhino Security is a part of Rhino tools, which is a set of already mature reusable classes and tools that cover various scenarios, such as well known:
1.Rhino Mocks - mocking framework
2.Rhino Service bus – enterprise service bus implementation
3.Rhino Common – set of reusable classes (working with threads, IoC, helpful Http Modules, ect..)
4.Rhino ETL (Extract Transform Load) – library that allows moving data in various formats.
5. Rhino Queues - queuing service that queues over the Internet
6. Rhino DHT - Rhino Persistent Hash Table
7.And many other goodies.
Rhino Security in my opinion is less popular then other Rhino stuff, one reason could be that is on web is not so much information. That is why I want to put the basics of Rhino Security and show how easy you can setup working application based on Rhino Security(by “easy” I mean that is much easier to setup rhino security then to try to implement at least a little part of it).
Rhino Security implementation is not very huge in terms of code lines, but is based on many other set of classes. I will describe them shortly later. Rhino Security as a persistence mechanism uses NHibernate that allows you very easy to integrate with many RDBMS types. To more precise Rhino Security could be configured with both: Castle Active Record and NHibernate. Castle Active Record is Active Record pattern implementation which behind the scene uses NHibernate (mmm,… yes,…it is not only Active Record pattern implementation, it has much more other features than are described by classic Active Record pattern). I’ve chosen to use NHibernate because it is more popular then Castle Active Record (Castle AR) and other reason is that Castle AR can’t be used without NHibernate but NHibernate without Castle AR can :).
Let’s do first steps and get all the Rhino bits from SVN repository, here is the SVN link:
https://rhino-tools.svn.sourceforge.net/svnroot/rhino-tools/trunk/in order to Check out the repository you can use any of SVN clients, there are many free:
1)http://tortoisesvn.net/about - easy to use and is integrated in windows explorer
2)SmartSVN – the SVN client has two versions, free and more limited and professional version, I like the client and I use it in day by day work, and even its free version is powerful enough to handle various scenarios.
I will not dive deep in “How To checkout” details, so the result of checking out from the trunk is on the next image. On the image is the last trunk at the moment, 2077. Rhino Security is in “security” folder, but we will not touch it now. In the root folder are “readme” files for some additional information. Also in the root folder are various NAnt and bat files that will invoke NAnt and build entire projects tree. In order to build a little bit faster run “build_without_tests.cmd” file. After a black screen and a lot of blinking lines will be generated “build” folder that will contain all the generated assemblies for all Rhino projects and dependencies.
If you have made all the steps successfully then let’s start and setup first Rhino Security project, if not then all required assemblies and project sample are in attached zip file.
So, create a new folder where we will set our new project. Then in the root of just created folder create a new folder “libs” where we will place all required assembly references and dependencies.
Now, copy following 20 assemblies from Rhino’s generated “build” folder to “libs” folder:
Then open visual studio and create a new console project, name the project “RhinoSecurity”, and place it in the created root folder (on the same level as “libs” folder).
In just created project reference following assemblies from our “libs” folder:
NHibernate.ByteCode.Castle.dll – assembly is not used directly from our code, but we reference it because it is loaded runtime and should be copied to the output bin folder. The assembly contains implementation for three NHibernate interfaces which are used to generate in memory proxy classes for our POCO mapped classes such as User entity which will be discussed further (that is why we need to set mapped properties as virtual). In this case is used Castle but you can find other implementations.
Now we have a good base to start type our logic. But, before to dive in implementation details, let’s discuss few Rhino Security types that can help us to integrate Rhino Security with our domain model. Most of the systems nowadays have User entity in the domain model, the User entity can have various proprieties that are specific for each different domains. Rhino Security allows attaching the security implementation to any User entity with minimal changes to the existent User entity, but how Rhino Security knows which entity from our model represents the User? For that is responsible Rhino.Security.IUser interface that marks the User entity, the only member of the interface which should be implemented is SecurityInfo member.
Here is typical implementation of the interface which fits in many common user implementations:
public class User : IUser
{
private long id;
private string name;
public virtual long Id
{
get { return id; }
set { id = value; }
}
public virtual string Name
{
get { return name; }
set { name = value; }
}
/// <summary>
/// Gets or sets the security info for this user
/// </summary>
/// <value>The security info.</value>
public virtual SecurityInfo SecurityInfo
{
get { return new SecurityInfo(name, id); }
}
}
The Id property is usually identity of the User entity (which not necessary should be a long). SecurityInfo property is used by Rhino Security to attach our user entity to Rhino Security implementation which is used internally by the framework to manage authorization logic for the User. [Mapping file for the User entity and NHibernate configuration are in the provided source code sample] Now I’ll try to describe main Rhino Security model entities from my point of view, which we will explore lately in practice:
-UserGroup – is a user group defined by its name, it’s used to associate users to the group and define common permissions for users that belong to the group. The groups can be structured hierarchically in parent/child relationship. In some cases you can think about users group in terms of Roles which have set of permissions ex: Administrator, Guest, etc…
- Operation – a named operation that also can be structured hierarchically using following convention: “/Content/View”, “/Content/Edit” etc… so if we create a new “/Content/Edit” operation then are created two operations “/Content” as parent and “/Content/Edit” as child. Further you can allow or deny the operation for a User or for a UserGroup. When an operation is allowed or denied for a User, UserGroup, etc… also could be specified a level, levels defines importance of the permission, but very often is used Default Level which is equal to 1. Permission with higher level is more dominant in taking decision of operation allowance. For example: if we deny operation “/Content/View” with default level for a user and after that allow for the same operation but with level 9 then finally the operation will be allowed for the user.
-Permission- is result of allow or deny process for an operation (e.g. “/Content/View”) for a User or UserGroup (or EntityGroups).
-User – represent any entity that implements IUser interface, we already covered it above…
I didn’t cover here EntityGroups, IEntityInformationExtractor, Query Permissions,.. maybe I will cover them in the next posts because I want to concentrate on the basic and most common things, here I only want to mention that they exists and you can use them to associate permissions for any entity that implements IEntityInformationExtractor interface.
For a completeness of the entire image I want to show Rhino Security Data Model and to comment it:
All tables which are prefixed with “security_” are related to directly Rhino Security, Users table is used to persist User entity.
Again, tables from red region I won’t touch now because they are related to Entities, shortly, the tables from red region are used to associate permission for entities like Accounts from a given EntitiesGroups or to entities directly (omitting EntitiesGroups) that implements IEntityInformationExtractor which are related by SecurityKey (Accounts.SecurityKey with security_EntityReferences.EntitySecurityKey).
So, from the above diagram we can see that we can allow or deny operations by permission for Users or UserGroups. A User can belong to one or more UserGroups. Operations and UserGroups can be structured hierarchically in parent\child relationship. That’s it... Nothing complicated, is it?
Back to Code:
So, let’s initialize Rhino Security on order to make it work.
First of all we need to initialize Windsor Inversion of Control Container and add at least two Windsor facilities:
1) NHibernateUnitOfWorkFacility2) RhinoSecurityFacility
we can do it programmatically or using configuration file, here is programmatic version://Create container
container = new WindsorContainer();
//Register Unit Of Work Facility instance
container.Kernel.AddFacility("nh", new NHibernateUnitOfWorkFacility());
//Regiser Rhino Security Facility instance:
//1) provide Rhino’s DB table naming convetion:
// Prefix '_' ex: security_Permissions or
// Schema '.'(is default). ex: security.Permissions
//2) provide User Type that implements IUser
container.Kernel.AddFacility("security",
new RhinoSecurityFacility(SecurityTableStructure.Prefix, typeof (User)));
All code explanations are in the comments, only what I want to add is that we need to run the code once in application startup.
The configurations could be setup in few ways, and most of them which I saw were using Rhino Bindsor which is Domain Specific Language written in Boo that allows configuring Castle Windsor Inversion of Control container without using any xml tag. Maybe some of you already noted I didn’t go with Boo way, because it can look as more “exotic” way for some folks.
Next, we need to resolve Rhino Security repository\services\facades that will allow us to manage and query Rhino Security model:
//The repository is main "player" that allows to manage security model
authorizationRepository = IoC.Resolve<IAuthorizationRepository>();
//The service provides authorization information
authorizationService = IoC.Resolve<IAuthorizationService>();
//Provide a fluent interface that can be used to assign permissions
permissionsBuilderService = IoC.Resolve<IPermissionsBuilderService>();
//Allow to retrieve and remove permissions
//on users, user groups, entities groups and entities.
permissionService = IoC.Resolve<IPermissionsService>();
Now, let’s create a transient User entity and save it to DB.
//Create a User
var userArt = new User { Name = ("ArturTrosin") };
UnitOfWork.CurrentSession.Save(userArt);
The user entity we will use to operate with. Then create two groups, child and parent:
//Create "AdminUserGroup" UsersGroup
authorizationRepository.CreateUsersGroup("AdminUserGroup");
//Create Child group for "AdminUserGroup"
authorizationRepository.CreateChildUserGroupOf("AdminUserGroup", "GuestUserGroup");
Then create three operations:
//Create two operations: root /Content operation
// and its child /Content/Manage operation
authorizationRepository.CreateOperation("/Content/Manage");
UnitOfWork.Current.TransactionalFlush();
//Create third operation as child of the /Content
authorizationRepository.CreateOperation("/Content/View");
Associate user with created group:
//add user to "AdminUserGroup", so all further permissions for "AdminUserGroup"
//group are aslo applied for the user also
authorizationRepository.AssociateUserWith(userArt, "AdminUserGroup");
Here is how we can associate two permissions for a user group or a user:
//Create Permission using Builder Pattern
//and its fluent interface:
permissionsBuilderService
//Allow "/Content" Operation
.Allow("/Content")
//for "AdminUserGroup"
.For("AdminUserGroup")
//Could be specified On an entityGroup
//or an entity that implemented IEntityInformationExtractor
.OnEverything()
//Here could be specified Permission priority Level
//DefaultLevel is equal with 1
.DefaultLevel()
.Save();
//Create Deny permission for user with level 5
permissionsBuilderService
.Deny("/Content/View")
.For(userArt)
.OnEverything()
.Level(5)
.Save();
//Ask users allowance for an Operation
bool isAllowedContentOp = authorizationService.IsAllowed(userArt, "/Content");
bool isAllowedContentManageOp = authorizationService.IsAllowed(userArt, "/Content/Manage");
//Retrieve Rhino Security entities
UsersGroup adminUsersGroupWithoutUser = authorizationRepository.GetUsersGroupByName("AdminUserGroup");
Operation contentViewOp = authorizationRepository.GetOperationByName("/Content/View");
Permission[] userArtPermission = permissionService.GetPermissionsFor(userArt);
//Retrieve athorization info that can help to
//understand reason of allowance (or not) of an operation
//its very helpful for debuging
AuthorizationInformation authInfo = authorizationService
.GetAuthorizationInformation(userArt, "/Content");
//Cleanup created entities
authorizationRepository.RemoveOperation("/Content/Manage");
authorizationRepository.RemoveOperation("/Content/View");
authorizationRepository.RemoveOperation("/Content");
//Remove child group first
authorizationRepository.RemoveUsersGroup("GuestUserGroup");
authorizationRepository.RemoveUsersGroup("AdminUserGroup");
authorizationRepository.RemoveUser(userArt);
UnitOfWork.CurrentSession.Delete(userArt);
Link to full source code and libs is here.
Note that I didn’t show code of UoW Flush method calls, which persists changes to DB. And of course in order to run the code you need a database with Rhino Security DB schema in it similar to provided earlier, so there is a method which will do all the dirty work for you:
new DbSchema().Generate(container);
The only thing that you should care is to create an empty DB (named Security_Test) and verify if connection string from hibernate.cfg.xml file is set properly.
Conclusion
Even Rhinos tools could be not compatible between SVN versions; However most of the Rhino bits are used already in production environments and are very popular with a great community support.
So, I would recommend taking Rhino Security in account if you already use NHibernate and if you need a similar model or your required model could be expressed with Rhino Security model.
Notice: The code is tested with VS 2008 sp1 and Sql server 2005.
Thank you,
Artur Trosin