Sunday, February 6, 2011

Visualizing Sitecore Roles Inheritance

Have you ever needed to review the security model of existing website? If yes, then you probably know how to use the Role Manager and "Member of" / "Members" buttons, or extract roles information via API. But how to get the quick summary if there is a hundred of roles? If A1 is a member of A2 where A2 also has members A3, A4, ... Can't imagine? -Visualize it!


Since Sitecore security allows "roles in roles" inheritance, the best way to represent it is a graph. I've tried different graph tools (Java Script and Silverlight-based, etc.) and finally found Glee. This tool from Microsoft Research has not been updated since 2007, but it has some unique features and very easy to use. In order to get it work, you just need to define graph edges - Glee will determine nodes, order them hierarchically and do the rest of the work.

After downloading Glee from here and installing it, you need to reference it's assemblies (you'll probably find them here: C:\Program Files (x86)\Microsoft Research\GLEE\bin).
In this example, I'll implement visualizer as an .ashx handler. Basically, interaction with Glee can be described as:

1) Create graph object.
2) Define edges or nodes.
3) Render result to the response stream.

Here is the sample code:

Microsoft.Glee.Drawing.Graph graph = new Microsoft.Glee.Drawing.Graph("customGraph");

...
graph.AddEdge("%source node%", "%target node%")
...

var renderer = new Microsoft.Glee.GraphViewerGdi.GraphRenderer(graph);
renderer.CalculateLayout();

var img = new System.Drawing.Bitmap((int)graph.Width, (int)graph.Height, System.Drawing.Imaging.PixelFormat.Format32bppPArgb);
renderer.Render(img);
            
System.Drawing.Imaging.EncoderParameters eParams = new System.Drawing.Imaging.EncoderParameters(1);
eParams.Param[0] = new System.Drawing.Imaging.EncoderParameter(System.Drawing.Imaging.Encoder.Quality, 100L);
System.Drawing.Imaging.ImageCodecInfo[] codecs = System.Drawing.Imaging.ImageCodecInfo.GetImageEncoders();
img.Save(context.Response.OutputStream, codecs[1], eParams);

Let's mix it up with Sitecore roles. Basically, all we need is to query all roles from the domain and add them and their members as a graph edges.

const string domain = "sitecore";
var roles = Sitecore.Security.Domains.Domain.GetDomain(domain).GetRoles();
foreach(Sitecore.Security.Accounts.Role role in roles)
{
    var roleMembers = Sitecore.Security.Accounts.RolesInRolesManager.GetRoleMembers(role, false);
    foreach(var roleMember in roleMembers)
    {
        if (roleMember.AccountType == AccountType.Role)
        {
            var edge = graph.AddEdge(role.Name.Replace(domain + @"\", string.Empty), roleMember.Name.Replace(domain + @"\", string.Empty));
            edge.SourceNode.Attr.Shape = Shape.Record;
            edge.TargetNode.Attr.Shape = Shape.Record;
        }
    }
}

Here is the sample output from Sitecore 6.2 (click to enlarge image):



And complete source code:

/// 
/// Summary description for RolesVisualizer
/// 
public class RolesVisualizer : IHttpHandler
{
    public void ProcessRequest(HttpContext context)
    {
        Microsoft.Glee.Drawing.Graph graph = new Microsoft.Glee.Drawing.Graph("customGraph");

        const string domain = "sitecore";
        var roles = Sitecore.Security.Domains.Domain.GetDomain(domain).GetRoles();
        foreach (Sitecore.Security.Accounts.Role role in roles)
        {
            var roleMembers = Sitecore.Security.Accounts.RolesInRolesManager.GetRoleMembers(role, false);
            foreach (var roleMember in roleMembers)
            {
                if (roleMember.AccountType == AccountType.Role)
                {
                    var edge = graph.AddEdge(role.Name.Replace(domain + @"\", string.Empty), roleMember.Name.Replace(domain + @"\", string.Empty));
                    edge.SourceNode.Attr.Shape = Shape.Record;
                    edge.TargetNode.Attr.Shape = Shape.Record;
                }
            }
        }

        graph.MinNodeHeight = 50;

        var renderer = new Microsoft.Glee.GraphViewerGdi.GraphRenderer(graph);
        renderer.CalculateLayout();

        var img = new System.Drawing.Bitmap((int) graph.Width, (int) graph.Height,
                                            System.Drawing.Imaging.PixelFormat.Format32bppPArgb);
        renderer.Render(img);

        System.Drawing.Imaging.EncoderParameters eParams = new System.Drawing.Imaging.EncoderParameters(1);
        eParams.Param[0] = new System.Drawing.Imaging.EncoderParameter(System.Drawing.Imaging.Encoder.Quality, 100L);
        System.Drawing.Imaging.ImageCodecInfo[] codecs = System.Drawing.Imaging.ImageCodecInfo.GetImageEncoders();
        img.Save(context.Response.OutputStream, codecs[1], eParams);
    }

    public bool IsReusable
    {
        get
        {
            return false;
        }
    }
}

You can download compiled solution here, just extract it into the root folder of you website and open /RolesVisualizer.ashx.
If you haven't got 40" screen yet, it makes sense to print the result (using PosteRazor -  http://posterazor.sourceforge.net/ or other app to print single image at the multiple A4 pages).
Graph rendered by Glee does not look nice, but it's very useful. Maybe, in future, it will be possible to view and edit roles in Sitecore using similar UI.