By: Shaun Walker
The permissions system in Oqtane is an essential core service as it manages access control within the framework. As part of the the initial creation of Oqtane, certain administrative functions were identified as requiring more privileges and were therefore made available only to users in the Administrators role. Although this approach works well for many installations, there are scenarios in larger organizations where a more fine-grained approach is required for delegated administration. The Oqtane 3.3.0 release offers new capabilities to resolve this challenge.
Dynamic Authorization Policies
Previously Oqtane only supported a static set of authorization policies for API methods which were declared in Startup using the standard .NET Core approach. For example there were PolicyNames.ViewModule and PolicyNames.EditModule policies which were declared and used to secure API methods using Oqtane's permissions model. The fact that all authorization policies had to be declared in Startup was not flexible, so an AuthorizationPolicyProvider was introduced which now allows policies to be dynamic. The format for policies is "EntityName:PermissionName:DefaultRoles". So if you want to use a Read policy for User API methods you can simply specify an attribute of [Authorize(Policy = "User:Read:Registered Users")] on your API method and the PermissionHandler is dynamically able to create the policy and use Oqtane's permissions model to authorize the request.
The permissions system in Oqtane was designed with flexibility in mind so that it can be used with any entity or permission type. All of the initial implementations of the permission model in the framework were based on managing user interface privileges where a user needed to be authorized to access a specific Entity and EntityId in order to perform a UI action. For example a user could be provided Edit rights to a specific Module instance based on its ModuleId. However to accomodate the delegated administration scenario, there was also a need to be able to indicate that a user is authorized to access an Entity API method at a site level without any need to specify an EntityId. For example, you might want to specify that a specific custom role (ie. "Supervisor") is authorized to access specific Inventory API methods within a site. So the framework was enhanced to support these site level API permissions.
IModule Permissions Definition
The IModule interface can be used to define various behavioral properties for a module. There has always been a property for PermissionNames. This property is used in the permission grid in Module Settings to define the list of permissions supported by the module. In the past only "Module" entity permissions were supported, however the PermissionNames property has now been enhanced to support API permissions as well. The format for API permissions is "EntityName:PermissionName:DefaultRoles" - which coincidentally is the exact same format as the new dynamic authorization policy specification I explained above. As an example, the system now allows User Management to be delegated to users other than the Administrator by configuring the permission grid in Module Settings. User Management relies on the User API and UserRole API for data access so a user needs to be granted rights to these API permissions in addition to the standard module permissions. So the User Management modules defines its PermissionNames in the IModule interface (ModuleInfo.cs) as "View,Edit,User:Write:Administrators,UserRole:Write:Administrators" (note that the full list of permissions including View and Edit module permissions are identified).
The combination of these three enhancements makes the framework much more flexible in terms of defining permissions and allows administrative tasks to be delegated to users who are not Administrators. Currently, User management, Role management, and Profile management have all been enhanced to support this new delegation capability.