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;
}
}
}