Thursday, August 21, 2008

Active Directory – using it to check permissions

In large organizations there's a tendency to use ActiveDirectory. Recently I had to find a method to use its tree structure. The first thing to do is delve down the tree, manager to employees. The following code does that:


private static int GetChilds(string path)
{
DateTime START = DateTime.Now;
System.DirectoryServices.DirectoryEntry DE = new System.DirectoryServices.DirectoryEntry("LDAP://" + path);
System.DirectoryServices.DirectorySearcher DS = new System.DirectoryServices.DirectorySearcher(DE);
System.DirectoryServices.SearchResult res = DS.FindOne();
if (res.Properties["directreports"].Count > 0)
{
int cnt = 1;
for (int i = 0; i < res.Properties["directreports"].Count; i++)
{
cnt += GetChilds((string)(res.Properties["directreports"][i]));
}
sw.WriteLine(res.Properties["name"][0] + " - Time: " + DateTime.Now.Subtract(START).ToString() + " (" + cnt.ToString() + ")");
sw.Flush();
return cnt;
}
else
{
return 1;
}
}


Assuming that sw is a streamwriter already initialized, the code delves down and writes the results + times. If your organization has more than 100 people the CEO would kick your ass if he'll have to wait for this list (or a permission using this method of search)

So I had to find a better way to check whether USER_A is a manager of USER_B. I chose this method:

private static bool IsManagerOf(string managerName, string path)
{
System.DirectoryServices.DirectoryEntry DE = new System.DirectoryServices.DirectoryEntry("LDAP://" + path);
System.DirectoryServices.DirectorySearcher DS = new System.DirectoryServices.DirectorySearcher(DE);
System.DirectoryServices.SearchResult res = DS.FindOne();
if (managerName.ToLower() == ((string)(res.Properties["samaccountname"][0])).ToLower())
{
return true;
}
else if (res.Properties["manager"].Count == 0)
{
return false;
}
else
{
return IsManagerOf(managerName, (string)(res.Properties["manager"][0]));
}
}


This method assumes that there's only one direct-manager per user, if it's not the case, quit the company and move to a company that wants to make money.

The 3rd method I needed is for the "special users" (usually they're the ones hiding in the HR-dep). They must have full access even though they don't have any connection the other users. For this case you'll need to define a new group in the active directory and do a check like this:

private static bool IsMemberOf(string GroupName, string path)
{
System.DirectoryServices.DirectoryEntry DE = new System.DirectoryServices.DirectoryEntry("LDAP://" + path);
System.DirectoryServices.DirectorySearcher DS = new System.DirectoryServices.DirectorySearcher(DE);
System.DirectoryServices.SearchResult res = DS.FindOne();
for (int i = 0; i < res.Properties["memberof"].Count; i++)
{
string curGroup = res.Properties["memberof"][i].ToString().Split(',')[0].Substring(3);
if (curGroup.ToLower() == GroupName.ToLower())
{
return true;
}
}
return false;
}


And the method for deciding whether to give access to view a certain user looks like:

private static bool GrantAccess(string Cur_Account, string userName, string GroupName)
{
System.DirectoryServices.DirectoryEntry DE = new System.DirectoryServices.DirectoryEntry("LDAP://MyCorp"");
System.DirectoryServices.DirectorySearcher DS = new System.DirectoryServices.DirectorySearcher(DE);
DS.Filter = "samaccountname=" + Cur_Account;
System.DirectoryServices.SearchResult res = DS.FindOne();
if (IsMemberOf(GroupName, (string)(res.Properties["distinguishedname"][0])))
{
return true;
}
else
{
DS = new System.DirectoryServices.DirectorySearcher(DE);
DS.Filter = "samaccountname=" + userName;
res = DS.FindOne();
if (IsManagerOf(Cur_Account, (string)(res.Properties["distinguishedname"][0])))
{
return true;
}
}
return false;
}
}