package org.zalando.problem.spring.web.advice.security;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
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.configuration.WebSecurityConfigurerAdapter;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
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.ControllerAdvice;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
import org.zalando.problem.ProblemModule;
import org.zalando.problem.spring.web.advice.MediaTypes;
import org.zalando.problem.spring.web.advice.ProblemHandling;
import java.util.List;
import static org.hamcrest.Matchers.is;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.anonymous;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user;
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.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
@WebAppConfiguration
public final class SecurityAdviceTraitTest {
@Configuration
@EnableWebMvc
@EnableWebSecurity
@Import({MvcConfiguration.class, SecurityConfiguration.class})
public static class TestConfiguration extends WebMvcConfigurationSupport {
@Bean
public MockMvc mvc(final WebApplicationContext context) {
return MockMvcBuilders
.webAppContextSetup(context)
.apply(springSecurity())
.build();
}
}
@Configuration
@Import({TestController.class, ExceptionHandling.class})
public static class MvcConfiguration extends WebMvcConfigurationSupport {
@Override
protected void configureMessageConverters(final List<HttpMessageConverter<?>> converters) {
converters.clear();
converters.add(new MappingJackson2HttpMessageConverter(new ObjectMapper()
.registerModule(new ProblemModule())));
}
}
@Configuration
@Import(SecurityProblemSupport.class)
public static class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Autowired
private SecurityProblemSupport problemSupport;
@Override
public void configure(final HttpSecurity http) throws Exception {
http.csrf().disable();
http.httpBasic().disable();
http.sessionManagement().disable();
http.authorizeRequests()
.antMatchers("/greet").hasRole("ADMIN")
.anyRequest().authenticated();
http.exceptionHandling()
.authenticationEntryPoint(problemSupport)
.accessDeniedHandler(problemSupport);
}
}
@ControllerAdvice
public static class ExceptionHandling implements ProblemHandling {
}
@RestController
public static class TestController {
@RequestMapping("/greet")
public String greet(@RequestParam final String name) {
return "Hello " + name + "!";
}
}
@Autowired
private MockMvc mvc;
@Test
public void notAuthenticated() throws Exception {
mvc.perform(post("/").with(anonymous()))
.andExpect(status().isUnauthorized())
.andExpect(content().contentType(MediaTypes.PROBLEM))
.andExpect(jsonPath("$.title", is("Unauthorized")))
.andExpect(jsonPath("$.status", is(401)))
.andExpect(jsonPath("$.detail", is("Full authentication is required to access this resource")));
}
@Test
public void notAuthorized() throws Exception {
mvc.perform(get("/greet").param("name", "Alice").with(user("user").roles("USER")))
.andExpect(status().isForbidden())
.andExpect(content().contentType(MediaTypes.PROBLEM))
.andExpect(jsonPath("$.title", is("Forbidden")))
.andExpect(jsonPath("$.status", is(403)))
.andExpect(jsonPath("$.detail", is("Access is denied")));
}
}