Yes, this image has been doctored so that the domain names are not visible. When the application starts, it will automatically populate the Domains window with all known (to the system) domains.
Introduction
If you work on a Microsoft network, chances are you're using Active Directory (AD). Active Directory stores information about network resources for a domain. This information requires specific authority for update, but is typically open to authenticated users for query.
I developed this tool to allow for exactly these queries. It provides a list of known (to the network) domains, and allows the user to view groups, group membership, users, and user details without the need to dive into LDAP queries. In short, it's easy to use, quick, and provides more information than the typical user really needs.
This tool was developed using the .NET Framework 2.0 only. There are no interop assemblies or Win32 API calls involved in ADSI operations, there is one Win32 API called for the About box animation. This is a .NET 2.0 Windows Forms application.
Background
Many of the organizations that I work for utilize AD to manage application and resource access by groups. Unfortunately for me (and others), many of these organizations do not permit access to the Microsoft Active Directory tools, so verifying that a particular user has been given membership to a particular group can be a bit of a pain. Hence, this tool was born.
Using the Code
The UI itself is pretty straightforward. Just a typical .NET Windows Forms application. The meat of the application is located in the ADLookup
class. This class performs all of the AD activities used to populate the lists in the UI. Perusing the source will provide you with an introduction (possibly a rude one) to the world of AD searches in the .NET environment.
If you look at the image above, the arrows indicate that a selection in a list will trigger the automatic update of the list being pointed to. In addition, if a user is selected from the Users in Group list, that user will be selected in the Domain Users list as well, triggering subsequent updates. Likewise, a selection in the Groups for User list will select that group in the Groups in Domain list, triggering subsequent updates. The numbers in parenthesis above each list indicate how many elements are in the list. This gives an at-a-glance answer to one of the most common AD questions: "How many users are in group xx?"
Searching
To utilize the search, you need to select a search option from the Search menu, or you can right-click on either the Groups in Domain, Users in Group, or Users in Domain lists. When you select one, a pop-up window will display for you to enter your search data. The search data you enter is used as a Regular Expression to evaluate against the data in the selected list, so feel free to use .NET Regular Expressions to perform your fuzzy search.
Only the first match of your search criteria is selected. When it is selected, the appropriate lists will be updated in their content as well.
Points of Interest
There are three methods in the ADLookup
class that deserve a little attention here. These three methods are used to decode arrays of bytes that are returned from the AD query in the user properties collection.
First, the easy one - SIDToString
:
private string SIDToString(byte[] sidBinary)
{
SecurityIdentifier sid = new SecurityIdentifier(sidBinary, 0);
return sid.ToString();
}
The best part of this method is that there's virtually nothing to converting a Windows SID (security identifier) bit array to a human readable string.
The next one is a Registry lookup used to determine the currently active time bias on the system. This is a value used by the system to convert from Greenwich Mean Time (GMT) to the local time.
private int GetActiveBias()
{
RegistryKey key =
Registry.LocalMachine.OpenSubKey(@"SYSTEM\CurrentControlSet" +
@"\Control\TimeZoneInformation");
if (key == null)
return 0;
int Bias = (int)key.GetValue("ActiveTimeBias");
key.Close();
return (Bias / 60);
}
This value is always subtracted from GMT to arrive at the local time. Where I live, we use daylight savings time as well as standard time, so my ActiveTimeBias
value will be either 7 (Pacific Daylight Time [PDT]) or 8 (Pacific Standard Time [PST]).
The last method we will visit here is called DecodeLoginHours
. Within the properties collection for a user in AD, there exists the ability to limit the hours that a user can log in to a system. This property consists of an array of 21 bytes, where each bit represents a one hour span beginning with Midnight Sunday GMT. Note that I said GMT. This is where the ActiveTimeBias
comes in. By performing the subtraction, we're able to re-align the bit-array to machine time. Obviously, this bit-array is not friendly to humans, so we decode it into something that we can easily read. Within the UI, it will show up in the Properties for User list as Login Hours: > Click to view <. Naturally, the user needs to click the item in the list to get the following display:
private string DecodeLoginHours(byte[] HoursValue)
{
if (HoursValue.Length < 1)
return string.Empty;
int Bias = GetActiveBias();
BitArray ba = new BitArray(HoursValue);
BitArray bt = new BitArray(168);
int ai = 0;
for (int i = 0; i < ba.Length; i++)
{
ai = i - Bias;
if (ai < 0)
ai += 168;
bt[ai] = ba[i];
}
int colbump = 0;
int rowbump = 0;
int rowcnt = 0;
StringBuilder resb = new StringBuilder();
resb.Append(" ------- Hour of the Day -------");
resb.Append(Environment.NewLine);
resb.Append(" M-3 3-6 6-9 9-N N-3 3-6 6-9 9-M");
resb.Append(Environment.NewLine);
resb.Append(_DayOfWeek[rowcnt]);
for (int i = 0; i < bt.Length; i++)
{
resb.Append((bt[i]) ? "1" : "0");
colbump++;
rowbump++;
if (rowbump == 24)
{
if (i < (bt.Length - 1))
{
rowbump = 0;
colbump = 0;
resb.Append(Environment.NewLine);
rowcnt++;
resb.Append(_DayOfWeek[rowcnt]);
}
}
else
{
if (colbump == 3)
{
resb.Append(" ");
colbump = 0;
}
}
}
return resb.ToString();
}
History
- Version 1.2.1.0 - Initial release to everyone
- Version 1.2.1.1 - Fixed decoding of the
LoginHours
when the TimeBias
value is negative