Skip to content

Commit 293553c

Browse files
committed
Add Simple Tests
1 parent 1ef141e commit 293553c

File tree

1 file changed

+124
-0
lines changed

1 file changed

+124
-0
lines changed

config/src/test/java/org/springframework/security/config/annotation/web/configurers/FormLoginConfigurerTests.java

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,16 @@
1616

1717
package org.springframework.security.config.annotation.web.configurers;
1818

19+
import java.time.Duration;
20+
1921
import org.junit.jupiter.api.Test;
2022
import org.junit.jupiter.api.extension.ExtendWith;
2123

2224
import org.springframework.beans.factory.annotation.Autowired;
2325
import org.springframework.context.annotation.Bean;
2426
import org.springframework.context.annotation.Configuration;
2527
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
28+
import org.springframework.security.config.Customizer;
2629
import org.springframework.security.config.ObjectPostProcessor;
2730
import org.springframework.security.config.annotation.SecurityContextChangedListenerConfig;
2831
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
@@ -34,20 +37,27 @@
3437
import org.springframework.security.core.context.SecurityContextChangedListener;
3538
import org.springframework.security.core.context.SecurityContextHolderStrategy;
3639
import org.springframework.security.core.userdetails.PasswordEncodedUser;
40+
import org.springframework.security.core.userdetails.UserDetails;
3741
import org.springframework.security.core.userdetails.UserDetailsService;
42+
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
43+
import org.springframework.security.crypto.password.PasswordEncoder;
3844
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
3945
import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders;
46+
import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors;
4047
import org.springframework.security.web.PortMapper;
4148
import org.springframework.security.web.PortResolver;
4249
import org.springframework.security.web.SecurityFilterChain;
4350
import org.springframework.security.web.access.ExceptionTranslationFilter;
4451
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
4552
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
4653
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
54+
import org.springframework.security.web.authentication.ott.OneTimeTokenGenerationSuccessHandler;
55+
import org.springframework.security.web.authentication.ott.RedirectOneTimeTokenGenerationSuccessHandler;
4756
import org.springframework.security.web.savedrequest.RequestCache;
4857
import org.springframework.test.web.servlet.MockMvc;
4958
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
5059

60+
import static org.hamcrest.Matchers.containsString;
5161
import static org.mockito.ArgumentMatchers.any;
5262
import static org.mockito.BDDMockito.given;
5363
import static org.mockito.Mockito.atLeastOnce;
@@ -61,6 +71,7 @@
6171
import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated;
6272
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
6373
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
74+
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
6475
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.forwardedUrl;
6576
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl;
6677
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@@ -386,6 +397,59 @@ public void configureWhenPortResolverBeanThenPortResolverUsed() throws Exception
386397
verify(this.spring.getContext().getBean(PortResolver.class)).getServerPort(any());
387398
}
388399

