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

Alright, to me, this is where things get interesting.  In this post we are going to examine the Users who report to our Owner and the Groups our Owner manages.  Then I am going to throw a curve-ball at you and show you how our Owner doesn't need to be a person at all.  Intrigued?  Read on.

First lets declare our new Collections and supply a GET and SET method for them:

private UserCollection directreports;
private GroupCollection groups;
 
/// <summary>
/// Collection of Users that report to this User
/// </summary>
public UserCollection DirectReports
{
    get 
    { return directreports;    }
    set 
    { directreports = value; }    
}
 
/// <summary>
/// Groups this User manages
/// </summary>
public GroupCollection Groups
{
    get{ return groups; }
    set{ groups = value;}
}

And now, add to the constructor the methods to populate these Collections and define those methods.

public Owner(DirectoryEntry de)
{
    .
    .
    .
        DirectReports = GetDirectReports();
        Groups = GetGroups();
    .
    .
    .
}
 
private UserCollection GetDirectReports()
{
    UserCollection rtn = new UserCollection();
    foreach( Object memberColl in this.originaldirectoryentry.Properties["directReports"])
    {
        DirectoryEntry userDE = new DirectoryEntry(DEFAULTDOMAIN +"/"+ memberColl, DEFAULTUSERNAME, DEFAULTPASSWORD, AuthenticationTypes.Secure);
                
        User user = new User(userDE, true);
        rtn.Add(user);
    }
    return rtn;
}
 
private GroupCollection GetGroups()
{
    GroupCollection rtn = new GroupCollection();
 
    foreach( Object memberColl in this.originaldirectoryentry.Properties["managedObjects"])
    {
        DirectoryEntry groupDE = new DirectoryEntry(DEFAULTDOMAIN +"/"+ memberColl, DEFAULTUSERNAME, DEFAULTPASSWORD, AuthenticationTypes.Secure);
        
        if(groupDE.SchemaClassName.ToString().ToLower() == "group")
        {
            Group grp = new Group(groupDE);
            rtn.Add(grp);
        }
    }
 
    return rtn;
}

Wow!  Lets look at each method individually:

