.Net and Active Directory - An OO solution to authority structure - Part 8

Well, it has been a long journey but we are almost complete.  As I mentioned at the end of Part 7, this project has a purpose. The purpose is to grant ownership of a resource that the Owner can then delegate further access to others, be that Read/Write or otherwise.  In order to take the tree structure we've created and do something useful with it, we need to have a way to extend it to include those properties & permissions the business deems necessary, store that combination of Authority structure and permissions and as all good structures must, provide a way to maintain itself should the environment change. 

The bridge to realizing this all is going to be XML, so lets look at the code to perform a custom serialization of the structure:

public XmlDocument Serialize
{
    get
    {
        if(xmldoc == null)
        {
            xmldoc = new XmlDocument();
    
            // RootNode
            XmlNode ownerNode = xmldoc.CreateNode(XmlNodeType.Element,"Owner", null);
 
            // Attributes 
            XmlAttribute att = xmldoc.CreateAttribute("Path");
            att.Value = this.Path;
            ownerNode.Attributes.Append(att);
 
            att = xmldoc.CreateAttribute("CommonName");
            att.Value = this.CommonName;
            ownerNode.Attributes.Append(att);
 
            att = xmldoc.CreateAttribute("ObjectType");
            att.Value = this.ObjectType;
            ownerNode.Attributes.Append(att);
 
            att = xmldoc.CreateAttribute("ID");
            att.Value = this.ID;
            ownerNode.Attributes.Append(att);
 
            att = xmldoc.CreateAttribute("Login");
            att.Value = this.Login;
            ownerNode.Attributes.Append(att);
 
            xmldoc.AppendChild(ownerNode);
 
            XmlNode PermissionsNode = xmldoc.CreateNode(XmlNodeType.Element, "Permissions", null);
            ownerNode.AppendChild(PermissionsNode);
            XmlNode DirectReportsNode = xmldoc.CreateNode(XmlNodeType.Element,"DirectReports",null);
            PermissionsNode.AppendChild(DirectReportsNode);
            XmlNode GroupsNode = xmldoc.CreateNode(XmlNodeType.Element,"Groups",null);
            PermissionsNode.AppendChild(GroupsNode);
            XmlNode MembersNode = xmldoc.CreateNode(XmlNodeType.Element,"Members",null);
            PermissionsNode.AppendChild(MembersNode);
            XmlNode ManagerNode = xmldoc.CreateNode(XmlNodeType.Element,"Manager",null);
            PermissionsNode.AppendChild(ManagerNode);
 
            XmlNode PeerNode = xmldoc.CreateNode(XmlNodeType.Element,"Peer",null);
            XmlNode MngrNode = xmldoc.CreateNode(XmlNodeType.Element,"Manager",null);
            XmlNode ReportsNode = xmldoc.CreateNode(XmlNodeType.Element,"Reports",null);
 
            if(this.DirectReports.Count > 0)
            {
                GetUserNodes(DirectReports,DirectReportsNode);
            }
 
            if(this.Groups.Count > 0)
            {
                GetGroupNodes(Groups,GroupsNode);
            }
 
            if(this.Members.Count > 0)
            {
                GetUserNodes(Members,MembersNode);
            }
 
            if(this.Manager != null)
            {
                ManagerNode.AppendChild(GetManagerNode(this.Manager));
            }
        }
        return xmldoc;
    }
}
 
private void GetUserNodes(UserCollection uc, XmlNode parentNode)
{
    XmlNode xNode;
    XmlAttribute att;
    foreach(User user in uc)
    {
        xNode = xmldoc.CreateNode(XmlNodeType.Element,"User",null);
        att = xmldoc.CreateAttribute("Path");
        att.Value = user.Path;
        xNode.Attributes.Append(att);
 
        att = xmldoc.CreateAttribute("UserName");
        att.Value = user.UserName;
        xNode.Attributes.Append(att);
 
        att = xmldoc.CreateAttribute("ID");
        att.Value = user.ID;
        xNode.Attributes.Append(att);
 
        att = xmldoc.CreateAttribute("Login");
        att.Value = user.Login;
        xNode.Attributes.Append(att);
 
        if(user.Groups.Count > 0)
        {
            XmlNode GroupsNode = xmldoc.CreateNode(XmlNodeType.Element,"Groups",null);
            GetGroupNodes(user.Groups,GroupsNode);
            xNode.AppendChild(GroupsNode);
        }
 
        if(user.DirectReports.Count > 0)
        {
            XmlNode DRsNode = xmldoc.CreateNode(XmlNodeType.Element,"DirectReports",null);
            GetUserNodes(user.DirectReports,DRsNode);
            xNode.AppendChild(DRsNode);
        }
                    
        parentNode.AppendChild(xNode);
    }
}
 
