Skip to content

Commit 7f839dc

Browse files
dgaleyKeyfactor
authored andcommitted
release: 1.1.1
* add some sync logging * allow manual lifetime specification * Update generated docs * rethrow existing exceptions * handle empty sync * changelog --------- Co-authored-by: Keyfactor <keyfactor@keyfactor.github.io>
1 parent eea0962 commit 7f839dc

6 files changed

Lines changed: 57 additions & 11 deletions

File tree

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,10 @@ Fix for JSON serialization of revocation
1414
1.1.0
1515
Add support for using the cert upload feature to upload auth certs
1616
Switch to .NET 8
17+
18+
1.1.1
19+
Allow for manual specification of enrollment term length
20+
21+
1.1.2
22+
Add Lifetime parameter to allow for manual specification of cert validity
23+
Bugfix - Properly handle syncs of 0 records

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ In addition, for the admin account you plan to use, make sure it has the API adm
113113
* **MultiDomain** - This flag lets Keyfactor know if the certificate can contain multiple domain names. Depending on the setting, the SAN entries of the request will change to support Sectigo requirements.
114114
* **Organization** - If the organization name is provided here, the Sectigo gateway will use that organization name in requests instead of whatever is in the O= field in the request subject.
115115
* **Department** - If your Sectigo account is using department-level products, put the appropriate department name here. Previously, this was alternatively supplied in the OU= subject field, which is now deprecated.
116+
* **Lifetime** - OPTIONAL: The term length (in days) to use for enrollment. If not provided, the default is the first value available in the profile definition in your Sectigo account.
116117

117118

118119

integration-manifest.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,10 @@
7878
{
7979
"name": "Department",
8080
"description": "If your Sectigo account is using department-level products, put the appropriate department name here. Previously, this was alternatively supplied in the OU= subject field, which is now deprecated."
81+
},
82+
{
83+
"name": "Lifetime",
84+
"description": "OPTIONAL: The term length (in days) to use for enrollment. If not provided, the default is the first value available in the profile definition in your Sectigo account."
8185
}
8286
]
8387
}

sectigo-scm-caplugin/Client/SectigoClient.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,10 @@ public async Task CertificateListProducer(BlockingCollection<Certificate> certs,
6161
Logger.LogInformation($"Request Certificates at Position {certIndex} with Page Size {pageSize}");
6262
certificatePageToProcess = await PageCertificates(certIndex, pageSize, filter);
6363
Logger.LogDebug($"Found {certificatePageToProcess.Count} certificate to process");
64-
64+
if (certificatePageToProcess.Count == 0)
65+
{
66+
return;
67+
}
6568
//Processing Loop will add and retry adding to queue until all certificates have been processed for a page
6669
batchCount = 0;
6770
blockedCount = 0;

sectigo-scm-caplugin/Constants.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ public class Config
2727
public const string MULTIDOMAIN = "MultiDomain";
2828
public const string ORGANIZATION = "Organization";
2929
public const string DEPARTMENT = "Department";
30+
public const string LIFETIME = "Lifetime";
3031
}
3132

3233
//headers for API client

sectigo-scm-caplugin/SectigoCAPlugin.cs

Lines changed: 40 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,26 @@ public async Task<EnrollmentResult> Enroll(string csr, string subject, Dictionar
196196
_logger.LogTrace($"Found {enrollmentProfile.name} profile for enroll request");
197197
}
198198

