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 Domains { get; private set; } = new Dictionary(); public Dictionary Scans { get; private set; } = new Dictionary(); public cDataHistoryConfigActiveDirectory(XmlElement XNode, Dictionary 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 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 has no valid entries."); return; } Scans = cDataHistoryAdScan.LoadFromXml(XRoot, this, Parser); if (Scans == null || Scans.Count == 0) { Parser.AddMessage(XNode, "The element has no valid 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 ScanNodes { get; private set; } = new List(); 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(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 element has no valid entries."); return; } IsValid = true; } catch (Exception E) { LogException(E); } finally { if (CM != null) LogMethodEnd(CM); } } internal static Dictionary LoadFromXml(XmlElement XNode, cDataHistoryConfigActiveDirectory AdConfig, cXmlParser Parser) { MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } var RetVal = new Dictionary(); try { var XRoot = XNode.SelectSingleNode("AD-Scans"); if (XRoot == null) { Parser.AddMessage(XNode, $"No 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(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 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(); 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 Servers { get; private set; } = new List(); internal cDataHistoryAdDomain(XmlElement XNode, Dictionary 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 element could be found."); return; } IsValid = true; } catch (Exception E) { LogException(E); } finally { if (CM != null) LogMethodEnd(CM); } } internal static Dictionary LoadFromXml(XmlElement XNode, Dictionary Credentials, cXmlParser Parser) { MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } var RetVal = new Dictionary(); try { var XRoot = XNode.SelectSingleNode("AD-Domains"); if (XRoot == null) { Parser.AddMessage(XNode, $"No 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 LoadFromXml(XmlElement XNode, cXmlParser Parser) { MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } var RetVal = new List(); 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; public string ScanFilter { get; private set; } = ""; public bool UseConsistencyLevelEventual { get; private set; } = false; internal cDataHistoryAzureTenant(XmlElement XNode, Dictionary 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"); var XFilterNode = XNode.SelectSingleNode("Azure-Scan-Filter"); if (XFilterNode is XmlElement XFilter) { Parser.EnterElement("Azure-AD"); try { ScanFilter = XFilter.SelectSingleNode("text()")?.Value; UseConsistencyLevelEventual = cXmlParser.GetBoolFromXmlAttribute(XFilter, "UseConsistencyLevelEventual"); } finally { Parser.LeaveElement("Azure-AD"); } } IsValid = true; } catch (Exception E) { LogException(E); } finally { if (CM != null) LogMethodEnd(CM); } } internal static Dictionary LoadFromXml(XmlElement XNode, Dictionary Credentials, cXmlParser Parser) { MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } var RetVal = new Dictionary(); 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); } } }