Skip to content

Commit 7db1973

Browse files
authored
Merge c21c2e7 into bcd1ce2
2 parents bcd1ce2 + c21c2e7 commit 7db1973

7 files changed

Lines changed: 106 additions & 43 deletions

File tree

.github/workflows/tests.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,11 @@ jobs:
1111
- name: Checkout code
1212
uses: actions/checkout@v4
1313

14-
# Setup dotnet 6.0
14+
# Setup dotnet 8.0
1515
# https://github.com/actions/setup-dotnet
1616
- uses: actions/setup-dotnet@v3
1717
with:
18-
dotnet-version: '6.x'
18+
dotnet-version: '8.x'
1919

2020
# Setup nuget
2121
- name: Setup build environment

CHANGELOG.md

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
1-
- 1.0.0
2-
- First production release of the GoDaddy AnyCA Gateway REST plugin that implements:
3-
- CA Sync
4-
- Download all issued certificates
5-
- Certificate enrollment for all published GoDaddy Certificate SKUs
6-
- Support certificate enrollment (new keys/certificate)
7-
- Support certificate renewal (extend the life of a previously issued certificate with the same or different domain names)
8-
- Support certificate re-issuance (new public/private keys with the same or different domain names)
9-
- Certificate revocation
10-
- Request revocation of a previously issued certificate
1+
# v1.2.0
2+
- Add special condition to handle status 409 when downloading certificates from GoDaddy. 409 indicates that the certificate state does not allow download.
113

12-
- 1.1.0
4+
# v1.1.0
135
- chore(docs): Upgrade GitHub Actions to use Bootstrap Workflow v3 to support Doctool
146

7+
# v1.0.0
8+
- First production release of the GoDaddy AnyCA Gateway REST plugin that implements:
9+
- CA Sync
10+
- Download all issued certificates
11+
- Certificate enrollment for all published GoDaddy Certificate SKUs
12+
- Support certificate enrollment (new keys/certificate)
13+
- Support certificate renewal (extend the life of a previously issued certificate with the same or different domain names)
14+
- Support certificate re-issuance (new public/private keys with the same or different domain names)
15+
- Certificate revocation
16+
- Request revocation of a previously issued certificate
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// Copyright 2024 Keyfactor
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
using System;
16+
17+
public class DownloadNotAllowed : Exception
18+
{
19+
public DownloadNotAllowed()
20+
{
21+
}
22+
23+
public DownloadNotAllowed(string message)
24+
: base(message)
25+
{
26+
}
27+
28+
public DownloadNotAllowed(string message, Exception inner)
29+
: base(message, inner)
30+
{
31+
}
32+
}
33+

GoDaddy/Client/GoDaddyClient.cs

Lines changed: 51 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -31,23 +31,27 @@
3131

3232
namespace Keyfactor.Extensions.CAPlugin.GoDaddy.Client;
3333

