From ad6fe4fdc368097895df3c1b4f0985dbd3dad651 Mon Sep 17 00:00:00 2001 From: Josh Cummings <3627351+jzheaux@users.noreply.github.com> Date: Wed, 24 Sep 2025 17:53:33 -0600 Subject: [PATCH] Polish MFA Samples This commit removes unneeded AuthorizationManagerFactory implementations, simplifies the custom AuthorizationManagerFactory example, and updates usage of hasAllAuthorities. Issue gh-17934 --- ...istAuthoritiesEverywhereConfiguration.java | 8 +- .../CustomAuthorizationManagerFactory.java | 48 +++-------- ...ustomAuthorizationManagerFactoryTests.java | 36 ++++---- .../ListAuthoritiesConfiguration.java | 7 +- .../MissingAuthorityConfiguration.java | 84 +------------------ .../CustomAuthorizationManagerFactory.kt | 42 +++------- .../CustomAuthorizationManagerFactoryTests.kt | 57 +++++++------ .../MissingAuthorityConfiguration.kt | 65 +------------- 8 files changed, 78 insertions(+), 269 deletions(-) diff --git a/docs/src/test/java/org/springframework/security/docs/servlet/authentication/authorizationmanagerfactory/ListAuthoritiesEverywhereConfiguration.java b/docs/src/test/java/org/springframework/security/docs/servlet/authentication/authorizationmanagerfactory/ListAuthoritiesEverywhereConfiguration.java index 4c9eee4a9d..42fc88dbba 100644 --- a/docs/src/test/java/org/springframework/security/docs/servlet/authentication/authorizationmanagerfactory/ListAuthoritiesEverywhereConfiguration.java +++ b/docs/src/test/java/org/springframework/security/docs/servlet/authentication/authorizationmanagerfactory/ListAuthoritiesEverywhereConfiguration.java @@ -13,10 +13,6 @@ import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.authentication.ott.OneTimeTokenGenerationSuccessHandler; import org.springframework.security.web.authentication.ott.RedirectOneTimeTokenGenerationSuccessHandler; -import static org.springframework.security.authorization.AuthorityAuthorizationManager.hasAuthority; -import static org.springframework.security.authorization.AuthorityAuthorizationManager.hasRole; -import static org.springframework.security.authorization.AuthorizationManagers.allOf; - @EnableWebSecurity @Configuration(proxyBeanMethods = false) public class ListAuthoritiesEverywhereConfiguration { @@ -27,8 +23,8 @@ public class ListAuthoritiesEverywhereConfiguration { // @formatter:off http .authorizeHttpRequests((authorize) -> authorize - .requestMatchers("/admin/**").access(allOf(hasAuthority(GrantedAuthorities.FACTOR_PASSWORD_AUTHORITY), hasAuthority(GrantedAuthorities.FACTOR_OTT_AUTHORITY), hasRole("ADMIN"))) // <1> - .anyRequest().access(allOf(hasAuthority(GrantedAuthorities.FACTOR_PASSWORD_AUTHORITY), hasAuthority(GrantedAuthorities.FACTOR_OTT_AUTHORITY))) + .requestMatchers("/admin/**").hasAllAuthorities(GrantedAuthorities.FACTOR_PASSWORD_AUTHORITY, GrantedAuthorities.FACTOR_OTT_AUTHORITY, "ROLE_ADMIN") // <1> + .anyRequest().hasAllAuthorities(GrantedAuthorities.FACTOR_PASSWORD_AUTHORITY, GrantedAuthorities.FACTOR_OTT_AUTHORITY) ) .formLogin(Customizer.withDefaults()) .oneTimeTokenLogin(Customizer.withDefaults()); diff --git a/docs/src/test/java/org/springframework/security/docs/servlet/authentication/customauthorizationmanagerfactory/CustomAuthorizationManagerFactory.java b/docs/src/test/java/org/springframework/security/docs/servlet/authentication/customauthorizationmanagerfactory/CustomAuthorizationManagerFactory.java index 7622e625d7..b23a41dd9e 100644 --- a/docs/src/test/java/org/springframework/security/docs/servlet/authentication/customauthorizationmanagerfactory/CustomAuthorizationManagerFactory.java +++ b/docs/src/test/java/org/springframework/security/docs/servlet/authentication/customauthorizationmanagerfactory/CustomAuthorizationManagerFactory.java @@ -1,16 +1,12 @@ package org.springframework.security.docs.servlet.authentication.customauthorizationmanagerfactory; -import java.util.Collection; import java.util.function.Supplier; -import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.security.access.expression.SecurityExpressionOperations; -import org.springframework.security.access.expression.SecurityExpressionRoot; -import org.springframework.security.authorization.AuthorityAuthorizationDecision; +import org.springframework.security.authorization.AuthorityAuthorizationManager; import org.springframework.security.authorization.AuthorizationDecision; import org.springframework.security.authorization.AuthorizationManager; import org.springframework.security.authorization.AuthorizationManagerFactory; @@ -21,10 +17,9 @@ import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.core.Authentication; import org.springframework.security.core.GrantedAuthorities; -import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.authority.AuthorityUtils; -import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.PasswordEncodedUser; import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.provisioning.InMemoryUserDetailsManager; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.authentication.ott.OneTimeTokenGenerationSuccessHandler; import org.springframework.security.web.authentication.ott.RedirectOneTimeTokenGenerationSuccessHandler; @@ -51,50 +46,31 @@ class CustomAuthorizationManagerFactory { // tag::authorizationManager[] @Component - class OptInToMfaAuthorizationManager implements AuthorizationManager { + class UserBasedOttAuthorizationManager implements AuthorizationManager { @Override public AuthorizationResult authorize(Supplier authentication, Object context) { - MyPrincipal principal = (MyPrincipal) authentication.get().getPrincipal(); - if (principal.optedIn()) { - SecurityExpressionOperations sec = new SecurityExpressionRoot<>(authentication, context) {}; - return new AuthorityAuthorizationDecision(sec.hasAuthority(GrantedAuthorities.FACTOR_OTT_AUTHORITY), - AuthorityUtils.createAuthorityList(GrantedAuthorities.FACTOR_OTT_AUTHORITY)); + if ("admin".equals(authentication.get().getName())) { + return AuthorityAuthorizationManager.hasAuthority(GrantedAuthorities.FACTOR_OTT_AUTHORITY) + .authorize(authentication, context); + } else { + return new AuthorizationDecision(true); } - return new AuthorizationDecision(true); } } // end::authorizationManager[] // tag::authorizationManagerFactory[] @Bean - AuthorizationManagerFactory authorizationManagerFactory(OptInToMfaAuthorizationManager optIn) { + AuthorizationManagerFactory authorizationManagerFactory(UserBasedOttAuthorizationManager optIn) { DefaultAuthorizationManagerFactory defaults = new DefaultAuthorizationManagerFactory<>(); defaults.setAdditionalAuthorization(optIn); return defaults; } // end::authorizationManagerFactory[] - @NullMarked - record MyPrincipal(String username, boolean optedIn) implements UserDetails { - @Override - public Collection getAuthorities() { - return AuthorityUtils.createAuthorityList("app"); - } - - @Override - public @Nullable String getPassword() { - return null; - } - - @Override - public String getUsername() { - return this.username; - } - } - @Bean - UserDetailsService users() { - return (username) -> new MyPrincipal(username, username.equals("optedin")); + public UserDetailsService users() { + return new InMemoryUserDetailsManager(PasswordEncodedUser.user(), PasswordEncodedUser.admin()); } @Bean diff --git a/docs/src/test/java/org/springframework/security/docs/servlet/authentication/customauthorizationmanagerfactory/CustomAuthorizationManagerFactoryTests.java b/docs/src/test/java/org/springframework/security/docs/servlet/authentication/customauthorizationmanagerfactory/CustomAuthorizationManagerFactoryTests.java index aea7626157..fe4f03e07b 100644 --- a/docs/src/test/java/org/springframework/security/docs/servlet/authentication/customauthorizationmanagerfactory/CustomAuthorizationManagerFactoryTests.java +++ b/docs/src/test/java/org/springframework/security/docs/servlet/authentication/customauthorizationmanagerfactory/CustomAuthorizationManagerFactoryTests.java @@ -20,19 +20,18 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.security.authentication.TestingAuthenticationToken; import org.springframework.security.config.test.SpringTestContext; import org.springframework.security.config.test.SpringTestContextExtension; import org.springframework.security.core.GrantedAuthorities; -import org.springframework.security.core.userdetails.UserDetails; -import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.docs.servlet.authentication.servletx509config.CustomX509Configuration; +import org.springframework.security.test.context.support.WithMockUser; +import org.springframework.security.test.context.support.WithSecurityContextTestExecutionListener; +import org.springframework.test.context.TestExecutionListeners; +import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.test.web.servlet.MockMvc; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; -import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.authentication; -import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user; import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl; @@ -43,7 +42,8 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. * * @author Rob Winch */ -@ExtendWith(SpringTestContextExtension.class) +@ExtendWith({SpringExtension.class, SpringTestContextExtension.class}) +@TestExecutionListeners(WithSecurityContextTestExecutionListener.class) public class CustomAuthorizationManagerFactoryTests { public final SpringTestContext spring = new SpringTestContext(this); @@ -51,40 +51,36 @@ public class CustomAuthorizationManagerFactoryTests { @Autowired MockMvc mockMvc; - @Autowired - UserDetailsService users; - @Test - void getWhenOptedInThenRedirectsToOtt() throws Exception { + @WithMockUser(username = "admin") + void getWhenAdminThenRedirectsToOtt() throws Exception { this.spring.register(CustomAuthorizationManagerFactory.class, Http200Controller.class).autowire(); - UserDetails user = this.users.loadUserByUsername("optedin"); // @formatter:off - this.mockMvc.perform(get("/").with(user(user))) + this.mockMvc.perform(get("/")) .andExpect(status().is3xxRedirection()) .andExpect(redirectedUrl("http://localhost/login?factor=ott")); // @formatter:on } @Test - void getWhenNotOptedInThenAllows() throws Exception { + @WithMockUser + void getWhenNotAdminThenAllows() throws Exception { this.spring.register(CustomAuthorizationManagerFactory.class, Http200Controller.class).autowire(); - UserDetails user = this.users.loadUserByUsername("user"); // @formatter:off - this.mockMvc.perform(get("/").with(user(user))) + this.mockMvc.perform(get("/")) .andExpect(status().isOk()) .andExpect(authenticated().withUsername("user")); // @formatter:on } @Test - void getWhenOptedAndHasFactorThenAllows() throws Exception { + @WithMockUser(username = "admin", authorities = GrantedAuthorities.FACTOR_OTT_AUTHORITY) + void getWhenAdminAndHasFactorThenAllows() throws Exception { this.spring.register(CustomAuthorizationManagerFactory.class, Http200Controller.class).autowire(); - UserDetails user = this.users.loadUserByUsername("optedin"); - TestingAuthenticationToken token = new TestingAuthenticationToken(user, "", GrantedAuthorities.FACTOR_OTT_AUTHORITY); // @formatter:off - this.mockMvc.perform(get("/").with(authentication(token))) + this.mockMvc.perform(get("/")) .andExpect(status().isOk()) - .andExpect(authenticated().withUsername("optedin")); + .andExpect(authenticated().withUsername("admin")); // @formatter:on } diff --git a/docs/src/test/java/org/springframework/security/docs/servlet/authentication/multifactorauthentication/ListAuthoritiesConfiguration.java b/docs/src/test/java/org/springframework/security/docs/servlet/authentication/multifactorauthentication/ListAuthoritiesConfiguration.java index c48e0b0a3e..80322a4c82 100644 --- a/docs/src/test/java/org/springframework/security/docs/servlet/authentication/multifactorauthentication/ListAuthoritiesConfiguration.java +++ b/docs/src/test/java/org/springframework/security/docs/servlet/authentication/multifactorauthentication/ListAuthoritiesConfiguration.java @@ -13,9 +13,6 @@ import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.authentication.ott.OneTimeTokenGenerationSuccessHandler; import org.springframework.security.web.authentication.ott.RedirectOneTimeTokenGenerationSuccessHandler; -import static org.springframework.security.authorization.AuthorityAuthorizationManager.hasAuthority; -import static org.springframework.security.authorization.AuthorizationManagers.allOf; - @EnableWebSecurity @Configuration(proxyBeanMethods = false) class ListAuthoritiesConfiguration { @@ -26,7 +23,7 @@ class ListAuthoritiesConfiguration { // @formatter:off http .authorizeHttpRequests((authorize) -> authorize - .anyRequest().access(allOf(hasAuthority(GrantedAuthorities.FACTOR_PASSWORD_AUTHORITY), hasAuthority(GrantedAuthorities.FACTOR_OTT_AUTHORITY))) // <1> + .anyRequest().hasAllAuthorities(GrantedAuthorities.FACTOR_PASSWORD_AUTHORITY, GrantedAuthorities.FACTOR_OTT_AUTHORITY) // <1> ) .formLogin(Customizer.withDefaults()) .oneTimeTokenLogin(Customizer.withDefaults()); @@ -36,7 +33,7 @@ class ListAuthoritiesConfiguration { // end::httpSecurity[] @Bean - UserDetailsService userDetailsService() { + UserDetailsService users() { return new InMemoryUserDetailsManager( User.withDefaultPasswordEncoder() .username("user") diff --git a/docs/src/test/java/org/springframework/security/docs/servlet/authentication/obtainingmoreauthorization/MissingAuthorityConfiguration.java b/docs/src/test/java/org/springframework/security/docs/servlet/authentication/obtainingmoreauthorization/MissingAuthorityConfiguration.java index e918a3b3d0..3de40e084a 100644 --- a/docs/src/test/java/org/springframework/security/docs/servlet/authentication/obtainingmoreauthorization/MissingAuthorityConfiguration.java +++ b/docs/src/test/java/org/springframework/security/docs/servlet/authentication/obtainingmoreauthorization/MissingAuthorityConfiguration.java @@ -8,8 +8,6 @@ import jakarta.servlet.http.HttpServletResponse; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.security.authorization.AuthorizationDecision; -import org.springframework.security.authorization.AuthorizationManager; import org.springframework.security.authorization.AuthorizationManagerFactory; import org.springframework.security.authorization.DefaultAuthorizationManagerFactory; import org.springframework.security.config.Customizer; @@ -22,12 +20,8 @@ import org.springframework.security.oauth2.client.registration.InMemoryClientReg import org.springframework.security.oauth2.client.registration.TestClientRegistrations; import org.springframework.security.web.AuthenticationEntryPoint; import org.springframework.security.web.SecurityFilterChain; -import org.springframework.security.web.access.intercept.RequestAuthorizationContext; import org.springframework.stereotype.Component; -import static org.springframework.security.authorization.AllAuthoritiesAuthorizationManager.hasAllAuthorities; -import static org.springframework.security.authorization.AuthorizationManagers.allOf; - @EnableWebSecurity @Configuration(proxyBeanMethods = false) class MissingAuthorityConfiguration { @@ -53,83 +47,13 @@ class MissingAuthorityConfiguration { // tag::authorizationManagerFactoryBean[] @Bean - AuthorizationManagerFactory authz() { - return new FactorAuthorizationManagerFactory(hasAllAuthorities(GrantedAuthorities.FACTOR_X509_AUTHORITY, GrantedAuthorities.FACTOR_AUTHORIZATION_CODE_AUTHORITY)); + AuthorizationManagerFactory authz() { + return DefaultAuthorizationManagerFactory.builder() + .requireAdditionalAuthorities(GrantedAuthorities.FACTOR_X509_AUTHORITY, GrantedAuthorities.FACTOR_AUTHORIZATION_CODE_AUTHORITY) + .build(); } // end::authorizationManagerFactoryBean[] - // tag::authorizationManagerFactory[] - class FactorAuthorizationManagerFactory implements AuthorizationManagerFactory { - private final AuthorizationManager hasAuthorities; - private final DefaultAuthorizationManagerFactory delegate = - new DefaultAuthorizationManagerFactory<>(); - - FactorAuthorizationManagerFactory(AuthorizationManager hasAuthorities) { - this.hasAuthorities = hasAuthorities; - } - - @Override - public AuthorizationManager permitAll() { - return this.delegate.permitAll(); - } - - @Override - public AuthorizationManager denyAll() { - return this.delegate.denyAll(); - } - - @Override - public AuthorizationManager hasRole(String role) { - return hasAnyRole(role); - } - - @Override - public AuthorizationManager hasAnyRole(String... roles) { - return allOf(new AuthorizationDecision(false), this.hasAuthorities, this.delegate.hasAnyRole(roles)); - } - - @Override - public AuthorizationManager hasAllRoles(String... roles) { - return allOf(new AuthorizationDecision(false), this.hasAuthorities, this.delegate.hasAllRoles(roles)); - } - - @Override - public AuthorizationManager hasAuthority(String authority) { - return hasAnyAuthority(authority); - } - - @Override - public AuthorizationManager hasAnyAuthority(String... authorities) { - return allOf(new AuthorizationDecision(false), this.hasAuthorities, this.delegate.hasAnyAuthority(authorities)); - } - - @Override - public AuthorizationManager hasAllAuthorities(String... authorities) { - return allOf(new AuthorizationDecision(false), this.hasAuthorities, this.delegate.hasAllAuthorities(authorities)); - } - - @Override - public AuthorizationManager authenticated() { - return allOf(new AuthorizationDecision(false), this.hasAuthorities, this.delegate.authenticated()); - } - - @Override - public AuthorizationManager fullyAuthenticated() { - return allOf(new AuthorizationDecision(false), this.hasAuthorities, this.delegate.fullyAuthenticated()); - } - - @Override - public AuthorizationManager rememberMe() { - return allOf(new AuthorizationDecision(false), this.hasAuthorities, this.delegate.rememberMe()); - } - - @Override - public AuthorizationManager anonymous() { - return this.delegate.anonymous(); - } - } - // end::authorizationManagerFactory[] - // tag::authenticationEntryPoint[] @Component class ScopeRetrievingAuthenticationEntryPoint implements AuthenticationEntryPoint { diff --git a/docs/src/test/kotlin/org/springframework/security/kt/docs/servlet/authentication/customauthorizationmanagerfactory/CustomAuthorizationManagerFactory.kt b/docs/src/test/kotlin/org/springframework/security/kt/docs/servlet/authentication/customauthorizationmanagerfactory/CustomAuthorizationManagerFactory.kt index 313f74e346..efea2598b4 100644 --- a/docs/src/test/kotlin/org/springframework/security/kt/docs/servlet/authentication/customauthorizationmanagerfactory/CustomAuthorizationManagerFactory.kt +++ b/docs/src/test/kotlin/org/springframework/security/kt/docs/servlet/authentication/customauthorizationmanagerfactory/CustomAuthorizationManagerFactory.kt @@ -1,19 +1,16 @@ package org.springframework.security.kt.docs.servlet.authentication.customauthorizationmanagerfactory -import org.jspecify.annotations.NullMarked import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration -import org.springframework.security.access.expression.SecurityExpressionRoot import org.springframework.security.authorization.* import org.springframework.security.config.annotation.web.builders.HttpSecurity import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity import org.springframework.security.config.annotation.web.invoke import org.springframework.security.core.Authentication import org.springframework.security.core.GrantedAuthorities -import org.springframework.security.core.GrantedAuthority -import org.springframework.security.core.authority.AuthorityUtils -import org.springframework.security.core.userdetails.UserDetails +import org.springframework.security.core.userdetails.PasswordEncodedUser import org.springframework.security.core.userdetails.UserDetailsService +import org.springframework.security.provisioning.InMemoryUserDetailsManager import org.springframework.security.web.SecurityFilterChain import org.springframework.security.web.authentication.ott.OneTimeTokenGenerationSuccessHandler import org.springframework.security.web.authentication.ott.RedirectOneTimeTokenGenerationSuccessHandler @@ -43,50 +40,31 @@ internal class CustomAuthorizationManagerFactory { // tag::authorizationManager[] @Component - internal open class OptInToMfaAuthorizationManager : AuthorizationManager { + internal open class UserBasedOttAuthorizationManager : AuthorizationManager { override fun authorize( authentication: Supplier, context: Object): AuthorizationResult { - val principal = authentication.get().getPrincipal() as MyPrincipal? - if (principal!!.optedIn) { - val root = object : SecurityExpressionRoot(authentication, context) { } - return AuthorityAuthorizationDecision( - root.hasAuthority(GrantedAuthorities.FACTOR_OTT_AUTHORITY), - AuthorityUtils.createAuthorityList(GrantedAuthorities.FACTOR_OTT_AUTHORITY) - ) + return if ("admin" == authentication.get().name) { + AuthorityAuthorizationManager.hasAuthority(GrantedAuthorities.FACTOR_OTT_AUTHORITY) + .authorize(authentication, context) + } else { + AuthorizationDecision(true) } - return AuthorizationDecision(true) } } // end::authorizationManager[] // tag::authorizationManagerFactory[] @Bean - fun authorizationManagerFactory(optIn: OptInToMfaAuthorizationManager?): AuthorizationManagerFactory { + fun authorizationManagerFactory(optIn: UserBasedOttAuthorizationManager?): AuthorizationManagerFactory { val defaults = DefaultAuthorizationManagerFactory() defaults.setAdditionalAuthorization(optIn) return defaults } // end::authorizationManagerFactory[] - @NullMarked - class MyPrincipal(val user: String, val optedIn: Boolean) : UserDetails { - override fun getAuthorities(): MutableCollection { - return AuthorityUtils.createAuthorityList("app") - } - - override fun getPassword(): String? { - return null - } - - override fun getUsername(): String { - return this.user - } - - } - @Bean fun users(): UserDetailsService { - return UserDetailsService { username: String? -> MyPrincipal(username!!, username == "optedin") } + return InMemoryUserDetailsManager(PasswordEncodedUser.user(), PasswordEncodedUser.admin()) } @Bean diff --git a/docs/src/test/kotlin/org/springframework/security/kt/docs/servlet/authentication/customauthorizationmanagerfactory/CustomAuthorizationManagerFactoryTests.kt b/docs/src/test/kotlin/org/springframework/security/kt/docs/servlet/authentication/customauthorizationmanagerfactory/CustomAuthorizationManagerFactoryTests.kt index 30e687b97a..839a83ab29 100644 --- a/docs/src/test/kotlin/org/springframework/security/kt/docs/servlet/authentication/customauthorizationmanagerfactory/CustomAuthorizationManagerFactoryTests.kt +++ b/docs/src/test/kotlin/org/springframework/security/kt/docs/servlet/authentication/customauthorizationmanagerfactory/CustomAuthorizationManagerFactoryTests.kt @@ -18,16 +18,18 @@ package org.springframework.security.kt.docs.servlet.authentication.customauthor import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.ExtendWith import org.springframework.beans.factory.annotation.Autowired -import org.springframework.security.authentication.TestingAuthenticationToken import org.springframework.security.config.test.SpringTestContext import org.springframework.security.config.test.SpringTestContextExtension import org.springframework.security.core.GrantedAuthorities -import org.springframework.security.core.userdetails.UserDetailsService -import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors -import org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers +import org.springframework.security.test.context.support.WithMockUser +import org.springframework.security.test.context.support.WithSecurityContextTestExecutionListener +import org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated +import org.springframework.test.context.TestExecutionListeners +import org.springframework.test.context.junit.jupiter.SpringExtension import org.springframework.test.web.servlet.MockMvc -import org.springframework.test.web.servlet.request.MockMvcRequestBuilders -import org.springframework.test.web.servlet.result.MockMvcResultMatchers +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get +import org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl +import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.RestController @@ -36,7 +38,8 @@ import org.springframework.web.bind.annotation.RestController * * @author Rob Winch */ -@ExtendWith(SpringTestContextExtension::class) +@ExtendWith(SpringExtension::class, SpringTestContextExtension::class) +@TestExecutionListeners(WithSecurityContextTestExecutionListener::class) class CustomAuthorizationManagerFactoryTests { @JvmField val spring: SpringTestContext = SpringTestContext(this) @@ -44,44 +47,40 @@ class CustomAuthorizationManagerFactoryTests { @Autowired var mockMvc: MockMvc? = null - @Autowired - var users: UserDetailsService? = null - @Test @Throws(Exception::class) - fun getWhenOptedInThenRedirectsToOtt() { + @WithMockUser(username = "admin") + fun getWhenAdminThenRedirectsToOtt() { this.spring.register(CustomAuthorizationManagerFactory::class.java, Http200Controller::class.java).autowire() - val user = this.users!!.loadUserByUsername("optedin") // @formatter:off - this.mockMvc!!.perform(MockMvcRequestBuilders.get("/").with(SecurityMockMvcRequestPostProcessors.user(user))) - .andExpect(MockMvcResultMatchers.status().is3xxRedirection()) - .andExpect(MockMvcResultMatchers.redirectedUrl("http://localhost/login?factor=ott")) - // @formatter:on + this.mockMvc!!.perform(get("/")) + .andExpect(status().is3xxRedirection()) + .andExpect(redirectedUrl("http://localhost/login?factor=ott")) + // @formatter:on } @Test @Throws(Exception::class) - fun getWhenNotOptedInThenAllows() { + @WithMockUser + fun getWhenNotAdminThenAllows() { this.spring.register(CustomAuthorizationManagerFactory::class.java, Http200Controller::class.java).autowire() - val user = this.users!!.loadUserByUsername("user") // @formatter:off - this.mockMvc!!.perform(MockMvcRequestBuilders.get("/").with(SecurityMockMvcRequestPostProcessors.user(user))) - .andExpect(MockMvcResultMatchers.status().isOk()) - .andExpect(SecurityMockMvcResultMatchers.authenticated().withUsername("user")) - // @formatter:on + this.mockMvc!!.perform(get("/")) + .andExpect(status().isOk()) + .andExpect(authenticated().withUsername("user")) + // @formatter:on } @Test @Throws(Exception::class) - fun getWhenOptedAndHasFactorThenAllows() { + @WithMockUser(username = "admin", authorities = [GrantedAuthorities.FACTOR_OTT_AUTHORITY]) + fun getWhenAdminAndHasFactorThenAllows() { this.spring.register(CustomAuthorizationManagerFactory::class.java, Http200Controller::class.java).autowire() - val user = this.users!!.loadUserByUsername("optedin") - val token = TestingAuthenticationToken(user, "", GrantedAuthorities.FACTOR_OTT_AUTHORITY) // @formatter:off - this.mockMvc!!.perform(MockMvcRequestBuilders.get("/").with(SecurityMockMvcRequestPostProcessors.authentication(token))) - .andExpect(MockMvcResultMatchers.status().isOk()) - .andExpect(SecurityMockMvcResultMatchers.authenticated().withUsername("optedin")) - // @formatter:on + this.mockMvc!!.perform(get("/")) + .andExpect(status().isOk()) + .andExpect(authenticated().withUsername("admin")) + // @formatter:on } @RestController diff --git a/docs/src/test/kotlin/org/springframework/security/kt/docs/servlet/authentication/obtainingmoreauthorization/MissingAuthorityConfiguration.kt b/docs/src/test/kotlin/org/springframework/security/kt/docs/servlet/authentication/obtainingmoreauthorization/MissingAuthorityConfiguration.kt index 4aa8543dc6..5305102ee1 100644 --- a/docs/src/test/kotlin/org/springframework/security/kt/docs/servlet/authentication/obtainingmoreauthorization/MissingAuthorityConfiguration.kt +++ b/docs/src/test/kotlin/org/springframework/security/kt/docs/servlet/authentication/obtainingmoreauthorization/MissingAuthorityConfiguration.kt @@ -58,70 +58,13 @@ internal class MissingAuthorityConfiguration { // tag::authorizationManagerFactoryBean[] @Bean - fun authz(): AuthorizationManagerFactory { - return FactorAuthorizationManagerFactory(hasAllAuthorities(GrantedAuthorities.FACTOR_X509_AUTHORITY, GrantedAuthorities.FACTOR_AUTHORIZATION_CODE_AUTHORITY)) + fun authz(): AuthorizationManagerFactory { + return DefaultAuthorizationManagerFactory.builder() + .requireAdditionalAuthorities(GrantedAuthorities.FACTOR_X509_AUTHORITY, GrantedAuthorities.FACTOR_AUTHORIZATION_CODE_AUTHORITY) + .build() } // end::authorizationManagerFactoryBean[] - // tag::authorizationManagerFactory[] - internal inner class FactorAuthorizationManagerFactory(private val hasAuthorities: AuthorizationManager) : - AuthorizationManagerFactory { - private val delegate = DefaultAuthorizationManagerFactory() - - override fun permitAll(): AuthorizationManager { - return this.delegate.permitAll() - } - - override fun denyAll(): AuthorizationManager { - return this.delegate.denyAll() - } - - override fun hasRole(role: String): AuthorizationManager { - return hasAnyRole(role) - } - - override fun hasAnyRole(vararg roles: String): AuthorizationManager { - return addFactors(this.delegate.hasAnyRole(*roles)) - } - - override fun hasAllRoles(vararg roles: String): AuthorizationManager { - return addFactors(this.delegate.hasAllRoles(*roles)) - } - - override fun hasAuthority(authority: String): AuthorizationManager { - return hasAnyAuthority(authority) - } - - override fun hasAnyAuthority(vararg authorities: String): AuthorizationManager { - return addFactors(this.delegate.hasAnyAuthority(*authorities)) - } - - override fun hasAllAuthorities(vararg authorities: String): AuthorizationManager { - return addFactors(this.delegate.hasAllAuthorities(*authorities)) - } - - override fun authenticated(): AuthorizationManager { - return addFactors(this.delegate.authenticated()) - } - - override fun fullyAuthenticated(): AuthorizationManager { - return addFactors(this.delegate.fullyAuthenticated()) - } - - override fun rememberMe(): AuthorizationManager { - return addFactors(this.delegate.rememberMe()) - } - - override fun anonymous(): AuthorizationManager { - return this.delegate.anonymous() - } - - private fun addFactors(delegate: AuthorizationManager): AuthorizationManager { - return allOf(AuthorizationDecision(false), this.hasAuthorities, delegate) - } - } - // end::authorizationManagerFactory[] - @Bean fun clients(): ClientRegistrationRepository { return InMemoryClientRegistrationRepository(TestClientRegistrations.clientRegistration().build())