The conception, birth, and first steps of an application named Charlie

Subscribe: Atom or RSS

A Beautiful Cascade

by Alister Jones (SomeNewKid)

After starting a weblog entry with that title, I had better keep my promise and show you a beautiful cascade, because the actual cascade I want to discuss is hardly beautiful.

Now that I have been true to my word, let me bore you with the actual cascading idea that is cropping up continually in the development of Charlie.

Let’s say that Charlie has an Articles plugin that allows a website to display—ten points if you guess this—articles. By default, users in the Anonymous role may view an article, but cannot create, edit, or delete an article. A further default is that users in the Owner role may view, create, edit, and delete an article. The Articles plugin also provides the ability for readers to comment on an article. Users in the Anonymous role may view and create a comment, but cannot edit or delete a comment. Users in the Owner role may view, create, edit, and delete a comment. Standard stuff.

One feature of Charlie is that it will serve multiple websites from a single installation, and a further feature of Charlie is that it will be highly configurable. These two features together mean that a particular website must be able to override these default roles. If Jennifer Aniston uses Charlie to maintain her website (and you may do so for no charge, dear lady), she may specify that users in the Author role may view, create, and edit, but not delete, articles, while users in the Editor role may view and edit, but not create or delete, articles. These are “more specific” roles than the default roles, because they are specific to her domain.

Jennifer then elects to include a members-only section on her website. For an article in this section, she needs to override the default that Anonymous users may view the article. She may also choose to override the “more specific” roles above, such that only the Owner role may create or edit the article. That is, neither an Author nor an Editor may edit one of these articles. These are the “most specific” roles that may apply to an article provided by the Articles plugin.

This meandering narrative not only reveals my affection for Jennifer, it also reveals just how fine-grained Charlie’s authorization needs to be.

Well, no way hosay am I going to implement such fine-grained authorization in each and every entity for each and every plugin. For this reason, I implemented a cascading authorization system. To start with, each plugin registers the entity types that it provides (so the Articles plugin would register both its Charlie.Articles.Business.Article entity and its Charlie.Articles.Business.Comment entity). Here is a screenshot of the table in SQL Enterprise Manager:

Then, the ArticlePlugin specifies the default CRUD roles for each of those entity types. It is specified that for EntityType_Id of 7 (look above to see that this means an Article entity), the Role_Id of 0 (which is an Anonymous role) has permission only to Retrieve the entity. For the same EntityType_Id of 7 (an Article), the Role_Id of 2 (which is an Administrator) has permission to Create, Retrieve, Update, and Delete the entity. These are default permissions, since the Domain_Id is -1 (a non-specified domain) and the Entity_Id is -1 (a non-specified entity).

Now, Jennifer gets her Charlie site, and decides that the Administrator of her site should not be able to delete articles. So, she can specify that for her domain (which has an ID value of 4), the Role_Id of 2 (the Administrator role) can only Create, Retrieve, and Edit an article. This rule applies to all articles, since the Entity_Id value is -1.

Next, Jennifer writes a specific article (its ID is 133) that only members may view. Here she can specify that for her domain (ID value of 4), the Role_Id of 0 (Anonymous user) cannot retrieve this specific article.

The last rule above is the most specific, since it specifies both an Entity_Id and a Domain_Id. The second-last rule is less-specific, since it specifies a Domain_Id but does not specify an Entity_Id. Finally, the first two rules are the least specific, since they do not specify a Domain_Id or an Entity_Id (they are the default rules that apply to all domains and all article entities).

What this means is that Charlie applies a specificity test to determine the authorization rules that apply to a given entity. First, any default rules are applied. Then, any domain-specific rules are applied. Finally, any domain-specific and entity-specific rules are applied. This is done in a cascading fashion, where the more specific rules will override the less specific rules.

The logic to implement the cascading authorization rules is placed in the base EntityMapper class. Currently, the cascading logic is in the C# persistence code, which is not ideal. I would like to move as much of this logic to the database as possible, but my SQL skills are not up to the task. I suspect that I will be engaging a database expert before too long.

Having the cascading authorization rules applied deep down in the Entity System provides two primary benefits.

First, it means that a plugin’s business classes, such as the Article entity and its ArticleMapper, do not have to worry about this background stuff. It’s all taken care of by the base Entity, EntityManager, and EntityMapper. As an example, here is the code required in the ArticleMapper’s Retrieve method:

// Add CRUD roles to the article
if (article != null)
{
    base.AddRolesToEntity(article, criteria, connection);
}

Simple, huh? Even better, if a mapper does not call this base method, the EntityManager checks whether the roles have been loaded, and will make its own call to the AddRolesToEntity method if needed (see Charlie’s Approach to Authorization). It is better if the mapper does it, because it can pass an open database connection to this base method. If the EntityManager must do the job, a new connection is required. Either way though, this is all taken care of by the Entity System.

The second benefit is that *I* do not have to worry about this background stuff. If I create a new Plugin, all I must do is register the new entity types—a single database record each—and the Entity System takes care of the authorization details.

This cascading approach has proven so simple and so flexible that I ended up applying it to Charlie’s globalization code, too.

by Alister Jones | Next up: Charlie’s Approach to Globalization

0 comments

----