【问题标题】:如何在不覆盖 checkAccess 和 hasAccess 方法的情况下控制对操作的访问
【发布时间】:2021-12-09 17:36:42
【问题描述】:

从 SonataAdminBundle 3.102.0 版本开始,AbstractAdmin 中的许多方法都被标记为 final。 最重要的(在我看来)“checkAccess”和“hasAccess”方法也被标记为“final”,并且不能再在 Admin 类中被覆盖以自行处理对操作的访问。 当我想根据对象的状态限制对某些操作的访问时,如何处理?

例如我有“任务”实体:

<?php
   class Task
   {
      private ?int $id = null;
      private ?string $name = null;
      private bool $closed = false;

      public function getId(): ?int
      {
         return $this->id;
      }

      public function getName(): ?string
      {
         return $this->name;
      }

      public function setName(string $name): self
      {
         $this->name = $name;
         return $this;
      }

      public function isClosed(): bool
      {
         return $this->closed;
      }

      public function setClosed(bool $closed): self
      {
         $this->closed = $closed;
         return $this;
      }
   }

如果任务对象关闭,我想拒绝访问编辑操作。

在 3.102 版本之前,这样做很简单:

<?php
   class TaskAdmin extends AbstractAdmin
   {
      protected function checkAccess($action, $object = null)
      {
         if ('edit' === $action && $object && $object->isClosed()) {
            throw new AccessDenied('Access Denied to action edit because task is closed.');
         }

         parent::checkAccess($action, $object);
      }

      protected function hasAccess($action, $object = null)
      {
         if ('edit' === $action && $object && $object->isClosed()) {
            return false;
         }

         return parent::hasAccess($action, $object);
      }
   }

当然现在我不能覆盖这些方法。

我考虑过选民,但在这种情况下并不完美,因为 Sonata 首先检查用户是否具有“超级管理员角色/角色”。如果不是,则接下来检查特定角色(例如 ROLE_ADMIN_TASK_TASK_EDIT 在我的情况下)。因此,具有超级管理员角色的用户仍然可以编辑任务对象,即使它已关闭。

另一个想法是为此 TaskAdmin 创建控制器并覆盖“preEdit”方法并检查对象是否已关闭并拒绝访问。这个解决方案也不完美,因为在模板中的许多地方都会触发“hasAccess”方法来检查 UI 的某些部分是否应该可见(例如编辑按钮),所以用户仍然会看到编辑按钮但不会能够进入编辑操作(在控制器级别阻止)。

如果“preCheckAccess”和“preHasAccess”等方法可以在 Admin 类中被覆盖(如果“checkAccess”和“hasAccess”方法必须保持标记为最终),那将是完美的。

还有其他想法吗?感谢您的帮助。

【问题讨论】:

    标签: php symfony symfony4 sonata-admin sonata


    【解决方案1】:

    解决方案是为特定的 Admin 类创建和使用自定义 SecurityHandler 服务。

    要解决我的问题,请按以下步骤操作:

    1. 创建自定义 SecurityHandler 类:
    // src/Security/Handler/TaskSecurityHandler.php
    <?php
       
    namespace App\Security\Handler;
    
    use App\Entity\Task;
    use Sonata\AdminBundle\Security\Handler\SecurityHandlerInterface;
    
    class TaskSecurityHandler extends SecurityHandlerInterface
    {
        private SecurityHandlerInterface $defaultSecurityHandler;
    
        public function __construct(SecurityHandlerInterface $defaultSecurityHandler)
        {
            $this->defaultSecurityHandler = $defaultSecurityHandler;
        }
    
        public function isGranted(AdminInterface $admin, $attributes, ?object $object = null): bool
        {
            // Handle custom access logic
            if (is_string($attributes) && 'EDIT' === $attributes && $object instanceof Task && $object->isClosed()) {
                return false;
            }
    
            // Leave default access logic
            return $this->defaultSecurityHandler->isGranted($admin, $attributes, $object);
        }
    
        public function getBaseRole(AdminInterface $admin): string
        {
            return '';
        }
    
        public function buildSecurityInformation(AdminInterface $admin): array
        {
            return [];
        }
    
        public function createObjectSecurity(AdminInterface $admin, object $object): void
        {
        }
    
        public function deleteObjectSecurity(AdminInterface $admin, object $object): void
        {
        }
    }
    
    1. 在services.yaml中注册自定义SecurityHandler类并注入默认SecurityHandler服务:
    # config/services.yaml
    
    services:
        App\Security\Handler\TaskSecurityHandler:
            arguments:
                - '@sonata.admin.security.handler' #default SecurityHandler service configured in global configuration of SonataAdminBundle
    
    1. 使用security_handler 标记指向您的自定义SecurityHandler 服务 对于特定的管理员类:
    # config/services.yaml
    
    services:
        # ...
        app.admin.task:
            class: App\Admin\TaskAdmin
            arguments: [~, App\Entity\Task, ~]
            tags:
                - { name: sonata.admin, manager_type: orm, label: Task, security_handler: App\Security\Handler\TaskSecurityHandler }
    

    【讨论】:

      猜你喜欢
      • 2015-11-15
      • 1970-01-01
      • 1970-01-01
      • 2012-09-09
      • 2015-09-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多