private void GetGroupNodes(GroupCollection gc, XmlNode parentNode)
{
    XmlNode xNode;
    XmlAttribute att;
    foreach(Group group in gc)
    {
        xNode = xmldoc.CreateNode(XmlNodeType.Element,"Group",null);
        att = xmldoc.CreateAttribute("Path");
        att.Value = group.Path;
        xNode.Attributes.Append(att);
 
        att = xmldoc.CreateAttribute("GroupName");
        att.Value = group.GroupName;
        xNode.Attributes.Append(att);
 
        att = xmldoc.CreateAttribute("ID");
        att.Value = group.ID;
        xNode.Attributes.Append(att);
 
        if(group.Users.Count > 0)
        {
            XmlNode UsersNode = xmldoc.CreateNode(XmlNodeType.Element,"Users",null);
            GetUserNodes(group.Users,UsersNode);
            xNode.AppendChild(UsersNode);
        }
                    
        parentNode.AppendChild(xNode);
    }
}
 
private XmlNode GetManagerNode(ManagerObject mgrObj)
{
    XmlAttribute att;
    XmlNode xNode = xmldoc.CreateNode(XmlNodeType.Element,"Manager",null);
 
    att = xmldoc.CreateAttribute("Path");
    att.Value = mgrObj.Path;
    xNode.Attributes.Append(att);
 
    att = xmldoc.CreateAttribute("ManagerName");
    att.Value = mgrObj.ManagerName;
    xNode.Attributes.Append(att);
 
    att = xmldoc.CreateAttribute("ID");
    att.Value = mgrObj.ID;
    xNode.Attributes.Append(att);
 
    att = xmldoc.CreateAttribute("Login");
    att.Value = mgrObj.Login;
    xNode.Attributes.Append(att);
 
    if(mgrObj.Manager != null)
    {
        xNode.AppendChild(GetManagerNode(mgrObj.Manager));
    }
 
    return xNode;
}

I hope you are familiar with producing XML via .Net because I am not going to go into that sort of detail here.  What I do want to point out is that we are performing a pretty standard serialization of the object.  This block is to be found at the bottom of the Owner class and is meant to be extended upon by you.  Rather than predict every application that might leverage such a structure, this is meant to be customized.  If you needed to have a Read and Write bit associated with each User, then the above serialization is where to provision for this.  Want to define whether the Owner can grant permissions to their Peers, add an attribute.  This is the bit where we take what is an academic exercises and turn it into a valuable tool for an organization.

Once you've extended the XML schema, passed in a real user to the GetAuthorityTree method in the Authority class, produced this XML, populated the extended bits you've added to represent business rules and permissions, now you have an XmlDocument that can be stored in the database and used to grant or deny whatever it it you are using this for.

Finally because Active Directory is not a static structure and people do get hired and fired, producing this XML and comparing it against the stored copy will tell you whether someone was added, removed or moved within the organization.  If the node structure doesn't compare properly, you will know to regenerate the structure, moving over the current settings and re-saving.  This way someone who was given access to a resource by their manager, but is moved to a different department no longer under the Owner's authority does not retain their permission to this resource.  Every good structure should contain a method for self-maintenance.  It also allows for the permissions associated with the current resource to be copied to a different resource, automatically replicating permissions and reducing the cost of ownership.

I sincerely hope you found this series helpful.  It represents many hours of work and the loss of more than a few handfuls of hair in the process.  The next and final post contain my conclusions following the completion of this structure and how it might be used within an organization.

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Posted by: AaronZalewski
Posted on: 1/23/2008 at 1:08 PM
Tags: , ,
Categories: Active Directory
Actions: E-mail | Kick it! | DZone it! | del.icio.us
Post Information: Permalink | Comments (0) | Post RSSRSS comment feed

Related posts

Comments are closed