103 lines
4.4 KiB
C#

using DigitalData.Core.Abstractions.Application;
using System.Diagnostics.CodeAnalysis;
using System.DirectoryServices;
using Microsoft.Extensions.Caching.Memory;
using System.DirectoryServices.AccountManagement;
using DigitalData.Core.DTO;
using Microsoft.Extensions.Options;
namespace DigitalData.Core.Application
{
[SuppressMessage("Interoperability", "CA1416:Validate platform compatibility", Justification = "<Pending>")]
public class DirectorySearchService : IDirectorySearchService
{
private readonly IMemoryCache _memoryCache;
public string ServerName { get; }
public string Root { get; }
public string SearchRootPath { get; }
private readonly DateTimeOffset _userCacheExpiration;
public Dictionary<string, string> CustomSearchFilters { get; }
public DirectorySearchService(IOptions<DirectorySearchOptions> options, IMemoryCache memoryCache)
{
_memoryCache = memoryCache;
var dirSearchOptions = options.Value;
ServerName = dirSearchOptions.ServerName ?? throw new InvalidOperationException("The server name for directory search is not configured. Please specify the 'DirectorySearch:ServerName' in the configuration.");
Root = dirSearchOptions.Root ?? throw new InvalidOperationException("The root for directory search is not configured. Please specify the 'DirectorySearch:Root' in the configuration.");
SearchRootPath = $"LDAP://{ServerName}/{Root}";
CustomSearchFilters = dirSearchOptions.CustomSearchFilters;
var dayCounts = dirSearchOptions.UserCacheExpirationDays;
if (dayCounts == default)
_userCacheExpiration = default;
else
_userCacheExpiration = DateTimeOffset.Now.Date.AddDays(dayCounts);
}
public bool ValidateCredentials(string dirEntryUsername, string dirEntryPassword)
{
using var context = new PrincipalContext(ContextType.Domain, ServerName, Root);
return context.ValidateCredentials(dirEntryUsername, dirEntryPassword);
}
public DataResult<IEnumerable<ResultPropertyCollection>> FindAll(DirectoryEntry searchRoot, string filter, SearchScope searchScope = SearchScope.Subtree, int sizeLimit = 5000, params string[] properties)
{
List<ResultPropertyCollection> list = new();
var searcher = new DirectorySearcher()
{
Filter = filter,
SearchScope = searchScope,
SizeLimit = sizeLimit,
SearchRoot = searchRoot
};
if (properties.Length > 0)
{
searcher.PropertiesToLoad.Clear();
foreach (var property in properties)
if(property is not null)
searcher.PropertiesToLoad.Add(property);
}
foreach (SearchResult result in searcher.FindAll())
{
ResultPropertyCollection rpc = result.Properties;
list.Add(rpc);
}
return Result.Success<IEnumerable<ResultPropertyCollection>>(list);
}
public DataResult<IEnumerable<ResultPropertyCollection>> FindAllByUserCache(string username, string filter, SearchScope searchScope = SearchScope.Subtree, int sizeLimit = 5000, params string[] properties)
{
List<ResultPropertyCollection> list = new();
_memoryCache.TryGetValue(username, out DirectoryEntry? searchRoot);
if (searchRoot is null)
return Result.Fail<IEnumerable<ResultPropertyCollection>>();
return FindAll(searchRoot, filter, searchScope, sizeLimit, properties);
}
public void SetSearchRootCache(string dirEntryUsername, string dirEntryPassword)
{
if (_userCacheExpiration == default)
_memoryCache.Set(key: dirEntryUsername, new DirectoryEntry(path: SearchRootPath, username: dirEntryUsername, password: dirEntryPassword));
else
_memoryCache.Set(key: dirEntryUsername, new DirectoryEntry(path: SearchRootPath, username: dirEntryUsername, password: dirEntryPassword), absoluteExpiration: _userCacheExpiration);
}
public DirectoryEntry? GetSearchRootCache(string dirEntryUsername)
{
_memoryCache.TryGetValue(dirEntryUsername, out DirectoryEntry? root);
return root;
}
}
}