2024-03-06 16:14:36 +01:00

129 lines
6.4 KiB
C#

using DigitalData.Core.Attributes;
using DigitalData.Core.Contracts.Authentication.Services;
using System.Collections;
using System.DirectoryServices;
using System.Reflection;
namespace DigitalData.Core.Authentication.Services
{
/// <summary>
/// Provides methods for interacting with Active Directory to perform searches and read data into objects of type <typeparamref name="T"/>.
/// </summary>
/// <typeparam name="T">The type into which Active Directory search results will be mapped.</typeparam>
public class ADService<T> : IADService<T> where T : new()
{
/// <summary>
/// Gets the <see cref="DirectorySearcher"/> used for performing Active Directory searches.
/// </summary>
public DirectorySearcher Searcher { get; }
/// <summary>
/// Initializes a new instance of the <see cref="ADService{T}"/> class.
/// Sets up the directory searcher with the appropriate search scope and size limit,
/// and applies a custom filter if defined on the type <typeparamref name="T"/>.
/// </summary>
/// <exception cref="PlatformNotSupportedException">Thrown when not running on Windows.</exception>
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<T>()?.Query is string filter)
Searcher.Filter = filter;
}
/// <summary>
/// Performs a search in Active Directory using the current filter and returns all matching entries.
/// </summary>
/// <returns>A <see cref="SearchResultCollection"/> containing all search results.</returns>
/// <exception cref="PlatformNotSupportedException">Thrown when not running on Windows.</exception>
public SearchResultCollection SearchAll()
{
if (!OperatingSystem.IsWindows())
throw new PlatformNotSupportedException("The ListGroups method is only supported on the windows platform.");
return Searcher.FindAll();
}
/// <summary>
/// Reads all search results into a list of objects of type <typeparamref name="T"/>.
/// </summary>
/// <returns>A list of objects of type <typeparamref name="T"/> that represents all found Active Directory entries.</returns>
/// <exception cref="PlatformNotSupportedException">Thrown when not running on Windows.</exception>
public IEnumerable<T> ReadAll()
{
if (!OperatingSystem.IsWindows())
throw new PlatformNotSupportedException("The ListGroups method is only supported on the windows platform.");
List<T> list = new();
foreach (SearchResult result in SearchAll())
{
ResultPropertyCollection rpc = result.Properties;
T obj = MapResultProperty<T>(rpc);
list.Add(obj);
}
return list;
}
/// <summary>
/// Maps properties from a <see cref="ResultPropertyCollection"/> to a new instance of type <typeparamref name="TDest"/>.
/// </summary>
/// <typeparam name="TDest">The destination type for the properties to be mapped to.</typeparam>
/// <param name="rpc">The collection of result properties from an Active Directory search result.</param>
/// <returns>A new instance of <typeparamref name="TDest"/> with properties set based on the search result.</returns>
/// <exception cref="PlatformNotSupportedException">Thrown when not running on Windows.</exception>
public static TDest MapResultProperty<TDest>(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;
}
}
}