NCommet Startup Guide

In this guide, we will try to describe the steps that are needed in order to build an application based on NCommet. This application will be a blog application with the following requirements:
  • It will be able to host many blogs.
  • Each blog will have a post and each post will have comments.

Business Model

We begin by creating the business model of our application. The required steps are the following:
  • We create a project that contains the Content classes and the item types.
  • We create a demo console project
Lets start by creating a new class library project that contains our Content classes. This project will contain the business model of our application and it's best to be in a separate assembly so that it can be used by different UI's. In this project we create the content classes Blog, Post and Comment. All of these inherit the class NCommet.ContentBase (a requirement of NCommet). These classes are simple POCO objects with the properties that are needed.

Blog class

public class Blog : NCommet.Core.ContentBase
{ 
    private string title;
    public string Title
    {
        get { return title; }
        set { title = value; }
    }
} 

Post class

public class Post : NCommet.Core.ContentBase
{ 
    private string title;
    public string Title
    {
        get { return title; }
        set { title = value; }
    }

    private string author;
    public string Author
    {
        get { return author; }
        set { author = value; }
    }

}

Comment class

public class Comment : NCommet.Core.ContentBase
{ 
    private string body;
    public string Body
    {
        get { return body; }
        set { body = value; }
    }

    private string author;
    public string Author
    {
        get { return author; }
        set { author = value; }
    }

}

Note. The content classes simply contain the properties of the entities they describe. They don't specify relationships between entities. The relationships are specified between items in NCommet and not between contents.

We must now define the item types that we are going to use. It would be best to create a static class that would contain them as constants. For example

public static class ItemTypes
{
    public const string BLOG = "blog";
    public const string POST = "post";
    public const string COMMENT = "comment";
}

We can now create an application to test our business model. For simplicity, we will create a console application. We must define NCommet.Core as a reference along with the assembly that describes our business model. After doing so, we can create a blog using the following code:

Item itBlog = Item.Create(ItemTypes.BLOG);
Blog blog = new Blog();
blog.Title = "My Blog";
itBlog.Content = blog;

We can now add a post to the blog we created using the following code:

Item itPost = Item.Create(ItemTypes.POST);
Post post = new Post();
post.Title = "My first post";
post.Body = "Welcome to my blog!";
itPost.Content = post;

itBlog.Children.Add(itPost);

Finally, we can add a comment to the post we created using the following code:

Item itComment = Item.Create(ItemTypes.COMMENT);
Comment comment = new Comment();
comment.Body = "Have a nice start!";
comment.Author = "Anonymous";
itComment.Content = comment;

itPost.Children.Add(itComment);

All of the above are nice and easy but not really usefull since we can't store anything. To be a able to store them in a database we need to proceed to the first agent of NCommet, the IPersister and its default implementation that exists in the project NCommet.Modules.Dao.

Pestistence with NCommet.Modules.Dao

NCommet.Modules.Dao is a project that implements IPersister based on NHibernate. To be able to use it we need to define all the content classes we created as Serializable and all their public properties and methods as virtual. For example, the Blog class would become something like this:

[Serializable]
public class Blog : NCommet.Core.ContentBase
{ 
    private string title;
    public virtual Title
    {
        get { return title; }
        set { title = value; }
    }
} 

In addition, we have to add the hbm files that are needed in order for NHibernate to find our classes. These files are very simple as shown below:

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
  <subclass name="Blogs.BusinessModel.Blog, Blogs.BusinessModel"
                 extends="NCommet.Core.ContentBase, NCommet.Core" >
  </subclass>
</hibernate-mapping>

We can have one hbm file for all the content classes or one hbm file per content class. The important thing is that these hbm files are included in the assembly as an embedded resource.

XML Serialization

ContentBase class contains a property with the name SerializedData. The value of this property is the XML representation of the content object. Setting this property results in deserialization of the XML and assignment of all the fields in the corresponing content class represented by the XML.

