Other Posts in LDAP

  1. Getting a User's Email Address from Active Directory
  2. Getting Names and Email Addresses from Active Directory Groups
  3. Getting All Contact Information from Active Directory
  4. Active Directory Utility for C#/ASP.Net
  5. Scanning a Network for Computers in C#

Active Directory Utility for C#/ASP.Net

10/15/2008

I've shown a couple of ways to interface with Active Directory but to be honest, most of them were rather basic at best. So I decided what was really needed was a basic utility class to help you, so I've come up with a couple of classes to help:

Directory.cs

   1: /*
   2: Copyright (c) 2010 <a href="http://www.gutgames.com">James Craig</a>
   3: 
   4: Permission is hereby granted, free of charge, to any person obtaining a copy
   5: of this software and associated documentation files (the "Software"), to deal
   6: in the Software without restriction, including without limitation the rights
   7: to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
   8: copies of the Software, and to permit persons to whom the Software is
   9: furnished to do so, subject to the following conditions:
  10: 
  11: The above copyright notice and this permission notice shall be included in
  12: all copies or substantial portions of the Software.
  13: 
  14: THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15: IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16: FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17: AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18: LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  19: OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  20: THE SOFTWARE.*/
  21:  
  22: #region Usings
  23: using System;
  24: using System.Collections.Generic;
  25: using System.DirectoryServices;
  26: #endregion
  27:  
  28: namespace Utilities.LDAP
  29: {
  30:     /// <summary>
  31:     /// Class for helping with AD
  32:     /// </summary>
  33:     public class Directory : IDisposable
  34:     {
  35:         #region Constructors
  36:         /// <summary>
  37:         /// Constructor
  38:         /// </summary>
  39:         /// <param name="UserName">User name used to log in</param>
  40:         /// <param name="Password">Password used to log in</param>
  41:         /// <param name="Path">Path of the LDAP server</param>
  42:         /// <param name="Query">Query to use in the search</param>
  43:         public Directory(string Query, string UserName, string Password, string Path)
  44:         {
  45:             Entry = new DirectoryEntry(Path, UserName, Password, AuthenticationTypes.Secure);
  46:             this.Path = Path;
  47:             this.UserName = UserName;
  48:             this.Password = Password;
  49:             this.Query = Query;
  50:             Searcher = new DirectorySearcher(Entry);
  51:             Searcher.Filter = Query;
  52:             Searcher.PageSize = 1000;
  53:         }
  54:         #endregion
  55:  
  56:         #region Public Functions
  57:         /// <summary>
  58:         /// Finds a user by his user name
  59:         /// </summary>
  60:         /// <param name="UserName">User name to search by</param>
  61:         /// <returns>The user's entry</returns>
  62:         public Entry FindUserByUserName(string UserName)
  63:         {
  64:             List<Entry> Entries = FindUsers("samAccountName=" + UserName);
  65:             if (Entries.Count > 0)
  66:             {
  67:                 return Entries[0];
  68:             }
  69:             return null;
  70:         }
  71:  
  72:         /// <summary>
  73:         /// Finds all active users
  74:         /// </summary>
  75:         /// <param name="Filter">Filter used to modify the query</param>
  76:         /// <param name="args">Additional arguments (used in string formatting</param>
  77:         /// <returns>A list of all active users' entries</returns>
  78:         public List<Entry> FindActiveUsers(string Filter, params object[] args)
  79:         {
  80:             Filter = string.Format(Filter, args);
  81:             Filter = string.Format("(&((userAccountControl:1.2.840.113556.1.4.803:=512)(!(userAccountControl:1.2.840.113556.1.4.803:=2))(!(cn=*$)))({0}))", Filter);
  82:             return FindUsers(Filter);
  83:         }
  84:  
  85:         /// <summary>
  86:         /// Finds all users
  87:         /// </summary>
  88:         /// <param name="Filter">Filter used to modify the query</param>
  89:         /// <param name="args">Additional arguments (used in string formatting</param>
  90:         /// <returns>A list of all users meeting the specified Filter</returns>
  91:         public List<Entry> FindUsers(string Filter, params object[] args)
  92:         {
  93:             Filter = string.Format(Filter, args);
  94:             Filter = string.Format("(&(objectClass=User)(objectCategory=Person)({0}))", Filter);
  95:             Searcher.Filter = Filter;
  96:             return FindAll();
  97:         }
  98:  
  99:         /// <summary>
 100:         /// Finds all computers
 101:         /// </summary>
 102:         /// <param name="Filter">Filter used to modify the query</param>
 103:         /// <param name="args">Additional arguments (used in string formatting</param>
 104:         /// <returns>A list of all computers meeting the specified Filter</returns>
 105:         public List<Entry> FindComputers(string Filter, params object[] args)
 106:         {
 107:             Filter = string.Format(Filter, args);
 108:             Filter = string.Format("(&(objectClass=computer)({0}))", Filter);
 109:             Searcher.Filter = Filter;
 110:             return FindAll();
 111:         }
 112:  
 113:         /// <summary>
 114:         /// Finds all active users and groups
 115:         /// </summary>
 116:         /// <param name="Filter">Filter used to modify the query</param>
 117:         /// <param name="args">Additional arguments (used in string formatting</param>
 118:         /// <returns>A list of all active groups' entries</returns>
 119:         public List<Entry> FindActiveUsersAndGroups(string Filter, params object[] args)
 120:         {
 121:             Filter = string.Format(Filter, args);
 122:             Filter = string.Format("(&((userAccountControl:1.2.840.113556.1.4.803:=512)(!(userAccountControl:1.2.840.113556.1.4.803:=2))(!(cn=*$)))({0}))", Filter);
 123:             return FindUsersAndGroups(Filter);
 124:         }
 125:  
 126:         /// <summary>
 127:         /// Finds all users and groups
 128:         /// </summary>
 129:         /// <param name="Filter">Filter used to modify the query</param>
 130:         /// <param name="args">Additional arguments (used in string formatting</param>
 131:         /// <returns>A list of all users and groups meeting the specified Filter</returns>
 132:         public List<Entry> FindUsersAndGroups(string Filter, params object[] args)
 133:         {
 134:             Filter = string.Format(Filter, args);
 135:             Filter = string.Format("(&(|(&(objectClass=Group)(objectCategory=Group))(&(objectClass=User)(objectCategory=Person)))({0}))", Filter);
 136:             Searcher.Filter = Filter;
 137:             return FindAll();
 138:         }
 139:  
 140:         /// <summary>
 141:         /// Finds all active groups
 142:         /// </summary>
 143:         /// <param name="Filter">Filter used to modify the query</param>
 144:         /// <param name="args">Additional arguments (used in string formatting</param>
 145:         /// <returns>A list of all active groups' entries</returns>
 146:         public List<Entry> FindActiveGroups(string Filter, params object[] args)
 147:         {
 148:             Filter = string.Format(Filter, args);
 149:             Filter = string.Format("(&((userAccountControl:1.2.840.113556.1.4.803:=512)(!(userAccountControl:1.2.840.113556.1.4.803:=2))(!(cn=*$)))({0}))", Filter);
 150:             return FindGroups(Filter);
 151:         }
 152:  
 153:         /// <summary>
 154:         /// Finds all groups
 155:         /// </summary>
 156:         /// <param name="Filter">Filter used to modify the query</param>
 157:         /// <param name="args">Additional arguments (used in string formatting</param>
 158:         /// <returns>A list of all groups meeting the specified Filter</returns>
 159:         public List<Entry> FindGroups(string Filter, params object[] args)
 160:         {
 161:             Filter = string.Format(Filter, args);
 162:             Filter = string.Format("(&(objectClass=Group)(objectCategory=Group)({0}))", Filter);
 163:             Searcher.Filter = Filter;
 164:             return FindAll();
 165:         }
 166:  
 167:         /// <summary>
 168:         /// Returns a group's list of members
 169:         /// </summary>
 170:         /// <param name="GroupName">The group's name</param>
 171:         /// <returns>A list of the members</returns>
 172:         public List<Utilities.LDAP.Entry> FindActiveGroupMembers(string GroupName)
 173:         {
 174:             try
 175:             {
 176:                 List<Utilities.LDAP.Entry> Entries = this.FindGroups("cn=" + GroupName);
 177:                 if (Entries.Count < 1)
 178:                     return new List<Utilities.LDAP.Entry>();
 179:  
 180:                 return this.FindActiveUsersAndGroups("memberOf=" + Entries[0].DistinguishedName);
 181:             }
 182:             catch
 183:             {
 184:                 return new List<Utilities.LDAP.Entry>();
 185:             }
 186:         }
 187:  
 188:         /// <summary>
 189:         /// Finds all entries that match the query
 190:         /// </summary>
 191:         /// <returns>A list of all entries that match the query</returns>
 192:         public List<Entry> FindAll()
 193:         {
 194:             List<Entry> ReturnedResults = new List<Entry>();
 195:             SearchResultCollection Results = Searcher.FindAll();
 196:             foreach (SearchResult Result in Results)
 197:             {
 198:                 ReturnedResults.Add(new Entry(Result.GetDirectoryEntry()));
 199:             }
 200:             Results.Dispose();
 201:             return ReturnedResults;
 202:         }
 203:  
 204:         /// <summary>
 205:         /// Finds one entry that matches the query
 206:         /// </summary>
 207:         /// <returns>A single entry matching the query</returns>
 208:         public Entry FindOne()
 209:         {
 210:             SearchResult Result = Searcher.FindOne();
 211:             return new Entry(Result.GetDirectoryEntry());
 212:         }
 213:  
 214:         /// <summary>
 215:         /// Closes the directory
 216:         /// </summary>
 217:         public void Close()
 218:         {
 219:             Entry.Close();
 220:         }
 221:  
 222:         /// <summary>
 223:         /// Checks to see if the person was authenticated
 224:         /// </summary>
 225:         /// <returns>true if they were authenticated properly, false otherwise</returns>
 226:         public bool Authenticate()
 227:         {
 228:             try
 229:             {
 230:                 if (!Entry.Guid.ToString().ToLower().Trim().Equals(""))
 231:                 {
 232:                     return true;
 233:                 }
 234:             }
 235:             catch
 236:             {
 237:             }
 238:             return false;
 239:         }
 240:         #endregion
 241:  
 242:         #region Properties
 243:         /// <summary>
 244:         /// Path of the server
 245:         /// </summary>
 246:         public string Path
 247:         {
 248:             get { return _Path; }
 249:             set
 250:             {
 251:                 _Path = value;
 252:                 if (Entry != null)
 253:                 {
 254:                     Entry.Close();
 255:                 }
 256:                 Entry = new DirectoryEntry(_Path, _UserName, _Password, AuthenticationTypes.Secure);
 257:                 Searcher = new DirectorySearcher(Entry);
 258:                 Searcher.Filter = Query;
 259:                 Searcher.PageSize = 1000;
 260:             }
 261:         }
 262:  
 263:         /// <summary>
 264:         /// User name used to log in
 265:         /// </summary>
 266:         public string UserName
 267:         {
 268:             get { return _UserName; }
 269:             set
 270:             {
 271:                 _UserName = value;
 272:                 if (Entry != null)
 273:                 {
 274:                     Entry.Close();
 275:                 }
 276:                 Entry = new DirectoryEntry(_Path, _UserName, _Password, AuthenticationTypes.Secure);
 277:                 Searcher = new DirectorySearcher(Entry);
 278:                 Searcher.Filter = Query;
 279:                 Searcher.PageSize = 1000;
 280:             }
 281:         }
 282:  
 283:         /// <summary>
 284:         /// Password used to log in
 285:         /// </summary>
 286:         public string Password
 287:         {
 288:             get { return _Password; }
 289:             set
 290:             {
 291:                 _Password = value;
 292:                 if (Entry != null)
 293:                 {
 294:                     Entry.Close();
 295:                 }
 296:                 Entry = new DirectoryEntry(_Path, _UserName, _Password, AuthenticationTypes.Secure);
 297:                 Searcher = new DirectorySearcher(Entry);
 298:                 Searcher.Filter = Query;
 299:                 Searcher.PageSize = 1000;
 300:             }
 301:         }
 302:  
 303:         /// <summary>
 304:         /// The query that is being made
 305:         /// </summary>
 306:         public string Query
 307:         {
 308:             get { return _Query; }
 309:             set
 310:             {
 311:                 _Query = value;
 312:                 Searcher.Filter = _Query;
 313:             }
 314:         }
 315:  
 316:         /// <summary>
 317:         /// Decides what to sort the information by
 318:         /// </summary>
 319:         public string SortBy
 320:         {
 321:             get { return _SortBy; }
 322:             set
 323:             {
 324:                 _SortBy = value;
 325:                 Searcher.Sort.PropertyName = _SortBy;
 326:                 Searcher.Sort.Direction = SortDirection.Ascending;
 327:             }
 328:         }
 329:         #endregion
 330:  
 331:         #region Private Variables
 332:         private string _Path = "";
 333:         private string _UserName = "";
 334:         private string _Password = "";
 335:         private DirectoryEntry Entry = null;
 336:         private string _Query = "";
 337:         private DirectorySearcher Searcher = null;
 338:         private string _SortBy = "";
 339:         #endregion
 340:  
 341:         #region IDisposable Members
 342:  
 343:         public void Dispose()
 344:         {
 345:             if (Entry != null)
 346:             {
 347:                 Entry.Dispose();
 348:                 Entry = null;
 349:             }
 350:             if (Searcher != null)
 351:             {
 352:                 Searcher.Dispose();
 353:                 Searcher = null;
 354:             }
 355:         }
 356:  
 357:         #endregion
 358:     }
 359: }

