Skip to content

Commit eb41b40

Browse files
Implement per instance get/add CA account
1 parent 9c7b66e commit eb41b40

File tree

10 files changed

+160
-38
lines changed

10 files changed

+160
-38
lines changed

src/Certify.Core/Management/CertifyManager/CertifyManager.ManagementHub.cs

+14-1
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ private async Task StartManagementHubConnection(string hubUri)
4848
var instanceInfo = new ManagedInstanceInfo
4949
{
5050
InstanceId = $"{this.InstanceId}",
51-
Title = Environment.MachineName
51+
Title = $"{Environment.MachineName} [{EnvironmentUtil.GetFriendlyOSName()}]",
5252
};
5353

5454
if (_managementServerClient != null)
@@ -162,6 +162,19 @@ private async Task<InstanceCommandResult> _managementServerClient_OnGetCommandRe
162162

163163
val = true;
164164
}
165+
else if (arg.CommandType == ManagementHubCommands.GetAcmeAccounts)
166+
{
167+
val = await GetAccountRegistrations();
168+
}
169+
else if (arg.CommandType == ManagementHubCommands.AddAcmeAccount)
170+
{
171+
172+
var args = JsonSerializer.Deserialize<KeyValuePair<string, string>[]>(arg.Value);
173+
var registrationArg = args.FirstOrDefault(a => a.Key == "registration");
174+
var registration = JsonSerializer.Deserialize<ContactRegistration>(registrationArg.Value);
175+
176+
val = await AddAccount(registration);
177+
}
165178
else if (arg.CommandType == ManagementHubCommands.Reconnect)
166179
{
167180
await _managementServerClient.Disconnect();

src/Certify.Models/API/Management/ManagementHubMessages.cs

+2
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ public class ManagementHubCommands
2424
public const string TestManagedItemConfiguration = "TestManagedItemConfiguration";
2525
public const string PerformManagedItemRequest = "PerformManagedItemRequest";
2626

27+
public const string GetAcmeAccounts = "GetAcmeAccounts";
28+
public const string AddAcmeAccount = "AddAcmeAccount";
2729
public const string Reconnect = "Reconnect";
2830
}
2931

src/Certify.Models/Util/EnvironmentUtil.cs

+41
Original file line numberDiff line numberDiff line change
@@ -114,5 +114,46 @@ public static string CreateAppDataPath(string? subDirectory = null)
114114

115115
return path;
116116
}
117+
118+
public static string GetFriendlyOSName()
119+
{
120+
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
121+
{
122+
return $"{RuntimeInformation.OSDescription}";
123+
}
124+
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
125+
{
126+
var osName = string.Empty;
127+
128+
string filePath = "/etc/os-release";
129+
130+
try
131+
{
132+
using (FileStream fileStream = new FileStream(filePath, FileMode.Open))
133+
{
134+
using (StreamReader reader = new StreamReader(fileStream))
135+
{
136+
string line;
137+
138+
while ((line = reader.ReadLine()) != null)
139+
{
140+
141+
if (line.StartsWith("NAME"))
142+
{
143+
osName = line.Split('\"')[1]; //split the line string by " and get the second slice
144+
return osName;
145+
}
146+
}
147+
}
148+
}
149+
}
150+
catch
151+
{
152+
return $"Linux - {RuntimeInformation.OSDescription}";
153+
}
154+
}
155+
156+
return $"{RuntimeInformation.OSDescription}";
157+
}
117158
}
118159
}

src/Certify.Server/Certify.Server.Api.Public.Client/Certify.API.Public.cs

+18-10
Original file line numberDiff line numberDiff line change
@@ -2242,9 +2242,9 @@ public virtual async System.Threading.Tasks.Task<ManagedCertificate> PerformRene
22422242
/// </summary>
22432243
/// <returns>OK</returns>
22442244
/// <exception cref="ApiException">A server side error occurred.</exception>
2245-
public virtual System.Threading.Tasks.Task<System.Collections.Generic.ICollection<AccountDetails>> GetAcmeAccountsAsync()
2245+
public virtual System.Threading.Tasks.Task<System.Collections.Generic.ICollection<AccountDetails>> GetAcmeAccountsAsync(string instanceId)
22462246
{
2247-
return GetAcmeAccountsAsync(System.Threading.CancellationToken.None);
2247+
return GetAcmeAccountsAsync(instanceId, System.Threading.CancellationToken.None);
22482248
}
22492249

