php - 如何在不覆盖 checkAccess 和 hasAccess 方法的情况下控制对操作的访问
问题描述
从 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 在我的情况下)。因此,具有超级管理员角色的用户仍然可以编辑任务对象,即使它已关闭。
另一个想法是为此 TaskAdmin 创建控制器并覆盖“preEdit”方法并检查对象是否已关闭并拒绝访问。这个解决方案也不完美,因为在模板中的许多地方都会触发“hasAccess”方法来检查 UI 的某些部分是否应该可见(例如编辑按钮),所以用户仍然会看到编辑按钮但不会能够输入编辑操作(防止在控制器级别)。
如果“preCheckAccess”和“preHasAccess”等方法可以在 Admin 类中被覆盖(如果“checkAccess”和“hasAccess”方法必须保持标记为最终),那将是完美的。
还有其他想法吗?谢谢你的帮助。
解决方案
解决方案是为特定的 Admin 类创建和使用自定义 SecurityHandler 服务。
要解决我的问题,请按照下列步骤操作:
- 创建自定义 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
{
}
}
- 在 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
- 使用
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 }
推荐阅读
- javascript - Angular2-mentions - 如何从 Angular 4 中的 angular2-mentions 添加 URL 链接到提到的用户
- linux - 为 linux 构建没有 PNG_READ_eXIf_SUPPORTED 的 libpng
- javascript - 在 UWP Javascript 中,有什么方法可以检测到辅助窗口已关闭?
- javascript - Javascript 阻止多次执行
- python-3.x - 我需要帮助来解决这个问题,我无法理解错误
- python - 熊猫同时将单行拆分为多列中的多行
- angular - 仍然可以在 Mat 表中选择禁用的复选框
- blockchain - 当我想将 remix 连接到我的本地仲裁网络时,我使用哪个端口?
- sql - 如何在 Pentaho Kettle Transformation 中创建 value Buffer
- windows - icacls 并删除意外添加的权限?