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.