When NHibernate stores any obejct inheriting from ContentBase class in the database, it actually stores its XML serialization along with a field indicating its type (i.e. Blogs.BusinessModel.Blog). Using this method, the database doesn't need to know the real fields inside the content classes so that any class can be stored as long as it's serializable and inherits from NCommet.Core.ContentBase.

We must now instruct NCommet that we want to use NCommet.Modules.Dao as the Persister and fine tune NHibernate. This is the time to look to the configuration file of our application.

Configuration File

We add the following lines in the app.config file of the console project we created:

<configSections>
  <section name="nhibernate" type="System.Configuration.NameValueSectionHandler, System,Version=1.0.1.0,Culture=neutral, PublicKeyToken=b77a5c561934e089"/>
  <section name="ncommet" type="NCommet.Core.Configuration.NCommetCfgSection, NCommet.Core"/>
</configSections>

As we can see, we added one configSection for NHibernate (in which NCommet.Modules.Dao is based on) and one configSection for NCommet.

NHibernate's section contains information about the database system we are using and some other things that are specific to NHibernate.

<nhibernate>
  <add key="hibernate.connection.provider" value="NHibernate.Connection.DriverConnectionProvider"/>
  <add key="hibernate.dialect" value="NHibernate.Dialect.MsSql2000Dialect"/>
  <add key="hibernate.connection.driver_class" value="NHibernate.Driver.SqlClientDriver"/>
  <add key="hibernate.connection.connection_string" value="Data Source=TOPI\SQLEXPRESS;Database=Blogs;Trusted_Connection=yes;"/>
  <add key="hibernate.connection.isolation" value="ReadCommitted"/>
  <add key="hibernate.default_schema" value="Blogs.dbo"/>
  <add key="hibernate.show_sql" value="false"/>
  <add key="hibernate.use_outer_join" value="true"/>
  <add key="hibernate.query.substitutions" value="true 1, false 0, yes 'Y', no 'N'"/>
</nhibernate>

Inside NCommet's section we define the persister we are using:

<ncommet persister="NCommet.Modules.Dao.Storage, NCommet.Modules.Dao">
</ncommet>

Note. NCommet.Modules.Dao is not necessary to be included as a reference in our console project. However, the assembly needs to be accessible from the runable file so that NCommet.Module.Dao implementation is loaded.

The last thing we have to do is instructing NHibernate about the dll's that include the hbm files. This is done by adding the following in the appSettings section:

<appSettings>
  <add key="HBM_ASSEMBLY_COUNT" value="2"/>
  <add key="HBM_ASSEMBLY_1" value="NCommet.Modules.Dao"/>
  <add key="HBM_ASSEMBLY_2" value="Blogs.BusinessModel"/>
</appSettings>

The configuration file of our application is now ready and we have support for persistence of our business model. In order for NCommet to read the appConfig file we need to call the following method:

NCommet.Core.Configuration.Configurator.Configure();

This call is better be done when we start our application, i.e. in the beginning of the Main method. For a web application, the right place to add this call is in the Application_Start method of global.asax file.

The last thing we need to make is to copy all the necessary dll's inside the folder of our console project:
  • NCommet.Modules.Dao
  • log4net
  • NHibernate
  • Castle.DynamicProxy
  • Iesi.Collections

We can now store and retrieve our objects:

Item itBlog = Item.Create(ItemTypes.BLOG);
Blog blog = new Blog();
blog.Title = "My Blog";
itBlog.Content = blog;
itBlog.Save();
Console.WriteLine(itBlog.Id);

Altering the business model

Altering the classes of our business model is very easy. If we want to add a new class or add/remove fields from an existing class, we simply do so. We don't need to change anything else. This is because xml serialization accepts these kind of changes, since the only thing that changes is the xml that is stored inside the database, which now contains the changes we made.

For example, let's add a new field in the Blog class containing the url of the blog:

[Serializable]
public class Blog : NCommet.Core.ContentBase
{ 
    private string title;
    public virtual Title
    {
        get { return title; }
        set { title = value; }
    }

