Skip to content

Commit 7d809b0

Browse files
authored
Merge branch 'ab#84816' into release-1.2
2 parents 6fe4dd4 + c5cf433 commit 7d809b0

9 files changed

Lines changed: 519 additions & 73 deletions

File tree

GCPSecretManager/GCPClient.cs

Lines changed: 168 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
1-
using Google.Api.Gax.ResourceNames;
2-
using Google.Cloud.SecretManager.V1;
1+
using Google.Api.Gax;
2+
using Google.Api.Gax.ResourceNames;
33
using Google.Cloud.ResourceManager.V3;
4-
5-
using Microsoft.Extensions.Logging;
6-
4+
using Google.Cloud.SecretManager.V1;
5+
using Google.Protobuf.Collections;
6+
using Google.Protobuf.WellKnownTypes;
77
using Keyfactor.Logging;
8-
8+
using Microsoft.Extensions.Logging;
99
using System;
1010
using System.Collections.Generic;
1111
using System.Linq;
12-
using Google.Api.Gax;
13-
using System.Xml.Linq;
12+
using System.Reflection;
13+
using System.Reflection.Metadata;
1414

1515
namespace Keyfactor.Extensions.Orchestrator.GCPSecretManager
1616
{
@@ -80,16 +80,26 @@ public List<string> GetSecretNames()
8080
return rtnSecrets;
8181
}
8282

83-
public string GetCertificateEntry(string name)
83+
public SecretWithLabels GetCertificateEntry(string name)
8484
{
8585
_logger.MethodEntry(LogLevel.Debug);
8686

87-
string rtnValue;
87+
SecretWithLabels rtnValue = new SecretWithLabels();
8888

8989
try
9090
{
9191
AccessSecretVersionResponse version = Client.AccessSecretVersion(new AccessSecretVersionRequest() { Name = name + "/versions/latest" });
92-
rtnValue = version.Payload.Data.ToStringUtf8();
92+
rtnValue.Secret = version.Payload.Data.ToStringUtf8();
93+
rtnValue.Labels = string.Empty;
94+
95+
Secret secret = GetSecret(name);
96+
List<string> labelsString = new List<string>();
97+
foreach(var label in secret.Labels)
98+
{
99+
labelsString.Add($"{label.Key}:{label.Value}");
100+
}
101+
if (labelsString.Count > 0)
102+
rtnValue.Labels = string.Join(",", labelsString.ToArray());
93103
}
94104
catch (Exception ex)
95105
{
@@ -104,21 +114,19 @@ public string GetCertificateEntry(string name)
104114
return rtnValue;
105115
}
106116

107-
public string GetSecret(string alias)
117+
public Secret GetSecret(string alias)
108118
{
109119
_logger.MethodEntry(LogLevel.Debug);
110120
string rtnValue = string.Empty;
111121

112122
try
113123
{
114-
var secret = Client.GetSecret(
124+
return Client.GetSecret(
115125
new GetSecretRequest()
116126
{
117127
SecretName = SecretName.FromProjectSecret(ProjectId, alias)
118128
}
119129
);
120-
121-
rtnValue = secret.Name;
122130
}
123131
catch (Exception ex)
124132
{
@@ -129,14 +137,14 @@ public string GetSecret(string alias)
129137
{
130138
_logger.MethodExit(LogLevel.Debug);
131139
}
132-
133-
return rtnValue;
134140
}
135141

136-
public void AddSecret(string alias, string secretContent, bool entryExists)
142+
public bool AddSecret(string alias, string secretContent, bool entryExists, string labels = null, List<ReplicationRegion> replicationRegions = null, TimeSpan? ttlDuration = null, TimeSpan? versionDestroyTtlDuration = null)
137143
{
138144
_logger.MethodEntry(LogLevel.Debug);
139145

146+
bool rtnWarning = false;
147+
140148
try
141149
{
142150
SecretName secretName = new SecretName(ProjectId, alias);
@@ -149,12 +157,38 @@ public void AddSecret(string alias, string secretContent, bool entryExists)
149157
CreateSecretRequest secretRequest = new CreateSecretRequest();
150158
secretRequest.ParentAsProjectName = new ProjectName(ProjectId);
151159
secretRequest.SecretId = alias;
152-
secretRequest.Secret = new Secret { Replication = new Replication { Automatic = new Replication.Types.Automatic() } };
160+
161+
secretRequest.Secret = new Secret();
162+
if (ttlDuration.HasValue) secretRequest.Secret.Ttl = Duration.FromTimeSpan(ttlDuration.Value);
163+
if (versionDestroyTtlDuration.HasValue) secretRequest.Secret.VersionDestroyTtl = Duration.FromTimeSpan(versionDestroyTtlDuration.Value);
164+
if (replicationRegions == null || replicationRegions.Count == 0)
165+
{
166+
secretRequest.Secret.Replication = new Replication { Automatic = new Replication.Types.Automatic() };
167+
}
168+
else
169+
{
170+
secretRequest.Secret.Replication = new Replication { UserManaged = new Replication.Types.UserManaged() };
171+
172+
foreach (ReplicationRegion replicationRegion in replicationRegions)
173+
{
174+
Replication.Types.UserManaged.Types.Replica replica = new Replication.Types.UserManaged.Types.Replica();
175+
replica.Location = replicationRegion.Region;
176+
if (replicationRegion.KmsKeyPath != null)
177+
replica.CustomerManagedEncryption = new CustomerManagedEncryption() { KmsKeyName = replicationRegion.KmsKeyPath };
178+
179+
secretRequest.Secret.Replication.UserManaged.Replicas.Add(replica);
180+
}
181+
}
182+
183+
AssignLabels(labels, secretRequest.Secret.Labels);
153184

154185
Secret secret = Client.CreateSecret(secretRequest);
155186
}
156187

157188
//create new version
189+
ClearSecretFields(alias, labels != null, ttlDuration.HasValue, versionDestroyTtlDuration.HasValue);
190+
UpdateSecretFields(alias, labels, ttlDuration, versionDestroyTtlDuration);
191+
158192
AddSecretVersionRequest secretVersionRequest = new AddSecretVersionRequest();
159193
secretVersionRequest.ParentAsSecretName = secretName;
160194
secretVersionRequest.Payload = new SecretPayload { Data = Google.Protobuf.ByteString.CopyFromUtf8(secretContent) };
@@ -170,6 +204,8 @@ public void AddSecret(string alias, string secretContent, bool entryExists)
170204
{
171205
_logger.MethodExit(LogLevel.Debug);
172206
}
207+
208+
return rtnWarning;
173209
}
174210

175211
public void DeleteCertificate(string name)
@@ -317,10 +353,10 @@ public List<TagValue> GetTagValues(string tagName)
317353
return rtnValues;
318354
}
319355

320-
public Dictionary<string, object> GetSecretTags(string secretResource)
356+
public string GetSecretTags(string secretResource)
321357
{
322358
_logger.MethodEntry(LogLevel.Debug);
323-
Dictionary<string, object> rtnValue = new Dictionary<string, object>();
359+
string rtnValue = string.Empty;
324360
List<string> tagPairs = new List<string>();
325361

326362
ListTagBindingsRequest request = new ListTagBindingsRequest()
@@ -349,7 +385,7 @@ public Dictionary<string, object> GetSecretTags(string secretResource)
349385
}
350386

351387
if (tagPairs.Count > 0)
352-
rtnValue.Add("tags", string.Join(",", tagPairs));
388+
rtnValue = string.Join(",", tagPairs);
353389

354390
return rtnValue;
355391
}
@@ -364,7 +400,7 @@ public void SetSecretTag(string alias, string tagValue)
364400
{
365401
TagBinding = new TagBinding()
366402
{
367-
Parent = $"{ResourcePrefix}{GetSecret(alias)}",
403+
Parent = $"{ResourcePrefix}{GetSecret(alias).Name}",
368404
TagValue = tagValue
369405
}
370406
});
@@ -380,14 +416,121 @@ public void SetSecretTag(string alias, string tagValue)
380416
}
381417
}
382418

