tripeur 4 роки тому
батько
коміт
3236700954

+ 7 - 8
src/main/java/fr/jaquin/bdlg/planner/ApiController.java → src/main/java/fr/jaquin/bdlg/planner/controller/ApiController.java

@@ -1,4 +1,4 @@
-package fr.jaquin.bdlg.planner;
+package fr.jaquin.bdlg.planner.controller;
 
 import java.time.LocalDateTime;
 import java.util.List;
@@ -16,7 +16,7 @@ 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;
+import fr.jaquin.bdlg.planner.persistence.CustomUser;
 
 @RestController("/api")
 public class ApiController {
@@ -48,8 +48,8 @@ public class ApiController {
         lastVersion = evenement;
       }
     }
-    return repositoryLob.findById(lastVersion.getId())
-        .orElseThrow(() -> new EvenementNotFoundException(uuid)).getJsonContent();
+    return repositoryLob.findById(lastVersion.getId()).orElseThrow(() -> new EvenementNotFoundException(uuid))
+        .getJsonContent();
   }
 
   @GetMapping("/evenements/history/{uuid}")
@@ -58,8 +58,7 @@ public class ApiController {
   }
 
   @PostMapping("/evenements")
-  Evenement newEvenement(@AuthenticationPrincipal Users customUser,
-      @RequestBody EvenementData evt) {
+  Evenement newEvenement(@AuthenticationPrincipal CustomUser customUser, @RequestBody EvenementData evt) {
     if (repository.findByUuid(evt.getUuid()).size() > 0) {
       throw new EvenementAlreadyExistException(evt.getUuid());
     }
@@ -67,7 +66,7 @@ public class ApiController {
   }
 
   @PutMapping("/evenements/{uuid}")
-  Evenement newEvenement(@AuthenticationPrincipal Users customUser, @RequestBody EvenementData evt,
+  Evenement newEvenement(@AuthenticationPrincipal CustomUser customUser, @RequestBody EvenementData evt,
       @PathVariable String id) {
     if (repository.findByUuid(evt.getUuid()).size() == 0) {
       throw new EvenementNotFoundException(evt.getUuid());
@@ -75,7 +74,7 @@ public class ApiController {
     return saveEvenementData(evt, customUser);
   }
 
-  private Evenement saveEvenementData(EvenementData evt, Users customUser) {
+  private Evenement saveEvenementData(EvenementData evt, CustomUser customUser) {
     Evenement newEvenement = new Evenement();
     newEvenement.setName(evt.getName());
     newEvenement.setUuid(evt.getUuid());

+ 13 - 0
src/main/java/fr/jaquin/bdlg/planner/controller/PageController.java

@@ -0,0 +1,13 @@
+package fr.jaquin.bdlg.planner.controller;
+
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.GetMapping;
+
+@Controller
+class PageController {
+
+  @GetMapping(value = "/{path:[^\\.]*}")
+  public String redirect() {
+    return "forward:/";
+  }
+}

+ 8 - 10
src/main/java/fr/jaquin/bdlg/planner/SolverController.java → src/main/java/fr/jaquin/bdlg/planner/controller/SolverController.java

@@ -1,4 +1,4 @@
-package fr.jaquin.bdlg.planner;
+package fr.jaquin.bdlg.planner.controller;
 
 import java.util.UUID;
 import java.util.concurrent.ExecutionException;
@@ -50,8 +50,7 @@ public class SolverController {
       throw new IllegalStateException("Solving failed.", e);
     }
 
-    ScoreExplanation<Planning, HardMediumSoftScore> explanation =
-        scoreManager.explainScore(solution);
+    ScoreExplanation<Planning, HardMediumSoftScore> explanation = scoreManager.explainScore(solution);
     PlanningSolution output = PlanningSolution.from(solution);
     output.setExplanation(stringifyExplanation(explanation));
     System.out.println(explanation.getSummary());
@@ -67,9 +66,8 @@ public class SolverController {
         // Populate the value with an array of justification objects
             + constraint.getConstraintMatchSet().stream()
                 // Filter pair that have a negative impact on the score
-                .filter(matchElt -> matchElt.getScore().getHardScore() < 0)
-                .map(match -> match.getJustificationList().stream()
-                    .map(elt -> stringifyConstraint(elt)).collect(listCollector))
+                .filter(matchElt -> matchElt.getScore().getHardScore() < 0).map(match -> match.getJustificationList()
+                    .stream().map(elt -> stringifyConstraint(elt)).collect(listCollector))
                 .collect(listCollector))
         .collect(Collectors.joining(",", "{", "}"));
   }
@@ -77,13 +75,13 @@ public class SolverController {
   private String stringifyConstraint(Object val) {
     if (val instanceof Assignement) {
       Assignement casted = (Assignement) val;
-      return "{\"type\":\"Assignement\",\"slotId\":\"" + casted.getSlot().getId()
-          + "\",\"volonteerId\":" + casted.getVolonteer().getId().toString() + "}";
+      return "{\"type\":\"Assignement\",\"slotId\":\"" + casted.getSlot().getId() + "\",\"volonteerId\":"
+          + casted.getVolonteer().getId().toString() + "}";
     }
     if (val instanceof MealAssignement) {
       MealAssignement casted = (MealAssignement) val;
-      return "{\"type\":\"MealAssignement\",\"slotId\":\"" + casted.getMealSlot().getId()
-          + "\",\"volonteerId\":" + casted.getVolonteer().getId().toString() + "}";
+      return "{\"type\":\"MealAssignement\",\"slotId\":\"" + casted.getMealSlot().getId() + "\",\"volonteerId\":"
+          + casted.getVolonteer().getId().toString() + "}";
     }
     if (val instanceof MealSlot) {
       MealSlot casted = (MealSlot) val;

+ 25 - 11
src/main/java/fr/jaquin/bdlg/planner/persistence/Users.java → src/main/java/fr/jaquin/bdlg/planner/persistence/CustomUser.java

@@ -7,18 +7,27 @@ import javax.persistence.GeneratedValue;
 import javax.persistence.GenerationType;
 import javax.persistence.Id;
 
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
+
 @Entity
-public class Users {
+public class CustomUser {
 
   @Id
   @GeneratedValue(strategy = GenerationType.AUTO)
   private Long id;
   @Column(unique = true, nullable = false)
-  private String name;
+  private String username;
+
   private String email;
   private String password;
 
-  public Users() {}
+  public CustomUser() {
+  }
+
+  public CustomUser(String username, String password) {
+    this.username = username;
+    this.setPassword(password);
+  }
 
   public Long getId() {
     return this.id;
@@ -28,12 +37,12 @@ public class Users {
     this.id = id;
   }
 
-  public String getName() {
-    return this.name;
+  public String getUsername() {
+    return this.username;
   }
 
-  public void setName(String name) {
-    this.name = name;
+  public void setUsername(String username) {
+    this.username = username;
   }
 
   public String getEmail() {
@@ -49,23 +58,28 @@ public class Users {
   }
 
   public void setPassword(String password) {
-    this.password = password;
+    this.password = new BCryptPasswordEncoder().encode(password);
   }
 
   @Override
   public boolean equals(Object o) {
     if (o == this)
       return true;
-    if (!(o instanceof Users)) {
+    if (!(o instanceof CustomUser)) {
       return false;
     }
-    Users users = (Users) o;
+    CustomUser users = (CustomUser) o;
     return id == users.id;
   }
 
   @Override
   public int hashCode() {
-    return Objects.hash(id, name);
+    return Objects.hash(id, username);
+  }
+
+  @Override
+  public String toString() {
+    return "User<name=" + username + ">";
   }
 
 }

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

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

+ 5 - 6
src/main/java/fr/jaquin/bdlg/planner/persistence/Evenement.java

@@ -4,7 +4,7 @@ import java.time.LocalDateTime;
 import java.util.Objects;
 import javax.persistence.Entity;
 import javax.persistence.Id;
-import javax.persistence.OneToMany;
+import javax.persistence.ManyToOne;
 
 @Entity
 public class Evenement {
@@ -13,8 +13,8 @@ public class Evenement {
   private String uuid;
   private String name;
   private LocalDateTime lastModified;
-  @OneToMany
-  private Users lastEditor;
+  @ManyToOne
+  private CustomUser lastEditor;
 
   public Long getId() {
     return this.id;
@@ -48,11 +48,11 @@ public class Evenement {
     this.lastModified = lastModified;
   }
 
-  public Users getLastEditor() {
+  public CustomUser getLastEditor() {
     return this.lastEditor;
   }
 
-  public void setLastEditor(Users lastEditor) {
+  public void setLastEditor(CustomUser lastEditor) {
     this.lastEditor = lastEditor;
   }
 
@@ -72,5 +72,4 @@ public class Evenement {
     return Objects.hash(id, uuid);
   }
 
-
 }

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

@@ -9,6 +9,4 @@ public interface EvenementRepository extends CrudRepository<Evenement, Long> {
 
   Optional<Evenement> findById(Long id);
 
-  Evenement lastVersionByUuid(String uuid);
-
 }

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

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

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

@@ -6,23 +6,23 @@ 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;
+import fr.jaquin.bdlg.planner.persistence.CustomUserRepository;
+import fr.jaquin.bdlg.planner.persistence.CustomUser;
 
 @Service
 public class CustomUserDetailService implements UserDetailsService {
 
   @Autowired
-  private UserRepository userRepository;
+  private CustomUserRepository userRepository;
 
   @Override
   public UserDetails loadUserByUsername(String login) throws UsernameNotFoundException {
-    final Users customUser = userRepository.findByName(login);
+    final CustomUser customUser = userRepository.findByUsername(login);
     if (customUser == null) {
       throw new UsernameNotFoundException(login);
     }
     UserDetails user = User.withUsername(customUser.getEmail()).password(customUser.getPassword())
-        .authorities("USER").build();
+        .authorities("ROLE_USER").build();
     return user;
   }
 }

+ 23 - 0
src/main/java/fr/jaquin/bdlg/planner/security/LoadFixedUser.java

@@ -0,0 +1,23 @@
+package fr.jaquin.bdlg.planner.security;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.boot.CommandLineRunner;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import fr.jaquin.bdlg.planner.persistence.CustomUser;
+import fr.jaquin.bdlg.planner.persistence.CustomUserRepository;
+
+@Configuration
+public class LoadFixedUser {
+  private static final Logger log = LoggerFactory.getLogger(LoadFixedUser.class);
+
+  @Bean
+  CommandLineRunner initDatabase(CustomUserRepository repository) {
+    return args -> {
+      log.info("Preloading " + repository.save(new CustomUser("admin", "notAnAdmin")));
+      log.info("Preloading " + repository.save(new CustomUser("clovis", "M1m2pBP.")));
+    };
+  }
+}

+ 7 - 6
src/main/java/fr/jaquin/bdlg/planner/security/WebSecurityConfig.java

@@ -26,19 +26,20 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
     // @formatter:off
       http
         .authorizeRequests()
-          .antMatchers(HttpMethod.GET,"/**").permitAll() // (3)
-          .antMatchers(HttpMethod.POST, "/api/evenements").authenticated()
-          .antMatchers(HttpMethod.PUT, "/api/evenements/**").authenticated() // (4)
+          .antMatchers(HttpMethod.POST, "/api/evenements").hasRole("USER")
+          .antMatchers(HttpMethod.PUT, "/api/evenements/**").hasRole("USER")
+          .antMatchers(HttpMethod.GET,"/**").permitAll()
+          .antMatchers(HttpMethod.POST, "/planning/solve").permitAll()
           .and()
        .formLogin() 
          .loginPage("/login") 
          .permitAll()
          .and()
-      .logout() // (6)
+      .logout() 
         .permitAll()
         .and()
-      .httpBasic(); // (7)
-      // @formatter:on
+      .httpBasic(); 
+    // @formatter:on
   }
 
   @Bean

+ 15 - 20
src/main/java/fr/jaquin/bdlg/planner/solver/PlanningConstraintProvider.java

@@ -13,8 +13,7 @@ import org.optaplanner.core.api.score.stream.uni.UniConstraintStream;
 
 public class PlanningConstraintProvider implements ConstraintProvider {
 
-  private static UniConstraintStream<Assignement> getAssignedSlotStream(
-      ConstraintFactory constraintFactory) {
+  private static UniConstraintStream<Assignement> getAssignedSlotStream(ConstraintFactory constraintFactory) {
     return constraintFactory.fromUnfiltered(Assignement.class) // To match DRL
         .filter(shift -> shift.getVolonteer() != null);
   }
@@ -29,18 +28,16 @@ public class PlanningConstraintProvider implements ConstraintProvider {
         // Soft constraints are only implemented in the "complete"
         // implementation
         volunteerMinRestTime(constraintFactory), competencyTeachingEffort(constraintFactory),
-        preferenceApplication(constraintFactory), balanceLoad(constraintFactory)};
+        preferenceApplication(constraintFactory), balanceLoad(constraintFactory) };
   }
 
   private Constraint completeAllTimeslot(ConstraintFactory constraintFactory) {
-    return constraintFactory.from(Assignement.class)
-        .filter((assignment) -> assignment.getVolonteer() == null)
+    return constraintFactory.from(Assignement.class).filter((assignment) -> assignment.getVolonteer() == null)
         .penalize("Timeslot not initialized", HardMediumSoftScore.ONE_HARD);
   }
 
   private Constraint feedAllVolunteer(ConstraintFactory constraintFactory) {
-    return constraintFactory.from(MealAssignement.class)
-        .filter((assignment) -> assignment.getMealSlot() == null)
+    return constraintFactory.from(MealAssignement.class).filter((assignment) -> assignment.getMealSlot() == null)
         .penalize("Meal not initialized", HardMediumSoftScore.ONE_HARD);
   }
 
@@ -97,22 +94,22 @@ public class PlanningConstraintProvider implements ConstraintProvider {
 
   private Constraint competencyConflict(ConstraintFactory constraintFactory) {
     // a volonteer must match required competencies.
-    return getAssignedSlotStream(constraintFactory).penalize("Competence conflict",
-        HardMediumSoftScore.ONE_HARD, Assignement::getCompetenceLackScore);
+    return getAssignedSlotStream(constraintFactory).penalize("Competence conflict", HardMediumSoftScore.ONE_HARD,
+        Assignement::getCompetenceLackScore);
   }
 
   private Constraint competencyTeachingEffort(ConstraintFactory constraintFactory) {
     // We favor volonteer that already have required competencies.
-    // TODO: Idealy you should group by per volonteer and computed missing teachable competencies
-    return getAssignedSlotStream(constraintFactory).penalize(
-        "Avoid teaching competences to volunteer", HardMediumSoftScore.ONE_SOFT,
-        Assignement::getSoftCompetenceLackScore);
+    // TODO: Idealy you should group by per volonteer and computed missing teachable
+    // competencies
+    return getAssignedSlotStream(constraintFactory).penalize("Avoid teaching competences to volunteer",
+        HardMediumSoftScore.ONE_SOFT, Assignement::getSoftCompetenceLackScore);
   }
 
   private Constraint preferenceApplication(ConstraintFactory constraintFactory) {
     // The system should favor timeslots selected by user.
-    return getAssignedSlotStream(constraintFactory).reward("Preference application",
-        HardMediumSoftScore.ONE_SOFT, Assignement::getPreferenceScore);
+    return getAssignedSlotStream(constraintFactory).reward("Preference application", HardMediumSoftScore.ONE_SOFT,
+        Assignement::getPreferenceScore);
   }
 
   private Constraint balanceLoad(ConstraintFactory constraintFactory) {
@@ -128,13 +125,11 @@ public class PlanningConstraintProvider implements ConstraintProvider {
 
   private Constraint mealMaxAttendee(ConstraintFactory constraintFactory) {
     // Each Meal slot can only accomodate a maximum of attendee
-    return constraintFactory.from(MealAssignement.class)
-        .filter(assignement -> assignement.getMealSlot() != null)
+    return constraintFactory.from(MealAssignement.class).filter(assignement -> assignement.getMealSlot() != null)
         .groupBy(MealAssignement::getMealSlot, ConstraintCollectors.count())
         .filter((slot, volonteerCount) -> slot.getMaxAttendee() < volonteerCount)
         // then penalize each pair with a hard weight.
-        .penalize("Meal max attendee", HardMediumSoftScore.ONE_HARD,
-            (slot, volonteerCount) -> (slot.getMaxAttendee() - volonteerCount)
-                * (slot.getMaxAttendee() - volonteerCount));
+        .penalize("Meal max attendee", HardMediumSoftScore.ONE_HARD, (slot,
+            volonteerCount) -> (slot.getMaxAttendee() - volonteerCount) * (slot.getMaxAttendee() - volonteerCount));
   }
 }