DATAREST-774 - Separated integration tests from core project to avoid classpath overlap.
Extracted store specific tests into separate test modules to prevent classpath overlap between projects. Those tests are now executed in an "it" build profile to prevent the tests being packaged for distribution on release. Use Map-based repositories and mapping contexts for test in the Core and WebMvc module. Slightly changed the configuration API for lookup types on RepositoryRestConfiguration. Related ticket: DATAREST-776.
This commit is contained in:
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright 2016 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
|
||||
*
|
||||
* http://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.data.rest.tests.security;
|
||||
|
||||
import lombok.Value;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import org.springframework.data.annotation.Id;
|
||||
|
||||
/**
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@Value
|
||||
public class Order {
|
||||
|
||||
@Id UUID id = UUID.randomUUID();
|
||||
Person customer;
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright 2016 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
|
||||
*
|
||||
* http://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.data.rest.tests.security;
|
||||
|
||||
import lombok.Value;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import org.springframework.data.annotation.Id;
|
||||
|
||||
/**
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@Value
|
||||
public class Person {
|
||||
|
||||
@Id UUID id = UUID.randomUUID();
|
||||
String firstname, lastname;
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright 2013-2016 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
|
||||
*
|
||||
* http://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.data.rest.tests.security;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
|
||||
/**
|
||||
* @author Oliver Gierke
|
||||
* @author Greg Turnquist
|
||||
*/
|
||||
// tag::code[]
|
||||
@PreAuthorize("hasRole('ROLE_USER')") // <1>
|
||||
public interface PreAuthorizedOrderRepository extends CrudRepository<Order, UUID> {
|
||||
|
||||
@PreAuthorize("hasRole('ROLE_ADMIN')") // <2>
|
||||
@Override
|
||||
void delete(UUID aLong);
|
||||
|
||||
@PreAuthorize("hasRole('ROLE_ADMIN')")
|
||||
@Override
|
||||
void delete(Order order);
|
||||
|
||||
@PreAuthorize("hasRole('ROLE_ADMIN')")
|
||||
@Override
|
||||
void delete(Iterable<? extends Order> orders);
|
||||
|
||||
@PreAuthorize("hasRole('ROLE_ADMIN')")
|
||||
@Override
|
||||
void deleteAll();
|
||||
}
|
||||
// end::code[]
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright 2014-2016 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
|
||||
*
|
||||
* http://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.data.rest.tests.security;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
import org.springframework.data.rest.core.annotation.RepositoryRestResource;
|
||||
import org.springframework.security.access.annotation.Secured;
|
||||
|
||||
// tag::code[]
|
||||
@Secured("ROLE_USER") // <1>
|
||||
@RepositoryRestResource(collectionResourceRel = "people", path = "people")
|
||||
public interface SecuredPersonRepository extends CrudRepository<Person, UUID> {
|
||||
|
||||
@Secured("ROLE_ADMIN") // <2>
|
||||
@Override
|
||||
void delete(UUID aLong);
|
||||
|
||||
@Secured("ROLE_ADMIN")
|
||||
@Override
|
||||
void delete(Person person);
|
||||
|
||||
@Secured("ROLE_ADMIN")
|
||||
@Override
|
||||
void delete(Iterable<? extends Person> persons);
|
||||
|
||||
@Secured("ROLE_ADMIN")
|
||||
@Override
|
||||
void deleteAll();
|
||||
}
|
||||
// end::code[]
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright 2014-2016 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
|
||||
*
|
||||
* http://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.data.rest.tests.security;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
|
||||
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;
|
||||
|
||||
// tag::code[]
|
||||
@Configuration // <1>
|
||||
@EnableWebSecurity
|
||||
@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true) // <2>
|
||||
public class SecurityConfiguration extends WebSecurityConfigurerAdapter { // <3>
|
||||
// end::code[]
|
||||
@Autowired
|
||||
public void configureAuth(AuthenticationManagerBuilder auth) throws Exception {
|
||||
|
||||
auth.inMemoryAuthentication()
|
||||
.withUser("user").password("user").roles("USER").and()
|
||||
.withUser("admin").password("admin").roles("USER", "ADMIN");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
|
||||
http.
|
||||
authorizeRequests()
|
||||
.antMatchers(HttpMethod.GET, "/").permitAll() // Ignore security at the root URI.
|
||||
.anyRequest().authenticated()
|
||||
.and()
|
||||
.httpBasic()
|
||||
.and()
|
||||
.csrf().disable(); // Disable CSRF since it's not critical for the scope of testing.
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,282 @@
|
||||
/*
|
||||
* Copyright 2014-2016 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
|
||||
*
|
||||
* http://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.data.rest.tests.security;
|
||||
|
||||
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.*;
|
||||
import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.*;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.map.repository.config.EnableMapRepositories;
|
||||
import org.springframework.data.rest.tests.AbstractWebIntegrationTests;
|
||||
import org.springframework.data.rest.tests.TestMvcClient;
|
||||
import org.springframework.data.rest.webmvc.config.RepositoryRestMvcConfiguration;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.security.access.intercept.aopalliance.MethodSecurityInterceptor;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.authority.AuthorityUtils;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.web.context.WebApplicationContext;
|
||||
|
||||
/**
|
||||
* Test Spring Data REST in the context of being locked down by Spring Security. Uses MockMvc to simulate HTTP-based
|
||||
* interactions. Testing is possible on the repository level, but that doesn't align with the mission of Spring Data
|
||||
* REST.
|
||||
*
|
||||
* @author Greg Turnquist
|
||||
* @author Rob Winch
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@Transactional
|
||||
@ContextConfiguration(classes = { SecurityIntegrationTests.Config.class, SecurityConfiguration.class,
|
||||
RepositoryRestMvcConfiguration.class })
|
||||
public class SecurityIntegrationTests extends AbstractWebIntegrationTests {
|
||||
|
||||
@Autowired WebApplicationContext context;
|
||||
@Autowired MethodSecurityInterceptor methodSecurityInterceptor;
|
||||
|
||||
@Autowired SecuredPersonRepository personRepository;
|
||||
@Autowired PreAuthorizedOrderRepository orderRepository;
|
||||
|
||||
@Configuration
|
||||
@EnableMapRepositories
|
||||
static class Config {}
|
||||
|
||||
@Before
|
||||
@Override
|
||||
public void setUp() {
|
||||
|
||||
super.setUp();
|
||||
|
||||
SecurityContextHolder.getContext().setAuthentication(new UsernamePasswordAuthenticationToken("user", "user",
|
||||
AuthorityUtils.createAuthorityList("ROLE_USER", "ROLE_ADMIN")));
|
||||
|
||||
personRepository.deleteAll();
|
||||
orderRepository.deleteAll();
|
||||
|
||||
Person frodo = personRepository.save(new Person("Frodo", "Baggins"));
|
||||
orderRepository.save(new Order(frodo));
|
||||
|
||||
SecurityContextHolder.clearContext();
|
||||
}
|
||||
|
||||
/**
|
||||
* Override the configuration used in {@link AbstractWebIntegrationTests} by plugging in Spring Security's
|
||||
* {@link TestSecurityContextHolderPostProcessor} via apply(springSecurity()).
|
||||
*/
|
||||
@Override
|
||||
protected void setupMockMvc() {
|
||||
this.mvc = MockMvcBuilders.webAppContextSetup(context).//
|
||||
defaultRequest(get("/").//
|
||||
accept(TestMvcClient.DEFAULT_MEDIA_TYPE))
|
||||
.//
|
||||
apply(springSecurity()).//
|
||||
build();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREST-327
|
||||
*/
|
||||
@Test
|
||||
public void deletePersonAccessDeniedForNoCredentials() throws Exception {
|
||||
|
||||
// Getting the collection is not tested here. This is to get the URI that will later be tested for DELETE
|
||||
final String people = client.discoverUnique("people").expand().getHref();
|
||||
|
||||
MockHttpServletResponse response = mvc
|
||||
.perform(get(people).//
|
||||
with(user("user").roles("USER")))
|
||||
.//
|
||||
andReturn().getResponse();
|
||||
String href = assertHasJsonPathValue("$._embedded.people[0]._links.self.href", response);
|
||||
|
||||
// Clear any side effects of logging in to get the URI from security.
|
||||
SecurityContextHolder.clearContext();
|
||||
|
||||
mvc.perform(delete(href)).andExpect(status().isUnauthorized());
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREST-327
|
||||
*/
|
||||
@Test
|
||||
public void deletePersonAccessDeniedForUsers() throws Exception {
|
||||
|
||||
MockHttpServletResponse response = mvc
|
||||
.perform(get(client.discoverUnique("people").expand().getHref()).//
|
||||
with(user("user").roles("USER")))
|
||||
.//
|
||||
andReturn().getResponse();
|
||||
String href = assertHasJsonPathValue("$._embedded.people[0]._links.self.href", response);
|
||||
|
||||
// Clear any side effects of logging in to get the URI from security.
|
||||
SecurityContextHolder.clearContext();
|
||||
|
||||
mvc.perform(delete(href).with(user("user").roles("USER"))).//
|
||||
andExpect(status().isForbidden());
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREST-327
|
||||
*/
|
||||
@Test
|
||||
public void deletePersonAccessGrantedForAdmins() throws Exception {
|
||||
|
||||
MockHttpServletResponse response = mvc
|
||||
.perform(get(client.discoverUnique("people").expand().getHref()).//
|
||||
with(user("user").roles("USER", "ADMIN")))
|
||||
.//
|
||||
andReturn().getResponse();
|
||||
String href = assertHasJsonPathValue("$._embedded.people[0]._links.self.href", response);
|
||||
|
||||
// Clear any side effects of logging in to get the URI from security.
|
||||
SecurityContextHolder.clearContext();
|
||||
|
||||
mvc.perform(delete(href).with(user("user").roles("USER", "ADMIN")))
|
||||
.andExpect(status().is(HttpStatus.NO_CONTENT.value()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREST-327
|
||||
*/
|
||||
@Test
|
||||
public void findAllPeopleAccessDeniedForNoCredentials() throws Throwable {
|
||||
|
||||
mvc.perform(get(client.discoverUnique("people").expand().getHref())).//
|
||||
andExpect(status().isUnauthorized());
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREST-327
|
||||
*/
|
||||
@Test
|
||||
public void findAllPeopleAccessGrantedForUsers() throws Throwable {
|
||||
|
||||
mvc.perform(get(client.discoverUnique("people").expand().getHref()).//
|
||||
with(user("user").roles("USER"))).//
|
||||
andExpect(status().isOk());
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREST-327
|
||||
*/
|
||||
@Test
|
||||
public void findAllPeopleAccessGrantedForAdmins() throws Throwable {
|
||||
|
||||
mvc.perform(get(client.discoverUnique("people").expand().getHref()).//
|
||||
with(user("user").roles("USER", "ADMIN"))).//
|
||||
andExpect(status().isOk());
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREST-327
|
||||
*/
|
||||
@Test
|
||||
public void deleteOrderAccessDeniedForNoCredentials() throws Exception {
|
||||
|
||||
// Getting the collection is not tested here. This is to get the URI that will later be tested for DELETE
|
||||
MockHttpServletResponse response = mvc
|
||||
.perform(get(client.discoverUnique("orders").expand().getHref()).//
|
||||
with(user("user").roles("USER")))
|
||||
.//
|
||||
andReturn().getResponse();
|
||||
String href = assertHasJsonPathValue("$._embedded.orders[0]._links.self.href", response);
|
||||
|
||||
// Clear any side effects of logging in to get the URI from security.
|
||||
SecurityContextHolder.clearContext();
|
||||
|
||||
mvc.perform(delete(href)).andExpect(status().isUnauthorized());
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREST-327
|
||||
*/
|
||||
@Test
|
||||
public void deleteOrderAccessDeniedForUsers() throws Exception {
|
||||
|
||||
MockHttpServletResponse response = mvc
|
||||
.perform(get(client.discoverUnique("orders").expand().getHref()).//
|
||||
with(user("user").roles("USER")))
|
||||
.//
|
||||
andReturn().getResponse();
|
||||
String href = assertHasJsonPathValue("$._embedded.orders[0]._links.self.href", response);
|
||||
|
||||
mvc.perform(delete(href).with(user("user").roles("USER"))).//
|
||||
andExpect(status().isForbidden());
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREST-327
|
||||
*/
|
||||
@Test
|
||||
public void deleteOrderAccessGrantedForAdmins() throws Exception {
|
||||
|
||||
MockHttpServletResponse response = mvc
|
||||
.perform(get(client.discoverUnique("orders").expand().getHref()).//
|
||||
with(user("user").roles("USER")))
|
||||
.//
|
||||
andReturn().getResponse();
|
||||
String href = assertHasJsonPathValue("$._embedded.orders[0]._links.self.href", response);
|
||||
|
||||
// Clear any side effects of logging in to get the URI from security.
|
||||
SecurityContextHolder.clearContext();
|
||||
|
||||
mvc.perform(delete(href).with(user("user").roles("USER", "ADMIN")))
|
||||
.andExpect(status().is(HttpStatus.NO_CONTENT.value()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREST-327
|
||||
*/
|
||||
@Test
|
||||
public void findAllOrdersAccessDeniedForNoCredentials() throws Throwable {
|
||||
|
||||
mvc.perform(get(client.discoverUnique("orders").expand().getHref())).//
|
||||
andExpect(status().isUnauthorized());
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREST-327
|
||||
*/
|
||||
@Test
|
||||
public void findAllOrdersAccessGrantedForUsers() throws Throwable {
|
||||
|
||||
mvc.perform(get(client.discoverUnique("orders").expand().getHref()).//
|
||||
with(user("user").roles("USER"))).//
|
||||
andExpect(status().isOk());
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREST-327
|
||||
*/
|
||||
@Test
|
||||
public void findAllOrdersAccessGrantedForAdmins() throws Throwable {
|
||||
|
||||
mvc.perform(get(client.discoverUnique("orders").expand().getHref()).//
|
||||
with(user("user").roles("USER", "ADMIN"))).//
|
||||
andExpect(status().isOk());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user