[Update on 9/14/09 - David Archer has provided some updated instructions for getting the latest Rhino.Security working with the latest of S#arp Architecture.]
I've always found the ASP.NET Membership and Role providers to be a bit limiting. I've usually ended up rolling my own security mechanism but have recently been using Rhino.Security which I find simple enough for basic scenarios, such as simple role support, while being sophisticated enough to handle operational permissions for more demanding situations. With help from Denis Connolly and Luca Lusetti on the S#arp Architecture discussion forum, what follows are the steps for integrating Rhino.Security into an ASP.NET MVC, S#arp Architecture project:
- Using your favorite SVN client, such as Tortoise SVN, download the latest Rhino.Security source from https://rhino-tools.svn.sourceforge.net/svnroot/rhino-tools/trunk
- Within the download folder, run build_without_tests.cmd
- Copy a few items from /build/net-3.5/debug/ to YourApp/lib/
- Castle.Components.Validator.dll/xml
- Rhino.Commons.Clr.dll/xml
- Rhino.Commons.dll/xml
- Rhino.Commons.NHibernate.dll/xml
- Rhino.Security.dll/xml
- To YourProject.Web, add references to all of the above assemblies added to YourApp/lib/
- To YourProject.Web/CastleWindsor/ComponentRegistrar.cs:
- Add a new using statement to the top: using Rhino.Commons;
- Within AddGenericRepositoriesTo(), change the "repositoryType" registration to the following, to remove the reference conflict (be sure to watch out for accidentally introducing other such IRepository<> conflicts in your code):
container.AddComponent("repositoryType",
typeof(SharpArch.Core.PersistenceSupport.IRepository<>), typeof(SharpArch.Data.NHibernate.Repository<>));
- Add a new method to register the dependencies for Rhino.Security:
private static void AddRhinoSecurityComponentsTo(IWindsorContainer container) {
container.AddComponent("rhinoRepositoryType",
typeof(Rhino.Commons.IRepository<>), typeof(NHRepository<>));
container.AddComponent("unitOfWorkFactoryType",
typeof(IUnitOfWorkFactory), typeof(NHibernateUnitOfWorkFactory));
- Add a call to the new method within AddComponentsTo()
- To YourProject.Core, add an assembly reference to Rhino.Security.dll
- To YourProject.Core, add the class YourProject.Core/User.cs and have it implement Rhino.Security.IUser, e.g.:
using Rhino.Security;
using SharpArch.Core.DomainModel;
namespace YourProject.Core
{
public class User : Entity, IUser
{
[DomainSignature]
public virtual string Username { get; set; }
public virtual SecurityInfo SecurityInfo {
get { return new SecurityInfo(Username, Id); }
}
}
}
- Create a "Users" table in YourProject's DB to support the User object
- Add a new file, User.hbm.xml to YourProject.Data/NHibernateMaps. After adding it, right click it, go to properties and set the Build Action to Embedded Resource. The HBM file should map the user class; e.g.:
<?xml version="1.0" encoding="utf-8"?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" default-lazy="true" assembly="YourProject.Core" namespace="YourProject.Core">
<class name="User" table="Users" xmlns="urn:nhibernate-mapping-2.2">
<id name="Id">
<generator class="identity" />
</id>
<property name="Username" />
</class>
</hibernate-mapping>
- Add a new class, YourProject.Web/CastleWindsor/FacilitiesRegistrar.cs and include the following:
using Rhino.Security;
using Rhino.Commons.Facilities;
using Rhino.Commons;
using WorkWithRhino.Core;
namespace WorkWithRhino.Web.CastleWindsor
{
public class FacilitiesRegistrar
{
public static void AddFacilitiesTo(Castle.Windsor.IWindsorContainer container) {
container.AddFacility("rhinoSecurityFacility",
new RhinoSecurityFacility(SecurityTableStructure.Prefix, typeof(YourProject.Core.User)));
IoC.Initialize(container);
}
}
}
- To YourProject.Web/Global.asax.cs, add the following:
using Rhino.Commons;
using Rhino.Commons.Facilities;
using Rhino.Security;
...
protected virtual void InitializeServiceLocator() {
...
FacilitiesRegistrar.AddFacilitiesTo(container);
...
}
- Copy (not rename) YourProject.Web/NHibernate.config to YourProject.Web/Hibernate.cfg.xml and add the following below the proxyfactory.factory_class property:
<property name="cache.provider_class">NHibernate.Cache.HashtableCacheProvider, NHibernate
<mapping assembly="YourProject.Data" />
<mapping assembly="Rhino.Security" />
Rhino.Security expects Hibernate.cfg.xml to exist, but adding the extra lines to NHibernate.config results in the user class being mapped twice; consequently, an exception is thrown at runtime. It seems kludgy to duplicate the NHibernate configuration file in this way, but I can't find a way around it at the moment.
- Run the attached "Rhino Security Schema.sql" on your project DB.
- Add references to Rhino.Commons.NHibernate and Rhino.Security to whichever application layers will be using Rhino.Security capabilities
- Now use it!
A somewhat useless, example usage:
using (UnitOfWork.Start()) {
// Can also be passed into a constructor via dependency injection
IAuthorizationRepository authorizationRepository =
SafeServiceLocator<IAuthorizationRepository>.GetService();
UsersGroup userGroup =
authorizationRepository.GetUsersGroupByName("admin");
}
Here are a few useful Rhino.Security resources:
Enjoy!
Billy McCafferty
Posted
04-30-2009 7:37 PM
by
Billy McCafferty