Skip to content

Commit 2249ff1

Browse files
committed
Polishing.
Simplify test, use token for assertions for tighter assumptions instead of relying on 403 responses potentially caused by other reasons. See gh-874 Original pull request: gh-875
1 parent 8d3fa6b commit 2249ff1

File tree

3 files changed

+146
-66
lines changed

3 files changed

+146
-66
lines changed

spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/config/ClientAuthenticationFactory.java

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
import org.springframework.vault.authentication.ClientAuthentication;
5151
import org.springframework.vault.authentication.ClientCertificateAuthentication;
5252
import org.springframework.vault.authentication.ClientCertificateAuthenticationOptions;
53+
import org.springframework.vault.authentication.ClientCertificateAuthenticationOptions.ClientCertificateAuthenticationOptionsBuilder;
5354
import org.springframework.vault.authentication.CubbyholeAuthentication;
5455
import org.springframework.vault.authentication.CubbyholeAuthenticationOptions;
5556
import org.springframework.vault.authentication.GcpComputeAuthentication;
@@ -378,14 +379,14 @@ private ClientAuthentication pcfAuthentication(VaultProperties vaultProperties)
378379

379380
private ClientAuthentication certificateAuthentication(VaultProperties vaultProperties) {
380381

381-
ClientCertificateAuthenticationOptions.ClientCertificateAuthenticationOptionsBuilder optionsBuilder = ClientCertificateAuthenticationOptions
382-
.builder();
383-
optionsBuilder.path(vaultProperties.getSsl().getCertAuthPath());
382+
ClientCertificateAuthenticationOptionsBuilder builder = ClientCertificateAuthenticationOptions.builder();
383+
builder.path(vaultProperties.getSsl().getCertAuthPath());
384+
384385
if (StringUtils.hasText(vaultProperties.getSsl().getRole())) {
385-
optionsBuilder.role(vaultProperties.getSsl().getRole());
386+
builder.role(vaultProperties.getSsl().getRole());
386387
}
387388

388-
return new ClientCertificateAuthentication(optionsBuilder.build(), this.restOperations);
389+
return new ClientCertificateAuthentication(builder.build(), this.restOperations);
389390
}
390391

