Skip to content

Commit 497724a

Browse files
Merge 4aa1cbf into f52ba37
2 parents f52ba37 + 4aa1cbf commit 497724a

File tree

10 files changed

+1707
-623
lines changed

10 files changed

+1707
-623
lines changed

CHANGELOG.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,17 @@
1+
- 2.0.0
2+
- Added Issuer DN Filter (IssuerDnFilter) configuration parameter to restrict certificate synchronization per Logical CA
3+
- Filter supports case-insensitive substring matching against the certificate's Issuer Distinguished Name
4+
- Filter can also be specified via endpoint URL suffix using ||issuerdnfilter=<value> syntax for backward compatibility
5+
- Filter applies to both bulk sync (DownloadAllIssuedCertificates) and single certificate downloads (DownloadCertificate)
6+
- Added FlowLogger diagnostic helper — renders timed, step-by-step ASCII flow diagrams at Trace level for every public operation
7+
- Added comprehensive structured logging throughout all plugin and client methods (MethodEntry/MethodExit, LogTrace, LogDebug, LogWarning, LogError)
8+
- Hardened error handling: input validation, null guards, per-item try/catch in sync loop, AggregateException unwrapping, FaultException differentiation
9+
- Sync loop now tracks and reports filtered, skipped, and error counts alongside successful certificate count
10+
- Constructor-level validation for required parameters (endpoint, client cert location, password)
11+
- EnsureClientIsEnabled now throws InvalidOperationException instead of bare Exception
12+
- Added .NET 10 target framework support (net6.0, net8.0, net10.0)
13+
- Uses X509CertificateLoader on .NET 10+ to resolve SYSLIB0057 obsolescence (falls back to X509Certificate2 constructors on older TFMs)
14+
- Skips ServicePointManager TLS configuration on .NET 10+ where TLS 1.2+ is the default (resolves SYSLIB0014)
15+
116
- 1.0.0
217
- Initial Version

Idnomic/FlowLogger.cs

