3841 lines
180 KiB
C#
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()
|
|
{
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
|