Skip to content
Open
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
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
## 1.0.2
* bug fix: _certDataReader is now initialized in the Initialize method

## 1.0.1
* added retrieval of roles associated with enrolled certificates via metadata for Vault Enterprise users

Expand Down
5 changes: 4 additions & 1 deletion hashicorp-vault-cagateway/APIProxy/CertResponse.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@ public class CertResponse
public string Certificate { get; set; }

[JsonPropertyName("revocation_time_rfc3339")]
public DateTime? RevocationTime { get; set; }
public string RevocationTime { get; set; }

[JsonPropertyName("revocation_time")]
public int? RevocationTimestamp { get; set; }

[JsonPropertyName("issuer_id")]
public string IssuerId { get; set; }
Expand Down
6 changes: 6 additions & 0 deletions hashicorp-vault-cagateway/APIProxy/WrappedResponse.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ namespace Keyfactor.Extensions.CAPlugin.HashicorpVault.APIProxy
{
public class WrappedResponse<T>
{
[JsonPropertyName("request_id")]
public string RequestId { get; set; }

[JsonPropertyName("lease_id")]
public string LeaseId { get; set; }

Expand All @@ -30,6 +33,9 @@ public class WrappedResponse<T>
[JsonPropertyName("mount_point")]
public string MountPoint { get; set; }

[JsonPropertyName("mount_type")]
public string MountType { get; set; }

[JsonPropertyName("mount_running_plugin_version")]
public string PluginVersion { get; set; }

Expand Down
30 changes: 19 additions & 11 deletions hashicorp-vault-cagateway/Client/HashicorpVaultClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -131,9 +131,15 @@ public async Task<CertResponse> GetCertificate(string certSerial)

try
{
var response = await _vaultHttp.GetAsync<CertResponse>($"cert/{certSerial}");
var response = await _vaultHttp.GetAsync<WrappedResponse<CertResponse>>($"cert/{certSerial}");

logger.LogTrace($"successfully received a response for certificate with serial number: {certSerial}");
return response;
logger.LogTrace($"--response data--");
logger.LogTrace($"cert string: {response.Data?.Certificate}");
logger.LogTrace($"revocation time: {response.Data?.RevocationTime}");


return response.Data;
}
catch (Exception ex)
{
Expand All @@ -152,9 +158,9 @@ public async Task<RevokeResponse> RevokeCertificate(string serial)
logger.LogTrace($"making request to revoke cert with serial: {serial}");
try
{
var response = await _vaultHttp.PostAsync<RevokeResponse>("revoke", new RevokeRequest(serial));
logger.LogTrace($"successfully revoked cert with serial {serial}, revocation time: {response.RevocationTime}");
return response;
var response = await _vaultHttp.PostAsync<WrappedResponse<RevokeResponse>>("revoke", new RevokeRequest(serial));
logger.LogTrace($"successfully revoked cert with serial {serial}, revocation time: {response.Data.RevocationTime}");
return response.Data;
}
catch (Exception ex)
{
Expand Down Expand Up @@ -189,7 +195,7 @@ public async Task<bool> PingServer()
}

/// <summary>
/// Retreives all serial numbers for issued certificates
/// Retrieves all serial numbers for issued certificates
/// </summary>
/// <returns>a list of the certificate serial number strings</returns>
public async Task<List<string>> GetAllCertSerialNumbers()
Expand All @@ -199,7 +205,7 @@ public async Task<List<string>> GetAllCertSerialNumbers()
try
{
var res = await _vaultHttp.GetAsync<WrappedResponse<KeyedList>>("certs/?list=true");
return res.Data.Entries;
return res.Data?.Entries;
Copy link

Copilot AI Dec 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Potential null reference issue: The code accesses res.Data?.Entries without null-checking res itself. If the GetAsync call returns null, this will throw a NullReferenceException. Consider adding a null check for the response object before accessing its Data property.

Copilot uses AI. Check for mistakes.
}
catch (Exception ex)
{
Expand All @@ -215,8 +221,8 @@ private async Task<List<string>> GetRevokedSerialNumbers()
var keys = new List<string>();
try
{
var res = await _vaultHttp.GetAsync<KeyedList>("certs/revoked");
keys = res.Entries;
var res = await _vaultHttp.GetAsync<WrappedResponse<KeyedList>>("certs/revoked");
keys = res.Data?.Entries;
Copy link

Copilot AI Dec 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Potential null reference issue: The code accesses res.Data?.Entries without null-checking res itself. If the GetAsync call returns null, this will throw a NullReferenceException. Consider adding a null check for the response object before accessing its Data property.

Copilot uses AI. Check for mistakes.
}
catch (Exception ex)
{
Expand Down Expand Up @@ -247,7 +253,7 @@ public async Task<List<string>> GetRoleNamesAsync()
}

/// <summary>
/// Retreives the metadata for the certificate
/// Retrieves the metadata for the certificate
/// </summary>
/// <param name="certSerial"></param>
/// <returns></returns>
Expand Down Expand Up @@ -275,7 +281,7 @@ public async Task<MetadataResponse> GetCertMetadata(string certSerial)
}
catch (Exception ex)
{
logger.LogError($"an error occurred when attempting to retreive the certificate metadata: {ex.Message}");
logger.LogError($"an error occurred when attempting to retrieve the certificate metadata: {ex.Message}");
throw;
}
finally { logger.MethodExit(); }
Expand Down Expand Up @@ -317,5 +323,7 @@ private static string ConvertSerialToTrackingId(string serialNumber)

return serialNumber.Replace(":", "-");
}


}
}
38 changes: 26 additions & 12 deletions hashicorp-vault-cagateway/Client/VaultHttp.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
using System.Collections.Generic;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading;
using System.Threading.Tasks;

