Skip to content

Commit c5bb5d8

Browse files
committed
Add sync product filter
Fixes for SAN duplications
1 parent e30f109 commit c5bb5d8

6 files changed

Lines changed: 144 additions & 31 deletions

File tree

globalsign-mssl-caplugin/Api/GlobalSignEnrollRequest.cs

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,32 @@ public BmV2PvOrderRequest Request
107107
continue;
108108
}
109109

110-
var entry = new SANEntry();
110+
string trimCN = CommonName, trimItem = item;
111+
112+
if (CommonName.StartsWith("*."))
113+
{
114+
trimCN = CommonName.Substring(2).ToLower();
115+
trimItem = item.ToLower();
116+
List<string> equivs = new List<string> { $"*.{trimCN}", $"www.{trimCN}", $"{trimCN}" };
117+
if (equivs.Contains(trimItem))
118+
{
119+
Logger.LogInformation($"SAN Entry {item} is equivalent to CN ignoring wildcards or www prefix, removing from request");
120+
continue;
121+
}
122+
}
123+
else if (CommonName.StartsWith("www."))
124+
{
125+
trimCN = CommonName.Substring(4).ToLower();
126+
trimItem = item.ToLower();
127+
List<string> equivs = new List<string> { $"www.{trimCN}", $"{trimCN}" };
128+
if (equivs.Contains(trimItem))
129+
{
130+
Logger.LogInformation($"SAN Entry {item} is equivalent to CN ignoring wildcards or www prefix, removing from request");
131+
continue;
132+
}
133+
}
134+
135+
var entry = new SANEntry();
111136
entry.SubjectAltName = item;
112137
var sb = new StringBuilder();
113138
sb.Append("Adding SAN entry of type ");

globalsign-mssl-caplugin/Api/GlobalSignRenewRequest.cs

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,31 @@ public GlobalSignRenewRequest(GlobalSignCAConfig config, bool privateDomain, boo
5353
Logger.LogInformation($"SAN Entry {item} matches CN, removing from request");
5454
continue;
5555
}
56+
string trimCN = CommonName, trimItem = item;
5657

57-
var entry = new SANEntry();
58+
if (CommonName.StartsWith("*."))
59+
{
60+
trimCN = CommonName.Substring(2).ToLower();
61+
trimItem = item.ToLower();
62+
List<string> equivs = new List<string> { $"*.{trimCN}", $"www.{trimCN}", $"{trimCN}" };
63+
if (equivs.Contains(trimItem))
64+
{
65+
Logger.LogInformation($"SAN Entry {item} is equivalent to CN ignoring wildcards or www prefix, removing from request");
66+
continue;
67+
}
68+
}
69+
else if (CommonName.StartsWith("www."))
70+
{
71+
trimCN = CommonName.Substring(4).ToLower();
72+
trimItem = item.ToLower();
73+
List<string> equivs = new List<string> { $"www.{trimCN}", $"{trimCN}" };
74+
if (equivs.Contains(trimItem))
75+
{
76+
Logger.LogInformation($"SAN Entry {item} is equivalent to CN ignoring wildcards or www prefix, removing from request");
77+
continue;
78+
}
79+
}
80+
var entry = new SANEntry();
5881
entry.SubjectAltName = item;
5982
var sb = new StringBuilder();
6083
sb.Append("Adding SAN entry of type ");

globalsign-mssl-caplugin/Client/GlobalSignApiClient.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -272,19 +272,19 @@ public async Task<EnrollmentResult> Enroll(GlobalSignEnrollRequest enrollRequest
272272
Logger.MethodEntry();
273273
var rawRequest = enrollRequest.Request;
274274
Logger.LogTrace("Request details:");
275-
Logger.LogTrace($"Profile ID: {enrollRequest.MsslProfileId}");
276-
Logger.LogTrace($"Domain ID: {enrollRequest.MsslDomainId}");
275+
Logger.LogTrace($"Profile ID: {rawRequest.MSSLProfileID}");
276+
Logger.LogTrace($"Domain ID: {rawRequest.MSSLDomainID}");
277277
Logger.LogTrace(
278-
$"Contact Info: {enrollRequest.FirstName}, {enrollRequest.LastName}, {enrollRequest.Email}, {enrollRequest.Phone}");
279-
Logger.LogTrace($"SAN Count: {enrollRequest.SANs.Count()}");
278+
$"Contact Info: {rawRequest.ContactInfo.FirstName}, {rawRequest.ContactInfo.LastName}, {rawRequest.ContactInfo.Email}, {rawRequest.ContactInfo.Phone}");
279+
Logger.LogTrace($"SAN Count: {rawRequest.SANEntries.Count()}");
280280
if (rawRequest.SANEntries.Count() > 0)
281281
Logger.LogTrace($"SANs: {string.Join(",", rawRequest.SANEntries.Select(s => s.SubjectAltName))}");
282282
Logger.LogTrace($"Product Code: {rawRequest.OrderRequestParameter.ProductCode}");
283283
Logger.LogTrace($"Order Kind: {rawRequest.OrderRequestParameter.OrderKind}");
284284
if (!string.IsNullOrEmpty(rawRequest.OrderRequestParameter.BaseOption))
285285
Logger.LogTrace($"Order Base Option: {rawRequest.OrderRequestParameter.BaseOption}");
286286

