using System.Net.Http.Headers; using System.Net.Http.Json; using System.Security.Cryptography; using System.Text; using FitConnect.Models; using Microsoft.Extensions.Logging; namespace FitConnect; public class FitConnectEnvironments { /// <summary> /// Default constructor. /// </summary> /// <param name="tokenUrl">URL for receiving the OAuth token</param> /// <param name="submissionApi">URL for the submission API</param> /// <param name="routingApi">URL for the routing API</param> public FitConnectEnvironments(string tokenUrl, string[] submissionApi, string routingApi) { TokenUrl = tokenUrl; SubmissionApi = submissionApi; RoutingApi = routingApi; } /// <summary> /// URL for receiving the OAuth token. /// </summary> public string TokenUrl { get; } /// <summary> /// URL for the submission API. /// </summary> public string[] SubmissionApi { get; } /// <summary> /// URL for the routing API. /// </summary> public string RoutingApi { get; } public enum EndpointType { Development, Testing, Production } /// <summary> /// Creates the endpoints for the given environment. /// </summary> /// <param name="endpointType">Environment to get endpoints for</param> /// <returns></returns> /// <exception cref="ArgumentException">Not all environments are ready to use</exception> public static FitConnectEnvironments Create(EndpointType endpointType) { return endpointType switch { EndpointType.Development => DevEnvironments, EndpointType.Testing => throw new ArgumentException("Not approved for online testing"), EndpointType.Production => throw new ArgumentException("NOT PRODUCTION READY"), _ => throw new ArgumentOutOfRangeException(nameof(endpointType), endpointType, null) }; } private static readonly FitConnectEnvironments DevEnvironments = new( "https://auth-testing.fit-connect.fitko.dev/token", new []{"https://submission-api-testing.fit-connect.fitko.dev"}, "https://routing-api-testing.fit-connect.fitko.dev" ); private static readonly FitConnectEnvironments TestEnvironments = new( "https://auth-testing.fit-connect.fitko.dev/token", new []{"https://submission-api-testing.fit-connect.fitko.dev"}, "https://routing-api-testing.fit-connect.fitko.dev" ); private static readonly FitConnectEnvironments ProductionEnvironments = new( "https://auth-testing.fit-connect.fitko.dev/token", new []{"https://submission-api-testing.fit-connect.fitko.dev"}, "https://routing-api-testing.fit-connect.fitko.dev" ); } public abstract class FunctionalBaseClass { protected readonly ILogger? logger; private RSA _rsa; public FitConnectEnvironments Environments { get; } protected FunctionalBaseClass(ILogger? logger, FitConnectEnvironments? endpoints) { Environments = endpoints ?? FitConnectEnvironments.Create(FitConnectEnvironments.EndpointType.Development); this.logger = logger; } /// <summary> /// Requesting an OAuth token from the FitConnect API. /// /// You can get the Client ID and Client Secret from the FitConnect Self Service portal /// under https://portal.auth-testing.fit-connect.fitko.dev /// </summary> /// <param name="clientId">Your client Id</param> /// <param name="clientSecret">Your client Secret</param> /// <param name="scope">Scope if needed</param> /// <returns></returns> public async Task<OAuthAccessToken?> GetTokenAsync(string clientId, string clientSecret, string? scope = null) { var client = new HttpClient(); client.DefaultRequestHeaders.Accept.Add( MediaTypeWithQualityHeaderValue.Parse("application/json")); var requestContent = new Dictionary<string, string> { { "grant_type", "client_credentials" }, { "client_id", clientId }, { "client_secret", clientSecret } }; if (scope != null) requestContent["scope"] = scope; var content = new FormUrlEncodedContent(requestContent); var request = new HttpRequestMessage(HttpMethod.Post, Environments.TokenUrl) { Content = content, Method = HttpMethod.Post }; var response = await client.SendAsync(request); return await response.Content.ReadFromJsonAsync<OAuthAccessToken>(); } public Task<SecurityEventToken> GetSetDataAsync() { throw new NotImplementedException(); } public byte[] EncryptDataAsync(byte[] data, byte[]? publicKey, string? password, int numberOfIterations) { _rsa = RSA.Create(2048); // _rsa.ImportRSAPublicKey(publicKey, out var read); logger?.LogInformation( "Encrypting data with public key: {}", Convert.ToBase64String(_rsa.ExportRSAPublicKey())); // var keyParams = new PbeParameters( // PbeEncryptionAlgorithm.Aes256Cbc, HashAlgorithmName.SHA256, numberOfIterations); // // var privateKey = // rsa.ExportEncryptedPkcs8PrivateKey(Encoding.UTF8.GetBytes(password), keyParams); // // logger?.LogInformation( // "Private key: {}", privateKey); return _rsa.Encrypt(data, RSAEncryptionPadding.OaepSHA256); } public byte[] DecryptDataAsync(byte[] data, byte[] privateKey) { return _rsa.Decrypt(data, RSAEncryptionPadding.OaepSHA256); } private async Task<T?> RestCall<T>(Uri uri, HttpMethod method, string body) { var client = new HttpClient(); client.DefaultRequestHeaders.Add("Accept", "application/json"); client.DefaultRequestHeaders.Add("Content-Type", "application/json"); var request = new HttpRequestMessage(); request.Method = method; request.Content = new StringContent(body, Encoding.UTF8, "application/json"); request.RequestUri = uri; var response = await client.SendAsync(request); if (response.IsSuccessStatusCode) return (await response.Content.ReadFromJsonAsync<T>()); throw new HttpRequestException("Error calling FitConnect API"); } }