419+
public void ClearSecretFields(string alias, bool clearLabels, bool clearTtl, bool clearVersionDestroyTtl)
420+
{
421+
_logger.MethodEntry(LogLevel.Debug);
422+
423+
Secret secret = new Secret { SecretName = new SecretName(ProjectId, alias) };
424+
425+
FieldMask updateMask = new FieldMask();
426+
if (clearLabels)
427+
{
428+
updateMask.Paths.Add("labels");
429+
secret.Labels.Clear();
430+
}
431+
432+
if (clearTtl)
433+
{
434+
updateMask.Paths.Add("ttl");
435+
secret.Ttl = null;
436+
}
437+
438+
if (clearVersionDestroyTtl)
439+
{
440+
updateMask.Paths.Add("version_destroy_ttl");
441+
secret.VersionDestroyTtl = null;
442+
}
443+
444+
try
445+
{
446+
var updatedSecret = Client.UpdateSecret(secret, updateMask);
447+
}
448+
catch (Exception ex)
449+
{
450+
_logger.LogError(ex.Message);
451+
throw;
452+
}
453+
finally
454+
{
455+
_logger.MethodExit(LogLevel.Debug);
456+
}
457+
}
458+
459+
public void UpdateSecretFields(string alias, string labels, TimeSpan? ttlDuration, TimeSpan? versionDestroyTtlDuration)
460+
{
461+
_logger.MethodEntry(LogLevel.Debug);
462+
463+
Secret secret = new Secret { SecretName = new SecretName(ProjectId, alias) };
464+
465+
FieldMask updateMask = new FieldMask();
466+
if (!string.IsNullOrEmpty(labels))
467+
{
468+
updateMask.Paths.Add("labels");
469+
AssignLabels(labels, secret.Labels);
470+
}
471+
472+
if (ttlDuration.HasValue)
473+
{
474+
updateMask.Paths.Add("ttl");
475+
secret.Ttl = Duration.FromTimeSpan(ttlDuration.Value);
476+
}
477+
478+
if (versionDestroyTtlDuration.HasValue)
479+
{
480+
updateMask.Paths.Add("version_destroy_ttl");
481+
secret.VersionDestroyTtl = Duration.FromTimeSpan(versionDestroyTtlDuration.Value);
482+
}
483+
484+
try
485+
{
486+
var updatedSecret = Client.UpdateSecret(secret, updateMask);
487+
}
488+
catch (Exception ex)
489+
{
490+
_logger.LogError(ex.Message);
491+
throw;
492+
}
493+
finally
494+
{
495+
_logger.MethodExit(LogLevel.Debug);
496+
}
497+
}
498+
383499
private string GetOrganizationFromProject()
384500
{
501+
_logger.MethodEntry(LogLevel.Debug);
502+
385503
string organization = string.Empty;
386504

387-
Project project = ProjectsClient.GetProject(new GetProjectRequest() { ProjectName = ProjectName.FromProject(ProjectId) });
388-
organization = project.Parent;
505+
try
506+
{
507+
Project project = ProjectsClient.GetProject(new GetProjectRequest() { ProjectName = ProjectName.FromProject(ProjectId) });
508+
organization = project.Parent;
509+
}
510+
catch (Exception ex)
511+
{
512+
_logger.LogError(ex.Message);
513+
throw;
514+
}
515+
finally
516+
{
517+
_logger.MethodExit(LogLevel.Debug);
518+
}
389519

390520
return organization.Substring(organization.IndexOf("/") + 1);
391521
}
522+
523+
private void AssignLabels(string labels, MapField<string, string> labelMap)
524+
{
525+
List<(string, string)> labelsList = labels != null ? null :
526+
labels.Split(',', StringSplitOptions.RemoveEmptyEntries)
527+
.Select(pair => pair.Split(':', 2))
528+
.Where(parts => parts.Length == 2)
529+
.Select(parts => (Key: parts[0].Trim(), Value: parts[1].Trim()))
530+
.ToList();
531+
532+
foreach (var label in labelsList)
533+
labelMap[label.Item1] = label.Item2;
534+
}
392535
}
393536
}