    private string url;
    public virtual Url
    {
        get { return url; }
        set { url = value; }
    }
} 

Typed Tree Validation

With all of the above, we have a business model that covers all the requirements of our application but there are still some things that need attention.

Because NCommet.Core is based on the core classes Item and ContentBase, it doesn't know the restrictions on the relation between parent and child. In other words, every item (with an associated content class) can be set as a child of another item (with another associated content class) in the hierarchy that's handled by NCommet.Core. However, this is not acceptible, since in our blog application we have set the following restrictions:
  1. Each blog has any number of posts.
  2. Each post has any number of comments.

NCommet solves this problem by adding validation rules through its interface ITypedTreeValidator. This interface supports the following validation rules:
  1. If an item can be set as a child of another item.
  2. If an item can be set as a root item (i.e. an item without a parent).
  3. If an item can be detached from its parent item.
  4. If an item can accept an object in the business model as its content.

To apply these rules, we only need to build an implementation of the ITypedTreeValidator and define it in the configuration file of our application. After NCommet is aware of this implementation, it begins calling these validation rules whenever needed.

One available implementation exists and it's located in the project NCommet.Modules.BRE. It's based on the rule engine of WWF. The implementation requires an xml file that describes the rules. This file is created using the External Ruleset Demo toolkit.

Now lets try to create our own implementation of ITypedTreeValidator.

BlogsTTV: Implementing ITypedTreeValidator

For the implementation of ITypedTreeValidator we create a new class library project and define the BlogsTTV class that implements the interface. With Visual Studio, we can automatically have the methods defined in the interface created, thus saving ourselves from some typing.

The interface's methods are divided in two categories. The decision methods that answer if an action can be performed (i.e. if an item can be set as the root) and the information methods that provide all the valid values for some actions (i.e. the item types of an item that can become a root).

Decision Methods

The decision methods answer the following questions:
  1. Can an item be set as a root; (method CanSetRoot)
  2. Can an item be set as a child of another item; (method CanAttach)
  3. Can an item be detached from its parent; (method CanRemove)
  4. Can an object in our business model be set as the content of an item; (method CanContain)
Every method has one overload with an extra out parameter where we can store any exception messages.

In the case of our blog application, the implementation of CanSetRoot is easy since we know that only a blog can be set as a root:

public bool CanSetRoot(Item CandidateRoot)
{
    return CandidateRoot.ItemType == ItemTypes.BLOG;
}

The rest of the methods are equally easy:

public bool CanAttach(Item Parent, Item CandidateChild)
{
    return
        (Parent.ItemType == ItemTypes.BLOG && CandidateChild.ItemType == ItemTypes.POST) ||
        (Parent.ItemType == ItemTypes.POST && CandidateChild.ItemType == ItemTypes.COMMENT));
}

public bool CanContain(Item Container, ContentBase CandidateContent)
{
    return
        (Container.ItemType == ItemTypes.BLOG && CandidateContent is Blog) ||
        (Container.ItemType == ItemTypes.POST && CandidateContent is Post) ||
        (Container.ItemType == ItemTypes.COMMENT && CandidateContent is Comment);
}

public bool CanRemove(Item Parent, Item Child)
{
    return true;
}


Note. The overloads of those methods are not included in our example for simplicity.

Information methods

Things are also easy here. Because each method returns a list of strings and because in every case of our blog application only one item is returned, we add a helper method for this reason.

private static ICollection ToCol(string oneItem)
{
    List result = new List();
    result.Add(oneItem);
    return result;
}

public System.Collections.Generic.ICollection GetValidChildItemTypes(Item Parent)
{
    if (Parent.ItemType == ItemTypes.BLOG)
    {
        return ToCol(ItemTypes.BLOG);
    }
    else if (Parent.ItemType == ItemTypes.POST)
    {
        return ToCol(ItemTypes.COMMENT);
    }
    else
    {
        return new List();
    }
}

