Skip to content

Commit

Permalink
Implement per instance get/add CA account
Browse files Browse the repository at this point in the history
  • Loading branch information
webprofusion-chrisc committed Oct 4, 2024
1 parent 9c7b66e commit eb41b40
Show file tree
Hide file tree
Showing 10 changed files with 160 additions and 38 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -162,6 +162,19 @@ private async Task<InstanceCommandResult> _managementServerClient_OnGetCommandRe

val = true;
}
else if (arg.CommandType == ManagementHubCommands.GetAcmeAccounts)
{
val = await GetAccountRegistrations();
}
else if (arg.CommandType == ManagementHubCommands.AddAcmeAccount)
{

var args = JsonSerializer.Deserialize<KeyValuePair<string, string>[]>(arg.Value);
var registrationArg = args.FirstOrDefault(a => a.Key == "registration");
var registration = JsonSerializer.Deserialize<ContactRegistration>(registrationArg.Value);

val = await AddAccount(registration);
}
else if (arg.CommandType == ManagementHubCommands.Reconnect)
{
await _managementServerClient.Disconnect();
Expand Down
2 changes: 2 additions & 0 deletions src/Certify.Models/API/Management/ManagementHubMessages.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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";
}

Expand Down
41 changes: 41 additions & 0 deletions src/Certify.Models/Util/EnvironmentUtil.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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}";
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2242,9 +2242,9 @@ public virtual async System.Threading.Tasks.Task<ManagedCertificate> PerformRene
/// </summary>
/// <returns>OK</returns>
/// <exception cref="ApiException">A server side error occurred.</exception>
public virtual System.Threading.Tasks.Task<System.Collections.Generic.ICollection<AccountDetails>> GetAcmeAccountsAsync()
public virtual System.Threading.Tasks.Task<System.Collections.Generic.ICollection<AccountDetails>> GetAcmeAccountsAsync(string instanceId)
{
return GetAcmeAccountsAsync(System.Threading.CancellationToken.None);
return GetAcmeAccountsAsync(instanceId, System.Threading.CancellationToken.None);
}

/// <param name="cancellationToken">A cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
Expand All @@ -2253,8 +2253,11 @@ public virtual async System.Threading.Tasks.Task<ManagedCertificate> PerformRene
/// </summary>
/// <returns>OK</returns>
/// <exception cref="ApiException">A server side error occurred.</exception>
public virtual async System.Threading.Tasks.Task<System.Collections.Generic.ICollection<AccountDetails>> GetAcmeAccountsAsync(System.Threading.CancellationToken cancellationToken)
public virtual async System.Threading.Tasks.Task<System.Collections.Generic.ICollection<AccountDetails>> GetAcmeAccountsAsync(string instanceId, System.Threading.CancellationToken cancellationToken)
{
if (instanceId == null)
throw new System.ArgumentNullException("instanceId");

var client_ = _httpClient;
var disposeClient_ = false;
try
Expand All @@ -2266,8 +2269,9 @@ public virtual async System.Threading.Tasks.Task<ManagedCertificate> 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_);

Expand Down Expand Up @@ -2326,9 +2330,9 @@ public virtual async System.Threading.Tasks.Task<ManagedCertificate> PerformRene
/// </summary>
/// <returns>OK</returns>
/// <exception cref="ApiException">A server side error occurred.</exception>
public virtual System.Threading.Tasks.Task<ActionResult> AddAcmeAccountAsync(ContactRegistration body)
public virtual System.Threading.Tasks.Task<ActionResult> AddAcmeAccountAsync(string instanceId, ContactRegistration body)
{
return AddAcmeAccountAsync(body, System.Threading.CancellationToken.None);
return AddAcmeAccountAsync(instanceId, body, System.Threading.CancellationToken.None);
}

