From ec0682e5d550b42126a9f163e26727c40332ad67 Mon Sep 17 00:00:00 2001 From: Christopher Cook Date: Wed, 30 Oct 2024 15:48:24 +0800 Subject: [PATCH] Implement per instance credential delete, dns zone lookup --- .../CertifyManager/CertifyManager.Account.cs | 8 +-- .../CertifyManager.ManagementHub.cs | 9 +++- .../API/Management/ManagementHubMessages.cs | 2 + .../Providers/ICredentialsManager.cs | 2 +- .../Certify.API.Public.cs | 38 +++++++------- .../internal/ChallengeProviderController.cs | 21 ++------ .../Controllers/internal/HubController.cs | 2 +- .../Services/ManagementAPI.cs | 33 ++++++++++++- .../Controllers/CredentialsController.cs | 4 +- .../Controllers/CredentialsController.cs | 4 +- src/Certify.SourceGenerators/ApiMethods.cs | 40 ++++++++++++++- .../PublicAPISourceGenerator.cs | 49 +++++++++---------- 12 files changed, 137 insertions(+), 75 deletions(-) diff --git a/src/Certify.Core/Management/CertifyManager/CertifyManager.Account.cs b/src/Certify.Core/Management/CertifyManager/CertifyManager.Account.cs index a0baf0e06..fa25508eb 100644 --- a/src/Certify.Core/Management/CertifyManager/CertifyManager.Account.cs +++ b/src/Certify.Core/Management/CertifyManager/CertifyManager.Account.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.IO; using System.Linq; @@ -417,7 +417,7 @@ public async Task RemoveAccount(string storageKey, bool includeAcc { _serviceLog?.Information($"Deleting account {storageKey}: " + account.AccountURI); - var resultOk = await _credentialsManager.Delete(_itemManager, storageKey); + var result = await _credentialsManager.Delete(_itemManager, storageKey); // invalidate accounts cache lock (_accountsLock) @@ -426,7 +426,7 @@ public async Task RemoveAccount(string storageKey, bool includeAcc } // attempt acme account deactivation - if (resultOk && includeAccountDeactivation && acmeProvider != null) + if (result.IsSuccess && includeAccountDeactivation && acmeProvider != null) { try { @@ -443,7 +443,7 @@ public async Task RemoveAccount(string storageKey, bool includeAcc } } - return new ActionResult("RemoveAccount", resultOk); + return new ActionResult("RemoveAccount", result.IsSuccess); } else { diff --git a/src/Certify.Core/Management/CertifyManager/CertifyManager.ManagementHub.cs b/src/Certify.Core/Management/CertifyManager/CertifyManager.ManagementHub.cs index f7e1fa0ba..8f640b199 100644 --- a/src/Certify.Core/Management/CertifyManager/CertifyManager.ManagementHub.cs +++ b/src/Certify.Core/Management/CertifyManager/CertifyManager.ManagementHub.cs @@ -191,9 +191,16 @@ private async Task _managementServerClient_OnGetCommandRe { var args = JsonSerializer.Deserialize[]>(arg.Value); var itemArg = args.FirstOrDefault(a => a.Key == "storageKey"); - var storedCredential = JsonSerializer.Deserialize(itemArg.Value); val = await _credentialsManager.Delete(_itemManager, itemArg.Value); } + else if (arg.CommandType == ManagementHubCommands.GetDnsZones) + { + var args = JsonSerializer.Deserialize[]>(arg.Value); + var providerTypeArg = args.FirstOrDefault(a => a.Key == "providerTypeId"); + var credentialsIdArg = args.FirstOrDefault(a => a.Key == "credentialsId"); + + val = await GetDnsProviderZones(providerTypeArg.Value, credentialsIdArg.Value); + } 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 7b00ef97b..2d0d482c5 100644 --- a/src/Certify.Models/API/Management/ManagementHubMessages.cs +++ b/src/Certify.Models/API/Management/ManagementHubMessages.cs @@ -31,6 +31,8 @@ public class ManagementHubCommands public const string UpdateStoredCredential = "UpdateStoredCredential"; public const string DeleteStoredCredential = "DeleteStoredCredential"; + public const string GetDnsZones = "GetDnsZones"; + public const string Reconnect = "Reconnect"; } diff --git a/src/Certify.Models/Providers/ICredentialsManager.cs b/src/Certify.Models/Providers/ICredentialsManager.cs index fd3917649..1888c28bc 100644 --- a/src/Certify.Models/Providers/ICredentialsManager.cs +++ b/src/Certify.Models/Providers/ICredentialsManager.cs @@ -12,7 +12,7 @@ public interface ICredentialsManager bool Init(string connectionString, ILog log); Task IsInitialised(); - Task Delete(IManagedItemStore itemStore, string storageKey); + Task Delete(IManagedItemStore itemStore, string storageKey); Task> GetCredentials(string type = null, string storageKey = null); Task GetCredential(string storageKey); Task GetUnlockedCredential(string storageKey); 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 f74d1eca5..b590ed498 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 @@ -2771,23 +2771,32 @@ public virtual async System.Threading.Tasks.Task RemoveAcmeAccount } /// - /// Fetch list of DNS zones for a given DNS provider and credential + /// Get List of Zones with the current DNS provider and credential [Generated by Certify.SourceGenerators] /// /// OK /// A server side error occurred. - public virtual System.Threading.Tasks.Task> GetDnsZonesAsync(string providerTypeId, string credentialsId) + public virtual System.Threading.Tasks.Task> GetDnsZonesAsync(string instanceId, string providerTypeId, string credentialsId) { - return GetDnsZonesAsync(providerTypeId, credentialsId, System.Threading.CancellationToken.None); + return GetDnsZonesAsync(instanceId, providerTypeId, credentialsId, System.Threading.CancellationToken.None); } /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. /// - /// Fetch list of DNS zones for a given DNS provider and credential + /// Get List of Zones with the current DNS provider and credential [Generated by Certify.SourceGenerators] /// /// OK /// A server side error occurred. - public virtual async System.Threading.Tasks.Task> GetDnsZonesAsync(string providerTypeId, string credentialsId, System.Threading.CancellationToken cancellationToken) + public virtual async System.Threading.Tasks.Task> GetDnsZonesAsync(string instanceId, string providerTypeId, string credentialsId, System.Threading.CancellationToken cancellationToken) { + if (instanceId == null) + throw new System.ArgumentNullException("instanceId"); + + if (providerTypeId == null) + throw new System.ArgumentNullException("providerTypeId"); + + if (credentialsId == null) + throw new System.ArgumentNullException("credentialsId"); + var client_ = _httpClient; var disposeClient_ = false; try @@ -2799,18 +2808,13 @@ public virtual async System.Threading.Tasks.Task RemoveAcmeAccount var urlBuilder_ = new System.Text.StringBuilder(); if (!string.IsNullOrEmpty(_baseUrl)) urlBuilder_.Append(_baseUrl); - // Operation Path: "internal/v1/challengeprovider/dnszones" - urlBuilder_.Append("internal/v1/challengeprovider/dnszones"); - urlBuilder_.Append('?'); - if (providerTypeId != null) - { - urlBuilder_.Append(System.Uri.EscapeDataString("providerTypeId")).Append('=').Append(System.Uri.EscapeDataString(ConvertToString(providerTypeId, System.Globalization.CultureInfo.InvariantCulture))).Append('&'); - } - if (credentialsId != null) - { - urlBuilder_.Append(System.Uri.EscapeDataString("credentialsId")).Append('=').Append(System.Uri.EscapeDataString(ConvertToString(credentialsId, System.Globalization.CultureInfo.InvariantCulture))).Append('&'); - } - urlBuilder_.Length--; + // Operation Path: "internal/v1/challengeprovider/challengeprovider/dnszones/{instanceId}/{providerTypeId}/{credentialsId}" + urlBuilder_.Append("internal/v1/challengeprovider/challengeprovider/dnszones/"); + urlBuilder_.Append(System.Uri.EscapeDataString(ConvertToString(instanceId, System.Globalization.CultureInfo.InvariantCulture))); + urlBuilder_.Append('/'); + urlBuilder_.Append(System.Uri.EscapeDataString(ConvertToString(providerTypeId, System.Globalization.CultureInfo.InvariantCulture))); + urlBuilder_.Append('/'); + urlBuilder_.Append(System.Uri.EscapeDataString(ConvertToString(credentialsId, System.Globalization.CultureInfo.InvariantCulture))); PrepareRequest(client_, request_, urlBuilder_); diff --git a/src/Certify.Server/Certify.Server.Api.Public/Controllers/internal/ChallengeProviderController.cs b/src/Certify.Server/Certify.Server.Api.Public/Controllers/internal/ChallengeProviderController.cs index 5b8002ab9..6c6d1fe6f 100644 --- a/src/Certify.Server/Certify.Server.Api.Public/Controllers/internal/ChallengeProviderController.cs +++ b/src/Certify.Server/Certify.Server.Api.Public/Controllers/internal/ChallengeProviderController.cs @@ -1,5 +1,6 @@ using Certify.Client; using Certify.Models.Providers; +using Certify.Server.Api.Public.Services; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; @@ -17,16 +18,17 @@ public partial class ChallengeProviderController : ApiControllerBase private readonly ILogger _logger; private readonly ICertifyInternalApiClient _client; - + private readonly ManagementAPI _mgmtAPI; /// /// Constructor /// /// /// - public ChallengeProviderController(ILogger logger, ICertifyInternalApiClient client) + public ChallengeProviderController(ILogger logger, ICertifyInternalApiClient client, ManagementAPI mgmtAPI) { _logger = logger; _client = client; + _mgmtAPI = mgmtAPI; } /// @@ -42,20 +44,5 @@ public async Task GetChallengeProviders() var list = await _client.GetChallengeAPIList(); return new OkObjectResult(list); } - - /// - /// Fetch list of DNS zones for a given DNS provider and credential - /// - /// - /// - /// - [HttpGet] - [Route("dnszones")] - [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] - [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(List))] - public async Task> GetDnsZones(string providerTypeId, string credentialsId) - { - return await _client.GetDnsProviderZones(providerTypeId, credentialsId); - } } } diff --git a/src/Certify.Server/Certify.Server.Api.Public/Controllers/internal/HubController.cs b/src/Certify.Server/Certify.Server.Api.Public/Controllers/internal/HubController.cs index 1bf48d2c7..13687ac78 100644 --- a/src/Certify.Server/Certify.Server.Api.Public/Controllers/internal/HubController.cs +++ b/src/Certify.Server/Certify.Server.Api.Public/Controllers/internal/HubController.cs @@ -70,7 +70,7 @@ public async Task GetHubManagedItems(string? instanceId, string? InstanceId = remote.InstanceId, InstanceTitle = instances.FirstOrDefault(i => i.InstanceId == remote.InstanceId)?.Title, Id = i.Id ?? "", - Title = $"[remote] {i.Name}" ?? "", + Title = $"{i.Name}" ?? "", PrimaryIdentifier = i.GetCertificateIdentifiers().FirstOrDefault(p => p.Value == i.RequestConfig.PrimaryDomain) ?? i.GetCertificateIdentifiers().FirstOrDefault(), Identifiers = i.GetCertificateIdentifiers(), DateRenewed = i.DateRenewed, 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 e344bd97d..985875d63 100644 --- a/src/Certify.Server/Certify.Server.Api.Public/Services/ManagementAPI.cs +++ b/src/Certify.Server/Certify.Server.Api.Public/Services/ManagementAPI.cs @@ -221,6 +221,28 @@ public async Task GetManagedCertificateSummary(AuthContext? curre } } + public async Task?> GetDnsZones(string instanceId, string providerTypeId, string credentialsId, AuthContext? currentAuthContext) + { + var args = new KeyValuePair[] { + new("instanceId", instanceId), + new("providerTypeId", providerTypeId), + new("credentialsId", credentialsId) + }; + + var cmd = new InstanceCommandRequest(ManagementHubCommands.GetDnsZones, args); + + var result = await GetCommandResult(instanceId, cmd); + + if (result?.Value != null) + { + return JsonSerializer.Deserialize>(result.Value); + } + else + { + return null; + } + } + public async Task?> GetStoredCredentials(string instanceId, AuthContext? currentAuthContext) { var args = new KeyValuePair[] { @@ -262,7 +284,7 @@ public async Task GetManagedCertificateSummary(AuthContext? curre } } - public async Task DeleteStoredCredential(string instanceId, string storageKey, AuthContext authContext) + public async Task DeleteStoredCredential(string instanceId, string storageKey, AuthContext authContext) { // delete stored credential via management hub @@ -275,7 +297,14 @@ public async Task DeleteStoredCredential(string instanceId, string storage var result = await GetCommandResult(instanceId, cmd); - return result?.ObjectValue as bool? ?? false; + if (result?.Value != null) + { + return JsonSerializer.Deserialize(result.Value); + } + else + { + return null; + } } public async Task GetItemLog(string instanceId, string managedCertId, int maxLines, AuthContext? currentAuthContext) diff --git a/src/Certify.Server/Certify.Server.Core/Certify.Server.Core/Controllers/CredentialsController.cs b/src/Certify.Server/Certify.Server.Core/Certify.Server.Core/Controllers/CredentialsController.cs index 90e53068c..c39f6c821 100644 --- a/src/Certify.Server/Certify.Server.Core/Certify.Server.Core/Controllers/CredentialsController.cs +++ b/src/Certify.Server/Certify.Server.Core/Certify.Server.Core/Controllers/CredentialsController.cs @@ -1,4 +1,4 @@ -using Certify.Management; +using Certify.Management; using Certify.Models.Config; using Microsoft.AspNetCore.Mvc; @@ -33,7 +33,7 @@ public async Task UpdateCredentials(StoredCredential credentia } [HttpDelete, Route("{storageKey}")] - public async Task DeleteCredential(string storageKey) + public async Task DeleteCredential(string storageKey) { DebugLog(); diff --git a/src/Certify.Service/Controllers/CredentialsController.cs b/src/Certify.Service/Controllers/CredentialsController.cs index eb4fe32bc..98e481aed 100644 --- a/src/Certify.Service/Controllers/CredentialsController.cs +++ b/src/Certify.Service/Controllers/CredentialsController.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.Threading.Tasks; using System.Web.Http; using Certify.Management; @@ -34,7 +34,7 @@ public async Task UpdateCredentials(StoredCredential credentia } [HttpDelete, Route("{storageKey}")] - public async Task DeleteCredential(string storageKey) + public async Task DeleteCredential(string storageKey) { DebugLog(); diff --git a/src/Certify.SourceGenerators/ApiMethods.cs b/src/Certify.SourceGenerators/ApiMethods.cs index 42979ae36..77ca3c668 100644 --- a/src/Certify.SourceGenerators/ApiMethods.cs +++ b/src/Certify.SourceGenerators/ApiMethods.cs @@ -1,10 +1,31 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; + using SourceGenerator; namespace Certify.SourceGenerators { internal class ApiMethods { + public static string HttpGet = "HttpGet"; + public static string HttpPost = "HttpPost"; + public static string HttpDelete = "HttpDelete"; + + public static string GetFormattedTypeName(Type type) + { + if (type.IsGenericType) + { + var genericArguments = type.GetGenericArguments() + .Select(x => x.FullName) + .Aggregate((x1, x2) => $"{x1}, {x2}"); + return $"{type.FullName.Substring(0, type.FullName.IndexOf("`"))}" + + $"<{genericArguments}>"; + } + + return type.FullName; + } public static List GetApiDefinitions() { // declaring an API definition here is then used by the source generators to: @@ -189,7 +210,7 @@ public static List GetApiDefinitions() PublicAPIRoute = "credentials/{instanceId}", ServiceAPIRoute = "credentials", ReturnType = "ICollection", - UseManagementAPI = true, + UseManagementAPI = true, Params =new Dictionary{ { "instanceId", "string" } } }, new GeneratedAPI { @@ -213,6 +234,21 @@ public static List GetApiDefinitions() ReturnType = "Models.Config.ActionResult", UseManagementAPI = true, Params =new Dictionary{ { "instanceId", "string" },{ "storageKey", "string" } } + }, + new GeneratedAPI { + OperationName = "GetDnsZones", + OperationMethod = HttpGet, + Comment = "Get List of Zones with the current DNS provider and credential", + PublicAPIController = "ChallengeProvider", + PublicAPIRoute = "challengeprovider/dnszones/{instanceId}/{providerTypeId}/{credentialsId}", + ServiceAPIRoute = "managedcertificates/dnszones/{providerTypeId}/{credentialsId}", + ReturnType = "ICollection", + UseManagementAPI = true, + Params =new Dictionary{ + { "instanceId", "string" } , + { "providerTypeId", "string" }, + { "credentialsId", "string" } + } }, new GeneratedAPI { OperationName = "PerformExport", diff --git a/src/Certify.SourceGenerators/PublicAPISourceGenerator.cs b/src/Certify.SourceGenerators/PublicAPISourceGenerator.cs index e2e7dbf7e..f3f3f99e4 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; @@ -83,17 +83,24 @@ public partial class {config.PublicAPIController}Controller if (context.Compilation.AssemblyName.EndsWith("Certify.Client")) { - - if (config.OperationMethod == "HttpGet") - { - context.AddSource($"{config.PublicAPIController}.{config.OperationName}.ICertifyInternalApiClient.g.cs", SourceText.From($@" + var template = @" using Certify.Models; +using Certify.Models.Config.Migration; +using Certify.Models.Providers; using Certify.Models.Config.AccessControl; using System.Collections.Generic; using System.Threading.Tasks; - namespace Certify.Client - {{ +namespace Certify.Client +{ + MethodTemplate +} +"; + + if (config.OperationMethod == "HttpGet") + { + var source = SourceText.From(template.Replace("MethodTemplate", $@" + public partial interface ICertifyInternalApiClient {{ /// @@ -117,7 +124,8 @@ public partial class CertifyApiClient }} }} - }}", Encoding.UTF8)); + "), Encoding.UTF8); + context.AddSource($"{config.PublicAPIController}.{config.OperationName}.ICertifyInternalApiClient.g.cs", source); } if (config.OperationMethod == "HttpPost") @@ -131,15 +139,9 @@ public partial class CertifyApiClient 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; -using System.Collections.Generic; -using System.Threading.Tasks; - namespace Certify.Client - {{ + context.AddSource($"{config.PublicAPIController}.{config.OperationName}.ICertifyInternalApiClient.g.cs", SourceText.From(template.Replace("MethodTemplate", $@" + public partial interface ICertifyInternalApiClient {{ /// @@ -164,19 +166,13 @@ public partial class CertifyApiClient }} }} - }}", Encoding.UTF8)); + "), Encoding.UTF8)); } if (config.OperationMethod == "HttpDelete") { - context.AddSource($"{config.PublicAPIController}.{config.OperationName}.ICertifyInternalApiClient.g.cs", SourceText.From($@" -using Certify.Models; -using Certify.Models.Config.AccessControl; -using System.Collections.Generic; -using System.Threading.Tasks; + context.AddSource($"{config.PublicAPIController}.{config.OperationName}.ICertifyInternalApiClient.g.cs", SourceText.From(template.Replace("MethodTemplate", $@" - namespace Certify.Client - {{ public partial interface ICertifyInternalApiClient {{ /// @@ -205,7 +201,7 @@ public partial class CertifyApiClient }} }} - }}", Encoding.UTF8)); + "), Encoding.UTF8)); } } @@ -215,6 +211,7 @@ public partial class CertifyApiClient using System.Collections.Generic; using System.Threading.Tasks; using Certify.Models; +using Certify.Models.Providers; using Certify.Models.Config.AccessControl; namespace Certify.UI.Client.Core @@ -237,7 +234,7 @@ public void Initialize(GeneratorInitializationContext context) // then add a watch on if (!Debugger.IsAttached) { - //Debugger.Launch(); + // Debugger.Launch(); } #endif }