Lines changed: 245 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,245 @@
1+
/*
2+
Copyright © 2025 Keyfactor
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
using System;
18+
using System.Collections.Generic;
19+
using System.Diagnostics;
20+
using System.Text;
21+
using System.Threading.Tasks;
22+
using Microsoft.Extensions.Logging;
23+
24+
namespace Keyfactor.Extensions.CAPlugin.Idnomic;
25+
26+
public enum FlowStepStatus { Success, Failed, Skipped, InProgress }
27+
28+
public class FlowStep
29+
{
30+
public string Name { get; set; }
31+
public FlowStepStatus Status { get; set; }
32+
public string Detail { get; set; }
33+
public long ElapsedMs { get; set; }
34+
public List<FlowStep> Children { get; } = new();
35+
}
36+
37+
public sealed class FlowLogger : IDisposable
38+
{
39+
private readonly ILogger _logger;
40+
private readonly string _flowName;
41+
private readonly Stopwatch _totalTimer;
42+
private readonly List<FlowStep> _steps = new();
43+
private FlowStep _currentParent;
44+
private bool _disposed;
45+
46+
public FlowLogger(ILogger logger, string flowName)
47+
{
48+
_logger = logger;
49+
_flowName = flowName;
50+
_totalTimer = Stopwatch.StartNew();
51+
_logger.LogTrace("===== FLOW START: {FlowName} =====", _flowName);
52+
}
53+
54+
public void Step(string name, string detail = null)
55+
{
56+
var step = new FlowStep
57+
{
58+
Name = name,
59+
Status = FlowStepStatus.Success,
60+
Detail = detail
61+
};
62+
AddStep(step);
63+
}
64+
65+
public void Step(string name, Action action, string detail = null)
66+
{
67+
var sw = Stopwatch.StartNew();
68+
var step = new FlowStep
69+
{
70+
Name = name,
71+
Status = FlowStepStatus.InProgress,
72+
Detail = detail
73+
};
74+
AddStep(step);
75+
76+
try
77+
{
78+
action();
79+
sw.Stop();
80+
step.ElapsedMs = sw.ElapsedMilliseconds;
81+
step.Status = FlowStepStatus.Success;
82+
}
83+
catch
84+
{
85+
sw.Stop();
86+
step.ElapsedMs = sw.ElapsedMilliseconds;
87+
step.Status = FlowStepStatus.Failed;
88+
throw;
89+
}
90+
}
91+
92+
public async Task StepAsync(string name, Func<Task> action, string detail = null)
93+
{
94+
var sw = Stopwatch.StartNew();
95+
var step = new FlowStep
96+
{
97+
Name = name,
98+
Status = FlowStepStatus.InProgress,
99+
Detail = detail
100+
};
101+
AddStep(step);
102+
103+
try
104+
{
105+
await action();
106+
sw.Stop();
107+
step.ElapsedMs = sw.ElapsedMilliseconds;
108+
step.Status = FlowStepStatus.Success;
109+
}
110+
catch
111+
{
112+
sw.Stop();
113+
step.ElapsedMs = sw.ElapsedMilliseconds;
114+
step.Status = FlowStepStatus.Failed;
115+
throw;
116+
}
117+
}
118+
119+
public void Fail(string name, string reason = null)
120+
{
121+
var step = new FlowStep
122+
{
123+
Name = name,
124+
Status = FlowStepStatus.Failed,
125+
Detail = reason
126+
};
127+
AddStep(step);
128+
}
129+
130+
public void Skip(string name, string reason = null)
131+
{
132+
var step = new FlowStep
133+
{
134+
Name = name,
135+
Status = FlowStepStatus.Skipped,
136+
Detail = reason
137+
};
138+
AddStep(step);
139+
}
140+
141+
public void Branch(string name)
142+
{
143+
var step = new FlowStep
144+
{
145+
Name = name,
146+
Status = FlowStepStatus.InProgress
147+
};
148+
AddStep(step);
149+
_currentParent = step;
150+
}
151+
152+
public void EndBranch()
153+
{
154+
_currentParent = null;
155+
}
156+
157+
private void AddStep(FlowStep step)
158+
{
159+
if (_currentParent != null)
160+
{
161+
_currentParent.Children.Add(step);
162+
}
163+
else
164+
{
165+
_steps.Add(step);
166+
}
167+
}
168+
169+
public void Dispose()
170+
{
171+
if (_disposed) return;
172+
_disposed = true;
173+
174+
_totalTimer.Stop();
175+
RenderFlow();
176+
}
177+
178+
private void RenderFlow()
179+
{
180+
var sb = new StringBuilder();
181+
var overallStatus = DetermineOverallStatus();
182+
183+
sb.AppendLine();
184+
sb.AppendLine($" ===== FLOW: {_flowName} ({_totalTimer.ElapsedMilliseconds}ms total) =====");
185+
sb.AppendLine();
186+
187+
for (int i = 0; i < _steps.Count; i++)
188+
{
189+
RenderStep(sb, _steps[i], " ");
190+
191+
if (i < _steps.Count - 1)
192+
{
193+
sb.AppendLine(" |");
194+
sb.AppendLine(" v");
195+
}
196+
}
197+
198+
sb.AppendLine();
199+
sb.AppendLine($" ===== FLOW RESULT: {overallStatus} =====");
200+
201+
_logger.LogTrace(sb.ToString());
202+
}
203+
204+
private void RenderStep(StringBuilder sb, FlowStep step, string indent)
205+
{
206+
var icon = step.Status switch
207+
{
208+
FlowStepStatus.Success => "[OK]",
209+
FlowStepStatus.Failed => "[FAIL]",
210+
FlowStepStatus.Skipped => "[SKIP]",
211+
FlowStepStatus.InProgress => "[...]",
212+
_ => "[?]"
213+
};
214+
215+
var timing = step.ElapsedMs > 0 ? $" ({step.ElapsedMs}ms)" : "";
216+
var detail = !string.IsNullOrEmpty(step.Detail) ? $" [{step.Detail}]" : "";
217+
218+
sb.AppendLine($"{indent}{icon} {step.Name}{timing}{detail}");
219+
220+
foreach (var child in step.Children)
221+
{
222+
RenderStep(sb, child, indent + " ");
223+
}
224+
}
225+
226+
private string DetermineOverallStatus()
227+
{
228+
foreach (var step in _steps)
229+
{
230+
if (step.Status == FlowStepStatus.Failed || HasFailedChild(step))
231+
return "FAILED";
232+
}
233+
return "SUCCESS";
234+
}
235+
236+
private bool HasFailedChild(FlowStep step)
237+
{
238+
foreach (var child in step.Children)
239+
{
240+
if (child.Status == FlowStepStatus.Failed || HasFailedChild(child))
241+
return true;
242+
}
243+
return false;
244+
}
245+
}

Idnomic/Idnomic.csproj

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22
<PropertyGroup>
3-
<TargetFrameworks>net6.0;net8.0</TargetFrameworks>
3+
<TargetFrameworks>net6.0;net8.0;net10.0</TargetFrameworks>
44
<ImplicitUsings>disable</ImplicitUsings>
55
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
66
<RootNamespace>Keyfactor.Extensions.CAPlugin.Idnomic</RootNamespace>
@@ -11,6 +11,8 @@
1111
<PackageReference Include="Keyfactor.AnyGateway.IAnyCAPlugin" Version="3.0.0" />
1212
<PackageReference Include="Keyfactor.Logging" Version="1.1.1" />
1313
<PackageReference Include="Keyfactor.PKI" Version="5.5.0" />
14+
<PackageReference Include="System.Drawing.Common" Version="10.0.5" />
15+
<PackageReference Include="System.Security.Cryptography.Pkcs" Version="10.0.5" />
1416
<PackageReference Include="System.ServiceModel.Primitives" Version="6.0.0" />
1517
<PackageReference Include="System.ServiceModel.Http" Version="6.0.0" />
1618
</ItemGroup>

0 commit comments

Comments
 (0)