34-
public class GoDaddyAuthenticator : AuthenticatorBase {
34+
public class GoDaddyAuthenticator : AuthenticatorBase
35+
{
3536
readonly string _baseUrl;
3637
readonly string _apiKey;
3738
readonly string _apiSecret;
3839

39-
public GoDaddyAuthenticator(string apiToken, string apiSecret) : base("") {
40+
public GoDaddyAuthenticator(string apiToken, string apiSecret) : base("")
41+
{
4042
_apiKey = apiToken;
4143
_apiSecret = apiSecret;
4244
}
4345

44-
protected override ValueTask<RestSharp.Parameter> GetAuthenticationParameter(string accessToken) {
46+
protected override ValueTask<RestSharp.Parameter> GetAuthenticationParameter(string accessToken)
47+
{
4548
var parameter = new HeaderParameter(KnownHeaders.Authorization, $"sso-key {_apiKey}:{_apiSecret}");
4649
return new ValueTask<RestSharp.Parameter>(parameter);
4750
}
4851
}
4952

50-
public class GoDaddyClient : IGoDaddyClient, IDisposable {
53+
public class GoDaddyClient : IGoDaddyClient, IDisposable
54+
{
5155
private ILogger _logger;
5256
readonly RestClient _client;
5357

@@ -67,15 +71,15 @@ public class Builder : IGoDaddyClientBuilder
6771
private string _apiKey { get; set; }
6872
private string _apiSecret { get; set; }
6973
private string _shopperId { get; set; }
70-
74+
7175
public IGoDaddyClientBuilder WithApiKey(string apiToken)
7276
{
7377
_apiKey = apiToken;
7478
return this;
7579
}
7680

7781
public IGoDaddyClientBuilder WithApiSecret(string apiSecret)
78-
{
82+
{
7983
_apiSecret = apiSecret;
8084
return this;
8185
}
@@ -86,7 +90,8 @@ public IGoDaddyClientBuilder WithBaseUrl(string baseUrl)
8690
return this;
8791
}
8892

89-
public IGoDaddyClientBuilder WithShopperId(string shopperId) {
93+
public IGoDaddyClientBuilder WithShopperId(string shopperId)
94+
{
9095
_shopperId = shopperId;
9196
return this;
9297
}
@@ -99,12 +104,14 @@ public IGoDaddyClient Build()
99104
}
100105
}
101106

102-
public GoDaddyClient(string apiUrl, string apiKey, string apiSecret, string shopperId) {
107+
public GoDaddyClient(string apiUrl, string apiKey, string apiSecret, string shopperId)
108+
{
103109
_logger = LogHandler.GetClassLogger<GoDaddyClient>();
104110

105111
_logger.LogDebug($"Creating GoDaddyClient with API URL: {apiUrl}, API Key: {apiKey}, Shopper ID: {shopperId}");
106112

107-
var options = new RestClientOptions(apiUrl){
113+
var options = new RestClientOptions(apiUrl)
114+
{
108115
Authenticator = new GoDaddyAuthenticator(apiKey, apiSecret),
109116
};
110117

@@ -154,7 +161,7 @@ public async Task Ping()
154161
_logger.LogDebug("Validating GoDaddy API connection");
155162

156163
string path = $"/v1/shoppers/{_shopperId}";
157-
IDictionary <string, string> query = new Dictionary<string, string> {
164+
IDictionary<string, string> query = new Dictionary<string, string> {
158165
{ "includes", "customerId" }
159166
};
160167

@@ -170,22 +177,25 @@ public async Task Ping()
170177
}
171178
}
172179

173-
private string GetCustomerId() {
180+
private string GetCustomerId()
181+
{
174182
EnsureClientIsEnabled();
175-
if (string.IsNullOrEmpty(_shopperId)) {
183+
if (string.IsNullOrEmpty(_shopperId))
184+
{
176185
_logger.LogError("Shopper ID is required to get customer ID");
177186
throw new ArgumentNullException(nameof(_shopperId));
178187
}
179188

180-
if (!string.IsNullOrEmpty(_customerId)) {
189+
if (!string.IsNullOrEmpty(_customerId))
190+
{
181191
_logger.LogTrace($"Returning cached customer ID: {_customerId}");
182192
return _customerId;
183193
}
184194

185195
_logger.LogDebug($"Getting customer ID for shopper ID: {_shopperId}");
186196

187197
string path = $"/v1/shoppers/{_shopperId}";
188-
IDictionary <string, string> query = new Dictionary<string, string> {
198+
IDictionary<string, string> query = new Dictionary<string, string> {
189199
{ "includes", "customerId" }
190200
};
191201

@@ -196,7 +206,8 @@ private string GetCustomerId() {
196206
return _customerId;
197207
}
198208

199-
public async Task<AnyCAPluginCertificate> DownloadCertificate(string certificateId) {
209+
public async Task<AnyCAPluginCertificate> DownloadCertificate(string certificateId)
210+
{
200211
EnsureClientIsEnabled();
201212
_logger.LogDebug($"Downloading certificate with ID: {certificateId}");
202213

@@ -214,8 +225,9 @@ public async Task<AnyCAPluginCertificate> DownloadCertificate(string certificate
214225
RevocationDate = details.revokedAt
215226
};
216227
}
217-
218-
public async Task<string> DownloadCertificatePem(string certificateId) {
228+
229+
public async Task<string> DownloadCertificatePem(string certificateId)
230+
{
219231
EnsureClientIsEnabled();
220232
_logger.LogDebug($"Downloading certificate with ID: {certificateId}");
221233

@@ -267,7 +279,7 @@ public async Task<int> DownloadAllIssuedCertificates(BlockingCollection<AnyCAPlu
267279
foreach (CertificateDetail certificateDetail in certificatesWithPagination.certificates)
268280
{
269281
string debugMessage = "Downloading certificate ";
270-
if (!string.IsNullOrEmpty(certificateDetail.commonName))
282+
if (!string.IsNullOrEmpty(certificateDetail.commonName))
271283
debugMessage += $"with CN {certificateDetail.commonName} ";
272284
if (!string.IsNullOrEmpty(certificateDetail.validStartAt))
273285
debugMessage += $"[issued at {certificateDetail.validStartAt}] ";
@@ -277,7 +289,17 @@ public async Task<int> DownloadAllIssuedCertificates(BlockingCollection<AnyCAPlu
277289
debugMessage += $"[revoked at {certificateDetail.revokedAt}]";
278290
_logger.LogDebug(debugMessage);
279291

280-
string certificatePemString = await DownloadCertificatePem(certificateDetail.certificateId);
292+
string certificatePemString;
293+
try
294+
{
295+
certificatePemString = await DownloadCertificatePem(certificateDetail.certificateId);
296+
}
297+
catch (DownloadNotAllowed)
298+
{
299+
_logger.LogWarning($"Certificate with request ID {certificateDetail.certificateId} cannot be downloaded (status 409)");
300+
continue;
301+
}
302+
281303
certificatesBuffer.Add(new AnyCAPluginCertificate()
282304
{
283305
CARequestID = certificateDetail.certificateId,
@@ -538,6 +560,11 @@ public async Task<TResponse> GetAsync<TResponse>(string endpoint, IDictionary<st
538560
}
539561

540562
_logger.LogError($"Received response with unexpected status code [{response.StatusCode}]");
563+
if (response.StatusCode == HttpStatusCode.Conflict)
564+
{
565+
throw new DownloadNotAllowed($"Conflict error occurred: {response.Content}");
566+
}
567+
541568
if (response.Content == null)
542569
{
543570
throw new Exception("Response was not successful and no content was returned.");
@@ -552,7 +579,7 @@ public async Task<TResponse> GetAsync<TResponse>(string endpoint, IDictionary<st
552579
try
553580
{
554581
_logger.LogTrace("Serializing response content to error object");
555-
582+
556583
JsonSerializerOptions jsonSerializerOptions = new JsonSerializerOptions
557584
{
558585
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
@@ -589,7 +616,7 @@ public async Task<TResponse> GetAsync<TResponse>(string endpoint, IDictionary<st
589616

590617
throw new Exception("Failed to GET request after all retries");
591618
}
592-
619+
593620
public async Task<TResponse> PostAsync<TRequest, TResponse>(string endpoint, TRequest body, IDictionary<string, string> query = null)
594621
where TRequest : class
595622
where TResponse : class
@@ -656,7 +683,7 @@ public async Task<TResponse> PostAsync<TRequest, TResponse>(string endpoint, TRe
656683
try
657684
{
658685
_logger.LogTrace("Serializing response content to error object");
659-
686+
660687
JsonSerializerOptions jsonSerializerOptions = new JsonSerializerOptions
661688
{
662689
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
@@ -699,7 +726,8 @@ private void EnsureClientIsEnabled()
699726

700727
record GoDaddySingleObject<T>(T Data);
701728

702-
public void Dispose() {
729+
public void Dispose()
730+
{
703731
_client?.Dispose();
704732
GC.SuppressFinalize(this);
705733
}

GoDaddy/GoDaddyCAPluginBuilder.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@ public GoDaddyCAPluginBuilder<TBuilder> WithConfigProvider(IAnyCAPluginConfigPro
3535
GoDaddyCAPluginConfig.Config properties = JsonConvert.DeserializeObject<GoDaddyCAPluginConfig.Config>(rawConfig);
3636

3737
_logger.LogTrace($"Builder - ApiKey: {properties.ApiKey}");
38-
_logger.LogTrace($"Builder - ApiSecret: {properties.ApiSecret}");
3938
_logger.LogTrace($"Builder - BaseUrl: {properties.BaseUrl}");
4039
_logger.LogTrace($"Builder - ShopperId: {properties.ShopperId}");
4140
_logger.LogTrace($"Builder - Enabled: {properties.Enabled}");
@@ -59,7 +58,6 @@ public GoDaddyCAPluginBuilder<TBuilder> WithConnectionInformation(Dictionary<str
5958
GoDaddyCAPluginConfig.Config properties = JsonConvert.DeserializeObject<GoDaddyCAPluginConfig.Config>(rawConfig);
6059

6160
_logger.LogTrace($"Builder - ApiKey: {properties.ApiKey}");
62-
_logger.LogTrace($"Builder - ApiSecret: {properties.ApiSecret}");
6361
_logger.LogTrace($"Builder - BaseUrl: {properties.BaseUrl}");
6462
_logger.LogTrace($"Builder - ShopperId: {properties.ShopperId}");
6563
_logger.LogTrace($"Builder - Enabled: {properties.Enabled}");

README.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -104,13 +104,16 @@ The GoDaddy AnyCA Gateway REST plugin is supported by Keyfactor for Keyfactor cu
104104

105105
2. On the server hosting the AnyCA Gateway REST, download and unzip the latest [GoDaddy AnyCA Gateway REST plugin](https://github.com/Keyfactor/godaddy-caplugin/releases/latest) from GitHub.
106106

107-
3. Copy the unzipped directory (usually called `net6.0`) to the Extensions directory:
107+
3. Copy the unzipped directory (usually called `net6.0` or `net8.0`) to the Extensions directory:
108+
108109

109110
```shell
111+
Depending on your AnyCA Gateway REST version, copy the unzipped directory to one of the following locations:
110112
Program Files\Keyfactor\AnyCA Gateway\AnyGatewayREST\net6.0\Extensions
113+
Program Files\Keyfactor\AnyCA Gateway\AnyGatewayREST\net8.0\Extensions
111114
```
112115

113-
> The directory containing the GoDaddy AnyCA Gateway REST plugin DLLs (`net6.0`) can be named anything, as long as it is unique within the `Extensions` directory.
116+
> The directory containing the GoDaddy AnyCA Gateway REST plugin DLLs (`net6.0` or `net8.0`) can be named anything, as long as it is unique within the `Extensions` directory.
114117

115118
4. Restart the AnyCA Gateway REST service.
116119

@@ -185,8 +188,6 @@ The GoDaddy AnyCA Gateway REST plugin is supported by Keyfactor for Keyfactor cu
185188

186189

187190

188-
189-
190191
## License
191192

192193
Apache License 2.0, see [LICENSE](LICENSE).

integration-manifest.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
"update_catalog": true,
1010
"gateway_framework": "24.2",
1111
"release_dir": "GoDaddy/bin/Release/net6.0",
12+
"release_project": "GoDaddy/GoDaddy.csproj",
1213
"about": {
1314
"carest": {
1415
"ca_plugin_config": [
@@ -113,4 +114,4 @@
113114
]
114115
}
115116
}
116-
}
117+
}

0 commit comments

Comments
 (0)