src/EventSubscriber/LoginSuspensionCheckSubscriber.php line 69

  1. <?php
  2. declare(strict_types=1);
  3. namespace App\EventSubscriber;
  4. use App\Entity\Students;
  5. use App\Security\Exception\StudentSuspendedException;
  6. use App\Security\StudentSuspensionChecker;
  7. use Lexik\Bundle\JWTAuthenticationBundle\Event\AuthenticationSuccessEvent;
  8. use Lexik\Bundle\JWTAuthenticationBundle\Events;
  9. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  10. use Symfony\Component\HttpFoundation\JsonResponse;
  11. use Symfony\Component\HttpKernel\Event\ExceptionEvent;
  12. use Symfony\Component\HttpKernel\KernelEvents;
  13. /**
  14.  * Subscriber that blocks suspended students from logging in.
  15.  * Intercepts the authentication success event BEFORE the JWT token is returned.
  16.  */
  17. class LoginSuspensionCheckSubscriber implements EventSubscriberInterface
  18. {
  19.     public function __construct(
  20.         private StudentSuspensionChecker $suspensionChecker
  21.     ) {
  22.     }
  23.     public static function getSubscribedEvents(): array
  24.     {
  25.         return [
  26.             Events::AUTHENTICATION_SUCCESS => ['onAuthenticationSuccess'0],
  27.             KernelEvents::EXCEPTION => ['onKernelException'10], // Higher priority to run before other exception handlers
  28.         ];
  29.     }
  30.     /**
  31.      * Called when login credentials are valid, before JWT token is returned.
  32.      * Blocks suspended students by throwing an exception.
  33.      */
  34.     public function onAuthenticationSuccess(AuthenticationSuccessEvent $event): void
  35.     {
  36.         $user $event->getUser();
  37.         // Only check for users with associated Person entity
  38.         if ($user === null || !method_exists($user'getPerson') || $user->getPerson() === null) {
  39.             return;
  40.         }
  41.         $person $user->getPerson();
  42.         // Only check suspension for Students
  43.         if (!$person instanceof Students) {
  44.             return;
  45.         }
  46.         // Check suspension status
  47.         $suspensionDetails $this->suspensionChecker->getSuspensionDetails($person);
  48.         if ($suspensionDetails['suspended']) {
  49.             throw new StudentSuspendedException(
  50.                 $person->getMatricNo() ?? 'UNKNOWN'
  51.             );
  52.         }
  53.     }
  54.     /**
  55.      * Handle StudentSuspendedException during login and return 500 response.
  56.      */
  57.     public function onKernelException(ExceptionEvent $event): void
  58.     {
  59.         $exception $event->getThrowable();
  60.         if (!$exception instanceof StudentSuspendedException) {
  61.             return;
  62.         }
  63.         // Return 500 status code as requested
  64.         $response = new JsonResponse([
  65.             'error' => 'Account suspended, contact admin',
  66.             'message' => 'Your account is suspended for this session. Please contact the administrator.'
  67.         ], 500);
  68.         $event->setResponse($response);
  69.     }
  70. }