public System.Collections.Generic.ICollection GetValidContentTypes(Item Container)
{
    switch (Container.ItemType)
    {
        case ItemTypes.BLOG:
            return ToCol(typeof(Blog).AssemblyQualifiedName);
        case ItemTypes.POST:
            return ToCol(typeof(Post).AssemblyQualifiedName);
        case ItemTypes.COMMENT:
            return ToCol(typeof(Comment).AssemblyQualifiedName);
        default:
            return null;
    }
}

public System.Collections.Generic.ICollection GetValidRootItemTypes()
{
    return ToCol(ItemTypes.BLOG);
}

The above conclude the BlogsTTV class that implements the interface ITypedTreeValidator in the our blog application scenario.

Using BlogsTTV

To use the class we created we need to instruct NCommet that this is the implementation of ITypedTreeValidator that we want to use. As in the case of persistence, this is done through the configuration file of our application. What we have to do is go to the app.config file and add the typedTreeValidator attribute:

<ncommet persister="NCommet.Modules.Dao.Storage, NCommet.Modules.Dao"
                 typedTreeValidator="Blogs.TypedTreeValidator.BlogsTTV, Blogs.TypedTreeValidator">
</ncommet>

Note. The easy way for our application to find the dll is to add it as a reference but as we said earlier this is not necessary. We can just copy it in the application folder since it is not used directly.

To use the typed tree validator we can try to do something that's not allowed and confirm that this will throw an exception. All exceptions that are related with typed tree validation are of type NCommet.Core.Exceptions.ValidationException.

Note. The rule regarding whether an item can be set as a root or not is checked only when the item is stored in the database. This is due to the fact that each time an item is created it doesn't have a parent and is typically a root. This means that this rule is working only if a persister exists.

The following code that tries to add a blog under a post throws an exception, ensuring that the hierarchy we created is always in a valid state:

Item itBlog = Item.Create(ItemTypes.BLOG);
Blog blog = new Blog();
blog.Title = "My Blog";
itBlog.Content = blog;

Item itPost = Item.Create(ItemTypes.POST);
Post post = new Post();
post.Title = "My first post";
post.Body = "Welcome to my blog!";
itPost.Content = post;

itPost.Children.Add(itBlog);

Note. It is very usefull to perform unit tests to check the validity of our code, when implementing the ITypedTreeValidator. Even though in our application the rules are very easy, a simple mistake can cause our hierarchy to enter a non-valid state, which is not desirable. The unit testing in the case of the implementation of ITypedTreeValidator is based on creating scenarios that we expect to cause an exception and determine if that's what's really happening.

Events

For the extensibility of NCommet's functionality, NCommet.Core raises events for many of the operations that are performed on items (i.e. when an item is added as a child of another item). These events are not inside the Item class but in the singleton class EventSink. If we want to subscribe to any of these events we need to use this class. This way it's possible to watch all the items without needing to subscribe to the events of every available item separately!

Let's create an example in which we will show a message each time an item is added as a child of another item. First we subscribe to the event ItemAdded:

EventSink.Instance.ItemAdded += new ItemHandler(OnItemAdded);

Then, we create the method OnItemAdded, which contains our code:

static void OnItemAdded(object sender, ItemEventArgs e)
{
    Console.WriteLine("Item with content {0} added under item with content {1}", e.Item.Content,e.Item.Parent.Content);
}

This way, we can extend NCommet's functionality by sending for example an e-mail whenever an item of a specific type is stored e.t.c.

Some events also allow us to interfere to the normal functionality and alter it as we prefer to. One such event is ItemDeleting, which is raised before the deletion of an item. We can catch this event and, instead of allowing the deletion of the item, we may move this item under another item that will play the role of a "Recycle Bin".

EventSink.Instance.ItemDeleting += new ItemCancellableHandler(OnItemDeleting);

...

static void OnItemDeleting(object sender, ItemCancellableEventArgs e)
{
    e.Cancel = true;
    e.Item.ChangeParent(itRecycleBin);
}

In the above code, we ask that the deletion process is canceled and that the item that is going to be deleted is placed as a child of the item itRecycleBin, which in this case plays the role of the recycle bin.

