<?php
namespace App\Manager\User;
use App\Annotations\HasPermission;
use App\Annotations\PermissionKey;
use App\Entity\Menu;
use App\Entity\User\User;
use Doctrine\Common\Annotations\Reader;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\Security\Core\Security;
class PermissionManager
{
private Security $security;
private Reader $annotationReader;
private RouterInterface $router;
private EntityManagerInterface $entityManager;
public function __construct(Security $security, Reader $annotationReader, RouterInterface $router,
EntityManagerInterface $entityManager)
{
$this->security = $security;
$this->annotationReader = $annotationReader;
$this->router = $router;
$this->entityManager = $entityManager;
}
public function hasPermission(string $action = null, ?\ReflectionMethod $method = null, ?Menu $menu = null): bool
{
if ($method)
{
return $this->hasPermissionByMethod($method);
}
elseif ($menu)
{
return $this->hasPermissionByMenu($action, $menu);
}
return false;
}
public function hasPermissionByRoute(string $route): bool
{
try {
if ($this->router->getRouteCollection()->get($route) === null)
{
throw new \Exception(`The controller for route "${$route}" does not exist`);
}
return $this->hasPermissionByMethod(
new \ReflectionMethod($this->router->getRouteCollection()->get($route)->getDefault('_controller'))
);
} catch (\ReflectionException $e) {
return true;
}
}
private function getMenuByRouteName(string $routeName)
{
return $this->entityManager->getRepository(Menu::class)->findOneBy([
'route' => $routeName
]);
}
private function hasPermissionByMethod(\ReflectionMethod $method): bool
{
/** @var PermissionKey $permissionKey */
$permissionKey = $this->annotationReader->getClassAnnotation($method->getDeclaringClass(), PermissionKey::class);
/** @var HasPermission $needlePermission */
$needlePermission = $this->annotationReader->getMethodAnnotation($method, HasPermission::class);
if (!$permissionKey || !$needlePermission) {
// If the controller OR the action are not defined, access is granted
return true;
}
$menu = $this->entityManager->getRepository(Menu::class)->findOneBy(['route' => $this->annotationReader->getMethodAnnotation($method, Route::class)->getName()]);
if (!$menu) {
$menu = $this->entityManager->getRepository(Menu::class)->findOneBy(['permission_key' => $permissionKey->name]);
}
return $this->hasPermissionByMenu($needlePermission->action, $menu);
}
public function hasPermissionByMenu(string $action, Menu $menu): bool
{
$user = $this->security->getUser();
if (!$user instanceof User) {
return false;
}
$menuPermissions = !$menu->getPermissions()->isEmpty() ? $menu : $menu->getMenu() ?? $menu;
return (bool) $menuPermissions->getPermissions()->filter(
fn ($v) => $v->getProfile() === $user->getProfile()
)->first()->getActions()[$action];
}
}