How test correctly the LoginForm through #WithAnonymousUser - spring

About Spring Security
For the class that extends WebSecurityConfigurerAdapter I have the following for the configure method:
.and()
.formLogin()
.loginPage("/perfom/login")
.loginProcessingUrl("/perform_/login")
.usernameParameter("username")//default
.passwordParameter("password")//default
.defaultSuccessUrl("/welcome")
.failureUrl("/perfom/login?error") //default is /login?error
.permitAll()
It works fine in runtime. Until here no problems.
When Spring MVC Test plays I have the following:
#Before
public void setUp(){
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext)
.apply(springSecurity())
.build();
}
#Test
#WithAnonymousUser
public void saveOneHtmlGetForNonAuthenticatedPrincipalTest() throws Exception {
resultActions = mockMvc.perform(get(url).with(csrf().asHeader()).accept(MediaType.TEXT_HTML_VALUE)
.header("Accept-Language", ClientUserLocale.ENGLISH.getLocale().toString())).andDo(print());
resultActions.andExpect(status().isFound())
.andExpect(redirectedUrl("/perfom/login"));
}
The test fails with the following error message:
java.lang.AssertionError:
Redirected URL expected:</perfom/login>
but was:<http://localhost/perfom/login>
Not sure if it is the expected behavior or something missing, not sure why
http://localhost/appears.

I had the same problem and was able to fix it using redirectedUrlPattern.
In your case the solution should be:
.andExpect(redirectedUrlPattern("**/perfom/login"))

Related

Unit test returns 401 Unauthorized on a permitted route in Spring WebFlux Security

I am trying to test a route that returns array of objects but the test fails because it returns Unauthorized instead of 200 OK
My test class
#RunWith(SpringRunner.class)
#WebFluxTest(value = CatController.class)
class ContentManagementTestApplicationTests {
#Autowired
ApplicationContext context;
#Autowired
private WebTestClient webTestClient;
#MockBean
CatRepository catRepository;
#BeforeEach
public void setup(){
webTestClient=WebTestClient.bindToApplicationContext(context)
.apply(SecurityMockServerConfigurers.springSecurity())
.configureClient()
.build();
}
#Test
void contextLoads() {
}
#Test
public void getApprovedCats(){
webTestClient.get()
.uri("/cat")
.accept(MediaType.APPLICATION_JSON)
.exchange()
.expectStatus().isOk();
}
}
And ApplicationSecurityConfig class, has a SecurityWebFilterChain Bean
#Bean
SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http, AuthConverter jwtAuthConverter, AuthManager jwtAuthManager){
AuthenticationWebFilter jwtFilter = new AuthenticationWebFilter(jwtAuthManager);
jwtFilter.setServerAuthenticationConverter(jwtAuthConverter);
return http .csrf().disable()
.authorizeExchange()
.pathMatchers(HttpMethod.GET,"/cat").permitAll()
.anyExchange()
.authenticated()
.and()
.addFilterAt(jwtFilter, SecurityWebFiltersOrder.AUTHORIZATION)
.formLogin().disable()
.httpBasic().disable()
.build();
}
The Error on JUint test shows following
java.lang.AssertionError: Status expected:<200 OK> but was:<401 UNAUTHORIZED>
Expected :200 OK
Actual :401 UNAUTHORIZED
Before test fails and shows Unauthorized, in console it prints
"Using generated security password: 8e5dd468-3fd1-42b6-864a-c4c2ed2227b7"
And I believe that should not be printed since I disabled it in securityFilterChain
From the Javadoc of #WebFluxTest:
Annotation that can be used for a Spring WebFlux test that focuses only on Spring WebFlux components.
Using this annotation will disable full auto-configuration and instead apply only configuration relevant to WebFlux tests
If you are looking to load your full application configuration and use WebTestClient, you should consider #SpringBootTest combined with #AutoConfigureWebTestClient rather than this annotation.
In other words, when using #WebFluxTest in your test, your SecurityWebFilterChain is not picked up.
Try annotating your class with #SpringBootTest instead.

Http401 in tests but working in normal usage

I'm using keycloak, but for tests I turned it off
keycloak.enabled = false
In my config I used almost everything to pass test as anonymous
public class KeyCloakConfig extends KeycloakWebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.headers().frameOptions().disable();
http
.anonymous().and() .csrf().disable()
.authorizeRequests()
.antMatchers("/ingredients").permitAll();
}
}
In test I've got 401 even when with #WithAnonymousUser
#WebMvcTest(value = IngredientController.class)
#TestPropertySource("classpath:application-development.properties")
class IngredientControllerTest {
#MockBean
IngredientService ingredientService;
#Autowired
MockMvc mockMvc;
#AfterEach
void tearDown() {
reset(ingredientService);
}
#Test
#WithAnonymousUser
void getAllIngredients() throws Exception {
given(ingredientService.findAll()).willReturn(Arrays.asList(new Ingredient(),new Ingredient()));
mockMvc.perform(get("/ingredients"))
.andExpect(status().isOk());
then(ingredientService).should().findAll();
assertThat(ingredientService.findAll()).hasSize(2);
}
}
What is funny when I use annotation #WithMockUser in test everything is ok, also turn on my web service and I go to this url everything is also ok
Also I've got basic authentication beacuse config class is disabled, I'm sure it's basic because in log I can see Using generated security password: 52cce531-b308-4d90-b76f-8984a88879fd. How to add to context slice configuration class ?
#SpringJUnitConfig(classes = TestSecurityConfig.class) doesn't work
This other stackoverflow question may help you out:
Spring Boot - How to disable Keycloak?.
The solution here is to add
spring.autoconfigure.exclude=org.keycloak.adapters.springboot.KeycloakSpringBootConfiguration
to your application-development.properties file

