Module Permission Customization

Trevor Fayas / August 07, 2017
Module Permission Customization

As we get into developing in Kentico, at some point we need to expand what is by default in Kentico and create systems within Kentico through Kentico's Modules.  This allows us to create classes (tables), custom UI elements, and a plethora of other resources.

These resources often need people to manage them (the end user), and sometimes we need to specify certain users to only be able to work on certain classes within the module.  This article is designed to help you understand your options within Kentico to set Class-specific permissions, so you can make the best decision for your project!

To Skip or not to Skip?

 

That tis the question!  During the creation of this blog, I created a Test Permissions Module (in my Kentico 10 instance) so i could fully explore the options.  There are many steps to create a custom module, set the classes, and set everything up.  So i give you two options.

  1. You can read and follow along my Steps
  2. You can simply Import the module and related items.
If you simply import, i would still skim through the steps so you can get a good understanding of why things are set the way they are.

Applying Custom Permissions to Classes

 

Now that everything is set up (either through you creating it, or importing the items), it's time to actually cover how we set custom permissions.

When a user is requesting to perform an action on an object (read, create, modify, delete, destroy), there are 2 primary calls made to that object's TypeInfo class.

The first is the string GetPermissionName(PermissionsEnum permission) function.  This returns what the actual permission name is for the given permission type.  So if requesting to get the PermissionsEnum.Read name, it will return what Permission name to check for (default is "Read").

 

	// Applied to the CustomPermissionClassLocalInfo.cs
	/// <lt;summary>
        /// Option 1: Override the GetPermissionsName to give it what Permission name it should use for the various permission requests.
        /// </summary>
        /// <param name="permission"></param>
        /// <returns></returns>
        protected override string GetPermissionName(PermissionsEnum permission)
        {
            switch (permission)
            {
                case PermissionsEnum.Read:
                    return "Read_Custom";
                default:
                    // Covers the remaining Create and Modify, Delete and Destroy Enums
                    return "Modify_Custom";
            }
        }

The Second is the bool CheckPermissionsInternal(PermissionsEnum permission, string siteName, IUserInfo userInfo, bool exceptionOnFailure) function.  This will check, for the given user and site and PermissionEnum type, if the user has permission (returning true or false).  This is actually called first, and will check if the current user is authorized for the resource of the current module's "GetPermissionName" permission.

 

	// Applied to the CustomPermissionClassGlobalInfo.cs
	// Note the call to the base.CheckPermissionsInternal which will do a check for the default permission names also ("Read", "Modify", etc)
        /// <summary>
        /// Option 2: Overwrite the CheckPermissionsInternal so it checks if the user is authorized for the specific 
        /// permissions (or the general default ones which the base.CheckPermissionsInternal will call
        /// </summary>
        /// <param name="permission"></param>
        /// <param name="siteName"></param>
        /// <param name="userInfo"></param>
        /// <param name="exceptionOnFailure"></param>
        /// <returns></returns>
        protected override bool CheckPermissionsInternal(PermissionsEnum permission, string siteName, IUserInfo userInfo, bool exceptionOnFailure)
        {
            switch (permission)
            {
                case PermissionsEnum.Read:
                    return userInfo.IsAuthorizedPerResource("TestPermissionsModule", "Read_Custom", siteName, exceptionOnFailure) || 
                           base.CheckPermissionsInternal(permission, siteName, userInfo, exceptionOnFailure);
                default:
                    // Only Global Admin can Create, Modify, Delete and Destroy
                    // Covers the remaining Create and Modify Enums
                    return userInfo.CheckPrivilegeLevel(UserPrivilegeLevelEnum.GlobalAdmin, siteName) 
                        && (
                            userInfo.IsAuthorizedPerResource("TestPermissionsModule", "Modify_Custom", siteName, exceptionOnFailure) || 
                            base.CheckPermissionsInternal(permission, siteName, userInfo, exceptionOnFailure)
                        );
            }
        }

 

