Skip to content

Commit f074d2f

Browse files
authored
Add support for plain text secrets (#638)
Fixes #636
1 parent 0ff91d3 commit f074d2f

File tree

3 files changed

+42
-7
lines changed

3 files changed

+42
-7
lines changed

docs/src/main/asciidoc/secrets-manager.adoc

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,15 @@ Spring Boot 2.4 changed the ways files are loaded which means profile loading ha
123123
Link to Spring Boot reference docs about file specific loading: https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-external-config-files-profile-specific[Reference Docs]
124124
Read more on the official https://spring.io/blog/2020/08/14/config-file-processing-in-spring-boot-2-4[Spring Blog]
125125

126+
==== Using plain text secrets
127+
128+
If a `SecretString` is a plain text, use secret name to retrieve its value.
129+
For example, we have JDBC url as plain text secret type with name `/secrets/jdbc-url`. Secret value can be retrieved by referencing secret name:
130+
[source,properties]
131+
----
132+
spring.datasource.url=${jdbc-url}
133+
----
134+
126135
==== IAM Permissions
127136
Following IAM permissions are required by Spring Cloud AWS:
128137

spring-cloud-aws-secrets-manager-config/src/main/java/io/awspring/cloud/secretsmanager/AwsSecretsManagerPropertySource.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import com.amazonaws.services.secretsmanager.model.GetSecretValueRequest;
2525
import com.amazonaws.services.secretsmanager.model.GetSecretValueResult;
2626
import com.amazonaws.services.secretsmanager.model.ResourceNotFoundException;
27+
import com.fasterxml.jackson.core.JsonParseException;
2728
import com.fasterxml.jackson.core.JsonProcessingException;
2829
import com.fasterxml.jackson.core.type.TypeReference;
2930
import com.fasterxml.jackson.databind.ObjectMapper;
@@ -87,8 +88,8 @@ public Object getProperty(String name) {
8788
}
8889

8990
private void readSecretValue(GetSecretValueRequest secretValueRequest) {
91+
GetSecretValueResult secretValueResult = source.getSecretValue(secretValueRequest);
9092
try {
91-
GetSecretValueResult secretValueResult = source.getSecretValue(secretValueRequest);
9293
Map<String, Object> secretMap = jsonMapper.readValue(secretValueResult.getSecretString(),
9394
new TypeReference<Map<String, Object>>() {
9495
});
@@ -99,6 +100,14 @@ private void readSecretValue(GetSecretValueRequest secretValueRequest) {
99100
properties.put(propertyKey, secretEntry.getValue());
100101
}
101102
}
103+
catch (JsonParseException e) {
104+
// If the secret is not a JSON string, then it is a simple "plain text" string
105+
String[] parts = secretValueResult.getName().split("/");
106+
String secretName = parts[parts.length - 1];
107+
LOG.debug("Populating property retrieved from AWS Secrets Manager: " + secretName);
108+
String propertyKey = prefix != null ? prefix + secretName : secretName;
109+
properties.put(propertyKey, secretValueResult.getSecretString());
110+
}
102111
catch (JsonProcessingException e) {
103112
throw new RuntimeException(e);
104113
}

spring-cloud-aws-secrets-manager-config/src/test/java/io/awspring/cloud/secretsmanager/AwsSecretsManagerPropertySourceTest.java

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
import com.amazonaws.services.secretsmanager.model.GetSecretValueRequest;
2121
import com.amazonaws.services.secretsmanager.model.GetSecretValueResult;
2222
import com.amazonaws.services.secretsmanager.model.ResourceNotFoundException;
23-
import com.fasterxml.jackson.core.JsonProcessingException;
2423
import org.junit.jupiter.api.BeforeEach;
2524
import org.junit.jupiter.api.Test;
2625
import org.mockito.ArgumentCaptor;
@@ -87,13 +86,31 @@ void throwsExceptionWhenSecretNotFound() {
8786
}
8887

8988
@Test
90-
void throwsExceptionWhenSecretIsNotJsonSecret() {
89+
void shouldProcessTextSecretWhenSecretIsNotJsonSecret() {
9190
GetSecretValueResult secretValueResult = new GetSecretValueResult()
92-
.withSecretString("plain text secret string, not json secret");
93-
when(client.getSecretValue(any(GetSecretValueRequest.class))).thenReturn(secretValueResult);
91+
.withSecretString("plain text secret string, not json secret").withName("/config/myservice");
92+
when(client.getSecretValue(secretValueRequestArgumentCaptor.capture())).thenReturn(secretValueResult);
93+
94+
propertySource.init();
95+
96+
assertThat(secretValueRequestArgumentCaptor.getValue().getSecretId()).isEqualTo("/config/myservice");
97+
assertThat(propertySource.getPropertyNames()).containsExactly("myservice");
98+
assertThat(propertySource.getProperty("myservice")).isEqualTo("plain text secret string, not json secret");
99+
}
100+
101+
@Test
102+
void shouldProcessTextSecretWithPrefix() {
103+
propertySource = new AwsSecretsManagerPropertySource("/config/myservice2?prefix=service2.", client);
104+
GetSecretValueResult secretValueResult = new GetSecretValueResult()
105+
.withSecretString("plain text secret string, not json secret").withName("/config/myservice2");
106+
when(client.getSecretValue(secretValueRequestArgumentCaptor.capture())).thenReturn(secretValueResult);
94107

95-
assertThatThrownBy(() -> propertySource.init()).isInstanceOf(RuntimeException.class)
96-
.extracting(Throwable::getCause).isInstanceOf(JsonProcessingException.class);
108+
propertySource.init();
109+
110+
assertThat(secretValueRequestArgumentCaptor.getValue().getSecretId()).isEqualTo("/config/myservice2");
111+
assertThat(propertySource.getPropertyNames()).containsExactly("service2.myservice2");
112+
assertThat(propertySource.getProperty("service2.myservice2"))
113+
.isEqualTo("plain text secret string, not json secret");
97114
}
98115

99116
}

0 commit comments

Comments
 (0)