diff --git a/geowebcache/core/pom.xml b/geowebcache/core/pom.xml index accff5c27..6036d9837 100644 --- a/geowebcache/core/pom.xml +++ b/geowebcache/core/pom.xml @@ -205,6 +205,12 @@ awaitility test + + + com.github.stefanbirkner + system-rules + test + diff --git a/geowebcache/core/src/main/java/org/geowebcache/GeoWebCacheEnvironment.java b/geowebcache/core/src/main/java/org/geowebcache/GeoWebCacheEnvironment.java index e3c7ba4bd..6ab8cbe74 100644 --- a/geowebcache/core/src/main/java/org/geowebcache/GeoWebCacheEnvironment.java +++ b/geowebcache/core/src/main/java/org/geowebcache/GeoWebCacheEnvironment.java @@ -23,7 +23,6 @@ import org.springframework.beans.factory.config.PlaceholderConfigurerSupport; import org.springframework.core.Constants; import org.springframework.util.PropertyPlaceholderHelper; -import org.springframework.util.PropertyPlaceholderHelper.PlaceholderResolver; /** * Utility class uses to process GeoWebCache configuration workflow through external environment variables. @@ -46,7 +45,7 @@ public class GeoWebCacheEnvironment { /** logger */ - public static Logger LOGGER = Logging.getLogger(GeoWebCacheEnvironment.class.getName()); + public static final Logger LOGGER = Logging.getLogger(GeoWebCacheEnvironment.class.getName()); private static final Constants constants = new Constants(PlaceholderConfigurerSupport.class); @@ -55,9 +54,13 @@ public class GeoWebCacheEnvironment { * placeholders translation. * *

Default to FALSE + * + * @deprecated a static final variable does prevents change during runtime and hinders testing. Use + * {@link #isAllowEnvParametrization()} instead. */ + @Deprecated(forRemoval = true) public static final boolean ALLOW_ENV_PARAMETRIZATION = - Boolean.valueOf(GeoWebCacheExtensions.getProperty("ALLOW_ENV_PARAMETRIZATION")); + Boolean.parseBoolean(GeoWebCacheExtensions.getProperty("ALLOW_ENV_PARAMETRIZATION")); private static final String nullValue = "null"; @@ -67,10 +70,17 @@ public class GeoWebCacheEnvironment { constants.asString("DEFAULT_VALUE_SEPARATOR"), true); - private final PlaceholderResolver resolver = placeholderName -> resolvePlaceholder(placeholderName); - private Properties props; + /** + * Determines if the {@code ALLOW_ENV_PARAMETRIZATION} environment variable is set to {@code true} and hence + * variable variable substitution of configuration parameters using ${} place holders can be performed + * through {@link #resolveValue(Object)}. + */ + public boolean isAllowEnvParametrization() { + return Boolean.parseBoolean(GeoWebCacheExtensions.getProperty("ALLOW_ENV_PARAMETRIZATION")); + } + /** * Internal "props" getter method. * @@ -107,7 +117,7 @@ protected String resolveSystemProperty(String key) { value = System.getenv(key); } return value; - } catch (Throwable ex) { + } catch (RuntimeException ex) { if (LOGGER.isLoggable(Level.FINE)) { LOGGER.fine("Could not access system property '" + key + "': " + ex); } @@ -116,7 +126,7 @@ protected String resolveSystemProperty(String key) { } protected String resolveStringValue(String strVal) throws BeansException { - String resolved = this.helper.replacePlaceholders(strVal, this.resolver); + String resolved = this.helper.replacePlaceholders(strVal, this::resolvePlaceholder); return (resolved.equals(nullValue) ? null : resolved); } @@ -127,19 +137,17 @@ protected String resolveStringValue(String strVal) throws BeansException { *

The method first looks for System variables which take precedence on local ones, then into internal props * injected through the applicationContext. */ - public Object resolveValue(Object value) { - if (value != null) { - if (value instanceof String) { - return resolveStringValue((String) value); - } + @SuppressWarnings("unchecked") + public T resolveValue(T value) { + if (value instanceof String) { + return (T) resolveStringValue((String) value); } return value; } private String resolveValueIfEnabled(String value) { - if (ALLOW_ENV_PARAMETRIZATION) return (String) resolveValue(value); - else return value; + return isAllowEnvParametrization() ? resolveValue(value) : value; } private boolean validateBoolean(String value) { diff --git a/geowebcache/core/src/main/java/org/geowebcache/config/XMLConfiguration.java b/geowebcache/core/src/main/java/org/geowebcache/config/XMLConfiguration.java index 6206baaa3..d320c71aa 100644 --- a/geowebcache/core/src/main/java/org/geowebcache/config/XMLConfiguration.java +++ b/geowebcache/core/src/main/java/org/geowebcache/config/XMLConfiguration.java @@ -53,6 +53,7 @@ import javax.xml.validation.SchemaFactory; import javax.xml.validation.Validator; import org.geotools.util.logging.Logging; +import org.geowebcache.GeoWebCacheEnvironment; import org.geowebcache.GeoWebCacheException; import org.geowebcache.GeoWebCacheExtensions; import org.geowebcache.config.ContextualConfigurationProvider.Context; @@ -282,6 +283,8 @@ public void setDefaultValues(TileLayer layer) { sourceHelper = new WMSHttpHelper(null, null, proxyUrl); log.fine("Not using HTTP credentials for " + wl.getName()); } + GeoWebCacheEnvironment gwcEnv = GeoWebCacheExtensions.bean(GeoWebCacheEnvironment.class); + sourceHelper.setGeoWebCacheEnvironment(gwcEnv); wl.setSourceHelper(sourceHelper); wl.setLockProvider(getGwcConfig().getLockProvider()); diff --git a/geowebcache/core/src/main/java/org/geowebcache/layer/wms/WMSHttpHelper.java b/geowebcache/core/src/main/java/org/geowebcache/layer/wms/WMSHttpHelper.java index cdc4faae6..c5b943877 100644 --- a/geowebcache/core/src/main/java/org/geowebcache/layer/wms/WMSHttpHelper.java +++ b/geowebcache/core/src/main/java/org/geowebcache/layer/wms/WMSHttpHelper.java @@ -13,6 +13,7 @@ */ package org.geowebcache.layer.wms; +import com.google.common.annotations.VisibleForTesting; import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; @@ -29,6 +30,7 @@ import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.NameValuePair; +import org.apache.http.client.ClientProtocolException; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; @@ -36,7 +38,9 @@ import org.apache.http.entity.StringEntity; import org.apache.http.message.BasicNameValuePair; import org.geotools.util.logging.Logging; +import org.geowebcache.GeoWebCacheEnvironment; import org.geowebcache.GeoWebCacheException; +import org.geowebcache.GeoWebCacheExtensions; import org.geowebcache.io.Resource; import org.geowebcache.layer.TileResponseReceiver; import org.geowebcache.mime.ErrorMime; @@ -48,17 +52,39 @@ import org.geowebcache.util.URLs; import org.springframework.util.Assert; -/** This class is a wrapper for HTTP interaction with WMS backend */ +/** + * Helper class for HTTP interactions of {@link WMSLayer} with a WMS backend. + * + *

HTTP Basic Auth username and password supplied to the constructor support environment parametrization. + * + * @see GeoWebCacheEnvironment#isAllowEnvParametrization() + * @see GeoWebCacheEnvironment#resolveValue(Object) + */ public class WMSHttpHelper extends WMSSourceHelper { private static final Logger log = Logging.getLogger(WMSHttpHelper.class.getName()); + /** + * Used by {@link #getResolvedHttpUsername()} and {@link #getResolvedHttpPassword()} to + * {@link GeoWebCacheEnvironment#resolveValue resolve} the actual values against environment variables when + * {@link GeoWebCacheEnvironment#isAllowEnvParametrization() ALLOW_ENV_PARAMETRIZATION} is enabled. + */ + protected GeoWebCacheEnvironment gwcEnv; + private final URL proxyUrl; + /** + * HTTP Basic Auth username, might be de-referenced using environment variable substitution. Always access it + * through {@link #getResolvedHttpUsername()} + */ private final String httpUsername; + /** + * HTTP Basic Auth password, might be de-referenced using environment variable substitution. Always access it + * through {@link #getResolvedHttpUsername()} + */ private final String httpPassword; - protected volatile HttpClient client; + protected HttpClient client; public WMSHttpHelper() { this(null, null, null); @@ -71,23 +97,75 @@ public WMSHttpHelper(String httpUsername, String httpPassword, URL proxyUrl) { this.proxyUrl = proxyUrl; } - HttpClient getHttpClient() { - if (client == null) { - synchronized (this) { - if (client != null) { - return client; - } + /** + * Used by {@link #executeRequest} + * + * @return the actual http username to use when executing requests + */ + public String getResolvedHttpUsername() { + return resolvePlaceHolders(httpUsername); + } + + /** + * Used by {@link #executeRequest} + * + * @return the actual http password to use when executing requests + */ + public String getResolvedHttpPassword() { + return resolvePlaceHolders(httpPassword); + } + + /** + * Assigns the environment variable {@link GeoWebCacheEnvironment#resolveValue resolver} to perform variable + * substitution against the configured http username and password during {@link #executeRequest} + * + *

When unset, a bean of this type will be looked up through {@link GeoWebCacheExtensions#bean(Class)} + */ + public void setGeoWebCacheEnvironment(GeoWebCacheEnvironment gwcEnv) { + this.gwcEnv = gwcEnv; + } - HttpClientBuilder builder = new HttpClientBuilder( - null, getBackendTimeout(), httpUsername, httpPassword, proxyUrl, getConcurrency()); + private String resolvePlaceHolders(String value) { + GeoWebCacheEnvironment env = getEnvironment(); + return env != null && env.isAllowEnvParametrization() ? env.resolveValue(value) : value; + } - client = builder.buildClient(); - } + private GeoWebCacheEnvironment getEnvironment() { + GeoWebCacheEnvironment env = this.gwcEnv; + if (env == null) { + env = GeoWebCacheExtensions.bean(GeoWebCacheEnvironment.class); + this.gwcEnv = env; } + return env; + } + /** + * Note: synchronizing the entire method avoids double-checked locking altogether. Modern JVMs optimize this and + * reduce the overhead of synchronization. Otherwise PMD complains with a `Double checked locking is not thread safe + * in Java.` error. + */ + synchronized HttpClient getHttpClient() { + if (client == null) { + int backendTimeout = getBackendTimeout(); + String user = getResolvedHttpUsername(); + String password = getResolvedHttpPassword(); + URL proxy = proxyUrl; + int concurrency = getConcurrency(); + client = buildHttpClient(backendTimeout, user, password, proxy, concurrency); + } return client; } + @VisibleForTesting + HttpClient buildHttpClient(int backendTimeout, String username, String password, URL proxy, int concurrency) { + + URL serverUrl = null; + HttpClientBuilder builder = + new HttpClientBuilder(serverUrl, backendTimeout, username, password, proxy, concurrency); + + return builder.buildClient(); + } + /** Loops over the different backends, tries the request */ @Override protected void makeRequest( @@ -319,7 +397,13 @@ public HttpResponse executeRequest( if (log.isLoggable(Level.FINER)) { log.finer(method.toString()); } - return getHttpClient().execute(method); + HttpClient httpClient = getHttpClient(); + return execute(httpClient, method); + } + + @VisibleForTesting + HttpResponse execute(HttpClient httpClient, HttpRequestBase method) throws IOException, ClientProtocolException { + return httpClient.execute(method); } private String processRequestParameters(Map parameters) throws UnsupportedEncodingException { diff --git a/geowebcache/core/src/test/java/org/geowebcache/GeoWebCacheEnvironmentTest.java b/geowebcache/core/src/test/java/org/geowebcache/GeoWebCacheEnvironmentTest.java index 210688586..d9840ab6f 100644 --- a/geowebcache/core/src/test/java/org/geowebcache/GeoWebCacheEnvironmentTest.java +++ b/geowebcache/core/src/test/java/org/geowebcache/GeoWebCacheEnvironmentTest.java @@ -58,7 +58,7 @@ public void testEnvironment() { Assert.assertEquals(1, extensions.size()); Assert.assertTrue(extensions.contains(genv)); - Assert.assertTrue(GeoWebCacheEnvironment.ALLOW_ENV_PARAMETRIZATION); + Assert.assertTrue(genv.isAllowEnvParametrization()); } @Test diff --git a/geowebcache/core/src/test/java/org/geowebcache/config/EnvironmentAwareXMLConfigurationTest.java b/geowebcache/core/src/test/java/org/geowebcache/config/EnvironmentAwareXMLConfigurationTest.java new file mode 100644 index 000000000..77ab90fb8 --- /dev/null +++ b/geowebcache/core/src/test/java/org/geowebcache/config/EnvironmentAwareXMLConfigurationTest.java @@ -0,0 +1,263 @@ +/** + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General + * Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any + * later version. + * + *

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + *

You should have received a copy of the GNU Lesser General Public License along with this program. If not, see + * . + */ +package org.geowebcache.config; + +import static java.util.Objects.requireNonNull; +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.io.File; +import java.net.URISyntaxException; +import java.util.List; +import java.util.Map; +import org.geowebcache.GeoWebCacheEnvironment; +import org.geowebcache.GeoWebCacheException; +import org.geowebcache.GeoWebCacheExtensions; +import org.geowebcache.grid.GridSetBroker; +import org.geowebcache.layer.wms.WMSHttpHelper; +import org.geowebcache.layer.wms.WMSLayer; +import org.geowebcache.layer.wms.WMSSourceHelper; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Rule; +import org.junit.Test; +import org.junit.contrib.java.lang.system.EnvironmentVariables; +import org.springframework.context.ApplicationContext; +import org.springframework.web.context.WebApplicationContext; + +public class EnvironmentAwareXMLConfigurationTest { + /** Allows to set environment variables for each individual test */ + @Rule + public final EnvironmentVariables environmentVariables = new EnvironmentVariables(); + + /** Loaded from {@literal geowebcache-config-env.xml}, defines the {@link WMSLayer}s below */ + private XMLConfiguration xmlConfig; + + /** Test layer with http user/pwd set from global config */ + private WMSLayer withDefaultCredentials; + + /** Test layer with http user/pwd defined as env variable placeholders */ + private WMSLayer withEnvVariableCredentials; + + /** Test layer with its own, non parametrized http user/pwd */ + private WMSLayer withCustomCredentials; + + /** + * Test layer with its own, non parametrized http user/pwd, where their values contain the '${' placeholder prefix + */ + private WMSLayer customCredentialsWithEnvPrefix; + + /** + * Make {@code GeoWebCacheExtensions.bean(GeoWebCacheEnvironment.class)} return a bean, for + * {@link XMLConfiguration#setDefaultValues(TileLayer)} to set it on each + * {@link WMSHttpHelper#setGeoWebCacheEnvironment(GeoWebCacheEnvironment)} + */ + @BeforeClass + public static void setUpAppContext() { + GeoWebCacheEnvironment gwcEnv = new GeoWebCacheEnvironment(); + ApplicationContext appContext = mock(ApplicationContext.class); + + when(appContext.getBeansOfType(GeoWebCacheEnvironment.class)) + .thenReturn(Map.of("geoWebCacheEnvironment", gwcEnv)); + when(appContext.getBean("geoWebCacheEnvironment")).thenReturn(gwcEnv); + new GeoWebCacheExtensions().setApplicationContext(appContext); + } + + /** + * Set up the following environment variables and load the test layers from {@literal geowebcache-config-env.xml}: + * + *

+ */ + @Before + public void setUp() throws URISyntaxException, GeoWebCacheException { + + setEnvironmentVariablesForCredentials(); + + xmlConfig = loadConfig(); + withDefaultCredentials = getLayer("default_credentials"); + withEnvVariableCredentials = getLayer("env_var_credentials"); + withCustomCredentials = getLayer("custom_credentials"); + customCredentialsWithEnvPrefix = getLayer("custom_credentials_with_env_prefix"); + } + + private void setEnvironmentVariablesForCredentials() { + environmentVariables.set("DEFAULT_USER", "default_user_value"); + environmentVariables.set("DEFAULT_SECRET", "default_secret_value"); + environmentVariables.set("CUSTOM_USER", "custom_user_value"); + environmentVariables.set("CUSTOM_SECRET", "custom_secret_value"); + } + + private void enableEnvParametrization() { + environmentVariables.set("ALLOW_ENV_PARAMETRIZATION", "true"); + } + + private WMSLayer getLayer(String layerName) { + return (WMSLayer) xmlConfig.getLayer(layerName).orElseThrow(); + } + + private XMLConfiguration loadConfig() throws GeoWebCacheException, URISyntaxException { + File cpDirectory = + new File(requireNonNull(this.getClass().getResource("./")).toURI()); + XMLFileResourceProvider resourceProvider = new XMLFileResourceProvider( + "geowebcache-config-env.xml", (WebApplicationContext) null, cpDirectory.getAbsolutePath(), null); + + XMLConfiguration config = new XMLConfiguration(null, resourceProvider); + config.setGridSetBroker(new GridSetBroker(List.of(new DefaultGridsets(true, true)))); + config.afterPropertiesSet(); + return config; + } + + @Test + public void testLayerWithDefaultCredentialsAllowEnvDisabled() { + assertCredentials(withDefaultCredentials, null, null, "layer loading shall not change the layer config"); + + xmlConfig.setDefaultValues(withDefaultCredentials); + assertCredentials( + withDefaultCredentials, null, null, "setting default values shall not change the layer config"); + + assertCredentials( + withDefaultCredentials.getSourceHelper(), + "${DEFAULT_USER}", + "${DEFAULT_SECRET}", + "sourceHelper should be assigned the default user and password, no env var substitution expected"); + } + + @Test + public void testLayerWithDefaultCredentialsAllowEnvEnabled() { + enableEnvParametrization(); + + assertCredentials(withDefaultCredentials, null, null, "layer loading shall not change the layer config"); + + xmlConfig.setDefaultValues(withDefaultCredentials); + assertCredentials( + withDefaultCredentials, null, null, "setting default values shall not change the layer config"); + + assertCredentials( + withDefaultCredentials.getSourceHelper(), + "default_user_value", + "default_secret_value", + "sourceHelper should have resolved the global user and password"); + } + + @Test + public void testLayerWithEnvVarCredentialsAllowEnvDisabled() { + assertCredentials( + withEnvVariableCredentials, + "${CUSTOM_USER}", + "${CUSTOM_SECRET}", + "layer loading shall not change the layer config"); + + xmlConfig.setDefaultValues(withEnvVariableCredentials); + assertCredentials( + withEnvVariableCredentials, + "${CUSTOM_USER}", + "${CUSTOM_SECRET}", + "setting default values shall not change the layer config"); + + assertCredentials( + withEnvVariableCredentials.getSourceHelper(), + "${CUSTOM_USER}", + "${CUSTOM_SECRET}", + "sourceHelper should keep the layer user and password, no env var substitution expected"); + } + + @Test + public void testLayerWithEnvVarCredentialsAllowEnvEnabled() { + enableEnvParametrization(); + + assertCredentials( + withEnvVariableCredentials, + "${CUSTOM_USER}", + "${CUSTOM_SECRET}", + "layer loading shall not change the layer config"); + + xmlConfig.setDefaultValues(withEnvVariableCredentials); + assertCredentials( + withEnvVariableCredentials, + "${CUSTOM_USER}", + "${CUSTOM_SECRET}", + "setting default values shall not change the layer config"); + + assertCredentials( + withEnvVariableCredentials.getSourceHelper(), + "custom_user_value", + "custom_secret_value", + "sourceHelper should have resolved the layer's user and password variables"); + } + + @Test + public void testLayerWithCustomCredentialsIsAllowEnvAgnostic() { + + assertLayerUnchanged(withCustomCredentials, "testuser", "testpass"); + + enableEnvParametrization(); + + assertLayerUnchanged(withCustomCredentials, "testuser", "testpass"); + } + + @Test + public void testLayerWithCustomCredentialsHavingPlaceholderPrefixIsAllowEnvAgnostic() { + + assertLayerUnchanged(customCredentialsWithEnvPrefix, "${user", "pass${word"); + + enableEnvParametrization(); + + assertLayerUnchanged(customCredentialsWithEnvPrefix, "${user", "pass${word"); + } + + public void assertLayerUnchanged(WMSLayer staticCredentials, String user, String password) { + assertCredentials( + staticCredentials, + user, + password, + "parametrization shouldn't affect a non parametrized layer's credentials"); + + xmlConfig.setDefaultValues(staticCredentials); + assertCredentials( + staticCredentials, + user, + password, + "set default values shouldn't affect a non parametrized layer's credenals"); + + assertCredentials( + (WMSHttpHelper) staticCredentials.getSourceHelper(), + user, + password, + "set default values shouldn't affect a non parametrized layer's credenals"); + + WMSHttpHelper sourceHelper = (WMSHttpHelper) staticCredentials.getSourceHelper(); + assertCredentials( + sourceHelper, + user, + password, + "source helper shouldn't not change credential values on a non parametrized layer"); + } + + private void assertCredentials( + WMSSourceHelper wmsSourceHelper, String expectedUser, String expectedPassword, String message) { + + WMSHttpHelper helper = (WMSHttpHelper) wmsSourceHelper; + assertEquals(message, expectedUser, helper.getResolvedHttpUsername()); + assertEquals(message, expectedPassword, helper.getResolvedHttpPassword()); + } + + private void assertCredentials(WMSLayer layer, String expectedUser, String expectedPassword, String message) { + assertEquals(message, expectedUser, layer.getHttpUsername()); + assertEquals(message, expectedPassword, layer.getHttpPassword()); + } +} diff --git a/geowebcache/core/src/test/java/org/geowebcache/layer/wms/WMSHttpHelperTest.java b/geowebcache/core/src/test/java/org/geowebcache/layer/wms/WMSHttpHelperTest.java new file mode 100644 index 000000000..7ca14cfb1 --- /dev/null +++ b/geowebcache/core/src/test/java/org/geowebcache/layer/wms/WMSHttpHelperTest.java @@ -0,0 +1,104 @@ +/** + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General + * Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any + * later version. + * + *

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + *

You should have received a copy of the GNU Lesser General Public License along with this program. If not, see + * . + */ +package org.geowebcache.layer.wms; + +import static org.junit.Assert.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; + +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import org.apache.http.client.HttpClient; +import org.apache.http.client.methods.HttpRequestBase; +import org.geowebcache.GeoWebCacheEnvironment; +import org.geowebcache.layer.wms.WMSLayer.HttpRequestMode; +import org.junit.Rule; +import org.junit.Test; +import org.junit.contrib.java.lang.system.EnvironmentVariables; +import org.mockito.Mockito; + +public class WMSHttpHelperTest { + + /** Allows to set environment variables for each individual test */ + @Rule + public final EnvironmentVariables environmentVariables = new EnvironmentVariables(); + + private WMSHttpHelper wmsHelper(String username, String password) { + WMSHttpHelper wmsHelper = new WMSHttpHelper(username, password, null); + wmsHelper.setGeoWebCacheEnvironment(new GeoWebCacheEnvironment()); + return wmsHelper; + } + + private void enableEnvParametrization(boolean enable) { + environmentVariables.set("ALLOW_ENV_PARAMETRIZATION", String.valueOf(enable)); + } + + @Test + public void testNoGeoWebCacheEnvironmentSet() { + WMSHttpHelper helper = wmsHelper("u${ername}", "pas${word}"); + helper.setGeoWebCacheEnvironment(null); + assertEquals("u${ername}", helper.getResolvedHttpUsername()); + assertEquals("pas${word}", helper.getResolvedHttpPassword()); + } + + @Test + public void testCredentialsUnchangedWhenEnvParametrizationDisabled() { + WMSHttpHelper helper = wmsHelper("u${ername}", "pas${word}"); + assertEquals("u${ername}", helper.getResolvedHttpUsername()); + assertEquals("pas${word}", helper.getResolvedHttpPassword()); + + enableEnvParametrization(false); + assertEquals("u${ername}", helper.getResolvedHttpUsername()); + assertEquals("pas${word}", helper.getResolvedHttpPassword()); + } + + @Test + public void testCredentialsResolvedWhenEnvParametrizationEnabled() { + WMSHttpHelper helper = wmsHelper("${GWC_USER}", "${GWC_PWD}"); + + enableEnvParametrization(true); + assertEquals("${GWC_USER}", helper.getResolvedHttpUsername()); + assertEquals("${GWC_PWD}", helper.getResolvedHttpPassword()); + + environmentVariables.set("GWC_USER", "user_resolved"); + environmentVariables.set("GWC_PWD", "pwd_resolved"); + + assertEquals("user_resolved", helper.getResolvedHttpUsername()); + assertEquals("pwd_resolved", helper.getResolvedHttpPassword()); + } + + @Test + public void testExecuteRequestUsesResolvedCredentials() throws IOException, URISyntaxException { + enableEnvParametrization(true); + environmentVariables.set("GWC_USER", "user_resolved"); + environmentVariables.set("GWC_PWD", "pwd_resolved"); + + WMSHttpHelper helper = spy(wmsHelper("${GWC_USER}", "${GWC_PWD}")); + // do not actually execute the request + doReturn(null).when(helper).execute(any(HttpClient.class), any(HttpRequestBase.class)); + + // just check the http client is built with the resolved credeltials + URL url = new URI("http://example.com/wms?request=getcapabilities").toURL(); + helper.executeRequest(url, null, 0, HttpRequestMode.Get); + verify(helper) + .buildHttpClient( + Mockito.anyInt(), + Mockito.eq("user_resolved"), + Mockito.eq("pwd_resolved"), + Mockito.nullable(URL.class), + Mockito.anyInt()); + } +} diff --git a/geowebcache/core/src/test/resources/org/geowebcache/config/geowebcache-config-env.xml b/geowebcache/core/src/test/resources/org/geowebcache/config/geowebcache-config-env.xml new file mode 100644 index 000000000..01e9f8b2d --- /dev/null +++ b/geowebcache/core/src/test/resources/org/geowebcache/config/geowebcache-config-env.xml @@ -0,0 +1,61 @@ + + + + 1.8.0 + ${DEFAULT_USER} + ${DEFAULT_SECRET} + + + + default_credentials + + Layer with default http user and pwd + + + https://example.com/geoserver/wms + + + + + custom_credentials + + Layer with non-parameterized custom http user and pwd + + + https://example.com/geoserver/wms + + testuser + testpass + + + + env_var_credentials + + Layer with parameterized custom http user and pwd + + + https://example.com/geoserver/wms + + ${CUSTOM_USER} + ${CUSTOM_SECRET} + + + + custom_credentials_with_env_prefix + + Layer with non-parameterized http user and pwd that contain the ${ placeholder prefix + + + https://example.com/geoserver/wms + + ${user + pass${word + + + + \ No newline at end of file diff --git a/geowebcache/diskquota/core/src/main/java/org/geowebcache/diskquota/ConfigLoader.java b/geowebcache/diskquota/core/src/main/java/org/geowebcache/diskquota/ConfigLoader.java index cf7e01b8d..4cc13dfa7 100644 --- a/geowebcache/diskquota/core/src/main/java/org/geowebcache/diskquota/ConfigLoader.java +++ b/geowebcache/diskquota/core/src/main/java/org/geowebcache/diskquota/ConfigLoader.java @@ -266,8 +266,8 @@ public static DiskQuotaConfig loadConfiguration(final Reader reader, XStream xst final GeoWebCacheEnvironment gwcEnvironment = GeoWebCacheExtensions.bean(GeoWebCacheEnvironment.class); - if (gwcEnvironment != null && GeoWebCacheEnvironment.ALLOW_ENV_PARAMETRIZATION) { - fromXML.setQuotaStore((String) gwcEnvironment.resolveValue(fromXML.getQuotaStore())); + if (gwcEnvironment != null && gwcEnvironment.isAllowEnvParametrization()) { + fromXML.setQuotaStore(gwcEnvironment.resolveValue(fromXML.getQuotaStore())); } return fromXML; diff --git a/geowebcache/diskquota/jdbc/src/main/java/org/geowebcache/diskquota/jdbc/JDBCConfiguration.java b/geowebcache/diskquota/jdbc/src/main/java/org/geowebcache/diskquota/jdbc/JDBCConfiguration.java index 9247b9444..9c92110dd 100644 --- a/geowebcache/diskquota/jdbc/src/main/java/org/geowebcache/diskquota/jdbc/JDBCConfiguration.java +++ b/geowebcache/diskquota/jdbc/src/main/java/org/geowebcache/diskquota/jdbc/JDBCConfiguration.java @@ -397,21 +397,18 @@ public JDBCConfiguration clone(boolean allowEnvParametrization) { final GeoWebCacheEnvironment gwcEnvironment = GeoWebCacheExtensions.bean(GeoWebCacheEnvironment.class); - if (allowEnvParametrization && gwcEnvironment != null && GeoWebCacheEnvironment.ALLOW_ENV_PARAMETRIZATION) { - conf.setDialect((String) gwcEnvironment.resolveValue(getDialect())); - conf.setJNDISource((String) gwcEnvironment.resolveValue(getJNDISource())); + if (allowEnvParametrization && gwcEnvironment != null && gwcEnvironment.isAllowEnvParametrization()) { + conf.setDialect(gwcEnvironment.resolveValue(getDialect())); + conf.setJNDISource(gwcEnvironment.resolveValue(getJNDISource())); ConnectionPoolConfiguration connectionPoolConfig = getConnectionPool(); if (connectionPoolConfig != null) { ConnectionPoolConfiguration expConnectionPoolConfig = SerializationUtils.clone(connectionPoolConfig); - expConnectionPoolConfig.setDriver( - (String) gwcEnvironment.resolveValue(connectionPoolConfig.getDriver())); - expConnectionPoolConfig.setUrl((String) gwcEnvironment.resolveValue(connectionPoolConfig.getUrl())); - expConnectionPoolConfig.setUsername( - (String) gwcEnvironment.resolveValue(connectionPoolConfig.getUsername())); - expConnectionPoolConfig.setPassword( - (String) gwcEnvironment.resolveValue(connectionPoolConfig.getPassword())); + expConnectionPoolConfig.setDriver(gwcEnvironment.resolveValue(connectionPoolConfig.getDriver())); + expConnectionPoolConfig.setUrl(gwcEnvironment.resolveValue(connectionPoolConfig.getUrl())); + expConnectionPoolConfig.setUsername(gwcEnvironment.resolveValue(connectionPoolConfig.getUsername())); + expConnectionPoolConfig.setPassword(gwcEnvironment.resolveValue(connectionPoolConfig.getPassword())); expConnectionPoolConfig.setValidationQuery( - (String) gwcEnvironment.resolveValue(connectionPoolConfig.getValidationQuery())); + gwcEnvironment.resolveValue(connectionPoolConfig.getValidationQuery())); conf.setConnectionPool(expConnectionPoolConfig); } diff --git a/geowebcache/pom.xml b/geowebcache/pom.xml index a7aa88cec..8cb17725b 100644 --- a/geowebcache/pom.xml +++ b/geowebcache/pom.xml @@ -493,6 +493,13 @@ jackson-core ${jackson.version} + + + com.github.stefanbirkner + system-rules + 1.19.0 + test + diff --git a/geowebcache/s3storage/src/main/java/org/geowebcache/s3/S3BlobStoreInfo.java b/geowebcache/s3storage/src/main/java/org/geowebcache/s3/S3BlobStoreInfo.java index 909bfb956..998911cc2 100644 --- a/geowebcache/s3storage/src/main/java/org/geowebcache/s3/S3BlobStoreInfo.java +++ b/geowebcache/s3storage/src/main/java/org/geowebcache/s3/S3BlobStoreInfo.java @@ -365,7 +365,7 @@ private String nullSafeResolveString(String value, GeoWebCacheEnvironment gwcEnv if (value == null) { return null; } - return gwcEnvironment.resolveValue(value).toString(); + return gwcEnvironment.resolveValue(value); } private Integer toInteger(String value) { diff --git a/geowebcache/swiftblob/src/main/java/org/geowebcache/swift/SwiftBlobStoreConfigProvider.java b/geowebcache/swiftblob/src/main/java/org/geowebcache/swift/SwiftBlobStoreConfigProvider.java index ce80e320b..57a681f06 100644 --- a/geowebcache/swiftblob/src/main/java/org/geowebcache/swift/SwiftBlobStoreConfigProvider.java +++ b/geowebcache/swiftblob/src/main/java/org/geowebcache/swift/SwiftBlobStoreConfigProvider.java @@ -60,12 +60,12 @@ private static String resolveFromEnv(String str) { if (gwcEnvironment == null) { gwcEnvironment = GeoWebCacheExtensions.bean(GeoWebCacheEnvironment.class); } - if (gwcEnvironment != null && str != null && GeoWebCacheEnvironment.ALLOW_ENV_PARAMETRIZATION) { - Object result = gwcEnvironment.resolveValue(str); + if (gwcEnvironment != null && str != null && gwcEnvironment.isAllowEnvParametrization()) { + String result = gwcEnvironment.resolveValue(str); if (result == null) { return null; } - return result.toString(); + return result; } return str; }