symfony - ApiPlatform - 基于 apiplatform 过滤器实现授权
问题描述
我正在使用ApiPlatform
和Symfony5
我在实体上放置了一个过滤器,User
以按名为的类的布尔值对它们进行排序$expose
用例:
- 对于
/users?expose=true
路由ROLE_USER
可以获取每个user
过滤器$expose
设置为的列表true
- 无论如何,
/users/
路线ROLE_ADMIN
都可以得到每一个的列表user
这是我的User
课:
/**
* @ApiResource(
* attributes={
* "normalization_context"={"groups"={"user:read", "user:list"}},
* "order"={"somefield.value": "ASC"}
* },
* collectionOperations={
* "get"={
* "mehtod"="GET",
* "security"="is_granted('LIST', object)",
* "normalization_context"={"groups"={"user:list"}},
* }
* }
* )
* @ApiFilter(ExistsFilter::class, properties={"expose"})
* @ApiFilter(SearchFilter::class, properties={
* "somefield.name": "exact"
* })
* @ORM\Entity(repositoryClass=UserRepository::class)
*/
我通过以下方式实施我的授权规则UserVoter
:
protected function supports($attribute, $subject): bool
{
return parent::supports($attribute, $subject) &&
($subject instanceof User ||
$this->arrayOf($subject, User::class) ||
(is_a($subject, Paginator::class) &&
$this->arrayOf($subject->getQuery()->getResult(), User::class))
);
}
protected function voteOnAttribute($attribute, $subject, TokenInterface $token): bool
{
/** @var User $user */
$user = $token->getUser();
if (!$user instanceof User) {
return false;
}
if ($this->accessDecisionManager->decide($token, [GenericRoles::ROLE_ADMIN])) {
return true;
}
switch ($attribute) {
case Actions::LIST:
break;
}
return false;
}
为了恢复列表, User
我恢复了通过LIST
属性传递的分页器对象,并确保请求结果中的对象是 type User
。这部分已经过测试并且可以正常工作。
现在我的问题来自这样一个事实,即这两条路线与 my 基本相同voter
,因此我通过它实施的授权规则适用于它们。
我想做的是告诉我的选民这两个请求是不同的(我认为我可以在恢复Paginator
对象时这样做,但似乎不可能)所以我可以在同一个开关盒中分别处理它们。
到目前为止,我还没有找到实现它的方法
有没有办法实现这种规则?
还是有另一种方式来实现这种授权?
谢谢!
解决方案
如果您选择坚持使用要求具有 ROLE_USER 的用户使用 /users?expose=true 的用例,您可以创建一个引发 FilterValidationException 的自定义 CollectionDataProvider:
<?php
namespace App\DataProvider;
use Symfony\Component\Security\Core\Security;
use ApiPlatform\Core\DataProvider\ContextAwareCollectionDataProviderInterface;
use ApiPlatform\Core\DataProvider\CollectionDataProviderInterface;
use ApiPlatform\Core\DataProvider\RestrictedDataProviderInterface;
use ApiPlatform\Core\Exception\FilterValidationException;
use App\Entity\User;
class UserCollectionDataProvider implements ContextAwareCollectionDataProviderInterface, RestrictedDataProviderInterface
{
/** @var CollectionDataProviderInterface */
private $dataProvider;
private $security;
/**
* @param CollectionDataProviderInterface $dataProvider The built-in orm CollectionDataProvider of API Platform
*/
public function __construct(CollectionDataProviderInterface $dataProvider, Security $security)
{
$this->dataProvider = $dataProvider;
$this->security = $security;
}
/**
* {@inheritdoc}
*/
public function supports(string $resourceClass, string $operationName = null, array $context = []): bool
{
return User::class === $resourceClass;
}
/** throws FilterValidationException */
private function validateFilters($context)
{
if ($this->security->isGranted('ROLE_ADMIN')) {
// Allow any filters, including no filters
return;
}
if (!$this->security->isGranted('ROLE_USER')) {
throw new \LogicException('No use case has been defined for this situation');
}
$errorList = [];
if (!isset($context["filters"]["expose"]) ||
$context["filters"]["expose"] !== "true" && $context["filters"]["expose"] !== '1'
) {
$errorList[] = 'expose=true filter is required.'
throw new FilterValidationException($errorList);
}
}
/**
* {@inheritdoc}
* @throws FilterValidationException;
*/
public function getCollection(string $resourceClass, string $operationName = null, array $context = []): array
{
$this->validateFilters($context);
return $this->dataProvider->getCollection($resourceClass, $operationName, $context);
}
您确实需要将以下内容添加到 api/config/services.yaml:
'App\DataProvider\UserCollectionDataProvider':
arguments:
$dataProvider: '@api_platform.doctrine.orm.default.collection_data_provider'
顺便说一句,要通过布尔值进行过滤,通常使用 BooleanFilter:
* @ApiFilter(BooleanFilter::class, properties={"expose"})
这是相关的,因为具有 ROLE_ADMIN 的用户可能会尝试通过 Expose=false 进行过滤。顺便说一句,如果 $expose 可以为空,您需要测试将 $expose 设置为 null 的用户会发生什么
警告:请注意,如果属性 $expose 不再被映射或属性 $expose 的名称已更改但在 UserCollectionDataProvider 中不是或过滤器中,您的安全性将静默失败,允许所有用户访问所有用户实体规范不是!
推荐阅读
- r - 生成 n 值为“x”和 p 值为“y”的列表
- bash - 如何捕获后台进程的PID
- php - 参数 1 传递给 Doctrine\Common\EventManager::addEventSubscriber()
- java - JPQL Query 和 Native Query 在 spring-data-jpa 中返回不同的结果
- javascript - 使用 JS/HTML5 Canvas 检测六边形中的所有像素
- pycharm - 无法在 PyCharm Professional 2018.1.3 中使用断点
- asp.net - 发布.Net core 2.0后无法找到sqlite数据库
- android - 为什么我编译源代码后没有boot.img?
- html - CSS不能淡出然后隐藏
- c - 删除未使用的变量会导致崩溃