Dynamic Roles in Sitecore
So what is a dynamic role? A dynamic role is one where membership is dynamically determined instead of statically assigned. The normal Sitecore roles are statically assigned to a user. They never change. Sitecore also contain 2 dynamic roles: the Everyone
role and the Creator-Owner
role. Inclusion in these roles is determined dynamically. OK, the Everyone
role may not be quite so dynamic, but the Creator-Owner
role is. Membership in this role is determined by the value of the Owner
field. You don’t assign users to the role.
Adding additional dynamic roles to Sitecore is quite easy. First we need the dynamic role to appear in the Security assignment dialogs so we can assign security permissions to the role. Then we need to assess whether a given user belongs to the dynamic role based on whatever logic we want to assess.
There are 2 classes in Sitecore that handle roles. One is the Sitecore ASP.NET role provider and the other is the roles in roles provider. The roles in roles provider is kind of a wrapper around the roles provider, extending it’s capabilities to support assigning roles within roles. It also already handles the existing Sitecore dynamic roles. Support for dynamic roles can be seen in the roles in roles provider in the fact it accepts a parameter to the GetAllRoles()
method to determine if “system” (read dynamic) roles should be included in the results.
You don’t want to include the dynamic roles in all calls to the GetAllRoles()
method as you don’t want to allow the user to statically assign one of the dynamic roles. If you opened the user properties dialog for an existing user and used the “Edit User Roles” dialog to assign roles to the user, you would not find the dynamic Everyone
and Creator-Owner
roles, but when using the “Security Settings” dialog to assign security to an item these roles do appear.
The dynamic roles I’m going to implement in this example come from a request I had from a client recently. The client wanted the ability to assign security to users who were not part of another role. In a default Sitecore site without customisation, an administrator would either need to assign security to all roles other than the role to be excluded, or create a role to group the other roles (roles in roles) and assign security to this role. This might be OK if you have a handful of roles, but if you have 50, this could become arduous. Not to mention the fact that every time a role was added you’d need to remember to update either security or the role membership for the role group.
In any case, this provided an interesting demonstration scenario for me and a solid example to use here .
So an appropriate class to override and extend with our dynamic roles is the roles in roles provider.
using System.Collections.Generic;
using Sitecore.Security.Accounts;
namespace CustomSitecoreSecurity
{
public class DynamicRolesProvider: SqlServerRolesInRolesProvider
{
}
}
And to have Sitecore use our custom roles in roles provider we need to update configuration to use our above class. Place the following in a configuration patch file in App_Config\Include
.
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
<sitecore>
<rolesInRolesManager>
<providers>
<add name="sql">
<patch:attribute name="type">CustomSitecoreSecurity.DynamicRolesProvider,CustomSitecoreSecurity</patch:attribute>
</add>
</providers>
</rolesInRolesManager>
</sitecore>
</configuration>
Now to have our dynamic role names returned so we can assign security to them. We need to override the GetAllRoles()
method to include our dynamic roles when includeSystemRoles
is true
. To implement the scenario I gave above, we want a dynamic role returned for every normal role, prefixed with “not”. So in addition to sitecore\Author
we want a dynamic role named sitecore\not Author
.
public override IEnumerable<Role> GetAllRoles(bool includeSystemRoles)
{
var baseRoles = base.GetAllRoles(includeSystemRoles);
foreach (var role in baseRoles)
{
yield return role;
if (includeSystemRoles)
{
var parts = role.Name.Split('\\');
if (parts.Length == 2)
{
var domain = parts[0];
var name = parts[1];
yield return Role.FromName(domain + "\\not " + name);
}
}
}
}
In the above method we call the base class implementation to get the roles, then iterate each role and yield return the role name plus the inverse dynamic role name (“not” version) if system roles are to be included.
The RolesInRolesProvider
also contains a method to return just the system roles which is used by some parts of the Sitecore UI. The below method overrides the base implementation to add the inverse roles.
public override IEnumerable<Role> GetSystemRoles()
{
var systemRoles = base.GetSystemRoles();
foreach (var role in systemRoles)
yield return role;
// Get 'not' roles
var roles = GetAllRoles(false);
foreach (var role in roles)
{
var parts = role.Name.Split('\\');
if (parts.Length == 2)
{
var domain = parts[0];
var name = parts[1];
yield return Role.FromName(domain + "\\not " + name);
}
}
}
The last method to override is the IsUserInRole()
method which is where we implement our logic to determine if the user is part of the dynamic role.
public override bool IsUserInRole(User user, Role targetRole, bool includeIndirectMemberships)
{
var parts = targetRole.Name.Split('\\');
var domain = parts[0];
var name = parts[1];
if (name.StartsWith("not "))
{
// Check if user belongs to normal role
var role = Role.FromName(domain + "\\" + name.Replace("not ", string.Empty));
var result = base.IsUserInRole(user, role, includeIndirectMemberships);
return !result;
}
return base.IsUserInRole(user, targetRole, includeIndirectMemberships);
}
In the above code we first determine if the role being assessed is one of the dynamic inverse roles by checking if the role name starts with “not “. Then if the role being checked for is a dynamic inverse role get the result for the non-inverse role (the real role the inverse role corresponds to) and inverse the result.
With the above dynamic role provider compiled and deployed, we can now assign security to a dynamic role and user security will be influenced by their dynamic membership within the role.
To test the provider is working properly let’s deny read access to a page for the sitecore\not Author
role. This would result in any user not in the sitecore\Author
role not being able to see the page.
Note in the above screenshot how each role has a corresponding inverse “not” role? The “not” roles are our dynamic roles returned by our dynamic roles provider.
You can see in the above screenshot I’ve denied read access to the sitecore\not Author
which with our dynamic roles provider would have the affect of denying read permissions to anyone without the sitecore\Author
role.
To test this out, we just need a user account that doesn’t have the sitecore\Author
role assigned. That user won’t be able to see the above item.
And that’s it. The above customisation can be used to add any dynamic roles you like to Sitecore.
Hi Alistair
First of all, thank you for a very informative and inspiring blog!
I'm having a hard time finding a suitable scenario for dynamic roles. Maybe it's because of missing experience :-)
Can you give an example of a scenario where dynamic roles are more suited than another approach?
/Tommy