400+
@Test
401+
void requestWhenUnauthenticatedThenRequiresTwoSteps() throws Exception {
402+
this.spring.register(MfaDslConfig.class).autowire();
403+
UserDetails user = PasswordEncodedUser.user();
404+
this.mockMvc.perform(get("/profile").with(SecurityMockMvcRequestPostProcessors.user(user)))
405+
.andExpect(status().is3xxRedirection())
406+
.andExpect(redirectedUrl("http://localhost/login"));
407+
this.mockMvc
408+
.perform(post("/ott/generate").param("username", "user")
409+
.with(SecurityMockMvcRequestPostProcessors.user(user))
410+
.with(SecurityMockMvcRequestPostProcessors.csrf()))
411+
.andExpect(status().is3xxRedirection())
412+
.andExpect(redirectedUrl("/ott/sent"));
413+
this.mockMvc
414+
.perform(post("/login").param("username", user.getUsername())
415+
.param("password", user.getPassword())
416+
.with(SecurityMockMvcRequestPostProcessors.csrf()))
417+
.andExpect(status().is3xxRedirection())
418+
.andExpect(redirectedUrl("/"));
419+
user = PasswordEncodedUser.withUserDetails(user).authorities("profile:read", "AUTHN_OTT").build();
420+
this.mockMvc.perform(get("/profile").with(SecurityMockMvcRequestPostProcessors.user(user)))
421+
.andExpect(status().is3xxRedirection())
422+
.andExpect(redirectedUrl("http://localhost/login"));
423+
user = PasswordEncodedUser.withUserDetails(user).authorities("profile:read", "AUTHN_FORM").build();
424+
this.mockMvc.perform(get("/profile").with(SecurityMockMvcRequestPostProcessors.user(user)))
425+
.andExpect(status().isOk())
426+
.andExpect(content().string(containsString("/ott/generate")));
427+
user = PasswordEncodedUser.withUserDetails(user).authorities("profile:read", "AUTHN_FORM", "AUTHN_OTT").build();
428+
this.mockMvc.perform(get("/profile").with(SecurityMockMvcRequestPostProcessors.user(user)))
429+
.andExpect(status().isNotFound());
430+
}
431+
432+
@Test
433+
void requestWhenUnauthenticatedX509ThenRequiresTwoSteps() throws Exception {
434+
this.spring.register(MfaDslX509Config.class).autowire();
435+
this.mockMvc.perform(get("/")).andExpect(status().isForbidden());
436+
this.mockMvc.perform(get("/login")).andExpect(status().isOk());
437+
this.mockMvc.perform(get("/").with(SecurityMockMvcRequestPostProcessors.x509("rod.cer")))
438+
.andExpect(status().is3xxRedirection())
439+
.andExpect(redirectedUrl("http://localhost/login"));
440+
UserDetails user = PasswordEncodedUser.withUsername("rod")
441+
.password("password")
442+
.authorities("AUTHN_FORM")
443+
.build();
444+
this.mockMvc
445+
.perform(post("/login").param("username", user.getUsername())
446+
.param("password", user.getPassword())
447+
.with(SecurityMockMvcRequestPostProcessors.x509("rod.cer"))
448+
.with(SecurityMockMvcRequestPostProcessors.csrf()))
449+
.andExpect(status().is3xxRedirection())
450+
.andExpect(redirectedUrl("/"));
451+
}
452+
389453
@Configuration
390454
@EnableWebSecurity
391455
static class RequestCacheConfig {
@@ -751,4 +815,64 @@ public <O> O postProcess(O object) {
751815

752816
}
753817

818+
@Configuration
819+
@EnableWebSecurity
820+
static class MfaDslConfig {
821+
822+
private static final Duration FIVE_MINUTES = Duration.ofMinutes(5);
823+
824+
@Bean
825+
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
826+
// @formatter:off
827+
http
828+
.formLogin((form) -> form.factor((f) -> f.grants(FIVE_MINUTES, "profile:read")))
829+
.oneTimeTokenLogin((ott) -> ott.factor(Customizer.withDefaults()))
830+
.authorizeHttpRequests((authorize) -> authorize
831+
.requestMatchers("/profile").hasAuthority("profile:read")
832+
.anyRequest().authenticated()
833+
);
834+
return http.build();
835+
// @formatter:on
836+
}
837+
838+
@Bean
839+
UserDetailsService users() {
840+
return new InMemoryUserDetailsManager(PasswordEncodedUser.user());
841+
}
842+
843+
@Bean
844+
PasswordEncoder encoder() {
845+
return NoOpPasswordEncoder.getInstance();
846+
}
847+
848+
@Bean
849+
OneTimeTokenGenerationSuccessHandler tokenGenerationSuccessHandler() {
850+
return new RedirectOneTimeTokenGenerationSuccessHandler("/ott/sent");
851+
}
852+
853+
}
854+
855+
@Configuration
856+
@EnableWebSecurity
857+
static class MfaDslX509Config {
858+
859+
@Bean
860+
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
861+
// @formatter:off
862+
http
863+
.formLogin((form) -> form.factor(Customizer.withDefaults()))
864+
.x509((x509) -> x509.factor(Customizer.withDefaults()))
865+
.authorizeHttpRequests((authorize) -> authorize.anyRequest().authenticated());
866+
return http.build();
867+
// @formatter:on
868+
}
869+
870+
@Bean
871+
UserDetailsService users() {
872+
return new InMemoryUserDetailsManager(
873+
PasswordEncodedUser.withUsername("rod").password("{noop}password").build());
874+
}
875+
876+
}
877+
754878
}

0 commit comments

Comments
 (0)