feat: gestion des utilisateurs, rôles et équipes

- Ajout de la section de gestion des utilisateurs avec rôles (Admin, Trésorerie, Agent de Saisie).
- Restriction des accès par rôle (Admin a tout, Trésorerie a adhérents/paiements, Agent de saisie a adhérents/planning).
- Flux de modification obligatoire de mot de passe temporaire au premier login (par défaut AsTalange123).
- Intégration de la configuration des équipes du club.
This commit is contained in:
2026-05-30 23:11:12 +02:00
parent 7e9504ffe0
commit 8a15139efb
29 changed files with 948 additions and 35 deletions
@@ -26,6 +26,9 @@ public class AppUser {
@Column(nullable = false)
private boolean enabled = true;
@Column(name = "must_change_password", nullable = false)
private boolean mustChangePassword = false;
@ManyToMany(fetch = FetchType.EAGER)
@JoinTable(
name = "user_role",
@@ -42,6 +42,10 @@ public class CreneauEntrainement {
@JoinColumn(name = "saison_id", nullable = false)
private Saison saison;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "equipe_id")
private Equipe equipe;
@Transient
private boolean enConflit = false;
@@ -187,6 +191,14 @@ public class CreneauEntrainement {
this.saison = saison;
}
public Equipe getEquipe() {
return equipe;
}
public void setEquipe(Equipe equipe) {
this.equipe = equipe;
}
public boolean isEnConflit() {
return enConflit;
}
@@ -0,0 +1,59 @@
package com.astalange.core.entity;
import jakarta.persistence.*;
import java.util.Objects;
@Entity
@Table(name = "equipe")
public class Equipe {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false)
private String nom;
@ManyToOne(optional = false, fetch = FetchType.LAZY)
@JoinColumn(name = "categorie_id", nullable = false)
private Categorie categorie;
@ManyToOne(optional = false, fetch = FetchType.LAZY)
@JoinColumn(name = "saison_id", nullable = false)
private Saison saison;
// Constructors
public Equipe() {}
public Equipe(String nom, Categorie categorie, Saison saison) {
this.nom = nom;
this.categorie = categorie;
this.saison = saison;
}
// Getters and Setters
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public String getNom() { return nom; }
public void setNom(String nom) { this.nom = nom; }
public Categorie getCategorie() { return categorie; }
public void setCategorie(Categorie categorie) { this.categorie = categorie; }
public Saison getSaison() { return saison; }
public void setSaison(Saison saison) { this.saison = saison; }
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Equipe equipe = (Equipe) o;
return Objects.equals(id, equipe.id);
}
@Override
public int hashCode() {
return Objects.hash(id);
}
}
@@ -25,6 +25,10 @@ public class Licence {
@JoinColumn(name = "saison_id", nullable = false)
private Saison saison;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "equipe_id")
private Equipe equipe;
@Column(nullable = false, length = 50)
private String etat;
@@ -93,6 +97,9 @@ public class Licence {
public Saison getSaison() { return saison; }
public void setSaison(Saison saison) { this.saison = saison; }
public Equipe getEquipe() { return equipe; }
public void setEquipe(Equipe equipe) { this.equipe = equipe; }
public String getEtat() { return etat; }
public void setEtat(String etat) { this.etat = etat; }
@@ -0,0 +1,13 @@
package com.astalange.core.repository;
import com.astalange.core.entity.Equipe;
import com.astalange.core.entity.Saison;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public interface EquipeRepository extends JpaRepository<Equipe, Long> {
List<Equipe> findByCategorieId(Long categorieId);
List<Equipe> findBySaison(Saison saison);
}
@@ -0,0 +1,9 @@
package com.astalange.core.repository;
import com.astalange.core.entity.Role;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.Optional;
public interface RoleRepository extends JpaRepository<Role, Long> {
Optional<Role> findByName(String name);
}
@@ -0,0 +1,25 @@
package com.astalange.core.security;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.User;
import java.util.Collection;
public class CustomUserDetails extends User {
private final Long id;
private final boolean mustChangePassword;
public CustomUserDetails(Long id, String username, String password, boolean enabled, boolean mustChangePassword,
Collection<? extends GrantedAuthority> authorities) {
super(username, password, enabled, true, true, true, authorities);
this.id = id;
this.mustChangePassword = mustChangePassword;
}
public Long getId() {
return id;
}
public boolean isMustChangePassword() {
return mustChangePassword;
}
}
@@ -25,11 +25,12 @@ public class UserDetailsServiceImpl implements UserDetailsService {
AppUser appUser = userRepository.findByUsername(username)
.orElseThrow(() -> new UsernameNotFoundException("User not found"));
return new User(
return new CustomUserDetails(
appUser.getId(),
appUser.getUsername(),
appUser.getPassword(),
appUser.isEnabled(),
true, true, true,
appUser.isMustChangePassword(),
appUser.getRoles().stream()
.map(role -> new SimpleGrantedAuthority(role.getName()))
.collect(Collectors.toList())
@@ -0,0 +1,19 @@
-- V11__add_equipes.sql
-- 1. Table des équipes
CREATE TABLE equipe (
id BIGSERIAL PRIMARY KEY,
nom VARCHAR(255) NOT NULL,
categorie_id BIGINT NOT NULL,
saison_id BIGINT NOT NULL,
CONSTRAINT fk_equipe_categorie FOREIGN KEY (categorie_id) REFERENCES categorie(id) ON DELETE CASCADE,
CONSTRAINT fk_equipe_saison FOREIGN KEY (saison_id) REFERENCES saison(id) ON DELETE CASCADE
);
-- 2. Ajouter equipe_id à la table licence (nullable, facultative)
ALTER TABLE licence ADD COLUMN equipe_id BIGINT;
ALTER TABLE licence ADD CONSTRAINT fk_licence_equipe FOREIGN KEY (equipe_id) REFERENCES equipe(id) ON DELETE SET NULL;
-- 3. Ajouter equipe_id à la table creneau_entrainement (nullable, facultative)
ALTER TABLE creneau_entrainement ADD COLUMN equipe_id BIGINT;
ALTER TABLE creneau_entrainement ADD CONSTRAINT fk_creneau_equipe FOREIGN KEY (equipe_id) REFERENCES equipe(id) ON DELETE SET NULL;
@@ -0,0 +1,2 @@
ALTER TABLE app_user ADD COLUMN must_change_password BOOLEAN NOT NULL DEFAULT FALSE;
INSERT INTO role (name) VALUES ('ROLE_TRESORERIE'), ('ROLE_AGENT_SAISIE') ON CONFLICT (name) DO NOTHING;
@@ -0,0 +1,34 @@
package com.astalange.core;
import com.astalange.core.entity.*;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
public class EquipeTest {
@Test
public void testEquipeEntityAndAssociations() {
Categorie U15 = new Categorie();
U15.setNom("U15");
Equipe equipeA = new Equipe();
equipeA.setNom("Équipe 1");
equipeA.setCategorie(U15);
assertEquals("Équipe 1", equipeA.getNom());
assertEquals(U15, equipeA.getCategorie());
Licence licence = new Licence();
licence.setCategorie(U15);
licence.setEquipe(equipeA);
assertEquals(equipeA, licence.getEquipe());
CreneauEntrainement creneau = new CreneauEntrainement();
creneau.setCategorie(U15);
creneau.setEquipe(equipeA);
assertEquals(equipeA, creneau.getEquipe());
}
}