using C4IT.Configuration; using C4IT.FASD.Base; using C4IT.Logging; using C4IT.Security; using C4IT.XML; using C4IT_DataHistoryProvider; using System; using System.Collections.Generic; using System.Reflection; using System.Xml; using static C4IT.Logging.cLogManager; using static C4IT_DataHistoryProvider.cDataHistoryConfigCitrix; namespace C4IT.DataHistoryProvider { public class cDataHistoryConfigInfrastructure : cFasdBaseConfig { private const string constFileNameF4sdConfig = "F4SD-Infrastructure-Configuration.xml"; private const string constFileNameF4sdSchema = "F4SD-Infrastructure-Configuration.xsd"; private const string constConfigRootElement = "F4SD-Infrastructure-Configuration"; public static readonly Guid ConfigId = new Guid("21CECFEE-0944-403F-A13F-11372BEE6905"); public Guid getId() { return ConfigId; } public Dictionary Credentials { get; private set; } = new Dictionary(); public Dictionary DbConnections { get; private set; } = new Dictionary(); public cDataHistoryConfigDatabase HistoryDB { get; private set; } = null; public cF4SDAnalyticsConfigDatabase F4SDAnalyticsDB { get; private set; } = null; public cDataHistoryConfigClientAgent ClientAgent { get; private set; } = null; public cDataHistoryConfigM42Wpm M42Wpm { get; private set; } = null; public cDataHistoryConfigCitrix Citrix { get; private set; } = null; public Dictionary CitrixTenants { get; private set; } = null; public cDataHistoryConfigNexthink Nexthink { get; private set; } = null; public cDataHistoryConfigActiveDirectory ActiveDirectory { get; private set; } = null; public Dictionary AzureTenants { get; private set; } = null; public cF4SDAuthorisation Authorisation { get; private set; } = null; internal cDataHistoryConfigInfrastructure() : base(constFileNameF4sdConfig, constFileNameF4sdSchema, constConfigRootElement) { } public override bool InstantiateProperties(XmlElement XRoot, cXmlParser Parser) { MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } try { // get the credentials var Creds = cCredentialXml.LoadListFromXml(XRoot, Parser, "Credentials"); if (Creds == null || Creds.Count == 0 || Parser.LogMessages.Count > 0) return false; Credentials = Creds; // get the db connections var Conns = cDataHistoryConfigDbConnection.LoadListFromXml(XRoot, Parser, Creds); if (Conns == null || Conns.Count == 0 || Parser.LogMessages.Count > 0) return false; DbConnections = Conns; // get the history db config var _db = new cDataHistoryConfigDatabase(XRoot, Conns, Parser); if (_db == null || !_db.IsValid || Parser.LogMessages.Count > 0) return false; HistoryDB = _db; var _db2 = new cF4SDAnalyticsConfigDatabase(XRoot, Conns, Parser); if (_db2 != null && _db2.IsValid && Parser.LogMessages.Count == 0) F4SDAnalyticsDB = _db2; // get the active directory config var _ad = new cDataHistoryConfigActiveDirectory(XRoot, Credentials, Parser); if (_ad?.IsValid == true) ActiveDirectory = _ad; AzureTenants = cDataHistoryAzureTenant.LoadFromXml(XRoot, Credentials, Parser); CitrixTenants = cDataHistoryCitrixTenant.LoadFromXml(XRoot, Credentials, Parser); // get the client agent config var _cla = new cDataHistoryConfigClientAgent(XRoot, Credentials, Conns, Parser); if (_cla?.IsValid == true) ClientAgent = _cla; // get the M42 WPM config var _m42wpm = new cDataHistoryConfigM42Wpm(XRoot, Credentials, Parser); if (_m42wpm?.IsValid == true) M42Wpm = _m42wpm; var _citrix = new cDataHistoryConfigCitrix(XRoot, Credentials, Parser); if (_citrix?.IsValid == true) Citrix = _citrix; // get the nexthink config var _nxt = new cDataHistoryConfigNexthink(XRoot, Credentials, Parser); if (_nxt?.IsValid == true) Nexthink = _nxt; var _auth = new cF4SDAuthorisation(XRoot, Parser, this); if (_auth?.IsValid == true) Authorisation = _auth; IsValid = true; return true; } catch (Exception E) { LogException(E); } finally { if (CM != null) LogMethodEnd(CM); } return false; } } public class cDataHistoryConfigDatabase : IConfigNodeValidation { public bool IsValid { get; set; } = false; public cDataHistoryConfigSqlConnection Connection { get; private set; } = null; public bool SearchForPhoneNumbers { get; set; } = false; public bool SearchWithLike { get; set; } = false; public int DaysToCache { get; set; } = 60; public List CleanupTimeFrames = null; internal cDataHistoryConfigDatabase(XmlElement XNode, Dictionary DbConnections, cXmlParser Parser) { MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } try { var XNode2 = XNode.SelectSingleNode("DataHistory-DB"); if (!(XNode2 is XmlElement XNode3)) { Parser.AddMessage(XNode, $"The root element <{XNode.Name}> does not have a element."); return; } Parser.EnterElement("DataHistory-DB"); try { var strConnection = cXmlParser.GetStringFromXmlAttribute(XNode2, "DB-Connection"); if (string.IsNullOrWhiteSpace(strConnection)) { Parser.AddMessage(XNode, $"The element <{XNode2.Name}> does not have a 'DB-Connection' attribute."); return; } if (!DbConnections.TryGetValue(strConnection, out var Conn)) { Parser.AddMessage(XNode, $"Could not find a DB connection '{strConnection}' in element <{XNode2.Name}>."); return; } if (!(Conn is cDataHistoryConfigSqlConnection SqlConn)) { Parser.AddMessage(XNode, $"The DB connection '{strConnection}' in element <{XNode2.Name}> has to be a SQL connection."); return; } Connection = SqlConn; SearchForPhoneNumbers = cXmlParser.GetBoolFromXmlAttribute(XNode2, "SearchForPhoneNumbers"); SearchWithLike = cXmlParser.GetBoolFromXmlAttribute(XNode2, "SearchWithLike"); DaysToCache = cXmlParser.GetIntegerFromXmlAttribute(XNode2, "DaysToCache", DaysToCache); CleanupTimeFrames = cDataHistoryCleanupTimeFrame.LoadFromXml(XNode3, Parser); IsValid = true; } finally { Parser.LeaveElement("DataHistory-DB"); } } catch (Exception E) { LogException(E); } finally { if (CM != null) LogMethodEnd(CM); } } } public class cF4SDAnalyticsConfigDatabase : IConfigNodeValidation { public bool IsValid { get; set; } = false; public bool AnonymousUser { get; set; } public int SessionTimeout { get; set; } = 60; public int CaseTimeout { get; set; } = 5; public int SessionCheckInterval { get; set; } = 20; public int CaseCheckInterval { get; set; } = 1; public cDataHistoryConfigSqlConnection Connection { get; private set; } = null; internal cF4SDAnalyticsConfigDatabase(XmlElement XNode, Dictionary DbConnections, cXmlParser Parser) { MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } try { var XNode2 = XNode.SelectSingleNode("F4SDAnalytics-DB"); if (!(XNode2 is XmlElement XNode3)) { Parser.AddMessage(XNode, $"The root element <{XNode.Name}> does not have a element.", LogLevels.Info); return; } Parser.EnterElement("F4SDAnalytics-DB"); try { var strConnection = cXmlParser.GetStringFromXmlAttribute(XNode2, "DB-Connection"); if (string.IsNullOrWhiteSpace(strConnection)) { Parser.AddMessage(XNode, $"The element <{XNode2.Name}> does not have a 'DB-Connection' attribute."); return; } if (!DbConnections.TryGetValue(strConnection, out var Conn)) { Parser.AddMessage(XNode, $"Could not find a DB connection '{strConnection}' in element <{XNode2.Name}>."); return; } if (!(Conn is cDataHistoryConfigSqlConnection SqlConn)) { Parser.AddMessage(XNode, $"The DB connection '{strConnection}' in element <{XNode2.Name}> has to be a SQL connection."); return; } Connection = SqlConn; AnonymousUser = !cXmlParser.GetBoolFromXmlAttribute(XNode2, "EnableUserId"); SessionTimeout = cXmlParser.GetIntegerFromXmlAttribute(XNode2, "SessionTimeout", 60); CaseTimeout = cXmlParser.GetIntegerFromXmlAttribute(XNode2, "CaseTimeout", 5); SessionCheckInterval = cXmlParser.GetIntegerFromXmlAttribute(XNode2, "SessionCheckInterval", 20); CaseCheckInterval = cXmlParser.GetIntegerFromXmlAttribute(XNode2, "CaseCheckInterval", 2); IsValid = true; } finally { Parser.LeaveElement("F4SDAnalytics-DB"); } } catch (Exception E) { LogException(E); } finally { if (CM != null) LogMethodEnd(CM); } } } public class cDataHistoryCleanupTimeFrame : IConfigNodeValidation { private const string constXmlTag = "Cleanup-Timeframe"; public bool IsValid { get; set; } = false; public int DowFrom = 0; public int DowTo = 0; public TimeSpan TimeFrom = new TimeSpan(0, 0, 0); public TimeSpan TimeTo = new TimeSpan(23, 59, 59); public TimeZoneInfo TimeZone = TimeZoneInfo.Utc; internal cDataHistoryCleanupTimeFrame(XmlElement XNode, cXmlParser Parser) { MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } try { TimeZone = cXmlParser.GetTimeZoneInfoFromAttribute(XNode, "Timezone"); if (TimeZone == null) TimeZone = TimeZoneInfo.Utc; var strFromDay = cXmlParser.GetStringFromXmlAttribute(XNode, "StartDay"); var fromTime = cXmlParser.GetTimeSpanFromAttribute(XNode, "StartTime", null); var strToDay = cXmlParser.GetStringFromXmlAttribute(XNode, "StopDay"); var toTime = cXmlParser.GetTimeSpanFromAttribute(XNode, "StopTime", null); if (fromTime == null || toTime == null) return; TimeFrom = (TimeSpan)fromTime; TimeTo = (TimeSpan)toTime; DowFrom = GetDayOfWeek(strFromDay); if (DowFrom < 0) return; DowTo = GetDayOfWeek(strToDay); if (DowTo < 0) { if (TimeTo <= TimeFrom) DowTo = (DowFrom + 1) % 7; else DowTo = DowFrom; } IsValid = true; } catch (Exception E) { LogException(E); } finally { if (CM != null) LogMethodEnd(CM); } } private int GetDayOfWeek(string Day) { try { int DayOfWeek = -1; if (Day != null) { var strDow = Day.ToUpperInvariant(); switch (strDow) { case "MON": DayOfWeek = 1; break; case "TUE": DayOfWeek = 2; break; case "WED": DayOfWeek = 3; break; case "THU": DayOfWeek = 4; break; case "FRI": DayOfWeek = 5; break; case "SAT": DayOfWeek = 6; break; case "SUN": DayOfWeek = 0; break; } } return DayOfWeek; } catch (Exception E) { cLogManager.DefaultLogger.LogException(LogLevels.Error, E); } return -1; } 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(constXmlTag); if (XList != null && XList.Count > 0) { Parser.EnterElement(constXmlTag); try { foreach (var Entry in XList) { if (!(Entry is XmlElement XEntry)) continue; var Node = new cDataHistoryCleanupTimeFrame(XEntry, Parser); if (Node.IsValid) RetVal.Add(Node); Parser.SelectElementNext(); } } finally { Parser.LeaveElement(constXmlTag); } } } catch (Exception E) { LogException(E); } finally { if (CM != null) LogMethodEnd(CM); } return RetVal; } private static int Compare(int DowFrom, TimeSpan TimeFrom, int DowTo, TimeSpan TimeTo) { if (DowFrom == DowTo) { if (TimeTo > TimeFrom) return 1; else if (TimeTo < TimeFrom) return -1; else return 0; } else if (DowTo > DowFrom) return 1; else return -1; } public bool Check() { try { var Now = TimeZoneInfo.ConvertTimeFromUtc(DateTime.UtcNow, this.TimeZone); var dow = (int)Now.DayOfWeek; var time = Now.TimeOfDay; var c0 = Compare(DowFrom, TimeFrom, DowTo, TimeTo); var c1 = Compare(DowFrom, TimeFrom, dow, time); var c2 = Compare(DowTo, TimeTo, dow, time); if (c0 <= 0) return c1 >= 0 || c2 <= 0; else return c1 >= 0 && c2 <= 0; } catch (Exception E) { cLogManager.DefaultLogger.LogException(LogLevels.Error, E); } return false; } } public class cDataHistoryScanTiming : IConfigNodeValidation { public bool IsValid { get; set; } = false; public TimeSpan Interval { get; set; } public TimeSpan Offset { get; set; } public TimeZoneInfo Timezone { get; set; } public cDataHistoryScanTiming() { } internal cDataHistoryScanTiming(XmlElement XNode, TimeSpan DefaultInterval, TimeSpan DefaultOffset, cXmlParser Parser) { MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } try { Interval = DefaultInterval; Offset = DefaultOffset; Timezone = TimeZoneInfo.Local; var XNode2 = XNode.SelectSingleNode("Scan-Timing"); if (XNode2 == null) return; Parser.EnterElement("Scan-Timing"); try { Interval = (TimeSpan)cXmlParser.GetTimeSpanFromAttribute(XNode2, "ScanInterval", Interval); Offset = (TimeSpan)cXmlParser.GetTimeSpanFromAttribute(XNode2, "ScanOffset", Interval); Timezone = cXmlParser.GetTimeZoneInfoFromAttribute(XNode2, "Timezone"); if (Timezone == null) Timezone = TimeZoneInfo.Utc; IsValid = true; } finally { Parser.LeaveElement("Scan-Timing"); } } catch (Exception E) { LogException(E); } finally { if (CM != null) LogMethodEnd(CM); } } } public class cF4SDAuthorisation : IConfigNodeValidation { public bool IsValid { get; set; } = false; public cF4SDMembershipGroups MembershipGroups = null; public Dictionary Roles = null; public cF4SDAuthorisation(XmlElement XNode, cXmlParser Parser, cDataHistoryConfigInfrastructure InfratructureConfig) { MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } try { if (!(XNode.SelectSingleNode("Authorization") is XmlElement _xmlAuth)) return; Parser.EnterElement("Authorization"); try { var _groups = new cF4SDMembershipGroups(_xmlAuth, Parser, InfratructureConfig); if (_groups?.IsValid != true) return; MembershipGroups = _groups; var _roles = cF4SDRole.LoadFromXml(_xmlAuth, Parser, MembershipGroups); if (_roles?.Count > 0) Roles = _roles; IsValid = true; } catch (Exception E) { LogException(E); } Parser.LeaveElement("Authorization"); } catch (Exception E) { LogException(E); } finally { if (CM != null) LogMethodEnd(CM); } } } public class cF4SDMembershipGroups : Dictionary, IConfigNodeValidation { public bool IsValid { get; set; } = false; public Dictionary GroupsAd = null; public cF4SDMembershipGroupsM42 GroupsM42 = null; public cF4SDMembershipGroups(XmlElement XNode, cXmlParser Parser, cDataHistoryConfigInfrastructure InfratructureConfig) { MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } try { if (!(XNode.SelectSingleNode("Membership-Groups") is XmlElement _xmlGroups)) return; Parser.EnterElement("Membership-Groups"); try { GroupsAd = cF4SDMembershipGroupsAd.LoadFromXml(_xmlGroups, Parser, InfratructureConfig?.ActiveDirectory?.Domains); if (GroupsAd != null) foreach (var _entry in GroupsAd.Values) foreach (var _entry2 in _entry.Values) this[_entry2.Name] = _entry2; var _groupM42 = new cF4SDMembershipGroupsM42(_xmlGroups, Parser); if (_groupM42?.IsValid == true) { GroupsM42 = _groupM42; foreach (var _entry in GroupsM42) this[_entry.Key] = _entry.Value; } IsValid = true; } catch (Exception E) { LogException(E); } Parser.LeaveElement("Membership-Groups"); } catch (Exception E) { LogException(E); } finally { if (CM != null) LogMethodEnd(CM); } } } public abstract class cF4SDMembershipGroup : cConfigNodeNamed { public cF4SDMembershipGroup(XmlElement XNode, cXmlParser Parser) : base(XNode, Parser) { } } public class cF4SDMembershipGroupsAd : Dictionary, IConfigNodeValidation { public bool IsValid { get; set; } = false; public string strDomain { get; private set; } = null; public cDataHistoryAdDomain Domain { get; private set; } = null; public cF4SDMembershipGroupsAd(XmlElement XNode, cXmlParser Parser, Dictionary Domains) { MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } try { strDomain = cXmlParser.GetStringFromXmlAttribute(XNode, "Domain"); if (!string.IsNullOrEmpty(strDomain)) { if (Domains.TryGetValue(strDomain, out var _dom)) { Domain = _dom; var _xList = XNode.SelectNodes("Membership-Group-AD"); if (_xList != null && _xList.Count > 0) { Parser.EnterElement("Membership-Group-AD"); foreach (var _x in _xList) { if (!(_x is XmlElement _xEl)) continue; var _grp = new cF4SDMembershipGroupAd(_xEl, Parser); if (_grp?.IsValid == true) this[_grp.Name] = _grp; Parser.SelectElementNext(); } Parser.LeaveElement("Membership-Group-AD"); } IsValid = true; } } } catch (Exception E) { LogException(E); } finally { if (CM != null) LogMethodEnd(CM); } } public static Dictionary LoadFromXml(XmlElement XNode, cXmlParser Parser, Dictionary Domains) { MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } var _retVal = new Dictionary(); try { var _xmlList = XNode.SelectNodes("Membership-Groups-AD"); if (_xmlList != null && _xmlList.Count > 0) { Parser.EnterElement("Membership-Groups-AD"); foreach (var _xmlEntry in _xmlList) { if (!(_xmlEntry is XmlElement _xmlEl)) continue; var _node = new cF4SDMembershipGroupsAd(_xmlEl, Parser, Domains); if (_node?.IsValid == true) _retVal[_node.Domain.Name] = _node; Parser.SelectElementNext(); } Parser.LeaveElement("Membership-Groups-AD"); } } catch (Exception E) { LogException(E); } finally { if (CM != null) LogMethodEnd(CM); } return _retVal; ; } } public class cF4SDMembershipGroupsM42 : Dictionary, IConfigNodeValidation { public bool IsValid { get; set; } = false; public cF4SDMembershipGroupsM42(XmlElement XNode, cXmlParser Parser) { MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } try { if (!(XNode.SelectSingleNode("Membership-Groups-Matrix42") is XmlElement _xGroup)) return; Parser.EnterElement("Membership-Groups-Matrix42"); var _xList = _xGroup.SelectNodes("Membership-Group-Matrix42"); if (_xList != null && _xList.Count > 0) { Parser.EnterElement("Membership-Group-Matrix42"); foreach (var _x in _xList) { if (!(_x is XmlElement _xEl)) continue; var _grp = new cF4SDMembershipGroupM42(_xEl, Parser); if (_grp?.IsValid == true) this[_grp.Name] = _grp; Parser.SelectElementNext(); } Parser.LeaveElement("Membership-Group-Matrix42"); } Parser.LeaveElement("Membership-Groups-Matrix42"); IsValid = true; } catch (Exception E) { LogException(E); } finally { if (CM != null) LogMethodEnd(CM); } } } public class cF4SDMembershipGroupAd : cF4SDMembershipGroup { public string Account { get; private set; } = null; public string SID { get; set; } = null; public cF4SDMembershipGroupAd(XmlElement XNode, cXmlParser Parser) : base(XNode, Parser) { try { if (!this.IsValid) return; IsValid = false; Account = cXmlParser.GetStringFromXmlAttribute(XNode, "Account"); if (string.IsNullOrEmpty(Account)) return; IsValid = true; } catch (Exception E) { LogException(E); } } } public class cF4SDMembershipGroupM42 : cF4SDMembershipGroup { public string RoleName { get; private set; } = null; public Guid RoleId { get; private set; } = Guid.Empty; public cF4SDMembershipGroupM42(XmlElement XNode, cXmlParser Parser) : base(XNode, Parser) { try { if (!this.IsValid) return; IsValid = false; RoleName = cXmlParser.GetStringFromXmlAttribute(XNode, "RoleName"); if (string.IsNullOrEmpty(RoleName) || string.IsNullOrEmpty(Name)) return; RoleId = cXmlParser.GetGuidFromXmlAttribute(XNode, "RoleID"); IsValid = true; } catch (Exception E) { LogException(E); } } } public class cF4SDRole : cConfigNodeNamed { public List MembershipGroups { get; private set; } = null; public cF4SDRole(XmlElement XNode, cXmlParser Parser, cF4SDMembershipGroups _MembershipGroups) : base(XNode, Parser) { MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } try { if (!IsValid) return; IsValid = false; var _lstGroupNames = new List(); var _strGroup = cXmlParser.GetStringFromXmlAttribute(XNode, "Group"); if (!string.IsNullOrEmpty(_strGroup)) _lstGroupNames.Add(_strGroup); var _xmlGroups = XNode.SelectNodes("GroupRef"); if (_xmlGroups?.Count > 0) { Parser.EnterElement("GroupRef"); foreach (var _ref in _xmlGroups) { if (!(_ref is XmlElement _refGroup)) continue; _strGroup = cXmlParser.GetStringFromXmlAttribute(_refGroup, "Name"); if (!string.IsNullOrEmpty(_strGroup) && !_lstGroupNames.Contains(_strGroup)) _lstGroupNames.Add(_strGroup); Parser.SelectElementNext(); } Parser.LeaveElement("GroupRef"); } if (_lstGroupNames.Count > 0) { var _lstMembers = new List(); foreach (var _entry in _lstGroupNames) { if (_MembershipGroups.TryGetValue(_entry, out var _grp)) { if (!_lstMembers.Contains(_grp)) _lstMembers.Add(_grp); } } if (_lstMembers.Count > 0) { MembershipGroups = _lstMembers; IsValid = true; } } } catch (Exception E) { LogException(E); } finally { if (CM != null) LogMethodEnd(CM); } } public static Dictionary LoadFromXml(XmlElement XNode, cXmlParser Parser, cF4SDMembershipGroups MembershipGroups) { MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } var _retVal = new Dictionary(); try { var _xmlNode2 = XNode.SelectSingleNode("Roles"); if (!(_xmlNode2 is XmlElement _xmlRoles)) return null; Parser.EnterElement("Roles"); try { var _xmlListRoles = _xmlRoles.SelectNodes("Role"); if (!(_xmlListRoles?.Count > 0)) return null; try { Parser.EnterElement("Role"); foreach (var _xmlEntry in _xmlListRoles) { if (!(_xmlEntry is XmlElement _xmlEl)) continue; var _group = new cF4SDRole(_xmlEl, Parser, MembershipGroups); if (_group?.IsValid == true) _retVal[_group.Name] = _group; Parser.SelectElementNext(); } } finally { Parser.LeaveElement("Role"); } } finally { Parser.LeaveElement("Roles"); } } catch (Exception E) { LogException(E); } finally { if (CM != null) LogMethodEnd(CM); } return _retVal; ; } } }