/// <param name="cancellationToken">A cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
Expand All @@ -2337,8 +2341,11 @@ public virtual System.Threading.Tasks.Task<ActionResult> AddAcmeAccountAsync(Con
/// </summary>
/// <returns>OK</returns>
/// <exception cref="ApiException">A server side error occurred.</exception>
public virtual async System.Threading.Tasks.Task<ActionResult> AddAcmeAccountAsync(ContactRegistration body, System.Threading.CancellationToken cancellationToken)
public virtual async System.Threading.Tasks.Task<ActionResult> AddAcmeAccountAsync(string instanceId, ContactRegistration body, System.Threading.CancellationToken cancellationToken)
{
if (instanceId == null)
throw new System.ArgumentNullException("instanceId");

var client_ = _httpClient;
var disposeClient_ = false;
try
Expand All @@ -2354,8 +2361,9 @@ public virtual async System.Threading.Tasks.Task<ActionResult> 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_);

Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -16,16 +17,18 @@ public partial class CertificateAuthorityController : ApiControllerBase
private readonly ILogger<CertificateAuthorityController> _logger;

private readonly ICertifyInternalApiClient _client;
private readonly ManagementAPI _mgmtAPI;

/// <summary>
/// Constructor
/// </summary>
/// <param name="logger"></param>
/// <param name="client"></param>
public CertificateAuthorityController(ILogger<CertificateAuthorityController> logger, ICertifyInternalApiClient client)
public CertificateAuthorityController(ILogger<CertificateAuthorityController> logger, ICertifyInternalApiClient client, ManagementAPI mgmtApi)
{
_logger = logger;
_client = client;
_mgmtAPI = mgmtApi;
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<InstanceManagementHub, IInstanceManagementHub> _mgmtHubContext;
Expand Down Expand Up @@ -163,6 +164,47 @@ public async Task<StatusSummary> GetManagedCertificateSummary(AuthContext? curre
return await Task.FromResult(sum);
}

public async Task<ICollection<Models.AccountDetails>?> GetAcmeAccounts(string instanceId, AuthContext? currentAuthContext)
{
var args = new KeyValuePair<string, string>[] {
new("instanceId", instanceId)
};

var cmd = new InstanceCommandRequest(ManagementHubCommands.GetAcmeAccounts, args);

var result = await GetCommandResult(instanceId, cmd);

if (result?.Value != null)
{
return JsonSerializer.Deserialize<ICollection<Models.AccountDetails>>(result.Value);
}
else
{
return null;
}
}

public async Task<ActionResult?> AddAcmeAccount(string instanceId, ContactRegistration registration, AuthContext? currentAuthContext)
{
var args = new KeyValuePair<string, string>[] {
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<ActionResult>(result.Value);
}
else
{
return null;
}
}

public async Task<LogItem[]> GetItemLog(string instanceId, string managedCertId, int maxLines, AuthContext? currentAuthContext)
{
var args = new KeyValuePair<string, string>[] {
Expand Down
4 changes: 2 additions & 2 deletions src/Certify.Server/Certify.Server.Api.Public/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand Down Expand Up @@ -245,7 +245,7 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
}

/// <summary>
/// Connect to status stream of backend service
/// Connect to status stream of primary service
/// </summary>
/// <param name="app"></param>
/// <returns></returns>
Expand Down
2 changes: 1 addition & 1 deletion src/Certify.Shared/Utils/Util.cs
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ public static async Task<List<ActionResult>> 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()
Expand Down
39 changes: 21 additions & 18 deletions src/Certify.SourceGenerators/ApiMethods.cs
Original file line number Diff line number Diff line change
Expand Up @@ -125,26 +125,28 @@ public static List<GeneratedAPI> GetApiDefinitions()
ReturnType = "Models.Config.ActionResult",
Params = new Dictionary<string, string>{{"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<Models.AccountDetails>"
ReturnType = "ICollection<Models.AccountDetails>",
Params =new Dictionary<string, string>{ { "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<string, string>{{"registration", "Certify.Models.ContactRegistration" } }
Params =new Dictionary<string, string>{ { "instanceId", "string" },{ "registration", "Certify.Models.ContactRegistration" } }
},
new GeneratedAPI {

Expand All @@ -157,18 +159,7 @@ public static List<GeneratedAPI> GetApiDefinitions()
ReturnType = "Models.Config.ActionResult",
Params =new Dictionary<string, string>{{ "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<string, string>{ { "instanceId", "string" },{ "managedCertId", "string" } }
},
new GeneratedAPI {

OperationName = "RemoveCertificateAuthority",
Expand Down Expand Up @@ -219,7 +210,19 @@ public static List<GeneratedAPI> GetApiDefinitions()
ServiceAPIRoute = "system/migration/import",
ReturnType = "ICollection<ActionStep>",
Params =new Dictionary<string, string>{{ "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<string, string>{ { "instanceId", "string" },{ "managedCertId", "string" } }
},
};
}
}
Expand Down
18 changes: 14 additions & 4 deletions src/Certify.SourceGenerators/PublicAPISourceGenerator.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System.Collections.Generic;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
Expand Down Expand Up @@ -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;
Expand All @@ -136,7 +146,7 @@ public partial interface ICertifyInternalApiClient
/// {config.Comment} [Generated by Certify.SourceGenerators]
/// </summary>
/// <returns></returns>
Task<{config.ReturnType}> {config.OperationName}({apiParamDecl});
Task<{config.ReturnType}> {config.OperationName}({postApiParamDecl});
}}
Expand All @@ -147,9 +157,9 @@ public partial class CertifyApiClient
/// {config.Comment} [Generated by Certify.SourceGenerators]
/// </summary>
/// <returns></returns>
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());
}}
Expand Down

0 comments on commit eb41b40

Please sign in to comment.