GCPSecretManager/Inventory.cs

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ public JobResult ProcessJob(InventoryJobConfiguration config, SubmitInventoryUpd
4444
List<string> secretNames = client.GetSecretNames();
4545
foreach(string secretName in secretNames)
4646
{
47-
string certificateEntry = string.Empty;
47+
SecretWithLabels certificateEntry = new SecretWithLabels();
4848
try
4949
{
5050
certificateEntry = client.GetCertificateEntry(secretName);
@@ -55,9 +55,25 @@ public JobResult ProcessJob(InventoryJobConfiguration config, SubmitInventoryUpd
5555
continue;
5656
}
5757

58-
if (!CertificateFormatter.IsValid(certificateEntry))
58+
if (!CertificateFormatter.IsValid(certificateEntry.Secret))
5959
continue;
60-
string[] certificateChain = CertificateFormatter.ConvertSecretToCertificateChain(certificateEntry);
60+
string[] certificateChain = CertificateFormatter.ConvertSecretToCertificateChain(certificateEntry.Secret);
61+
62+
string secretTags = string.Empty;
63+
try
64+
{
65+
secretTags = client.GetSecretTags(secretName);
66+
}
67+
catch (Exception)
68+
{
69+
hasWarnings = true;
70+
}
71+
72+
Dictionary<string, object> entryParameters = new()
73+
{
74+
{ "tags", secretTags },
75+
{ "labels", certificateEntry.Labels }
76+
};
6177

6278
Dictionary<string, object> secretTags = new Dictionary<string, object>();
6379
try
@@ -74,10 +90,10 @@ public JobResult ProcessJob(InventoryJobConfiguration config, SubmitInventoryUpd
7490
{
7591
ItemStatus = OrchestratorInventoryItemStatus.Unknown,
7692
Alias = secretName.Substring(secretName.LastIndexOf("/") + 1),
77-
PrivateKeyEntry = CertificateFormatter.HasPrivateKey(certificateEntry),
93+
PrivateKeyEntry = CertificateFormatter.HasPrivateKey(certificateEntry.Secret),
7894
UseChainLevel = certificateChain.Length > 1,
7995
Certificates = certificateChain,
80-
Parameters = secretTags
96+
Parameters = entryParameters
8197
});
8298
}
8399
}

GCPSecretManager/JobBase.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
1-
using Keyfactor.Logging;
1+
using Google.Protobuf.WellKnownTypes;
2+
using Keyfactor.Logging;
23
using Keyfactor.Orchestrators.Extensions;
34
using Keyfactor.Orchestrators.Extensions.Interfaces;
45
using Microsoft.Extensions.Logging;
56
using Newtonsoft.Json;
7+
using System;
8+
using System.Collections.Generic;
69

710
namespace Keyfactor.Extensions.Orchestrator.GCPSecretManager
811
{
@@ -16,6 +19,7 @@ public class JobBase
1619
internal string PasswordSecretSuffix { get; set; }
1720
internal bool IncludeChain { get; set; }
1821

22+
1923
internal void Initialize(CertificateStore certificateStoreDetails)
2024
{
2125
Logger.MethodEntry(LogLevel.Debug);

0 commit comments

Comments
 (0)