22502250
/// <param name="cancellationToken">A cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
@@ -2253,8 +2253,11 @@ public virtual async System.Threading.Tasks.Task<ManagedCertificate> PerformRene
22532253
/// </summary>
22542254
/// <returns>OK</returns>
22552255
/// <exception cref="ApiException">A server side error occurred.</exception>
2256-
public virtual async System.Threading.Tasks.Task<System.Collections.Generic.ICollection<AccountDetails>> GetAcmeAccountsAsync(System.Threading.CancellationToken cancellationToken)
2256+
public virtual async System.Threading.Tasks.Task<System.Collections.Generic.ICollection<AccountDetails>> GetAcmeAccountsAsync(string instanceId, System.Threading.CancellationToken cancellationToken)
22572257
{
2258+
if (instanceId == null)
2259+
throw new System.ArgumentNullException("instanceId");
2260+
22582261
var client_ = _httpClient;
22592262
var disposeClient_ = false;
22602263
try
@@ -2266,8 +2269,9 @@ public virtual async System.Threading.Tasks.Task<ManagedCertificate> PerformRene
22662269

22672270
var urlBuilder_ = new System.Text.StringBuilder();
22682271
if (!string.IsNullOrEmpty(_baseUrl)) urlBuilder_.Append(_baseUrl);
2269-
// Operation Path: "internal/v1/certificateauthority/accounts"
2270-
urlBuilder_.Append("internal/v1/certificateauthority/accounts");
2272+
// Operation Path: "internal/v1/certificateauthority/accounts/{instanceId}"
2273+
urlBuilder_.Append("internal/v1/certificateauthority/accounts/");
2274+
urlBuilder_.Append(System.Uri.EscapeDataString(ConvertToString(instanceId, System.Globalization.CultureInfo.InvariantCulture)));
22712275

22722276
PrepareRequest(client_, request_, urlBuilder_);
22732277

@@ -2326,9 +2330,9 @@ public virtual async System.Threading.Tasks.Task<ManagedCertificate> PerformRene
23262330
/// </summary>
23272331
/// <returns>OK</returns>
23282332
/// <exception cref="ApiException">A server side error occurred.</exception>
2329-
public virtual System.Threading.Tasks.Task<ActionResult> AddAcmeAccountAsync(ContactRegistration body)
2333+
public virtual System.Threading.Tasks.Task<ActionResult> AddAcmeAccountAsync(string instanceId, ContactRegistration body)
23302334
{
2331-
return AddAcmeAccountAsync(body, System.Threading.CancellationToken.None);
2335+
return AddAcmeAccountAsync(instanceId, body, System.Threading.CancellationToken.None);
23322336
}
23332337

23342338
/// <param name="cancellationToken">A cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
@@ -2337,8 +2341,11 @@ public virtual System.Threading.Tasks.Task<ActionResult> AddAcmeAccountAsync(Con
23372341
/// </summary>
23382342
/// <returns>OK</returns>
23392343
/// <exception cref="ApiException">A server side error occurred.</exception>
2340-
public virtual async System.Threading.Tasks.Task<ActionResult> AddAcmeAccountAsync(ContactRegistration body, System.Threading.CancellationToken cancellationToken)
2344+
public virtual async System.Threading.Tasks.Task<ActionResult> AddAcmeAccountAsync(string instanceId, ContactRegistration body, System.Threading.CancellationToken cancellationToken)
23412345
{
2346+
if (instanceId == null)
2347+
throw new System.ArgumentNullException("instanceId");
2348+
23422349
var client_ = _httpClient;
23432350
var disposeClient_ = false;
23442351
try
@@ -2354,8 +2361,9 @@ public virtual async System.Threading.Tasks.Task<ActionResult> AddAcmeAccountAsy
23542361

23552362
var urlBuilder_ = new System.Text.StringBuilder();
23562363
if (!string.IsNullOrEmpty(_baseUrl)) urlBuilder_.Append(_baseUrl);
2357-
// Operation Path: "internal/v1/certificateauthority/account"
2358-
urlBuilder_.Append("internal/v1/certificateauthority/account");
2364+
// Operation Path: "internal/v1/certificateauthority/account/{instanceId}"
2365+
urlBuilder_.Append("internal/v1/certificateauthority/account/");
2366+
urlBuilder_.Append(System.Uri.EscapeDataString(ConvertToString(instanceId, System.Globalization.CultureInfo.InvariantCulture)));
23592367

23602368
PrepareRequest(client_, request_, urlBuilder_);
23612369

src/Certify.Server/Certify.Server.Api.Public/Controllers/internal/CertificateAuthorityController.cs

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using Certify.Client;
2+
using Certify.Server.Api.Public.Services;
23
using Microsoft.AspNetCore.Authentication.JwtBearer;
34
using Microsoft.AspNetCore.Authorization;
45
using Microsoft.AspNetCore.Mvc;
@@ -16,16 +17,18 @@ public partial class CertificateAuthorityController : ApiControllerBase
1617
private readonly ILogger<CertificateAuthorityController> _logger;
1718

1819
private readonly ICertifyInternalApiClient _client;
20+
private readonly ManagementAPI _mgmtAPI;
1921

2022
/// <summary>
2123
/// Constructor
2224
/// </summary>
2325
/// <param name="logger"></param>
2426
/// <param name="client"></param>
25-
public CertificateAuthorityController(ILogger<CertificateAuthorityController> logger, ICertifyInternalApiClient client)
27+
public CertificateAuthorityController(ILogger<CertificateAuthorityController> logger, ICertifyInternalApiClient client, ManagementAPI mgmtApi)
2628
{
2729
_logger = logger;
2830
_client = client;
31+
_mgmtAPI = mgmtApi;
2932
}
3033

3134
/// <summary>

src/Certify.Server/Certify.Server.Api.Public/Services/ManagementAPI.cs

+43-1
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,14 @@
33
using Certify.Client;
44
using Certify.Models;
55
using Certify.Models.API;
6+
using Certify.Models.Config;
67
using Certify.Models.Reporting;
78
using Certify.Server.Api.Public.SignalR.ManagementHub;
89
using Microsoft.AspNetCore.SignalR;
910

1011
namespace Certify.Server.Api.Public.Services
1112
{
12-
public class ManagementAPI
13+
public partial class ManagementAPI
1314
{
1415
IInstanceManagementStateProvider _mgmtStateProvider;
1516
IHubContext<InstanceManagementHub, IInstanceManagementHub> _mgmtHubContext;
@@ -163,6 +164,47 @@ public async Task<StatusSummary> GetManagedCertificateSummary(AuthContext? curre
163164
return await Task.FromResult(sum);
164165
}
165166

167+
public async Task<ICollection<Models.AccountDetails>?> GetAcmeAccounts(string instanceId, AuthContext? currentAuthContext)
168+
{
169+
var args = new KeyValuePair<string, string>[] {
170+
new("instanceId", instanceId)
171+
};
172+
173+
var cmd = new InstanceCommandRequest(ManagementHubCommands.GetAcmeAccounts, args);
174+
175+
var result = await GetCommandResult(instanceId, cmd);
176+
177+
if (result?.Value != null)
178+
{
179+
return JsonSerializer.Deserialize<ICollection<Models.AccountDetails>>(result.Value);
180+
}
181+
else
182+
{
183+
return null;
184+
}
185+
}
186+
187+
public async Task<ActionResult?> AddAcmeAccount(string instanceId, ContactRegistration registration, AuthContext? currentAuthContext)
188+
{
189+
var args = new KeyValuePair<string, string>[] {
190+
new("instanceId", instanceId) ,
191+
new("registration", JsonSerializer.Serialize(registration))
192+
};
193+
194+
var cmd = new InstanceCommandRequest(ManagementHubCommands.AddAcmeAccount, args);
195+
196+
var result = await GetCommandResult(instanceId, cmd);
197+
198+
if (result?.Value != null)
199+
{
200+
return JsonSerializer.Deserialize<ActionResult>(result.Value);
201+
}
202+
else
203+
{
204+
return null;
205+
}
206+
}
207+
166208
public async Task<LogItem[]> GetItemLog(string instanceId, string managedCertId, int maxLines, AuthContext? currentAuthContext)
167209
{
168210
var args = new KeyValuePair<string, string>[] {

src/Certify.Server/Certify.Server.Api.Public/Startup.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ public void ConfigureServices(IServiceCollection services)
140140

141141
});
142142

143-
// connect to certify service
143+
// connect to primary certify service
144144
var configManager = new ServiceConfigManager();
145145
var serviceConfig = configManager.GetServiceConfig();
146146

@@ -245,7 +245,7 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
245245
}
246246

247247
/// <summary>
248-
/// Connect to status stream of backend service
248+
/// Connect to status stream of primary service
249249
/// </summary>
250250
/// <param name="app"></param>
251251
/// <returns></returns>

src/Certify.Shared/Utils/Util.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ public static async Task<List<ActionResult>> PerformAppDiagnostics(bool includeT
161161
public static string GetUserAgent()
162162
{
163163
var versionName = "Certify/" + GetAppVersion().ToString();
164-
return $"{versionName} (Windows; {Environment.OSVersion}) ";
164+
return $"{versionName} ({RuntimeInformation.OSDescription}; {Environment.OSVersion}) ";
165165
}
166166

167167
public static Version GetAppVersion()

src/Certify.SourceGenerators/ApiMethods.cs

+21-18
Original file line numberDiff line numberDiff line change
@@ -125,26 +125,28 @@ public static List<GeneratedAPI> GetApiDefinitions()
125125
ReturnType = "Models.Config.ActionResult",
126126
Params = new Dictionary<string, string>{{"id","string"}}
127127
},
128+
/* per instance API, via management hub */
128129
new GeneratedAPI {
129-
130130
OperationName = "GetAcmeAccounts",
131131
OperationMethod = "HttpGet",
132132
Comment = "Get All Acme Accounts",
133+
UseManagementAPI = true,
133134
PublicAPIController = "CertificateAuthority",
134-
PublicAPIRoute = "accounts",
135+
PublicAPIRoute = "accounts/{instanceId}",
135136
ServiceAPIRoute = "accounts",
136-
ReturnType = "ICollection<Models.AccountDetails>"
137+
ReturnType = "ICollection<Models.AccountDetails>",
138+
Params =new Dictionary<string, string>{ { "instanceId", "string" } }
137139
},
138140
new GeneratedAPI {
139-
140141
OperationName = "AddAcmeAccount",
141142
OperationMethod = "HttpPost",
142143
Comment = "Add New Acme Account",
144+
UseManagementAPI = true,
143145
PublicAPIController = "CertificateAuthority",
144-
PublicAPIRoute = "account",
146+
PublicAPIRoute = "account/{instanceId}",
145147
ServiceAPIRoute = "accounts",
146148
ReturnType = "Models.Config.ActionResult",
147-
Params =new Dictionary<string, string>{{"registration", "Certify.Models.ContactRegistration" } }
149+
Params =new Dictionary<string, string>{ { "instanceId", "string" },{ "registration", "Certify.Models.ContactRegistration" } }
148150
},
149151
new GeneratedAPI {
150152

@@ -157,18 +159,7 @@ public static List<GeneratedAPI> GetApiDefinitions()
157159
ReturnType = "Models.Config.ActionResult",
158160
Params =new Dictionary<string, string>{{ "certificateAuthority", "Certify.Models.CertificateAuthority" } }
159161
},
160-
new GeneratedAPI {
161162

162-
OperationName = "RemoveManagedCertificate",
163-
OperationMethod = "HttpDelete",
164-
Comment = "Remove Managed Certificate",
165-
PublicAPIController = "Certificate",
166-
PublicAPIRoute = "settings/{instanceId}/{managedCertId}",
167-
UseManagementAPI = true,
168-
ServiceAPIRoute = "managedcertificates/delete/{managedCertId}",
169-
ReturnType = "bool",
170-
Params =new Dictionary<string, string>{ { "instanceId", "string" },{ "managedCertId", "string" } }
171-
},
172163
new GeneratedAPI {
173164

174165
OperationName = "RemoveCertificateAuthority",
@@ -219,7 +210,19 @@ public static List<GeneratedAPI> GetApiDefinitions()
219210
ServiceAPIRoute = "system/migration/import",
220211
ReturnType = "ICollection<ActionStep>",
221212
Params =new Dictionary<string, string>{{ "importRequest", "Certify.Models.Config.Migration.ImportRequest" } }
222-
}
213+
},
214+
new GeneratedAPI {
215+
216+
OperationName = "RemoveManagedCertificate",
217+
OperationMethod = "HttpDelete",
218+
Comment = "Remove Managed Certificate",
219+
PublicAPIController = "Certificate",
220+
PublicAPIRoute = "settings/{instanceId}/{managedCertId}",
221+
UseManagementAPI = true,
222+
ServiceAPIRoute = "managedcertificates/delete/{managedCertId}",
223+
ReturnType = "bool",
224+
Params =new Dictionary<string, string>{ { "instanceId", "string" },{ "managedCertId", "string" } }
225+
},
223226
};
224227
}
225228
}

