繁体   English   中英

如何在不覆盖 checkAccess 和 hasAccess 方法的情况下控制对操作的访问

[英]How to control access to actions without override checkAccess and hasAccess methods

从 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;
      }
   }

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

在 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);
      }
   }

当然,现在我无法覆盖这些方法。

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

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

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

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

您可以使用选民

在其中检查任务是否已关闭

switch ($attribute) {
    case self::EDIT:
        return !$subject->isClosed();
}

如果您有多个投票者(您的和/或 Sonata 投票者),请查看访问决策策略

例如,您可以将策略设置为unanimous ,这将使用户只有在奏鸣曲您的选民授予访问权限时才能访问

另一个例子,你可以在你的选民中定义整个逻辑(也检查用户角色)并将策略设置为priority ,然后你可以给你的选民一个高优先级,这样你的选民将始终被执行而不是奏鸣曲

最后阅读所有选项并为您做最合适的解决方案,因为我们不知道您所有的代码上下文

解决方案是为特定的 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标记指向特定 Admin 类的自定义 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 }

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM