From ff3574933872d8577463dc604035c89ddf6d5160 Mon Sep 17 00:00:00 2001 From: felipe-gdr Date: Fri, 16 Dec 2016 05:26:52 -0200 Subject: [PATCH] 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 --- spring-security-core/.gitignore | 13 ++ spring-security-core/README.md | 7 + spring-security-core/pom.xml | 168 ++++++++++++++++++ .../src/main/java/org/baeldung/app/App.java | 17 ++ .../org/baeldung/config/DatabaseLoader.java | 23 +++ .../baeldung/config/WebSecurityConfig.java | 39 ++++ .../baeldung/controller/TaskController.java | 32 ++++ .../main/java/org/baeldung/entity/Task.java | 46 +++++ .../baeldung/repository/TaskRepository.java | 8 + .../org/baeldung/service/TaskService.java | 26 +++ .../test/java/org/baeldung/test/LiveTest.java | 75 ++++++++ .../src/test/resources/.gitignore | 13 ++ 12 files changed, 467 insertions(+) create mode 100644 spring-security-core/.gitignore create mode 100644 spring-security-core/README.md create mode 100644 spring-security-core/pom.xml create mode 100644 spring-security-core/src/main/java/org/baeldung/app/App.java create mode 100644 spring-security-core/src/main/java/org/baeldung/config/DatabaseLoader.java create mode 100644 spring-security-core/src/main/java/org/baeldung/config/WebSecurityConfig.java create mode 100644 spring-security-core/src/main/java/org/baeldung/controller/TaskController.java create mode 100644 spring-security-core/src/main/java/org/baeldung/entity/Task.java create mode 100644 spring-security-core/src/main/java/org/baeldung/repository/TaskRepository.java create mode 100644 spring-security-core/src/main/java/org/baeldung/service/TaskService.java create mode 100644 spring-security-core/src/test/java/org/baeldung/test/LiveTest.java create mode 100644 spring-security-core/src/test/resources/.gitignore diff --git a/spring-security-core/.gitignore b/spring-security-core/.gitignore new file mode 100644 index 0000000000..83c05e60c8 --- /dev/null +++ b/spring-security-core/.gitignore @@ -0,0 +1,13 @@ +*.class + +#folders# +/target +/neoDb* +/data +/src/main/webapp/WEB-INF/classes +*/META-INF/* + +# Packaged files # +*.jar +*.war +*.ear \ No newline at end of file diff --git a/spring-security-core/README.md b/spring-security-core/README.md new file mode 100644 index 0000000000..c7e0f645c7 --- /dev/null +++ b/spring-security-core/README.md @@ -0,0 +1,7 @@ +## @PreFilter and @PostFilter annotations + +### Build the Project ### + +``` +mvn clean install +``` diff --git a/spring-security-core/pom.xml b/spring-security-core/pom.xml new file mode 100644 index 0000000000..519ee73296 --- /dev/null +++ b/spring-security-core/pom.xml @@ -0,0 +1,168 @@ + + 4.0.0 + com.baeldung + spring-security-core + 0.1-SNAPSHOT + + spring-security-core + war + + + org.springframework.boot + spring-boot-starter-parent + 1.4.2.RELEASE + + + + + + org.springframework.boot + spring-boot-starter-data-jpa + + + org.springframework.boot + spring-boot-devtools + + + org.springframework.boot + spring-boot-starter-security + + + org.springframework.boot + spring-boot-starter-thymeleaf + + + com.h2database + h2 + runtime + + + org.springframework.boot + spring-boot-starter-test + test + + + org.springframework.security + spring-security-test + + + + + + spring-security-core + + + src/main/resources + true + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 1.8 + 1.8 + true + source + + + + + org.apache.maven.plugins + maven-war-plugin + + + + org.apache.maven.plugins + maven-surefire-plugin + + + **/*LiveTest.java + **/*ManualTest.java + + + + + + + + + + + + + + live + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + integration-test + + test + + + + none + + + **/*LiveTest.java + + + cargo + + + + + + + + + + + + + 4.3.4.RELEASE + 4.2.0.RELEASE + + + 4.4.5 + 4.5.2 + + + 1.7.21 + 1.1.7 + + + 5.3.3.Final + 1.2 + 3.1.0 + 2.8.5 + + + 19.0 + 3.5 + + + 1.3 + 4.12 + 1.10.19 + + 2.9.0 + + + 3.6.0 + 2.6 + 2.19.1 + 1.6.1 + + + diff --git a/spring-security-core/src/main/java/org/baeldung/app/App.java b/spring-security-core/src/main/java/org/baeldung/app/App.java new file mode 100644 index 0000000000..06c295fcd7 --- /dev/null +++ b/spring-security-core/src/main/java/org/baeldung/app/App.java @@ -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); + } +} diff --git a/spring-security-core/src/main/java/org/baeldung/config/DatabaseLoader.java b/spring-security-core/src/main/java/org/baeldung/config/DatabaseLoader.java new file mode 100644 index 0000000000..e311f62fff --- /dev/null +++ b/spring-security-core/src/main/java/org/baeldung/config/DatabaseLoader.java @@ -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")); + } +} \ No newline at end of file diff --git a/spring-security-core/src/main/java/org/baeldung/config/WebSecurityConfig.java b/spring-security-core/src/main/java/org/baeldung/config/WebSecurityConfig.java new file mode 100644 index 0000000000..02e60d29a2 --- /dev/null +++ b/spring-security-core/src/main/java/org/baeldung/config/WebSecurityConfig.java @@ -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"); + } +} diff --git a/spring-security-core/src/main/java/org/baeldung/controller/TaskController.java b/spring-security-core/src/main/java/org/baeldung/controller/TaskController.java new file mode 100644 index 0000000000..d99109c543 --- /dev/null +++ b/spring-security-core/src/main/java/org/baeldung/controller/TaskController.java @@ -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> findAllTasks() { + Iterable tasks = taskService.findAll(); + + return ResponseEntity.ok().body(tasks); + } + + @RequestMapping(method = RequestMethod.POST, consumes = "application/json") + public ResponseEntity> addTasks(@RequestBody Iterable newTasks) { + Iterable tasks = taskService.save(newTasks); + + return ResponseEntity.ok().body(tasks); + } +} diff --git a/spring-security-core/src/main/java/org/baeldung/entity/Task.java b/spring-security-core/src/main/java/org/baeldung/entity/Task.java new file mode 100644 index 0000000000..5d3321ef2e --- /dev/null +++ b/spring-security-core/src/main/java/org/baeldung/entity/Task.java @@ -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; + } + +} diff --git a/spring-security-core/src/main/java/org/baeldung/repository/TaskRepository.java b/spring-security-core/src/main/java/org/baeldung/repository/TaskRepository.java new file mode 100644 index 0000000000..651b11684f --- /dev/null +++ b/spring-security-core/src/main/java/org/baeldung/repository/TaskRepository.java @@ -0,0 +1,8 @@ +package org.baeldung.repository; + +import org.baeldung.entity.Task; +import org.springframework.data.repository.CrudRepository; + +public interface TaskRepository extends CrudRepository { + +} diff --git a/spring-security-core/src/main/java/org/baeldung/service/TaskService.java b/spring-security-core/src/main/java/org/baeldung/service/TaskService.java new file mode 100644 index 0000000000..4a0dae3aac --- /dev/null +++ b/spring-security-core/src/main/java/org/baeldung/service/TaskService.java @@ -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 findAll() { + return taskRepository.findAll(); + } + + @PreFilter("hasRole('MANAGER') or filterObject.assignee == authentication.name") + public Iterable save(Iterable entities) { + return taskRepository.save(entities); + } + +} diff --git a/spring-security-core/src/test/java/org/baeldung/test/LiveTest.java b/spring-security-core/src/test/java/org/baeldung/test/LiveTest.java new file mode 100644 index 0000000000..596476d058 --- /dev/null +++ b/spring-security-core/src/test/java/org/baeldung/test/LiveTest.java @@ -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'}]")); + } + +} diff --git a/spring-security-core/src/test/resources/.gitignore b/spring-security-core/src/test/resources/.gitignore new file mode 100644 index 0000000000..83c05e60c8 --- /dev/null +++ b/spring-security-core/src/test/resources/.gitignore @@ -0,0 +1,13 @@ +*.class + +#folders# +/target +/neoDb* +/data +/src/main/webapp/WEB-INF/classes +*/META-INF/* + +# Packaged files # +*.jar +*.war +*.ear \ No newline at end of file