src/Certify.SourceGenerators/PublicAPISourceGenerator.cs

+14-4
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using System.Collections.Generic;
1+
using System.Collections.Generic;
22
using System.Diagnostics;
33
using System.Linq;
44
using System.Text;
@@ -122,6 +122,16 @@ public partial class CertifyApiClient
122122

123123
if (config.OperationMethod == "HttpPost")
124124
{
125+
var postAPIRoute = config.ServiceAPIRoute;
126+
var postApiCall = apiParamCall;
127+
var postApiParamDecl = apiParamDecl;
128+
129+
if (config.UseManagementAPI)
130+
{
131+
postApiCall = apiParamCall.Replace("instanceId,", "");
132+
postApiParamDecl = apiParamDecl.Replace("string instanceId,", "");
133+
}
134+
125135
context.AddSource($"{config.PublicAPIController}.{config.OperationName}.ICertifyInternalApiClient.g.cs", SourceText.From($@"
126136
using Certify.Models;
127137
using Certify.Models.Config.AccessControl;
@@ -136,7 +146,7 @@ public partial interface ICertifyInternalApiClient
136146
/// {config.Comment} [Generated by Certify.SourceGenerators]
137147
/// </summary>
138148
/// <returns></returns>
139-
Task<{config.ReturnType}> {config.OperationName}({apiParamDecl});
149+
Task<{config.ReturnType}> {config.OperationName}({postApiParamDecl});
140150
141151
}}
142152
@@ -147,9 +157,9 @@ public partial class CertifyApiClient
147157
/// {config.Comment} [Generated by Certify.SourceGenerators]
148158
/// </summary>
149159
/// <returns></returns>
150-
public async Task<{config.ReturnType}> {config.OperationName}({apiParamDecl})
160+
public async Task<{config.ReturnType}> {config.OperationName}({postApiParamDecl})
151161
{{
152-
var result = await PostAsync($""{config.ServiceAPIRoute}"", {apiParamCall});
162+
var result = await PostAsync($""{postAPIRoute}"", {postApiCall});
153163
return JsonToObject<{config.ReturnType}>(await result.Content.ReadAsStringAsync());
154164
}}
155165

0 commit comments

Comments
 (0)