Merge branch '4.1.x' into 4.2.x

This commit is contained in:
Ryan Baxter
2025-04-22 08:57:13 -04:00
2 changed files with 154 additions and 2 deletions

View File

@@ -21,6 +21,7 @@ import java.util.List;
import org.springframework.cloud.config.server.environment.ConfigTokenProvider;
import org.springframework.cloud.config.server.environment.VaultEnvironmentProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.util.StringUtils;
import org.springframework.vault.core.VaultTemplate;
/**
@@ -43,10 +44,21 @@ public class SpringVaultTemplateBuilder {
}
public VaultTemplate build(VaultEnvironmentProperties vaultProperties) {
ConfigTokenProvider tokenProvider = isStaticToken(vaultProperties) ? vaultProperties::getToken
: configTokenProvider;
SpringVaultClientConfiguration clientConfiguration = new SpringVaultClientConfiguration(vaultProperties,
configTokenProvider, authProviders);
tokenProvider, authProviders);
clientConfiguration.setApplicationContext(applicationContext);
return clientConfiguration.vaultTemplate();
var vaultTemplate = clientConfiguration.vaultTemplate();
vaultTemplate.setSessionManager(clientConfiguration.sessionManager());
return vaultTemplate;
}
private boolean isStaticToken(VaultEnvironmentProperties vaultProperties) {
return vaultProperties.getAuthentication() == null && StringUtils.hasText(vaultProperties.getToken());
}
}

View File

@@ -0,0 +1,140 @@
/*
* Copyright 2018-2025 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.config.server.environment.vault;
import java.util.Collections;
import java.util.List;
import com.github.tomakehurst.wiremock.WireMockServer;
import com.github.tomakehurst.wiremock.client.WireMock;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.cloud.config.server.environment.ConfigTokenProvider;
import org.springframework.cloud.config.server.environment.VaultEnvironmentProperties;
import org.springframework.cloud.config.server.environment.vault.authentication.AppRoleClientAuthenticationProvider;
import org.springframework.context.support.StaticApplicationContext;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.vault.authentication.SessionManager;
import org.springframework.vault.config.AbstractVaultConfiguration;
import org.springframework.vault.support.VaultToken;
import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
import static com.github.tomakehurst.wiremock.client.WireMock.equalTo;
import static com.github.tomakehurst.wiremock.client.WireMock.equalToJson;
import static com.github.tomakehurst.wiremock.client.WireMock.getRequestedFor;
import static com.github.tomakehurst.wiremock.client.WireMock.post;
import static com.github.tomakehurst.wiremock.client.WireMock.postRequestedFor;
import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo;
import static com.github.tomakehurst.wiremock.client.WireMock.verify;
/**
* @author Kaveh Shamsi
* @author Max Brauer
*/
class SpringVaultTemplateBuilderTest {
private WireMockServer mockVaultServer;
@BeforeEach
void setUp() {
mockVaultServer = new WireMockServer();
mockVaultServer.start();
WireMock.configureFor("http", "localhost", mockVaultServer.port());
}
@AfterEach
void tearDown() {
mockVaultServer.stop();
}
@Test
void shouldUseDefaultToken() {
ConfigTokenProvider defaultTokenProvider = () -> "default-token";
var springVaultTemplateBuilder = new SpringVaultTemplateBuilder(defaultTokenProvider, Collections.emptyList(),
givenApplicationContext(defaultTokenProvider));
var vaultProperties = new VaultEnvironmentProperties();
vaultProperties.setPort(mockVaultServer.port());
springVaultTemplateBuilder.build(vaultProperties).read("/secrets/test");
verify(1,
getRequestedFor(urlEqualTo("/v1/secrets/test")).withHeader("X-Vault-Token", equalTo("default-token")));
}
@Test
void shouldUseStaticToken() {
ConfigTokenProvider defaultTokenProvider = () -> "default-token";
var springVaultTemplateBuilder = new SpringVaultTemplateBuilder(defaultTokenProvider, Collections.emptyList(),
givenApplicationContext(defaultTokenProvider));
var vaultProperties = new VaultEnvironmentProperties();
vaultProperties.setPort(mockVaultServer.port());
vaultProperties.setToken("config-token");
springVaultTemplateBuilder.build(vaultProperties).read("/secrets/test");
verify(1, getRequestedFor(urlEqualTo("/v1/secrets/test")).withHeader("X-Vault-Token", equalTo("config-token")));
}
@Test
void shouldUseAppRoleToken() {
ConfigTokenProvider defaultTokenProvider = () -> "default-token";
mockVaultServer.stubFor(post("/v1/auth/approle/login").willReturn(aResponse().withStatus(200).withBody("""
{"auth": {"client_token": "approle-token"}}
""").withHeader("Content-Type", "application/json")));
var springVaultTemplateBuilder = new SpringVaultTemplateBuilder(defaultTokenProvider,
List.of(new AppRoleClientAuthenticationProvider()), givenApplicationContext(defaultTokenProvider));
var vaultProperties = new VaultEnvironmentProperties();
vaultProperties.setPort(mockVaultServer.port());
vaultProperties.setAuthentication(VaultEnvironmentProperties.AuthenticationMethod.APPROLE);
var appRole = vaultProperties.getAppRole();
appRole.setSecretId("secret-id");
appRole.setRoleId("role-id");
springVaultTemplateBuilder.build(vaultProperties).read("/secrets/test");
verify(1,
getRequestedFor(urlEqualTo("/v1/secrets/test")).withHeader("X-Vault-Token", equalTo("approle-token")));
verify(1, postRequestedFor(urlEqualTo("/v1/auth/approle/login")).withRequestBody(equalToJson("""
{"role_id": "role-id", "secret_id": "secret-id"}
""")));
}
private static StaticApplicationContext givenApplicationContext(ConfigTokenProvider defaultTokenProvider) {
var context = new StaticApplicationContext();
context.getBeanFactory()
.registerSingleton("sessionManager", (SessionManager) () -> VaultToken.of(defaultTokenProvider.getToken()));
context.getBeanFactory()
.registerSingleton("vaultThreadPoolTaskScheduler",
new AbstractVaultConfiguration.TaskSchedulerWrapper(new ThreadPoolTaskScheduler()));
context.getBeanFactory()
.registerSingleton("clientHttpRequestFactoryWrapper",
new AbstractVaultConfiguration.ClientFactoryWrapper(new SimpleClientHttpRequestFactory()));
return context;
}
}