Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
50 changes: 50 additions & 0 deletions cscglobal-caplugin.Tests/CSCGlobalCAPluginTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -837,6 +837,56 @@ 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.
Assert.NotNull(result.EnrollmentContext);
Assert.True(result.EnrollmentContext.ContainsKey("Flow Summary"));
Assert.Contains("Enroll-New", result.EnrollmentContext["Flow Summary"]);
}

[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 Summary"));
}

[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
28 changes: 28 additions & 0 deletions cscglobal-caplugin/CSCGlobalCAPlugin.cs
Original file line number Diff line number Diff line change
Expand Up @@ -711,10 +711,12 @@ await flow.StepAsync("PollForIssuance", async () =>
if (newPolled != null)
{
flow.Step("PollResult", "issued during poll window");
AttachFlowSummary(newPolled, flow);
Logger.MethodExit(LogLevel.Debug);
return newPolled;
}

AttachFlowSummary(enrollResult, flow);
Logger.MethodExit(LogLevel.Debug);
return enrollResult;

Expand Down Expand Up @@ -864,6 +866,7 @@ await flow.StepAsync("PollForIssuance", async () =>
{
renewPolled = await TryPollForIssuedCertAsync(renewResult?.CARequestID);
});
AttachFlowSummary(renewPolled ?? renewResult, flow);
Logger.MethodExit(LogLevel.Debug);
return renewPolled ?? renewResult;
}
Expand Down Expand Up @@ -943,6 +946,7 @@ await flow.StepAsync("PollForIssuance", async () =>
{
reissuePolled = await TryPollForIssuedCertAsync(reissueResult?.CARequestID);
});
AttachFlowSummary(reissuePolled ?? reissueResult, flow);
Logger.MethodExit(LogLevel.Debug);
return reissuePolled ?? reissueResult;
}
Expand Down Expand Up @@ -987,6 +991,30 @@ await flow.StepAsync("PollForIssuance", async () =>
}
}

// CSC Global business-level failures (e.g. "Open order in progress") come back from
// RequestManager as a terse StatusMessage with no context on what the plugin actually did
// before hitting that error - prepend the flow's step-by-step summary so the message shown
// to the requester in Command explains what ran, not just how it ended. Command's enrollment
// UI does not surface StatusMessage on a successful/pending result at all - only
// EnrollmentContext is - so attach the summary there instead, as its own entry alongside
// whatever DCV instructions came back. Must be called after TryPublishCnameDcvAsync, which
// treats every EnrollmentContext entry as a candidate DNS record to publish - calling this
// first would make it try to publish "Flow Summary" as a CNAME.
private static void AttachFlowSummary(EnrollmentResult? result, FlowLogger flow)
{
if (result == null)
return;

if (result.Status == (int)EndEntityStatus.FAILED)
{
result.StatusMessage = $"{flow.GetSummary()}\n\n{result.StatusMessage}";
return;
}

result.EnrollmentContext ??= new Dictionary<string, string>();
result.EnrollmentContext["Flow Summary"] = flow.GetSummary();
}

//done
public async Task Ping()
{
Expand Down
Loading