commit
						ff22bd4827
					
				| 
						 | 
				
			
			@ -16,6 +16,7 @@
 | 
			
		|||
 | 
			
		||||
package org.springframework.boot.actuate.autoconfigure.health;
 | 
			
		||||
 | 
			
		||||
import java.security.Principal;
 | 
			
		||||
import java.util.Collection;
 | 
			
		||||
import java.util.function.Predicate;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -24,6 +25,9 @@ import org.springframework.boot.actuate.endpoint.SecurityContext;
 | 
			
		|||
import org.springframework.boot.actuate.health.HealthEndpointGroup;
 | 
			
		||||
import org.springframework.boot.actuate.health.HttpCodeStatusMapper;
 | 
			
		||||
import org.springframework.boot.actuate.health.StatusAggregator;
 | 
			
		||||
import org.springframework.security.core.Authentication;
 | 
			
		||||
import org.springframework.security.core.GrantedAuthority;
 | 
			
		||||
import org.springframework.util.ClassUtils;
 | 
			
		||||
import org.springframework.util.CollectionUtils;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
| 
						 | 
				
			
			@ -97,10 +101,34 @@ class AutoConfiguredHealthEndpointGroup implements HealthEndpointGroup {
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	private boolean isAuthorized(SecurityContext securityContext) {
 | 
			
		||||
		if (securityContext.getPrincipal() == null) {
 | 
			
		||||
		Principal principal = securityContext.getPrincipal();
 | 
			
		||||
		if (principal == null) {
 | 
			
		||||
			return false;
 | 
			
		||||
		}
 | 
			
		||||
		return CollectionUtils.isEmpty(this.roles) || this.roles.stream().anyMatch(securityContext::isUserInRole);
 | 
			
		||||
		if (CollectionUtils.isEmpty(this.roles)) {
 | 
			
		||||
			return true;
 | 
			
		||||
		}
 | 
			
		||||
		boolean checkAuthorities = isSpringSecurityAuthentication(principal);
 | 
			
		||||
		for (String role : this.roles) {
 | 
			
		||||
			if (securityContext.isUserInRole(role)) {
 | 
			
		||||
				return true;
 | 
			
		||||
			}
 | 
			
		||||
			if (checkAuthorities) {
 | 
			
		||||
				Authentication authentication = (Authentication) principal;
 | 
			
		||||
				for (GrantedAuthority authority : authentication.getAuthorities()) {
 | 
			
		||||
					String name = authority.getAuthority();
 | 
			
		||||
					if (role.equals(name)) {
 | 
			
		||||
						return true;
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private boolean isSpringSecurityAuthentication(Principal principal) {
 | 
			
		||||
		return ClassUtils.isPresent("org.springframework.security.core.Authentication", null)
 | 
			
		||||
				&& (principal instanceof Authentication);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -29,9 +29,12 @@ import org.springframework.boot.actuate.autoconfigure.health.HealthProperties.Sh
 | 
			
		|||
import org.springframework.boot.actuate.endpoint.SecurityContext;
 | 
			
		||||
import org.springframework.boot.actuate.health.HttpCodeStatusMapper;
 | 
			
		||||
import org.springframework.boot.actuate.health.StatusAggregator;
 | 
			
		||||
import org.springframework.security.core.Authentication;
 | 
			
		||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
 | 
			
		||||
 | 
			
		||||
import static org.assertj.core.api.Assertions.assertThat;
 | 
			
		||||
import static org.mockito.BDDMockito.given;
 | 
			
		||||
import static org.mockito.Mockito.mock;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Tests for {@link AutoConfiguredHealthEndpointGroup}.
 | 
			
		||||
| 
						 | 
				
			
			@ -123,6 +126,30 @@ class AutoConfiguredHealthEndpointGroupTests {
 | 
			
		|||
		assertThat(group.showDetails(this.securityContext)).isFalse();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	void showDetailsWhenShowDetailsIsWhenAuthorizedAndUserHasRightAuthorityReturnsTrue() {
 | 
			
		||||
		AutoConfiguredHealthEndpointGroup group = new AutoConfiguredHealthEndpointGroup((name) -> true,
 | 
			
		||||
				this.statusAggregator, this.httpCodeStatusMapper, null, Show.WHEN_AUTHORIZED,
 | 
			
		||||
				Arrays.asList("admin", "root", "bossmode"));
 | 
			
		||||
		Authentication principal = mock(Authentication.class);
 | 
			
		||||
		given(principal.getAuthorities())
 | 
			
		||||
				.willAnswer((invocation) -> Collections.singleton(new SimpleGrantedAuthority("admin")));
 | 
			
		||||
		given(this.securityContext.getPrincipal()).willReturn(principal);
 | 
			
		||||
		assertThat(group.showDetails(this.securityContext)).isTrue();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	void showDetailsWhenShowDetailsIsWhenAuthorizedAndUserDoesNotHaveRightAuthoritiesReturnsFalse() {
 | 
			
		||||
		AutoConfiguredHealthEndpointGroup group = new AutoConfiguredHealthEndpointGroup((name) -> true,
 | 
			
		||||
				this.statusAggregator, this.httpCodeStatusMapper, null, Show.WHEN_AUTHORIZED,
 | 
			
		||||
				Arrays.asList("admin", "rot", "bossmode"));
 | 
			
		||||
		Authentication principal = mock(Authentication.class);
 | 
			
		||||
		given(principal.getAuthorities())
 | 
			
		||||
				.willAnswer((invocation) -> Collections.singleton(new SimpleGrantedAuthority("other")));
 | 
			
		||||
		given(this.securityContext.getPrincipal()).willReturn(principal);
 | 
			
		||||
		assertThat(group.showDetails(this.securityContext)).isFalse();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	void showComponentsWhenShowComponentsIsNullDelegatesToShowDetails() {
 | 
			
		||||
		AutoConfiguredHealthEndpointGroup alwaysGroup = new AutoConfiguredHealthEndpointGroup((name) -> true,
 | 
			
		||||
| 
						 | 
				
			
			@ -185,6 +212,30 @@ class AutoConfiguredHealthEndpointGroupTests {
 | 
			
		|||
		assertThat(group.showComponents(this.securityContext)).isFalse();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	void showComponentsWhenShowComponentsIsWhenAuthorizedAndUserHasRightAuthoritiesReturnsTrue() {
 | 
			
		||||
		AutoConfiguredHealthEndpointGroup group = new AutoConfiguredHealthEndpointGroup((name) -> true,
 | 
			
		||||
				this.statusAggregator, this.httpCodeStatusMapper, Show.WHEN_AUTHORIZED, Show.NEVER,
 | 
			
		||||
				Arrays.asList("admin", "root", "bossmode"));
 | 
			
		||||
		Authentication principal = mock(Authentication.class);
 | 
			
		||||
		given(principal.getAuthorities())
 | 
			
		||||
				.willAnswer((invocation) -> Collections.singleton(new SimpleGrantedAuthority("admin")));
 | 
			
		||||
		given(this.securityContext.getPrincipal()).willReturn(principal);
 | 
			
		||||
		assertThat(group.showComponents(this.securityContext)).isTrue();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	void showComponentsWhenShowComponentsIsWhenAuthorizedAndUserDoesNotHaveRightAuthoritiesReturnsFalse() {
 | 
			
		||||
		AutoConfiguredHealthEndpointGroup group = new AutoConfiguredHealthEndpointGroup((name) -> true,
 | 
			
		||||
				this.statusAggregator, this.httpCodeStatusMapper, Show.WHEN_AUTHORIZED, Show.NEVER,
 | 
			
		||||
				Arrays.asList("admin", "rot", "bossmode"));
 | 
			
		||||
		Authentication principal = mock(Authentication.class);
 | 
			
		||||
		given(principal.getAuthorities())
 | 
			
		||||
				.willAnswer((invocation) -> Collections.singleton(new SimpleGrantedAuthority("other")));
 | 
			
		||||
		given(this.securityContext.getPrincipal()).willReturn(principal);
 | 
			
		||||
		assertThat(group.showComponents(this.securityContext)).isFalse();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	void getStatusAggregatorReturnsStatusAggregator() {
 | 
			
		||||
		AutoConfiguredHealthEndpointGroup group = new AutoConfiguredHealthEndpointGroup((name) -> true,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -16,11 +16,15 @@
 | 
			
		|||
 | 
			
		||||
package org.springframework.boot.actuate.health;
 | 
			
		||||
 | 
			
		||||
import java.security.Principal;
 | 
			
		||||
import java.util.Set;
 | 
			
		||||
import java.util.function.Supplier;
 | 
			
		||||
 | 
			
		||||
import org.springframework.boot.actuate.endpoint.SecurityContext;
 | 
			
		||||
import org.springframework.boot.actuate.endpoint.web.WebEndpointResponse;
 | 
			
		||||
import org.springframework.security.core.Authentication;
 | 
			
		||||
import org.springframework.security.core.GrantedAuthority;
 | 
			
		||||
import org.springframework.util.ClassUtils;
 | 
			
		||||
import org.springframework.util.CollectionUtils;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
| 
						 | 
				
			
			@ -108,12 +112,29 @@ public class HealthWebEndpointResponseMapper {
 | 
			
		|||
		if (CollectionUtils.isEmpty(this.authorizedRoles)) {
 | 
			
		||||
			return true;
 | 
			
		||||
		}
 | 
			
		||||
		Principal principal = securityContext.getPrincipal();
 | 
			
		||||
		boolean checkAuthorities = isSpringSecurityAuthentication(principal);
 | 
			
		||||
		for (String role : this.authorizedRoles) {
 | 
			
		||||
			if (securityContext.isUserInRole(role)) {
 | 
			
		||||
				return true;
 | 
			
		||||
			}
 | 
			
		||||
			if (checkAuthorities) {
 | 
			
		||||
				Authentication authentication = (Authentication) principal;
 | 
			
		||||
				for (GrantedAuthority authority : authentication.getAuthorities()) {
 | 
			
		||||
					String name = authority.getAuthority();
 | 
			
		||||
					if (role.equals(name)) {
 | 
			
		||||
						return true;
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private boolean isSpringSecurityAuthentication(Principal principal) {
 | 
			
		||||
		return ClassUtils.isPresent("org.springframework.security.core.Authentication", null)
 | 
			
		||||
				&& (principal instanceof Authentication);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -29,6 +29,8 @@ import org.mockito.stubbing.Answer;
 | 
			
		|||
import org.springframework.boot.actuate.endpoint.SecurityContext;
 | 
			
		||||
import org.springframework.boot.actuate.endpoint.web.WebEndpointResponse;
 | 
			
		||||
import org.springframework.http.HttpStatus;
 | 
			
		||||
import org.springframework.security.core.Authentication;
 | 
			
		||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
 | 
			
		||||
 | 
			
		||||
import static org.assertj.core.api.Assertions.assertThat;
 | 
			
		||||
import static org.mockito.ArgumentMatchers.anyString;
 | 
			
		||||
| 
						 | 
				
			
			@ -85,6 +87,39 @@ class HealthWebEndpointResponseMapperTests {
 | 
			
		|||
		verify(securityContext).isUserInRole("ACTUATOR");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	void mapDetailsWithRightAuthoritiesInvokesSupplier() {
 | 
			
		||||
		HealthWebEndpointResponseMapper mapper = createMapper(ShowDetails.WHEN_AUTHORIZED);
 | 
			
		||||
		Supplier<Health> supplier = mockSupplier();
 | 
			
		||||
		given(supplier.get()).willReturn(Health.down().build());
 | 
			
		||||
		SecurityContext securityContext = getSecurityContext("ACTUATOR");
 | 
			
		||||
		WebEndpointResponse<Health> response = mapper.mapDetails(supplier, securityContext);
 | 
			
		||||
		assertThat(response.getStatus()).isEqualTo(HttpStatus.SERVICE_UNAVAILABLE.value());
 | 
			
		||||
		assertThat(response.getBody().getStatus()).isEqualTo(Status.DOWN);
 | 
			
		||||
		verify(supplier).get();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	void mapDetailsWithOtherAuthoritiesShouldNotInvokeSupplier() {
 | 
			
		||||
		HealthWebEndpointResponseMapper mapper = createMapper(ShowDetails.WHEN_AUTHORIZED);
 | 
			
		||||
		Supplier<Health> supplier = mockSupplier();
 | 
			
		||||
		given(supplier.get()).willReturn(Health.down().build());
 | 
			
		||||
		SecurityContext securityContext = getSecurityContext("OTHER");
 | 
			
		||||
		WebEndpointResponse<Health> response = mapper.mapDetails(supplier, securityContext);
 | 
			
		||||
		assertThat(response.getStatus()).isEqualTo(HttpStatus.NOT_FOUND.value());
 | 
			
		||||
		assertThat(response.getBody()).isNull();
 | 
			
		||||
		verifyNoInteractions(supplier);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private SecurityContext getSecurityContext(String other) {
 | 
			
		||||
		SecurityContext securityContext = mock(SecurityContext.class);
 | 
			
		||||
		Authentication principal = mock(Authentication.class);
 | 
			
		||||
		given(securityContext.getPrincipal()).willReturn(principal);
 | 
			
		||||
		given(principal.getAuthorities())
 | 
			
		||||
				.willAnswer((invocation) -> Collections.singleton(new SimpleGrantedAuthority(other)));
 | 
			
		||||
		return securityContext;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	void mapDetailsWithUnavailableHealth() {
 | 
			
		||||
		HealthWebEndpointResponseMapper mapper = createMapper(ShowDetails.ALWAYS);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue