Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
61 commits
Select commit Hold shift + click to select a range
5836b34
Add custom field support
dgaley Jul 25, 2025
2695756
changelog
dgaley Jul 28, 2025
75e8166
support cname return from enrollment
dgaley Sep 5, 2025
88123e1
Update generated docs
Sep 5, 2025
c90a7a8
Merge pull request #5 from Keyfactor/customfields
mkachk Nov 5, 2025
261a82b
feat: release 1.1.1
mkachk Jan 21, 2026
a1c23f6
200 day renewal fixes
bhillkeyfactor Apr 7, 2026
ea18abf
Update generated docs
Apr 7, 2026
a742e03
Improved logging .net 10 support
bhillkeyfactor Apr 8, 2026
fba58ac
Removed Template Sync Logic
bhillkeyfactor Apr 8, 2026
58177df
Update generated docs
Apr 8, 2026
b8d74db
fixed template mapping issue
bhillkeyfactor Apr 8, 2026
ddf1330
Merge branch '200dayfixes' of https://github.com/Keyfactor/cscglobal-…
bhillkeyfactor Apr 8, 2026
0b6f8a5
product fixes
bhillkeyfactor Apr 8, 2026
445eb57
Update generated docs
Apr 8, 2026
8f098a9
fixed renewal issue
bhillkeyfactor Apr 8, 2026
77b42cd
documentation fixes
bhillkeyfactor Apr 8, 2026
204367d
Update generated docs
Apr 8, 2026
ff2f4c5
DNS Changes
bhillkeyfactor May 22, 2026
b138670
Update generated docs
May 22, 2026
84d48a8
dns code updates
bhillkeyfactor May 26, 2026
448f167
Update generated docs
May 26, 2026
eb39fcf
change type
bhillkeyfactor May 26, 2026
fe49f01
Merge branch 'feature/dns-plugins' of https://github.com/Keyfactor/cs…
bhillkeyfactor May 26, 2026
3ba367b
Update generated docs
May 26, 2026
65454d0
fixed mismatch
bhillkeyfactor May 26, 2026
0127331
Use 'cname' validation type for CSC CNAME DCV
bhillkeyfactor May 26, 2026
5ea38b4
Update generated docs
May 26, 2026
73f7ddd
added polling to grab cert
bhillkeyfactor May 27, 2026
95e9337
Update generated docs
May 27, 2026
1564d81
Fix NullReferenceException in GetEnrollmentResult for null DCV email …
bhillkeyfactor Jun 30, 2026
38354fb
Merge branch 'main' into release-1.1
indrora Jun 30, 2026
cfcc069
Merge branch 'release-2.0' into feature/dns-plugins
bhillkeyfactor Jul 14, 2026
b016c6c
Update integration-manifest.json
bhillkeyfactor Jul 14, 2026
cc97504
Update CSCGlobalCAPlugin.csproj
bhillkeyfactor Jul 14, 2026
4d9ff86
Update CSCGlobalCAPlugin.csproj
bhillkeyfactor Jul 14, 2026
78f95a3
Update keyfactor-bootstrap-workflow-v3.yml
bhillkeyfactor Jul 14, 2026
979379b
docs: auto-generate README and documentation [skip ci]
github-actions[bot] Jul 14, 2026
a1275f6
Add Enabled CA connection flag
bhillkeyfactor Jul 14, 2026
c034180
docs: auto-generate README and documentation [skip ci]
github-actions[bot] Jul 14, 2026
4433cb5
Feature/dns plugins (#12)
bhillkeyfactor Jul 30, 2026
f622574
Update integration-manifest.json (#14)
indrora Jul 30, 2026
51a983f
Merge branch 'main' into release-2.0
indrora Jul 30, 2026
07cfb6f
Port product rename, new certificate types, and bug fixes from featur…
bhillkeyfactor Sep 21, 2026
ef36792
docs: auto-generate README and documentation [skip ci]
github-actions[bot] Sep 21, 2026
ed6b3ad
Raise unit test coverage to ~95% line coverage
bhillkeyfactor Sep 21, 2026
82d3228
Raise unit test branch coverage from ~75% to ~80%
bhillkeyfactor Sep 21, 2026
7f20484
Merge pull request #20 from Keyfactor/feature/dns-plugins-multiname-c…
bhillkeyfactor Sep 21, 2026
1adbfb3
Merge branch 'release-2.0' into feature/dns-plugins
bhillkeyfactor Sep 21, 2026
fa8eb3c
Merge branch 'main' into feature/dns-plugins
indrora Sep 21, 2026
1123ee7
Sync ProductID directly from CSC's certificateType, drop reverse-mapp…
bhillkeyfactor Sep 21, 2026
def2798
Merge pull request #23 from Keyfactor/feature/dns-plugins-multiname-c…
bhillkeyfactor Sep 22, 2026
1ee7a73
Fix Enroll failures reporting INPROCESS instead of FAILED, surface fl…
bhillkeyfactor Sep 22, 2026
f92fd04
Merge pull request #25 from Keyfactor/feature/dns-plugins-multiname-c…
bhillkeyfactor Sep 22, 2026
d6fdefa
Attach flow summary to EnrollmentContext on success, not just StatusM…
bhillkeyfactor Sep 22, 2026
e908cef
Merge pull request #26 from Keyfactor/feature/dns-plugins-multiname-c…
bhillkeyfactor Sep 22, 2026
32a132c
Render flow summary as one bullet per step instead of one blob
bhillkeyfactor Sep 22, 2026
e05221a
Merge pull request #27 from Keyfactor/feature/dns-plugins-multiname-c…
bhillkeyfactor Sep 22, 2026
44bc7ff
Rename PublishCnameDcv step and explain why it's a no-op for non-CNAM…
bhillkeyfactor Sep 22, 2026
d014595
Merge pull request #28 from Keyfactor/feature/dns-plugins-multiname-c…
bhillkeyfactor Sep 22, 2026
d7d934e
Merge branch 'main' into feature/dns-plugins
indrora Sep 25, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 62 additions & 3 deletions cscglobal-caplugin.Tests/CSCGlobalCAPluginTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -420,7 +420,7 @@ public async Task Synchronize_ValidCertificateContent_AddsToBufferWithMappedProd
{
Results = new List<CertificateResponse>
{
new CertificateResponse { Uuid = "u1", Status = "ACTIVE", Certificate = apiBase64, CertificateType = "4" }
new CertificateResponse { Uuid = "u1", Status = "ACTIVE", Certificate = apiBase64, CertificateType = "CSC TrustedSecure DV" }
}
});

Expand All @@ -432,7 +432,10 @@ public async Task Synchronize_ValidCertificateContent_AddsToBufferWithMappedProd
var items = buffer.ToArray();
Assert.Single(items);
Assert.Equal("u1", items[0].CARequestID);
Assert.Equal("CSC TrustedSecure Domain Validated SSL", items[0].ProductID);
// CSC's list/sync API returns the certificate's current product name directly, so the
// synced ProductID must match it verbatim (and therefore match the canonical Certificate
// Profile name configured in Command) rather than going through a name-remapping table.
Assert.Equal("CSC TrustedSecure DV", items[0].ProductID);
}

