I guess you already heard the term “claims based authorization” several times by now. But how exactly does the authorization work?
The typical pattern is that you iterate through the claims associated with the user and search for a special claim and maybe also a special value of that claim. Based on that you can grant/deny access or modify behavior or UI elements.
This typically leads to code like this:
if (principal.ClaimExists(“http://someclaim”, “somevalue”)) {}
or
principal.DemandClaim(WSAuthorizationConstants.Action, “AddCustomer”);
This has the downside, that claim types and values are embedded in your application code and this may become hard to maintain in certain situations. That’s the reason why the Geneva framework Beta 2 introduced a built-in infrastructure for authorization that operates at a higher level of abstraction.
In Geneva authorization information is abstracted as a resource that the subject tries to access and the action the subject wants to perform on that resource. The container for this information is called the AuthorizationContext.
Operation and action are collections of claims. This allows to express from simple to more complicated authorization statements. A simple resource/action pair could be e.g. Directory/Browse. A more complicated statement could be e.g. “Purge printer queue of laser printer in building 61”.
That’s how you could construct the corresponding AuthorizationContext:
var simpleContext = new AuthorizationContext(principal, “Directory”, “Browse”);
and
var advContext = new AuthorizationContext(principal,
new Collection<Claim>
{
new Claim(“http://claims/device”, “Printer”),
new Claim(“http://claims/building”, “61”)
},
new Collection<Claim>
{
new Claim(WSAuthorizationConstants.Action, “PurgeQueue”)
});
The second piece of plumbing introduced by Geneva is the ClaimsAuthorizationManager. You derive from this class and implement the CheckAccess method. This method accepts an AuthorizationContext and returns true/false. In this method you have to do whatever mapping is necessary to resolve the resource/action pair to claims of the subject.
Use this sample authorization manager to inspect the data that gets passed in:
class SimpleAuthorizationManager : ClaimsAuthorizationManager
{
public override bool CheckAccess(AuthorizationContext context)
{
Console.WriteLine(“SimpleAuthorizationManager:”);
Console.WriteLine(“nSubject: {0}n”, context.Subject.Identity.Name);
Console.WriteLine(“Actions:”);
foreach (var action in context.Action)
{
Console.WriteLine(” {0}”, action.ClaimType);
Console.WriteLine(” {0}n”, action.Value);
}
Console.WriteLine(“Resources:”);
foreach (var resource in context.Resource)
{
Console.WriteLine(” {0}”, resource.ClaimType);
Console.WriteLine(” {0}n”, resource.Value);
}
return true;
}
}
While you could manually new up that class and directly call CheckAccess method – you can also use configuration to create the authorization manager. This has some interesting features.
First you need to configure the authorization manager in config:
<microsoft.identityModel>
<service>
<claimsAuthorizationManager
type=“LeastPrivilege.PolicyAuthorizationManager, ClaimsAuthorization” />
</service>
</microsoft.identityModel>
And then use the following code to create and use the authorization manager:
var config = new ServiceConfiguration();
var authz = config.ClaimsAuthorizationManager;
var simpleContext = new AuthorizationContext(
principal, “Directory”, “Browse”);
var allowed = authz.CheckAccess(simpleContext);
You can also use the ClaimsPrincipalPermission/Attribute classes which simplify the call and automate the creation from configuration:
new ClaimsPrincipalPermission(“Directory”, “Browse”).Demand();
or
[ClaimsPrincipalPermission(SecurityAction.Demand,
Resource = “Directory”, Operation = “Browse”)]
private static void BrowseDirectory()
{ }
As with the normal PrincipalPermission – these classes throw a SecurityException and assume that you have populated Thread.CurrentPrincipal.
The configuration for the claims authorization manager has an interesting extensibility point. You can attach arbitrary XML to the configuration element. This allows to create policy statements directly in config or provide enough information for the authorization manager to load the policy from somewhere else. Whenever such an attached XML fragment exists, the configuration loader calls a special constructor of ClaimsAuthorizationManager and passes over the fragment as an XmlNodeList.
public class PolicyAuthorizationManager : ClaimsAuthorizationManager
{
// this ctor gets called when there is a child element in app.config
public PolicyAuthorizationManager( object objXmlElement )
{
XmlNodeList nodes = objXmlElement as XmlNodeList;
Process(nodes);
}
}
The same mechanism is also used for SecurityTokenHandlers btw.
The Geneva SDK includes a sample that shows how to use this extensibility point to parse authorization policy (in the Extensibility folder). With this sample you could write something like this for the above authorization statements:
<claimsAuthorizationManager type=“type“>
<policy resource=“Directory“
action=“Browse“>
<claim claimType=“http://schemas.microsoft.com/ws/2008/06/identity/claims/role“
claimValue=“Users“ />
</policy>
<policy resource=“Printer_61“
action=“PurgeQueue“>
<or>
<claim claimType=“http://schemas.microsoft.com/ws/2008/06/identity/claims/role“
claimValue=“Enterprise Administrators“ />
<and>
<claim claimType=“http://schemas.microsoft.com/ws/2008/06/identity/claims/role“
claimValue=“Administrators“ />
<claim claimType=http://claims/building
claimValue=“61“ />
</and>
</or>
</policy>
</claimsAuthorizationManager>
Everything you’ve seen here can be used in arbitrary application types. In the next post I show how this is integrated with WCF and ASP.NET.