Automatic Mid-tier Cache!

I have been thinking about this for a while. I started to work on a Silverlight project last year which was a nifty little business app. One of those apps that is just sitting down and working, you know, a web server, an average DB with a few million records, and a silverlight client with a bunch of forms, grids, and menus.

Everything was straightforward and brainless, like just follow a pattern and do the job. However, I was deeply dis satisfied from one aspect of the project. The Caching. What I could not accept was that in 21st century, when you have Entity Framework and IQueryable, you should still manually cache your data when it is appropriate and do all the pointless work of cache invalidation and loading, etc. Apart from the pain of working on something that should have been automated, I wouldn’t trust a programmer to decide which parts of the data should be cached and which part shouldn’t be. Not that I don’t believe they can do a good job on that, they don’t have enough information (at the time of dev work) to decide on it.

Caching strategy should be based on user behaviour and is subject to change by passage of time. For example at some stage lots of QLD pharmacies are queried, but next week NSW users decide to get ready for their conferences and start hammering the system for NSW pharmacies.

Le me be clear about my expectations of a caching system. It should have the following charachteristics:

  1. It should know what users are going to query a lot and cache that (and only that) part of the database.
  2. It should be able to re-use the caches. For example if I say 1.”Give me all QLD pharmacies”, and next one says 2.”Give me all QLD Chemists Warehouses”, the cache manager should be smart enough to run this new query 2., over the results of query 1. which has been retrieved a few minutes ago.
  3. It should optimize the indexes for performance based on the user queries.
  4. It should change the cache when user behaviour changes.
  5. It can call back the database only if there is absolutely no way of answering the query from cache.

Above requests seems to be a lot, but not really in 2011. All these methods are possible, in fact DBMSs do those kind of stuff for ages. We also have IQueryable, which makes it even easier to have a decent caching system.

So let me write a few examples:

Q1: Pharmacies.Join( … Address …).Join( … State …).Where( s => s.Sate = “QLD”).Select(…)

Q2: Pharmacies.Join( … Address …).Join( … State …).Where( a =>a.Sate = “QLD” && a.PostCode > 4000 && a.PostCode<4079).Select(…)

Q3: Pharmacies.Join( … Address …).Join( … State …).Where( s => s.Sate = “QLD”).GroupBy(…).Where( pg => pg.Count() > 4).Select(…)

Q4:  PharmacyStaff.Where(ps => ps.Position == “Manager”).Select(…)

Q5: Pharmacies.Join( … Address …).Join( … State …).Join(…PharmacyStaff…).Where( s => s.Sate=”QLD” && s.Position == “Manager” ).Select(…)

Users login to our system  and do stuff that will cause the above queries to be issued. Normally they will all be issued against the database, but it means that our caching strategy is stupid as a donkey. Really what I would expect is that only Q1 and Q4 are ran against the database. Q2, Q3, and Q4 are all subsets of Q1, hence if we already have those results from Q1, such a waste to run these new queries against the database. Why not look at the Expression Tree and figure out that Q2 is forms a query which is a subset of Q1. Then change the queries as below:

Q1: not changed…

Q2: Q1.Where(a => a.PostCode > 4000 && a. PostCode<4079).Select(…)

Q3: Q1.GroupBy(…).Where(…)

Q4: not changed…

Q5: Q1.Join(…Q4…).Select(…)

Check out the above queries. Aren’t they much better. We don’t expect user or programmer to waste his time on translating those queries. The caching system should do that. It should be an IQueryable that reads the ExpressionTree and translates it into a new ExpressionTree that uses existing data in the cache if there is no need to query database.

This specially make  sense in CLOUD, where you have to pay for querying your SQL Asure.

Enough talking about the dreams, lets become realistic! I did a bit of research and as I expected no such caching manager exists (if you know some tell me and save my hair). So I decided to do it myself. Check the Auto-mid-tier-cache project which I have already started. I haven’t gone far with it yet. It is just a proof on concepts and it implements no IQueryable. It uses a set of objects defined by myself for Relational Algebra operators. It does the very basic of view-matching to find what query is subset of what other, and it is able to translates queries to run against the database or cache alternatively.

