9 changed files with 660 additions and 0 deletions
@ -0,0 +1,65 @@
@@ -0,0 +1,65 @@
|
||||
/* |
||||
* Copyright 2004-present 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 |
||||
* |
||||
* https://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 org.springframework.security.config.annotation.authorization; |
||||
|
||||
import java.lang.annotation.Documented; |
||||
import java.lang.annotation.ElementType; |
||||
import java.lang.annotation.Retention; |
||||
import java.lang.annotation.RetentionPolicy; |
||||
import java.lang.annotation.Target; |
||||
|
||||
import org.springframework.context.annotation.Import; |
||||
import org.springframework.security.authorization.DefaultAuthorizationManagerFactory; |
||||
|
||||
/** |
||||
* Exposes a {@link DefaultAuthorizationManagerFactory} as a Bean with the |
||||
* {@link #authorities()} specified as additional required authorities. The configuration |
||||
* will be picked up by both |
||||
* {@link org.springframework.security.config.annotation.web.configuration.EnableWebSecurity} |
||||
* and |
||||
* {@link org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity}. |
||||
* |
||||
* <pre> |
||||
|
||||
* @Configuration |
||||
* @EnableGlobalMultiFactorAuthentication(authorities = { GrantedAuthorities.FACTOR_OTT, GrantedAuthorities.FACTOR_PASSWORD }) |
||||
* public class MyConfiguration { |
||||
* // ...
|
||||
* } |
||||
* </pre> |
||||
* |
||||
* NOTE: At this time reactive applications do not support MFA and thus are not impacted. |
||||
* This will likely be enhanced in the future. |
||||
* |
||||
* @author Rob Winch |
||||
* @since 7.0 |
||||
*/ |
||||
@Retention(RetentionPolicy.RUNTIME) |
||||
@Target(ElementType.TYPE) |
||||
@Documented |
||||
@Import(GlobalMultiFactorAuthenticationConfiguration.class) |
||||
public @interface EnableGlobalMultiFactorAuthentication { |
||||
|
||||
/** |
||||
* The additional authorities that are required. |
||||
* @return the additional authorities that are required (e.g. { |
||||
* GrantedAuthorities.FACTOR_OTT, GrantedAuthorities.FACTOR_PASSWORD }) |
||||
* @see org.springframework.security.core.GrantedAuthorities |
||||
*/ |
||||
String[] authorities(); |
||||
|
||||
} |
||||
@ -0,0 +1,56 @@
@@ -0,0 +1,56 @@
|
||||
/* |
||||
* Copyright 2004-present 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 |
||||
* |
||||
* https://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 org.springframework.security.config.annotation.authorization; |
||||
|
||||
import java.util.Map; |
||||
|
||||
import org.springframework.beans.factory.ObjectProvider; |
||||
import org.springframework.context.annotation.Bean; |
||||
import org.springframework.context.annotation.ImportAware; |
||||
import org.springframework.core.type.AnnotationMetadata; |
||||
import org.springframework.security.access.hierarchicalroles.RoleHierarchy; |
||||
import org.springframework.security.authorization.DefaultAuthorizationManagerFactory; |
||||
|
||||
/** |
||||
* Uses {@link EnableGlobalMultiFactorAuthentication} to configure a |
||||
* {@link DefaultAuthorizationManagerFactory}. |
||||
* |
||||
* @author Rob Winch |
||||
* @since 7.0 |
||||
* @see EnableGlobalMultiFactorAuthentication |
||||
*/ |
||||
class GlobalMultiFactorAuthenticationConfiguration implements ImportAware { |
||||
|
||||
private String[] authorities; |
||||
|
||||
@Bean |
||||
DefaultAuthorizationManagerFactory authorizationManagerFactory(ObjectProvider<RoleHierarchy> roleHierarchy) { |
||||
DefaultAuthorizationManagerFactory.Builder<Object> builder = DefaultAuthorizationManagerFactory.builder() |
||||
.requireAdditionalAuthorities(this.authorities); |
||||
roleHierarchy.ifAvailable(builder::roleHierarchy); |
||||
return builder.build(); |
||||
} |
||||
|
||||
@Override |
||||
public void setImportMetadata(AnnotationMetadata importMetadata) { |
||||
Map<String, Object> multiFactorAuthenticationAttrs = importMetadata |
||||
.getAnnotationAttributes(EnableGlobalMultiFactorAuthentication.class.getName()); |
||||
|
||||
this.authorities = (String[]) multiFactorAuthenticationAttrs.get("authorities"); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,123 @@
@@ -0,0 +1,123 @@
|
||||
/* |
||||
* Copyright 2004-present 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 |
||||
* |
||||
* https://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 org.springframework.security.config.annotation.authorization; |
||||
|
||||
import org.assertj.core.api.Assertions; |
||||
import org.junit.jupiter.api.Test; |
||||
import org.junit.jupiter.api.extension.ExtendWith; |
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired; |
||||
import org.springframework.context.annotation.Bean; |
||||
import org.springframework.context.annotation.Configuration; |
||||
import org.springframework.security.access.AccessDeniedException; |
||||
import org.springframework.security.access.prepost.PreAuthorize; |
||||
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; |
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; |
||||
import org.springframework.security.core.GrantedAuthorities; |
||||
import org.springframework.security.test.context.support.WithMockUser; |
||||
import org.springframework.test.context.junit.jupiter.SpringExtension; |
||||
import org.springframework.test.context.web.WebAppConfiguration; |
||||
import org.springframework.test.web.servlet.MockMvc; |
||||
import org.springframework.test.web.servlet.setup.MockMvcBuilders; |
||||
import org.springframework.web.bind.annotation.GetMapping; |
||||
import org.springframework.web.bind.annotation.RestController; |
||||
import org.springframework.web.context.WebApplicationContext; |
||||
|
||||
import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity; |
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; |
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; |
||||
|
||||
/** |
||||
* Tests for {@link EnableGlobalMultiFactorAuthentication}. |
||||
* |
||||
* @author Rob Winch |
||||
*/ |
||||
@ExtendWith(SpringExtension.class) |
||||
@WebAppConfiguration |
||||
public class EnableGlobalMultiFactorAuthenticationTests { |
||||
|
||||
@Autowired |
||||
MockMvc mvc; |
||||
|
||||
@Autowired |
||||
Service service; |
||||
|
||||
@Test |
||||
@WithMockUser( |
||||
authorities = { GrantedAuthorities.FACTOR_PASSWORD_AUTHORITY, GrantedAuthorities.FACTOR_OTT_AUTHORITY }) |
||||
void webWhenAuthorized() throws Exception { |
||||
this.mvc.perform(get("/")).andExpect(status().isOk()); |
||||
} |
||||
|
||||
@Test |
||||
@WithMockUser |
||||
void webWhenNotAuthorized() throws Exception { |
||||
this.mvc.perform(get("/")).andExpect(status().isUnauthorized()); |
||||
} |
||||
|
||||
@Test |
||||
@WithMockUser( |
||||
authorities = { GrantedAuthorities.FACTOR_PASSWORD_AUTHORITY, GrantedAuthorities.FACTOR_OTT_AUTHORITY }) |
||||
void methodWhenAuthorized() throws Exception { |
||||
Assertions.assertThatNoException().isThrownBy(() -> this.service.authenticated()); |
||||
} |
||||
|
||||
@Test |
||||
@WithMockUser |
||||
void methodWhenNotAuthorized() throws Exception { |
||||
Assertions.assertThatExceptionOfType(AccessDeniedException.class) |
||||
.isThrownBy(() -> this.service.authenticated()); |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
@EnableMethodSecurity |
||||
@Configuration |
||||
@EnableGlobalMultiFactorAuthentication( |
||||
authorities = { GrantedAuthorities.FACTOR_OTT_AUTHORITY, GrantedAuthorities.FACTOR_PASSWORD_AUTHORITY }) |
||||
static class Config { |
||||
|
||||
@Bean |
||||
Service service() { |
||||
return new Service(); |
||||
} |
||||
|
||||
@Bean |
||||
MockMvc mvc(WebApplicationContext context) { |
||||
return MockMvcBuilders.webAppContextSetup(context).apply(springSecurity()).build(); |
||||
} |
||||
|
||||
@RestController |
||||
static class OkController { |
||||
|
||||
@GetMapping("/") |
||||
String ok() { |
||||
return "ok"; |
||||
} |
||||
|
||||
} |
||||
|
||||
} |
||||
|
||||
static class Service { |
||||
|
||||
@PreAuthorize("isAuthenticated()") |
||||
void authenticated() { |
||||
} |
||||
|
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,58 @@
@@ -0,0 +1,58 @@
|
||||
package org.springframework.security.docs.servlet.authentication.enableglobalmfa; |
||||
|
||||
import org.springframework.context.annotation.Bean; |
||||
import org.springframework.context.annotation.Configuration; |
||||
import org.springframework.security.config.Customizer; |
||||
import org.springframework.security.config.annotation.authorization.EnableGlobalMultiFactorAuthentication; |
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity; |
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; |
||||
import org.springframework.security.core.GrantedAuthorities; |
||||
import org.springframework.security.core.userdetails.User; |
||||
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; |
||||
|
||||
@EnableWebSecurity |
||||
@Configuration(proxyBeanMethods = false) |
||||
// tag::enable-global-mfa[]
|
||||
@EnableGlobalMultiFactorAuthentication(authorities = { |
||||
GrantedAuthorities.FACTOR_PASSWORD_AUTHORITY, |
||||
GrantedAuthorities.FACTOR_OTT_AUTHORITY }) |
||||
// end::enable-global-mfa[]
|
||||
public class EnableGlobalMultiFactorAuthenticationConfiguration { |
||||
|
||||
// tag::httpSecurity[]
|
||||
@Bean |
||||
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { |
||||
// @formatter:off
|
||||
http |
||||
.authorizeHttpRequests((authorize) -> authorize |
||||
.requestMatchers("/admin/**").hasRole("ADMIN") |
||||
.anyRequest().authenticated() |
||||
) |
||||
.formLogin(Customizer.withDefaults()) |
||||
.oneTimeTokenLogin(Customizer.withDefaults()); |
||||
// @formatter:on
|
||||
return http.build(); |
||||
} |
||||
// end::httpSecurity[]
|
||||
|
||||
@Bean |
||||
UserDetailsService userDetailsService() { |
||||
return new InMemoryUserDetailsManager( |
||||
User.withDefaultPasswordEncoder() |
||||
.username("user") |
||||
.password("password") |
||||
.authorities("app") |
||||
.build() |
||||
); |
||||
} |
||||
|
||||
@Bean |
||||
OneTimeTokenGenerationSuccessHandler tokenGenerationSuccessHandler() { |
||||
return new RedirectOneTimeTokenGenerationSuccessHandler("/ott/sent"); |
||||
} |
||||
} |
||||
|
||||
@ -0,0 +1,115 @@
@@ -0,0 +1,115 @@
|
||||
/* |
||||
* Copyright 2004-present 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 |
||||
* |
||||
* https://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 org.springframework.security.docs.servlet.authentication.enableglobalmfa; |
||||
|
||||
import org.junit.jupiter.api.Test; |
||||
import org.junit.jupiter.api.extension.ExtendWith; |
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired; |
||||
import org.springframework.security.config.test.SpringTestContext; |
||||
import org.springframework.security.config.test.SpringTestContextExtension; |
||||
import org.springframework.security.core.GrantedAuthorities; |
||||
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.response.SecurityMockMvcResultMatchers.authenticated; |
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; |
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl; |
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; |
||||
|
||||
/** |
||||
* Tests {@link CustomX509Configuration}. |
||||
* |
||||
* @author Rob Winch |
||||
*/ |
||||
@ExtendWith({ SpringExtension.class, SpringTestContextExtension.class }) |
||||
@TestExecutionListeners(WithSecurityContextTestExecutionListener.class) |
||||
public class EnableGlobalMultiFactorAuthenticationTests { |
||||
|
||||
public final SpringTestContext spring = new SpringTestContext(this); |
||||
|
||||
@Autowired |
||||
MockMvc mockMvc; |
||||
|
||||
@Test |
||||
@WithMockUser(authorities = { GrantedAuthorities.FACTOR_PASSWORD_AUTHORITY, GrantedAuthorities.FACTOR_OTT_AUTHORITY }) |
||||
void getWhenAuthenticatedWithPasswordAndOttThenPermits() throws Exception { |
||||
this.spring.register(EnableGlobalMultiFactorAuthenticationConfiguration.class, Http200Controller.class).autowire(); |
||||
// @formatter:off
|
||||
this.mockMvc.perform(get("/")) |
||||
.andExpect(status().isOk()) |
||||
.andExpect(authenticated().withUsername("user")); |
||||
// @formatter:on
|
||||
} |
||||
|
||||
@Test |
||||
@WithMockUser(authorities = GrantedAuthorities.FACTOR_PASSWORD_AUTHORITY) |
||||
void getWhenAuthenticatedWithPasswordThenRedirectsToOtt() throws Exception { |
||||
this.spring.register(EnableGlobalMultiFactorAuthenticationConfiguration.class, Http200Controller.class).autowire(); |
||||
// @formatter:off
|
||||
this.mockMvc.perform(get("/")) |
||||
.andExpect(status().is3xxRedirection()) |
||||
.andExpect(redirectedUrl("http://localhost/login?factor=ott")); |
||||
// @formatter:on
|
||||
} |
||||
|
||||
@Test |
||||
@WithMockUser(authorities = GrantedAuthorities.FACTOR_OTT_AUTHORITY) |
||||
void getWhenAuthenticatedWithOttThenRedirectsToPassword() throws Exception { |
||||
this.spring.register(EnableGlobalMultiFactorAuthenticationConfiguration.class, Http200Controller.class).autowire(); |
||||
// @formatter:off
|
||||
this.mockMvc.perform(get("/")) |
||||
.andExpect(status().is3xxRedirection()) |
||||
.andExpect(redirectedUrl("http://localhost/login?factor=password")); |
||||
// @formatter:on
|
||||
} |
||||
|
||||
@Test |
||||
@WithMockUser |
||||
void getWhenAuthenticatedThenRedirectsToPassword() throws Exception { |
||||
this.spring.register(EnableGlobalMultiFactorAuthenticationConfiguration.class, Http200Controller.class).autowire(); |
||||
// @formatter:off
|
||||
this.mockMvc.perform(get("/")) |
||||
.andExpect(status().is3xxRedirection()) |
||||
.andExpect(redirectedUrl("http://localhost/login?factor=password")); |
||||
// @formatter:on
|
||||
} |
||||
|
||||
@Test |
||||
void getWhenUnauthenticatedThenRedirectsToBoth() throws Exception { |
||||
this.spring.register(EnableGlobalMultiFactorAuthenticationConfiguration.class, Http200Controller.class).autowire(); |
||||
// @formatter:off
|
||||
this.mockMvc.perform(get("/")) |
||||
.andExpect(status().is3xxRedirection()) |
||||
.andExpect(redirectedUrl("http://localhost/login")); |
||||
// @formatter:on
|
||||
} |
||||
|
||||
@RestController |
||||
static class Http200Controller { |
||||
@GetMapping("/**") |
||||
String ok() { |
||||
return "ok"; |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,120 @@
@@ -0,0 +1,120 @@
|
||||
/* |
||||
* Copyright 2004-present 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 |
||||
* |
||||
* https://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 org.springframework.security.kt.docs.servlet.authentication.enableglobalmfa |
||||
|
||||
import org.junit.jupiter.api.Test |
||||
import org.junit.jupiter.api.extension.ExtendWith |
||||
import org.springframework.beans.factory.annotation.Autowired |
||||
import org.springframework.security.config.test.SpringTestContext |
||||
import org.springframework.security.config.test.SpringTestContextExtension |
||||
import org.springframework.security.core.GrantedAuthorities |
||||
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 |
||||
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.web.bind.annotation.GetMapping |
||||
import org.springframework.web.bind.annotation.RestController |
||||
|
||||
/** |
||||
* Tests [CustomX509Configuration]. |
||||
* |
||||
* @author Rob Winch |
||||
*/ |
||||
@ExtendWith(SpringExtension::class, SpringTestContextExtension::class) |
||||
@TestExecutionListeners(WithSecurityContextTestExecutionListener::class) |
||||
class AuthorizationManagerFactoryTests { |
||||
@JvmField |
||||
val spring: SpringTestContext = SpringTestContext(this) |
||||
|
||||
@Autowired |
||||
var mockMvc: MockMvc? = null |
||||
|
||||
@Test |
||||
@WithMockUser(authorities = [GrantedAuthorities.FACTOR_PASSWORD_AUTHORITY, GrantedAuthorities.FACTOR_OTT_AUTHORITY]) |
||||
@Throws(Exception::class) |
||||
fun getWhenAuthenticatedWithPasswordAndOttThenPermits() { |
||||
this.spring.register(UseAuthorizationManagerFactoryConfiguration::class.java, Http200Controller::class.java) |
||||
.autowire() |
||||
// @formatter:off |
||||
this.mockMvc!!.perform(MockMvcRequestBuilders.get("/")) |
||||
.andExpect(MockMvcResultMatchers.status().isOk()) |
||||
.andExpect(SecurityMockMvcResultMatchers.authenticated().withUsername("user")) |
||||
// @formatter:on |
||||
} |
||||
|
||||
@Test |
||||
@WithMockUser(authorities = [GrantedAuthorities.FACTOR_PASSWORD_AUTHORITY]) |
||||
@Throws(Exception::class) |
||||
fun getWhenAuthenticatedWithPasswordThenRedirectsToOtt() { |
||||
this.spring.register(UseAuthorizationManagerFactoryConfiguration::class.java, Http200Controller::class.java) |
||||
.autowire() |
||||
// @formatter:off |
||||
this.mockMvc!!.perform(MockMvcRequestBuilders.get("/")) |
||||
.andExpect(MockMvcResultMatchers.status().is3xxRedirection()) |
||||
.andExpect(MockMvcResultMatchers.redirectedUrl("http://localhost/login?factor=ott")) |
||||
// @formatter:on |
||||
} |
||||
|
||||
@Test |
||||
@WithMockUser(authorities = [GrantedAuthorities.FACTOR_OTT_AUTHORITY]) |
||||
@Throws(Exception::class) |
||||
fun getWhenAuthenticatedWithOttThenRedirectsToPassword() { |
||||
this.spring.register(UseAuthorizationManagerFactoryConfiguration::class.java, Http200Controller::class.java) |
||||
.autowire() |
||||
// @formatter:off |
||||
this.mockMvc!!.perform(MockMvcRequestBuilders.get("/")) |
||||
.andExpect(MockMvcResultMatchers.status().is3xxRedirection()) |
||||
.andExpect(MockMvcResultMatchers.redirectedUrl("http://localhost/login?factor=password")) |
||||
// @formatter:on |
||||
} |
||||
|
||||
@Test |
||||
@WithMockUser |
||||
@Throws(Exception::class) |
||||
fun getWhenAuthenticatedThenRedirectsToPassword() { |
||||
this.spring.register(UseAuthorizationManagerFactoryConfiguration::class.java, Http200Controller::class.java) |
||||
.autowire() |
||||
// @formatter:off |
||||
this.mockMvc!!.perform(MockMvcRequestBuilders.get("/")) |
||||
.andExpect(MockMvcResultMatchers.status().is3xxRedirection()) |
||||
.andExpect(MockMvcResultMatchers.redirectedUrl("http://localhost/login?factor=password")) |
||||
// @formatter:on |
||||
} |
||||
|
||||
@Test |
||||
@Throws(Exception::class) |
||||
fun getWhenUnauthenticatedThenRedirectsToBoth() { |
||||
this.spring.register(UseAuthorizationManagerFactoryConfiguration::class.java, Http200Controller::class.java) |
||||
.autowire() |
||||
// @formatter:off |
||||
this.mockMvc!!.perform(MockMvcRequestBuilders.get("/")) |
||||
.andExpect(MockMvcResultMatchers.status().is3xxRedirection()) |
||||
.andExpect(MockMvcResultMatchers.redirectedUrl("http://localhost/login")) |
||||
// @formatter:on |
||||
} |
||||
|
||||
@RestController |
||||
internal class Http200Controller { |
||||
@GetMapping("/**") |
||||
fun ok(): String { |
||||
return "ok" |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,54 @@
@@ -0,0 +1,54 @@
|
||||
package org.springframework.security.kt.docs.servlet.authentication.enableglobalmfa |
||||
|
||||
import org.springframework.context.annotation.Bean |
||||
import org.springframework.context.annotation.Configuration |
||||
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.GrantedAuthorities |
||||
import org.springframework.security.core.userdetails.User |
||||
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 |
||||
|
||||
@EnableWebSecurity |
||||
@Configuration(proxyBeanMethods = false) |
||||
class ListAuthoritiesEverywhereConfiguration { |
||||
|
||||
// tag::httpSecurity[] |
||||
@Bean |
||||
fun securityFilterChain(http: HttpSecurity): SecurityFilterChain? { |
||||
// @formatter:off |
||||
http { |
||||
authorizeHttpRequests { |
||||
authorize("/admin/**", hasAllAuthorities(GrantedAuthorities.FACTOR_PASSWORD_AUTHORITY, GrantedAuthorities.FACTOR_OTT_AUTHORITY, "ROLE_ADMIN")) // <1> |
||||
authorize(anyRequest, hasAllAuthorities(GrantedAuthorities.FACTOR_PASSWORD_AUTHORITY, GrantedAuthorities.FACTOR_OTT_AUTHORITY)) |
||||
} |
||||
formLogin { } |
||||
oneTimeTokenLogin { } |
||||
} |
||||
// @formatter:on |
||||
return http.build() |
||||
} |
||||
// end::httpSecurity[] |
||||
|
||||
|
||||
// end::httpSecurity[] |
||||
@Bean |
||||
fun userDetailsService(): UserDetailsService { |
||||
return InMemoryUserDetailsManager( |
||||
User.withDefaultPasswordEncoder() |
||||
.username("user") |
||||
.password("password") |
||||
.authorities("app") |
||||
.build() |
||||
) |
||||
} |
||||
|
||||
@Bean |
||||
fun tokenGenerationSuccessHandler(): OneTimeTokenGenerationSuccessHandler { |
||||
return RedirectOneTimeTokenGenerationSuccessHandler("/ott/sent") |
||||
} |
||||
} |
||||
@ -0,0 +1,61 @@
@@ -0,0 +1,61 @@
|
||||
package org.springframework.security.kt.docs.servlet.authentication.enableglobalmfa |
||||
|
||||
import org.springframework.context.annotation.Bean |
||||
import org.springframework.context.annotation.Configuration |
||||
import org.springframework.security.authorization.AuthorizationManagerFactory |
||||
import org.springframework.security.authorization.DefaultAuthorizationManagerFactory |
||||
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.GrantedAuthorities |
||||
import org.springframework.security.core.userdetails.User |
||||
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 |
||||
|
||||
@EnableWebSecurity |
||||
@Configuration(proxyBeanMethods = false) |
||||
internal class UseAuthorizationManagerFactoryConfiguration { |
||||
// tag::httpSecurity[] |
||||
@Bean |
||||
fun securityFilterChain(http: HttpSecurity): SecurityFilterChain? { |
||||
// @formatter:off |
||||
http { |
||||
authorizeHttpRequests { |
||||
authorize("/admin/**", hasRole("ADMIN")) |
||||
authorize(anyRequest, authenticated) |
||||
} |
||||
formLogin { } |
||||
oneTimeTokenLogin { } |
||||
} |
||||
// @formatter:on |
||||
return http.build() |
||||
} |
||||
// end::httpSecurity[] |
||||
|
||||
// tag::authorizationManagerFactoryBean[] |
||||
@Bean |
||||
fun authz(): AuthorizationManagerFactory<Object> { |
||||
return DefaultAuthorizationManagerFactory.builder<Object>() |
||||
.requireAdditionalAuthorities(GrantedAuthorities.FACTOR_PASSWORD_AUTHORITY, GrantedAuthorities.FACTOR_OTT_AUTHORITY).build() |
||||
} |
||||
// end::authorizationManagerFactoryBean[] |
||||
|
||||
@Bean |
||||
fun userDetailsService(): UserDetailsService { |
||||
return InMemoryUserDetailsManager( |
||||
User.withDefaultPasswordEncoder() |
||||
.username("user") |
||||
.password("password") |
||||
.authorities("app") |
||||
.build() |
||||
) |
||||
} |
||||
|
||||
@Bean |
||||
fun tokenGenerationSuccessHandler(): OneTimeTokenGenerationSuccessHandler { |
||||
return RedirectOneTimeTokenGenerationSuccessHandler("/ott/sent") |
||||
} |
||||
} |
||||
Loading…
Reference in new issue