vendor/easycorp/easyadmin-bundle/src/Security/SecurityVoter.php line 18

Open in your IDE?
  1. <?php
  2. namespace EasyCorp\Bundle\EasyAdminBundle\Security;
  3. use EasyCorp\Bundle\EasyAdminBundle\Contracts\Provider\AdminContextProviderInterface;
  4. use EasyCorp\Bundle\EasyAdminBundle\Dto\ActionDto;
  5. use EasyCorp\Bundle\EasyAdminBundle\Dto\CrudDto;
  6. use EasyCorp\Bundle\EasyAdminBundle\Dto\EntityDto;
  7. use EasyCorp\Bundle\EasyAdminBundle\Dto\FieldDto;
  8. use EasyCorp\Bundle\EasyAdminBundle\Dto\MenuItemDto;
  9. use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
  10. use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
  11. use Symfony\Component\Security\Core\Authorization\Voter\Voter;
  12. /*
  13. * @author Javier Eguiluz <javier.eguiluz@gmail.com>
  14. */
  15. final class SecurityVoter extends Voter
  16. {
  17. public function __construct(
  18. private readonly AuthorizationCheckerInterface $authorizationChecker,
  19. private readonly AdminContextProviderInterface $adminContextProvider,
  20. ) {
  21. }
  22. protected function supports(string $permissionName, mixed $subject): bool
  23. {
  24. return $this->supportsAttribute($permissionName);
  25. }
  26. public function supportsAttribute(string $permissionName): bool
  27. {
  28. return Permission::exists($permissionName);
  29. }
  30. protected function voteOnAttribute($permissionName, $subject, TokenInterface $token): bool
  31. {
  32. if (Permission::EA_VIEW_MENU_ITEM === $permissionName) {
  33. return $this->voteOnViewMenuItemPermission($subject);
  34. }
  35. if (Permission::EA_EXECUTE_ACTION === $permissionName) {
  36. return $this->voteOnExecuteActionPermission($this->adminContextProvider->getContext()->getCrud(), $subject['action'] ?? null, $subject['entity'] ?? null, $subject['entityFqcn'] ?? null);
  37. }
  38. if (Permission::EA_VIEW_FIELD === $permissionName) {
  39. return $this->voteOnViewPropertyPermission($subject);
  40. }
  41. if (Permission::EA_ACCESS_ENTITY === $permissionName) {
  42. return $this->voteOnViewEntityPermission($subject);
  43. }
  44. if (Permission::EA_EXIT_IMPERSONATION === $permissionName) {
  45. return $this->voteOnExitImpersonationPermission();
  46. }
  47. return true;
  48. }
  49. private function voteOnViewMenuItemPermission(MenuItemDto $menuItemDto): bool
  50. {
  51. // users can see the menu item if they have the permission required by the menu item
  52. return $this->authorizationChecker->isGranted($menuItemDto->getPermission(), $menuItemDto);
  53. }
  54. private function voteOnExecuteActionPermission(CrudDto $crudDto, ActionDto|string $actionNameOrDto, ?EntityDto $entityDto, ?string $entityFqcn): bool
  55. {
  56. // users can run the Crud action if:
  57. // * they have the required permission to execute the action on the given entity instance
  58. // * the action is not disabled
  59. $actionName = \is_string($actionNameOrDto) ? $actionNameOrDto : $actionNameOrDto->getName();
  60. $actionPermission = $crudDto->getActionsConfig()->getActionPermissions()[$actionName] ?? null;
  61. $disabledActionNames = $crudDto->getActionsConfig()->getDisabledActions();
  62. $subject = $entityDto?->getInstance() ?? $entityFqcn;
  63. return !\in_array($actionName, $disabledActionNames, true) && $this->authorizationChecker->isGranted($actionPermission, $subject);
  64. }
  65. private function voteOnViewPropertyPermission(FieldDto $field): bool
  66. {
  67. // users can see the field if they have the permission required by the field
  68. return $this->authorizationChecker->isGranted($field->getPermission(), $field);
  69. }
  70. private function voteOnViewEntityPermission(EntityDto $entityDto): bool
  71. {
  72. // users can see the entity if they have the required permission on the specific entity instance
  73. return $this->authorizationChecker->isGranted($entityDto->getPermission(), $entityDto->getInstance());
  74. }
  75. private function voteOnExitImpersonationPermission(): bool
  76. {
  77. // users can exit impersonation if they are currently impersonating another user.
  78. // In Symfony, that means that current user has the special impersonator permission
  79. if (\defined('Symfony\Component\Security\Core\Authorization\Voter\AuthenticatedVoter::IS_IMPERSONATOR')) {
  80. $impersonatorPermission = 'IS_IMPERSONATOR';
  81. } else {
  82. $impersonatorPermission = 'ROLE_PREVIOUS_ADMIN';
  83. }
  84. return $this->authorizationChecker->isGranted($impersonatorPermission);
  85. }
  86. }