Skip to content

Commit

Permalink
Ensure that reactive actuator security has an auth manager
Browse files Browse the repository at this point in the history
This is a follow-on from afad358 and ensures that the auto-configured
security for Actuator in a WebFlux app has an authentication manager
to back its use of HTTP basic and form login.

Fixes gh-39069
  • Loading branch information
wilkinsona committed Jan 12, 2024
1 parent a48e2d3 commit 6ec56da
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

package org.springframework.boot.actuate.autoconfigure.security.reactive;

import reactor.core.publisher.Mono;

import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.health.HealthEndpointAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.info.InfoEndpointAutoConfiguration;
Expand All @@ -28,10 +30,14 @@
import org.springframework.boot.autoconfigure.security.oauth2.client.reactive.ReactiveOAuth2ClientAutoConfiguration;
import org.springframework.boot.autoconfigure.security.oauth2.resource.reactive.ReactiveOAuth2ResourceServerAutoConfiguration;
import org.springframework.boot.autoconfigure.security.reactive.ReactiveSecurityAutoConfiguration;
import org.springframework.boot.autoconfigure.security.reactive.ReactiveUserDetailsServiceAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.security.authentication.ReactiveAuthenticationManager;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.security.config.web.server.SecurityWebFiltersOrder;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.core.userdetails.ReactiveUserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.web.server.SecurityWebFilterChain;
import org.springframework.security.web.server.WebFilterChainProxy;
import org.springframework.web.cors.reactive.PreFlightRequestHandler;
Expand All @@ -50,7 +56,8 @@
@AutoConfiguration(before = ReactiveSecurityAutoConfiguration.class,
after = { HealthEndpointAutoConfiguration.class, InfoEndpointAutoConfiguration.class,
WebEndpointAutoConfiguration.class, ReactiveOAuth2ClientAutoConfiguration.class,
ReactiveOAuth2ResourceServerAutoConfiguration.class })
ReactiveOAuth2ResourceServerAutoConfiguration.class,
ReactiveUserDetailsServiceAutoConfiguration.class })
@ConditionalOnClass({ EnableWebFluxSecurity.class, WebFilterChainProxy.class })
@ConditionalOnMissingBean({ SecurityWebFilterChain.class, WebFilterChainProxy.class })
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE)
Expand All @@ -69,4 +76,10 @@ public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http,
return http.build();
}

@Bean
@ConditionalOnMissingBean({ ReactiveAuthenticationManager.class, ReactiveUserDetailsService.class })
ReactiveAuthenticationManager denyAllAuthenticationManager() {
return (authentication) -> Mono.error(new UsernameNotFoundException(authentication.getName()));
}

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2012-2023 the original author or authors.
* Copyright 2012-2024 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.
Expand Down Expand Up @@ -71,28 +71,39 @@ class ReactiveManagementWebSecurityAutoConfigurationTests {
HealthEndpointAutoConfiguration.class, InfoEndpointAutoConfiguration.class,
WebFluxAutoConfiguration.class, EnvironmentEndpointAutoConfiguration.class,
EndpointAutoConfiguration.class, WebEndpointAutoConfiguration.class,
ReactiveSecurityAutoConfiguration.class, ReactiveManagementWebSecurityAutoConfiguration.class))
.withUserConfiguration(UserDetailsServiceConfiguration.class);
ReactiveSecurityAutoConfiguration.class, ReactiveManagementWebSecurityAutoConfiguration.class));

@Test
void permitAllForHealth() {
this.contextRunner.run((context) -> assertThat(getAuthenticateHeader(context, "/actuator/health")).isNull());
this.contextRunner.withUserConfiguration(UserDetailsServiceConfiguration.class)
.run((context) -> assertThat(getAuthenticateHeader(context, "/actuator/health")).isNull());
}

@Test
void securesEverythingElse() {
this.contextRunner.withUserConfiguration(UserDetailsServiceConfiguration.class).run((context) -> {
assertThat(getAuthenticateHeader(context, "/actuator").get(0)).contains("Basic realm=");
assertThat(getAuthenticateHeader(context, "/foo").toString()).contains("Basic realm=");
});
}

@Test
void noExistingAuthenticationManagerOrUserDetailsService() {
this.contextRunner.run((context) -> {
assertThat(getAuthenticateHeader(context, "/actuator/health")).isNull();
assertThat(getAuthenticateHeader(context, "/actuator").get(0)).contains("Basic realm=");
assertThat(getAuthenticateHeader(context, "/foo").toString()).contains("Basic realm=");
});
}

@Test
void usesMatchersBasedOffConfiguredActuatorBasePath() {
this.contextRunner.withPropertyValues("management.endpoints.web.base-path=/").run((context) -> {
assertThat(getAuthenticateHeader(context, "/health")).isNull();
assertThat(getAuthenticateHeader(context, "/foo").get(0)).contains("Basic realm=");
});
this.contextRunner.withUserConfiguration(UserDetailsServiceConfiguration.class)
.withPropertyValues("management.endpoints.web.base-path=/")
.run((context) -> {
assertThat(getAuthenticateHeader(context, "/health")).isNull();
assertThat(getAuthenticateHeader(context, "/foo").get(0)).contains("Basic realm=");
});
}

@Test
Expand Down Expand Up @@ -180,6 +191,11 @@ SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
return http.build();
}

@Bean
ReactiveAuthenticationManager authenticationManager() {
return mock(ReactiveAuthenticationManager.class);
}

}

@Configuration(proxyBeanMethods = false)
Expand Down

0 comments on commit 6ec56da

Please sign in to comment.