Как с помощью Api-Platform автоматически добавить авторизованного пользователя в качестве владельца ресурса при его создании?

Как автоматически добавить текущего авторизованного пользователя к ресурсу при создании (POST).

Я использую аутентификацию JWT, а маршруты /api/ защищены от неавторизованных пользователей. Я хочу настроить его так, чтобы когда аутентифицированный пользователь создавал новый ресурс (т. е. отправляя запрос POST на /api/articles), вновь созданный Article ресурс был связан с аутентифицированным пользователем.

В настоящее время я использую пользовательский EventSubscriber для каждого типа ресурса, чтобы добавить пользователя из хранилища токенов.

Вот суть для базового класса подписчика: а>

И подписчик объекта, который его расширяет: https://gist.github.com/dsuurlant/a8af7e6922679f45b818ec4ddad36286

Однако это не работает, если, например, конструктор объекта требует пользователя в качестве параметра.

E.g.

class Book {

    public User $owner;
    public string $name;

    public class __construct(User $user, string $name) {
        $this->owner = $user;
        $this->name  = $name;
    }
}

Как автоматически внедрить авторизованного пользователя при создании объекта?


person Danielle Suurlant    schedule 01.01.2018    source источник
comment
Gedmo\Blameable должно помочь вам atlantic18.github.io/DoctrineExtensions/doc/blameable.html   -  person nealio82    schedule 09.01.2018
comment
Я также рекомендую Gedmo\Blameable =› symfony.com/doc/current/bundles/ StofDoctrineExtensionsBundle/   -  person lavb    schedule 15.01.2018
comment
Почему вы хотите ввести что-то волшебным образом? Простая абстракция интерфейса factory для сущности, и все готово.   -  person emix    schedule 13.06.2020


Ответы (2)


Как сказали @nealio82 и @lavb, вам следует взглянуть на Gedmo\Blameable, которые помогут вам обрабатывать свойства как createdBy или updatedBy, где вы можете хранить User, создающих ресурс.

Обвинительный StofDoctrineExtensionsBundle

Затем, чтобы справиться с доступом, взгляните на Voters, который отлично справляется с безопасностью и другим доступом.

Официальная документация Symfony об избирателях


e.g

Книжный объект

...
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/Безопасность/Voter/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();
    }

    ...

}
person D. Schreier    schedule 13.06.2020
comment
Это не то, чего я хочу. Я не только не хочу зависеть от расширения доктрины; но вопрос показывает пример, когда хозяин установлен при строительстве; обеспечение того, чтобы сущность всегда находилась в допустимом состоянии. Обвиняемый означает, что мне нужны сущности с нулевыми владельцами; что противоречило бы спецификации домена. - person yivi; 13.06.2020
comment
Избиратели не имеют отношения к вопросу. Спасибо, в любом случае. - person yivi; 13.06.2020
comment
Почему обнуляемые владельцы? Blameable автоматически свяжет пользователя с ресурсом при сохранении (или сбросе, я забыл). - person D. Schreier; 13.06.2020
comment
Но при создании экземпляра свойство не задано. Опять же, как показано в вопросе, приоритет должен быть отдан строительству. Спасибо, в любом случае. - person yivi; 13.06.2020

В настоящее время я использую DTO и преобразователи данных.

Основным недостатком является необходимость создания нового 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);
    }

}

Это логически работает только для одного ресурса. Чтобы иметь общий преобразователь для нескольких ресурсов, я в конечном итоге использую некоторые интерфейсы для разделения «принадлежащих» ресурсов и немного размышлений для создания экземпляра каждого класса.

Я бы подумал, что это выполнимо на этапе денормализации, но я не мог заставить это работать.

person yivi    schedule 14.06.2020