diff --git a/C4IT_DataHistoryProvider_Base/F4SD-Cockpit-Server.csproj b/C4IT_DataHistoryProvider_Base/F4SD-Cockpit-Server.csproj index 6fa295a..0c6f115 100644 --- a/C4IT_DataHistoryProvider_Base/F4SD-Cockpit-Server.csproj +++ b/C4IT_DataHistoryProvider_Base/F4SD-Cockpit-Server.csproj @@ -149,6 +149,9 @@ + + Common\C4IT.F4SD.Base.Ticket.cs + Common\C4IT.F4SD.GlobalConfig.cs @@ -254,9 +257,6 @@ DataHistoryCollectorActiveDirectory.cs - - DataHistoryCollectorCitrix.cs - DataHistoryCollectorClientAgent.cs @@ -275,9 +275,6 @@ DataHistoryConfigActiveDirectory.cs - - DataHistoryConfigCitrix.cs - DataHistoryConfigClientAgent.cs @@ -302,6 +299,9 @@ DataHistorySqlHelper.cs + + DataSources\IRemoteDesktopManager.cs + DataSources\ISearchResultRelationProvider.cs @@ -314,6 +314,27 @@ IisConfigHelper.cs + + Modules\Agent\AgentApiHandler.cs + + + Modules\Agent\AgentRemoteClientInfo.cs + + + Modules\Agent\AgentRemoteConnectionInfo.cs + + + Modules\Agent\AgentRemoteDesktopManager.cs + + + Modules\Agent\DTOs\AgentLogonResultDto.cs + + + Modules\Citrix\DataHistoryCollectorCitrix.cs + + + Modules\Citrix\DataHistoryConfigCitrix.cs + Services\IStagedRelationService.cs diff --git a/C4IT_DataHistoryProvider_Base/Publish/F4SD-Cockpit-Server.dll b/C4IT_DataHistoryProvider_Base/Publish/F4SD-Cockpit-Server.dll index 7948232..3578b7d 100644 Binary files a/C4IT_DataHistoryProvider_Base/Publish/F4SD-Cockpit-Server.dll and b/C4IT_DataHistoryProvider_Base/Publish/F4SD-Cockpit-Server.dll differ diff --git a/C4IT_DataHistoryProvider_Test/Config/F4SD-Global-Configuration.xml b/C4IT_DataHistoryProvider_Test/Config/F4SD-Global-Configuration.xml index d4cbfcd..abd254e 100644 --- a/C4IT_DataHistoryProvider_Test/Config/F4SD-Global-Configuration.xml +++ b/C4IT_DataHistoryProvider_Test/Config/F4SD-Global-Configuration.xml @@ -14,9 +14,13 @@ - + + + + + @@ -24,4 +28,9 @@ + + + + + diff --git a/C4IT_DataHistoryProvider_Test/Config/F4SD-Global-Configuration.xsd b/C4IT_DataHistoryProvider_Test/Config/F4SD-Global-Configuration.xsd index 03b1e75..07e37ef 100644 --- a/C4IT_DataHistoryProvider_Test/Config/F4SD-Global-Configuration.xsd +++ b/C4IT_DataHistoryProvider_Test/Config/F4SD-Global-Configuration.xsd @@ -18,11 +18,29 @@ + + + + + + + + + + - - + + + + + + + + + + @@ -76,6 +94,14 @@ + + + + + + + + @@ -131,6 +157,22 @@ + + + + + + + + + + + + + + + + @@ -145,6 +187,7 @@ + @@ -166,5 +209,29 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/C4IT_DataHistoryProvider_Test/Config/F4SD-HealthCard-Configuration.xml b/C4IT_DataHistoryProvider_Test/Config/F4SD-HealthCard-Configuration.xml index 5e3bcc1..817c258 100644 --- a/C4IT_DataHistoryProvider_Test/Config/F4SD-HealthCard-Configuration.xml +++ b/C4IT_DataHistoryProvider_Test/Config/F4SD-HealthCard-Configuration.xml @@ -252,6 +252,9 @@ IntuneDeviceId + + EmpirumDeviceId + BshRegistryKeyExists @@ -891,6 +894,21 @@ + + + Empirum Informationen + + + Empirum Id + + + Letzte Inventory + + + IsPxe + + + @@ -1372,7 +1390,7 @@ - + Session diff --git a/C4IT_DataHistoryProvider_Test/Config/F4SD-Infrastructure-Configuration.xml b/C4IT_DataHistoryProvider_Test/Config/F4SD-Infrastructure-Configuration.xml index ea81c56..3bb761d 100644 --- a/C4IT_DataHistoryProvider_Test/Config/F4SD-Infrastructure-Configuration.xml +++ b/C4IT_DataHistoryProvider_Test/Config/F4SD-Infrastructure-Configuration.xml @@ -7,7 +7,8 @@ R0ICvoG+yjJFGXGgagNQbzAwcBacJr47wRHOIwYhBl/YGDmxpoRWrI1GZ9x1C7ofSqeE1UeZNHX/oqMo/04h+jfUrqk1QHo/6e43Lns1z7UVhWrzOlPSxYwCjv7e4+6wqTWTx6UNGR5jCeD/eHAXHW24dGu7mph9xhcB3VC1kRY= ZLAAU+jFXU/+x9FVjx4EAZRnK/D+G/OpZGkn4ek56vhDUeoR5QnSAsNu/cMa1yM3Zxa3CgmSjUmpBy+X2gr1Vo8yAeLVhqI8k9hzev+/LhrinTZIqVZZ4CaA5TEiBuNAy2uB8WEkned6uh3hRyIVNavlfoO9sToZn8f+xn8NKrM= gdZccNCwzFrAdA+qGOA9I9ChD8hJQq/4Xuc5iC2i2I+Rtp6KCRrlgqPU0Wn6zwQ8SKitRLUSeNCJcspzkpewfv0Lnb2VIDMhdDeA5zyxVss0dZqDrMgYRhZcT+odtFTX8GlWUTyqBSQpUh8YQYdggvFZkE5ZOTaFU6kqUoKAgcs= - a2btKvTS6UnTiD3vTaLX4JjQe3YB8iOpgqwDD3Imy9QzfIZwOBkKuI+jZDtea9VPpFsO6P7pVPmL5y3eZQpVe9oBlw74Ve0hMvX+RrNjAO5DINbBNG8JxZ4gPIMQsUnYVVkHH3pvoiJIJ8AlctQuMEQ2v2slnOvEhTMWiJF2vzQWPgY7vCu6hp92AJvalKIOmK6qT3u7gf45L06R16hpzxD73DbmYwEnMs1ZuF9e1NrLdXRBr+/m4/K8RneZf/hLnW70JJm1IfA/PEVYUEk5HG0fWZFTnsBI3xNXuxXg4OdiR/7Bcrmpj36ATEU6XfWnJBP5qZwhvNzIdMMcDLOnk52b56Szjr5wEOSJIvG93knmZJAFPDIqnRY9BLYtZb6rZ6sCpLMD7R97HKhUy0CFgNNY43CniAtIiARpnGFpdxPISSWeVFsqrckEDeya4hUpZQRUf7T53nTnOBFMuygDgWxfJnMxEl0tg6m5kfAhkguyFDRueA/XkSs0bXQ8xuERBZpKCWA7TJZnMbljK9G22VMYPFCJbB8hHkBjW49wsM8rF0MAS2KvweJdvFjclhqRNSH6sXHKTh4r870YYhYXNnrqobikmP81ZCoq6avKH10/1UPJJAQjBT4MwM488M7TfPv4q9l0ezi7v91fnl2MORRlZljoQ57ao6flIf/KCgQhxl20PtYPz4TcRU/onZ5OKRDXEgaWpkJZkt4ZZX2NgjIvEI8wTEATY3Eu2dBOKeWKD7jwhkPJtnRka8+miNSFanhwJ78WjIx1B/s9jMJ2njCMAzsabjVDMZPwedXigz67zcSl3NnCg4ycCukPnk/Vs2r90XjYKOCuvlU6CsTCeQLLX28Y2AaABY+7vQIEV3XPZVc4kimy6EEShfNZkkLNHQjoZqVXkFp8AKxWCZLzJOFyGFTAX0UqsN9O6soJ/rzAF68ByjYT2y9iggA8GDYUAHZxe0fX/b01+8D+I14c9qPctBaEPhJ2DjYOfJhKRglP1nkIX6U/wd0A19brME7fr50k/ndYpavzGOhVFHrcnxufn6CDJ9/8XeeQ3XdqHwCnYT4G0dUBDEQXcCGWJa0KVkefl4vW25A8Uz5aa4yQoOj7wsnJdLVoijClWEj4a7AWP1AaH9l+dQels1ek7nJ5M8BgjGK5QSSbSrgdNLVRTRZ8PxsSdSjs1tXpSZB6SfSIWW0l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 @@ -81,7 +82,7 @@ - + M42 Demo Server (Imagoverum) diff --git a/C4IT_DataHistoryProvider_Test/Config/F4SD-Infrastructure-Configuration.xsd b/C4IT_DataHistoryProvider_Test/Config/F4SD-Infrastructure-Configuration.xsd index 3387100..089e8c8 100644 --- a/C4IT_DataHistoryProvider_Test/Config/F4SD-Infrastructure-Configuration.xsd +++ b/C4IT_DataHistoryProvider_Test/Config/F4SD-Infrastructure-Configuration.xsd @@ -391,12 +391,18 @@ + + + + + - + + @@ -405,14 +411,17 @@ - + - - - - - - + + + + + + + + + @@ -420,6 +429,15 @@ + + + + + + + + + diff --git a/C4IT_DataHistoryProvider_Test/Config/F4SD-QuickAction-Configuration.xml b/C4IT_DataHistoryProvider_Test/Config/F4SD-QuickAction-Configuration.xml index 787f4fb..08f025d 100644 --- a/C4IT_DataHistoryProvider_Test/Config/F4SD-QuickAction-Configuration.xml +++ b/C4IT_DataHistoryProvider_Test/Config/F4SD-QuickAction-Configuration.xml @@ -1,6 +1,10 @@ + + Remote Verbindung starten + + AD Computer Objekt bearbeiten Opens Active Directory to edit the current selected computer. @@ -933,14 +937,14 @@ - + Intune Komformitätsstatus erzwingen Enforce Intune compliance state Komformitätsstatus aktualisieren Update compliance state - + Bitlocker Schlüssel anzeigen Show Bitlocker recoverykey @@ -956,14 +960,29 @@ Laufwerkstyp - + Erstellungsdatum + - + + TAP Passwort anzeigen + Show TAP Password + TAP Passwort anzeigen + Show TAP Password + + + + Passwort + + + Startdatum + + + - + Verwaltete Anwendungen anzeigen Show managed applications @@ -996,7 +1015,7 @@ - - + Test:Bitlocker Schlüssel anzeigen Test:Show Bitlocker recoverykey @@ -1035,13 +1054,13 @@ Laufwerkstyp - + Erstellungsdatum - + Test:LAPS Passwort anzeigen Test:Show LAPS Password @@ -1057,14 +1076,14 @@ Passwort - + Sicherungsdatum - - - + Session abmelden Session logoff Session Abmelden @@ -1140,7 +1159,7 @@ - + Session verstecken Session hidden Session verstecken @@ -1151,7 +1170,7 @@ - + Nachricht senden zu Session Send Message to Session @@ -1162,5 +1181,24 @@ 5 + + + Empirum Inventur öffnen + This Quick Action requires information about the "Inventory of Device". + Um diese Quick Action verwenden zu können werden Informationen über das "Gerät" benötigt. + + https://intune.microsoft.com/#view/Microsoft_Intune_Devices/DeviceSettingsMenuBlade/~/overview/mdmDeviceId/PARAM_EmpirumDeviceId + + + + + Nachricht senden zu Session + Send Message to Session + + Nachricht senden zu Session + Send Message to Session + + + \ No newline at end of file diff --git a/C4IT_DataHistoryProvider_Test/Config/F4SD-QuickAction-Configuration.xsd b/C4IT_DataHistoryProvider_Test/Config/F4SD-QuickAction-Configuration.xsd index 7c45ca3..e0b615a 100644 --- a/C4IT_DataHistoryProvider_Test/Config/F4SD-QuickAction-Configuration.xsd +++ b/C4IT_DataHistoryProvider_Test/Config/F4SD-QuickAction-Configuration.xsd @@ -27,7 +27,7 @@ - + @@ -197,6 +197,16 @@ + + + + + + + + + + @@ -249,7 +259,7 @@ - + diff --git a/C4IT_DataHistoryProvider_Test/DataHistorySql.xml b/C4IT_DataHistoryProvider_Test/DataHistorySql.xml index e970b21..b244523 100644 --- a/C4IT_DataHistoryProvider_Test/DataHistorySql.xml +++ b/C4IT_DataHistoryProvider_Test/DataHistorySql.xml @@ -723,6 +723,22 @@ SELECT @cnt + + + DECLARE @cnt INT + SET @cnt = 0 + + IF EXISTS(SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[main-user-tokens]') AND type in (N'U')) + AND NOT EXISTS (SELECT * FROM Information_Schema.Columns WHERE Table_Name = 'main-user-tokens' AND Column_Name = 'id_remote') + BEGIN + PRINT N'Start adding column [id_remote] to table [main-user-tokens] ...'; + ALTER Table [main-user-tokens] ADD [id_remote] NVARCHAR(64) NULL; + SET @cnt = @cnt + 1; + PRINT N'... done.' + END + + SELECT @cnt + IF NOT EXISTS (SELECT 1 FROM [main-db-version] WHERE [db_version] = @version AND [db_revision] = @revision) INSERT INTO [main-db-version] ([db_version], [db_revision]) VALUES (@version,@revision); @@ -931,10 +947,24 @@ UPDATE [main-computer] SET [id_intune]=@id WHERE [deviceId_azure]=@deviceId - + UPDATE [main-computer] SET [id_intune]=@id, [deviceid_azure]=@deviceid, tenant_azure = @tenant WHERE upn_internal = @upn_internal and [domainNB] is null - + + DECLARE @ID UNIQUEIDENTIFIER + SET @ID = (SELECT id FROM [main-user] WHERE [sid_bin] = @SID) + IF (@ID is null) SET @ID = (SELECT id FROM [main-user] WHERE [upn_external] = @UPN) + SELECT @ID + + + SELECT [id],[origin],[instance],[id_foreign] FROM [main-user-foreign-ids] WHERE [ID] = @ID + + + UPDATE [main-user-foreign-ids] SET [id] = @ID WHERE [origin] = @origin and [instance] = @instance and [id_foreign] = @id_foreign + IF @@ROWCOUNT = 0 + INSERT INTO [main-user-foreign-ids] ([id], [origin], [instance], [id_foreign]) VALUES (@id, @origin, @instance, @id_foreign) + + UPDATE [main-user] SET [id_citrix]=@id WHERE [sid_bin]=@sid_bin @@ -2526,14 +2556,14 @@ SELECT [Key],[Value] FROM [main-config] - SELECT [id],[type],[name],[name2],[secret],[validUntil],[renewUntil] FROM [main-user-tokens] + SELECT [id],[type],[name],[name2],[secret],[validUntil],[renewUntil],[id_remote] FROM [main-user-tokens] DELETE FROM [main-user-tokens] WHERE [validUntil] < @ValidUntil - UPDATE [main-user-tokens] SET [name]=@Name, [name2]=@Name2, [secret]=@Secret, [validUntil]=@ValidUntil, [renewUntil]=@RenewUntil WHERE [id]=@Id AND [type]=@Type - IF @@ROWCOUNT = 0 INSERT INTO [main-user-tokens] ([id], [type], [name], [name2], [secret], [validUntil], [renewUntil]) VALUES (@Id, @Type, @Name, @Name2, @Secret, @ValidUntil, @RenewUntil) + UPDATE [main-user-tokens] SET [name]=@Name, [name2]=@Name2, [secret]=@Secret, [validUntil]=@ValidUntil, [renewUntil]=@RenewUntil, [id_remote]=@IdRemote WHERE [id]=@Id AND [type]=@Type + IF @@ROWCOUNT = 0 INSERT INTO [main-user-tokens] ([id], [type], [name], [name2], [secret], [validUntil], [renewUntil], [id_remote]) VALUES (@Id, @Type, @Name, @Name2, @Secret, @ValidUntil, @RenewUntil, @IdRemote) SELECT @@ROWCOUNT diff --git a/C4IT_DataHistoryProvider_Test/License/F4SD_License.xml b/C4IT_DataHistoryProvider_Test/License/F4SD_License.xml index 750e156..5da51c1 100644 --- a/C4IT_DataHistoryProvider_Test/License/F4SD_License.xml +++ b/C4IT_DataHistoryProvider_Test/License/F4SD_License.xml @@ -1,12 +1,13 @@ - + - - - - - - - - - + + + + + + + + + + \ No newline at end of file diff --git a/C4IT_DataHistoryProvider_Test/ctrlCitrix.cs b/C4IT_DataHistoryProvider_Test/ctrlCitrix.cs index cc407f1..75838a3 100644 --- a/C4IT_DataHistoryProvider_Test/ctrlCitrix.cs +++ b/C4IT_DataHistoryProvider_Test/ctrlCitrix.cs @@ -1,19 +1,15 @@ using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Drawing; -using System.Data; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using System.Windows.Forms; -using C4IT.DataHistoryProvider; -using System.Reflection; -using C4IT.Logging; +using System.Threading; +using System.Reflection; + +using C4IT.Logging; +using C4IT.DataHistoryProvider; +using C4IT.DataHistoryProvider.Base.Modules.Citrix; using static C4IT.Logging.cLogManager; -using System.Threading; + namespace C4IT_DataHistoryProvider_Test { public partial class ctrlCitrix: UserControl @@ -39,12 +35,22 @@ namespace C4IT_DataHistoryProvider_Test Program.Collector.CleanupUiMessageHandler(); Program.Collector.ProcessUiMessage += ctrlProtocolBox1.ProcessMessage; var _requestInfo = new cF4sdWebRequestInfo("btnConnectCitrix_Click", null); - var tenants = Program.Collector.InfrastructureConfig.CitrixTenants?.Values; + var systems = Program.Collector.InfrastructureConfig.Citrix.Systems; + ctrlProtocolBox1.ProcessMessage($"Validating Citrix connections..."); - foreach (var Tenant in tenants) + await Program.Collector.CitrixCollector.ValidateConnectionsAsync(CancellationToken.None); + + ctrlProtocolBox1.ProcessMessage($"Validating Citrix connections finished."); + + return; + + foreach (var _system in systems) { - var result = await Program.Collector.CitrixCollector.GetCitrixCommunicationForTenantAsync(Tenant.TenantID, false); + if (!(_system is cDataHistoryCitrixCloudTenant Tenant)) + continue; + + var result = await Program.Collector.CitrixCollector.GetCitrixCommunicationForSystemAsync(Tenant.TenantID, false); if (result != null) { ctrlProtocolBox1.ProcessMessage($"Citrix connection for tenant {Tenant.TenantID} successful"); @@ -84,13 +90,17 @@ namespace C4IT_DataHistoryProvider_Test Program.Collector.CleanupUiMessageHandler(); Program.Collector.ProcessUiMessage += ctrlProtocolBox1.ProcessMessage; var _requestInfo = new cF4sdWebRequestInfo("btnConnectCitrix_Click", null); - var tenants = Program.Collector.InfrastructureConfig.CitrixTenants?.Values; + + var systems = Program.Collector.InfrastructureConfig.Citrix.Systems; - foreach (var Tenant in tenants) + foreach (var _system in systems) { + if (!(_system is cDataHistoryCitrixCloudTenant Tenant)) + continue; + var result = await Program.Collector.CitrixCollector.GetCitrixUsersAsync(); - if (result != null) + if (result) { ctrlProtocolBox1.ProcessMessage($"Citrix connection for tenant {Tenant.TenantID} successful"); } @@ -129,13 +139,16 @@ namespace C4IT_DataHistoryProvider_Test Program.Collector.CleanupUiMessageHandler(); Program.Collector.ProcessUiMessage += ctrlProtocolBox1.ProcessMessage; var _requestInfo = new cF4sdWebRequestInfo("btnGetSessions_Click", null); - var tenants = Program.Collector.InfrastructureConfig.CitrixTenants?.Values; + var systems = Program.Collector.InfrastructureConfig.Citrix.Systems; - foreach (var Tenant in tenants) + foreach (var _system in systems) { + if (!(_system is cDataHistoryCitrixCloudTenant Tenant)) + continue; + var result = await Program.Collector.CitrixCollector.GetCitrixSessionsAsync(); - if (result != null) + if (result) { ctrlProtocolBox1.ProcessMessage($"Citrix connection for tenant {Tenant.TenantID} successful"); } @@ -170,8 +183,6 @@ namespace C4IT_DataHistoryProvider_Test { Cursor = Cursors.WaitCursor; - - if (Program.Collector == null) return; @@ -179,23 +190,16 @@ namespace C4IT_DataHistoryProvider_Test Program.Collector.CleanupUiMessageHandler(); Program.Collector.ProcessUiMessage += ctrlProtocolBox1.ProcessMessage; var _requestInfo = new cF4sdWebRequestInfo("btnScanUsers_Click", null); - var tenants = Program.Collector.InfrastructureConfig.CitrixTenants?.Values; + ctrlProtocolBox1.ProcessMessage($"Starting Citrix user scans..."); - foreach (var Tenant in tenants) - { - var result = await Program.Collector.CitrixCollector.DoScanAsync(true, true, _requestInfo, 1, CancellationToken.None); - if (result) - { - ctrlProtocolBox1.ProcessMessage($"Citrix connection for tenant {Tenant.TenantID} successful"); - } - else - { - ctrlProtocolBox1.ProcessMessage($"Citrix connection for tenant {Tenant.TenantID} failed"); - - } - } + Program.Collector.CitrixCollector.DoScanSequential = true; + var result = await Program.Collector.CitrixCollector.DoScanAsync(true, true, _requestInfo, 0, CancellationToken.None); + if (result) + ctrlProtocolBox1.ProcessMessage($"All Citrix user scans finished successfully."); + else + ctrlProtocolBox1.ProcessMessage($"At least one Citrix user scan finished with errors."); } catch (Exception E) { diff --git a/C4IT_DataHistoryProvider_Test/ctrlMainFunction.cs b/C4IT_DataHistoryProvider_Test/ctrlMainFunction.cs index 2a49cec..8edbcaf 100644 --- a/C4IT_DataHistoryProvider_Test/ctrlMainFunction.cs +++ b/C4IT_DataHistoryProvider_Test/ctrlMainFunction.cs @@ -231,7 +231,7 @@ namespace C4IT_DataHistoryProvider_Test if (Program.Collector.InfrastructureConfig.F4SDAnalyticsDB != null) Program.mainForm.tabControlMain.TabPages.Add(Program.mainForm.tabPageF4SDAnalytics); - if (Program.Collector.CitrixCollector != null && Program.Collector.InfrastructureConfig.CitrixTenants?.Values != null) + if (Program.Collector.CitrixCollector != null && Program.Collector.InfrastructureConfig.Citrix.Systems != null) Program.mainForm.tabControlMain.TabPages.Add(Program.mainForm.tabPageCitrix); if (Program.Collector.NxqlCollector != null && !Program.mainForm.tabControlMain.TabPages.Contains(Program.mainForm.tabPageNexthink)) diff --git a/CommonAssemblyInfo.cs b/CommonAssemblyInfo.cs index e0f59d2..b2dc583 100644 --- a/CommonAssemblyInfo.cs +++ b/CommonAssemblyInfo.cs @@ -15,8 +15,8 @@ using System.Runtime.InteropServices; [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] -[assembly: AssemblyInformationalVersion("2.6.1.1")] -[assembly: AssemblyVersion("2.6.*")] +[assembly: AssemblyInformationalVersion("2.7.1.0")] +[assembly: AssemblyVersion("2.7.*")] [assembly: AssemblyMinClientVersion("2.6.0.0")] diff --git a/F4SD-Cockpit-ConfigLoader/F4SD-Cockpit-ConfigLoader.csproj b/F4SD-Cockpit-ConfigLoader/F4SD-Cockpit-ConfigLoader.csproj index 8a4e9e6..6d27e8d 100644 --- a/F4SD-Cockpit-ConfigLoader/F4SD-Cockpit-ConfigLoader.csproj +++ b/F4SD-Cockpit-ConfigLoader/F4SD-Cockpit-ConfigLoader.csproj @@ -111,6 +111,9 @@ MSBuild:Compile Designer + + Common\C4IT.F4SD.Base.Ticket.cs + Common\C4IT.F4SD.GlobalConfig.cs diff --git a/F4SD-Cockpit-ServerCore/Common/C4IT.FASD.Communication.Citrix.cs b/F4SD-Cockpit-ServerCore/Common/C4IT.FASD.Communication.Citrix.cs index 1d659e6..2c472f1 100644 --- a/F4SD-Cockpit-ServerCore/Common/C4IT.FASD.Communication.Citrix.cs +++ b/F4SD-Cockpit-ServerCore/Common/C4IT.FASD.Communication.Citrix.cs @@ -1,66 +1,579 @@ using System; using System.Collections.Generic; +using System.ComponentModel.Design; using System.Linq; using System.Net; +using System.Net.Http; using System.Reflection; using System.Runtime.CompilerServices; using System.Text; +using System.Threading; using System.Threading.Tasks; + using C4IT.HTTP; -using C4IT.MsGraph; -using Newtonsoft.Json.Linq; using C4IT.Logging; + +using Newtonsoft.Json.Linq; + using static C4IT.Logging.cLogManager; -using C4IT.DataHistoryProvider; namespace C4IT.FASD.Communication.Citrix { - public class cCitrixCommunication : cHttpApiBase + public class cCitrixApiResultList : List + { + public string NextResultUrl { get; private set; } = null; + public bool retryForbidden { get; private set; } = false; + + public cCitrixApiResultList(bool retryForbidden) + { + this.retryForbidden = retryForbidden; + } + + public bool hasRemaining + { + get + { + return !string.IsNullOrEmpty(NextResultUrl); + } + } + + public void AddResult(dynamic Result) + { + try + { + NextResultUrl = null; + + if (Result.TryGetValue("@odata.nextLink", out JToken JT)) + { + var JO = (JValue)JT; + + NextResultUrl = Result["@odata.nextLink"]; + } + + if (Result.TryGetValue("value", out JToken JT2)) + { + foreach (dynamic Entry in Result.value) + try + { + var val = new cCitrixApiResultBase(Entry); + this.Add(val); + } + catch (Exception E) + { + cLogManager.DefaultLogger.LogException(E); + } + } + } + catch (Exception E) + { + cLogManager.DefaultLogger.LogException(E); + } + } + + } + + public class cCitrixApiResultBase + { + public string ID { get; private set; } = null; + public string ODataId { get; private set; } = null; + public string DisplayName { get; private set; } = null; + + public string Context { get; private set; } = null; + + public dynamic Result { get; private set; } = null; + + public cCitrixApiResultBase(dynamic Result, string namePropery = null) + { + this.Result = Result; + + try { ID = Result.Id; } catch { } + ; + try + { + if (namePropery == null) + namePropery = "displayName"; + if (Result.TryGetValue(namePropery, out JToken JT1)) + { + var _ty = JT1.GetType().ToString(); + if (JT1 is JValue _jVal) + DisplayName = _jVal.Value?.ToString(); + } + else if (Result.TryGetValue("name", out JToken JT2)) + DisplayName = Result.name; + } + catch { } + try { Context = Result["@odata.context"]; } catch { } + ODataId = GetStringFromDynamic(Result, "@odata.id"); + if (string.IsNullOrEmpty(ODataId)) + ODataId = @"https://api.cloud.com/" + ID; + } + + public cCitrixApiResultBase(cCitrixApiResultBase Result) + { + if (Result == null) + return; + + this.Result = Result.Result; + ID = Result.ID; + ODataId = Result.ODataId; + DisplayName = Result.DisplayName; + Context = Result.Context; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string GetStringFromDynamic(dynamic O, string ProperyName) + { + try + { + return (string)O[ProperyName]; + } + catch { } + return null; + } + } + + public class cCitrixCustomerInfo + { + public string Id { get; set; } + public string Name { get; set; } = string.Empty; + + public cCitrixSiteInfo PrimarySite { get; set; } + + public Dictionary Sites { get; private set; } = new Dictionary(); + } + + public class cCitrixSiteInfo + { + public Guid Id { get; set; } + public string Name { get; set; } = string.Empty; + + public bool? IsValid { get; set; } = null; + public bool IsPrimarySite { get; set; } = false; + } + + public class cCitrixCommunicationSystem + { + public const string constCitrixCloudMonitorUrl = "monitorodata/"; + + public const string constCitrixOnPremMonitorUrl = "Citrix/Monitor/OData/v4/Data/"; + + public cCitrixCommunicationMonitor Monitor { get; private set; } + public cCitrixCommunicationCvad CVAD { get; private set; } + + public bool IsCloud { get; private set; } + + public string Name { get; private set; } = string.Empty; + + public cCitrixCommunicationSystem(cCitrixCommunicationCloudBase CitrixCloud, string Name) + { + IsCloud = true; + this.Name = Name; + this.Monitor = new cCitrixCommunicationMonitor(CitrixCloud); + this.CVAD = new cCitrixCommunicationCvad(CitrixCloud); + } + + public cCitrixCommunicationSystem(cCitrixCommunicationOnPremCvad Cvad, cCitrixCommunicationOnPremMonitor Monitor, string Name) + { + IsCloud = false; + this.Name = Name; + this.Monitor = new cCitrixCommunicationMonitor(Monitor); + this.CVAD = new cCitrixCommunicationCvad(Cvad); + } + + public async Task ScanCustomerInfosAsync(CancellationToken token) + { + MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } + + try + { + var _retVal = new cCitrixCustomerInfo(); + var CustomerInfo = await CVAD.GetCustomerInfoAsync(token); + if (token.IsCancellationRequested) + return null; + + if (CustomerInfo == null) + return _retVal; + + _retVal.Id = CustomerInfo.Id; + _retVal.Name = CustomerInfo.Name; + + cCitrixSiteInfo PrimarySite = null; + int maxConsitency = 0; + int countC0 = 0; + int countC1 = 0; + int countC2 = 0; + foreach (var site in CustomerInfo.Sites.Values) + { + var _newSite = new cCitrixSiteInfo() + { + Name = site.Name, + Id = site.Id, + IsValid = false + }; + + // validate the site consitency + var _resultConsitency = await CheckSiteConsitencyAsync(site.Id.ToString("D"), token); + if (token.IsCancellationRequested) + return null; + switch (_resultConsitency) + { + case 0: _newSite.IsValid = null; countC0++; break; + case 1: _newSite.IsValid = true; countC1++; break; + case 2: _newSite.IsValid = true; countC2++; break; + } + + if (maxConsitency < _resultConsitency) + PrimarySite = _newSite; + + maxConsitency = Math.Max(maxConsitency, _resultConsitency); + + _retVal.Sites.Add(_newSite.Id, _newSite); + } + + if (PrimarySite != null) + { + if (countC2 == 1) + PrimarySite.IsPrimarySite = true; + else if (countC1 == 1 && countC2 == 0) + PrimarySite.IsPrimarySite = true; + } + + if (PrimarySite?.IsPrimarySite == true) + _retVal.PrimarySite = PrimarySite; + + return _retVal; + } + catch (Exception E) + { + LogException(E); + } + finally + { + if (CM != null) LogMethodEnd(CM); + } + + + return null; + } + + public async Task CheckSiteConsitencyAsync(string SiteId, CancellationToken token) + { + // check if the machines queried from the monitor component could be found in the cvad component + // return values: + // 2: all machines could be found + // 1: al least one machine could be found + // 0: no machines in monitor component + // -1: none of the machines could be found + // -2: internal error occured + + MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } + + var _lastSiteId = CVAD.CitrixInfo.SiteId; + try + { + CVAD.CitrixInfo.SiteId = SiteId; + + // get the machine list from cvad + var _machinesCvad = await CVAD.GetMachineNamesAsync(token); + if (token.IsCancellationRequested) + return -2; + + // get the machine list from monitor + var _machinesMonitor = await Monitor.GetMachineNamesAsync(token); + if (token.IsCancellationRequested) + return -2; + + if (_machinesMonitor == null || _machinesMonitor.Count == 0) + return 0; + + var atLeastOne = false; + var all = true; + foreach ( var machineName in _machinesMonitor ) + { + if (_machinesCvad.Contains(machineName)) + atLeastOne = true; + else + all = false; + } + + if (atLeastOne && all) + return 2; + if (atLeastOne) + return 1; + + return -1; + } + catch (Exception E) + { + LogException(E); + } + finally + { + CVAD.CitrixInfo.SiteId = _lastSiteId; + if (CM != null) LogMethodEnd(CM); + } + + return -2; + } + } + + public interface iCitrixCvadInfo + { + string CustomerId { get; } + + string SiteId { get; set; } + + } + + public class cCitrixCommunicationCloudBase : cHttpApiBase, iCitrixCvadInfo { public const int constMaxPageSize = 1000; - public const string constCitrixBaseUrl = "https://api.cloud.com/{Request}"; - public const string constCitrixUserList = "monitorodata/Users?$top={top}&$skip={skip}&$select=Id,Sid,Upn,Domain&$filter=Domain ne ''"; - public const string constCitrixUserSessions = "monitorodata/Sessions?$filter=UserId eq {UserId} and (EndDate eq null or StartDate ge {MinStartDate})&$expand=Machine($select=Id,DnsName,Name)&$select=SessionKey,StartDate,EndDate,ConnectionState"; - public cCitrixCommunication() : base("Citrix ID tenant", "https://api.cloud.com/cctrustoauth2/{Tenant}/tokens/clients", "Citrix Comunication") + private string InstanceId = null; + + public cCitrixCommunicationCloudBase(string baseUrlCloud) : base("Citrix customer tenant", $"{baseUrlCloud}/cctrustoauth2/{{Tenant}}/tokens/clients", "Citrix API", baseUrlCloud) { - KnownAutoRetryErrors.Add(HttpStatusCode.Forbidden); - autoRetryCount = 2; - autoRetryDelay = 100; + MaxConcurrentRequests = 1; + KnownAutoRetryErrors.Add((HttpStatusCode)429); + autoRetryCount = 50; + autoRetryDelay = 200; + autoRetryOnKnownErrors = true; } private protected override List> GetRequestHeaders(cOAuthLogonInfo privLogonInfo) { - + return new List>() { new KeyValuePair("Authorization", $"CwsAuth Bearer={AccessToken}"), new KeyValuePair("Citrix-CustomerId", privLogonInfo.Tenant), - new KeyValuePair("Citrix-InstanceId", privLogonInfo.InstanceId) + new KeyValuePair("Citrix-InstanceId", InstanceId) }; } - public async Task RequestAsync(string Request, eHttpMethod httpMethod = eHttpMethod.get, object JsonData = null, bool retryForbidden = false, string nameProperty = null, bool noAutoRelogon = false) + + public string CustomerId { get { + return this.privLogonInfo?.Tenant; + } } + + public string SiteId + { + get { return InstanceId; } + set { InstanceId = value; } + } + } + + public class cCitrixCommunicationOnPremCvad : cHttpApiBase, iCitrixCvadInfo + { + private const string constLogonUrl = "manage/Tokens"; + private string privCustomerId = null; + private string InstanceId = null; + + public cCitrixCommunicationOnPremCvad(string baseUrl) : base("Citrix on-prem system", baseUrl, "Citrix API", baseUrl + "/cvad/") + { + // MaxConcurrentRequests = 1; + //KnownAutoRetryErrors.Add((HttpStatusCode)429); + //autoRetryCount = 50; + //autoRetryDelay = 200; + //autoRetryOnKnownErrors = true; + } + + public string CustomerId { get { return privCustomerId; } } + + public string SiteId + { + get { return InstanceId; } + set { InstanceId = value; } + } + + private protected override async Task privLogonWithCredentialsAsync(NetworkCredential credentials) + { + MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } + + try + { + var _usr = credentials.UserName; + if (!string.IsNullOrEmpty(credentials.Domain)) + _usr = $"{credentials.Domain}\\{_usr}"; + var _auth = Convert.ToBase64String(Encoding.UTF8.GetBytes($"{_usr}:{credentials.Password}")); + using (var httpClient = new HttpClient()) + { + httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", _auth); + httpClient.BaseAddress = new Uri(RequestBaseUrl); + var _result = await httpClient.PostAsync(constLogonUrl, null); + if (_result != null) + { + var content = await _result.Content.ReadAsStringAsync(); + if (_result.IsSuccessStatusCode) + { + var JO = JObject.Parse(content); + var token = JO["Token"]?.ToString(); + var CustomerId = JO["CustomerId"]?.ToString(); + var strExpires = JO["ExpiresAt"]?.ToString(); + if (string.IsNullOrEmpty(token) || string.IsNullOrEmpty(CustomerId) || string.IsNullOrEmpty(strExpires)) + { + LastErrorMessage = $"CVAD Logon failed because no valid token information could be found in the logon result."; + return new cCredentialLoginInfo(); + } + + + if (!DateTime.TryParse(strExpires, out var expires)) + { + LastErrorMessage = $"CVAD Logon failed because no valid expiration information could be found in the logon result."; + return new cCredentialLoginInfo(); + } + + expires = DateTime.SpecifyKind(expires, DateTimeKind.Utc); + var _now = DateTime.UtcNow; + + var _expieresInteral = expires - _now; + var _delta = _expieresInteral.TotalSeconds * RelogonFactor; + var expires2 = _now + TimeSpan.FromSeconds(_delta); + + this.privCustomerId = CustomerId; + + return new cCredentialLoginInfo() { IsValid = true, Token = token, TokenExpiresIn = expires2 }; + } + else + { + LastErrorMessage = $"CVAD Logon failed with status code {(int)_result.StatusCode}: {_result.ReasonPhrase}"; + } + } + else + LastErrorMessage = $"CVAD Logon failed due to unknown reason."; + } + + } + catch (Exception E) + { + LastErrorMessage = $"CVAD Logon failed with exception: {E.Message}"; + LogException(E); + } + finally + { + if (CM != null) LogMethodEnd(CM); + } + + + return new cCredentialLoginInfo(); + } + + private protected override List> GetRequestHeaders(cOAuthLogonInfo privLogonInfo) + { + var _retVal = new List>() + { + new KeyValuePair("Authorization", $"CwsAuth Bearer={AccessToken}") + }; + + if (privCustomerId != null) + _retVal.Add(new KeyValuePair("Citrix-CustomerId", privCustomerId)); + + if (InstanceId != null) + _retVal.Add(new KeyValuePair("Citrix-InstanceId", InstanceId)); + + return _retVal; + } + + } + + public class cCitrixCommunicationOnPremMonitor : cHttpApiBase + { + private const string constLogonUrl = "Users?$top=1&$select=Id"; + + public cCitrixCommunicationOnPremMonitor(string baseUrl) : base("Citrix on-prem system", baseUrl, "Citrix API", baseUrl + string.Format("/Citrix/Monitor/OData/v4/Data/")) + { + } + + private protected override async Task privLogonWithCredentialsAsync(NetworkCredential credentials) + { + MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } + + try + { + var _auth = Convert.ToBase64String(Encoding.UTF8.GetBytes($"{credentials.Domain}\\{credentials.UserName}:{credentials.Password}")); + + using (var _handler = new HttpClientHandler()) + { + _handler.Credentials = credentials; + _handler.PreAuthenticate = true; + using (var httpClient = new HttpClient(_handler)) + { + httpClient.BaseAddress = new Uri(RequestBaseUrl); + httpClient.DefaultRequestHeaders.Add("Accept", "application/json"); + var _result = await httpClient.GetAsync(constLogonUrl); + if (_result != null) + { + var content = await _result.Content.ReadAsStringAsync(); + if (_result.IsSuccessStatusCode) + return new cCredentialLoginInfo() { IsValid = true, Token = null, TokenExpiresIn = DateTime.MaxValue, useAlwaysCredentials = true }; + else + LastErrorMessage = $"Monitor Logon failed with status code {(int)_result.StatusCode}: {_result.ReasonPhrase}"; + } + else + LastErrorMessage = $"Monitor Logon failed due to unknown reason."; + + } + } + } + catch (Exception E) + { + LastErrorMessage = $"Monitor Logon failed with exception: {E.Message}"; + LogException(E); + } + finally + { + if (CM != null) LogMethodEnd(CM); + } + + + return new cCredentialLoginInfo(); + } + + private protected override List> GetRequestHeaders(cOAuthLogonInfo privLogonInfo) + { + var _retVal = new List>(); + { + new KeyValuePair("Accept", "application/json"); + } + ; + + return _retVal; + } + } + + public class cCitrixCommunicationBase + { + private cHttpApiBase HttpApi; + + public Uri baseUri { get; private set; } + + public string LastErrorMessage { get { return HttpApi.LastErrorMessage; } } + + public cCitrixCommunicationBase(cHttpApiBase HttpApi, string baseUrl) + { + this.HttpApi = HttpApi; + this.baseUri = new Uri(new Uri(HttpApi.RequestBaseUrl), baseUrl); + } + + public async Task RequestAsync(string Request, eHttpMethod httpMethod, object JsonData, string nameProperty = null, bool retryForbidden = false, bool noAutoRelogon = false, CancellationToken token = default) { MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } try { + var uriRequest = new Uri(baseUri, Request); - var strUrl = constCitrixBaseUrl; - strUrl = strUrl.Replace("{Request}", Request); - - var data = await privRequestAsync(strUrl, httpMethod, JsonData, retryForbidden, noAutoRelogon: noAutoRelogon); + var data = await HttpApi.privRequestAsync(uriRequest.OriginalString, httpMethod, JsonData, retryForbidden, noAutoRelogon: noAutoRelogon, token: token); if (data == null) return null; if (data is bool isValid) { if (isValid) - return new cCitrixResultBase(null); + return new cCitrixApiResultBase(null); else return null; } - var RetVal = new cCitrixResultBase(data, nameProperty); + var RetVal = new cCitrixApiResultBase(data, nameProperty); return RetVal; } catch (Exception E) @@ -74,19 +587,15 @@ namespace C4IT.FASD.Communication.Citrix return null; } - public async Task RequestListAsync(string Request, bool loadPaged = false, bool retryForbidden = false) + public async Task RequestListAsync(string Request, bool loadPaged, bool retryForbidden, CancellationToken token = default) { try { - // get the request url - - var strUrl = constCitrixBaseUrl; - strUrl = strUrl.Replace("{Request}", Request); - - var res = await privRequestAsync(strUrl, autoRetry: retryForbidden); + var uriRequest = new Uri(baseUri, Request); + var res = await HttpApi.privRequestAsync(uriRequest.OriginalString, autoRetry: retryForbidden, token: token); if (res != null) { - var RetVal = new cCitrixResultList(retryForbidden); + var RetVal = new cCitrixApiResultList(retryForbidden); RetVal.AddResult(res); if (!RetVal.hasRemaining || loadPaged) return RetVal; @@ -105,11 +614,11 @@ namespace C4IT.FASD.Communication.Citrix return null; } - public async Task RequestNextAsync(cCitrixResultList List) + public async Task RequestNextAsync(cCitrixApiResultList List, CancellationToken token = default) { try { - var res = await privRequestAsync(List.NextResultUrl, autoRetry: List.retryForbidden); + var res = await HttpApi.privRequestAsync(List.NextResultUrl, autoRetry: List.retryForbidden, token: token); if (res != null) { List.AddResult(res); @@ -124,117 +633,390 @@ namespace C4IT.FASD.Communication.Citrix return false; } - public class cCitrixResultList : List + } + + public class cCitrixCommunicationCvad + { + public const string constCitrixCvadUrl = "cvad/"; + + public const string constCitrixApiMe = "manage/Me"; + public const string constCitrixApiMachineNames = "manage/Machines?fields=DnsName"; + public const string constCitrixApiSessions = "manage/Sessions?fields=Id,Uid,State,StartTime"; + + public cCitrixCommunicationBase Request { get; private set; } + + public iCitrixCvadInfo CitrixInfo { get; private set; } + + public cCitrixCommunicationCvad(cCitrixCommunicationCloudBase CitrixCloud) { - public string NextResultUrl { get; private set; } = null; - public bool retryForbidden { get; private set; } = false; + this.Request = new cCitrixCommunicationBase(CitrixCloud, constCitrixCvadUrl); + this.CitrixInfo = CitrixCloud as iCitrixCvadInfo; + } - public cCitrixResultList(bool retryForbidden) + public cCitrixCommunicationCvad(cCitrixCommunicationOnPremCvad OnPremCvad) + { + this.Request = new cCitrixCommunicationBase(OnPremCvad, null); + this.CitrixInfo = OnPremCvad as iCitrixCvadInfo; + } + + + public string GetServer() + { + return Request?.baseUri?.Host; + } + + public async Task GetCustomerInfoAsync(CancellationToken Token) + { + MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } + + try { - this.retryForbidden = retryForbidden; - } + var _resultMe = await Request.RequestAsync(constCitrixApiMe, eHttpMethod.get, null); - public bool hasRemaining - { - get - { - return !string.IsNullOrEmpty(NextResultUrl); - } - } + if (Token.IsCancellationRequested) + return null; - public void AddResult(dynamic Result) - { - try - { - NextResultUrl = null; + if (_resultMe?.Result == null) + return null; - if (Result.TryGetValue("@odata.nextLink", out JToken JT)) + var _obj = _resultMe.Result.Customers; + var _customers = _obj as IEnumerable; + + if (_customers == null) + return null; + + cCitrixCustomerInfo _retVal = null; + + foreach (var _customer in _customers) { + var id = _customer.Id; + if (id != CitrixInfo?.CustomerId) + continue; + + _retVal = new cCitrixCustomerInfo() { - var JO = (JValue)JT; + Id = _customer.Id, + Name = _customer?.Name + }; - NextResultUrl = Result["@odata.nextLink"]; - } - - if (Result.TryGetValue("value", out JToken JT2)) - { - foreach (dynamic Entry in Result.value) + _obj = _customer.Sites; + var _sites = _obj as IEnumerable; + if (_sites != null) + foreach (var _site in _sites) + { try { - var val = new cCitrixResultBase(Entry); - this.Add(val); + if (Guid.TryParse(_site.Id.ToString(), out Guid _id)) + { + var siteInfo = new cCitrixSiteInfo() + { + Id = _id, + Name = _site.Name + }; + _retVal.Sites[_id] = siteInfo; + } } catch (Exception E) { cLogManager.DefaultLogger.LogException(E); } + } + + } + + return _retVal; + } + catch (Exception E) + { + LogException(E); + } + finally + { + if (CM != null) LogMethodEnd(CM); + } + + return null; + } + + public async Task> GetMachineNamesAsync(CancellationToken token) + { + MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } + + try + { + var _result = await Request.RequestAsync(constCitrixApiMachineNames, eHttpMethod.get, null, token: token); + + if (_result?.Result == null) + return null; + + var _items = _result.Result?.Items as IEnumerable; + + if (_items == null) + return null; + + var _retVal = new List(); + foreach ( var item in _items ) + { + try + { + var _dnsName = item.DnsName?.ToString()?.ToLowerInvariant(); + if (!string.IsNullOrEmpty( _dnsName )) + _retVal.Add( _dnsName ); + } + catch (Exception E) + { + LogException(E); + } + } + + return _retVal; + } + catch (Exception E) + { + LogException(E); + } + finally + { + if (CM != null) LogMethodEnd(CM); + } + + return null; + } + + public class SessionInfo + { + public string Id { get; set; } + public string Uid { get; set; } + public string State { get; set; } + public DateTime? StartTime { get; set; } + } + + public async Task> GetSessionsAsync() + { + MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } + try + { + var _result = await Request.RequestAsync(constCitrixApiSessions, eHttpMethod.get, null); + if (_result?.Result == null) + return null; + var _items = _result.Result?.Items as IEnumerable; + if (_items == null) + return null; + var _retVal = new List(); + foreach (var item in _items) + { + try + { + var _sessionInfo = new SessionInfo() + { + Id = item.Id?.ToString(), + Uid = item.Uid?.ToString(), + State = item.State?.ToString(), + StartTime = item.StartTime != null ? (DateTime?)item.StartTime : null + }; + _retVal.Add(_sessionInfo); + } + catch (Exception E) + { + LogException(E); + } + } + return _retVal; + } + catch (Exception E) + { + LogException(E); + } + finally + { + if (CM != null) LogMethodEnd(CM); + } + return null; + } + } + + public class cCitrixCommunicationMonitor + { + public const string constCitrixCloudUrl = "monitorodata/"; + + public const string constCitrixMachineNames = "Machines/?$select=DnsName&$filter=DesktopGroupId+ne+null+and+Name+ne+null"; + public const string constCitrixUserList = "Users?$select=Id,Sid,Upn,Domain&$filter=Domain ne ''"; + public const string constCitrixMachineList = "Machines?$select=Id,Sid,DnsName&$filter=DesktopGroupId ne null and Name ne null"; + public const string constCitrixSessionListDetailed = "Sessions?$select=SessionKey,StartDate,EndDate,ConnectionState, UserId, MachineId&$expand=Machine($select=Id,DnsName,Name)&$filter=EndDate eq null or EndDate gt {0}"; + + public int MaxPageSize { get; set; } = 100; + + public cCitrixCommunicationBase Request { get; private set; } + + public cCitrixCommunicationMonitor(cCitrixCommunicationCloudBase CitrixCloud) + { + this.Request = new cCitrixCommunicationBase(CitrixCloud, constCitrixCloudUrl); + } + + public cCitrixCommunicationMonitor(cCitrixCommunicationOnPremMonitor OnPremMonitor) + { + this.Request = new cCitrixCommunicationBase(OnPremMonitor, null); + } + + private string getFormattedDateTime(int DayOffset = 0) + { + var _dateTime = DateTime.UtcNow.AddDays(DayOffset); + var _strDateTime = _dateTime.ToString("yyyy-MM-ddTHH:mm:ssZ"); + return _strDateTime; + } + + private string getFormattedGuid(Guid id) + { + return id.ToString("D"); + } + + public async Task> GetMachineNamesAsync(CancellationToken token) + { + var _processor = new ScanEntryStringListProcessor("DnsName", true); + var _count = await RequestListAsync(constCitrixMachineList, _processor.ProcessScannedItemAsync, token: token); + return _processor.Result; + } + + public delegate Task ProcessScanEntryDelegate(dynamic ScanEntry, CancellationToken token); + + public abstract class ScanEntryProcessor + { + public abstract Task ProcessScannedItemAsync(dynamic _node, CancellationToken token); + } + + public class ScanEntryCountProcessor : ScanEntryProcessor + { + private string nameProperty = null; + + public ScanEntryCountProcessor(string nameProperty) + { + this.nameProperty = nameProperty; + } + + public async override Task ProcessScannedItemAsync(dynamic _node, CancellationToken token) + { + try + { + string _prop = cCitrixApiResultBase.GetStringFromDynamic(_node, nameProperty); + if (!string.IsNullOrEmpty(_prop)) + return true; + } + catch (Exception E) + { + LogException(E); + } + return false; + } + + } + + public class ScanEntryStringListProcessor : ScanEntryProcessor + { + private string nameProperty = null; + private bool toLowercase = false; + + public List Result = new List(); + + public ScanEntryStringListProcessor(string nameProperty, bool toLowercase = false) + { + this.nameProperty = nameProperty; + this.toLowercase = toLowercase; + } + + public async override Task ProcessScannedItemAsync(dynamic _node, CancellationToken token) + { + try + { + string _prop = cCitrixApiResultBase.GetStringFromDynamic(_node, nameProperty); + if (!string.IsNullOrEmpty(_prop)) + { + if (toLowercase) + _prop = _prop.ToLowerInvariant(); + Result.Add(_prop); + return true; } } catch (Exception E) { - cLogManager.DefaultLogger.LogException(E); + LogException(E); } + return false; } } - public class cCitrixResultBase + private async Task RequestListAsync(string Url, ProcessScanEntryDelegate ProcessScanEntry, CancellationToken token) { - public string ID { get; private set; } = null; - public string ODataId { get; private set; } = null; - public string DisplayName { get; private set; } = null; + MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } - public string Context { get; private set; } = null; - - public dynamic Result { get; private set; } = null; - - public cCitrixResultBase(dynamic Result, string namePropery = null) + try { - this.Result = Result; - - try { ID = Result.Id; } catch { } - ; - try + int countTotal = 0; + int skipping = 0; + int lastCount = 0; + do { - if (namePropery == null) - namePropery = "displayName"; - if (Result.TryGetValue(namePropery, out JToken JT1)) + lastCount = 0; + var _url = Url + $"&$top={MaxPageSize}&$skip={skipping}"; + var _result = await Request.RequestAsync(_url, eHttpMethod.get, null, token: token); + if (_result == null || _result.Result == null) + return -1; + + var _items = _result.Result?.value as IEnumerable; + + if (_items == null) + return -1; + + foreach (var item in _items) { - var _ty = JT1.GetType().ToString(); - if (JT1 is JValue _jVal) - DisplayName = _jVal.Value?.ToString(); + try + { + var _item = item as dynamic; + var _ok = await ProcessScanEntry(_item, token); + if (token.IsCancellationRequested) + return 0; + if (_ok) + { + lastCount++; + countTotal++; + } + } + catch (Exception E) + { + LogException(E); + } } - else if (Result.TryGetValue("name", out JToken JT2)) - DisplayName = Result.name; - } - catch { } - try { Context = Result["@odata.context"]; } catch { } - ODataId = GetStringFromDynamic(Result, "@odata.id"); - if (string.IsNullOrEmpty(ODataId)) - ODataId = @"https://api.cloud.com/" + ID; - } - public cCitrixResultBase(cCitrixResultBase Result) + skipping += lastCount; + } while (lastCount >= MaxPageSize); + + return countTotal; + } + catch (Exception E) { - if (Result == null) - return; - - this.Result = Result.Result; - ID = Result.ID; - ODataId = Result.ODataId; - DisplayName = Result.DisplayName; - Context = Result.Context; + LogException(E); } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static string GetStringFromDynamic(dynamic O, string ProperyName) + finally { - try - { - return (string)O[ProperyName]; - } - catch { } - return null; + if (CM != null) LogMethodEnd(CM); } + + return -1; + } + + public async Task ScanUsersAsync(ScanEntryProcessor _processor, CancellationToken token) + { + return await RequestListAsync(constCitrixUserList, _processor.ProcessScannedItemAsync, token); + } + + public async Task ScanMachinesAsync(ScanEntryProcessor _processor, CancellationToken token) + { + return await RequestListAsync(constCitrixMachineList, _processor.ProcessScannedItemAsync, token); + } + + public async Task ScanSessionsAsync(ScanEntryProcessor _processor, int DayOffset, CancellationToken token) + { + var _url = string.Format(constCitrixSessionListDetailed, getFormattedDateTime(DayOffset)); + return await RequestListAsync(_url, _processor.ProcessScannedItemAsync, token); } } -} +} \ No newline at end of file diff --git a/F4SD-Cockpit-ServerCore/DataHistoryCollector.cs b/F4SD-Cockpit-ServerCore/DataHistoryCollector.cs index 543227b..a9863dc 100644 --- a/F4SD-Cockpit-ServerCore/DataHistoryCollector.cs +++ b/F4SD-Cockpit-ServerCore/DataHistoryCollector.cs @@ -2,7 +2,11 @@ using System.Collections.Generic; using System.Data.Common; using System.Diagnostics; +using System.DirectoryServices.AccountManagement; +using System.DirectoryServices.ActiveDirectory; using System.Globalization; +using System.IdentityModel.Tokens.Jwt; +using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; @@ -13,28 +17,26 @@ using System.Text; using System.Threading; using System.Threading.Tasks; using System.Xml.Linq; -using System.IdentityModel.Tokens.Jwt; -using System.DirectoryServices.AccountManagement; -using System.DirectoryServices.ActiveDirectory; -using System.IO; -using Microsoft.IdentityModel.Tokens; - -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; - -using C4IT.Configuration; +using C4IT.DataHistoryProvider.Base.Modules.Citrix; using C4IT.FASD.Base; using C4IT.FASD.Licensing; using C4IT.Logging; +using C4IT.Matrix42.WebClient; using C4IT.Security; using C4IT.XML; -using C4IT.Matrix42.WebClient; using C4IT_DataHistoryProvider_Base; using C4IT_DataHistoryProvider_Base.DataSources; using C4IT_DataHistoryProvider_Base.Services; +using Microsoft.Extensions.Logging; +using Microsoft.IdentityModel.Tokens; + +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +using static C4IT.FASD.Base.cF4sdQuickActionRevision; using static C4IT.Logging.cLogManager; @@ -105,7 +107,18 @@ namespace C4IT.DataHistoryProvider public abstract Task DoScanAsync(bool Always, bool Rescan, cF4sdWebRequestInfo requestInfo, int LogDeep, CancellationToken Token, bool EnhancedDebug = false); - public abstract Task> GetIds(cF4sdConnectorIds IdEntry, enumFasdInformationClass InfoClass, cF4sdWebRequestInfo requestInfo, int LogDeep); + public virtual async Task> GetIds(cF4sdConnectorIds IdEntry, enumFasdInformationClass InfoClass, cF4sdWebRequestInfo requestInfo, int LogDeep) + { + await Task.CompletedTask; + if (IdEntry.sid == null) + return null; + return new List() { IdEntry.Id }; + } + + public virtual bool UseForeignUserIds() + { + return false; + } public virtual List GetFilterValues(List IDs, enumFasdInformationClass InfoClass) { @@ -222,6 +235,15 @@ namespace C4IT.DataHistoryProvider await Task.CompletedTask; return null; } + + public virtual async Task ValidateTokenInfo(cDataHistoryCollectorTokenCache.cTokenInfo tokenInfo) + // 0: Token Info is correct + // -1: Token Info is incorrect + // 1: Token Info as to be updated due to newer infos + { + await Task.CompletedTask; + return 0; + } } public class cDataHistoryCollector : cDataHistoryCollectorModule, IConfigNodeValidation, ISearchResultRelationProvider @@ -257,6 +279,7 @@ namespace C4IT.DataHistoryProvider public cDataHistoryCollectorM42Wpm M42WpmCollector { get; private set; } = null; public cDataHistoryCollectorNxql NxqlCollector { get; private set; } = null; public cDataHistoryCollectorCitrix CitrixCollector { get; private set; } = null; + //public cDataHistoryCollectorEmpirum EmpirumCollector { get; private set; } = null; public bool F4SDAnalyticsValid = false; public cF4SDAnalyticsConfigDatabase F4SDAnalyticsDb { get; private set; } = null; public event DataHistoryUiMessageFunc ProcessUiMessage; @@ -279,6 +302,8 @@ namespace C4IT.DataHistoryProvider private readonly List _searchResultRelationProviders = new List(); + private Dictionary _tokenValidators = new Dictionary(); + public bool RunningInIISExpress = false; #region Services @@ -288,7 +313,7 @@ namespace C4IT.DataHistoryProvider public IStagedRelationService GetStagedRelationService() { if (_stagedRelationService is null) - _stagedRelationService = new StagedRelationService(_searchResultRelationProviders); + _stagedRelationService = new StagedRelationService(this, _searchResultRelationProviders); return _stagedRelationService; } @@ -329,7 +354,7 @@ namespace C4IT.DataHistoryProvider if (cF4SDLicense.Instance.Modules.ContainsKey(_ActiveDirectory.LicenseId)) { ActiveDirectory = _ActiveDirectory; - Connectors.Add(enumDataHistoryOrigin.ActiveDirectory, ActiveDirectory); + Connectors.Add(enumDataHistoryOrigin.ActiveDirectory, ActiveDirectory); } var _F4sdAgent = new cDataHistoryCollectorClientAgent(this); @@ -450,6 +475,7 @@ namespace C4IT.DataHistoryProvider var HasIntuneConfig = (_cfgInfrastructure.AzureTenants?.Values.Any(t => t.ScanIntuneDevices) == true); var HasMobileDeviceConfig = (_cfgInfrastructure.AzureTenants?.Values.Any(t => t.WithMobileDevices) == true); var HasCitrixConfig = (InfrastructureConfig.Citrix != null); + var HasEmpirumConfig = true; DoProcessUiMessage(1, "resolving the ad membership groups"); ActiveDirectory.ResolveWinAuthenticationGroups(); @@ -462,7 +488,7 @@ namespace C4IT.DataHistoryProvider DoProcessUiMessage(0, ""); DoProcessUiMessage(1, "loading the data cluster definitions..."); var _cfgDataClusters = new cDataHistoryConfigClusters(); - _cfgDataClusters.LoadFromFile(out PMs, withM42Config: HasM42Config, withIntuneConfig: HasIntuneConfig,withMobileDeviceConfig:HasMobileDeviceConfig,withCitrixConfig:HasCitrixConfig); + _cfgDataClusters.LoadFromFile(out PMs, withM42Config: HasM42Config, withIntuneConfig: HasIntuneConfig, withMobileDeviceConfig: HasMobileDeviceConfig, withCitrixConfig: HasCitrixConfig); if (PMs != null && PMs.Count > 0) DoProcessUiMessage(2, PMs); @@ -509,24 +535,32 @@ namespace C4IT.DataHistoryProvider { var _CitrixCollector = new cDataHistoryCollectorCitrix(this); if (cF4SDLicense.Instance?.Modules != null) - //if (cF4SDLicense.Instance.Modules.ContainsKey(_CitrixCollector.LicenseId)) - //{ - CitrixCollector = _CitrixCollector; - Connectors.Add(enumDataHistoryOrigin.Citrix, _CitrixCollector); - RegisterSearchRelationProvider(_CitrixCollector); - //} + if (cF4SDLicense.Instance.Modules.ContainsKey(_CitrixCollector.LicenseId)) + { + CitrixCollector = _CitrixCollector; + Connectors.Add(enumDataHistoryOrigin.Citrix, _CitrixCollector); + RegisterSearchRelationProvider(_CitrixCollector); + } } - if(HasIntuneConfig) + if (HasIntuneConfig) { Connectors.Add(enumDataHistoryOrigin.Intune, ActiveDirectory); - + } //if (HasIntuneConfig && HasMobileDeviceConfig) //{ // RegisterSearchRelationProvider(ActiveDirectory); //} + //if (HasEmpirumConfig) + //{ + // var _EmpirumCollector = new cDataHistoryCollectorEmpirum(this); + // EmpirumCollector = _EmpirumCollector; + // Connectors.Add(enumDataHistoryOrigin.Empirum, EmpirumCollector); + + //} + // do the post configuration tasks afer the infrastructure was loaded DoProcessUiMessage(1, "processing post configuration tasks"); foreach (var Table in _cfgDataClusters.Tables.Values) @@ -673,7 +707,7 @@ namespace C4IT.DataHistoryProvider var HasMobileDeviceConfig = (InfrastructureConfig.AzureTenants?.Values.Any(t => t.WithMobileDevices) == true); var HasCitrixConfig = (InfrastructureConfig.Citrix != null); - var _cfg = cFasdBaseConfig.GetConfigByType(_item, null, _msg, withM42Config: HasM42Config, withIntuneConfig: HasIntuneConfig, withMobileDeviceConfig: HasMobileDeviceConfig, withCitrixConfig:HasCitrixConfig); + var _cfg = cFasdBaseConfig.GetConfigByType(_item, null, _msg, withM42Config: HasM42Config, withIntuneConfig: HasIntuneConfig, withMobileDeviceConfig: HasMobileDeviceConfig, withCitrixConfig: HasCitrixConfig); if (_cfg.IsValid) { _Configs[_item] = _cfg; @@ -1201,7 +1235,6 @@ namespace C4IT.DataHistoryProvider return false; } - private async Task UpdateF4SDAnalyticsViewsAsync(cDbConnection DbConn, cF4sdWebRequestInfo requestInfo, int LogDeep, CancellationToken Token, bool ForceCreate) { MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } @@ -2490,7 +2523,7 @@ namespace C4IT.DataHistoryProvider var anonymous = InfrastructureConfig.F4SDAnalyticsDB?.AnonymousUser; Guid.TryParse(requestInfo.userInfo?.Id.ToString(), out Guid userGuid); if (anonymous != null && (bool)anonymous) - Param.User = GetUserHashGuid(userGuid).ToString(); + Param.User = GetUserHashGuid(userGuid).ToString(); else Param.User = userGuid.ToString(); @@ -2666,17 +2699,27 @@ namespace C4IT.DataHistoryProvider } - public async Task GetCurrentUserInfoAsync(CancellationToken Token, cF4sdWebRequestInfo requestInfo, int LogDeep) + public async Task GetCurrentUserInfoAsync(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; + 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 _user = WindowsIdentity.GetCurrent(); - var _res = await GetWinUserInfoAsync(_user, CultureInfo.CurrentCulture.DisplayName, true, Token, requestInfo, LogDeep + 1); - return _res; + if (!ActiveDirectory.WinAuthenticationGropsResolved) + { + ActiveDirectory.ResolveWinAuthenticationGroups(); + + if (!ActiveDirectory.WinAuthenticationGropsResolved) + { + LogEntry("Could not resolve authentication groups", LogLevels.Warning); + return null; + } + } + + WindowsIdentity user = WindowsIdentity.GetCurrent(); + return await GetWinUserInfoAsync(user, CultureInfo.CurrentCulture.DisplayName, true, Token, requestInfo, logDeep + 1); } catch (Exception E) { @@ -2684,7 +2727,7 @@ namespace C4IT.DataHistoryProvider } finally { - if (cPerformanceLogger.IsActive && requestInfo != null) { cPerformanceLogger.LogPerformanceEnd(LogDeep, CM, requestInfo.id, requestInfo.created, _startTime); } + if (cPerformanceLogger.IsActive && requestInfo != null) { cPerformanceLogger.LogPerformanceEnd(logDeep, CM, requestInfo.id, requestInfo.created, startTime); } if (CM != null) LogMethodEnd(CM); } @@ -2938,8 +2981,8 @@ namespace C4IT.DataHistoryProvider } if (strPhone != null) { - Entry.Result.Name = strName + " (" + strPhone + ")"; - Entry.Result.DisplayName = strName + " (" + strPhone + ")"; + Entry.Result.Name = strName; + Entry.Result.DisplayName = $"{strName} ({strPhone})"; } else { @@ -3853,7 +3896,7 @@ namespace C4IT.DataHistoryProvider return null; } - public async Task> GetUsageFromOnlyAgentData(enumFasdInformationClass infoClass, List Ids, int Age, CancellationToken Token, cF4sdWebRequestInfo requestInfo, int LogDeep) + public async Task> GetUsageFromOnlyAgentData(IEnumerable ids, enumFasdInformationClass infoClass, int Age, CancellationToken Token, cF4sdWebRequestInfo requestInfo, int LogDeep) { MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } @@ -3862,20 +3905,15 @@ namespace C4IT.DataHistoryProvider try { + var _count = ids.Count(); var Age2 = Age; - if (Ids.Count == 1) + if (_count == 1) Age2 = 365; var AgentLimit = DateTime.UtcNow + TimeSpan.FromDays(-Age2); - var idInput = Ids.Select(v => new cF4sdIdentityEntry() { Id = v, Class = infoClass }).ToList(); - var listIds = await getConnectorIdList(idInput, Token, requestInfo, LogDeep + 1); + var lstAgentIds = new Dictionary(_count); - if (listIds == null || listIds.Count == 0) - return null; - - var lstAgentIds = new Dictionary(Ids.Count); - - foreach (var Entry in listIds) + foreach (var Entry in ids) { if (Entry.lastSeenByAgent != null && Entry.lastSeenByAgent >= AgentLimit) { @@ -5237,7 +5275,7 @@ namespace C4IT.DataHistoryProvider if (Connectors.TryGetValue(tableConfig.ParentCluster.Origin, out var C)) { var IdValues = await C.GetIds(Identity, tableConfig.ParentCluster.InformationClass, requestInfo, LogDeep + 1); - if (IdValues != null && tableConfig.KeyColumns.Count == IdValues.Count) + if (IdValues != null && tableConfig.KeyColumns.Count == IdValues.Count) { var Filters = new Dictionary(); for (int i = 0; i < IdValues.Count; i++) @@ -5357,22 +5395,16 @@ namespace C4IT.DataHistoryProvider return null; } - private async Task getConnectorIds(enumFasdInformationClass infoClass, cF4sdIdentityEntry Identity, CancellationToken Token, cF4sdWebRequestInfo requestInfo, int LogDeep, bool WithLocalAccounts = false) + private async Task getConnectorIdsFromDb(enumFasdInformationClass infoClass, cF4sdIdentityEntry Identity, CancellationToken Token, cF4sdWebRequestInfo requestInfo, int LogDeep, bool WithLocalAccounts = 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; - int apiError = 0; - try { - if (infoClass == enumFasdInformationClass.Ticket) - return new cF4sdConnectorIds() { Id = Identity.Id, infoClass = enumFasdInformationClass.Ticket }; - if (infoClass == enumFasdInformationClass.VirtualSession) - return new cF4sdConnectorIds() { Id = Identity.Id, infoClass = enumFasdInformationClass.VirtualSession }; - if (infoClass == enumFasdInformationClass.MobileDevice) - return new cF4sdConnectorIds() { Id = Identity.Id, infoClass = enumFasdInformationClass.MobileDevice }; + + int apiError = 0; if (!DataHistorySqlHelper.GetWellKnownSqlStatement($"GetConnectorIdsFrom{infoClass}Id", out var Query)) return null; @@ -5516,6 +5548,66 @@ namespace C4IT.DataHistoryProvider 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 getConnectorIds(enumFasdInformationClass infoClass, cF4sdIdentityEntry Identity, CancellationToken Token, cF4sdWebRequestInfo requestInfo, int LogDeep, bool WithLocalAccounts = 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 (infoClass == enumFasdInformationClass.Ticket) + return new cF4sdConnectorIds() { Id = Identity.Id, infoClass = enumFasdInformationClass.Ticket }; + if (infoClass == enumFasdInformationClass.VirtualSession) + return new cF4sdConnectorIds() { Id = Identity.Id, infoClass = enumFasdInformationClass.VirtualSession }; + if (infoClass == enumFasdInformationClass.MobileDevice) + return new cF4sdConnectorIds() { Id = Identity.Id, infoClass = enumFasdInformationClass.MobileDevice }; + + var lstTasks = new List(); + + var _mainTask = getConnectorIdsFromDb(infoClass, Identity, Token, requestInfo, LogDeep + 1, WithLocalAccounts); + lstTasks.Add(_mainTask); + + var _needForeignUserInfo = false; + if (infoClass == enumFasdInformationClass.User) + foreach (var module in this.Connectors.Values) + _needForeignUserInfo |= module.UseForeignUserIds(); + + Task _userInfoTask = null; + if (_needForeignUserInfo) + { + _userInfoTask = GetForeignUserInfoAsync(Identity.Id, Token); + lstTasks.Add(_userInfoTask); + } + + + await Task.WhenAll(lstTasks); + + var _result = _mainTask.Result; + + if (_result == null) + return null; + + if (_needForeignUserInfo) + _result.ForeignUserInfo = _userInfoTask?.Result; + + return _result; } catch (Exception E) { @@ -6431,12 +6523,6 @@ namespace C4IT.DataHistoryProvider return true; } - public override async Task> GetIds(cF4sdConnectorIds IdEntry, enumFasdInformationClass InfoClass, cF4sdWebRequestInfo requestInfo, int LogDeep) - { - await Task.CompletedTask; - return new List() { IdEntry.Id }; - } - public static string getStingList(List L) { if (L == null || L.Count == 0) @@ -6495,7 +6581,7 @@ namespace C4IT.DataHistoryProvider return null; } - public cCollectorStatusInfo GetCollectorStatus(cF4sdWebRequestInfo requestInfo) + public cCollectorStatusInfo CheckCollectorStatus(cF4sdWebRequestInfo requestInfo) { MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } try @@ -6598,11 +6684,100 @@ namespace C4IT.DataHistoryProvider public HashSet GetSupportedInformationClasses() => new HashSet() { enumFasdInformationClass.User, enumFasdInformationClass.Computer }; - public async Task GetRelationsAsync(IEnumerable ids, enumFasdInformationClass informationClass, int age, CancellationToken token = default) + public async Task GetRelationsAsync(IEnumerable ids, enumFasdInformationClass informationClass, int age, CancellationToken token = default) { - List usageRelations = await GetUsageFromOnlyAgentData(informationClass, ids.Select(id => id.Id).ToList(), age, token, null, 1); + List usageRelations = await GetUsageFromOnlyAgentData(ids, informationClass, age, token, null, 1); return new cF4sdStagedSearchResultRelations() { Relations = usageRelations }; } + + public void RegisterTokenValidator(cF4SDTokenRegistration.enumTokenType tokenType, cDataHistoryCollectorModule module) + { + _tokenValidators[tokenType] = module; + } + + public async override Task ValidateTokenInfo(cDataHistoryCollectorTokenCache.cTokenInfo tokenInfo) + { + if (_tokenValidators.TryGetValue(tokenInfo.type, out var module)) + return await module.ValidateTokenInfo(tokenInfo); + return 0; + } + + public class cForeignUserInfoEntry + { + public Guid MainID { get; set; } + public enumDataHistoryOrigin Origin { get; set; } + public string InstanceID { get; set; } + public string ForeignID { get; set; } + } + + public class cForeignUserInfo : Dictionary> { } + + public async Task GetForeignUserInfoAsync(Guid MainID, CancellationToken token) + { + MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } + + try + { + if (!DataHistorySqlHelper.GetWellKnownSqlStatement("GetForeignUserInfo", out var QueryGetUser)) + return null; + + 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 null; + } + + var Params = new Dictionary() { { "@ID", MainID } }; + + using (var Reader = await DataHistorySqlHelper.GetDataReaderAsync(Conn, QueryGetUser, Params, token)) + { + if (Reader != null) + if (Reader.HasRows) + { + var _retVal = new cForeignUserInfo(); + while (await Reader.ReadAsync(token)) + { + try + { + var Entry = new cForeignUserInfoEntry(); + Entry.MainID = Reader.GetGuid(0); + Entry.Origin = (enumDataHistoryOrigin)Reader.GetInt32(1); + Entry.InstanceID = Reader.GetString(2); + Entry.ForeignID = Reader.GetString(3); + + if (!_retVal.TryGetValue(Entry.Origin, out var _lst)) + { + _lst = new List(); + _retVal.Add(Entry.Origin, _lst); + } + _lst.Add(Entry); + } + catch (Exception E) + { + cLogManager.DefaultLogger.LogException(E); + } + } + + return _retVal; + + } + } + + } + } + catch (Exception E) + { + LogException(E); + } + finally + { + if (CM != null) LogMethodEnd(CM); + } + + return null; + } } public class cScanTimeInfo @@ -6736,7 +6911,13 @@ namespace C4IT.DataHistoryProvider public Guid tenantId { get; set; } public Guid intuneId { get; set; } public Guid azureDeviceId { get; set; } + + public cDataHistoryCollector.cForeignUserInfo ForeignUserInfo = null; + + [Obsolete] public string citrixTenantId { get; set; } + + [Obsolete] public int? citrixUserId { get; set; } } diff --git a/F4SD-Cockpit-ServerCore/DataHistoryCollectorActiveDirectory.cs b/F4SD-Cockpit-ServerCore/DataHistoryCollectorActiveDirectory.cs index e048cf8..2d22169 100644 --- a/F4SD-Cockpit-ServerCore/DataHistoryCollectorActiveDirectory.cs +++ b/F4SD-Cockpit-ServerCore/DataHistoryCollectorActiveDirectory.cs @@ -1,7 +1,15 @@ -using System; +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 System; using System.Collections.Generic; using System.Data.Common; using System.DirectoryServices.AccountManagement; +using System.DirectoryServices.ActiveDirectory; using System.DirectoryServices.Protocols; using System.Globalization; using System.Linq; @@ -9,16 +17,8 @@ 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 @@ -50,6 +50,7 @@ namespace C4IT.DataHistoryProvider 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 string constMsGraphTAP = "users/{0}/authentication/temporaryAccessPassMethods"; public const int constMsGraphManagedDevicePaging = 100; public const string constTableNameIntuneDeviceDetails = "intune-deviceInfo"; public const string constTableNameIntuneUserDeviceCount = "intune-userDeviceCount"; @@ -93,6 +94,7 @@ namespace C4IT.DataHistoryProvider private DateTime LastDomainsUpdate = DateTime.MinValue; private bool DomainsUpdateRunning = false; private Dictionary tenantGraphCache = new Dictionary(); + public bool WinAuthenticationGropsResolved { get; private set; } static cDataHistoryCollectorActiveDirectory() { BaseDateTime = new DateTime(1601, 1, 1, 0, 0, 0, DateTimeKind.Utc); @@ -2053,13 +2055,17 @@ namespace C4IT.DataHistoryProvider return true; } - public async Task GetRelationsAsync(IEnumerable ids, enumFasdInformationClass informationClass, int age, CancellationToken token = default) + + public async Task GetRelationsAsync(IEnumerable ids, enumFasdInformationClass informationClass, int age, CancellationToken token = default) { try { + /* List Ids = await _collector.getConnectorIdList(ids.ToList(), token, null, 0); List relations = await GetUserMobileDevicesAsync(Ids, token, null); return new cF4sdStagedSearchResultRelations() { Relations = relations }; + */ + return null; } catch (Exception ex) { @@ -2082,14 +2088,16 @@ namespace C4IT.DataHistoryProvider 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) + if (!Identities.TryGetValue(enumFasdInformationClass.User, out var user) && user?.intuneId != Guid.Empty && user?.tenantId != Guid.Empty) + bUserDeviceExists = false; + + if (!Identities.TryGetValue(enumFasdInformationClass.Computer, out var computer) && computer?.intuneId != Guid.Empty && user?.tenantId != Guid.Empty) bDeviceExists = false; - if(!Identities.TryGetValue(enumFasdInformationClass.MobileDevice, out var mobile) && mobile?.Id != Guid.Empty) + 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)) @@ -2111,8 +2119,8 @@ namespace C4IT.DataHistoryProvider } else - listTasks.Add(Task.Run>(async () => { return await GetIntuneDeviceTableAsync(computer.intuneId, computer.tenantId, requestInfo, LogDeep + 1, Token); })); - + listTasks.Add(Task.Run>(async () => { return await GetIntuneDeviceTableAsync(computer.intuneId, user.tenantId, requestInfo, LogDeep + 1, Token); })); + if (bMobileDeviceExists) if (CacheId == null && !instantly) { @@ -2237,20 +2245,22 @@ namespace C4IT.DataHistoryProvider if (MsGraph == null) return null; + var retVal = new cF4SDHealthCardRawData.cHealthCardTable() + { + Name = constTableNameIntuneDeviceDetails, + InformationClass = enumFasdInformationClass.Computer, + Origin = enumDataHistoryOrigin.Intune, + IsStatic = true, + TableType = eDataHistoryTableType.Static + }; + + var dicVals = new Dictionary(); + 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(); var vals = deviceInfos.Children(); if (cLogManager.DefaultLogger.IsDebug) LogEntry("SpecialDebug GetIntuneMainTableAsync #3"); @@ -2290,6 +2300,16 @@ namespace C4IT.DataHistoryProvider else { LogEntry($"Could not get detailed device information from intune with id {IntuneId}. Device content is empty.", LogLevels.Warning); + foreach (var colInfo in IntuneTableDetails.Columns.Values) + { + try + { + var _col = new cF4SDHealthCardRawData.cHealthCardTableColumn(retVal) { ColumnName = colInfo.Name, Values = new List(1) { } }; + retVal.Columns[colInfo.Name] = _col; + } + catch { } + } + return new List(1) { retVal }; } } catch (Exception E) @@ -2326,17 +2346,19 @@ namespace C4IT.DataHistoryProvider 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"); + + var retVal = new cF4SDHealthCardRawData.cHealthCardTable() + { + Name = constTableNameIntuneUserDeviceCount, + InformationClass = enumFasdInformationClass.User, + Origin = enumDataHistoryOrigin.Intune, + IsStatic = true, + TableType = eDataHistoryTableType.Static + }; + 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(); @@ -2405,6 +2427,16 @@ namespace C4IT.DataHistoryProvider else { LogEntry($"Could not get detailed DeviceCount information from intune with id {Userid}. DeviceCount content is empty.", LogLevels.Warning); + foreach (var colInfo in IntuneTableDetails.Columns.Values) + { + try + { + var _col = new cF4SDHealthCardRawData.cHealthCardTableColumn(retVal) { ColumnName = colInfo.Name, Values = new List(1) { } }; + retVal.Columns[colInfo.Name] = _col; + } + catch { } + } + return new List(1) { retVal }; } } catch (Exception E) @@ -2437,26 +2469,25 @@ namespace C4IT.DataHistoryProvider var MsGraph = await GetGraphForTenantAsync(TenantId, true); if (MsGraph == null) return null; - + var retValList = new List(); var res = await MsGraph.RequestAsync(strUrl, UseBeta: false, retryForbidden: false); + + var retVal = new cF4SDHealthCardRawData.cHealthCardDetailsTable() + { + Name = constTableNameIntuneUserDeviceCountDetail, + Columns = IntuneTableDetails.Columns.Values.Select(v => v.Name).ToList(), + Values = new Dictionary>() + + }; + + if (res != null) { JObject deviceInfos = res.Result; - - var retValList = new List(); - - 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 index = 0; @@ -2488,6 +2519,8 @@ namespace C4IT.DataHistoryProvider else { LogEntry($"Could not get detailed DeviceCount information from intune with id {Userid}. DeviceCount content is empty.", LogLevels.Warning); + retValList.Add(retVal); + return retValList; } } return null; @@ -2512,25 +2545,25 @@ namespace C4IT.DataHistoryProvider { 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 tenantId = UserId?.FirstOrDefault(u => u?.tenantId != Guid.Empty)?.tenantId ?? Guid.Empty; + var strUrl = string.Format(constMsGraphManagedMobileDeviceList, userIds); var MsGraph = await GetGraphForTenantAsync(tenantId, true); if (MsGraph == null) return null; - + var Result = await MsGraph.RequestListAsync(strUrl, UseBeta: false, retryForbidden: false); if (Result != null == Result.Count > 0) { var searchResult = new List(Result.Count); - + foreach (var Entry in Result) { var endDate = DateTime.MinValue; if (!DateTime.TryParse(Entry.Result.lastSyncDateTime?.ToString(), out endDate)) endDate = DateTime.UtcNow; - + var searchInfo = new cF4sdApiSearchResultRelation() { Type = enumF4sdSearchResultClass.MobileDevice, @@ -2677,6 +2710,9 @@ namespace C4IT.DataHistoryProvider case "GetLAPS": result = await GetLAPS(jsonRequest.Identities, requestInfo, CancellationToken.None); break; + case "GetTAP": + result = await GetTAP(jsonRequest.Identities, requestInfo, CancellationToken.None); + break; case "GetBitlockerKeyTest": result = await GetBitlockerKeyTest(jsonRequest.Identities, requestInfo, CancellationToken.None); break; @@ -2688,7 +2724,7 @@ namespace C4IT.DataHistoryProvider } public override async Task> GetQuickActionList() - + { var HasIntuneConfig = (Collector.InfrastructureConfig.AzureTenants?.Values.Any(t => t.ScanIntuneDevices) == true); if (HasIntuneConfig) @@ -2699,8 +2735,9 @@ namespace C4IT.DataHistoryProvider "Get Managed Apps", "Get BitlockerKey", "Get LAPS", + "Get TAP", "GetLapsKeyTest", - }; + }; return await Task.FromResult(quickactions); } var result = new List(); @@ -2827,6 +2864,40 @@ namespace C4IT.DataHistoryProvider return null; } + public async Task GetTAP(List identities, cF4sdWebRequestInfo requestInfo, CancellationToken Token) + { + var Ids = await Collector.getConntectorIds(identities, Token, requestInfo, 1); + if (!Ids.TryGetValue(enumFasdInformationClass.User, out var _ids)) + return null; + + var strUrl = string.Format(constMsGraphTAP, _ids.intuneId); + + + var MsGraph = await GetGraphForTenantAsync(_ids.tenantId, true); + if (MsGraph == null) + return null; + + var IntuneConfig = Collector?.GetGlobalConfig()?.IntuneConfiguration; + + var body = new Dictionary() + { + { "isUsableOnce", IntuneConfig?.TAPIsUsableOnce ?? cF4sdIntuneConfig.DefaultTAPIsUsableOnce}, + { "lifetimeInMinutes", IntuneConfig?.TAPLifeTimeInMinutes ?? cF4sdIntuneConfig.DefaultTAPLifeTimeInMinutes} + }; + + var Result = await MsGraph.RequestAsync(strUrl, UseBeta: false, eHttpMethod.post, JsonData: body, retryForbidden: false); + if (Result != null) + { + var _resultJson = Result.Result as JObject; + if (_resultJson != null) + { + return _resultJson; + } + } + + return null; + } + public async Task GetManagedApps(List identities, cF4sdWebRequestInfo requestInfo, CancellationToken Token) { var Ids = await Collector.getConntectorIds(identities, Token, requestInfo, 1); @@ -3131,6 +3202,9 @@ namespace C4IT.DataHistoryProvider if (Collector.InfrastructureConfig.AzureTenants != null) await DoAzureScanAsync(requestInfo, LogDeep + 1, Token); + if (Collector.InfrastructureConfig.AzureTenants?.Values.Any(t => t.ScanIntuneDevices) == true) + await DoScanIntuneAsync(requestInfo, LogDeep + 1, Token); + await Collector.SetLastScanTime("AdScan-all", DateTime.UtcNow, requestInfo, LogDeep + 1, Token); return true; @@ -3443,7 +3517,7 @@ namespace C4IT.DataHistoryProvider if (Table?.ParentCluster?.Origin == enumDataHistoryOrigin.Intune) return true; return false; - } + } private object ConvertToF4sdType(object objVal, enumFasdValueType ValueType) { @@ -3691,68 +3765,26 @@ namespace C4IT.DataHistoryProvider MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } try { - if (Collector?.InfrastructureConfig?.Authorisation?.MembershipGroups?.GroupsAd == null) + if (Collector?.InfrastructureConfig?.Authorisation?.MembershipGroups?.GroupsAd is null) return; LogEntry("Resolving AD groups SIDs..."); - var _lstDomains = Collector.InfrastructureConfig.Authorisation.MembershipGroups.GroupsAd; + var domains = Collector.InfrastructureConfig.Authorisation.MembershipGroups.GroupsAd.Values; LogEntry("Resolving AD groups SIDs finished"); - foreach (var _domain in _lstDomains.Values) + + bool hasResolved = true; + + foreach (cF4SDMembershipGroupsAd domain in domains) { - 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"); - } + hasResolved = hasResolved && TryProcessDomain(domain); } + + WinAuthenticationGropsResolved = hasResolved; } - catch (Exception E) + catch (Exception ex) { - LogException(E); + LogException(ex); + WinAuthenticationGropsResolved = false; } finally { @@ -3760,6 +3792,80 @@ namespace C4IT.DataHistoryProvider } } + private bool TryProcessDomain(cF4SDMembershipGroupsAd domain) + { + try + { + bool hasProcessed = true; + if (domain?.Domain is null) + return hasProcessed; + + LogEntry($"Start processing domain"); + string domainName = domain.Domain.FQDN; + LogEntry($"Processing domain {domainName}"); + + PrincipalContext principalContext; + if (domain.Domain.Credential == null) + principalContext = new PrincipalContext(ContextType.Domain, domainName); + else + principalContext = new PrincipalContext(ContextType.Domain, domainName, domain.Domain.Credential.User, domain.Domain.Credential.NwCredential.Password); + + LogEntry($"Principal context successful created"); + + foreach (var group in domain.Values) + { + hasProcessed = hasProcessed && TryProcessMembershipGroup(group, principalContext, domainName); + } + + return hasProcessed; + } + catch (Exception ex) + { + LogException(ex); + return false; + } + finally + { + LogEntry($"End processing domain"); + } + } + + private bool TryProcessMembershipGroup(cF4SDMembershipGroupAd group, PrincipalContext principalContext, string domainName) + { + try + { + if (group is null) + return true; + + LogEntry($"Start processing group."); + LogEntry($"Group: {group.Name}."); + var groupIdentity = GroupPrincipal.FindByIdentity(principalContext, IdentityType.SamAccountName, group.Account); + LogEntry($"Group principal found"); + LogEntry($"GI.SID: {groupIdentity.Sid}"); + + if (groupIdentity?.Sid != null) + { + group.SID = groupIdentity.Sid.ToString(); + LogEntry($"AD group '{group.Account}' in domain '{domainName}' resolved. SID = {group.SID}"); + } + + if (group.SID == null) + LogEntry($"Could not resolve AD group '{group.Account}' in domain '{domainName}'", LogLevels.Warning); + + return true; + } + catch (Exception ex) + { + LogException(ex); + return false; + } + finally + { + LogEntry($"End processing group."); + } + } + + static public Guid ConvertAzureSidToGuid(string sid) { if (!sid.StartsWith("S-1-12-1-")) diff --git a/F4SD-Cockpit-ServerCore/DataHistoryCollectorClientAgent.cs b/F4SD-Cockpit-ServerCore/DataHistoryCollectorClientAgent.cs index 36f2e9f..91e0b67 100644 --- a/F4SD-Cockpit-ServerCore/DataHistoryCollectorClientAgent.cs +++ b/F4SD-Cockpit-ServerCore/DataHistoryCollectorClientAgent.cs @@ -592,7 +592,6 @@ namespace C4IT.DataHistoryProvider // check if wwe have a local account and process the localAccountAssignments if (string.IsNullOrEmpty(domain) && localAccountAssignments != null) { - var AdId = Guid.Empty; foreach (var localAccountAssignment in localAccountAssignments) { diff --git a/F4SD-Cockpit-ServerCore/DataHistoryCollectorM42Wpm.cs b/F4SD-Cockpit-ServerCore/DataHistoryCollectorM42Wpm.cs index 765d03b..ffb05a5 100644 --- a/F4SD-Cockpit-ServerCore/DataHistoryCollectorM42Wpm.cs +++ b/F4SD-Cockpit-ServerCore/DataHistoryCollectorM42Wpm.cs @@ -1,22 +1,31 @@ -using C4IT.F4SD.SupportCaseProtocoll.Models; -using C4IT.FASD.Base; -using C4IT.Logging; -using C4IT.Matrix42.WebClient; -using C4IT.XML; -using C4IT_DataHistoryProvider_Base.DataSources; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using System; +using System; +using System.Collections; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using System.Net.Http; +using System.Numerics; using System.Reflection; using System.Security.Principal; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Web; + +using C4IT.F4SD.SupportCaseProtocoll.Models; +using C4IT.FASD.Base; +using C4IT.Logging; +using C4IT.Matrix42.WebClient; +using C4IT.XML; + +using C4IT_DataHistoryProvider_Base.DataSources; + +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +using static C4IT.DataHistoryProvider.cDataHistoryCollectorTokenCache; using static C4IT.FASD.Base.cF4SDHealthCardRawData; +using static C4IT.FASD.Base.cHealthCardTranslator; using static C4IT.Logging.cLogManager; using static C4IT.Matrix42.WebClient.cM42WebClient; @@ -28,36 +37,48 @@ namespace C4IT.DataHistoryProvider public const string constLicenseId = "9CE1A6BE-6A0C-4A27-94A5-44AB997B8E62"; private const string constUrlIsAlive = "m42Services/api/c4itf4sdwebapi/isalive"; - private const string constUrlGetTickets = "m42Services/api/c4itf4sdwebapi/getticketlist?sid={0}&hours={1}&queueoption={2}"; - private const string constUrlGetTicketsQueuesParam = "&queues={0}"; + private const string constUrlGetTickets = "m42Services/api/c4itf4sdwebapi/getticketlist?sid={0}&hours={1}&queueoption={2}"; + private const string constUrlGetTicketsQueuesParam = "&queues={0}"; private const string constUrlGetTicketDetails = "m42Services/api/c4itf4sdwebapi/getticketdetails?objectid={0}"; private const string constUrlGetTicketHistory = "m42Services/api/c4itf4sdwebapi/gettickethistory?objectid={0}"; private const string constUrlGetCreateTicket = "m42Services/api/c4itf4sdwebapi/getdirectlinkcreateticket?sid={0}{1}"; private const string constUrlGetCreateTicketAssetParam = "&assetname={0}"; private const string constUrlUpdateSolution = "m42Services/api/c4itf4sdwebapi/updateactivitysolution/{0}/"; - private const string constUrlGetPickupValues = "m42Services/api/c4itf4sdwebapi/getpickup/{0}?group={1}"; - private const string constUrlGetRoleMeberships = "m42services/api/c4itf4sdwebapi/getrolememberships/?sid={0}"; - private const string constUrlGetTicketOverviewCounts = "m42Services/api/c4itf4sdwebapi/getticketoverviewcounts"; - private const string constUrlGetTicketOverviewCountsByRoles = "m42Services/api/c4itf4sdwebapi/getticketoverviewcountsbyroles"; - private const string constUrlGetTicketOverviewRelations = "m42Services/api/c4itf4sdwebapi/getticketoverviewrelations"; - private const string constUrlGetDataQueryRelationItems = "m42Services/api/dataquery/relationitems"; - private const string constUrlGetDataQueryRelationItemsCount = "m42Services/api/dataquery/relationitems/count"; + private const string constUrlGetPickupValues = "m42Services/api/c4itf4sdwebapi/getpickup/{0}?group={1}"; + private const string constUrlGetDataQueryRelationItems = "m42Services/api/dataquery/relationitems"; + private const string constUrlGetDataQueryRelationItemsCount = "m42Services/api/dataquery/relationitems/count"; private const string constUrlCreateTicket = "m42Services/api/ticket/create?activitytype={0}"; private const string constUrlActivityGetQuickCallTemplateInfo = "m42Services/api/activity/quickcalltemplateinfo/{0}"; private const string constUrlCreateJournalEntry = "m42Services/api/journal/Add"; private const string constUrlFragmentsGetList = "m42Services/api/data/fragments/{0}?where={1}&columns={2}&pagesize={3}&pagenumber={4}&sort={5}"; - private const string constUrlObjectsGetObject = "m42Services/api/data/objects/{0}/{1}?full={2}"; - private const string constUrlObjectsUpdateObject = "m42Services/api/data/objects/{0}?full={1}"; - private const string constUrlTicketClose = "m42Services/api/ticket/close"; + private const string constUrlObjectsGetObject = "m42Services/api/data/objects/{0}/{1}?full={2}"; + private const string constUrlObjectsUpdateObject = "m42Services/api/data/objects/{0}?full={1}"; + private const string constUrlTicketClose = "m42Services/api/ticket/close"; private const string constUrlActivityPause = "m42Services/api/activity/Pause"; private const string constUrlActivityForward = "m42Services/api/activity/Forward"; private const string constUrlActivityTrackWorkingTime = "m42Services/api/activity/trackworkingtime"; private const string constUrlTicketTransform = "m42Services/api/ticket/transform"; private const string constUrlActivityReopen = "m42Services/api/activity/reopen"; - private const string constUrlGenerateApiTokenForMe = "m42Services/api/apitoken/generateapitokenforme"; - private const string constUrlGenerateAccessTokenFromApiToken = "m42Services/api/apitoken/generateaccesstokenfromapitoken"; - private const string constActivityTypeTicket = "SPSActivityTypeTicket"; - private static readonly Guid constActivityTypeTicketTypeId = new Guid("13F950A8-F2F8-E611-E182-60E327035D31"); + private const string constUrlGenerateApiTokenForMe = "m42Services/api/apitoken/generateapitokenforme"; + private const string constUrlGenerateAccessTokenFromApiToken = "m42Services/api/apitoken/generateaccesstokenfromapitoken"; + + [Obsolete] + private const string constUrlGetRoleMeberships = "m42services/api/c4itf4sdwebapi/getrolememberships/?sid={0}"; + + [Obsolete] + private const string constUrlGetTicketOverviewCounts = "m42Services/api/c4itf4sdwebapi/getticketoverviewcounts"; + + [Obsolete] + private const string constUrlGetTicketOverviewCountsByRoles = "m42Services/api/c4itf4sdwebapi/getticketoverviewcountsbyroles"; + + [Obsolete] + private const string constUrlGetTicketOverviewRelations = "m42Services/api/c4itf4sdwebapi/getticketoverviewrelations"; + + private const string constActivityTypeTicket = "SPSActivityTypeTicket"; + private const string constActivityTypeIncident = "SPSActivityTypeIncident"; + private const string constActivityTypeServiceRequest = "SPSActivityTypeServiceRequest"; + + private static readonly Guid constActivityTypeTicketTypeId = new Guid("13F950A8-F2F8-E611-E182-60E327035D31"); public const string constTableNameTicketDetails = "M42Wpm-Tickets"; @@ -69,16 +90,30 @@ namespace C4IT.DataHistoryProvider private readonly Timer onlineTimer = null; - private bool IsOnline = false; - - private Dictionary UserWebClientCache = new Dictionary(); - - private readonly object _ticketOverviewCacheLock = new object(); - private readonly Dictionary _ticketOverviewPersonalCache = new Dictionary(StringComparer.OrdinalIgnoreCase); - private readonly Dictionary _ticketOverviewRoleCache = new Dictionary(); - private readonly Dictionary _ticketOverviewRoleListCache = new Dictionary(StringComparer.OrdinalIgnoreCase); - - private readonly cDataHistoryCollector _collector; + private bool IsOnline = false; + + private Dictionary UserWebClientCache = new Dictionary(); + + private readonly object _ticketOverviewCacheLock = new object(); + + [Obsolete] + private readonly Dictionary _ticketOverviewPersonalCache = new Dictionary(StringComparer.OrdinalIgnoreCase); + + [Obsolete] + private readonly Dictionary _ticketOverviewRoleCache = new Dictionary(); + + [Obsolete] + private readonly Dictionary _ticketOverviewRoleListCache = new Dictionary(StringComparer.OrdinalIgnoreCase); + + private readonly cDataHistoryCollector _collector; + + private GlobalServerSettings _globalServerSettings = new GlobalServerSettings(); + + private class GlobalServerSettings + { + internal DateTime LastRead = DateTime.MinValue; + internal bool TicketAndServiceRequestEnabled = false; + } private static class _OnlineCheckCriticalSection { @@ -90,46 +125,51 @@ namespace C4IT.DataHistoryProvider public static SemaphoreSlim Semaphore = new SemaphoreSlim(1, 1); } + private static class _GlobalSettingsQueryCriticalSection + { + public static SemaphoreSlim Semaphore = new SemaphoreSlim(1, 1); + } + private struct WebClientCacheIndex { public Guid Id; public string Language; } - private class WebClientCacheEntry - { - public cM42WebClient WebClient; - public DateTime LastUsed; - public DateTime ValidUntil; - } - - private sealed class TicketOverviewCountCacheEntry - { - public Dictionary Counts { get; set; } = new Dictionary(StringComparer.OrdinalIgnoreCase); - public DateTime ExpiresAtUtc { get; set; } - } - - private sealed class TicketOverviewRoleListCacheEntry - { - public List RoleIds { get; set; } = new List(); - public DateTime ExpiresAtUtc { get; set; } - } - - private sealed class TicketOverviewCountsByRolesRequest - { - public string Sid { get; set; } - public List RoleGuids { get; set; } = new List(); - public List Keys { get; set; } = new List(); - public int QueueOption { get; set; } - public string Queues { get; set; } - } - - private sealed class TicketOverviewCountsByRolesResponse - { - [JsonProperty("countsByRole")] - public Dictionary> CountsByRole { get; set; } - = new Dictionary>(StringComparer.OrdinalIgnoreCase); - } + private class WebClientCacheEntry + { + public cM42WebClient WebClient; + public DateTime LastUsed; + public DateTime ValidUntil; + } + + private sealed class TicketOverviewCountCacheEntry + { + public Dictionary Counts { get; set; } = new Dictionary(StringComparer.OrdinalIgnoreCase); + public DateTime ExpiresAtUtc { get; set; } + } + + private sealed class TicketOverviewRoleListCacheEntry + { + public List RoleIds { get; set; } = new List(); + public DateTime ExpiresAtUtc { get; set; } + } + + private sealed class TicketOverviewCountsByRolesRequest + { + public string Sid { get; set; } + public List RoleGuids { get; set; } = new List(); + public List Keys { get; set; } = new List(); + public int QueueOption { get; set; } + public string Queues { get; set; } + } + + private sealed class TicketOverviewCountsByRolesResponse + { + [JsonProperty("countsByRole")] + public Dictionary> CountsByRole { get; set; } + = new Dictionary>(StringComparer.OrdinalIgnoreCase); + } public class cM42ApiTokenModel { @@ -153,6 +193,9 @@ namespace C4IT.DataHistoryProvider _collector = Collector; + Collector.RegisterTokenValidator(cF4SDTokenRegistration.enumTokenType.M42Bearer, this); + Collector.RegisterTokenValidator(cF4SDTokenRegistration.enumTokenType.M42Api, this); + WebClient = new cM42WebClient(Collector.InfrastructureConfig.M42Wpm.Server); if (Collector.autoConnectionCheck) @@ -160,6 +203,35 @@ namespace C4IT.DataHistoryProvider } + private static enumTicketType ConvertActivityToTicketType(string ActivityType, bool TicketAndServiceRequestEnabled) + { + if (!TicketAndServiceRequestEnabled) + return enumTicketType.Ticket; + + switch (ActivityType) + { + case constActivityTypeIncident: + return enumTicketType.Incident; + case constActivityTypeServiceRequest: + return enumTicketType.ServiceRequest; + default: + return enumTicketType.UnclassifiedTicket; + } + } + + private string BuildTicketDeepLink(Guid ticketId, string activityType) + { + if (ticketId == Guid.Empty) + return null; + + var server = Collector.InfrastructureConfig.M42Wpm.Server; + + var viewOptionsJson = $"{{\"embedded\":false,\"objectId\":\"{ticketId}\",\"type\":\"{activityType}\",\"viewType\":\"preview\",\"archived\":0}}"; + var viewOptionsEncoded = Uri.EscapeDataString(viewOptionsJson); + + return $"https://{server}/wm/app-ServiceDesk/?view-options={viewOptionsEncoded}"; + } + private async Task GetWebClient(cF4sdWebRequestInfo requestInfo, CancellationToken Token) { @@ -299,6 +371,103 @@ namespace C4IT.DataHistoryProvider } } + private async Task CheckAndRefreshGlobalServerSettingsAsync(CancellationToken Token) + { + if ((DateTime.UtcNow - _globalServerSettings.LastRead) < TimeSpan.FromMinutes(10)) + return; + + MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } + + try + { + var _strUrl = string.Format(constUrlFragmentsGetList, "SPSGlobalConfigurationClassServiceDesk", string.Empty, "TicketAndServiceRequestEnabled", string.Empty, string.Empty, string.Empty); + _strUrl = RemoveEmptyQueryParameters(_strUrl); + + + // call the api + var requestInfo = new cF4sdWebRequestInfo("CheckAndRefreshGlobalServerSettings", "-"); + var _wc = await GetWebClient(requestInfo, Token); + var res = await _wc.HttpEnh.GetAsync(_strUrl); + + if (res?.IsSuccessStatusCode == true) + { + var strJson = await res.Content.ReadAsStringAsync(); + + if (!string.IsNullOrEmpty(strJson)) + { + // get the result as JArray + JArray PickupResult = JsonConvert.DeserializeObject(strJson); + + // loop all rows (should be at least one) + foreach (var _row in PickupResult) + { + try + { + // get the row values from JSON result + foreach (var _col in _row) + { + if (_col is JProperty prop) + if (prop.Value is JValue val) + { + Debug.WriteLine($"Property: {prop.Name}, Value: {val.Value}"); + switch (prop.Name) + { + case "TicketAndServiceRequestEnabled": + + if (val.Type == JTokenType.Integer) + { + var _i = val.Value(); + _globalServerSettings.TicketAndServiceRequestEnabled = _i != 0; + } + break; + } + } + } + } + catch (Exception E) + { + LogException(E); + } + + } + } + } + } + catch (Exception E) + { + LogException(E); + } + finally + { + if (CM != null) LogMethodEnd(CM); + } + + + } + + private async Task GetSettingTicketAndServiceRequestEnabledAsync(CancellationToken Token) + { + MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } + + await _GlobalSettingsQueryCriticalSection.Semaphore.WaitAsync(); + try + { + await CheckAndRefreshGlobalServerSettingsAsync(Token); + return _globalServerSettings.TicketAndServiceRequestEnabled; + } + catch (Exception E) + { + LogException(E); + } + finally + { + _GlobalSettingsQueryCriticalSection.Semaphore.Release(); + if (CM != null) LogMethodEnd(CM); + } + + return true; + } + public override async Task 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); } @@ -345,12 +514,6 @@ namespace C4IT.DataHistoryProvider return true; } - public override async Task> GetIds(cF4sdConnectorIds IdEntry, enumFasdInformationClass InfoClass, cF4sdWebRequestInfo requestInfo, int LogDeep) - { - await Task.CompletedTask; - return new List() { IdEntry.Id }; - } - public override async Task GetScanTimeInfoAsync(cF4sdWebRequestInfo requestInfo, int LogDeep, CancellationToken Token) { MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } @@ -449,7 +612,6 @@ namespace C4IT.DataHistoryProvider if (onlineTimer != null) onlineTimer.Change(30000, 60000); - return IsOnline; } catch (Exception E) { @@ -459,6 +621,25 @@ namespace C4IT.DataHistoryProvider { _OnlineCheckCriticalSection.Semaphore.Release(); } + + if (IsOnline) + { + await _GlobalSettingsQueryCriticalSection.Semaphore.WaitAsync(Token); + try + { + await CheckAndRefreshGlobalServerSettingsAsync(Token); + } + catch (Exception E) + { + LogException(E); + } + finally + { + _GlobalSettingsQueryCriticalSection.Semaphore.Release(); + } + } + + return IsOnline; } catch (Exception E) { @@ -504,26 +685,20 @@ namespace C4IT.DataHistoryProvider if (!await CheckOnline()) return null; - var objSid = new SecurityIdentifier(UserId.sid, 0); - var strSid = objSid.ToString(); - var queueOption = (int)Collector.InfrastructureConfig.M42Wpm.ActivityQueueFilterOption; - var strUrl = string.Format(constUrlGetTickets, - strSid, - Collector.InfrastructureConfig.M42Wpm.ClosedTicketHistory, - queueOption); - - if (queueOption != 0) - { - var encodedQueues = string.Join("|", - Collector.InfrastructureConfig.M42Wpm.ActivityQueues.Select(q => - string.Format("{0}:{1}", - HttpUtility.UrlEncode(q.QueueName), - HttpUtility.UrlEncode(q.QueueID.ToString()) - ) - ) - ); - strUrl += string.Format(constUrlGetTicketsQueuesParam, encodedQueues); - } + var objSid = new SecurityIdentifier(UserId.sid, 0); + var strSid = objSid.ToString(); + var queueOption = (int)Collector.InfrastructureConfig.M42Wpm.ActivityQueueFilterOption; + var strUrl = string.Format(constUrlGetTickets, + strSid, + Collector.InfrastructureConfig.M42Wpm.ClosedTicketHistory, + queueOption); + + if (queueOption != 0) + { + var encodedQueues = GetEncodedActivityQueues(); + if (!string.IsNullOrWhiteSpace(encodedQueues)) + strUrl += string.Format(constUrlGetTicketsQueuesParam, encodedQueues); + } var _wc = await GetWebClient(requestInfo, Token); var res = await _wc.HttpEnh.GetAsync(strUrl); @@ -557,11 +732,13 @@ namespace C4IT.DataHistoryProvider return null; } - public async Task> getTicketUsage(List lstTicketIds, 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; + public async Task> getTicketUsage(IEnumerable lstTicketIds, 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; + + var _ticketAndServiceRequestEnabled = await GetSettingTicketAndServiceRequestEnabledAsync(Token); try { @@ -592,16 +769,20 @@ namespace C4IT.DataHistoryProvider var ticketResult = new List(retVal.Count); foreach (var Entry in retVal.Values) { + var TicketType = ConvertActivityToTicketType(Entry.ActivityType, _ticketAndServiceRequestEnabled); + var DeepLink = BuildTicketDeepLink(Entry.TicketObjectId, Entry.ActivityType); var Infos = new Dictionary() { { "Summary", Entry.Summary }, + { "TicketType", TicketType.ToString() }, { "ActivityType", Entry.ActivityType }, { "Status", Entry.Status }, { "StatusId", ConvertM42TicketCode(Entry.StatusId).ToString() }, { "Asset", Entry.AssetName }, { "ServiceName", Entry.ServiceName }, { "ServiceId", Entry.ServiceId.ToString() }, - { "AssetType", Entry.AssetCIName } + { "AssetType", Entry.AssetCIName }, + { "TicketLink", DeepLink } }; var ticketInfo = new cF4sdApiSearchResultRelation() @@ -633,612 +814,535 @@ namespace C4IT.DataHistoryProvider if (cPerformanceLogger.IsActive && requestInfo != null) { cPerformanceLogger.LogPerformanceEnd(LogDeep, CM, requestInfo.id, requestInfo.created, _startTime); } if (CM != null) LogMethodEnd(CM); } - - return null; - } - - private TimeSpan GetTicketOverviewCacheDuration(bool useRoleScope) - { - var ticketConfig = Collector?.GetGlobalConfig()?.TicketConfiguration; - var minutes = useRoleScope - ? ticketConfig?.OverviewPollingRole ?? cF4sdTicketConfig.DefaultOverviewPollingRole - : ticketConfig?.OverviewPollingPersonal ?? cF4sdTicketConfig.DefaultOverviewPollingPersonal; - - if (minutes < 1) - minutes = 1; - - return TimeSpan.FromMinutes(minutes); - } - - private bool TryGetTicketOverviewPersonalCache(string sid, DateTime now, out Dictionary counts) - { - counts = null; - if (string.IsNullOrWhiteSpace(sid)) - return false; - - lock (_ticketOverviewCacheLock) - { - if (_ticketOverviewPersonalCache.TryGetValue(sid, out var entry)) - { - if (entry.ExpiresAtUtc > now) - { - counts = new Dictionary(entry.Counts, StringComparer.OrdinalIgnoreCase); - return true; - } - - _ticketOverviewPersonalCache.Remove(sid); - } - } - - return false; - } - - private bool TryGetTicketOverviewRoleCache(Guid roleId, DateTime now, out Dictionary counts) - { - counts = null; - if (roleId == Guid.Empty) - return false; - - lock (_ticketOverviewCacheLock) - { - if (_ticketOverviewRoleCache.TryGetValue(roleId, out var entry)) - { - if (entry.ExpiresAtUtc > now) - { - counts = new Dictionary(entry.Counts, StringComparer.OrdinalIgnoreCase); - return true; - } - - _ticketOverviewRoleCache.Remove(roleId); - } - } - - return false; - } - - private void SetTicketOverviewPersonalCache(string sid, Dictionary counts, DateTime expiresAtUtc) - { - if (string.IsNullOrWhiteSpace(sid)) - return; - - lock (_ticketOverviewCacheLock) - { - _ticketOverviewPersonalCache[sid] = new TicketOverviewCountCacheEntry - { - Counts = new Dictionary(counts ?? new Dictionary(), StringComparer.OrdinalIgnoreCase), - ExpiresAtUtc = expiresAtUtc - }; - } - } - - private void SetTicketOverviewRoleCache(Guid roleId, Dictionary counts, DateTime expiresAtUtc) - { - if (roleId == Guid.Empty) - return; - - lock (_ticketOverviewCacheLock) - { - _ticketOverviewRoleCache[roleId] = new TicketOverviewCountCacheEntry - { - Counts = new Dictionary(counts ?? new Dictionary(), StringComparer.OrdinalIgnoreCase), - ExpiresAtUtc = expiresAtUtc - }; - } - } - - private bool TryGetTicketOverviewRoleIdsFromCache(string sid, DateTime now, out List roleIds) - { - roleIds = null; - if (string.IsNullOrWhiteSpace(sid)) - return false; - - lock (_ticketOverviewCacheLock) - { - if (_ticketOverviewRoleListCache.TryGetValue(sid, out var entry)) - { - if (entry.ExpiresAtUtc > now) - { - roleIds = new List(entry.RoleIds); - return true; - } - - _ticketOverviewRoleListCache.Remove(sid); - } - } - - return false; - } - - private void SetTicketOverviewRoleIdsCache(string sid, IEnumerable roleIds, DateTime expiresAtUtc) - { - if (string.IsNullOrWhiteSpace(sid)) - return; - - var normalized = (roleIds ?? Enumerable.Empty()) - .Where(id => id != Guid.Empty) - .Distinct() - .ToList(); - - lock (_ticketOverviewCacheLock) - { - _ticketOverviewRoleListCache[sid] = new TicketOverviewRoleListCacheEntry - { - RoleIds = normalized, - ExpiresAtUtc = expiresAtUtc - }; - } - } - - private async Task> GetTicketOverviewRoleIdsAsync(string sid, DateTime now, TimeSpan ttl, CancellationToken token) - { - if (TryGetTicketOverviewRoleIdsFromCache(sid, now, out var cached)) - return cached; - - var userInfo = await GetM42UserInfoAsync(sid, token); - var roles = userInfo?.Roles ?? new List(); - var roleIds = roles - .Where(r => r != null && r.Id != Guid.Empty) - .Select(r => r.Id) - .Distinct() - .ToList(); - - SetTicketOverviewRoleIdsCache(sid, roleIds, now.Add(ttl)); - return roleIds; - } - - private async Task>> FetchTicketOverviewCountsByRolesAsync( - IEnumerable roleIds, - IReadOnlyCollection requestedKeys, - 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 roleList = (roleIds ?? Enumerable.Empty()) - .Where(id => id != Guid.Empty) - .Distinct() - .ToList(); - - if (roleList.Count == 0) - return new Dictionary>(); - - var queueOption = GetActivityQueueOption(); - var encodedQueues = queueOption != 0 ? GetEncodedActivityQueues() : null; - - var request = new TicketOverviewCountsByRolesRequest - { - Sid = requestInfo?.userInfo?.AdSid, - RoleGuids = roleList, - Keys = requestedKeys?.Where(k => !string.IsNullOrWhiteSpace(k)).ToList() ?? new List(), - QueueOption = queueOption, - Queues = encodedQueues - }; - - var jsonBody = JsonConvert.SerializeObject(request, Formatting.None, - new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore }); - var content = new StringContent(jsonBody, Encoding.UTF8, "application/json"); - - var wc = await GetWebClient(requestInfo, token); - var res = await wc.HttpEnh.PostAsync(constUrlGetTicketOverviewCountsByRoles, content, token); - if (token.IsCancellationRequested) - return new Dictionary>(); - - if (res?.IsSuccessStatusCode == true) - { - var json = await res.Content.ReadAsStringAsync(); - if (!string.IsNullOrWhiteSpace(json)) - { - var response = JsonConvert.DeserializeObject(json); - if (response?.CountsByRole != null) - { - var result = new Dictionary>(); - foreach (var entry in response.CountsByRole) - { - if (!Guid.TryParse(entry.Key, out var roleId) || roleId == Guid.Empty) - continue; - - var counts = entry.Value ?? new Dictionary(); - result[roleId] = new Dictionary(counts, StringComparer.OrdinalIgnoreCase); - } - return result; - } - } - } - - StartOnlineValidation(); - } - 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 new Dictionary>(); - } - - private Dictionary SumTicketOverviewCounts(IEnumerable> sources) - { - var result = new Dictionary(StringComparer.OrdinalIgnoreCase); - if (sources == null) - return result; - - foreach (var counts in sources) - { - if (counts == null) - continue; - - foreach (var kvp in counts) - { - if (string.IsNullOrWhiteSpace(kvp.Key)) - continue; - - result[kvp.Key] = result.TryGetValue(kvp.Key, out var existing) - ? existing + kvp.Value - : kvp.Value; - } - } - - return result; - } - - private Dictionary FilterTicketOverviewCounts(Dictionary counts, IReadOnlyCollection requestedKeys) - { - if (counts == null) - return new Dictionary(StringComparer.OrdinalIgnoreCase); - - if (requestedKeys == null || requestedKeys.Count == 0) - return new Dictionary(counts, StringComparer.OrdinalIgnoreCase); - - var filtered = new Dictionary(StringComparer.OrdinalIgnoreCase); - foreach (var key in requestedKeys) - { - if (string.IsNullOrWhiteSpace(key)) - continue; - - filtered[key] = counts.TryGetValue(key, out var value) ? value : 0; - } - - return filtered; - } - - private int GetActivityQueueOption() - { - return (int)Collector.InfrastructureConfig.M42Wpm.ActivityQueueFilterOption; - } - - private string GetEncodedActivityQueues() - { - var queues = Collector.InfrastructureConfig.M42Wpm.ActivityQueues; - if (queues == null || queues.Count == 0) - return null; - - return string.Join("|", - queues.Select(q => - string.Format("{0}:{1}", - HttpUtility.UrlEncode(q.QueueName), - HttpUtility.UrlEncode(q.QueueID.ToString()) - ) - ) - ); - } - - private static void AppendQueueFilterQuery(StringBuilder urlBuilder, int queueOption, string encodedQueues) - { - if (queueOption != 0) - urlBuilder.Append("&queueoption=").Append(queueOption); - - if (!string.IsNullOrWhiteSpace(encodedQueues)) - urlBuilder.Append("&queues=").Append(encodedQueues); - } - - private async Task> FetchTicketOverviewCountsAsync( - string sid, - string scope, - IReadOnlyCollection requestedKeys, - cF4sdWebRequestInfo requestInfo, - CancellationToken token) - { - try - { - var urlBuilder = new StringBuilder(constUrlGetTicketOverviewCounts); - urlBuilder.Append("?sid=").Append(HttpUtility.UrlEncode(sid)); - urlBuilder.Append("&scope=").Append(HttpUtility.UrlEncode(scope ?? string.Empty)); - - if (requestedKeys != null && requestedKeys.Count > 0) - { - urlBuilder.Append("&keys=").Append(HttpUtility.UrlEncode(string.Join(",", requestedKeys))); - } - - var queueOption = GetActivityQueueOption(); - var encodedQueues = queueOption != 0 ? GetEncodedActivityQueues() : null; - AppendQueueFilterQuery(urlBuilder, queueOption, encodedQueues); - - var url = urlBuilder.ToString(); - var wc = await GetWebClient(requestInfo, token); - var res = await wc.HttpEnh.GetAsync(url, token); - if (token.IsCancellationRequested) - return new Dictionary(StringComparer.OrdinalIgnoreCase); - - if (res?.IsSuccessStatusCode == true) - { - var json = await res.Content.ReadAsStringAsync(); - if (!string.IsNullOrWhiteSpace(json)) - { - var root = JsonConvert.DeserializeObject(json); - var countsToken = root?["counts"] as JObject; - var counts = new Dictionary(StringComparer.OrdinalIgnoreCase); - if (countsToken != null) - { - foreach (var prop in countsToken.Properties()) - { - if (prop?.Name == null) - continue; - var value = prop.Value?.Type == JTokenType.Integer - ? prop.Value.Value() - : int.TryParse(prop.Value?.ToString(), out var parsed) ? parsed : 0; - counts[prop.Name] = value; - } - } - return counts; - } - } - - StartOnlineValidation(); - } - catch (Exception E) - { - LogException(E); - } - - return new Dictionary(StringComparer.OrdinalIgnoreCase); - } - - public async Task> GetTicketOverviewCountsAsync(IEnumerable keys, bool useRoleScope, 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 (!await CheckOnline()) - return new Dictionary(StringComparer.OrdinalIgnoreCase); - - var sid = requestInfo?.userInfo?.AdSid; - if (string.IsNullOrWhiteSpace(sid)) - return new Dictionary(StringComparer.OrdinalIgnoreCase); - - var normalizedKeys = (keys ?? Enumerable.Empty()) - .Where(k => !string.IsNullOrWhiteSpace(k)) - .Distinct(StringComparer.OrdinalIgnoreCase) - .ToList(); - var now = DateTime.UtcNow; - var ttl = GetTicketOverviewCacheDuration(useRoleScope); - - if (!useRoleScope) - { - if (TryGetTicketOverviewPersonalCache(sid, now, out var cachedPersonal)) - return FilterTicketOverviewCounts(cachedPersonal, normalizedKeys); - - var counts = await FetchTicketOverviewCountsAsync(sid, "personal", normalizedKeys, requestInfo, Token); - if (Token.IsCancellationRequested) - return new Dictionary(StringComparer.OrdinalIgnoreCase); - - SetTicketOverviewPersonalCache(sid, counts, now.Add(ttl)); - return FilterTicketOverviewCounts(counts, normalizedKeys); - } - - var roleIds = await GetTicketOverviewRoleIdsAsync(sid, now, ttl, Token); - if (roleIds == null || roleIds.Count == 0) - return new Dictionary(StringComparer.OrdinalIgnoreCase); - - var countsByRole = new Dictionary>(); - var missingRoles = new List(); - - foreach (var roleId in roleIds) - { - if (TryGetTicketOverviewRoleCache(roleId, now, out var cachedRole)) - { - countsByRole[roleId] = cachedRole; - } - else - { - missingRoles.Add(roleId); - } - } - - if (missingRoles.Count > 0) - { - var fetched = await FetchTicketOverviewCountsByRolesAsync(missingRoles, normalizedKeys, requestInfo, LogDeep + 1, Token); - var expiresAt = DateTime.UtcNow.Add(ttl); - - foreach (var roleId in missingRoles) - { - if (!fetched.TryGetValue(roleId, out var roleCounts)) - roleCounts = new Dictionary(StringComparer.OrdinalIgnoreCase); - - SetTicketOverviewRoleCache(roleId, roleCounts, expiresAt); - countsByRole[roleId] = roleCounts; - } - } - - var summed = SumTicketOverviewCounts(countsByRole.Values); - return FilterTicketOverviewCounts(summed, normalizedKeys); - } - 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 new Dictionary(StringComparer.OrdinalIgnoreCase); - } - - public async Task> GetTicketOverviewRelationsAsync(string key, bool useRoleScope, int count, 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 (!await CheckOnline()) - return new List(); - - if (string.IsNullOrWhiteSpace(key)) - return new List(); - - var sid = requestInfo?.userInfo?.AdSid; - if (string.IsNullOrWhiteSpace(sid)) - return new List(); - - var scope = useRoleScope ? "role" : "personal"; - var urlBuilder = new StringBuilder(constUrlGetTicketOverviewRelations); - urlBuilder.Append("?sid=").Append(HttpUtility.UrlEncode(sid)); - urlBuilder.Append("&scope=").Append(HttpUtility.UrlEncode(scope)); - urlBuilder.Append("&key=").Append(HttpUtility.UrlEncode(key)); - urlBuilder.Append("&count=").Append(Math.Max(0, count)); - - var queueOption = GetActivityQueueOption(); - var encodedQueues = queueOption != 0 ? GetEncodedActivityQueues() : null; - AppendQueueFilterQuery(urlBuilder, queueOption, encodedQueues); - - var url = urlBuilder.ToString(); - - var wc = await GetWebClient(requestInfo, Token); - var res = await wc.HttpEnh.GetAsync(url, Token); - if (Token.IsCancellationRequested) - return new List(); - - if (res?.IsSuccessStatusCode == true) - { - var json = await res.Content.ReadAsStringAsync(); - if (!string.IsNullOrWhiteSpace(json)) - { - var relations = JsonConvert.DeserializeObject>(json); - if (relations != null) - { - await EnsureTicketOverviewRelationIdentitiesAsync(relations, requestInfo, LogDeep + 1, Token); - return relations; - } - } - } - - StartOnlineValidation(); - } - 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 new List(); - } - - private async Task EnsureTicketOverviewRelationIdentitiesAsync( - List relations, - cF4sdWebRequestInfo requestInfo, - int logDeep, - CancellationToken token) - { - if (relations == null) - return; - - foreach (var relation in relations) - { - if (token.IsCancellationRequested || relation == null) - return; - - var identities = relation.Identities ?? new cF4sdIdentityList(); - relation.Identities = identities; - - if (!identities.Any(i => i.Class == enumFasdInformationClass.Ticket) && relation.id != Guid.Empty) - { - identities.Add(new cF4sdIdentityEntry - { - Class = enumFasdInformationClass.Ticket, - Id = relation.id - }); - } - - if (relation.Infos == null) - relation.Infos = new Dictionary(StringComparer.OrdinalIgnoreCase); - - if (!relation.Infos.ContainsKey("Sids") && relation.Infos.TryGetValue("UserSid", out var userSid) && !string.IsNullOrWhiteSpace(userSid)) - relation.Infos["Sids"] = userSid; - - Guid userId = Guid.Empty; - if (relation.Infos.Count > 0) - { - if (relation.Infos.TryGetValue("Sids", out var sidsValue)) - { - var sids = sidsValue?.Split(',') - .Select(v => v.Trim()) - .Where(v => !string.IsNullOrWhiteSpace(v)) - .ToList(); - - if (sids != null && sids.Count > 0) - { - var resolved = await Collector.UserSearchBySidsAsync(sids, token, requestInfo, logDeep + 1); - var user = resolved?.Values?.FirstOrDefault()?.FirstOrDefault(); - if (user?.Result != null) - userId = user.Result.id; - } - } - - if (userId == Guid.Empty && relation.Infos.TryGetValue("UserAccount", out var userAccount)) - { - relation.Infos.TryGetValue("UserDomain", out var userDomain); - if (!string.IsNullOrWhiteSpace(userAccount)) - { - var resolved = await Collector.GetUserIdFromAccountAsync(userAccount, userDomain, requestInfo, logDeep + 1, token); - if (resolved.HasValue) - userId = resolved.Value; - } - } - - if (userId == Guid.Empty) - { - if (relation.Infos.TryGetValue("UserId", out var userIdString) || - relation.Infos.TryGetValue("UserGuid", out userIdString) || - relation.Infos.TryGetValue("UserIdentityId", out userIdString)) - { - Guid.TryParse(userIdString, out userId); - } - } - } - - if (userId == Guid.Empty) - continue; - - identities.RemoveAll(i => i.Class == enumFasdInformationClass.User); - identities.Add(new cF4sdIdentityEntry - { - Class = enumFasdInformationClass.User, - Id = userId - }); - - relation.Infos["UserId"] = userId.ToString(); - relation.Infos["UserGuid"] = userId.ToString(); - } - } + + return null; + } + + private TimeSpan GetTicketOverviewCacheDuration(bool useRoleScope) + { + var ticketConfig = Collector?.GetGlobalConfig()?.TicketConfiguration; + var minutes = useRoleScope + ? ticketConfig?.OverviewPollingRole ?? cF4sdTicketConfig.DefaultOverviewPollingRole + : ticketConfig?.OverviewPollingPersonal ?? cF4sdTicketConfig.DefaultOverviewPollingPersonal; + + if (minutes < 1) + minutes = 1; + + return TimeSpan.FromMinutes(minutes); + } + + private bool TryGetTicketOverviewPersonalCache(string sid, DateTime now, out Dictionary counts) + { + counts = null; + if (string.IsNullOrWhiteSpace(sid)) + return false; + + lock (_ticketOverviewCacheLock) + { + if (_ticketOverviewPersonalCache.TryGetValue(sid, out var entry)) + { + if (entry.ExpiresAtUtc > now) + { + counts = new Dictionary(entry.Counts, StringComparer.OrdinalIgnoreCase); + return true; + } + + _ticketOverviewPersonalCache.Remove(sid); + } + } + + return false; + } + + private bool TryGetTicketOverviewRoleCache(Guid roleId, DateTime now, out Dictionary counts) + { + counts = null; + if (roleId == Guid.Empty) + return false; + + lock (_ticketOverviewCacheLock) + { + if (_ticketOverviewRoleCache.TryGetValue(roleId, out var entry)) + { + if (entry.ExpiresAtUtc > now) + { + counts = new Dictionary(entry.Counts, StringComparer.OrdinalIgnoreCase); + return true; + } + + _ticketOverviewRoleCache.Remove(roleId); + } + } + + return false; + } + + private void SetTicketOverviewPersonalCache(string sid, Dictionary counts, DateTime expiresAtUtc) + { + if (string.IsNullOrWhiteSpace(sid)) + return; + + lock (_ticketOverviewCacheLock) + { + _ticketOverviewPersonalCache[sid] = new TicketOverviewCountCacheEntry + { + Counts = new Dictionary(counts ?? new Dictionary(), StringComparer.OrdinalIgnoreCase), + ExpiresAtUtc = expiresAtUtc + }; + } + } + + private void SetTicketOverviewRoleCache(Guid roleId, Dictionary counts, DateTime expiresAtUtc) + { + if (roleId == Guid.Empty) + return; + + lock (_ticketOverviewCacheLock) + { + _ticketOverviewRoleCache[roleId] = new TicketOverviewCountCacheEntry + { + Counts = new Dictionary(counts ?? new Dictionary(), StringComparer.OrdinalIgnoreCase), + ExpiresAtUtc = expiresAtUtc + }; + } + } + + private bool TryGetTicketOverviewRoleIdsFromCache(string sid, DateTime now, out List roleIds) + { + roleIds = null; + if (string.IsNullOrWhiteSpace(sid)) + return false; + + lock (_ticketOverviewCacheLock) + { + if (_ticketOverviewRoleListCache.TryGetValue(sid, out var entry)) + { + if (entry.ExpiresAtUtc > now) + { + roleIds = new List(entry.RoleIds); + return true; + } + + _ticketOverviewRoleListCache.Remove(sid); + } + } + + return false; + } + + private void SetTicketOverviewRoleIdsCache(string sid, IEnumerable roleIds, DateTime expiresAtUtc) + { + if (string.IsNullOrWhiteSpace(sid)) + return; + + var normalized = (roleIds ?? Enumerable.Empty()) + .Where(id => id != Guid.Empty) + .Distinct() + .ToList(); + + lock (_ticketOverviewCacheLock) + { + _ticketOverviewRoleListCache[sid] = new TicketOverviewRoleListCacheEntry + { + RoleIds = normalized, + ExpiresAtUtc = expiresAtUtc + }; + } + } + + private async Task> GetTicketOverviewRoleIdsAsync(string sid, DateTime now, TimeSpan ttl, CancellationToken token) + { + if (TryGetTicketOverviewRoleIdsFromCache(sid, now, out var cached)) + return cached; + + var userInfo = await GetM42UserInfoAsync(sid, token); + var roles = userInfo?.Roles ?? new List(); + var roleIds = roles + .Where(r => r != null && r.Id != Guid.Empty) + .Select(r => r.Id) + .Distinct() + .ToList(); + + SetTicketOverviewRoleIdsCache(sid, roleIds, now.Add(ttl)); + return roleIds; + } + + private async Task>> FetchTicketOverviewCountsByRolesAsync( + IEnumerable roleIds, + IReadOnlyCollection requestedKeys, + 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 roleList = (roleIds ?? Enumerable.Empty()) + .Where(id => id != Guid.Empty) + .Distinct() + .ToList(); + + if (roleList.Count == 0) + return new Dictionary>(); + + var queueOption = GetActivityQueueOption(); + var encodedQueues = queueOption != 0 ? GetEncodedActivityQueues() : null; + + var request = new TicketOverviewCountsByRolesRequest + { + Sid = requestInfo?.userInfo?.AdSid, + RoleGuids = roleList, + Keys = requestedKeys?.Where(k => !string.IsNullOrWhiteSpace(k)).ToList() ?? new List(), + QueueOption = queueOption, + Queues = encodedQueues + }; + + var jsonBody = JsonConvert.SerializeObject(request, Formatting.None, + new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore }); + var content = new StringContent(jsonBody, Encoding.UTF8, "application/json"); + + var wc = await GetWebClient(requestInfo, token); + var res = await wc.HttpEnh.PostAsync(constUrlGetTicketOverviewCountsByRoles, content, token); + if (token.IsCancellationRequested) + return new Dictionary>(); + + if (res?.IsSuccessStatusCode == true) + { + var json = await res.Content.ReadAsStringAsync(); + if (!string.IsNullOrWhiteSpace(json)) + { + var response = JsonConvert.DeserializeObject(json); + if (response?.CountsByRole != null) + { + var result = new Dictionary>(); + foreach (var entry in response.CountsByRole) + { + if (!Guid.TryParse(entry.Key, out var roleId) || roleId == Guid.Empty) + continue; + + var counts = entry.Value ?? new Dictionary(); + result[roleId] = new Dictionary(counts, StringComparer.OrdinalIgnoreCase); + } + return result; + } + } + } + + StartOnlineValidation(); + } + 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 new Dictionary>(); + } + + private Dictionary SumTicketOverviewCounts(IEnumerable> sources) + { + var result = new Dictionary(StringComparer.OrdinalIgnoreCase); + if (sources == null) + return result; + + foreach (var counts in sources) + { + if (counts == null) + continue; + + foreach (var kvp in counts) + { + if (string.IsNullOrWhiteSpace(kvp.Key)) + continue; + + result[kvp.Key] = result.TryGetValue(kvp.Key, out var existing) + ? existing + kvp.Value + : kvp.Value; + } + } + + return result; + } + + private Dictionary FilterTicketOverviewCounts(Dictionary counts, IReadOnlyCollection requestedKeys) + { + if (counts == null) + return new Dictionary(StringComparer.OrdinalIgnoreCase); + + if (requestedKeys == null || requestedKeys.Count == 0) + return new Dictionary(counts, StringComparer.OrdinalIgnoreCase); + + var filtered = new Dictionary(StringComparer.OrdinalIgnoreCase); + foreach (var key in requestedKeys) + { + if (string.IsNullOrWhiteSpace(key)) + continue; + + filtered[key] = counts.TryGetValue(key, out var value) ? value : 0; + } + + return filtered; + } + + private int GetActivityQueueOption() + { + return (int)Collector.InfrastructureConfig.M42Wpm.ActivityQueueFilterOption; + } + + private string GetEncodedActivityQueues() + { + var queues = Collector.InfrastructureConfig.M42Wpm.ActivityQueues; + if (queues == null || queues.Count == 0) + return null; + + var encodedQueues = queues + .Where(q => q != null) + .Select(q => + { + if (q.QueueID != Guid.Empty) + { + // If both are configured, prefer ID-based filtering and ignore the name. + return ":" + HttpUtility.UrlEncode(q.QueueID.ToString()); + } + + if (!string.IsNullOrWhiteSpace(q.QueueName)) + return HttpUtility.UrlEncode(q.QueueName.Trim()); + + return null; + }) + .Where(entry => !string.IsNullOrWhiteSpace(entry)) + .Distinct(StringComparer.OrdinalIgnoreCase) + .ToList(); + + return encodedQueues.Count > 0 ? string.Join("|", encodedQueues) : null; + } + + private static void AppendQueueFilterQuery(StringBuilder urlBuilder, int queueOption, string encodedQueues) + { + if (queueOption != 0) + urlBuilder.Append("&queueoption=").Append(queueOption); + + if (!string.IsNullOrWhiteSpace(encodedQueues)) + urlBuilder.Append("&queues=").Append(encodedQueues); + } + + private async Task> FetchTicketOverviewCountsAsync( + string sid, + string scope, + IReadOnlyCollection requestedKeys, + cF4sdWebRequestInfo requestInfo, + CancellationToken token) + { + try + { + var urlBuilder = new StringBuilder(constUrlGetTicketOverviewCounts); + urlBuilder.Append("?sid=").Append(HttpUtility.UrlEncode(sid)); + urlBuilder.Append("&scope=").Append(HttpUtility.UrlEncode(scope ?? string.Empty)); + + if (requestedKeys != null && requestedKeys.Count > 0) + { + urlBuilder.Append("&keys=").Append(HttpUtility.UrlEncode(string.Join(",", requestedKeys))); + } + + var queueOption = GetActivityQueueOption(); + var encodedQueues = queueOption != 0 ? GetEncodedActivityQueues() : null; + AppendQueueFilterQuery(urlBuilder, queueOption, encodedQueues); + + var url = urlBuilder.ToString(); + var wc = await GetWebClient(requestInfo, token); + var res = await wc.HttpEnh.GetAsync(url, token); + if (token.IsCancellationRequested) + return new Dictionary(StringComparer.OrdinalIgnoreCase); + + if (res?.IsSuccessStatusCode == true) + { + var json = await res.Content.ReadAsStringAsync(); + if (!string.IsNullOrWhiteSpace(json)) + { + var root = JsonConvert.DeserializeObject(json); + var countsToken = root?["counts"] as JObject; + var counts = new Dictionary(StringComparer.OrdinalIgnoreCase); + if (countsToken != null) + { + foreach (var prop in countsToken.Properties()) + { + if (prop?.Name == null) + continue; + var value = prop.Value?.Type == JTokenType.Integer + ? prop.Value.Value() + : int.TryParse(prop.Value?.ToString(), out var parsed) ? parsed : 0; + counts[prop.Name] = value; + } + } + return counts; + } + } + + StartOnlineValidation(); + } + catch (Exception E) + { + LogException(E); + } + + return new Dictionary(StringComparer.OrdinalIgnoreCase); + } + + public async Task> GetTicketOverviewCountsAsync(IEnumerable keys, bool useRoleScope, 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 (!await CheckOnline()) + return new Dictionary(StringComparer.OrdinalIgnoreCase); + + var sid = requestInfo?.userInfo?.AdSid; + if (string.IsNullOrWhiteSpace(sid)) + return new Dictionary(StringComparer.OrdinalIgnoreCase); + + var normalizedKeys = (keys ?? Enumerable.Empty()) + .Where(k => !string.IsNullOrWhiteSpace(k)) + .Distinct(StringComparer.OrdinalIgnoreCase) + .ToList(); + var now = DateTime.UtcNow; + var ttl = GetTicketOverviewCacheDuration(useRoleScope); + + if (!useRoleScope) + { + // process thr personal scope + if (TryGetTicketOverviewPersonalCache(sid, now, out var cachedPersonal)) + return FilterTicketOverviewCounts(cachedPersonal, normalizedKeys); + + var counts = await FetchTicketOverviewCountsAsync(sid, "personal", normalizedKeys, requestInfo, Token); + if (Token.IsCancellationRequested) + return new Dictionary(StringComparer.OrdinalIgnoreCase); + + SetTicketOverviewPersonalCache(sid, counts, now.Add(ttl)); + return FilterTicketOverviewCounts(counts, normalizedKeys); + } + + // process the role scope + var roleIds = await GetTicketOverviewRoleIdsAsync(sid, now, ttl, Token); + if (roleIds == null || roleIds.Count == 0) + return new Dictionary(StringComparer.OrdinalIgnoreCase); + + var countsByRole = new Dictionary>(); + var missingRoles = new List(); + + foreach (var roleId in roleIds) + { + if (TryGetTicketOverviewRoleCache(roleId, now, out var cachedRole)) + { + countsByRole[roleId] = cachedRole; + } + else + { + missingRoles.Add(roleId); + } + } + + if (missingRoles.Count > 0) + { + var fetched = await FetchTicketOverviewCountsByRolesAsync(missingRoles, normalizedKeys, requestInfo, LogDeep + 1, Token); + var expiresAt = DateTime.UtcNow.Add(ttl); + + foreach (var roleId in missingRoles) + { + if (!fetched.TryGetValue(roleId, out var roleCounts)) + roleCounts = new Dictionary(StringComparer.OrdinalIgnoreCase); + + SetTicketOverviewRoleCache(roleId, roleCounts, expiresAt); + countsByRole[roleId] = roleCounts; + } + } + + var summed = SumTicketOverviewCounts(countsByRole.Values); + return FilterTicketOverviewCounts(summed, normalizedKeys); + } + 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 new Dictionary(StringComparer.OrdinalIgnoreCase); + } + + public async Task> GetTicketOverviewRelationsAsync(string key, bool useRoleScope, int count, 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 (!await CheckOnline()) + return new List(); + + if (string.IsNullOrWhiteSpace(key)) + return new List(); + + var sid = requestInfo?.userInfo?.AdSid; + if (string.IsNullOrWhiteSpace(sid)) + return new List(); + + var scope = useRoleScope ? "role" : "personal"; + var urlBuilder = new StringBuilder(constUrlGetTicketOverviewRelations); + urlBuilder.Append("?sid=").Append(HttpUtility.UrlEncode(sid)); + urlBuilder.Append("&scope=").Append(HttpUtility.UrlEncode(scope)); + urlBuilder.Append("&key=").Append(HttpUtility.UrlEncode(key)); + urlBuilder.Append("&count=").Append(Math.Max(0, count)); + + var queueOption = GetActivityQueueOption(); + var encodedQueues = queueOption != 0 ? GetEncodedActivityQueues() : null; + AppendQueueFilterQuery(urlBuilder, queueOption, encodedQueues); + + var url = urlBuilder.ToString(); + + var wc = await GetWebClient(requestInfo, Token); + var res = await wc.HttpEnh.GetAsync(url, Token); + if (Token.IsCancellationRequested) + return new List(); + + if (res?.IsSuccessStatusCode == true) + { + var json = await res.Content.ReadAsStringAsync(); + if (!string.IsNullOrWhiteSpace(json)) + { + var relations = JsonConvert.DeserializeObject>(json); + if (relations != null) + return relations; + } + } + + StartOnlineValidation(); + } + 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 new List(); + } public override async Task> GetTableResultsVirtualAsync(List Tables, Dictionary Identities, DateTime RefTime, int MaxAge, bool instantly, Guid? CacheId, CancellationToken Token, cF4sdWebRequestInfo requestInfo, int LogDeep) { @@ -2180,7 +2284,8 @@ namespace C4IT.DataHistoryProvider { TokenType = cF4SDTokenRegistration.enumTokenType.M42Bearer, UserId = userInfo.Id, - Secret = _m42.BearerToken + Secret = _m42.BearerToken, + IdRemote = m42UserInfo.User.ObjectID?.ToString("D") }; // set the new bearer token into the token cache @@ -2264,16 +2369,16 @@ namespace C4IT.DataHistoryProvider } var _cols = Table.KeyColumns.First().SourceName; - var _QueryRelationInfo = new cM42DataQueryRelationInfo() - { - EntityClassName = _template.EntityClassName, - EntityTypeName = string.Join(",", _template.EntityTypeNames), - DataQueryName = string.IsNullOrWhiteSpace(_template.DataQueryName) ? null : _template.DataQueryName, - search = search, - WhereExpression = _whereExpr, - pageSize = 0, - page = 0, - totalCounted = true, + var _QueryRelationInfo = new cM42DataQueryRelationInfo() + { + EntityClassName = _template.EntityClassName, + EntityTypeName = string.Join(",", _template.EntityTypeNames), + DataQueryName = string.IsNullOrWhiteSpace(_template.DataQueryName) ? null : _template.DataQueryName, + search = search, + WhereExpression = _whereExpr, + pageSize = 0, + page = 0, + totalCounted = true, columns = _cols }; @@ -2323,15 +2428,15 @@ namespace C4IT.DataHistoryProvider var _cols = Table.KeyColumns.First().SourceName; - var _QueryRelationInfo = new cM42DataQueryRelationInfo() - { - EntityClassName = _template.EntityClassName, - EntityTypeName = string.Join(",", _template.EntityTypeNames), - DataQueryName = string.IsNullOrWhiteSpace(_template.DataQueryName) ? null : _template.DataQueryName, - WhereExpression = _whereExpr, - search = search, - pageSize = PageSize, - page = Page, + var _QueryRelationInfo = new cM42DataQueryRelationInfo() + { + EntityClassName = _template.EntityClassName, + EntityTypeName = string.Join(",", _template.EntityTypeNames), + DataQueryName = string.IsNullOrWhiteSpace(_template.DataQueryName) ? null : _template.DataQueryName, + WhereExpression = _whereExpr, + search = search, + pageSize = PageSize, + page = Page, columns = _cols }; @@ -2444,6 +2549,7 @@ namespace C4IT.DataHistoryProvider { if (_M42UserInfo.BearerValidUntil > DateTime.UtcNow && TokenRegistration.AutoCreatePermanent && _M42UserInfo.User?.Id != null) { + TokenRegistration.IdRemote = _M42UserInfo.User.ObjectID?.ToString("D"); var _res2 = await CreateApiToken(TokenRegistration, _M42UserInfo, Token, requestInfo, LogDeep + 1); if (Token.IsCancellationRequested) return null; @@ -2503,7 +2609,7 @@ namespace C4IT.DataHistoryProvider try { // obtain token name - var tokenName = $"F4SD_{UserInfo.User.LastName ?? ""}_{UserInfo.User.FirstName ?? ""}_{UserInfo.User.Id}"; + var tokenName = $"F4SD_{UserInfo?.User?.LastName ?? ""}_{UserInfo?.User?.FirstName ?? ""}_{UserInfo?.User?.Id}"; // create M42 web client var _m42 = new cM42WebClient(Collector.InfrastructureConfig.M42Wpm.Server, UseSSL: true); @@ -2561,6 +2667,7 @@ namespace C4IT.DataHistoryProvider UserId = TokenRegistration.UserId, Name = _tokenModel?.Name, Secret = _tokenModel.ApiToken, + IdRemote = UserInfo?.User?.Id?.ToString("D"), AutoCreatePermanent = false }; @@ -2668,24 +2775,24 @@ namespace C4IT.DataHistoryProvider LogEntry($"No initiating user found with state active (710) and AD Common Name: {ticketInfo.User}", LogLevels.Error); return null; } - Guid? requestCategoryId = ticketInfo.Category; - if (requestCategoryId.HasValue && requestCategoryId.Value == Guid.Empty) - { - requestCategoryId = null; - } - - var ticketInfo2 = new cM42TicketInfo() - { - Subject = ticketInfo.Summary, - DescriptionHTML = ticketInfo.DescriptionHtml, - SolutionHTML = ticketInfo.SolutionHtml, - User = userID, + Guid? requestCategoryId = ticketInfo.Category; + if (requestCategoryId.HasValue && requestCategoryId.Value == Guid.Empty) + { + requestCategoryId = null; + } + + var ticketInfo2 = new cM42TicketInfo() + { + Subject = ticketInfo.Summary, + DescriptionHTML = ticketInfo.DescriptionHtml, + SolutionHTML = ticketInfo.SolutionHtml, + User = userID, Asset = assetId, - Service = ticketInfo.AffectedService, - Template = ticketInfo.Quickcall, - Category = requestCategoryId, - EntryBy = 20500 - }; + Service = ticketInfo.AffectedService, + Template = ticketInfo.Quickcall, + Category = requestCategoryId, + EntryBy = 20500 + }; var quickCallFragments = quickCallFragmentListTask?.Result; if (quickCallFragments != null) @@ -2719,8 +2826,8 @@ namespace C4IT.DataHistoryProvider if (Guid.TryParse((string)quickCallData?.Service, out Guid service)) ticketInfo2.Service = ticketInfo2.Service ?? service; - if (!ticketInfo2.Category.HasValue && Guid.TryParse((string)quickCallData?.Category, out Guid category)) - ticketInfo2.Category = category; + if (!ticketInfo2.Category.HasValue && Guid.TryParse((string)quickCallData?.Category, out Guid category)) + ticketInfo2.Category = category; if (bool.TryParse((string)quickCallData?.NotifyResponsible, out bool notifyResponsible)) ticketInfo2.NotifyResponsible = notifyResponsible; @@ -2813,100 +2920,100 @@ namespace C4IT.DataHistoryProvider return null; } - private async Task ProtocollSupportCaseProtocollEntries(List supportCaseProtocollEntries, Guid ticketId, cF4sdWebRequestInfo requestInfo, int logDeep, CancellationToken token) - { - foreach (var protocollEntry in supportCaseProtocollEntries.OrderBy(entry => entry.Time)) - { - cCreateJournalEntryInfo _entry = null; - if (protocollEntry is QuickActionProtocollEntry quickActionEntry) - { - switch (quickActionEntry.ResultCode) - { - case -1: - case 1: - case 0: - var entryType = quickActionEntry.WasRunningOnAffectedDevice ? 20602 : 20601; - _entry = new cCreateJournalEntryInfo() - { - Comments = "", - ObjectId = ticketId, - EntryType = entryType, - Parameters = new List() { new cCreateJournalEntryInfo.Parameter() - { - Name = "result", - Value = quickActionEntry.HtmlContent ?? quickActionEntry.MeasureValues ?? quickActionEntry.ErrorMessage - }, - new cCreateJournalEntryInfo.Parameter() - { - Name = "quickActionName", - Value = quickActionEntry.Name - }, - new cCreateJournalEntryInfo.Parameter() - { - Name = "QuickActionId", - Value = quickActionEntry.Id - }, - new cCreateJournalEntryInfo.Parameter() - { - Name = "quickActionExecutionType", - Value = quickActionEntry.ExecutionTypeId - }, - new cCreateJournalEntryInfo.Parameter() - { - Name = "ExecutionTime", - Value = quickActionEntry.Time - }, - new cCreateJournalEntryInfo.Parameter() - { - Name = "AffectedDeviceName", - Value = quickActionEntry.AffectedDeviceName - },} - }; - break; - default: - break; - } - } - else if (protocollEntry is QuickTipStepProtocollEntry quickTipEntry) - { - _entry = new cCreateJournalEntryInfo() - { - Comments = "", - ObjectId = ticketId, - EntryType = 20605, - Parameters = new List() { new cCreateJournalEntryInfo.Parameter() - { - Name = "result", - Value = quickTipEntry.HtmlContent ?? quickTipEntry.AsciiContent - }, - new cCreateJournalEntryInfo.Parameter() - { - Name = "quickTipName", - Value = quickTipEntry.Name - }, - new cCreateJournalEntryInfo.Parameter() - { - Name = "QuickTipId", - Value = quickTipEntry.Id - }, - new cCreateJournalEntryInfo.Parameter() - { - Name = "ExecutionTime", - Value = quickTipEntry.Time - }, - new cCreateJournalEntryInfo.Parameter() - { - Name = "WasSuccessfull", - Value = quickTipEntry.WasSuccessfull - },} - }; - } - - if (_entry != null) - { - await CreateM42JournalAsync(_entry, requestInfo, logDeep + 1, token); - if (token.IsCancellationRequested) return; - } + private async Task ProtocollSupportCaseProtocollEntries(List supportCaseProtocollEntries, Guid ticketId, cF4sdWebRequestInfo requestInfo, int logDeep, CancellationToken token) + { + foreach (var protocollEntry in supportCaseProtocollEntries.OrderBy(entry => entry.Time)) + { + cCreateJournalEntryInfo _entry = null; + if (protocollEntry is QuickActionProtocollEntry quickActionEntry) + { + switch (quickActionEntry.ResultCode) + { + case -1: + case 1: + case 0: + var entryType = quickActionEntry.WasRunningOnAffectedDevice ? 20602 : 20601; + _entry = new cCreateJournalEntryInfo() + { + Comments = "", + ObjectId = ticketId, + EntryType = entryType, + Parameters = new List() { new cCreateJournalEntryInfo.Parameter() + { + Name = "result", + Value = quickActionEntry.HtmlContent ?? quickActionEntry.MeasureValues ?? quickActionEntry.ErrorMessage + }, + new cCreateJournalEntryInfo.Parameter() + { + Name = "quickActionName", + Value = quickActionEntry.Name + }, + new cCreateJournalEntryInfo.Parameter() + { + Name = "QuickActionId", + Value = quickActionEntry.Id + }, + new cCreateJournalEntryInfo.Parameter() + { + Name = "quickActionExecutionType", + Value = quickActionEntry.ExecutionTypeId + }, + new cCreateJournalEntryInfo.Parameter() + { + Name = "ExecutionTime", + Value = quickActionEntry.Time + }, + new cCreateJournalEntryInfo.Parameter() + { + Name = "AffectedDeviceName", + Value = quickActionEntry.AffectedDeviceName + },} + }; + break; + default: + break; + } + } + else if (protocollEntry is QuickTipStepProtocollEntry quickTipEntry) + { + _entry = new cCreateJournalEntryInfo() + { + Comments = "", + ObjectId = ticketId, + EntryType = 20605, + Parameters = new List() { new cCreateJournalEntryInfo.Parameter() + { + Name = "result", + Value = quickTipEntry.HtmlContent ?? quickTipEntry.AsciiContent + }, + new cCreateJournalEntryInfo.Parameter() + { + Name = "quickTipName", + Value = quickTipEntry.Name + }, + new cCreateJournalEntryInfo.Parameter() + { + Name = "QuickTipId", + Value = quickTipEntry.Id + }, + new cCreateJournalEntryInfo.Parameter() + { + Name = "ExecutionTime", + Value = quickTipEntry.Time + }, + new cCreateJournalEntryInfo.Parameter() + { + Name = "WasSuccessfull", + Value = quickTipEntry.WasSuccessfull + },} + }; + } + + if (_entry != null) + { + await CreateM42JournalAsync(_entry, requestInfo, logDeep + 1, token); + if (token.IsCancellationRequested) return; + } } } @@ -2989,15 +3096,15 @@ namespace C4IT.DataHistoryProvider var ticketID = (Guid)ticketInfo.Ticket; var ticket = await GetObjectAsync(activityType, ticketID, false, requestInfo, LogDeep + 1, token); - if (ticketInfo.Quickcall.HasValue && ticketInfo.Quickcall.Value != Guid.Empty) - { - ticket["SPSActivityClassIncident"]["Quickcall"] = ticketInfo.Quickcall.Value; - } - - if (ticketInfo.Category.HasValue && ticketInfo.Category.Value != Guid.Empty) - { - ticket["SPSActivityClassIncident"]["Category"] = ticketInfo.Category.Value; - } + if (ticketInfo.Quickcall.HasValue && ticketInfo.Quickcall.Value != Guid.Empty) + { + ticket["SPSActivityClassIncident"]["Quickcall"] = ticketInfo.Quickcall.Value; + } + + if (ticketInfo.Category.HasValue && ticketInfo.Category.Value != Guid.Empty) + { + ticket["SPSActivityClassIncident"]["Category"] = ticketInfo.Category.Value; + } if (ticketInfo.AffectedService.HasValue && ticketInfo.AffectedService.Value == Guid.Empty) { ticket["SPSActivityClassBase"]["Service"] = null; @@ -3260,30 +3367,30 @@ namespace C4IT.DataHistoryProvider Guid.TryParse(res, out ticketId); } } - else - { - await UpdateM42TicketAsync(ticketInfo, requestInfo, LogDeep + 1, token); - if (token.IsCancellationRequested) return null; - - ticketId = (Guid)ticketInfo.Ticket; - } - - if ((!ticketInfo.Ticket.HasValue || ticketInfo.Ticket.Value == Guid.Empty) && ticketId != Guid.Empty) - { - ticketInfo.Ticket = ticketId; - } - - var isTicket = await ResolveIsTicketAsync(ticketInfo, ticketId, requestInfo, LogDeep + 1, token); - if (token.IsCancellationRequested) return null; - - if (isTicket) - { - await TransformM42TicketAsync(ticketInfo, requestInfo, LogDeep + 1, token); - } - - var url = constUrlTicketClose; - var ticket = new cApiM42TicketClosureInfo() - { + else + { + await UpdateM42TicketAsync(ticketInfo, requestInfo, LogDeep + 1, token); + if (token.IsCancellationRequested) return null; + + ticketId = (Guid)ticketInfo.Ticket; + } + + if ((!ticketInfo.Ticket.HasValue || ticketInfo.Ticket.Value == Guid.Empty) && ticketId != Guid.Empty) + { + ticketInfo.Ticket = ticketId; + } + + var isTicket = await ResolveIsTicketAsync(ticketInfo, ticketId, requestInfo, LogDeep + 1, token); + if (token.IsCancellationRequested) return null; + + if (isTicket) + { + await TransformM42TicketAsync(ticketInfo, requestInfo, LogDeep + 1, token); + } + + var url = constUrlTicketClose; + var ticket = new cApiM42TicketClosureInfo() + { ObjectIds = new List() { ticketId }, Comments = ticketInfo.SolutionHtml, ErrorType = (int)ticketInfo.ErrorType == -1 ? null : ticketInfo.ErrorType, @@ -3435,10 +3542,10 @@ namespace C4IT.DataHistoryProvider return false; } - public async Task TransformM42TicketAsync(cApiM42Ticket ticketInfo, 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); } + public async Task TransformM42TicketAsync(cApiM42Ticket ticketInfo, 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 @@ -3483,110 +3590,110 @@ namespace C4IT.DataHistoryProvider if (cPerformanceLogger.IsActive && requestInfo != null) { cPerformanceLogger.LogPerformanceEnd(LogDeep, CM, requestInfo.id, requestInfo.created, _startTime); } if (CM != null) LogMethodEnd(CM); } - - return null; - } - - private async Task IsTicketActivityAsync(Guid ticketId, 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 (ticketId == Guid.Empty) - return false; - - var fragments = await GetFragmentListAsync(new cDataFragmentsParams() - { - Columns = "TypeID", - Ddname = "SPSCommonClassBase", - Pagenumber = 0, - Pagesize = 1, - Where = string.Format("[Expression-ObjectId] = '{0}'", ticketId), - Sort = "TypeID" - }, requestInfo, logDeep + 1, token); - - if (token.IsCancellationRequested || fragments == null || fragments.Count == 0) - return false; - - var activityTypeRawValue = fragments[0]?.TypeID; - Guid activityTypeId = Guid.Empty; - if (activityTypeRawValue is Guid guidValue) - { - activityTypeId = guidValue; - } - else - { - var activityTypeText = Convert.ToString(activityTypeRawValue); - Guid.TryParse(activityTypeText, out activityTypeId); - } - - if (activityTypeId == Guid.Empty) - return false; - - return activityTypeId == constActivityTypeTicketTypeId; - } - 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 ResolveIsTicketAsync(cApiM42Ticket ticketInfo, Guid ticketId, cF4sdWebRequestInfo requestInfo, int logDeep, CancellationToken token) - { - if (ticketInfo != null && ticketInfo.AdditionalValues != null) - { - object additionalValue; - if (ticketInfo.AdditionalValues.TryGetValue("IsTicket", out additionalValue) && additionalValue is bool boolValue && boolValue) - return true; - - if (ticketInfo.AdditionalValues.TryGetValue("TypeID", out additionalValue) && IsTicketActivityTypeValue(additionalValue)) - return true; - - if (ticketInfo.AdditionalValues.TryGetValue("ActivityType", out additionalValue) && IsTicketActivityTypeValue(additionalValue)) - return true; - } - - if (ticketId == Guid.Empty) - return false; - - return await IsTicketActivityAsync(ticketId, requestInfo, logDeep, token); - } - - private static bool IsTicketActivityTypeValue(object activityTypeValue) - { - if (activityTypeValue == null) - return false; - - if (activityTypeValue is Guid guidValue) - return guidValue == constActivityTypeTicketTypeId; - - var activityTypeText = activityTypeValue as string; - if (string.IsNullOrWhiteSpace(activityTypeText)) - activityTypeText = Convert.ToString(activityTypeValue); - - if (string.IsNullOrWhiteSpace(activityTypeText)) - return false; - - Guid activityTypeGuid; - if (Guid.TryParse(activityTypeText, out activityTypeGuid)) - return activityTypeGuid == constActivityTypeTicketTypeId; - - return string.Equals(activityTypeText, constActivityTypeTicket, StringComparison.OrdinalIgnoreCase); - } - public async Task GetQuickCallTemplateInfoAsync(Guid quickCallId, 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); } + + return null; + } + + private async Task IsTicketActivityAsync(Guid ticketId, 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 (ticketId == Guid.Empty) + return false; + + var fragments = await GetFragmentListAsync(new cDataFragmentsParams() + { + Columns = "TypeID", + Ddname = "SPSCommonClassBase", + Pagenumber = 0, + Pagesize = 1, + Where = string.Format("[Expression-ObjectId] = '{0}'", ticketId), + Sort = "TypeID" + }, requestInfo, logDeep + 1, token); + + if (token.IsCancellationRequested || fragments == null || fragments.Count == 0) + return false; + + var activityTypeRawValue = fragments[0]?.TypeID; + Guid activityTypeId = Guid.Empty; + if (activityTypeRawValue is Guid guidValue) + { + activityTypeId = guidValue; + } + else + { + var activityTypeText = Convert.ToString(activityTypeRawValue); + Guid.TryParse(activityTypeText, out activityTypeId); + } + + if (activityTypeId == Guid.Empty) + return false; + + return activityTypeId == constActivityTypeTicketTypeId; + } + 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 ResolveIsTicketAsync(cApiM42Ticket ticketInfo, Guid ticketId, cF4sdWebRequestInfo requestInfo, int logDeep, CancellationToken token) + { + if (ticketInfo != null && ticketInfo.AdditionalValues != null) + { + object additionalValue; + if (ticketInfo.AdditionalValues.TryGetValue("IsTicket", out additionalValue) && additionalValue is bool boolValue && boolValue) + return true; + + if (ticketInfo.AdditionalValues.TryGetValue("TypeID", out additionalValue) && IsTicketActivityTypeValue(additionalValue)) + return true; + + if (ticketInfo.AdditionalValues.TryGetValue("ActivityType", out additionalValue) && IsTicketActivityTypeValue(additionalValue)) + return true; + } + + if (ticketId == Guid.Empty) + return false; + + return await IsTicketActivityAsync(ticketId, requestInfo, logDeep, token); + } + + private static bool IsTicketActivityTypeValue(object activityTypeValue) + { + if (activityTypeValue == null) + return false; + + if (activityTypeValue is Guid guidValue) + return guidValue == constActivityTypeTicketTypeId; + + var activityTypeText = activityTypeValue as string; + if (string.IsNullOrWhiteSpace(activityTypeText)) + activityTypeText = Convert.ToString(activityTypeValue); + + if (string.IsNullOrWhiteSpace(activityTypeText)) + return false; + + Guid activityTypeGuid; + if (Guid.TryParse(activityTypeText, out activityTypeGuid)) + return activityTypeGuid == constActivityTypeTicketTypeId; + + return string.Equals(activityTypeText, constActivityTypeTicket, StringComparison.OrdinalIgnoreCase); + } + public async Task GetQuickCallTemplateInfoAsync(Guid quickCallId, 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 @@ -3698,30 +3805,30 @@ namespace C4IT.DataHistoryProvider Guid.TryParse(res, out ticketId); } } - else - { - await UpdateM42TicketAsync(ticketInfo, requestInfo, LogDeep + 1, token); - if (token.IsCancellationRequested) return null; - - ticketId = (Guid)ticketInfo.Ticket; - } - - if ((!ticketInfo.Ticket.HasValue || ticketInfo.Ticket.Value == Guid.Empty) && ticketId != Guid.Empty) - { - ticketInfo.Ticket = ticketId; - } - - var isTicket = await ResolveIsTicketAsync(ticketInfo, ticketId, requestInfo, LogDeep + 1, token); - if (token.IsCancellationRequested) return null; - - if (isTicket) - { - await TransformM42TicketAsync(ticketInfo, requestInfo, LogDeep + 1, token); - } - - var url = constUrlActivityPause; - var ticket = new cApiM42TicketPauseInfo() - { + else + { + await UpdateM42TicketAsync(ticketInfo, requestInfo, LogDeep + 1, token); + if (token.IsCancellationRequested) return null; + + ticketId = (Guid)ticketInfo.Ticket; + } + + if ((!ticketInfo.Ticket.HasValue || ticketInfo.Ticket.Value == Guid.Empty) && ticketId != Guid.Empty) + { + ticketInfo.Ticket = ticketId; + } + + var isTicket = await ResolveIsTicketAsync(ticketInfo, ticketId, requestInfo, LogDeep + 1, token); + if (token.IsCancellationRequested) return null; + + if (isTicket) + { + await TransformM42TicketAsync(ticketInfo, requestInfo, LogDeep + 1, token); + } + + var url = constUrlActivityPause; + var ticket = new cApiM42TicketPauseInfo() + { ObjectIds = new List() { ticketId }, Comments = ticketInfo.Comment, ReminderDate = (DateTime)ticketInfo.ReminderDate, @@ -4069,12 +4176,11 @@ namespace C4IT.DataHistoryProvider public HashSet GetSupportedInformationClasses() => new HashSet() { enumFasdInformationClass.Ticket }; - public async Task GetRelationsAsync(IEnumerable ids, enumFasdInformationClass informationClass, int age, CancellationToken token = default) + public async Task GetRelationsAsync(IEnumerable ids, enumFasdInformationClass informationClass, int age, CancellationToken token = default) { try { - List ticketIds = await _collector.getConnectorIdList(ids.ToList(), token, null, 0); - List relations = await getTicketUsage(ticketIds, token, null, 0); + List relations = await getTicketUsage(ids, token, null, 0); return new cF4sdStagedSearchResultRelations() { Relations = relations }; } catch (Exception ex) @@ -4084,6 +4190,51 @@ namespace C4IT.DataHistoryProvider return null; } + + public async override Task ValidateTokenInfo(cDataHistoryCollectorTokenCache.cTokenInfo tokenInfo) + { + if (Guid.TryParseExact(tokenInfo?.IdRemote, "D", out Guid remoteId)) + return 0; + + if (tokenInfo.type == cF4SDTokenRegistration.enumTokenType.M42Bearer) + { + using (var client = new cM42WebClient(Collector.InfrastructureConfig.M42Wpm.Server, null)) + { + client.SetBearerToken(tokenInfo.secret); + var _reason = await client.Open(); + if (_reason == eReason.ok) + { + var userInfo = await client.GetUserInfoAsync(); + if (userInfo?.User != null) + { + tokenInfo.IdRemote = userInfo.User.Id.ToString(); + return 1; + } + } + } + } + + if (tokenInfo.type == cF4SDTokenRegistration.enumTokenType.M42Api) + { + var _ti = tokenInfo.GetNewInstance(); + using (var client = new cM42WebClient(Collector.InfrastructureConfig.M42Wpm.Server, null)) + { + var _reason = await client.LogonWithApiKeyAsync(_ti.secret); + if (_reason == eReason.ok) + { + var userInfo = await client.GetUserInfoAsync(); + if (userInfo?.User != null) + { + tokenInfo.IdRemote = userInfo.User.Id.ToString(); + return 1; + } + } + } + } + + return -1; + } + } public class cDataFragmentsParams @@ -4095,6 +4246,7 @@ namespace C4IT.DataHistoryProvider public int Pagenumber { get; set; } public string Sort { get; set; } } + public class cCreateJournalEntryInfo { public Guid ObjectId { get; set; } @@ -4208,16 +4360,16 @@ namespace C4IT.DataHistoryProvider } } -public class cM42DataQueryRelationInfo -{ - public int ClientTimeOffset { get; set; } = 0; - public string EntityClassName { get; set; } - public string EntityTypeName { get; set; } - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] - public string DataQueryName { get; set; } - public string AddedRelations { get; set; } = ""; - public string RemovedRelations { get; set; } = ""; - public string WhereExpression { get; set; } = ""; +public class cM42DataQueryRelationInfo +{ + public int ClientTimeOffset { get; set; } = 0; + public string EntityClassName { get; set; } + public string EntityTypeName { get; set; } + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + public string DataQueryName { get; set; } + public string AddedRelations { get; set; } = ""; + public string RemovedRelations { get; set; } = ""; + public string WhereExpression { get; set; } = ""; public cM42QueryFilterGroup AdditionalFilter { get; set; } = new cM42QueryFilterGroup(); public int pageSize { get; set; } = 20; public int page { get; set; } = 0; @@ -4264,23 +4416,6 @@ public class cM42QueryFilterCondition public bool DenyWildcard { get; set; } } -public class cF4SDTicketSummary -{ - public Guid TicketObjectId { get; set; } - public string Name { get; set; } - public string ActivityType { get; set; } - public string Summary { get; set; } - public string Sid { get; set; } - public string Status { get; set; } - public int StatusId { get; set; } - public string AssetCIName { get; set; } - public string AssetName { get; set; } - public string ServiceName { get; set; } - public Guid ServiceId { get; set; } - public bool IsPrimaryAccount { get; set; } - public Guid UserId { get; set; } -} - public class M42UserUserInfo { public class M42User diff --git a/F4SD-Cockpit-ServerCore/DataHistoryCollectorTokenCache.cs b/F4SD-Cockpit-ServerCore/DataHistoryCollectorTokenCache.cs index 3c5fe6e..e833af0 100644 --- a/F4SD-Cockpit-ServerCore/DataHistoryCollectorTokenCache.cs +++ b/F4SD-Cockpit-ServerCore/DataHistoryCollectorTokenCache.cs @@ -1,11 +1,16 @@ -using C4IT.FASD.Base; -using C4IT.Logging; -using C4IT.Security; -using System; +using System; using System.Collections.Generic; using System.Reflection; using System.Threading; using System.Threading.Tasks; + +using C4IT.FASD.Base; +using C4IT.Logging; +using C4IT.Security; + +using Newtonsoft.Json.Linq; + +using static C4IT.DataHistoryProvider.cDataHistoryCollectorTokenCache; using static C4IT.Logging.cLogManager; namespace C4IT.DataHistoryProvider @@ -29,6 +34,7 @@ namespace C4IT.DataHistoryProvider public string secret; public DateTime validUntil; public DateTime renewUntil; + public string IdRemote; public bool IsValid() { @@ -36,6 +42,7 @@ namespace C4IT.DataHistoryProvider return false; if (validUntil < DateTime.UtcNow + TimeSpan.FromMinutes(cDataHistoryCollector.constMinutesUntilTokenIsInvalid)) return false; + return true; } @@ -52,7 +59,8 @@ namespace C4IT.DataHistoryProvider name2 = name2, secret = _sec, validUntil = validUntil, - renewUntil = renewUntil + renewUntil = renewUntil, + IdRemote = IdRemote }; return _ret; @@ -132,6 +140,9 @@ namespace C4IT.DataHistoryProvider { dicToken.Clear(); + var _toDelete = new List(); + var _toUpdate = new List(); + using (var _reader = await DataHistorySqlHelper.GetDataReaderAsync(DbConn, Query, null, Token)) { if (_reader != null) @@ -139,7 +150,6 @@ namespace C4IT.DataHistoryProvider { if (_reader.HasRows) { - var _toDelete = new List(); while (await _reader.ReadAsync()) { try @@ -156,13 +166,22 @@ namespace C4IT.DataHistoryProvider _entry.secret = cSecurePassword.Instance.Decode(_secret); _entry.validUntil = _reader.IsDBNull(5) ? DateTime.MinValue : _reader.GetDateTime(5); _entry.renewUntil = _reader.IsDBNull(6) ? DateTime.MinValue : _reader.GetDateTime(6); + _entry.IdRemote = _reader.IsDBNull(7) ? null : _reader.GetString(7); + + int valid = -1; if (_entry.IsValid()) + valid = await Collector.ValidateTokenInfo(_entry); + + if (valid < 0) + _toDelete.Add(_entry); + else { if (!privInsert(_entry, false)) _toDelete.Add(_entry); + + if (valid > 0) + _toUpdate.Add(_entry); } - else - _toDelete.Add(_entry); } catch (Exception E) { @@ -178,9 +197,18 @@ namespace C4IT.DataHistoryProvider { cLogManager.DefaultLogger.LogException(E); } - } + if (_toUpdate.Count > 0) + { + foreach (var entry in _toUpdate) + { + var _secEnc = entry.secret; + if (!HasTokenTypeLongLifetime(entry.type)) + _secEnc = cSecurePassword.Instance.Encode(entry.secret); + await SaveToDbAsync(entry, _secEnc, requestInfo, LogDeep + 1, Token); + } + } } catch (Exception E) { @@ -204,6 +232,54 @@ namespace C4IT.DataHistoryProvider } } + private async Task SaveToDbAsync(cTokenInfo _tokenInfo, string secEnc, cF4sdWebRequestInfo requestInfo, int LogDeep, CancellationToken Token) + { + MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } + + var sqlerror = 0; + var sqlStartTime = DateTime.UtcNow; + + if (!DataHistorySqlHelper.GetWellKnownSqlStatement("InsertOrUpdateExternalToken", out var Query)) + return; + + try + { + + var Params = new Dictionary() + { + { "Id", _tokenInfo.id }, + { "Type", (int)_tokenInfo.type }, + { "Name", _tokenInfo.name }, + { "Name2", _tokenInfo.name2 }, + { "Secret", secEnc }, + { "ValidUntil", _tokenInfo.validUntil }, + { "RenewUntil", _tokenInfo.renewUntil }, + { "IdRemote", _tokenInfo.IdRemote }, + }; + + using (var Conn = new cDbConnection(Collector.mainDbConnection)) + { + if (!Conn.IsOpen) + { + LogEntry($"Could not open main database '{Collector.mainDbConnection.Database}', aborting query'", LogLevels.Error); + return; + } + + await DataHistorySqlHelper.ExecuteAsync(Conn, Query, Params, requestInfo, Token); + } + } + catch (Exception E) + { + sqlerror = E.HResult; + LogException(E); + } + finally + { + if (CM != null) LogMethodEnd(CM); + } + } + + public async Task SetAsync(cF4SDTokenRegistration _tokenRegistration, cF4SDTokenValidityPeriod validityPeriod, CancellationToken Token, cF4sdWebRequestInfo requestInfo, int LogDeep) { MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } @@ -229,50 +305,23 @@ namespace C4IT.DataHistoryProvider renewUntil = validityPeriod.renewUntil }; - if (!DataHistorySqlHelper.GetWellKnownSqlStatement("InsertOrUpdateExternalToken", out var Query)) - return; - - var Params = new Dictionary() - { - { "Id", _tokenInfo.id }, - { "Type", (int)_tokenInfo.type }, - { "Name", _tokenInfo.name }, - { "Name2", _tokenInfo.name2 }, - { "Secret", _secEnc }, - { "ValidUntil", _tokenInfo.validUntil }, - { "RenewUntil", _tokenInfo.renewUntil }, - }; - - var sqlerror = 0; - var sqlStartTime = DateTime.UtcNow; - await TokenCacheCriticalSection.EnterAsync(); + + try { + await SaveToDbAsync(_tokenInfo, _secEnc, requestInfo, LogDeep + 1, Token); privInsert(_tokenInfo, true); - - - using (var Conn = new cDbConnection(Collector.mainDbConnection)) - { - if (!Conn.IsOpen) - { - LogEntry($"Could not open main database '{Collector.mainDbConnection.Database}', aborting query'", LogLevels.Error); - return; - } - - await DataHistorySqlHelper.ExecuteAsync(Conn, Query, Params, requestInfo, Token); - } - } catch (Exception E) { - sqlerror = E.HResult; LogException(E); } finally { TokenCacheCriticalSection.Leave(); } + } catch (Exception E) { diff --git a/F4SD-Cockpit-ServerCore/DataHistoryConfigCitrix.cs b/F4SD-Cockpit-ServerCore/DataHistoryConfigCitrix.cs deleted file mode 100644 index 48e7ca3..0000000 --- a/F4SD-Cockpit-ServerCore/DataHistoryConfigCitrix.cs +++ /dev/null @@ -1,188 +0,0 @@ -using C4IT.DataHistoryProvider; -using C4IT.Logging; -using C4IT.Security; -using C4IT.XML; -using System; -using System.Collections.Generic; -using System.Reflection; -using System.Xml; -using static C4IT.Logging.cLogManager; - -namespace C4IT_DataHistoryProvider -{ - public class cDataHistoryConfigCitrix : IConfigNodeValidation - { - public bool IsValid { get; private set; } = false; - - public string Domain { get; private set; } = ""; - - public string TenantID { get; private set; } = String.Empty; - - public cCredential Credential { get; private set; } = null; - public cDataHistoryScanTiming ScanTiming { get; private set; } = null; - public cDataHistoryConfigCitrix(XmlElement XNode, Dictionary Credentials, cXmlParser Parser) - { - MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } - try - { - - var XNode2 = XNode.SelectSingleNode("Citrix"); - if (!(XNode2 is XmlElement XCitrix)) - { - return; - } - - Parser.EnterElement("Citrix"); - - try - { - ScanTiming = new cDataHistoryScanTiming(XCitrix, TimeSpan.FromHours(24), TimeSpan.FromMinutes(10), Parser); - IsValid = true; - } - finally - { - Parser.LeaveElement("Citrix"); - } - } - catch (Exception E) - { - LogException(E); - } - finally - { - if (CM != null) LogMethodEnd(CM); - } - } - - - public class cDataHistoryCitrixTenant : IConfigNodeValidation - { - public bool IsValid { get; private set; } = false; - - public string Domain { get; private set; } = ""; - - public string TenantID { get; private set; } = String.Empty; - public string InstanceID { get; private set; } = String.Empty; - - public cCredential Credential { get; private set; } = null; - - internal cDataHistoryCitrixTenant(XmlElement XNode, Dictionary Credentials, cXmlParser Parser) - { - MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } - try - { - Domain = cXmlParser.GetStringFromXmlAttribute(XNode, "Domain"); - if (string.IsNullOrWhiteSpace(Domain)) - { - Parser.AddInvalidAttribute(XNode, null, "Domain"); - return; - } - TenantID = cXmlParser.GetStringFromXmlAttribute(XNode, "TenantID"); - if (TenantID == String.Empty) - { - Parser.AddInvalidAttribute(XNode, Domain, "TenantID"); - return; - } - - InstanceID = cXmlParser.GetStringFromXmlAttribute(XNode, "InstanceID"); - if (InstanceID == String.Empty) - { - Parser.AddInvalidAttribute(XNode, Domain, "InstanceID"); - return; - } - - var strCreds = cXmlParser.GetStringFromXmlAttribute(XNode, "Credential"); - if (!string.IsNullOrWhiteSpace(strCreds)) - { - if (Credentials.TryGetValue(strCreds, out var Cred)) - Credential = Cred; - } - - if (Credential == null) - { - Parser.AddInvalidAttribute(XNode, Domain, "Credential"); - return; - } - - IsValid = true; - } - catch (Exception E) - { - LogException(E); - } - finally - { - if (CM != null) LogMethodEnd(CM); - } - } - - internal static Dictionary LoadFromXml(XmlElement XNode, Dictionary Credentials, cXmlParser Parser) - { - MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } - - var RetVal = new Dictionary(); - - try - { - var XRoot = XNode.SelectSingleNode("Citrix"); - if (XRoot == null) - { - return null; - } - - Parser.EnterElement("Citrix"); - try - { - - var XList = XRoot.SelectNodes("Citrix-Tenant"); - if (XList != null && XList.Count > 0) - { - Parser.EnterElement("Citrix-Tenant"); - try - { - foreach (var Entry in XList) - { - if (!(Entry is XmlElement XEntry)) - continue; - - var Node = new cDataHistoryCitrixTenant(XEntry, Credentials, Parser); - if (Node.IsValid) - { - if (RetVal.ContainsKey(Node.Domain)) - { - Parser.AddDuplicateName(XEntry, Node.Domain, LogLevels.Warning); - } - else - RetVal.Add(Node.Domain, Node); - } - Parser.SelectElementNext(); - } - } - finally - { - Parser.LeaveElement("Citrix-Tenant"); - } - } - } - finally - { - Parser.LeaveElement("Citrix"); - } - } - catch (Exception E) - { - LogException(E); - } - finally - { - if (CM != null) LogMethodEnd(CM); - } - - return RetVal; - } - - } - - - } -} diff --git a/F4SD-Cockpit-ServerCore/DataHistoryConfigClusters.cs b/F4SD-Cockpit-ServerCore/DataHistoryConfigClusters.cs index 1b91009..4d6939a 100644 --- a/F4SD-Cockpit-ServerCore/DataHistoryConfigClusters.cs +++ b/F4SD-Cockpit-ServerCore/DataHistoryConfigClusters.cs @@ -128,6 +128,8 @@ namespace C4IT.DataHistoryProvider RetVal |= DoXmlInsertTableRow(XmlRoot, "AD-ComputerInfo", "ad-computer", "", "account"); RetVal |= DoXmlInsertTableRow(XmlRoot, "AD-ComputerInfo", "ad-computer", "", "account"); + RetVal |= DoXmlInsertTableRow(XmlRoot, "Agent-DeviceInfo", "agnt-computer", ""); + RetVal |= DoXmlInsertTableRow(XmlRoot, "Agent-DeviceInfo", "agnt-computer-event-numerical", ""); RetVal |= DoXmlInsertTableRow(XmlRoot, "Agent-DeviceInfo", "agnt-computer-event-numerical", ""); RetVal |= DoXmlInsertTableRow(XmlRoot, "Agent-DeviceInfo", "agnt-computer-event-numerical", ""); @@ -248,6 +250,11 @@ namespace C4IT.DataHistoryProvider , "
" ); + RetVal |= DoXmlInsertElement(XmlRoot + , "DataCluster[@Name='citrixMetrics']" + , "Table[@Name='citrix-connectionState-metrics']" + , "
"); + RetVal |= DoXmlInsertElement(XmlRoot , "DataCluster[@Name='citrixMetrics']" , "Table[@Name='citrix-session-details-icaRttMS']" @@ -277,6 +284,10 @@ namespace C4IT.DataHistoryProvider , "Table[@Name='citrix-session-details-inputBandwidthUsed']" , "
" ); + RetVal |= DoXmlInsertElement(XmlRoot + , "DataCluster[@Name='citrixMetrics']" + , "Table[@Name='citrix-session-details-outputBandwidthUsed']" + , "
"); RetVal |= DoXmlInsertElement(XmlRoot , "DataCluster[@Name='citrixMetrics']" @@ -317,8 +328,7 @@ namespace C4IT.DataHistoryProvider RetVal |= DoXmlInsertElement(XmlRoot , "DataCluster[@Name='citrixMetrics']" , "Table[@Name='citrix-session-machine']" - , "
" - ); + , "
"); RetVal |= DoXmlInsertElement(XmlRoot , "DataCluster[@Name='citrixMetrics']" @@ -378,19 +388,19 @@ namespace C4IT.DataHistoryProvider , "Table[@Name='M42Wpm-Ticket-Services']" ); - RetVal |= DoXmlInsertElement(XmlRoot - , "DataCluster[@Name='M42Tickets']" - , "Table[@Name='M42Wpm-Ticket-QuickCalls']" - , "
" - , "Table[@Name='M42Wpm-Ticket-Assets']" - ); + RetVal |= DoXmlInsertElement(XmlRoot + , "DataCluster[@Name='M42Tickets']" + , "Table[@Name='M42Wpm-Ticket-QuickCalls']" + , "
" + , "Table[@Name='M42Wpm-Ticket-Assets']" + ); - RetVal |= DoXmlInsertElement(XmlRoot - , "DataCluster[@Name='M42Tickets']" - , "Table[@Name='M42Wpm-Ticket-Categories']" - , "
" - , "Table[@Name='M42Wpm-Ticket-QuickCalls']" - ); + RetVal |= DoXmlInsertElement(XmlRoot + , "DataCluster[@Name='M42Tickets']" + , "Table[@Name='M42Wpm-Ticket-Categories']" + , "
" + , "Table[@Name='M42Wpm-Ticket-QuickCalls']" + ); RetVal |= DoXmlInsertElement(XmlRoot , "DataCluster[@Name='M42Tickets']" @@ -493,83 +503,96 @@ namespace C4IT.DataHistoryProvider } // update table 'M42Wpm-Tickets' columns - RetVal |= DoXmlInsertTableRow(XmlRoot, "M42Tickets", "M42Wpm-Tickets", "", "AffectedUser"); - RetVal |= DoXmlInsertTableRow(XmlRoot, "M42Tickets", "M42Wpm-Tickets", "", "Urgency"); - RetVal |= DoXmlInsertTableRow(XmlRoot, "M42Tickets", "M42Wpm-Tickets", "", "UrgencyId"); - RetVal |= DoXmlInsertTableRow(XmlRoot, "M42Tickets", "M42Wpm-Tickets", "", "Impact"); - - RetVal |= DoXmlInsertTableRow(XmlRoot, "M42Tickets", "M42Wpm-Ticket-QuickCalls", "", "ResponsibleRole"); - - var quickCallsCategoryColumn = XmlRoot.SelectSingleNode("DataCluster[@Name='M42Tickets']/Table[@Name='M42Wpm-Ticket-QuickCalls']/Table-Columns/Table-Column[@Name='Category']") as XmlElement; - if (quickCallsCategoryColumn != null) - { - if (!string.Equals(quickCallsCategoryColumn.GetAttribute("SourceName"), "Category_Value", StringComparison.OrdinalIgnoreCase)) - { - quickCallsCategoryColumn.SetAttribute("SourceName", "Category_Value"); - RetVal = true; - } - - if (!string.Equals(quickCallsCategoryColumn.GetAttribute("Type"), "guid", StringComparison.OrdinalIgnoreCase)) - { - quickCallsCategoryColumn.SetAttribute("Type", "guid"); - RetVal = true; - } - } - - var quickCallsTemplate = XmlRoot.SelectSingleNode("DataCluster[@Name='M42Tickets']/Table[@Name='M42Wpm-Ticket-QuickCalls']/Matrix42-DataQueryItems-Template") as XmlElement; - if (quickCallsTemplate != null) - { - if (!string.Equals(quickCallsTemplate.GetAttribute("DataQueryName"), "C4IT - F4SD - Quick Calls", StringComparison.OrdinalIgnoreCase)) - { - quickCallsTemplate.SetAttribute("DataQueryName", "C4IT - F4SD - Quick Calls"); - RetVal = true; - } - } - - var categoriesColumns = XmlRoot.SelectSingleNode("DataCluster[@Name='M42Tickets']/Table[@Name='M42Wpm-Ticket-Categories']/Table-Columns") as XmlElement; - if (categoriesColumns != null) - { - RetVal |= DoXmlInsertTableRow(XmlRoot, "M42Tickets", "M42Wpm-Ticket-Categories", "", "Name"); - RetVal |= DoXmlInsertTableRow(XmlRoot, "M42Tickets", "M42Wpm-Ticket-Categories", "", "parentValue"); - - var parentValueColumn = categoriesColumns.SelectSingleNode("Table-Column[@Name='parentValue']") as XmlElement; - if (parentValueColumn != null) - { - if (!string.Equals(parentValueColumn.GetAttribute("SourceName"), "Parent_Value", StringComparison.OrdinalIgnoreCase)) - { - parentValueColumn.SetAttribute("SourceName", "Parent_Value"); - RetVal = true; - } - - if (!string.Equals(parentValueColumn.GetAttribute("Type"), "guid", StringComparison.OrdinalIgnoreCase)) - { - parentValueColumn.SetAttribute("Type", "guid"); - RetVal = true; - } - } - - var parentColumn = categoriesColumns.SelectSingleNode("Table-Column[@Name='parent']") as XmlElement; - if (parentColumn != null) - { - if (!string.Equals(parentColumn.GetAttribute("SourceName"), "Parent", StringComparison.OrdinalIgnoreCase)) - { - parentColumn.SetAttribute("SourceName", "Parent"); - RetVal = true; - } - - if (!string.Equals(parentColumn.GetAttribute("Type"), "string", StringComparison.OrdinalIgnoreCase)) - { - parentColumn.SetAttribute("Type", "string"); - RetVal = true; - } - - if (!string.Equals(parentColumn.GetAttribute("Cardinal"), "300", StringComparison.OrdinalIgnoreCase)) - { - parentColumn.SetAttribute("Cardinal", "300"); - RetVal = true; - } - } - } + RetVal |= DoXmlInsertTableRow(XmlRoot, "M42Tickets", "M42Wpm-Tickets", "", "AffectedUser"); + RetVal |= DoXmlInsertTableRow(XmlRoot, "M42Tickets", "M42Wpm-Tickets", "", "Urgency"); + RetVal |= DoXmlInsertTableRow(XmlRoot, "M42Tickets", "M42Wpm-Tickets", "", "UrgencyId"); + RetVal |= DoXmlInsertTableRow(XmlRoot, "M42Tickets", "M42Wpm-Tickets", "", "Impact"); + + RetVal |= DoXmlInsertTableRow(XmlRoot, "M42Tickets", "M42Wpm-Ticket-QuickCalls", "", "ResponsibleRole"); + + var quickCallsCategoryColumn = XmlRoot.SelectSingleNode("DataCluster[@Name='M42Tickets']/Table[@Name='M42Wpm-Ticket-QuickCalls']/Table-Columns/Table-Column[@Name='Category']") as XmlElement; + if (quickCallsCategoryColumn != null) + { + if (!string.Equals(quickCallsCategoryColumn.GetAttribute("SourceName"), "Category_Value", StringComparison.OrdinalIgnoreCase)) + { + quickCallsCategoryColumn.SetAttribute("SourceName", "Category_Value"); + RetVal = true; + } + + if (!string.Equals(quickCallsCategoryColumn.GetAttribute("Type"), "guid", StringComparison.OrdinalIgnoreCase)) + { + quickCallsCategoryColumn.SetAttribute("Type", "guid"); + RetVal = true; + } + } + + var quickCallsTemplate = XmlRoot.SelectSingleNode("DataCluster[@Name='M42Tickets']/Table[@Name='M42Wpm-Ticket-QuickCalls']/Matrix42-DataQueryItems-Template") as XmlElement; + if (quickCallsTemplate != null) + { + if (!string.Equals(quickCallsTemplate.GetAttribute("DataQueryName"), "C4IT - F4SD - Quick Calls", StringComparison.OrdinalIgnoreCase)) + { + quickCallsTemplate.SetAttribute("DataQueryName", "C4IT - F4SD - Quick Calls"); + RetVal = true; + } + } + + var categoriesColumns = XmlRoot.SelectSingleNode("DataCluster[@Name='M42Tickets']/Table[@Name='M42Wpm-Ticket-Categories']/Table-Columns") as XmlElement; + if (categoriesColumns != null) + { + RetVal |= DoXmlInsertTableRow(XmlRoot, "M42Tickets", "M42Wpm-Ticket-Categories", "", "Name"); + RetVal |= DoXmlInsertTableRow(XmlRoot, "M42Tickets", "M42Wpm-Ticket-Categories", "", "parentValue"); + + var parentValueColumn = categoriesColumns.SelectSingleNode("Table-Column[@Name='parentValue']") as XmlElement; + if (parentValueColumn != null) + { + if (!string.Equals(parentValueColumn.GetAttribute("SourceName"), "Parent_Value", StringComparison.OrdinalIgnoreCase)) + { + parentValueColumn.SetAttribute("SourceName", "Parent_Value"); + RetVal = true; + } + + if (!string.Equals(parentValueColumn.GetAttribute("Type"), "guid", StringComparison.OrdinalIgnoreCase)) + { + parentValueColumn.SetAttribute("Type", "guid"); + RetVal = true; + } + } + + var parentColumn = categoriesColumns.SelectSingleNode("Table-Column[@Name='parent']") as XmlElement; + if (parentColumn != null) + { + if (!string.Equals(parentColumn.GetAttribute("SourceName"), "Parent", StringComparison.OrdinalIgnoreCase)) + { + parentColumn.SetAttribute("SourceName", "Parent"); + RetVal = true; + } + + if (!string.Equals(parentColumn.GetAttribute("Type"), "string", StringComparison.OrdinalIgnoreCase)) + { + parentColumn.SetAttribute("Type", "string"); + RetVal = true; + } + + if (!string.Equals(parentColumn.GetAttribute("Cardinal"), "300", StringComparison.OrdinalIgnoreCase)) + { + parentColumn.SetAttribute("Cardinal", "300"); + RetVal = true; + } + } + } + + var categoriesTemplate = XmlRoot.SelectSingleNode("DataCluster[@Name='M42Tickets']/Table[@Name='M42Wpm-Ticket-Categories']/Matrix42-DataQueryItems-Template") as XmlElement; + if (categoriesTemplate != null) + { + var whereExpression = categoriesTemplate.GetAttribute("WhereExpression"); + var normalizedWhereExpression = Regex.Replace(whereExpression ?? string.Empty, @"\s+", string.Empty); + if (string.IsNullOrWhiteSpace(whereExpression) + || string.Equals(normalizedWhereExpression, "Hidden=false", StringComparison.OrdinalIgnoreCase)) + { + categoriesTemplate.SetAttribute("WhereExpression", "Hidden = 0"); + RetVal = true; + } + } // correct the type attribute for the M42Wpm-Ticket-History table to Events var M42JournalItemNode = XmlRoot.SelectSingleNode("DataCluster[@Name='M42Tickets']/Table[@Name='M42Wpm-Ticket-History' and @Type!='HistoryEvents']") as XmlElement; @@ -2517,13 +2540,13 @@ namespace C4IT.DataHistoryProvider } - public class cDataHistoryConfigQueryTemplateM42DataQueryItems : cDataHistoryConfigQueryTemplate - { - public string EntityClassName { get; private set; } = null; - public List EntityTypeNames { get; private set; } = null; - public string OrderBy { get; private set; } = null; - public string WhereExpression { get; private set; } = null; - public string DataQueryName { get; private set; } = null; + public class cDataHistoryConfigQueryTemplateM42DataQueryItems : cDataHistoryConfigQueryTemplate + { + public string EntityClassName { get; private set; } = null; + public List EntityTypeNames { get; private set; } = null; + public string OrderBy { get; private set; } = null; + public string WhereExpression { get; private set; } = null; + public string DataQueryName { get; private set; } = null; internal cDataHistoryConfigQueryTemplateM42DataQueryItems(XmlElement XNode, cXmlParser Parser) : @@ -2555,11 +2578,11 @@ namespace C4IT.DataHistoryProvider EntityTypeNames.Add(_e2); } - OrderBy = cXmlParser.GetStringFromXmlAttribute(XNode2, "OrderBy"); - WhereExpression = cXmlParser.GetStringFromXmlAttribute(XNode2, "WhereExpression"); - DataQueryName = cXmlParser.GetStringFromXmlAttribute(XNode2, "DataQueryName"); - - Parser.LeaveElement("Matrix42-DataQueryItems-Template"); + OrderBy = cXmlParser.GetStringFromXmlAttribute(XNode2, "OrderBy"); + WhereExpression = cXmlParser.GetStringFromXmlAttribute(XNode2, "WhereExpression"); + DataQueryName = cXmlParser.GetStringFromXmlAttribute(XNode2, "DataQueryName"); + + Parser.LeaveElement("Matrix42-DataQueryItems-Template"); IsValid = true; } diff --git a/F4SD-Cockpit-ServerCore/DataHistoryConfigGolabalParameters.cs b/F4SD-Cockpit-ServerCore/DataHistoryConfigGolabalParameters.cs index 7d82264..5314715 100644 --- a/F4SD-Cockpit-ServerCore/DataHistoryConfigGolabalParameters.cs +++ b/F4SD-Cockpit-ServerCore/DataHistoryConfigGolabalParameters.cs @@ -58,292 +58,355 @@ namespace C4IT.DataHistoryProvider } - private cConfigHelperParameterList LoadParameters (XmlElement XRoot, cXmlParser Parser) - { - MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } - - var _result = new cConfigHelperParameterList(); - - try - { - var Items = XRoot.ChildNodes; - foreach (var _node in Items) - { - if (!(_node is XmlElement _item)) continue; - - Parser.EnterElement(_item.Name); - - try - { - var _attPolicy = _item.GetAttribute("Policy"); - var _attValue = _item.GetAttribute("Value"); - - var _ElementCount = 0; - foreach (XmlNode _subNode in _item.ChildNodes) - { - if (_subNode is XmlElement) - _ElementCount++; - } - - // check for special parameters - switch (_item.Name) - { - case "InformationClassSearchPriority": - var _val = getInformationClassSearchPriority(_item, Parser); - if (_val != null) - _result.Items[_val.Name] = _val; - continue; - case "OpenActivitiesExternally": - var _openActivitiesEntry = getOpenActivitiesExternallyEntry(_item); - if (_openActivitiesEntry != null) - _result.Items[_openActivitiesEntry.Name] = _openActivitiesEntry; - - var _overrideValues = getOpenActivitiesExternallyOverrides(_item, Parser); - if (_overrideValues != null && _overrideValues.ValueList != null && _overrideValues.ValueList.Count > 0) - _result.Items[_overrideValues.Name] = _overrideValues; - continue; - } - - if (_attPolicy != null && _attValue != null && _ElementCount == 0) - { - // we have a simple parameter - var _policy = cXmlParser.GetEnumFromAttribute(_item, "Policy", enumConfigPolicy.Default); - var _value = cXmlParser.GetStringFromXmlAttribute(_item, "Value", String.Empty); - - _result.Items[_item.Name] = new cConfigHelperParameterEntry() - { - Name = _item.Name, - Value = _value, - Policy = _policy.ToString(), - }; - } - else if (string.IsNullOrEmpty(_attPolicy) && string.IsNullOrEmpty(_attValue) && _ElementCount > 0) - { - // we have a sublist - if (_result.SubItems == null) - _result.SubItems = new Dictionary(); - _result.SubItems[_item.Name] = LoadParameters(_item, Parser); - } - else - { - // we have an invalid entry - var _msg = $"Invalid entry in F4SD-Global-Configuration.xml: {_item.Name}"; - Parser.AddMessage(_item, _msg, LogLevels.Warning); - LogEntry(_msg, LogLevels.Warning); - } - } - catch (Exception E) - { - LogException(E); - } - finally - { - Parser.LeaveElement(_item.Name); - } - } - } - catch (Exception E) - { - LogException(E); - } - finally - { - if (CM != null) LogMethodEnd(CM); - } - - return _result; - } - - private cConfigHelperParameterEntry getInformationClassSearchPriority(XmlElement XNode, cXmlParser Parser) - { - - MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } - - try - { - var _xNodes = XNode.ChildNodes; - - var _policy = cXmlParser.GetEnumFromAttribute(XNode, "Policy", enumConfigPolicy.Default); - - var _result = new List(); - - foreach (var _xNode in _xNodes) - { - if (!(_xNode is XmlElement _item)) - continue; - - Parser.EnterElement(_item.Name); - - try - { - if (_item.Name != "InformationClass") - { - // we have an invalid entry - var _msg = $"Invalid entry in F4SD-Global-Configuration.xml: {_item.Name}, should be "; - Parser.AddMessage(_item, _msg, LogLevels.Warning); - LogEntry(_msg, LogLevels.Warning); - continue; - } - - var _InfoClass = cXmlParser.GetEnumFromAttribute(_item, "Type", enumFasdInformationClass.Unknown); - if (_InfoClass == enumFasdInformationClass.Unknown) - { - // we have an invalid type attribute - var _strType = cXmlParser.GetStringFromXmlAttribute(_item, "Type", String.Empty); - var _msg = $"Invalid type attribute value ({_strType}) in entry {_item.Name}"; - Parser.AddMessage(_item, _msg, LogLevels.Warning); - LogEntry(_msg, LogLevels.Warning); - continue; - } - - var _strInfoClass = _InfoClass.ToString(); - if (!_result.Contains(_strInfoClass)) - _result.Add(_strInfoClass); - } - catch (Exception E) - { - LogException(E); - } - finally - { - Parser.LeaveElement(_item.Name); - } - } - - return new cConfigHelperParameterEntry() - { - Name = XNode.Name, - ValueList = _result, - Policy = _policy.ToString() - }; - } - catch (Exception E) - { - LogException(E); - } - finally - { - if (CM != null) LogMethodEnd(CM); - } - - return null; - } - - private static cConfigHelperParameterEntry getOpenActivitiesExternallyEntry(XmlElement XNode) - { - var _policy = cXmlParser.GetEnumFromAttribute(XNode, "Policy", enumConfigPolicy.Default); - var _value = cXmlParser.GetStringFromXmlAttribute(XNode, "Value", String.Empty); - - if (string.IsNullOrWhiteSpace(_value)) - return null; - - return new cConfigHelperParameterEntry() - { - Name = "OpenActivitiesExternally", - Value = _value, - Policy = _policy.ToString(), - }; - } - - private cConfigHelperParameterEntry getOpenActivitiesExternallyOverrides(XmlElement XNode, cXmlParser Parser) - { - MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } - - try - { - var _xNodes = XNode.ChildNodes; - var _policy = cXmlParser.GetEnumFromAttribute(XNode, "Policy", enumConfigPolicy.Default); - var _result = new List(); - - foreach (var _xNode in _xNodes) - { - if (!(_xNode is XmlElement _item)) - continue; - - Parser.EnterElement(_item.Name); - - try - { - if (_item.Name != "OpenActivityOverride") - { - var _msg = $"Invalid entry in F4SD-Global-Configuration.xml: {_item.Name}, should be "; - Parser.AddMessage(_item, _msg, LogLevels.Warning); - LogEntry(_msg, LogLevels.Warning); - continue; - } - - var activityType = cXmlParser.GetStringFromXmlAttribute(_item, "ActivityType", String.Empty)?.Trim(); - if (string.IsNullOrWhiteSpace(activityType)) - { - var _msg = $"Missing ActivityType attribute value in entry {_item.Name}"; - Parser.AddMessage(_item, _msg, LogLevels.Warning); - LogEntry(_msg, LogLevels.Warning); - continue; - } - - var valueText = cXmlParser.GetStringFromXmlAttribute(_item, "Value", String.Empty)?.Trim(); - if (string.IsNullOrWhiteSpace(valueText)) - { - var _msg = $"Missing Value attribute value in entry {_item.Name} ({activityType})"; - Parser.AddMessage(_item, _msg, LogLevels.Warning); - LogEntry(_msg, LogLevels.Warning); - continue; - } - - var normalizedValue = valueText.ToLowerInvariant(); - bool? parsedValue = null; - switch (normalizedValue) - { - case "true": - case "1": - case "yes": - parsedValue = true; - break; - case "false": - case "0": - case "no": - parsedValue = false; - break; - } - - if (!parsedValue.HasValue) - { - var _msg = $"Invalid Value attribute value ({valueText}) in entry {_item.Name} ({activityType})"; - Parser.AddMessage(_item, _msg, LogLevels.Warning); - LogEntry(_msg, LogLevels.Warning); - continue; - } - - _result.Add($"{activityType}={(parsedValue.Value ? "true" : "false")}"); - } - catch (Exception E) - { - LogException(E); - } - finally - { - Parser.LeaveElement(_item.Name); - } - } - - return new cConfigHelperParameterEntry() - { - Name = "OpenActivitiesExternallyOverrides", - ValueList = _result, - Policy = _policy.ToString() - }; - } - catch (Exception E) - { - LogException(E); - } - finally - { - if (CM != null) LogMethodEnd(CM); - } - - return null; - } + private cConfigHelperParameterList LoadParameters (XmlElement XRoot, cXmlParser Parser) + { + MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } + + var _result = new cConfigHelperParameterList(); + + try + { + var Items = XRoot.ChildNodes; + foreach (var _node in Items) + { + if (!(_node is XmlElement _item)) continue; + + Parser.EnterElement(_item.Name); + + try + { + var _attPolicy = _item.GetAttribute("Policy"); + var _attValue = _item.GetAttribute("Value"); + + var _ElementCount = 0; + foreach (XmlNode _subNode in _item.ChildNodes) + { + if (_subNode is XmlElement) + _ElementCount++; + } + + // check for special parameters + switch (_item.Name) + { + case "InformationClassSearchPriority": + var _val = getInformationClassSearchPriority(_item, Parser); + if (_val != null) + _result.Items[_val.Name] = _val; + continue; + case "OpenActivitiesExternally": + var _openActivitiesEntry = getOpenActivitiesExternallyEntry(_item); + if (_openActivitiesEntry != null) + _result.Items[_openActivitiesEntry.Name] = _openActivitiesEntry; + + var _overrideValues = getOpenActivitiesExternallyOverrides(_item, Parser); + if (_overrideValues != null && _overrideValues.ValueList != null && _overrideValues.ValueList.Count > 0) + _result.Items[_overrideValues.Name] = _overrideValues; + continue; + case "TicketProcessing": + var _ticketProcessingEntry = getTicketProcessingEntry(_item, Parser); + if (_ticketProcessingEntry != null) + _result.Items[_ticketProcessingEntry.Name] = _ticketProcessingEntry; + continue; + } + + if (_attPolicy != null && _attValue != null && _ElementCount == 0) + { + // we have a simple parameter + var _policy = cXmlParser.GetEnumFromAttribute(_item, "Policy", enumConfigPolicy.Default); + var _value = cXmlParser.GetStringFromXmlAttribute(_item, "Value", String.Empty); + + _result.Items[_item.Name] = new cConfigHelperParameterEntry() + { + Name = _item.Name, + Value = _value, + Policy = _policy.ToString(), + }; + } + else if (string.IsNullOrEmpty(_attPolicy) && string.IsNullOrEmpty(_attValue) && _ElementCount > 0) + { + // we have a sublist + if (_result.SubItems == null) + _result.SubItems = new Dictionary(); + _result.SubItems[_item.Name] = LoadParameters(_item, Parser); + } + else + { + // we have an invalid entry + var _msg = $"Invalid entry in F4SD-Global-Configuration.xml: {_item.Name}"; + Parser.AddMessage(_item, _msg, LogLevels.Warning); + LogEntry(_msg, LogLevels.Warning); + } + } + catch (Exception E) + { + LogException(E); + } + finally + { + Parser.LeaveElement(_item.Name); + } + } + } + catch (Exception E) + { + LogException(E); + } + finally + { + if (CM != null) LogMethodEnd(CM); + } + + return _result; + } + + private cConfigHelperParameterEntry getInformationClassSearchPriority(XmlElement XNode, cXmlParser Parser) + { + + MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } + + try + { + var _xNodes = XNode.ChildNodes; + + var _policy = cXmlParser.GetEnumFromAttribute(XNode, "Policy", enumConfigPolicy.Default); + + var _result = new List(); + + foreach (var _xNode in _xNodes) + { + if (!(_xNode is XmlElement _item)) + continue; + + Parser.EnterElement(_item.Name); + + try + { + if (_item.Name != "InformationClass") + { + // we have an invalid entry + var _msg = $"Invalid entry in F4SD-Global-Configuration.xml: {_item.Name}, should be "; + Parser.AddMessage(_item, _msg, LogLevels.Warning); + LogEntry(_msg, LogLevels.Warning); + continue; + } + + var _InfoClass = cXmlParser.GetEnumFromAttribute(_item, "Type", enumFasdInformationClass.Unknown); + if (_InfoClass == enumFasdInformationClass.Unknown) + { + // we have an invalid type attribute + var _strType = cXmlParser.GetStringFromXmlAttribute(_item, "Type", String.Empty); + var _msg = $"Invalid type attribute value ({_strType}) in entry {_item.Name}"; + Parser.AddMessage(_item, _msg, LogLevels.Warning); + LogEntry(_msg, LogLevels.Warning); + continue; + } + + var _strInfoClass = _InfoClass.ToString(); + if (!_result.Contains(_strInfoClass)) + _result.Add(_strInfoClass); + } + catch (Exception E) + { + LogException(E); + } + finally + { + Parser.LeaveElement(_item.Name); + } + } + + return new cConfigHelperParameterEntry() + { + Name = XNode.Name, + ValueList = _result, + Policy = _policy.ToString() + }; + } + catch (Exception E) + { + LogException(E); + } + finally + { + if (CM != null) LogMethodEnd(CM); + } + + return null; + } + + private static cConfigHelperParameterEntry getTicketProcessingEntry(XmlElement XNode, cXmlParser Parser) + { + MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } + + try + { + var _xNodes = XNode.SelectNodes("TicketTypeProcessing"); + + var _policy = cXmlParser.GetEnumFromAttribute(XNode, "Policy", enumConfigPolicy.Default); + var _value = cXmlParser.GetEnumFromAttribute(XNode, "Value", enumTicketProcessing.Both); + + var _result = new List(); + + foreach (var _xNode in _xNodes) + { + if (!(_xNode is XmlElement _item)) + continue; + + Parser.EnterElement(_item.Name); + + try + { + var _ticketType = cXmlParser.GetEnumFromAttribute(_item, "Type", enumTicketType.Ticket); + var _processingValue = cXmlParser.GetEnumFromAttribute(_item, "Value", enumTicketProcessing.Both); + _result.Add(_ticketType.ToString() + "=" + _processingValue.ToString()); + } + catch (Exception E) + { + LogException(E); + } + finally + { + Parser.LeaveElement(_item.Name); + } + } + + var _retVal = new cConfigHelperParameterEntry() + { + Name = XNode.Name, + ValueList = _result, + Policy = _policy.ToString() + }; + + return _retVal; + } + catch (Exception E) + { + LogException(E); + } + finally + { + if (CM != null) LogMethodEnd(CM); + } + + return null; + } + + + private static cConfigHelperParameterEntry getOpenActivitiesExternallyEntry(XmlElement XNode) + { + var _policy = cXmlParser.GetEnumFromAttribute(XNode, "Policy", enumConfigPolicy.Default); + var _value = cXmlParser.GetStringFromXmlAttribute(XNode, "Value", String.Empty); + + if (string.IsNullOrWhiteSpace(_value)) + return null; + + return new cConfigHelperParameterEntry() + { + Name = "OpenActivitiesExternally", + Value = _value, + Policy = _policy.ToString(), + }; + } + + private cConfigHelperParameterEntry getOpenActivitiesExternallyOverrides(XmlElement XNode, cXmlParser Parser) + { + MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } + + try + { + var _xNodes = XNode.ChildNodes; + var _policy = cXmlParser.GetEnumFromAttribute(XNode, "Policy", enumConfigPolicy.Default); + var _result = new List(); + + foreach (var _xNode in _xNodes) + { + if (!(_xNode is XmlElement _item)) + continue; + + Parser.EnterElement(_item.Name); + + try + { + if (_item.Name != "OpenActivityOverride") + { + var _msg = $"Invalid entry in F4SD-Global-Configuration.xml: {_item.Name}, should be "; + Parser.AddMessage(_item, _msg, LogLevels.Warning); + LogEntry(_msg, LogLevels.Warning); + continue; + } + + var activityType = cXmlParser.GetStringFromXmlAttribute(_item, "ActivityType", String.Empty)?.Trim(); + if (string.IsNullOrWhiteSpace(activityType)) + { + var _msg = $"Missing ActivityType attribute value in entry {_item.Name}"; + Parser.AddMessage(_item, _msg, LogLevels.Warning); + LogEntry(_msg, LogLevels.Warning); + continue; + } + + var valueText = cXmlParser.GetStringFromXmlAttribute(_item, "Value", String.Empty)?.Trim(); + if (string.IsNullOrWhiteSpace(valueText)) + { + var _msg = $"Missing Value attribute value in entry {_item.Name} ({activityType})"; + Parser.AddMessage(_item, _msg, LogLevels.Warning); + LogEntry(_msg, LogLevels.Warning); + continue; + } + + var normalizedValue = valueText.ToLowerInvariant(); + bool? parsedValue = null; + switch (normalizedValue) + { + case "true": + case "1": + case "yes": + parsedValue = true; + break; + case "false": + case "0": + case "no": + parsedValue = false; + break; + } + + if (!parsedValue.HasValue) + { + var _msg = $"Invalid Value attribute value ({valueText}) in entry {_item.Name} ({activityType})"; + Parser.AddMessage(_item, _msg, LogLevels.Warning); + LogEntry(_msg, LogLevels.Warning); + continue; + } + + _result.Add($"{activityType}={(parsedValue.Value ? "true" : "false")}"); + } + catch (Exception E) + { + LogException(E); + } + finally + { + Parser.LeaveElement(_item.Name); + } + } + + return new cConfigHelperParameterEntry() + { + Name = "OpenActivitiesExternallyOverrides", + ValueList = _result, + Policy = _policy.ToString() + }; + } + catch (Exception E) + { + LogException(E); + } + finally + { + if (CM != null) LogMethodEnd(CM); + } + + return null; + } public override bool DoXmlUpdates(XmlElement XmlRoot, bool withM42Config = false, bool withIntuneConfig = false, bool withMobileDeviceConfig = false, bool withCitrixConfig = false) { @@ -409,31 +472,31 @@ namespace C4IT.DataHistoryProvider , "NotesMandatory" , "" ); - RetVal |= DoXmlInsertElement(XmlRoot - , "TicketConfiguration" - , "ShowOverview" - , "" - ); - RetVal |= DoXmlInsertElement(XmlRoot - , "TicketConfiguration" - , "OpenActivitiesExternally" - , "" - ); - RetVal |= DoXmlInsertElement(XmlRoot - , "TicketConfiguration/OpenActivitiesExternally" - , "OpenActivityOverride[@ActivityType='SPSActivityTypeTicket']" - , "" - ); - RetVal |= DoXmlInsertElement(XmlRoot - , "TicketConfiguration/OpenActivitiesExternally" - , "OpenActivityOverride[@ActivityType='SPSActivityTypeServiceRequest']" - , "" - ); - RetVal |= DoXmlInsertElement(XmlRoot - , "TicketConfiguration" - , "OverviewPollingPersonal" - , $"" - ); + RetVal |= DoXmlInsertElement(XmlRoot + , "TicketConfiguration" + , "ShowOverview" + , "" + ); + RetVal |= DoXmlInsertElement(XmlRoot + , "TicketConfiguration" + , "OpenActivitiesExternally" + , "" + ); + RetVal |= DoXmlInsertElement(XmlRoot + , "TicketConfiguration/OpenActivitiesExternally" + , "OpenActivityOverride[@ActivityType='SPSActivityTypeTicket']" + , "" + ); + RetVal |= DoXmlInsertElement(XmlRoot + , "TicketConfiguration/OpenActivitiesExternally" + , "OpenActivityOverride[@ActivityType='SPSActivityTypeServiceRequest']" + , "" + ); + RetVal |= DoXmlInsertElement(XmlRoot + , "TicketConfiguration" + , "OverviewPollingPersonal" + , $"" + ); RetVal |= DoXmlInsertElement(XmlRoot , "TicketConfiguration" , "OverviewPollingRole" @@ -451,8 +514,30 @@ namespace C4IT.DataHistoryProvider RetVal |= DoXmlInsertElement(XmlRoot , "TicketConfiguration" , "DisableAutomaticTimeTracking" - , $"" + , $"" ); + + if(withIntuneConfig) + { + RetVal |= DoXmlInsertElement(XmlRoot + , "" + , "IntuneConfiguration" + , "" + ); + + RetVal |= DoXmlInsertElement(XmlRoot + , "IntuneConfiguration" + , "TAPLifeTimeInMinutes" + , $"" + ); + + RetVal |= DoXmlInsertElement(XmlRoot + , "IntuneConfiguration" + , "TAPIsUsableOnce" + , $"" + ); + + } } catch (Exception E) diff --git a/F4SD-Cockpit-ServerCore/DataHistoryConfigInfrastructure.cs b/F4SD-Cockpit-ServerCore/DataHistoryConfigInfrastructure.cs index d4594d7..b0c910b 100644 --- a/F4SD-Cockpit-ServerCore/DataHistoryConfigInfrastructure.cs +++ b/F4SD-Cockpit-ServerCore/DataHistoryConfigInfrastructure.cs @@ -1,15 +1,15 @@ -using C4IT.Configuration; -using C4IT.FASD.Base; +using C4IT.FASD.Base; using C4IT.Logging; using C4IT.Security; using C4IT.XML; -using C4IT_DataHistoryProvider; using System; using System.Collections.Generic; using System.Reflection; using System.Xml; + +using C4IT.DataHistoryProvider.Base.Modules.Citrix; + using static C4IT.Logging.cLogManager; -using static C4IT_DataHistoryProvider.cDataHistoryConfigCitrix; namespace C4IT.DataHistoryProvider { @@ -32,13 +32,14 @@ namespace C4IT.DataHistoryProvider public cDataHistoryConfigClientAgent ClientAgent { get; private set; } = null; public cDataHistoryConfigM42Wpm M42Wpm { get; private set; } = null; + public cDataHistoryConfigCitrix Citrix { get; private set; } = null; - public Dictionary CitrixTenants { get; private set; } = null; + public cDataHistoryConfigNexthink Nexthink { get; private set; } = null; public cDataHistoryConfigActiveDirectory ActiveDirectory { get; private set; } = null; - public Dictionary AzureTenants { get; private set; } = null; + public Dictionary AzureTenants { get; private set; } = null; //ToDo: create a class for the azure configuration instead of only the tenants public cF4SDAuthorisation Authorisation { get; private set; } = null; @@ -81,7 +82,6 @@ namespace C4IT.DataHistoryProvider ActiveDirectory = _ad; AzureTenants = cDataHistoryAzureTenant.LoadFromXml(XRoot, Credentials, Parser); - CitrixTenants = cDataHistoryCitrixTenant.LoadFromXml(XRoot, Credentials, Parser); // get the client agent config var _cla = new cDataHistoryConfigClientAgent(XRoot, Credentials, Conns, Parser); diff --git a/F4SD-Cockpit-ServerCore/DataHistoryConfigM42Wpm.cs b/F4SD-Cockpit-ServerCore/DataHistoryConfigM42Wpm.cs index 377d035..13fe4f4 100644 --- a/F4SD-Cockpit-ServerCore/DataHistoryConfigM42Wpm.cs +++ b/F4SD-Cockpit-ServerCore/DataHistoryConfigM42Wpm.cs @@ -102,9 +102,12 @@ namespace C4IT.DataHistoryProvider string name = a["QueueName"]?.Value; string id = a["QueueID"]?.Value; - Guid guid; + Guid guid = Guid.Empty; - if (string.IsNullOrWhiteSpace(name) || !Guid.TryParse(id, out guid)) + if (string.IsNullOrWhiteSpace(name)) + return null; + + if (!string.IsNullOrWhiteSpace(id) && !Guid.TryParse(id, out guid)) return null; return new cApiM42TicketQueueInfo diff --git a/F4SD-Cockpit-ServerCore/DataSources/IRemoteDesktopManager.cs b/F4SD-Cockpit-ServerCore/DataSources/IRemoteDesktopManager.cs new file mode 100644 index 0000000..2d3cd5c --- /dev/null +++ b/F4SD-Cockpit-ServerCore/DataSources/IRemoteDesktopManager.cs @@ -0,0 +1,16 @@ +using C4IT.FASD.Base; +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace C4IT.DataHistoryProvider.Base.DataSources +{ + public interface IRemoteDesktopManager + { + Task InitiateConnectionOfClient(TClientInfo clientInfos, bool isElevated, CancellationToken token = default); + Task TerminateConnection(Guid connectionId); + Task GetConnectionStatus(Guid connectionId); + bool IsActive(); + Task GetHealthInfo(); + } +} diff --git a/F4SD-Cockpit-ServerCore/DataSources/ISearchResultRelationProvider.cs b/F4SD-Cockpit-ServerCore/DataSources/ISearchResultRelationProvider.cs index 0ca1b36..e1790d8 100644 --- a/F4SD-Cockpit-ServerCore/DataSources/ISearchResultRelationProvider.cs +++ b/F4SD-Cockpit-ServerCore/DataSources/ISearchResultRelationProvider.cs @@ -1,9 +1,11 @@ -using C4IT.FASD.Base; -using System; +using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; +using C4IT.DataHistoryProvider; +using C4IT.FASD.Base; + namespace C4IT_DataHistoryProvider_Base.DataSources { public interface ISearchResultRelationProvider @@ -17,6 +19,6 @@ namespace C4IT_DataHistoryProvider_Base.DataSources /// Determines the information class the objects have, for which relations should be searched. /// Specifies the time, in which the relations should be considered. /// - Task GetRelationsAsync(IEnumerable ids, enumFasdInformationClass informationClass, int ageInDays, CancellationToken token = default); + Task GetRelationsAsync(IEnumerable ids, enumFasdInformationClass informationClass, int ageInDays, CancellationToken token = default); } } diff --git a/F4SD-Cockpit-ServerCore/F4SD-Cockpit-ServerCore.csproj b/F4SD-Cockpit-ServerCore/F4SD-Cockpit-ServerCore.csproj index 57ce3be..b69338c 100644 --- a/F4SD-Cockpit-ServerCore/F4SD-Cockpit-ServerCore.csproj +++ b/F4SD-Cockpit-ServerCore/F4SD-Cockpit-ServerCore.csproj @@ -7,7 +7,7 @@ Library - C4IT_DataHistoryProvider_Base + C4IT.DataHistoryProvider.Base Debug;Debug-Demo;Release;Release-Demo false @@ -50,6 +50,7 @@ + Common\C4IT.F4SD.GlobalConfig.cs diff --git a/F4SD-Cockpit-ServerCore/Modules/Agent/AgentApiHandler.cs b/F4SD-Cockpit-ServerCore/Modules/Agent/AgentApiHandler.cs new file mode 100644 index 0000000..dffd74a --- /dev/null +++ b/F4SD-Cockpit-ServerCore/Modules/Agent/AgentApiHandler.cs @@ -0,0 +1,95 @@ +using C4IT.DataHistoryProvider.Base.Modules.Agent.DTOs; +using C4IT.FASD.Base; +using C4IT.FASD.Communication.Agent; +using C4IT.HTTP; +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Net; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using static C4IT.Logging.cLogManager; + +namespace C4IT.DataHistoryProvider.Base.Modules.Agent +{ + internal class AgentApiHandler : cHttpApiBase + { + private readonly Uri _baseUrl; + private const string _agentLoginPath = "connect/token"; + private readonly cAgentApiConfiguration _agentConfig; + + public AgentApiHandler(cAgentApiConfiguration agentConfig) : base("Agent ID tenant", $"{agentConfig.ApiUrl}/{agentConfig.LogonUrl}") + { + _agentConfig = agentConfig; + _baseUrl = new Uri(_agentConfig.LogonUrl); + AlwaysReturnContent = true; + } + + private protected override async Task privLogonAsync(cOAuthLogonInfo logonInfo) + { + try + { + var postData = new List>() + { + new KeyValuePair("grant_type", "client_credentials"), + new KeyValuePair("client_id", logonInfo.ClientID), + new KeyValuePair("client_secret", logonInfo.ClientSecret) + }; + + var formData = new FormUrlEncodedContent(postData); + + var request = new HttpRequestMessage(HttpMethod.Post, new Uri(_baseUrl, _agentLoginPath)) { Content = formData }; + + using (var httpClient = new HttpClient()) + { + var response = await httpClient.SendAsync(request); + + if (response.StatusCode != HttpStatusCode.OK) + { + var responseMessage = await response.Content.ReadAsStreamAsync(); + LogEntry($"F4SD Agent Logon failed. StatusCode: {response.StatusCode}"); + LogEntry($"F4SD Agent Logon failed. Response: {responseMessage}"); + return false; + } + + string responseContent = await response.Content.ReadAsStringAsync(); + var logonResult = JsonConvert.DeserializeObject(responseContent); + + if (logonResult.TokenType.Equals("Bearer", StringComparison.OrdinalIgnoreCase) == false) + { + LogEntry("F4SD Agent Access token is not of type 'Bearer'."); + return false; + } + + cAgentApiLogonInfo.Instance = logonResult; + AccessToken = logonResult.AccessToken; + TokenExpiresIn = DateTime.UtcNow + TimeSpan.FromSeconds(logonResult.SecondsTillTokenExpires * RelogonFactor); + IsOnline = true; + } + + return true; + } + catch (Exception ex) + { + LogException(ex); + return false; + } + } + + internal async Task Request(string url, eHttpMethod httpMethod = eHttpMethod.get, object bodyData = null, CancellationToken token = default) where T : AgentApiResult + { + await LogonAsync(new cOAuthLogonInfo() { ClientID = _agentConfig.ClientId, ClientSecret = _agentConfig.ClientSecret, Tenant = "F4SD Agent" }); + Uri agentUrl = new Uri(_baseUrl, url); + dynamic requestResult = await privRequestAsync(agentUrl.ToString(), httpMethod, bodyData, token: token); + // todo handle not successfull status + requestResult.ToObject(); + string serialized = JsonConvert.SerializeObject(requestResult); // todo find better approach for casting the dynamic + T deserialized = JsonConvert.DeserializeObject(serialized); + return deserialized; + } + + internal async Task Request(string url, eHttpMethod httpMethod = eHttpMethod.get, object bodyData = null) + => await privRequestAsync(url, httpMethod, bodyData); + } +} diff --git a/F4SD-Cockpit-ServerCore/Modules/Agent/AgentRemoteClientInfo.cs b/F4SD-Cockpit-ServerCore/Modules/Agent/AgentRemoteClientInfo.cs new file mode 100644 index 0000000..2c192b2 --- /dev/null +++ b/F4SD-Cockpit-ServerCore/Modules/Agent/AgentRemoteClientInfo.cs @@ -0,0 +1,16 @@ +using Newtonsoft.Json; + +namespace C4IT.DataHistoryProvider.Base.Modules.Agent +{ + public class AgentRemoteClientInfo + { + [JsonProperty("orgCode")] + public int OrganisationCode { get; set; } + + [JsonProperty("deviceCode")] + public int DeviceCode { get; set; } + + [JsonProperty("accountCode")] + public int AccountCode { get; set; } + } +} diff --git a/F4SD-Cockpit-ServerCore/Modules/Agent/AgentRemoteConnectionInfo.cs b/F4SD-Cockpit-ServerCore/Modules/Agent/AgentRemoteConnectionInfo.cs new file mode 100644 index 0000000..64ced1e --- /dev/null +++ b/F4SD-Cockpit-ServerCore/Modules/Agent/AgentRemoteConnectionInfo.cs @@ -0,0 +1,18 @@ +using C4IT.FASD.Communication.Agent; +using Newtonsoft.Json; +using System; + +namespace C4IT.DataHistoryProvider.Base.Modules.Agent +{ + public class AgentRemoteConnectionInfo : AgentApiResult + { + [JsonProperty("connectionId")] + public Guid ConnectionId { get; set; } + + [JsonProperty("phoenixServiceUrl")] + public Uri ServiceUrl { get; set; } + + [JsonProperty("secret")] + public string Secret { get; set; } + } +} diff --git a/F4SD-Cockpit-ServerCore/Modules/Agent/AgentRemoteDesktopManager.cs b/F4SD-Cockpit-ServerCore/Modules/Agent/AgentRemoteDesktopManager.cs new file mode 100644 index 0000000..47a14e4 --- /dev/null +++ b/F4SD-Cockpit-ServerCore/Modules/Agent/AgentRemoteDesktopManager.cs @@ -0,0 +1,46 @@ +using C4IT.DataHistoryProvider.Base.DataSources; +using C4IT.FASD.Base; +using C4IT.FASD.Communication.Agent; +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace C4IT.DataHistoryProvider.Base.Modules.Agent +{ + public class AgentRemoteDesktopManager : IRemoteDesktopManager + { + private readonly AgentApiHandler _agentApiHandler; + + public AgentRemoteDesktopManager(cAgentApiConfiguration agentConfig) + { + _agentApiHandler = new AgentApiHandler(agentConfig); + } + + public async Task GetConnectionStatus(Guid connectionId) + { + return await _agentApiHandler.Request($"api/management/c4it/phoenix-connections/{connectionId}/status", HTTP.eHttpMethod.get); + } + + public async Task InitiateConnectionOfClient(AgentRemoteClientInfo clientInfos, bool isElevated, CancellationToken token) + { + string url = "api/management/c4it/phoenix-connections"; + if (isElevated) + url += "/admin"; + + return await _agentApiHandler.Request(url, HTTP.eHttpMethod.post, clientInfos, token); + } + + public async Task TerminateConnection(Guid connectionId) + { + await _agentApiHandler.Request($"api/management/c4it/phoenix-connections/{connectionId}", HTTP.eHttpMethod.delete); + } + + public bool IsActive() => true; + + public Task GetHealthInfo() + { + // todo Agent Server has to implement an "health" endpoint and return its result + return Task.FromResult(new HealthInformation(HealthStatus.healthy)); + } + } +} diff --git a/F4SD-Cockpit-ServerCore/Modules/Agent/DTOs/AgentErrorDto.cs b/F4SD-Cockpit-ServerCore/Modules/Agent/DTOs/AgentErrorDto.cs new file mode 100644 index 0000000..ff0a873 --- /dev/null +++ b/F4SD-Cockpit-ServerCore/Modules/Agent/DTOs/AgentErrorDto.cs @@ -0,0 +1,13 @@ +using Newtonsoft.Json; + +namespace C4IT.DataHistoryProvider.Base.Modules.Agent.DTOs +{ + public class AgentErrorDto + { + [JsonProperty("code")] + public string Code { get; set; } + + [JsonProperty("message")] + public string Message { get; set; } + } +} diff --git a/F4SD-Cockpit-ServerCore/Modules/Agent/DTOs/AgentLogonResultDto.cs b/F4SD-Cockpit-ServerCore/Modules/Agent/DTOs/AgentLogonResultDto.cs new file mode 100644 index 0000000..d1a81dd --- /dev/null +++ b/F4SD-Cockpit-ServerCore/Modules/Agent/DTOs/AgentLogonResultDto.cs @@ -0,0 +1,22 @@ +using Newtonsoft.Json; + +namespace C4IT.DataHistoryProvider.Base.Modules.Agent.DTOs +{ + public class AgentLogonResultDto + { + [JsonProperty("access_token")] + public string AccessToken { get; set; } + + [JsonProperty("expires_in")] + public int ExpiresIn { get; set; } + + [JsonProperty("token_type")] + public string TokenType { get; set; } + + [JsonProperty("scope")] + public string Scope { get; set; } + + [JsonProperty("remote_ip")] + public string RemoteIp { get; set; } + } +} diff --git a/F4SD-Cockpit-ServerCore/DataHistoryCollectorCitrix.cs b/F4SD-Cockpit-ServerCore/Modules/Citrix/DataHistoryCollectorCitrix.cs similarity index 76% rename from F4SD-Cockpit-ServerCore/DataHistoryCollectorCitrix.cs rename to F4SD-Cockpit-ServerCore/Modules/Citrix/DataHistoryCollectorCitrix.cs index f1d99c7..2ff0717 100644 --- a/F4SD-Cockpit-ServerCore/DataHistoryCollectorCitrix.cs +++ b/F4SD-Cockpit-ServerCore/Modules/Citrix/DataHistoryCollectorCitrix.cs @@ -1,57 +1,74 @@ using System; using System.Collections.Generic; +using System.Collections.ObjectModel; using System.Data.Common; using System.Data.SqlTypes; +using System.DirectoryServices.ActiveDirectory; using System.Dynamic; using System.Linq; +using System.Net; +using System.Net.Http.Headers; using System.Reflection; +using System.Security.Cryptography; using System.Security.Principal; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; + +using C4IT.Configuration; +using C4IT.DataHistoryProvider; using C4IT.FASD.Base; using C4IT.FASD.Communication.Citrix; using C4IT.HTTP; using C4IT.Logging; -using C4IT_DataHistoryProvider_Base.DataSources; -using Newtonsoft.Json.Linq; -using static C4IT.Logging.cLogManager; -using static C4IT_DataHistoryProvider.cDataHistoryConfigCitrix; +using C4IT.MultiLanguage; +using C4IT.XML; -namespace C4IT.DataHistoryProvider +using C4IT_DataHistoryProvider_Base.DataSources; + +using Microsoft.Extensions.Logging.Abstractions; + +using Newtonsoft.Json.Linq; + +using static C4IT.DataHistoryProvider.cDataHistoryCollector; +using static C4IT.FASD.Base.cHealthCardTranslator; +using static C4IT.FASD.Communication.Citrix.cCitrixCommunicationMonitor; +using static C4IT.Logging.cLogManager; + +namespace C4IT.DataHistoryProvider.Base.Modules.Citrix { public class cDataHistoryCollectorCitrix : cDataHistoryCollectorModule, ISearchResultRelationProvider { - public const string constConnectorName = "Citrix connector"; + public const string constConnectorName = "Citrix Virtual Apps and Desktops connector"; public const string constLicenseId = "F2DA5E05-FD3C-4182-9164-9C722140BF85"; - public const string constCitrixUserList = "monitorodata/Users?$top={0}&$select=Id,Sid,Upn,Domain&$filter=Domain ne ''"; - public const string constCitrixSessionList = "monitorodata/Sessions?$expand=Machine($select=Id,DnsName,Name)&$select=SessionKey,StartDate,EndDate,ConnectionState"; - public const string constCitrixSessions = "monitorodata/Sessions?$filter=UserId in ({0})&$expand=Machine($select=Id,DnsName,Name)&$select=SessionKey,StartDate,EndDate,ConnectionState"; - public const string constCitrixSession = "monitorodata/Sessions?$filter=SessionKey eq {0}"; - public const string constCitrixSessionMetrics = "monitorodata/Sessions?$select=SessionKey&$filter=SessionKey eq {0}&$expand=SessionMetrics"; - public const string constCitrixSessionMetricsIcaRTT = "monitorodata/Sessions?$select=SessionKey&$filter=SessionKey eq {0}&$expand=SessionMetrics($select=Id,CollectedDate,IcaRttMs)"; - public const string constCitrixSessionMetricsIcaLatency = "monitorodata/Sessions?$select=SessionKey&$filter=SessionKey eq {0}&$expand=SessionMetrics($select=Id,CollectedDate,IcaLatency)"; - public const string constCitrixSessionMetricsClientL7Latency = "monitorodata/Sessions?$select=SessionKey&$filter=SessionKey eq {0}&$expand=SessionMetrics($select=Id,CollectedDate,ClientL7Latency)"; - public const string constCitrixSessionMetricsServerL7Latency = "monitorodata/Sessions?$select=SessionKey&$filter=SessionKey eq {0}&$expand=SessionMetrics($select=Id,CollectedDate,ServerL7Latency)"; - public const string constCitrixSessionMetricsInputBandwidthUsed = "monitorodata/Sessions?$select=SessionKey&$filter=SessionKey eq {0}&$expand=SessionMetrics($select=Id,CollectedDate,InputBandwidthUsed)"; - public const string constCitrixSessionMetricsOutputBandwidthUsed = "monitorodata/Sessions?$select=SessionKey&$filter=SessionKey eq {0}&$expand=SessionMetrics($select=Id,CollectedDate,OutputBandwidthUsed)"; - public const string constCitrixSessionMetricsOutputBandwidthAvailable = "monitorodata/Sessions?$select=SessionKey&$filter=SessionKey eq {0}&$expand=SessionMetrics($select=Id,CollectedDate,OutputBandwidthAvailable)"; - public const string constCitrixSessionMetricsFps = "monitorodata/Sessions?$select=SessionKey&$filter=SessionKey eq {0}&$expand=SessionMetrics($select=Id,CollectedDate,Fps)"; - public const string constCitrixSessionMetricsInputFps = "monitorodata/Sessions?$select=SessionKey&$filter=SessionKey eq {0}&$expand=SessionMetrics($select=Id,CollectedDate,InputFps)"; - public const string constCitrixSessionMetricsOutputFps = "monitorodata/Sessions?$select=SessionKey&$filter=SessionKey eq {0}&$expand=SessionMetrics($select=Id,CollectedDate,OutputFps)"; - public const string constCitrixSessionMetricsWanLatency = "monitorodata/Sessions?$select=SessionKey&$filter=SessionKey eq {0}&$expand=SessionMetrics($select=Id,CollectedDate,WanLatency)"; - public const string constCitrixSessionMetricsDcLatency = "monitorodata/Sessions?$select=SessionKey&$filter=SessionKey eq {0}&$expand=SessionMetrics($select=Id,CollectedDate,DcLatency)"; - public const string constCitrixSessionMachine = "monitorodata/Sessions?$select=SessionKey&$filter=SessionKey eq {0}&$expand=Machine($expand=CurrentLoadIndex,Catalog,DesktopGroup($select=Name)),Machine"; - public const string constCitrixSessionCurrentConnection = "monitorodata/Sessions?$select=SessionKey&$filter=SessionKey eq {0}&$expand=CurrentConnection,LogOnMetrics"; - public const string constCitrixSessionConnections = "monitorodata/Sessions?$select=SessionKey&$filter=SessionKey eq {0}&$expand=Connection"; - public const string constCitrixSessionLogInSummaries = "monitorodata/Sessions?$filter=UserId in ({0})$select=Id,StartDate&$expand=User($select=UserName),LogOnSummaries($select=LogOnDuration,AuthenticationDuration,BrokingDuration,ProfileLoadDuration,GpoProcessingDuration,InteractiveSessionDuration)"; - public const string constCitrixSessionLogInSummaries2 = "monitorodata/LogOnSummaries?$expand=Session($expand=User)&$filter=UserId eq {0}"; - public const string constCitrixUserConnections = "monitorodata/Sessions?$filter=UserId in ({0})&$expand=Connection"; - public const string constCitrixSessionUser = "monitorodata/Sessions?$select=SessionKey&$filter=SessionKey eq {0}&$expand=User"; - public const string constCitrixSessionLogoff = "cvad/manage/Sessions/{0}/$logoff"; - public const string constCitrixSessionHide = "cvad/manage/Sessions/{0}/$hide"; - public const string constCitrixSessionSendMessage = "cvad/manage/Sessions/{0}/$sendMessage"; + public const string constCitrixUserList = "Users?$top={0}&$select=Id,Sid,Upn,Domain&$filter=Domain ne ''"; + public const string constCitrixSessionList = "Sessions?$expand=Machine($select=Id,DnsName,Name)&$select=SessionKey,StartDate,EndDate,ConnectionState"; + public const string constCitrixSessions = "Sessions?$filter=(EndDate eq null or EndDate gt {1}) and UserId in ({0})&$expand=Machine($select=Id,DnsName,Name)&$select=SessionKey,StartDate,EndDate,ConnectionState"; + public const string constCitrixSession = "Sessions?$filter=SessionKey eq {0}"; + public const string constCitrixSessionMetrics = "Sessions?$select=SessionKey&$filter=SessionKey eq {0}&$expand=SessionMetrics"; + public const string constCitrixSessionMetricsIcaRTT = "Sessions?$select=SessionKey&$filter=SessionKey eq {0}&$expand=SessionMetrics($select=Id,CollectedDate,IcaRttMs)"; + public const string constCitrixSessionMetricsIcaLatency = "Sessions?$select=SessionKey&$filter=SessionKey eq {0}&$expand=SessionMetrics($select=Id,CollectedDate,IcaLatency)"; + public const string constCitrixSessionMetricsClientL7Latency = "Sessions?$select=SessionKey&$filter=SessionKey eq {0}&$expand=SessionMetrics($select=Id,CollectedDate,ClientL7Latency)"; + public const string constCitrixSessionMetricsServerL7Latency = "Sessions?$select=SessionKey&$filter=SessionKey eq {0}&$expand=SessionMetrics($select=Id,CollectedDate,ServerL7Latency)"; + public const string constCitrixSessionMetricsInputBandwidthUsed = "Sessions?$select=SessionKey&$filter=SessionKey eq {0}&$expand=SessionMetrics($select=Id,CollectedDate,InputBandwidthUsed)"; + public const string constCitrixSessionMetricsOutputBandwidthUsed = "Sessions?$select=SessionKey&$filter=SessionKey eq {0}&$expand=SessionMetrics($select=Id,CollectedDate,OutputBandwidthUsed)"; + public const string constCitrixSessionMetricsOutputBandwidthAvailable = "Sessions?$select=SessionKey&$filter=SessionKey eq {0}&$expand=SessionMetrics($select=Id,CollectedDate,OutputBandwidthAvailable)"; + public const string constCitrixSessionMetricsFps = "Sessions?$select=SessionKey&$filter=SessionKey eq {0}&$expand=SessionMetrics($select=Id,CollectedDate,Fps)"; + public const string constCitrixSessionMetricsInputFps = "Sessions?$select=SessionKey&$filter=SessionKey eq {0}&$expand=SessionMetrics($select=Id,CollectedDate,InputFps)"; + public const string constCitrixSessionMetricsOutputFps = "Sessions?$select=SessionKey&$filter=SessionKey eq {0}&$expand=SessionMetrics($select=Id,CollectedDate,OutputFps)"; + public const string constCitrixSessionMetricsWanLatency = "Sessions?$select=SessionKey&$filter=SessionKey eq {0}&$expand=SessionMetrics($select=Id,CollectedDate,WanLatency)"; + public const string constCitrixSessionMetricsDcLatency = "Sessions?$select=SessionKey&$filter=SessionKey eq {0}&$expand=SessionMetrics($select=Id,CollectedDate,DcLatency)"; + public const string constCitrixSessionMachine = "Sessions?$select=SessionKey&$filter=SessionKey eq {0}&$expand=Machine($expand=CurrentLoadIndex,Catalog,DesktopGroup($select=Name)),Machine"; + public const string constCitrixSessionCurrentConnection = "Sessions?$select=SessionKey&$filter=SessionKey eq {0}&$expand=CurrentConnection,LogOnMetrics"; + public const string constCitrixSessionConnections = "Sessions?$select=SessionKey&$filter=SessionKey eq {0}&$expand=Connection"; + public const string constCitrixSessionLogInSummaries = "Sessions?$filter=UserId in ({0})$select=Id,StartDate&$expand=User($select=UserName),LogOnSummaries($select=LogOnDuration,AuthenticationDuration,BrokingDuration,ProfileLoadDuration,GpoProcessingDuration,InteractiveSessionDuration)"; + public const string constCitrixSessionLogInSummaries2 = "LogOnSummaries?$expand=Session($expand=User)&$filter=UserId eq {0}"; + public const string constCitrixUserConnections = "Sessions?$filter=UserId in ({0})&$expand=Connection"; + public const string constCitrixSessionUser = "Sessions?$select=SessionKey&$filter=SessionKey eq {0}&$expand=User"; + public const string constCitrixSessionLogoff = "manage/Sessions/{0}/$logoff"; + public const string constCitrixSessionHide = "manage/Sessions/{0}/$hide"; + public const string constCitrixSessionSendMessage = "manage/Sessions/{0}/$sendMessage"; public const string constTableNameSession = "citrix-session"; public const string constTableNameSessionMachine = "citrix-session-machine"; public const string constTableNameSessionConnection = "citrix-session-connection"; @@ -73,32 +90,57 @@ namespace C4IT.DataHistoryProvider public const string constTableNameSessionDetailsDcLatency = "citrix-session-details-dcLatency"; public const int constCitrixUserPaging = 100; + + public bool DoScanSequential = false; + public readonly List UserMainProperties = new List() { new cLdapPropertyInfo() { Name="objectSid", LdapType= enumLdapPropertyType.ldapSid, DbName=null}, new cLdapPropertyInfo() { Name="userPrincipalName", LdapType= enumLdapPropertyType.ldapString, DbName="upn_external"}, }; - private Dictionary tenantCitrixCache = new Dictionary(); - private readonly cDataHistoryCollector _collector; + + public static Dictionary UserTestReplacement = new Dictionary() + { + { "07ac970e-8bde-454e-b14d-9c7f58cf8a8d:demouser@f4sd.dev", "burak.orakci@consulting4it.de"} + }; + + [Obsolete] + private Dictionary tenantCitrixCache = new Dictionary(); + + private Dictionary cachedConnections = new Dictionary(); + + private List unauthorizedSystems = new List(); + public cDataHistoryCollectorCitrix(cDataHistoryCollector Collector) : base(Collector, enumDataHistoryOrigin.Citrix, constConnectorName, constLicenseId) { - _collector = Collector; + if (Collector?.InfrastructureConfig?.Citrix?.Systems != null) + { + foreach (var _system in Collector.InfrastructureConfig.Citrix.Systems) + unauthorizedSystems.Add(_system); + } } - public async Task GetCitrixCommunicationForTenantAsync(string tenant, bool force = false) + + [Obsolete] + public async Task GetCitrixCommunicationForSystemAsync(string tenant, bool force = false) { MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } try { - if (tenant == null) + if (string.IsNullOrEmpty(tenant)) return null; - cDataHistoryCitrixTenant _CitrixTenant = null; - var tenants = Collector.InfrastructureConfig.CitrixTenants?.Values; - if (tenants is null) - { + if (Collector?.InfrastructureConfig?.Citrix?.Systems == null) return null; - } - foreach (var Tenant in tenants) + + cDataHistoryCitrixCloudTenant _CitrixTenant = null; + var systems = Collector.InfrastructureConfig.Citrix.Systems; + if (systems is null) + return null; + + foreach (var _system in systems) { + if (!(_system is cDataHistoryCitrixCloudTenant Tenant)) + continue; + if (Tenant.TenantID == tenant) { _CitrixTenant = Tenant; @@ -128,12 +170,284 @@ namespace C4IT.DataHistoryProvider return null; } - public async Task LogonCitrixTenantAsync(cDataHistoryCitrixTenant Tenant, bool force = false) + + private async Task GetSiteIdForSystem(cCitrixCommunicationSystem System, cDataHistoryCitrixSystem config, CancellationToken token) + { + MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } + + try + { + var _customerInfo = await System.CVAD.GetCustomerInfoAsync(token); + if (token.IsCancellationRequested) + return null; + + if (_customerInfo?.Sites == null || _customerInfo.Sites.Count == 0) + { + Collector.DoProcessUiMessage(3, $"Could not get customer info for {config.Name}: {System.CVAD.Request.LastErrorMessage}", LogLevels.Error); + return null; + } + + if (_customerInfo.Sites.Count == 1) + { + var _id = _customerInfo.Sites.First().Value.Id; + if (config.SiteID == Guid.Empty) + return _id; + else if (_id == config.SiteID) + return _id; + else + { + Collector.DoProcessUiMessage(3, $"The configured site '{config.SiteID}' id does not match the authenticated site id '{_id}'."); + return null; + } + } + else + { + if (config.SiteID == Guid.Empty) + { + Collector.DoProcessUiMessage(3, $"There are more than one site defined (Count={_customerInfo.Sites.Count}). You have to specify the Site ID in your configuration for {config.Name}"); + return null; + } + + foreach (var site in _customerInfo.Sites.Values) + { + if (site.Id == config.SiteID) + return site.Id; + } + + Collector.DoProcessUiMessage(3, $"The specified Site ID in your configuration could not be found in the site list for {config.Name}"); + return null; + } + + } + catch (Exception E) + { + LogException(E); + } + finally + { + if (CM != null) LogMethodEnd(CM); + } + + return null; + + } + + private async Task LogonToCloudTenantAsync(cDataHistoryCitrixCloudTenant Tenant, CancellationToken token) + { + MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } + + try + { + var _citrixComm = new cCitrixCommunicationCloudBase("https://" + Tenant.Domain); + + var _logonInfo = new cOAuthLogonInfo + { + Tenant = Tenant.TenantID.ToString(), + ClientID = Tenant.Credential?.User, + ClientSecret = Tenant.Credential?.NwCredential.Password, + }; + + var IsAuthenticated = await _citrixComm.LogonAsync(_logonInfo); + + if (!IsAuthenticated) + { + Collector.DoProcessUiMessage(3, $"Could not login to cloud tenant '{Tenant.Name}': {_citrixComm.LastErrorMessage}", LogLevels.Error); + return null; + } + + var CitrixSystem = new cCitrixCommunicationSystem(_citrixComm, Tenant.Name); + + var siteID = await GetSiteIdForSystem(CitrixSystem, Tenant, token); + if (token.IsCancellationRequested) + return null; + + if (siteID == null) + return null; + + CitrixSystem.CVAD.CitrixInfo.SiteId = siteID?.ToString("D"); + + return CitrixSystem; + } + catch (Exception E) + { + LogException(E); + } + finally + { + if (CM != null) LogMethodEnd(CM); + } + + return null; + } + + private async Task LogonToOnPremSystemAsync(cDataHistoryCitrixOnPrem System, CancellationToken token) + { + MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } + + try + { + var strUrlCvad = $"https://{System.Server}"; + var _creds = System.Credential.NwCredential; + + // logon to the CVAD API + var _citrixComm = new cCitrixCommunicationOnPremCvad(strUrlCvad); + var IsAuthenticated = await _citrixComm.LogonWithCredentialsAsync(_creds); + + if (!IsAuthenticated) + { + Collector.DoProcessUiMessage(3, $"Could not login to OnPrem CVAD API at'{System.Name}': {_citrixComm.LastErrorMessage}", LogLevels.Error); + return null; + } + + // logon to the monitor API + var strUrlMonitor = $"https://{System.Server}"; + + var _citrixMonitor = new cCitrixCommunicationOnPremMonitor(strUrlMonitor); + var _monitorResult = await _citrixMonitor.LogonWithCredentialsAsync(_creds); + if (token.IsCancellationRequested) + return null; + + if (!_monitorResult) + { + _citrixMonitor = null; + strUrlMonitor = $"http://{System.Server}"; + _citrixMonitor = new cCitrixCommunicationOnPremMonitor(strUrlMonitor); + _monitorResult = await _citrixMonitor.LogonWithCredentialsAsync(_creds); + + if (token.IsCancellationRequested) + return null; + + if (!_monitorResult) + { + Collector.DoProcessUiMessage(3, $"Could not login to OnPrem monitor API at'{System.Name}': {_citrixComm.LastErrorMessage}", LogLevels.Error); + return null; + } + } + + var CitrixSystem = new cCitrixCommunicationSystem(_citrixComm, _citrixMonitor, System.Name); + + var siteID = await GetSiteIdForSystem(CitrixSystem, System, token); + if (token.IsCancellationRequested) + return null; + + if (siteID == null) + return null; + + CitrixSystem.CVAD.CitrixInfo.SiteId = siteID?.ToString("D"); + return CitrixSystem; + } + catch (Exception E) + { + LogException(E); + } + finally + { + if (CM != null) LogMethodEnd(CM); + } + + + return null; + } + + private async Task LogonToSystemAsync(cDataHistoryCitrixSystem System, CancellationToken token) { MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } try { - var CitrixConn = new cCitrixCommunication(); + if (System is cDataHistoryCitrixCloudTenant Tenant) + return await LogonToCloudTenantAsync(Tenant, token); + else if (System is cDataHistoryCitrixOnPrem OnPrem) + { + return await LogonToOnPremSystemAsync(OnPrem, token); + } + } + catch (Exception E) + { + LogException(E); + } + finally + { + if (CM != null) LogMethodEnd(CM); + } + return null; + } + + public async Task ValidateConnectionsAsync(CancellationToken token) + { + MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } + + try + { + List _toRemove = new List(); + foreach (var _system in unauthorizedSystems) + { + Collector.DoProcessUiMessage(2, $"Try logon to {_system.Name}..."); + var _ok = false; + var CitrixConn = await LogonToSystemAsync(_system, token); + if (token.IsCancellationRequested) + return false; + if (CitrixConn != null) + { + if (!Guid.TryParse(CitrixConn.CVAD.CitrixInfo.SiteId, out var siteId)) + continue; + + _toRemove.Add(_system); + cachedConnections[siteId] = CitrixConn; + _ok = true; + } + if (_ok) + Collector.DoProcessUiMessage(2, $"Successfull logon to {_system.Name}."); + else + Collector.DoProcessUiMessage(2, $"Could not logon to {_system.Name}.", LogLevels.Error); + } + + foreach (var _system in _toRemove) + unauthorizedSystems.Remove(_system); + + if (unauthorizedSystems.Count > 0) + return true; + } + catch (Exception E) + { + LogException(E); + } + finally + { + if (CM != null) LogMethodEnd(CM); + } + + return false; + } + + public async Task GetConnection(Guid SiteId) + { + MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } + + try + { + if (cachedConnections.TryGetValue(SiteId, out var cachedConnection)) + return cachedConnection; + + } + catch (Exception E) + { + LogException(E); + } + finally + { + if (CM != null) LogMethodEnd(CM); + } + + return null; + } + + public async Task LogonCitrixTenantAsync(cDataHistoryCitrixCloudTenant Tenant, bool force = false) + { + MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } + try + { + var _domain = Tenant.Domain; + var CitrixConn = new cCitrixCommunicationCloudBase(_domain); if (Tenant == null) { return null; @@ -143,11 +457,12 @@ namespace C4IT.DataHistoryProvider Tenant = Tenant?.TenantID.ToString(), ClientID = Tenant.Credential?.User, ClientSecret = Tenant.Credential?.NwCredential.Password, - InstanceId = Tenant?.InstanceID.ToString() }; - var RetVal = await CitrixConn.LogonAsync(LI); - if (RetVal) - return CitrixConn; + var _ok = await CitrixConn.LogonAsync(LI); + if (_ok) + { + return new cCitrixCommunicationSystem(CitrixConn, Tenant.Name); + } } catch (Exception E) { @@ -161,46 +476,57 @@ namespace C4IT.DataHistoryProvider return null; } - public async Task GetCitrixUsersAsync() + public override bool UseForeignUserIds() + { + return true; + } + + [Obsolete] + public async Task GetCitrixUsersAsync() { MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } try { var strUrl = string.Format(constCitrixUserList, constCitrixUserPaging); - var tenants = Collector.InfrastructureConfig.CitrixTenants?.Values; - if (tenants is null) + var systems = Collector.InfrastructureConfig.Citrix.Systems; + if (systems is null) { Collector.DoProcessUiMessage(2, $" Could not logon to tenant - Tenant is null", LogLevels.Warning); - return null; + return false; } - foreach (var Tenant in tenants) - { - var CitrixConn = await GetCitrixCommunicationForTenantAsync(Tenant.TenantID, true); + var _retVal = true; + foreach (var _system in systems) + { + if (!(_system is cDataHistoryCitrixCloudTenant Tenant)) + continue; + + var CitrixConn = await GetCitrixCommunicationForSystemAsync(Tenant.TenantID, true); if (CitrixConn == null) { Collector.DoProcessUiMessage(2, $"error: could not logon to tenat {Tenant.Domain}", LogLevels.Error); + _retVal = false; continue; } - var Result = await CitrixConn.RequestAsync(strUrl, retryForbidden: false); + var Result = await CitrixConn.Monitor.Request.RequestAsync(strUrl, eHttpMethod.get, null); if (Result != null) { - - - var _ty = Result.GetType(); var _props = Result.Result as JObject; if (_props != null) { Collector.DoProcessUiMessage(3, $" properties of selected '{_props}' with {_props.Count} properties could be read."); - } - } - + else + { + Collector.DoProcessUiMessage(2, $" Could not get user list from tenant {Tenant.Domain}", LogLevels.Warning); + } } + + return _retVal; } catch (Exception E) { @@ -211,27 +537,164 @@ namespace C4IT.DataHistoryProvider if (CM != null) LogMethodEnd(CM); } + return false; + } + + private class ScanSessionEntryProcessor : ScanEntryProcessor + { + private cCitrixCommunicationMonitor _comm; + + private Guid userId; + + private Guid instanceId; + + private string instanceName; + + public readonly List sessionResult = new List(); + + public ScanSessionEntryProcessor(cCitrixCommunicationMonitor _comm, Guid userId, Guid instanceId, string instandName) + { + this._comm = _comm; + this.userId = userId; + this.instanceId = instanceId; + this.instanceName = instandName; + } + + public async override Task ProcessScannedItemAsync(dynamic _node, CancellationToken token) + { + try + { + var endDate = DateTime.MinValue; + if (!DateTime.TryParse(_node.EndDate?.ToString(), out endDate)) + endDate = DateTime.UtcNow; + + var startDate = DateTime.MinValue; + if (!DateTime.TryParse(_node.StartDate?.ToString(), out startDate)) + startDate = DateTime.UtcNow; + + string SessionKey = _node.SessionKey.ToString(); + if (!Guid.TryParse(SessionKey, out var SessionId)) + SessionId = Guid.Empty; + + var _duration = (endDate - startDate).TotalSeconds; + + Enum.TryParse(_node?.ConnectionState.ToString(), out enumCitrixSessionStatus connectionState); + + string machineName = _node?.Machine?.Name?.ToString(); + if (machineName != null) + machineName = Regex.Replace(machineName, @"^.*\\", ""); + + var Infos = new Dictionary() + { + { "Status", connectionState.ToString() }, + { "StatusId", _node?.ConnectionState?.ToString() }, + { "Domain", instanceName } + }; + + var sessionInfo = new cF4sdApiSearchResultRelation() + { + Type = enumF4sdSearchResultClass.VirtualSession, + Name = machineName, + DisplayName = machineName, + LastUsed = endDate, + UsingLevel = _duration == 0 ? 1 : _duration, + id = SessionId, + Status = enumF4sdSearchResultStatus.Active, + Infos = Infos, + Identities = new cF4sdIdentityList() + { + new cF4sdIdentityEntry(){ Class = enumFasdInformationClass.User, Id = this.userId}, + new cF4sdIdentityEntry(){ Class = enumFasdInformationClass.VirtualSession, Id = SessionId, Instance = instanceId} + } + }; + + sessionResult.Add(sessionInfo); + } + catch (Exception E) + { + LogException(E); + } + return false; + } + + } + + public async Task> GetUserSessionsForSystemAsync(cForeignUserInfoEntry UserInfo, int age, CancellationToken Token, cF4sdWebRequestInfo requestInfo) + { + MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } + + try + { + if (!Guid.TryParse(UserInfo.InstanceID, out var _SiteId)) + return null; + + if (!int.TryParse(UserInfo.ForeignID, out var _CitrixId)) + return null; + + if (!cachedConnections.TryGetValue(_SiteId, out var _comm)) + return null; + + var scanProcessor = new ScanSessionEntryProcessor(_comm.Monitor, UserInfo.MainID, _SiteId, _comm.Name); + await _comm.Monitor.ScanSessionsAsync(scanProcessor, -age, Token); + + // get the percentage of the Using Level by determining the maximal duration and compute the ratio + double _maxDuration = 0; + foreach (var _entry in scanProcessor.sessionResult) + _maxDuration = Math.Max(_maxDuration, _entry.UsingLevel); + + if (_maxDuration > 0) + foreach (var _entry in scanProcessor.sessionResult) + _entry.UsingLevel = _entry.UsingLevel / _maxDuration; + + return scanProcessor.sessionResult; + + } + catch (Exception E) + { + LogException(E); + } + finally + { + if (CM != null) LogMethodEnd(CM); + } + return null; } - public async Task> GetUserSessionsAsync(List UserId, CancellationToken Token, cF4sdWebRequestInfo requestInfo) + public async Task> GetUserSessionsAsync(IEnumerable UserIds, int age, 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?.citrixUserId.ToString())).Select(u => u.citrixUserId)); + var _retVal = new List(); + foreach (var _userEntry in UserIds) + { + if (_userEntry.ForeignUserInfo != null && _userEntry.ForeignUserInfo.TryGetValue(enumDataHistoryOrigin.Citrix, out var _entries2)) + foreach (var _userEntry2 in _entries2) + { + var _res = await GetUserSessionsForSystemAsync(_userEntry2, age, Token, requestInfo); + if (_res != null) + _retVal.AddRange(_res); + } + } + return _retVal; - var strUrl = string.Format(constCitrixSessions, userIds); + string userIds = string.Join(",", UserIds.Where(u => !string.IsNullOrWhiteSpace(u?.citrixUserId.ToString())).Select(u => u.citrixUserId)); - var tenantId = UserId?.FirstOrDefault(u => !string.IsNullOrWhiteSpace(u?.citrixTenantId))?.citrixTenantId; + //DateTime latestEndDate = DateTime.UtcNow.AddDays(-age); + //ToDo: originale Altersberechnung wiederherstellen + DateTime latestEndDate = DateTime.UtcNow.AddDays(-90); + var strUrl = string.Format(constCitrixSessions, userIds, latestEndDate.ToString("yyyy-MM-ddThh:mm:ssK")); + + var tenantId = UserIds?.FirstOrDefault(u => !string.IsNullOrWhiteSpace(u?.citrixTenantId))?.citrixTenantId; if (tenantId is null) { Collector.DoProcessUiMessage(2, $" Could not logon to tenant - Tenant is null", LogLevels.Warning); return null; } - var CitrixConn = await GetCitrixCommunicationForTenantAsync(tenantId, true); + var CitrixConn = await GetCitrixCommunicationForSystemAsync(tenantId, true); if (CitrixConn == null) { @@ -239,10 +702,11 @@ namespace C4IT.DataHistoryProvider return null; } + // ToDo: Implement a paged walkthrough of results in case there are more results than the API can return in one call // maybe we should think about a concrete implementation of the results instead of a dynamic, what do you think? - var Result = await CitrixConn.RequestListAsync(strUrl, retryForbidden: false); - - if (Result != null == Result.Count > 0) + var Result = await CitrixConn.Monitor.Request.RequestListAsync(strUrl, false, retryForbidden: false); + + if (Result != null && Result.Count > 0) { var sessionResult = new List(Result.Count); double _sumDuration = 0; @@ -283,7 +747,7 @@ namespace C4IT.DataHistoryProvider Infos = Infos, Identities = new cF4sdIdentityList() { - new cF4sdIdentityEntry(){ Class = enumFasdInformationClass.User, Id = UserId[0].Id}, + new cF4sdIdentityEntry(){ Class = enumFasdInformationClass.User, Id = UserIds.First().Id}, new cF4sdIdentityEntry(){ Class = enumFasdInformationClass.VirtualSession, Id = Entry.Result.SessionKey} } }; @@ -312,7 +776,7 @@ namespace C4IT.DataHistoryProvider return null; } - private async Task> GetSessionMetricsAsync(Guid Sessionid, string TenantId, cF4sdWebRequestInfo RequestInfo,int MaxAge, int LogDeep, CancellationToken Token) + private async Task> GetSessionMetricsAsync(Guid Sessionid, string TenantId, cF4sdWebRequestInfo RequestInfo, int MaxAge, 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); } @@ -325,11 +789,12 @@ namespace C4IT.DataHistoryProvider var strUrl = string.Format(constCitrixSessionMetrics, Sessionid); - var citrixCommunication = await GetCitrixCommunicationForTenantAsync(TenantId, true); + var citrixCommunication = await GetCitrixCommunicationForSystemAsync(TenantId, true); if (citrixCommunication == null) return null; - var res = await citrixCommunication.RequestListAsync(strUrl, retryForbidden: false); + // ToDo: Implement a paged walkthrough of results in case there are more results than the API can return in one call + var res = await citrixCommunication.Monitor.Request.RequestListAsync(strUrl, false, retryForbidden: false); if (res != null == res.Count > 0) { JObject sessionInfos = res[0].Result; @@ -342,13 +807,14 @@ namespace C4IT.DataHistoryProvider TableType = eDataHistoryTableType.Static }; - foreach (var Entry in res) { - - - + foreach (var Entry in res) + { + + + } - var dicVals = new List>(); + var dicVals = new List>(); var vals = sessionInfos.Children(); var metricsObj = sessionInfos["SessionMetrics"] as JArray; @@ -455,7 +921,7 @@ namespace C4IT.DataHistoryProvider private async Task> GetSessionStateMetricsAsync(cF4sdConnectorIds UserIds, string TenantId, cF4sdWebRequestInfo RequestInfo, int MaxAge, 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; @@ -468,11 +934,11 @@ namespace C4IT.DataHistoryProvider if (!Collector.ClusterConfig.Tables.TryGetValue(constTableNameSessionStateMetrics, out var SessionTableDetails)) return null; - var citrixCommunication = await GetCitrixCommunicationForTenantAsync(TenantId, true); + var citrixCommunication = await GetCitrixCommunicationForSystemAsync(TenantId, true); if (citrixCommunication == null) return null; - var res = await citrixCommunication.RequestAsync(strUrl, retryForbidden: false); + var res = await citrixCommunication.Monitor.Request.RequestAsync(strUrl, eHttpMethod.get, null); if (res != null) { JObject sessionInfos = res.Result; @@ -585,7 +1051,7 @@ namespace C4IT.DataHistoryProvider var value = valuesOfColumn.FirstOrDefault(); AddColumnData(retVal, colInfo.SourceName, ConvertToF4sdType(value, colInfo.ValueType)); } - + } @@ -626,7 +1092,7 @@ namespace C4IT.DataHistoryProvider } } - private async Task> GetSessionMetricsDetailsIcaRttMSAsync(Guid Sessionid, string TenantId, cF4sdWebRequestInfo RequestInfo,int MaxAge, DateTime RefTime, int LogDeep, CancellationToken Token) + private async Task> GetSessionMetricsDetailsIcaRttMSAsync(Guid Sessionid, string TenantId, cF4sdWebRequestInfo RequestInfo, int MaxAge, DateTime RefTime, 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); } @@ -639,17 +1105,18 @@ namespace C4IT.DataHistoryProvider var strUrl = string.Format(constCitrixSessionMetricsIcaRTT, Sessionid); - var citrixCommunication = await GetCitrixCommunicationForTenantAsync(TenantId, true); + var citrixCommunication = await GetCitrixCommunicationForSystemAsync(TenantId, true); if (citrixCommunication == null) return null; - var res = await citrixCommunication.RequestListAsync(strUrl, retryForbidden: false); + // ToDo: Implement a paged walkthrough of results in case there are more results than the API can return in one call + var res = await citrixCommunication.Monitor.Request.RequestListAsync(strUrl, false, retryForbidden: false); if (res != null) { JObject sessionInfos = res[0].Result; var retValList = new List(); - + var vals = sessionInfos.Children(); var metricsObj = sessionInfos["SessionMetrics"] as JArray; @@ -668,7 +1135,7 @@ namespace C4IT.DataHistoryProvider var groupedValues = metricsObj.Where(v => v[collectDateColumnName] != null && DateTime.TryParse(v[collectDateColumnName]?.ToString(), out _)) .GroupBy(v => DateTime.Parse(v[collectDateColumnName].ToString()).Date); - + for (int i = 0; i < MaxAge; i++) { DateTime referenceDate = DateTime.Now.AddDays(-i).Date; @@ -684,7 +1151,7 @@ namespace C4IT.DataHistoryProvider foreach (var session in metricsObj) { try - { + { var dicVals = session.Children().ToDictionary(prop => prop.Name, prop => prop.Value?.ToObject()); var row = retVal.Columns.Select(colName => dicVals.TryGetValue(colName, out var value) ? value : null).ToArray(); @@ -702,7 +1169,7 @@ namespace C4IT.DataHistoryProvider } index++; } - + } retVal.Columns = SessionTableDetails.Columns.Values.Select(v => v.Name).ToList(); return new List(1) { retVal }; @@ -727,7 +1194,7 @@ namespace C4IT.DataHistoryProvider return null; } - private async Task> GetSessionMetricsDetailsIcaLatencyAsync(Guid Sessionid, string TenantId, cF4sdWebRequestInfo RequestInfo,int MaxAge, DateTime RefTime, int LogDeep, CancellationToken Token) + private async Task> GetSessionMetricsDetailsIcaLatencyAsync(Guid Sessionid, string TenantId, cF4sdWebRequestInfo RequestInfo, int MaxAge, DateTime RefTime, 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); } @@ -740,11 +1207,12 @@ namespace C4IT.DataHistoryProvider var strUrl = string.Format(constCitrixSessionMetricsIcaLatency, Sessionid); - var citrixCommunication = await GetCitrixCommunicationForTenantAsync(TenantId, true); + var citrixCommunication = await GetCitrixCommunicationForSystemAsync(TenantId, true); if (citrixCommunication == null) return null; - var res = await citrixCommunication.RequestListAsync(strUrl, retryForbidden: false); + // ToDo: Implement a paged walkthrough of results in case there are more results than the API can return in one call + var res = await citrixCommunication.Monitor.Request.RequestListAsync(strUrl, false, retryForbidden: false); if (res != null) { JObject sessionInfos = res[0].Result; @@ -840,11 +1308,12 @@ namespace C4IT.DataHistoryProvider var strUrl = string.Format(constCitrixSessionMetricsClientL7Latency, Sessionid); - var citrixCommunication = await GetCitrixCommunicationForTenantAsync(TenantId, true); + var citrixCommunication = await GetCitrixCommunicationForSystemAsync(TenantId, true); if (citrixCommunication == null) return null; - var res = await citrixCommunication.RequestListAsync(strUrl, retryForbidden: false); + // ToDo: Implement a paged walkthrough of results in case there are more results than the API can return in one call + var res = await citrixCommunication.Monitor.Request.RequestListAsync(strUrl, false, retryForbidden: false); if (res != null) { JObject sessionInfos = res[0].Result; @@ -869,7 +1338,7 @@ namespace C4IT.DataHistoryProvider var groupedValues = metricsObj.Where(v => v[collectDateColumnName] != null && DateTime.TryParse(v[collectDateColumnName]?.ToString(), out _)) .GroupBy(v => DateTime.Parse(v[collectDateColumnName].ToString()).Date); - + for (int i = 0; i < MaxAge; i++) { DateTime referenceDate = DateTime.Now.AddDays(-i).Date; @@ -941,11 +1410,12 @@ namespace C4IT.DataHistoryProvider var strUrl = string.Format(constCitrixSessionMetricsServerL7Latency, Sessionid); - var citrixCommunication = await GetCitrixCommunicationForTenantAsync(TenantId, true); + var citrixCommunication = await GetCitrixCommunicationForSystemAsync(TenantId, true); if (citrixCommunication == null) return null; - var res = await citrixCommunication.RequestListAsync(strUrl, retryForbidden: false); + // ToDo: Implement a paged walkthrough of results in case there are more results than the API can return in one call + var res = await citrixCommunication.Monitor.Request.RequestListAsync(strUrl, false, retryForbidden: false); if (res != null) { JObject sessionInfos = res[0].Result; @@ -1041,11 +1511,12 @@ namespace C4IT.DataHistoryProvider var strUrl = string.Format(constCitrixSessionMetricsInputBandwidthUsed, Sessionid); - var citrixCommunication = await GetCitrixCommunicationForTenantAsync(TenantId, true); + var citrixCommunication = await GetCitrixCommunicationForSystemAsync(TenantId, true); if (citrixCommunication == null) return null; - var res = await citrixCommunication.RequestListAsync(strUrl, retryForbidden: false); + // ToDo: Implement a paged walkthrough of results in case there are more results than the API can return in one call + var res = await citrixCommunication.Monitor.Request.RequestListAsync(strUrl, false, retryForbidden: false); if (res != null) { JObject sessionInfos = res[0].Result; @@ -1141,11 +1612,12 @@ namespace C4IT.DataHistoryProvider var strUrl = string.Format(constCitrixSessionMetricsOutputBandwidthUsed, Sessionid); - var citrixCommunication = await GetCitrixCommunicationForTenantAsync(TenantId, true); + var citrixCommunication = await GetCitrixCommunicationForSystemAsync(TenantId, true); if (citrixCommunication == null) return null; - var res = await citrixCommunication.RequestListAsync(strUrl, retryForbidden: false); + // ToDo: Implement a paged walkthrough of results in case there are more results than the API can return in one call + var res = await citrixCommunication.Monitor.Request.RequestListAsync(strUrl, false, retryForbidden: false); if (res != null) { JObject sessionInfos = res[0].Result; @@ -1241,11 +1713,12 @@ namespace C4IT.DataHistoryProvider var strUrl = string.Format(constCitrixSessionMetricsOutputBandwidthAvailable, Sessionid); - var citrixCommunication = await GetCitrixCommunicationForTenantAsync(TenantId, true); + var citrixCommunication = await GetCitrixCommunicationForSystemAsync(TenantId, true); if (citrixCommunication == null) return null; - var res = await citrixCommunication.RequestListAsync(strUrl, retryForbidden: false); + // ToDo: Implement a paged walkthrough of results in case there are more results than the API can return in one call + var res = await citrixCommunication.Monitor.Request.RequestListAsync(strUrl, false, retryForbidden: false); if (res != null) { JObject sessionInfos = res[0].Result; @@ -1341,11 +1814,12 @@ namespace C4IT.DataHistoryProvider var strUrl = string.Format(constCitrixSessionMetricsFps, Sessionid); - var citrixCommunication = await GetCitrixCommunicationForTenantAsync(TenantId, true); + var citrixCommunication = await GetCitrixCommunicationForSystemAsync(TenantId, true); if (citrixCommunication == null) return null; - var res = await citrixCommunication.RequestListAsync(strUrl, retryForbidden: false); + // ToDo: Implement a paged walkthrough of results in case there are more results than the API can return in one call + var res = await citrixCommunication.Monitor.Request.RequestListAsync(strUrl, false, retryForbidden: false); if (res != null) { JObject sessionInfos = res[0].Result; @@ -1441,11 +1915,12 @@ namespace C4IT.DataHistoryProvider var strUrl = string.Format(constCitrixSessionMetricsInputFps, Sessionid); - var citrixCommunication = await GetCitrixCommunicationForTenantAsync(TenantId, true); + var citrixCommunication = await GetCitrixCommunicationForSystemAsync(TenantId, true); if (citrixCommunication == null) return null; - var res = await citrixCommunication.RequestListAsync(strUrl, retryForbidden: false); + // ToDo: Implement a paged walkthrough of results in case there are more results than the API can return in one call + var res = await citrixCommunication.Monitor.Request.RequestListAsync(strUrl, false, retryForbidden: false); if (res != null) { JObject sessionInfos = res[0].Result; @@ -1475,7 +1950,7 @@ namespace C4IT.DataHistoryProvider { var utc = DateTime.Parse(v[collectDateColumnName].ToString(), null, System.Globalization.DateTimeStyles.AssumeUniversal | System.Globalization.DateTimeStyles.AdjustToUniversal); - + return (utc - RefTime.TimeOfDay).Date.AddDays(1); }); @@ -1550,11 +2025,12 @@ namespace C4IT.DataHistoryProvider var strUrl = string.Format(constCitrixSessionMetricsOutputFps, Sessionid); - var citrixCommunication = await GetCitrixCommunicationForTenantAsync(TenantId, true); + var citrixCommunication = await GetCitrixCommunicationForSystemAsync(TenantId, true); if (citrixCommunication == null) return null; - var res = await citrixCommunication.RequestListAsync(strUrl, retryForbidden: false); + // ToDo: Implement a paged walkthrough of results in case there are more results than the API can return in one call + var res = await citrixCommunication.Monitor.Request.RequestListAsync(strUrl, false, retryForbidden: false); if (res != null) { JObject sessionInfos = res[0].Result; @@ -1650,11 +2126,12 @@ namespace C4IT.DataHistoryProvider var strUrl = string.Format(constCitrixSessionMetricsWanLatency, Sessionid); - var citrixCommunication = await GetCitrixCommunicationForTenantAsync(TenantId, true); + var citrixCommunication = await GetCitrixCommunicationForSystemAsync(TenantId, true); if (citrixCommunication == null) return null; - var res = await citrixCommunication.RequestListAsync(strUrl, retryForbidden: false); + // ToDo: Implement a paged walkthrough of results in case there are more results than the API can return in one call + var res = await citrixCommunication.Monitor.Request.RequestListAsync(strUrl, false, retryForbidden: false); if (res != null) { JObject sessionInfos = res[0].Result; @@ -1737,7 +2214,6 @@ namespace C4IT.DataHistoryProvider return null; } - private async Task> GetSessionMetricsDetailsDcLatencyAsync(Guid Sessionid, string TenantId, cF4sdWebRequestInfo RequestInfo, int MaxAge, DateTime RefTime, int LogDeep, CancellationToken Token) { MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } @@ -1751,11 +2227,12 @@ namespace C4IT.DataHistoryProvider var strUrl = string.Format(constCitrixSessionMetricsDcLatency, Sessionid); - var citrixCommunication = await GetCitrixCommunicationForTenantAsync(TenantId, true); + var citrixCommunication = await GetCitrixCommunicationForSystemAsync(TenantId, true); if (citrixCommunication == null) return null; - var res = await citrixCommunication.RequestListAsync(strUrl, retryForbidden: false); + // ToDo: Implement a paged walkthrough of results in case there are more results than the API can return in one call + var res = await citrixCommunication.Monitor.Request.RequestListAsync(strUrl, false, retryForbidden: false); if (res != null) { JObject sessionInfos = res[0].Result; @@ -1851,11 +2328,12 @@ namespace C4IT.DataHistoryProvider var strUrl = string.Format(constCitrixSession, Sessionid); - var citrixCommunication = await GetCitrixCommunicationForTenantAsync(TenantId, true); + var citrixCommunication = await GetCitrixCommunicationForSystemAsync(TenantId, true); if (citrixCommunication == null) return null; - var res = await citrixCommunication.RequestListAsync(strUrl, retryForbidden: false); + // ToDo: Implement a paged walkthrough of results in case there are more results than the API can return in one call + var res = await citrixCommunication.Monitor.Request.RequestListAsync(strUrl, false, retryForbidden: false); if (res != null) { JObject sessionInfos = res[0].Result; @@ -1934,11 +2412,12 @@ namespace C4IT.DataHistoryProvider var strUrl = string.Format(constCitrixSessionUser, Sessionid); - var citrixCommunication = await GetCitrixCommunicationForTenantAsync(TenantId, true); + var citrixCommunication = await GetCitrixCommunicationForSystemAsync(TenantId, true); if (citrixCommunication == null) return null; - var res = await citrixCommunication.RequestListAsync(strUrl, retryForbidden: false); + // ToDo: Implement a paged walkthrough of results in case there are more results than the API can return in one call + var res = await citrixCommunication.Monitor.Request.RequestListAsync(strUrl, false, retryForbidden: false); if (res != null) { JObject sessionInfos = res[0].Result; @@ -2012,7 +2491,6 @@ namespace C4IT.DataHistoryProvider return null; } - private async Task> GetSessionMachineAsync(Guid Sessionid, string TenantId, cF4sdWebRequestInfo RequestInfo, int LogDeep, CancellationToken Token) { MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } @@ -2026,11 +2504,12 @@ namespace C4IT.DataHistoryProvider var strUrl = string.Format(constCitrixSessionMachine, Sessionid); - var citrixCommunication = await GetCitrixCommunicationForTenantAsync(TenantId, true); + var citrixCommunication = await GetCitrixCommunicationForSystemAsync(TenantId, true); if (citrixCommunication == null) return null; - var res = await citrixCommunication.RequestListAsync(strUrl, retryForbidden: false); + // ToDo: Implement a paged walkthrough of results in case there are more results than the API can return in one call + var res = await citrixCommunication.Monitor.Request.RequestListAsync(strUrl, false, retryForbidden: false); if (res != null) { JObject sessionInfos = res[0].Result; @@ -2045,7 +2524,7 @@ namespace C4IT.DataHistoryProvider var dicVals = new Dictionary(); var vals = sessionInfos.Children(); - var machineObj = sessionInfos["Machine"] as JObject; + var machineObj = sessionInfos["Machine"] as JObject; if (machineObj != null) { foreach (var prop in machineObj.Properties()) @@ -2078,7 +2557,7 @@ namespace C4IT.DataHistoryProvider if (colInfo is cDataHistoryConfigColumn configColumn && !string.IsNullOrEmpty(configColumn.SourceJsonField)) { - var _objVal = JObject.Parse(objVal.ToString()); + var _objVal = JObject.Parse(objVal.ToString()); var prop = _objVal.Property(configColumn.SourceJsonField); objVal = prop?.Value.ToString(); } @@ -2122,11 +2601,12 @@ namespace C4IT.DataHistoryProvider var strUrl = string.Format(constCitrixSessionCurrentConnection, Sessionid); - var citrixCommunication = await GetCitrixCommunicationForTenantAsync(TenantId, true); + var citrixCommunication = await GetCitrixCommunicationForSystemAsync(TenantId, true); if (citrixCommunication == null) return null; - var res = await citrixCommunication.RequestListAsync(strUrl, retryForbidden: false); + // ToDo: Implement a paged walkthrough of results in case there are more results than the API can return in one call + var res = await citrixCommunication.Monitor.Request.RequestListAsync(strUrl, false, retryForbidden: false); if (res != null) { JObject sessionInfos = res[0].Result; @@ -2243,11 +2723,12 @@ namespace C4IT.DataHistoryProvider var strUrl = string.Format(constCitrixSessionConnections, Sessionid); - var citrixCommunication = await GetCitrixCommunicationForTenantAsync(TenantId, true); + var citrixCommunication = await GetCitrixCommunicationForSystemAsync(TenantId, true); if (citrixCommunication == null) return null; - var res = await citrixCommunication.RequestListAsync(strUrl, retryForbidden: false); + // ToDo: Implement a paged walkthrough of results in case there are more results than the API can return in one call + var res = await citrixCommunication.Monitor.Request.RequestListAsync(strUrl, false, retryForbidden: false); if (res != null) { JObject sessionInfos = res[0].Result; @@ -2350,10 +2831,10 @@ namespace C4IT.DataHistoryProvider var listTasks = new List>>(2); if (bIcaRttMSDetailExists) - listTasks.Add(Task.Run>(async () => { return await GetSessionMetricsDetailsIcaRttMSAsync(session.Id, userIds.citrixTenantId, requestInfo,MaxAge, RefTime, LogDeep + 1, Token); })); + listTasks.Add(Task.Run>(async () => { return await GetSessionMetricsDetailsIcaRttMSAsync(session.Id, userIds.citrixTenantId, requestInfo, MaxAge, RefTime, LogDeep + 1, Token); })); if (bIcaLatencyDetailExists) - listTasks.Add(Task.Run>(async () => { return await GetSessionMetricsDetailsIcaLatencyAsync(session.Id, userIds.citrixTenantId, requestInfo, MaxAge, RefTime,LogDeep + 1, Token); })); + listTasks.Add(Task.Run>(async () => { return await GetSessionMetricsDetailsIcaLatencyAsync(session.Id, userIds.citrixTenantId, requestInfo, MaxAge, RefTime, LogDeep + 1, Token); })); if (bClientL7LatencyDetailExists) listTasks.Add(Task.Run>(async () => { return await GetSessionMetricsDetailsClientL7LatencyAsync(session.Id, userIds.citrixTenantId, requestInfo, MaxAge, RefTime, LogDeep + 1, Token); })); @@ -2384,7 +2865,7 @@ namespace C4IT.DataHistoryProvider if (bDcLatencyDetailExists) listTasks.Add(Task.Run>(async () => { return await GetSessionMetricsDetailsDcLatencyAsync(session.Id, userIds.citrixTenantId, requestInfo, MaxAge, RefTime, LogDeep + 1, Token); })); - + var arrRes = await Task.WhenAll(listTasks.ToArray()); var retVal = new List(); @@ -2412,7 +2893,6 @@ namespace C4IT.DataHistoryProvider return null; } - public override async Task> GetQuickActionList() { var quickactions = new List{ @@ -2424,6 +2904,7 @@ namespace C4IT.DataHistoryProvider return await Task.FromResult(quickactions); } + public async Task QuickActionRun(cF4SDServerQuickActionParameters jsonRequest, cF4sdWebRequestInfo requestInfo, CancellationToken Token) { dynamic result = null; @@ -2454,13 +2935,13 @@ namespace C4IT.DataHistoryProvider var strUrl = string.Format(constCitrixSessionLogoff, session?.Id); - var citrixCommunication = await GetCitrixCommunicationForTenantAsync(userIds?.citrixTenantId, true); + var citrixCommunication = await GetCitrixCommunicationForSystemAsync(userIds?.citrixTenantId, true); if (citrixCommunication == null) { return null; } - var Result = await citrixCommunication.RequestAsync(strUrl, httpMethod: eHttpMethod.post, retryForbidden: false); + var Result = await citrixCommunication.CVAD.Request.RequestAsync(strUrl, httpMethod: eHttpMethod.post, null); if (Result != null) { @@ -2489,13 +2970,13 @@ namespace C4IT.DataHistoryProvider var strUrl = string.Format(constCitrixSessionHide, session?.Id); - var citrixCommunication = await GetCitrixCommunicationForTenantAsync(userIds?.citrixTenantId, true); + var citrixCommunication = await GetCitrixCommunicationForSystemAsync(userIds?.citrixTenantId, true); if (citrixCommunication == null) { return null; } - var Result = await citrixCommunication.RequestAsync(strUrl, httpMethod: eHttpMethod.post, retryForbidden: false); + var Result = await citrixCommunication.CVAD.Request.RequestAsync(strUrl, httpMethod: eHttpMethod.post, null); if (Result != null) { @@ -2510,7 +2991,7 @@ namespace C4IT.DataHistoryProvider } return null; - } + } public async Task SendMessageToSession(List Identities, cF4sdWebRequestInfo requestInfo, CancellationToken Token) { @@ -2521,12 +3002,12 @@ namespace C4IT.DataHistoryProvider if (!Ids.TryGetValue(enumFasdInformationClass.User, out var userIds) || userIds?.citrixTenantId is null) return null; - - + + var strUrl = string.Format(constCitrixSessionSendMessage, session?.Id); - var citrixCommunication = await GetCitrixCommunicationForTenantAsync(userIds?.citrixTenantId, true); + var citrixCommunication = await GetCitrixCommunicationForSystemAsync(userIds?.citrixTenantId, true); if (citrixCommunication == null) { return null; @@ -2534,13 +3015,13 @@ namespace C4IT.DataHistoryProvider var message = new SessionMessage { - Style = "Critical", - Title = "Abmeldung bevorstehend!", - Text = "Eine dringende Wartung muss durchgeführt werden. Sie werden in Kürze abgemeldet. Bitte speichern Sie Ihre Daten." + Style = enumMessageSytle.Critical.ToString(), + Title = cMultiLanguageSupport.GetItem("Quickaction.Title.LogOff"), + Text = cMultiLanguageSupport.GetItem("Quickaction.Text.LogOff") }; - var Result = await citrixCommunication.RequestAsync(strUrl, httpMethod: eHttpMethod.post, message, retryForbidden: false); + var Result = await citrixCommunication.CVAD.Request.RequestAsync(strUrl, httpMethod: eHttpMethod.post, message); if (Result != null) { @@ -2550,7 +3031,7 @@ namespace C4IT.DataHistoryProvider Collector.DoProcessUiMessage(3, $" properties of selected '{ResultJson}' with {ResultJson.Count} properties could be read."); } - return ResultJson; + return Result.Context; } return null; @@ -2590,7 +3071,7 @@ namespace C4IT.DataHistoryProvider //{ // retVal.Add(new cF4SDHealthCardRawData.cHealthCardTable() // { - // Name = constTableNameSessionMetrics, + // Name = constTableNameSession, // InformationClass = enumFasdInformationClass.VirtualSession, // IsIncomplete = true // }); @@ -2599,7 +3080,8 @@ namespace C4IT.DataHistoryProvider //else listTasks.Add(Task.Run>(async () => { return await GetSessionAsync(session.Id, userIds.citrixTenantId, requestInfo, LogDeep + 1, Token); })); - if (_sessionMetricsExists) { + if (_sessionMetricsExists) + { //if (CacheId == null && !instantly) //{ // retVal.Add(new cF4SDHealthCardRawData.cHealthCardTable() @@ -2612,7 +3094,7 @@ namespace C4IT.DataHistoryProvider //} //else listTasks.Add(Task.Run>(async () => { return await GetSessionMetricsAsync(session.Id, userIds.citrixTenantId, requestInfo, MaxAge, LogDeep + 1, Token); })); - listTasks.Add(Task.Run>(async () => { return await GetSessionStateMetricsAsync(userIds, userIds.citrixTenantId, requestInfo, MaxAge, LogDeep + 1, Token); })); + //listTasks.Add(Task.Run>(async () => { return await GetSessionStateMetricsAsync(userIds, userIds.citrixTenantId, requestInfo, MaxAge, LogDeep + 1, Token); })); } @@ -2621,7 +3103,7 @@ namespace C4IT.DataHistoryProvider //{ // retVal.Add(new cF4SDHealthCardRawData.cHealthCardTable() // { - // Name = constTableNameMachineMetrics, + // Name = constTableNameSessionMachine, // InformationClass = enumFasdInformationClass.VirtualSession, // IsIncomplete = true // }); @@ -2634,7 +3116,7 @@ namespace C4IT.DataHistoryProvider //{ // retVal.Add(new cF4SDHealthCardRawData.cHealthCardTable() // { - // Name = constTableNameMachineMetrics, + // Name = constTableNameSessionConnection, // InformationClass = enumFasdInformationClass.VirtualSession, // IsIncomplete = true // }); @@ -2647,7 +3129,7 @@ namespace C4IT.DataHistoryProvider //{ // retVal.Add(new cF4SDHealthCardRawData.cHealthCardTable() // { - // Name = constTableNameMachineMetrics, + // Name = constTableNameSessionCurrentConnection, // InformationClass = enumFasdInformationClass.VirtualSession, // IsIncomplete = true // }); @@ -2660,7 +3142,7 @@ namespace C4IT.DataHistoryProvider //{ // retVal.Add(new cF4SDHealthCardRawData.cHealthCardTable() // { - // Name = constTableNameMachineMetrics, + // Name = constTableNameSessionUser, // InformationClass = enumFasdInformationClass.VirtualSession, // IsIncomplete = true // }); @@ -2694,45 +3176,42 @@ namespace C4IT.DataHistoryProvider return null; } - public async Task GetCitrixSessionsAsync() + public async Task GetCitrixSessionsAsync() { MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } try { var strUrl = string.Format(constCitrixSessionList, constCitrixUserPaging); - var tenants = Collector.InfrastructureConfig.CitrixTenants?.Values; - if (tenants is null) + var systems = Collector.InfrastructureConfig.Citrix.Systems; + if (systems is null) { Collector.DoProcessUiMessage(2, $" Could not logon to tenant - Tenant is null", LogLevels.Warning); - return null; + return false; } - foreach (var Tenant in tenants) + foreach (var _system in systems) { + if (!(_system is cDataHistoryCitrixCloudTenant Tenant)) + continue; - var CitrixConn = await GetCitrixCommunicationForTenantAsync(Tenant.TenantID, true); + var CitrixConn = await GetCitrixCommunicationForSystemAsync(Tenant.TenantID, true); if (CitrixConn == null) { Collector.DoProcessUiMessage(2, $"error: could not logon to tenat {Tenant.Domain}", LogLevels.Error); continue; } - var Result = await CitrixConn.RequestAsync(strUrl, retryForbidden: false); + var Result = await CitrixConn.Monitor.Request.RequestAsync(strUrl, eHttpMethod.get, null); if (Result != null) { - - var _ty = Result.GetType(); var _props = Result.Result as JObject; if (_props != null) { Collector.DoProcessUiMessage(3, $" properties of selected '{_props}' with {_props.Count} properties could be read."); - + return true; } - - } - } } catch (Exception E) @@ -2744,10 +3223,10 @@ namespace C4IT.DataHistoryProvider if (CM != null) LogMethodEnd(CM); } - return null; + return false; } - public override async Task DoScanAsync(bool Always, bool Rescan, cF4sdWebRequestInfo requestInfo, int LogDeep, CancellationToken Token, bool EnhancedDebug = false) + public async override Task 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); } @@ -2756,7 +3235,7 @@ namespace C4IT.DataHistoryProvider try { - if (Always) + if (!Always) { var scanInfo = await Collector.CitrixCollector.GetScanTimeInfoAsync(requestInfo, LogDeep + 1, Token); if (scanInfo == null) @@ -2780,10 +3259,10 @@ namespace C4IT.DataHistoryProvider Collector.DoProcessUiMessage(0, ""); - if (Collector.InfrastructureConfig.CitrixTenants != null) + if (Collector.InfrastructureConfig.Citrix.Systems != null) await DoCitrixScanAsync(requestInfo, LogDeep + 1, Token); - await Collector.SetLastScanTime("CitrixScan-all", DateTime.UtcNow, requestInfo, LogDeep + 1, Token); + await Collector.SetLastScanTime("CitrixScan", DateTime.UtcNow, requestInfo, LogDeep + 1, Token); return true; @@ -2801,121 +3280,168 @@ namespace C4IT.DataHistoryProvider return false; } - public async Task DoCitrixScanAsync(cF4sdWebRequestInfo requestInfo, int LogDeep, CancellationToken Token) + private class UserScanProcessor : cCitrixCommunicationMonitor.ScanEntryProcessor { - 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; + private cDataHistoryCollector Collector; + private cDbConnection dbConnection; + private cDbQuery GetUserQuery; + private cDbQuery UpdateUserQuer; + private cF4sdWebRequestInfo requestInfo; + private int LogDeep; + private string SiteId; - try + private CancellationToken token; + + public int UpdatedCount { get; private set; } = 0; + public int UnknownCount { get; private set; } = 0; + public int TotalCount { get; private set; } = 0; + + public UserScanProcessor(cDataHistoryCollector Collector, string SiteId, cDbConnection dbConnection, cDbQuery GetUserQuery, cDbQuery UpdateUserQuery, cF4sdWebRequestInfo requestInfo, int LogDeep, CancellationToken token) { - var tenants = Collector.InfrastructureConfig.CitrixTenants?.Values; - if (tenants is null) - { - Collector.DoProcessUiMessage(2, $" could not logon to tenant - Tenant is null ", LogLevels.Error); - return true; - } + this.Collector = Collector; + this.dbConnection = dbConnection; + this.GetUserQuery = GetUserQuery; + this.UpdateUserQuer = UpdateUserQuery; + this.requestInfo = requestInfo; + this.LogDeep = LogDeep; + this.token = token; + this.SiteId = SiteId; + } - if (!DataHistorySqlHelper.GetWellKnownSqlStatement("UpdateUserCitrix", out var QueryInsert)) - return false; + public async override Task ProcessScannedItemAsync(dynamic _node, CancellationToken token) + { + if (token.IsCancellationRequested) + return false; ; - using (var Conn = new cDbConnection(Collector.mainDbConnection)) + try { - if (!Conn.IsOpen) + TotalCount++; + + int Id = _node?.Id; + string strSid = _node?.Sid; + string Upn = _node?.Upn; + Upn = Upn?.ToLowerInvariant(); + string Domain = _node?.Domain; + + // check if we have a substitution for the upn for testing purposes + if (UserTestReplacement != null) { - LogEntry($"Could not open main sql database '{Collector.mainDbConnection.Database}', aborting scan...'", LogLevels.Error); - return false; + var _search = SiteId + ":" + Upn; + if (UserTestReplacement.TryGetValue(_search, out var _newUpn)) + Upn = _newUpn; } - - Collector.DoProcessUiMessage(0, string.Format("doing the Citrix cloud scans:")); - foreach (var Tenant in tenants) + // translate the sid to binary format + byte[] arrSid = null; + if (!string.IsNullOrEmpty(strSid)) { try { - Collector.DoProcessUiMessage(1, $"scanning tenat {Tenant.Domain} for users ..."); - var CitrixConn = await GetCitrixCommunicationForTenantAsync(Tenant.TenantID, true); - if (CitrixConn == null) - { - Collector.DoProcessUiMessage(2, $"error: could not logon to tenat {Tenant.Domain}", LogLevels.Error); - continue; - } - - var strUrl = string.Format(constCitrixUserList, constCitrixUserPaging); - var Result = await CitrixConn.RequestListAsync(strUrl, retryForbidden: false); - - var Count = 0; - var Updated = 0; - while (Result != null == Result.Count > 0) - { - foreach (var Entry in Result) - { - - Count++; - try - { - var _strId = Entry.Result.Id.ToString(); - - - var _sid = Entry.Result.Sid.ToString(); - var sid = new SecurityIdentifier(_sid); - var arrSid = new byte[sid.BinaryLength]; - sid.GetBinaryForm(arrSid, 0); - - var SqlParams = new Dictionary(2) - { - { "sid_bin", arrSid }, - { "id", _strId }, - { "tenant", Tenant.TenantID } - }; - - var _res = await DataHistorySqlHelper.ExecuteAsync(Conn, QueryInsert, SqlParams, requestInfo, Token); - if (_res > 0) - Updated++; - else - { - if (DataHistorySqlHelper.GetWellKnownSqlStatement("UpdateUserCitrix2", out var QueryInsert2)) - { - string upn = Entry.Result.Upn.ToString(); - var SqlParams2 = new Dictionary(2) - { - { "id", _strId }, - { "tenant", Tenant.TenantID }, - { "upn_internal", upn } - }; - var _res2 = await DataHistorySqlHelper.ExecuteAsync(Conn, QueryInsert2, SqlParams2, requestInfo, Token); - if (_res2 > 0) - Updated++; - } - } - } - catch (Exception E) - { - LogException(E); - } - } - Collector.DoProcessUiMessage(2, $"{Count} accounts processed, {Updated} updated."); - - if (Result.Count < constCitrixUserPaging) - break; - - Result.Clear(); - if (!await CitrixConn.RequestNextAsync(Result)) - break; - } + var sid = new SecurityIdentifier(strSid); + arrSid = new byte[sid.BinaryLength]; + sid.GetBinaryForm(arrSid, 0); } catch (Exception E) { LogException(E); } - - Collector.DoProcessUiMessage(1, $"...scanning tenat {Tenant.Domain} finished"); } - Collector.DoProcessUiMessage(0, "... Citrix cloud scans finished."); + // identify the f4sd user id by SID or UPN + var SqlParams = new Dictionary(2) + { + { "SID", arrSid }, + { "UPN", Upn }, + }; + + var _res = await DataHistorySqlHelper.GetScalarResultAsync(dbConnection, GetUserQuery, SqlParams, requestInfo, token); + if (token.IsCancellationRequested) + return false; + + if (_res != null && _res != Guid.Empty) + { + // update the foreign ids table + var _mainId = (Guid)_res; + SqlParams = new Dictionary(5) + { + { "id", _mainId }, + { "origin", (int)enumDataHistoryOrigin.Citrix }, + { "instance", SiteId }, + { "id_foreign", Id.ToString() }, + }; + + var _insRes = await DataHistorySqlHelper.ExecuteAsync(dbConnection, UpdateUserQuer, SqlParams, requestInfo, token); + if (_insRes > 0) + UpdatedCount++; + else + UnknownCount++; + } + else + { + // we could not identify a known f4sd user + UnknownCount++; + } return true; } + catch (Exception E) + { + UnknownCount++; + LogException(E); + } + finally + { + if ((TotalCount ^ 100) == 0) + Collector.DoProcessUiMessage(LogDeep, $"{TotalCount} users scanned, {UpdatedCount} inserted or updated. "); + } + + return false; + } + + } + + public async Task DoCitrixScanSystemAsync(cCitrixCommunicationSystem CitrixSystem, cF4sdWebRequestInfo requestInfo, int LogDeep, CancellationToken token) + { + MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } + + try + { + if (!DataHistorySqlHelper.GetWellKnownSqlStatement("GetUserBySidOrUpn", out var QueryGetUser)) + return false; + + if (!DataHistorySqlHelper.GetWellKnownSqlStatement("UpdateForeignUsernfo", out var InsertUpdateUser)) + return false; + + try + { + Collector.DoProcessUiMessage(LogDeep, $"Start scanning Citrix system: {CitrixSystem.Name}"); + + 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; + } + + var _processor = new UserScanProcessor(Collector, CitrixSystem.CVAD.CitrixInfo.SiteId, Conn, QueryGetUser, InsertUpdateUser, requestInfo, LogDeep + 1, token); + var _n = await CitrixSystem.Monitor.ScanUsersAsync(_processor, token); + if (_n >= 0) + { + if ((_processor.TotalCount ^ 100) != 0) + Collector.DoProcessUiMessage(LogDeep +1, $"{_processor.TotalCount} users scanned, {_processor.UpdatedCount} inserted or updated. "); + return true; + } + } + } + catch (Exception E) + { + LogException(E); + } + finally + { + Collector.DoProcessUiMessage(LogDeep, $"Scanning Citrix system finished: {CitrixSystem.Name}"); + } } catch (Exception E) @@ -2924,17 +3450,58 @@ namespace C4IT.DataHistoryProvider } 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> GetIds(cF4sdConnectorIds IdEntry, enumFasdInformationClass InfoClass, cF4sdWebRequestInfo requestInfo, int LogDeep) + public async Task DoCitrixScanAsync(cF4sdWebRequestInfo requestInfo, int LogDeep, CancellationToken token) { - await Task.CompletedTask; - return new List() { IdEntry.sid }; + MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } + + try + { + var _doSequential = this.DoScanSequential; + + await ValidateConnectionsAsync(token); + if (token.IsCancellationRequested) + return false; + + var scanErrors = false; + var lstTasks = new List>(); + foreach (var _comm in cachedConnections.Values) + { + if (_doSequential) + { + var _res = await DoCitrixScanSystemAsync(_comm, requestInfo, LogDeep + 1, token); + if (!_res) + scanErrors = true; + } + else + lstTasks.Add(Task.Run(async () => { return await DoCitrixScanSystemAsync(_comm, requestInfo, LogDeep + 1, token); })); + } + + if (!_doSequential) + { + var _res = await Task.WhenAll(lstTasks); + foreach (var _resEntry in _res) + if (!_resEntry) + scanErrors = true; + } + + return !scanErrors; + } + catch (Exception E) + { + LogException(E); + } + finally + { + if (CM != null) LogMethodEnd(CM); + } + + return false; } public override async Task GetScanTimeInfoAsync(cF4sdWebRequestInfo requestInfo, int LogDeep, CancellationToken Token) @@ -2942,52 +3509,13 @@ namespace C4IT.DataHistoryProvider return await GetScanTimeInfoAsync(Collector.InfrastructureConfig.Citrix.ScanTiming, "CitrixScan", requestInfo, LogDeep, Token); } - private static void AddToLdapPropertyList(List list, ref Dictionary dicLdap) - { - foreach (var Entry in list) - { - if (!dicLdap.ContainsKey(Entry.Name)) - dicLdap.Add(Entry.Name.ToLowerInvariant(), Entry); - } - } - - private Dictionary GetPropertyList(enumFasdInformationClass InformationClass) - { - MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } - - var dicLdap = new Dictionary(); - - try - { - switch (InformationClass) - { - case enumFasdInformationClass.User: - AddToLdapPropertyList(UserMainProperties, ref dicLdap); - break; - } - - } - catch (Exception E) - { - LogException(E); - } - finally - { - if (CM != null) LogMethodEnd(CM); - } - - return dicLdap; - - } - public HashSet GetSupportedInformationClasses() => new HashSet() { enumFasdInformationClass.VirtualSession }; - - public async Task GetRelationsAsync(IEnumerable ids, enumFasdInformationClass informationClass, int age, CancellationToken token = default) + + public async Task GetRelationsAsync(IEnumerable ids, enumFasdInformationClass informationClass, int age, CancellationToken token = default) { try { - List sessionsIds = await _collector.getConnectorIdList(ids.ToList(), token, null, 0); - List relations = await GetUserSessionsAsync(sessionsIds, token, null); + List relations = await GetUserSessionsAsync(ids, age, token, null); return new cF4sdStagedSearchResultRelations() { Relations = relations }; } catch (Exception ex) @@ -2997,42 +3525,6 @@ namespace C4IT.DataHistoryProvider return null; } - - private enumCitrixSessionStatus ConvertSessionStatusCode(int sessionStatus) - { - try - { - switch (sessionStatus) - { - case 0: - return enumCitrixSessionStatus.Unknown; - case 1: - return enumCitrixSessionStatus.Connected; - case 2: - return enumCitrixSessionStatus.Disconnected; - case 3: - return enumCitrixSessionStatus.Terminated; - case 4: - return enumCitrixSessionStatus.PreparingSession; - case 5: - return enumCitrixSessionStatus.Active; - case 6: - return enumCitrixSessionStatus.Reconnecting; - case 7: - return enumCitrixSessionStatus.NonBrokeredSession; - case 8: - return enumCitrixSessionStatus.Other; - case 9: - return enumCitrixSessionStatus.Pending; - - } - } - catch (Exception E) - { - LogException(E); - } - return enumCitrixSessionStatus.Unknown; - } private object GetAggregationValue(IEnumerable objVal, eDataHistoryAggregationType AggregationType, enumFasdValueType ValueType) { @@ -3046,7 +3538,7 @@ namespace C4IT.DataHistoryProvider { case eDataHistoryAggregationType.average: return aggregatedValue = objVal.Where(v => v != null) - .Select(v =>Convert.ToDouble(v)) + .Select(v => Convert.ToDouble(v)) .DefaultIfEmpty(0) .Average(); case eDataHistoryAggregationType.sum: @@ -3083,7 +3575,7 @@ namespace C4IT.DataHistoryProvider case enumFasdValueType.INT: return cF4SDHealthCardRawData.GetInteger(objVal); case enumFasdValueType.GUID: - return XML.cXmlParser.GetGuidFromString(objVal.ToString()); + return cXmlParser.GetGuidFromString(objVal.ToString()); case enumFasdValueType.BOOLEAN: if (objVal == null) return false; @@ -3129,18 +3621,4 @@ namespace C4IT.DataHistoryProvider public string Text { get; set; } = null; } } - - public class cF4SDVirtualSession : cF4sdApiSearchResultRelation - { - public Guid SessionId { get; set; } - public string ConnectionState { get; set; } - public string FailureState { get; set; } - public string Startdate { get; set; } - public string Enddate { get; set; } - public string MachineId { get; set; } - public string MachineName { get; set; } - public Guid UserId { get; set; } - } - - } diff --git a/F4SD-Cockpit-ServerCore/Modules/Citrix/DataHistoryConfigCitrix.cs b/F4SD-Cockpit-ServerCore/Modules/Citrix/DataHistoryConfigCitrix.cs new file mode 100644 index 0000000..0c55776 --- /dev/null +++ b/F4SD-Cockpit-ServerCore/Modules/Citrix/DataHistoryConfigCitrix.cs @@ -0,0 +1,295 @@ +using System; +using System.Collections.Generic; +using System.DirectoryServices.ActiveDirectory; +using System.Net; +using System.Reflection; +using System.Xml; + +using C4IT.DataHistoryProvider; +using C4IT.Logging; +using C4IT.Security; +using C4IT.XML; + +using static C4IT.Logging.cLogManager; + +namespace C4IT.DataHistoryProvider.Base.Modules.Citrix +{ + public class cDataHistoryConfigCitrix : IConfigNodeValidation + { + public bool IsValid { get; private set; } = false; + public cDataHistoryScanTiming ScanTiming { get; private set; } = null; + public cDataHistoryConfigCitrix(XmlElement XNode, Dictionary Credentials, cXmlParser Parser) + { + MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } + try + { + + var XNode2 = XNode.SelectSingleNode("Citrix"); + if (!(XNode2 is XmlElement XCitrix)) + { + return; + } + + Parser.EnterElement("Citrix"); + + try + { + ScanTiming = new cDataHistoryScanTiming(XCitrix, TimeSpan.FromHours(24), TimeSpan.FromMinutes(10), Parser); + + Systems = cDataHistoryCitrixCloudTenant.LoadFromXml(XCitrix, Credentials, Parser); + + if (Systems == null) + Systems = new List(); + + var _onPremSystems = cDataHistoryCitrixOnPrem.LoadFromXml(XCitrix, Credentials, Parser); + if (_onPremSystems != null) + Systems.AddRange(_onPremSystems); + + IsValid = true; + } + finally + { + Parser.LeaveElement("Citrix"); + } + } + catch (Exception E) + { + LogException(E); + } + finally + { + if (CM != null) LogMethodEnd(CM); + } + } + + public List Systems { get; private set; } = null; //ToDo: Check all uses of the citrix systems to implement on prem and tenant specific handling if needed + + } + + public abstract class cDataHistoryCitrixSystem : cConfigNodeNamed + { + public cCredential Credential { get; private set; } = null; + + public Guid SiteID { get; set; } = Guid.Empty; + + public cDataHistoryCitrixSystem(XmlElement XNode, Dictionary Credentials, cXmlParser Parser) : base(XNode, Parser) + { + MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } + + try + { + if (!IsValid) + return; + + SiteID = cXmlParser.GetGuidFromXmlAttribute(XNode, "SiteID"); + + var strCreds = cXmlParser.GetStringFromXmlAttribute(XNode, "Credential"); + if (!string.IsNullOrWhiteSpace(strCreds)) + { + if (Credentials.TryGetValue(strCreds, out var Cred)) + Credential = Cred; + } + + if (Credential == null) + { + Parser.AddInvalidAttribute(XNode, null, "Credential"); + return; + } + + IsValid = true; + } + catch (Exception E) + { + LogException(E); + } + finally + { + if (CM != null) LogMethodEnd(CM); + } + } + + } + + public class cDataHistoryCitrixCloudTenant : cDataHistoryCitrixSystem + { + public string Domain { get; private set; } = ""; + + public string TenantID { get; private set; } = String.Empty; + + internal cDataHistoryCitrixCloudTenant(XmlElement XNode, Dictionary Credentials, cXmlParser Parser) : base(XNode, Credentials, Parser) + { + MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } + try + { + if (!IsValid) + return; + + Domain = cXmlParser.GetStringFromXmlAttribute(XNode, "Domain"); + if (string.IsNullOrWhiteSpace(Domain)) + { + Parser.AddInvalidAttribute(XNode, null, "Domain"); + return; + } + + TenantID = cXmlParser.GetStringFromXmlAttribute(XNode, "TenantID"); + if (TenantID == String.Empty) + { + Parser.AddInvalidAttribute(XNode, Domain, "TenantID"); + return; + } + + + IsValid = true; + } + catch (Exception E) + { + LogException(E); + } + finally + { + if (CM != null) LogMethodEnd(CM); + } + } + + internal static List LoadFromXml(XmlElement XNode, Dictionary Credentials, cXmlParser Parser) + { + MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } + + var RetVal = new List(); + + try + { + var XList = XNode.SelectNodes("Citrix-Cloud-Tenant"); + if (XList != null && XList.Count > 0) + { + Parser.EnterElement("Citrix-Cloud-Tenant"); + try + { + foreach (var Entry in XList) + { + if (!(Entry is XmlElement XEntry)) + continue; + + var Node = new cDataHistoryCitrixCloudTenant(XEntry, Credentials, Parser); + if (Node.IsValid) + RetVal.Add(Node); + Parser.SelectElementNext(); + } + } + finally + { + Parser.LeaveElement("Citrix-Cloud-Tenant"); + } + } + + XList = XNode.SelectNodes("Citrix-OnPrem-System"); + if (XList != null && XList.Count > 0) + { + Parser.EnterElement("Citrix-OnPrem-System"); + try + { + foreach (var Entry in XList) + { + if (!(Entry is XmlElement XEntry)) + continue; + + var Node = new cDataHistoryCitrixOnPrem(XEntry, Credentials, Parser); + if (Node.IsValid) + RetVal.Add(Node); + Parser.SelectElementNext(); + } + } + finally + { + Parser.LeaveElement("Citrix-OnPrem-System"); + } + } + + } + catch (Exception E) + { + LogException(E); + } + finally + { + if (CM != null) LogMethodEnd(CM); + } + + return RetVal; + } + } + + public class cDataHistoryCitrixOnPrem : cDataHistoryCitrixSystem + { + public string Server { get; private set; } = string.Empty; + + internal cDataHistoryCitrixOnPrem(XmlElement XNode, Dictionary Credentials, cXmlParser Parser) : base(XNode, Credentials, Parser) + { + MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } + + try + { + Server = cXmlParser.GetStringFromXmlAttribute(XNode, "Server"); + if (string.IsNullOrWhiteSpace(Server)) + { + Parser.AddInvalidAttribute(XNode, null, "Server"); + return; + } + + IsValid = true; + } + catch (Exception E) + { + LogException(E); + } + finally + { + if (CM != null) LogMethodEnd(CM); + } + } + + internal static List LoadFromXml(XmlElement XNode, Dictionary Credentials, cXmlParser Parser) + { + MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } + + var RetVal = new List(); + + try + { + var XList = XNode.SelectNodes("Citrix-OnPrem"); + if (XList != null && XList.Count > 0) + { + Parser.EnterElement("Citrix-OnPrem"); + try + { + foreach (var Entry in XList) + { + if (!(Entry is XmlElement XEntry)) + continue; + + var Node = new cDataHistoryCitrixOnPrem(XEntry, Credentials, Parser); + if (Node.IsValid) + RetVal.Add(Node); + Parser.SelectElementNext(); + } + } + finally + { + Parser.LeaveElement("Citrix-OnPrem"); + } + } + } + catch (Exception E) + { + LogException(E); + } + finally + { + if (CM != null) LogMethodEnd(CM); + } + + return RetVal; + } + } + +} diff --git a/F4SD-Cockpit-ServerCore/Publish/F4SD-Cockpit-ServerCore.dll b/F4SD-Cockpit-ServerCore/Publish/F4SD-Cockpit-ServerCore.dll new file mode 100644 index 0000000..e85dbef Binary files /dev/null and b/F4SD-Cockpit-ServerCore/Publish/F4SD-Cockpit-ServerCore.dll differ diff --git a/F4SD-Cockpit-ServerCore/Services/IStagedRelationService.cs b/F4SD-Cockpit-ServerCore/Services/IStagedRelationService.cs index 537911e..279b704 100644 --- a/F4SD-Cockpit-ServerCore/Services/IStagedRelationService.cs +++ b/F4SD-Cockpit-ServerCore/Services/IStagedRelationService.cs @@ -11,7 +11,7 @@ namespace C4IT_DataHistoryProvider_Base.Services /// /// Triggers search for relations to given information objects. /// - cF4sdStagedSearchResultRelationTaskId StartGatheringRelatedObjects(IEnumerable relatedTo, int ageInDays); + Task StartGatheringRelatedObjects(IEnumerable relatedTo, int ageInDays); /// /// Used after triggering search for related objects in to retrieve the already found relations. diff --git a/F4SD-Cockpit-ServerCore/Services/StagedRelationService.cs b/F4SD-Cockpit-ServerCore/Services/StagedRelationService.cs index 81bf334..2657938 100644 --- a/F4SD-Cockpit-ServerCore/Services/StagedRelationService.cs +++ b/F4SD-Cockpit-ServerCore/Services/StagedRelationService.cs @@ -1,4 +1,5 @@ -using C4IT.FASD.Base; +using C4IT.DataHistoryProvider; +using C4IT.FASD.Base; using C4IT.Logging; using C4IT_DataHistoryProvider_Base.DataSources; using System; @@ -16,13 +17,16 @@ namespace C4IT_DataHistoryProvider_Base.Services private static readonly Dictionary _relationTasks = new Dictionary(); private readonly IEnumerable _providers; + private readonly cDataHistoryCollector _collector; + private static readonly System.Timers.Timer _relationTaskCleanupTimer = new System.Timers.Timer(CleanupInterval); private const int CleanupInterval = 5 * 60 * 1000; private readonly TimeSpan RelationTaskExpiration = TimeSpan.FromMinutes(15); - public StagedRelationService(IEnumerable providers) + public StagedRelationService(cDataHistoryCollector collector, IEnumerable providers) { _providers = providers; + _collector = collector; SetupCleanupTimer(); } @@ -36,7 +40,7 @@ namespace C4IT_DataHistoryProvider_Base.Services _relationTaskCleanupTimer.Start(); } - public cF4sdStagedSearchResultRelationTaskId StartGatheringRelatedObjects(IEnumerable relatedTo, int age) + public async Task StartGatheringRelatedObjects(IEnumerable relatedTo, int age) { cF4sdStagedSearchResultRelationTaskId taskId = new cF4sdStagedSearchResultRelationTaskId(); try @@ -49,12 +53,14 @@ namespace C4IT_DataHistoryProvider_Base.Services List identities = relatedTo.Select(relation => new cF4sdIdentityEntry() { Id = relation.id, Class = relatedToInformationClass }).ToList(); + var _ids = await _collector.getConnectorIdList(identities, CancellationToken.None, null, age); + foreach (var collectorModule in _providers) { taskId.PendingInformationClasses.UnionWith(collectorModule.GetSupportedInformationClasses()); Task task = Task.Run(() => - collectorModule.GetRelationsAsync(identities, relatedToInformationClass, age, relationTask.TokenSource.Token)); + collectorModule.GetRelationsAsync(_ids, relatedToInformationClass, age, relationTask.TokenSource.Token)); relationTask.RelatedObjectTasks.Add((collectorModule.GetSupportedInformationClasses(), task)); } diff --git a/F4SDwebService/Authentication.cs b/F4SDwebService/Authentication.cs index 2dddd86..e8809ca 100644 --- a/F4SDwebService/Authentication.cs +++ b/F4SDwebService/Authentication.cs @@ -147,6 +147,13 @@ namespace F4SDwebService if (_roles?.Count() > 0) _retVal.Roles = _roles; + const string cockpitUserRole = "Cockpit.User"; + if (!_roles.Contains(cockpitUserRole)) + { + LogEntry($"bearer roles does not contain the role \"{cockpitUserRole}\"", LogLevels.Debug); + return null; + } + if (cLogManager.DefaultLogger.IsDebug) { var _str = JsonConvert.SerializeObject(_retVal, Formatting.Indented); diff --git a/F4SDwebService/Config/F4SD-Global-Configuration.xml b/F4SDwebService/Config/F4SD-Global-Configuration.xml index d4cbfcd..abd254e 100644 --- a/F4SDwebService/Config/F4SD-Global-Configuration.xml +++ b/F4SDwebService/Config/F4SD-Global-Configuration.xml @@ -14,9 +14,13 @@ - + + + + + @@ -24,4 +28,9 @@ + + + + + diff --git a/F4SDwebService/Config/F4SD-HealthCard-Configuration.xml b/F4SDwebService/Config/F4SD-HealthCard-Configuration.xml index 5e3bcc1..817c258 100644 --- a/F4SDwebService/Config/F4SD-HealthCard-Configuration.xml +++ b/F4SDwebService/Config/F4SD-HealthCard-Configuration.xml @@ -252,6 +252,9 @@ IntuneDeviceId + + EmpirumDeviceId + BshRegistryKeyExists @@ -891,6 +894,21 @@ + + + Empirum Informationen + + + Empirum Id + + + Letzte Inventory + + + IsPxe + + + @@ -1372,7 +1390,7 @@ - + Session diff --git a/F4SDwebService/Config/F4SD-Infrastructure-Configuration.xml b/F4SDwebService/Config/F4SD-Infrastructure-Configuration.xml index ea81c56..3bb761d 100644 --- a/F4SDwebService/Config/F4SD-Infrastructure-Configuration.xml +++ b/F4SDwebService/Config/F4SD-Infrastructure-Configuration.xml @@ -7,7 +7,8 @@ R0ICvoG+yjJFGXGgagNQbzAwcBacJr47wRHOIwYhBl/YGDmxpoRWrI1GZ9x1C7ofSqeE1UeZNHX/oqMo/04h+jfUrqk1QHo/6e43Lns1z7UVhWrzOlPSxYwCjv7e4+6wqTWTx6UNGR5jCeD/eHAXHW24dGu7mph9xhcB3VC1kRY= ZLAAU+jFXU/+x9FVjx4EAZRnK/D+G/OpZGkn4ek56vhDUeoR5QnSAsNu/cMa1yM3Zxa3CgmSjUmpBy+X2gr1Vo8yAeLVhqI8k9hzev+/LhrinTZIqVZZ4CaA5TEiBuNAy2uB8WEkned6uh3hRyIVNavlfoO9sToZn8f+xn8NKrM= gdZccNCwzFrAdA+qGOA9I9ChD8hJQq/4Xuc5iC2i2I+Rtp6KCRrlgqPU0Wn6zwQ8SKitRLUSeNCJcspzkpewfv0Lnb2VIDMhdDeA5zyxVss0dZqDrMgYRhZcT+odtFTX8GlWUTyqBSQpUh8YQYdggvFZkE5ZOTaFU6kqUoKAgcs= - a2btKvTS6UnTiD3vTaLX4JjQe3YB8iOpgqwDD3Imy9QzfIZwOBkKuI+jZDtea9VPpFsO6P7pVPmL5y3eZQpVe9oBlw74Ve0hMvX+RrNjAO5DINbBNG8JxZ4gPIMQsUnYVVkHH3pvoiJIJ8AlctQuMEQ2v2slnOvEhTMWiJF2vzQWPgY7vCu6hp92AJvalKIOmK6qT3u7gf45L06R16hpzxD73DbmYwEnMs1ZuF9e1NrLdXRBr+/m4/K8RneZf/hLnW70JJm1IfA/PEVYUEk5HG0fWZFTnsBI3xNXuxXg4OdiR/7Bcrmpj36ATEU6XfWnJBP5qZwhvNzIdMMcDLOnk52b56Szjr5wEOSJIvG93knmZJAFPDIqnRY9BLYtZb6rZ6sCpLMD7R97HKhUy0CFgNNY43CniAtIiARpnGFpdxPISSWeVFsqrckEDeya4hUpZQRUf7T53nTnOBFMuygDgWxfJnMxEl0tg6m5kfAhkguyFDRueA/XkSs0bXQ8xuERBZpKCWA7TJZnMbljK9G22VMYPFCJbB8hHkBjW49wsM8rF0MAS2KvweJdvFjclhqRNSH6sXHKTh4r870YYhYXNnrqobikmP81ZCoq6avKH10/1UPJJAQjBT4MwM488M7TfPv4q9l0ezi7v91fnl2MORRlZljoQ57ao6flIf/KCgQhxl20PtYPz4TcRU/onZ5OKRDXEgaWpkJZkt4ZZX2NgjIvEI8wTEATY3Eu2dBOKeWKD7jwhkPJtnRka8+miNSFanhwJ78WjIx1B/s9jMJ2njCMAzsabjVDMZPwedXigz67zcSl3NnCg4ycCukPnk/Vs2r90XjYKOCuvlU6CsTCeQLLX28Y2AaABY+7vQIEV3XPZVc4kimy6EEShfNZkkLNHQjoZqVXkFp8AKxWCZLzJOFyGFTAX0UqsN9O6soJ/rzAF68ByjYT2y9iggA8GDYUAHZxe0fX/b01+8D+I14c9qPctBaEPhJ2DjYOfJhKRglP1nkIX6U/wd0A19brME7fr50k/ndYpavzGOhVFHrcnxufn6CDJ9/8XeeQ3XdqHwCnYT4G0dUBDEQXcCGWJa0KVkefl4vW25A8Uz5aa4yQoOj7wsnJdLVoijClWEj4a7AWP1AaH9l+dQels1ek7nJ5M8BgjGK5QSSbSrgdNLVRTRZ8PxsSdSjs1tXpSZB6SfSIWW0l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 @@ -81,7 +82,7 @@ - + M42 Demo Server (Imagoverum) diff --git a/F4SDwebService/Config/F4SD-QuickAction-Configuration.xml b/F4SDwebService/Config/F4SD-QuickAction-Configuration.xml index 787f4fb..08f025d 100644 --- a/F4SDwebService/Config/F4SD-QuickAction-Configuration.xml +++ b/F4SDwebService/Config/F4SD-QuickAction-Configuration.xml @@ -1,6 +1,10 @@ + + Remote Verbindung starten + + AD Computer Objekt bearbeiten Opens Active Directory to edit the current selected computer. @@ -933,14 +937,14 @@ - + Intune Komformitätsstatus erzwingen Enforce Intune compliance state Komformitätsstatus aktualisieren Update compliance state - + Bitlocker Schlüssel anzeigen Show Bitlocker recoverykey @@ -956,14 +960,29 @@ Laufwerkstyp - + Erstellungsdatum + - + + TAP Passwort anzeigen + Show TAP Password + TAP Passwort anzeigen + Show TAP Password + + + + Passwort + + + Startdatum + + + - + Verwaltete Anwendungen anzeigen Show managed applications @@ -996,7 +1015,7 @@ - - + Test:Bitlocker Schlüssel anzeigen Test:Show Bitlocker recoverykey @@ -1035,13 +1054,13 @@ Laufwerkstyp - + Erstellungsdatum - + Test:LAPS Passwort anzeigen Test:Show LAPS Password @@ -1057,14 +1076,14 @@ Passwort - + Sicherungsdatum - - - + Session abmelden Session logoff Session Abmelden @@ -1140,7 +1159,7 @@ - + Session verstecken Session hidden Session verstecken @@ -1151,7 +1170,7 @@ - + Nachricht senden zu Session Send Message to Session @@ -1162,5 +1181,24 @@ 5 + + + Empirum Inventur öffnen + This Quick Action requires information about the "Inventory of Device". + Um diese Quick Action verwenden zu können werden Informationen über das "Gerät" benötigt. + + https://intune.microsoft.com/#view/Microsoft_Intune_Devices/DeviceSettingsMenuBlade/~/overview/mdmDeviceId/PARAM_EmpirumDeviceId + + + + + Nachricht senden zu Session + Send Message to Session + + Nachricht senden zu Session + Send Message to Session + + + \ No newline at end of file diff --git a/F4SDwebService/Config/F4SD-QuickAction-Configuration.xsd b/F4SDwebService/Config/F4SD-QuickAction-Configuration.xsd index 7c45ca3..e0b615a 100644 --- a/F4SDwebService/Config/F4SD-QuickAction-Configuration.xsd +++ b/F4SDwebService/Config/F4SD-QuickAction-Configuration.xsd @@ -27,7 +27,7 @@ - + @@ -197,6 +197,16 @@ + + + + + + + + + + @@ -249,7 +259,7 @@ - + diff --git a/F4SDwebService/Controllers/GetConfigurationController.cs b/F4SDwebService/Controllers/GetConfigurationController.cs index 70e36a7..46e98bd 100644 --- a/F4SDwebService/Controllers/GetConfigurationController.cs +++ b/F4SDwebService/Controllers/GetConfigurationController.cs @@ -84,7 +84,7 @@ namespace FasdWebService.Controllers return BadRequest(); } - [Route("api/GetCollectorStatus")] + [Route("api/CheckCollectorStatus")] public IHttpActionResult GetCollectorStatus() { MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } @@ -95,7 +95,7 @@ namespace FasdWebService.Controllers try { - var retVal = WebApiApplication.Collector.GetCollectorStatus(requestInfo); + var retVal = WebApiApplication.Collector.CheckCollectorStatus(requestInfo); if (retVal != null) return Ok(retVal); diff --git a/F4SDwebService/Controllers/QuickActionDataController.cs b/F4SDwebService/Controllers/QuickActionDataController.cs index d1e35b7..bcf3c0b 100644 --- a/F4SDwebService/Controllers/QuickActionDataController.cs +++ b/F4SDwebService/Controllers/QuickActionDataController.cs @@ -17,7 +17,7 @@ namespace F4SDwebService.Controllers public async Task QuickActionRun([FromBody] cF4SDServerQuickActionParameters jsonRequest) { MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } - var requestInfo = new cF4sdWebRequestInfo(jsonRequest.Action, jsonRequest.ParamaterType); + var requestInfo = new cF4sdWebRequestInfo(jsonRequest.Action, jsonRequest.ParameterType); if (cPerformanceLogger.IsActive && requestInfo != null) { cPerformanceLogger.LogPerformanceStart(0, requestInfo.requestName, requestInfo.id, requestInfo.created); } var apiError = 0; diff --git a/F4SDwebService/Controllers/RemoteDesktopController.cs b/F4SDwebService/Controllers/RemoteDesktopController.cs new file mode 100644 index 0000000..b67f7eb --- /dev/null +++ b/F4SDwebService/Controllers/RemoteDesktopController.cs @@ -0,0 +1,72 @@ +using C4IT.DataHistoryProvider.Base.DataSources; +using C4IT.DataHistoryProvider.Base.Modules.Agent; +using C4IT.FASD.Base; +using C4IT.FASD.Communication.Agent; +using System; +using System.Threading; +using System.Threading.Tasks; +using System.Web.Http; + +namespace F4SDwebService.Controllers +{ + public class RemoteDesktopController : ApiController + { + private readonly IRemoteDesktopManager _remoteDesktopManager; + + public RemoteDesktopController() + { + var agentConfig = WebApiApplication.Collector.F4sdAgent.GetAgentApiConfiguration(true); + _remoteDesktopManager = new AgentRemoteDesktopManager(agentConfig); + } + + [HttpPost] + [Route("api/RemoteDesktop/Initiate")] + public async Task Initiate([FromBody] AgentRemoteClientInfo clientInfo, [FromUri] bool isElevated, CancellationToken token = default) + { + AgentRemoteConnectionInfo connection = await _remoteDesktopManager.InitiateConnectionOfClient(clientInfo, isElevated, token); + + if (connection is null) + return NotFound(); + else if (connection.Errors is null) + return Ok(connection); + else + return Content(System.Net.HttpStatusCode.BadRequest, connection); + } + + [HttpGet] + [Route("api/RemoteDesktop/{connectionId}/Status")] + public async Task GetStatus(Guid connectionId) + { + AgentRemoteConnectionStatusDto status = await _remoteDesktopManager.GetConnectionStatus(connectionId); + + if (status is null) + return NotFound(); + + return Ok(status); + } + + [HttpDelete] + [Route("api/RemoteDesktop/{connectionId}/Stop")] + public async Task Stop(Guid connectionId) + { + await _remoteDesktopManager.TerminateConnection(connectionId); + return Ok(); + } + + [HttpGet] + [Route("api/RemoteDesktop/IsActive")] + public IHttpActionResult IsActive() + { + bool isActive = _remoteDesktopManager.IsActive(); + return Ok(isActive); + } + + [HttpGet] + [Route("api/RemoteDesktop/health")] + public async Task GetHealth() + { + HealthInformation healthInfo = await _remoteDesktopManager.GetHealthInfo(); + return Ok(healthInfo); + } + } +} \ No newline at end of file diff --git a/F4SDwebService/Controllers/StagedSearchRelationController.cs b/F4SDwebService/Controllers/StagedSearchRelationController.cs index 1fd5451..4f8a1f8 100644 --- a/F4SDwebService/Controllers/StagedSearchRelationController.cs +++ b/F4SDwebService/Controllers/StagedSearchRelationController.cs @@ -1,37 +1,52 @@ -using C4IT.FASD.Base; -using C4IT_DataHistoryProvider_Base.DataSources; -using C4IT_DataHistoryProvider_Base.Services; -using System; +using System; using System.Collections.Generic; using System.Net; using System.Net.Http; using System.Reflection; +using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; using System.Web.Http; using System.Web.Http.Results; + +using C4IT.FASD.Base; + +using C4IT_DataHistoryProvider_Base.DataSources; +using C4IT_DataHistoryProvider_Base.Services; + using static C4IT.Logging.cLogManager; namespace F4SDwebService.Controllers { public class StagedSearchRelationController : ApiController { - private readonly IStagedRelationService _relationService; + private IStagedRelationService _relationService = null; public StagedSearchRelationController() { - _relationService = WebApiApplication.Collector.GetStagedRelationService(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private bool checkRelationService() + { + if (_relationService == null && WebApiApplication.Collector != null) + _relationService = WebApiApplication.Collector.GetStagedRelationService(); + + return (_relationService != null); } [HttpPost] [Route("api/StagedSearchRelations")] - public IHttpActionResult StartGatheringRelatedInformationObjects([FromBody] IEnumerable relatedTo, [FromUri] int age) + public async Task StartGatheringRelatedInformationObjects([FromBody] IEnumerable relatedTo, [FromUri] int age) { MethodBase CM = null; if (DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } try { - cF4sdStagedSearchResultRelationTaskId taskId = _relationService.StartGatheringRelatedObjects(relatedTo, age); + if (!checkRelationService()) + return NotFound(); + + cF4sdStagedSearchResultRelationTaskId taskId = await _relationService.StartGatheringRelatedObjects(relatedTo, age); if (taskId.Id == Guid.Empty) return BadRequest(); @@ -54,6 +69,9 @@ namespace F4SDwebService.Controllers MethodBase CM = null; if (DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } try { + if (!checkRelationService()) + return NotFound(); + return Ok(await _relationService.GetRelatedObjectsAsync(id, token)); } catch (Exception ex) @@ -72,6 +90,9 @@ namespace F4SDwebService.Controllers MethodBase CM = null; if (DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } try { + if (!checkRelationService()) + return NotFound(); + _relationService.StopGatheringRelatedObjects(id); HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.Accepted); return new ResponseMessageResult(response); diff --git a/F4SDwebService/F4SD-Cockpit-WebService.csproj b/F4SDwebService/F4SD-Cockpit-WebService.csproj index 5f1d582..bd323af 100644 --- a/F4SDwebService/F4SD-Cockpit-WebService.csproj +++ b/F4SDwebService/F4SD-Cockpit-WebService.csproj @@ -321,6 +321,7 @@ + diff --git a/F4SDwebService/Global.asax.cs b/F4SDwebService/Global.asax.cs index 489eceb..6cfdafc 100644 --- a/F4SDwebService/Global.asax.cs +++ b/F4SDwebService/Global.asax.cs @@ -131,6 +131,9 @@ namespace F4SDwebService if (Collector.M42WpmCollector != null) await Collector.M42WpmCollector.ValidateConnectionAsync(true, CancellationToken.None); + if (Collector.CitrixCollector != null) + await Collector.CitrixCollector.ValidateConnectionsAsync(CancellationToken.None); + Collector.ServerStatus = C4IT.FASD.Base.enumWebServerStatus.active; } catch (Exception E) diff --git a/F4SDwebService/License/F4SD_License.xml b/F4SDwebService/License/F4SD_License.xml index 750e156..5da51c1 100644 --- a/F4SDwebService/License/F4SD_License.xml +++ b/F4SDwebService/License/F4SD_License.xml @@ -1,12 +1,13 @@ - + - - - - - - - - - + + + + + + + + + + \ No newline at end of file diff --git a/F4SDwebService/Publish/Config/F4SD-Global-Configuration.xml b/F4SDwebService/Publish/Config/F4SD-Global-Configuration.xml index d4cbfcd..abd254e 100644 --- a/F4SDwebService/Publish/Config/F4SD-Global-Configuration.xml +++ b/F4SDwebService/Publish/Config/F4SD-Global-Configuration.xml @@ -14,9 +14,13 @@ - + + + + + @@ -24,4 +28,9 @@ + + + + + diff --git a/F4SDwebService/Publish/Config/F4SD-Global-Configuration.xsd b/F4SDwebService/Publish/Config/F4SD-Global-Configuration.xsd index 03b1e75..07e37ef 100644 --- a/F4SDwebService/Publish/Config/F4SD-Global-Configuration.xsd +++ b/F4SDwebService/Publish/Config/F4SD-Global-Configuration.xsd @@ -18,11 +18,29 @@ + + + + + + + + + + - - + + + + + + + + + + @@ -76,6 +94,14 @@ + + + + + + + + @@ -131,6 +157,22 @@ + + + + + + + + + + + + + + + + @@ -145,6 +187,7 @@ + @@ -166,5 +209,29 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/F4SDwebService/Publish/Config/F4SD-HealthCard-Configuration.xml b/F4SDwebService/Publish/Config/F4SD-HealthCard-Configuration.xml index 5e3bcc1..817c258 100644 --- a/F4SDwebService/Publish/Config/F4SD-HealthCard-Configuration.xml +++ b/F4SDwebService/Publish/Config/F4SD-HealthCard-Configuration.xml @@ -252,6 +252,9 @@ IntuneDeviceId + + EmpirumDeviceId + BshRegistryKeyExists @@ -891,6 +894,21 @@ + + + Empirum Informationen + + + Empirum Id + + + Letzte Inventory + + + IsPxe + + + @@ -1372,7 +1390,7 @@ - + Session diff --git a/F4SDwebService/Publish/Config/F4SD-Infrastructure-Configuration.xml b/F4SDwebService/Publish/Config/F4SD-Infrastructure-Configuration.xml index ea81c56..3bb761d 100644 --- a/F4SDwebService/Publish/Config/F4SD-Infrastructure-Configuration.xml +++ b/F4SDwebService/Publish/Config/F4SD-Infrastructure-Configuration.xml @@ -7,7 +7,8 @@ R0ICvoG+yjJFGXGgagNQbzAwcBacJr47wRHOIwYhBl/YGDmxpoRWrI1GZ9x1C7ofSqeE1UeZNHX/oqMo/04h+jfUrqk1QHo/6e43Lns1z7UVhWrzOlPSxYwCjv7e4+6wqTWTx6UNGR5jCeD/eHAXHW24dGu7mph9xhcB3VC1kRY= ZLAAU+jFXU/+x9FVjx4EAZRnK/D+G/OpZGkn4ek56vhDUeoR5QnSAsNu/cMa1yM3Zxa3CgmSjUmpBy+X2gr1Vo8yAeLVhqI8k9hzev+/LhrinTZIqVZZ4CaA5TEiBuNAy2uB8WEkned6uh3hRyIVNavlfoO9sToZn8f+xn8NKrM= gdZccNCwzFrAdA+qGOA9I9ChD8hJQq/4Xuc5iC2i2I+Rtp6KCRrlgqPU0Wn6zwQ8SKitRLUSeNCJcspzkpewfv0Lnb2VIDMhdDeA5zyxVss0dZqDrMgYRhZcT+odtFTX8GlWUTyqBSQpUh8YQYdggvFZkE5ZOTaFU6kqUoKAgcs= - a2btKvTS6UnTiD3vTaLX4JjQe3YB8iOpgqwDD3Imy9QzfIZwOBkKuI+jZDtea9VPpFsO6P7pVPmL5y3eZQpVe9oBlw74Ve0hMvX+RrNjAO5DINbBNG8JxZ4gPIMQsUnYVVkHH3pvoiJIJ8AlctQuMEQ2v2slnOvEhTMWiJF2vzQWPgY7vCu6hp92AJvalKIOmK6qT3u7gf45L06R16hpzxD73DbmYwEnMs1ZuF9e1NrLdXRBr+/m4/K8RneZf/hLnW70JJm1IfA/PEVYUEk5HG0fWZFTnsBI3xNXuxXg4OdiR/7Bcrmpj36ATEU6XfWnJBP5qZwhvNzIdMMcDLOnk52b56Szjr5wEOSJIvG93knmZJAFPDIqnRY9BLYtZb6rZ6sCpLMD7R97HKhUy0CFgNNY43CniAtIiARpnGFpdxPISSWeVFsqrckEDeya4hUpZQRUf7T53nTnOBFMuygDgWxfJnMxEl0tg6m5kfAhkguyFDRueA/XkSs0bXQ8xuERBZpKCWA7TJZnMbljK9G22VMYPFCJbB8hHkBjW49wsM8rF0MAS2KvweJdvFjclhqRNSH6sXHKTh4r870YYhYXNnrqobikmP81ZCoq6avKH10/1UPJJAQjBT4MwM488M7TfPv4q9l0ezi7v91fnl2MORRlZljoQ57ao6flIf/KCgQhxl20PtYPz4TcRU/onZ5OKRDXEgaWpkJZkt4ZZX2NgjIvEI8wTEATY3Eu2dBOKeWKD7jwhkPJtnRka8+miNSFanhwJ78WjIx1B/s9jMJ2njCMAzsabjVDMZPwedXigz67zcSl3NnCg4ycCukPnk/Vs2r90XjYKOCuvlU6CsTCeQLLX28Y2AaABY+7vQIEV3XPZVc4kimy6EEShfNZkkLNHQjoZqVXkFp8AKxWCZLzJOFyGFTAX0UqsN9O6soJ/rzAF68ByjYT2y9iggA8GDYUAHZxe0fX/b01+8D+I14c9qPctBaEPhJ2DjYOfJhKRglP1nkIX6U/wd0A19brME7fr50k/ndYpavzGOhVFHrcnxufn6CDJ9/8XeeQ3XdqHwCnYT4G0dUBDEQXcCGWJa0KVkefl4vW25A8Uz5aa4yQoOj7wsnJdLVoijClWEj4a7AWP1AaH9l+dQels1ek7nJ5M8BgjGK5QSSbSrgdNLVRTRZ8PxsSdSjs1tXpSZB6SfSIWW0l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 @@ -81,7 +82,7 @@ - + M42 Demo Server (Imagoverum) diff --git a/F4SDwebService/Publish/Config/F4SD-Infrastructure-Configuration.xsd b/F4SDwebService/Publish/Config/F4SD-Infrastructure-Configuration.xsd index 3387100..089e8c8 100644 --- a/F4SDwebService/Publish/Config/F4SD-Infrastructure-Configuration.xsd +++ b/F4SDwebService/Publish/Config/F4SD-Infrastructure-Configuration.xsd @@ -391,12 +391,18 @@ + + + + + - + + @@ -405,14 +411,17 @@ - + - - - - - - + + + + + + + + + @@ -420,6 +429,15 @@ + + + + + + + + + diff --git a/F4SDwebService/Publish/Config/F4SD-QuickAction-Configuration.xml b/F4SDwebService/Publish/Config/F4SD-QuickAction-Configuration.xml index 787f4fb..08f025d 100644 --- a/F4SDwebService/Publish/Config/F4SD-QuickAction-Configuration.xml +++ b/F4SDwebService/Publish/Config/F4SD-QuickAction-Configuration.xml @@ -1,6 +1,10 @@ + + Remote Verbindung starten + + AD Computer Objekt bearbeiten Opens Active Directory to edit the current selected computer. @@ -933,14 +937,14 @@ - + Intune Komformitätsstatus erzwingen Enforce Intune compliance state Komformitätsstatus aktualisieren Update compliance state - + Bitlocker Schlüssel anzeigen Show Bitlocker recoverykey @@ -956,14 +960,29 @@ Laufwerkstyp - + Erstellungsdatum + - + + TAP Passwort anzeigen + Show TAP Password + TAP Passwort anzeigen + Show TAP Password + + + + Passwort + + + Startdatum + + + - + Verwaltete Anwendungen anzeigen Show managed applications @@ -996,7 +1015,7 @@ - - + Test:Bitlocker Schlüssel anzeigen Test:Show Bitlocker recoverykey @@ -1035,13 +1054,13 @@ Laufwerkstyp - + Erstellungsdatum - + Test:LAPS Passwort anzeigen Test:Show LAPS Password @@ -1057,14 +1076,14 @@ Passwort - + Sicherungsdatum - - - + Session abmelden Session logoff Session Abmelden @@ -1140,7 +1159,7 @@ - + Session verstecken Session hidden Session verstecken @@ -1151,7 +1170,7 @@ - + Nachricht senden zu Session Send Message to Session @@ -1162,5 +1181,24 @@ 5 + + + Empirum Inventur öffnen + This Quick Action requires information about the "Inventory of Device". + Um diese Quick Action verwenden zu können werden Informationen über das "Gerät" benötigt. + + https://intune.microsoft.com/#view/Microsoft_Intune_Devices/DeviceSettingsMenuBlade/~/overview/mdmDeviceId/PARAM_EmpirumDeviceId + + + + + Nachricht senden zu Session + Send Message to Session + + Nachricht senden zu Session + Send Message to Session + + + \ No newline at end of file diff --git a/F4SDwebService/Publish/Config/F4SD-QuickAction-Configuration.xsd b/F4SDwebService/Publish/Config/F4SD-QuickAction-Configuration.xsd index 7c45ca3..e0b615a 100644 --- a/F4SDwebService/Publish/Config/F4SD-QuickAction-Configuration.xsd +++ b/F4SDwebService/Publish/Config/F4SD-QuickAction-Configuration.xsd @@ -27,7 +27,7 @@ - + @@ -197,6 +197,16 @@ + + + + + + + + + + @@ -249,7 +259,7 @@ - + diff --git a/F4SDwebService/Publish/License/F4SD_License.xml b/F4SDwebService/Publish/License/F4SD_License.xml index 750e156..5da51c1 100644 --- a/F4SDwebService/Publish/License/F4SD_License.xml +++ b/F4SDwebService/Publish/License/F4SD_License.xml @@ -1,12 +1,13 @@ - + - - - - - - - - - + + + + + + + + + + \ No newline at end of file diff --git a/Setup/SignSourceFiles.cmd b/Setup/SignSourceFiles.cmd index 882ccfd..d5e6b76 100644 --- a/Setup/SignSourceFiles.cmd +++ b/Setup/SignSourceFiles.cmd @@ -3,7 +3,7 @@ set ProductName="C4IT First Aid ServiceDesk (F4SD)" set SignTool=..\..\Common Code\Tools\signtool.exe set TimeStamp=http://rfc3161timestamp.globalsign.com/advanced -"%SignTool%" sign /a /tr %TimeStamp% /td SHA256 /fd SHA256 /d %ProductName% ".\Source\bin\F4SD-Cockpit-Collector.exe" ".\Source\bin\F4SD-Cockpit-WebService.dll" ".\Source\bin\F4SD-Cockpit-Server.dll" ".\Source\bin\C4IT.F4SD.DisplayFormatting.dll" +"%SignTool%" sign /a /tr %TimeStamp% /td SHA256 /fd SHA256 /d %ProductName% ".\Source\bin\F4SD-Cockpit-Collector.exe" ".\Source\bin\F4SD-Cockpit-WebService.dll" ".\Source\bin\F4SD-Cockpit-Server.dll" ".\Source\bin\C4IT.F4SD.DisplayFormatting.dll" ".\Source\bin\C4IT.F4SD.SupportCaseProtocoll.dll" pause