<?php
declare(strict_types=1);
namespace App\Security;
use App\Entity\User\AdminRoleInterface;
use App\Entity\User\AdminUser;
use LogicException;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
use function in_array;
class AdminUserVoter extends Voter
{
public const SHOW = 'show';
public const UPDATE = 'update';
public const ARCHIVE = 'archive';
public function __construct(
private readonly RequestStack $requestStack,
) {
}
protected function supports(string $attribute, mixed $subject): bool
{
// if the attribute isn't one we support, return false
if (! in_array($attribute, [self::SHOW, self::UPDATE, self::ARCHIVE], true)) {
return false;
}
return $subject instanceof AdminUser;
}
protected function voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token): bool
{
$loggedAdminUser = $token->getUser();
if (! $loggedAdminUser instanceof AdminUser) {
// the user must be logged in; if not, deny access
return false;
}
/** @var AdminUser $adminUser */
$adminUser = $subject;
return match ($attribute) {
self::SHOW => $this->canShow($adminUser, $loggedAdminUser),
self::UPDATE => $this->canUpdate($adminUser, $loggedAdminUser),
self::ARCHIVE => $this->canArchive($adminUser, $loggedAdminUser),
default => throw new LogicException('This code should not be reached!')
};
}
private function canShow(AdminUser $adminUser, AdminUser $loggedAdminUser): bool
{
return $this->checkAccess($adminUser, $loggedAdminUser, self::SHOW);
}
private function canUpdate(AdminUser $adminUser, AdminUser $loggedAdminUser): bool
{
return $this->checkAccess($adminUser, $loggedAdminUser, self::UPDATE);
}
private function canArchive(AdminUser $adminUser, AdminUser $loggedAdminUser): bool
{
$loggedClinicalManager = $loggedAdminUser->getCustomer()?->getClinicalManager();
return $loggedClinicalManager === null ||
$adminUser->getCustomer()?->getClinicalManager()?->getAttachedClinicalManager()?->getId() === $loggedClinicalManager->getId();
}
public function checkAccess(AdminUser $adminUser, AdminUser $loggedAdminUser, string $attribute): bool
{
$loggedClinicalManager = $loggedAdminUser->getCustomer()?->getClinicalManager();
$request = $this->requestStack->getCurrentRequest();
if ($request === null) {
return false;
}
if ($request->get('_route') === 'app_admin_clinical_manager_' . $attribute && $adminUser->hasRoleResourceByCode(AdminRoleInterface::CLINICAL_MANAGER)) {
if ($loggedClinicalManager === null) {
return true;
}
return $adminUser->getCustomer()?->getClinicalManager()?->getId() === $loggedClinicalManager->getId() || $adminUser->getCustomer()?->getClinicalManager()?->getAttachedClinicalManager()?->getId() === $loggedClinicalManager->getId();
}
return $request->get('_route') === 'sylius_admin_admin_user_' . $attribute && ! $adminUser->hasRoleResourceByCode(AdminRoleInterface::CLINICAL_MANAGER) && ! $adminUser->hasRole('ROLE_API_ACCESS');
}
}