Browse Source

first commit of persistence & security

tripeur 4 years ago
parent
commit
dd7be15f63

+ 27 - 2
pom.xml

@@ -9,7 +9,7 @@
   </parent>
   <groupId>fr.jaquin</groupId>
   <artifactId>bdlg.planner</artifactId>
-  <version>0.0.1-SNAPSHOT</version>
+  <version>0.1.0-SNAPSHOT</version>
   <packaging>war</packaging>
   <name>bdlg.planner</name>
   <description>Plannification automatique des créneaux bénévoles pour </description>
@@ -19,11 +19,27 @@
   </properties>
 
   <dependencies>
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter-data-jpa</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter-data-rest</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>com.h2database</groupId>
+      <artifactId>h2</artifactId>
+      <scope>runtime</scope>
+    </dependency>
+
     <dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-web</artifactId>
     </dependency>
-    
+
     <dependency>
       <groupId>org.optaplanner</groupId>
       <artifactId>optaplanner-spring-boot-starter</artifactId>
@@ -50,6 +66,15 @@
         </exclusion>
       </exclusions>
     </dependency>
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter-security</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.springframework.security</groupId>
+      <artifactId>spring-security-test</artifactId>
+      <scope>test</scope>
+    </dependency>
   </dependencies>
 
   <dependencyManagement>

+ 91 - 0
src/main/java/fr/jaquin/bdlg/planner/ApiController.java

@@ -0,0 +1,91 @@
+package fr.jaquin.bdlg.planner;
+
+import java.time.LocalDateTime;
+import java.util.List;
+import org.springframework.security.core.annotation.AuthenticationPrincipal;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RestController;
+import fr.jaquin.bdlg.planner.persistence.Evenement;
+import fr.jaquin.bdlg.planner.persistence.EvenementAlreadyExistException;
+import fr.jaquin.bdlg.planner.persistence.EvenementData;
+import fr.jaquin.bdlg.planner.persistence.EvenementLob;
+import fr.jaquin.bdlg.planner.persistence.EvenementLobRepository;
+import fr.jaquin.bdlg.planner.persistence.EvenementNotFoundException;
+import fr.jaquin.bdlg.planner.persistence.EvenementRepository;
+import fr.jaquin.bdlg.planner.persistence.Users;
+
+@RestController("/api")
+public class ApiController {
+
+  private final EvenementRepository repository;
+
+  private final EvenementLobRepository repositoryLob;
+
+  public ApiController(EvenementRepository repository, EvenementLobRepository repositoryLob) {
+    this.repository = repository;
+    this.repositoryLob = repositoryLob;
+  }
+
+  @GetMapping("/evenements")
+  public List<Evenement> getEvenements() {
+    return (List<Evenement>) repository.findAll();
+  }
+
+  // Single evenement data
+  @GetMapping("/evenements/{uuid}")
+  String one(@PathVariable String uuid) {
+    List<Evenement> allVersion = repository.findByUuid(uuid);
+    if (allVersion.size() < 1) {
+      throw new EvenementNotFoundException(uuid);
+    }
+    Evenement lastVersion = allVersion.get(0);
+    for (Evenement evenement : allVersion) {
+      if (evenement.getLastModified().isAfter(evenement.getLastModified())) {
+        lastVersion = evenement;
+      }
+    }
+    return repositoryLob.findById(lastVersion.getId())
+        .orElseThrow(() -> new EvenementNotFoundException(uuid)).getJsonContent();
+  }
+
+  @GetMapping("/evenements/history/{uuid}")
+  List<Evenement> history(@PathVariable String uuid) {
+    return repository.findByUuid(uuid);
+  }
+
+  @PostMapping("/evenements")
+  Evenement newEvenement(@AuthenticationPrincipal Users customUser,
+      @RequestBody EvenementData evt) {
+    if (repository.findByUuid(evt.getUuid()).size() > 0) {
+      throw new EvenementAlreadyExistException(evt.getUuid());
+    }
+    return saveEvenementData(evt, customUser);
+  }
+
+  @PutMapping("/evenements/{uuid}")
+  Evenement newEvenement(@AuthenticationPrincipal Users customUser, @RequestBody EvenementData evt,
+      @PathVariable String id) {
+    if (repository.findByUuid(evt.getUuid()).size() == 0) {
+      throw new EvenementNotFoundException(evt.getUuid());
+    }
+    return saveEvenementData(evt, customUser);
+  }
+
+  private Evenement saveEvenementData(EvenementData evt, Users customUser) {
+    Evenement newEvenement = new Evenement();
+    newEvenement.setName(evt.getName());
+    newEvenement.setUuid(evt.getUuid());
+    newEvenement.setLastModified(LocalDateTime.now());
+    newEvenement.setLastEditor(customUser);
+    newEvenement = repository.save(newEvenement);
+    EvenementLob blob = new EvenementLob();
+    blob.setEvenement(newEvenement);
+    blob.setJsonContent(evt.getContent());
+    repositoryLob.save(blob);
+    return newEvenement;
+  }
+}

