129 lines
6.4 KiB
C#
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;
|
|
}
|
|
}
|
|
} |