using System; using System.Collections.Generic; using System.Linq; using System.Security.Principal; namespace Disco.Services.Interop.ActiveDirectory { public class ADGroup : IADObject { internal static readonly string[] LoadProperties = { "name", "distinguishedName", "sAMAccountName", "objectSid", "memberOf", "member" }; internal static string LdapSearchFilterTemplate = "(&(objectCategory=Group)(|(sAMAccountName={0}*)(name={0}*)(cn={0}*)))"; internal const string LdapSamAccountNameFilterTemplate = "(&(objectCategory=Group)(sAMAccountName={0}))"; internal const string LdapSecurityIdentifierFilterTemplate = "(&(objectCategory=Group)(objectSid={0}))"; public ADDomain Domain { get; private set; } public string DistinguishedName { get; private set; } public SecurityIdentifier SecurityIdentifier { get; private set; } public string Id { get { return $@"{Domain.NetBiosName}\{SamAccountName}"; } } public string SamAccountName { get; private set; } public string Name { get; private set; } public string DisplayName { get { return Name; } } public List MemberOf { get; } public List Members { get; } public Dictionary LoadedProperties { get; private set; } private ADGroup(ADDomain Domain, string DistinguishedName, SecurityIdentifier SecurityIdentifier, string SamAccountName, string Name, List MemberOf, List Members, Dictionary LoadedProperties) { this.Domain = Domain; this.DistinguishedName = DistinguishedName; this.SecurityIdentifier = SecurityIdentifier; this.SamAccountName = SamAccountName; this.Name = Name; this.MemberOf = MemberOf; this.Members = Members; this.LoadedProperties = LoadedProperties; } public static ADGroup FromSearchResult(ADSearchResult SearchResult, string[] AdditionalProperties) { if (SearchResult == null) throw new ArgumentNullException("SearchResult"); var name = SearchResult.Value("name"); var distinguishedName = SearchResult.Value("distinguishedName"); var sAMAccountName = SearchResult.Value("sAMAccountName"); var objectSid = new SecurityIdentifier(SearchResult.Value("objectSid"), 0); var memberOf = SearchResult.Values("memberOf").ToList(); var members = SearchResult.Values("member").ToList(); // Additional Properties Dictionary additionalProperties; if (AdditionalProperties != null) additionalProperties = AdditionalProperties .Select(p => Tuple.Create(p, SearchResult.Values(p).ToArray())) .ToDictionary(t => t.Item1, t => t.Item2); else { additionalProperties = new Dictionary(); } return new ADGroup(SearchResult.Domain, distinguishedName, objectSid, sAMAccountName, name, memberOf, members, additionalProperties); } public static ADGroup FromDirectoryEntry(ADDirectoryEntry DirectoryEntry, string[] AdditionalProperties) { if (DirectoryEntry == null) throw new ArgumentNullException("DirectoryEntry"); var properties = DirectoryEntry.Entry.Properties; var name = properties.Value("name"); var distinguishedName = properties.Value("distinguishedName"); var sAMAccountName = properties.Value("sAMAccountName"); var objectSid = new SecurityIdentifier(properties.Value("objectSid"), 0); var memberOf = properties.Values("memberOf").ToList(); var members = properties.Values("member").ToList(); Dictionary additionalProperties; if (AdditionalProperties != null) additionalProperties = AdditionalProperties .Select(p => Tuple.Create(p, properties.Values(p).ToArray())) .ToDictionary(t => t.Item1, t => t.Item2); else { additionalProperties = new Dictionary(); } return new ADGroup(DirectoryEntry.Domain, distinguishedName, objectSid, sAMAccountName, name, memberOf, members, additionalProperties); } [Obsolete("Use generic equivalents: GetPropertyValue(string PropertyName)")] public object GetPropertyValue(string PropertyName, int Index = 0) { return GetPropertyValues(PropertyName).Skip(Index).FirstOrDefault(); } public T GetPropertyValue(string PropertyName) { return GetPropertyValues(PropertyName).FirstOrDefault(); } public IEnumerable GetPropertyValues(string PropertyName) { switch (PropertyName.ToLower()) { case "name": return new string[] { Name }.OfType(); case "samaccountname": return new string[] { SamAccountName }.OfType(); case "distinguishedname": return new string[] { DistinguishedName }.OfType(); case "objectsid": return new SecurityIdentifier[] { SecurityIdentifier }.OfType(); case "memberof": return MemberOf.OfType(); case "member": return Members.OfType(); default: object[] adProperty; if (LoadedProperties.TryGetValue(PropertyName, out adProperty)) return adProperty.OfType(); else return Enumerable.Empty(); } } public IEnumerable GetUserMembersRecursive() { var foundGroups = new HashSet(StringComparer.OrdinalIgnoreCase); return GetUserMembersRecursive(foundGroups); } private IEnumerable GetUserMembersRecursive(HashSet foundGroups) { if (!foundGroups.Add(DistinguishedName)) yield break; var memberGroups = new List(); foreach (var memberDn in Members) { if (foundGroups.Contains(memberDn)) continue; var adObject = ActiveDirectory.RetrieveADObjectByDistinguishedName(memberDn, true); if (adObject == null) continue; else if (adObject is ADGroup group) memberGroups.Add(group); else if (adObject is ADUserAccount adUser) yield return adUser; } foreach (var group in memberGroups) { if (foundGroups.Contains(group.DistinguishedName)) continue; foreach (var adUser in group.GetUserMembersRecursive(foundGroups)) yield return adUser; } } public override string ToString() { return Id; } public override bool Equals(object obj) { if (obj == null || !(obj is ADGroup)) return false; else return DistinguishedName == ((ADGroup)obj).DistinguishedName; } public override int GetHashCode() { return System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode(DistinguishedName); } } }