Entry.cs

   1: /*
   2: Copyright (c) 2010 <a href="http://www.gutgames.com">James Craig</a>
   3: 
   4: Permission is hereby granted, free of charge, to any person obtaining a copy
   5: of this software and associated documentation files (the "Software"), to deal
   6: in the Software without restriction, including without limitation the rights
   7: to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
   8: copies of the Software, and to permit persons to whom the Software is
   9: furnished to do so, subject to the following conditions:
  10: 
  11: The above copyright notice and this permission notice shall be included in
  12: all copies or substantial portions of the Software.
  13: 
  14: THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15: IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16: FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17: AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18: LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  19: OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  20: THE SOFTWARE.*/
  21:  
  22: #region Usings
  23: using System;
  24: using System.Collections.Generic;
  25: using System.DirectoryServices;
  26: #endregion
  27:  
  28: namespace Utilities.LDAP
  29: {
  30:     /// <summary>
  31:     /// Directory entry class
  32:     /// </summary>
  33:     public class Entry : IDisposable
  34:     {
  35:         #region Constructors
  36:         /// <summary>
  37:         /// Constructor
  38:         /// </summary>
  39:         /// <param name="DirectoryEntry">Directory entry for the item</param>
  40:         public Entry(DirectoryEntry DirectoryEntry)
  41:         {
  42:             this._DirectoryEntry = DirectoryEntry;
  43:         }
  44:         #endregion
  45:  
  46:         #region Properties
  47:         /// <summary>
  48:         /// Actual base directory entry
  49:         /// </summary>
  50:         public DirectoryEntry DirectoryEntry
  51:         {
  52:             get { return _DirectoryEntry; }
  53:             set { _DirectoryEntry = value; }
  54:         }
  55:  
  56:         /// <summary>
  57:         /// Email property for this entry
  58:         /// </summary>
  59:         public string Email
  60:         {
  61:             get { return (string)GetValue("mail"); }
  62:             set { SetValue("mail", value); }
  63:         }
  64:  
  65:         /// <summary>
  66:         /// distinguished name property for this entry
  67:         /// </summary>
  68:         public string DistinguishedName
  69:         {
  70:             get { return (string)GetValue("distinguishedname"); }
  71:             set { SetValue("distinguishedname", value); }
  72:         }
  73:  
  74:         /// <summary>
  75:         /// country code property for this entry
  76:         /// </summary>
  77:         public string CountryCode
  78:         {
  79:             get { return (string)GetValue("countrycode"); }
  80:             set { SetValue("countrycode", value); }
  81:         }
  82:  
  83:         /// <summary>
  84:         /// company property for this entry
  85:         /// </summary>
  86:         public string Company
  87:         {
  88:             get { return (string)GetValue("company"); }
  89:             set { SetValue("company", value); }
  90:         }
  91:  
  92:         /// <summary>
  93:         /// MemberOf property for this entry
  94:         /// </summary>
  95:         public List<string> MemberOf
  96:         {
  97:             get
  98:             {
  99:                 List<string> Values = new List<string>();
 100:                 PropertyValueCollection Collection = DirectoryEntry.Properties["memberof"];
 101:                 foreach (object Item in Collection)
 102:                 {
 103:                     Values.Add((string)Item);
 104:                 }
 105:                 return Values;
 106:             }
 107:         }
 108:  
 109:         /// <summary>
 110:         /// display name property for this entry
 111:         /// </summary>
 112:         public string DisplayName
 113:         {
 114:             get { return (string)GetValue("displayname"); }
 115:             set { SetValue("displayname", value); }
 116:         }
 117:  
 118:         /// <summary>
 119:         /// initials property for this entry
 120:         /// </summary>
 121:         public string Initials
 122:         {
 123:             get { return (string)GetValue("initials"); }
 124:             set { SetValue("initials", value); }
 125:         }
 126:  
 127:         /// <summary>
 128:         /// title property for this entry
 129:         /// </summary>
 130:         public string Title
 131:         {
 132:             get { return (string)GetValue("title"); }
 133:             set { SetValue("title", value); }
 134:         }
 135:  
 136:         /// <summary>
 137:         /// samaccountname property for this entry
 138:         /// </summary>
 139:         public string SamAccountName
 140:         {
 141:             get { return (string)GetValue("samaccountname"); }
 142:             set { SetValue("samaccountname", value); }
 143:         }
 144:  
 145:         /// <summary>
 146:         /// givenname property for this entry
 147:         /// </summary>
 148:         public string GivenName
 149:         {
 150:             get { return (string)GetValue("givenname"); }
 151:             set { SetValue("givenname", value); }
 152:         }
 153:  
 154:         /// <summary>
 155:         /// cn property for this entry
 156:         /// </summary>
 157:         public string CN
 158:         {
 159:             get { return (string)GetValue("cn"); }
 160:             set { SetValue("cn", value); }
 161:         }
 162:  
 163:         /// <summary>
 164:         /// name property for this entry
 165:         /// </summary>
 166:         public string Name
 167:         {
 168:             get { return (string)GetValue("name"); }
 169:             set { SetValue("name", value); }
 170:         }
 171:  
 172:         /// <summary>
 173:         /// office property for this entry
 174:         /// </summary>
 175:         public string Office
 176:         {
 177:             get { return (string)GetValue("physicaldeliveryofficename"); }
 178:             set { SetValue("physicaldeliveryofficename", value); }
 179:         }
 180:  
 181:         /// <summary>
 182:         /// telephone number property for this entry
 183:         /// </summary>
 184:         public string TelephoneNumber
 185:         {
 186:             get { return (string)GetValue("telephonenumber"); }
 187:             set { SetValue("telephonenumber", value); }
 188:         }
 189:         #endregion
 190:  
 191:         #region Public Functions
 192:         /// <summary>
 193:         /// Saves any changes that have been made
 194:         /// </summary>
 195:         public void Save()
 196:         {
 197:             _DirectoryEntry.CommitChanges();
 198:         }
 199:  
 200:         /// <summary>
 201:         /// Gets a value from the entry
 202:         /// </summary>
 203:         /// <param name="Property">Property you want the information about</param>
 204:         /// <returns>an object containing the property's information</returns>
 205:         public object GetValue(string Property)
 206:         {
 207:             PropertyValueCollection Collection = DirectoryEntry.Properties[Property];
 208:             if (Collection != null)
 209:             {
 210:                 return Collection.Value;
 211:             }
 212:             return null;
 213:         }
 214:  
 215:         /// <summary>
 216:         /// Sets a property of the entry to a specific value
 217:         /// </summary>
 218:         /// <param name="Property">Property of the entry to set</param>
 219:         /// <param name="Value">Value to set the property to</param>
 220:         public void SetValue(string Property, object Value)
 221:         {
 222:             PropertyValueCollection Collection = DirectoryEntry.Properties[Property];
 223:             if (Collection != null)
 224:             {
 225:                 Collection.Value = Value;
 226:             }
 227:         }
 228:         #endregion
 229:  
 230:         #region Private Variables
 231:         private DirectoryEntry _DirectoryEntry;
 232:         #endregion
 233:  
 234:         #region IDisposable Members
 235:  
 236:         public void Dispose()
 237:         {
 238:             if (_DirectoryEntry != null)
 239:             {
 240:                 _DirectoryEntry.Dispose();
 241:                 _DirectoryEntry = null;
 242:             }
 243:         }
 244:  
 245:         #endregion
 246:     }
 247: }

This contains two files: Directory.cs and Entry.cs. Directory is the main object for connecting to your Active Directory setup, set up the search that you want to conduct, etc. It also comes with a couple of basic searches set up: get a user by user name, get all active users, get all users, etc. The last two can be modified further by entering your own sub filter, for example making sure they have an email address.

The second class is the entry class, which is the actual entry itself. I added some properties to help you find common things (email, phone, screen name, etc.) as well as a couple functions to help you out (Save, GetValue, and SetValue). So combined you should have enough of the functionality rolled up such that you can get started.

I will say that some functionality that I want in there is missing (entry creation/deletion, resetting/setting a user's password, etc.) and I'll add it prior to moving it over to my utility library but I think it's a decent start. Anyway, try out the code, leave feedback, and happy coding.



Comments