webauthn: introduce WebAuthnConfigurer#disableDefaultRegistrationPage

This commit is contained in:
Daniel Garnier-Moiroux
2024-11-13 18:33:32 +01:00
committed by Rob Winch
parent de7c452e42
commit 2639ac6545
2 changed files with 149 additions and 4 deletions

View File

@@ -16,6 +16,8 @@
package org.springframework.security.config.annotation.web.configurers;
import java.util.List;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
@@ -29,9 +31,12 @@ import org.springframework.security.config.test.SpringTestContext;
import org.springframework.security.config.test.SpringTestContextExtension;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.FilterChainProxy;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.ui.DefaultResourcesFilter;
import org.springframework.test.web.servlet.MockMvc;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hamcrest.Matchers.containsString;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
@@ -50,7 +55,7 @@ public class WebAuthnConfigurerTests {
MockMvc mvc;
@Test
public void javascriptWhenWebauthnConfiguredThenServesJavascript() throws Exception {
public void webauthnWhenConfiguredConfiguredThenServesJavascript() throws Exception {
this.spring.register(DefaultWebauthnConfiguration.class).autowire();
this.mvc.perform(get("/login/webauthn.js"))
.andExpect(status().isOk())
@@ -58,6 +63,69 @@ public class WebAuthnConfigurerTests {
.andExpect(content().string(containsString("async function authenticate(")));
}
@Test
public void webauthnWhenConfiguredConfiguredThenServesCss() throws Exception {
this.spring.register(DefaultWebauthnConfiguration.class).autowire();
this.mvc.perform(get("/default-ui.css"))
.andExpect(status().isOk())
.andExpect(header().string("content-type", "text/css;charset=UTF-8"))
.andExpect(content().string(containsString("body {")));
}
@Test
public void webauthnWhenNoFormLoginAndDefaultRegistrationPageConfiguredThenServesJavascript() throws Exception {
this.spring.register(NoFormLoginAndDefaultRegistrationPageConfiguration.class).autowire();
this.mvc.perform(get("/login/webauthn.js"))
.andExpect(status().isOk())
.andExpect(header().string("content-type", "text/javascript;charset=UTF-8"))
.andExpect(content().string(containsString("async function authenticate(")));
}
@Test
public void webauthnWhenNoFormLoginAndDefaultRegistrationPageConfiguredThenServesCss() throws Exception {
this.spring.register(NoFormLoginAndDefaultRegistrationPageConfiguration.class).autowire();
this.mvc.perform(get("/default-ui.css"))
.andExpect(status().isOk())
.andExpect(header().string("content-type", "text/css;charset=UTF-8"))
.andExpect(content().string(containsString("body {")));
}
@Test
public void webauthnWhenFormLoginAndDefaultRegistrationPageConfiguredThenNoDuplicateFilters() {
this.spring.register(DefaultWebauthnConfiguration.class).autowire();
FilterChainProxy filterChain = this.spring.getContext().getBean(FilterChainProxy.class);
List<DefaultResourcesFilter> defaultResourcesFilters = filterChain.getFilterChains()
.get(0)
.getFilters()
.stream()
.filter(DefaultResourcesFilter.class::isInstance)
.map(DefaultResourcesFilter.class::cast)
.toList();
assertThat(defaultResourcesFilters).map(DefaultResourcesFilter::toString)
.filteredOn((filterDescription) -> filterDescription.contains("login/webauthn.js"))
.hasSize(1);
assertThat(defaultResourcesFilters).map(DefaultResourcesFilter::toString)
.filteredOn((filterDescription) -> filterDescription.contains("default-ui.css"))
.hasSize(1);
}
@Test
public void webauthnWhenConfiguredAndFormLoginThenDoesServesJavascript() throws Exception {
this.spring.register(FormLoginAndNoDefaultRegistrationPageConfiguration.class).autowire();
this.mvc.perform(get("/login/webauthn.js"))
.andExpect(status().isOk())
.andExpect(header().string("content-type", "text/javascript;charset=UTF-8"))
.andExpect(content().string(containsString("async function authenticate(")));
}
@Test
public void webauthnWhenConfiguredAndNoDefaultRegistrationPageThenDoesNotServeJavascript() throws Exception {
this.spring.register(NoDefaultRegistrationPageConfiguration.class).autowire();
this.mvc.perform(get("/login/webauthn.js")).andExpect(status().isNotFound());
}
@Configuration
@EnableWebSecurity
static class DefaultWebauthnConfiguration {
@@ -67,6 +135,22 @@ public class WebAuthnConfigurerTests {
return new InMemoryUserDetailsManager();
}
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
return http.formLogin(Customizer.withDefaults()).webAuthn(Customizer.withDefaults()).build();
}
}
@Configuration
@EnableWebSecurity
static class NoFormLoginAndDefaultRegistrationPageConfiguration {
@Bean
UserDetailsService userDetailsService() {
return new InMemoryUserDetailsManager();
}
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
return http.webAuthn(Customizer.withDefaults()).build();
@@ -74,4 +158,40 @@ public class WebAuthnConfigurerTests {
}
@Configuration
@EnableWebSecurity
static class FormLoginAndNoDefaultRegistrationPageConfiguration {
@Bean
UserDetailsService userDetailsService() {
return new InMemoryUserDetailsManager();
}
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
return http.formLogin(Customizer.withDefaults())
.webAuthn((webauthn) -> webauthn.disableDefaultRegistrationPage(true))
.build();
}
}
@Configuration
@EnableWebSecurity
static class NoDefaultRegistrationPageConfiguration {
@Bean
UserDetailsService userDetailsService() {
return new InMemoryUserDetailsManager();
}
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
return http.formLogin((login) -> login.loginPage("/custom-login-page"))
.webAuthn((webauthn) -> webauthn.disableDefaultRegistrationPage(true))
.build();
}
}
}