diff --git a/src/Certify.Core/Management/CertifyManager/CertifyManager.ManagementHub.cs b/src/Certify.Core/Management/CertifyManager/CertifyManager.ManagementHub.cs index a9890800d..01e8a8c47 100644 --- a/src/Certify.Core/Management/CertifyManager/CertifyManager.ManagementHub.cs +++ b/src/Certify.Core/Management/CertifyManager/CertifyManager.ManagementHub.cs @@ -48,7 +48,7 @@ private async Task StartManagementHubConnection(string hubUri) var instanceInfo = new ManagedInstanceInfo { InstanceId = $"{this.InstanceId}", - Title = Environment.MachineName + Title = $"{Environment.MachineName} [{EnvironmentUtil.GetFriendlyOSName()}]", }; if (_managementServerClient != null) @@ -162,6 +162,19 @@ private async Task _managementServerClient_OnGetCommandRe val = true; } + else if (arg.CommandType == ManagementHubCommands.GetAcmeAccounts) + { + val = await GetAccountRegistrations(); + } + else if (arg.CommandType == ManagementHubCommands.AddAcmeAccount) + { + + var args = JsonSerializer.Deserialize[]>(arg.Value); + var registrationArg = args.FirstOrDefault(a => a.Key == "registration"); + var registration = JsonSerializer.Deserialize(registrationArg.Value); + + val = await AddAccount(registration); + } else if (arg.CommandType == ManagementHubCommands.Reconnect) { await _managementServerClient.Disconnect(); diff --git a/src/Certify.Models/API/Management/ManagementHubMessages.cs b/src/Certify.Models/API/Management/ManagementHubMessages.cs index 62dc9ebff..aa05b8106 100644 --- a/src/Certify.Models/API/Management/ManagementHubMessages.cs +++ b/src/Certify.Models/API/Management/ManagementHubMessages.cs @@ -24,6 +24,8 @@ public class ManagementHubCommands public const string TestManagedItemConfiguration = "TestManagedItemConfiguration"; public const string PerformManagedItemRequest = "PerformManagedItemRequest"; + public const string GetAcmeAccounts = "GetAcmeAccounts"; + public const string AddAcmeAccount = "AddAcmeAccount"; public const string Reconnect = "Reconnect"; } diff --git a/src/Certify.Models/Util/EnvironmentUtil.cs b/src/Certify.Models/Util/EnvironmentUtil.cs index 033e497bf..41217f3f2 100644 --- a/src/Certify.Models/Util/EnvironmentUtil.cs +++ b/src/Certify.Models/Util/EnvironmentUtil.cs @@ -114,5 +114,46 @@ public static string CreateAppDataPath(string? subDirectory = null) return path; } + + public static string GetFriendlyOSName() + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + return $"{RuntimeInformation.OSDescription}"; + } + else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + { + var osName = string.Empty; + + string filePath = "/etc/os-release"; + + try + { + using (FileStream fileStream = new FileStream(filePath, FileMode.Open)) + { + using (StreamReader reader = new StreamReader(fileStream)) + { + string line; + + while ((line = reader.ReadLine()) != null) + { + + if (line.StartsWith("NAME")) + { + osName = line.Split('\"')[1]; //split the line string by " and get the second slice + return osName; + } + } + } + } + } + catch + { + return $"Linux - {RuntimeInformation.OSDescription}"; + } + } + + return $"{RuntimeInformation.OSDescription}"; + } } } diff --git a/src/Certify.Server/Certify.Server.Api.Public.Client/Certify.API.Public.cs b/src/Certify.Server/Certify.Server.Api.Public.Client/Certify.API.Public.cs index 8dfaf9c6d..5fb22bcbe 100644 --- a/src/Certify.Server/Certify.Server.Api.Public.Client/Certify.API.Public.cs +++ b/src/Certify.Server/Certify.Server.Api.Public.Client/Certify.API.Public.cs @@ -2242,9 +2242,9 @@ public virtual async System.Threading.Tasks.Task PerformRene /// /// OK /// A server side error occurred. - public virtual System.Threading.Tasks.Task> GetAcmeAccountsAsync() + public virtual System.Threading.Tasks.Task> GetAcmeAccountsAsync(string instanceId) { - return GetAcmeAccountsAsync(System.Threading.CancellationToken.None); + return GetAcmeAccountsAsync(instanceId, System.Threading.CancellationToken.None); } /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. @@ -2253,8 +2253,11 @@ public virtual async System.Threading.Tasks.Task PerformRene /// /// OK /// A server side error occurred. - public virtual async System.Threading.Tasks.Task> GetAcmeAccountsAsync(System.Threading.CancellationToken cancellationToken) + public virtual async System.Threading.Tasks.Task> GetAcmeAccountsAsync(string instanceId, System.Threading.CancellationToken cancellationToken) { + if (instanceId == null) + throw new System.ArgumentNullException("instanceId"); + var client_ = _httpClient; var disposeClient_ = false; try @@ -2266,8 +2269,9 @@ public virtual async System.Threading.Tasks.Task PerformRene var urlBuilder_ = new System.Text.StringBuilder(); if (!string.IsNullOrEmpty(_baseUrl)) urlBuilder_.Append(_baseUrl); - // Operation Path: "internal/v1/certificateauthority/accounts" - urlBuilder_.Append("internal/v1/certificateauthority/accounts"); + // Operation Path: "internal/v1/certificateauthority/accounts/{instanceId}" + urlBuilder_.Append("internal/v1/certificateauthority/accounts/"); + urlBuilder_.Append(System.Uri.EscapeDataString(ConvertToString(instanceId, System.Globalization.CultureInfo.InvariantCulture))); PrepareRequest(client_, request_, urlBuilder_); @@ -2326,9 +2330,9 @@ public virtual async System.Threading.Tasks.Task PerformRene /// /// OK /// A server side error occurred. - public virtual System.Threading.Tasks.Task AddAcmeAccountAsync(ContactRegistration body) + public virtual System.Threading.Tasks.Task AddAcmeAccountAsync(string instanceId, ContactRegistration body) { - return AddAcmeAccountAsync(body, System.Threading.CancellationToken.None); + return AddAcmeAccountAsync(instanceId, body, System.Threading.CancellationToken.None); } /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. @@ -2337,8 +2341,11 @@ public virtual System.Threading.Tasks.Task AddAcmeAccountAsync(Con /// /// OK /// A server side error occurred. - public virtual async System.Threading.Tasks.Task AddAcmeAccountAsync(ContactRegistration body, System.Threading.CancellationToken cancellationToken) + public virtual async System.Threading.Tasks.Task AddAcmeAccountAsync(string instanceId, ContactRegistration body, System.Threading.CancellationToken cancellationToken) { + if (instanceId == null) + throw new System.ArgumentNullException("instanceId"); + var client_ = _httpClient; var disposeClient_ = false; try @@ -2354,8 +2361,9 @@ public virtual async System.Threading.Tasks.Task AddAcmeAccountAsy var urlBuilder_ = new System.Text.StringBuilder(); if (!string.IsNullOrEmpty(_baseUrl)) urlBuilder_.Append(_baseUrl); - // Operation Path: "internal/v1/certificateauthority/account" - urlBuilder_.Append("internal/v1/certificateauthority/account"); + // Operation Path: "internal/v1/certificateauthority/account/{instanceId}" + urlBuilder_.Append("internal/v1/certificateauthority/account/"); + urlBuilder_.Append(System.Uri.EscapeDataString(ConvertToString(instanceId, System.Globalization.CultureInfo.InvariantCulture))); PrepareRequest(client_, request_, urlBuilder_); diff --git a/src/Certify.Server/Certify.Server.Api.Public/Controllers/internal/CertificateAuthorityController.cs b/src/Certify.Server/Certify.Server.Api.Public/Controllers/internal/CertificateAuthorityController.cs index 42964187c..f6687833b 100644 --- a/src/Certify.Server/Certify.Server.Api.Public/Controllers/internal/CertificateAuthorityController.cs +++ b/src/Certify.Server/Certify.Server.Api.Public/Controllers/internal/CertificateAuthorityController.cs @@ -1,4 +1,5 @@ using Certify.Client; +using Certify.Server.Api.Public.Services; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; @@ -16,16 +17,18 @@ public partial class CertificateAuthorityController : ApiControllerBase private readonly ILogger _logger; private readonly ICertifyInternalApiClient _client; + private readonly ManagementAPI _mgmtAPI; /// /// Constructor /// /// /// - public CertificateAuthorityController(ILogger logger, ICertifyInternalApiClient client) + public CertificateAuthorityController(ILogger logger, ICertifyInternalApiClient client, ManagementAPI mgmtApi) { _logger = logger; _client = client; + _mgmtAPI = mgmtApi; } /// diff --git a/src/Certify.Server/Certify.Server.Api.Public/Services/ManagementAPI.cs b/src/Certify.Server/Certify.Server.Api.Public/Services/ManagementAPI.cs index 3fd9a9de1..5d0462a2c 100644 --- a/src/Certify.Server/Certify.Server.Api.Public/Services/ManagementAPI.cs +++ b/src/Certify.Server/Certify.Server.Api.Public/Services/ManagementAPI.cs @@ -3,13 +3,14 @@ using Certify.Client; using Certify.Models; using Certify.Models.API; +using Certify.Models.Config; using Certify.Models.Reporting; using Certify.Server.Api.Public.SignalR.ManagementHub; using Microsoft.AspNetCore.SignalR; namespace Certify.Server.Api.Public.Services { - public class ManagementAPI + public partial class ManagementAPI { IInstanceManagementStateProvider _mgmtStateProvider; IHubContext _mgmtHubContext; @@ -163,6 +164,47 @@ public async Task GetManagedCertificateSummary(AuthContext? curre return await Task.FromResult(sum); } + public async Task?> GetAcmeAccounts(string instanceId, AuthContext? currentAuthContext) + { + var args = new KeyValuePair[] { + new("instanceId", instanceId) + }; + + var cmd = new InstanceCommandRequest(ManagementHubCommands.GetAcmeAccounts, args); + + var result = await GetCommandResult(instanceId, cmd); + + if (result?.Value != null) + { + return JsonSerializer.Deserialize>(result.Value); + } + else + { + return null; + } + } + + public async Task AddAcmeAccount(string instanceId, ContactRegistration registration, AuthContext? currentAuthContext) + { + var args = new KeyValuePair[] { + new("instanceId", instanceId) , + new("registration", JsonSerializer.Serialize(registration)) + }; + + var cmd = new InstanceCommandRequest(ManagementHubCommands.AddAcmeAccount, args); + + var result = await GetCommandResult(instanceId, cmd); + + if (result?.Value != null) + { + return JsonSerializer.Deserialize(result.Value); + } + else + { + return null; + } + } + public async Task GetItemLog(string instanceId, string managedCertId, int maxLines, AuthContext? currentAuthContext) { var args = new KeyValuePair[] { diff --git a/src/Certify.Server/Certify.Server.Api.Public/Startup.cs b/src/Certify.Server/Certify.Server.Api.Public/Startup.cs index 7553214d3..1504c467f 100644 --- a/src/Certify.Server/Certify.Server.Api.Public/Startup.cs +++ b/src/Certify.Server/Certify.Server.Api.Public/Startup.cs @@ -140,7 +140,7 @@ public void ConfigureServices(IServiceCollection services) }); - // connect to certify service + // connect to primary certify service var configManager = new ServiceConfigManager(); var serviceConfig = configManager.GetServiceConfig(); @@ -245,7 +245,7 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env) } /// - /// Connect to status stream of backend service + /// Connect to status stream of primary service /// /// /// diff --git a/src/Certify.Shared/Utils/Util.cs b/src/Certify.Shared/Utils/Util.cs index 798f3a5f9..be71b4722 100644 --- a/src/Certify.Shared/Utils/Util.cs +++ b/src/Certify.Shared/Utils/Util.cs @@ -161,7 +161,7 @@ public static async Task> PerformAppDiagnostics(bool includeT public static string GetUserAgent() { var versionName = "Certify/" + GetAppVersion().ToString(); - return $"{versionName} (Windows; {Environment.OSVersion}) "; + return $"{versionName} ({RuntimeInformation.OSDescription}; {Environment.OSVersion}) "; } public static Version GetAppVersion() diff --git a/src/Certify.SourceGenerators/ApiMethods.cs b/src/Certify.SourceGenerators/ApiMethods.cs index 0ba2896f9..fb0d93abd 100644 --- a/src/Certify.SourceGenerators/ApiMethods.cs +++ b/src/Certify.SourceGenerators/ApiMethods.cs @@ -125,26 +125,28 @@ public static List GetApiDefinitions() ReturnType = "Models.Config.ActionResult", Params = new Dictionary{{"id","string"}} }, + /* per instance API, via management hub */ new GeneratedAPI { - OperationName = "GetAcmeAccounts", OperationMethod = "HttpGet", Comment = "Get All Acme Accounts", + UseManagementAPI = true, PublicAPIController = "CertificateAuthority", - PublicAPIRoute = "accounts", + PublicAPIRoute = "accounts/{instanceId}", ServiceAPIRoute = "accounts", - ReturnType = "ICollection" + ReturnType = "ICollection", + Params =new Dictionary{ { "instanceId", "string" } } }, new GeneratedAPI { - OperationName = "AddAcmeAccount", OperationMethod = "HttpPost", Comment = "Add New Acme Account", + UseManagementAPI = true, PublicAPIController = "CertificateAuthority", - PublicAPIRoute = "account", + PublicAPIRoute = "account/{instanceId}", ServiceAPIRoute = "accounts", ReturnType = "Models.Config.ActionResult", - Params =new Dictionary{{"registration", "Certify.Models.ContactRegistration" } } + Params =new Dictionary{ { "instanceId", "string" },{ "registration", "Certify.Models.ContactRegistration" } } }, new GeneratedAPI { @@ -157,18 +159,7 @@ public static List GetApiDefinitions() ReturnType = "Models.Config.ActionResult", Params =new Dictionary{{ "certificateAuthority", "Certify.Models.CertificateAuthority" } } }, - new GeneratedAPI { - OperationName = "RemoveManagedCertificate", - OperationMethod = "HttpDelete", - Comment = "Remove Managed Certificate", - PublicAPIController = "Certificate", - PublicAPIRoute = "settings/{instanceId}/{managedCertId}", - UseManagementAPI = true, - ServiceAPIRoute = "managedcertificates/delete/{managedCertId}", - ReturnType = "bool", - Params =new Dictionary{ { "instanceId", "string" },{ "managedCertId", "string" } } - }, new GeneratedAPI { OperationName = "RemoveCertificateAuthority", @@ -219,7 +210,19 @@ public static List GetApiDefinitions() ServiceAPIRoute = "system/migration/import", ReturnType = "ICollection", Params =new Dictionary{{ "importRequest", "Certify.Models.Config.Migration.ImportRequest" } } - } + }, + new GeneratedAPI { + + OperationName = "RemoveManagedCertificate", + OperationMethod = "HttpDelete", + Comment = "Remove Managed Certificate", + PublicAPIController = "Certificate", + PublicAPIRoute = "settings/{instanceId}/{managedCertId}", + UseManagementAPI = true, + ServiceAPIRoute = "managedcertificates/delete/{managedCertId}", + ReturnType = "bool", + Params =new Dictionary{ { "instanceId", "string" },{ "managedCertId", "string" } } + }, }; } } diff --git a/src/Certify.SourceGenerators/PublicAPISourceGenerator.cs b/src/Certify.SourceGenerators/PublicAPISourceGenerator.cs index 44ab3f3b3..e2e7dbf7e 100644 --- a/src/Certify.SourceGenerators/PublicAPISourceGenerator.cs +++ b/src/Certify.SourceGenerators/PublicAPISourceGenerator.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Text; @@ -122,6 +122,16 @@ public partial class CertifyApiClient if (config.OperationMethod == "HttpPost") { + var postAPIRoute = config.ServiceAPIRoute; + var postApiCall = apiParamCall; + var postApiParamDecl = apiParamDecl; + + if (config.UseManagementAPI) + { + postApiCall = apiParamCall.Replace("instanceId,", ""); + postApiParamDecl = apiParamDecl.Replace("string instanceId,", ""); + } + context.AddSource($"{config.PublicAPIController}.{config.OperationName}.ICertifyInternalApiClient.g.cs", SourceText.From($@" using Certify.Models; using Certify.Models.Config.AccessControl; @@ -136,7 +146,7 @@ public partial interface ICertifyInternalApiClient /// {config.Comment} [Generated by Certify.SourceGenerators] /// /// - Task<{config.ReturnType}> {config.OperationName}({apiParamDecl}); + Task<{config.ReturnType}> {config.OperationName}({postApiParamDecl}); }} @@ -147,9 +157,9 @@ public partial class CertifyApiClient /// {config.Comment} [Generated by Certify.SourceGenerators] /// /// - public async Task<{config.ReturnType}> {config.OperationName}({apiParamDecl}) + public async Task<{config.ReturnType}> {config.OperationName}({postApiParamDecl}) {{ - var result = await PostAsync($""{config.ServiceAPIRoute}"", {apiParamCall}); + var result = await PostAsync($""{postAPIRoute}"", {postApiCall}); return JsonToObject<{config.ReturnType}>(await result.Content.ReadAsStringAsync()); }}