DNN Blog

Mar 28

Posted by: Shaun Walker
3/28/2008  RssIcon

This blog started out as a description of the permission enhancements in Cambrian, but somehow evolved into a more comprehensive article regarding the DotNetNuke security model.

A fundamental aspect of any enterprise software application is security. As opposed to more simple applications such as blogs or photo galleries which typically only require a single authorized user, enterprise software is used by multiple users, each with varying levels of authority. As a result, proper governance of information is a critical requirement which can not be ignored.

In any enterprise software application, a significant amount of time and money is invested in creating a capable security model. However the one thing I have run into more times than I care to recollect in my career, are custom security models in enterprise applications which are weak, cumbersome, and lack extensibility. Often the initial security model is developed to meet a specific use case without any consideration for future needs. As time goes on, the basic requirements of the application change and since the original security model is not adaptable, other security mechanisms end up getting bolted on, creating the inevitable 'house of cards'. Ultimately, this ends up making the application more difficult to administrate, less secure, and difficult and costly to maintain.

If there is one fundamental feature of the DotNetNuke framework which is more critically important than the rest, it is the security model. The DotNetNuke security model is intricately linked to the portal architecture and has evolved over the past four years to become an essential backbone for the platform. Every action taken in DotNetNuke has security implications, and our model has been highly optimized for both end-users and developers.
As enterprise users of the platform, stakeholders get to take advantage of the massive benefit provided by DotNetNuke's flexible and robust security model ( and get to save the considerable time and expense of rolling their own ).

The security model in DotNetNuke is abstracted into a number of different objects. On the one side there are the Entities within the application for which access needs to be controlled. Core examples of these entities are Pages, Modules, and Folders. On the other side are the actual Users of the application which need to be granted authority to perform specific actions. Since it is common in the business world for categories of users to have similar levels of authority, they are often logically organized to form groups which we call Roles. Linking the two sides together is accomplished through a system known as Permissions. Basically a permission is a business rule contract which indicates that a User is allowed to perform a specific action on an Entity ( ie. View or Edit ). The actual authorization logic for each permission is embedded within the application, based on its specific needs.

From a technical perspective, the security model in DotNetNuke is comprised of three basic parts: the database layer permissions storage, the business layer API, and the user interface layer web controls. Each of these layers play an integral role in providing an extensible security architecture.

At the database layer there is a Permission table where each granular security contract is registered. For example, this is where the ability for a user to VIEW a PAGE is registered. It is important to note that the Permission table has the ability to register both core framework permissions as well as custom permissions for specific modules ( I will cover this extensibility option in more detail later ). The mapping of permissions to entities occurs in their own relational database tables, conventionally named TabPermission, ModulePermission, etc... The data layer is architected this way to provide optimal performance and maintainability. Data access is provided through stored procedures as per the standard DotNetNuke methodology.

The business layer API is where the majority of the heavy lifting occurs. There are a variety of API calls which are related to the administration of permission information ( ie. add, upate, delete ) but from a developers perspective, the most important API calls are those which perform the actual user authorization. We use a common naming convention in this area, exemplified by the following code fragment:

If TabPermissionController.HasTabPermission("VIEW") Then ...

The user interface layer provides web controls for the management of permissions. The architecture for this feature involves the use of a base PermissionsGrid control which contains all the essential permissions functionality as well as a number of overridable methods which can be leveraged to create derived permission management grids for any entity ( ie. TabPermissionsGrid, ModulePermissionsGrid, etc... ). The benefit to this approach is that a common permissions management user interface is used throughout the DotNetNuke application, resulting in a more intuitive user experience. It also enables simpler integration for developers and higher integrity in terms of permissions management.

Since the user interface permissions grid is the area where the majority of users are exposed to the DotNetNuke security model, let's take a look at the functionality:

You will noticed that there is a Group (1) filter displayed at the top of the grid. This will only be displayed if you have actually created multiple groups for organizing your security roles. The group functionality is very handy in large enterprise sites where there are many security roles to manage ( as you can only imagine how large the grid footprint would be if all security roles for the entire site needed to be displayed at one time ). Below groups, you see each of the Permission types (2) which are associated to the specific entity  displayed as columns. And beneath this, you see the list of Roles (3) which belong to the Group selected. This grid allows a user to specify the roles which are granted access to specific permissions. Below the role grid you see a section for User permissions (4), including a Username textbox (5). This section allows you to enter the username for a specific user to which you want to apply permissions. This can be extremely useful if you want to grant a specific user access to an entity, but do not want to grant them all the privilege associated to a Role. There is no limit to the number of users which can be specified; however, it makes sense to only use this feature for exception conditions and rely on the robust Roles system for standard scenarios.

So this is where I finally get to describe the new security enhancements in Cambrian.

For quite some time there has been an enhancement request related to the integration of Deny permissions. Deny permissions can be used to exclude roles or users from receiving a specific permission for an entity. For example, you may want to grant a group of users access to a module; however, there may be a requirement that one of the members of the group must be denied access. In this case you could grant access to the role, but Deny access to the user. This would effectively provide access to the module for all users in the group except for one. As you can see from this example, the deny permissions take precedence over the grant permissions. The addition of Deny permissions provide a lot more flexibility to the security model and allow it to be adapted to almost any business requirement you can think of.

Jon Henning created a new multi-state web control for Cambrian which we have used to accomodate the new Deny permission. Basically, the initial permission state is an empty checkbox ( or unspecified ). Clicking the box once displays the Grant permissions icon. Clicking a second time displays the Deny permissions icon. And clicking again restores the state to the original empty checkbox. We feel that this UI paradigm is simple and intuitive for end users who are confronted with this new capability.

Getting back to the security model, it is important for module developers to understand the extensibility options available, as well as the situations where they are most applicable.

The first type of extensibility which module developers can take advantage of very easily is adding extra permission types at the module level. By default, DotNetNuke only provides basic View and Edit permissions for modules. If you have special needs in your module for more granular permissions, you can add additional permission types. Currently this can be accomplished by adding some SQL script to your module's install script:

INSERT INTO ModulePermission ( PermissionCode, ModuleDefID, PermissionKey, PermissionName )
VALUES ( 'MODULENAME', ModuleDefID, 'KEY', 'DESCRIPTION )

Or it can be done through code executed in the UpgradeModule method of the IUpgradeable interface:

Dim pc As New PermissionController
Dim p As New PermissionInfo
p.ModuleDefID = ModuleDefId
p.PermissionCode = "MODULENAME"
p.PermissionKey = "KEY"
p.PermissionName = "DESCRIPTION"
pc.AddPermission(p)

In Cambrian, we plan to make this even simpler by adding install/uninstall support for module permission types in the module manifest file ( *.dnn ).

Once a new permission type is added for a module, it will automatically appear in the module permissions grid ( accessible through Module Settings ).

Since this is an extension to module permissions, the developer does not have to do anything in their module other than wrapping their sensitive functionality with the standard module permissions logic:

If ModulePermissionController.HasModulePermission(ModuleID, TabID, "KEY") Then ...

A core module which takes advantage of this extensibility option is the User Defined Table module.

The second type of extensibility that module developers can take advantage of, which is a bit more challenging but far more powerful, is adding permission types for new entities. A core module which takes advantage of this extensibility option is the Forums module. The forums module needs to define certain permissions at the Forum level. Since Forums are child entities of a Module, it means that the module level permissions are not granular enough for its needs. In this case, a Forum is a new custom entity which needs its own security. There are a number of items which need to be created to secure a new entity:

Database Layer - you need to register the permission types for the module in the Permission table ( similar to the extensibility option described above ). You also need to create your own permission table in the database for your entity ( ie. {Entity}Permission ). The structure of this table should be identical to the core permission tables except for your own unique ID field ( ie. {Entity}ID ). You need to create your own stored procedures for managing the data in your permission table ( again you can use the core stored procedures as templates for your own ).

Business Layer - you need to create classes for managing your permissions. Similar to the database layer, you should be able to copy the business layer classes for the core permissions and modify them to suit your needs ( ie. {Entity}PermissionCollection.vb, {Entity}PermissionController.vb, and {Entity}PermissionInfo.vb ). Using the core classes provided best practices in terms of API, caching, etc...

User Interface Layer - you need to create a derived permissions grid. You will be able to copy and modify an existing core permission grid for this purpose. The process is fairly straightforward if you follow the existing model and the end result will be a familiar user interface for the users of your module.

With these components in place, you will be able to implement the permissions into your module using logic similar to:

If {Entity}PermissionController.Has{Entity}Permission({Entity}ID, "KEY") Then ...

Well that wraps up the description of the security model in DotNetNuke. I hope you have found the information useful and recognize the exceptional value which DotNetNuke provides in terms of building enterprise web applications. I also hope you are somewhat excited about the new features coming in Cambrian.

Tags:
Categories:
Location: Blogs Parent Separator Shaun Walker

4 comment(s) so far...


Re: DotNetNuke Security

Thanks Shaun, very helpful. I also would be interested in some additional information about where the membership provider changes stand moving forward with Cambrian. I've seen some of the blog entries describing the new architecture which includes a visual interface, but I'm not clear on whether this aspect will continue to change with Cambrian. If an enterprise customer is looking to create new membership, role and profile providers, should they hold off for a few months, or will this aspect of DotNetNuke remain the same as we head into Cambrian.

By dworthley on   4/1/2008

Re: DotNetNuke Security

Thanks Shaun for the overview -- looks very good!

My question is this: Will the security model be able reference custom user profile entries to grant/deny access to page or module content? So for example, I would like an entity to be viewable where Region Code = GA, Date of Birth <= 1/1/1980, favorite color = blue, etc.... Currently I have to setup "shadow" Roles (which mirror custom user profiles) and implement synchronization code (if a custom profile entry is changed, add/remove the user from the related Role).

By jyjohnson on   4/1/2008

Re: DotNetNuke Security

Whew...
Good stuff.

By pspeth on   4/1/2008

Re: DotNetNuke Security

Shaun,

I'm very excited to see these new capabilities coming with Cambrian. I have been waiting for the Deny flag to be implemented for some time, and I hadn't even though about the power of adding entity level permissions into the DotNetNuke core, these are wonderful.

There is one more feature that I would really like to see; I think it would add a significant amount of power to DotNetNuke:

It would be great if a module developer could add "Module Roles", these roles would show up as security roles, however their value would not come from a setting in the database (Is user A in Group B?) but rather the module provides a method that is called to deturmine if a user is in a role.

This would, for example, allow you to create a module role inside the forums module named "AveragePostsOver1PerMonth".

Then, another module could consume the value of that role like any other role; and so the Newsletter module could quickly send an email to all users who have an average of more than 1 post per month in the forums module...

Or the newsletter module could quickly send an email to all users who have purchased a particular product in a custom ecommerce module, but have not purchased that product in the last 60 days...

Currently; module communication is very limited when it comes to "granting permission within one module to all users that meet criteria defined by another module". The only way to accopmlish this is to build two modules that interact with each other, it's impossible to provide information to a module already written.

You could even use this functionality within the same module, wouldn't it be cool if anyone who already has 100 approved posts in a forum is automatically granted permission to post without approval, but only if the administrator configures it that way?

Thanks for all the hard work that has been put into DotNetNuke. I'm looking forward to the release of Cambrian.

Matt Christenson

By Matt Christenson on   4/1/2008
Attend A Webinar
Free Demo Site
Download DotNetNuke Professional Edition Trial
Have Someone Contact Me

Like Us on Facebook Join our Network on LinkedIn Follow DNN Corporate on Twitter Follow DNN on Twitter

Advertisers

Sponsors

DotNetNuke Corporation

DotNetNuke Corp. is the steward of the DotNetNuke open source project, the most widely adopted Web Content Management Platform for building web sites and web applications on Microsoft .NET. Organizations use DotNetNuke to quickly develop and deploy interactive and dynamic web sites, intranets, extranets and web applications. The DotNetNuke platform is available in a free Community and subscription-based Professional and Enterprise Editions with an Elite Support option. DotNetNuke Corp. also operates the DotNetNuke Store where users purchase third party apps for the platform.