+ 0 - 5
src/main/java/fr/jaquin/bdlg/planner/AppController.java

@@ -1,5 +0,0 @@
-package fr.jaquin.bdlg.planner;
-
-public class AppController {
-  
-}

+ 76 - 0
src/main/java/fr/jaquin/bdlg/planner/persistence/Evenement.java

@@ -0,0 +1,76 @@
+package fr.jaquin.bdlg.planner.persistence;
+
+import java.time.LocalDateTime;
+import java.util.Objects;
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.OneToMany;
+
+@Entity
+public class Evenement {
+  @Id
+  private Long id;
+  private String uuid;
+  private String name;
+  private LocalDateTime lastModified;
+  @OneToMany
+  private Users lastEditor;
+
+  public Long getId() {
+    return this.id;
+  }
+
+  public void setId(Long id) {
+    this.id = id;
+  }
+
+  public String getUuid() {
+    return this.uuid;
+  }
+
+  public void setUuid(String uuid) {
+    this.uuid = uuid;
+  }
+
+  public String getName() {
+    return this.name;
+  }
+
+  public void setName(String name) {
+    this.name = name;
+  }
+
+  public LocalDateTime getLastModified() {
+    return this.lastModified;
+  }
+
+  public void setLastModified(LocalDateTime lastModified) {
+    this.lastModified = lastModified;
+  }
+
+  public Users getLastEditor() {
+    return this.lastEditor;
+  }
+
+  public void setLastEditor(Users lastEditor) {
+    this.lastEditor = lastEditor;
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (o == this)
+      return true;
+    if (!(o instanceof Evenement)) {
+      return false;
+    }
+    Evenement evenement = (Evenement) o;
+    return id == evenement.id;
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(id, uuid);
+  }
+
+
+}

+ 7 - 0
src/main/java/fr/jaquin/bdlg/planner/persistence/EvenementAlreadyExistException.java

@@ -0,0 +1,7 @@
+package fr.jaquin.bdlg.planner.persistence;
+
+public class EvenementAlreadyExistException extends RuntimeException {
+  public EvenementAlreadyExistException(String uuid) {
+    super("Evenement could not be created. uuid already exists " + uuid);
+  }
+}

+ 34 - 0
src/main/java/fr/jaquin/bdlg/planner/persistence/EvenementData.java

@@ -0,0 +1,34 @@
+package fr.jaquin.bdlg.planner.persistence;
+
+public class EvenementData {
+
+  private String uuid;
+  private String name;
+  private String content;
+
+  public EvenementData() {}
+
+  public String getUuid() {
+    return this.uuid;
+  }
+
+  public void setUuid(String uuid) {
+    this.uuid = uuid;
+  }
+
+  public String getName() {
+    return this.name;
+  }
+
+  public void setName(String name) {
+    this.name = name;
+  }
+
+  public String getContent() {
+    return this.content;
+  }
+
+  public void setContent(String content) {
+    this.content = content;
+  }
+}

+ 64 - 0
src/main/java/fr/jaquin/bdlg/planner/persistence/EvenementLob.java

@@ -0,0 +1,64 @@
+package fr.jaquin.bdlg.planner.persistence;
+
+import java.util.Objects;
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.Lob;
+import javax.persistence.MapsId;
+import javax.persistence.OneToOne;
+
+@Entity
+public class EvenementLob {
+
+  @Id
+  private Long id;
+
+  @OneToOne
+  @MapsId()
+  private Evenement evenement;
+  @Lob
+  private String jsonContent;
+
+  public EvenementLob() {}
+
+  public Long getId() {
+    return this.id;
+  }
+
+  public void setId(Long id) {
+    this.id = id;
+  }
+
+  public Evenement getEvenement() {
+    return this.evenement;
+  }
+
+  public void setEvenement(Evenement evenement) {
+    this.evenement = evenement;
+  }
+
+  public String getJsonContent() {
+    return this.jsonContent;
+  }
+
+  public void setJsonContent(String jsonContent) {
+    this.jsonContent = jsonContent;
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (o == this)
+      return true;
+    if (!(o instanceof EvenementLob)) {
+      return false;
+    }
+    EvenementLob evenementLob = (EvenementLob) o;
+    return id == evenementLob.id;
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hashCode(id);
+  }
+
+}

+ 7 - 0
src/main/java/fr/jaquin/bdlg/planner/persistence/EvenementLobRepository.java

@@ -0,0 +1,7 @@
+package fr.jaquin.bdlg.planner.persistence;
+
+import org.springframework.data.repository.CrudRepository;
+
+public interface EvenementLobRepository extends CrudRepository<EvenementLob, Long> {
+
+}

+ 7 - 0
src/main/java/fr/jaquin/bdlg/planner/persistence/EvenementNotFoundException.java

@@ -0,0 +1,7 @@
+package fr.jaquin.bdlg.planner.persistence;
+
+public class EvenementNotFoundException extends RuntimeException {
+  public EvenementNotFoundException(String uuid) {
+    super("Could not find evenement " + uuid);
+  }
+}

