I finally found some thing to write another post on the things I learned from Ayende. This is the third one in the series. This time I will deal with Rhino Security. Ayende showed us what it could do and in my opinion it is the solution to the requirements of our customer.
Today we took some time to do try out some stuff and integrate Rhino Security in our project. While he was in Belgium Ayende added a small console application to test some stuff with Rhino Security. We reused that one to get a feeling how it works.
After you've created a new console application, the first thing you'll need is a windsor.boo file to configure Windsor
1: import System.Reflection
2: import Rhino.Security
3: import Rhino.Security.Interfaces from Rhino.Security
4: import Rhino.Commons from Rhino.Commons.NHibernate as nh
5: import Rhino.Security.Tests from ConsoleApplication1
6: import ConsoleApplication1
7:
8:
9: facility RhinoSecurityFacility:
10: securityTableStructure = SecurityTableStructure.Schema
11: userType = User
12:
13: component INHibernateInitializationAware, NHibernateMappingModifier
14: component INHibernateInitializationAware, CaptureConfiguration
15:
16: component IEntityInformationExtractor of Account, AccountInfromationExtractor
17:
18: component IRepository, NHRepository
19: component IUnitOfWorkFactory, NHibernateUnitOfWorkFactory
The "RhinoSecurityFacility" is the main player here. You need to tell it whether it should use a separate schema to create its tables or just use a prefix. Next to that you also need to tell the facility which class is your "User" class. Rhino Security does not contain a "User" entity and that's a good design decision but of course it needs to know your "User" entity.
"User" needs to implement "IUser" and looks for example like this:
1: public class User : IUser
2: {
3: private long id;
4: private string name;
5:
6: public virtual long Id
7: {
8: get { return id; }
9: set { id = value; }
10: }
11:
12: public virtual string Name
13: {
14: get { return name; }
15: set { name = value; }
16: }
17:
18: /// <summary>
19: /// Gets or sets the security info for this user
20: /// </summary>
21: /// <value>The security info.</value>
22: public SecurityInfo SecurityInfo
23: {
24: get { return new SecurityInfo(name, id); }
25: }
26: }
We also added a simple business entity, being an Account (as in bank account):
1: public class Account
2: {
3: private long id;
4: private Guid securityKey = Guid.NewGuid();
5: private string name;
6:
7: public virtual long Id
8: {
9: get { return id; }
10: set { id = value; }
11: }
12:
13: public virtual Guid SecurityKey
14: {
15: get { return securityKey; }
16: set { securityKey = value; }
17: }
18:
19: public virtual string Name
20: {
21: get { return name; }
22: set { name = value; }
23: }
24: }
Let's get started now. First we need to get the database schema in place. That's something NHibernate can do for you of course:
Setup
1: static void Main(string[] args)
2: {
3: var container = new RhinoContainer("Windsor.boo");
4: IoC.Initialize(container);
5: using(UnitOfWork.Start())
6: {
7: new SchemaExport(CaptureConfiguration.cfg).Create(true, true);
8:
9: UnitOfWork.Current.TransactionalFlush();
10: }
11: }
where CaptureConfiguration looks like this:
1: public class CaptureConfiguration : INHibernateInitializationAware
2: {
3: public static Configuration cfg;
4: public void BeforeInitialization()
5: {
6:
7: }
8:
9: public void Configured(Configuration cfg)
10: {
11: }
12:
13: public void Initialized(Configuration cfg, ISessionFactory sessionFactory)
14: {
15: CaptureConfiguration.cfg = cfg;
16: }
17: }
Ok, when that's done, we can really start with defining things. Let's start with creating two operations: /Account/Delete and Account/Name. In fact the last one will be used to define security on the property "Name" of the "Account" entity.
Define operations
1: static void Main(string[] args)
2: {
3: var container = new RhinoContainer("Windsor.boo");
4: IoC.Initialize(container);
5: using(UnitOfWork.Start())
6: {
7: var authRepos = container.Resolve<IAuthorizationRepository>();
8: authRepos.CreateOperation("/Account/Delete");
9: UnitOfWork.Current.TransactionalFlush();
10: authRepos.CreateOperation("/Account/Name");
11: UnitOfWork.Current.TransactionalFlush();
12: }
13: }
btw, when you create the operation "/Account/Delete", Rhino Security will automatically create the parent "Account" operation. You don't need to do that yourself.
To continue the exercise I need a entity group: Strategic Partners and a user group Management.
Define user groups and entity groups
1: static void Main(string[] args)
2: {
3: var container = new RhinoContainer("Windsor.boo");
4: IoC.Initialize(container);
5: using(UnitOfWork.Start())
6: {
7: var authRepos = container.Resolve<IAuthorizationRepository>();
8: authRepos.CreateUsersGroup("Management");
9: authRepos.CreateEntitiesGroup("Strategic Partners");
10: UnitOfWork.Current.TransactionalFlush();
11: }
12: }
Define permissions
1: static void Main(string[] args)
2: {
3: var container = new RhinoContainer("Windsor.boo");
4: IoC.Initialize(container);
5: using(UnitOfWork.Start())
6: {
7: var authRepos = container.Resolve<IAuthorizationRepository>();
8: var permissionsBuilderService = container.Resolve<IPermissionsBuilderService>();
9:
10: Account account = Repository<Account>.FindFirst();
11: User user = Repository<User>.FindFirst();
12: permissionsBuilderService
13: .Deny("/Account/Name")
14: .For(user)
15: .On("Strategic Partners")
16: .Level(10)
17: .Save();
18:
19: UnitOfWork.Current.TransactionalFlush();
20:
21: permissionsBuilderService
22: .Allow("/Account/Delete")
23: .For("Management")
24: .On<Account>()
25: .Level(20)
26: .Save();
27:
28: UnitOfWork.Current.TransactionalFlush();
29: }
In the first permission we tell the system that we deny access to the property "Name" of "Account" for the specific user instance but only when it concerns accounts of "Strategic Partners". In the second permission we define that the usergroup "Management" can delete accounts. You immediately see that you can create an extremely flexible security model with this. The only thing we still need to do is to create some sort of user interface so that an administrator can setup these rules.
Check permission
Now we are at the point where can actually verify the set permissions. The cool thing there is that you also ask "Why" something is allowed or not:
1: static void Main(string[] args)
2: {
3: var container = new RhinoContainer("Windsor.boo");
4: IoC.Initialize(container);
5: using(UnitOfWork.Start())
6: {
7: Account account = Repository<Account>.FindFirst();
8: User user = Repository<User>.FindFirst();
9: var auth = container.Resolve<IAuthorizationService>();
10: Console.WriteLine(auth.IsAllowed(user, account, "/Account/Delete"));
11: Console.WriteLine(auth.GetAuthorizationInformation(user, account, "/Account/Delete"));
12: }
13: }
And the result looks like this:
False
Permission for operation '/Account/Delete' was not granted to user 'Bart' or to
the groups 'Bart' is associated with ('not assoicated with any group') on 'Accou
nt: my account' or any of the groups 'Account: my account' is associated with ('
not assoicated with any group')
To get the information above, you have to give the system some info about "Account".
1: public class AccountInfromationExtractor : IEntityInformationExtractor<Account>
2: {
3: private readonly IRepository<Account> accountsRepository;
4:
5: public AccountInfromationExtractor(IRepository<Account> account)
6: {
7: this.accountsRepository = account;
8: }
9:
10: public Guid GetSecurityKeyFor(Account entity)
11: {
12: return entity.SecurityKey;
13: }
14:
15: public string GetDescription(Guid securityKey)
16: {
17: Account account = accountsRepository.FindOne(Expression.Eq("SecurityKey", securityKey));
18: return string.Format("Account: {0}", account.Name);
19: }
20:
21: /// <summary>
22: /// Gets the name of the security key property.
23: /// </summary>
24: /// <value>The name of the security key property.</value>
25: public string SecurityKeyPropertyName
26: {
27: get { return "SecurityKey"; }
28: }
29: }
One last thing. You can also add security to a query.
1: static void Main(string[] args)
2: {
3: var container = new RhinoContainer("Windsor.boo");
4: IoC.Initialize(container);
5: using(UnitOfWork.Start())
6: {
7: Account account = Repository<Account>.FindFirst();
8: User user = Repository<User>.FindFirst();
9: var auth = container.Resolve<IAuthorizationService>();
10: DetachedCriteria criteria = DetachedCriteria.For<Account>();
11: auth.AddPermissionsToQuery(user, "/Account/Edit", criteria);
12: Repository<Account>.FindAll(criteria);
13: }
14: }
Rhino Security will automatically add security to the query and it will generate a query like the one you see in this post from Ayende.
I think this should get you started with Rhino Security. In the next post I'll discuss how you can integrate this with ASP.NET MVC. That's in fact really easy.

17 comments:
Are you going to post the Rhino Security + ASP.Net MVC anytime soon?
That was the plan but holidays came in between.
The plan is still alive and if you have a specific question I'll try to address it in my post.
I'm trying to decide if I want to use Rhino Security in my ASP.Net MVC project verses rolling my own. I had planned on using Linq to SQL instead of NHibernate for persistence. It appears that Rhino Security depends on hibernate though.
If I had the choice I would not roll my own. There's so much functionality already available in NHibernate and Rhino Security. In my opinion it would take a considerable amount of time doing the same thing in Linq2SQL.
Hello,
Can you please continue and show in an another post how did you make Rhino Security + ASP.NET MVC work together?
I am more than interested on seeing this in conjuction with Rhino.Tools boo configuration.
Thanks,
Robert
I will put it on my to do list and since several people are asking this it will get priority. I hope to find some time this week to post about it.
I did a follow up post on Rhino.Security in combination with ASP.NET MVC. You can read about it here
hi
>
> Do we need to add tables in the rhino security schema for
> each and every entity we want to add to entity group ??? or
> the entities would be picked from application schema only
> ???? How does the rhino security interact with the entities
> present in application schema(say in sharparch . core )?
> The examples given always show a coding line as
>
> Account account =
> Repository(Account).FindFirst();
>
> User user = Repository(User>)FindFirst();
> var auth =
> container.Resolve(IAuthorizationService)();
> Console.WriteLine(auth.IsAllowed(user,
> account, "/Account/Delete"));
> Console.WriteLine(auth.GetAuthorizationInformation(user,
> account, "/Account/Delete"));
>
> Now where is this "Accounts " table ? is it in application
> schema or rhino schema ? n what is the use of
> Repository(Account).FindFirst(); ???
>
> Can u pls send a demo on functionalities like adding
> entities to entity groups ,IEntityInformationextractor
> ? as the data on these things are not very
> elaborartive on the forums ?
Hi,
could you show rhino security example with linq to sql or entity framework. Nhibernate is not professional product, which could be used in real applications.
Hi witiokz,
I don't have an example that uses linq 2 sql or EF.
On your statement that NHibernate is not a professional product I could not disagree more. NHibernate is the most mature ORM in the .NET space that is currently available. I can imagine it is easier to sell EF to your manager because it comes from NHibernate but I'm sure EF is not on the same maturity level as NHibernate.
Hi Bart,
permissionsBuilderService
.Allow("/Account/Delete") .For("Management").On()
is not available as one of the options in my RS assembly. was this suppose to be
permissionsBuilderService
.Allow("/Account/Delete") .For("Management").On(account)
or
permissionsBuilderService
.Allow("/Account/Delete") .For("Management").OnEverything()?
sorry but the angle brakets did not show for the class Account on the On()
Hi Bart, greetings from Atlanta. I was reading your post on Rhino Security and I find it very interesting and useful. I think it would be the solution to the requirements of my customers too
Thank you so much for the post. It has helped me a lot. I could get Security up and running at operation level. Could not find any material regarding Security at entity level.
My scenario is :
1. Plan is my entity, it has a local planner attribute.
Rule says : Show all the plans that are belonging to current planner only.
2. If logged in user is Head planner then show all the plans.
Also can we have conditions in the entity, like if the user is LocalPlanner display all the items where qty <100 and if he is HeadPlanner then display all the items..
Any help is highly appreciated.
Thank you.
Bhoomi.
Hi Bart, thanks for the sharing, i like your blog for a long time, and this is my first time to comment.
The PTLLS Course is for new comers to the Lifelong Learning Sector – This means you don’t have to have any previous teaching qualifications or experience. The qualification is also ideal for people who already work in the industry but want to obtain an official qualification.
Post a Comment