DaoEventsSink

The use of a persister somehow complexes the case of events. What happens if we delete an item that has children attached to it; Will they also be deleted automatically or should we cancel this operation throwing an exception; The answer is that the functionality should be the same as if we didn't have a persister in the first place, which is to delete all the children items automatically.

However, the automatic deletion of the children items is done automatically without any of NCommet.Core methods been called. This means that the events regarding these items are not raised. To catch these events, the persister must notify us somehow.

This functionality is provided with the interfaces IDaoEventsSink and IDaoEventsListener. IDaoEventsSink is an interface which informs its subscribers (IDaoEventsListener) for the addition and deletion of items. The implementation of IDaoEventsSink is tightly connected with the persister we are using.

In the case of NCommet.Modules.Dao, the implemenetation is the class DaoInterceptor, which implements the interface IInterceptor of NHibernate. NHibernate provides access to its events and these are the events where DaoInterceptor subscribes to. It then passes them to IDaoEventListener and its own subscribers. This is the Object Adapter design pattern, where DaoInterceptor is the adapter and IDaoEventsListener is the adaptee.

Class EventSink implements the interface IDaoEventsListener and is also subscribing to the events of IDaoEventsSink (if it exists). This way, all the events are coming from the same place (class EventSink) and the user of NCommet.Core is silently unaware that some of the events are coming directly from NCommet.Core and some other from the persister.

We define the implementation of IDaoEventsSink that we are using in the configuration file of our application as follows:

<ncommet persister="NCommet.Modules.Dao.Storage, NCommet.Modules.Dao"
                daoEventsSink="NCommet.Modules.Dao.DaoInterceptor, NCommet.Modules.Dao">
</ncommet>

Note. If NHibernate didn't have an event mechanism like IInterceptor, we wouldn't be able to support this functionality.

NCommetContainer

Until now, we have seen the following interfaces that we can include in the configuration file of our application:
  1. IPersister
  2. ITyperTreeValidator
  3. IDaoEventsSink

NCommet.Core loads the implementation of these interfaces with its Configurator class, so that they can be used. Their instances are stored inside the singleton class NCommetContainer. For example, the persister is accessible through the property NCommetContainer.Instance.Persister. There are similar properties for all the other interfaces as well.

Note. These properties are read/write so that the use of Configurator is not the only way to fill the properties of the instances of the interfaces.

To sum up, NCommetContainer is responsible to contain an instance for the interface implementations and makes them available through properties. NCommetContainer is essentially an object container and resembles the application context of Spring Application Framework.

Other interfaces contained in NCommetContainer are:
  1. ISorter, which allows sorting based on the content of items.
  2. ISearcher, which allows searching based on the content of items.
  3. IAuthorization and IWhoAmI, which allow role-based authorization.

Sorting

The children items of an item have a specific order. This order can be altered by using the methods MoveUp, MoveDown and MoveAt of the Item class. However, there are cases where we want to retrieve the items with some other order and more specifically with an order which has to do with the content of the items. This is supported from the ISorter interface and its sort method. The sort method takes as input a list of item ids and a key and returns an enumeration of items (IEnumerable<Item>) sorted with ascending order by the key. The relationship between the key (which is an integer) and the corresponding field in the content class is done using the attribute NCommet.Code.IndexableAttribute.

Note. ISorter expects a list of ids, which means that a persister is required to assign those ids. Thus, ISorter only works with persisted items. This has been done for two reasons:
  1. To facilitate implementations that use some stored index for the sorting of items.
  2. To allow the functionality of ISorter as a filter that accepts the list that is going to be sorted from another subsystem, which is able to return only ids.

Note. The items to be sorted don't need to have a relation between them (for example to be children of the same item). What's necessary is that they have similar content and their content class to have a property attributed with the IndexableAttribute and the sorting key.

Lets see the above with an example. Inside a blog, the posts are usually in descending chronological order and the post comments in ascending chronological order. To represent this, we will add the following new field in the Post and Comment classes:

