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
20 changes: 0 additions & 20 deletions .github/workflows/keyfactor-bootstrap-workflow-v3.yml

This file was deleted.

12 changes: 10 additions & 2 deletions .github/workflows/keyfactor-bootstrap-workflow.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,17 @@ on:

jobs:
call-starter-workflow:
uses: keyfactor/actions/.github/workflows/starter.yml@v2
uses: keyfactor/actions/.github/workflows/starter.yml@v4
with:
command_token_url: ${{ vars.COMMAND_TOKEN_URL }}
command_hostname: ${{ vars.COMMAND_HOSTNAME }}
command_base_api_path: ${{ vars.COMMAND_API_PATH }}
secrets:
token: ${{ secrets.V2BUILDTOKEN}}
APPROVE_README_PUSH: ${{ secrets.APPROVE_README_PUSH}}
gpg_key: ${{ secrets.KF_GPG_PRIVATE_KEY }}
gpg_pass: ${{ secrets.KF_GPG_PASSPHRASE }}
scan_token: ${{ secrets.SAST_TOKEN }}
entra_username: ${{ secrets.DOCTOOL_ENTRA_USERNAME }}
entra_password: ${{ secrets.DOCTOOL_ENTRA_PASSWD }}
command_client_id: ${{ secrets.COMMAND_CLIENT_ID }}
command_client_secret: ${{ secrets.COMMAND_CLIENT_SECRET }}
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
## 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

## 1.0.0
* initial release
7 changes: 5 additions & 2 deletions hashicorp-vault-cagateway/APIProxy/CertResponse.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2024 Keyfactor
// Copyright 2025 Keyfactor
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.
// You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS,
Expand All @@ -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
2 changes: 1 addition & 1 deletion hashicorp-vault-cagateway/APIProxy/ErrorResponse.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2024 Keyfactor
// Copyright 2025 Keyfactor
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.
// You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS,
Expand Down
2 changes: 1 addition & 1 deletion hashicorp-vault-cagateway/APIProxy/KeyedList.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2024 Keyfactor
// Copyright 2025 Keyfactor
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.
// You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS,
Expand Down
30 changes: 30 additions & 0 deletions hashicorp-vault-cagateway/APIProxy/MetadataResponse.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Copyright 2025 Keyfactor
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.
// You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions
// and limitations under the License.

using System;
using System.Text.Json.Serialization;

namespace Keyfactor.Extensions.CAPlugin.HashicorpVault.APIProxy
{
public class MetadataResponse
{
[JsonPropertyName("issuer_id")]
public string IssuerId { get; set; }

[JsonPropertyName("expiration")]
public DateTime? Expiration { get; set; }

[JsonPropertyName("cert_metadata")]
public string CertMetadata { get; set; }

[JsonPropertyName("role")]
public string Role { get; set; }

[JsonPropertyName("serial_number")]
public string SerialNumber { get; set; }
}
}
2 changes: 1 addition & 1 deletion hashicorp-vault-cagateway/APIProxy/RevokeRequest.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2024 Keyfactor
// Copyright 2025 Keyfactor
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.
// You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS,
Expand Down
2 changes: 1 addition & 1 deletion hashicorp-vault-cagateway/APIProxy/RevokeResponse.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2024 Keyfactor
// Copyright 2025 Keyfactor
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.
// You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS,
Expand Down
2 changes: 1 addition & 1 deletion hashicorp-vault-cagateway/APIProxy/SealStatusResponse.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2024 Keyfactor
// Copyright 2025 Keyfactor
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.
// You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS,
Expand Down
2 changes: 1 addition & 1 deletion hashicorp-vault-cagateway/APIProxy/SignRequest.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2024 Keyfactor
// Copyright 2025 Keyfactor
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.
// You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS,
Expand Down
2 changes: 1 addition & 1 deletion hashicorp-vault-cagateway/APIProxy/SignResponse.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2024 Keyfactor
// Copyright 2025 Keyfactor
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.
// You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS,
Expand Down
2 changes: 1 addition & 1 deletion hashicorp-vault-cagateway/APIProxy/TokenLookupResponse.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2024 Keyfactor
// Copyright 2025 Keyfactor
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.
// You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS,
Expand Down
8 changes: 7 additions & 1 deletion hashicorp-vault-cagateway/APIProxy/WrappedResponse.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2024 Keyfactor
// Copyright 2025 Keyfactor
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.
// You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS,
Expand All @@ -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
63 changes: 53 additions & 10 deletions hashicorp-vault-cagateway/Client/HashicorpVaultClient.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2024 Keyfactor
// Copyright 2025 Keyfactor
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.
// You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS,
Expand Down 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;
}
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;
}
catch (Exception ex)
{
Expand Down Expand Up @@ -246,6 +252,41 @@ public async Task<List<string>> GetRoleNamesAsync()
finally { logger.MethodExit(); }
}

/// <summary>
/// Retrieves the metadata for the certificate
/// </summary>
/// <param name="certSerial"></param>
/// <returns></returns>
public async Task<MetadataResponse> GetCertMetadata(string certSerial)
{
logger.MethodEntry();

try
{
var res = await _vaultHttp.GetAsync<WrappedResponse<MetadataResponse>>($"cert-metadata/{certSerial}");
var md = res?.Data;
if (md != null)
{
logger.LogTrace($"got response from cert-metadata");
logger.LogTrace($"serial number: {md.SerialNumber}");
logger.LogTrace($"issuer id: {md.IssuerId}");
logger.LogTrace($"expiration: {md.Expiration}");
logger.LogTrace($"metadata: {md.CertMetadata}");
logger.LogTrace($"role: {md.Role}");
}
else {
logger.LogTrace($"no metadata associated with cert {certSerial} could be found.");
}
return md;
}
catch (Exception ex)
{
logger.LogError($"an error occurred when attempting to retrieve the certificate metadata: {ex.Message}");
throw;
}
finally { logger.MethodExit(); }
}

private void SetClientValuesFromConfigs(HashicorpVaultCAConfig caConfig, HashicorpVaultCATemplateConfig templateConfig)
{
logger.MethodEntry();
Expand Down Expand Up @@ -282,5 +323,7 @@ private static string ConvertSerialToTrackingId(string serialNumber)

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


}
}
37 changes: 26 additions & 11 deletions hashicorp-vault-cagateway/Client/VaultHttp.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2024 Keyfactor
// Copyright 2025 Keyfactor
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.
// You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS,
Expand All @@ -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,18 +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($"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 @@ -107,7 +122,7 @@ 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);
string serializedParams = JsonSerializer.Serialize(parameters);
logger.LogTrace($"serialized parameters (from {parameters.GetType()?.Name}): {serializedParams}");
request.AddJsonBody(serializedParams);
}
Expand All @@ -126,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
2 changes: 1 addition & 1 deletion hashicorp-vault-cagateway/Constants.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2024 Keyfactor
// Copyright 2025 Keyfactor
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.
// You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS,
Expand Down
Loading
Loading