199+
int termLength;
200+
var profileTerms = Task.Run(async () => await GetProfileTerms(int.Parse(productInfo.ProductID))).Result;
201+
if (!string.IsNullOrEmpty(productInfo.ProductParameters[Constants.Config.LIFETIME]))
202+
{
203+
var tempTerm = int.Parse(productInfo.ProductParameters[Constants.Config.LIFETIME]);
204+
if (profileTerms.Contains(tempTerm))
205+
{
206+
termLength = tempTerm;
207+
}
208+
else
209+
{
210+
_logger.LogError($"Specified term length of {tempTerm} does not match available terms for product ID {productInfo.ProductID}. Available terms are {string.Join(",", profileTerms)}");
211+
throw new Exception($"Specified term length of {tempTerm} does not match available terms for product ID {productInfo.ProductID}");
212+
}
213+
}
214+
else
215+
{
216+
termLength = profileTerms[0];
217+
}
218+
199219
int sslId;
200220
string priorSn = string.Empty;
201221
Certificate newCert = null;
@@ -216,7 +236,7 @@ public async Task<EnrollmentResult> Enroll(string csr, string subject, Dictionar
216236
{
217237
csr = csr,
218238
orgId = requestOrgId,
219-
term = Task.Run(async () => await GetProfileTerm(int.Parse(productInfo.ProductID))).Result,
239+
term = termLength,
220240
certType = enrollmentProfile.id,
221241
//External requestor is expected to be an email. Use config to pull the enrollment field or send blank
222242
//sectigo will default to the account (API account) making the request.
@@ -245,7 +265,7 @@ public async Task<EnrollmentResult> Enroll(string csr, string subject, Dictionar
245265
catch (HttpRequestException httpEx)
246266
{
247267
_logger.LogError($"Enrollment Failed due to a HTTP error: {httpEx.Message}");
248-
throw new Exception(httpEx.Message);
268+
throw;
249269
}
250270
catch (Exception ex)
251271
{
@@ -257,7 +277,7 @@ public async Task<EnrollmentResult> Enroll(string csr, string subject, Dictionar
257277
retError = ex.InnerException.Message;
258278
}
259279

260-
throw new Exception(retError);
280+
throw;
261281
}
262282
}
263283

@@ -431,6 +451,13 @@ public Dictionary<string, PropertyConfigInfo> GetTemplateParameterAnnotations()
431451
Hidden = false,
432452
DefaultValue = "",
433453
Type = "String"
454+
},
455+
[Constants.Config.LIFETIME] = new PropertyConfigInfo()
456+
{
457+
Comments = "OPTIONAL: The term length (in days) to use for enrollment. If not provided, the default is the first value available in the profile definition in your Sectigo account.",
458+
Hidden = false,
459+
DefaultValue = "",
460+
Type = "String"
434461
}
435462
};
436463
}
@@ -520,7 +547,7 @@ public async Task Synchronize(BlockingCollection<AnyCAPluginCertificate> blockin
520547
_logger.LogError($"Synchronize task failed with the following message: {producerTask.Exception.Flatten().Message}");
521548
throw producerTask.Exception.Flatten();
522549
}
523-
550+
_logger.LogTrace($"SYNC TRACE ({certToAdd.Id}): Processing record {certToAdd.Id}");
524551
string dbCertId = null;
525552
int dbCertStatus = -1;
526553
//serial number is blank on certs that have not been issued (awaiting approval)
@@ -566,23 +593,26 @@ public async Task Synchronize(BlockingCollection<AnyCAPluginCertificate> blockin
566593
}
567594

568595
//Download to get full certdata required for sync process
569-
_logger.LogTrace($"Attempt to Pickup Certificate {certToAdd.CommonName} (ID: {certToAdd.Id})");
596+
_logger.LogTrace($"SYNC TRACE ({certToAdd.Id}): Attempt to Pickup Certificate {certToAdd.CommonName}");
570597
var certdataApi = Task.Run(async () => await client.PickupCertificate(certToAdd.Id, certToAdd.CommonName)).Result;
571598
if (certdataApi != null)
572599
certData = Convert.ToBase64String(certdataApi.GetRawCertData());
573600

601+
574602
if (certToAdd == null || String.IsNullOrEmpty(certToAdd.SerialNumber) || String.IsNullOrEmpty(certToAdd.CommonName) || String.IsNullOrEmpty(certData))
575603
{
576604
_logger.LogDebug($"Certificate Data unavailable for {certToAdd.CommonName} (ID: {certToAdd.Id}). Skipping ");
577605
continue;
578606
}
607+
_logger.LogTrace($"SYNC TRACE ({certToAdd.Id}): Retrieved cert data: {certData}");
579608

580609
string prodId = "";
581610
try
582611
{
583-
_logger.LogTrace($"Cert ID: {certToAdd.Id.ToString()}");
584-
_logger.LogTrace($"Sync ID: {syncReqId.ToString()}");
585-
_logger.LogTrace($"Product ID: {certToAdd.CertType.id.ToString()}");
612+
_logger.LogTrace($"SYNC TRACE ({certToAdd.Id}): Cert ID: {certToAdd.Id.ToString()}");
613+
_logger.LogTrace($"SYNC TRACE ({certToAdd.Id}): Sync ID: {syncReqId.ToString()}");
614+
_logger.LogTrace($"SYNC TRACE ({certToAdd.Id}): Product ID: {certToAdd.CertType.id.ToString()}");
615+
_logger.LogTrace($"SYNC TRACE ({certToAdd.Id}): Status: {certToAdd.status}");
586616
prodId = certToAdd.CertType.id.ToString();
587617
}
588618
catch { }
@@ -674,11 +704,11 @@ private async Task<Organization> GetOrganizationAsync(string orgName)
674704
return orgList.Organizations.Where(x => x.name.ToLower().Equals(orgName.ToLower())).FirstOrDefault();
675705
}
676706

677-
private async Task<int> GetProfileTerm(int profileId)
707+
private async Task<List<int>> GetProfileTerms(int profileId)
678708
{
679709
var client = SectigoClient.InitializeClient(_config, _certificateResolver);
680710
var profileList = await client.ListSslProfiles();
681-
return profileList.SslProfiles.Where(x => x.id == profileId).FirstOrDefault().terms[0];
711+
return profileList.SslProfiles.Where(x => x.id == profileId).FirstOrDefault().terms.ToList();
682712
}
683713

684714
private async Task<Profile> GetProfile(int profileId)

0 commit comments

Comments
 (0)