BAEL-127: @PreFilter and @PostFilter annotations (#898)
* BAEL-127: simple app with filters * removed data rest dependency, final adjustments * added first live test for the rest api * move filters code to new module * moved to root of module, create service layer, standard pom
This commit is contained in:
parent
afa7fed038
commit
ff35749338
|
@ -0,0 +1,13 @@
|
||||||
|
*.class
|
||||||
|
|
||||||
|
#folders#
|
||||||
|
/target
|
||||||
|
/neoDb*
|
||||||
|
/data
|
||||||
|
/src/main/webapp/WEB-INF/classes
|
||||||
|
*/META-INF/*
|
||||||
|
|
||||||
|
# Packaged files #
|
||||||
|
*.jar
|
||||||
|
*.war
|
||||||
|
*.ear
|
|
@ -0,0 +1,7 @@
|
||||||
|
## @PreFilter and @PostFilter annotations
|
||||||
|
|
||||||
|
### Build the Project ###
|
||||||
|
|
||||||
|
```
|
||||||
|
mvn clean install
|
||||||
|
```
|
|
@ -0,0 +1,168 @@
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<groupId>com.baeldung</groupId>
|
||||||
|
<artifactId>spring-security-core</artifactId>
|
||||||
|
<version>0.1-SNAPSHOT</version>
|
||||||
|
|
||||||
|
<name>spring-security-core</name>
|
||||||
|
<packaging>war</packaging>
|
||||||
|
|
||||||
|
<parent>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-parent</artifactId>
|
||||||
|
<version>1.4.2.RELEASE</version>
|
||||||
|
<relativePath />
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-data-jpa</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-devtools</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-security</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-thymeleaf</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.h2database</groupId>
|
||||||
|
<artifactId>h2</artifactId>
|
||||||
|
<scope>runtime</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-test</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.security</groupId>
|
||||||
|
<artifactId>spring-security-test</artifactId>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<finalName>spring-security-core</finalName>
|
||||||
|
<resources>
|
||||||
|
<resource>
|
||||||
|
<directory>src/main/resources</directory>
|
||||||
|
<filtering>true</filtering>
|
||||||
|
</resource>
|
||||||
|
</resources>
|
||||||
|
|
||||||
|
<plugins>
|
||||||
|
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
|
<configuration>
|
||||||
|
<source>1.8</source>
|
||||||
|
<target>1.8</target>
|
||||||
|
<debug>true</debug>
|
||||||
|
<debuglevel>source</debuglevel>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-war-plugin</artifactId>
|
||||||
|
</plugin>
|
||||||
|
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-surefire-plugin</artifactId>
|
||||||
|
<configuration>
|
||||||
|
<excludes>
|
||||||
|
<exclude>**/*LiveTest.java</exclude>
|
||||||
|
<exclude>**/*ManualTest.java</exclude>
|
||||||
|
</excludes>
|
||||||
|
<systemPropertyVariables>
|
||||||
|
<!-- <provPersistenceTarget>h2</provPersistenceTarget> -->
|
||||||
|
</systemPropertyVariables>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
|
||||||
|
</plugins>
|
||||||
|
|
||||||
|
</build>
|
||||||
|
|
||||||
|
<profiles>
|
||||||
|
<profile>
|
||||||
|
<id>live</id>
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-surefire-plugin</artifactId>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<phase>integration-test</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>test</goal>
|
||||||
|
</goals>
|
||||||
|
<configuration>
|
||||||
|
<excludes>
|
||||||
|
<exclude>none</exclude>
|
||||||
|
</excludes>
|
||||||
|
<includes>
|
||||||
|
<include>**/*LiveTest.java</include>
|
||||||
|
</includes>
|
||||||
|
<systemPropertyVariables>
|
||||||
|
<webTarget>cargo</webTarget>
|
||||||
|
</systemPropertyVariables>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
</profile>
|
||||||
|
</profiles>
|
||||||
|
<properties>
|
||||||
|
<!-- Spring -->
|
||||||
|
<org.springframework.version>4.3.4.RELEASE</org.springframework.version>
|
||||||
|
<org.springframework.security.version>4.2.0.RELEASE</org.springframework.security.version>
|
||||||
|
|
||||||
|
<!-- http -->
|
||||||
|
<httpcore.version>4.4.5</httpcore.version>
|
||||||
|
<httpclient.version>4.5.2</httpclient.version>
|
||||||
|
|
||||||
|
<!-- logging -->
|
||||||
|
<org.slf4j.version>1.7.21</org.slf4j.version>
|
||||||
|
<logback.version>1.1.7</logback.version> <!-- do not upgrade - see http://jira.qos.ch/browse/LOGBACK-851 -->
|
||||||
|
|
||||||
|
<!-- various -->
|
||||||
|
<hibernate-validator.version>5.3.3.Final</hibernate-validator.version>
|
||||||
|
<jstl.version>1.2</jstl.version>
|
||||||
|
<javax.servlet.version>3.1.0</javax.servlet.version>
|
||||||
|
<jackson.version>2.8.5</jackson.version>
|
||||||
|
|
||||||
|
<!-- util -->
|
||||||
|
<guava.version>19.0</guava.version>
|
||||||
|
<commons-lang3.version>3.5</commons-lang3.version>
|
||||||
|
|
||||||
|
<!-- testing -->
|
||||||
|
<org.hamcrest.version>1.3</org.hamcrest.version>
|
||||||
|
<junit.version>4.12</junit.version>
|
||||||
|
<mockito.version>1.10.19</mockito.version>
|
||||||
|
|
||||||
|
<rest-assured.version>2.9.0</rest-assured.version>
|
||||||
|
|
||||||
|
<!-- Maven plugins -->
|
||||||
|
<maven-compiler-plugin.version>3.6.0</maven-compiler-plugin.version>
|
||||||
|
<maven-war-plugin.version>2.6</maven-war-plugin.version>
|
||||||
|
<maven-surefire-plugin.version>2.19.1</maven-surefire-plugin.version>
|
||||||
|
<cargo-maven2-plugin.version>1.6.1</cargo-maven2-plugin.version>
|
||||||
|
|
||||||
|
</properties>
|
||||||
|
</project>
|
|
@ -0,0 +1,17 @@
|
||||||
|
package org.baeldung.app;
|
||||||
|
|
||||||
|
import org.springframework.boot.SpringApplication;
|
||||||
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
import org.springframework.boot.autoconfigure.domain.EntityScan;
|
||||||
|
import org.springframework.context.annotation.ComponentScan;
|
||||||
|
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
|
||||||
|
|
||||||
|
@SpringBootApplication
|
||||||
|
@EnableJpaRepositories("org.baeldung.repository")
|
||||||
|
@ComponentScan("org.baeldung")
|
||||||
|
@EntityScan("org.baeldung.entity")
|
||||||
|
public class App {
|
||||||
|
public static void main(String[] args) {
|
||||||
|
SpringApplication.run(App.class, args);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
package org.baeldung.config;
|
||||||
|
|
||||||
|
import org.baeldung.entity.Task;
|
||||||
|
import org.baeldung.repository.TaskRepository;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.boot.CommandLineRunner;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class DatabaseLoader implements CommandLineRunner {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private TaskRepository taskRepository;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run(String... strings) throws Exception {
|
||||||
|
this.taskRepository.save(new Task("Send a fax", "pam"));
|
||||||
|
this.taskRepository.save(new Task("Print a document", "pam"));
|
||||||
|
this.taskRepository.save(new Task("Answer the phone", "pam"));
|
||||||
|
this.taskRepository.save(new Task("Call a client", "jim"));
|
||||||
|
this.taskRepository.save(new Task("Organize a meeting", "michael"));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
package org.baeldung.config;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
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;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@EnableWebSecurity
|
||||||
|
@EnableGlobalMethodSecurity(prePostEnabled = true)
|
||||||
|
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void configure(HttpSecurity http) throws Exception {
|
||||||
|
http
|
||||||
|
.authorizeRequests()
|
||||||
|
.antMatchers("/css/**", "/js/**", "/loggedout").permitAll()
|
||||||
|
.anyRequest().authenticated()
|
||||||
|
.and()
|
||||||
|
.httpBasic()
|
||||||
|
.and()
|
||||||
|
.logout().disable()
|
||||||
|
.csrf().disable();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
|
||||||
|
auth
|
||||||
|
.inMemoryAuthentication()
|
||||||
|
.withUser("jim").password("jim").roles("USER")
|
||||||
|
.and()
|
||||||
|
.withUser("pam").password("pam").roles("USER")
|
||||||
|
.and()
|
||||||
|
.withUser("michael").password("michael").roles("MANAGER");
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
package org.baeldung.controller;
|
||||||
|
|
||||||
|
import org.baeldung.entity.Task;
|
||||||
|
import org.baeldung.service.TaskService;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.stereotype.Controller;
|
||||||
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMethod;
|
||||||
|
|
||||||
|
@Controller
|
||||||
|
@RequestMapping("api/tasks")
|
||||||
|
public class TaskController {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private TaskService taskService;
|
||||||
|
|
||||||
|
@RequestMapping(method = RequestMethod.GET)
|
||||||
|
public ResponseEntity<Iterable<Task>> findAllTasks() {
|
||||||
|
Iterable<Task> tasks = taskService.findAll();
|
||||||
|
|
||||||
|
return ResponseEntity.ok().body(tasks);
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequestMapping(method = RequestMethod.POST, consumes = "application/json")
|
||||||
|
public ResponseEntity<Iterable<Task>> addTasks(@RequestBody Iterable<Task> newTasks) {
|
||||||
|
Iterable<Task> tasks = taskService.save(newTasks);
|
||||||
|
|
||||||
|
return ResponseEntity.ok().body(tasks);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,46 @@
|
||||||
|
package org.baeldung.entity;
|
||||||
|
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.GeneratedValue;
|
||||||
|
import javax.persistence.Id;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
public class Task {
|
||||||
|
private @Id @GeneratedValue Long id;
|
||||||
|
private String description;
|
||||||
|
|
||||||
|
private String assignee;
|
||||||
|
|
||||||
|
public Task() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task(String description, String assignee) {
|
||||||
|
this.description = description;
|
||||||
|
this.assignee = assignee;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(Long id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDescription() {
|
||||||
|
return description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDescription(String description) {
|
||||||
|
this.description = description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAssignee() {
|
||||||
|
return assignee;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAssignee(String assignee) {
|
||||||
|
this.assignee = assignee;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
package org.baeldung.repository;
|
||||||
|
|
||||||
|
import org.baeldung.entity.Task;
|
||||||
|
import org.springframework.data.repository.CrudRepository;
|
||||||
|
|
||||||
|
public interface TaskRepository extends CrudRepository<Task, Long> {
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
package org.baeldung.service;
|
||||||
|
|
||||||
|
import org.baeldung.entity.Task;
|
||||||
|
import org.baeldung.repository.TaskRepository;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.security.access.prepost.PostFilter;
|
||||||
|
import org.springframework.security.access.prepost.PreFilter;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class TaskService {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private TaskRepository taskRepository;
|
||||||
|
|
||||||
|
@PostFilter("hasRole('MANAGER') or filterObject.assignee == authentication.name")
|
||||||
|
public Iterable<Task> findAll() {
|
||||||
|
return taskRepository.findAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
@PreFilter("hasRole('MANAGER') or filterObject.assignee == authentication.name")
|
||||||
|
public Iterable<Task> save(Iterable<Task> entities) {
|
||||||
|
return taskRepository.save(entities);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,75 @@
|
||||||
|
package org.baeldung.test;
|
||||||
|
|
||||||
|
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.status;
|
||||||
|
|
||||||
|
import org.baeldung.app.App;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.FixMethodOrder;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.junit.runners.MethodSorters;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.security.test.context.support.WithMockUser;
|
||||||
|
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||||
|
import org.springframework.test.web.servlet.MockMvc;
|
||||||
|
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
|
||||||
|
import org.springframework.web.context.WebApplicationContext;
|
||||||
|
|
||||||
|
@RunWith(SpringJUnit4ClassRunner.class)
|
||||||
|
@SpringBootTest(classes = App.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
|
||||||
|
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
|
||||||
|
public class LiveTest {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private WebApplicationContext context;
|
||||||
|
private MockMvc mockMvc;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
mockMvc = MockMvcBuilders.webAppContextSetup(context).dispatchOptions(true).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@WithMockUser(roles = "MANAGER")
|
||||||
|
public void givenUserIsManager_whenGetTasks_thenAllTasks() throws Exception {
|
||||||
|
String allTasks = "[{'id':1,'description':'Send a fax','assignee':'pam'}," +
|
||||||
|
"{'id':2,'description':'Print a document','assignee':'pam'}," +
|
||||||
|
"{'id':3,'description':'Answer the phone','assignee':'pam'}," +
|
||||||
|
"{'id':4,'description':'Call a client','assignee':'jim'}," +
|
||||||
|
"{'id':5,'description':'Organize a meeting','assignee':'michael'}]";
|
||||||
|
|
||||||
|
mockMvc.perform(get("/api/tasks")).andExpect(status().isOk()).andExpect(content().json(allTasks));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@WithMockUser(username = "jim")
|
||||||
|
public void givenUserNotManager_whenGetTasks_thenReturnAssignedToMe() throws Exception {
|
||||||
|
String myTasks = "[{'id':4,'description':'Call a client','assignee':'jim'}]";
|
||||||
|
|
||||||
|
mockMvc.perform(get("/api/tasks")).andExpect(status().isOk()).andExpect(content().json(myTasks));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@WithMockUser(roles = "MANAGER")
|
||||||
|
public void givenUserIsManager_whenPostTasks_thenIncludeAllTasks() throws Exception {
|
||||||
|
String newTasks = "[{\"description\":\"New to Michael\",\"assignee\":\"michael\"}," +
|
||||||
|
"{\"description\":\"New to Pam\",\"assignee\":\"pam\"}]";
|
||||||
|
|
||||||
|
mockMvc.perform(post("/api/tasks").contentType(MediaType.APPLICATION_JSON).content(newTasks)).andExpect(status().isOk()).andExpect(content().json("[{'id': 6,'description':'New to Michael','assignee':'michael'}, {'id': 7,'description':'New to Pam','assignee':'pam'}]"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@WithMockUser(username = "jim")
|
||||||
|
public void givenUserNotManager_whenPostTasks_thenIncludeOnlyAssignedToMe() throws Exception {
|
||||||
|
String newTasks = "[{\"description\":\"New to Jim\",\"assignee\":\"jim\"}," +
|
||||||
|
"{\"description\":\"New to Pam\",\"assignee\":\"pam\"}]";
|
||||||
|
|
||||||
|
mockMvc.perform(post("/api/tasks").contentType(MediaType.APPLICATION_JSON).content(newTasks)).andExpect(status().isOk()).andExpect(content().json("[{'id': 8,'description':'New to Jim','assignee':'jim'}]"));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
*.class
|
||||||
|
|
||||||
|
#folders#
|
||||||
|
/target
|
||||||
|
/neoDb*
|
||||||
|
/data
|
||||||
|
/src/main/webapp/WEB-INF/classes
|
||||||
|
*/META-INF/*
|
||||||
|
|
||||||
|
# Packaged files #
|
||||||
|
*.jar
|
||||||
|
*.war
|
||||||
|
*.ear
|
Loading…
Reference in New Issue