Skip to content

Commit

Permalink
Fix/1769 (#1792)
Browse files Browse the repository at this point in the history
Co-authored-by: huan415 <435826111@qq.com>
  • Loading branch information
SteKoe and huan415 committed Aug 15, 2021
1 parent e921831 commit c5ec088
Show file tree
Hide file tree
Showing 8 changed files with 221 additions and 21 deletions.
2 changes: 1 addition & 1 deletion .editorconfig
Expand Up @@ -20,7 +20,7 @@ ij_java_blank_lines_after_class_header = 0
ij_java_doc_enable_formatting = false
ij_java_class_count_to_use_import_on_demand = 100
ij_java_names_count_to_use_import_on_demand = 100
ij_java_imports_layout = |,java.**,|,javax.**,|,*,org.springframework.**,|,de.codecentric.boot.admin.**,|,$*
ij_java_imports_layout = |,java.**,|,javax.**,|,*,|,de.codecentric.boot.admin.**,|,$*
ij_java_layout_static_imports_separately = true

[*.xml]
Expand Down
Expand Up @@ -46,6 +46,7 @@
import de.codecentric.boot.admin.server.ui.extensions.UiExtensions;
import de.codecentric.boot.admin.server.ui.extensions.UiExtensionsScanner;
import de.codecentric.boot.admin.server.ui.extensions.UiRoutesScanner;
import de.codecentric.boot.admin.server.ui.web.HomepageForwardingFilterConfig;
import de.codecentric.boot.admin.server.ui.web.UiController;
import de.codecentric.boot.admin.server.ui.web.UiController.Settings;

Expand Down Expand Up @@ -123,6 +124,20 @@ public SpringResourceTemplateResolver adminTemplateResolver() {
return resolver;
}

@Bean
public HomepageForwardingFilterConfig homepageForwardingFilterConfig() throws IOException {
String homepage = this.adminServer.path("/");

List<String> extensionRoutes = new UiRoutesScanner(this.applicationContext)
.scan(this.adminUi.getExtensionResourceLocations());
List<String> routesIncludes = Stream.concat(DEFAULT_UI_ROUTES.stream(), extensionRoutes.stream())
.map(this.adminServer::path).collect(Collectors.toList());
List<String> routesExcludes = Stream.concat(DEFAULT_UI_ROUTE_EXCLUDES.stream(), extensionRoutes.stream())
.map(this.adminServer::path).collect(Collectors.toList());

return new HomepageForwardingFilterConfig(homepage, routesIncludes, routesExcludes);
}

@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE)
public static class ReactiveUiConfiguration {
Expand Down Expand Up @@ -155,15 +170,10 @@ public void addResourceHandlers(org.springframework.web.reactive.config.Resource

@Bean
@ConditionalOnMissingBean
public de.codecentric.boot.admin.server.ui.web.reactive.HomepageForwardingFilter homepageForwardFilter()
throws IOException {
List<String> extensionRoutes = new UiRoutesScanner(this.applicationContext)
.scan(this.adminUi.getExtensionResourceLocations());
List<String> routes = Stream.concat(DEFAULT_UI_ROUTES.stream(), extensionRoutes.stream())
.map(this.adminServer::path).collect(Collectors.toList());
String homepage = this.adminServer.path("/");
return new de.codecentric.boot.admin.server.ui.web.reactive.HomepageForwardingFilter(homepage, routes,
DEFAULT_UI_ROUTE_EXCLUDES);
public de.codecentric.boot.admin.server.ui.web.reactive.HomepageForwardingFilter homepageForwardFilter(
HomepageForwardingFilterConfig homepageForwardingFilterConfig) throws IOException {
return new de.codecentric.boot.admin.server.ui.web.reactive.HomepageForwardingFilter(
homepageForwardingFilterConfig);
}

}
Expand Down Expand Up @@ -203,15 +213,10 @@ public void addResourceHandlers(

@Bean
@ConditionalOnMissingBean
public de.codecentric.boot.admin.server.ui.web.servlet.HomepageForwardingFilter homepageForwardFilter()
throws IOException {
List<String> extensionRoutes = new UiRoutesScanner(this.applicationContext)
.scan(this.adminUi.getExtensionResourceLocations());
List<String> routes = Stream.concat(DEFAULT_UI_ROUTES.stream(), extensionRoutes.stream())
.map(this.adminServer::path).collect(Collectors.toList());
String homepage = this.adminServer.path("/");
return new de.codecentric.boot.admin.server.ui.web.servlet.HomepageForwardingFilter(homepage, routes,
DEFAULT_UI_ROUTE_EXCLUDES);
public de.codecentric.boot.admin.server.ui.web.servlet.HomepageForwardingFilter homepageForwardFilter(
HomepageForwardingFilterConfig homepageForwardingFilterConfig) throws IOException {
return new de.codecentric.boot.admin.server.ui.web.servlet.HomepageForwardingFilter(
homepageForwardingFilterConfig);
}

}
Expand Down
@@ -0,0 +1,32 @@
/*
* Copyright 2014-2021 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
*
* http://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 de.codecentric.boot.admin.server.ui.web;

import java.util.List;

import lombok.Value;

@Value
public class HomepageForwardingFilterConfig {

private final String homepage;

private final List<String> routesIncludes;

private final List<String> routesExcludes;

}
Expand Up @@ -53,8 +53,9 @@ public boolean test(T request) {
}

String path = this.pathAccessor.apply(request);
if (this.excludeRoutes.stream().anyMatch((p) -> p.matcher(path).matches())
|| this.includeRoutes.stream().noneMatch((p) -> p.matcher(path).matches())) {
boolean isExcludedRoute = this.excludeRoutes.stream().anyMatch((p) -> p.matcher(path).matches());
boolean isIncludedRoute = this.includeRoutes.stream().anyMatch((p) -> p.matcher(path).matches());
if (isExcludedRoute || !isIncludedRoute) {
return false;
}

Expand Down
Expand Up @@ -26,6 +26,7 @@
import org.springframework.web.server.WebFilterChain;
import reactor.core.publisher.Mono;

import de.codecentric.boot.admin.server.ui.web.HomepageForwardingFilterConfig;
import de.codecentric.boot.admin.server.ui.web.HomepageForwardingMatcher;

public class HomepageForwardingFilter implements WebFilter {
Expand All @@ -42,6 +43,10 @@ public HomepageForwardingFilter(String homepage, List<String> routeIncludes, Lis
(r) -> r.getPath().pathWithinApplication().toString(), (r) -> r.getHeaders().getAccept());
}

public HomepageForwardingFilter(HomepageForwardingFilterConfig filterConfig) {
this(filterConfig.getHomepage(), filterConfig.getRoutesIncludes(), filterConfig.getRoutesExcludes());
}

@Override
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
if (this.matcher.test(exchange.getRequest())) {
Expand Down
Expand Up @@ -33,6 +33,7 @@
import org.springframework.http.MediaType;
import org.springframework.web.util.UrlPathHelper;

import de.codecentric.boot.admin.server.ui.web.HomepageForwardingFilterConfig;
import de.codecentric.boot.admin.server.ui.web.HomepageForwardingMatcher;

/**
Expand All @@ -58,6 +59,10 @@ public HomepageForwardingFilter(String homepage, List<String> routes, List<Strin
(r) -> MediaType.parseMediaTypes(r.getHeader(HttpHeaders.ACCEPT)));
}

public HomepageForwardingFilter(HomepageForwardingFilterConfig filterConfig) {
this(filterConfig.getHomepage(), filterConfig.getRoutesIncludes(), filterConfig.getRoutesExcludes());
}

@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
Expand Down
@@ -0,0 +1,151 @@
/*
* Copyright 2014-2021 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
*
* http://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 de.codecentric.boot.admin.server.ui.config;

import javax.servlet.FilterChain;
import javax.servlet.ServletResponse;

import org.assertj.core.api.WithAssertions;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.test.context.runner.ReactiveWebApplicationContextRunner;
import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.mock.http.server.reactive.MockServerHttpRequest;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.mock.web.server.MockServerWebExchange;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilterChain;

import de.codecentric.boot.admin.server.config.AdminServerMarkerConfiguration;
import de.codecentric.boot.admin.server.config.AdminServerProperties;
import de.codecentric.boot.admin.server.config.SpringBootAdminServerEnabledCondition;
import de.codecentric.boot.admin.server.ui.web.reactive.HomepageForwardingFilter;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.atMostOnce;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;

@ExtendWith(MockitoExtension.class)
public class AdminServerUiAutoConfigurationTest implements WithAssertions {

@Nested
public class ReactiveUiConfigurationTest {

private final ReactiveWebApplicationContextRunner contextRunner = new ReactiveWebApplicationContextRunner()
.withPropertyValues("--spring.boot.admin.ui.available-languages=de",
"--spring.boot.admin.contextPath=test")
.withBean(AdminServerProperties.class)
.withConfiguration(AutoConfigurations.of(AdminServerUiAutoConfiguration.class));

@Mock
WebFilterChain webFilterChain;

@ParameterizedTest
@CsvSource({ "/test/extensions/myextension", "/test/instances/1/actuator/heapdump",
"/test/instances/1/actuator/logfile" })
public void contextPathIsRespectedInExcludedRoutes(String routeExcludes) {
MockServerHttpRequest serverHttpRequest = MockServerHttpRequest.get(routeExcludes)
.header(HttpHeaders.ACCEPT, MediaType.TEXT_HTML_VALUE).build();

ServerWebExchange serverWebExchange = spy(MockServerWebExchange.from(serverHttpRequest));

this.contextRunner.withUserConfiguration(SpringBootAdminServerEnabledCondition.class,
AdminServerMarkerConfiguration.Marker.class).run((context) -> {
HomepageForwardingFilter bean = context.getBean(HomepageForwardingFilter.class);
bean.filter(serverWebExchange, webFilterChain);

verify(serverWebExchange, never()).mutate();
});
}

@ParameterizedTest
@CsvSource({ "/test/about", "/test/applications", "/test/instances", "/test/journal", "/test/wallboard",
"/test/external" })
public void contextPathIsRespectedInIncludedRoutes(String routeIncludes) {
MockServerHttpRequest serverHttpRequest = MockServerHttpRequest.get(routeIncludes)
.header(HttpHeaders.ACCEPT, MediaType.TEXT_HTML_VALUE).build();

ServerWebExchange serverWebExchange = spy(MockServerWebExchange.from(serverHttpRequest));

this.contextRunner.withUserConfiguration(SpringBootAdminServerEnabledCondition.class,
AdminServerMarkerConfiguration.Marker.class).run((context) -> {
HomepageForwardingFilter bean = context.getBean(HomepageForwardingFilter.class);
bean.filter(serverWebExchange, webFilterChain);

verify(serverWebExchange, atMostOnce()).mutate();
});
}

}

@Nested
public class ServletUiConfiguration {

private final WebApplicationContextRunner contextRunner = new WebApplicationContextRunner()
.withPropertyValues("--spring.boot.admin.ui.available-languages=de",
"--spring.boot.admin.contextPath=test")
.withBean(AdminServerProperties.class)
.withConfiguration(AutoConfigurations.of(AdminServerUiAutoConfiguration.class));

@ParameterizedTest
@CsvSource({ "/test/extensions/myextension", "/test/instances/1/actuator/heapdump",
"/test/instances/1/actuator/logfile" })
public void contextPathIsRespectedInExcludedRoutes(String routeExcludes) {
MockHttpServletRequest httpServletRequest = spy(new MockHttpServletRequest("GET", routeExcludes));
httpServletRequest.addHeader(HttpHeaders.ACCEPT, MediaType.TEXT_HTML_VALUE);

this.contextRunner.withUserConfiguration(SpringBootAdminServerEnabledCondition.class,
AdminServerMarkerConfiguration.Marker.class).run((context) -> {
de.codecentric.boot.admin.server.ui.web.servlet.HomepageForwardingFilter bean = context.getBean(
de.codecentric.boot.admin.server.ui.web.servlet.HomepageForwardingFilter.class);
bean.doFilter(httpServletRequest, mock(ServletResponse.class), mock(FilterChain.class));

verify(httpServletRequest, never()).getRequestDispatcher(any());
});
}

@ParameterizedTest
@CsvSource({ "/test/about", "/test/applications", "/test/instances", "/test/journal", "/test/wallboard",
"/test/external" })
public void contextPathIsRespectedInIncludedRoutes(String routeIncludes) {
MockHttpServletRequest httpServletRequest = spy(new MockHttpServletRequest("GET", routeIncludes));
httpServletRequest.addHeader(HttpHeaders.ACCEPT, MediaType.TEXT_HTML_VALUE);

this.contextRunner.withUserConfiguration(SpringBootAdminServerEnabledCondition.class,
AdminServerMarkerConfiguration.Marker.class).run((context) -> {
de.codecentric.boot.admin.server.ui.web.servlet.HomepageForwardingFilter bean = context.getBean(
de.codecentric.boot.admin.server.ui.web.servlet.HomepageForwardingFilter.class);
bean.doFilter(httpServletRequest, new MockHttpServletResponse(), mock(FilterChain.class));

verify(httpServletRequest, atMostOnce()).getRequestDispatcher(any());
});
}

}

}
@@ -0,0 +1 @@
mock-maker-inline

0 comments on commit c5ec088

Please sign in to comment.