<?php declare(strict_types=1);
namespace Sq\Service\Security\Voter\User;
use Sq\Entity\Schema\ORM\User;
use Sq\Service\Security\Voter\SecurityAttributes;
use Symfony\Component\Security\Core\Authentication\Token\SwitchUserToken;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
class UserVoter extends Voter
{
protected function supports($attribute, $subject): bool
{
$attributes = [
SecurityAttributes::VIEW,
// SecurityAttributes::CREATE is only allowed by an anonymous user that is not logged in.
SecurityAttributes::EDIT,
SecurityAttributes::DELETE,
];
return in_array($attribute, $attributes, true)
&& $subject instanceof User;
}
protected function voteOnAttribute($attribute, $subject, TokenInterface $token): bool
{
/** @var \Sq\Entity\Schema\ORM\User $subject */
$user = $token->getUser();
if (!$user instanceof User)
{
return false;
}
// A user can view another user if they share an organization.
if ($attribute === SecurityAttributes::VIEW)
{
return $user->getRole() === User::ROLE_ADMIN || $this->doUsersBelongToSameOrganization($user, $subject);
}
// Other users cannot edit or delete a user that is not themselves.
if ($token instanceof SwitchUserToken)
{
return false;
}
return $subject->getId() === $user->getId()
|| $user->getRole() === User::ROLE_ADMIN;
}
private function doUsersBelongToSameOrganization(User $user, User $subject): bool
{
foreach ($user->getOrganizations() as $organization)
{
if ($subject->getOrganizations()->contains($organization))
{
return true;
}
}
return false;
}
}