Symfony - Api平台 - 如何自动将授权用户添加为资源所有者

时间:2018-01-01 18:58:15

标签: symfony symfony4 api-platform.com symfony-flex

我正在使用新的Symfony 4和API平台构建API,而我似乎从文档中无法收集的是如何在创建时自动将当前授权的用户添加到资源。

我正在使用JWT身份验证,并且/ api / routes受到保护,以防止未经授权的用户。我想设置它,以便当经过身份验证的用户创建新资源时(即通过向/api/articles发送Article请求),新创建的range(0, 3) 资源与经过身份验证的用户相关。

我目前正在使用每种资源类型的自定义EventSubscriber来从令牌存储中添加用户。

以下是订阅者基类的要点: https://gist.github.com/dsuurlant/5988f90e757b41454ce52050fd502273

扩展它的实体订阅者: https://gist.github.com/dsuurlant/a8af7e6922679f45b818ec4ddad36286

有没有更好的方法来使用API​​平台框架而不是为每个资源创建一个事件订阅者?

2 个答案:

答案 0 :(得分:0)

目前,我正在使用DTOs and data transformers

主要缺点是必须为每个需要这种行为的资源创建一个新的DTO。

作为一个简单的例子,我正在做这样的事情:

class BootDtoTransformer implements DataTransformerInterface
{

    private Security $security;

    public function __construct(Security $security)
    {
        $this->security = $security;
    }

    public function transform($data, string $to, array $context = [])
    {

        $owner = $this->security->getUser();

        return $new Book($owner, $data->name);;
    }

    public function supportsTransformation($data, string $to, array $context = []): bool
    {

        if ($data instanceof Book) {
            return false;
        }

        return Book::class === $to && null !== ($context['input']['class'] ?? null);
    }

}

这在逻辑上仅适用于单个资源。为了拥有用于多种资源的通用转换器,我最终使用一些接口来划分“拥有的”资源,并进行一些实例化以实例化每个类。

我本以为在非规范化阶段这是可行的,但我无法使其正常工作。

答案 1 :(得分:-1)

正如@ n​​ealio82和@lavb所说,您应该看看Gedmo \ Blameable,它可以帮助您将属性createdByupdatedBy处理为User的存储位置。资源。

Blameable StofDoctrineExtensionsBundle

然后要处理访问权限,请看一下Voters,它对处理安全性和其他访问权限非常有用。

Official Symfony documentation about Voters


例如

图书实体

...
use Gedmo\Mapping\Annotation as Gedmo;

class Book {

    ...

    /**
     * @var string $createdBy
     *
     * @Gedmo\Blameable(on="create")
     * @ORM\Column
     */
    public User $owner;

    public function getOwner() {
        return $this->owner;
    }

    public function setOwner(User $owner) {
        $this->owner = $owner
    }
}

src /安全性/投票/ BookVoter

namespace App\Security;

use App\Entity\Book;
use App\Entity\User;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Authorization\Voter\Voter;

class BookVoter extends Voter
{
    const VIEW = 'view';
    const EDIT = 'edit';

    protected function supports(string $attribute, $subject)
    {
        // if the attribute isn't one we support, return false
        if (!in_array($attribute, [self::VIEW, self::EDIT])) {
            return false;
        }

        // only vote on `Book` objects
        if (!$subject instanceof Book) {
            return false;
        }

        return true;
    }

    protected function voteOnAttribute(string $attribute, $subject, TokenInterface $token) {
        $user = $token->getUser();

        if (!$user instanceof User) {
            // the user must be logged in; if not, deny access
            return false;
        }

        /** @var Book $book */
        $book = $subject;

        switch ($attribute) {
            case self::VIEW:
                return $this->canView($book, $user);
            case self::EDIT:
                return $this->canEdit($book, $user);
        }

        throw new \LogicException('This code should not be reached!');
    }

    private function canEdit(Book $book, User $user) {
        // ONLY OWNER CAN EDIT BOOK
        return $user === $book->getOwner();
    }

    private function canView(Book $book, User $user) {
        // DIFFERENT LOGIC ?
        return $user === $book->getOwner();
    }

    ...

}