Skip to content

Commit

Permalink
Merge branch '2.1.x' into 2.2.x
Browse files Browse the repository at this point in the history
Closes gh-19059
  • Loading branch information
mbhave committed Nov 19, 2019
2 parents a003a5c + a3a53d2 commit ff22bd4
Show file tree
Hide file tree
Showing 4 changed files with 137 additions and 2 deletions.
Expand Up @@ -16,6 +16,7 @@

package org.springframework.boot.actuate.autoconfigure.health;

import java.security.Principal;
import java.util.Collection;
import java.util.function.Predicate;

Expand All @@ -24,6 +25,9 @@
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;

/**
Expand Down Expand Up @@ -97,10 +101,34 @@ private boolean getShowResult(SecurityContext securityContext, Show show) {
}

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
Expand Down
Expand Up @@ -29,9 +29,12 @@
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}.
Expand Down Expand Up @@ -123,6 +126,30 @@ void showDetailsWhenShowDetailsIsWhenAuthorizedAndUseIsNotInRoleReturnsFalse() {
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,
Expand Down Expand Up @@ -185,6 +212,30 @@ void showComponentsWhenShowDetailsIsWhenAuthorizedAndUseIsNotInRoleReturnsFalse(
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,
Expand Down
Expand Up @@ -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;

/**
Expand Down Expand Up @@ -108,12 +112,29 @@ private boolean isUserInRole(SecurityContext securityContext) {
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);
}

}
Expand Up @@ -29,6 +29,8 @@
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;
Expand Down Expand Up @@ -85,6 +87,39 @@ void mapDetailsWithAuthorizedUserInvokeSupplier() {
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);
Expand Down

0 comments on commit ff22bd4

Please sign in to comment.