May 2010 Archives

The other day, I was looking through some code of a RP that a colleague wrote. He was mapping the claims to properties on a user class. The property names were very dissimilar to those of the claim types. This got me to thinking. What is needed by RP developers, in general, is a simple user class w/ dynamic properties and a way to map claims in an IClaimsPrincipal to them. Looking for an opportunity to learn more about .NET 4, I coded up the following little framework (I use that term loosely here) that uses the new dynamic language features of C# to do exactly this.

Before diving into the various classes and interfaces, have a look at the following snippet which shows how you might use this framework and the problem it's trying to solve:

public static void Main()
{
    var claims = new[]
    {
        new Claim("foo", "3"),
        new Claim("foo_bar", "true"),
        new Claim("foo_baz", "Ted"),
        new Claim("http://schemas.travisspencer.com/2010/05/test/claims/shoesize", "11"),
        new Claim("http://schemas.travisspencer.com/2010/05/test/claims/haircolor", "blond"),
        new Claim("Age", "16"),
    };
    var identity = new ClaimsIdentity(claims);
    dynamic user = new MyGoodUser(identity);

    Console.WriteLine("Foo = {0}", user.Foo);
    Console.WriteLine("FooBar = {0}", user.FooBar);
    Console.WriteLine("FooBaz = {0}", user.FooBaz);
    Console.WriteLine("ShoeSize  = {0}", user.ShoeSize);
    Console.WriteLine("HairColor = {0}", user.HairColor);
    Console.WriteLine("Age = {0}", user.Age);

    Console.ReadLine();
}

In this snippet, we have some claim types (e.g., foo_baz, http://.../shoesize, etc.), but you can see that the RP developer really wants a nice programming model -- a user object w/ Pascal-cased properties. This is provided by the MyGoodUser class.  This class is intended to be an RP-specific class that derives from a User class that is provided in the framework (described below); it would be created and put on the thread's principal by a custom HTTP module that uses the IClaimsIdentity created by WIF.

The object model of the framework is shown in the following figure.

UserObjectModel2.gif

With these, you can create your own user class (e.g., MyGoodUser) and mappers that will provide you with a nice programming model in your RP regardless of what comes over in the claims. Here is the User class in its entirety:

public class User : DynamicObject
{
    private readonly ClaimsIdentity identity;
    private readonly List<IClaimsIdentityMapper> mappers;

    public User(ClaimsIdentity identity)
        : this(identity, new NullClaimsIdentityMapper())
    {
    }

    public User(ClaimsIdentity identity, params IClaimsIdentityMapper[] mappers)
        : this(identity, false, mappers)
    {
    }

    public User(ClaimsIdentity identity, bool includeNullMapper, params IClaimsIdentityMapper[] mappers)
    {
        if (mappers == null || mappers.Length == 0)
        {
            throw new ArgumentNullException("mappers");
        }

        this.identity = identity;
        this.mappers = new List<IClaimsIdentityMapper>(mappers);

        if (includeNullMapper)
        {
            this.mappers.Add(new NullClaimsIdentityMapper());
        }
    }

    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {           
        result = null;

        foreach (var mapper in mappers)
        {
            var claimType = mapper.Map(binder.Name);
            var c = identity.Claims.Where(x => x.ClaimType == claimType);

            if (c != null && c.Count() == 1)
            {
                result = c.ElementAt(0).Value;
                break;
            }
        }

        return result != null;
    }

    private class NullClaimsIdentityMapper : IClaimsIdentityMapper
    {
        public string Map(string name)
        {
            return name;
        }
    }
}

The important part is the overridden TryGetMember method. That is fired every time a property on a User object is accessed for which there is no static definition. In it, the collection of mappers transpose the property name to a value that is used to look up a claim of that type. If one is found, its value is returned; otherwise, party at run-time :-)

The mappers are pretty simple because they do very little. Here's the one that maps property names to URLs:

public class UrlClaimsIdentityMapper : IClaimsIdentityMapper
{
    public string Map(string name)
    {
        return "http://schemas.travisspencer.com/2010/05/test/claims/" + name.ToLowerInvariant();
    }
}

The derived user class is also pretty trivial:

public class MyGoodUser : User
{
    public MyGoodUser(ClaimsIdentity identity)
        : base(identity, true, new UnderScoreClaimsIdentityMapper(),
            new UrlClaimsIdentityMapper(),
            new AddressClaimsIdentityMapper())
    {
    }
}

I can imagine that claim types will often be hierarchical and the RP developer would like a programming model such as this:

Console.WriteLine("Address - Line 1 = {0}", user.Address.Line1);
Console.WriteLine("Address - Line 2 = {0}", user.Address.Line2);
Console.WriteLine("City = {0}", user.Address.City);
Console.WriteLine("State = {0}", user.Address.State);
Console.WriteLine("Zip Code = {0}", user.Address.ZipCode);

For that, I will refer you to ElasticObject by Anoop Madhusudanan.

I hope this gets some creative juices flowing. If you have questions or thoughts, feel free to leave a comment here or contact me.