[Fact]
Expand Down Expand Up @@ -834,6 +837,58 @@ public async Task Enroll_New_Success_ReturnsExternalValidation()

Assert.Equal((int)EndEntityStatus.EXTERNALVALIDATION, result.Status);
Assert.Equal("uuid-new", result.CARequestID);
// Command's enrollment UI doesn't surface StatusMessage on a successful/pending result -
// only EnrollmentContext is - so the flow summary must be attached there instead, one
// bullet per step so it renders readably rather than as a single run-on blob.
Assert.NotNull(result.EnrollmentContext);
Assert.True(result.EnrollmentContext.ContainsKey("Flow: Enroll-New"));
Assert.True(result.EnrollmentContext.Keys.Count(k => k.StartsWith("Flow Step ")) > 1);
}

[Fact]
public async Task Enroll_New_SuccessWithDcvDetails_KeepsDcvEntriesAlongsideFlowSummary()
{
var mockClient = new Mock<ICscGlobalClient>();
mockClient.Setup(c => c.SubmitGetCustomFields()).ReturnsAsync(new List<GetCustomField>());
mockClient.Setup(c => c.SubmitRegistrationAsync(It.IsAny<RegistrationRequest>())).ReturnsAsync(new RegistrationResponse
{
Result = new Result
{
CommonName = "dcv.example.com",
Status = new Status { Uuid = "uuid-dcv" },
DcvDetails = new List<DcvDetail>
{
new DcvDetail { CName = new CName { Name = "_dnsauth.example.com", Value = "token" } }
}
}
});

var plugin = MakePlugin(mockClient);
var result = await plugin.Enroll("csr", "CN=test", new Dictionary<string, string[]>(), ProductInfo(),
RequestFormat.PKCS10, EnrollmentType.New);

Assert.Equal("token", result.EnrollmentContext["_dnsauth.example.com"]);
Assert.True(result.EnrollmentContext.ContainsKey("Flow: Enroll-New"));
Assert.True(result.EnrollmentContext.Keys.Count(k => k.StartsWith("Flow Step ")) > 1);
}

[Fact]
public async Task Enroll_New_RegistrationErrorFromCsc_PrependsFlowSummaryToStatusMessage()
{
var mockClient = new Mock<ICscGlobalClient>();
mockClient.Setup(c => c.SubmitGetCustomFields()).ReturnsAsync(new List<GetCustomField>());
mockClient.Setup(c => c.SubmitRegistrationAsync(It.IsAny<RegistrationRequest>())).ReturnsAsync(new RegistrationResponse
{
RegistrationError = new RegistrationError { Description = "Open order in progress" }
});

var plugin = MakePlugin(mockClient);
var result = await plugin.Enroll("csr", "CN=test", new Dictionary<string, string[]>(), ProductInfo(),
RequestFormat.PKCS10, EnrollmentType.New);

Assert.Equal((int)EndEntityStatus.FAILED, result.Status);
Assert.Contains("Enroll-New", result.StatusMessage);
Assert.Contains("Open order in progress", result.StatusMessage);
}

