From 0735c03154555c5946c292e111ed06dae51876cd Mon Sep 17 00:00:00 2001 From: lor6 Date: Sun, 30 Apr 2017 11:39:11 +0300 Subject: [PATCH] togglz aspect (#1743) * togglz aspect * formatting * trigger travis build --- spring-boot/pom.xml | 18 ++++ .../java/com/baeldung/toggle/Employee.java | 37 ++++++++ .../baeldung/toggle/EmployeeRepository.java | 7 ++ .../baeldung/toggle/FeatureAssociation.java | 12 +++ .../com/baeldung/toggle/FeaturesAspect.java | 27 ++++++ .../java/com/baeldung/toggle/MyFeatures.java | 23 +++++ .../com/baeldung/toggle/SalaryController.java | 20 +++++ .../com/baeldung/toggle/SalaryService.java | 19 +++++ .../com/baeldung/toggle/SecurityConfig.java | 33 ++++++++ .../baeldung/toggle/ToggleApplication.java | 17 ++++ .../baeldung/toggle/ToggleConfiguration.java | 28 +++++++ .../java/com/baeldung/toggle/ToggleTest.java | 84 +++++++++++++++++++ 12 files changed, 325 insertions(+) create mode 100644 spring-boot/src/main/java/com/baeldung/toggle/Employee.java create mode 100644 spring-boot/src/main/java/com/baeldung/toggle/EmployeeRepository.java create mode 100644 spring-boot/src/main/java/com/baeldung/toggle/FeatureAssociation.java create mode 100644 spring-boot/src/main/java/com/baeldung/toggle/FeaturesAspect.java create mode 100644 spring-boot/src/main/java/com/baeldung/toggle/MyFeatures.java create mode 100644 spring-boot/src/main/java/com/baeldung/toggle/SalaryController.java create mode 100644 spring-boot/src/main/java/com/baeldung/toggle/SalaryService.java create mode 100644 spring-boot/src/main/java/com/baeldung/toggle/SecurityConfig.java create mode 100644 spring-boot/src/main/java/com/baeldung/toggle/ToggleApplication.java create mode 100644 spring-boot/src/main/java/com/baeldung/toggle/ToggleConfiguration.java create mode 100644 spring-boot/src/test/java/com/baeldung/toggle/ToggleTest.java diff --git a/spring-boot/pom.xml b/spring-boot/pom.xml index b00af7fc94..dfecbc8e29 100644 --- a/spring-boot/pom.xml +++ b/spring-boot/pom.xml @@ -40,6 +40,11 @@ org.springframework.boot spring-boot-starter-security + + + org.springframework.security + spring-security-test + org.springframework.boot @@ -125,6 +130,18 @@ mysql-connector-java 6.0.6 + + + org.togglz + togglz-spring-boot-starter + ${togglz.version} + + + + org.togglz + togglz-spring-security + ${togglz.version} + @@ -262,6 +279,7 @@ 3.1.7 8.5.11 1.4.194 + 2.4.1.Final diff --git a/spring-boot/src/main/java/com/baeldung/toggle/Employee.java b/spring-boot/src/main/java/com/baeldung/toggle/Employee.java new file mode 100644 index 0000000000..64a8b3ce5b --- /dev/null +++ b/spring-boot/src/main/java/com/baeldung/toggle/Employee.java @@ -0,0 +1,37 @@ +package com.baeldung.toggle; + +import javax.persistence.Entity; +import javax.persistence.Id; + +@Entity +public class Employee { + + @Id + private long id; + private double salary; + + public Employee() { + } + + public Employee(long id, double salary) { + this.id = id; + this.salary = salary; + } + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public double getSalary() { + return salary; + } + + public void setSalary(double salary) { + this.salary = salary; + } + +} diff --git a/spring-boot/src/main/java/com/baeldung/toggle/EmployeeRepository.java b/spring-boot/src/main/java/com/baeldung/toggle/EmployeeRepository.java new file mode 100644 index 0000000000..7ea7c11fde --- /dev/null +++ b/spring-boot/src/main/java/com/baeldung/toggle/EmployeeRepository.java @@ -0,0 +1,7 @@ +package com.baeldung.toggle; + +import org.springframework.data.repository.CrudRepository; + +public interface EmployeeRepository extends CrudRepository{ + +} diff --git a/spring-boot/src/main/java/com/baeldung/toggle/FeatureAssociation.java b/spring-boot/src/main/java/com/baeldung/toggle/FeatureAssociation.java new file mode 100644 index 0000000000..4578b8498e --- /dev/null +++ b/spring-boot/src/main/java/com/baeldung/toggle/FeatureAssociation.java @@ -0,0 +1,12 @@ +package com.baeldung.toggle; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target({ ElementType.METHOD, ElementType.TYPE }) +public @interface FeatureAssociation { + MyFeatures value(); +} diff --git a/spring-boot/src/main/java/com/baeldung/toggle/FeaturesAspect.java b/spring-boot/src/main/java/com/baeldung/toggle/FeaturesAspect.java new file mode 100644 index 0000000000..9bc643fccc --- /dev/null +++ b/spring-boot/src/main/java/com/baeldung/toggle/FeaturesAspect.java @@ -0,0 +1,27 @@ +package com.baeldung.toggle; + +import org.apache.log4j.Logger; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.springframework.stereotype.Component; + +@Aspect +@Component +public class FeaturesAspect { + + private static final Logger LOG = Logger.getLogger(FeaturesAspect.class); + + @Around(value = "@within(featureAssociation) || @annotation(featureAssociation)") + public Object checkAspect(ProceedingJoinPoint joinPoint, FeatureAssociation featureAssociation) throws Throwable { + if (featureAssociation.value() + .isActive()) { + return joinPoint.proceed(); + } else { + LOG.info("Feature " + featureAssociation.value() + .name() + " is not enabled!"); + return null; + } + } + +} diff --git a/spring-boot/src/main/java/com/baeldung/toggle/MyFeatures.java b/spring-boot/src/main/java/com/baeldung/toggle/MyFeatures.java new file mode 100644 index 0000000000..e19d302c76 --- /dev/null +++ b/spring-boot/src/main/java/com/baeldung/toggle/MyFeatures.java @@ -0,0 +1,23 @@ +package com.baeldung.toggle; + +import org.togglz.core.Feature; +import org.togglz.core.activation.UserRoleActivationStrategy; +import org.togglz.core.annotation.ActivationParameter; +import org.togglz.core.annotation.DefaultActivationStrategy; +import org.togglz.core.annotation.EnabledByDefault; +import org.togglz.core.annotation.Label; +import org.togglz.core.context.FeatureContext; + +public enum MyFeatures implements Feature { + + @Label("Administrator Feature") + @EnabledByDefault + @DefaultActivationStrategy(id = UserRoleActivationStrategy.ID, parameters = { @ActivationParameter(name = UserRoleActivationStrategy.PARAM_ROLES_NAME, value = "ROLE_ADMIN") }) + ADMIN_FEATURE; + + public boolean isActive() { + return FeatureContext.getFeatureManager() + .isActive(this); + } + +} diff --git a/spring-boot/src/main/java/com/baeldung/toggle/SalaryController.java b/spring-boot/src/main/java/com/baeldung/toggle/SalaryController.java new file mode 100644 index 0000000000..5d72f0105a --- /dev/null +++ b/spring-boot/src/main/java/com/baeldung/toggle/SalaryController.java @@ -0,0 +1,20 @@ +package com.baeldung.toggle; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; + +@Controller +public class SalaryController { + + @Autowired + SalaryService salaryService; + + @PostMapping(value = "/increaseSalary") + @ResponseBody + public void increaseSalary(@RequestParam long id) { + salaryService.increaseSalary(id); + } +} diff --git a/spring-boot/src/main/java/com/baeldung/toggle/SalaryService.java b/spring-boot/src/main/java/com/baeldung/toggle/SalaryService.java new file mode 100644 index 0000000000..4ea4a2ab35 --- /dev/null +++ b/spring-boot/src/main/java/com/baeldung/toggle/SalaryService.java @@ -0,0 +1,19 @@ +package com.baeldung.toggle; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class SalaryService { + + @Autowired + EmployeeRepository employeeRepository; + + @FeatureAssociation(value = MyFeatures.ADMIN_FEATURE) + public void increaseSalary(long id) { + Employee employee = employeeRepository.findOne(id); + employee.setSalary(employee.getSalary() + employee.getSalary() * 0.1); + employeeRepository.save(employee); + } + +} diff --git a/spring-boot/src/main/java/com/baeldung/toggle/SecurityConfig.java b/spring-boot/src/main/java/com/baeldung/toggle/SecurityConfig.java new file mode 100644 index 0000000000..f558ccb0e1 --- /dev/null +++ b/spring-boot/src/main/java/com/baeldung/toggle/SecurityConfig.java @@ -0,0 +1,33 @@ +package com.baeldung.toggle; + +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.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; + +@Configuration +@EnableWebSecurity +public class SecurityConfig extends WebSecurityConfigurerAdapter { + + @Autowired + public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { + //@formatter:off + auth.inMemoryAuthentication() + .withUser("user").password("pass").roles("USER") + .and() + .withUser("admin").password("pass").roles("ADMIN"); + //@formatter:on + } + + @Override + public void configure(HttpSecurity http) throws Exception { + //@formatter:off + http.authorizeRequests().antMatchers("/increaseSalary").permitAll() + .and() + .csrf().disable() + .httpBasic(); + //@formatter:on + } +} diff --git a/spring-boot/src/main/java/com/baeldung/toggle/ToggleApplication.java b/spring-boot/src/main/java/com/baeldung/toggle/ToggleApplication.java new file mode 100644 index 0000000000..c269262ab2 --- /dev/null +++ b/spring-boot/src/main/java/com/baeldung/toggle/ToggleApplication.java @@ -0,0 +1,17 @@ +package com.baeldung.toggle; + +import javax.annotation.security.RolesAllowed; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +import com.baeldung.autoconfiguration.MySQLAutoconfiguration; + +@SpringBootApplication(exclude = MySQLAutoconfiguration.class) +public class ToggleApplication { + @RolesAllowed("*") + public static void main(String[] args) { + System.setProperty("security.basic.enabled", "false"); + SpringApplication.run(ToggleApplication.class, args); + } +} diff --git a/spring-boot/src/main/java/com/baeldung/toggle/ToggleConfiguration.java b/spring-boot/src/main/java/com/baeldung/toggle/ToggleConfiguration.java new file mode 100644 index 0000000000..3ae6abcbc9 --- /dev/null +++ b/spring-boot/src/main/java/com/baeldung/toggle/ToggleConfiguration.java @@ -0,0 +1,28 @@ +package com.baeldung.toggle; + +import org.springframework.boot.autoconfigure.domain.EntityScan; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.EnableAspectJAutoProxy; +import org.springframework.data.jpa.repository.config.EnableJpaRepositories; +import org.togglz.core.manager.EnumBasedFeatureProvider; +import org.togglz.core.spi.FeatureProvider; +import org.togglz.core.user.UserProvider; +import org.togglz.spring.security.SpringSecurityUserProvider; + +@Configuration +@EnableJpaRepositories("com.baeldung.toggle") +@EntityScan("com.baeldung.toggle") +public class ToggleConfiguration { + + @Bean + public FeatureProvider featureProvider() { + return new EnumBasedFeatureProvider(MyFeatures.class); + } + + @Bean + public UserProvider userProvider() { + return new SpringSecurityUserProvider("admin"); + } +} diff --git a/spring-boot/src/test/java/com/baeldung/toggle/ToggleTest.java b/spring-boot/src/test/java/com/baeldung/toggle/ToggleTest.java new file mode 100644 index 0000000000..fa51d19e3a --- /dev/null +++ b/spring-boot/src/test/java/com/baeldung/toggle/ToggleTest.java @@ -0,0 +1,84 @@ +package com.baeldung.toggle; + +import static org.junit.Assert.*; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; +import org.springframework.security.web.FilterChainProxy; +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; + +import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.*; + +@RunWith(SpringJUnit4ClassRunner.class) +@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, classes = ToggleApplication.class) +@AutoConfigureMockMvc +public class ToggleTest { + + @Autowired + SalaryService salaryService; + + @Autowired + EmployeeRepository employeeRepository; + + @Autowired + private MockMvc mvc; + + @Autowired + private WebApplicationContext wac; + + @Autowired + private FilterChainProxy springSecurityFilterChain; + + @Before + public void setup() { + this.mvc = MockMvcBuilders.webAppContextSetup(this.wac) + .addFilter(springSecurityFilterChain) + .build(); + } + + @Test + public void givenNoAuthentication_whenIncreaseSalary_thenNoIncrease() throws Exception { + Employee emp = new Employee(1, 2000); + employeeRepository.save(emp); + mvc.perform(post("/increaseSalary").param("id", emp.getId() + "")) + .andExpect(status().is(200)); + + emp = employeeRepository.findOne(1L); + assertEquals("salary incorrect", 2000, emp.getSalary(), 0.5); + } + + @Test + public void givenAdminAuthentication_whenIncreaseSalary_thenIncrease() throws Exception { + Employee emp = new Employee(1, 2000); + employeeRepository.save(emp); + mvc.perform(post("/increaseSalary").param("id", emp.getId() + "") + .with(httpBasic("admin", "pass"))) + .andExpect(status().is(200)); + + emp = employeeRepository.findOne(1L); + assertEquals("salary incorrect", 2200, emp.getSalary(), 0.5); + } + + @Test + public void givenUserAuthentication_whenIncreaseSalary_thenNoIncrease() throws Exception { + Employee emp = new Employee(1, 2000); + employeeRepository.save(emp); + mvc.perform(post("/increaseSalary").param("id", emp.getId() + "") + .with(httpBasic("user", "pass"))) + .andExpect(status().is(200)); + + emp = employeeRepository.findOne(1L); + assertEquals("salary incorrect", 2000, emp.getSalary(), 0.5); + } + +}