<?php
namespace App\Security;
use App\Entity\FinancialScope;
use App\Service\ScopeResolverService;
use App\Service\HierarchyChecker;
use App\Repository\FinancialScopeRepository;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
class FinancialScopeVoter extends Voter
{
public const MANAGE = 'FIN_MANAGE';
private $scopeResolver;
private $scopeRepo;
private $hierarchyChecker;
public function __construct(ScopeResolverService $scopeResolver, FinancialScopeRepository $scopeRepo, HierarchyChecker $hierarchyChecker)
{
$this->scopeResolver = $scopeResolver;
$this->scopeRepo = $scopeRepo;
$this->hierarchyChecker = $hierarchyChecker;
}
protected function supports(string $attribute, $subject): bool
{
if ($attribute !== self::MANAGE) return false;
// support any object we can resolve
return true;
}
protected function voteOnAttribute(string $attribute, $subject, TokenInterface $token): bool
{
$user = $token->getUser();
if (!$user || !is_object($user)) return false;
// quick role check: must have ROLE_FINANCIAL or higher
if (!in_array('ROLE_FINANCIAL', $user->getRoles()) && !in_array('ROLE_ADMIN', $user->getRoles())) {
return false;
}
// get user's scopes
$scopes = $this->scopeRepo->findBy(['financialManager' => $user]);
// resolve target
$targetScope = $this->scopeResolver->resolve($subject);
return $this->hierarchyChecker->isCovered($scopes, $targetScope);
}
}