|
/**********************************
* Author: John Storer II
* Version: 2.1
* Do not use without prior consent of the author.
*
* History:
* Apr 1 2007: Version 2.0 released.
* Apr 9 2007: Version 2.1 General Update/Bug Fix.
*********************************/
using System;
using System.Collections.Generic;
using System.Text;
using System.DirectoryServices;
namespace Storer.ActiveDirectory
{
/// <summary>
/// Represents a Active Directory Group.
/// </summary>
public class Group
{
//***************************************************************************************
#region *** Constructor
/// <summary>
/// Represents a Active Directory Group.
/// </summary>
protected Group()
{
}
/// <summary>
/// Represents an Active Directory Group. Populates the internal fields from a SearchResult Property Collection.
/// </summary>
/// <param name="Collection">A SearchResult Property Collection.</param>
public Group(ResultPropertyCollection Collection)
{
PopulateFields(Collection);
}
/// <summary>
/// Represents an Active Directory Group. Populates the internal fields from a DirectoryEntry Property Collection.
/// </summary>
/// <param name="Collection">A DirectoryEntry Property Collection.</param>
public Group(PropertyCollection Collection)
{
PopulateFields(Collection);
}
/// <summary>
/// Represents an Active Directory Group. Populates the internal fields from a DirectoryEntry User Object.
/// </summary>
/// <param name="Collection">A DirectoryEntry User Object.</param>
public Group(DirectoryEntry UserDirectoryEntry)
{
PopulateFields(UserDirectoryEntry.Properties);
}
#endregion
//***************************************************************************************
#region *** Private Members
/// <summary>
/// The Ranged PageSize Attribute. (Win2000 = 1000, Win2003 = 1500)
/// </summary>
private const int RangedPageSize = 1500; // Win2003
//private const int RangedPageSize = 1000; // Win2000
/// <summary>
/// The Internal Values of the Group Object.
/// </summary>
private Dictionary<string, object> _Values = new Dictionary<string, object>();
/// <summary>
/// The Properties Loaded into the Group Object.
/// </summary>
private List<string> _PropertiesLoaded = new List<string>();
#endregion
//***************************************************************************************
#region *** Private Methods
/// <summary>
/// Sets the PropertyLoaded Field for the Specified Key.
/// </summary>
/// <param name="Key"></param>
private void SetPropertyLoaded(string Key)
{
if (!_PropertiesLoaded.Contains(Key))
_PropertiesLoaded.Add(Key);
}
/// <summary>
/// Retrieve Ranged Values.
/// </summary>
private void GetRangedValues()
{
MemberActions = new SortedDictionary<string, MemberAction>();
MemberOf = new List<string>();
int PageSize = RangedPageSize;
int Page = 0;
int StartRecord = 0;
int RecordCount = 0;
using (DirectoryEntry deGroup = Search.ForDirectoryEntry(Properties.OBJECTSID, ObjectSIDString))
{
Page = 0;
do
{
RecordCount = 0;
StartRecord = (Page * PageSize);
deGroup.RefreshCache(new string[] { Properties.MEMBER + ";range=" + StartRecord + "-*" });
if (deGroup.Properties[Properties.MEMBER] != null)
{
RecordCount = deGroup.Properties[Properties.MEMBER].Count;
foreach (string _Member in deGroup.Properties[Properties.MEMBER])
MemberActions.Add(_Member, MemberAction.None);
}
Page++;
}
while (RecordCount == PageSize);
Page = 0;
do
{
RecordCount = 0;
StartRecord = (Page * PageSize);
deGroup.RefreshCache(new string[] { Properties.MEMBEROF + ";range=" + StartRecord + "-*" });
if (deGroup.Properties[Properties.MEMBEROF] != null)
{
RecordCount = deGroup.Properties[Properties.MEMBEROF].Count;
foreach (string _MemberOf in deGroup.Properties[Properties.MEMBEROF])
MemberOf.Add(_MemberOf);
}
Page++;
}
while (RecordCount == PageSize);
}
MemberOf.Sort();
}
/// <summary>
/// Populates the internal fields using a SearchResult Property Collection.
/// </summary>
/// <param name="Collection">A SearchResult Property Collection.</param>
private void PopulateFields(ResultPropertyCollection Collection)
{
if (Collection.Contains(Properties.CHANGED))
Changed = Convert.ToDateTime(Collection[Properties.CHANGED][0]).ToLocalTime();
if (Collection.Contains(Properties.COMMONNAME))
CommonName = Collection[Properties.COMMONNAME][0] as string;
if (Collection.Contains(Properties.CREATED))
Created = Convert.ToDateTime(Collection[Properties.CREATED][0]).ToLocalTime();
if (Collection.Contains(Properties.DISTINGUISHEDNAME))
DistinguishedName = Collection[Properties.DISTINGUISHEDNAME][0] as string;
if (Collection.Contains(Properties.OBJECTGUID))
{
ObjectGUID = Collection[Properties.OBJECTGUID][0] as byte[];
ObjectGUIDString = Methods.ConvertBytesToStringGUID(ObjectGUID);
}
if (Collection.Contains(Properties.OBJECTSID))
{
ObjectSID = Collection[Properties.OBJECTSID][0] as byte[];
ObjectSIDString = Methods.ConvertBytesToStringSid(ObjectSID);
}
if (Collection.Contains(Properties.SAMACCOUNTNAME))
SAMAccountName = Collection[Properties.SAMACCOUNTNAME][0] as string;
GetRangedValues();
}
/// <summary>
/// Populates the internal fields using a DirectoryEntry Property Collection.
/// </summary>
/// <param name="Collection">A DirectoryEntry Property Collection.</param>
private void PopulateFields(PropertyCollection Collection)
{
if (Collection.Contains(Properties.CHANGED))
Changed = Convert.ToDateTime(Collection[Properties.CHANGED][0]).ToLocalTime();
if (Collection.Contains(Properties.COMMONNAME))
CommonName = Collection[Properties.COMMONNAME][0] as string;
if (Collection.Contains(Properties.CREATED))
Created = Convert.ToDateTime(Collection[Properties.CREATED][0]).ToLocalTime();
if (Collection.Contains(Properties.DISTINGUISHEDNAME))
DistinguishedName = Collection[Properties.DISTINGUISHEDNAME][0] as string;
if (Collection.Contains(Properties.OBJECTGUID))
{
ObjectGUID = Collection[Properties.OBJECTGUID][0] as byte[];
ObjectGUIDString = Methods.ConvertBytesToStringGUID(ObjectGUID);
}
if (Collection.Contains(Properties.OBJECTSID))
{
ObjectSID = Collection[Properties.OBJECTSID][0] as byte[];
ObjectSIDString = Methods.ConvertBytesToStringSid(ObjectSID);
}
if (Collection.Contains(Properties.SAMACCOUNTNAME))
SAMAccountName = Collection[Properties.SAMACCOUNTNAME][0] as string;
GetRangedValues();
}
#endregion
//***************************************************************************************
#region *** Public Properties
/// <summary>
/// Retrieve the value by the specified key.
/// </summary>
/// <param name="Key">The property key.</param>
/// <returns>An object that must be cast to the correct type to be used.</returns>
public object this[string Key]
{
get
{
return (_Values.ContainsKey(Key) ? _Values[Key] : null);
}
set
{
if (_Values.ContainsKey(Key))
_Values[Key] = value;
else
_Values.Add(Key, value);
SetPropertyLoaded(Key);
}
}
/// <summary>
/// Changed - Shows the last time the user was updated/modified.
/// </summary>
public DateTime Changed
{
get { return (DateTime?)this[Properties.CHANGED] ?? DateTime.MinValue; }
private set { this[Properties.CHANGED] = value; }
}
/// <summary>
/// Common Name - The user's Short Path Name in Active Directory.
/// This value cannot be set through this property.
/// </summary>
public string CommonName
{
get { return this[Properties.COMMONNAME] as string; }
set { this[Properties.COMMONNAME] = value; }
}
/// <summary>
/// Created - Shows the date/time the user was created.
/// </summary>
public DateTime Created
{
get { return (DateTime?)this[Properties.CREATED] ?? DateTime.MinValue; }
private set { this[Properties.CREATED] = value; }
}
/// <summary>
/// Distinguished Name - The user's Full Path in Active Directory.
/// </summary>
public string DistinguishedName
{
get { return this[Properties.DISTINGUISHEDNAME] as string; }
private set { this[Properties.DISTINGUISHEDNAME] = value; }
}
/// <summary>
/// Member Of - The Groups the this group is a direct member of.
/// </summary>
public List<string> MemberOf
{
get { return (List<string>)this[Properties.MEMBEROF] ?? new List<string>(); }
private set { this[Properties.MEMBEROF] = value; }
}
/// <summary>
/// Members - The Groups that are a direct member of this group.
/// </summary>
public List<string> Members
{
get
{
List<string> _Members = new List<string>();
foreach (string _Member in MemberActions.Keys)
_Members.Add(_Member);
return _Members;
}
}
/// <summary>
/// Member Actions - The Actions to Perform on Groups that are a direct member of this group.
/// </summary>
public SortedDictionary<string, MemberAction> MemberActions
{
get { return this[Properties.MEMBERACTIONS] as SortedDictionary<string, MemberAction>; }
private set { this[Properties.MEMBERACTIONS] = value; }
}
/// <summary>
/// Object GUID - The GUID Identifier in byte[] format.
/// </summary>
public byte[] ObjectGUID
{
get { return this[Properties.OBJECTGUID] as byte[]; }
private set { this[Properties.OBJECTGUID] = value; }
}
/// <summary>
/// Object GUID - The GUID Identifier in string format.
/// </summary>
public string ObjectGUIDString
{
get { return this[Properties.OBJECTGUIDSTRING] as string; }
private set { this[Properties.OBJECTGUIDSTRING] = value; }
}
/// <summary>
/// Object SID - The Unique Object Identifier in byte[] format.
/// </summary>
public byte[] ObjectSID
{
get { return this[Properties.OBJECTSID] as byte[]; }
private set { this[Properties.OBJECTSID] = value; }
}
/// <summary>
/// Object SID String - The Unique Object Identifier in string format.
/// </summary>
public string ObjectSIDString
{
get { return this[Properties.OBJECTSIDSTRING] as string; }
private set { this[Properties.OBJECTSIDSTRING] = value; }
}
/// <summary>
/// SAM Account Name - The User's Login Username. (without domain info)
/// </summary>
public string SAMAccountName
{
get { return this[Properties.SAMACCOUNTNAME] as string; }
set { this[Properties.SAMACCOUNTNAME] = value; }
}
#endregion
//***************************************************************************************
#region *** Public Methods
/// <summary>
/// Returns True if the Property is being used (by initialization or by access) in this object.
/// </summary>
/// <param name="Key">the Property Key.</param>
/// <returns>True if Property Key is being used. False if not.</returns>
public bool IsPropertyLoaded(string Key)
{
return _PropertiesLoaded.Contains(Key);
}
/// <summary>
/// Add a Member to this Group. Returns true if successful. (Call SaveChanges to Commit)
/// </summary>
/// <param name="DistinguishedName">The DistinguishedName of a User Object.</param>
/// <returns>True if Successful.</returns>
public bool AddMember(string DistinguishedName)
{
if (!MemberActions.ContainsKey(DistinguishedName))
{
MemberActions.Add(DistinguishedName, MemberAction.Add);
return true;
}
return false;
}
/// <summary>
/// Remove a Member from this Group. Returns true if successful. (Call SaveChanges to Commit)
/// </summary>
/// <param name="DistinguishedName">The DistinguishedName of a User Object.</param>
/// <returns>True if Successful.</returns>
public bool RemoveMember(string DistinguishedName)
{
if (MemberActions.ContainsKey(DistinguishedName))
{
MemberActions[DistinguishedName] = MemberAction.Remove;
return true;
}
return false;
}
/// <summary>
/// Saves the Current Changes.
/// </summary>
public void SaveChanges()
{
List<string> AddKeys = new List<string>();
List<string> RemoveKeys = new List<string>();
try
{
using (DirectoryEntry deGroup = Search.ForDirectoryEntry(Properties.OBJECTSID, ObjectSIDString))
{
if (!SAMAccountName.Equals(deGroup.Properties[Properties.SAMACCOUNTNAME].Value) && _PropertiesLoaded.Contains(Properties.SAMACCOUNTNAME))
deGroup.Properties[Properties.SAMACCOUNTNAME].Value = SAMAccountName;
foreach (string MemberName in MemberActions.Keys)
{
if (MemberActions[MemberName] == MemberAction.Add)
{
deGroup.Invoke("Add", new object[] { @"LDAP://" + MemberName });
AddKeys.Add(MemberName);
}
else if (MemberActions[MemberName] == MemberAction.Remove)
{
deGroup.Invoke("Remove", new object[] { @"LDAP://" + MemberName });
RemoveKeys.Add(MemberName);
}
}
deGroup.CommitChanges();
// Reset All AddKeys
foreach (string Key in AddKeys)
MemberActions[Key] = MemberAction.None;
// Remove All RemoveKeys
foreach (string Key in RemoveKeys)
MemberActions.Remove(Key);
if (_PropertiesLoaded.Contains(Properties.COMMONNAME))
if (!object.Equals(deGroup.Properties[Properties.COMMONNAME].Value, CommonName))
{
deGroup.Rename("CN=" + CommonName);
deGroup.CommitChanges();
}
}
}
catch (Exception Error)
{ throw new Exception("Save Error.", Error); }
}
#endregion
//***************************************************************************************
#region *** Internal Classes
/// <summary>
/// The Action to Perform on the Group Member
/// </summary>
public enum MemberAction
{
/// <summary>
/// No Change to the Group Member.
/// </summary>
None,
/// <summary>
/// Add Group Member to Group.
/// </summary>
Add,
/// <summary>
/// Remove Group Member from Group.
/// </summary>
Remove
}
/// <summary>
/// The Property Keys for the Active Directory Group Object.
/// </summary>
public class Properties
{
/// <summary>
/// (DateTime, 'whenChanged') Changed - Shows the last time the object was updated/modified.
/// </summary>
public const string CHANGED = "whenChanged";
/// <summary>
/// (string, 'cn') Common Name - The object's Short Path Name in Active Directory.
/// </summary>
public const string COMMONNAME = "cn";
/// <summary>
/// (DateTime, 'whenCreated') Created - Shows the time the object was created.
/// </summary>
public const string CREATED = "whenCreated";
/// <summary>
/// (string, 'distinguishedName') Distinguished Name - The object's Full Path Name in Active Directory.
/// </summary>
public const string DISTINGUISHEDNAME = "distinguishedName";
/// <summary>
/// (string[], 'member') Member - The groups that are a member of this group..
/// </summary>
public const string MEMBER = "member";
/// <summary>
/// (string[], 'memberOf') Member Of - The groups that this group is a direct member of.
/// </summary>
public const string MEMBEROF = "memberOf";
/// <summary>
/// (byte[], 'objectGuid') Object GUID - The Unique Object Identifier.
/// </summary>
public const string OBJECTGUID = "objectGuid";
/// <summary>
/// (byte[], 'objectSid') Object SID - The Unique Object Identifier.
/// </summary>
public const string OBJECTSID = "objectSid";
/// <summary>
/// (string, 'sAMAccountName') SAM Account Name - The Group UserName.
/// </summary>
public const string SAMACCOUNTNAME = "sAMAccountName";
/// <summary>
/// (string, 'tokenGroups') Token Groups - The recursive group membership list. (CANNOT BE RETRIEVED THROUGH DIRECTORYSEARCHER)
/// </summary>
public const string TOKENGROUPS = "tokenGroups";
// NON-ACTIVE DIRECTORY BASED PROPERTIES
/// <summary>
/// (string, 'objectGuidString') Object GUID String - String Value of the (byte[]) Object GUID.
/// [NOTE: Use 'OBJECTGUID' for keys/searching outside of the parent 'User' class.]
/// </summary>
public const string OBJECTGUIDSTRING = "objectGuidString";
/// <summary>
/// (string, 'objectSidString') Object SID String - String Value of the (byte[]) Object SID.
/// [NOTE: Use 'OBJECTSID' for keys/searching outside of the parent 'Group' class.]
/// </summary>
public const string OBJECTSIDSTRING = "objectSidString";
/// <summary>
/// (Dictionary, 'member') MemberActions - The Actions to Perform on Groups that are a member of this group..
/// </summary>
public const string MEMBERACTIONS = "MemberActions";
}
#endregion
//***************************************************************************************
}
}
|
By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.
If a file you wish to view isn't highlighted, and is a text file (not binary), please
let us know and we'll add colourisation support for it.
I'm a Computer Support Technician for a Indiana School System. Really, I'm a C#/ASP.NET Developer in a Software Support coat. Jack-of-all-trades, I do Hardware Support, Software Support, Programming in C#.NET, ASP.NET / Javascript, Web Design, Graphic Arts and lots of other stuff.
I've been programming almost 21 years now, and have used more than 10 different languages in the past.