391392
private ClientAuthentication tokenAuthentication(VaultProperties vaultProperties) {

spring-cloud-vault-config/src/test/java/org/springframework/cloud/vault/config/VaultConfigTlsCertAuthenticationTests.java

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -20,25 +20,23 @@
2020
import java.nio.charset.StandardCharsets;
2121
import java.util.Collections;
2222
import java.util.HashMap;
23+
import java.util.List;
2324
import java.util.Map;
2425

2526
import org.assertj.core.util.Files;
2627
import org.junit.jupiter.api.AfterEach;
2728
import org.junit.jupiter.api.BeforeAll;
2829
import org.junit.jupiter.api.Test;
2930

30-
import org.junit.jupiter.api.extension.ExtendWith;
3131
import org.springframework.boot.test.context.TestConfiguration;
32-
import org.springframework.boot.test.system.CapturedOutput;
33-
import org.springframework.boot.test.system.OutputCaptureExtension;
3432
import org.springframework.cloud.vault.util.Settings;
3533
import org.springframework.cloud.vault.util.VaultRule;
3634
import org.springframework.cloud.vault.util.VaultTestContextRunner;
3735
import org.springframework.vault.core.VaultOperations;
36+
import org.springframework.vault.core.VaultTemplate;
37+
import org.springframework.vault.support.VaultResponse;
3838

3939
import static org.assertj.core.api.Assertions.assertThat;
40-
import static org.junit.jupiter.api.Assertions.assertNull;
41-
import static org.springframework.cloud.vault.config.VaultProperties.AuthenticationMethod.CERT;
4240
import static org.springframework.cloud.vault.util.Settings.findWorkDir;
4341

4442
/**
@@ -50,14 +48,12 @@
5048
* @author Mark Paluch
5149
* @author Issam El-atif
5250
*/
53-
@ExtendWith(OutputCaptureExtension.class)
5451
public class VaultConfigTlsCertAuthenticationTests {
5552

56-
VaultTestContextRunner contextRunner = VaultTestContextRunner.of(CERT)
57-
.testClass(getClass())
58-
.bootstrap(true)
59-
.reactive(false)
60-
.configurations(TestConfig.class);
53+
VaultTestContextRunner contextRunner = VaultTestContextRunner.of(VaultConfigTlsCertAuthenticationTests.class)
54+
.auth(VaultProperties.AuthenticationMethod.CERT)
55+
.configurations(TestConfig.class)
56+
.settings(s -> s.bootstrap());
6157

6258
private static VaultOperations vaultOperations;
6359

@@ -111,22 +107,28 @@ void authenticateUsingTlsCertificate() {
111107
void authenticateUsingNamedCertificateRole() {
112108
Map<String, String> anotherRole = new HashMap<>();
113109
anotherRole.put("certificate", certificate);
110+
anotherRole.put("policies", "another-policy");
114111
vaultOperations.write("auth/cert/certs/another-role", anotherRole);
115112

116-
this.contextRunner.property("spring.cloud.vault.ssl.role", "my-role")
117-
.run(context -> assertThat(context.getEnvironment().getProperty("vault.value")).isEqualTo("foo"));
113+
this.contextRunner.property("spring.cloud.vault.ssl.role", "my-role").run(context -> {
114+
VaultTemplate template = context.getBean(VaultTemplate.class);
115+
VaultResponse tokenLookup = template.read("auth/token/lookup-self");
116+
assertThat(tokenLookup.getRequiredData()).containsEntry("policies", List.of("default", "testpolicy"));
117+
assertThat(context.getEnvironment().getProperty("vault.value")).isEqualTo("foo");
118+
});
118119
}
119120

120121
@Test
121-
void authenticationFailsWhenMultipleCertsAndNoNamedRole(CapturedOutput capturedOutput) {
122+
void authenticationFailsWhenMultipleCertsAndNoNamedRole() {
122123
Map<String, String> anotherRole = new HashMap<>();
123124
anotherRole.put("certificate", certificate);
124125
vaultOperations.write("auth/cert/certs/another-role", anotherRole);
125126

126127
this.contextRunner.run(context -> {
127-
assertThat(capturedOutput.getOut())
128-
.contains("org.springframework.vault.VaultException: Status 403 Forbidden");
129-
assertNull(context.getEnvironment().getProperty("vault.value"));
128+
VaultTemplate template = context.getBean(VaultTemplate.class);
129+
VaultResponse tokenLookup = template.read("auth/token/lookup-self");
130+
assertThat(tokenLookup.getRequiredData()).containsEntry("policies", Collections.singletonList("default"));
131+
assertThat(context.getEnvironment().getProperty("vault.value")).isNull();
130132
});
131133
}
132134

spring-cloud-vault-config/src/test/java/org/springframework/cloud/vault/util/VaultTestContextRunner.java

Lines changed: 121 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -16,96 +16,128 @@
1616

1717
package org.springframework.cloud.vault.util;
1818

19-
import org.springframework.boot.WebApplicationType;
20-
import org.springframework.boot.builder.SpringApplicationBuilder;
21-
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
22-
import org.springframework.cloud.vault.config.VaultProperties.AuthenticationMethod;
23-
import org.springframework.context.ConfigurableApplicationContext;
24-
2519
import java.util.ArrayList;
2620
import java.util.Arrays;
2721
import java.util.LinkedHashMap;
2822
import java.util.List;
2923
import java.util.Map;
3024
import java.util.function.Consumer;
3125

26+
import org.springframework.boot.WebApplicationType;
27+
import org.springframework.boot.builder.SpringApplicationBuilder;
28+
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
29+
import org.springframework.cloud.vault.config.VaultProperties.AuthenticationMethod;
30+
import org.springframework.context.ConfigurableApplicationContext;
31+
3232
/**
3333
* A utility class built on top of {@link SpringApplicationBuilder} that follows Spring
3434
* Boot’s testing patterns but designed for Spring Cloud Vault integration tests.
3535
*
3636
* <p>
3737
* Its goal is to eliminate repetitive boilerplate particularly when repeatedly
38-
* configuring:
39-
* </p>
40-
*
41-
* <ul>
42-
* <li>Bootstrap mode</li>
43-
* <li>Reactive vs. non-reactive modes</li>
44-
* <li>Environment and test classes</li>
45-
* <li>Vault-specific properties</li>
46-
* </ul>
38+
* configuring startup properties.
4739
*
4840
* @author Issam EL-ATIF
49-
* @since 5.0.1
41+
* @author Mark Paluch
5042
* @see SpringApplicationBuilder
5143
* @see ApplicationContextRunner
5244
*/
53-
public class VaultTestContextRunner {
45+
public final class VaultTestContextRunner {
5446

5547
private final List<Class<?>> configurationClasses = new ArrayList<>();
5648

5749
private final Map<String, Object> properties = new LinkedHashMap<>();
5850

51+
private final TestSettings testSettings = new TestSettings();
52+
5953
private VaultTestContextRunner(Map<String, Object> properties) {
6054
this.properties.putAll(properties);
6155
}
6256

63-
public static VaultTestContextRunner of(AuthenticationMethod authenticationMethod) {
64-
Map<String, Object> authProperties = new LinkedHashMap<>();
65-
switch (authenticationMethod) {
66-
case CERT -> {
67-
authProperties.put("spring.cloud.vault.authentication", "cert");
68-
authProperties.put("spring.cloud.vault.ssl.key-store", "file:../work/client-cert.jks");
69-
authProperties.put("spring.cloud.vault.ssl.key-store-password", "changeit");
70-
}
71-
case KUBERNETES -> {
72-
authProperties.put("spring.cloud.vault.authentication", "kubernetes");
73-
authProperties.put("spring.cloud.vault.kubernetes.service-account-token-file",
74-
"../work/minikube/hello-minikube-token");
75-
}
76-
}
57+
/**
58+
* Create a {@code VaultTestContextRunner} with default TLS configuration.
59+
* @return the {@link VaultTestContextRunner}.
60+
*/
61+
public static VaultTestContextRunner create() {
62+
63+
Map<String, Object> configurationProperties = new LinkedHashMap<>();
64+
configurationProperties.put("spring.cloud.vault.ssl.key-store", "file:../work/client-cert.jks");
65+
configurationProperties.put("spring.cloud.vault.ssl.key-store-password", "changeit");
7766

78-
return new VaultTestContextRunner(authProperties);
67+
return new VaultTestContextRunner(configurationProperties);
7968
}
8069

81-
public VaultTestContextRunner configurations(Class<?>... configClasses) {
82-
this.configurationClasses.addAll(Arrays.asList(configClasses));
83-
return this;
70+
/**
71+
* Create a {@code VaultTestContextRunner} with default TLS configuration and using
72+
* the {@code testClass} to set the application name.
73+
* @return the {@link VaultTestContextRunner}.
74+
*/
75+
public static VaultTestContextRunner of(Class<?> testClass) {
76+
return create().testClass(testClass);
8477
}
8578

86-
public VaultTestContextRunner property(String key, Object value) {
87-
this.properties.put(key, value);
88-
return this;
79+
/**
80+
* Configure an authentication method to be used.
81+
* @param authenticationMethod the authentication method to use.
82+
* @return this builder.
83+
*/
84+
public VaultTestContextRunner auth(AuthenticationMethod authenticationMethod) {
85+
return property("spring.cloud.vault.authentication", authenticationMethod.name());
8986
}
9087

88+
/**
89+
* Configure the application name based on the given test class.
90+
* @param testClass the test class.
91+
* @return this builder.
92+
*/
9193
public VaultTestContextRunner testClass(Class<?> testClass) {
92-
this.properties.put("spring.cloud.vault.application-name", testClass.getSimpleName());
94+
return property("spring.cloud.vault.application-name", testClass.getSimpleName());
95+
}
96+
97+
/**
98+
* Configure configuration classes to be added.
99+
* @param configClasses configuration classes to add.
100+
* @return this builder.
101+
*/
102+
public VaultTestContextRunner configurations(Class<?>... configClasses) {
103+
this.configurationClasses.addAll(Arrays.asList(configClasses));
93104
return this;
94105
}
95106

96-
public VaultTestContextRunner bootstrap(boolean bootstrap) {
97-
this.properties.put("spring.cloud.bootstrap.enabled", bootstrap);
107+
/**
108+
* Set a property to be used in the test context.
109+
* @param key the property key.
110+
* @param value the property value.
111+
* @return this builder.
112+
*/
113+
public VaultTestContextRunner property(String key, Object value) {
114+
this.properties.put(key, value);
98115
return this;
99116
}
100117

101-
public VaultTestContextRunner reactive(boolean reactive) {
102-
this.properties.put("spring.cloud.vault.reactive.enabled", reactive);
118+
/**
119+
* Provides access to {@link TestSettings} with the possibility to update test
120+
* settings.
121+
* @param settingsConsumer a function that consumes the test settings.
122+
* @return this builder.
123+
*/
124+
public VaultTestContextRunner settings(Consumer<TestSettings> settingsConsumer) {
125+
settingsConsumer.accept(this.testSettings);
103126
return this;
104127
}
105128

129+
/**
130+
* Run the application and provide access to the application context through the
131+
* {@code consumer}.
132+
* @param consumer consumes the application context.
133+
*/
106134
public void run(Consumer<ConfigurableApplicationContext> consumer) {
135+
136+
property("spring.cloud.bootstrap.enabled", this.testSettings.bootstrap);
137+
property("spring.cloud.vault.reactive.enabled", this.testSettings.reactive);
138+
107139
SpringApplicationBuilder builder = new SpringApplicationBuilder();
108-
builder.sources(configurationClasses.toArray(new Class<?>[0]))
140+
builder.sources(this.configurationClasses.toArray(new Class<?>[0]))
109141
.web(WebApplicationType.NONE)
110142
.properties(this.properties);
111143

@@ -114,4 +146,49 @@ public void run(Consumer<ConfigurableApplicationContext> consumer) {
114146
}
115147
}
116148

149+
/**
150+
* Common settings used in tests.
151+
*/
152+
public class TestSettings {
153+
154+
private boolean bootstrap = false;
155+
156+
private boolean reactive = false;
157+
158+
/**
159+
* Enable Spring Cloud bootstrap context usage (disabled by default).
160+
* @return this settings builder.
161+
*/
162+
public TestSettings bootstrap() {
163+
return bootstrap(true);
164+
}
165+
166+
/**
167+
* Enable or disable Spring Cloud bootstrap context usage (disabled by default).
168+
* @return this settings builder.
169+
*/
170+
public TestSettings bootstrap(boolean bootstrap) {
171+
this.bootstrap = bootstrap;
172+
return this;
173+
}
174+
175+
/**
176+
* Enable reactive usage (disabled by default).
177+
* @return this settings builder.
178+
*/
179+
public TestSettings reactive() {
180+
return reactive(true);
181+
}
182+
183+
/**
184+
* Enable or disable reactive usage (disabled by default).
185+
* @return this settings builder.
186+
*/
187+
public TestSettings reactive(boolean reactive) {
188+
this.reactive = reactive;
189+
return this;
190+
}
191+
192+
}
193+
117194
}

0 commit comments

Comments
 (0)