<?php
namespace App\Listener;
use App\Entity\User;
use App\Manager\UserRightsManagerInterface;
use App\Model\Permissions\AreaRights;
use Lexik\Bundle\JWTAuthenticationBundle\Event\JWTCreatedEvent;
use Psr\Log\LoggerInterface;
use RuntimeException;
use Symfony\Component\Security\Core\User\UserInterface;
class JWTListener
{
/**
* @var UserRightsManagerInterface
*/
protected $rightsManager;
/**
* @var LoggerInterface
*/
protected $logger;
/**
* JWTListener constructor.
*
* @param UserRightsManagerInterface $rightsManager
* @param LoggerInterface $logger
*/
public function __construct(
UserRightsManagerInterface $rightsManager,
LoggerInterface $logger
) {
$this->rightsManager = $rightsManager;
$this->logger = $logger;
}
public const TOKEN_TTL = 3600; // one hour
/**
* Hook for when JWT is created: let's customize the payload.
*
* @see https://github.com/lexik/LexikJWTAuthenticationBundle/blob/master/Resources/doc/2-data-customization.md
*
* @param JWTCreatedEvent $event
*
* @return void
*/
public function onJWTCreated(JWTCreatedEvent $event)
{
$eventUser = $event->getUser();
$payload = $event->getData();
// Authentication
$payload['id'] = $eventUser->getId(); //uidNumber
$payload['email'] = $eventUser->getEmail(); //uid
$payload['firstName'] = $eventUser->getFirstName(); //givenName
$payload['lastName'] = $eventUser->getLastName(); //sn
$payload['ldapUuid'] = $eventUser->getLdapUuid(); //ldapUuid
$payload['phone'] = $eventUser->getPhone(); //telephonenumber
$payload['mobilePhone'] = $eventUser->getMobilePhone(); // mobile
$payload['organisation'] = $eventUser->getOrganisation(); // o
// Authorizations
$payload['roles'] = array_merge($this->getRoles($eventUser), $eventUser->getRoles());
$areas = $this->getAreas($eventUser);
$payload['areas'] = $areas->toJson();
$event->setData($payload);
}
/**
* Defines set of role for User.
*
* @param UserInterface $user
*
* @return array
*
* @throws RuntimeException When user has an illegal type
*/
protected function getRoles(UserInterface $user): array
{
switch ($user->getType()) {
case User::TYPE_SAML:
case User::TYPE_PUBLIK_SSO_POLE_MANAGER:
return $this->getSamlRights($user);
case User::TYPE_PUBLIK_SSO:
// Roles set by entrypoint, no need to recalculate them.
return $user->getRoles();
default:
$this->logger->critical(
'[JWTCreated] User with type {type} is not eligible to role computation!',
[
'type' => $user->getType(),
'email' => $user->getEmail(),
]
);
throw new RuntimeException('Invalid user type');
}
}
/**
* Defines set of areas for User.
*
* @param UserInterface $user
*
* @return AreaRights
*
* @throws RuntimeException When user has an illegal type
*/
protected function getAreas(UserInterface $user): AreaRights
{
switch ($user->getType()) {
case User::TYPE_SAML:
case User::TYPE_PUBLIK_SSO_POLE_MANAGER:
return $this->getSamlAreas($user);
case User::TYPE_PUBLIK_SSO:
// Areas set by entrypoint, no need to recalculate them.
return $user->getAreaRights();
default:
$this->logger->critical(
'[JWTCreated] User with type {type} is not eligible to area computation!',
[
'type' => $user->getType(),
'email' => $user->getEmail(),
]
);
throw new RuntimeException('Invalid user type');
}
}
/**
* Calls Heimdall to compute user roles
*
* @param User $user
*
* @return array
*/
protected function getSamlRights(User $user): array
{
$roles = $this->rightsManager->getUserRoles($user);
$this->logger->info(
'[JWTCreated] Computing rights for SAML user {email}. Granted: {roles}',
[
'email' => $user->getEmail(),
'roles' => json_encode($roles),
]
);
return $roles;
}
/**
* Calls Heimdall to compute user areas
*
* @param UserInterface $user
*
* @return AreaRights
*/
protected function getSamlAreas(UserInterface $user): AreaRights
{
$areas = $this->rightsManager->getAreaRights($user);
$this->logger->info(
'[JWTCreated] Computing rights for SAML user {email}. Granted: {roles}',
[
'email' => $user->getEmail(),
'areas' => $areas->toJson(),
]
);
return $areas;
}
}