Files
C4IT-F4SD-Collector/F4SD-Cockpit-ServerCore/DataHistoryConfigActiveDirectory.cs
2025-11-11 11:12:05 +01:00

648 lines
24 KiB
C#

using C4IT.FASD.Base;
using C4IT.Logging;
using C4IT.Security;
using C4IT.XML;
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text.RegularExpressions;
using System.Xml;
using static C4IT.Logging.cLogManager;
namespace C4IT.DataHistoryProvider
{
public class cDataHistoryConfigActiveDirectory : IConfigNodeValidation
{
public bool IsValid { get; private set; } = false;
public bool ScanPhoneNumbers { get; private set; } = false;
public cDataHistoryScanTiming ScanTiming { get; private set; } = null;
public Dictionary<string, cDataHistoryAdDomain> Domains { get; private set; } = new Dictionary<string, cDataHistoryAdDomain>();
public Dictionary<string, cDataHistoryAdScan> Scans { get; private set; } = new Dictionary<string, cDataHistoryAdScan>();
public cDataHistoryConfigActiveDirectory(XmlElement XNode, Dictionary<string, cCredential> Credentials, cXmlParser Parser)
{
MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); }
try
{
var XNode2 = XNode.SelectSingleNode("Active-Directory");
if (!(XNode2 is XmlElement XRoot))
{
Parser.AddMessage(XNode, "Could not find a valid <Active-Directory> element.");
return;
}
Parser.EnterElement("Active-Directory");
try
{
ScanPhoneNumbers = cXmlParser.GetBoolFromXmlAttribute(XNode2, "ScanPhoneNumbers");
ScanTiming = new cDataHistoryScanTiming(XRoot, TimeSpan.FromHours(24), TimeSpan.FromMinutes(10), Parser);
Domains = cDataHistoryAdDomain.LoadFromXml(XRoot, Credentials, Parser);
if (Domains == null || Domains.Count == 0)
{
Parser.AddMessage(XNode, "The element <Active-Directory> has no valid <AD-Domain> entries.");
return;
}
Scans = cDataHistoryAdScan.LoadFromXml(XRoot, this, Parser);
if (Scans == null || Scans.Count == 0)
{
Parser.AddMessage(XNode, "The element <Active-Directory> has no valid <AD-Scan> entries.");
return;
}
IsValid = true;
}
finally
{
Parser.LeaveElement("Active-Directory");
}
}
catch (Exception E)
{
LogException(E);
}
finally
{
if (CM != null) LogMethodEnd(CM);
}
}
}
public class cDataHistoryAdScan : cConfigNodeNamed
{
public enumFasdInformationClass Type { get; private set; }
public List<cDataHistoryAdScanNode> ScanNodes { get; private set; } = new List<cDataHistoryAdScanNode>();
internal cDataHistoryAdScan(XmlElement XNode, cDataHistoryConfigActiveDirectory AdConfig, cXmlParser Parser) :
base(XNode, Parser)
{
MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); }
try
{
if (!IsValid)
return;
IsValid = false;
Type = (enumFasdInformationClass)cXmlParser.GetEnumFromAttribute<enumFasdInformationClass>(XNode, "Type", enumFasdInformationClass.Unknown);
if (Type != enumFasdInformationClass.User && Type != enumFasdInformationClass.Computer)
{
Parser.AddInvalidAttribute(XNode, Name, "AD-Type");
return;
}
ScanNodes = cDataHistoryAdScanNode.LoadFromXml(XNode, AdConfig, this, Parser);
if (ScanNodes == null || ScanNodes.Count == 0)
{
Parser.AddMessage(XNode, "The <AD-Scan> element has no valid <AD-Scan-Node> entries.");
return;
}
IsValid = true;
}
catch (Exception E)
{
LogException(E);
}
finally
{
if (CM != null) LogMethodEnd(CM);
}
}
internal static Dictionary<string, cDataHistoryAdScan> LoadFromXml(XmlElement XNode, cDataHistoryConfigActiveDirectory AdConfig, cXmlParser Parser)
{
MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); }
var RetVal = new Dictionary<string, cDataHistoryAdScan>();
try
{
var XRoot = XNode.SelectSingleNode("AD-Scans");
if (XRoot == null)
{
Parser.AddMessage(XNode, $"No <AD-Scans> element found within element <{XNode.Name}>.");
return null;
}
Parser.EnterElement("AD-Scans");
try
{
var XList = XRoot.SelectNodes("AD-Scan");
if (XList != null && XList.Count > 0)
{
Parser.EnterElement("AD-Scan");
try
{
foreach (var Entry in XList)
{
if (!(Entry is XmlElement XEntry))
continue;
var Node = new cDataHistoryAdScan(XEntry, AdConfig, Parser);
if (Node.IsValid)
{
if (RetVal.ContainsKey(Node.Name))
{
Parser.AddDuplicateName(XEntry, Node.Name, LogLevels.Warning);
}
else
RetVal.Add(Node.Name, Node);
}
Parser.SelectElementNext();
}
}
finally
{
Parser.LeaveElement("AD-Scan");
}
}
}
finally
{
Parser.LeaveElement("AD-Scans");
}
}
catch (Exception E)
{
LogException(E);
}
finally
{
if (CM != null) LogMethodEnd(CM);
}
return RetVal;
}
}
public class cDataHistoryAdScanNode : IConfigNodeValidation
{
public enum enumFilterPropery { unknown = 0, commonName, distinguishedName, samAccountName }
public bool IsValid { get; private set; } = false;
public cDataHistoryAdDomain AdDomain { get; private set; }
public string Path { get; private set; }
public string LdapFilter { get; private set; }
public enumFilterPropery FilterProperty { get; private set; } = enumFilterPropery.unknown;
public bool WildcardFilterNot { get; private set; } = false;
public Regex WildcardFilter { get; private set; }
public Regex RegExFilter { get; private set; }
public cDataHistoryAdScan parentScan { get; private set; }
internal cDataHistoryAdScanNode(XmlElement XNode, cDataHistoryConfigActiveDirectory AdConfig, cDataHistoryAdScan Scan, cXmlParser Parser)
{
MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); }
try
{
parentScan = Scan;
var strAdDomain = cXmlParser.GetStringFromXmlAttribute(XNode, "AD-Domain");
if (!AdConfig.Domains.TryGetValue(strAdDomain, out var AdD))
{
Parser.AddInvalidAttribute(XNode, null, "AD-Domain");
return;
}
AdDomain = AdD;
Path = cXmlParser.GetStringFromXmlAttribute(XNode, "Path", "");
LdapFilter = cXmlParser.GetStringFromXmlAttribute(XNode, "LDAP-Filter");
FilterProperty = cXmlParser.GetEnumFromAttribute<enumFilterPropery>(XNode, "Filter-Property", enumFilterPropery.unknown);
var strWildcardFilter = cXmlParser.GetStringFromXmlAttribute(XNode, "Wildcard-Filter");
if (!string.IsNullOrEmpty(strWildcardFilter))
{
if (strWildcardFilter.StartsWith("!"))
{
WildcardFilterNot = true;
strWildcardFilter.Remove(0, 1);
}
var _expression = "^" + Regex.Escape(strWildcardFilter)
.Replace("\\\\\\?", "??").Replace("\\?", ".").Replace("??", "\\?")
.Replace("\\\\\\*", "**").Replace("\\*", ".*").Replace("**", "\\*") + "$";
WildcardFilter = new Regex(_expression, RegexOptions.Compiled);
}
var strRexExFilter = cXmlParser.GetStringFromXmlAttribute(XNode, "RegEx-Filter");
if (!string.IsNullOrEmpty(strRexExFilter))
RegExFilter = new Regex(strRexExFilter, RegexOptions.Compiled);
IsValid = true;
}
catch (Exception E)
{
LogException(E);
}
finally
{
if (CM != null) LogMethodEnd(CM);
}
}
internal static List<cDataHistoryAdScanNode> LoadFromXml(XmlElement XNode, cDataHistoryConfigActiveDirectory AdConfig, cDataHistoryAdScan Scan, cXmlParser Parser)
{
MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); }
var RetVal = new List<cDataHistoryAdScanNode>();
try
{
var XList = XNode.SelectNodes("AD-Scan-Node");
if (XList != null && XList.Count > 0)
{
Parser.EnterElement("AD-Scan-Node");
try
{
foreach (var Entry in XList)
{
if (!(Entry is XmlElement XEntry))
continue;
var Node = new cDataHistoryAdScanNode(XEntry, AdConfig, Scan, Parser);
if (Node.IsValid)
RetVal.Add(Node);
Parser.SelectElementNext();
}
}
finally
{
Parser.LeaveElement("AD-Scan-Node");
}
}
}
catch (Exception E)
{
LogException(E);
}
finally
{
if (CM != null) LogMethodEnd(CM);
}
return RetVal;
}
}
public class cDataHistoryAdDomain : cConfigNodeNamed
{
public string FQDN { get; private set; } = null;
public cCredential Credential { get; private set; } = null;
public List<cDataHistoryAdServer> Servers { get; private set; } = new List<cDataHistoryAdServer>();
internal cDataHistoryAdDomain(XmlElement XNode, Dictionary<string, cCredential> Credentials, cXmlParser Parser) :
base(XNode, Parser)
{
MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); }
try
{
if (!IsValid)
return;
IsValid = false;
FQDN = cXmlParser.GetStringFromXmlAttribute(XNode, "FQDN");
if (string.IsNullOrWhiteSpace(FQDN))
{
Parser.AddInvalidAttribute(XNode, null, "FQDN");
return;
}
var strCreds = cXmlParser.GetStringFromXmlAttribute(XNode, "Credential");
if (!string.IsNullOrWhiteSpace(strCreds))
{
if (Credentials.TryGetValue(strCreds, out var Cred))
Credential = Cred;
}
if (Credential == null)
{
Parser.AddInvalidAttribute(XNode, Name, "Credential");
return;
}
Servers = cDataHistoryAdServer.LoadFromXml(XNode, Parser);
if (Servers?.Count == 0)
{
Parser.AddInvalidAttribute(XNode, Name, "No valid <AD-Server> element could be found.");
return;
}
IsValid = true;
}
catch (Exception E)
{
LogException(E);
}
finally
{
if (CM != null) LogMethodEnd(CM);
}
}
internal static Dictionary<string, cDataHistoryAdDomain> LoadFromXml(XmlElement XNode, Dictionary<string, cCredential> Credentials, cXmlParser Parser)
{
MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); }
var RetVal = new Dictionary<string, cDataHistoryAdDomain>();
try
{
var XRoot = XNode.SelectSingleNode("AD-Domains");
if (XRoot == null)
{
Parser.AddMessage(XNode, $"No <AD-Domains> element found within element <{XNode.Name}>.");
return null;
}
Parser.EnterElement("AD-Domains");
try
{
var XList = XRoot.SelectNodes("AD-Domain");
if (XList != null && XList.Count > 0)
{
Parser.EnterElement("AD-Domain");
try
{
foreach (var Entry in XList)
{
if (!(Entry is XmlElement XEntry))
continue;
var Node = new cDataHistoryAdDomain(XEntry, Credentials, Parser);
if (Node.IsValid)
{
if (RetVal.ContainsKey(Node.Name))
{
Parser.AddDuplicateName(XEntry, Node.Name, LogLevels.Warning);
}
else
RetVal.Add(Node.Name, Node);
}
Parser.SelectElementNext();
}
}
finally
{
Parser.LeaveElement("AD-Domain");
}
}
}
finally
{
Parser.LeaveElement("AD-Domains");
}
}
catch (Exception E)
{
LogException(E);
}
finally
{
if (CM != null) LogMethodEnd(CM);
}
return RetVal;
}
}
public class cDataHistoryAdServer : IConfigNodeValidation
{
public bool IsValid { get; private set; } = false;
public string FQDN { get; private set; }
public int Port { get; private set; }
public bool useSSL { get; private set; }
internal cDataHistoryAdServer(XmlElement XNode, cXmlParser Parser)
{
MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); }
try
{
FQDN = cXmlParser.GetStringFromXmlAttribute(XNode, "FQDN");
if (string.IsNullOrWhiteSpace(FQDN))
{
Parser.AddInvalidAttribute(XNode, null, "FQDN");
return;
}
Port = cXmlParser.GetIntegerFromXmlAttribute(XNode, "Port");
useSSL = cXmlParser.GetBoolFromXmlAttribute(XNode, "UseSSL");
IsValid = true;
}
catch (Exception E)
{
LogException(E);
}
finally
{
if (CM != null) LogMethodEnd(CM);
}
}
internal static List<cDataHistoryAdServer> LoadFromXml(XmlElement XNode, cXmlParser Parser)
{
MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); }
var RetVal = new List<cDataHistoryAdServer>();
try
{
var XList = XNode.SelectNodes("AD-Server");
if (XList != null && XList.Count > 0)
{
Parser.EnterElement("AD-Server");
try
{
foreach (var Entry in XList)
{
if (!(Entry is XmlElement XEntry))
continue;
var Node = new cDataHistoryAdServer(XEntry, Parser);
if (Node.IsValid)
RetVal.Add(Node);
Parser.SelectElementNext();
}
}
finally
{
Parser.LeaveElement("AD-Server");
}
}
}
catch (Exception E)
{
LogException(E);
}
finally
{
if (CM != null) LogMethodEnd(CM);
}
return RetVal;
}
}
public class cDataHistoryAzureTenant : IConfigNodeValidation
{
public bool IsValid { get; private set; } = false;
public string Domain { get; private set; } = "";
public Guid TenantID { get; private set; } = Guid.Empty;
public cCredential Credential { get; private set; } = null;
public bool ScanIntuneDevices { get; private set; } = false;
public bool WithMobileDevices { get; private set; } = false;
internal cDataHistoryAzureTenant(XmlElement XNode, Dictionary<string, cCredential> Credentials, cXmlParser Parser)
{
MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); }
try
{
Domain = cXmlParser.GetStringFromXmlAttribute(XNode, "Domain");
if (string.IsNullOrWhiteSpace(Domain))
{
Parser.AddInvalidAttribute(XNode, null, "Domain");
return;
}
TenantID = cXmlParser.GetGuidFromXmlAttribute(XNode, "TenantID");
if (TenantID == Guid.Empty)
{
Parser.AddInvalidAttribute(XNode, Domain, "TenantID");
return;
}
var strCreds = cXmlParser.GetStringFromXmlAttribute(XNode, "Credential");
if (!string.IsNullOrWhiteSpace(strCreds))
{
if (Credentials.TryGetValue(strCreds, out var Cred))
Credential = Cred;
}
if (Credential == null)
{
Parser.AddInvalidAttribute(XNode, Domain, "Credential");
return;
}
ScanIntuneDevices = cXmlParser.GetBoolFromXmlAttribute(XNode, "ScanIntuneDevices");
WithMobileDevices = cXmlParser.GetBoolFromXmlAttribute(XNode, "WithMobileDevices");
IsValid = true;
}
catch (Exception E)
{
LogException(E);
}
finally
{
if (CM != null) LogMethodEnd(CM);
}
}
internal static Dictionary<string, cDataHistoryAzureTenant> LoadFromXml(XmlElement XNode, Dictionary<string, cCredential> Credentials, cXmlParser Parser)
{
MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); }
var RetVal = new Dictionary<string, cDataHistoryAzureTenant>();
try
{
var XRoot = XNode.SelectSingleNode("Azure-AD");
if (XRoot == null)
{
return null;
}
Parser.EnterElement("Azure-AD");
try
{
var XList = XRoot.SelectNodes("Azure-Tenant");
if (XList != null && XList.Count > 0)
{
Parser.EnterElement("Azure-Tenant");
try
{
foreach (var Entry in XList)
{
if (!(Entry is XmlElement XEntry))
continue;
var Node = new cDataHistoryAzureTenant(XEntry, Credentials, Parser);
if (Node.IsValid)
{
if (RetVal.ContainsKey(Node.Domain))
{
Parser.AddDuplicateName(XEntry, Node.Domain, LogLevels.Warning);
}
else
RetVal.Add(Node.Domain, Node);
}
Parser.SelectElementNext();
}
}
finally
{
Parser.LeaveElement("Azure-Tenant");
}
}
}
finally
{
Parser.LeaveElement("Azure-AD");
}
}
catch (Exception E)
{
LogException(E);
}
finally
{
if (CM != null) LogMethodEnd(CM);
}
return RetVal;
}
}
public class cWildcardPattern
{
private readonly string _expression;
private readonly Regex _regex;
public cWildcardPattern(string pattern)
{
if (string.IsNullOrEmpty(pattern)) throw new ArgumentNullException(nameof(pattern));
_expression = "^" + Regex.Escape(pattern)
.Replace("\\\\\\?", "??").Replace("\\?", ".").Replace("??", "\\?")
.Replace("\\\\\\*", "**").Replace("\\*", ".*").Replace("**", "\\*") + "$";
_regex = new Regex(_expression, RegexOptions.Compiled);
}
public bool IsMatch(string value)
{
return _regex.IsMatch(value);
}
}
}