Polishing.
Tweak Javadoc. Switch TTL from String to Duration for type-safe conversion. Extract anonymous classes into inner classes. Add unit test. See gh-572 Closes gh-575.
This commit is contained in:
@@ -14,13 +14,14 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @author Kris Iyer
|
||||
*
|
||||
* Supported Aws credential types.
|
||||
*/
|
||||
package org.springframework.cloud.vault.config.aws;
|
||||
|
||||
/**
|
||||
* Supported AWS credential types.
|
||||
*
|
||||
* @author Kris Iyer
|
||||
* @since 3.0.2
|
||||
*/
|
||||
public enum AwsCredentialType {
|
||||
|
||||
IAM_USER, ASSUMED_ROLE, FEDERATION_TOKEN
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package org.springframework.cloud.vault.config.aws;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.cloud.vault.config.VaultSecretBackendDescriptor;
|
||||
import org.springframework.lang.Nullable;
|
||||
@@ -66,17 +68,19 @@ public class VaultAwsProperties implements VaultSecretBackendDescriptor {
|
||||
private String sessionTokenKeyProperty = "cloud.aws.credentials.sessionToken";
|
||||
|
||||
/**
|
||||
*
|
||||
* Role arn for assumed_role in case we have multiple roles associated with the vault
|
||||
* role
|
||||
* role.
|
||||
* @since 3.0.2
|
||||
*/
|
||||
@Nullable
|
||||
private String roleArn;
|
||||
|
||||
/**
|
||||
* TTL for sts tokens. Defaults to whatever the vault Role may have for Max. Also
|
||||
* limited to what AWS supports to be the max for STS.
|
||||
* @since 3.0.2
|
||||
*/
|
||||
private String ttl;
|
||||
private Duration ttl = Duration.ZERO;
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
@@ -122,7 +126,7 @@ public class VaultAwsProperties implements VaultSecretBackendDescriptor {
|
||||
}
|
||||
|
||||
public AwsCredentialType getCredentialType() {
|
||||
return credentialType;
|
||||
return this.credentialType;
|
||||
}
|
||||
|
||||
public void setCredentialType(AwsCredentialType credentialType) {
|
||||
@@ -130,26 +134,27 @@ public class VaultAwsProperties implements VaultSecretBackendDescriptor {
|
||||
}
|
||||
|
||||
public String getSessionTokenKeyProperty() {
|
||||
return sessionTokenKeyProperty;
|
||||
return this.sessionTokenKeyProperty;
|
||||
}
|
||||
|
||||
public void setSessionTokenKeyProperty(String sessionTokenKeyProperty) {
|
||||
this.sessionTokenKeyProperty = sessionTokenKeyProperty;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getRoleArn() {
|
||||
return roleArn;
|
||||
return this.roleArn;
|
||||
}
|
||||
|
||||
public void setRoleArn(String roleArn) {
|
||||
this.roleArn = roleArn;
|
||||
}
|
||||
|
||||
public String getTtl() {
|
||||
return ttl;
|
||||
public Duration getTtl() {
|
||||
return this.ttl;
|
||||
}
|
||||
|
||||
public void setTtl(String ttl) {
|
||||
public void setTtl(Duration ttl) {
|
||||
this.ttl = ttl;
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ package org.springframework.cloud.vault.config.aws;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.StringJoiner;
|
||||
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
@@ -30,7 +31,6 @@ import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.vault.core.lease.domain.RequestedSecret;
|
||||
import org.springframework.vault.core.lease.domain.RequestedSecret.Mode;
|
||||
import org.springframework.vault.core.util.PropertyTransformer;
|
||||
|
||||
@@ -39,7 +39,6 @@ import org.springframework.vault.core.util.PropertyTransformer;
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @author Kris Iyer
|
||||
*
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@EnableConfigurationProperties(VaultAwsProperties.class)
|
||||
@@ -81,93 +80,10 @@ public class VaultConfigAwsBootstrapConfiguration {
|
||||
// security token transformer for STS
|
||||
transformer.addKeyTransformation("security_token", properties.getSessionTokenKeyProperty());
|
||||
|
||||
return new LeasingSecretBackendMetadata() {
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return String.format("%s with Role %s", properties.getBackend(), properties.getRole());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPath() {
|
||||
String defaultPath = "%s/sts/%s";
|
||||
|
||||
// do we have any ttl or role parameters configured?
|
||||
if (StringUtils.hasText(properties.getTtl())
|
||||
|| (properties.getCredentialType() == AwsCredentialType.ASSUMED_ROLE
|
||||
&& StringUtils.hasText(properties.getRoleArn()))) {
|
||||
defaultPath += "?";
|
||||
}
|
||||
// ttl for assumed_role or federation_token
|
||||
// pass through to let aws take care of min and max validations
|
||||
// per
|
||||
// the vault role
|
||||
if (StringUtils.hasText(properties.getTtl())) {
|
||||
defaultPath += "ttl=" + properties.getTtl() + "&";
|
||||
}
|
||||
|
||||
// role_arn for assumed_role for vault role that has multiple role
|
||||
// associations.
|
||||
if (properties.getCredentialType() == AwsCredentialType.ASSUMED_ROLE
|
||||
&& StringUtils.hasText(properties.getRoleArn())) {
|
||||
defaultPath += "role_arn=" + properties.getRoleArn();
|
||||
}
|
||||
return String.format(defaultPath, properties.getBackend(), properties.getRole());
|
||||
}
|
||||
|
||||
@Override
|
||||
public PropertyTransformer getPropertyTransformer() {
|
||||
return transformer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> getVariables() {
|
||||
|
||||
Map<String, String> variables = new HashMap<>();
|
||||
|
||||
variables.put("backend", properties.getBackend());
|
||||
variables.put("key", String.format("sts/%s", properties.getRole()));
|
||||
|
||||
return variables;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mode getLeaseMode() {
|
||||
return RequestedSecret.Mode.ROTATE;
|
||||
}
|
||||
};
|
||||
|
||||
return new AwsStsLeasingSecretBackendMetadata(properties, transformer);
|
||||
}
|
||||
else {
|
||||
|
||||
return new SecretBackendMetadata() {
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return String.format("%s with Role %s", properties.getBackend(), properties.getRole());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPath() {
|
||||
return String.format("%s/creds/%s", properties.getBackend(), properties.getRole());
|
||||
}
|
||||
|
||||
@Override
|
||||
public PropertyTransformer getPropertyTransformer() {
|
||||
return transformer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> getVariables() {
|
||||
|
||||
Map<String, String> variables = new HashMap<>();
|
||||
|
||||
variables.put("backend", properties.getBackend());
|
||||
variables.put("key", String.format("creds/%s", properties.getRole()));
|
||||
|
||||
return variables;
|
||||
}
|
||||
};
|
||||
return new AwsLeasingSecretBackendMetadata(properties, transformer);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -181,6 +97,109 @@ public class VaultConfigAwsBootstrapConfiguration {
|
||||
return backendDescriptor instanceof VaultAwsProperties;
|
||||
}
|
||||
|
||||
private static class AwsStsLeasingSecretBackendMetadata implements LeasingSecretBackendMetadata {
|
||||
|
||||
private final VaultAwsProperties properties;
|
||||
|
||||
private final PropertyNameTransformer transformer;
|
||||
|
||||
AwsStsLeasingSecretBackendMetadata(VaultAwsProperties properties, PropertyNameTransformer transformer) {
|
||||
this.properties = properties;
|
||||
this.transformer = transformer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return String.format("%s with Role %s", this.properties.getBackend(), this.properties.getRole());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPath() {
|
||||
|
||||
String defaultPath = "%s/sts/%s";
|
||||
StringJoiner joiner = new StringJoiner("&");
|
||||
|
||||
// ttl for assumed_role or federation_token
|
||||
// pass through to let aws take care of min and max validations
|
||||
// per the vault role
|
||||
if (!this.properties.getTtl().isZero()) {
|
||||
joiner.add("ttl=" + this.properties.getTtl().toMillis() + "ms");
|
||||
}
|
||||
|
||||
// role_arn for assumed_role for vault role that has multiple role
|
||||
// associations.
|
||||
if (this.properties.getCredentialType() == AwsCredentialType.ASSUMED_ROLE
|
||||
&& StringUtils.hasText(this.properties.getRoleArn())) {
|
||||
joiner.add("role_arn=" + this.properties.getRoleArn());
|
||||
}
|
||||
|
||||
String pathToUse = joiner.length() == 0 ? defaultPath : defaultPath + "?" + joiner;
|
||||
|
||||
return String.format(pathToUse, this.properties.getBackend(), this.properties.getRole());
|
||||
}
|
||||
|
||||
@Override
|
||||
public PropertyTransformer getPropertyTransformer() {
|
||||
return this.transformer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> getVariables() {
|
||||
|
||||
Map<String, String> variables = new HashMap<>();
|
||||
|
||||
variables.put("backend", this.properties.getBackend());
|
||||
variables.put("key", String.format("sts/%s", this.properties.getRole()));
|
||||
|
||||
return variables;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mode getLeaseMode() {
|
||||
return Mode.ROTATE;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class AwsLeasingSecretBackendMetadata implements SecretBackendMetadata {
|
||||
|
||||
private final VaultAwsProperties properties;
|
||||
|
||||
private final PropertyNameTransformer transformer;
|
||||
|
||||
AwsLeasingSecretBackendMetadata(VaultAwsProperties properties, PropertyNameTransformer transformer) {
|
||||
this.properties = properties;
|
||||
this.transformer = transformer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return String.format("%s with Role %s", this.properties.getBackend(), this.properties.getRole());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPath() {
|
||||
return String.format("%s/creds/%s", this.properties.getBackend(), this.properties.getRole());
|
||||
}
|
||||
|
||||
@Override
|
||||
public PropertyTransformer getPropertyTransformer() {
|
||||
return this.transformer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> getVariables() {
|
||||
|
||||
Map<String, String> variables = new HashMap<>();
|
||||
|
||||
variables.put("backend", this.properties.getBackend());
|
||||
variables.put("key", String.format("creds/%s", this.properties.getRole()));
|
||||
|
||||
return variables;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright 2016-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.vault.config.aws;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.cloud.vault.config.SecretBackendMetadata;
|
||||
import org.springframework.cloud.vault.config.SecretBackendMetadataFactory;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link VaultConfigAwsBootstrapConfiguration}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public class VaultConfigAwsBootstrapConfigurationUnitTests {
|
||||
|
||||
@Test
|
||||
void shouldCreateIamTokenSecretBackendMetadataFactory() {
|
||||
|
||||
VaultAwsProperties properties = new VaultAwsProperties();
|
||||
properties.setRole("readonly");
|
||||
|
||||
SecretBackendMetadataFactory<VaultAwsProperties> factory = new VaultConfigAwsBootstrapConfiguration()
|
||||
.awsSecretBackendMetadataFactory();
|
||||
|
||||
SecretBackendMetadata metadata = factory.createMetadata(properties);
|
||||
|
||||
assertThat(metadata.getPath()).isEqualTo("aws/creds/readonly");
|
||||
assertThat(metadata.getVariables()).containsEntry("backend", "aws").containsEntry("key", "creds/readonly");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldCreateStsTokenSecretBackendMetadataFactory() {
|
||||
|
||||
VaultAwsProperties properties = new VaultAwsProperties();
|
||||
properties.setCredentialType(AwsCredentialType.FEDERATION_TOKEN);
|
||||
properties.setRole("readonly");
|
||||
|
||||
SecretBackendMetadataFactory<VaultAwsProperties> factory = new VaultConfigAwsBootstrapConfiguration()
|
||||
.awsSecretBackendMetadataFactory();
|
||||
|
||||
SecretBackendMetadata metadata = factory.createMetadata(properties);
|
||||
|
||||
assertThat(metadata.getPath()).isEqualTo("aws/sts/readonly");
|
||||
assertThat(metadata.getVariables()).containsEntry("backend", "aws").containsEntry("key", "sts/readonly");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldCreateStsTokenSecretBackendMetadataFactoryWithTtlAndRoleArn() {
|
||||
|
||||
VaultAwsProperties properties = new VaultAwsProperties();
|
||||
properties.setCredentialType(AwsCredentialType.ASSUMED_ROLE);
|
||||
properties.setRoleArn("1:2:3");
|
||||
properties.setTtl(Duration.ofMinutes(1));
|
||||
properties.setRole("readonly");
|
||||
|
||||
SecretBackendMetadataFactory<VaultAwsProperties> factory = new VaultConfigAwsBootstrapConfiguration()
|
||||
.awsSecretBackendMetadataFactory();
|
||||
|
||||
SecretBackendMetadata metadata = factory.createMetadata(properties);
|
||||
|
||||
assertThat(metadata.getPath()).isEqualTo("aws/sts/readonly?ttl=60000ms&role_arn=1:2:3");
|
||||
assertThat(metadata.getVariables()).containsEntry("backend", "aws").containsEntry("key", "sts/readonly");
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user