Pin Active Directory provider to domain controller
This commit is contained in:
@@ -30,6 +30,8 @@ namespace C4IT.LIAM
|
||||
public class cLiamProviderAD : cLiamProviderBase
|
||||
{
|
||||
public static Guid adModuleId = new Guid("e820a625-0653-ee11-b886-00155d300101");
|
||||
private const string AdditionalConfigurationAdDomainControllersKey = "AdDomainControllers";
|
||||
private const string AdditionalConfigurationActiveDirectoryDomainControllersKey = "ActiveDirectoryDomainControllers";
|
||||
public readonly cActiveDirectoryBase activeDirectoryBase = new cActiveDirectoryBase();
|
||||
private readonly ADServiceGroupCreator _serviceGroupCreator;
|
||||
|
||||
@@ -76,6 +78,7 @@ namespace C4IT.LIAM
|
||||
var LI = new cADLogonInfo()
|
||||
{
|
||||
Domain = Domain,
|
||||
DomainControllers = GetConfiguredDomainControllers(),
|
||||
User = Credential?.Identification,
|
||||
UserSecret = Credential?.Secret,
|
||||
TargetGroupPath = this.GroupPath
|
||||
@@ -95,6 +98,26 @@ namespace C4IT.LIAM
|
||||
return false;
|
||||
}
|
||||
|
||||
private string GetConfiguredDomainControllers()
|
||||
{
|
||||
var value = GetAdditionalConfigurationValue(AdditionalConfigurationAdDomainControllersKey);
|
||||
if (!string.IsNullOrWhiteSpace(value))
|
||||
return value;
|
||||
|
||||
return GetAdditionalConfigurationValue(AdditionalConfigurationActiveDirectoryDomainControllersKey);
|
||||
}
|
||||
|
||||
private string GetAdditionalConfigurationValue(string key)
|
||||
{
|
||||
if (AdditionalConfiguration == null || string.IsNullOrWhiteSpace(key))
|
||||
return string.Empty;
|
||||
|
||||
if (!AdditionalConfiguration.TryGetValue(key, out var rawValue) || string.IsNullOrWhiteSpace(rawValue))
|
||||
return string.Empty;
|
||||
|
||||
return rawValue.Trim();
|
||||
}
|
||||
|
||||
public override async Task<List<cLiamDataAreaBase>> getDataAreasAsync(int Depth = -1)
|
||||
{
|
||||
var CM = MethodBase.GetCurrentMethod();
|
||||
@@ -368,4 +391,4 @@ namespace C4IT.LIAM
|
||||
this.scope = secGroup.Scope.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,6 +52,7 @@ namespace LiamAD
|
||||
public class cADLogonInfo
|
||||
{
|
||||
public string Domain;
|
||||
public string DomainControllers;
|
||||
public string User;
|
||||
public string UserSecret;
|
||||
public string TargetGroupPath;
|
||||
|
||||
@@ -19,7 +19,6 @@ namespace LiamAD
|
||||
{
|
||||
private readonly cLiamProviderAD _provider;
|
||||
private readonly cActiveDirectoryBase _adBase;
|
||||
private readonly string _ldapRoot;
|
||||
private readonly string _user;
|
||||
private readonly string _password;
|
||||
public enum ADGroupType
|
||||
@@ -31,11 +30,25 @@ namespace LiamAD
|
||||
{
|
||||
_provider = provider ?? throw new ArgumentNullException(nameof(provider));
|
||||
_adBase = provider.activeDirectoryBase;
|
||||
_ldapRoot = $"LDAP://{provider.Domain}/{provider.GroupPath}";
|
||||
_user = provider.Credential.Identification;
|
||||
_password = new System.Net.NetworkCredential(_user, provider.Credential.Secret).Password;
|
||||
}
|
||||
|
||||
private string GetLdapServer()
|
||||
{
|
||||
return string.IsNullOrWhiteSpace(_adBase.EffectiveDomainController) ? _provider.Domain : _adBase.EffectiveDomainController;
|
||||
}
|
||||
|
||||
private string GetLdapRoot()
|
||||
{
|
||||
return $"LDAP://{GetLdapServer()}/{_provider.GroupPath}";
|
||||
}
|
||||
|
||||
private string GetLdapDomainRoot()
|
||||
{
|
||||
return $"LDAP://{GetLdapServer()}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Erstellt oder findet beide AD-Gruppen (Member & Owner) für einen Service.
|
||||
/// Neu mit: gruppenbereich (Scope) und gruppentyp (für Member-Gruppe).
|
||||
@@ -115,7 +128,7 @@ namespace LiamAD
|
||||
if (sidList == null) return;
|
||||
|
||||
// Basis für die Suche: komplette Domäne, nicht nur der OU-Pfad
|
||||
string domainRoot = $"LDAP://{_provider.Domain}";
|
||||
string domainRoot = GetLdapDomainRoot();
|
||||
using (var root = new DirectoryEntry(domainRoot, _user, _password, AuthenticationTypes.Secure))
|
||||
using (var grpSearch = new DirectorySearcher(root))
|
||||
{
|
||||
@@ -185,7 +198,7 @@ namespace LiamAD
|
||||
|
||||
private string GetSid(string name)
|
||||
{
|
||||
using (var root = new DirectoryEntry(_ldapRoot, _user, _password, AuthenticationTypes.Secure))
|
||||
using (var root = new DirectoryEntry(GetLdapRoot(), _user, _password, AuthenticationTypes.Secure))
|
||||
using (var ds = new DirectorySearcher(root))
|
||||
{
|
||||
ds.Filter = $"(&(objectCategory=group)(sAMAccountName={name}))";
|
||||
@@ -219,7 +232,7 @@ namespace LiamAD
|
||||
{
|
||||
if (!GroupExists(groupName))
|
||||
{
|
||||
using (var root = new DirectoryEntry(_ldapRoot, _user, _password, AuthenticationTypes.Secure))
|
||||
using (var root = new DirectoryEntry(GetLdapRoot(), _user, _password, AuthenticationTypes.Secure))
|
||||
{
|
||||
var grp = root.Children.Add("CN=" + groupName, "group");
|
||||
grp.Properties["sAMAccountName"].Value = groupName;
|
||||
@@ -261,7 +274,7 @@ namespace LiamAD
|
||||
|
||||
private string GetDistinguishedName(string name)
|
||||
{
|
||||
using (var root = new DirectoryEntry(_ldapRoot, _user, _password, AuthenticationTypes.Secure))
|
||||
using (var root = new DirectoryEntry(GetLdapRoot(), _user, _password, AuthenticationTypes.Secure))
|
||||
using (var ds = new DirectorySearcher(root))
|
||||
{
|
||||
ds.Filter = "(&(objectClass=group)(sAMAccountName=" + name + "))";
|
||||
@@ -285,4 +298,4 @@ namespace LiamAD
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.DirectoryServices;
|
||||
using System.DirectoryServices.ActiveDirectory;
|
||||
using System.DirectoryServices.AccountManagement;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
@@ -25,6 +26,7 @@ namespace LiamAD
|
||||
private cADLogonInfo privLogonInfo = null;
|
||||
public PrincipalContext adContext = null;
|
||||
public DirectoryEntry directoryEntry = null;
|
||||
public string EffectiveDomainController { get; private set; } = null;
|
||||
public Exception LastException { get; private set; } = null;
|
||||
public string LastErrorMessage { get; private set; } = null;
|
||||
|
||||
@@ -50,8 +52,12 @@ namespace LiamAD
|
||||
//TODO: remove dummy delay?
|
||||
await Task.Delay(0);
|
||||
ResetError();
|
||||
adContext = new PrincipalContext(ContextType.Domain, LogonInfo.Domain, LogonInfo.User, new NetworkCredential("", LogonInfo.UserSecret).Password);
|
||||
var ldapPath = $"LDAP://{LogonInfo.Domain}/{LogonInfo.TargetGroupPath}";
|
||||
var adServer = ResolveEffectiveDomainController(LogonInfo);
|
||||
adContext = new PrincipalContext(ContextType.Domain, adServer, LogonInfo.User, new NetworkCredential("", LogonInfo.UserSecret).Password);
|
||||
EffectiveDomainController = adContext.ConnectedServer ?? adServer;
|
||||
LogEntry($"AD provider domain controller pinned to '{EffectiveDomainController}' for domain '{LogonInfo.Domain}'.", LogLevels.Debug);
|
||||
|
||||
var ldapPath = $"LDAP://{EffectiveDomainController}/{LogonInfo.TargetGroupPath}";
|
||||
directoryEntry = new DirectoryEntry
|
||||
{
|
||||
Path = ldapPath,
|
||||
@@ -70,6 +76,78 @@ namespace LiamAD
|
||||
return false;
|
||||
}
|
||||
|
||||
private string ResolveEffectiveDomainController(cADLogonInfo logonInfo)
|
||||
{
|
||||
var configuredDomainControllers = ParseDomainControllers(logonInfo?.DomainControllers);
|
||||
foreach (var domainController in configuredDomainControllers)
|
||||
{
|
||||
if (CanConnectToDomainController(domainController, logonInfo))
|
||||
return domainController;
|
||||
|
||||
LogEntry($"Configured AD provider domain controller '{domainController}' is not reachable. Trying next candidate.", LogLevels.Warning);
|
||||
}
|
||||
|
||||
var pdc = TryGetPdcRoleOwner(logonInfo);
|
||||
if (!string.IsNullOrWhiteSpace(pdc))
|
||||
return pdc;
|
||||
|
||||
LogEntry($"Could not determine PDC emulator for domain '{logonInfo?.Domain}'. Falling back to domain locator.", LogLevels.Warning);
|
||||
return logonInfo?.Domain;
|
||||
}
|
||||
|
||||
private static IEnumerable<string> ParseDomainControllers(string domainControllers)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(domainControllers))
|
||||
return Enumerable.Empty<string>();
|
||||
|
||||
return domainControllers
|
||||
.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
|
||||
.Select(i => i.Trim())
|
||||
.Where(i => !string.IsNullOrWhiteSpace(i));
|
||||
}
|
||||
|
||||
private bool CanConnectToDomainController(string domainController, cADLogonInfo logonInfo)
|
||||
{
|
||||
try
|
||||
{
|
||||
using (var context = new PrincipalContext(ContextType.Domain, domainController, logonInfo.User, new NetworkCredential("", logonInfo.UserSecret).Password))
|
||||
return !string.IsNullOrWhiteSpace(context.ConnectedServer);
|
||||
}
|
||||
catch (Exception E)
|
||||
{
|
||||
LogException(E, LogLevels.Debug);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private string TryGetPdcRoleOwner(cADLogonInfo logonInfo)
|
||||
{
|
||||
try
|
||||
{
|
||||
var credentials = new DirectoryContext(
|
||||
DirectoryContextType.Domain,
|
||||
logonInfo.Domain,
|
||||
logonInfo.User,
|
||||
new NetworkCredential("", logonInfo.UserSecret).Password);
|
||||
|
||||
using (var domain = Domain.GetDomain(credentials))
|
||||
return domain?.PdcRoleOwner?.Name;
|
||||
}
|
||||
catch (Exception E)
|
||||
{
|
||||
LogException(E, LogLevels.Debug);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private string GetAdServer()
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(EffectiveDomainController))
|
||||
return EffectiveDomainController;
|
||||
|
||||
return privLogonInfo?.Domain;
|
||||
}
|
||||
|
||||
private async Task<bool> privRelogon()
|
||||
{
|
||||
if (privLogonInfo == null)
|
||||
@@ -193,7 +271,7 @@ namespace LiamAD
|
||||
string managedByDn = k.Properties["managedBy"][0].ToString();
|
||||
|
||||
// Erstellen eines DirectoryEntry-Objekts für den managedBy-DN
|
||||
using (DirectoryEntry managedByEntry = new DirectoryEntry($"LDAP://{managedByDn}"))
|
||||
using (DirectoryEntry managedByEntry = new DirectoryEntry($"LDAP://{GetAdServer()}/{managedByDn}", privLogonInfo.User, new NetworkCredential("", privLogonInfo.UserSecret).Password, AuthenticationTypes.Secure | AuthenticationTypes.Sealing))
|
||||
{
|
||||
if (managedByEntry.Properties.Contains("objectSid") && managedByEntry.Properties["objectSid"].Count > 0)
|
||||
{
|
||||
@@ -513,4 +591,4 @@ namespace LiamAD
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user