HotDog's Blog

Hotdog (Robert Verpalen) about C# and vb.net

This blog hosted by:
http://blogs.vbcity.com      
  Home :: Syndication  :: Login

AprMay 2008Jun
SMTWTFS
27282930123
45678910
11121314151617
18192021222324
25262728293031
1234567

Articles

Archives

Topics

CONTACT

Fun but useful linkies

General

VS 2005

Wolfenstein ET

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:

Code Copy
//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:

Code Copy
//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:

Code CopyHideScrollFull
//#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 Exists
{
get
{
return pv != null;
}
}
public int Capacity
{
get
{
return pv.Capacity;
}
}
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);
}
else
pv.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);
else
c.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
Framework30
public 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
{