diff --git a/.gitignore b/.gitignore
index 8932a7e..6be482b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -352,3 +352,4 @@ healthchecksdb
logs
*.pem
*.crt
+.claude/settings.local.json
diff --git a/CHANGELOG.md b/CHANGELOG.md
index e2b85e7..2525ce1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,7 @@
+- 1.2.2
+ - Fixed Sync Issues at CA Level, was ignoring and always syncing at pool level
+- 1.2.1
+ - Doc Updates
- 1.2.0
- Added Enable Flag
- Dual Build Support
diff --git a/GCPCAS/Client/GCPCASClient.cs b/GCPCAS/Client/GCPCASClient.cs
index e3ed3e3..392c2c7 100644
--- a/GCPCAS/Client/GCPCASClient.cs
+++ b/GCPCAS/Client/GCPCASClient.cs
@@ -59,7 +59,7 @@ public class GCPCASClient : IGCPCASClient
/// The CA Pool ID in GCP CAS to use for certificate operations. If the CA Pool has resource name projects/my-project/locations/us-central1/caPools/my-pool, this field should be set to my-pool
/// The CA ID of a CA in the same CA Pool as CAPool. For example, to issue certificates from a CA with resource name projects/my-project/locations/us-central1/caPools/my-pool/certificateAuthorities/my-ca, this field should be set to my-ca.
public GCPCASClient(string locationId, string projectId, string caPool, string caId)
- {
+ {
_logger = LogHandler.GetClassLogger();
_logger.MethodEntry();
_logger.LogDebug($"Creating GCP CA Services Client with Location: {locationId}, Project ID: {projectId}, CA Pool: {caPool}, CA ID: {caId}");
@@ -84,13 +84,13 @@ public override string ToString()
///
///
public Task Enable()
- {
+ {
_logger.MethodEntry();
if (!_clientIsEnabled)
{
_logger.LogDebug($"Enabling GCPCAS client {this.ToString()}");
_clientIsEnabled = true;
- }
+ }
_logger.MethodExit();
return Task.CompletedTask;
}
@@ -100,7 +100,7 @@ public Task Enable()
///
///
public Task Disable()
- {
+ {
_logger.MethodEntry();
if (_clientIsEnabled)
{
@@ -118,12 +118,12 @@ public Task Disable()
/// A indicating if the client is enabled.
///
public bool IsEnabled()
- {
+ {
_logger.MethodEntry();
_logger.MethodExit();
return _clientIsEnabled;
- }
-
+ }
+
///
/// Attempts to connect to the GCP CAS service to verify connectivity. Verifies that the GCP Application Default Credentials are properly configured.
///
@@ -131,53 +131,53 @@ public bool IsEnabled()
/// Returns nothing if the connection is successful.
///
/// Thrown if the GCP Application Default Credentials are not properly configured, if the GCP CAS CA Pool/CA is not found/is not compatible, or if the was not enabled via the method.
- public async Task ValidateConnection()
- {
- _logger.MethodEntry();
- EnsureClientIsEnabled();
-
- if (string.IsNullOrEmpty(_caId))
- {
- _logger.LogTrace($"Validating CA Pool {_caPool} since no specific CA ID was provided");
-
- CaPoolName poolName = new CaPoolName(_projectId, _locationId, _caPool);
- CaPool pool = await _client.GetCaPoolAsync(poolName);
-
- if (pool.Tier != CaPool.Types.Tier.Enterprise)
- {
- string error = $"CA Pool {_caPool} is in Tier {pool.Tier}, expected {CaPool.Types.Tier.Enterprise}.";
- _logger.LogError(error);
- throw new Exception(error);
- }
-
- _logger.LogDebug($"CA Pool {_caPool} is Enterprise tier and valid.");
- _logger.MethodExit();
- return;
- }
-
- _logger.LogTrace($"Searching for CA called {_caId} in CA Pool {_caPool}");
- CertificateAuthorityName caName = new CertificateAuthorityName(_projectId, _locationId, _caPool, _caId);
- CertificateAuthority ca = await _client.GetCertificateAuthorityAsync(caName);
-
- _logger.LogDebug($"Found CA {ca.CertificateAuthorityName.CertificateAuthorityId} in CA Pool {ca.CertificateAuthorityName.CaPoolId}");
-
- if (ca.State != CertificateAuthority.Types.State.Enabled)
- {
- string error = $"CA {_caId} is in state {ca.State}. Expected Enabled.";
- _logger.LogError(error);
- throw new Exception(error);
- }
-
- if (ca.Tier != CaPool.Types.Tier.Enterprise)
- {
- string error = $"CA {_caId} is in tier {ca.Tier}. Only Enterprise tier is supported.";
- _logger.LogError(error);
- throw new Exception(error);
- }
-
- _logger.LogDebug($"{nameof(GCPCASClient)} is compatible with CA {_caId} in Pool {_caPool}.");
- _logger.MethodExit();
- }
+ public async Task ValidateConnection()
+ {
+ _logger.MethodEntry();
+ EnsureClientIsEnabled();
+
+ if (string.IsNullOrEmpty(_caId))
+ {
+ _logger.LogTrace($"Validating CA Pool {_caPool} since no specific CA ID was provided");
+
+ CaPoolName poolName = new CaPoolName(_projectId, _locationId, _caPool);
+ CaPool pool = await _client.GetCaPoolAsync(poolName);
+
+ if (pool.Tier != CaPool.Types.Tier.Enterprise)
+ {
+ string error = $"CA Pool {_caPool} is in Tier {pool.Tier}, expected {CaPool.Types.Tier.Enterprise}.";
+ _logger.LogError(error);
+ throw new Exception(error);
+ }
+
+ _logger.LogDebug($"CA Pool {_caPool} is Enterprise tier and valid.");
+ _logger.MethodExit();
+ return;
+ }
+
+ _logger.LogTrace($"Searching for CA called {_caId} in CA Pool {_caPool}");
+ CertificateAuthorityName caName = new CertificateAuthorityName(_projectId, _locationId, _caPool, _caId);
+ CertificateAuthority ca = await _client.GetCertificateAuthorityAsync(caName);
+
+ _logger.LogDebug($"Found CA {ca.CertificateAuthorityName.CertificateAuthorityId} in CA Pool {ca.CertificateAuthorityName.CaPoolId}");
+
+ if (ca.State != CertificateAuthority.Types.State.Enabled)
+ {
+ string error = $"CA {_caId} is in state {ca.State}. Expected Enabled.";
+ _logger.LogError(error);
+ throw new Exception(error);
+ }
+
+ if (ca.Tier != CaPool.Types.Tier.Enterprise)
+ {
+ string error = $"CA {_caId} is in tier {ca.Tier}. Only Enterprise tier is supported.";
+ _logger.LogError(error);
+ throw new Exception(error);
+ }
+
+ _logger.LogDebug($"{nameof(GCPCASClient)} is compatible with CA {_caId} in Pool {_caPool}.");
+ _logger.MethodExit();
+ }
///
@@ -195,84 +195,100 @@ public async Task ValidateConnection()
///
/// Thrown if the is null or if the operation fails.
///
- public async Task DownloadAllIssuedCertificates(BlockingCollection certificatesBuffer, CancellationToken cancelToken, DateTime? issuedAfter = null)
- {
- _logger.MethodEntry();
- EnsureClientIsEnabled();
-
- if (certificatesBuffer == null)
- {
- string message = "Failed to download issued certificates - certificatesBuffer is null";
- _logger.LogError(message);
- throw new ArgumentNullException(nameof(certificatesBuffer), message);
- }
-
- _logger.LogTrace($"Setting up {typeof(ListCertificatesRequest).ToString()} with {this.ToString()}");
-
- ListCertificatesRequest request = new ListCertificatesRequest
- {
- ParentAsCaPoolName = new CaPoolName(_projectId, _locationId, _caPool),
- };
-
- if (issuedAfter != null)
- {
- Timestamp ts = Timestamp.FromDateTime(issuedAfter.Value.ToUniversalTime());
- _logger.LogDebug($"Filtering issued certificates by update_time >= {ts}");
- request.Filter = $"update_time >= {ts}";
- }
-
- _logger.LogTrace($"Setting up {typeof(CallSettings).ToString()} with provided {typeof(CancellationToken).ToString()} {this.ToString()}");
- CallSettings settings = CallSettings.FromCancellationToken(cancelToken);
-
- _logger.LogDebug($"Downloading all issued certificates from GCP CAS {this.ToString()}");
- PagedAsyncEnumerable certificates = _client.ListCertificatesAsync(request, settings);
-
- int pageNumber = 0;
- int numberOfCertificates = 0;
-
- try
- {
- await foreach (var response in certificates.AsRawResponses())
- {
- if (response.Certificates == null)
- {
- _logger.LogWarning($"GCP returned null certificate list for page number {pageNumber} - continuing {this.ToString()}");
- continue;
- }
-
- foreach (Certificate certificate in response.Certificates)
- {
- certificatesBuffer.Add(AnyCAPluginCertificateFromGCPCertificate(certificate));
- numberOfCertificates++;
- _logger.LogDebug($"Found Certificate with name {certificate.CertificateName.CertificateId} {this.ToString()}");
- }
-
- _logger.LogTrace($"Fetched page {pageNumber} - Next Page Token: {response.NextPageToken}");
- pageNumber++;
- }
- }
- catch (RpcException ex) when (ex.StatusCode == StatusCode.ResourceExhausted)
- {
- _logger.LogError($"Rate limit exceeded while fetching certificates: {ex.Message}");
- throw;
- }
- catch (OperationCanceledException)
- {
- _logger.LogWarning("Certificate download operation was canceled.");
- throw;
- }
- catch (Exception ex)
- {
- _logger.LogError($"Unexpected error while fetching certificates: {ex.Message}");
- throw;
- }
- finally
- {
- certificatesBuffer.CompleteAdding();
- _logger.LogDebug($"Fetched {certificatesBuffer.Count} certificates from GCP over {pageNumber} pages.");
- }
- _logger.MethodExit();
- return numberOfCertificates;
+ public async Task DownloadAllIssuedCertificates(BlockingCollection certificatesBuffer, CancellationToken cancelToken, DateTime? issuedAfter = null)
+ {
+ _logger.MethodEntry();
+ EnsureClientIsEnabled();
+
+ if (certificatesBuffer == null)
+ {
+ string message = "Failed to download issued certificates - certificatesBuffer is null";
+ _logger.LogError(message);
+ throw new ArgumentNullException(nameof(certificatesBuffer), message);
+ }
+
+ _logger.LogTrace($"Setting up {typeof(ListCertificatesRequest).ToString()} with {this.ToString()}");
+
+ ListCertificatesRequest request = new ListCertificatesRequest
+ {
+ ParentAsCaPoolName = new CaPoolName(_projectId, _locationId, _caPool),
+ };
+
+ string caFilter = null;
+ if (!string.IsNullOrEmpty(_caId))
+ {
+ caFilter = _caId;
+ _logger.LogDebug($"Will filter certificates client-side by issuing CA ID: {caFilter}");
+ }
+
+ if (issuedAfter != null)
+ {
+ Timestamp ts = Timestamp.FromDateTime(issuedAfter.Value.ToUniversalTime());
+ _logger.LogDebug($"Filtering issued certificates by update_time >= {ts}");
+ request.Filter = $"update_time >= {ts}";
+ }
+
+ _logger.LogTrace($"Setting up {typeof(CallSettings).ToString()} with provided {typeof(CancellationToken).ToString()} {this.ToString()}");
+ CallSettings settings = CallSettings.FromCancellationToken(cancelToken);
+
+ _logger.LogDebug($"Downloading all issued certificates from GCP CAS {this.ToString()}");
+ PagedAsyncEnumerable certificates = _client.ListCertificatesAsync(request, settings);
+
+ int pageNumber = 0;
+ int numberOfCertificates = 0;
+
+ try
+ {
+ await foreach (var response in certificates.AsRawResponses())
+ {
+ if (response.Certificates == null)
+ {
+ _logger.LogWarning($"GCP returned null certificate list for page number {pageNumber} - continuing {this.ToString()}");
+ continue;
+ }
+
+ foreach (Certificate certificate in response.Certificates)
+ {
+ if (caFilter != null)
+ {
+ CertificateAuthorityName issuer = CertificateAuthorityName.Parse(certificate.IssuerCertificateAuthority);
+ if (issuer.CertificateAuthorityId != caFilter)
+ {
+ _logger.LogTrace($"Skipping certificate {certificate.CertificateName.CertificateId} - issued by {issuer.CertificateAuthorityId}, not {caFilter}");
+ continue;
+ }
+ }
+ certificatesBuffer.Add(AnyCAPluginCertificateFromGCPCertificate(certificate));
+ numberOfCertificates++;
+ _logger.LogDebug($"Found Certificate with name {certificate.CertificateName.CertificateId} {this.ToString()}");
+ }
+
+ _logger.LogTrace($"Fetched page {pageNumber} - Next Page Token: {response.NextPageToken}");
+ pageNumber++;
+ }
+ }
+ catch (RpcException ex) when (ex.StatusCode == StatusCode.ResourceExhausted)
+ {
+ _logger.LogError($"Rate limit exceeded while fetching certificates: {ex.Message}");
+ throw;
+ }
+ catch (OperationCanceledException)
+ {
+ _logger.LogWarning("Certificate download operation was canceled.");
+ throw;
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError($"Unexpected error while fetching certificates: {ex.Message}");
+ throw;
+ }
+ finally
+ {
+ certificatesBuffer.CompleteAdding();
+ _logger.LogDebug($"Fetched {certificatesBuffer.Count} certificates from GCP over {pageNumber} pages.");
+ }
+ _logger.MethodExit();
+ return numberOfCertificates;
}
@@ -287,7 +303,7 @@ public async Task DownloadAllIssuedCertificates(BlockingCollection and task result as a containing the downloaded certificate.
///
public async Task DownloadCertificate(string certificateId)
- {
+ {
_logger.MethodEntry();
EnsureClientIsEnabled();
@@ -300,13 +316,13 @@ public async Task DownloadCertificate(string certificate
};
Certificate certificate = await _client.GetCertificateAsync(request);
- _logger.LogTrace("GetCertificateAsync succeeded");
+ _logger.LogTrace("GetCertificateAsync succeeded");
_logger.MethodExit();
return AnyCAPluginCertificateFromGCPCertificate(certificate);
}
private AnyCAPluginCertificate AnyCAPluginCertificateFromGCPCertificate(Certificate certificate)
- {
+ {
_logger.MethodEntry();
string productId = "";
if (certificate.CertificateTemplateAsCertificateTemplateName == null)
@@ -328,7 +344,7 @@ private AnyCAPluginCertificate AnyCAPluginCertificateFromGCPCertificate(Certific
revocationDate = certificate.RevocationDetails.RevocationTime.ToDateTime();
status = EndEntityStatus.REVOKED;
revocationReason = (int)certificate.RevocationDetails.RevocationState;
- }
+ }
_logger.MethodExit();
return new AnyCAPluginCertificate
{
@@ -355,23 +371,23 @@ private AnyCAPluginCertificate AnyCAPluginCertificateFromGCPCertificate(Certific
public async Task Enroll(ICreateCertificateRequestBuilder createCertificateRequestBuilder, CancellationToken cancelToken)
{
try
- {
+ {
_logger.MethodEntry();
EnsureClientIsEnabled();
CreateCertificateRequest request = createCertificateRequestBuilder.Build(_locationId, _projectId, _caPool, _caId);
- if (request != null)
- {
- _logger.LogTrace($"Request Json {JsonConvert.SerializeObject(request)}");
+ if (request != null)
+ {
+ _logger.LogTrace($"Request Json {JsonConvert.SerializeObject(request)}");
}
Certificate certificate = await _client.CreateCertificateAsync(request, cancelToken);
- if (certificate != null)
- {
- _logger.LogTrace($"Response Json {JsonConvert.SerializeObject(certificate)}");
- }
+ if (certificate != null)
+ {
+ _logger.LogTrace($"Response Json {JsonConvert.SerializeObject(certificate)}");
+ }
_logger.MethodExit();
return new EnrollmentResult
{
@@ -421,7 +437,7 @@ public async Task Enroll(ICreateCertificateRequestBuilder crea
///
///
public Task RevokeCertificate(string certificateId, RevocationReason reason)
- {
+ {
_logger.MethodEntry();
EnsureClientIsEnabled();
@@ -444,7 +460,7 @@ public Task RevokeCertificate(string certificateId, RevocationReason reason)
/// A of containing the available s.
///
public List GetTemplates()
- {
+ {
_logger.MethodEntry();
EnsureClientIsEnabled();