Prepare Data History Provider 2.7 integration update

- modularize Citrix and agent integrations and add remote desktop endpoints
- extend ticket overview, ticket links, token validation, and staged relation handling
- update configuration, licensing, project, signing, and publish artifacts
This commit is contained in:
Meik
2026-07-20 09:31:52 +02:00
parent a3357e2a36
commit ccc48c521a
62 changed files with 5267 additions and 2521 deletions

View File

@@ -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<bool> privLogonAsync(cOAuthLogonInfo logonInfo)
{
try
{
var postData = new List<KeyValuePair<string, string>>()
{
new KeyValuePair<string, string>("grant_type", "client_credentials"),
new KeyValuePair<string, string>("client_id", logonInfo.ClientID),
new KeyValuePair<string, string>("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<cAgentApiLogonInfo>(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<T> Request<T>(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<T>();
string serialized = JsonConvert.SerializeObject(requestResult); // todo find better approach for casting the dynamic
T deserialized = JsonConvert.DeserializeObject<T>(serialized);
return deserialized;
}
internal async Task Request(string url, eHttpMethod httpMethod = eHttpMethod.get, object bodyData = null)
=> await privRequestAsync(url, httpMethod, bodyData);
}
}

View File

@@ -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; }
}
}

View File

@@ -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; }
}
}

View File

@@ -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<AgentRemoteConnectionInfo, AgentRemoteClientInfo, AgentRemoteConnectionStatusDto>
{
private readonly AgentApiHandler _agentApiHandler;
public AgentRemoteDesktopManager(cAgentApiConfiguration agentConfig)
{
_agentApiHandler = new AgentApiHandler(agentConfig);
}
public async Task<AgentRemoteConnectionStatusDto> GetConnectionStatus(Guid connectionId)
{
return await _agentApiHandler.Request<AgentRemoteConnectionStatusDto>($"api/management/c4it/phoenix-connections/{connectionId}/status", HTTP.eHttpMethod.get);
}
public async Task<AgentRemoteConnectionInfo> InitiateConnectionOfClient(AgentRemoteClientInfo clientInfos, bool isElevated, CancellationToken token)
{
string url = "api/management/c4it/phoenix-connections";
if (isElevated)
url += "/admin";
return await _agentApiHandler.Request<AgentRemoteConnectionInfo>(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<HealthInformation> GetHealthInfo()
{
// todo Agent Server has to implement an "health" endpoint and return its result
return Task.FromResult(new HealthInformation(HealthStatus.healthy));
}
}
}

View File

@@ -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; }
}
}

View File

@@ -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; }
}
}