287-
var requestwrapper = new PVOrder(enrollRequest.Request);
287+
var requestwrapper = new PVOrder(rawRequest);
288288
var responsewrapper = await OrderService.PVOrderAsync(requestwrapper);
289289
;
290290
var response = responsewrapper.Response;

globalsign-mssl-caplugin/Constants.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,13 @@ internal class Constants
2121
public static string PICKUPDELAY = "DelayTime";
2222
public static string SYNCSTARTDATE = "SyncStartDate";
2323
public static string SYNCINTERNVALDAYS = "SyncIntervalDays";
24+
public static string SYNCPRODUCTS = "SyncProducts";
2425
}
2526

2627
public static class EnrollmentConfigConstants
2728
{
2829
public const string RootCAType = "RootCAType";
2930
public const string SlotSize = "SlotSize";
30-
public const string CertificateValidityInYears = "CertificateValidityInYears";
31+
public const string CertificateValidityInDays = "CertificateValidityInDays";
32+
public const string MSSLProfileId = "MSSLProfileId";
3133
}

globalsign-mssl-caplugin/GlobalSignCAConfig.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ public class GlobalSignCAConfig
3232

3333
public string SyncStartDate { get; set; } = "";
3434
public int SyncIntervalDays { get; set; } = 0;
35+
public string SyncProducts { get; set; } = "";
3536

3637

3738
public string GetUrl(GlobalSignServiceType queryType)

globalsign-mssl-caplugin/GlobalSignCAPlugin.cs