I ran it and it worked fine and a bunch of benchmark analysis proved its effectiveness. What is left now is to complete the view-matching and write an IQueryable on top of it. Lot of work but it is worth it.

I forgot to say that you can limit the cache size by setting cost upper-bound. Next issue is that it does not keep itself up-to-date, but this is really another story.

Advertisements

Nullsafe dereference operator (?.) in C#

Nullsafe dereference operator (?.) as exists in some languages is a handy and nice pattern which saves a few bugs. Null references are serious problems and as people say nearly 70% of all bugs in the industry are directly or indirectly related to “Object reference not set to an instance of an object!”

However, there is noway to completely avoid null references, unless you write in a fully functional programming language like HASCUL. Some object oriented languages like groovy support a notion of nullsafe dereference operator which helps your null checks to be simpler. Here is the syntax in groovy:

bossName = Employee?.Supervisor?.Manager?.Boss?.Name

The above code returns the Name of the boss of the manager of the supervisor of an specific employee. It checks if any part of the right operand is null. If employee has no supervisor or supervisor has no manager, etc. bossName will become “null”.

Writing the above code in C# becomes like this:

bossName = Employee != null && Employee.Supervisor != null && Employee.Supervisor.Manager != null && Employee.Supervisor.Manager.Boss != null ? Employee.Supervisor.Manager.Boss.Name : null;

Pretty ugly, isn’t it? We can make a bunch of C# functions that takes us as closer to the groovy syntax of groovy. Note that nullsafe operator is going to be used in many places and inside the loops and it is not efficient to define any class for just a null check. Anyway a simple function won’t add much overhead.

//Groovy:
bossName = Employee?.Supervisor?.Manager?.Boss?.Name
 //C#:
 bossName = Nullify.Get(Employee, e => e.Supervisor, s => s.Manager, m => m.Boss, b => b.Name);

 

This one looks much more beautiful and more readable and this the the Nullify (static) class:

 public static class Nullify
 {
 public static TR Get<TF, TR>(TF t, Func<TF, TR> f) where TF : class
 {
 return t != null ? f(t) : default(TR);
 }

 public static TR Get<T1, T2, TR>(T1 p1, Func<T1, T2> p2, Func<T2, TR> p3)
 where T1 : class
 where T2 : class
 {
 return Get(Get(p1, p2), p3);
 }

 /// <summary>
 /// Simplifies null checking as for the pseudocode
 /// var r = Pharmacy?.GuildMembership?.State?.Name
 /// can be written as
 /// var r = Nullify( Pharmacy, p => p.GuildMembership, g => g.State, s => s.Name );
 /// </summary>
 public static TR Get<T2, T2, T3, TR>(T1 p1, Func<T1, T2> p2, Func<T2, T3> p3, Func<T3, TR> p4)
 where T1 : class
 where T2 : class
 where T3 : class
 {
 return Get(Get(Get(p1, p2), p3), p4);
 }
 }
 

UPDATE:

Below is the alternative way by using extension methods. It is really your preference to decide which way is better. Above approach might have slightly less overhead since it just adds a single function call in the IL code and is more concise for short expressions, but the code is shorter in the bottom approach and visually looks better for larger expressions.


 public static TOut NullSafe<TIn, TOut>(this TIn obj, Func<TIn, TOut> memberAction)
 {
 //Note we should not use obj != null because it can not test value types and also
 //compiler has to lift the type to a nullable type for doing the comparision with null.
 return (EqualityComparer<TIn>.Default.Equals(obj, default(TIn))) ? memberAction(obj) : default(TOut);

 }

 //Usage:..

 Employee.NullSafe( e => e.Supervisor ).NullSafe( s => s.Boss ).NullSafe( b => b.Name );

 
