Permissions & Authorization for Users #24

Initial Release; Includes Database and MVC refactoring
This commit is contained in:
Gary Sharp
2013-10-10 19:13:16 +11:00
parent 172ce5524a
commit a099d68915
458 changed files with 40221 additions and 12130 deletions
@@ -12,6 +12,17 @@ namespace Disco.BI.Interop.ActiveDirectory
{
public static class ActiveDirectory
{
#region Machine Accounts
private static readonly string[] MachineLoadProperties = {
"name",
"distinguishedName",
"sAMAccountName",
"objectSid",
"dNSHostName",
"netbootGUID",
"isCriticalSystemObject"
};
public static ActiveDirectoryMachineAccount GetMachineAccount(string ComputerName, System.Guid? UUIDNetbootGUID = null, System.Guid? MacAddressNetbootGUID = null, params string[] AdditionalProperties)
{
if (string.IsNullOrWhiteSpace(ComputerName))
@@ -26,36 +37,36 @@ namespace Disco.BI.Interop.ActiveDirectory
using (DirectoryEntry dRootEntry = ActiveDirectoryHelpers.DefaultLdapRoot)
{
var loadProperties = new List<string> { "name", "distinguishedName", "sAMAccountName", "objectSid", "dNSHostName", "netbootGUID", "isCriticalSystemObject" };
loadProperties.AddRange(AdditionalProperties);
using (DirectorySearcher dSearcher = new DirectorySearcher(dRootEntry, string.Format("(&(objectClass=computer)(sAMAccountName={0}))", ActiveDirectoryHelpers.EscapeLdapQuery(sAMAccountName)), loadProperties.ToArray(), SearchScope.Subtree))
var loadProperties = AdditionalProperties == null ? MachineLoadProperties : MachineLoadProperties.Concat(AdditionalProperties).ToArray();
using (DirectorySearcher dSearcher = new DirectorySearcher(dRootEntry, string.Format("(&(objectCategory=computer)(sAMAccountName={0}))", ActiveDirectoryHelpers.EscapeLdapQuery(sAMAccountName)), loadProperties, SearchScope.Subtree))
{
SearchResult dResult = dSearcher.FindOne();
if (dResult != null)
{
return ActiveDirectory.DirectorySearchResultToMachineAccount(dResult, AdditionalProperties);
return ActiveDirectory.ActiveDirectoryMachineAccountFromSearchResult(dResult, AdditionalProperties);
}
}
if (UUIDNetbootGUID.HasValue)
{
using (DirectorySearcher dSearcher = new DirectorySearcher(dRootEntry, string.Format("(&(objectClass=computer)(netbootGUID={0}))", ActiveDirectoryHelpers.FormatGuidForLdapQuery(UUIDNetbootGUID.Value)), loadProperties.ToArray(), SearchScope.Subtree))
using (DirectorySearcher dSearcher = new DirectorySearcher(dRootEntry, string.Format("(&(objectCategory=computer)(netbootGUID={0}))", ActiveDirectoryHelpers.FormatGuidForLdapQuery(UUIDNetbootGUID.Value)), loadProperties, SearchScope.Subtree))
{
SearchResult dResult = dSearcher.FindOne();
if (dResult != null)
{
return ActiveDirectory.DirectorySearchResultToMachineAccount(dResult, AdditionalProperties);
return ActiveDirectory.ActiveDirectoryMachineAccountFromSearchResult(dResult, AdditionalProperties);
}
}
}
if (MacAddressNetbootGUID.HasValue)
{
using (DirectorySearcher dSearcher = new DirectorySearcher(dRootEntry, string.Format("(&(objectClass=computer)(netbootGUID={0}))", ActiveDirectoryHelpers.FormatGuidForLdapQuery(MacAddressNetbootGUID.Value)), loadProperties.ToArray(), SearchScope.Subtree))
using (DirectorySearcher dSearcher = new DirectorySearcher(dRootEntry, string.Format("(&(objectCategory=computer)(netbootGUID={0}))", ActiveDirectoryHelpers.FormatGuidForLdapQuery(MacAddressNetbootGUID.Value)), loadProperties, SearchScope.Subtree))
{
SearchResult dResult = dSearcher.FindOne();
if (dResult != null)
{
return ActiveDirectory.DirectorySearchResultToMachineAccount(dResult, AdditionalProperties);
return ActiveDirectory.ActiveDirectoryMachineAccountFromSearchResult(dResult, AdditionalProperties);
}
}
}
@@ -64,7 +75,7 @@ namespace Disco.BI.Interop.ActiveDirectory
return null;
}
private static ActiveDirectoryMachineAccount DirectorySearchResultToMachineAccount(SearchResult result, params string[] AdditionalProperties)
private static ActiveDirectoryMachineAccount ActiveDirectoryMachineAccountFromSearchResult(SearchResult result, params string[] AdditionalProperties)
{
string name = result.Properties["name"][0].ToString();
string sAMAccountName = result.Properties["sAMAccountName"][0].ToString();
@@ -89,21 +100,22 @@ namespace Disco.BI.Interop.ActiveDirectory
// Additional Properties
Dictionary<string, object[]> additionalProperties = new Dictionary<string, object[]>();
foreach (string propertyName in AdditionalProperties)
{
var property = result.Properties[propertyName];
var propertyValues = new List<object>();
for (int index = 0; index < property.Count; index++)
propertyValues.Add(property[index]);
additionalProperties.Add(propertyName, propertyValues.ToArray());
}
if (AdditionalProperties != null)
foreach (string propertyName in AdditionalProperties)
{
var property = result.Properties[propertyName];
var propertyValues = new List<object>();
for (int index = 0; index < property.Count; index++)
propertyValues.Add(property[index]);
additionalProperties.Add(propertyName, propertyValues.ToArray());
}
return new ActiveDirectoryMachineAccount
{
Name = name,
DistinguishedName = distinguishedName,
sAMAccountName = sAMAccountName,
ObjectSid = objectSid,
SamAccountName = sAMAccountName,
SecurityIdentifier = objectSid,
NetbootGUID = netbootGUIDResult,
Path = result.Path,
Domain = ActiveDirectoryHelpers.DefaultDomainNetBiosName,
@@ -112,132 +124,9 @@ namespace Disco.BI.Interop.ActiveDirectory
LoadedProperties = additionalProperties
};
}
private static ActiveDirectoryUserAccount SearchResultToActiveDirectoryUserAccount(SearchResult result, params string[] AdditionalProperties)
{
string name = result.Properties["name"][0].ToString();
string username = result.Properties["sAMAccountName"][0].ToString();
string distinguishedName = result.Properties["distinguishedName"][0].ToString();
byte[] objectSid = (byte[])result.Properties["objectSid"][0];
string objectSidSDDL = ActiveDirectoryHelpers.ConvertBytesToSDDLString(objectSid);
ResultPropertyValueCollection displayNameProp = result.Properties["displayName"];
string displayName = username;
if (displayNameProp.Count > 0)
displayName = displayNameProp[0].ToString();
string surname = null;
ResultPropertyValueCollection surnameProp = result.Properties["sn"];
if (surnameProp.Count > 0)
surname = surnameProp[0].ToString();
string givenName = null;
ResultPropertyValueCollection givenNameProp = result.Properties["givenName"];
if (givenNameProp.Count > 0)
givenName = givenNameProp[0].ToString();
string email = null;
ResultPropertyValueCollection emailProp = result.Properties["mail"];
if (emailProp.Count > 0)
email = emailProp[0].ToString();
string phone = null;
ResultPropertyValueCollection phoneProp = result.Properties["telephoneNumber"];
if (phoneProp.Count > 0)
phone = phoneProp[0].ToString();
#endregion
int primaryGroupID = (int)result.Properties["primaryGroupID"][0];
string primaryGroupSid = ActiveDirectoryHelpers.ConvertBytesToSDDLString(ActiveDirectoryHelpers.BuildPrimaryGroupSid(objectSid, primaryGroupID));
var groupCNs = result.Properties["memberOf"].Cast<string>().ToList();
groupCNs.Add(ActiveDirectoryCachedGroups.GetGroupsCnForSid(primaryGroupSid));
List<string> groups = ActiveDirectoryCachedGroups.GetGroups(groupCNs).Select(g => g.ToLower()).ToList();
//foreach (string groupCN in result.Properties["memberOf"])
//{
// Removed 2012-11-30 G# - Moved to Recursive Cache
//var groupCNlower = groupCN.ToLower();
//if (groupCNlower.StartsWith("cn="))
// groups.Add(groupCNlower.Substring(3, groupCNlower.IndexOf(",") - 3));
// End Removed 2012-11-30 G#
//}
string type = null;
if (groups.Contains("domain admins") || groups.Contains("disco admins"))
{
type = "Admin";
}
else
{
if (groups.Contains("staff"))
{
type = "Staff";
}
else
{
if (groups.Contains("students"))
{
type = "Student";
}
}
}
// Additional Properties
Dictionary<string, object[]> additionalProperties = new Dictionary<string, object[]>();
foreach (string propertyName in AdditionalProperties)
{
var property = result.Properties[propertyName];
var propertyValues = new List<object>();
for (int index = 0; index < property.Count; index++)
propertyValues.Add(property[index]);
additionalProperties.Add(propertyName, propertyValues.ToArray());
}
return new ActiveDirectoryUserAccount
{
Domain = ActiveDirectoryHelpers.DefaultDomainNetBiosName,
Name = name,
Surname = surname,
GivenName = givenName,
Email = email,
Phone = phone,
DistinguishedName = distinguishedName,
sAMAccountName = username,
DisplayName = displayName,
ObjectSid = objectSidSDDL,
Type = type,
Path = result.Path,
LoadedProperties = additionalProperties
};
}
public static ActiveDirectoryUserAccount GetUserAccount(string Username, params string[] AdditionalProperties)
{
if (string.IsNullOrWhiteSpace(Username))
throw new System.ArgumentException("Invalid User Account", "Username");
string sAMAccountName = Username;
if (sAMAccountName.Contains("\\"))
sAMAccountName = sAMAccountName.Substring(checked(sAMAccountName.IndexOf("\\") + 1));
using (DirectoryEntry dRootEntry = ActiveDirectoryHelpers.DefaultLdapRoot)
{
var loadProperties = new List<string> {
"name",
"distinguishedName",
"sAMAccountName",
"objectSid",
"displayName",
"sn",
"givenName",
"memberOf",
"primaryGroupID",
"mail",
"telephoneNumber"
};
loadProperties.AddRange(AdditionalProperties);
using (DirectorySearcher dSearcher = new DirectorySearcher(dRootEntry, string.Format("(&(objectClass=user)(sAMAccountName={0}))", ActiveDirectoryHelpers.EscapeLdapQuery(sAMAccountName)), loadProperties.ToArray(), SearchScope.Subtree))
{
SearchResult dResult = dSearcher.FindOne();
if (dResult != null)
return ActiveDirectory.SearchResultToActiveDirectoryUserAccount(dResult, AdditionalProperties);
else
return null;
}
}
}
public static string OfflineDomainJoinProvision(ref ActiveDirectoryMachineAccount ExistingAccount, string ComputerName, string OrganisationalUnit = null, string EnrolSessionId = null)
{
if (ExistingAccount != null && ExistingAccount.IsCriticalSystemObject)
@@ -323,6 +212,21 @@ namespace Disco.BI.Interop.ActiveDirectory
return DJoinResult;
}
#region Users
private static readonly string[] UserLoadProperties = {
"name",
"distinguishedName",
"sAMAccountName",
"objectSid",
"displayName",
"sn",
"givenName",
"memberOf",
"primaryGroupID",
"mail",
"telephoneNumber"
};
public static List<ActiveDirectoryUserAccount> SearchUsers(string term)
{
List<ActiveDirectoryUserAccount> users = new List<ActiveDirectoryUserAccount>();
@@ -331,31 +235,118 @@ namespace Disco.BI.Interop.ActiveDirectory
term = ActiveDirectoryHelpers.EscapeLdapQuery(term);
using (DirectoryEntry entry = new DirectoryEntry(string.Format("LDAP://{0}", defaultQualifiedDomainName)))
{
using (DirectorySearcher searcher = new DirectorySearcher(entry, string.Format("(&(objectClass=User)(objectCategory=Person)(|(sAMAccountName=*{0}*)(displayName=*{0}*)))", term), new string[]
{
"name",
"distinguishedName",
"sAMAccountName",
"objectSid",
"displayName",
"sn",
"givenName",
"memberOf",
"primaryGroupID",
"mail",
"telephoneNumber"
}, SearchScope.Subtree))
using (DirectorySearcher searcher = new DirectorySearcher(entry, string.Format("(&(objectCategory=Person)(objectCategory=Person)(|(sAMAccountName=*{0}*)(displayName=*{0}*)))", term), UserLoadProperties, SearchScope.Subtree))
{
searcher.SizeLimit = 30;
SearchResultCollection results = searcher.FindAll();
foreach (SearchResult result in results)
{
users.Add(ActiveDirectory.SearchResultToActiveDirectoryUserAccount(result));
users.Add(ActiveDirectory.ActiveDirectoryUserAccountFromSearchResult(result));
}
}
}
return users;
}
private static ActiveDirectoryUserAccount ActiveDirectoryUserAccountFromSearchResult(SearchResult result, params string[] AdditionalProperties)
{
string name = result.Properties["name"][0].ToString();
string username = result.Properties["sAMAccountName"][0].ToString();
string distinguishedName = result.Properties["distinguishedName"][0].ToString();
byte[] objectSid = (byte[])result.Properties["objectSid"][0];
string objectSidSDDL = ActiveDirectoryHelpers.ConvertBytesToSDDLString(objectSid);
ResultPropertyValueCollection displayNameProp = result.Properties["displayName"];
string displayName = username;
if (displayNameProp.Count > 0)
displayName = displayNameProp[0].ToString();
string surname = null;
ResultPropertyValueCollection surnameProp = result.Properties["sn"];
if (surnameProp.Count > 0)
surname = surnameProp[0].ToString();
string givenName = null;
ResultPropertyValueCollection givenNameProp = result.Properties["givenName"];
if (givenNameProp.Count > 0)
givenName = givenNameProp[0].ToString();
string email = null;
ResultPropertyValueCollection emailProp = result.Properties["mail"];
if (emailProp.Count > 0)
email = emailProp[0].ToString();
string phone = null;
ResultPropertyValueCollection phoneProp = result.Properties["telephoneNumber"];
if (phoneProp.Count > 0)
phone = phoneProp[0].ToString();
int primaryGroupID = (int)result.Properties["primaryGroupID"][0];
string primaryGroupSid = ActiveDirectoryHelpers.ConvertBytesToSDDLString(ActiveDirectoryHelpers.BuildPrimaryGroupSid(objectSid, primaryGroupID));
var groupDistinguishedNames = result.Properties["memberOf"].Cast<string>().ToList();
groupDistinguishedNames.Add(ActiveDirectoryCachedGroups.GetGroupsDistinguishedNameForSecurityIdentifier(primaryGroupSid));
List<string> groups = ActiveDirectoryCachedGroups.GetGroups(groupDistinguishedNames).ToList();
//foreach (string groupCN in result.Properties["memberOf"])
//{
// Removed 2012-11-30 G# - Moved to Recursive Cache
//var groupCNlower = groupCN.ToLower();
//if (groupCNlower.StartsWith("cn="))
// groups.Add(groupCNlower.Substring(3, groupCNlower.IndexOf(",") - 3));
// End Removed 2012-11-30 G#
//}
// Additional Properties
Dictionary<string, object[]> additionalProperties = new Dictionary<string, object[]>();
if (AdditionalProperties != null)
foreach (string propertyName in AdditionalProperties)
{
var property = result.Properties[propertyName];
var propertyValues = new List<object>();
for (int index = 0; index < property.Count; index++)
propertyValues.Add(property[index]);
additionalProperties.Add(propertyName, propertyValues.ToArray());
}
return new ActiveDirectoryUserAccount
{
Domain = ActiveDirectoryHelpers.DefaultDomainNetBiosName,
Name = name,
Surname = surname,
GivenName = givenName,
Email = email,
Phone = phone,
DistinguishedName = distinguishedName,
SamAccountName = username,
DisplayName = displayName,
SecurityIdentifier = objectSidSDDL,
Groups = groups,
Path = result.Path,
LoadedProperties = additionalProperties
};
}
public static ActiveDirectoryUserAccount GetUserAccount(string Username, params string[] AdditionalProperties)
{
if (string.IsNullOrWhiteSpace(Username))
throw new System.ArgumentException("Invalid User Account", "Username");
string sAMAccountName = Username;
if (sAMAccountName.Contains("\\"))
sAMAccountName = sAMAccountName.Substring(checked(sAMAccountName.IndexOf("\\") + 1));
using (DirectoryEntry dRootEntry = ActiveDirectoryHelpers.DefaultLdapRoot)
{
var loadProperties = AdditionalProperties == null ? UserLoadProperties : UserLoadProperties.Concat(AdditionalProperties).ToArray();
using (DirectorySearcher dSearcher = new DirectorySearcher(dRootEntry, string.Format("(&(objectCategory=Person)(sAMAccountName={0}))", ActiveDirectoryHelpers.EscapeLdapQuery(sAMAccountName)), loadProperties, SearchScope.Subtree))
{
SearchResult dResult = dSearcher.FindOne();
if (dResult != null)
return ActiveDirectory.ActiveDirectoryUserAccountFromSearchResult(dResult, AdditionalProperties);
else
return null;
}
}
}
#endregion
#region Organisation Units
public static List<ActiveDirectoryOrganisationalUnit> GetOrganisationalUnitStructure()
{
ActiveDirectoryOrganisationalUnit DomainOUs = new ActiveDirectoryOrganisationalUnit
@@ -399,5 +390,173 @@ namespace Disco.BI.Interop.ActiveDirectory
}
}
#endregion
#region Groups
private static readonly string[] GroupLoadProperties = {
"name",
"distinguishedName",
"cn",
"sAMAccountName",
"objectSid",
"memberOf"
};
public static ActiveDirectoryGroup GetGroup(string SamAccountName)
{
if (string.IsNullOrWhiteSpace(SamAccountName))
throw new System.ArgumentException("Invalid Group Account", "SamAccountName");
string sAMAccountName = SamAccountName;
if (sAMAccountName.Contains("\\"))
sAMAccountName = sAMAccountName.Substring(checked(sAMAccountName.IndexOf("\\") + 1));
using (DirectoryEntry dRootEntry = ActiveDirectoryHelpers.DefaultLdapRoot)
{
using (DirectorySearcher dSearcher = new DirectorySearcher(dRootEntry, string.Format("(&(objectCategory=Group)(objectSid={0}))", ActiveDirectoryHelpers.EscapeLdapQuery(sAMAccountName)), GroupLoadProperties, SearchScope.Subtree))
{
SearchResult dResult = dSearcher.FindOne();
if (dResult != null)
{
return ActiveDirectoryGroupFromSearchResult(dResult);
}
else
return null;
}
}
}
public static ActiveDirectoryGroup GetGroupFromDistinguishedName(string DistinguishedName)
{
ActiveDirectoryGroup group = null;
using (DirectoryEntry groupDE = new DirectoryEntry(string.Concat(ActiveDirectoryHelpers.DefaultLdapPath, DistinguishedName)))
{
if (groupDE != null)
{
return ActiveDirectoryGroupFromDirectoryEntry(groupDE);
}
}
return group;
}
public static ActiveDirectoryGroup GetGroupFromSecurityIdentifier(string SecurityIdentifier)
{
using (DirectoryEntry dRootEntry = ActiveDirectoryHelpers.DefaultLdapRoot)
{
var sidBytes = ActiveDirectoryHelpers.ConvertSDDLStringToBytes(SecurityIdentifier);
var sidBinaryString = ActiveDirectoryHelpers.ConvertBytesToBinarySidString(sidBytes);
using (DirectorySearcher dSearcher = new DirectorySearcher(dRootEntry, string.Format("(&(objectCategory=Group)(objectSid={0}))", sidBinaryString), GroupLoadProperties, SearchScope.Subtree))
{
SearchResult dResult = dSearcher.FindOne();
if (dResult != null)
{
return ActiveDirectoryGroupFromSearchResult(dResult);
}
else
return null;
}
}
}
public static List<ActiveDirectoryGroup> SearchGroups(string term)
{
List<ActiveDirectoryGroup> results = new List<ActiveDirectoryGroup>();
string defaultQualifiedDomainName = ActiveDirectoryHelpers.DefaultDomainQualifiedName;
string defaultNetBiosDomainName = ActiveDirectoryHelpers.DefaultDomainNetBiosName;
term = ActiveDirectoryHelpers.EscapeLdapQuery(term);
using (DirectoryEntry entry = new DirectoryEntry(string.Format("LDAP://{0}", defaultQualifiedDomainName)))
{
using (DirectorySearcher searcher = new DirectorySearcher(entry, string.Format("(&(objectCategory=Group)(|(sAMAccountName=*{0}*)(name=*{0}*)(cn=*{0}*)))", term), GroupLoadProperties, SearchScope.Subtree))
{
searcher.SizeLimit = 30;
SearchResultCollection searchResults = searcher.FindAll();
foreach (SearchResult result in searchResults)
{
results.Add(ActiveDirectory.ActiveDirectoryGroupFromSearchResult(result));
}
}
}
return results;
}
private static ActiveDirectoryGroup ActiveDirectoryGroupFromDirectoryEntry(DirectoryEntry entry)
{
var name = (string)entry.Properties["name"].Value;
var distinguishedName = (string)entry.Properties["distinguishedName"].Value;
var cn = (string)entry.Properties["cn"].Value;
var sAMAccountName = (string)entry.Properties["sAMAccountName"].Value;
var objectSid = ActiveDirectoryHelpers.ConvertBytesToSDDLString((byte[])entry.Properties["objectSid"].Value);
var memberOf = entry.Properties["memberOf"].Cast<string>().ToList();
return new ActiveDirectoryGroup()
{
Name = name,
DistinguishedName = distinguishedName,
CommonName = cn,
SamAccountName = sAMAccountName,
SecurityIdentifier = objectSid,
MemberOf = memberOf
};
}
private static ActiveDirectoryGroup ActiveDirectoryGroupFromSearchResult(SearchResult result)
{
var name = (string)result.Properties["name"][0];
var distinguishedName = (string)result.Properties["distinguishedName"][0];
var cn = (string)result.Properties["cn"][0];
var sAMAccountName = (string)result.Properties["sAMAccountName"][0];
var objectSid = ActiveDirectoryHelpers.ConvertBytesToSDDLString((byte[])result.Properties["objectSid"][0]);
var memberOf = result.Properties["memberOf"].Cast<string>().ToList();
return new ActiveDirectoryGroup()
{
Name = name,
DistinguishedName = distinguishedName,
CommonName = cn,
SamAccountName = sAMAccountName,
SecurityIdentifier = objectSid,
MemberOf = memberOf
};
}
#endregion
private static readonly string[] ObjectLoadProperties = { "objectCategory" };
private static readonly string[] ObjectLoadPropertiesAll = ObjectLoadProperties.Concat(UserLoadProperties).Concat(MachineLoadProperties).Concat(GroupLoadProperties).Distinct().ToArray();
public static IActiveDirectoryObject GetObject(string SamAccountName)
{
if (string.IsNullOrWhiteSpace(SamAccountName))
throw new System.ArgumentException("Invalid Object Account Name", "SamAccountName");
string sAMAccountName = SamAccountName;
if (sAMAccountName.Contains("\\"))
sAMAccountName = sAMAccountName.Substring(checked(sAMAccountName.IndexOf("\\") + 1));
using (DirectoryEntry dRootEntry = ActiveDirectoryHelpers.DefaultLdapRoot)
{
using (DirectorySearcher dSearcher = new DirectorySearcher(dRootEntry, string.Format("(&(|(objectCategory=Person)(objectCategory=Computer)(objectCategory=Group))(sAMAccountName={0}))", ActiveDirectoryHelpers.EscapeLdapQuery(sAMAccountName)), ObjectLoadPropertiesAll, SearchScope.Subtree))
{
SearchResult dResult = dSearcher.FindOne();
if (dResult != null)
{
var objectCategory = (string)dResult.Properties["objectCategory"][0];
objectCategory = objectCategory.Substring(0, objectCategory.IndexOf(',')).ToLower();
switch (objectCategory)
{
case "cn=person":
return ActiveDirectoryUserAccountFromSearchResult(dResult);
case "cn=computer":
return ActiveDirectoryMachineAccountFromSearchResult(dResult);
case "cn=group":
return ActiveDirectoryGroupFromSearchResult(dResult);
default:
throw new InvalidOperationException("Unexpected objectCategory");
}
}
else
return null;
}
}
}
}
}
@@ -8,43 +8,44 @@ using System.Threading.Tasks;
using Disco.Data.Repository;
using Disco.Services.Tasks;
using Quartz;
using Disco.Models.Interop.ActiveDirectory;
namespace Disco.BI.Interop.ActiveDirectory
{
public class ActiveDirectoryCachedGroups : ScheduledTask
{
private static ConcurrentDictionary<string, Tuple<ADCachedGroup, DateTime>> _SidCache = new ConcurrentDictionary<string, Tuple<ADCachedGroup, DateTime>>();
private static ConcurrentDictionary<string, Tuple<ADCachedGroup, DateTime>> _Cache = new ConcurrentDictionary<string, Tuple<ADCachedGroup, DateTime>>();
private static ConcurrentDictionary<string, Tuple<ActiveDirectoryGroup, DateTime>> _SecurityIdentifierCache = new ConcurrentDictionary<string, Tuple<ActiveDirectoryGroup, DateTime>>();
private static ConcurrentDictionary<string, Tuple<ActiveDirectoryGroup, DateTime>> _DistinguishedNameCache = new ConcurrentDictionary<string, Tuple<ActiveDirectoryGroup, DateTime>>();
private const long CacheTimeoutTicks = 6000000000; // 10 Minutes
public static IEnumerable<string> GetGroups(IEnumerable<string> GroupCNs)
public static IEnumerable<string> GetGroups(IEnumerable<string> DistinguishedNames)
{
List<ADCachedGroup> groups = new List<ADCachedGroup>();
List<ActiveDirectoryGroup> groups = new List<ActiveDirectoryGroup>();
foreach (var groupCN in GroupCNs)
foreach (var group in GetGroupsRecursive(groupCN, new Stack<ADCachedGroup>()))
foreach (var distinguishedName in DistinguishedNames)
foreach (var group in GetGroupsRecursive(distinguishedName, new Stack<ActiveDirectoryGroup>()))
if (!groups.Contains(group))
{
groups.Add(group);
yield return group.FriendlyName;
yield return group.SamAccountName;
}
}
public static IEnumerable<string> GetGroups(string GroupCN)
public static IEnumerable<string> GetGroups(string DistinguishedName)
{
foreach (var group in GetGroupsRecursive(GroupCN, new Stack<ADCachedGroup>()))
yield return group.FriendlyName;
foreach (var group in GetGroupsRecursive(DistinguishedName, new Stack<ActiveDirectoryGroup>()))
yield return group.SamAccountName;
}
public static string GetGroupsCnForSid(string GroupSid)
public static string GetGroupsDistinguishedNameForSecurityIdentifier(string SecurityIdentifier)
{
var sidGroup = GetGroupBySid(GroupSid);
if (sidGroup == null)
var group = GetGroupBySecurityIdentifier(SecurityIdentifier);
if (group == null)
return null;
else
return sidGroup.CN;
return group.DistinguishedName;
}
private static IEnumerable<ADCachedGroup> GetGroupsRecursive(string GroupCN, Stack<ADCachedGroup> RecursiveTree)
private static IEnumerable<ActiveDirectoryGroup> GetGroupsRecursive(string DistinguishedName, Stack<ActiveDirectoryGroup> RecursiveTree)
{
var group = GetGroup(GroupCN);
var group = GetGroup(DistinguishedName);
if (group != null && !RecursiveTree.Contains(group))
{
@@ -54,24 +55,24 @@ namespace Disco.BI.Interop.ActiveDirectory
{
RecursiveTree.Push(group);
foreach (var memberOfGroupCN in group.MemberOf)
foreach (var memberOfGroup in GetGroupsRecursive(memberOfGroupCN, RecursiveTree))
yield return memberOfGroup;
foreach (var parentDistinguishedName in group.MemberOf)
foreach (var parentGroup in GetGroupsRecursive(parentDistinguishedName, RecursiveTree))
yield return parentGroup;
RecursiveTree.Pop();
}
}
}
private static ADCachedGroup GetGroup(string GroupCN)
private static ActiveDirectoryGroup GetGroup(string DistinguishedName)
{
// Check Cache
Tuple<ADCachedGroup, DateTime> groupRecord = TryCache(GroupCN);
Tuple<ActiveDirectoryGroup, DateTime> groupRecord = TryCache(DistinguishedName);
if (groupRecord == null)
{
// Load from AD
var group = ADCachedGroup.LoadWithCN(GroupCN);
var group = ActiveDirectory.GetGroupFromDistinguishedName(DistinguishedName);
SetValue(group);
return group;
@@ -82,15 +83,15 @@ namespace Disco.BI.Interop.ActiveDirectory
return groupRecord.Item1;
}
}
private static ADCachedGroup GetGroupBySid(string GroupSid)
private static ActiveDirectoryGroup GetGroupBySecurityIdentifier(string SecurityIdentifier)
{
// Check Cache
Tuple<ADCachedGroup, DateTime> groupRecord = TrySidCache(GroupSid);
Tuple<ActiveDirectoryGroup, DateTime> groupRecord = TrySecurityIdentifierCache(SecurityIdentifier);
if (groupRecord == null)
{
// Load from AD
var group = ADCachedGroup.LoadWithSid(GroupSid);
var group = ActiveDirectory.GetGroupFromSecurityIdentifier(SecurityIdentifier);
SetValue(group);
return group;
@@ -102,177 +103,96 @@ namespace Disco.BI.Interop.ActiveDirectory
}
}
private static Tuple<ADCachedGroup, DateTime> TryCache(string GroupCN)
private static Tuple<ActiveDirectoryGroup, DateTime> TryCache(string DistinguishedName)
{
string groupCN = GroupCN.ToLower();
Tuple<ADCachedGroup, DateTime> groupRecord;
if (_Cache.TryGetValue(groupCN, out groupRecord))
string distinguishedName = DistinguishedName.ToLower();
Tuple<ActiveDirectoryGroup, DateTime> groupRecord;
if (_DistinguishedNameCache.TryGetValue(distinguishedName, out groupRecord))
{
if (groupRecord.Item2 > DateTime.Now)
return groupRecord;
else
{
if (_Cache.TryRemove(groupCN, out groupRecord))
_SidCache.TryRemove(groupRecord.Item1.ObjectSid, out groupRecord);
if (_DistinguishedNameCache.TryRemove(distinguishedName, out groupRecord))
_SecurityIdentifierCache.TryRemove(groupRecord.Item1.SecurityIdentifier, out groupRecord);
}
}
return null;
}
private static Tuple<ADCachedGroup, DateTime> TrySidCache(string GroupSid)
private static Tuple<ActiveDirectoryGroup, DateTime> TrySecurityIdentifierCache(string SecurityIdentifier)
{
Tuple<ADCachedGroup, DateTime> groupRecord;
if (_SidCache.TryGetValue(GroupSid, out groupRecord))
Tuple<ActiveDirectoryGroup, DateTime> groupRecord;
if (_SecurityIdentifierCache.TryGetValue(SecurityIdentifier, out groupRecord))
{
if (groupRecord.Item2 > DateTime.Now)
return groupRecord;
else
{
if (_SidCache.TryRemove(GroupSid, out groupRecord))
_Cache.TryRemove(groupRecord.Item1.CN.ToLower(), out groupRecord);
if (_SecurityIdentifierCache.TryRemove(SecurityIdentifier, out groupRecord))
_DistinguishedNameCache.TryRemove(groupRecord.Item1.DistinguishedName.ToLower(), out groupRecord);
}
}
return null;
}
private static bool SetValue(ADCachedGroup GroupRecord)
private static bool SetValue(ActiveDirectoryGroup Group)
{
Tuple<ADCachedGroup, DateTime> groupRecord = new Tuple<ADCachedGroup, DateTime>(GroupRecord, DateTime.Now.AddTicks(CacheTimeoutTicks));
Tuple<ADCachedGroup, DateTime> oldGroupRecord;
Tuple<ActiveDirectoryGroup, DateTime> groupRecord = new Tuple<ActiveDirectoryGroup, DateTime>(Group, DateTime.Now.AddTicks(CacheTimeoutTicks));
Tuple<ActiveDirectoryGroup, DateTime> oldGroupRecord;
string key = GroupRecord.CN.ToLower();
if (_Cache.ContainsKey(key))
string key = Group.DistinguishedName.ToLower();
if (_DistinguishedNameCache.ContainsKey(key))
{
if (_Cache.TryGetValue(key, out oldGroupRecord))
if (_DistinguishedNameCache.TryGetValue(key, out oldGroupRecord))
{
_Cache.TryUpdate(key, groupRecord, oldGroupRecord);
_DistinguishedNameCache.TryUpdate(key, groupRecord, oldGroupRecord);
}
}
else
{
_Cache.TryAdd(key, groupRecord);
_DistinguishedNameCache.TryAdd(key, groupRecord);
}
string sid = GroupRecord.ObjectSid;
if (_SidCache.ContainsKey(sid))
string securityIdentifier = Group.SecurityIdentifier;
if (_SecurityIdentifierCache.ContainsKey(securityIdentifier))
{
if (_SidCache.TryGetValue(sid, out oldGroupRecord))
if (_SecurityIdentifierCache.TryGetValue(securityIdentifier, out oldGroupRecord))
{
_SidCache.TryUpdate(sid, groupRecord, oldGroupRecord);
_SecurityIdentifierCache.TryUpdate(securityIdentifier, groupRecord, oldGroupRecord);
}
}
else
{
_SidCache.TryAdd(sid, groupRecord);
_SecurityIdentifierCache.TryAdd(securityIdentifier, groupRecord);
}
return true;
}
private class ADCachedGroup
{
public string ObjectSid { get; set; }
public string CN { get; private set; }
public string FriendlyName { get; private set; }
public List<string> MemberOf { get; private set; }
public static ADCachedGroup LoadWithCN(string CN)
{
ADCachedGroup group = null;
using (DirectoryEntry groupDE = new DirectoryEntry(string.Concat(ActiveDirectoryHelpers.DefaultLdapPath, CN)))
{
if (groupDE != null)
{
group = new ADCachedGroup()
{
CN = CN
};
group.ObjectSid = ActiveDirectoryHelpers.ConvertBytesToSDDLString((byte[])groupDE.Properties["objectSid"].Value);
group.FriendlyName = (string)groupDE.Properties["sAMAccountName"].Value;
var groupMemberOf = groupDE.Properties["memberOf"];
if (groupMemberOf != null && groupMemberOf.Count > 0)
{
group.MemberOf = groupMemberOf.Cast<string>().ToList();
}
}
}
return group;
}
public static ADCachedGroup LoadWithSid(string Sid)
{
using (DirectoryEntry dRootEntry = ActiveDirectoryHelpers.DefaultLdapRoot)
{
var loadProperties = new List<string> {
"distinguishedName",
"objectSid",
"sAMAccountName",
"memberOf"
};
var sidBytes = ActiveDirectoryHelpers.ConvertSDDLStringToBytes(Sid);
var sidBinaryString = ActiveDirectoryHelpers.ConvertBytesToBinarySidString(sidBytes);
using (DirectorySearcher dSearcher = new DirectorySearcher(dRootEntry, string.Format("(&(objectClass=group)(objectSid={0}))", sidBinaryString), loadProperties.ToArray(), SearchScope.Subtree))
{
SearchResult dResult = dSearcher.FindOne();
if (dResult != null)
{
var group = new ADCachedGroup()
{
CN = (string)dResult.Properties["distinguishedName"][0],
ObjectSid = ActiveDirectoryHelpers.ConvertBytesToSDDLString((byte[])dResult.Properties["objectSid"][0]),
FriendlyName = (string)dResult.Properties["sAMAccountName"][0]
};
var groupMemberOf = dResult.Properties["memberOf"];
if (groupMemberOf != null && groupMemberOf.Count > 0)
{
group.MemberOf = groupMemberOf.Cast<string>().ToList();
}
return group;
}
else
return null;
}
}
}
private ADCachedGroup()
{
// Private Constructor
}
}
private static void CleanStaleCache()
{
// Clean Cache
var groupKeys = _Cache.Keys.ToArray();
var groupKeys = _DistinguishedNameCache.Keys.ToArray();
foreach (string groupKey in groupKeys)
{
Tuple<ADCachedGroup, DateTime> groupRecord;
if (_Cache.TryGetValue(groupKey, out groupRecord))
Tuple<ActiveDirectoryGroup, DateTime> groupRecord;
if (_DistinguishedNameCache.TryGetValue(groupKey, out groupRecord))
{
if (groupRecord.Item2 <= DateTime.Now)
{
_Cache.TryRemove(groupKey, out groupRecord);
_DistinguishedNameCache.TryRemove(groupKey, out groupRecord);
}
}
}
// Clean SID Cache
groupKeys = _SidCache.Keys.ToArray();
groupKeys = _SecurityIdentifierCache.Keys.ToArray();
foreach (string groupKey in groupKeys)
{
Tuple<ADCachedGroup, DateTime> groupRecord;
if (_SidCache.TryGetValue(groupKey, out groupRecord))
Tuple<ActiveDirectoryGroup, DateTime> groupRecord;
if (_SecurityIdentifierCache.TryGetValue(groupKey, out groupRecord))
{
if (groupRecord.Item2 <= DateTime.Now)
{
_SidCache.TryRemove(groupKey, out groupRecord);
_SecurityIdentifierCache.TryRemove(groupKey, out groupRecord);
}
}
}
@@ -284,7 +204,7 @@ namespace Disco.BI.Interop.ActiveDirectory
public override bool CancelInitiallySupported { get { return false; } }
public override bool LogExceptionsOnly { get { return true; } }
public override void InitalizeScheduledTask(DiscoDataContext dbContext)
public override void InitalizeScheduledTask(DiscoDataContext Database)
{
// Run @ every 15mins
@@ -0,0 +1,19 @@
using Disco.Models.Interop.ActiveDirectory;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Disco.BI.Interop.ActiveDirectory
{
public static class ActiveDirectoryGroupExtensions
{
public static IEnumerable<Tuple<string, string>> GetMembers(ActiveDirectoryGroup group)
{
throw new NotImplementedException();
}
}
}
@@ -191,11 +191,11 @@ namespace Disco.BI.Interop.ActiveDirectory
case "name":
return account.Name;
case "samaccountname":
return account.sAMAccountName;
return account.SamAccountName;
case "distinguishedname":
return account.DistinguishedName;
case "objectsid":
return account.ObjectSid;
return account.SecurityIdentifier;
case "netbootguid":
return account.NetbootGUID;
default:
@@ -268,7 +268,9 @@ namespace Disco.BI.Interop.ActiveDirectory
if (account.IsCriticalSystemObject)
throw new InvalidOperationException(string.Format("This account {0} is a Critical System Active Directory Object and Disco refuses to modify it", account.DistinguishedName));
if (!account.ParentDistinguishedName.Equals(NewOrganisationUnit, StringComparison.InvariantCultureIgnoreCase))
var parentDistinguishedName = account.ParentDistinguishedName();
if (parentDistinguishedName != null && !parentDistinguishedName.Equals(NewOrganisationUnit, StringComparison.InvariantCultureIgnoreCase))
{
string ouPath;
if (string.IsNullOrWhiteSpace(NewOrganisationUnit))
@@ -286,5 +288,14 @@ namespace Disco.BI.Interop.ActiveDirectory
}
}
public static string ParentDistinguishedName(this ActiveDirectoryMachineAccount account)
{
// Determine Parent
if (!string.IsNullOrWhiteSpace(account.DistinguishedName))
return account.DistinguishedName.Substring(0, account.DistinguishedName.IndexOf(",DC=")).Substring(account.DistinguishedName.IndexOf(",") + 1);
else
return null;
}
}
}
@@ -21,7 +21,7 @@ namespace Disco.BI.Interop.ActiveDirectory
public override bool SingleInstanceTask { get { return true; } }
public override bool CancelInitiallySupported { get { return false; } }
public override void InitalizeScheduledTask(DiscoDataContext dbContext)
public override void InitalizeScheduledTask(DiscoDataContext Database)
{
// ActiveDirectoryUpdateLastNetworkLogonDateJob @ 11:30pm
TriggerBuilder triggerBuilder = TriggerBuilder.Create().
@@ -35,11 +35,11 @@ namespace Disco.BI.Interop.ActiveDirectory
int changeCount;
this.Status.UpdateStatus(1, "Starting", "Connecting to the Database and initializing the environment");
using (DiscoDataContext dbContext = new DiscoDataContext())
using (DiscoDataContext database = new DiscoDataContext())
{
UpdateLastNetworkLogonDates(dbContext, this.Status);
UpdateLastNetworkLogonDates(database, this.Status);
this.Status.UpdateStatus(95, "Updating Database", "Writing last network logon dates to the Database");
changeCount = dbContext.SaveChanges();
changeCount = database.SaveChanges();
this.Status.Finished(string.Format("{0} Device last network logon dates updated", changeCount), "/Config/SystemConfig");
}
@@ -86,7 +86,7 @@ namespace Disco.BI.Interop.ActiveDirectory
{
using (DirectoryEntry dRootEntry = ActiveDirectoryHelpers.DefaultDCLdapRoot(dcName))
{
DirectorySearcher dSearcher = new DirectorySearcher(dRootEntry, string.Format("(&(objectClass=computer)(sAMAccountName={0}$))", ActiveDirectoryHelpers.EscapeLdapQuery(Device.ComputerName)), new string[]
DirectorySearcher dSearcher = new DirectorySearcher(dRootEntry, string.Format("(&(objectCategory=Computer)(sAMAccountName={0}$))", ActiveDirectoryHelpers.EscapeLdapQuery(Device.ComputerName)), new string[]
{
"lastLogon"
}, SearchScope.Subtree);
@@ -150,7 +150,7 @@ namespace Disco.BI.Interop.ActiveDirectory
UpdateLastNetworkLogonDate = false;
return UpdateLastNetworkLogonDate;
}
private static void UpdateLastNetworkLogonDates(DiscoDataContext context, ScheduledTaskStatus status)
private static void UpdateLastNetworkLogonDates(DiscoDataContext Database, ScheduledTaskStatus status)
{
System.Collections.Generic.Dictionary<string, System.DateTime> computerLastLogonDates = new System.Collections.Generic.Dictionary<string, System.DateTime>();
@@ -176,7 +176,7 @@ namespace Disco.BI.Interop.ActiveDirectory
double progressDCStart = 5 + (progressDCCount * progressDCProgress);
status.UpdateStatus(progressDCStart, string.Format("Querying Domain Controller: {0}", dcName), "Searching...");
using (DirectorySearcher dSearcher = new DirectorySearcher(dRootEntry, "(objectClass=computer)", new string[] { "sAMAccountName", "lastLogon" }, SearchScope.Subtree))
using (DirectorySearcher dSearcher = new DirectorySearcher(dRootEntry, "(objectCategory=Computer)", new string[] { "sAMAccountName", "lastLogon" }, SearchScope.Subtree))
{
using (SearchResultCollection dResults = dSearcher.FindAll())
{
@@ -238,7 +238,7 @@ namespace Disco.BI.Interop.ActiveDirectory
}
foreach (Device d in context.Devices.Where(device => device.ComputerName != null))
foreach (Device d in Database.Devices.Where(device => device.ComputerName != null))
{
DateTime computerLastLogonDate;
if (computerLastLogonDates.TryGetValue(d.ComputerName.ToUpper(), out computerLastLogonDate))
@@ -5,11 +5,6 @@ namespace Disco.BI.Interop.ActiveDirectory
{
internal static class ActiveDirectoryUserAccountExtensions
{
public static bool HasRole(this ActiveDirectoryUserAccount account, string Role)
{
return account.Groups != null && account.Groups.Contains(Role.ToLower());
}
public static object GetPropertyValue(this ActiveDirectoryUserAccount account, string PropertyName, int Index = 0)
{
switch (PropertyName.ToLower())
@@ -17,11 +12,11 @@ namespace Disco.BI.Interop.ActiveDirectory
case "name":
return account.Name;
case "samaccountname":
return account.sAMAccountName;
return account.SamAccountName;
case "distinguishedname":
return account.DistinguishedName;
case "objectsid":
return account.ObjectSid;
return account.SecurityIdentifier;
case "sn":
return account.Surname;
case "givenname":