As with pretty much every aspect of the TypeInfo, these 2 methods can be overwritten, and are how we can customize the Permissions for the specific class itself.

Limitations of Overwriting the GetPermissionName


Overwriting the GetPermissionsName provides an easy way to set a permission to control specifically that one object.  In most cases, this is all that is usually needed, however the limitation is that you can only specify 1 permission name for the given Permission type.  Because of this, you cannot use "inheritance" type logic, such as "The user should be able to read this if they have Permission Specific_Read Or The general Read permission on the module."

Also if the given user is of type Administrator then the Permission name is really of no use, because they will be authorized for any non-global module resource (which pretty much all custom modules are "site" level modules, only UI elements can be designated as Global).  So you cannot use the GetPermissionName to limit Administrator access, only Editors.

Flexibility of CheckPermissionsInternal


The CheckPermissionsInternal does offer much more flexibility because you have complete control over the validation Logic.  With it you can check multiple permissions to grant access to, execute any custom logic you need, and since it passes you the userInfo, you can also do validation checks based on the user itself.  You can even BLOCK Administrators and Global Administrators from performing actions (useful if a resource is supposed to be "global" but you still want Non Global Admins to be able to read it, like my example code above does).

Ways to limit access to the UI Pages

In most situations, you want to limit complete access to various UI Pages and the objects they modify.  There are multiple ways to do this in Kentico:

  • UI Access Condition: Limits via macro rule access to a UI element, does not hide it from navigation if access is false.
  • UI Visibility Condition: Limits via macro rule visibility (and thus access) to the UI element.
  • Role UI Personalization: Limits page editors to what UI elements they have access to.  Access must be granted through this, and then the user must also pass the UI Visibility and Access conditions
  • UI Check "Read" Permission: Limits page editors and hides/blocks access to the UI page if they are not in a role that has the general "Read" permission on the module.

Utilize these if you want to exclude access in general, because if you can’t read, you also can’t modify, create, or delete (if the UI page is the only means of managing its content).

See the below table on what methods can limit what types of users:

UI Access Table

Conclusion: What road to take?

Based on all the information above, here is my recommendation when setting up your permissions on your modules.

Class Specific Permissions


If you want to specify a permission name for every class, then you can overwrite the GetPermissionName on each class and set the singular permission there, using the Read only for UI element access.

If, however, you are going to have a mixture of some classes have specific permissions while others use a generic or global permission, OR if you want to use really advanced permission logic, I would then overwrite the default CheckPermissionsInternal() on the classes you want to overwrite.  This way you can still have generic Read, Modify, Delete, Create, Destroy permissions for the general classes, and you can set custom permissions on your custom classes (using the userInfo.IsAuthorizedPerResource, and if you wish utilizing the base.CheckPermissionsInternal() or other checks to grant multiple permissions access).

General Read/Access


Because of the extra steps you have to go through by not having it set this way, I would recommend that you create a general "Read" permission for your module, and use it to control general read access for User Interface objects (So you can enable the "Check Read Permission" on UI Pages), and grant it to any role that will be touching any of the Module’s classes.

To block specific resources, use Access/Visibility Conditions on the UI elements if you need to limit Administrators, otherwise you can use the Role UI Personalization in the Roles to only grant access to Page Editors to what UI elements they should have accesses.

Use the "Is Global Application" checkbox on the UI elements to block non-global administrators from UI You do not want them to access at all.

Closing out

Hopefully this article was helpful, I apologize for it's length but indeed it's a complex subject with many things to consider.  Please feel free to leave comments below, happy Kentico-ing!

Trevor Fayas
About the Author


Trevor Fayas
Software Engineer II

Trevor Fayas is a Software Engineer II at Heartland Business Systems, and one of the lead Kentico Developers on staff.  He is ranked in the top 10 Kentico Developer Network Q&A Contributors, and has published multiple tools on the Kentico Marketplace.  Trevor’s passion is to help build and equip Kentico users and developers with the tools to really take their site to the next level.

Comments
Blog post currently doesn't have any comments.