src/Platform/SecurityBundle/Service/Voters/ImpersonateVoter.php line 17

Open in your IDE?
  1. <?php
  2. namespace Platform\SecurityBundle\Service\Voters;
  3. use Cms\CoreBundle\Util\Doctrine\EntityManager;
  4. use Platform\SecurityBundle\Entity\Identity\Account;
  5. use Symfony\Component\HttpFoundation\RequestStack;
  6. use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface;
  7. use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
  8. /**
  9.  * Handles the checking of permissions to see if a person is able to impersonate users.
  10.  *
  11.  * Class ImpersonateVoter
  12.  * @package Platform\SecurityBundle\Service\Voters
  13.  */
  14. final class ImpersonateVoter implements VoterInterface
  15. {
  16.     /**
  17.      * Link to current requests.
  18.      *
  19.      * @var RequestStack
  20.      */
  21.     public RequestStack $requestStack;
  22.     /**
  23.      * Will need to do DB work, mainly loading impersonated user.
  24.      *
  25.      * @var EntityManager;
  26.      */
  27.     public EntityManager $em;
  28.     /**
  29.      * @param RequestStack $requestStack
  30.      * @param EntityManager $em
  31.      */
  32.     public function __construct(RequestStack $requestStackEntityManager $em)
  33.     {
  34.         $this->requestStack $requestStack;
  35.         $this->em $em;
  36.     }
  37.     /**
  38.      * {@inheritdoc}
  39.      */
  40.     public function vote(
  41.         TokenInterface $token,
  42.         $subject,
  43.         array $attributes
  44.     ): int
  45.     {
  46.         // TODO: prevent double impersonation? maybe ensure the token isn't already an impersonated token...
  47.         // do not do anything if not legit check
  48.         if (count($attributes) !== || $attributes[0] !== 'ROLE_ALLOWED_TO_SWITCH') {
  49.             return VoterInterface::ACCESS_ABSTAIN;
  50.         }
  51.         // get the current user from the token and ensure proper type
  52.         $user $token->getUser();
  53.         if ( ! $user instanceof Account) {
  54.             return VoterInterface::ACCESS_ABSTAIN;
  55.         }
  56.         // NOTE:
  57.         // from here, we need to either grant or deny access
  58.         // we have ensured that we are trying to do something with impersonation, so a decision needs made!
  59.         // make sure this account is able to impersonate to begin with
  60.         if ( ! $user->getSpecialPermissions()->canImpersonate()) {
  61.             return VoterInterface::ACCESS_DENIED;
  62.         }
  63.         // get the account we are trying to impersonate
  64.         $impersonated $this->getImpersonatedAccount();
  65.         // ensure that there is a real account to switch to
  66.         if ( ! $impersonated instanceof Account) {
  67.             return VoterInterface::ACCESS_DENIED;
  68.         }
  69.         // if a user who is being impersonated is a superuser and the one requesting impersonation is not, must deny
  70.         if ($impersonated->getSpecialPermissions()->isSuperUser() && ! $user->getSpecialPermissions()->isSuperUser()) {
  71.             return VoterInterface::ACCESS_DENIED;
  72.         }
  73.         // if we are impersonating an internal user, and we are not, we must deny
  74.         if ($impersonated->isInternal() && ! $user->isInternal()) {
  75.             return VoterInterface::ACCESS_DENIED;
  76.         }
  77.         // make sure user can't impersonate themselves
  78.         if ($user->getId() === $impersonated->getId()) {
  79.             return VoterInterface::ACCESS_DENIED;
  80.         }
  81.         // we have run all the checks we need to run, so grant the impersonation action
  82.         return VoterInterface::ACCESS_GRANTED;
  83.     }
  84.     /**
  85.      * Tries to load impersonated account.
  86.      *
  87.      * @return Account|null
  88.      */
  89.     private function getImpersonatedAccount(): ?Account
  90.     {
  91.         // make sure we have the things we need to try and pull the id from the request
  92.         // TODO: should this use the master request instead?
  93.         if ( ! $this->requestStack->getCurrentRequest()) {
  94.             return null;
  95.         }
  96.         // obtain the id of the user we are trying to switch to
  97.         $id $this->requestStack->getCurrentRequest()->get('_switch_user');
  98.         if ( ! $id) {
  99.             return null;
  100.         }
  101.         // attempt lookup in the database
  102.         return $this->em->getRepository(Account::class)->find($id);
  103.     }
  104. }