当我想要向具有不同角色的用户授予访问权限时,我因问题而被阻止. 当我在表上下文中存储一些角色时,我为此创建了一个定制投票器.

<?php

namespace App\Security\Voter;

use App\Constants\UserRoles;
use App\Entity\Team;
use App\Entity\User;
use App\Repository\UserTeamRepository;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Authorization\Voter\Voter;

class TeamVoter extends Voter
{

    public function __construct(
        private UserTeamRepository $userTeamRepository,
    )
    {}

    /**
     * @inheritDoc
     */
    protected function supports(string $attribute, mixed $subject): bool
    {
        if (!in_array($attribute, UserRoles::TEAM_ROLES)) {
            return false;
        }

        return $subject instanceof Team;
    } 

    /**
     * @inheritDoc
     */
    protected function voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token): bool
    {
        $user = $token->getUser();
        if (!$user instanceof User) {
            return false;
        }
        $team = $subject;

        if (in_array($attribute, UserRoles::TEAM_RELATED_ROLES)) {
            return $this->voteOnTeamRelatedAttribute($attribute, $user, $team);
        }

        return match($attribute) {
            UserRoles::ROLE_TEAM_ADMIN => $this->canEdit($user, $team),
            UserRoles::ROLE_TEAM_OWNER => $this->canDelete($user, $team),
            default => false,
        };
    }

    private function voteOnTeamRelatedAttribute(string $attribute, User $user, Team $team): bool
    {
        $userRoles = $this->userTeamRepository->getUserTeamRoles($user, $team->getExternalUuid());

        if (!$userRoles) {
            return false;
        }

        return match($attribute) {
            UserRoles::ROLE_TEAM_MEMBER_ADMIN => in_array(UserRoles::ROLE_TEAM_MEMBER_ADMIN, $userRoles),
            UserRoles::ROLE_TEAM_MEMBER => in_array(UserRoles::ROLE_TEAM_MEMBER, $userRoles),
            default => false,
        };
    }

    private function canEdit(User $user, Team $team): bool
    {
        if ($this->canDelete($user, $team)) {
            return true;
        }

        return in_array(UserRoles::ROLE_TEAM_ADMIN, $user->getRoles());
    }

    public function canDelete(User $user, Team $team): bool
    {
        return $team->getOwner() === $user || $team->getCreator() === $user;
    }
}

现在,我希望角色为ROLE_TEAM_ADMIN的用户或角色为ROLE_TEAM_MEMBER_ADMIN的用户可以访问该方法(note: ROLE_TEAM_ADMIN更像是一个编辑所有团队的全局角色,而ROLE_TEAM_MEMBER_ADMIN更专注于单个团队,因此自定义投票者判断mm表的内容)

下面是我的控制器中的受保护方法:

    #[Route('/rest/team/{externalUuid}/member/add', name: 'add_team_member', methods: ["PUT"])]
    #[IsGranted(UserRoles::ROLE_TEAM_ADMIN, subject: 'team'), IsGranted(UserRoles::ROLE_TEAM_MEMBER_ADMIN, subject: 'team')]
    public function addTeamMember(Request $request, Team $team): JsonResponse
    {
        $request = $this->transformJsonBody($request);
    ...

documentation表示可以配置拨款决策策略,但其缺省值被设置为"肯定",这意味着如果参与拨款的选民之一说可以,那么访问权限就会被授予.

我的问题是,这并不像预期的那样起作用,而是遵循"一致"的决策模式.我已经浏览了这个主题的Stackoverflow,有些人建议使用表达式,但这些答案已经过时了,因为它是针对symfony 5和不建议使用的Sensio安全包的.

在调试它之后,我注意到Symfony\Component\Security\Core\Authorization\Authorization DecisionManager类收集结果(在collectResults方法中),寻找投票者并在此基础上构建其决策,但属性数组始终只有一个属性.

所以我猜我的注释是以错误的方式定义的,但我在文档中找不到任何关于如何处理决策经理的相关示例,显然设置一个又一个注释是不够的.

有没有人有好办法来处理这件事?

推荐答案

我相信使用多个IsGranted()调用与使用表达式$this->isGranted() && $this->isGranted()相同.

要使用OR逻辑运算符而不是AND,请看一下Expression syntax.您可以在底座#[IsGranted(string)]的能力之上进行非常复杂的判断:

use Symfony\Component\ExpressionLanguage\Expression;

// ...

    #[Route('/rest/team/{externalUuid}/member/add', name: 'add_team_member', methods: ["PUT"])]
    #[IsGranted(
        new Expression(
            'is_granted("' . UserRoles::ROLE_TEAM_ADMIN . '", subject) or ' .
            'is_granted("' . UserRoles::ROLE_TEAM_MEMBER_ADMIN . '", subject)'
        ),
        subject: 'team'
    ]
    public function addTeamMember(Request $request, Team $team): JsonResponse
    {
        $request = $this->transformJsonBody($request);
        // ...
    }

Php相关问答推荐

在WooCommerce中处理客户总支出中的退款

dockerized laravel服务器拒绝POST请求

为什么调用干预\映像引发错误驱动程序\GD\Driver not found?

如何优化-PHP 7.3.14+Laravel 6

如果WooCommerce中存在特定的运输方式,则隐藏其他运输方式

在AJAX请求后,Laravel重定向.(不允许使用方法)

如何在php中生成包含第100秒的时间序列?

将客户重定向到自定义感谢页后的空白页

在带有livewire 3的laravel中使用规则方法时,无法进行实时验证

Laravel Carbon DiffForHumans选项(年,月,天)

允许在WooCommerce管理订单列表中使用计费邮箱进行搜索

如何在自定义邮箱内容中获取WooCommerce订单项目的详细信息?

Filament v3:具有 hasMany 和 BelongsTo 的关系管理器

致命错误:未捕获 Google_Exception:客户端机密 JSON 文件无效.在 /somepath/lib/vendor/google/apiclient/src/Google/Client.php

如何搜索和替换 XML 文件中的一段文本?

如何从此字符串创建 Carbon 对象:2022-06-21T05:52:46.648533678Z?

按数字然后按字符串对关联 PHP 数组进行排序

Php 的新WeakMap - 枚举曾经被垃圾收集过吗?

图像不会显示在生产中的 Laravel 应用程序中

fopen 功能不断向我发送我重新加载网页时已发布的重复版本的表单