Lines changed: 85 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -90,8 +90,30 @@ public async Task Synchronize(BlockingCollection<AnyCAPluginCertificate> blockin
9090
var certs = await apiClient.GetCertificatesForSyncAsync(fullSync, syncFrom, fullSyncFrom,
9191
Config.SyncIntervalDays);
9292

93+
bool productFilter = false;
94+
List<string> products = null;
95+
if (!string.IsNullOrEmpty(Config.SyncProducts))
96+
{
97+
products = Config.SyncProducts.Split(',').ToList();
98+
products.ForEach(p => p.ToUpper());
99+
productFilter = true;
100+
}
101+
93102
foreach (var c in certs)
94103
{
104+
if (productFilter)
105+
{
106+
bool prodMatch = false;
107+
if (c.OrderInfo?.ProductCode != null && products.Contains(c.OrderInfo.ProductCode.ToUpper()))
108+
{
109+
prodMatch = true;
110+
}
111+
if (!prodMatch)
112+
{
113+
Logger.LogInformation($"Found certificate with product code {c.OrderInfo?.ProductCode}, which does not match the filter criteria. Skipping.");
114+
continue;
115+
}
116+
}
95117
var orderStatus = (GlobalSignOrderStatus)Enum.Parse(typeof(GlobalSignOrderStatus),
96118
c.CertificateInfo?.CertificateStatus ?? string.Empty);
97119
DateTime? subDate = DateTime.TryParse(c.OrderInfo?.OrderDate, out var orderDate) ? orderDate : null;
@@ -237,6 +259,8 @@ public async Task<EnrollmentResult> Enroll(string csr, string subject, Dictionar
237259
if (sanDict.TryGetValue("ipaddress", out var ipSans))
238260
Logger.LogTrace($"IP SAN Count: {ipSans.Length}");
239261

262+
List<GetDomainsDomainDetail> matchedDomains = new List<GetDomainsDomainDetail>();
263+
240264
// only try to resolve a domain if we don't already have a commonName
241265
if (string.IsNullOrWhiteSpace(commonName))
242266
{
@@ -247,16 +271,16 @@ public async Task<EnrollmentResult> Enroll(string csr, string subject, Dictionar
247271
if (string.IsNullOrWhiteSpace(ipSan))
248272
continue;
249273

250-
var tempDomain = validDomains?
251-
.FirstOrDefault(d =>
274+
var tempDomains = validDomains
275+
.Where(d =>
252276
!string.IsNullOrEmpty(d?.DomainName) &&
253277
ipSan.EndsWith($".{d.DomainName}", StringComparison.OrdinalIgnoreCase)
254-
);
278+
).ToList();
255279

256-
if (tempDomain != null)
280+
if (tempDomains != null && tempDomains.Count > 0)
257281
{
258282
Logger.LogDebug($"ipSAN Domain match found for ipSAN: {ipSan}");
259-
domain = tempDomain;
283+
matchedDomains = tempDomains;
260284
commonName = ipSan;
261285
break;
262286
}
@@ -269,23 +293,23 @@ public async Task<EnrollmentResult> Enroll(string csr, string subject, Dictionar
269293
if (string.IsNullOrWhiteSpace(dnsSan))
270294
continue;
271295

272-
var tempDomain = validDomains?
273-
.FirstOrDefault(d =>
296+
var tempDomains = validDomains
297+
.Where(d =>
274298
!string.IsNullOrEmpty(d?.DomainName) &&
275299
dnsSan.EndsWith(d.DomainName, StringComparison.OrdinalIgnoreCase)
276-
);
300+
).ToList();
277301

278-
if (tempDomain != null)
302+
if (tempDomains != null && tempDomains.Count > 0)
279303
{
280304
Logger.LogDebug($"SAN Domain match found for SAN: {dnsSan}");
281-
domain = tempDomain;
305+
matchedDomains = tempDomains;
282306
commonName = dnsSan;
283307
break;
284308
}
285309
}
286310
}
287311
// If private domain skip domain resolution.
288-
if (privateDomain)
312+
else if (privateDomain)
289313
{
290314
var profiles = await apiClient.GetProfiles();
291315
var fillProfile = profiles.FirstOrDefault();
@@ -302,18 +326,41 @@ public async Task<EnrollmentResult> Enroll(string csr, string subject, Dictionar
302326
}
303327
};
304328
domain.MSSLProfileID = fillProfile.MSSLProfileId;
329+
matchedDomains = new List<GetDomainsDomainDetail> { domain };
305330
}
306331

307332
// 3) Fallback: if we did obtain a commonName (or it was already set), try matching it
308-
if (domain == null && !string.IsNullOrWhiteSpace(commonName))
309-
domain = validDomains?
310-
.FirstOrDefault(d =>
333+
else if (domain == null && !string.IsNullOrWhiteSpace(commonName))
334+
matchedDomains = validDomains
335+
.Where(d =>
311336
!string.IsNullOrEmpty(d?.DomainName) &&
312337
commonName.EndsWith(d.DomainName, StringComparison.OrdinalIgnoreCase)
313-
);
314-
315-
316-
if (domain == null) throw new Exception("Unable to determine GlobalSign domain");
338+
).ToList();
339+
340+
if (matchedDomains.Count == 1)
341+
{
342+
domain = matchedDomains[0];
343+
}
344+
else
345+
{
346+
var profId = productInfo.ProductParameters["MSSLProfileID"];
347+
if (!string.IsNullOrEmpty(profId) )
348+
{
349+
var tempDomain = matchedDomains.Where(d =>
350+
d.MSSLProfileID.Equals(profId, StringComparison.OrdinalIgnoreCase)).FirstOrDefault();
351+
if (tempDomain != null)
352+
{
353+
domain = tempDomain;
354+
}
355+
else
356+
{
357+
throw new Exception($"No domain matching common name {commonName} has provided MSSLProfileID of {profId}. Check configuration.");
358+
}
359+
}
360+
}
361+
362+
363+
if (domain == null) throw new Exception("Unable to determine GlobalSign domain");
317364

318365

319366

@@ -592,7 +639,14 @@ public Dictionary<string, PropertyConfigInfo> GetCAConnectorAnnotations()
592639
Hidden = false,
593640
DefaultValue = "2000-01-01",
594641
Type = "Integer"
595-
}
642+
},
643+
[Constants.SYNCPRODUCTS] = new()
644+
{
645+
Comments = "OPTIONAL: If provided as a comma-separated list of product IDs, will limit the certificate sync to only certificates of those products. If blank or not provided, will sync all certs.",
646+
Hidden = false,
647+
DefaultValue = null,
648+
Type = "String"
649+
}
596650
};
597651
}
598652

@@ -601,11 +655,11 @@ public Dictionary<string, PropertyConfigInfo> GetTemplateParameterAnnotations()
601655
{
602656
return new Dictionary<string, PropertyConfigInfo>
603657
{
604-
[EnrollmentConfigConstants.CertificateValidityInYears] = new()
658+
[EnrollmentConfigConstants.CertificateValidityInDays] = new()
605659
{
606-
Comments = "Number of years the certificate will be valid for",
660+
Comments = "Number of days the certificate will be valid for",
607661
Hidden = false,
608-
DefaultValue = "1",
662+
DefaultValue = "199",
609663
Type = "Number"
610664
},
611665
[EnrollmentConfigConstants.SlotSize] = new()
@@ -623,7 +677,15 @@ public Dictionary<string, PropertyConfigInfo> GetTemplateParameterAnnotations()
623677
Hidden = false,
624678
DefaultValue = "GLOBALSIGN_ROOT_R3",
625679
Type = "String"
626-
}
680+
},
681+
[EnrollmentConfigConstants.MSSLProfileId] = new()
682+
{
683+
Comments =
684+
"OPTIONAL: If specified, enrollments will use that profile ID for domain lookups. If not provided, domain lookup will be done based on the Common Name or first DNS SAN. Useful if your GlobalSign account has multiple domain objects with the same domain string, or subdomains (e.g. sub.test.com vs test.com).",
685+
Hidden = false,
686+
DefaultValue = null,
687+
Type = "String"
688+
}
627689
};
628690
}
629691

0 commit comments

Comments
 (0)