From e9c7f02436a319ee8a6f9a2e9a8071d51f90be1a Mon Sep 17 00:00:00 2001 From: Brian Hill Date: Wed, 8 Apr 2026 08:19:18 -0400 Subject: [PATCH] Add comprehensive logging and null reference protection Add extensive trace/error logging and null guards across all project files to aid debugging and prevent NullReferenceExceptions: - SslStoreCaProxy.cs: Harden Synchronize, Enroll, Revoke, GetSingleRecord, Initialize, ValidateEmails, GetEnrollmentResult - SslStoreClient.cs: Add logging/null checks to all API methods, constructor, and query pagination - KeyfactorClient.cs: Add logging/null checks to constructor, template update, and template query pagination - RequestManager.cs: Add logging/null checks to all request builders, MapReturnStatus, GetCertificateContent, and contact helpers - .gitignore: Add .claude/ directory Fixes sync NullReferenceException and operator precedence bug. AB#84952 Co-Authored-By: Claude Opus 4.6 (1M context) --- .gitignore | 3 + SslStoreCaProxy/Client/KeyfactorClient.cs | 151 +++- SslStoreCaProxy/Client/SslStoreClient.cs | 292 ++++++-- SslStoreCaProxy/RequestManager.cs | 271 +++++-- SslStoreCaProxy/SslStoreCaProxy.cs | 818 +++++++++++++++++----- 5 files changed, 1214 insertions(+), 321 deletions(-) diff --git a/.gitignore b/.gitignore index dfcfd56..8a88a85 100644 --- a/.gitignore +++ b/.gitignore @@ -348,3 +348,6 @@ MigrationBackup/ # Ionide (cross platform F# VS Code tools) working folder .ionide/ + +# Claude Code +.claude/ diff --git a/SslStoreCaProxy/Client/KeyfactorClient.cs b/SslStoreCaProxy/Client/KeyfactorClient.cs index 900e096..5528388 100644 --- a/SslStoreCaProxy/Client/KeyfactorClient.cs +++ b/SslStoreCaProxy/Client/KeyfactorClient.cs @@ -23,36 +23,86 @@ public sealed class KeyfactorClient: LoggingClientBase, IKeyfactorClient public KeyfactorClient(ICAConnectorConfigProvider configProvider) { - var keyfactorBaseUrl = new Uri(configProvider.CAConnectionData[Constants.KeyfactorApiUrl].ToString()); - var keyfactorAuth = configProvider.CAConnectionData[Constants.KeyfactorApiUserId] + ":" + configProvider.CAConnectionData[Constants.KeyfactorApiPassword]; - var plainTextBytes = Encoding.UTF8.GetBytes(keyfactorAuth); - - var clientHandler = new WebRequestHandler(); - RestClient = new HttpClient(clientHandler, true) { BaseAddress = keyfactorBaseUrl }; - RestClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); - RestClient.DefaultRequestHeaders.Add("x-keyfactor-requested-with", "APIClient"); - RestClient.DefaultRequestHeaders.Add("Authorization", "Basic " + Convert.ToBase64String(plainTextBytes)); + Logger.Trace("KeyfactorClient constructor called."); + try + { + if (configProvider?.CAConnectionData == null) + { + Logger.Error("KeyfactorClient: configProvider or CAConnectionData is null."); + throw new ArgumentNullException(nameof(configProvider), "configProvider or CAConnectionData is null."); + } + + if (!configProvider.CAConnectionData.ContainsKey(Constants.KeyfactorApiUrl)) + { + Logger.Error($"KeyfactorClient: Missing required config key '{Constants.KeyfactorApiUrl}'."); + throw new InvalidOperationException($"Missing required config key '{Constants.KeyfactorApiUrl}'."); + } + + var apiUrlValue = configProvider.CAConnectionData[Constants.KeyfactorApiUrl]?.ToString(); + Logger.Trace($"KeyfactorClient: KeyfactorApiUrl={apiUrlValue ?? "(null)"}"); + + if (string.IsNullOrEmpty(apiUrlValue)) + { + Logger.Error("KeyfactorClient: KeyfactorApiUrl value is null or empty."); + throw new InvalidOperationException("KeyfactorApiUrl value is null or empty."); + } + + var keyfactorBaseUrl = new Uri(apiUrlValue); + + var userId = configProvider.CAConnectionData.ContainsKey(Constants.KeyfactorApiUserId) + ? configProvider.CAConnectionData[Constants.KeyfactorApiUserId]?.ToString() ?? "" + : ""; + var password = configProvider.CAConnectionData.ContainsKey(Constants.KeyfactorApiPassword) + ? configProvider.CAConnectionData[Constants.KeyfactorApiPassword]?.ToString() ?? "" + : ""; + + if (string.IsNullOrEmpty(userId)) + Logger.Warn("KeyfactorClient: KeyfactorApiUserId is null or empty."); + + Logger.Trace($"KeyfactorClient: Configuring with userId={userId}, BaseAddress={keyfactorBaseUrl}"); + + var keyfactorAuth = userId + ":" + password; + var plainTextBytes = Encoding.UTF8.GetBytes(keyfactorAuth); + + var clientHandler = new WebRequestHandler(); + RestClient = new HttpClient(clientHandler, true) { BaseAddress = keyfactorBaseUrl }; + RestClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); + RestClient.DefaultRequestHeaders.Add("x-keyfactor-requested-with", "APIClient"); + RestClient.DefaultRequestHeaders.Add("Authorization", "Basic " + Convert.ToBase64String(plainTextBytes)); + + Logger.Trace("KeyfactorClient: RestClient configured successfully."); + } + catch (Exception ex) + { + Logger.Error($"KeyfactorClient constructor failed: {ex.Message}\n{ex.StackTrace}"); + throw; + } } public async Task