FEAT : Ajout du mécanisme READ, COMMENT et ADMIN pour les photos à partager

This commit is contained in:
2025-12-03 08:36:16 +01:00
parent 6a3569029d
commit 6c3ea128af
12 changed files with 127 additions and 74 deletions

View File

@@ -148,6 +148,11 @@ public class PhotoController {
model.addAttribute("currentPage", page); model.addAttribute("currentPage", page);
String currentUser = (auth != null ? auth.getName() : null); String currentUser = (auth != null ? auth.getName() : null);
boolean canComment = partageService.canComment(id, currentUser);
boolean canAdmin = partageService.canAdmin(id, currentUser);
model.addAttribute("canComment", canComment);
model.addAttribute("canAdmin", canAdmin);
model.addAttribute("currentUser", currentUser); model.addAttribute("currentUser", currentUser);
model.addAttribute("partages", model.addAttribute("partages",
@@ -181,6 +186,7 @@ public class PhotoController {
} }
@GetMapping("/photo/{id}/unshare/{email}") @GetMapping("/photo/{id}/unshare/{email}")
public String unshare(@PathVariable Long id, @PathVariable String email) { public String unshare(@PathVariable Long id, @PathVariable String email) {

View File

@@ -1,16 +1,15 @@
package local.epul4a.fotosharing.repository; package local.epul4a.fotosharing.repository;
import local.epul4a.fotosharing.model.Partage; import local.epul4a.fotosharing.model.Partage;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List; import java.util.List;
import java.util.Optional;
public interface PartageRepository extends JpaRepository<Partage, Long> { public interface PartageRepository extends JpaRepository<Partage, Long> {
// liste des partages pour une photo
List<Partage> findByPhoto_Id(Long photoId); List<Partage> findByPhoto_Id(Long photoId);
// vérifier si un utilisateur a accès partagé
boolean existsByPhoto_IdAndUtilisateur_Email(Long photoId, String email); boolean existsByPhoto_IdAndUtilisateur_Email(Long photoId, String email);
List<Partage> findByUtilisateur_Email(String email); List<Partage> findByUtilisateur_Email(String email);
Optional<Partage> findByPhoto_IdAndUtilisateur_Email(Long photoId, String email);
} }

View File

@@ -1,48 +1,23 @@
package local.epul4a.fotosharing.security; package local.epul4a.fotosharing.security;
import local.epul4a.fotosharing.model.Photo; import local.epul4a.fotosharing.service.PartageService;
import local.epul4a.fotosharing.repository.PartageRepository;
import local.epul4a.fotosharing.repository.PhotoRepository;
import org.springframework.stereotype.Service;
import javax.naming.ldap.PagedResultsControl;
import java.util.Optional;
import org.springframework.security.core.Authentication; import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Service;
@Service("securityService") @Service("securityService")
public class SecurityService { public class SecurityService {
private final PhotoRepository photoRepository; private final PartageService partageService;
private final PartageRepository partageRepository; public SecurityService(PartageService partageService) {
this.partageService = partageService;
public SecurityService(PhotoRepository photoRepository, PartageRepository partageRepository) {
this.photoRepository = photoRepository;
this.partageRepository = partageRepository;
} }
public boolean canAccessPhoto(Authentication authentication, Long photoId) { public boolean canAccessPhoto(Authentication authentication, Long photoId) {
Photo photo = photoRepository.findById(photoId).orElse(null);
if (photo == null) return false;
// PUBLIC → accès total
if (photo.getVisibilite() == Photo.Visibilite.PUBLIC) {
return true;
}
// Pas connecté → rejeter tout sauf PUBLIC
if (authentication == null || !authentication.isAuthenticated()) { if (authentication == null || !authentication.isAuthenticated()) {
return false; return false;
} }
String email = authentication.getName(); String email = authentication.getName();
// Propriétaire → OK // Vérification basée sur les ACL (READ / COMMENT / ADMIN)
if (photo.getProprietaire().getEmail().equals(email)) { return partageService.canView(photoId, email);
return true;
}
// SHARED → vérifier dans la table PARTAGE
if (photo.getVisibilite() == Photo.Visibilite.SHARED) {
return partageRepository.existsByPhoto_IdAndUtilisateur_Email(photoId, email);
}
// PRIVATE par défaut → refus
return false;
} }
} }

View File

@@ -1,10 +1,17 @@
package local.epul4a.fotosharing.service; package local.epul4a.fotosharing.service;
import local.epul4a.fotosharing.dto.PartageDTO; import local.epul4a.fotosharing.dto.PartageDTO;
import local.epul4a.fotosharing.model.Partage;
import java.util.List; import java.util.List;
import java.util.Optional;
public interface PartageService { public interface PartageService {
void share(Long photoId, String targetEmail, String permission, String ownerEmail); void share(Long photoId, String targetEmail, String permission, String ownerEmail);
void unshare(Long photoId, String targetEmail); void unshare(Long photoId, String targetEmail);
List<PartageDTO> getPartagesForPhoto(Long photoId); List<PartageDTO> getPartagesForPhoto(Long photoId);
boolean canView(Long photoId, String email);
boolean canComment(Long photoId, String email);
boolean canAdmin(Long photoId, String email);
} }

View File

@@ -75,4 +75,50 @@ public class PartageServiceImpl implements PartageService {
if (partage != null) if (partage != null)
partageRepository.delete(partage); partageRepository.delete(partage);
} }
@Override
public boolean canView(Long photoId, String email) {
Photo photo = photoRepository.findById(photoId).orElse(null);
if (photo == null) return false;
// Propriétaire => accès total
if (photo.getProprietaire().getEmail().equals(email))
return true;
// Photo publique => tout le monde peut voir
if (photo.getVisibilite() == Photo.Visibilite.PUBLIC)
return true;
Partage partage = partageRepository
.findByPhoto_IdAndUtilisateur_Email(photoId, email)
.orElse(null);
return partage != null; // READ / COMMENT / ADMIN
}
@Override
public boolean canComment(Long photoId, String email) {
Photo photo = photoRepository.findById(photoId).orElse(null);
if (photo == null) return false;
// propriétaire = admin total
if (photo.getProprietaire().getEmail().equals(email))
return true;
Partage partage = partageRepository
.findByPhoto_IdAndUtilisateur_Email(photoId, email)
.orElse(null);
if (partage == null) return false;
return partage.getPermission() == Partage.Permission.COMMENT
|| partage.getPermission() == Partage.Permission.ADMIN;
}
@Override
public boolean canAdmin(Long photoId, String email) {
Photo photo = photoRepository.findById(photoId).orElse(null);
if (photo == null) return false;
// propriétaire = admin total
if (photo.getProprietaire().getEmail().equals(email))
return true;
Partage partage = partageRepository
.findByPhoto_IdAndUtilisateur_Email(photoId, email)
.orElse(null);
if (partage == null) return false;
return partage.getPermission() == Partage.Permission.ADMIN;
}
} }

View File

@@ -23,6 +23,7 @@
<!-- Infos --> <!-- Infos -->
<ul> <ul>
<li><strong>Votre rôle :</strong><span th:text="${photo.proprietaire.email == currentUser ? 'Propriétaire' : (canAdmin ? 'Admin' : (canComment ? 'Commentateur' : 'Lecteur'))}"></span></li>
<li><strong>Nom original :</strong> <span th:text="${photo.nomFichierOriginal}"></span></li> <li><strong>Nom original :</strong> <span th:text="${photo.nomFichierOriginal}"></span></li>
<li><strong>Date upload :</strong> <span th:text="${photo.dateUpload}"></span></li> <li><strong>Date upload :</strong> <span th:text="${photo.dateUpload}"></span></li>
<li><strong>Visibilité :</strong> <span th:text="${photo.visibilite}"></span></li> <li><strong>Visibilité :</strong> <span th:text="${photo.visibilite}"></span></li>
@@ -34,31 +35,35 @@
<ul> <ul>
<li th:each="p : ${partages}"> <li th:each="p : ${partages}">
<span th:text="${p.utilisateur.email}"></span> <span th:text="${p.utilisateur.email}"></span>
<span> - <b th:text="${p.permission}"></b></span>
<span th:text="${p.permission}"></span> <!-- ADMIN OU PROPRIÉTAIRE : peut retirer -->
<a th:href="@{'/photo/' + ${photo.id} + '/unshare/' + ${p.utilisateur.email}}" <a th:if="${canAdmin}"
style="color:red;">Retirer</a> th:href="@{'/photo/' + ${photo.id} + '/unshare/' + ${p.utilisateur.email}}"
style="color:red; margin-left:10px;">Retirer</a>
</li> </li>
</ul> </ul>
<h2>Partager la photo</h2>
<div th:if="${currentUser == photo.proprietaire.email}"> <!-- Formulaire dajout visible uniquement en ADMIN ou PROPRIÉTAIRE -->
<div th:if="${canAdmin}">
<h3>Partager la photo</h3>
<form th:action="@{'/photo/' + ${photo.id} + '/share'}" method="post"> <form th:action="@{'/photo/' + ${photo.id} + '/share'}" method="post">
<label>Email de l'utilisateur :</label> <label>Email de l'utilisateur :</label>
<input type="email" name="email" required /> <input type="email" name="email" required />
<label>Permission :</label> <label>Permission :</label>
<select name="permission"> <select name="permission">
<option value="READ">Lecture</option> <option value="READ">Lecture seule</option>
<option value="COMMENT">Commenter</option> <option value="COMMENT">Commentaire autorisé</option>
<option value="ADMIN">Administrer</option> <option value="ADMIN">Administrateur</option>
</select> </select>
<button type="submit">Partager</button> <button type="submit">Partager</button>
</form> </form>
</div> </div>
<div th:if="${currentUser != photo.proprietaire.email}"> <div th:if="${!canAdmin}">
<em>Seul le propriétaire peut partager cette photo.</em> <em>Vous navez pas les droits de gestion du partage.</em>
</div> </div>
<!-- Commentaires --> <!-- Commentaires -->
<h2>Commentaires</h2> <h2>Commentaires</h2>
<div th:each="c : ${commentairesPage.content}"> <div th:each="c : ${commentairesPage.content}">
@@ -76,17 +81,22 @@
</div> </div>
<!-- Formulaire d'ajout de commentaire --> <!-- Formulaire d'ajout de commentaire -->
<div th:if="${currentUser}"> <!-- Si pas connecté -->
<h3>Ajouter un commentaire</h3>
<form th:action="@{'/photo/' + ${photo.id} + '/comment'}" method="post">
<textarea name="contenu" rows="3" cols="50"></textarea><br/>
<button type="submit">Envoyer</button>
</form>
</div>
<div th:if="${currentUser == null}"> <div th:if="${currentUser == null}">
<p><a th:href="@{/login}">Connectez-vous</a> pour commenter.</p> <p><a th:href="@{/login}">Connectez-vous</a> pour commenter.</p>
</div> </div>
<!-- Si connecté mais pas autorisé -->
<div th:if="${currentUser != null and !canComment}">
<em>Vous pouvez consulter cette photo, mais pas commenter.</em>
</div>
<!-- Si COMMENT ou ADMIN ou PROPRIÉTAIRE -->
<div th:if="${canComment}">
<h3>Ajouter un commentaire</h3>
<form th:action="@{'/photo/' + ${photo.id} + '/comment'}" method="post">
<textarea name="contenu" rows="3" cols="50" required></textarea><br/>
<button type="submit">Envoyer</button>
</form>
</div>
<p> <p>
<a th:href="@{'/photo/' + ${photo.id} + '/raw'}" target="_blank">Voir en grande taille</a> <a th:href="@{'/photo/' + ${photo.id} + '/raw'}" target="_blank">Voir en grande taille</a>

View File

@@ -23,6 +23,7 @@
<!-- Infos --> <!-- Infos -->
<ul> <ul>
<li><strong>Votre rôle :</strong><span th:text="${photo.proprietaire.email == currentUser ? 'Propriétaire' : (canAdmin ? 'Admin' : (canComment ? 'Commentateur' : 'Lecteur'))}"></span></li>
<li><strong>Nom original :</strong> <span th:text="${photo.nomFichierOriginal}"></span></li> <li><strong>Nom original :</strong> <span th:text="${photo.nomFichierOriginal}"></span></li>
<li><strong>Date upload :</strong> <span th:text="${photo.dateUpload}"></span></li> <li><strong>Date upload :</strong> <span th:text="${photo.dateUpload}"></span></li>
<li><strong>Visibilité :</strong> <span th:text="${photo.visibilite}"></span></li> <li><strong>Visibilité :</strong> <span th:text="${photo.visibilite}"></span></li>
@@ -34,31 +35,35 @@
<ul> <ul>
<li th:each="p : ${partages}"> <li th:each="p : ${partages}">
<span th:text="${p.utilisateur.email}"></span> <span th:text="${p.utilisateur.email}"></span>
<span> - <b th:text="${p.permission}"></b></span>
<span th:text="${p.permission}"></span> <!-- ADMIN OU PROPRIÉTAIRE : peut retirer -->
<a th:href="@{'/photo/' + ${photo.id} + '/unshare/' + ${p.utilisateur.email}}" <a th:if="${canAdmin}"
style="color:red;">Retirer</a> th:href="@{'/photo/' + ${photo.id} + '/unshare/' + ${p.utilisateur.email}}"
style="color:red; margin-left:10px;">Retirer</a>
</li> </li>
</ul> </ul>
<h2>Partager la photo</h2>
<div th:if="${currentUser == photo.proprietaire.email}"> <!-- Formulaire dajout visible uniquement en ADMIN ou PROPRIÉTAIRE -->
<div th:if="${canAdmin}">
<h3>Partager la photo</h3>
<form th:action="@{'/photo/' + ${photo.id} + '/share'}" method="post"> <form th:action="@{'/photo/' + ${photo.id} + '/share'}" method="post">
<label>Email de l'utilisateur :</label> <label>Email de l'utilisateur :</label>
<input type="email" name="email" required /> <input type="email" name="email" required />
<label>Permission :</label> <label>Permission :</label>
<select name="permission"> <select name="permission">
<option value="READ">Lecture</option> <option value="READ">Lecture seule</option>
<option value="COMMENT">Commenter</option> <option value="COMMENT">Commentaire autorisé</option>
<option value="ADMIN">Administrer</option> <option value="ADMIN">Administrateur</option>
</select> </select>
<button type="submit">Partager</button> <button type="submit">Partager</button>
</form> </form>
</div> </div>
<div th:if="${currentUser != photo.proprietaire.email}"> <div th:if="${!canAdmin}">
<em>Seul le propriétaire peut partager cette photo.</em> <em>Vous navez pas les droits de gestion du partage.</em>
</div> </div>
<!-- Commentaires --> <!-- Commentaires -->
<h2>Commentaires</h2> <h2>Commentaires</h2>
<div th:each="c : ${commentairesPage.content}"> <div th:each="c : ${commentairesPage.content}">
@@ -76,17 +81,22 @@
</div> </div>
<!-- Formulaire d'ajout de commentaire --> <!-- Formulaire d'ajout de commentaire -->
<div th:if="${currentUser}"> <!-- Si pas connecté -->
<h3>Ajouter un commentaire</h3>
<form th:action="@{'/photo/' + ${photo.id} + '/comment'}" method="post">
<textarea name="contenu" rows="3" cols="50"></textarea><br/>
<button type="submit">Envoyer</button>
</form>
</div>
<div th:if="${currentUser == null}"> <div th:if="${currentUser == null}">
<p><a th:href="@{/login}">Connectez-vous</a> pour commenter.</p> <p><a th:href="@{/login}">Connectez-vous</a> pour commenter.</p>
</div> </div>
<!-- Si connecté mais pas autorisé -->
<div th:if="${currentUser != null and !canComment}">
<em>Vous pouvez consulter cette photo, mais pas commenter.</em>
</div>
<!-- Si COMMENT ou ADMIN ou PROPRIÉTAIRE -->
<div th:if="${canComment}">
<h3>Ajouter un commentaire</h3>
<form th:action="@{'/photo/' + ${photo.id} + '/comment'}" method="post">
<textarea name="contenu" rows="3" cols="50" required></textarea><br/>
<button type="submit">Envoyer</button>
</form>
</div>
<p> <p>
<a th:href="@{'/photo/' + ${photo.id} + '/raw'}" target="_blank">Voir en grande taille</a> <a th:href="@{'/photo/' + ${photo.id} + '/raw'}" target="_blank">Voir en grande taille</a>