I demand configuration

In the previous posts I described the intentions behind Themis. It’s time do deepen into demand and the whole configuration. Imagine a domain of car drivers. It includes car drivers as well as driven cars. The simplest demand which one can come up with is a simple check, whether one can drive a specific car.

public class CanDrive : IDemand<bool>
{
    public CanDrive(Car car)
    {
        Car = car;
    }

    public Car Car { get; private set; }
}

Speaking about a car and car driver:

public class DriverRole
{
    public DriverRole(int skillLevel)
    {
        SkillLevel = skillLevel;
    }

    public int SkillLevel { get; private set; }
}

public class Car
{
    public Car(int requiredSkillLevel)
    {
        RequiredSkillLevel = requiredSkillLevel;
    }

    public int RequiredSkillLevel { get; private set; }
}

The last but one thing, which should be done is to create a defintion of the DriverRole. By creating a definition I mean, creating a set of rules applied to evaluated demands in scope of the role context:

public class DriverRoleDefinition : RoleDefinition<DriverRole>
{
    public DriverRoleDefinition( )
    {
        Add<CanDrive, bool>((d, r) => d.Car.RequiredSkillLevel <= r.SkillLevel);
    }
}

Having all of this, during application startup one can easily configure the demand service with fluent configuration:

var demandService = Fluently.Configure()
    .AddRoleDefinition(new DriverRoleDefinition())
    .BuildDemandService();

and use the created service to evaluate demand canDrive created on a retrieved car basis, passing the driver role as the argument

return _demandService.Evaluate<CanDrive, bool>(canDrive,  driverRole).Any(b=>b);

In the solution, I created a more meaningful example (Themis.Example project), showing how with simple extension methods get rid of plenty of generic parameters.

In the next posts I’ll write about Themis extension points and describe a way in which I want to integrate it with NHibernate.

I demand IDemand

The main interface of Themis is IDemand.

public interface IDemand
{
}

An example implementations:

public class EntityPermission : IDemand
{
    public EntityPermission(TEntity entity)
    {
        Entity = entity;
    }

    public TEntity Entity { get; private set; }
}

public class View : EntityPermission
{
    public View(TEntity entity)
        : base(entity)
    {
    }
}

A simple interface, with no members at all. The class implementing it defines the demand name as well as the result which should be provided by the system evaluating the demand. Implementations of IDemand should be contextful, passing all the needed context for the demand evaluation in their properties. The TResult can be any type, but in majority of authorization cases it’ll be nothing more then bool. We want a yes/no answer for ‘Can I?’ question, don’t we? :)

Having the defined roles and demands we can move to the UI and write a simple authorization check. Being given an instance of IDemandService (by constructor injection), I can write in my ASP MVC controller a simple check of a demand/authorization. Below you can find the signature of IDemandService, a simple extension method written to ease using the service in a specific domain (a wrapping service is also a good solution) and the usage example itself.

public interface IDemandService
{
    IEnumerable<TResult> Evaluate<TDemand, TResult>(TDemand permission, params object[] roles)
        where TDemand : IDemand<TResult>;
}

Useful extension methods written in the app using Themis. The names, I hope, are self-descriptive.

    public static class AuthorizationServiceExtensionMethods
    {
        public static bool Can<TDemand>(this IDemandService demandService, TDemand permission, 
            params object[] roles)
            where TDemand : IDemand<bool>
        {
            return demandService
                .Evaluate<TDemand, bool>(permission, roles)
                .Any(b => b);
        }

        public static bool CanEdit<TEntity>(this IDemandService demandService, TEntity entity, 
            params object[] roles)
        {
            return demandService
                .Evaluate<Edit<TEntity>, bool>(new Edit<TEntity>(entity), roles)
                .Any(b => b);
        }

        public static bool CanView<TEntity>(this IDemandService demandService, TEntity entity, 
            params object[] roles)
        {
            return demandService
                .Evaluate<View<TEntity>, bool>(new View<TEntity>(entity), roles)
                .Any(b => b);
        }
    }

And finally, the app code example in which you can see that role management was delegated to an enigmatic IRoleService. The ‘Should I use NH session to manage roles?’ question is out of scope of this entry.

public ActionResult View(Guid id)
{
    var car = _session.Get<Car>(id);
    var roles = _roleService.GetCurrentUserRoles();
    if (!_demandService.CanView(car, roles))
        throw new InvalidOperationException("You cannot view this car!");

    return View(car);
}

Isn’t is simple? What about the expressions and the configuration? I’ll cover it soon.

It’s all about your domain

I dislike libraries which make me pollute the domain with any implementation and reference. I made up my mind about Log4Net and NHibernate, and now I’m totally cool to add the naked (not wrapped in a _super_abstracted_layered_total_wrap_ ) reference to my project and query the NH’s session treating it as the repository. What I dislike is when the friction between the library and application makes me leak its interfaces all around or wrap its plenty of interfaces.

The Themis is about providing an (in majority of cases) authorization system, with almost no overhead. In the last part I mentioned the three main parts of the Themis. I’ll start with the last, the most meaningful – your domain. Imagine a very complex system with plenty of roles. Imagine that each role can have a different context for different users, for instance:

  • Bob, has roles:
    • Administrator, with properties
      • SkillLevel: 5
      • Permissions: Permissions.Max
    • Worker, with properties
      • Floor: 4
    • Worker, with properties
      • Floor: 5
  • Alice, has roles:
    • Manager, with properties:
      • MobbingFloor: 4
    • Manager, with properties:
      • MobbingFloor: 7
      • Mobbing

As you can see roles, for me are not only simple strings, but rather containers for contexts and they should be included in your domain. Using NHibernate I’d create a base class Role and derive other roles from it. Additionally, I’d add a set property to the User, simply called Roles and that would be it. Going back to the contextful roles, it allows you to incorporate them into more meaningful statements like “Administrator can Manage only Programs with RequiredSkillLevel lesser then his/her”. A simple business rule having a few parts: the privilege itself – Manage, the subject of rule – Program, and the condition. Having the roles implemented in the way described earlier, I’d be very pleased if I could simply write a lambda expression having Role and Program as parameters and checking the relation between skill levels. I’ll write them in the next post.

To sum up: it’s all about your domain, having roles as nonempty entities with connected to the aggregate, the user.