Skip to content

Commit

Permalink
Merge branch '2.1.x'
Browse files Browse the repository at this point in the history
  • Loading branch information
mbhave committed Feb 21, 2019
2 parents f4bd0b8 + c3e4754 commit 6f045d8
Show file tree
Hide file tree
Showing 5 changed files with 174 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,12 @@

import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointProperties;
import org.springframework.boot.actuate.autoconfigure.web.server.ManagementPortType;
import org.springframework.boot.actuate.endpoint.EndpointId;
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
import org.springframework.boot.actuate.endpoint.web.PathMappedEndpoints;
import org.springframework.boot.security.reactive.ApplicationContextServerWebExchangeMatcher;
import org.springframework.context.ApplicationContext;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.security.web.server.util.matcher.OrServerWebExchangeMatcher;
import org.springframework.security.web.server.util.matcher.PathPatternParserServerWebExchangeMatcher;
Expand Down Expand Up @@ -239,9 +241,28 @@ private List<ServerWebExchangeMatcher> getDelegateMatchers(Set<String> paths) {
@Override
protected Mono<MatchResult> matches(ServerWebExchange exchange,
Supplier<PathMappedEndpoints> context) {
if (!isManagementContext(exchange)) {
return MatchResult.notMatch();
}
return this.delegate.matches(exchange);
}

static boolean isManagementContext(ServerWebExchange exchange) {
ApplicationContext applicationContext = exchange.getApplicationContext();
if (ManagementPortType.get(applicationContext
.getEnvironment()) == ManagementPortType.DIFFERENT) {
if (applicationContext.getParent() == null) {
return false;
}
String managementContextId = applicationContext.getParent().getId()
+ ":management";
if (!managementContextId.equals(applicationContext.getId())) {
return false;
}
}
return true;
}

}

/**
Expand Down Expand Up @@ -273,6 +294,9 @@ private ServerWebExchangeMatcher createDelegate(
@Override
protected Mono<MatchResult> matches(ServerWebExchange exchange,
Supplier<WebEndpointProperties> context) {
if (!EndpointServerWebExchangeMatcher.isManagementContext(exchange)) {
return MatchResult.notMatch();
}
return this.delegate.matches(exchange);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@

import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointProperties;
import org.springframework.boot.actuate.autoconfigure.web.server.ManagementPortType;
import org.springframework.boot.actuate.endpoint.EndpointId;
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
import org.springframework.boot.actuate.endpoint.web.PathMappedEndpoints;
Expand All @@ -43,6 +44,7 @@
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;

/**
* Factory that can be used to create a {@link RequestMatcher} for actuator endpoint
Expand Down Expand Up @@ -133,6 +135,19 @@ protected final void initialized(Supplier<WebApplicationContext> context) {
@Override
protected final boolean matches(HttpServletRequest request,
Supplier<WebApplicationContext> context) {
WebApplicationContext applicationContext = WebApplicationContextUtils
.getRequiredWebApplicationContext(request.getServletContext());
if (ManagementPortType.get(applicationContext
.getEnvironment()) == ManagementPortType.DIFFERENT) {
if (applicationContext.getParent() == null) {
return false;
}
String managementContextId = applicationContext.getParent().getId()
+ ":management";
if (!managementContextId.equals(applicationContext.getId())) {
return false;
}
}
return this.delegate.matches(request);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,16 @@ public enum ManagementPortType {
*/
DIFFERENT;

static ManagementPortType get(Environment environment) {
/**
* Look at the given environment to determine if the {@link ManagementPortType} is
* {@link #DISABLED}, {@link #SAME} or {@link #DIFFERENT}.
* @param environment the Spring environment
* @return {@link #DISABLED} if `management.server.port` is set to a negative value,
* {@link #SAME} if `management.server.port` is not specified or equal to
* `server.port`and {@link #DIFFERENT} otherwise.
* @since 2.1.4
*/
public static ManagementPortType get(Environment environment) {
Integer managementPort = getPortProperty(environment, "management.server.");
if (managementPort != null && managementPort < 0) {
return DISABLED;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,14 @@ public void testHome() {
assertThat(entity.getBody()).contains("Hello World");
}

@Test
public void actuatorPathOnMainPortShouldNotMatch() {
ResponseEntity<String> entity = new TestRestTemplate().getForEntity(
"http://localhost:" + this.port + "/actuator/health",
String.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.UNAUTHORIZED);
}

@Test
public void testSecureActuator() {
ResponseEntity<String> entity = new TestRestTemplate().getForEntity(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
/*
* Copyright 2012-2018 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 sample.secure.webflux;

import java.util.Base64;

import org.assertj.core.api.Assertions;
import org.junit.Test;
import org.junit.runner.RunWith;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.autoconfigure.security.reactive.EndpointRequest;
import org.springframework.boot.actuate.autoconfigure.web.server.LocalManagementPort;
import org.springframework.boot.actuate.web.mappings.MappingsEndpoint;
import org.springframework.boot.autoconfigure.security.reactive.PathRequest;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.boot.web.server.LocalServerPort;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.web.server.SecurityWebFilterChain;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.reactive.server.WebTestClient;

/**
* Integration tests for separate management and main service ports.
*
* @author Madhura Bhave
*/
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, properties = {
"management.server.port=0" }, classes = {
ManagementPortSampleSecureWebFluxTests.SecurityConfiguration.class,
SampleSecureWebFluxApplication.class })
public class ManagementPortSampleSecureWebFluxTests {

@LocalServerPort
private int port = 9010;

@LocalManagementPort
private int managementPort = 9011;

@Autowired
private WebTestClient webClient;

@Test
public void testHome() {
this.webClient.get().uri("http://localhost:" + this.port, String.class)
.header("Authorization", "basic " + getBasicAuth()).exchange()
.expectStatus().isOk().expectBody(String.class).isEqualTo("Hello user");
}

@Test
public void actuatorPathOnMainPortShouldNotMatch() {
this.webClient.get()
.uri("http://localhost:" + this.port + "/actuator", String.class)
.exchange().expectStatus().isUnauthorized();
this.webClient.get()
.uri("http://localhost:" + this.port + "/actuator/health", String.class)
.exchange().expectStatus().isUnauthorized();
}

@Test
public void testSecureActuator() {
this.webClient.get()
.uri("http://localhost:" + this.managementPort + "/actuator/env",
String.class)
.exchange().expectStatus().isUnauthorized();
}

@Test
public void testInsecureActuator() {
String responseBody = this.webClient.get()
.uri("http://localhost:" + this.managementPort + "/actuator/health",
String.class)
.exchange().expectStatus().isOk().expectBody(String.class).returnResult()
.getResponseBody();
Assertions.assertThat(responseBody).contains("\"status\":\"UP\"");
}

private String getBasicAuth() {
return new String(Base64.getEncoder().encode(("user:password").getBytes()));
}

@Configuration
static class SecurityConfiguration {

@Bean
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
return http.authorizeExchange().matchers(EndpointRequest.to("health", "info"))
.permitAll()
.matchers(EndpointRequest.toAnyEndpoint()
.excluding(MappingsEndpoint.class))
.hasRole("ACTUATOR")
.matchers(PathRequest.toStaticResources().atCommonLocations())
.permitAll().pathMatchers("/login").permitAll().anyExchange()
.authenticated().and().httpBasic().and().build();
}

}

}

0 comments on commit 6f045d8

Please sign in to comment.