<?php
namespace App\Security\Voter;
use App\Entity\Episode;
use App\Entity\Program;
use App\Entity\Radio;
use App\Entity\User;
use EasyCorp\Bundle\EasyAdminBundle\Dto\ActionDto;
use EasyCorp\Bundle\EasyAdminBundle\Provider\AdminContextProvider;
use EasyCorp\Bundle\EasyAdminBundle\Security\Permission;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
use Symfony\Component\Security\Core\Security;
use Symfony\Component\Security\Core\User\UserInterface;
/**
* This class vote on applications entities that may be owned by current user.
* Basically allows to view anything but edit only owned entities.
*/
class OwnerVoter extends Voter
{
private $security;
private $adminContextProvider;
public function __construct(Security $security, AdminContextProvider $adminContextProvider)
{
$this->security = $security;
$this->adminContextProvider = $adminContextProvider;
}
protected function supports($attribute, $subject)
{
// Currently disabled. Easyer to maintain with displayIf() in controllers.
// May need to come back to voter to enforce permission.
return false;
// $crud = $this->adminContextProvider->getContext()?->getCrud();
$context = $this->adminContextProvider->getContext();
$crud = $context ? $context->getCrud() : null;
// dump([
// "supports" => $attribute,
// 'subject' => $subject,
// 'crud' => $crud,
// ]);
// replace with your own logic
// https://symfony.com/doc/current/security/voters.html
$supports = $attribute == Permission::EA_EXECUTE_ACTION &&
// in_array($subject?->getName(), ['new', 'edit', 'delete']) &&
$crud && in_array($crud->getEntityFqcn(), [
Radio::class,
Program::class,
Episode::class,
]);
// dump('supports: ' . ($supports ? 'true' : 'false'));
return $supports;
}
protected function voteOnAttribute($attribute, $subject, TokenInterface $token)
{
$user = $token->getUser();
// if the user is anonymous, do not grant access
if (!$user instanceof UserInterface) {
return false;
}
// ... (check conditions and return true to grant permission) ...
switch ($attribute) {
case Permission::EA_EXECUTE_ACTION:
// dump([
// "voteOnAttribute" => $attribute,
// 'subject' => $subject,
// 'token' => $token,
// // 'crud' => $crud,
// ]);
if ($this->security->isGranted(User::ROLE_MANAGER)) {
// dump("voted true for user with ROLE_MANAGER");
// Managers and above
return true;
} else {
if ($subject instanceof ActionDto) {
$action = $subject->getName();
if ($subject->isGlobalAction()) {
$context = $this->adminContextProvider->getContext();
$crud = $context ? $context->getCrud() : null;
$class = $crud->getEntityFqcn();
switch ($action) {
case 'new':
$vote = in_array($class, [Program::class, Episode::class]);
break;
default:
$vote = true;
}
// dump("shouldBeDisplayedFor voted " . ($vote ? "true" : 'false') . " for $action");
return $vote;
} elseif ($subject->isEntityAction()) {
// Need to decide for each entity
// Note: this only removes the action links, but does not prevent to access the page
// if the user has a way to forge the url.
// Find a way to enforce the permission on the (not displayed) url
$subject->setDisplayCallable(
function ($entity) use ($user, $action): bool {
// dump([
// "shouldBeDisplayedFor" => $entity?->getName(),
// 'entity' => $entity,
// 'action name' => $action,
// ]);
$owner = null;
if ($entity instanceof Radio) {
$owner = $entity->getOwner();
} elseif ($entity instanceof Program) {
$owner = $entity->getRadio()?->getOwner();
} elseif ($entity instanceof Episode) {
$owner = $entity->getProgram()?->getRadio()->getOwner();
}
switch ($action) {
case 'edit':
case 'delete':
$vote = $owner == $user;
break;
default:
$vote = true;
}
// dump("shouldBeDisplayedFor voted " . ($vote ? "true" : 'false') . " for $action on " . $entity);
return $vote;
// throw new \LogicException('This code should not be reached!');
}
);
// dump("voted true for $attribute $action with displayCallable");
return true;
}
// dump("voted true for $attribute $action with displayCallable");
//dump("shouldBeDisplayedFor voted " . ($vote ? "true" : 'false') . " for $action");
return false;
}
}
}
return false;
}
}