Files
C4IT-F4SD-Collector/F4SD-Cockpit-ServerCore/DataHistoryCollectorActiveDirectory.cs
2026-01-28 12:24:39 +01:00

3841 lines
180 KiB
C#

using System;
using System.Collections.Generic;
using System.Data.Common;
using System.DirectoryServices.AccountManagement;
using System.DirectoryServices.Protocols;
using System.Globalization;
using System.Linq;
using System.Net;
using System.Reflection;
using System.Security.Principal;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using ActiveDs;
using C4IT.FASD.Base;
using C4IT.HTTP;
using C4IT.Logging;
using C4IT.MsGraph;
using C4IT_DataHistoryProvider_Base.DataSources;
using Newtonsoft.Json.Linq;
using static C4IT.Logging.cLogManager;
namespace C4IT.DataHistoryProvider
{
public enum enumLdapPropertyType { ldapString, ldapTimeInterval, ldapSid, ldapPhone };
public class cDataHistoryCollectorActiveDirectory : cDataHistoryCollectorModule, ISearchResultRelationProvider
{
public const string constConnectorName = "MS Active Directory, Entra ID & Intune connector";
public const string constLicenseId = "B2D48023-56E8-44D5-AB77-BE1DCF4EA4EB";
public const string constMsGraphUserList = "users?$top={0}&$count=true&$select=id,displayName,onPremisesSyncEnabled,onPremisesSecurityIdentifier&$filter=onPremisesSyncEnabled eq true";
public const string constMsGraphUserEntry = "users/{0}";
public const string constMsGraphUserDeviceEntry = "users/{0}";
public const int constMsGraphUserPaging = 100;
public const string constMsGraphDeviceList = "devices/?$top={0}&$select=id,deviceId,displayName,onPremisesSyncEnabled,onPremisesSecurityIdentifier&$filter=onPremisesSyncEnabled eq true";
public const string constMsGraphDeviceEntry = "devices/{0}";
public const int constMsGraphDevicePaging = 100;
public const string constMsGraphManagedDeviceList = "deviceManagement/managedDevices?$top={0}&$select=id,azureActiveDirectoryDeviceId,deviceName,userPrincipalName,lastSyncDateTime&$filter=operatingSystem eq 'Windows'";
public const string constMsGraphManagedMobileDeviceList = "users/{0}/managedDevices?$select=id,userId,deviceName,lastSyncDateTime&$filter=operatingSystem eq 'Android' or operatingSystem eq 'iOS' or operatingSystem eq 'iPadOS'";
public const string constMsGraphManagedDeviceEntry = "deviceManagement/managedDevices/{0}?$select=id,complianceState,userPrincipalName,userDisplayName,managementState,lastSyncDateTime";
public const string constMsGraphManagedDeviceAllEntry = "deviceManagement/managedDevices/{0}";
public const string constMsGraphManagedDeviceSyncDevice = "deviceManagement/managedDevices/{0}/syncDevice";
public const string constMsGraphManagedDeviceDetectedApps = "deviceManagement/managedDevices/{0}/detectedApps";
public const string constMsGraphManagedDeviceCount = "users/{0}/managedDevices?$select=operatingSystem";
public const string constMsGraphManagedUserDeviceEntry = "users/{0}/managedDevices?$select=id,deviceName,model,operatingSystem,manufacturer,serialNumber";
public const string constMsGraphManagedDeviceChangeManagedStatus = "deviceManagement/managedDevices/{0}/{1}";
public const string constMsGraphTest = "deviceManagement/managedDevices";
public const string constMsGraphDeviceBitlockerRecoveryKey = "informationProtection/bitlocker/recoveryKeys?$filter=deviceId eq '{0}'";
public const string constMsGraphDeviceBitlockerRecoveryKeyDetails = "informationProtection/bitlocker/recoveryKeys/{0}?$select=key";
public const string constMsGraphDeviceLAPS = "directory/deviceLocalCredentials/{0}?$select=credentials,deviceName";
public const int constMsGraphManagedDevicePaging = 100;
public const string constTableNameIntuneDeviceDetails = "intune-deviceInfo";
public const string constTableNameIntuneUserDeviceCount = "intune-userDeviceCount";
public const string constTableNameIntuneUserDeviceCountDetail = "intune-userDeviceCount-details";
public const string constTableNameIntuneMobileDeviceDetails = "intune-mobile-deviceInfo";
public readonly List<cLdapPropertyInfo> UserMainProperties = new List<cLdapPropertyInfo>() {
new cLdapPropertyInfo() { Name="objectSid", LdapType= DataHistoryProvider.enumLdapPropertyType.ldapSid, DbName=null},
new cLdapPropertyInfo() { Name="cn", LdapType= DataHistoryProvider.enumLdapPropertyType.ldapString, DbName=null},
new cLdapPropertyInfo() { Name="DistinguishedName", LdapType= DataHistoryProvider.enumLdapPropertyType.ldapString, DbName=null},
new cLdapPropertyInfo() { Name="uSNChanged", LdapType= DataHistoryProvider.enumLdapPropertyType.ldapTimeInterval, DbName=null},
new cLdapPropertyInfo() { Name="sAMAccountName", LdapType= DataHistoryProvider.enumLdapPropertyType.ldapString, DbName=null},
new cLdapPropertyInfo() { Name="displayName", LdapType= DataHistoryProvider.enumLdapPropertyType.ldapString, DbName=null},
new cLdapPropertyInfo() { Name="userPrincipalName", LdapType= DataHistoryProvider.enumLdapPropertyType.ldapString, DbName="upn_external"},
new cLdapPropertyInfo() { Name="userAccountControl", LdapType= DataHistoryProvider.enumLdapPropertyType.ldapString, DbName=null},
new cLdapPropertyInfo() { Name="mail", LdapType= DataHistoryProvider.enumLdapPropertyType.ldapString, DbName="mail"}
};
public readonly List<cLdapPropertyInfo> UserPhoneProperties = new List<cLdapPropertyInfo>() {
new cLdapPropertyInfo() { Name="telephoneNumber", LdapType= DataHistoryProvider.enumLdapPropertyType.ldapPhone, DbName="phone", Cumulative = true, Reverse = true},
new cLdapPropertyInfo() { Name="mobile", LdapType= DataHistoryProvider.enumLdapPropertyType.ldapPhone, DbName="phone", Cumulative = true, Reverse = true},
new cLdapPropertyInfo() { Name="ipPhone", LdapType= DataHistoryProvider.enumLdapPropertyType.ldapPhone, DbName="phone", Cumulative = true, Reverse = true},
new cLdapPropertyInfo() { Name="otherTelephone", LdapType= DataHistoryProvider.enumLdapPropertyType.ldapPhone, DbName="phone", Cumulative = true, Reverse = true},
new cLdapPropertyInfo() { Name="otherMobile", LdapType= DataHistoryProvider.enumLdapPropertyType.ldapPhone, DbName="phone", Cumulative = true, Reverse = true},
new cLdapPropertyInfo() { Name="otherIpPhone", LdapType= DataHistoryProvider.enumLdapPropertyType.ldapPhone, DbName="phone", Cumulative = true, Reverse = true}
};
public readonly List<cLdapPropertyInfo> ComputerMainProperties = new List<cLdapPropertyInfo>() {
new cLdapPropertyInfo() { Name="objectSid", LdapType= DataHistoryProvider.enumLdapPropertyType.ldapSid, DbName=null},
new cLdapPropertyInfo() { Name="cn", LdapType= DataHistoryProvider.enumLdapPropertyType.ldapString, DbName=null},
new cLdapPropertyInfo() { Name="DistinguishedName", LdapType= DataHistoryProvider.enumLdapPropertyType.ldapString, DbName=null},
new cLdapPropertyInfo() { Name="uSNChanged", LdapType= DataHistoryProvider.enumLdapPropertyType.ldapTimeInterval, DbName=null},
new cLdapPropertyInfo() { Name="sAMAccountName", LdapType= DataHistoryProvider.enumLdapPropertyType.ldapString, DbName=null},
new cLdapPropertyInfo() { Name="userAccountControl", LdapType= DataHistoryProvider.enumLdapPropertyType.ldapString, DbName=null}
};
private readonly cDataHistoryCollector _collector;
private static readonly DateTime BaseDateTime;
private static readonly long MinSqlDateTimeTicks = 0;
private static readonly long MaxSqlDateTimeTicks = 0;
private readonly Dictionary<string, string> Domains = new Dictionary<string, string>();
private readonly Dictionary<string, string> DomainsRev = new Dictionary<string, string>();
private DateTime LastDomainsUpdate = DateTime.MinValue;
private bool DomainsUpdateRunning = false;
private Dictionary<Guid, cMsGraphBase> tenantGraphCache = new Dictionary<Guid, cMsGraphBase>();
static cDataHistoryCollectorActiveDirectory()
{
BaseDateTime = new DateTime(1601, 1, 1, 0, 0, 0, DateTimeKind.Utc);
MinSqlDateTimeTicks = (long)(new DateTime(1753, 1, 1, 0, 0, 0, DateTimeKind.Utc) - BaseDateTime).TotalMilliseconds * 10000 + 1;
var dt = DateTime.SpecifyKind(DateTime.MaxValue, DateTimeKind.Utc) - TimeSpan.FromDays(1);
MaxSqlDateTimeTicks = (long)(dt - BaseDateTime).TotalMilliseconds * 10000 - 1;
}
public cDataHistoryCollectorActiveDirectory(cDataHistoryCollector Collector)
: base(Collector, new List<enumDataHistoryOrigin>() { enumDataHistoryOrigin.ActiveDirectory, enumDataHistoryOrigin.Intune }, constConnectorName, constLicenseId)
{
_collector = Collector;
}
public HashSet<enumFasdInformationClass> GetSupportedInformationClasses() => new HashSet<enumFasdInformationClass>() { enumFasdInformationClass.MobileDevice };
private async Task<Dictionary<string, cDbAdScanInfo>> GetLastAdScansAsync(cDbConnection Conn, cF4sdWebRequestInfo requestInfo, int LogDeep, CancellationToken Token)
{
MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); }
if (cPerformanceLogger.IsActive && requestInfo != null) { if (CM == null) CM = MethodBase.GetCurrentMethod(); cPerformanceLogger.LogPerformanceStart(LogDeep, CM, requestInfo.id, requestInfo.created); }
var _startTime = DateTime.UtcNow;
if (!DataHistorySqlHelper.GetWellKnownSqlStatement("GetAdScanInfo", out var Query))
return null;
var Retval = new Dictionary<string, cDbAdScanInfo>();
int apiError = 0;
try
{
if (Token.IsCancellationRequested)
return null;
DbDataReader Reader = await DataHistorySqlHelper.GetTableResultAsync(Conn, Query, null, Token);
if (Token.IsCancellationRequested)
return null;
if (Reader == null)
return null;
try
{
if (Reader.HasRows)
{
while (await Reader.ReadAsync())
{
if (Token.IsCancellationRequested)
return null;
try
{
var Entry = new cDbAdScanInfo
{
ID = Reader.GetInt32(0),
Name = Reader.GetString(1),
LastRun = DataHistorySqlHelper.ConvertDBDateTimeToUTC(Reader.GetDateTime(2))
};
if (Retval.ContainsKey(Entry.Name))
Retval[Entry.Name] = Entry;
else
Retval.Add(Entry.Name, Entry);
}
catch (Exception E)
{
apiError = E.HResult;
cLogManager.DefaultLogger.LogException(E);
}
}
}
}
catch (Exception E)
{
apiError = E.HResult;
cLogManager.DefaultLogger.LogException(E);
}
finally
{
Reader.Close();
Reader.Dispose();
}
return Retval;
}
catch (Exception E)
{
apiError = E.HResult;
cLogManager.DefaultLogger.LogException(E);
}
finally
{
if (DataHistorySqlHelper.LogSql) DataHistorySqlHelper.SaveSqlTimingEntry(Query.Name, _startTime, "", apiError, requestInfo?.requestName);
if (cPerformanceLogger.IsActive && requestInfo != null) { cPerformanceLogger.LogPerformanceEnd(LogDeep, CM, requestInfo.id, requestInfo.created, _startTime); }
if (CM != null) LogMethodEnd(CM);
}
return null;
}
private async Task RemoveAdScanEntryAsync(cDbConnection Conn, int ID, cF4sdWebRequestInfo requestInfo, int LogDeep, CancellationToken Token)
{
MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); }
if (cPerformanceLogger.IsActive && requestInfo != null) { if (CM == null) CM = MethodBase.GetCurrentMethod(); cPerformanceLogger.LogPerformanceStart(LogDeep, CM, requestInfo.id, requestInfo.created); }
var _startTime = DateTime.UtcNow;
try
{
if (!DataHistorySqlHelper.GetWellKnownSqlStatement("RemoveAdScanInfo", out var Query))
return;
if (Token.IsCancellationRequested)
return;
var Params = new Dictionary<string, object>
{
{ "ID", ID }
};
var Cnt = await DataHistorySqlHelper.ExecuteAsync(Conn, Query, Params, requestInfo, Token);
}
catch (Exception E)
{
cLogManager.DefaultLogger.LogException(E);
}
finally
{
if (cPerformanceLogger.IsActive && requestInfo != null) { cPerformanceLogger.LogPerformanceEnd(LogDeep, CM, requestInfo.id, requestInfo.created, _startTime); }
if (CM != null) LogMethodEnd(CM);
}
return;
}
private async Task<int> UpdateAdScanEntryAsync(cDbConnection Conn, cDbAdScanInfo Entry, cF4sdWebRequestInfo requestInfo, int LogDeep, CancellationToken Token)
{
MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); }
if (cPerformanceLogger.IsActive && requestInfo != null) { if (CM == null) CM = MethodBase.GetCurrentMethod(); cPerformanceLogger.LogPerformanceStart(LogDeep, CM, requestInfo.id, requestInfo.created); }
var _startTime = DateTime.UtcNow;
try
{
if (!DataHistorySqlHelper.GetWellKnownSqlStatement("UpdateAdScanInfo", out var Query))
return -1;
if (Token.IsCancellationRequested)
return -1;
var Params = new Dictionary<string, object>
{
{ "ID", Entry.ID },
{ "Name", Entry.Name },
{ "LastRun", Entry.LastRun }
};
var ID = await DataHistorySqlHelper.GetScalarResultAsync<int>(Conn, Query, Params, requestInfo, Token);
return ID;
}
catch (Exception E)
{
cLogManager.DefaultLogger.LogException(E);
}
finally
{
if (cPerformanceLogger.IsActive && requestInfo != null) { cPerformanceLogger.LogPerformanceEnd(LogDeep, CM, requestInfo.id, requestInfo.created, _startTime); }
if (CM != null) LogMethodEnd(CM);
}
return -1;
}
private async Task<Dictionary<string, cDbAdScanNode>> GetLastAdScanNode(int ScanID, cF4sdWebRequestInfo requestInfo, int LogDeep, CancellationToken Token)
{
MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); }
if (cPerformanceLogger.IsActive && requestInfo != null) { if (CM == null) CM = MethodBase.GetCurrentMethod(); cPerformanceLogger.LogPerformanceStart(LogDeep, CM, requestInfo.id, requestInfo.created); }
var _startTime = DateTime.UtcNow;
if (!DataHistorySqlHelper.GetWellKnownSqlStatement("GetAdScanNodes", out var Query))
return null;
var Params = new Dictionary<string, object>
{
{ "ScanID", ScanID }
};
int apiError = 0;
var Retval = new Dictionary<string, cDbAdScanNode>();
try
{
using (var Conn = new cDbConnection(Collector.mainDbConnection))
{
if (Token.IsCancellationRequested)
return null;
DbDataReader Reader = await DataHistorySqlHelper.GetTableResultAsync(Conn, Query, Params, Token);
if (Token.IsCancellationRequested)
return null;
if (Reader == null)
return null;
try
{
if (Reader.HasRows)
{
while (await Reader.ReadAsync())
{
if (Token.IsCancellationRequested)
return null;
try
{
var Entry = new cDbAdScanNode
{
ScanID = Reader.GetInt32(0),
Name = Reader.GetString(1),
Fingerprint = Reader.GetString(2),
InvocationID = Reader.GetString(3)
};
var o = Reader[4];
if ((o == null) || (o is DBNull))
Entry.LastUSN = 0;
else
{
Decimal d = (Decimal)o;
Entry.LastUSN = Convert.ToUInt64(d);
}
if (Retval.ContainsKey(Entry.Name) && (Entry.ScanID == ScanID))
Retval[Entry.Name] = Entry;
else
Retval.Add(Entry.Name, Entry);
}
catch (Exception E)
{
cLogManager.DefaultLogger.LogException(E);
}
}
}
}
catch (Exception E)
{
cLogManager.DefaultLogger.LogException(E);
}
finally
{
Reader.Close();
Reader.Dispose();
}
}
return Retval;
}
catch (Exception E)
{
apiError = E.HResult;
cLogManager.DefaultLogger.LogException(E);
}
finally
{
if (DataHistorySqlHelper.LogSql) DataHistorySqlHelper.SaveSqlTimingEntry(Query.Name, _startTime, Params, apiError, requestInfo?.requestName);
if (cPerformanceLogger.IsActive && requestInfo != null) { cPerformanceLogger.LogPerformanceEnd(LogDeep, CM, requestInfo.id, requestInfo.created, _startTime); }
if (CM != null) LogMethodEnd(CM);
}
return null;
}
private async Task UpdateAdScanNodeEntryAsync(cDbAdScanNode Node, cF4sdWebRequestInfo requestInfo, int LogDeep, CancellationToken Token)
{
MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); }
if (cPerformanceLogger.IsActive && requestInfo != null) { if (CM == null) CM = MethodBase.GetCurrentMethod(); cPerformanceLogger.LogPerformanceStart(LogDeep, CM, requestInfo.id, requestInfo.created); }
var _startTime = DateTime.UtcNow;
try
{
if (!DataHistorySqlHelper.GetWellKnownSqlStatement("UpdateAdScanNode", out var Query))
return;
using (var Conn = new cDbConnection(Collector.mainDbConnection))
{
if (Token.IsCancellationRequested)
return;
var Params = new Dictionary<string, object>
{
{ "ScanID", Node.ScanID },
{ "Name", Node.Name },
{ "Fingerprint", Node.Fingerprint },
{ "InvocationId", Node.InvocationID },
{ "LastUsn", Convert.ToDecimal(Node.LastUSN) }
};
await DataHistorySqlHelper.ExecuteAsync(Conn, Query, Params, requestInfo, Token);
}
}
catch (Exception E)
{
cLogManager.DefaultLogger.LogException(E);
}
finally
{
if (cPerformanceLogger.IsActive && requestInfo != null) { cPerformanceLogger.LogPerformanceEnd(LogDeep, CM, requestInfo.id, requestInfo.created, _startTime); }
if (CM != null) LogMethodEnd(CM);
}
}
private static string GetStringFromLdapAttribute(SearchResultAttributeCollection Attributes, string Name, string ObjectName, int MaxLength = -1, Dictionary<string, cLdapPropertyInfo> Props = null)
{
try
{
if (Attributes.Contains(Name))
{
var arrAttr = Attributes[Name];
if ((arrAttr != null) && (arrAttr.Count > 0))
{
var objAttr = arrAttr[0];
if (objAttr == null)
return null;
var strAttr = objAttr.ToString();
if (MaxLength > 0 && strAttr.Length > MaxLength)
strAttr = strAttr.Substring(0, MaxLength);
if (Props != null && Props.TryGetValue(Name, out var _prop))
{
if (_prop.Cardinal > 0 && strAttr.Length > _prop.Cardinal)
strAttr = strAttr.Substring(0, _prop.Cardinal);
}
return strAttr;
}
}
}
catch (Exception E)
{
cLogManager.DefaultLogger.LogEntry(LogLevels.Debug, string.Format("error getting LDAP property '{0}' for object '{1}'", Name, ObjectName));
cLogManager.DefaultLogger.LogException(E, LogLevels.Debug);
}
return null;
}
private static Int32? GetInt32FromLdapAttribute(SearchResultAttributeCollection Attributes, string Name, string ObjectName)
{
try
{
if (Attributes.Contains(Name))
{
var arrAttr = Attributes[Name];
if ((arrAttr != null) && (arrAttr.Count > 0))
{
var objAttr = arrAttr[0];
if (objAttr == null)
return null;
var strAttr = objAttr.ToString();
if (Int32.TryParse(strAttr, out var RetVal))
return RetVal;
}
}
}
catch (Exception E)
{
cLogManager.DefaultLogger.LogEntry(LogLevels.Debug, string.Format("error getting LDAP property '{0}' for object '{1}'", Name, ObjectName));
cLogManager.DefaultLogger.LogException(E, LogLevels.Debug);
}
return null;
}
private static UInt32? GetUInt32FromLdapAttribute(SearchResultAttributeCollection Attributes, string Name, string ObjectName)
{
try
{
if (Attributes.Contains(Name))
{
var arrAttr = Attributes[Name];
if ((arrAttr != null) && (arrAttr.Count > 0))
{
var objAttr = arrAttr[0];
if (objAttr == null)
return null;
var strAttr = objAttr.ToString();
if (UInt32.TryParse(strAttr, out var RetVal))
return RetVal;
}
}
}
catch (Exception E)
{
cLogManager.DefaultLogger.LogEntry(LogLevels.Debug, string.Format("error getting LDAP property '{0}' for object '{1}'", Name, ObjectName));
cLogManager.DefaultLogger.LogException(E, LogLevels.Debug);
}
return null;
}
private static UInt64? GetUInt64FromLdapAttribute(SearchResultAttributeCollection Attributes, string Name, string ObjectName)
{
try
{
if (Attributes.Contains(Name))
{
var arrAttr = Attributes[Name];
if ((arrAttr != null) && (arrAttr.Count > 0))
{
var objAttr = arrAttr[0];
if (objAttr == null)
return null;
var strAttr = objAttr.ToString();
if (UInt64.TryParse(strAttr, out var RetVal))
return RetVal;
}
}
}
catch (Exception E)
{
cLogManager.DefaultLogger.LogEntry(LogLevels.Debug, string.Format("error getting LDAP property '{0}' for object '{1}'", Name, ObjectName));
cLogManager.DefaultLogger.LogException(E, LogLevels.Debug);
}
return null;
}
private static DateTime? GetDateTimeFromLdapAttribute(SearchResultAttributeCollection Attributes, string Name, string ObjectName)
{
try
{
if (Attributes.Contains(Name))
{
var arrAttr = Attributes[Name];
if ((arrAttr != null) && (arrAttr.Count > 0))
{
var objAttr = arrAttr[0];
if (objAttr == null)
return null;
var strAttr = objAttr.ToString();
if (strAttr.EndsWith("Z"))
{
strAttr = strAttr.Remove(strAttr.Length - 1, 1);
if (DateTime.TryParseExact(strAttr, "yyyyMMddHHmmss.f", CultureInfo.InvariantCulture, System.Globalization.DateTimeStyles.AdjustToUniversal, out var dt))
{
dt = DateTime.SpecifyKind(dt, DateTimeKind.Utc);
return dt;
}
}
else
{
if (long.TryParse(strAttr, out var ticks))
{
if (ticks <= MinSqlDateTimeTicks || ticks >= MaxSqlDateTimeTicks)
return null;
long msecs = ticks / 10000;
var ts = TimeSpan.FromMilliseconds(msecs);
var RetVal = BaseDateTime + ts;
return RetVal;
}
}
}
}
}
catch (Exception E)
{
cLogManager.DefaultLogger.LogEntry(LogLevels.Debug, string.Format("error getting LDAP property '{0}' for object '{1}'", Name, ObjectName));
cLogManager.DefaultLogger.LogException(E, LogLevels.Debug);
}
return null;
}
private static object GetValueFromLdapAttribute(SearchResultAttributeCollection Attributes, string Name, string ObjectName)
{
try
{
if (Attributes.Contains(Name))
{
var arrAttr = Attributes[Name];
if ((arrAttr != null) && (arrAttr.Count > 0))
{
var objAttr = arrAttr[0];
if (objAttr != null)
return objAttr;
}
}
}
catch (Exception E)
{
cLogManager.DefaultLogger.LogEntry(LogLevels.Debug, string.Format("error getting LDAP property '{0}' for object '{1}'", Name, ObjectName));
cLogManager.DefaultLogger.LogException(E, LogLevels.Debug);
}
return null;
}
private static byte[] GetByteArrayFromLdapAttribute(SearchResultAttributeCollection Attributes, string Name, string ObjectName)
{
try
{
if (Attributes.Contains(Name))
{
var arrAttr = Attributes[Name];
if ((arrAttr != null) && (arrAttr.Count > 0))
{
var objAttr = arrAttr[0];
if (objAttr is byte[] RetVal)
return RetVal;
}
}
}
catch (Exception E)
{
cLogManager.DefaultLogger.LogEntry(LogLevels.Debug, string.Format("error getting LDAP property '{0}' for object '{1}'", Name, ObjectName));
cLogManager.DefaultLogger.LogException(E, LogLevels.Debug);
}
return null;
}
private static string GetSidFromByteArray(byte[] B, string Name, string ObjectName)
{
try
{
var objSid = new SecurityIdentifier(B, 0);
var strSid = objSid.ToString();
return strSid;
}
catch (Exception E)
{
cLogManager.DefaultLogger.LogEntry(LogLevels.Debug, string.Format("error converting ldap property '{0}' for object '{1}' to SID", Name, ObjectName));
cLogManager.DefaultLogger.LogException(E, LogLevels.Debug);
}
return null;
}
public static byte[] GetByteArrayFromSid(string Sid)
{
try
{
var objSid = new SecurityIdentifier(Sid);
byte[] RetVal = new byte[objSid.BinaryLength];
objSid.GetBinaryForm(RetVal, 0);
return RetVal;
}
catch (Exception E)
{
cLogManager.DefaultLogger.LogEntry(LogLevels.Warning, string.Format($"error converting string to SID: {Sid}"));
cLogManager.DefaultLogger.LogException(E, LogLevels.Debug);
}
return null;
}
private static string FormatPlainPhone(string s)
{
if (s == null)
return null;
var RetVal = "";
foreach (var C in s)
if ((C >= '0') && (C <= '9'))
RetVal += C;
return RetVal;
}
private void GetSubOUs(LdapConnection LC, List<string> nodeList, string DSN, List<string> AlreadyScannedNodes)
{
var UL = new List<string>();
try
{
foreach (var AlreadyNode in AlreadyScannedNodes)
{
if (AlreadyNode == DSN.ToUpperInvariant())
return;
}
PageResultRequestControl pageRequestControl = new PageResultRequestControl((int)1000);
PageResultResponseControl pageResponseControl;
SearchOptionsControl soc = new SearchOptionsControl(System.DirectoryServices.Protocols.SearchOption.DomainScope);
string[] Attr = { "ou", "DistinguishedName" };
var SR = new SearchRequest(DSN, "(objectclass=organizationalUnit)", System.DirectoryServices.Protocols.SearchScope.OneLevel, Attr);
SR.Controls.Add(pageRequestControl);
SR.Controls.Add(soc);
while (true)
{
var searchResponse = (SearchResponse)LC.SendRequest(SR);
pageResponseControl = (PageResultResponseControl)searchResponse.Controls[0];
foreach (SearchResultEntry Entry in searchResponse.Entries)
{
try
{
var dn = Entry.Attributes["DistinguishedName"][0].ToString();
UL.Add(dn);
}
catch { }
}
if (pageResponseControl.Cookie.Length == 0)
break;
pageRequestControl.Cookie = pageResponseControl.Cookie;
}
foreach (var DN in UL)
{
var skip = false;
foreach (var AlreadyNode in AlreadyScannedNodes)
{
if (AlreadyNode == DN.ToUpperInvariant())
{
skip = true;
break;
}
}
if (!skip)
{
nodeList.Add(DN);
GetSubOUs(LC, nodeList, DN, AlreadyScannedNodes);
}
}
}
catch (Exception E)
{
cLogManager.DefaultLogger.LogEntry(LogLevels.Error, string.Format("Error getting sub OUs from '{0}' at server {1}", DSN, LC.SessionOptions.HostName));
cLogManager.DefaultLogger.LogException(E);
return;
}
}
private async Task<string> DoAdScanAsync(cDbConnection Conn, cDataHistoryConfigActiveDirectory AdConfig, Dictionary<string, cLdapPropertyInfo> LdapProps, cDataHistoryAdScan Scan, cDataHistoryAdScanNode Node, List<string> AlreadyScannedNodes, cDbAdScanNode NodeInfo, cDbAdGeneralInfo GeneralInfo, bool Rescan, cF4sdWebRequestInfo requestInfo, int LogDeep, CancellationToken Token)
{
MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); }
if (cPerformanceLogger.IsActive && requestInfo != null) { if (CM == null) CM = MethodBase.GetCurrentMethod(); cPerformanceLogger.LogPerformanceStart(LogDeep, CM, requestInfo.id, requestInfo.created); }
var _startTime = DateTime.UtcNow;
try
{
cDataHistoryAdServer Server = null;
string InvocationId = null;
UInt64 maxUsn = 0;
LdapConnection LC = null;
var AdDomain = Node.AdDomain;
var Creds = new NetworkCredential(AdDomain.Credential.User, AdDomain.Credential.NwCredential.Password, AdDomain.Credential.Domain);
foreach (var tmpServer in AdDomain.Servers)
{
maxUsn = 0;
// create the Ldap connection object
var LdapInfo = new LdapDirectoryIdentifier(tmpServer.FQDN, tmpServer.Port, false, false);
LC = new LdapConnection(LdapInfo);
LC.SessionOptions.ProtocolVersion = 3;
LC.Credential = Creds;
// try get the server invocationId
try
{
string[] Attr0 = { "dsServiceName", "highestCommittedUSN", "defaultNamingContext", "configurationNamingContext" };
var SR1 = new SearchRequest(null, "(objectclass=*)", System.DirectoryServices.Protocols.SearchScope.Base, Attr0);
var SRR = (SearchResponse)LC.SendRequest(SR1);
var currentDomain = SRR.Entries[0].Attributes["defaultNamingContext"][0].ToString();
var CNC = SRR.Entries[0].Attributes["configurationNamingContext"][0].ToString();
NodeInfo.domainDNS = "";
var arrDoms = currentDomain.Split(',');
if (arrDoms != null)
for (int i = 0; i < arrDoms.Length; i++)
{
var arr2 = arrDoms[i].Split('=');
if (arr2 == null || arr2.Length != 2 || string.IsNullOrEmpty(arr2[1]))
break;
if (NodeInfo.domainDNS != "")
NodeInfo.domainDNS += ".";
NodeInfo.domainDNS += arr2[1].ToLowerInvariant();
}
if (NodeInfo.domainDNS != AdDomain.FQDN.ToLowerInvariant())
{
LogEntry($"The DC server '{tmpServer.FQDN}' is not member of the domain '{AdDomain.FQDN}'.", LogLevels.Warning);
Collector.DoProcessUiMessage(2, $"Warning: The DC server '{tmpServer.FQDN}' is not member of the domain '{AdDomain.FQDN}', it is member of '{NodeInfo.domainDNS}'.");
continue;
}
var SN = SRR.Entries[0].Attributes["dsServiceName"][0].ToString();
var strMaxUsn = SRR.Entries[0].Attributes["highestcommittedusn"][0].ToString();
if (!UInt64.TryParse(strMaxUsn, out maxUsn))
maxUsn = 0;
string[] Attr1 = { "invocationId" };
SR1 = new SearchRequest(SN, "(objectclass=*)", System.DirectoryServices.Protocols.SearchScope.Base, Attr1);
SRR = (SearchResponse)LC.SendRequest(SR1);
var arrId = (byte[])SRR.Entries[0].Attributes["invocationId"][0];
InvocationId = "";
foreach (var b in arrId)
InvocationId += b.ToString("X2");
NodeInfo.domainNB = null;
try
{
string[] Attr2 = { "netbiosname" };
var filter = $"(&(objectclass=crossRef)(nCName={currentDomain}))";
//var filter = $"(objectclass=crossRef)";
SR1 = new SearchRequest("CN=Partitions," + CNC, filter, System.DirectoryServices.Protocols.SearchScope.OneLevel, Attr2);
SRR = (SearchResponse)LC.SendRequest(SR1);
NodeInfo.domainNB = SRR.Entries[0].Attributes["netbiosname"][0].ToString().ToUpperInvariant();
}
catch (Exception E)
{
LogEntry("", LogLevels.Warning);
LogException(E, LogLevels.Warning);
}
if (!string.IsNullOrEmpty(NodeInfo.domainNB))
{
if (!GeneralInfo.Domains.ContainsKey(NodeInfo.domainDNS))
GeneralInfo.Domains[NodeInfo.domainDNS] = NodeInfo.domainNB;
}
}
catch (Exception E)
{
cLogManager.DefaultLogger.LogEntry(LogLevels.Debug, string.Format("Could not get AD invocation id from server {0}", tmpServer));
cLogManager.DefaultLogger.LogException(E, LogLevels.Debug);
InvocationId = null;
}
if (!string.IsNullOrEmpty(InvocationId))
{
Server = tmpServer;
break;
}
}
// exit, if no login was sucessfull
if (Server == null)
{
Collector.DoProcessUiMessage(2, $"Error: Could not get a valid AD server with invocation id for domain '{AdDomain.FQDN}', aborting scan...");
LogEntry("Could not get a valid AD server with invocation id, aborting AD scan.", LogLevels.Error);
return null;
}
// check, if we have to do a full scan
var Fingerprint = string.Format("{0}|{1}|{2}|{3}", Scan.Name, Node.AdDomain, Node.Path, Server.FQDN);
Fingerprint = GetHash(Fingerprint);
bool bFullScan = true;
if ((NodeInfo.Fingerprint == Fingerprint) && (NodeInfo.InvocationID == InvocationId) && (NodeInfo.LastUSN > 0) && !Rescan)
bFullScan = false;
else
{
NodeInfo.Fingerprint = Fingerprint;
NodeInfo.InvocationID = InvocationId;
NodeInfo.LastUSN = 0;
}
// create the ldap filter & the list of ldap poperties needed
string ldapFilter;
string[] defAttr;
if (Scan.Type == enumFasdInformationClass.User)
{
ldapFilter = "(objectClass=user)(objectCategory=person)";
}
else if (Scan.Type == enumFasdInformationClass.Computer)
{
ldapFilter = "(objectClass=computer)(objectCategory=computer)";
}
else
{
LogEntry($"Illegal scan tpye '{Scan.Type}' used, aborting AD scan.", LogLevels.Error);
return null;
}
defAttr = LdapProps.Keys.ToArray();
if (!bFullScan)
ldapFilter += string.Format("(!(uSNChanged<={0}))", NodeInfo.LastUSN.ToString());
if (!string.IsNullOrEmpty(Node.LdapFilter))
{
var strFilter = Node.LdapFilter;
if (!(strFilter.StartsWith("(") && strFilter.EndsWith(")")))
strFilter = "(" + strFilter + ")";
ldapFilter += strFilter;
}
ldapFilter = "(&" + ldapFilter + ")";
// get the list of sub ous
var nodeList = new List<string>();
var DN = GetBaseDN(Node);
nodeList.Add(DN);
GetSubOUs(LC, nodeList, DN, AlreadyScannedNodes);
AlreadyScannedNodes.Add(DN.ToUpperInvariant());
Collector.DoProcessUiMessage(2, string.Format("Scanning {0} OUs on server {1}...", nodeList.Count, Server.FQDN));
// scan the ous
int numChanges = 0;
foreach (var dnOU in nodeList)
{
try
{
Collector.DoProcessUiMessage(4, $"scan node '{dnOU}'");
PageResultRequestControl pageRequestControl = new PageResultRequestControl((int)1000);
PageResultResponseControl pageResponseControl;
SearchOptionsControl soc = new SearchOptionsControl(System.DirectoryServices.Protocols.SearchOption.DomainScope);
var SR = new SearchRequest(dnOU, ldapFilter, System.DirectoryServices.Protocols.SearchScope.OneLevel, defAttr);
var _al = defAttr.ToList();
_al.Insert(0, "Search request with attribute list:");
cLogManager.DefaultLogger.LogList(LogLevels.Debug, _al);
SR.Controls.Add(pageRequestControl);
SR.Controls.Add(soc);
while (true)
{
var searchResponse = (SearchResponse)LC.SendRequest(SR);
pageResponseControl = (PageResultResponseControl)searchResponse.Controls[0];
if (searchResponse?.Entries?.Count > 0)
{
LogEntry($" processing {searchResponse.Entries.Count} entries for scan node '{dnOU}'", LogLevels.Debug);
foreach (SearchResultEntry Entry in searchResponse.Entries)
{
try
{
bool res = await ProcessSearchEntry(Conn, Entry, LdapProps, Scan, Node, NodeInfo, requestInfo, LogDeep + 1, Token);
if (res)
numChanges++;
}
catch (Exception E)
{
cLogManager.DefaultLogger.LogException(E);
}
}
}
if (pageResponseControl.Cookie.Length == 0)
break;
pageRequestControl.Cookie = pageResponseControl.Cookie;
}
LogEntry($" finished scan node '{dnOU}'", LogLevels.Debug);
}
catch (Exception E)
{
cLogManager.DefaultLogger.LogException(E);
}
}
Collector.DoProcessUiMessage(2, string.Format("... finished, {0} updates processed", numChanges));
NodeInfo.LastUSN = maxUsn;
return Server.FQDN;
}
catch (Exception E)
{
cLogManager.DefaultLogger.LogException(E);
}
finally
{
if (cPerformanceLogger.IsActive && requestInfo != null) { cPerformanceLogger.LogPerformanceEnd(LogDeep, CM, requestInfo.id, requestInfo.created, _startTime); }
if (CM != null) LogMethodEnd(CM);
}
return null;
}
private async Task<bool> ProcessSearchEntry(cDbConnection Conn, SearchResultEntry Entry, Dictionary<string, cLdapPropertyInfo> LdapProps, cDataHistoryAdScan Scan, cDataHistoryAdScanNode Node, cDbAdScanNode NodeInfo, cF4sdWebRequestInfo requestInfo, int LogDeep, CancellationToken Token)
{
MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); }
if (cPerformanceLogger.IsActive && requestInfo != null) { if (CM == null) CM = MethodBase.GetCurrentMethod(); cPerformanceLogger.LogPerformanceStart(LogDeep, CM, requestInfo.id, requestInfo.created); }
var _startTime = DateTime.UtcNow;
try
{
var SqlParams = new Dictionary<string, object>();
// get the USN
var cName = GetStringFromLdapAttribute(Entry.Attributes, "cn", Entry.DistinguishedName, Props: LdapProps);
var account = GetStringFromLdapAttribute(Entry.Attributes, "sAMAccountName", Entry.DistinguishedName, Props: LdapProps);
if (Node.WildcardFilter != null || Node.RegExFilter != null)
{
string strFilter;
switch (Node.FilterProperty)
{
case cDataHistoryAdScanNode.enumFilterPropery.distinguishedName:
strFilter = Entry.DistinguishedName;
break;
case cDataHistoryAdScanNode.enumFilterPropery.samAccountName:
strFilter = account;
break;
default:
strFilter = cName;
break;
}
if (Node.WildcardFilter != null)
if (Node.WildcardFilter.IsMatch(strFilter) ^ !Node.WildcardFilterNot)
return false;
if (Node.RegExFilter != null)
if (!Node.RegExFilter.IsMatch(strFilter))
return false;
}
var strSNC = GetStringFromLdapAttribute(Entry.Attributes, "usnchanged", Entry.DistinguishedName, Props: LdapProps);
if (!UInt64.TryParse(strSNC, out ulong uSNChanged))
uSNChanged = 0;
if (cLogManager.DefaultLogger.IsDebug)
{
var _al = new List<string> { $"SearchResultEntry attributes for '{Entry.DistinguishedName}':" };
foreach (var _E in Entry.Attributes.AttributeNames)
_al.Add(_E?.ToString());
cLogManager.DefaultLogger.LogList(LogLevels.Debug, _al);
}
if (uSNChanged >= NodeInfo.LastUSN)
{
// get the SID
var Sid = GetByteArrayFromLdapAttribute(Entry.Attributes, "objectSid", Entry.DistinguishedName);
if (Sid == null)
return false;
var strSid = GetSidFromByteArray(Sid, "objectSid", Entry.DistinguishedName);
if (string.IsNullOrEmpty(strSid))
return false;
SqlParams.Add("sid", strSid);
SqlParams.Add("sid_bin", Sid);
// get the account name & internal UPN
if (string.IsNullOrEmpty(account))
return false;
var internalUPN = account.ToLowerInvariant() + "@" + Node.AdDomain.FQDN;
if (Scan.Type == enumFasdInformationClass.User)
SqlParams.Add("account", account);
SqlParams.Add("upn_internal", internalUPN);
// get the userAccountControl & active flag
int? isActive = null;
var Uac = GetUInt32FromLdapAttribute(Entry.Attributes, "userAccountControl", Entry.DistinguishedName);
if (Uac is UInt32 Uac2)
{
if ((Uac2 & 2) == 0)
isActive = 1;
else
isActive = 0;
}
if (isActive == null)
{
if (Entry.Attributes.Contains("userAccountControl"))
{
var arrAttr = Entry.Attributes["userAccountControl"];
if ((arrAttr != null) && (arrAttr.Count > 0))
{
var objAttr = arrAttr[0];
if (objAttr == null)
{
LogEntry($"The ldap result for '{Entry.DistinguishedName}' contains property 'userAccountControl' with value NULL", LogLevels.Debug);
}
else
{
var strAttr = objAttr.ToString();
LogEntry($"The ldap result for '{Entry.DistinguishedName}' contains property 'userAccountControl' with value string {strAttr}", LogLevels.Debug);
if (!UInt32.TryParse(strAttr, out var RetVal))
LogEntry($"The ldap result for '{Entry.DistinguishedName}' has a property which is not UINT32", LogLevels.Debug);
}
}
else
LogEntry($"The ldap result for '{Entry.DistinguishedName}' has an empty property 'userAccountControl'", LogLevels.Debug);
}
else
{
LogEntry($"The ldap result for '{Entry.DistinguishedName}' do not contain the property 'userAccountControl'", LogLevels.Debug);
}
}
SqlParams.Add("is_active", isActive);
if (Scan.Type == enumFasdInformationClass.User)
{
SqlParams.Add("domainDns", NodeInfo.domainDNS);
SqlParams.Add("domainNB", NodeInfo.domainNB);
}
GetSqlParameters(Entry, LdapProps, ref SqlParams);
bool res = false;
if (Scan.Type == enumFasdInformationClass.User)
res = await ProcessSearchEntryUser(Conn, Entry, SqlParams, LdapProps, requestInfo, LogDeep + 1, Token);
else if (Scan.Type == enumFasdInformationClass.Computer)
res = await ProcessSearchEntryComputer(Conn, SqlParams, requestInfo, LogDeep + 1, Token);
if (res)
await ProcessAdditionalTables(Conn, Entry, Scan.Type, Node, NodeInfo, requestInfo, LogDeep + 1, Token);
return res;
}
}
catch (Exception E)
{
LogException(E);
}
finally
{
if (cPerformanceLogger.IsActive && requestInfo != null) { cPerformanceLogger.LogPerformanceEnd(LogDeep, CM, requestInfo.id, requestInfo.created, _startTime); }
if (CM != null) LogMethodEnd(CM);
}
return false;
}
private void GetSqlParameters(SearchResultEntry Entry, Dictionary<string, cLdapPropertyInfo> LdapProps, ref Dictionary<string, object> SqlParams)
{
MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); }
try
{
foreach (var Prop in LdapProps.Values)
{
if (string.IsNullOrEmpty(Prop.DbName))
continue;
var objValue = GetValueFromLdapAttribute(Entry.Attributes, Prop.Name, Entry.DistinguishedName);
if (Prop.Cumulative)
{
var strVal = GetStringFromLdapAttribute(Entry.Attributes, Prop.Name, Entry.DistinguishedName, Prop.Cardinal);
switch (Prop.LdapType)
{
case DataHistoryProvider.enumLdapPropertyType.ldapString:
break;
case DataHistoryProvider.enumLdapPropertyType.ldapPhone:
strVal = FormatPlainPhone(strVal);
break;
default:
continue;
}
if (!string.IsNullOrWhiteSpace(strVal))
{
if (SqlParams.ContainsKey(Prop.DbName))
SqlParams[Prop.DbName] += " " + strVal;
else
SqlParams.Add(Prop.DbName, strVal);
if (Prop.Reverse)
{
char[] arrRev = (strVal + 'x').ToArray();
Array.Reverse(arrRev);
var strRev = new string(arrRev);
var pName = Prop.DbName + "_rev";
if (SqlParams.ContainsKey(pName))
SqlParams[pName] += " " + strRev;
else
SqlParams.Add(pName, strRev);
}
}
}
else
{
switch (Prop.LdapType)
{
case DataHistoryProvider.enumLdapPropertyType.ldapString:
var strVal = GetStringFromLdapAttribute(Entry.Attributes, Prop.Name, Entry.DistinguishedName, Prop.Cardinal);
SqlParams[Prop.DbName] = strVal;
break;
}
}
}
foreach (var Prop in LdapProps.Values)
{
if (Prop.DbName == null)
continue;
if (!SqlParams.ContainsKey(Prop.DbName))
SqlParams.Add(Prop.DbName, DBNull.Value);
if (Prop.Cumulative && Prop.Reverse)
{
var strName = Prop.DbName + "_rev";
if (!SqlParams.ContainsKey(strName))
SqlParams.Add(strName, DBNull.Value);
}
}
}
catch (Exception E)
{
LogException(E);
}
finally
{
if (CM != null) LogMethodEnd(CM);
}
}
private async Task<bool> ProcessSearchEntryUser(cDbConnection Conn, SearchResultEntry Entry, Dictionary<string, object> SqlParams, Dictionary<string, cLdapPropertyInfo> LdapProps, cF4sdWebRequestInfo requestInfo, int LogDeep, CancellationToken Token)
{
MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); }
if (cPerformanceLogger.IsActive && requestInfo != null) { if (CM == null) CM = MethodBase.GetCurrentMethod(); cPerformanceLogger.LogPerformanceStart(LogDeep, CM, requestInfo.id, requestInfo.created); }
var _startTime = DateTime.UtcNow;
try
{
// get the full name
var fullName = GetStringFromLdapAttribute(Entry.Attributes, "displayName", Entry.DistinguishedName, Props: LdapProps);
if (string.IsNullOrEmpty(fullName))
fullName = SqlParams["account"] as string;
SqlParams.Add("full_name", fullName);
var lastSeen = DateTime.UtcNow;
SqlParams.Add("last_seen", lastSeen);
var res = await DataHistorySqlHelper.DoUpdateOrInsertAsync(Conn, "main-user", SqlParams, new List<string>() { "sid_bin" }, requestInfo, LogDeep + 1, Token);
if (res)
return true;
}
catch (Exception E)
{
LogException(E);
}
finally
{
if (cPerformanceLogger.IsActive && requestInfo != null) { cPerformanceLogger.LogPerformanceEnd(LogDeep, CM, requestInfo.id, requestInfo.created, _startTime); }
if (CM != null) LogMethodEnd(CM);
}
return false;
}
private async Task<bool> ProcessSearchEntryComputer(cDbConnection Conn, Dictionary<string, object> SqlParams, cF4sdWebRequestInfo requestInfo, int LogDeep, CancellationToken Token)
{
MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); }
if (cPerformanceLogger.IsActive && requestInfo != null) { if (CM == null) CM = MethodBase.GetCurrentMethod(); cPerformanceLogger.LogPerformanceStart(LogDeep, CM, requestInfo.id, requestInfo.created); }
var _startTime = DateTime.UtcNow;
try
{
if (!DataHistorySqlHelper.GetWellKnownSqlStatement("UpdateMainComputerLdap", out var QueryInsert))
return false;
var ret = await DataHistorySqlHelper.ExecuteAsync(Conn, QueryInsert, SqlParams, requestInfo, Token);
return ret > 0;
}
catch (Exception E)
{
LogException(E);
}
finally
{
if (cPerformanceLogger.IsActive && requestInfo != null) { cPerformanceLogger.LogPerformanceEnd(LogDeep, CM, requestInfo.id, requestInfo.created, _startTime); }
if (CM != null) LogMethodEnd(CM);
}
return false;
}
private async Task ProcessAdditionalTables(cDbConnection Conn, SearchResultEntry Entry, enumFasdInformationClass InformationClass, cDataHistoryAdScanNode Node, cDbAdScanNode NodeInfo, cF4sdWebRequestInfo requestInfo, int LogDeep, CancellationToken Token)
{
MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); }
if (cPerformanceLogger.IsActive && requestInfo != null) { if (CM == null) CM = MethodBase.GetCurrentMethod(); cPerformanceLogger.LogPerformanceStart(LogDeep, CM, requestInfo.id, requestInfo.created); }
var _startTime = DateTime.UtcNow;
try
{
foreach (var DataCluster in Collector.ClusterConfig.Clusters.Values)
{
if (DataCluster.Origin != enumDataHistoryOrigin.ActiveDirectory || DataCluster.InformationClass != InformationClass)
continue;
foreach (var Table in DataCluster.Tables.Values)
{
if (Table.Type != eDataHistoryTableType.Static)
continue;
var SqlParams = new Dictionary<string, object>();
foreach (var col in Table.Columns.Values)
{
if (!(col is cDataHistoryConfigColumn tableCol))
continue;
if (SqlParams.ContainsKey(col.Name))
continue;
try
{
object Value = null;
object Value_bin = null;
bool IsBin = false;
switch (tableCol.SourceType)
{
case eDataHistoryQueryType.Static:
switch (tableCol.SourceName)
{
case "scan":
Value = Node.parentScan?.Name;
break;
case "domainDns":
Value = NodeInfo.domainDNS;
break;
case "domainNB":
Value = NodeInfo.domainNB;
break;
}
break;
case eDataHistoryQueryType.Query:
switch (tableCol.ValueType)
{
case enumFasdValueType.SID:
IsBin = true;
var arrSid = GetByteArrayFromLdapAttribute(Entry.Attributes, tableCol.SourceName, Entry.DistinguishedName);
Value_bin = arrSid;
if (Value_bin != null)
Value = GetSidFromByteArray(arrSid, tableCol.SourceName, Entry.DistinguishedName);
break;
case enumFasdValueType.STRING:
Value = GetStringFromLdapAttribute(Entry.Attributes, tableCol.SourceName, Entry.DistinguishedName, tableCol.Cardinal);
break;
case enumFasdValueType.INT:
Value = GetInt32FromLdapAttribute(Entry.Attributes, tableCol.SourceName, Entry.DistinguishedName);
break;
case enumFasdValueType.BIGINT:
Value = (Int64?)GetUInt64FromLdapAttribute(Entry.Attributes, tableCol.SourceName, Entry.DistinguishedName);
break;
case enumFasdValueType.DATETIME:
Value = GetDateTimeFromLdapAttribute(Entry.Attributes, tableCol.SourceName, Entry.DistinguishedName);
break;
}
break;
}
SqlParams.Add(tableCol.Name, Value);
if (IsBin)
SqlParams.Add(tableCol.Name + "_bin", Value_bin);
}
catch (Exception E)
{
LogEntry($"error getting ldap property '{tableCol.SourceName}' from object {Entry.DistinguishedName}", LogLevels.Warning);
LogException(E, LogLevels.Warning);
}
}
var lstIndex = new List<string>();
foreach (var Key in Table.KeyColumns)
{
if (Key.SqlTypeBin != null)
lstIndex.Add(Key.Name + "_bin");
else
lstIndex.Add(Key.Name);
}
var res = await DataHistorySqlHelper.DoUpdateOrInsertAsync(Conn, Table.SourceName, SqlParams, lstIndex, requestInfo, LogDeep + 1, Token);
}
}
}
catch (Exception E)
{
LogException(E);
}
finally
{
if (cPerformanceLogger.IsActive && requestInfo != null) { cPerformanceLogger.LogPerformanceEnd(LogDeep, CM, requestInfo.id, requestInfo.created, _startTime); }
if (CM != null) LogMethodEnd(CM);
}
}
private async Task ProcessScanNode(cDbConnection Conn, Dictionary<string, cLdapPropertyInfo> LdapProps, cDataHistoryAdScan Scan, cDataHistoryAdScanNode Node, List<string> AlreadyScannedNodes, cDbAdScanNode NodeInfo, cDbAdGeneralInfo GeneralInfo, bool Rescan, cF4sdWebRequestInfo requestInfo, int LogDeep, CancellationToken Token)
{
MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); }
if (cPerformanceLogger.IsActive && requestInfo != null) { if (CM == null) CM = MethodBase.GetCurrentMethod(); cPerformanceLogger.LogPerformanceStart(LogDeep, CM, requestInfo.id, requestInfo.created); }
var _startTime = DateTime.UtcNow;
try
{
try
{
var Server = await Task<string>.Run(async () => { return await DoAdScanAsync(Conn, Collector.InfrastructureConfig.ActiveDirectory, LdapProps, Scan, Node, AlreadyScannedNodes, NodeInfo, GeneralInfo, Rescan, requestInfo, LogDeep + 1, Token); });
}
catch (Exception E)
{
cLogManager.DefaultLogger.LogException(E);
}
// save the new run info
await UpdateAdScanNodeEntryAsync(NodeInfo, requestInfo, LogDeep + 1, Token);
}
catch (Exception E)
{
LogException(E);
}
finally
{
if (cPerformanceLogger.IsActive && requestInfo != null) { cPerformanceLogger.LogPerformanceEnd(LogDeep, CM, requestInfo.id, requestInfo.created, _startTime); }
if (CM != null) LogMethodEnd(CM);
}
}
public async Task<cMsGraphBase> LogonAzureTenantAsync(cDataHistoryAzureTenant Tenant, bool force = false)
{
MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); }
try
{
var MsGraph = new cMsGraphBase();
if (Tenant == null)
{
return null;
}
var LI = new cMsGraphLogonInfo()
{
Tenant = Tenant?.TenantID.ToString(),
ClientID = Tenant.Credential?.User,
ClientSecret = Tenant.Credential?.NwCredential.Password
};
var RetVal = await MsGraph.LogonAsync(LI);
if (RetVal)
return MsGraph;
}
catch (Exception E)
{
LogException(E);
}
finally
{
if (CM != null) LogMethodEnd(CM);
}
return null;
}
public async Task<cMsGraphBase> GetGraphForTenantAsync(Guid tenant, bool force = false)
{
MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); }
try
{
if (tenant == null)
return null;
cDataHistoryAzureTenant _AzureTenant = null;
var tenants = Collector.InfrastructureConfig.AzureTenants?.Values;
if (tenants is null)
{
return null;
}
foreach (var Tenant in tenants)
{
if (Tenant.TenantID == tenant)
{
_AzureTenant = Tenant;
break;
}
}
if (_AzureTenant != null)
{
if (_AzureTenant != null && tenantGraphCache.TryGetValue(_AzureTenant.TenantID, out var cachedGraph))
return cachedGraph;
var graph = await LogonAzureTenantAsync(_AzureTenant, force: force);
if (graph != null)
tenantGraphCache[_AzureTenant.TenantID] = graph;
return graph;
}
}
catch (Exception E)
{
LogException(E);
}
finally
{
if (CM != null) LogMethodEnd(CM);
}
return null;
}
public async Task<bool> DoAzureScanAsync(cF4sdWebRequestInfo requestInfo, int LogDeep, CancellationToken Token)
{
MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); }
if (cPerformanceLogger.IsActive && requestInfo != null) { if (CM == null) CM = MethodBase.GetCurrentMethod(); cPerformanceLogger.LogPerformanceStart(LogDeep, CM, requestInfo.id, requestInfo.created); }
var _startTime = DateTime.UtcNow;
try
{
var tenants = Collector.InfrastructureConfig.AzureTenants?.Values;
if (tenants is null)
{
Collector.DoProcessUiMessage(2, $" could not logon to tenant - Tenant is null ", LogLevels.Error);
return true;
}
if (!DataHistorySqlHelper.GetWellKnownSqlStatement("UpdateMainUserAzureAd", out var QueryInsert))
return false;
using (var Conn = new cDbConnection(Collector.mainDbConnection))
{
if (!Conn.IsOpen)
{
LogEntry($"Could not open main sql database '{Collector.mainDbConnection.Database}', aborting scan...'", LogLevels.Error);
return false;
}
Collector.DoProcessUiMessage(0, string.Format("doing the Azure cloud scans:"));
foreach (var Tenant in tenants)
{
try
{
Collector.DoProcessUiMessage(1, $"scanning tenat {Tenant.Domain} for users ...");
var MsGraph = await GetGraphForTenantAsync(Tenant.TenantID, true);
if (MsGraph == null)
{
Collector.DoProcessUiMessage(2, $"error: could not logon to tenat {Tenant.Domain}", LogLevels.Error);
continue;
}
var strUrl = string.Format(constMsGraphUserList, constMsGraphUserPaging);
if (!string.IsNullOrEmpty(Tenant.ScanFilter))
strUrl += " and (" + Tenant.ScanFilter + ")";
var Result = await MsGraph.RequestListAsync(strUrl, UseBeta: false, retryForbidden: false, loadPaged: true, ConsistencyLevelEventual: Tenant.UseConsistencyLevelEventual);
var Count = 0;
var Updated = 0;
while (Result != null && Result.Count > 0)
{
foreach (var Entry in Result)
{
if (Entry.Result.onPremisesSecurityIdentifier == null)
continue;
Count++;
try
{
var _strId = Entry.ID;
if (!Guid.TryParse(_strId, out var Id))
continue;
var _sid = Entry.Result.onPremisesSecurityIdentifier.ToString();
var sid = new SecurityIdentifier(_sid);
var arrSid = new byte[sid.BinaryLength];
sid.GetBinaryForm(arrSid, 0);
var SqlParams = new Dictionary<string, object>(2)
{
{ "sid_bin", arrSid },
{ "id_azure", Id },
{ "tenant", Tenant.TenantID }
};
var _res = await DataHistorySqlHelper.ExecuteAsync(Conn, QueryInsert, SqlParams, requestInfo, Token);
if (_res >= 0)
Updated++;
}
catch (Exception E)
{
LogException(E);
}
}
Collector.DoProcessUiMessage(2, $"{Count} accounts processed, {Updated} updated.");
if (Result.Count < constMsGraphUserPaging)
break;
Result.Clear();
if (!await MsGraph.RequestNextAsync(Result))
break;
}
}
catch (Exception E)
{
LogException(E);
}
Collector.DoProcessUiMessage(1, $"...scanning tenat {Tenant.Domain} finished");
}
Collector.DoProcessUiMessage(0, "... Azure cloud scans finished.");
return true;
}
}
catch (Exception E)
{
LogException(E);
}
finally
{
if (cPerformanceLogger.IsActive && requestInfo != null) { cPerformanceLogger.LogPerformanceEnd(LogDeep, CM, requestInfo.id, requestInfo.created, _startTime); }
if (CM != null) LogMethodEnd(CM);
}
return false;
}
private async Task TestAzureAccessItem(cMsGraphBase MsGraph, bool UseBeta, string strUrlList, string strUrlEntry, string MessageList, string MessageEntry, CancellationToken Token, string nameProperty = null)
{
var strUrl = string.Format(strUrlList, 999);
var ResultAdList = await MsGraph.RequestListAsync(strUrl, UseBeta: UseBeta, retryForbidden: false, loadPaged: false);
if (ResultAdList != null && ResultAdList.Count > 0)
{
Collector.DoProcessUiMessage(1, $" {ResultAdList.Count} {MessageList} found.");
var _rnd = new Random();
int p = _rnd.Next(ResultAdList.Count - 1);
var _id = ResultAdList[p].ID;
if (!string.IsNullOrEmpty(_id))
{
strUrl = string.Format(strUrlEntry, _id);
var ResultAdEntry = await MsGraph.RequestAsync(strUrl, UseBeta: UseBeta, retryForbidden: false, nameProperty: nameProperty);
if (ResultAdEntry != null)
{
var _ty = ResultAdEntry.Result.GetType();
var _props = ResultAdEntry.Result as JObject;
if (_props != null)
{
Collector.DoProcessUiMessage(3, $" properties of random selected {MessageEntry} '{ResultAdEntry.DisplayName}' with {_props.Count} properties could be read.");
}
}
}
}
else
{
Collector.DoProcessUiMessage(1, $" warning: no {MessageList} could be found.", LogLevels.Warning);
}
}
public async Task TestAzureAccess(CancellationToken Token)
{
MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); }
try
{
var tenants = Collector.InfrastructureConfig.AzureTenants?.Values;
if (tenants is null)
{
Collector.DoProcessUiMessage(2, $" could not logon to tenant - Tenant is null ", LogLevels.Error);
return;
}
foreach (var Tenant in tenants)
{
// logon to tenant
Collector.DoProcessUiMessage(0, $"test scanning tenat {Tenant.Domain} for users, devices and managed devices ...");
var MsGraph = await GetGraphForTenantAsync(Tenant.TenantID, true);
if (MsGraph == null)
{
Collector.DoProcessUiMessage(2, $"error: could not logon to tenat {Tenant.Domain}", LogLevels.Error);
continue;
}
await TestAzureAccessItem(MsGraph, false, constMsGraphUserList, constMsGraphUserEntry, "synchronized user accounts", "user", Token);
await TestAzureAccessItem(MsGraph, false, constMsGraphDeviceList, constMsGraphDeviceEntry, "synchronized device accounts", "device", Token);
await TestAzureAccessItem(MsGraph, true, constMsGraphManagedDeviceList, constMsGraphManagedDeviceEntry, "Intune managed Windows device accounts", "Windows device", Token, nameProperty: "deviceName");
}
}
catch (Exception E)
{
LogException(E);
}
finally
{
if (CM != null) LogMethodEnd(CM);
}
}
public async Task<bool> DoDeviceScanAzureAsync(cF4sdWebRequestInfo requestInfo, int LogDeep, CancellationToken Token)
{
MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); }
if (cPerformanceLogger.IsActive && requestInfo != null) { if (CM == null) CM = MethodBase.GetCurrentMethod(); cPerformanceLogger.LogPerformanceStart(LogDeep, CM, requestInfo.id, requestInfo.created); }
var _startTime = DateTime.UtcNow;
try
{
var tenants = Collector.InfrastructureConfig.AzureTenants?.Values;
if (tenants is null)
{
return true;
}
if (!DataHistorySqlHelper.GetWellKnownSqlStatement("UpdateComputerAzure", out var QueryInsert))
return false;
using (var Conn = new cDbConnection(Collector.mainDbConnection))
{
if (!Conn.IsOpen)
{
LogEntry($"Could not open main sql database '{Collector.mainDbConnection.Database}', aborting scan...'", LogLevels.Error);
return false;
}
foreach (var Tenant in tenants)
{
try
{
if (Tenant.ScanIntuneDevices)
{
// logon to tenant
Collector.DoProcessUiMessage(1, $"scanning tenat {Tenant.Domain} for devices ...");
var MsGraph = await GetGraphForTenantAsync(Tenant.TenantID, true);
if (MsGraph == null)
{
Collector.DoProcessUiMessage(2, $"error: could not logon to tenat {Tenant.Domain}", LogLevels.Error);
continue;
}
var strUrl = string.Format(constMsGraphDeviceList, constMsGraphDevicePaging);
var Result = await MsGraph.RequestListAsync(strUrl, UseBeta: false, retryForbidden: false, loadPaged: true);
var Count = 0;
var Updated = 0;
while (Result != null == Result.Count > 0)
{
foreach (var Entry in Result)
{
if (Entry.Result.onPremisesSecurityIdentifier == null)
continue;
Count++;
try
{
var _strId = Entry.ID;
if (!Guid.TryParse(_strId, out var Id))
continue;
string _dvcId = Entry.Result.deviceId;
if (!Guid.TryParse(_dvcId, out var deviceId))
continue;
var _sid = Entry.Result.onPremisesSecurityIdentifier.ToString();
var sid = new SecurityIdentifier(_sid);
var arrSid = new byte[sid.BinaryLength];
sid.GetBinaryForm(arrSid, 0);
var SqlParams = new Dictionary<string, object>(2)
{
{ "sid_bin", arrSid },
{ "id", Id },
{ "deviceid", deviceId },
{ "tenant", Tenant.TenantID }
};
var _res = await DataHistorySqlHelper.ExecuteAsync(Conn, QueryInsert, SqlParams, requestInfo, Token);
if (_res > 0)
Updated++;
}
catch (Exception E)
{
LogException(E);
}
}
Collector.DoProcessUiMessage(2, $"{Count} devices processed, {Updated} updated.");
if (Result.Count < constMsGraphManagedDevicePaging)
break;
Result.Clear();
if (!await MsGraph.RequestNextAsync(Result))
break;
}
}
}
catch (Exception E)
{
LogException(E);
}
}
Collector.DoProcessUiMessage(0, "... Device cloud scans finished.");
}
}
catch (Exception E)
{
LogException(E);
}
finally
{
if (cPerformanceLogger.IsActive && requestInfo != null) { cPerformanceLogger.LogPerformanceEnd(LogDeep, CM, requestInfo.id, requestInfo.created, _startTime); }
if (CM != null) LogMethodEnd(CM);
}
return true;
}
private async Task SetVitalSignInfo(cDbConnection Conn, Guid AzureDeviceID, DateTime LastIntuneSync, Guid Tenant, Guid IntuneID, string Name, string UserName, cF4sdWebRequestInfo requestInfo, int LogDeep, CancellationToken Token)
{
MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); }
if (cPerformanceLogger.IsActive && requestInfo != null) { if (CM == null) CM = MethodBase.GetCurrentMethod(); cPerformanceLogger.LogPerformanceStart(LogDeep, CM, requestInfo.id, requestInfo.created); }
var _startTime = DateTime.UtcNow;
if (!DataHistorySqlHelper.GetWellKnownSqlStatement("GetComputerVitalSignInfoForIntune", out var QueryGetVitalSignInfo))
return;
if (!DataHistorySqlHelper.GetWellKnownSqlStatement("UpdateVitalSignMonitoringAll", out var QueryUpdateVitalSignInfoAll))
return;
if (!DataHistorySqlHelper.GetWellKnownSqlStatement("UpdateVitalSignMonitoringIntune", out var QueryUpdateVitalSignInfoIntune))
return;
try
{
// try getting the F4SD device id and the lastSeenByAgent info for the given AzureDeviceID
var SqlParams = new Dictionary<string, object>(1)
{
{ "AZURE_ID", AzureDeviceID }
};
Guid _id = Guid.Empty;
DateTime? lastSeen = null;
using (var _reader = await DataHistorySqlHelper.GetDataReaderAsync(Conn, QueryGetVitalSignInfo, SqlParams, Token))
{
if (_reader != null)
try
{
if (_reader.HasRows)
{
if (await _reader.ReadAsync())
{
try
{
_id = _reader.GetGuid(0);
lastSeen = _reader.IsDBNull(1) ? (DateTime?)null : _reader.GetDateTime(1);
}
catch (Exception E)
{
cLogManager.DefaultLogger.LogException(E);
}
}
}
}
catch (Exception E)
{
cLogManager.DefaultLogger.LogException(E);
}
}
if (_id == Guid.Empty)
{
SqlParams = new Dictionary<string, object>(3)
{
{ "@TenantId", Tenant },
{ "@IntuneId", IntuneID },
{ "@name", Name },
{ "@AzureID", AzureDeviceID },
{ "@UserName", UserName },
{ "@TimeStamp", LastIntuneSync }
};
await DataHistorySqlHelper.ExecuteAsync(Conn, QueryUpdateVitalSignInfoIntune, SqlParams, requestInfo, Token);
return;
}
// update the vital sign info of the device found
int diffToAgent = int.MinValue;
if (lastSeen != null)
diffToAgent = (int)(LastIntuneSync - lastSeen.Value).TotalHours;
if (diffToAgent == 0)
{
}
SqlParams = new Dictionary<string, object>(3)
{
{ "@Id", _id },
{ "@TimeStamp", LastIntuneSync },
{ "@DiffToAgent", diffToAgent },
{ "@UserName", UserName },
};
await DataHistorySqlHelper.ExecuteAsync(Conn, QueryUpdateVitalSignInfoAll, SqlParams, requestInfo, Token);
}
catch (Exception E)
{
LogException(E);
}
finally
{
if (cPerformanceLogger.IsActive && requestInfo != null) { cPerformanceLogger.LogPerformanceEnd(LogDeep, CM, requestInfo.id, requestInfo.created, _startTime); }
if (CM != null) LogMethodEnd(CM);
}
}
public async Task<bool> DoScanIntuneAsync(cF4sdWebRequestInfo requestInfo, int LogDeep, CancellationToken Token)
{
MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); }
if (cPerformanceLogger.IsActive && requestInfo != null) { if (CM == null) CM = MethodBase.GetCurrentMethod(); cPerformanceLogger.LogPerformanceStart(LogDeep, CM, requestInfo.id, requestInfo.created); }
var _startTime = DateTime.UtcNow;
try
{
var tenants = Collector.InfrastructureConfig.AzureTenants?.Values;
if (tenants is null)
{
return true;
}
if (!DataHistorySqlHelper.GetWellKnownSqlStatement("UpdateComputerIntune", out var QueryInsert))
return false;
using (var Conn = new cDbConnection(Collector.mainDbConnection))
{
if (!Conn.IsOpen)
{
LogEntry($"Could not open main sql database '{Collector.mainDbConnection.Database}', aborting scan...'", LogLevels.Error);
return false;
}
bool HasTenantToScan = false;
foreach (var Tenant in tenants)
{
try
{
if (Tenant.ScanIntuneDevices)
{
HasTenantToScan = true;
// logon to tenant
Collector.DoProcessUiMessage(1, $"scanning tenat {Tenant.Domain} for devices in Intune ...");
var MsGraph = await GetGraphForTenantAsync(Tenant.TenantID, true);
if (MsGraph == null)
{
Collector.DoProcessUiMessage(2, $"error: could not logon to tenat {Tenant.Domain}", LogLevels.Error);
continue;
}
var strUrl = string.Format(constMsGraphManagedDeviceList, constMsGraphManagedDevicePaging);
var Result = await MsGraph.RequestListAsync(strUrl, UseBeta: true, retryForbidden: false, loadPaged: true);
var Count = 0;
var Updated = 0;
while (Result != null == Result.Count > 0)
{
foreach (var Entry in Result)
{
Count++;
try
{
var _strId = Entry.ID;
if (!Guid.TryParse(_strId, out var Id))
continue;
string _dvcId = Entry.Result.azureActiveDirectoryDeviceId;
if (!Guid.TryParse(_dvcId, out var deviceId))
continue;
var SqlParams = new Dictionary<string, object>(2)
{
{ "id", Id },
{ "deviceid", deviceId }
};
var _res = await DataHistorySqlHelper.ExecuteAsync(Conn, QueryInsert, SqlParams, requestInfo, Token);
if (_res > 0)
Updated++;
else
{
if (DataHistorySqlHelper.GetWellKnownSqlStatement("UpdateComputerIntune2", out var QueryInsert2))
{
string _dvcName = Entry.Result.deviceName;
var SqlParams2 = new Dictionary<string, object>(2)
{
{ "id", Id },
{ "deviceid", deviceId },
{ "tenant", Tenant.TenantID },
{ "upn_internal", (_dvcName+"$@"+Tenant.TenantID).ToLowerInvariant() }
};
var _res2 = await DataHistorySqlHelper.ExecuteAsync(Conn, QueryInsert2, SqlParams2, requestInfo, Token);
if (_res2 > 0)
Updated++;
}
}
#if VITALSIGN_MONITORING
String Name = Entry.Result.deviceName;
String UserName = Entry.Result.userPrincipalName;
if (Entry.Result.lastSyncDateTime is JValue proplastSyncTime)
{
var lastSyncTime = proplastSyncTime.ToObject<DateTime>();
await SetVitalSignInfo(Conn, deviceId, lastSyncTime, Tenant.TenantID, Id, Name, UserName, requestInfo, LogDeep + 1, Token);
}
#endif
}
catch (Exception E)
{
LogException(E);
}
}
Collector.DoProcessUiMessage(2, $"{Count} devices processed, {Updated} updated.");
if (Result.Count < constMsGraphManagedDevicePaging)
break;
Result.Clear();
if (!await MsGraph.RequestNextAsync(Result))
break;
}
}
}
catch (Exception E)
{
LogException(E);
}
}
if (HasTenantToScan)
Collector.DoProcessUiMessage(0, "... Intune cloud scans finished.");
}
}
catch (Exception E)
{
LogException(E);
}
finally
{
if (cPerformanceLogger.IsActive && requestInfo != null) { cPerformanceLogger.LogPerformanceEnd(LogDeep, CM, requestInfo.id, requestInfo.created, _startTime); }
if (CM != null) LogMethodEnd(CM);
}
return true;
}
public async Task<cF4sdStagedSearchResultRelations> GetRelationsAsync(IEnumerable<cF4sdIdentityEntry> ids, enumFasdInformationClass informationClass, int age, CancellationToken token = default)
{
try
{
List<cF4sdConnectorIds> Ids = await _collector.getConnectorIdList(ids.ToList(), token, null, 0);
List<cF4sdApiSearchResultRelation> relations = await GetMobileDeviceAsync(Ids, token, null);
return new cF4sdStagedSearchResultRelations() { Relations = relations };
}
catch (Exception ex)
{
LogException(ex);
}
return null;
}
public override async Task<List<cF4SDHealthCardRawData.cHealthCardTable>> GetTableResultsVirtualAsync(List<cDataHistoryConfigTable> Tables, Dictionary<enumFasdInformationClass, cF4sdConnectorIds> Identities, DateTime RefTime, int MaxAge, bool instantly, Guid? CacheId, CancellationToken Token, cF4sdWebRequestInfo requestInfo, int LogDeep)
{
MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); }
if (cPerformanceLogger.IsActive && requestInfo != null) { if (CM == null) CM = MethodBase.GetCurrentMethod(); cPerformanceLogger.LogPerformanceStart(LogDeep, CM, requestInfo.id, requestInfo.created); }
var _startTime = DateTime.UtcNow;
try
{
var bDeviceExists = Tables.Exists(v => v.Name == constTableNameIntuneDeviceDetails && v.ParentCluster?.Origin == enumDataHistoryOrigin.Intune);
var bUserDeviceExists = Tables.Exists(v => v.Name == constTableNameIntuneUserDeviceCount && v.ParentCluster?.Origin == enumDataHistoryOrigin.Intune);
var bMobileDeviceExists = Tables.Exists(v => v.Name == constTableNameIntuneMobileDeviceDetails && v.ParentCluster?.Origin == enumDataHistoryOrigin.Intune);
if (!Identities.TryGetValue(enumFasdInformationClass.Computer, out var computer) && computer?.intuneId != Guid.Empty)
bDeviceExists = false;
if(!Identities.TryGetValue(enumFasdInformationClass.MobileDevice, out var mobile) && mobile?.Id != Guid.Empty)
bMobileDeviceExists = false;
if(!Identities.TryGetValue(enumFasdInformationClass.User, out var user) && user?.intuneId != Guid.Empty)
bUserDeviceExists = false;
if (!(bDeviceExists || bMobileDeviceExists || bUserDeviceExists))
return null;
var retVal = new List<cF4SDHealthCardRawData.cHealthCardTable>();
var listTasks = new List<Task<List<cF4SDHealthCardRawData.cHealthCardTable>>>(2);
if (bDeviceExists)
if (CacheId == null && !instantly)
{
retVal.Add(new cF4SDHealthCardRawData.cHealthCardTable()
{
Name = constTableNameIntuneDeviceDetails,
InformationClass = enumFasdInformationClass.Computer,
Origin = enumDataHistoryOrigin.Intune,
IsIncomplete = true
});
}
else
listTasks.Add(Task.Run<List<cF4SDHealthCardRawData.cHealthCardTable>>(async () => { return await GetIntuneDeviceTableAsync(computer.intuneId, computer.tenantId, requestInfo, LogDeep + 1, Token); }));
if (bMobileDeviceExists)
if (CacheId == null && !instantly)
{
retVal.Add(new cF4SDHealthCardRawData.cHealthCardTable()
{
Name = constTableNameIntuneMobileDeviceDetails,
InformationClass = enumFasdInformationClass.MobileDevice,
Origin = enumDataHistoryOrigin.Intune,
IsIncomplete = true
});
}
else
listTasks.Add(Task.Run<List<cF4SDHealthCardRawData.cHealthCardTable>>(async () => { return await GetIntuneMobileDeviceTableAsync(mobile.Id, user.tenantId, requestInfo, LogDeep + 1, Token); }));
if (bUserDeviceExists)
if (CacheId == null && !instantly)
{
retVal.Add(new cF4SDHealthCardRawData.cHealthCardTable()
{
Name = constTableNameIntuneUserDeviceCount,
InformationClass = enumFasdInformationClass.User,
Origin = enumDataHistoryOrigin.Intune,
IsIncomplete = true
});
}
else
listTasks.Add(Task.Run<List<cF4SDHealthCardRawData.cHealthCardTable>>(async () => { return await GetIntuneUserDeviceCountTableAsync(user.intuneId, user.tenantId, requestInfo, LogDeep + 1, Token); }));
var arrRes = await Task.WhenAll(listTasks.ToArray());
foreach (var Entry in arrRes)
if (Entry != null)
retVal.AddRange(Entry);
if (retVal.Count == 0)
return null;
return retVal;
}
catch (Exception E)
{
LogException(E);
}
finally
{
if (cPerformanceLogger.IsActive && requestInfo != null) { cPerformanceLogger.LogPerformanceEnd(LogDeep, CM, requestInfo.id, requestInfo.created, _startTime); }
if (CM != null) LogMethodEnd(CM);
}
return null;
}
public override async Task<List<cF4SDHealthCardRawData.cHealthCardDetailsTable>> GetDetailsTableResultsVirtualAsync(List<cDataHistoryConfigTable> Tables, Dictionary<enumFasdInformationClass, cF4sdConnectorIds> Identities, DateTime RefTime, int MaxAge, CancellationToken Token, cF4sdWebRequestInfo requestInfo, int LogDeep)
{
MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); }
if (cPerformanceLogger.IsActive && requestInfo != null) { if (CM == null) CM = MethodBase.GetCurrentMethod(); cPerformanceLogger.LogPerformanceStart(LogDeep, CM, requestInfo.id, requestInfo.created); }
var _startTime = DateTime.UtcNow;
try
{
var bUserDeviceDetailExists = Tables.Exists(v => v.Name == constTableNameIntuneUserDeviceCountDetail && v.ParentCluster?.Origin == enumDataHistoryOrigin.Intune);
if (!Identities.TryGetValue(enumFasdInformationClass.User, out var userIds))
{
bUserDeviceDetailExists = false;
}
if (!bUserDeviceDetailExists)
return null;
var listTasks = new List<Task<List<cF4SDHealthCardRawData.cHealthCardDetailsTable>>>(2);
if (bUserDeviceDetailExists)
listTasks.Add(Task.Run<List<cF4SDHealthCardRawData.cHealthCardDetailsTable>>(async () => { return await GetIntuneUserDeviceCountDetailTableAsync(userIds.intuneId, userIds.tenantId, requestInfo, LogDeep + 1, Token); }));
var arrRes = await Task.WhenAll(listTasks.ToArray());
var retVal = new List<cF4SDHealthCardRawData.cHealthCardDetailsTable>();
foreach (var Entry in arrRes)
if (Entry != null)
retVal.AddRange(Entry);
if (retVal.Count == 0)
return null;
return retVal;
}
catch (Exception E)
{
LogException(E);
}
finally
{
if (cPerformanceLogger.IsActive && requestInfo != null) { cPerformanceLogger.LogPerformanceEnd(LogDeep, CM, requestInfo.id, requestInfo.created, _startTime); }
if (CM != null) LogMethodEnd(CM);
}
return null;
}
private async Task<List<cF4SDHealthCardRawData.cHealthCardTable>> GetIntuneDeviceTableAsync(Guid IntuneId, Guid TenantId, cF4sdWebRequestInfo requestInfo, int LogDeep, CancellationToken Token)
{
MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); }
if (cPerformanceLogger.IsActive && requestInfo != null) { if (CM == null) CM = MethodBase.GetCurrentMethod(); cPerformanceLogger.LogPerformanceStart(LogDeep, CM, requestInfo.id, requestInfo.created); }
var _startTime = DateTime.UtcNow;
try
{
if (!Collector.ClusterConfig.Tables.TryGetValue(constTableNameIntuneDeviceDetails, out var IntuneTableDetails))
return null;
var strUrl = string.Format(constMsGraphManagedDeviceEntry, IntuneId);
var MsGraph = await GetGraphForTenantAsync(TenantId, true);
if (MsGraph == null)
return null;
var res = await MsGraph.RequestAsync(strUrl, UseBeta: true, retryForbidden: false);
if (res != null)
{
JObject deviceInfos = res.Result;
var retVal = new cF4SDHealthCardRawData.cHealthCardTable()
{
Name = constTableNameIntuneDeviceDetails,
InformationClass = enumFasdInformationClass.Computer,
Origin = enumDataHistoryOrigin.Intune,
IsStatic = true,
TableType = eDataHistoryTableType.Static
};
var dicVals = new Dictionary<string, object>();
var vals = deviceInfos.Children();
if (cLogManager.DefaultLogger.IsDebug) LogEntry("SpecialDebug GetIntuneMainTableAsync #3");
foreach (var token in vals)
{
try
{
if (token is JProperty prop)
if (prop.Value is JValue val)
dicVals[prop.Name] = val.Value;
}
catch (Exception E)
{
LogEntry($"Error getting intune detail property '{token.Path}'", LogLevels.Error);
LogException(E);
}
}
foreach (var colInfo in IntuneTableDetails.Columns.Values)
{
try
{
if (!dicVals.TryGetValue(colInfo.SourceName, out var objVal))
objVal = null;
objVal = ConvertToF4sdType(objVal, colInfo.ValueType);
var _col = new cF4SDHealthCardRawData.cHealthCardTableColumn(retVal) { ColumnName = colInfo.Name, Values = new List<object>(1) { objVal } };
retVal.Columns[colInfo.Name] = _col;
}
catch { }
}
return new List<cF4SDHealthCardRawData.cHealthCardTable>(1) { retVal };
}
else
{
LogEntry($"Could not get detailed device information from intune with id {IntuneId}. Device content is empty.", LogLevels.Warning);
}
}
catch (Exception E)
{
LogException(E);
}
finally
{
if (cPerformanceLogger.IsActive && requestInfo != null) { cPerformanceLogger.LogPerformanceEnd(LogDeep, CM, requestInfo.id, requestInfo.created, _startTime); }
if (CM != null) LogMethodEnd(CM);
}
return null;
}
private async Task<List<cF4SDHealthCardRawData.cHealthCardTable>> GetIntuneUserDeviceCountTableAsync(Guid Userid, Guid TenantId, cF4sdWebRequestInfo RequestInfo, int LogDeep, CancellationToken Token)
{
MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); }
if (cPerformanceLogger.IsActive && RequestInfo != null) { if (CM == null) CM = MethodBase.GetCurrentMethod(); cPerformanceLogger.LogPerformanceStart(LogDeep, CM, RequestInfo.id, RequestInfo.created); }
var _startTime = DateTime.UtcNow;
try
{
if (!Collector.ClusterConfig.Tables.TryGetValue(constTableNameIntuneUserDeviceCount, out var IntuneTableDetails))
return null;
var strUrl = string.Format(constMsGraphManagedDeviceCount, Userid);
if (cLogManager.DefaultLogger.IsDebug) LogEntry("SpecialDebug GetIntuneUserDeviceCountTableAsync #1");
var MsGraph = await GetGraphForTenantAsync(TenantId, true);
if (cLogManager.DefaultLogger.IsDebug) LogEntry("SpecialDebug GetIntuneUserDeviceCountTableAsync #2");
if (MsGraph == null)
return null;
if (cLogManager.DefaultLogger.IsDebug) LogEntry("SpecialDebug GetIntuneUserDeviceCountTableAsync #3");
var res = await MsGraph.RequestAsync(strUrl, UseBeta: false, retryForbidden: false);
if (cLogManager.DefaultLogger.IsDebug) LogEntry("SpecialDebug GetIntuneUserDeviceCountTableAsync #4");
if (res != null)
{
JObject deviceInfos = res.Result;
var retVal = new cF4SDHealthCardRawData.cHealthCardTable()
{
Name = constTableNameIntuneUserDeviceCount,
InformationClass = enumFasdInformationClass.User,
Origin = enumDataHistoryOrigin.Intune,
IsStatic = true,
TableType = eDataHistoryTableType.Static
};
var vals = deviceInfos.Children();
var devices = deviceInfos["value"]?
.Select(d => (JObject)d)
.GroupBy(d => (string)d["operatingSystem"])
.ToDictionary(g => g.Key, g => (object)g.Count()).ToList();
if (cLogManager.DefaultLogger.IsDebug) LogEntry("SpecialDebug GetIntuneUserDeviceCountTableAsync #5");
var dicVals = new Dictionary<string, object>
{
{ "id", Userid },
{ "windows", 0 },
{ "iOS", 0 },
{ "android", 0 },
{ "linux", 0 },
{ "macOs", 0 },
{ "total", 0 }
};
if (devices != null)
{
dicVals["total"] = devices.Sum(kvp => (int)kvp.Value);
foreach (var value in devices)
{
switch (value.Key.ToLower())
{
case "windows":
dicVals["windows"] = value.Value;
break;
case "ios":
dicVals["iOS"] = value.Value;
break;
case "android":
dicVals["android"] = value.Value;
break;
case "linux":
dicVals["linux"] = value.Value;
break;
case "macos":
dicVals["macOs"] = value.Value;
break;
}
}
foreach (var colInfo in IntuneTableDetails.Columns.Values)
{
try
{
if (!dicVals.TryGetValue(colInfo.SourceName, out var objVal))
objVal = null;
objVal = ConvertToF4sdType(objVal, colInfo.ValueType);
var _col = new cF4SDHealthCardRawData.cHealthCardTableColumn(retVal) { ColumnName = colInfo.Name, Values = new List<object>(1) { objVal } };
retVal.Columns[colInfo.Name] = _col;
}
catch { }
}
}
return new List<cF4SDHealthCardRawData.cHealthCardTable>(1) { retVal };
}
else
{
LogEntry($"Could not get detailed DeviceCount information from intune with id {Userid}. DeviceCount content is empty.", LogLevels.Warning);
}
}
catch (Exception E)
{
LogException(E);
}
finally
{
if (cPerformanceLogger.IsActive && RequestInfo != null) { cPerformanceLogger.LogPerformanceEnd(LogDeep, CM, RequestInfo.id, RequestInfo.created, _startTime); }
if (CM != null) LogMethodEnd(CM);
}
return null;
}
private async Task<List<cF4SDHealthCardRawData.cHealthCardDetailsTable>> GetIntuneUserDeviceCountDetailTableAsync(Guid Userid, Guid TenantId, cF4sdWebRequestInfo RequestInfo, int LogDeep, CancellationToken Token)
{
MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); }
if (cPerformanceLogger.IsActive && RequestInfo != null) { if (CM == null) CM = MethodBase.GetCurrentMethod(); cPerformanceLogger.LogPerformanceStart(LogDeep, CM, RequestInfo.id, RequestInfo.created); }
var _startTime = DateTime.UtcNow;
try
{
if (!Collector.ClusterConfig.Tables.TryGetValue(constTableNameIntuneUserDeviceCountDetail, out var IntuneTableDetails))
return null;
var strUrl = string.Format(constMsGraphManagedUserDeviceEntry, Userid);
// logon to tenant
var MsGraph = await GetGraphForTenantAsync(TenantId, true);
if (MsGraph == null)
return null;
var res = await MsGraph.RequestAsync(strUrl, UseBeta: false, retryForbidden: false);
if (res != null)
{
JObject deviceInfos = res.Result;
var retValList = new List<cF4SDHealthCardRawData.cHealthCardDetailsTable>();
var vals = deviceInfos["value"] as JArray;
if (vals != null)
{
var retVal = new cF4SDHealthCardRawData.cHealthCardDetailsTable()
{
Name = constTableNameIntuneUserDeviceCountDetail,
Columns = IntuneTableDetails.Columns.Values.Select(v => v.Name).ToList(),
Values = new Dictionary<int, List<object[]>>()
};
int index = 0;
foreach (var device in vals)
{
try
{
var dicVals = device.Children<JProperty>().ToDictionary(prop => prop.Name, prop => prop.Value?.ToObject<object>());
var row = retVal.Columns.Select(colName => dicVals.TryGetValue(colName, out var value) ? value : null).ToArray();
if (!retVal.Values.ContainsKey(0))
{
retVal.Values[0] = new List<object[]>();
}
retVal.Values[0].Add(row);
}
catch (Exception e)
{
LogEntry($"Error processing device entry '{device.Path}'", LogLevels.Error);
LogException(e);
}
index++;
}
retValList.Add(retVal);
return retValList;
}
else
{
LogEntry($"Could not get detailed DeviceCount information from intune with id {Userid}. DeviceCount content is empty.", LogLevels.Warning);
}
}
return null;
}
catch (Exception E)
{
LogException(E);
}
finally
{
if (cPerformanceLogger.IsActive && RequestInfo != null) { cPerformanceLogger.LogPerformanceEnd(LogDeep, CM, RequestInfo.id, RequestInfo.created, _startTime); }
if (CM != null) LogMethodEnd(CM);
}
return null;
}
public async Task<List<cF4sdApiSearchResultRelation>> GetMobileDeviceAsync(List<cF4sdConnectorIds> UserId, CancellationToken Token, cF4sdWebRequestInfo requestInfo)
{
MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); }
try
{
string userIds = string.Join(",", UserId.Where(u => !string.IsNullOrWhiteSpace(u?.intuneId.ToString())).Select(u => u.intuneId));
var tenantId = UserId?.FirstOrDefault(u => u?.tenantId != Guid.Empty)?.tenantId ?? Guid.Empty;
var userId_str = "ef333f80-0ae1-40b3-826f-2dc69c80c1f0";
var intuneId = new Guid(userId_str);
var strUrl = string.Format(constMsGraphManagedMobileDeviceList, intuneId);
var MsGraph = await GetGraphForTenantAsync(tenantId, true);
if (MsGraph == null)
return null;
// Schritt 1: Abrufen der Recovery-Key IDs
var Result = await MsGraph.RequestListAsync(strUrl, UseBeta: false, retryForbidden: false);
if (Result != null == Result.Count > 0)
{
var searchResult = new List<cF4sdApiSearchResultRelation>(Result.Count);
//double _sumDuration = 0;
foreach (var Entry in Result)
{
var endDate = DateTime.MinValue;
if (!DateTime.TryParse(Entry.Result.lastSyncDateTime?.ToString(), out endDate))
endDate = DateTime.UtcNow;
//var startDate = DateTime.MinValue;
//if (!DateTime.TryParse(Entry.Result.StartDate?.ToString(), out startDate))
// startDate = DateTime.UtcNow;
//TimeSpan _duration = endDate - startDate;
//_sumDuration += _duration.TotalSeconds;
var searchInfo = new cF4sdApiSearchResultRelation()
{
Type = enumF4sdSearchResultClass.MobileDevice,
Name = Entry.Result.deviceName?.ToString(),
DisplayName = Entry.Result.deviceName?.ToString(),
LastUsed = endDate,
UsingLevel = 0,
id = Entry.Result.id,
Status = enumF4sdSearchResultStatus.Active,
Identities = new cF4sdIdentityList()
{
new cF4sdIdentityEntry(){ Class = enumFasdInformationClass.MobileDevice, Id = Entry.Result?.id},
new cF4sdIdentityEntry(){ Class = enumFasdInformationClass.User, Id = (Entry.Result.userId == null || Entry.Result?.userId == DBNull.Value) ? UserId[0].Id : Entry.Result.userId }
}
};
searchResult.Add(searchInfo);
}
//if (searchResult != null && searchResult.Count > 0)
//{
// foreach (var Entry in searchResult)
// {
// Entry.UsingLevel /= _sumDuration;
// }
//}
return searchResult;
}
}
catch (Exception E)
{
LogException(E);
}
finally
{
if (CM != null) LogMethodEnd(CM);
}
return null;
}
private async Task<List<cF4SDHealthCardRawData.cHealthCardTable>> GetIntuneMobileDeviceTableAsync(Guid IntuneId, Guid TenantId, cF4sdWebRequestInfo requestInfo, int LogDeep, CancellationToken Token)
{
MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); }
if (cPerformanceLogger.IsActive && requestInfo != null) { if (CM == null) CM = MethodBase.GetCurrentMethod(); cPerformanceLogger.LogPerformanceStart(LogDeep, CM, requestInfo.id, requestInfo.created); }
var _startTime = DateTime.UtcNow;
try
{
if (!Collector.ClusterConfig.Tables.TryGetValue(constTableNameIntuneMobileDeviceDetails, out var IntuneTableDetails))
return null;
var strUrl = string.Format(constMsGraphManagedDeviceAllEntry, IntuneId);
var MsGraph = await GetGraphForTenantAsync(TenantId, true);
if (MsGraph == null)
return null;
var res = await MsGraph.RequestAsync(strUrl, UseBeta: true, retryForbidden: false);
if (res != null)
{
JObject deviceInfos = res.Result;
var retVal = new cF4SDHealthCardRawData.cHealthCardTable()
{
Name = constTableNameIntuneMobileDeviceDetails,
InformationClass = enumFasdInformationClass.MobileDevice,
Origin = enumDataHistoryOrigin.Intune,
IsStatic = true,
TableType = eDataHistoryTableType.Static
};
var dicVals = new Dictionary<string, object>();
var vals = deviceInfos.Children();
if (cLogManager.DefaultLogger.IsDebug) LogEntry("SpecialDebug GetIntuneMainTableAsync #3");
foreach (var token in vals)
{
try
{
if (token is JProperty prop)
if (prop.Value is JValue val)
dicVals[prop.Name] = val.Value;
}
catch (Exception E)
{
LogEntry($"Error getting intune detail property '{token.Path}'", LogLevels.Error);
LogException(E);
}
}
foreach (var colInfo in IntuneTableDetails.Columns.Values)
{
try
{
if (!dicVals.TryGetValue(colInfo.SourceName, out var objVal))
objVal = null;
objVal = ConvertToF4sdType(objVal, colInfo.ValueType);
var _col = new cF4SDHealthCardRawData.cHealthCardTableColumn(retVal) { ColumnName = colInfo.Name, Values = new List<object>(1) { objVal } };
retVal.Columns[colInfo.Name] = _col;
}
catch { }
}
return new List<cF4SDHealthCardRawData.cHealthCardTable>(1) { retVal };
}
else
{
LogEntry($"Could not get detailed device information from intune with id {IntuneId}. Device content is empty.", LogLevels.Warning);
}
}
catch (Exception E)
{
LogException(E);
}
finally
{
if (cPerformanceLogger.IsActive && requestInfo != null) { cPerformanceLogger.LogPerformanceEnd(LogDeep, CM, requestInfo.id, requestInfo.created, _startTime); }
if (CM != null) LogMethodEnd(CM);
}
return null;
}
public async Task<dynamic> QuickActionRun(cF4SDServerQuickActionParameters jsonRequest, cF4sdWebRequestInfo requestInfo, CancellationToken Token)
{
dynamic result = null;
switch (jsonRequest.Action)
{
case "UpdateComplianceState":
result = await UpdateComplianceState(jsonRequest.Identities, requestInfo, CancellationToken.None);
break;
case "ChangeManagedStatus":
result = await ChangeManagedStatus(jsonRequest.Identities, requestInfo, jsonRequest.AdjustableParameter?.Values?.FirstOrDefault()?.ToString() ?? string.Empty, CancellationToken.None);
break;
case "GetManagedApps":
result = await GetManagedApps(jsonRequest.Identities, requestInfo, CancellationToken.None);
break;
case "GetBitlockerKey":
result = await GetBitlockerKey(jsonRequest.Identities, requestInfo, CancellationToken.None);
break;
case "GetLAPS":
result = await GetLAPS(jsonRequest.Identities, requestInfo, CancellationToken.None);
break;
case "GetBitlockerKeyTest":
result = await GetBitlockerKeyTest(jsonRequest.Identities, requestInfo, CancellationToken.None);
break;
case "GetLapsKeyTest":
result = await GetLapsKeyTest(jsonRequest.Identities, requestInfo, CancellationToken.None);
break;
}
return result;
}
public override async Task<List<string>> GetQuickActionList()
{
var HasIntuneConfig = (Collector.InfrastructureConfig.AzureTenants?.Values.Any(t => t.ScanIntuneDevices) == true);
if (HasIntuneConfig)
{
var quickactions = new List<string>{
"Update ComplianceState",
"Change ManagedStatus",
"Get Managed Apps",
"Get BitlockerKey",
"Get LAPS",
"GetLapsKeyTest",
};
return await Task.FromResult(quickactions);
}
var result = new List<string>();
return result;
}
public async Task<dynamic> UpdateComplianceState(List<cF4sdIdentityEntry> Identities, cF4sdWebRequestInfo requestInfo, CancellationToken Token)
{
var Ids = await Collector.getConntectorIds(Identities, Token, requestInfo, 1);
if (!Ids.TryGetValue(enumFasdInformationClass.Computer, out var _ids))
return null;
var strUrl = string.Format(constMsGraphManagedDeviceSyncDevice, _ids.intuneId);
var MsGraph = await GetGraphForTenantAsync(_ids.tenantId, true);
if (MsGraph == null)
{
return null;
}
var Result = await MsGraph.RequestAsync(strUrl, UseBeta: false, httpMethod: eHttpMethod.post, retryForbidden: false);
if (Result != null)
{
var ResultJson = Result.Result as JObject;
if (ResultJson != null)
{
Collector.DoProcessUiMessage(3, $" properties of selected '{ResultJson}' with {ResultJson.Count} properties could be read.");
}
return ResultJson;
}
return null;
}
public async Task<dynamic> GetBitlockerKey(List<cF4sdIdentityEntry> identities, cF4sdWebRequestInfo requestInfo, CancellationToken Token)
{
var Ids = await Collector.getConntectorIds(identities, Token, requestInfo, 1);
if (!Ids.TryGetValue(enumFasdInformationClass.Computer, out var _ids))
return null;
// URL zum Abrufen der Recovery-Keys
var strUrl = string.Format(constMsGraphDeviceBitlockerRecoveryKey, _ids.azureDeviceId);
var MsGraph = await GetGraphForTenantAsync(_ids.tenantId, true);
if (MsGraph == null)
return null;
// Schritt 1: Abrufen der Recovery-Key IDs
var resultRecoveryKeys = await MsGraph.RequestAsync(strUrl, UseBeta: false, retryForbidden: false);
if (resultRecoveryKeys?.Result["value"] is JArray recoveryKeyArray && recoveryKeyArray.Count > 0)
{
var results = new JArray();
foreach (var item in recoveryKeyArray)
{
var recoveryKeyId = item["id"].ToString();
// Schritt 2: Abrufen des Recovery-Key-Wertes
var strUrl2 = string.Format(constMsGraphDeviceBitlockerRecoveryKeyDetails, recoveryKeyId);
var resultKey = await MsGraph.RequestAsync(strUrl2, UseBeta: false, retryForbidden: false);
if (resultKey?.Result != null)
{
results.Add(resultKey.Result as JObject);
}
}
Collector.DoProcessUiMessage(3, $"{results.Count} Bitlocker-Key(s) erfolgreich abgerufen.");
return results;
}
Collector.DoProcessUiMessage(3, "Kein Bitlocker-Key gefunden oder Abruf fehlgeschlagen.");
return null;
}
public async Task<dynamic> GetLAPS(List<cF4sdIdentityEntry> identities, cF4sdWebRequestInfo requestInfo, CancellationToken Token)
{
var Ids = await Collector.getConntectorIds(identities, Token, requestInfo, 1);
if (!Ids.TryGetValue(enumFasdInformationClass.Computer, out var _ids))
return null;
var strUrl = string.Format(constMsGraphDeviceLAPS, _ids.azureDeviceId);
var MsGraph = await GetGraphForTenantAsync(_ids.tenantId, true);
if (MsGraph == null)
return null;
var Result = await MsGraph.RequestAsync(strUrl, UseBeta: false, retryForbidden: false);
if (Result != null)
{
var _resultJson = Result.Result["credentials"] as JArray;
if (_resultJson != null)
{
try
{
var ResultJson = _resultJson.ToObject<List<JObject>>().GroupBy(obj => (string)obj["accountName"])
.Select(group => group.OrderByDescending(obj => DateTimeOffset.Parse((string)obj["backupDateTime"], CultureInfo.InvariantCulture)).First()).ToList();
foreach (JObject item in ResultJson)
{
string _base64Password = (string)item["passwordBase64"];
byte[] passwordBytes = Convert.FromBase64String(_base64Password);
string decodedPassword = Encoding.UTF8.GetString(passwordBytes);
item["passwordBase64"] = decodedPassword;
}
Collector.DoProcessUiMessage(3, $" properties of selected '{ResultJson}' with {ResultJson.Count} properties could be read.");
return ResultJson;
}
catch (Exception E)
{
cLogManager.DefaultLogger.LogException(E);
}
}
}
return null;
}
public async Task<dynamic> GetManagedApps(List<cF4sdIdentityEntry> identities, cF4sdWebRequestInfo requestInfo, CancellationToken Token)
{
var Ids = await Collector.getConntectorIds(identities, Token, requestInfo, 1);
if (!Ids.TryGetValue(enumFasdInformationClass.Computer, out var _ids))
return null;
var strUrl = string.Format(constMsGraphManagedDeviceDetectedApps, _ids.intuneId);
var MsGraph = await GetGraphForTenantAsync(_ids.tenantId, true);
if (MsGraph == null)
return null;
var Result = await MsGraph.RequestAsync(strUrl, UseBeta: true, retryForbidden: false);
if (Result != null)
{
var ResultJson = Result.Result["value"] as JArray;
if (ResultJson != null)
{
Collector.DoProcessUiMessage(3, $" properties of selected '{ResultJson}' with {ResultJson.Count} properties could be read.");
}
return ResultJson;
}
return null;
}
public async Task<dynamic> ChangeManagedStatus(List<cF4sdIdentityEntry> identities, cF4sdWebRequestInfo requestInfo, string status, CancellationToken Token)
{
if (!string.IsNullOrWhiteSpace(status))
{
var Ids = await Collector.getConntectorIds(identities, Token, requestInfo, 1);
if (!Ids.TryGetValue(enumFasdInformationClass.Computer, out var _ids))
return null;
var strUrl = string.Format(constMsGraphManagedDeviceChangeManagedStatus, _ids.intuneId, status);
var MsGraph = await GetGraphForTenantAsync(_ids.tenantId, true);
if (MsGraph == null)
return null;
var Result = await MsGraph.RequestAsync(strUrl, UseBeta: false, retryForbidden: false);
if (Result != null)
{
var ResultJson = Result.Result as JObject;
if (ResultJson != null)
{
Collector.DoProcessUiMessage(3, $" properties of selected '{ResultJson}' with {ResultJson.Count} properties could be read.");
}
return ResultJson;
}
}
return null;
}
public async Task<dynamic> GetBitlockerKeyTest(List<cF4sdIdentityEntry> identities, cF4sdWebRequestInfo requestInfo, CancellationToken Token)
{
var Ids = await Collector.getConntectorIds(identities, Token, requestInfo, 1);
if (!Ids.TryGetValue(enumFasdInformationClass.Computer, out var _ids))
return null;
// URL zum Abrufen der Recovery-Keys
var deviceId_str = "02f2fd68-a1c6-4e73-96a5-48f72792ee08";
_ids.intuneId = new Guid(deviceId_str);
var strUrl = string.Format(constMsGraphDeviceBitlockerRecoveryKey, _ids.intuneId);
var MsGraph = await GetGraphForTenantAsync(_ids.tenantId, true);
if (MsGraph == null)
return null;
// Schritt 1: Abrufen der Recovery-Key IDs
var resultRecoveryKeys = await MsGraph.RequestAsync(strUrl, UseBeta: false, retryForbidden: false);
if (resultRecoveryKeys?.Result["value"] is JArray recoveryKeyArray && recoveryKeyArray.Count > 0)
{
var results = new JArray();
foreach (var item in recoveryKeyArray)
{
var recoveryKeyId = item["id"].ToString();
// Schritt 2: Abrufen des Recovery-Key-Wertes
var strUrl2 = string.Format(constMsGraphDeviceBitlockerRecoveryKeyDetails, recoveryKeyId);
var resultKey = await MsGraph.RequestAsync(strUrl2, UseBeta: false, retryForbidden: false);
if (resultKey?.Result != null)
{
results.Add(resultKey.Result as JObject);
}
}
Collector.DoProcessUiMessage(3, $"{results.Count} Bitlocker-Key(s) erfolgreich abgerufen.");
return results;
}
Collector.DoProcessUiMessage(3, "Kein Bitlocker-Key gefunden oder Abruf fehlgeschlagen.");
return null;
}
public async Task<dynamic> GetLapsKeyTest(List<cF4sdIdentityEntry> identities, cF4sdWebRequestInfo requestInfo, CancellationToken Token)
{
var Ids = await Collector.getConntectorIds(identities, Token, requestInfo, 1);
if (!Ids.TryGetValue(enumFasdInformationClass.Computer, out var _ids))
return null;
var azureId_str = "02f2fd68-a1c6-4e73-96a5-48f72792ee08";
_ids.azureDeviceId = new Guid(azureId_str);
var strUrl = string.Format(constMsGraphDeviceLAPS, _ids.azureDeviceId);
var MsGraph = await GetGraphForTenantAsync(_ids.tenantId, true);
if (MsGraph == null)
return null;
var Result = await MsGraph.RequestAsync(strUrl, UseBeta: false, retryForbidden: false);
if (Result != null)
{
var ResultJson = Result.Result["credentials"] as JArray;
if (ResultJson != null)
{
foreach (JObject item in ResultJson)
{
string _base64Password = (string)item["passwordBase64"];
byte[] passwordBytes = Convert.FromBase64String(_base64Password);
string decodedPassword = Encoding.UTF8.GetString(passwordBytes);
item["passwordBase64"] = decodedPassword;
}
Collector.DoProcessUiMessage(3, $" properties of selected '{ResultJson}' with {ResultJson.Count} properties could be read.");
}
JObject singleCredential = ResultJson[0] as JObject;
return singleCredential;
}
return null;
}
public override async Task<bool> DoScanAsync(bool Always, bool Rescan, cF4sdWebRequestInfo requestInfo, int LogDeep, CancellationToken Token, bool EnhancedDebug = false)
{
MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); }
if (cPerformanceLogger.IsActive && requestInfo != null) { if (CM == null) CM = MethodBase.GetCurrentMethod(); cPerformanceLogger.LogPerformanceStart(LogDeep, CM, requestInfo.id, requestInfo.created); }
var _startTime = DateTime.UtcNow;
try
{
if (Collector?.InfrastructureConfig?.ActiveDirectory?.Scans?.Count != null && Collector.InfrastructureConfig.ActiveDirectory.Scans.Count == 0)
{
Collector.DoProcessUiMessage(0, "no AD scans defined, aborting AD scan.");
Collector.DoProcessUiMessage(0, "");
return false;
}
if (!Always)
{
var scanInfo = await Collector.ActiveDirectory.GetScanTimeInfoAsync(requestInfo, LogDeep + 1, Token);
if (scanInfo == null)
{
Collector.DoProcessUiMessage(0, "could not get valid scan times info for AD connector");
return false;
}
if (scanInfo.NextScan == null)
{
Collector.DoProcessUiMessage(0, "not valid next scan time info for AD connector");
return false;
}
var nextScan = (DateTime)scanInfo.NextScan;
if (!(scanInfo.LastScan is DateTime lastScan) || (lastScan >= nextScan))
{
Collector.DoProcessUiMessage(0, $"currently no scan needed for AD connector, lastScan={scanInfo.LastScan} (UTC), nextScan={scanInfo.NextScan} (UTC)");
return false;
}
}
Collector.DoProcessUiMessage(0, "");
var Scans = new List<cDataHistoryAdScan>();
var LdapProps = new Dictionary<enumFasdInformationClass, Dictionary<string, cLdapPropertyInfo>>()
{
{enumFasdInformationClass.User, GetPropertyList( enumFasdInformationClass.User) },
{enumFasdInformationClass.Computer, GetPropertyList( enumFasdInformationClass.Computer) }
};
using (var Conn = new cDbConnection(Collector.mainDbConnection))
{
if (!Conn.IsOpen)
{
LogEntry($"Could not open main sql database '{Collector.mainDbConnection.Database}', aborting scan...'", LogLevels.Error);
return false;
}
// get the last as scan infos
var ScanHistory = await GetLastAdScansAsync(Conn, requestInfo, LogDeep + 1, Token);
var AlreadyScannedNodes = new Dictionary<enumFasdInformationClass, List<string>>()
{
{ enumFasdInformationClass.Computer, new List<string>() },
{ enumFasdInformationClass.User, new List<string>() }
};
// do the configured ad scans
var ScanCount = 0;
var GeneralInfo = new cDbAdGeneralInfo();
foreach (var Scan in Collector.InfrastructureConfig.ActiveDirectory.Scans.Values)
{
if (Scan.Type != enumFasdInformationClass.User && Scan.Type != enumFasdInformationClass.Computer)
continue;
// get the infos of the last scan
if (!ScanHistory.TryGetValue(Scan.Name, out var LastScanInfo))
LastScanInfo = null;
else
{
ScanHistory.Remove(Scan.Name);
}
// now we have to do the ad scan !!!
if (ScanCount == 0)
Collector.DoProcessUiMessage(0, string.Format("doing the AD scans:"));
ScanCount++;
Collector.DoProcessUiMessage(1, string.Format("starting AD scan '{0}'...", Scan.Name));
// create a new scan info, if not exist
if (LastScanInfo == null)
{
LastScanInfo = new cDbAdScanInfo()
{
ID = -1,
Name = Scan.Name,
LastRun = new DateTime(1970, 1, 1)
};
LastScanInfo.ID = await UpdateAdScanEntryAsync(Conn, LastScanInfo, requestInfo, LogDeep + 1, Token);
}
LastScanInfo.LastRun = DateTime.UtcNow;
// get the infos about the last node scans
var NodeHistory = await GetLastAdScanNode(LastScanInfo.ID, requestInfo, LogDeep + 1, Token);
if (NodeHistory == null)
NodeHistory = new Dictionary<string, cDbAdScanNode>();
// do the defined scan nodes
foreach (var ScanNode in Scan.ScanNodes)
{
// if we could not find a history for the scan node, we create a new one
var SName = $"{Scan.Name}|{ScanNode.AdDomain.Name}|{ScanNode.Path}";
if (!NodeHistory.TryGetValue(SName, out var NodeInfo))
NodeInfo = new cDbAdScanNode()
{
ScanID = LastScanInfo.ID,
Name = SName,
Fingerprint = "",
InvocationID = "0",
LastUSN = 0
};
if (LdapProps != null)
await ProcessScanNode(Conn, LdapProps[Scan.Type], Scan, ScanNode, AlreadyScannedNodes[Scan.Type], NodeInfo, GeneralInfo, Rescan, requestInfo, LogDeep + 1, Token);
}
// and save the new scan info
LastScanInfo.ID = await UpdateAdScanEntryAsync(Conn, LastScanInfo, requestInfo, LogDeep + 1, Token);
if (LastScanInfo.ID < 0)
{
cLogManager.DefaultLogger.LogEntry(LogLevels.Error, string.Format("The ad scan db entry with ID='{0}' and Name='{1}' could not be created or updated!", LastScanInfo.ID, LastScanInfo.Name));
continue;
}
}
if (ScanCount == 0)
Collector.DoProcessUiMessage(0, "currently no AD scans needed.");
else
Collector.DoProcessUiMessage(0, "... AD scan finished.");
// update the domain list
await UpdateAdDomains(Conn, GeneralInfo.Domains, requestInfo, LogDeep + 1, Token);
// delete the remaining scan history entries, they are not needed any more
foreach (var Scan in ScanHistory.Values)
{
try
{
await RemoveAdScanEntryAsync(Conn, Scan.ID, requestInfo, LogDeep + 1, Token);
}
catch (Exception E)
{
cLogManager.DefaultLogger.LogException(E);
}
}
Collector.DoProcessUiMessage(0, "");
}
if (Collector.InfrastructureConfig.AzureTenants != null)
await DoAzureScanAsync(requestInfo, LogDeep + 1, Token);
await Collector.SetLastScanTime("AdScan-all", DateTime.UtcNow, requestInfo, LogDeep + 1, Token);
return true;
}
catch (Exception E)
{
LogException(E);
}
finally
{
if (cPerformanceLogger.IsActive && requestInfo != null) { cPerformanceLogger.LogPerformanceEnd(LogDeep, CM, requestInfo.id, requestInfo.created, _startTime); }
if (CM != null) LogMethodEnd(CM);
}
return false;
}
private async Task UpdateAdDomains(cDbConnection Conn, Dictionary<string, string> Domains, cF4sdWebRequestInfo requestInfo, int LogDeep, CancellationToken Token)
{
MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); }
if (cPerformanceLogger.IsActive && requestInfo != null) { if (CM == null) CM = MethodBase.GetCurrentMethod(); cPerformanceLogger.LogPerformanceStart(LogDeep, CM, requestInfo.id, requestInfo.created); }
var _startTime = DateTime.UtcNow;
try
{
if (!DataHistorySqlHelper.GetWellKnownSqlStatement("UpdateAdScanDomains", out var DbQuery))
return;
foreach (var Entry in Domains)
{
var Params = new Dictionary<string, object>(2) { { "domainDns", Entry.Key }, { "domainNB", Entry.Value } };
await DataHistorySqlHelper.ExecuteAsync(Conn, DbQuery, Params, requestInfo, Token);
}
}
catch (Exception E)
{
LogException(E);
}
finally
{
if (cPerformanceLogger.IsActive && requestInfo != null) { cPerformanceLogger.LogPerformanceEnd(LogDeep, CM, requestInfo.id, requestInfo.created, _startTime); }
if (CM != null) LogMethodEnd(CM);
}
}
public async Task<string> GetDomainDns(string domain, cF4sdWebRequestInfo requestInfo, int LogDeep, CancellationToken Token)
{
MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); }
if (cPerformanceLogger.IsActive && requestInfo != null) { if (CM == null) CM = MethodBase.GetCurrentMethod(); cPerformanceLogger.LogPerformanceStart(LogDeep, CM, requestInfo.id, requestInfo.created); }
var _startTime = DateTime.UtcNow;
try
{
await UpdateDomainList(requestInfo, LogDeep + 1, Token);
if (DomainsRev.TryGetValue(domain, out var retVal))
return retVal;
}
catch (Exception E)
{
LogException(E);
}
finally
{
if (cPerformanceLogger.IsActive && requestInfo != null) { cPerformanceLogger.LogPerformanceEnd(LogDeep, CM, requestInfo.id, requestInfo.created, _startTime); }
if (CM != null) LogMethodEnd(CM);
}
return null;
}
public async Task<string> GetDomainNB(string domain, cF4sdWebRequestInfo requestInfo, int LogDeep, CancellationToken Token)
{
MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); }
if (cPerformanceLogger.IsActive && requestInfo != null) { if (CM == null) CM = MethodBase.GetCurrentMethod(); cPerformanceLogger.LogPerformanceStart(LogDeep, CM, requestInfo.id, requestInfo.created); }
var _startTime = DateTime.UtcNow;
try
{
await UpdateDomainList(requestInfo, LogDeep + 1, Token);
if (Domains.TryGetValue(domain, out var retVal))
return retVal;
}
catch (Exception E)
{
LogException(E);
}
finally
{
if (cPerformanceLogger.IsActive && requestInfo != null) { cPerformanceLogger.LogPerformanceEnd(LogDeep, CM, requestInfo.id, requestInfo.created, _startTime); }
if (CM != null) LogMethodEnd(CM);
}
return null;
}
public async Task UpdateDomainList(cF4sdWebRequestInfo requestInfo, int LogDeep, CancellationToken Token)
{
MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); }
if (cPerformanceLogger.IsActive && requestInfo != null) { if (CM == null) CM = MethodBase.GetCurrentMethod(); cPerformanceLogger.LogPerformanceStart(LogDeep, CM, requestInfo.id, requestInfo.created); }
var _startTime = DateTime.UtcNow;
try
{
// check, if we have to update the cached domain list
var DoUpdate = false;
Dictionary<string, string> _domains = null;
try
{
var TS = DateTime.UtcNow;
lock (Domains)
{
if (TS > LastDomainsUpdate.AddMinutes(10) && !DomainsUpdateRunning)
{
DoUpdate = true;
DomainsUpdateRunning = true;
}
}
try
{
if (DoUpdate)
{
// read the new domain list from SQL table
if (DataHistorySqlHelper.GetWellKnownSqlStatement("GetAdScanDomains", out var DbQuery))
{
int apiError = 0;
try
{
using (var Conn = new cDbConnection(Collector.mainDbConnection))
{
using (var _reader = await DataHistorySqlHelper.GetDataReaderAsync(Conn, DbQuery, null, Token))
{
if (_reader != null)
try
{
_domains = new Dictionary<string, string>();
if (_reader.HasRows)
{
while (await _reader.ReadAsync())
{
try
{
var domainDns = _reader.GetString(0);
var domainNB = _reader.GetString(1);
if (!string.IsNullOrEmpty(domainDns) && !string.IsNullOrEmpty(domainNB))
_domains[domainDns] = domainNB;
}
catch (Exception E)
{
cLogManager.DefaultLogger.LogException(E);
}
}
}
}
catch (Exception E)
{
cLogManager.DefaultLogger.LogException(E);
}
}
}
}
catch (Exception E)
{
apiError = E.HResult;
LogException(E);
}
finally
{
if (DataHistorySqlHelper.LogSql) DataHistorySqlHelper.SaveSqlTimingEntry(DbQuery.Name, _startTime, "", apiError, requestInfo?.requestName);
}
}
}
}
catch (Exception E)
{
LogException(E);
}
}
catch (Exception E)
{
LogException(E);
}
if (DoUpdate)
// yes, we have an newer one
lock (Domains)
{
DomainsUpdateRunning = true;
if (_domains != null && _domains.Count > 0)
{
Domains.Clear();
DomainsRev.Clear();
foreach (var Entry in _domains)
{
Domains[Entry.Key] = Entry.Value;
DomainsRev[Entry.Value] = Entry.Key;
}
}
LastDomainsUpdate = DateTime.UtcNow;
}
}
catch (Exception E)
{
LogException(E);
}
finally
{
if (cPerformanceLogger.IsActive && requestInfo != null) { cPerformanceLogger.LogPerformanceEnd(LogDeep, CM, requestInfo.id, requestInfo.created, _startTime); }
if (CM != null) LogMethodEnd(CM);
}
}
private static void AddToLdapPropertyList(List<cLdapPropertyInfo> list, ref Dictionary<string, cLdapPropertyInfo> dicLdap)
{
foreach (var Entry in list)
{
if (!dicLdap.ContainsKey(Entry.Name))
dicLdap.Add(Entry.Name.ToLowerInvariant(), Entry);
}
}
private static void AddToLdapPropertyList(cDataHistoryConfigColumn column, ref Dictionary<string, cLdapPropertyInfo> dicLdap)
{
if (column.SourceType == eDataHistoryQueryType.Static)
return;
if (dicLdap.TryGetValue(column.SourceName.ToLowerInvariant(), out var _entry))
_entry.Cardinal = Math.Max(_entry.Cardinal, column.Cardinal);
else
{
var ldapType = enumLdapPropertyType.ldapString;
var prop = new cLdapPropertyInfo
{
Name = column.SourceName,
LdapType = ldapType,
Cardinal = column.Cardinal
};
dicLdap.Add(prop.Name.ToLowerInvariant(), prop);
}
}
private static void AddToLdapPropertyList(cDataHistoryConfigTable table, ref Dictionary<string, cLdapPropertyInfo> dicLdap)
{
foreach (var col in table.Columns.Values)
if (col is cDataHistoryConfigColumn col2)
AddToLdapPropertyList(col2, ref dicLdap);
}
private Dictionary<string, cLdapPropertyInfo> GetPropertyList(enumFasdInformationClass InformationClass)
{
MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); }
var dicLdap = new Dictionary<string, cLdapPropertyInfo>();
try
{
switch (InformationClass)
{
case enumFasdInformationClass.User:
AddToLdapPropertyList(UserMainProperties, ref dicLdap);
if (Collector.InfrastructureConfig.ActiveDirectory.ScanPhoneNumbers)
AddToLdapPropertyList(UserPhoneProperties, ref dicLdap);
break;
case enumFasdInformationClass.Computer:
AddToLdapPropertyList(ComputerMainProperties, ref dicLdap);
break;
}
foreach (var DataCluster in Collector.ClusterConfig.Clusters.Values)
{
if (DataCluster.Origin != enumDataHistoryOrigin.ActiveDirectory || DataCluster.InformationClass != InformationClass)
continue;
foreach (var Table in DataCluster.Tables.Values)
if (Table.Type == eDataHistoryTableType.Static || Table.Type == eDataHistoryTableType.History)
AddToLdapPropertyList(Table, ref dicLdap);
}
}
catch (Exception E)
{
LogException(E);
}
finally
{
if (CM != null) LogMethodEnd(CM);
}
return dicLdap;
}
public override bool CheckIfLateDelivery(cDataHistoryConfigTable Table)
{
if (Table?.ParentCluster?.Origin == enumDataHistoryOrigin.Intune)
return true;
return false;
}
private object ConvertToF4sdType(object objVal, enumFasdValueType ValueType)
{
try
{
if (objVal != null)
{
switch (ValueType)
{
case enumFasdValueType.INT:
return cF4SDHealthCardRawData.GetInteger(objVal);
case enumFasdValueType.GUID:
return XML.cXmlParser.GetGuidFromString(objVal.ToString());
case enumFasdValueType.BOOLEAN:
if (objVal == null) return false;
if (objVal is bool) return (bool)objVal;
if (objVal is int) return (int)objVal != 0;
if (objVal is string) return bool.TryParse((string)objVal, out bool result) ? result : ((string)objVal == "1");
return false; // Standardrückgabewert, falls keine Bedingung zutrifft
case enumFasdValueType.DATETIME:
DateTime? dt = null;
if (objVal is DateTime _dt)
dt = _dt;
else
{
if (DateTime.TryParse(objVal.ToString(), out _dt))
dt = _dt;
}
if (dt is DateTime _dt2)
{
if (_dt2 == new DateTime(1, 1, 1, 0, 0, 0))
dt = null;
}
return dt;
default:
return objVal.ToString();
}
}
}
catch (Exception E)
{
LogException(E);
}
finally
{
}
return null;
}
private static string GetHash(string s)
{
var arr = Encoding.UTF8.GetBytes(s);
byte[] hashBytes;
using (System.Security.Cryptography.MD5 md5 = System.Security.Cryptography.MD5.Create())
{
hashBytes = md5.ComputeHash(arr);
}
return Convert.ToBase64String(hashBytes);
}
private static string GetBaseDN(cDataHistoryAdScanNode Node)
{
try
{
var strDN = Node.Path;
var arrDom = Node.AdDomain.FQDN.Split('.');
foreach (var Entry in arrDom)
{
strDN += $",DC={Entry}";
}
if (strDN.StartsWith(","))
strDN = strDN.Remove(0, 1);
return strDN;
}
catch (Exception E)
{
LogException(E);
}
return null;
}
public Dictionary<string, string> GetNT4DomainNames()
{
MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); }
var RetVal = new Dictionary<string, string>();
try
{
foreach (var Domain in Collector?.InfrastructureConfig?.ActiveDirectory?.Domains?.Values)
{
var NT4 = GetNT4DomainName(Domain);
if (!string.IsNullOrEmpty(NT4))
RetVal.Add(NT4, Domain.FQDN);
}
}
catch (Exception E)
{
LogException(E);
}
finally
{
if (CM != null) LogMethodEnd(CM);
}
return RetVal;
}
private static string GetNT4DomainName(cDataHistoryAdDomain Domain)
{
MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); }
try
{
IADsNameTranslate oNameTranslate = new ActiveDs.NameTranslate();
oNameTranslate.InitEx((int)ADS_NAME_INITTYPE_ENUM.ADS_NAME_INITTYPE_DOMAIN, Domain.FQDN, Domain.Credential.User, Domain.Credential.Domain, Domain.Credential.NwCredential.Password);
string[] aSplitDN = Domain.FQDN.Split(new Char[] { '.' });
string sDistinguishedName = "";
//Convert Domain Name to Distinguished Name
foreach (string sDomainPart in aSplitDN)
{
if (sDistinguishedName != "")
sDistinguishedName += ",";
sDistinguishedName = sDistinguishedName + "DC=" + sDomainPart;
}
oNameTranslate.Set((int)ADS_NAME_TYPE_ENUM.ADS_NAME_TYPE_UNKNOWN, sDistinguishedName);
string sFriendlyName = oNameTranslate.Get((int)ADS_NAME_TYPE_ENUM.ADS_NAME_TYPE_NT4);
sFriendlyName = sFriendlyName.Replace(@"\", "");
return sFriendlyName;
}
catch (Exception E)
{
LogException(E);
}
finally
{
if (CM != null) LogMethodEnd(CM);
}
return null;
}
public override async Task<List<object>> GetIds(cF4sdConnectorIds IdEntry, enumFasdInformationClass InfoClass, cF4sdWebRequestInfo requestInfo, int LogDeep)
{
await Task.CompletedTask;
if (IdEntry.sid == null)
return null;
return new List<object>() { IdEntry.sid };
}
public override async Task<cScanTimeInfo> GetScanTimeInfoAsync(cF4sdWebRequestInfo requestInfo, int LogDeep, CancellationToken Token)
{
return await GetScanTimeInfoAsync(Collector.InfrastructureConfig.ActiveDirectory.ScanTiming, "AdScan-all", requestInfo, LogDeep, Token);
}
public override async Task<bool> ColumnUpatePostProcessingAsync(cDbConnection DbConn, cF4sdWebRequestInfo requestInfo, int LogDeep, CancellationToken Token)
{
MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); }
if (cPerformanceLogger.IsActive && requestInfo != null) { if (CM == null) CM = MethodBase.GetCurrentMethod(); cPerformanceLogger.LogPerformanceStart(LogDeep, CM, requestInfo.id, requestInfo.created); }
var _startTime = DateTime.UtcNow;
try
{
if (!DataHistorySqlHelper.GetWellKnownSqlStatement("PostColumnCreateActiveDirectory", out var Query2))
return false;
await DataHistorySqlHelper.ExecuteAsync(DbConn, Query2, null, requestInfo, Token);
return true;
}
catch (Exception E)
{
LogException(E);
}
finally
{
if (cPerformanceLogger.IsActive && requestInfo != null) { cPerformanceLogger.LogPerformanceEnd(LogDeep, CM, requestInfo.id, requestInfo.created, _startTime); }
if (CM != null) LogMethodEnd(CM);
}
return false;
}
public override async Task<bool> ValidateMainTablesAsync(cDbConnection DbConn, cF4sdWebRequestInfo requestInfo, int LogDeep, CancellationToken Token)
{
MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); }
if (cPerformanceLogger.IsActive && requestInfo != null) { if (CM == null) CM = MethodBase.GetCurrentMethod(); cPerformanceLogger.LogPerformanceStart(LogDeep, CM, requestInfo.id, requestInfo.created); }
var _startTime = DateTime.UtcNow;
try
{
// validate the ad scan tables
Collector.DoProcessUiMessage(1, $"start validating the ad scan tables...");
if (!DataHistorySqlHelper.GetWellKnownSqlStatement("Validate_AdScanTables", out var Query))
return false;
var n = await DataHistorySqlHelper.GetScalarResultAsync<int>(DbConn, Query, null, requestInfo, Token);
if (n > 0)
{
Collector.DoProcessUiMessage(1, $"the validation of the ad scan tables finished successful.");
return true;
}
// create the ad scan tables
Collector.DoProcessUiMessage(2, $"the ad scan tables are not complete, creating them...");
if (!DataHistorySqlHelper.GetWellKnownSqlStatement("Create_AdScanTables", out Query))
return false;
var n2 = await DataHistorySqlHelper.ExecuteAsync(DbConn, Query, null, requestInfo, Token);
// validate the created ad scan tables
if (!DataHistorySqlHelper.GetWellKnownSqlStatement("Validate_AdScanTables", out Query))
return false;
n = await DataHistorySqlHelper.GetScalarResultAsync<int>(DbConn, Query, null, requestInfo, Token);
if (n > 0)
{
Collector.DoProcessUiMessage(2, $"the ad scan tables were created sucessfull.");
return true;
}
Collector.DoProcessUiMessage(2, "An error occurred when creating the ad main tables.");
}
catch (Exception E)
{
Collector.DoProcessUiMessage(1, $"the validation of the ad scan tables aborted with an exceptional error.");
LogException(E);
}
finally
{
if (cPerformanceLogger.IsActive && requestInfo != null) { cPerformanceLogger.LogPerformanceEnd(LogDeep, CM, requestInfo.id, requestInfo.created, _startTime); }
if (CM != null) LogMethodEnd(CM);
}
LogEntry($"the main tables could not be validated sucessfull.", LogLevels.Fatal);
return false;
}
public void ResolveWinAuthenticationGroups()
{
MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); }
try
{
if (Collector?.InfrastructureConfig?.Authorisation?.MembershipGroups?.GroupsAd == null)
return;
LogEntry("Resolving AD groups SIDs...");
var _lstDomains = Collector.InfrastructureConfig.Authorisation.MembershipGroups.GroupsAd;
LogEntry("Resolving AD groups SIDs finished");
foreach (var _domain in _lstDomains.Values)
{
try
{
LogEntry($"Start processing domain");
LogEntry($"Processing domain {_domain?.Domain?.FQDN}");
PrincipalContext PC;
if (_domain.Domain.Credential == null)
PC = new PrincipalContext(ContextType.Domain, _domain.Domain.FQDN);
else
PC = new PrincipalContext(ContextType.Domain, _domain.Domain.FQDN, _domain.Domain.Credential.User, _domain.Domain.Credential.NwCredential.Password);
LogEntry($"Principal context successful created");
foreach (var _group in _domain.Values)
{
try
{
LogEntry($"Start processing group.");
LogEntry($"Group: {_group.Name}.");
var GI = GroupPrincipal.FindByIdentity(PC, IdentityType.SamAccountName, _group.Account);
LogEntry($"Group principal found");
LogEntry($"GI.SID: {GI.Sid.ToString()}");
if (GI != null)
if (GI.Sid != null)
{
_group.SID = GI.Sid.ToString();
LogEntry($"AD group '{_group.Account}' in domain '{_domain.Domain.FQDN}' resolved. SID = {_group.SID}");
}
if (_group.SID == null)
LogEntry($"Could not resolve AD group '{_group.Account}' in domain '{_domain.Domain.FQDN}'", LogLevels.Warning);
}
catch (Exception E)
{
LogException(E);
}
finally
{
LogEntry($"End processing group.");
}
}
}
catch (Exception E)
{
LogException(E);
}
finally
{
LogEntry($"End processing domain");
}
}
}
catch (Exception E)
{
LogException(E);
}
finally
{
if (CM != null) LogMethodEnd(CM);
}
}
static public Guid ConvertAzureSidToGuid(string sid)
{
if (!sid.StartsWith("S-1-12-1-"))
return Guid.Empty;
try
{
var objSid = GetByteArrayFromSid(sid);
if (objSid == null || objSid.Length != 28)
return Guid.Empty;
var arrB = new byte[16];
Array.Copy(objSid, 12, arrB, 0, 16);
var azureId = new Guid(arrB);
return azureId;
}
catch (Exception)
{
return Guid.Empty;
}
}
}
public class cDbAdScanInfo
{
public int ID;
public string Name;
public DateTime LastRun;
}
public class cDbAdScanNode
{
public int ScanID;
public string Name;
public string Fingerprint;
public string InvocationID;
public UInt64 LastUSN;
public string domainDNS;
public string domainNB;
}
public class cDbAdGeneralInfo
{
public Dictionary<string, string> Domains = new Dictionary<string, string>();
}
public class cLdapPropertyInfo
{
public string Name { get; set; }
public enumLdapPropertyType LdapType { get; set; }
public string DbName { get; set; }
public bool Cumulative { get; set; } = false;
public bool Reverse { get; set; } = false;
public int Cardinal = -1;
public cLdapPropertyInfo()
{
}
}
}