Every now and again I find myself disappointed in the asp.net security model. The ability to assign roles is useful but if a role changes and I have implemented code security by role I now have to alter my PrincipalAttributes. That isn't a huge issue, but I am not of a fan of recompiling my code because the of a minor change when I could secure the code by a right and assign a right to a role in a data store.
The first thing we need is a right
using System; namespace ObjectHelpDesk.Security { public class Right { private Guid _id = Guid.Empty; public Guid Id { get { return _id; } set { _id = value; } } private String _rightName = String.Empty; public String RightName { get { return _rightName; } set { _rightName = value; } } } }
Nothing to exciting there, its a simple little object. It is however the IPrincipal the is the workhorse of the asp.net security model. Now that we have a right to extend the security system with we need an IPrincipal for asp.net to pass around.
using System; using System.Collections.Generic; using System.Linq; using System.Security.Principal; namespace ObjectHelpDesk.Security { public class RightPrincipal : IPrincipal { public RightPrincipal(IPrincipal user) { _user = user; } public RightPrincipal(IPrincipal user, List<Right> rights) : this(user) { _rights = rights; } private IPrincipal _user = null; private List<Right> _rights = new List<Right>(); public bool HasRight(String rightName) { bool result = false; if (_user is RightPrincipal) { result = ((RightPrincipal)_user).HasRight(rightName); } if (!result) { result = _rights.Count(r => r.RightName == rightName) > 0; } return result; } #region IPrincipal Members public IIdentity Identity { get { return _user.Identity; } } public bool IsInRole(string role) { return _user.IsInRole(role); } #endregion } }
There are a few things to talk about here. There are two constructors, both take an IPrincipal object. We are going to need to create an IHTTPModule later that builds our custom RightPrincipal and the it will pass the existing asp.net IPrincipal object into the constructor.
We store existing IPrincipal in a private field and use it to get the Identity object and implement the IsInRole function. We also create a list of List<Right> and a HasRight method. Has right checks the existing IPrincipal to see if it is a RightPrincipal and calls has right on it if it is. If the IPrincipal is not a RightPrincipal or the right is not found it does a quick little check to see if the Right exists in the RightPrincipal's collection of rights.
We now have Right and a custom IPrincipal, but how do we attach them to the current request without doing something in every page?
Up next - Part 2: the IHttpModule