<?php declare(strict_types=1);
namespace App\Security\Authorization\Voter;
use App\Entity\ResourceController;
use App\Modules\Users\DomainModel\Entity\User;
use App\Repository\ResourceControllerRepository;
use App\Service\CacheService;
use Doctrine\ORM\EntityManagerInterface;
use Exception;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
class AccessToPathVoter implements VoterInterface
{
protected ?Request $request = null;
protected RequestStack $requestStack;
protected EntityManagerInterface $doctrine;
protected RouterInterface $router;
protected ResourceControllerRepository $resourceControllerRepository;
protected $_controller;
protected $resources;
protected ?array $cacheRouters = null;
public function __construct(
RequestStack $requestStack,
EntityManagerInterface $doctrine,
RouterInterface $router,
ResourceControllerRepository $resourceControllerRepository
)
{
$this->requestStack = $requestStack;
$this->doctrine = $doctrine;
$this->router = $router;
$this->resourceControllerRepository = $resourceControllerRepository;
$this->resources = $this->getResources();
}
private function getResources()
{
if ($this->resources)
{
return $this->resources;
}
return $this->getResourcesData();
}
private function getResourcesData(): ?array
{
/** @var ResourceController[] $resources */
$resources = $this->resourceControllerRepository->findAllReturnArray();
$data = null;
foreach ($resources as $resource)
{
foreach ($resource['roles'] as $role)
{
$data[strtolower($resource['controller'])][strtolower($resource['action'])][strtolower($role['name'])] = $resource['id'];
}
}
return $data;
}
private function getRoute($controller)
{
if ($this->cacheRouters)
{
return $this->cacheRouters[$controller] ?? null;
}
$this->cacheRouters = $this->getRouteData();
return $this->cacheRouters[$controller] ?? null;
}
private function getRouteData(): array
{
$data = [];
$routers = $this->router->getRouteCollection()->all();
foreach ($routers as $name => $route)
{
$data[$name] = $route->getDefault('_controller');
}
return $data;
}
private function findResource($controller, $action, $roleName)
{
$controller = strtolower($controller);
$action = strtolower($action);
$roleName = strtolower($roleName);
if (isset($this->resources[$controller]['*'][$roleName]))
{
return $this->resources[$controller]['*'][$roleName];
}
if (isset($this->resources[$controller][$action][$roleName]))
{
return $this->resources[$controller][$action][$roleName];
}
return null;
}
public function getRequest(): ?Request
{
if (null === $this->request)
{
$this->request = $this->requestStack->getCurrentRequest();
}
return $this->request;
}
public function supportsAttribute($attribute): bool
{
return true;
}
public function supportsClass($class): bool
{
return $class instanceof User;
}
/**
* Get current controller name
*/
public function getControllerName()
{
if (null !== $this -> getRequest())
{
$pattern = '#(?:.+)\\\([a-zA-Z\\\]+)Controller#';
$matches = [];
preg_match($pattern, $this->getController(), $matches);
if (false === isset($matches[1]))
{
throw new Exception('Nie znaleziono nazwy kontrolera w ścieżce '.$this -> getController());
}
return strtolower($matches[1]);
}
return null;
}
/**
* Get current action name
*/
public function getActionName(): ?string
{
if (null !== $this -> getRequest())
{
$pattern = "#::([a-zA-Z]*?)(Action|$)#";
$matches = array();
preg_match($pattern, $this -> getController(), $matches);
return strtolower($matches[1]);
}
return null;
}
public function setController($controller): self
{
$this -> _controller = $this->getRoute($controller);
return $this;
}
public function getController()
{
return $this->_controller;
}
public function vote(TokenInterface $token, $object, array $attributes)
{
$this->setController($attributes[0]);
if (!$this->getController())
{
return VoterInterface::ACCESS_ABSTAIN;
}
if (!$this->supportsClass($object))
{
return VoterInterface::ACCESS_ABSTAIN;
}
if (!$this->getControllerName())
{
return VoterInterface::ACCESS_ABSTAIN;
}
if (!$this->getActionName())
{
return VoterInterface::ACCESS_ABSTAIN;
}
/** @var ResourceController $resource */
$resource = $this->findResource(
$this->getControllerName(),
$this->getActionName(),
$object->getRole()->getName()
);
if ($resource)
{
return VoterInterface::ACCESS_GRANTED;
}
return VoterInterface::ACCESS_DENIED;
}
}