using DigitalData.Core.Attributes; using DigitalData.Core.Contracts.Authentication.Services; using System.Collections; using System.DirectoryServices; using System.Reflection; namespace DigitalData.Core.Authentication.Services { /// /// Provides methods for interacting with Active Directory to perform searches and read data into objects of type . /// /// The type into which Active Directory search results will be mapped. public class ADService : IADService where T : new() { /// /// Gets the used for performing Active Directory searches. /// public DirectorySearcher Searcher { get; } /// /// Initializes a new instance of the class. /// Sets up the directory searcher with the appropriate search scope and size limit, /// and applies a custom filter if defined on the type . /// /// Thrown when not running on Windows. public ADService() { if (!OperatingSystem.IsWindows()) throw new PlatformNotSupportedException("The ListGroups method is only supported on the windows platform."); Searcher = new() { SearchScope = SearchScope.Subtree, SizeLimit = 50000 }; if (AttrHelper.GetFilterAttrOf()?.Query is string filter) Searcher.Filter = filter; } /// /// Performs a search in Active Directory using the current filter and returns all matching entries. /// /// A containing all search results. /// Thrown when not running on Windows. public SearchResultCollection SearchAll() { if (!OperatingSystem.IsWindows()) throw new PlatformNotSupportedException("The ListGroups method is only supported on the windows platform."); return Searcher.FindAll(); } /// /// Reads all search results into a list of objects of type . /// /// A list of objects of type that represents all found Active Directory entries. /// Thrown when not running on Windows. public IEnumerable ReadAll() { if (!OperatingSystem.IsWindows()) throw new PlatformNotSupportedException("The ListGroups method is only supported on the windows platform."); List list = new(); foreach (SearchResult result in SearchAll()) { ResultPropertyCollection rpc = result.Properties; T obj = MapResultProperty(rpc); list.Add(obj); } return list; } /// /// Maps properties from a to a new instance of type . /// /// The destination type for the properties to be mapped to. /// The collection of result properties from an Active Directory search result. /// A new instance of with properties set based on the search result. /// Thrown when not running on Windows. public static TDest MapResultProperty(ResultPropertyCollection rpc) where TDest : new() { if (!OperatingSystem.IsWindows()) throw new PlatformNotSupportedException("The method is only supported on the Windows platform."); TDest resultObject = new(); foreach (string propertyName in rpc.PropertyNames ?? throw new ArgumentNullException("PropertyNames are null")) { var propertyValue = rpc[propertyName][0]; PropertyInfo? pi = typeof(TDest).GetProperty(propertyName, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance); if (pi != null && propertyValue != null) { Type propertyType = pi.PropertyType; Type underlyingType = Nullable.GetUnderlyingType(propertyType) ?? propertyType; if (propertyType != underlyingType) { propertyValue = Convert.ChangeType(propertyValue, underlyingType); pi.SetValue(resultObject, propertyValue); } else if (typeof(IEnumerable).IsAssignableFrom(propertyType) && propertyType != typeof(string)) { var itemType = propertyType.GetGenericArguments()[0]; IList list = (IList)Activator.CreateInstance(typeof(List<>).MakeGenericType(itemType)); list.Add(Convert.ChangeType(propertyValue, itemType)); pi.SetValue(resultObject, list); } else if (propertyValue is byte[] && propertyType == typeof(string)) { pi.SetValue(resultObject, Convert.ToBase64String((byte[])propertyValue)); } else if (propertyValue is byte[] stBytes && propertyType == typeof(string)) { pi.SetValue(resultObject, BitConverter.ToString(stBytes).Replace("-", "")); } else if (propertyValue is byte[] guiBytes && propertyType == typeof(Guid)) { pi.SetValue(resultObject, new Guid(guiBytes)); } else { propertyValue = Convert.ChangeType(propertyValue, propertyType); pi.SetValue(resultObject, propertyValue); } } } return resultObject; } } }