Extract ResourceEntityResolver HTTPS schema resolution fallback

This commit extracts the DTD/XSD remote lookup fallback from the
resolveEntity() method into a protected method.

A WARN-level logging statement is added to the extracted fallback in
order to make it clear that remote lookup happened.

Overriding the protected method would allow users to avoid this
fallback entirely if it isn't desirable, without the need to duplicate
the local resolution code.

Closes gh-29697
This commit is contained in:
Simon Baslé
2022-12-16 11:47:22 +01:00
committed by Sam Brannen
parent 57cfb94f1f
commit 5965917d16
2 changed files with 174 additions and 17 deletions

View File

@@ -0,0 +1,131 @@
/*
* Copyright 2002-2022 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.beans.factory.xml;
import java.io.IOException;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.lang.Nullable;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
/**
* @author Simon Baslé
*/
class ResourceEntityResolverTests {
@Test
void resolveEntityCallsFallbackWithNullOnDtd() throws IOException, SAXException {
ResourceEntityResolver resolver = new FallingBackEntityResolver(false, null);
assertThat(resolver.resolveEntity("testPublicId", "https://example.org/exampleschema.dtd"))
.isNull();
}
@Test
void resolveEntityCallsFallbackWithNullOnXsd() throws IOException, SAXException {
ResourceEntityResolver resolver = new FallingBackEntityResolver(false, null);
assertThat(resolver.resolveEntity("testPublicId", "https://example.org/exampleschema.xsd"))
.isNull();
}
@Test
void resolveEntityCallsFallbackWithThrowOnDtd() {
ResourceEntityResolver resolver = new FallingBackEntityResolver(true, null);
assertThatIllegalStateException().isThrownBy(
() -> resolver.resolveEntity("testPublicId", "https://example.org/exampleschema.dtd"))
.withMessage("FallingBackEntityResolver that throws");
}
@Test
void resolveEntityCallsFallbackWithThrowOnXsd() {
ResourceEntityResolver resolver = new FallingBackEntityResolver(true, null);
assertThatIllegalStateException().isThrownBy(
() -> resolver.resolveEntity("testPublicId", "https://example.org/exampleschema.xsd"))
.withMessage("FallingBackEntityResolver that throws");
}
@Test
void resolveEntityCallsFallbackWithInputSourceOnDtd() throws IOException, SAXException {
InputSource expected = Mockito.mock(InputSource.class);
ResourceEntityResolver resolver = new FallingBackEntityResolver(false, expected);
assertThat(resolver.resolveEntity("testPublicId", "https://example.org/exampleschema.dtd"))
.isNotNull()
.isSameAs(expected);
}
@Test
void resolveEntityCallsFallbackWithInputSourceOnXsd() throws IOException, SAXException {
InputSource expected = Mockito.mock(InputSource.class);
ResourceEntityResolver resolver = new FallingBackEntityResolver(false, expected);
assertThat(resolver.resolveEntity("testPublicId", "https://example.org/exampleschema.xsd"))
.isNotNull()
.isSameAs(expected);
}
@Test
void resolveEntityDoesntCallFallbackIfNotSchema() throws IOException, SAXException {
ResourceEntityResolver resolver = new FallingBackEntityResolver(true, null);
assertThat(resolver.resolveEntity("testPublicId", "https://example.org/example.xml"))
.isNull();
}
private static final class NoOpResourceLoader implements ResourceLoader {
@Override
public Resource getResource(String location) {
return null;
}
@Override
public ClassLoader getClassLoader() {
return ResourceEntityResolverTests.class.getClassLoader();
}
}
private static class FallingBackEntityResolver extends ResourceEntityResolver {
private final boolean shouldThrow;
@Nullable
private final InputSource returnValue;
private FallingBackEntityResolver(boolean shouldThrow, @Nullable InputSource returnValue) {
super(new NoOpResourceLoader());
this.shouldThrow = shouldThrow;
this.returnValue = returnValue;
}
@Nullable
@Override
protected InputSource resolveSchemaEntity(String publicId, String systemId) {
if (shouldThrow) throw new IllegalStateException("FallingBackEntityResolver that throws");
return this.returnValue;
}
}
}