codeflood logo

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 Smile .

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.

dynamic roles

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.

dynamic role assignment

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.

Comments

Tommy Holm

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

Alistair Deneys

Hi Tommy, Dynamic roles are an ideal approach when those roles are not static but based on some other logic which changes quickly over time, or when membership to that role is not static but is based on some other logic. The example I gave in the blog (although a little contrived) was that of have a "not" role for each "real" role. This is the kind of thing dynamic roles could be ideal for. When an admin creates a role they don't have to worry about creating the complimentary "not" role and managing role membership to that role.
Another use for dynamic roles may be to expose additional roles and manage membership based on custom properties of the user profile. For example, you may have a "state/province" custom property on your author (or general user) role and could expose a set of dynamic roles, one for each state or province. The admin doesn't have to manage whom belongs to each role as that is determined by the user's profile.
Although I've not used this approach on any real project yet, it's an interesting design to keep in mind for those kinds of concerns.

Awesome!
Another use, in my case dynamic mailing list for campaign manager.
Thanks.

Vikas Rathore

Hi Alistair
Can you please let me know if there is anyway in sitecore to rename sitecore roles and groups.
I am aware of that just delete the existing role and create new one.
But in my scenario it is not possible to delete the existing role and create new role i want to update the role name.Is it possibe in sitecore?
I am using sitecore 7 right now.

Thanks Vikas Rathore

Alistair Deneys

Hi Vikas, I'm not aware of anything that allows renaming roles in Sitecore. It's probably because role identity in Sitecore is done by role name, not by some hidden ID. You can see this by having a look at the raw value of the __security field which looks something like this:
ar|Everyone|pe|!*|pd|!*|ar|sitecore\Analytics Testing|pe|+item:read|pd|+item:read|ar|sitecore\Analytics Management Reporting|pe|+item:read|pd|+item:read|
So if you wanted to change the name of a role, you'd also need to ensure any usage of that role name in security fields is also updated. And there lies the trick in how we can do this. After you create your new, correctly named role, just use one of the scripting modules like Revolver or PSE to traverse all items and replace occurrences of the old role name for the new role name.
If you really couldn't delete the old role and did need to rename it then you'll also have some tweaking to do in the core DB ASPNET membership tables.
Sounds like I have a blog post to write for you :)

Leave a comment

All fields are required.