namespace Keyfactor.Extensions.CAPlugin.HashicorpVault.Client
Expand All @@ -36,12 +37,12 @@ public VaultHttp(string host, string mountPoint, string authToken, string nameSp
_serializerOptions = new()
{
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingDefault,
RespectNullableAnnotations = true,
PropertyNameCaseInsensitive = true,
PreferredObjectCreationHandling = JsonObjectCreationHandling.Replace,
RespectNullableAnnotations = true,
PreferredObjectCreationHandling = JsonObjectCreationHandling.Replace
};

var restClientOptions = new RestClientOptions($"{host.TrimEnd('/')}/v1") { ThrowOnAnyError = true };
var restClientOptions = new RestClientOptions($"{host.TrimEnd('/')}/v1") { ThrowOnAnyError = true };
_restClient = new RestClient(restClientOptions, configureSerialization: s => s.UseSystemTextJson(_serializerOptions));

_mountPoint = mountPoint.TrimStart('/').TrimEnd('/'); // remove leading and trailing slashes
Expand Down Expand Up @@ -69,19 +70,32 @@ public VaultHttp(string host, string mountPoint, string authToken, string nameSp
public async Task<T> GetAsync<T>(string path, Dictionary<string, string> parameters = null)
{
logger.MethodEntry();
logger.LogTrace($"preparing to send GET request to {path} with parameters {JsonSerializer.Serialize(parameters)}");
logger.LogTrace($"will attempt to deserialize the response into a {typeof(T)}");
logger.LogTrace($"preparing to send GET request to {_mountPoint}/{path} with parameters {JsonSerializer.Serialize(parameters)}");

try
{
var request = new RestRequest($"{_mountPoint}/{path}", Method.Get);
if (parameters != null) { request.AddJsonBody(parameters); }
if (parameters != null && parameters.Keys.Count > 0) { request.AddJsonBody(parameters); }
var response = await _restClient.ExecuteGetAsync(request);

logger.LogTrace($"raw response: {JsonSerializer.Serialize(response)}");

logger.LogTrace($"response content: {response.Content}");

logger.LogTrace($"response status: {response.StatusCode}");

var response = await _restClient.ExecuteGetAsync<T>(request);
logger.LogTrace($"raw response: {response.Content}");
logger.LogTrace($"response error msg: {response.ErrorMessage}");

response.ThrowIfError();
if (string.IsNullOrEmpty(response.Content)) throw new Exception(response.ErrorMessage ?? "no content returned from Vault");

return response.Data;
logger.LogTrace($"deserializing the response into a {typeof(T)}");

var deserialized = JsonSerializer.Deserialize<T>(response.Content, _serializerOptions);

logger.LogTrace($"successfully deserialized the response");

return deserialized;
}
catch (Exception ex)
{
Expand All @@ -108,8 +122,8 @@ public async Task<T> PostAsync<T>(string path, dynamic parameters = default)
var request = new RestRequest(resourcePath, Method.Post);
if (parameters != null)
{
string serializedParams = JsonSerializer.Serialize(parameters, _serializerOptions);
logger.LogTrace($"serialized parameters (from {parameters.GetType()?.Name}): {serializedParams}");
string serializedParams = JsonSerializer.Serialize(parameters);
logger.LogTrace($"deserialized parameters (from {parameters.GetType()?.Name}): {serializedParams}");
Copy link

Copilot AI Dec 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The log message refers to "deserialized parameters" but should be "serialized parameters" since the code is serializing the parameters object into JSON format.

Suggested change
logger.LogTrace($"deserialized parameters (from {parameters.GetType()?.Name}): {serializedParams}");
logger.LogTrace($"serialized parameters (from {parameters.GetType()?.Name}): {serializedParams}");

Copilot uses AI. Check for mistakes.
request.AddJsonBody(serializedParams);
}

Expand All @@ -127,7 +141,7 @@ public async Task<T> PostAsync<T>(string path, dynamic parameters = default)

if (response.StatusCode == System.Net.HttpStatusCode.BadRequest)
{
errorResponse = JsonSerializer.Deserialize<ErrorResponse>(response.Content!);
errorResponse = JsonSerializer.Deserialize<ErrorResponse>(response.Content ?? "no content");
string allErrors = "(Bad Request)";
if (errorResponse?.Errors.Count > 0)
{
Expand Down
Loading
Loading