private DateTime publishedAt;

[Indexable(1)]
public virtual DateTime PublishedAt
{
    get { return publishedAt; }
    set { publishedAt = value; }
}

As we can see, the property PublishedAt has been marked as Indexable and the sorting key has a value of 1.

To sort the posts of a blog with reverse order, we can write the following method in the Blog class:

{{
public virtual List PostItemsSorted()
{
IList toSort = Item.ToIntList(Container.Children);
List lst = new List(NCommetContainer.Instance.Sorter.Sort(toSort,1));
lst.Reverse();
return lst;
}
*Note.* The method should be virtual since we are using NCommet.Modules.Dao persister. We first take the list with blog item's children ids. We then sort them and reverse the list that's created since we want descending chronological order.

We can also write a helper method that returns a list of Post instead of a list of items using the method ToContentList of the Item class:

public virtual List PostsSorted()
{
    return Item.ToContentList(PostItemsSorted());
}

In order to use the ISorter we must define its implementation in the configuration file of our application. Inside NCommet there are two implementations available: InMemorySorter and DBSorter.

InMemorySorter

NCommet.Modules.InMemorySorter takes as input a list of ids and loads all the items that are stored in the database. The sorting is then performed in memory. This implementation is not optimal if the number of items is very large since we may run out of memory.

DBSorter

NCommet.Modules.DBSorter uses a custom table in the database to store the sorting-keys. This way, the sorting is performed through the SELECT query in the database.

The fields on this custom database table are filled by the class NCommet.Modules.DBSorter.DBIndexer, which implements IModule. During the initialization of the module, a subscription to the events of NCommet.Core is performed and a new thread is started that handles the events. That way, in every storing or deletion of an item, the appropriate table in the database is informed.

The tables used by DBIndexer are shown in the following diagram:

dbindexer-db.png

Note. This implementation only sorts integers, alphanumerics and dates.

Authorization

Until now, our application doesn't have the notion of users and rights. Anyone can see any blog and any post and comment on a post. Lets alter this, by setting the following requirements:
  1. All users can read a blog.
  2. Some users can read a blog and submit a comment on a post.
  3. Some users can read a blog and write posts and comments.

NCommet.Core provides the IAuthorizationManager interface, which supports role based authorization and the assignment of rights on a role over an item. In addition, interface IWhoAmI provides the link to an external system of users and roles, which gives information about who is the logged in user, the roles assigned to him e.t.c. NCommet.Core uses the authorization subsystem internally so that the user doesn't have to do any checks before he performs an action.

The various rights that exist are defined by the enumeration NCommet.Core.Agents.AccessLevel and are the following:
  1. None: No right.
  2. View: Right to retrieve an item from the persistent storage.
  3. Edit: Right to save an item to the persistent storage.
  4. Delete: Right to delete an item from the persistent storage.
  5. Detach: Right to detach an item from its parent.
  6. AddChild: Right to add a child to a parent item.
  7. Everything: All of the above rights.
From the requirements that we set earlier for our blog application, we can see that we have the following user groups:
  1. Viewers (the anonymous user is also included here) that have only the AccessLevel.View right for an item.
  2. Blog Commenters that have AccessLevel.View right for every item and also have AccessLevel.AddChild right for every post in their blog.
  3. Blog Writers that have AccessLevel.View for every item and AccessLevel.Everything right for items in their blog.
The problem now is how we relate users with blogs. As we have already said, we are using role based authorization so the problem is how we give each blog a role and then assign this role to the designated users. One way is to define a field inside the Blog class that contains the name of the role. Another way is to assume that we are dealing only with persisted items, that already have an Id, and create roles of the type "blog editor 42" etc. To avoid depending on the persister we choose the first option and we add the following two fields inside the Blog class:

...

private string editorsRoleName;
public virtual string EditorsRoleName
{
  get { return editorsRoleName; }
  set { editorsRoleName = value; }
}

private string commentersRoleName;
public virtual string CommentersRoleName
{
  get { return commentersRoleName; }
  set { commentersRoleName = value; }
}

...

Implementing IAuthorizationManager

The best way to implement the interface IAuthorizationManager is to use the abstract class NCommet.Modules.Authorization.ReadOnlyAuthorizationManagerBase, which only has two methods to implement (IAuthorizationManager has 10) and it's more appropriate for hard coded cases (the code defines the rights) and immutable (no changes in the rights are allowed) authorization. We create a new project with the name NCommet.Authorization where we will implement this abstract class. In addition, we have to add the project Blogs.BusinessModel as a reference.

Note. The abstract class ReadOnlyAuthorizationManagerBase inherits class AuthorizationManagerBase. That's the class that implements the method which returns the rights of a user over an item as a sum of the user's roles plus the anonymous user's roles. This means that in implementations that are based in this class, the logged in user also has the anonymous user's rights over an item.

The code is relatively short:

namespace Blogs.AuthorizationManager
{
    public class BlogsAM : ReadOnlyAuthorizationManagerBase
    {
        public override AccessLevel GetAccessLevel(Item item, string role)
        {
            Blog blog = item.GetRootAncestor().Content as Blog;
            if (blog.EditorsRoleName == role)
            {
                return AccessLevel.Everything;
            }
            else if (AnonymousRoleName == role) 
            {
                return AccessLevel.View;
            }
            else if (blog.CommentersRoleName == role)
            {
                if (item.ItemType == ItemTypes.POST)
                {
                    return AccessLevel.View | AccessLevel.AddChild;
                }
                else 
                {
                    return AccessLevel.View;
                }
            }
            else
            {
                return AccessLevel.None;
            }  
        }

        public override string[] RolesWithAccess(Item item, AccessLevel accessLevel)
        {
            Blog blog = item.GetRootAncestor().Content as Blog;
            if (accessLevel == AccessLevel.View)
            {
                return new string[] { AnonymousRoleName, blog.CommentersRoleName, blog.EditorsRoleName };
            }
            else if (item.ItemType == ItemTypes.POST && (accessLevel & AccessLevel.AddChild) == AccessLevel.AddChild) 
            {
                return new string[] { blog.CommentersRoleName, blog.EditorsRoleName };
            }
            else
            {
                return new string[] { blog.EditorsRoleName };
            }
        }
    }
}


The first method returns the rights of a role over an item and the second method returns the roles that have a certain right over an item.

In order to user this implementation we need to define it in the configuration file of our application as usual.

IWhoAmI

In order for authorization to work, we need some other system to provide the users and the roles. Inside NCommet this is done, with the interface IWhoAmI which:
  1. Provides the running user as a System.Security.Principal.IPrincipal object.
  2. Gives the roles of the running user in a string array.
  3. Gives all the systems roles in a string array.

There is an available implementation inside the project NCommet.Modules.AspNetWhoAmI, which is based on the Membership & Roles system of .NET. We must note here that the Membership & Roles system of .NET does not require an ASP.NET application to work.

AuthorizationManagerImpl

The implementation of IAuthorizationManager that's provided by NCommet is the AuthorizationManagerImpl and it's contained in the project NCommet.Modules.Authorization. This implementation only works with persisted items and stores inside a table in the database the connections between item ids, roles and rights. The following diagram shows the table that this implementation uses:

authorizationmanagerimpl-db.png

This implementation also supports to change the rights that are assigned, providing greater flexibility. The only constraint is that it only works with persisted items. Inheritance of the rights is also supported to save some space on the number of entries i.e. if there are no entries in the table about a combination of item-role, the parent item is the checked etc.

Finally, there is also the implementation AMCachedImpl, which inherits AuthorizationManagerImpl and supports caching to minimize the number of queries to the database.

Last edited Jun 29, 2010 at 8:04 PM by kingherc, version 29

Comments

kkara Aug 15, 2008 at 3:05 PM 
Hmmm... According to the definition of POCO, the content classes of NCommet are not POCOs. If they were, they would not have to inherit from ContentBase...