Posted in C#. 6 Comments »

Authorization Rule Library in SAF and how to use it with RIA Services and Silverlight

A problem of applying rules by decorating objects using attributes is that there is no concept of re-usability. For example, we want NationalAdmins, StateAdmins, and Managers to be have all permissions over everything. Also permissions for my customer, customer_address, customer_emails, etc. are all similar. Normally you should apply all authorization attributes to every part of the model.

SAF provides an ImportAuthorizationAttribute that solves this problem.

public class AuthorizationMetadata
{
    [Grant...]
    [Grant...]
    public class AdminGroup { }

    [Deny...]
    [Deny...]
    [Deny...]
    public class RestrictedWriteGroup {}

    [Grant...]
    public class TitleViewersGroup {}
}

You can use AuthorizationMetadata class to centralize all your authorization rules. You can then use it in your model as follows:

[ImportAuthorization(SourceType=AuthorizationMetadata.AdminGroup)]
public partial class Model
{
}

What else do you want?

Refactoring SAF to be de coupled from the Authorization Attributes

SAF framework look at the metadata type to figure out the authorization rules. Although this is exactly what we want, but is a design flow. The reason I think it is a design flow and needs re-factoring is that it makes the rules to be defined in code.

Let’s imagine we want to seperate the role of Authorization rule definers from the programmers. Although, it is possible now, to implement IMetadataTypeProvider to load some external dll, but it is still bound to types which is not really necessary.

This means, the Grant and Deny attributes should not be limited to be attributes. It is possible with a small re factoring of the current code.

Let’s define a rule provider: IAuthorizationRuleProvider which retrieves rules for a given object. Grant and Deny are IPrincipalAuthorizer. We can give the IAuthorizationRuleProvider instead of to IMetadataProvier to the methods in PermissionHelper.

Then we need to define the AttributeAuthorizationRuleProvider which has a method GetAuthorizers and relies on an IMetadataProvider.

	public class AttributeAuthorizationRuleProvider<T> : IAuthorizationRuleProvider
	{
		private MetadataProvider _metadataProvider;

		public AttributeAuthorizatonRuleProvider(MetadataProvider meta)
		{
			_metadataProvider = meta;
		}

		public IEnumerable<IPrincipalAuthorizer<T>> GetAuthorizers(Type type)
		{
			var meta = metadataProvider.GetMetadataType(type);

            //Get all attributes for the type
            return meta.GetCustomAttributes(false).OfType<IPrincipalAuthorizer<Permission>>();
		}
	}

We later need an AuthorizationContext as a singleton like:

	public class AuthorizationContext
	{
		IMetadataProvider _metadataProvider;
		IAuthorizationRuleProvider _authorizationRuleProvider;
		public AuthorizationContext(IMetadataProvider meta, IAuthorizationRuleProvider rule)
		{
			_metadataProvider = meta;
			_authorizationRuleProvider = rule;
		}

		//Wrap all the methods in Permission Helper.
	}

Later, we can define a new authorizationRuleProvider called SqlAurhorizationRuleProvider that uses database for authorization rule and in database we can have a table like:
TABLE Authorizations (AuthID, Type, Property)
TABLE AuthorizationRules (RuleID, AuthID, RuleType (Grand/Deny/Custom), ConditionCode, ConditionQuery)
TABLE AuthorizationRuleAssignments (RuleID, Role)
In my providers I avoid using the traditional Provider model because the complexity is not required here and using IoC and a Context object, eliminates the role of Manager for every single provider.

Simple Authorization in RIA Services and Silverlight using SAF-Framework

SAF is designed with simplicity and extensibility in mind. All you have to do to use it in RIA-Services and Silverlight  to manage end-to-end security is not much. The following example shows how to use SAF to manage Authorization in a project with RIA services and Silverlight.

We have an Australia wide shop. We use ASP.Net membership provider. And three Roles: Customer, StateAdmin, and NationalAdmin.