The GetDirectReports method starts off by declaring our return object as a new UserCollection (I always name whatever object I am returning "rtn" so I don't mess up and return a different object of the same type).  Then it accesses the attribute ["directReports"] off our Owner object's originaldirectoryentry which is a string array.  We can use each string in the Array to instantiate a new DirectoryEntry object by supplying the same DEFAULTDOMAIN, DEFAULTUSERNAME and DEFAULTPASSWORD we used in Part 1 (which is why in the solution you see our Owner, User and Group objects inheriting the BaseObject class).

With this DirectoryEntry, which represents a person who reports directly to our Owner, we can create a new User object and add it to the collection.  When we have completed the iteration of ["directReports"] Array, we have a fully populated UserCollection containing information about every person who reports to our Owner.

Likewise, the GetGroups method iterates through the ["managedObjects"] Array.  After testing to ensure the managed object is a group, we can instantiate a new Group object, add it to our collection to produce a list of every Group our owner manages.

Lets do one better. Perhaps we have reason to be aware of those fellow managers who are considered peers of the Owner:

private UserCollection peers;
 
/// <summary>
/// Users on a peer level
/// </summary>
public UserCollection Peers
{
    get 
    { return peers;    }
    set 
    { peers = value; }    
}
 
public Owner(DirectoryEntry de)
{
    .
    .
    .
    Peers = GetPeers();
    .
    .
    .
}
 
private UserCollection GetPeers()
{
    UserCollection rtn = new UserCollection();
    foreach( Object memberColl in this.originaldirectoryentry.Properties["memberof"])
    {
        DirectoryEntry memberDE = new DirectoryEntry(DEFAULTDOMAIN +"/"+ memberColl, DEFAULTUSERNAME, DEFAULTPASSWORD, AuthenticationTypes.Secure);
        
        if(memberDE.SchemaClassName.ToLower() == "group")
        {
            foreach( Object memberColl2 in memberDE.Properties["member"])
            {
                DirectoryEntry userDE = new DirectoryEntry(DEFAULTDOMAIN +"/"+ memberColl2, DEFAULTUSERNAME, DEFAULTPASSWORD, AuthenticationTypes.Secure);
                if(userDE.SchemaClassName.ToLower() == "user" && userDE.Name.Replace("CN=","") != this.CommonName)
                {
                    User user = new User(userDE, false);
                    rtn.Add(user);
                }
            }
        }
    }
    return rtn;
}

We create a new UserCollection called Peers, add the GetPeers method to our Owner constructor and populate this UserCollection with the Users who are on equal footing with the Owner in the organization's hierarchy.

In the GetPeers method we iterate through the ["memberof"] Array, instantiating a DirectoryEntry for each member found.  We test to see if this is a group the Owner belongs to.  If it is, we iterate through the group's ["member"] Array to get the members of this group the Owner belongs to.  In what is now old hat, we instantiate a User object and add it to our UserCollection called Peers.

We now have a person, who we are calling the Owner of a resource in the system.  We know what Groups and Users the Owner manages, and what Users our Owner is a peer of.  Depending on the business rules we have a list of people and groups, our Owner has the authority to delegate access to the resource he owns since each of these entities he has direct control over, or in the case of Peers, is on equal footing with.

I don't know about you but I think this stuff is really cool!

Now for the curve-ball I promised.  Who says the Owner needs to be a person?  Perhaps the company wants to assign group ownership of a resource.  How would be modify the Owner object to allow for this?  It's actually pretty simple:

private UserCollection members;
 
/// <summary>
/// Members of this group
/// </summary>
public UserCollection Members
{
    get{ return members; }
    set{ members = value; }
}

Set up another UserCollection.  This one will represent members of the Group assigned ownership.

public Owner(DirectoryEntry de)
{
    .
    .
    .
    if(ObjectType == "user")
    {
        Login = de.Properties["sAMAccountName"].Value.ToString();
        DirectReports = GetDirectReports();
        Groups = GetGroups();
        Members = new UserCollection();
        Peers = GetPeers();
    }
    else
    {
        Login = String.Empty;
        DirectReports = new UserCollection();
        Groups = new GroupCollection();
        Members = GetMembers();
        Peers = new UserCollection();
    }
    .
    .
    .
}

Add a simple IF statement to our Owner constructor which if the Owner's ObjectType is not a User, we set up an alternate assignment path for our Collections.  In the ELSE block we see how the Login is left as an empty String since Groups don't log into anything.  No one reports to a Group, and we don't want to infer groups of this Group have ownership so DirectReports and Groups are left as empty Collections.  A Group has no Peers so same empty assignment goes to peers.  What we are left with is a UserCollection called Members which represents all the Users who belong to the Group assigned ownership.

At this point, the population of this UserCollection should be predictable:

private UserCollection GetMembers()
{
    UserCollection rtn = new UserCollection();
    foreach( Object memberColl in this.originaldirectoryentry.Properties["member"])
    {
        DirectoryEntry userDE = new DirectoryEntry(DEFAULTDOMAIN +"/"+ memberColl, DEFAULTUSERNAME, DEFAULTPASSWORD, AuthenticationTypes.Secure);
        if(userDE.SchemaClassName.ToString().ToLower() == "user")
        {
            User user = new User(userDE, true);
            rtn.Add(user);
        }
    }
    return rtn;
}

For each member of the Group assigned ownership, if the member is a User, add them to the Collection.

I don't know about you but this is a pretty useful chunk of code when it comes to constructing a Security Object based on Active Directory linkage.  You may be ready to throw in the towel right now, but believe it or not, it is a short hop from here to expand the usefulness of our object even further.  In the next post we will add onto our User and Group objects and recursively walk down the Active Directory tree to find all the people that could fall under our Owner's sphere of influence.

Be the first to rate this post

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

Posted by: AaronZalewski
Posted on: 1/22/2008 at 2:06 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