Add SpEL support for @Secret annotation.
We now support SpEL expressions in the Secret annotation.
@Secret(backend = "#{myBackend}", value = "#{myKeySpace}")
class ExpressionEntity {
// …
}
Closes gh-436.
This commit is contained in:
@@ -18,6 +18,11 @@ package org.springframework.vault.repository.mapping;
|
||||
import org.springframework.data.keyvalue.core.mapping.BasicKeyValuePersistentEntity;
|
||||
import org.springframework.data.keyvalue.core.mapping.KeySpaceResolver;
|
||||
import org.springframework.data.util.TypeInformation;
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.expression.ParserContext;
|
||||
import org.springframework.expression.common.LiteralExpression;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
@@ -30,9 +35,13 @@ public class BasicVaultPersistentEntity<T> extends
|
||||
BasicKeyValuePersistentEntity<T, VaultPersistentProperty> implements
|
||||
VaultPersistentEntity<T> {
|
||||
|
||||
private final String keyspace;
|
||||
private static final SpelExpressionParser PARSER = new SpelExpressionParser();
|
||||
|
||||
private final String secretBackend;
|
||||
private final @Nullable String backend;
|
||||
private final @Nullable Expression backendExpression;
|
||||
|
||||
private final @Nullable String keyspace;
|
||||
private final @Nullable Expression keyspaceExpression;
|
||||
|
||||
/**
|
||||
* Creates new {@link BasicVaultPersistentEntity}.
|
||||
@@ -46,26 +55,55 @@ public class BasicVaultPersistentEntity<T> extends
|
||||
|
||||
Secret annotation = findAnnotation(Secret.class);
|
||||
|
||||
String keyspace = super.getKeySpace();
|
||||
String secretBackend = "secret";
|
||||
if (annotation != null && StringUtils.hasText(annotation.backend())) {
|
||||
|
||||
if (annotation != null) {
|
||||
if (StringUtils.hasText(annotation.backend())) {
|
||||
secretBackend = annotation.backend();
|
||||
}
|
||||
this.backend = annotation.backend();
|
||||
this.backendExpression = detectExpression(this.backend);
|
||||
|
||||
this.keyspace = super.getKeySpace();
|
||||
this.keyspaceExpression = detectExpression(this.keyspace);
|
||||
}
|
||||
else {
|
||||
this.backend = "secret";
|
||||
this.backendExpression = null;
|
||||
|
||||
this.secretBackend = secretBackend;
|
||||
this.keyspace = String.format("%s/%s", secretBackend, keyspace);
|
||||
this.keyspace = super.getKeySpace();
|
||||
this.keyspaceExpression = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a SpEL {@link Expression} if the given {@link String} is actually an
|
||||
* expression that does not evaluate to a {@link LiteralExpression} (indicating that
|
||||
* no subsequent evaluation is necessary).
|
||||
*
|
||||
* @param potentialExpression can be {@literal null}
|
||||
* @return
|
||||
*/
|
||||
@Nullable
|
||||
private static Expression detectExpression(String potentialExpression) {
|
||||
|
||||
Expression expression = PARSER.parseExpression(potentialExpression,
|
||||
ParserContext.TEMPLATE_EXPRESSION);
|
||||
return expression instanceof LiteralExpression ? null : expression;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getKeySpace() {
|
||||
return keyspace;
|
||||
return String.format("%s/%s", getSecretBackend(), doGetKeySpace());
|
||||
}
|
||||
|
||||
private String doGetKeySpace() {
|
||||
return keyspaceExpression == null //
|
||||
? keyspace //
|
||||
: keyspaceExpression.getValue(getEvaluationContext(null), String.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSecretBackend() {
|
||||
return secretBackend;
|
||||
|
||||
return backendExpression == null //
|
||||
? backend //
|
||||
: backendExpression.getValue(getEvaluationContext(null), String.class);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,14 +40,17 @@ import org.springframework.data.keyvalue.annotation.KeySpace;
|
||||
public @interface Secret {
|
||||
|
||||
/**
|
||||
* The prefix to distinguish between domain types.
|
||||
* The prefix to distinguish between domain types. The attribute supports SpEL
|
||||
* expressions to dynamically calculate the keyspace based on a per-operation basis.
|
||||
*
|
||||
* @see KeySpace
|
||||
*/
|
||||
@AliasFor(annotation = KeySpace.class, attribute = "value")
|
||||
String value() default "";
|
||||
|
||||
/**
|
||||
* Secret backend mount, defaults to {@literal secret}.
|
||||
* Secret backend mount, defaults to {@literal secret}. The attribute supports SpEL
|
||||
* expressions to dynamically calculate the backend based on a per-operation basis.
|
||||
*/
|
||||
String backend() default "secret";
|
||||
}
|
||||
|
||||
@@ -15,9 +15,15 @@
|
||||
*/
|
||||
package org.springframework.vault.repository.mapping;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.spel.ExtensionAwareEvaluationContextProvider;
|
||||
import org.springframework.data.spel.spi.EvaluationContextExtension;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@@ -48,6 +54,21 @@ public class BasicVaultPersistentEntityUnitTests {
|
||||
assertThat(persistentEntity.getIdProperty()).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldEvaluateExpression() {
|
||||
|
||||
VaultPersistentEntity<?> persistentEntity = mappingContext
|
||||
.getPersistentEntity(ExpressionEntity.class);
|
||||
|
||||
persistentEntity
|
||||
.setEvaluationContextProvider(new ExtensionAwareEvaluationContextProvider(
|
||||
Collections.singletonList(new SampleExtension())));
|
||||
|
||||
assertThat(persistentEntity.getSecretBackend()).isEqualTo("collectionName");
|
||||
|
||||
assertThat(persistentEntity.getKeySpace()).isEqualTo("collectionName/foo");
|
||||
}
|
||||
|
||||
static class IdProperty {
|
||||
String id, username;
|
||||
}
|
||||
@@ -57,4 +78,26 @@ public class BasicVaultPersistentEntityUnitTests {
|
||||
String username;
|
||||
}
|
||||
|
||||
@Secret(backend = "#{myProperty}", value = "#{myKeySpace}")
|
||||
static class ExpressionEntity {
|
||||
@Id
|
||||
String username;
|
||||
}
|
||||
|
||||
static class SampleExtension implements EvaluationContextExtension {
|
||||
|
||||
@Override
|
||||
public String getExtensionId() {
|
||||
return "sampleExtension";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getProperties() {
|
||||
|
||||
Map<String, Object> properties = new LinkedHashMap<>();
|
||||
properties.put("myProperty", "collectionName");
|
||||
properties.put("myKeySpace", "foo");
|
||||
return properties;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
[[new-features.2-2-0]]
|
||||
=== What's new in Spring Vault 2.2
|
||||
* Support for Key-Value v2 (versioned backend) secrets through `@VaultPropertySource`.
|
||||
* SpEL support in `@Secret`.
|
||||
|
||||
[[new-features.2-1-0]]
|
||||
=== What's new in Spring Vault 2.1
|
||||
|
||||
Reference in New Issue
Block a user