getting http 404 when testing Spring Boot with Spring Security

TLDR: spring boot test does not find url endpoint defined using spring security
Long story:
My SpringBoot application uses Spring Security.
In its Security context it defines:
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf()
.disable()
.authorizeRequests()
.antMatchers("/api/login").permitAll()
.and()
.formLogin()
.permitAll()
.loginProcessingUrl("/api/login")
.antMatchers(POST, "/api/**")
.hasAuthority(ADMIN)
}
}
My test code is initialized as a SpringBoot Test:
#RunWith(SpringRunner.class)
#SpringBootTest(classes = MySpringBootApplication.class)
public class ContractVerifierBase {
#Autowired
private WebApplicationContext context;
#Before
public void setUp() throws Exception {
RestAssuredMockMvc.webAppContextSetup(context);
}
}
My test sends s POST request to /api/login and although I expect a 401 to be rturned, a 404 is returned.
ResponseOptions response = given().spec(request.post("/api/login");
Why is it not finding the /api/login?
I think you need to pass to rest assured in the base class basic authentication data. E.g.
#Before
public void setup() {
RestAssuredMockMvc.authentication =
RestAssuredMockMvc.basic("sk_test_BQokikJOvBiI2HlWgH4olfQ2", "");
}

Spring MVC testing (security Integration test), JSESSIONID is not present

I have created custom login form for my spring boot app.
In my form integration test, I want to check that received cookies contain JSESSIONID and XSRF-TOKEN.
But, I received only XSRF-TOKEN.
Here is my test:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = Application.class)
#WebAppConfiguration
#IntegrationTest("server.port:0")
public class UserIT {
#Autowired
private WebApplicationContext context;
#Autowired
private FilterChainProxy springSecurityFilterChain;
#Value("${local.server.port}")
private Integer port;
private MockMvc mockMvc;
#Before
public void setup() {
mockMvc =
MockMvcBuilders.webAppContextSetup(context).addFilters(springSecurityFilterChain)
.build();
}
#Test
public void getUserInfoTest() throws Exception {
disableSslVerification();
MvcResult result =
mockMvc.perform(formLogin("/login").user("roy").password("spring")).andExpect(authenticated())
.andReturn();
Cookie sessionId = result.getResponse().getCookie("JSESSIONID");
Cookie token = result.getResponse().getCookie("XSRF-TOKEN");
}
Security conf:
#Override
public void configure(HttpSecurity http) throws Exception {
// #formatter:off
http
//.httpBasic()
//.and()
.headers().frameOptions().disable()
.and()
.antMatcher("/**").authorizeRequests()
.antMatchers("/actuator/health").permitAll()
.antMatchers("/actuator/**").hasAuthority(Authority.Type.ROLE_ADMIN.getName())
.antMatchers("/login**", "/index.html", "/home.html").permitAll()
.anyRequest().authenticated()
.and()
.formLogin().loginPage("/login.jsp")
.usernameParameter("username")
.passwordParameter("password")
.loginProcessingUrl("/login")
.permitAll()
.and()
.logout().logoutSuccessUrl("/login.jsp").permitAll()
.and()
.csrf().csrfTokenRepository(csrfTokenRepository())
.and()
.addFilterAfter(csrfHeaderFilter(), CsrfFilter.class)
.addFilterBefore(ssoFilter(), BasicAuthenticationFilter.class);
// #formatter:on
}
Please, help me to obtain the required result.
You also don't see Set-Cookie header. For me it's a big limitation of MockMVC. For a workaround see Why does Spring MockMvc result not contain a cookie?.
if you are using spring security with default login endpoint, there is a good news.
you dont need JSESSION from cookies after invoking /login ep. just use #WithMockUser
above your test method, perform login request and from now, all your requests will be authorized as the last logged user.

WIthMockUser doesn't work

I can't use #WithMockUser, no matter what I do it doesn't provide authorized user in spring test. Maybe it's because its resource server but..
Config:
#Configuration
#EnableResourceServer
class ResourceServerConfig extends ResourceServerConfigurerAdapter
...
#Override
public void configure(HttpSecurity http) throws Exception {
http
.anonymous().and().authorizeRequests().antMatchers("/**").hasRole("USER");
}
...
}
And test class
#BeforeClass
public void setup(){
mockMvc = MockMvcBuilders
.webAppContextSetup(wac)
.apply(springSecurity())
.build();
}
#Test
#WithMockUser()
public void shouldAddValueToStore() throws Exception {
ResultActions response = mockMvc.perform(post("/bucket/key")
.content("value"));
...
}
I keep getting 401 Access is denied (user is anonymous); redirecting to authentication entry point. I've tried with setting usernames, roles in annotation parameters, passing with(user..) to mockMvc, nothing helps. It's spring security 4.0.4.RELEASE.
ok, it works after setting 'stateless' parameter to 'false' as described here: https://github.com/spring-projects/spring-security-oauth/issues/385
and using with(user..
ResultActions response = mockMvc.perform(post("/bucket/key")
.with(user("john#example.com"))
.content("value"));
WithMockUser is still not working, but this method is good enough for now.

Resources