the System.DirectoryServices namespace from .net allready contains a lot of build in functionality for use with Active Directory. However some default task have to coded manually still. Of course that meant writing some support tools which I've been happilly using for some time now :)
The code posted here are the base classes used for those tools. At a later time if anyone is interested I'll include the tools project too (simple LDAP browser, user form that can lock/unlock, set pw and mail forwardings)
The code is wrapped around the System.DirectoryServices objects and thus need a reference to that (default .net) namespace. The static ActiveDirectory class contains methods to quickly search entries. The ActiveDirectoryEntry class is a wrapper around the .net DirectoryEntry class which encapsulates functions to quickly access properties. ADFilter makes creating queries easier and UserInfo is a class that uses the other classes to quickly get and set user info.
Example usage:
//get a single entry
ActiveDirectoryEntry d = ActiveDirectory.GetEntry("srv*", "computer");
Console.WriteLine(d["whenCreated"]);
//The default activedirectory queries can be used to find entries
//list all computers:
foreach (
ActiveDirectoryEntry de
in ActiveDirectory.GetEntries(
"objectClass=computer"))
{
Console.WriteLine(de.Name);
}
//list all users whose password was changed in the last 2 months
string date =
ActiveDirectory.FormatDateTime(
DateTime.Today.AddMonths(-2));
foreach (
ActiveDirectoryEntry de
in ActiveDirectory.GetEntries(
"(&(objectClass=user)(whenCreated>="+date+
"))"))
{
Console.WriteLine("{0}, created: {1}", de.Name, de["whenCreated"]);
}
//Another method is to use the ADFilter object, which makes writing those nasty queries somewhat easer
//Doing the same as above:
ADFilter filter =
new ADFilter();
filter.objectClass =
"user";
filter.Add(
new ADCriterium(
"whenCreated",
Operator.Equals |
Operator.GreaterThan,
DateTime.Today.AddMonths(-2)));
Console.WriteLine(filter);
foreach (
ActiveDirectoryEntry de
in ActiveDirectory.GetEntries(filter))
{
Console.WriteLine("{0}, created: {1}", de.Name, de["whenCreated"]);
}
//for a small query as above the filter isn't so important, but imagine the more complicated
//scenario of also wanting to search for at names beginning with w or a badlogincount of 0
ADFilter subquery =
new ADFilter();
subquery[
"cn"] =
"w*";
subquery[
"badPwdCount"] = 0;
filter.Add(subquery);
//this produces the following filter:
// (&(objectClass=user)(whenCreated>=070505000000Z)(&(cn=w*)(badPwdCount=0)))
Console.WriteLine(filter);
foreach (
ActiveDirectoryEntry de
in ActiveDirectory.GetEntries(filter))
{
Console.WriteLine("{0}, created: {1}", de.Name, de["whenCreated"]);
}
Example usage UserInfo class:
//getting the current user can be done with the GetCurrentUser function
UserInfo ui =
UserInfo.GetCurrentUser();
string surname = ui.SurName;
//some other user (when using wildcard searches, the first found entry is used)ui =
new UserInfo(
"Hotd*");
//NB: if the user is not found, calling properties will throw an exception
// which informs that no active directory entry exists
// You can check if a valid entry was found by checking the HasActiveDirectoryEntry property
if (!ui.HasActiveDirectoryEntry)
return;
if (ui.IsMailForwarded)
{
string mail = ui.MailForwardedTo.FullName;
}
//other functions included, for example password changing
ui.SetPassword("NewPassword", true);
The classes:
//#define Framework30 //uncomment this line if you want to include framework 3.0 functions such as Linq
using System;
using System.Collections.Generic;
using System.Text;
//needs a reference to System.DirectoryServices
using System.DirectoryServices;
using ad = System.DirectoryServices.ActiveDirectory;
using System.Runtime.InteropServices;
using System.Collections.Specialized;
#if Framework30
using System.Linq;
using System.Linq.Expressions;
#endif
namespace AD
{
///
<summary>
/// Base class for all returned directory entries
/// </summary>
public class ActiveDirectoryEntry{
public
readonly DirectoryEntry Entry;
public ActiveDirectoryEntry(
string Path)
: this(new DirectoryEntry(Path))
{
}
internal ActiveDirectoryEntry(
DirectoryEntry Entry)
{
if (Entry ==
null)
throw new ArgumentNullException();
this.Entry = Entry;
}
public static ActiveDirectoryEntry GetEntry(DirectoryEntry Entry)
{if (Entry == null) return null;
return new ActiveDirectoryEntry(Entry);
}
/*
public static implicit operator ActiveDirectoryEntry(DirectoryEntry de)
{if (de == null) return null;
return new ActiveDirectoryEntry(de);
}*/
/*
public static implicit operator DirectoryEntry(ActiveDirectoryEntry de)
{return de.Entry;
}*/
public
string Path
{
get { return Entry.Path; }
}public
void Invoke(
string methodName,
params object[] args)
{
Entry.Invoke(methodName, args);
}
public object InvokeGet(
string methodName)
{
return Entry.InvokeGet(methodName);
}
public void InvokeSet(string methodName, params object[] args)
{Entry.InvokeSet(methodName, args);
}public
void CommitChanges()
{
Entry.CommitChanges();
}
public void RefreshCache()
{
Entry.RefreshCache();
}
ActiveDirectoryEntry parent;
public ActiveDirectoryEntry Parent
{
get
{
if (parent ==
null)
parent = new ActiveDirectoryEntry(Entry.Parent);
return parent;
} }const string memberof = "memberof"
const string members = "member"
public IEnumerable<ActiveDirectoryEntry> EnumerateMemberOf()
{
foreach (
string s
in Properties[memberof])
{
yield return GetEntry(s);
}
ActiveDirectoryEntry e = GetPrimaryGroup();
if (e != null)yield return e;
}
internal
bool IsMemberOf(
string Group)
{
object o =
this[memberof];
if (o ==
null)
return false;
IEnumerable<
object> owners = o
is string ?
new string[] { (
string)o } : (
object[])o;
Group =
"CN=" + Group;
foreach (
string s
in owners)
{
if (s.Contains(Group)) return true;
}
return false; }public
bool IsMemberOf(
ActiveDirectoryEntry group)
{
foreach (
ActiveDirectoryEntry ad
in EnumerateMemberOf())
{
if (ad.Equals(group)) return true;
}
return false; }public
AccessType GetAccessType(
ActiveDirectoryEntry group)
{
List<
ActiveDirectoryEntry> list = GetMemberOf();
foreach (
ActiveDirectoryEntry ad
in list)
{
if (ad.Equals(group)) return AccessType.Direct;
}
foreach (ActiveDirectoryEntry ad in list)
{AccessType at = ad.GetAccessType(group);
if (at !=
AccessType.None)
return AccessType.Nested;
}
return AccessType.None; }public
string Prefix
{
get
{
return Path.Remove(Path.IndexOf("//") + 2);
} }ActiveDirectoryEntry GetEntry(
string s)
{
string path = Prefix + Clean(s);
return new ActiveDirectoryEntry(path);
}
string Clean(string s)
{if (s.IndexOf(
'/') >= 0)
{
s = s.Replace("/", @"\/");
}
return s;
}public
bool IsTop
{
get
{
return Entry.Parent.SchemaClassName == "domainDNS"
} }public
bool IsEndpoint
{
get
{
switch (Class)
{
case
DefaultClass.organizationalUnit:
case DefaultClass.container:
return false;
default:
return true;
} } }public
string SchemaClassName
{
get { return Entry.SchemaClassName; }
}public
DefaultClass Class
{
get
{
try
{
return (DefaultClass)Enum.Parse(typeof(DefaultClass), Entry.SchemaClassName);
}
catch
{return DefaultClass.any;
} } }public
List<
ActiveDirectoryEntry> GetMemberOf()
{
List<
ActiveDirectoryEntry> res =
new List<
ActiveDirectoryEntry>();
foreach (
ActiveDirectoryEntry de
in EnumerateMemberOf())
{
res.Add(de);
}
return res;
}public
PropertyValueCollection MemberOf
{
get { return Properties[memberof]; }
}public
PropertyValueCollection Members
{
get { return Properties[members]; }
}public
IEnumerable<
ActiveDirectoryEntry> GetMembers()
{
foreach (
string s
in Members)
{
yield return GetEntry(s);
}System.Security.Principal.
SecurityIdentifier sid = GetSID();
if (sid !=
null)
{
string s = sid.ToString();
int i = s.LastIndexOf(
'-');
if (i != -1)
{
s = s.Substring(++i);
//byte[] b = GetSIDArray();
ADFilter filter = new ADFilter("primarygroupID", s);
filter.ObjectClass = DefaultClass.user;
foreach (
ActiveDirectoryEntry ad
in filter.GetEntries())
{
//if(IsEqual(b,ad.GetSIDArray(),b.Length-4))
yield return ad;
}}
}
}
bool IsEqual(
byte[] b1,
byte[] b2,
int len)
{
for (
int i = 0; i < len; i++)
{
if (b1[i] != b2[i]) return false;
}
return true; }public
IEnumerable<
ActiveDirectoryEntry> DeepEnumerateMembers()
{
if (
this.Class ==
DefaultClass.user)
yield return this;
else{
foreach (
ActiveDirectoryEntry ad
in GetMembers())
{
foreach (
ActiveDirectoryEntry m
in ad.DeepEnumerateMembers())
{
yield return m;
} } } }public
string Name
{
get
{
object o = this["cn"];
if (o != null) return o.ToString();
o = this["ou"];
if (o != null) return o.ToString();
return Entry.Name;
} }public System.Security.Principal.
SecurityIdentifier GetSID()
{
byte[] b = GetSIDArray();
if (b == null) return null;
return ActiveDirectory.GetSID(b);
}byte[] GetSIDArray()
{
object o = GetValue(
"objectSid");
if (o ==
null)
return null;
byte[] b = o
as byte[];
if (b ==
null)
{
if (o
is Guid)
b = ((Guid)o).ToByteArray();
else
throw new Exception("Could not parse SID of type " + o.GetType().FullName);
}
return b; }public
int PrimaryGroupID
{
get { return GetValue<int>("primarygroupID"); }
}public
ActiveDirectoryEntry GetPrimaryGroup()
{
int pg = PrimaryGroupID;
if (pg == 0) return null;
System.Security.Principal.SecurityIdentifier sid = GetSID();
if (sid == null) return null;
string s = sid.ToString();
int i = s.LastIndexOf('-');
if (i == -1) return null;
return ActiveDirectory.GetBySID(s.Substring(0, ++i) + pg.ToString());
}public T GetValue<T>(
string Property)
{
object o = this[Property];
if (o == null) return default(T);
return (T)o;
}public T GetValue<T>(
string Property, T Default)
{
object o = this[Property];
if (o == null) return Default;
return (T)o;
}public
object GetValue(
string Property)
{
return this[Property];
}
/// <summary>
/// When true, all changes are automatically comitted when a property is changed
/// </summary>
public bool AutoSaveChanges = true;
public
object this[
string PropertyName]
{
get
{
return ActiveDirectoryProperty.GetValue(Properties[PropertyName]);
}
set
{PropertyValueCollection pv = Properties[PropertyName];
if (pv ==
null)
{
if (value == null) return;
throw new Exception(PropertyName + " not found");
}
ActiveDirectoryProperty.SetValue(pv, value);if (AutoSaveChanges)
CommitChanges();
}
}
public
PropertyCollection Properties
{
get { return Entry.Properties; }
}///
<summary>
/// You can get the value directly by using the indexed property (e.g. YourEntry["PropertyName"] ), but
/// the <see cref="ActiveDirectoryProperty"/> encapsulates more functionality around the property.
/// </summary>
/// <param name="PropertyName"></param>
/// <returns></returns>
public ActiveDirectoryProperty GetProperty(
string PropertyName)
{
return new ActiveDirectoryProperty(this, PropertyName);
}public
IEnumerable<
ActiveDirectoryProperty> GetProperties()
{
foreach (
PropertyValueCollection pv
in Properties)
{
yield return GetProperty(pv.PropertyName);
} }public
DirectoryEntries Children
{
get { return Entry.Children; }
}public
override int GetHashCode()
{
return Path.GetHashCode();
}public
override bool Equals(
object obj)
{
return obj
is ActiveDirectoryEntry &&
(obj as ActiveDirectoryEntry).Path == Path;
}public
override string ToString()
{
return Name;
}public
bool ImplementsClass(
params string[] classes)
{
foreach (
string s
in Properties[
"objectClass"].Value
as object[])
{
for (
int i = 0; i < classes.Length; i++)
{
if (classes[i] == s) return true;
} }
return false; }}
public
enum AccessType{
None,
Direct,
Nested
}
public
class ActiveDirectoryProperty{
public
readonly ActiveDirectoryEntry Entry;
public readonly string PropertyName;
public readonly bool IsSID;
PropertyValueCollection pv;
public ActiveDirectoryProperty(
ActiveDirectoryEntry Entry,
string PropertyName)
{
this.Entry = Entry;
this.PropertyName = PropertyName;
this.pv = Entry.Properties[PropertyName];
IsSID = PropertyName == "objectSid"
}
public
bool AutoSaveChanges
{
get { return Entry.AutoSaveChanges; }
set { Entry.AutoSaveChanges = value; }
}public
object Value
{
get
{
return GetValue(pv);
}
set
{Entry[PropertyName] = value;
} }public
bool HasValue
{
get { return pv != null && pv.Count > 0; }
}public
IEnumerable<
object> GetValues()
{
if (!HasValue)
yield break;
for (
int i = 0; i < pv.Count; i++)
{
yield return this[i];
} }public
object this[
int Index]
{
get
{
return ActiveDirectory.Convert(pv[Index]);
} }public
string GetValueString()
{
return GetValueString(Environment.NewLine);
}public
string GetValueString(
string Seperator)
{
if (!HasValue)
return null;
string res =
null;
for (
int i = 0; i < pv.Count; i++)
{
if (i > 0) res += Seperator;
res += GetValueString(this[i]);
}
return res; }string GetValueString(
object o)
{
if (o
is byte[])
{
if (IsSID) return ActiveDirectory.GetSID((byte[])o).ToString();
return ActiveDirectory.ByteArrayToString((byte[])o);
}
return o.ToString(); }public
override string ToString()
{
return PropertyName + ": " + GetValueString(", ");
}public
static object GetValue(
PropertyValueCollection pv)
{
object o;
if (pv == null || (o = pv.Value) == null) return null;
return ActiveDirectory.Convert(o);
}public
static void SetValue(
PropertyValueCollection pv,
object value)
{
if (pv.Value == value)
return;
if (value ==
null)
pv.Remove(pv.Value);
else
{
if (pv.Value
is IADsLargeInteger && value
is DateTime)
{
//pv.Value = (int)(uint)((DateTime)value).ToFileTime();
ActiveDirectory.SetDateTime(pv, (DateTime)value);
}
elsepv.Value = value;
} }}
///
<summary>
/// The default 'objectClass' types on which a search can be performed
/// </summary>
public enum DefaultClass{
any, group, user, organizationalUnit, computer, container
}
///
<summary>
/// Interface type to convert AD date time objects
/// </summary>[
ComImport]
[
Guid(
"9068270B-0939-11D1-8BE1-00C04FD8D503")]
[
InterfaceType(
ComInterfaceType.InterfaceIsDual)]
internal interface IADsLargeInteger{
[DispId(0x00000002)]
int HighPart { get; set; }
[DispId(0x00000003)]
int LowPart { get; set; }
}
///
<summary>
/// Static functions for querying Active Directory
/// </summary>
public static class ActiveDirectory{
#region Main get entries functions
public
static IEnumerable<
ActiveDirectoryEntry> GetEntries(
string Filter)
{
return GetEntries(Filter, (ActiveDirectoryEntry)null);
}
public static IEnumerable<ActiveDirectoryEntry> GetEntries(string Filter, string RootFilter)
{ActiveDirectoryEntry root =
null;
if (RootFilter !=
null)
root = GetFirstEntry(RootFilter);
return GetEntries(Filter, root);
}
public static IEnumerable<ActiveDirectoryEntry> GetEntries(string Filter, ActiveDirectoryEntry root)
{return GetEntries(Filter, root, SearchScope.Subtree);
}
public static IEnumerable<ActiveDirectoryEntry> GetEntries(string Filter, ActiveDirectoryEntry Root, SearchScope Scope)
{AdSearchInfo si = new AdSearchInfo();
si.SearchRoot = Root;
si.Scope = Scope;
si.Filter = Filter;
return si;
}public
static IEnumerable<
ActiveDirectoryEntry> GetEntries(
DefaultClass ObjectClass)
{
return GetEntries(new ADFilter(ObjectClass));
}public
static ActiveDirectoryEntry GetFirstEntry(
string Filter)
{
return GetFirstEntry(Filter, null);
}
public static ActiveDirectoryEntry GetFirstEntry(string Filter, ActiveDirectoryEntry root)
{return GetFirstEntry(GetEntries(Filter, root));
}public
static ActiveDirectoryEntry GetFirstEntry(
IEnumerable<
ActiveDirectoryEntry> ie)
{
foreach (
ActiveDirectoryEntry de
in ie)
{
return de;
}
return null; }#endregion
#region Specific entry searching/ supporting functions
public
static ActiveDirectoryEntry GetFolder(
params string[] Path)
{
ActiveDirectoryEntry de =
null;
foreach (
string s
in Path)
{
de = GetFolder(s, de);
if (de ==
null)
return null;
}
return de;
}
public static ActiveDirectoryEntry GetFolder(string name, ActiveDirectoryEntry parent)
{foreach (
ActiveDirectoryEntry de
in GetFolders(name, parent))
{
return de;
}
return null; }
public static IEnumerable<ActiveDirectoryEntry> GetFolders(string name, ActiveDirectoryEntry parent)
{if (
string.IsNullOrEmpty(name)) name =
"*"
return GetEntries(
string.Format(
//"(|(&(cn={0})(objectClass=group))(ou={0}))""(ou={0})", name), parent, SearchScope.OneLevel);
}public
static IEnumerable<
ActiveDirectoryEntry> GetContainers()
{
return GetContainers(null);
}public
static IEnumerable<
ActiveDirectoryEntry> GetContainers(
ActiveDirectoryEntry parent)
{
return GetEntries(new ADFilter(DefaultClass.container));
}public
static ActiveDirectoryEntry GetSecurityGroup(
string group)
{
return GetFirstEntry(GetFilter(group, "group"));
}public
static ActiveDirectoryEntry GetBySID(System.Security.Principal.
SecurityIdentifier sid)
{
return GetBySID(sid.Value);
}public
static ActiveDirectoryEntry GetBySID(
byte[] sid)
{
StringBuilder sb =
new StringBuilder();
for (
int i = 0; i < sid.Length; i++)
{
if (sid[i] < 33 || sid[i] > 125)
sb.Append("\\").Append(sid[i].ToString("x2"));
else
sb.Append((char)sid[i]);
}
return GetBySID(sb.ToString()); }public
static ActiveDirectoryEntry GetBySID(
string sid)
{
return GetFirstEntry(new ADFilter("objectSid", sid));
}internal
static System.Security.Principal.
SecurityIdentifier GetSID(
byte[] value)
{
return new System.Security.Principal.SecurityIdentifier(value, 0);
}public
static ActiveDirectoryEntry GetEntry(
string cn)
{
return GetEntry(cn, null);
}
public
static ActiveDirectoryEntry GetEntry(
string cn,
string objectclass)
{
return GetFirstEntry(GetFilter(cn, objectclass));
}public
static ActiveDirectoryEntry GetEntry(
string cn,
DefaultClass objectclass)
{
return GetEntry(cn, objectclass.ToString());
}public
static ADFilter GetFilter(
string cn,
string objectclass)
{
ADFilter filter = new ADFilter();
filter.objectClass = objectclass;
filter["cn"] = cn == null ? "*" : cn;
return filter;
}
#endregion#region Other AD related functions
public
static int SynchronizeDomain(
string TargetServer)
{
ad.
DirectoryContext context =
new ad.
DirectoryContext(ad.
DirectoryContextType.DirectoryServer, TargetServer);
int res = 0;
using (ad.
DomainController dc = ad.
DomainController.GetDomainController(context))
{
//dc.SyncReplicaFromAllServers(
foreach (
string partition
in dc.Partitions)
{
dc.SyncReplicaFromAllServers(partition, ad.SyncFromAllServersOptions.AbortIfServerUnavailable);
res++;
}
}
return res;}
static IEnumerable<ad.DomainController> GetDomainControllers()
{
foreach (ad.
DomainController dc
in ad.
Domain.GetCurrentDomain().DomainControllers)
{
yield return dc;
}}
public
static IEnumerable<
string> GetDomainServers()
{
foreach (ad.
DomainController dc
in GetDomainControllers())
{
yield return dc.Name;
} }#endregion
#region DateTime support
public
static bool IsDateTimeComObject(
object o)
{
return o is IADsLargeInteger;
}/// <summary>
/// Converts an <see cref="IADsLargeInteger"/> to a date time
/// </summary>
/// <param name="ComObject"></param>
/// <returns></returns>
public static DateTime GetDateTime(object ComObject)
{
IADsLargeInteger li = (
IADsLargeInteger)ComObject;
if (li.LowPart == -1)
return DateTime.MinValue;
long date = (
long)li.HighPart << 32 | (
uint)li.LowPart;
try{
return DateTime.FromFileTime(date);
}
catch (Exception ex)
{System.Diagnostics.Debug.WriteLine(ex.Message);
return DateTime.MinValue;
}}
const
long lowmask = 0xFFFFFFFF;
internal static void SetDateTime(
PropertyValueCollection pv,
DateTime value)
{
IADsLargeInteger li = (IADsLargeInteger)pv.Value;
long l = value.ToFileTime();
li.HighPart = (int)(l >> 32);
li.LowPart = (int)(l & lowmask);
//Forces update
pv.Value = li;
}
///
<summary>
/// Formats the DateTime to LDIF
/// </summary>
/// <param name="dt"></param>
/// <returns></returns>
public static string FormatDateTime(
DateTime dt)
{
return dt.ToString(@"yyMMddHHmmssZ");
}public
static string FormatBoolean(
bool b)
{
return b ? "TRUE" : "FALSE"
}
#endregion#region Conversion
internal
static object Convert(
object o)
{
if (IsDateTimeComObject(o))
return GetDateTime(o);
if (o
is byte[])
{
byte[] b = (
byte[])o;
if (b.Length == 16)
return new Guid(b);
}//TODO: other com objects
return o;
}
internal static string ByteArrayToString(
byte[] b)
{
StringBuilder sb =
new StringBuilder(
"0x ");
for (
int i = 0; i < b.Length; i++)
{
sb.Append(b[i].ToString("X"));
}
return sb.ToString();
}#endregion
#if Framework30
/// <summary>
/// Used for querying with Linq
/// </summary>
public static ADQuery Entries
{
get
{
return new ADQuery();
}
}
public static ADQuery<UserInfo> Users
{
get
{
return new ADQuery<UserInfo>(UserInfo.GetDefaultUserGroupsFilter());
}
}
#endif}
#region Filter
public interface IADCriterium
{
}
public
class ADFilter :
List<
IADCriterium>,
IADCriterium{
public ADFilter()
{
}
public ADFilter(
string Property,
string Value)
{
this[Property] = Value;
}
public ADFilter(DefaultClass ObjectClass)
{this.ObjectClass = ObjectClass;
}public ADLogicalOperator LogicalOperator;
public
override string ToString()
{
if (Count == 0)
return null;
if (Count == 1)
return this[0].ToString();
string s =
null;
foreach (
IADCriterium c
in this)
{
s += c.ToString();
}
return string.Format(
"({0}{1})", GetOperator(), s);
}string GetOperator()
{
switch (LogicalOperator)
{
case
ADLogicalOperator.AND:
return "&"
case ADLogicalOperator.OR:
return "|"
case ADLogicalOperator.NOT:
return "!"
default:
return null;
} }///
<summary>
/// Gets or sets the criterium for the secified property. If a property is set that was set
/// in a previous step, the previous property criterium will be overridden. For adding
/// multiple criteria of the same property, please use <see cref="Add"/>
/// </summary>
/// <param name="Property"></param>
/// <returns></returns>
public object this[
string Property]
{
get
{
ADCriterium c = GetCriterium(Property);
if (c == null) return null;
return c.Value;
}
set
{ADCriterium c = GetCriterium(Property);
if (c ==
null)
{
if (value != null) Add(Property, value);
}
else if (value == null)Remove(c);
elsec.Value = value;
} }public
void Add(
string Property,
object value)
{
Add(new ADCriterium(Property, value));
}
public
ADFilter AddSubFilter(
ADLogicalOperator Operator)
{
ADFilter filter = new ADFilter();
filter.LogicalOperator = Operator;
Add(filter);
return filter;
}
public ADFilter AddSubFilter(ADLogicalOperator Operator,params IADCriterium[] criteria)
{ADFilter filter = AddSubFilter(Operator);
filter.AddRange(criteria);
return filter;
}public
ActiveDirectoryEntry GetFirstEntry()
{
return ActiveDirectory.GetFirstEntry(GetEntries());
}public
IEnumerable<
ActiveDirectoryEntry> GetEntries()
{
return new AdSearchInfo(this);
}public
ADCriterium GetCriterium(
string Property)
{
foreach (
IADCriterium ic
in this)
{
ADCriterium c = ic
as ADCriterium;
if (c !=
null &&
string.Equals(c.Property, Property,
StringComparison.OrdinalIgnoreCase))
return c;
}
return null; }[System.ComponentModel.
DefaultValue(
null)]
public string objectClass
{
get
{
return this["objectClass"] as string;
}
set
{this["objectClass"] = value;
} }[System.ComponentModel.
DesignerSerializationVisibility(System.ComponentModel.
DesignerSerializationVisibility.Hidden)]
public DefaultClass ObjectClass
{
get
{
string s = objectClass;
if (s == null) return DefaultClass.any;
return (DefaultClass)Enum.Parse(typeof(DefaultClass), s);
}
set
{if (
value ==
DefaultClass.any)
objectClass = null;
else
objectClass = value.ToString();
} }public
static implicit operator string(
ADFilter f)
{
if (f == null) return null;
return f.ToString();
}
public static implicit operator ADFilter(string s)
{if (string.IsNullOrEmpty(s)) return null;
ADFilter f = new ADFilter();
f.Add(new ADLiteralCriterium(s));
return f;
}public
static implicit operator ADFilter(
ADCriterium c)
{
ADFilter f = new ADFilter();
f.Add(c);
return f;
}
public static implicit operator ADFilter(DefaultClass cl)
{ADFilter f = new ADFilter();
f.ObjectClass = cl;
return f;
}}
public
class AdSearchInfo:
IEnumerable<
ActiveDirectoryEntry>
{
ADFilter filter;
public ActiveDirectoryEntry SearchRoot;
public List<string> PropertiesToLoad;
public SearchScope Scope = SearchScope.Subtree;
public AdSearchInfo()
{
}
public AdSearchInfo(
ADFilter Filter)
{
this.filter = Filter;
}
public AdSearchInfo(ADFilter Filter, IEnumerable<string> PropertiesToLoad):this(Filter)
{this.PropertiesToLoad = new List<string>(PropertiesToLoad);
}public
ADFilter Filter
{
get
{
if (filter ==
null)
filter = new ADFilter();
return filter;
}
set
{filter = value;
} }public
void AddLoadProperty(
string prop)
{
if (PropertiesToLoad ==
null)
PropertiesToLoad = new List<string>();
PropertiesToLoad.Add(prop);
}#region IEnumerable<ActiveDirectoryEntry> Members
public
IEnumerator<
ActiveDirectoryEntry> GetEnumerator()
{
DirectoryEntry root = SearchRoot == null ? new DirectoryEntry() : SearchRoot.Entry;
using (
DirectorySearcher ds =
new DirectorySearcher(root, filter,
PropertiesToLoad==null ? null : PropertiesToLoad.ToArray()))
{
ds.SearchScope = Scope;
SearchResultCollection res = ds.FindAll();
foreach (
SearchResult sr
in res)
{
DirectoryEntry de = sr.GetDirectoryEntry();
if (de.Name != root.Name)
yield return new ActiveDirectoryEntry(de);
}}
}
#endregion
#region IEnumerable Members
System.Collections.
IEnumerator System.Collections.
IEnumerable.GetEnumerator()
{
return GetEnumerator();
}#endregion
}
public enum ADLogicalOperator{
AND, OR, NOT
}
[
Flags]
public enum Operator{
Equals = 1,
Approxamately = 3,
GreaterThan = 4,
LessThan = 8
}
public
class ADCriterium :
IADCriterium{
public
readonly string Property;
/// <summary>
/// Operator, can be combined (eg combine ">" and "=" to get ">=" )
/// </summary>
public Operator Operator;
public object Value;
public ADCriterium(
string Property,
object Value)
{
this.Property = Property;
this.Value = Value;
}
public ADCriterium(string Property, Operator Operator, object Value): this(Property, Value)
{this.Operator = Operator;
}protected
virtual string GetFormattedValue()
{
if (Value ==
null)
return null;
if (Value
is DateTime)
return ActiveDirectory.FormatDateTime((DateTime)Value);
if (Value
is bool)
return ActiveDirectory.FormatBoolean((bool)Value);
return Value.ToString();
}bool Implements(
Operator o)
{
return (o & this.Operator) == o;
}
string GetOperator()
{if (Operator == 0)
return "="
string res =
null;
if (Implements(
Operator.Approxamately))
res += "~"
if (Implements(
Operator.LessThan))
res += "<"
if (Implements(
Operator.GreaterThan))
res += ">"
if (Implements(
Operator.Equals))
res += "="
return res;
}public
override string ToString()
{
return string.Format("({0}{1}{2})", Property, GetOperator(), GetFormattedValue());
}}
public class ADLiteralCriterium : IADCriterium
{
public
readonly string Criterium;
public ADLiteralCriterium(
string Criterium)
{
this.Criterium = Criterium;
}public
override string ToString()
{
return Criterium;
}}
#endregion
#if Framework30public class ADQuery<T>
{
AdSearchInfo inf;
public ADQuery() :
this(null)
{
}
public ADQuery(ADFilter filter)
{
this.inf = new AdSearchInfo(filter);
}
public ADQuery<T> Where(Expression<Func<T,bool>> predicate)
{
Add(predicate,inf.Filter);
return this;
}
bool Add(Expression e,ADFilter filter)
{
if (e is LambdaExpression)
{
return Add(((LambdaExpression)e).Body, filter);
}
if (e is BinaryExpression)
{
var b= e as BinaryExpression;
ADLogicalOperator op = filter.LogicalOperator;
if (b.NodeType == ExpressionType.NotEqual)
{
filter = filter.AddSubFilter(ADLogicalOperator.NOT);
}
else if (b.NodeType == ExpressionType.AndAlso)
return Add(b, filter, ADLogicalOperator.AND);
else if(b.NodeType== ExpressionType.OrElse)
return Add(b, filter, ADLogicalOperator.OR);
return Add(b.Left,GetOperator(b.NodeType),b.Right,filter);
}
if (e is MethodCallExpression)
{
var m = e as MethodCallExpression;
var name = m.Method.Name;
if (m.Arguments.Count == 1)
{
if (name == "Equals")
{
return Add(m, Operator.Equals, m.Arguments[0], filter);
}
if (m.Arguments[0].Type == typeof(string))
{
string val =(string) (m.Arguments[0] as ConstantExpression).Value;
if (name == "StartsWith")
val += "*"
else if (name == "EndsWith")
val = "*" + val;
else if (name == "Contains")
val = "*" + val + "*"
else
val = null;
if(val!=null)
return Add(m, Operator.Equals, val, filter);
}
}
}
throw new NotSupportedException();
}
bool Add(Expression prop, Operator op, object value, ADFilter filter)
{
filter.Add(
new ADCriterium(
GetProperty(prop), op, GetValue(value)));
return true;
}
bool Add(BinaryExpression b, ADFilter filter, ADLogicalOperator op)
{
if (op != filter.LogicalOperator)
{
filter.Add(filter = new ADFilter());
filter.LogicalOperator = op;
}
return Add(b.Left, filter)
&& Add(b.Right, filter);
}
string GetProperty(Expression e)
{
if (e is MethodCallExpression)
{
}
if (e is LambdaExpression)
return GetProperty((e as LambdaExpression).Body);
if (e is MemberExpression)
{
var mi = (e as MemberExpression).Member;
foreach (ActiveDirectoryAliasAttribute alias in
mi.GetCustomAttributes(typeof(ActiveDirectoryAliasAttribute),true))
{
return alias.Alias;
}
return mi.Name;
}
if (e is MethodCallExpression)
{
return GetProperty((e as MethodCallExpression).Object);
}
throw new NotSupportedException();
}
object GetValue(object e)
{
if (e is Expression)
{
if (e is ConstantExpression)
return (e as ConstantExpression).Value;
throw new NotSupportedException();
}
return e;
}
Operator GetOperator(ExpressionType type)
{
if (type == ExpressionType.Equal || type== ExpressionType.NotEqual)
return Operator.Equals;
if (type == ExpressionType.LessThan)
return Operator.LessThan;
if (type == ExpressionType.LessThanOrEqual)
return Operator.LessThan | Operator.Equals;
if (type == ExpressionType.GreaterThan)
return Operator.GreaterThan;
if (type == ExpressionType.GreaterThanOrEqual)
return Operator.GreaterThan | Operator.Equals;
throw new NotSupportedException();
}
public IEnumerable<ActiveDirectoryEntry> Select(Expression<Func<T,ADQuery<T>>> selector)
{
return inf;
}
public IEnumerable<S> Select<S>(Expression<Func<T, S>> selector)
{
var ne = (selector as LambdaExpression).Body as NewExpression;
if (ne != null)
return Select<S>(ne);
return Select<S>(GetProperty(selector));
}
IEnumerable<S> Select<S>(string prop)
{
inf.AddLoadProperty(prop);
foreach (var entry in inf)
{
yield return entry.GetValue<S>(prop);
}
}
IEnumerable<S> Select<S>(NewExpression ne)
{
string[] props = new string[ne.Arguments.Count];
object[] pars = new object[props.Length];
int i = 0;
foreach (Expression e in ne.Arguments)
{
inf.AddLoadProperty( props[i++] = GetProperty(e));
}
foreach (var entry in inf)
{
for (i = 0; i < props.Length; i++)
{
pars[i] = entry[props[i]];
}
S res =(S) ne.Constructor.Invoke(pars);
yield return res;
}
}
}
public class ADQuery:ADQuery<ActiveDirectoryEntry>
{
public ADQuery() :
base()
{
}
public ADQuery(ADFilter filter):base(filter)
{
}
}
#endif
[
AttributeUsage(
AttributeTargets.Field |
AttributeTargets.Property)]
public class ActiveDirectoryAliasAttribute:
Attribute{
public
readonly string Alias;
public ActiveDirectoryAliasAttribute(
string Alias)
{
this.Alias = Alias;
} }public
partial class UserInfo{
public UserInfo(
string Name)
{
this.name = Name;
}///
<summary>
/// The scope of this constructor is not made public because that would force projects
/// referencing the project with this code to have a reference to System.DirectoryServices.
/// Use GetInfo(ActiveDirectoryEntry) instead
/// </summary>
/// <param name="ActiveDirectoryEntry"></param>
protected UserInfo(
ActiveDirectoryEntry ActiveDirectoryEntry)
{
this.ActiveDirectoryEntry = ActiveDirectoryEntry;
}
#region AD
ActiveDirectoryEntry adEntry;
public
ActiveDirectoryEntry ActiveDirectoryEntry
{
get
{
if (!HasActiveDirectoryEntry)
throw new Exception(name + "  niet gevonden");
return adEntry;
}
set{
if (adEntry == value) return;
adEntry = value;
name = null;
}}
public
bool HasActiveDirectoryEntry
{
get
{
if (adEntry ==
null && !
string.IsNullOrEmpty(name))
adEntry = Search(name);
return adEntry !=
null;
} }public
object this[
string PropertyName]
{
get {
return ActiveDirectoryEntry[PropertyName]; }
set{
ActiveDirectoryEntry[PropertyName] = value;
}
}public const string NameSearchField = "sAMAccountName"
protected
virtual ActiveDirectoryEntry Search(
string name)
{
ADFilter filter = new ADFilter();
filter.objectClass = "user"
filter[NameSearchField] = name;
return ActiveDirectory.GetFirstEntry(filter);
}string name;
public virtual string Name
{
get
{
if (name ==
null)
name = this[NameSearchField].ToString();
return name;
} }
public
static UserInfo GetCurrentUser()
{
return new UserInfo(Environment.UserName);
}public
static UserInfo GetInfo(
ActiveDirectoryEntry ActiveDirectoryEntry)
{
return new UserInfo(ActiveDirectoryEntry);
}public
static UserInfo GetInfo(
string Name)
{
return new UserInfo(Name);
}public
static ADFilter GetDefaultUserGroupsFilter()
{
ADFilter filter = new ADFilter("userPrincipalName", "*"); //userPrincipalName = to prevent computers from being included
filter.ObjectClass = DefaultClass.user;
return filter;
}public
static IEnumerable<
UserInfo> GetUsers()
{
foreach (
ActiveDirectoryEntry e
in GetDefaultUserGroupsFilter().GetEntries())
yield return GetInfo(e);
}
#endregion#region Account
public
void SetPassword(
string PW)
{
SetPassword(PW, false);
}
public void SetPassword(
string PW,
bool MustChangeAtNextLogin)
{
// http://www.awprofessional.com/articles/article.asp?p=474649&seqNum=4&rl=1
ActiveDirectoryEntry.Invoke("SetPassword", new object[] { PW });
if (MustChangeAtNextLogin)
this["pwdLastSet"] = 0x0;
adEntry.CommitChanges();
adEntry.RefreshCache();
}
public
bool AccountLocked
{
get
{
object o = this.ActiveDirectoryEntry.InvokeGet("IsAccountLocked");
if (o == null) return false;
return (bool)o;
} }const
string prop_hidefromaddresslists =
"msExchHideFromAddressLists"
public bool HideFromAddressLists
{
get
{
return this.ActiveDirectoryEntry.GetValue<bool>(prop_hidefromaddresslists);
}
set
{this.ActiveDirectoryEntry[prop_hidefromaddresslists] = value;
} }public
static ADFilter GetHiddenFromAddressListFilter()
{
ADFilter filter = new ADFilter(DefaultClass.user);
filter[prop_hidefromaddresslists] = true;
return filter;
}public void UnlockAccount()
{
//unlock account
this["LockOutTime"] = 0x0000;
}
public
string GetStringValue(
string property)
{
return ActiveDirectoryEntry[property] as string;
}public
DateTime GetDateTime(
string Property)
{
return this.ActiveDirectoryEntry.GetValue<DateTime>(Property);
}///
<summary>
/// Flags for the <see cref="ActiveDirectoryEntry"/> "userAccountControl" property flags
/// </summary>
//http://support.microsoft.com/default.aspx?scid=kb;en-us;305144[
Flags]
public enum AccountFlags{
Run_Logon_Script = 0x0001,
ACCOUNTDISABLE = 0x0002,
HomeDirectory_REQUIRED = 0x0008,
LOCKOUT = 0x0010,
Password_Not_Required = 0x0020,
/// <summary>
/// Note You cannot assign this permission by directly modifying the UserAccountControl attribute. For information about how to set the permission programmatically, see the "Property flag descriptions" section on http://support.microsoft.com/default.aspx?scid=kb;en-us;305144
/// </summary>
PASSWD_CANT_CHANGE = 0x0040,
ENCRYPTED_TEXT_PWD_ALLOWED = 0x0080,
TEMP_DUPLICATE_ACCOUNT = 0x0100,
NORMAL_ACCOUNT = 0x0200,
INTERDOMAIN_TRUST_ACCOUNT = 0x0800,
WORKSTATION_TRUST_ACCOUNT = 0x1000,
SERVER_TRUST_ACCOUNT = 0x2000,
DONT_EXPIRE_PASSWORD = 0x10000,
MNS_LOGON_ACCOUNT = 0x20000,
SMARTCARD_REQUIRED = 0x40000,
TRUSTED_FOR_DELEGATION = 0x80000,
NOT_DELEGATED = 0x100000,
USE_DES_KEY_ONLY = 0x200000,
DONT_REQ_PREAUTH = 0x400000,
PASSWORD_EXPIRED = 0x800000,
TRUSTED_TO_AUTH_FOR_DELEGATION = 0x1000000,
}
public
bool AccountEnabled
{
get
{
return (AccountStatus & AccountFlags.ACCOUNTDISABLE) == 0;
}
}
public
AccountFlags AccountStatus
{
get
{
return (AccountFlags)this["userAccountControl"];
}
set
{this["userAccountControl"] = value;
} }#endregion
#region Mail
[
ActiveDirectoryAlias(
"mail")]
public string Mail
{
get
{
return GetStringValue("mail");
} }const
string altRecipient =
"altRecipient"
[
ActiveDirectoryAlias(altRecipient)]
public bool IsMailForwarded
{
get
{
return this[altRecipient] != null;
} }protected
virtual UserInfo CreateUserInstance(
ActiveDirectoryEntry de)
{
return new UserInfo(de);
}public
UserInfo MailForwardedTo
{
get
{
string path = GetStringValue(altRecipient);
if (path ==
null)
return null;
path = ActiveDirectoryEntry.Prefix + path;
if (!
DirectoryEntry.Exists(path))
throw new Exception(path + " does not exist");
return CreateUserInstance(
new ActiveDirectoryEntry(path));
}
set{
this[altRecipient] = value == null ? null : value.ActiveDirectoryEntry.Path.Replace(value.ActiveDirectoryEntry.Prefix, null);
}}
[ActiveDirectoryAlias("deliverAndRedirect")]
public bool DeliverAndRedirect
{