Skip to content
This repository has been archived by the owner on Jan 19, 2022. It is now read-only.

Polish Secret Manager Code #2270

Merged
merged 5 commits into from
Mar 26, 2020
Merged
Show file tree
Hide file tree
Changes from 2 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
5 changes: 4 additions & 1 deletion docs/src/main/asciidoc/secretmanager.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,10 @@ Spring Cloud GCP Secret Manager offers several configuration properties to custo
| Name | Description | Required | Default value
| `spring.cloud.gcp.secretmanager.bootstrap.enabled` | Enables loading secrets from Secret Manager as a bootstrap property source. Set this to **true** to enable the feature. | No | `false`
| `spring.cloud.gcp.secretmanager.secret-name-prefix` | A prefix string to prepend to the property names of secrets read from Secret Manager | No | "" (empty string)
| `spring.cloud.gcp.secretmanager.version` | Defines a specific version of secrets to read from Secret Manager | No | "latest"
| `spring.cloud.gcp.secretmanager.default-version`
| The default version that secrets will be read at, if a specific version for the secret is not configured.

Note: If you set this to a value other than "latest" and a secret has not reached that version yet, then the secret **will not** be injected into the environment. | No | "latest"
| `spring.cloud.gcp.secretmanager.versions.<secret-id>` | Defines a version for a specific secret-id to read from Secret Manager. It will take precedence over `spring.cloud.gcp.secretmanager.version` if both are specified | No | "" (empty string)
|===

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ public SecretManagerServiceClient secretManagerClient() throws IOException {
public PropertySourceLocator secretManagerPropertySourceLocator(SecretManagerServiceClient client) {
SecretManagerPropertySourceLocator propertySourceLocator = new SecretManagerPropertySourceLocator(
client, this.gcpProjectIdProvider, this.properties.getSecretNamePrefix());
propertySourceLocator.setVersion(this.properties.getVersion());
propertySourceLocator.setVersion(this.properties.getDefaultVersion());
propertySourceLocator.setVersions(this.properties.getVersions());
return propertySourceLocator;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ public class GcpSecretManagerProperties implements CredentialsSupplier {
/**
* Defines the secret's version to be used.
*/
private String version;
private String defaultVersion = "latest";

/**
* Defines versions for specific secret-ids.
Expand All @@ -75,12 +75,12 @@ public void setSecretNamePrefix(String secretNamePrefix) {
this.secretNamePrefix = secretNamePrefix;
}

public String getVersion() {
return version;
public String getDefaultVersion() {
return defaultVersion;
}

public void setVersion(String version) {
this.version = version;
public void setDefaultVersion(String defaultVersion) {
this.defaultVersion = defaultVersion;
}

public Map<String, String> getVersions() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,23 @@

package org.springframework.cloud.gcp.autoconfigure.secretmanager;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

import com.google.api.gax.rpc.NotFoundException;
import com.google.cloud.secretmanager.v1beta1.AccessSecretVersionResponse;
import com.google.cloud.secretmanager.v1beta1.ProjectName;
import com.google.cloud.secretmanager.v1beta1.Secret;
import com.google.cloud.secretmanager.v1beta1.SecretManagerServiceClient;
import com.google.cloud.secretmanager.v1beta1.SecretManagerServiceClient.ListSecretsPagedResponse;
import com.google.cloud.secretmanager.v1beta1.SecretVersionName;
import com.google.protobuf.ByteString;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.springframework.cloud.gcp.core.GcpProjectIdProvider;
import org.springframework.core.env.EnumerablePropertySource;
import org.springframework.util.StringUtils;

/**
* Retrieves secrets from GCP Secret Manager under the current GCP project.
Expand All @@ -40,6 +43,8 @@
*/
public class SecretManagerPropertySource extends EnumerablePropertySource<SecretManagerServiceClient> {

private static final Log LOGGER = LogFactory.getLog(SecretManagerPropertySource.class);

private static final String LATEST_VERSION_STRING = "latest";

private final Map<String, Object> properties;
Expand All @@ -51,26 +56,20 @@ public SecretManagerPropertySource(
SecretManagerServiceClient client,
GcpProjectIdProvider projectIdProvider,
String secretsPrefix) {
super(propertySourceName, client);

Map<String, Object> propertiesMap = createSecretsPropertiesMap(
client, projectIdProvider.getProjectId(), secretsPrefix, null, null);

this.properties = propertiesMap;
this.propertyNames = propertiesMap.keySet().toArray(new String[propertiesMap.size()]);
this(propertySourceName, client, projectIdProvider, secretsPrefix, LATEST_VERSION_STRING, Collections.EMPTY_MAP);
}

public SecretManagerPropertySource(
String propertySourceName,
SecretManagerServiceClient client,
GcpProjectIdProvider projectIdProvider,
String secretsPrefix,
String version,
String defaultVersion,
Map<String, String> versions) {
super(propertySourceName, client);

Map<String, Object> propertiesMap = createSecretsPropertiesMap(
client, projectIdProvider.getProjectId(), secretsPrefix, version, versions);
client, projectIdProvider.getProjectId(), secretsPrefix, defaultVersion, versions);

this.properties = propertiesMap;
this.propertyNames = propertiesMap.keySet().toArray(new String[propertiesMap.size()]);
Expand All @@ -87,43 +86,51 @@ public Object getProperty(String name) {
}

private static Map<String, Object> createSecretsPropertiesMap(
SecretManagerServiceClient client, String projectId, String secretsPrefix, String version, Map<String, String> versions) {
SecretManagerServiceClient client, String projectId, String secretsPrefix, String defaultVersion, Map<String, String> versions) {

ListSecretsPagedResponse response = client.listSecrets(ProjectName.of(projectId));

Map<String, Object> secretsMap = new HashMap<>();
for (Secret secret : response.iterateAll()) {
String secretId = extractSecretId(secret);
ByteString secretPayload = resolveSecretVersion(client, projectId, version, versions, secretId);
secretsMap.put(secretsPrefix + secretId, secretPayload);
ByteString secretPayload = getSecretPayload(client, projectId, secretId, defaultVersion, versions);
if (secretPayload != null) {
secretsMap.put(secretsPrefix + secretId, secretPayload);
}
}

return secretsMap;
}

private static ByteString resolveSecretVersion(SecretManagerServiceClient client, String projectId, String version, Map<String, String> versions, String secretId) {
if (!versions.isEmpty() && versions.containsKey(secretId)) {
String secretVersion = versions.get(secretId);
return getSecretPayload(client, projectId, secretId, secretVersion);
}
return getSecretPayload(client, projectId, secretId, resolveVersion(version));
}

private static String resolveVersion(String version) {
return StringUtils.hasText(version) ? version : LATEST_VERSION_STRING;
}

private static ByteString getSecretPayload(
SecretManagerServiceClient client, String projectId, String secretId, String version) {
SecretManagerServiceClient client,
String projectId,
String secretId,
String defaultVersion,
Map<String, String> versions) {

boolean usingSpecificVersion = versions.containsKey(secretId);
String version = usingSpecificVersion ? versions.get(secretId) : defaultVersion;

SecretVersionName secretVersionName = SecretVersionName.newBuilder()
.setProject(projectId)
.setSecret(secretId)
.setSecretVersion(version)
.build();

AccessSecretVersionResponse response = client.accessSecretVersion(secretVersionName);
return response.getPayload().getData();
try {
AccessSecretVersionResponse response = client.accessSecretVersion(secretVersionName);
return response.getPayload().getData();
}
catch (NotFoundException e) {
if (usingSpecificVersion) {
throw e;
}
else {
LOGGER.debug("Skipped loading secret " + secretId + " because it does not have version " + defaultVersion);
}
}

return null;
}

/**
Expand Down
Loading