+ 14 - 0
src/main/java/fr/jaquin/bdlg/planner/persistence/EvenementRepository.java

@@ -0,0 +1,14 @@
+package fr.jaquin.bdlg.planner.persistence;
+
+import java.util.List;
+import java.util.Optional;
+import org.springframework.data.repository.CrudRepository;
+
+public interface EvenementRepository extends CrudRepository<Evenement, Long> {
+  List<Evenement> findByUuid(String uuid);
+
+  Optional<Evenement> findById(Long id);
+
+  Evenement lastVersionByUuid(String uuid);
+
+}

+ 7 - 0
src/main/java/fr/jaquin/bdlg/planner/persistence/UserRepository.java

@@ -0,0 +1,7 @@
+package fr.jaquin.bdlg.planner.persistence;
+
+import org.springframework.data.repository.CrudRepository;
+
+public interface UserRepository extends CrudRepository<Users, Long> {
+  Users findByName(String name);
+}

+ 71 - 0
src/main/java/fr/jaquin/bdlg/planner/persistence/Users.java

@@ -0,0 +1,71 @@
+package fr.jaquin.bdlg.planner.persistence;
+
+import java.util.Objects;
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+
+@Entity
+public class Users {
+
+  @Id
+  @GeneratedValue(strategy = GenerationType.AUTO)
+  private Long id;
+  @Column(unique = true, nullable = false)
+  private String name;
+  private String email;
+  private String password;
+
+  public Users() {}
+
+  public Long getId() {
+    return this.id;
+  }
+
+  public void setId(Long id) {
+    this.id = id;
+  }
+
+  public String getName() {
+    return this.name;
+  }
+
+  public void setName(String name) {
+    this.name = name;
+  }
+
+  public String getEmail() {
+    return this.email;
+  }
+
+  public void setEmail(String email) {
+    this.email = email;
+  }
+
+  public String getPassword() {
+    return this.password;
+  }
+
+  public void setPassword(String password) {
+    this.password = password;
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (o == this)
+      return true;
+    if (!(o instanceof Users)) {
+      return false;
+    }
+    Users users = (Users) o;
+    return id == users.id;
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(id, name);
+  }
+
+}

+ 28 - 0
src/main/java/fr/jaquin/bdlg/planner/security/CustomUserDetailService.java

@@ -0,0 +1,28 @@
+package fr.jaquin.bdlg.planner.security;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.core.userdetails.User;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.security.core.userdetails.UsernameNotFoundException;
+import org.springframework.stereotype.Service;
+import fr.jaquin.bdlg.planner.persistence.UserRepository;
+import fr.jaquin.bdlg.planner.persistence.Users;
+
+@Service
+public class CustomUserDetailService implements UserDetailsService {
+
+  @Autowired
+  private UserRepository userRepository;
+
+  @Override
+  public UserDetails loadUserByUsername(String login) throws UsernameNotFoundException {
+    final Users customUser = userRepository.findByName(login);
+    if (customUser == null) {
+      throw new UsernameNotFoundException(login);
+    }
+    UserDetails user = User.withUsername(customUser.getEmail()).password(customUser.getPassword())
+        .authorities("USER").build();
+    return user;
+  }
+}

+ 61 - 0
src/main/java/fr/jaquin/bdlg/planner/security/WebSecurityConfig.java

@@ -0,0 +1,61 @@
+package fr.jaquin.bdlg.planner.security;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.http.HttpMethod;
+import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
+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;
+import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
+import org.springframework.security.crypto.password.PasswordEncoder;
+
+import javax.annotation.Resource;
+
+@Configuration
+@EnableWebSecurity
+public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
+
+  @Resource
+  private UserDetailsService userDetailsService;
+
+  @Override
+  protected void configure(HttpSecurity http) throws Exception {
+    // @formatter:off
+      http
+        .authorizeRequests()
+          .antMatchers(HttpMethod.GET,"/**").permitAll() // (3)
+          .antMatchers(HttpMethod.POST, "/api/evenements").authenticated()
+          .antMatchers(HttpMethod.PUT, "/api/evenements/**").authenticated() // (4)
+          .and()
+       .formLogin() 
+         .loginPage("/login") 
+         .permitAll()
+         .and()
+      .logout() // (6)
+        .permitAll()
+        .and()
+      .httpBasic(); // (7)
+      // @formatter:on
+  }
+
+  @Bean
+  public DaoAuthenticationProvider authProvider() {
+    DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
+    authProvider.setUserDetailsService(userDetailsService);
+    authProvider.setPasswordEncoder(passwordEncoder());
+    return authProvider;
+  }
+
+  @Bean
+  public PasswordEncoder passwordEncoder() {
+    return new BCryptPasswordEncoder();
+  }
+
+  @Override
+  protected void configure(AuthenticationManagerBuilder auth) throws Exception {
+    auth.authenticationProvider(authProvider());
+  }
+}