Rule Based Access Control using an Expression Evaluator
September 17, 2008I’ve written previously about “claims based authorization,” and how I believe it will eventually become a ubiquitous approach to identity management as the world of online digital identity reinvents itself in order to avoid implosion. If you aren’t familiar with the concept of claims based identity, I recommend checking out the post linked above or following some of the links from this post. If you just want to examine the sample code for this post, it is here.
In this post I’m interested in how a set of claims presented by a user when making a service call can be evaluated against specific authorization rules to determine whether or not the user has access to the requested resource.
The details of how a user is associated with such claims, how they are secured, and how claims are embedded inside calls to your service are beyond the scope of this post, but the links listed above should provide a place to put the tip of that chisel. For the purposes of this post I will assume that claims are presented via a list of IClaim objects, and an IClaim is nothing but a Name/Value pair – IList<IClaim>. This is very similar to how Micrisofts new Zermatt Identity Framework represents claims, and this was intentional as I believe that Zermatt is probably the future of Identity Management on the .NET platform.
So then, given IList<IClaim> claims, how can we make rational authorization decisions, such as “I only want users who are in the Premium Members role and whose age is older than 21 to access this (probably seedy) service method.” One obvious, simple approach would be to evaluate the claims programmatically. Some simple extension methods can make this much easier:
public static class ClaimsExtensions
{
public static T GetClaimValue<t>(this IList<iclaim> claims, string claimType)
{
foreach (IClaim claim in claims)
{
if (String.Compare(claim.ClaimType, claimType, true) == 0)
{
return TypeNormalizer.EnsureType<t>(claim.Value, default(T));
}
}
return default(T);
}
public static bool ClaimEqualsAny(this IList<iclaim> claims, string claimType, string claimValue)
{
foreach (IClaim claim in claims)
{
if (String.Compare(claim.ClaimType, claimType, true) == 0)
{
if (claim.Value.Equals(claimValue, StringComparison.InvariantCultureIgnoreCase))
return true;
}
}
return false;
}
public static bool EvaluateClaim<t>(this IList<iclaim> claims, string claimType, Func<t , bool> eval)
{
var claimValue = claims.GetClaimValue</t><t>(claimType);
return eval(claimValue);
}
}
Then, from within a method that needs protecting, you can easily do something like this:
public void MyServiceMethod()
{
if (CurrentUser.Claims.EvaluateClaim<int>(CustomClaimTypes.AgeInYears,age => age < 21) ||
CurrentUser.Claims.ClaimEqualsAny(CustomClaimTypes.Role,"Premium User") == false)
{
//Whatever you do when someone isn't authorized. Throw a not authorized exception? Return a fault code?
}
//Implement your service method here, your user is old enough and has paid their money
}
If you were clever, you could probably figure out a way to use LINQ expressions to make the syntax even cleaner. This is all good and well, but it has at least one major problem – authorization rules are probably likely to change far more frequently than your application code. Even worse, you may wish your power users to be able to manage their own authorization rules for certain kinds of access or for named permissions. Using the technique described above, any change to any authorization rule will require you compile and deploy your application.
One common solution to conundrums such as these is to use a DSL (Domain Specific Language). And while you certainly could write a custom DSL to implement your authorization rules, authorization rules have an interesting characteristic: they always resolve to a discrete value, a true or a false. They are simply boolean expressions.
There are a number of Expression Evaluators available from the open source community that will fit the bill perfectly. An Expression Evaluator will allow you to specify your authorization rules as simple strings, which means they can be stored in a configuration file or a database or some other persistent store and modified and run-time as needed. They are also serializable by definition and can easily be requested by and cached at the user interface to allow the user interface to use the same authorization logic to render itself appropriately with respect to the permissions of the currently logged in user that the server will use to authorize access to its API without (necessarily) having any code in common other than a reference to the same expression evaluator library.
In this example I will be using Simple Expression Evaluator available on Codeplex. I chose this library purely out of nepotism, because I wrote it, but you should be able to use any expression evaluator that catches your fancy. A number of options for .NET are listed here.
Using an Simple Expression Evaluator we could rewrite the above authorization rule like this:
AgeInYears >= 21 and MatchesAny(Role = "Premium User")
In the sample project I added a custom function, IsInRole, allowing this syntax:
AgeInYears >= 21 and IsInRole("Premium User")
To make this happen, Simple Expression Evaluator will need a little help in interpreting claim names. The main reason behind this is that claims are usually represented by a URI, such as http://thefreakparade.com/claims/role, but accessed through code using string constants, such as
public static class CustomClaimTypes
{
public const string Role = "http://thefreakparade.com/claims/role";
//...more...
}
and therefore accessed in code like this:
var claim = new Claim(CustomClaimTypes.AgeInYears,43);
The expression evaluator will interperet something like AgeInYears to be a variable, and the name AgeInYears won’t naturally correspond to anything, because it’s really just a property name, the real claim is represented by a URI that would be far too ugly to stick in an expression. The solution is to give the expression evaluator a little help when evaluating variables, like so:
public bool Authorize(IList<iclaim> claims,string authExpression)
{
var context = new ExpressionContext(claims);
context.ResolveUnknownVariable +=
(sender, args) =>
{
args.VariableValue =
claims.GetClaimValue(_claimAliases[args.VariableName]);
};
context.ResolveMissingFunction +=
(sender, args) =>
{
if (args.FunctionName.Equals("IsInRole",StringComparison.OrdinalIgnoreCase))
{
args.Function = (funcArgs) => IsInRole(claims,funcArgs);
}
};
return ExpressionEvaluator.EvaluateExpression(authExpression,context);
}
This method was modified a little from the actual sample to the purpose of clarity, but the general idea is the same. Also demonstrated is the technique required to add an IsInRole method to the expression language. The actual implementation of the IsInRole functionality is implemented off screen, see the sample for the actual code. This method also relies on a hashtable called _claimAliases being pre-populated with a mapping between the URI of a claim type and the friendly name the expression language will use. In the sample app, I simply used reflection to load the alias table with a mapping between the field name of each const and the actual URI.
Now, if you simple use the expression evaluator an call Authorize from within your service methods, like this:
public void MyServiceMethod()
{
if (AuthorizationHelper.Authorize(
CurrentUser.Claims,"AgeInYears >= 21 and IsInRole('Premium User')" == false)
{
//Whatever you do when someone isn't authorized. Throw a not authorized exception? Return a fault code?
}
//Implement your service method here, your user is old enough and has paid their money
}
Then all we added with the dynamic expression was complexity – your rules are still defined inline in your code. What you really want is a syntax like this:
public void MyServiceMethod()
{
if (AuthorizationManager.Authorize(CurrentUser.Claims,Permissions.CanAccessServiceMethod) == false)
{
//no access
}
//service code
}
Where Permissions.CanAccessServiceMethod will key into a persistent authorization rule store such as a config file or database, so that the rule expressions themselves can be dynamically loaded at runtime and therefore modified outside of the code. The sample application takes advantage of a very simplified implementation of the Repository pattern and introduces an IAuthorizationRuleRepository to solve that issue. If you were really, really smart you could get to a syntax like this without much extra effort. Note this sample application does not quite go this far, but it shouldn’t be a stretch for you get there yourself if you care to:
[RequiresPermission(Permissions.CanAccessServiceMethod)]
public void MyServiceMethod()
{
//service code
}
The only caveat with an attribute based approach is that access to your protected method is all or nothing. You can’t return a set of records filtered one way when one permission level is present, and another for a lower level of permission. But nothing would prevent you from mixing an attribute based approach with an inline-code based approach as appropriate.
Finally, here is a sample of the actual authorization syntax as implemented by the Identity at Rest sample application:
public List<monkey> ListAllMonkeys()
{
//Only administrators can list all monkeys.
//Everyone else can just list shaved monkeys.
if (AuthorizeFor(
Permissions.MonkeyShavingService.CanViewUnshavedMonkeys) == false)
{
return ListMonkeysByStatus(MonkeyStatus.ShavedClean.ToString());
}
return _monkeys;
}
And that’s about it. If you’ve made it this far, your very well aware that this post is describing a general approach to claims based authorization using an expression oriented DSL – the code samples listed here aren’t detailed enough to get you from point A to point B in an actual implementation. However, the included sample application, which is an evolution of the Identity At Rest sample application provided for this post, does provide an end to end application that fully implements the concepts discussed here. The sample comes with a “smart client” Winforms application that uses WCF to communicate with a remote REST based API. All API calls are secured using the above described techniques.
I would be the last one to suggest that this approach is a “best practice” in managing claims based authorization, but it made sense to us and seems to work pretty well so far. As always, I’d love to hear about any other approaches that you may be using.









Recent Comments