Use context class loader when watching SSL resource

Update `BundleContentProperty` to use a provided resource loader when
watching files.

Fixes gh-42468
This commit is contained in:
Phillip Webb 2024-10-22 19:22:33 -07:00
parent 499672184c
commit dcbf0096d8
3 changed files with 25 additions and 12 deletions

View File

@ -18,9 +18,9 @@ package org.springframework.boot.autoconfigure.ssl;
import java.nio.file.Path; import java.nio.file.Path;
import org.springframework.boot.io.ApplicationResourceLoader;
import org.springframework.boot.ssl.pem.PemContent; import org.springframework.boot.ssl.pem.PemContent;
import org.springframework.core.io.Resource; import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
@ -51,9 +51,10 @@ record BundleContentProperty(String name, String value) {
return StringUtils.hasText(this.value); return StringUtils.hasText(this.value);
} }
Path toWatchPath() { Path toWatchPath(ResourceLoader resourceLoader) {
try { try {
Resource resource = getResource(); Assert.state(!isPemContent(), "Value contains PEM content");
Resource resource = resourceLoader.getResource(this.value);
if (!resource.isFile()) { if (!resource.isFile()) {
throw new BundleContentNotWatchableException(this); throw new BundleContentNotWatchableException(this);
} }
@ -68,9 +69,4 @@ record BundleContentProperty(String name, String value) {
} }
} }
private Resource getResource() {
Assert.state(!isPemContent(), "Value contains PEM content");
return new ApplicationResourceLoader().getResource(this.value);
}
} }

View File

@ -111,7 +111,7 @@ class SslPropertiesBundleRegistrar implements SslBundleRegistrar {
try { try {
return properties.stream() return properties.stream()
.filter(BundleContentProperty::hasValue) .filter(BundleContentProperty::hasValue)
.map(BundleContentProperty::toWatchPath) .map((content) -> content.toWatchPath(this.resourceLoader))
.collect(Collectors.toSet()); .collect(Collectors.toSet());
} }
catch (BundleContentNotWatchableException ex) { catch (BundleContentNotWatchableException ex) {

View File

@ -22,9 +22,15 @@ import java.nio.file.Path;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.boot.io.ApplicationResourceLoader;
import org.springframework.core.io.ResourceLoader;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.assertj.core.api.Assertions.assertThatIllegalStateException; import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
import static org.mockito.BDDMockito.then;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.spy;
/** /**
* Tests for {@link BundleContentProperty}. * Tests for {@link BundleContentProperty}.
@ -72,7 +78,7 @@ class BundleContentPropertyTests {
@Test @Test
void toWatchPathWhenNotPathThrowsException() { void toWatchPathWhenNotPathThrowsException() {
BundleContentProperty property = new BundleContentProperty("name", PEM_TEXT); BundleContentProperty property = new BundleContentProperty("name", PEM_TEXT);
assertThatIllegalStateException().isThrownBy(property::toWatchPath) assertThatIllegalStateException().isThrownBy(() -> property.toWatchPath(new ApplicationResourceLoader()))
.withMessage("Unable to convert value of property 'name' to a path"); .withMessage("Unable to convert value of property 'name' to a path");
} }
@ -81,13 +87,24 @@ class BundleContentPropertyTests {
URL resource = getClass().getResource("keystore.jks"); URL resource = getClass().getResource("keystore.jks");
Path file = Path.of(resource.toURI()).toAbsolutePath(); Path file = Path.of(resource.toURI()).toAbsolutePath();
BundleContentProperty property = new BundleContentProperty("name", file.toString()); BundleContentProperty property = new BundleContentProperty("name", file.toString());
assertThat(property.toWatchPath()).isEqualTo(file); assertThat(property.toWatchPath(new ApplicationResourceLoader())).isEqualTo(file);
}
@Test
void toWatchPathUsesResourceLoader() throws URISyntaxException {
URL resource = getClass().getResource("keystore.jks");
Path file = Path.of(resource.toURI()).toAbsolutePath();
BundleContentProperty property = new BundleContentProperty("name", file.toString());
ResourceLoader resourceLoader = spy(new ApplicationResourceLoader());
assertThat(property.toWatchPath(resourceLoader)).isEqualTo(file);
then(resourceLoader).should(atLeastOnce()).getResource(file.toString());
} }
@Test @Test
void shouldThrowBundleContentNotWatchableExceptionIfContentIsNotWatchable() { void shouldThrowBundleContentNotWatchableExceptionIfContentIsNotWatchable() {
BundleContentProperty property = new BundleContentProperty("name", "https://example.com/"); BundleContentProperty property = new BundleContentProperty("name", "https://example.com/");
assertThatExceptionOfType(BundleContentNotWatchableException.class).isThrownBy(property::toWatchPath) assertThatExceptionOfType(BundleContentNotWatchableException.class)
.isThrownBy(() -> property.toWatchPath(new ApplicationResourceLoader()))
.withMessageContaining("Only 'file:' resources are watchable"); .withMessageContaining("Only 'file:' resources are watchable");
} }