问题描述
是否可以在实体上实现 ApiResource 安全性,以便没有实体 id 的 GET 请求(即集合操作)仅返回与当前经过身份验证的用户匹配的项目(或者,为此目的,必须返回任何其他规则)针对每个实体进行检查)?
到目前为止我的实现:
/**
* @ORM\Entity(repositoryClass=UserRepository::class)
* @ORM\Table(name="`user`")
* @ApiResource(
* attributes={"security"="is_granted('ROLE_USER')"},* collectionoperations={"get"={"security"="is_granted('ROLE_ADMIN') or object == user"}},* itemOperations={"get"={"security"="is_granted('ROLE_ADMIN') or object == user"}},* )
*/
class User implements UserInterface
{
/**
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
*/
private ?int $id;
// ...
}
上面的代码适用于项目操作,但不适用于集合操作符,即假设用户 id。 1(不是管理员)已通过身份验证:
- GET
/api/user/1
使用用户 #1 数据返回 200 OK(根据需要) - GET
/api/user/2
返回 403 FORBIDDEN(根据需要,用户 1 不应能够获取其他用户的数据)。 - GET
/api/user
返回 403 FORBIDDEN(NOT 根据需要,期望的行为将返回一个列表,该列表仅包含通过规则的实体,即用户 #1 的数据)。立>
解决方法
为此使用 Doctrine Extension。这不是安全问题,您不想限制对资源的访问,而是要修改返回的结果。
例如,匆忙修改链接文档上的示例:
final class CurrentUserExtension implements QueryCollectionExtensionInterface
{
public function __construct(private Security $security)
{
}
public function applyToCollection(QueryBuilder $queryBuilder,QueryNameGeneratorInterface $queryNameGenerator,string $resourceClass,string $operationName = null): void
{
if (User::class !== $resourceClass
|| $this->security->isGranted('ROLE_ADMIN')
|| null === $user = $this->security->getUser())
{
return;
}
$rootAlias = $queryBuilder->getRootAliases()[0];
$queryBuilder->andWhere(sprintf('%s.id = :current_user',$rootAlias));
$queryBuilder->setParameter('current_user',$user->getId());
}
}