1. In your data model:

Assign security attributes:

	[MetadataType(typeof(Metadata)]
	public partial class ShoppingItem
	{
		[Grant(Roles=new[]{"StateAdmin"}, Permissions = Permission.All)]
		[Grant(Roles=new[]{"NationalAdmin"}, Permissions = Permission.All)]
		[Deny(Roles=new[]{"StateAdmin"}, Permissions = Permission.All, ConditionMethod = "NotInState")]
                [Grant(Roles=new[]{"Customer"}, Permissions = Permission.View)]
		[Deny(Roles=new[]{"Customer"}, Permissions = Permission.View, ConditionMethod = "NotInState")]
		public class Metadata
		{
			public static bool NotInState(object instance, IPrincipal principal)
			{
				var user = principal as CustomUser;
				var shoppingItem = instance as ShoppingItem;
				//You should check for null in production code
				if (!user.ActivityStates.Contains(instance.State))
					return true;
				return false;
			}

			[Deny(Roles=new[]{"Customer"}, Permission.View)]
			public decimal PriceThatWeBought { get; set; };

			[Deny(Roles=new[]{"StateAdmin"}, Permission.Edit)]
			public decimal PriceThatWeSell {get; set; };
		}
	}

Above code means that you have a ShoppingItem in your model, and following security rules apply:

  1. Customers only can view items and can not change anything.
  2. State admins have all the rights over items in their states of activity. They have no access to anything in other states. (They can view other state items only if they have Customer role as well and this is because the grant view to customer if after deny to the StateAdmin)
  3. NationalAdmins can do anything
  4. Customers can not see the PriceThatWeBought
  5. SateAdmins can not change the PriceThatWeSell because CEO only trusts himself.

As you can see writing the above rules in code is as easy as describing them in English.

2. At your Service Level:

You have three methods. One to get a specific shopping item. Second to get all shopping items. And third to save a shopping item. Here is all the code you have to write:

	public partial class MyDomainService
	{
		public Tuple<ShoppingItem, AuthorizationToken> GetShoppingItemById(int id)
		{
			DomainContext.ShoppingItems
				.Where( s => s.Id = id)
				.Select(s => s)
				.FilterUnAtuthorizedWithToken()  //What user should not see will be scapped here.
				.First();
		}

		public IEnumerable<Tuple<ShoppingItem, AuthorizationToken>> GetShoppingItems()
		{
			DomainContext.ShoppingItems
				.Select(s => s)
				.FilterUnAtuthorizedWithToken()  //What user should not see will be scapped here.
				.First();
		}

		[RequiresPermission(Permissions.Write)]
		public SaveShoppingItem(ShoppingItem item)
		{
			DmoainContext.SaveChanges();
		}
	}

We have only written three lines of code here to do all the authorization:
First and Second are the FilterUnAuthorizedWithToken() extension method that filters everything based on the user’s privileges and scraps fields that user should not see like PriceThatWeBought.
Third is the RequresPremission attribute which infers only someone can run this method that has Write permission over ShoppingItem object. For example an StateAdmin of Queensland can not edit a shopping item from NSW.

You can see the return types are Tuple<ShoppingItem, AuthorizationToken>. This is because the FilterUnAuthorizedWithToken() will attach an authorization token to any item returned. You can use FilterUnAuthorized() method if you don’t want an authorization token.

3. At the View (and ViewModel)

In you silverlight, the data context for your view is your view model.

	public class ShoppingItemViewModel
	{
		private ShoppingItem _shoppingItem;
		private AuthorizationToken _authorizationToken;

		public void Load()
		{
			//Load the data and authorization tokens here.
		}

		public string ItemName {get{...}set{...}}
		public bool ItemNameVisible {get{return _authorizationToken.IsVisible("ItemName");}}
		public bool ItemNameEditable {get{return _authorizationToken.IsEditable("ItemName");}}

		//Do the same for all other properties.
	}

The only change here is the AuthorizationToken which we saw is return by the FilterUnAuthorizedWithToken() method in the previous section.
Now you have to specify the visibility and editability properties and bind them to the visibility and editability of you XAML controls.
If you want to be more smart in the client side you can write a converter for visibility and editability. And instead of using strings in your code for “ItemName” you can use the property.Name to be type safe.
The magic here is that you can change anything in your authorization rules and policies, but the only place you have to change is your model. You should not worry about the service or view, they get updated auto-magically.

Automatic End to End Authorization (Part 3)

I promised to show you how does the SAF framework really do the magic. I changed my test model from my last post a little bit so I post it back again here:

<pre> [Grant(Roles = new[] { "Everyone" }, Permission = Permission.View)]
[Grant(Roles = new[] { "QLDAdmin", "NSWAdmin" }, Permission = Permission.View | Permission.Edit | Permission.Create)]
[Grant(Roles = new[] { "Deleter" }, Permission = Permission.Delete)]
[Grant(Roles = new[] { "God" }, Permission = Permission.View | Permission.Own)]
[Deny(Roles = new[] { "NSWAdmin" }, Permission = Permission.Edit)]
[AuthorizationCustom(CustomType = typeof(TestObject), Method = "OnlyQld")]
public class TestObject
{
[Grant(Roles = new[] { "Everyone" }, Permission = Permission.View)]
[Deny(Roles = new[] { "God" }, Permission = Permission.View)]
public int YouCanSeeMe { get; set; }

[Grant(Roles = new[] { "Everyone" }, Permission = Permission.Edit)]
[Deny(Roles = new[] { "God" }, Permission = Permission.Edit | Permission.View)]
public string YouCanEditMe { get; set; }

[Deny(Roles = new[] { "Everyone" }, Permission = Permission.View)]
public string YouCanNotSeeMe { get; set; }

[Deny(Roles = new[] { "Everyone" }, Permission = Permission.Edit)]
public string YouCanSeeMeButNotEditMe { get; set; }

[Grant(Roles = new[] { "*" }, Permission = Permission.View)]
[Deny(Roles = new[] { "QLDMan" }, Permission = Permission.All)]
public string EverybodyCanSeeMe { get; set; }

public string[] States { get; set; }

public static Permission? OnlyQld(IPrincipal pri, object instance)
{
var user = pri as TestUser;
if (user.Roles.Any(r => r.Contains("QLD"))
&&
(((TestObject)instance).States != null && ((TestObject)instance).States.Contains("QLD"))
)
return Permission.All;
return null;
}
}</pre>

Note that the Permission for the custom authorization method is nullable this time. It actually won’t work if the return type is not nullable.

Now the magic comes:

  1. Filter what user should not see automatically using FilterAuthorized() extension method.
  2. Send authorization tokens to client. Client can use AuthorizationToken.Visible or AuthorizationToken.Editable methods to bind view parts to data.

Below is an example case for more clarity:

<pre>[TestMethod()]
public void FilterAuthorizedTest()
{
IMetadataClassProvider sm = new SelfMetadata();
var everyone = new TestUser() { Roles = new[] { "Everyone" } };
var qldMan = new TestUser() { Roles = new[] { "QLDMan" } };

var coll = new[]
{
new TestObject() {YouCanSeeMe = 0, States = new[] {"QLD", "NSW"}},
new TestObject() {YouCanSeeMe = 1},
new TestObject() {YouCanSeeMe = 2, YouCanNotSeeMe = "a", States = new[] {"QLD"}},
new TestObject() {YouCanSeeMe = 3},
new TestObject() {YouCanSeeMe = 4, YouCanNotSeeMe = "b"}
};
var filtered = coll.FilterAuthorized(sm, everyone).ToList();
Assert.AreEqual(5, filtered.Count());

filtered = coll.FilterAuthorized(sm, qldMan).ToList();
Assert.AreEqual(2, filtered.Count());
}</pre>

The framework auto-magically cares about what user can see and what he can’t see.

In case you were not following me, here is the link to SAF Framework.

The major hole here is that the framework does not scrap properties that are not available to user at this stage. This should be a quick fix once I get some more time. Feel free, if you like to contribute. The architecture is quiet extensible and easy to understand.

Automatic End to End Authorization (Part 2)

In my previous post, I talked about the importance of an end to end authorization system. I had an open source project here a while ago for this purpose. I describe the framework with some example here. An old saying says that a piece of code talk more than 1000 words:

Below is a sample class annotated with different authentication attributes.

<pre>
[Grant(Roles = new[] { "Everyone" }, Permission = Permission.View)]
[Grant(Roles = new[] { "QLDAdmin", "NSWAdmin" }, Permission = Permission.View | Permission.Edit | Permission.Create)]
[Grant(Roles = new[] { "Deleter" }, Permission = Permission.Delete)]
[Grant(Roles = new[] { "God" }, Permission = Permission.View | Permission.Own)]
[Deny(Roles = new[] { "NSWAdmin" }, Permission = Permission.Edit)]
[AuthorizationCustom(CustomType = typeof(TestObject), Method = "OnlyQld")]
public class TestObject
{
[Grant(Roles = new[] { "Everyone" }, Permission = Permission.View)]
[Deny(Roles = new[] { "God" }, Permission = Permission.View)]
public int YouCanSeeMe { get; set; }

[Grant(Roles = new[] { "Everyone" }, Permission = Permission.Edit)]
[Deny(Roles = new[] { "God" }, Permission = Permission.Edit | Permission.View)]
public string YouCanEditMe { get; set; }

[Deny(Roles = new[] { "Everyone" }, Permission = Permission.View)]
public string YouCanNotSeeMe { get; set; }

[Deny(Roles = new[] { "Everyone" }, Permission = Permission.Edit)]
public string YouCanSeeMeButNotEditMe { get; set; }

[Grant(Roles = new[] { "*" }, Permission = Permission.View)]
public string EverybodyCanSeeMe { get; set; }

public string[] States { get; set; }

public static Permission OnlyQld( IPrincipal pri, object instance )
{
var user = pri as TestUser;
if (user.Roles.Any(r => r.Contains("QLD"))
&&
(((TestObject)instance).States != null && ((TestObject)instance).States.Contains("QLD"))
)
return Permission.All;
return Permission.None;
}
}

</pre>

Next step is to check the permission for them in different scenarios:

<pre>[TestMethod()]
public void GetObjectLevelPremissionTest()
{
IMetadataClassProvider metadataProvider = new SelfMetadata();
var to = new TestObject();
var everyone = new TestUser() { Roles = new[] { "Everyone" } };
var god = new TestUser() { Roles = new[] { "God" } };
var adminNsw = new TestUser() { Roles = new[] { "NSWAdmin" } };
var adminQld = new TestUser() { Roles = new[] { "QLDAdmin" } };
var userWA = new TestUser() { Roles = new[] { "WAuser" } };
var userQLD = new TestUser() { Roles = new[] { "QLDuser" } };

var actual = PermissionHelper.GetObjectLevelPremission(metadataProvider, typeof(TestObject), to, everyone);
Assert.AreEqual(true, actual.Key.HasFlag(Permission.View));
Assert.AreEqual(false, actual.Key.HasFlag(Permission.Edit));
Assert.AreEqual(false, actual.Key.HasFlag(Permission.Create));

actual = PermissionHelper.GetObjectLevelPremission(metadataProvider, typeof(TestObject), to, god);
Assert.AreEqual(true, actual.Key.HasFlag(Permission.Own));

actual = PermissionHelper.GetObjectLevelPremission(metadataProvider, typeof(TestObject), to, adminNsw);
Assert.AreEqual(false, actual.Key.HasFlag(Permission.Edit));
Assert.AreEqual(true, actual.Key.HasFlag(Permission.View));

actual = PermissionHelper.GetObjectLevelPremission(metadataProvider, typeof(TestObject), to, adminQld);
Assert.AreEqual(true, actual.Key.HasFlag(Permission.Edit));
Assert.AreEqual(true, actual.Key.HasFlag(Permission.View));

to = new TestObject() { States = new[] { "QLD", "NSW" } };
actual = PermissionHelper.GetObjectLevelPremission(metadataProvider, typeof(TestObject), to, userWA);
Assert.AreEqual(false, actual.Key.HasFlag(Permission.View));

actual = PermissionHelper.GetObjectLevelPremission(metadataProvider, typeof(TestObject), to, userQLD);
Assert.AreEqual(true, actual.Key.HasFlag(Permission.View));
Assert.AreEqual(true, actual.Key.HasFlag(Permission.Create));

actual = PermissionHelper.GetObjectLevelPremission(metadataProvider, typeof(TestObject), to, adminQld);
Assert.AreEqual(true, actual.Key.HasFlag(Permission.Delete));
Assert.AreEqual(true, actual.Key.HasFlag(Permission.View));
}</pre>

Above code tests the permissions at the object level. We can get permissions at property level also in an array:

<pre>[TestMethod()]
public void GetPropertyLevelPremissionsTest()
{
IMetadataClassProvider metadataProvider = new SelfMetadata();
var to = new TestObject();
var everyone = new TestUser() { Roles = new[] { "Everyone" } };
var god = new TestUser() { Roles = new[] { "God" } };
var anon = new TestUser() { Roles = new string[0] };

var parent = PermissionHelper.GetObjectLevelPremission(metadataProvider, typeof(TestObject), to, everyone);
var actual = PermissionHelper.GetPropertyLevelPremissions(metadataProvider, parent, typeof(TestObject), to, everyone);
Assert.AreEqual(true, actual["YouCanSeeMe"].Key.HasFlag(Permission.View));
Assert.AreEqual(false, actual["YouCanSeeMe"].Key.HasFlag(Permission.Edit));
Assert.AreEqual(false, actual["YouCanSeeMe"].Key.HasFlag(Permission.Create));
Assert.AreEqual(false, actual["YouCanNotSeeMe"].Key.HasFlag(Permission.View));
Assert.AreEqual(true, actual["YouCanSeeMeButNotEditMe"].Key.HasFlag(Permission.View));
Assert.AreEqual(false, actual["YouCanSeeMeButNotEditMe"].Key.HasFlag(Permission.Edit));

parent = PermissionHelper.GetObjectLevelPremission(metadataProvider, typeof(TestObject), to, god);
actual = PermissionHelper.GetPropertyLevelPremissions(metadataProvider, parent, typeof(TestObject), to, god);
Assert.AreEqual(false, actual["YouCanSeeMe"].Key.HasFlag(Permission.View));
Assert.AreEqual(false, actual["YouCanEditMe"].Key.HasFlag(Permission.View));
Assert.AreEqual(false, actual["YouCanEditMe"].Key.HasFlag(Permission.Edit));
Assert.AreEqual(false, actual.ContainsKey("YouCanNotSeeMe"));

parent = null;
actual = PermissionHelper.GetPropertyLevelPremissions(metadataProvider, parent, typeof(TestObject), to, everyone);
Assert.AreEqual(true, actual["YouCanSeeMe"].Key.HasFlag(Permission.View));

parent = PermissionHelper.GetObjectLevelPremission(metadataProvider, typeof(TestObject), to, anon);
actual = PermissionHelper.GetPropertyLevelPremissions(metadataProvider, parent, typeof(TestObject), to, anon);
Assert.AreEqual(true, actual["EverybodyCanSeeMe"].Key.HasFlag(Permission.View));
}</pre>

Don’t get too excited. So far we haven’t seen much. The real beauty is when we get to automatically filter collections and set the client view visibility using the framework with one line of code.

To be continued…