[Fact]
Expand Down Expand Up @@ -993,10 +1048,14 @@ public async Task Enroll_New_WithDnsValidatorFactoryButEmailMethod_DoesNotAttemp
[EnrollmentConfigConstants.DomainControlValidationMethod] = "EMAIL"
});

await plugin.Enroll("csr", "CN=test", new Dictionary<string, string[]>(), productInfo,
var result = await plugin.Enroll("csr", "CN=test", new Dictionary<string, string[]>(), productInfo,
RequestFormat.PKCS10, EnrollmentType.New);

mockFactory.Verify(f => f.ResolveDomainValidator(It.IsAny<string>(), It.IsAny<string>()), Times.Never);
// The DcvAutoPublish step must explain *why* it was a no-op for a non-CNAME method,
// rather than showing a bare [OK] under a CNAME-sounding step name.
var publishStep = result.EnrollmentContext.Single(e => e.Key.Contains("DcvAutoPublish"));
Assert.Contains("not CNAME", publishStep.Value);
}

[Fact]
Expand Down
45 changes: 45 additions & 0 deletions cscglobal-caplugin.Tests/FlowLoggerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,51 @@ public void EndBranch_WithoutBranch_DoesNotThrow()
flow.EndBranch();
}

[Fact]
public void GetSummaryEntries_OneEntryPerStepPlusHeader()
{
using var flow = new FlowLogger(NewLoggerMock().Object, "MyFlow");
flow.Step("StepOne");
flow.Fail("StepTwo", "boom");

var entries = flow.GetSummaryEntries();

Assert.True(entries.ContainsKey("Flow: MyFlow"));
Assert.Contains("FAILED", entries["Flow: MyFlow"]);
Assert.Equal(3, entries.Count); // header + 2 steps
Assert.Contains(entries, e => e.Key.Contains("StepOne") && e.Value.Contains("OK"));
Assert.Contains(entries, e => e.Key.Contains("StepTwo") && e.Value.Contains("boom"));
}

[Fact]
public void GetSummaryEntries_AllStepsSucceed_HeaderReportsOk()
{
using var flow = new FlowLogger(NewLoggerMock().Object, "MyFlow");
flow.Step("StepOne");
flow.Step("StepTwo");

var entries = flow.GetSummaryEntries();

Assert.Contains("[OK]", entries["Flow: MyFlow"]);
}

[Fact]
public void GetSummaryEntries_BranchChildren_IncludedAsSeparateEntries()
{
using var flow = new FlowLogger(NewLoggerMock().Object, "MyFlow");
flow.Branch("Inner");
flow.Step("NestedStep");
flow.Fail("NestedFail", "inner reason");
flow.EndBranch();
flow.Step("TopLevelStep");

var entries = flow.GetSummaryEntries();

Assert.Contains(entries, e => e.Key.Contains("NestedStep"));
Assert.Contains(entries, e => e.Key.Contains("NestedFail") && e.Value.Contains("inner reason"));
Assert.Contains(entries, e => e.Key.Contains("TopLevelStep"));
}

[Fact]
public void Dispose_NoSteps_DoesNotThrow()
{
Expand Down
24 changes: 1 addition & 23 deletions cscglobal-caplugin.Tests/RequestManagerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -402,7 +402,7 @@ public void GetRevokeResult_Success_ReturnsRevoked()
}

// ---------------------------------------------------------------------
// MapReturnStatus / MapCertificateTypeToProductId
// MapReturnStatus
// ---------------------------------------------------------------------

[Theory]
Expand All @@ -417,28 +417,6 @@ public void MapReturnStatus_MapsExpectedStatus(string? cscStatus, Keyfactor.PKI.
Assert.Equal((int)expected, Manager.MapReturnStatus(cscStatus!));
}

[Theory]
[InlineData("4", "CSC TrustedSecure Domain Validated SSL")]
[InlineData("CSC TrustedSecure Domain Validated SSL", "CSC TrustedSecure Domain Validated SSL")]
[InlineData("CSC Trusted Secure Domain Validated SSL", "CSC TrustedSecure Domain Validated SSL")]
[InlineData("9", "CSC TrustedSecure DV Wildcard, Multiple Names")]
public void MapCertificateTypeToProductId_KnownValue_MapsToProductId(string cscType, string expectedProductId)
{
Assert.Equal(expectedProductId, Manager.MapCertificateTypeToProductId(cscType));
}

[Fact]
public void MapCertificateTypeToProductId_UnknownValue_PassesThrough()
{
Assert.Equal("SomeUnknownType", Manager.MapCertificateTypeToProductId("SomeUnknownType"));
}

[Fact]
public void MapCertificateTypeToProductId_Null_ReturnsFallback()
{
Assert.Equal("CscGlobal", Manager.MapCertificateTypeToProductId(null!));
}

// ---------------------------------------------------------------------
// GetNotifications
// ---------------------------------------------------------------------
Expand Down
Loading
Loading