Working state
This commit is contained in:
@@ -5,33 +5,20 @@ namespace App\User;
|
||||
|
||||
use App\Shared\Http\FlashServiceInterface;
|
||||
use App\Shared\Http\SessionManagerInterface;
|
||||
use App\Shared\Pagination\PaginationPresenter;
|
||||
use App\User\Exception\DuplicateEmailException;
|
||||
use App\User\Exception\DuplicateUsernameException;
|
||||
use App\User\Exception\InvalidRoleException;
|
||||
use App\User\Exception\RoleAssignmentNotAllowedException;
|
||||
use App\User\Exception\WeakPasswordException;
|
||||
use Psr\Http\Message\ResponseInterface as Response;
|
||||
use Psr\Http\Message\ServerRequestInterface as Request;
|
||||
use Slim\Views\Twig;
|
||||
|
||||
/**
|
||||
* Contrôleur pour la gestion des utilisateurs en administration.
|
||||
*
|
||||
* Accessible uniquement aux administrateurs (AdminMiddleware).
|
||||
* Gère la liste, la création, la modification de rôle et la suppression des comptes.
|
||||
* Toute la logique de persistance est déléguée à UserService.
|
||||
*
|
||||
* Règles de protection communes :
|
||||
* - Le compte administrateur (role = 'admin') ne peut pas être supprimé ni rétrogradé
|
||||
* - Un administrateur ne peut pas supprimer son propre compte ni changer son propre rôle
|
||||
*/
|
||||
final class UserController
|
||||
{
|
||||
/**
|
||||
* @param Twig $view Moteur de templates Twig
|
||||
* @param UserServiceInterface $userService Service de gestion des utilisateurs
|
||||
* @param FlashServiceInterface $flash Service de messages flash
|
||||
* @param SessionManagerInterface $sessionManager Gestionnaire de session
|
||||
*/
|
||||
private const PER_PAGE = 15;
|
||||
|
||||
public function __construct(
|
||||
private readonly Twig $view,
|
||||
private readonly UserServiceInterface $userService,
|
||||
@@ -40,84 +27,47 @@ final class UserController
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Affiche la liste de tous les utilisateurs.
|
||||
*
|
||||
* Passe l'identifiant de l'utilisateur courant à la vue
|
||||
* pour conditionner l'affichage du bouton de suppression.
|
||||
*
|
||||
* @param Request $req La requête HTTP
|
||||
* @param Response $res La réponse HTTP
|
||||
*
|
||||
* @return Response La vue admin/users/index.twig
|
||||
*/
|
||||
public function index(Request $req, Response $res): Response
|
||||
{
|
||||
$page = PaginationPresenter::resolvePage($req->getQueryParams());
|
||||
$paginated = $this->userService->findPaginated($page, self::PER_PAGE);
|
||||
|
||||
return $this->view->render($res, 'admin/users/index.twig', [
|
||||
'users' => $this->userService->findAll(),
|
||||
'users' => $paginated->getItems(),
|
||||
'pagination' => PaginationPresenter::fromRequest($req, $paginated),
|
||||
'currentUserId' => $this->sessionManager->getUserId(),
|
||||
'assignableRoles' => User::assignableRoles(),
|
||||
'error' => $this->flash->get('user_error'),
|
||||
'success' => $this->flash->get('user_success'),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Affiche le formulaire de création d'un utilisateur.
|
||||
*
|
||||
* @param Request $req La requête HTTP
|
||||
* @param Response $res La réponse HTTP
|
||||
*
|
||||
* @return Response La vue admin/users/form.twig
|
||||
*/
|
||||
public function showCreate(Request $req, Response $res): Response
|
||||
{
|
||||
return $this->view->render($res, 'admin/users/form.twig', [
|
||||
'assignableRoles' => User::assignableRoles(),
|
||||
'error' => $this->flash->get('user_error'),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Traite la soumission du formulaire de création d'utilisateur.
|
||||
*
|
||||
* Vérifie que les mots de passe correspondent avant de déléguer
|
||||
* la création à UserService. En cas d'erreur, redirige vers le
|
||||
* formulaire avec un message flash.
|
||||
*
|
||||
* @param Request $req La requête HTTP
|
||||
* @param Response $res La réponse HTTP
|
||||
*
|
||||
* @return Response Redirection vers /admin/users en cas de succès,
|
||||
* ou vers /admin/users/create en cas d'erreur
|
||||
*/
|
||||
public function create(Request $req, Response $res): Response
|
||||
{
|
||||
/** @var array<string, mixed> $data */
|
||||
$data = (array) $req->getParsedBody();
|
||||
$username = trim((string) ($data['username'] ?? ''));
|
||||
$email = trim((string) ($data['email'] ?? ''));
|
||||
$password = trim((string) ($data['password'] ?? ''));
|
||||
$confirm = trim((string) ($data['password_confirm'] ?? ''));
|
||||
|
||||
// Restreindre les rôles assignables depuis le formulaire.
|
||||
// Le rôle 'admin' est exclu : il ne peut être attribué que directement
|
||||
// en base de données, pour éviter qu'un admin ne crée d'autres admins
|
||||
// en manipulant la requête HTTP.
|
||||
$allowedRoles = [User::ROLE_USER, User::ROLE_EDITOR];
|
||||
$rawRole = trim((string) ($data['role'] ?? ''));
|
||||
$role = in_array($rawRole, $allowedRoles, true)
|
||||
? $rawRole
|
||||
: User::ROLE_USER;
|
||||
$rawRole = trim((string) ($data['role'] ?? ''));
|
||||
$role = in_array($rawRole, User::assignableRoles(), true) ? $rawRole : User::ROLE_USER;
|
||||
|
||||
if ($password !== $confirm) {
|
||||
$this->flash->set('user_error', 'Les mots de passe ne correspondent pas');
|
||||
|
||||
return $res->withHeader('Location', '/admin/users/create')->withStatus(302);
|
||||
}
|
||||
|
||||
try {
|
||||
$this->userService->createUser($username, $email, $password, $role);
|
||||
$this->flash->set('user_success', "L'utilisateur « {$username} » a été créé avec succès");
|
||||
|
||||
return $res->withHeader('Location', '/admin/users')->withStatus(302);
|
||||
} catch (DuplicateUsernameException) {
|
||||
$this->flash->set('user_error', "Ce nom d'utilisateur est déjà pris");
|
||||
@@ -125,7 +75,7 @@ final class UserController
|
||||
$this->flash->set('user_error', 'Cette adresse e-mail est déjà utilisée');
|
||||
} catch (WeakPasswordException) {
|
||||
$this->flash->set('user_error', 'Le mot de passe doit contenir au moins 8 caractères');
|
||||
} catch (InvalidRoleException $e) {
|
||||
} catch (InvalidRoleException|RoleAssignmentNotAllowedException $e) {
|
||||
$this->flash->set('user_error', $e->getMessage());
|
||||
} catch (\Throwable) {
|
||||
$this->flash->set('user_error', "Une erreur inattendue s'est produite");
|
||||
@@ -135,18 +85,7 @@ final class UserController
|
||||
}
|
||||
|
||||
/**
|
||||
* Met à jour le rôle d'un utilisateur.
|
||||
*
|
||||
* La modification est refusée dans trois cas :
|
||||
* - l'utilisateur cible est introuvable
|
||||
* - l'administrateur connecté tente de modifier son propre rôle
|
||||
* - l'utilisateur cible est déjà administrateur
|
||||
*
|
||||
* @param Request $req La requête HTTP
|
||||
* @param Response $res La réponse HTTP
|
||||
* @param array<string, string> $args Les paramètres de route (id)
|
||||
*
|
||||
* @return Response Redirection vers /admin/users dans tous les cas
|
||||
* @param array<string, mixed> $args
|
||||
*/
|
||||
public function updateRole(Request $req, Response $res, array $args): Response
|
||||
{
|
||||
@@ -155,53 +94,40 @@ final class UserController
|
||||
|
||||
if ($user === null) {
|
||||
$this->flash->set('user_error', 'Utilisateur introuvable');
|
||||
|
||||
return $res->withHeader('Location', '/admin/users')->withStatus(302);
|
||||
}
|
||||
|
||||
if ($id === $this->sessionManager->getUserId()) {
|
||||
$this->flash->set('user_error', 'Vous ne pouvez pas modifier votre propre rôle');
|
||||
|
||||
return $res->withHeader('Location', '/admin/users')->withStatus(302);
|
||||
}
|
||||
|
||||
if ($user->isAdmin()) {
|
||||
$this->flash->set('user_error', 'Le rôle d\'un administrateur ne peut pas être modifié');
|
||||
|
||||
$this->flash->set('user_error', 'Le rôle d\'un administrateur ne peut pas être modifié depuis l\'interface');
|
||||
return $res->withHeader('Location', '/admin/users')->withStatus(302);
|
||||
}
|
||||
|
||||
$allowedRoles = [User::ROLE_USER, User::ROLE_EDITOR, User::ROLE_ADMIN];
|
||||
/** @var array<string, mixed> $body */
|
||||
$body = (array) $req->getParsedBody();
|
||||
$rawRole = trim((string) ($body['role'] ?? ''));
|
||||
$role = in_array($rawRole, $allowedRoles, true) ? $rawRole : null;
|
||||
$body = (array) $req->getParsedBody();
|
||||
$rawRole = trim((string) ($body['role'] ?? ''));
|
||||
$role = in_array($rawRole, User::assignableRoles(), true) ? $rawRole : null;
|
||||
|
||||
if ($role === null) {
|
||||
$this->flash->set('user_error', 'Rôle invalide');
|
||||
|
||||
return $res->withHeader('Location', '/admin/users')->withStatus(302);
|
||||
}
|
||||
|
||||
$this->userService->updateRole($id, $role);
|
||||
$this->flash->set('user_success', "Le rôle de « {$user->getUsername()} » a été mis à jour");
|
||||
try {
|
||||
$this->userService->updateRole($id, $role);
|
||||
$this->flash->set('user_success', "Le rôle de « {$user->getUsername()} » a été mis à jour");
|
||||
} catch (InvalidRoleException|RoleAssignmentNotAllowedException $e) {
|
||||
$this->flash->set('user_error', $e->getMessage());
|
||||
}
|
||||
|
||||
return $res->withHeader('Location', '/admin/users')->withStatus(302);
|
||||
}
|
||||
|
||||
/**
|
||||
* Supprime un utilisateur.
|
||||
*
|
||||
* La suppression est refusée dans trois cas :
|
||||
* - l'utilisateur cible est introuvable
|
||||
* - l'utilisateur cible est administrateur (role = 'admin')
|
||||
* - l'administrateur connecté tente de supprimer son propre compte
|
||||
*
|
||||
* @param Request $req La requête HTTP
|
||||
* @param Response $res La réponse HTTP
|
||||
* @param array<string, string> $args Les paramètres de route (id)
|
||||
*
|
||||
* @return Response Redirection vers /admin/users dans tous les cas
|
||||
* @param array<string, mixed> $args
|
||||
*/
|
||||
public function delete(Request $req, Response $res, array $args): Response
|
||||
{
|
||||
@@ -210,19 +136,16 @@ final class UserController
|
||||
|
||||
if ($user === null) {
|
||||
$this->flash->set('user_error', 'Utilisateur introuvable');
|
||||
|
||||
return $res->withHeader('Location', '/admin/users')->withStatus(302);
|
||||
}
|
||||
|
||||
if ($user->isAdmin()) {
|
||||
$this->flash->set('user_error', 'Le compte administrateur ne peut pas être supprimé');
|
||||
|
||||
return $res->withHeader('Location', '/admin/users')->withStatus(302);
|
||||
}
|
||||
|
||||
if ($id === $this->sessionManager->getUserId()) {
|
||||
$this->flash->set('user_error', 'Vous ne pouvez pas supprimer votre propre compte');
|
||||
|
||||
return $res->withHeader('Location', '/admin/users')->withStatus(302);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user