Аннотации избирателей Symfony 4 (@IsGranted)

Я пытаюсь использовать Symfony Voters и Controller Annotation, чтобы разрешить или ограничить доступ к определенным действиям в моем приложении Symfony 4.

Например, «Мой интерфейс» предоставляет возможность удалить «Сообщение», но только если у пользователя установлен атрибут «DELETE_POST» для этого поста.

Внешний интерфейс отправляет действие HTTP «DELETE» в мою конечную точку Symfony, передавая идентификатор сообщения в URL-адресе (например, /api/post/delete/19).

Пытаюсь использовать аннотацию @IsGranted, как описано здесь.

Вот моя конечная точка Symfony:

/**
 * @Route("/delete/{id}")
 * @Method("DELETE")
 * @IsGranted("DELETE_POST", subject = "post")
 */
public function deletePost($post)  {
    ... some logic to delete post
    return new Response("Deleting " . $post->getId());
}

Вот мой избиратель:

class PostVoter extends Voter {

    private $attributes = array(
        "VIEW_POST", "EDIT_POST", "DELETE_POST", "CREATE_POST"
    );

    protected function supports($attribute, $subject) { 
        return in_array($attribute, $this->attributes, true)  &&  $subject instanceof Post;
    }

    protected function voteOnAttribute($attribute, $subject, TokenInterface $token) {
        ... logic to figure out if user has permissions.

        return $check;

    }
}

Проблема, с которой я столкнулся, заключается в том, что мой интерфейс просто отправляет идентификатор ресурса в мою конечную точку. Затем Symfony разрешает аннотацию @IsGranted, вызывая Voters и передавая атрибут "DELETE_POST" и идентификатор сообщения.

Проблема в том, что $ post - это просто идентификатор сообщения, а не фактический объект Post. Поэтому, когда избиратель достигает $subject instanceof Post, он возвращает false.

Я попытался ввести Post в свой метод контроллера, изменив сигнатуру метода на public function deletePost(Post $post). Конечно, это не работает, потому что javascript отправляет идентификатор в URL-адресе, а не объект Post.

(Кстати: я знаю, что этот тип внедрения должен работать с Doctrine, но я использую Doctrine как нет).

Мой вопрос в том, как мне заставить @IsGranted понять, что «пост» должен быть объектом поста? Есть ли способ указать ему искать сообщение по переданному идентификатору и оценивать его на основе этого? Или даже обратиться к другому методу контроллера, чтобы определить, что должен представлять subject = "post"?

Спасибо.

ОБНОВИТЬ

Благодаря @NicolasB я добавил ParamConverter:

class PostConverter implements ParamConverterInterface {
    private $dao;

    public function __construct(MySqlPostDAO $dao) {
        $this->dao = $dao;
    }

    public function apply(Request $request, ParamConverter $configuration)  {
        $name = $configuration->getName();            
        $object = $this->dao->getById($request->get("id"));

        if (!$object)  {
            throw new NotFoundHttpException("Post not found!");
        }

        $request->attributes->set($name, $object);

        return true;
    }

    public function supports(ParamConverter $configuration) {
        if ($configuration->getClass() === "App\\Model\\Objects\\Post")  {
            return true;
        }

        return false;
    }
}

Кажется, это работает, как ожидалось. Мне даже не пришлось использовать аннотацию @ParamConverter, чтобы она работала. Единственным другим изменением, которое мне пришлось внести в контроллер, было изменение сигнатуры метода моего маршрута на public function deletePost(Post $post) (как я пробовал ранее - но теперь работает благодаря моему PostConverter).

Мои последние два вопроса:

  1. Что именно я должен проверить в методе supports()? Сейчас я просто проверяю соответствие класса. Следует ли мне также проверить этот $configuration->getName() == "id", чтобы убедиться, что я работаю с правильным полем?

  2. Как я могу сделать его более общим? Правильно ли я предполагаю, что каждый раз, когда вы вводите объект в метод контроллера, Symfony будет вызывать метод supports для всего, что реализует ParamConverterInterface?

Спасибо.

api/post/delete/19 - не совсем конечная точка REST. Это должен быть api/post/19, и вы знаете, что это удаление из команды HTTP. У вас была особая причина, почему бы не сделать это как api/post/19?
Gil Sousa 02.10.2018 06:19
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Symfony Station Communiqué - 7 июля 2023 г
Symfony Station Communiqué - 7 июля 2023 г
Это коммюнике первоначально появилось на Symfony Station .
Оживление вашего приложения Laravel: Понимание режима обслуживания
Оживление вашего приложения Laravel: Понимание режима обслуживания
Здравствуйте, разработчики! В сегодняшней статье мы рассмотрим важный аспект управления приложениями, который часто упускается из виду в суете...
Установка и настройка Nginx и PHP на Ubuntu-сервере
Установка и настройка Nginx и PHP на Ubuntu-сервере
В этот раз я сделаю руководство по установке и настройке nginx и php на Ubuntu OS.
Коллекции в Laravel более простым способом
Коллекции в Laravel более простым способом
Привет, читатели, сегодня мы узнаем о коллекциях. В Laravel коллекции - это способ манипулировать массивами и играть с массивами данных. Благодаря...
Как установить PHP на Mac
Как установить PHP на Mac
PHP - это популярный язык программирования, который используется для разработки веб-приложений. Если вы используете Mac и хотите разрабатывать...
1
1
1 454
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Что бы произошло, если бы вы использовали Doctrine, так это то, что вам нужно было бы ввести свою переменную $post. После того, как вы это сделаете, об остальном позаботится Doctrine ParamConverter. Прямо сейчас Symfony не знает, как связать заполнитель URL-адреса id с параметром $post, потому что он не знает, к какому объекту относится $post. Подсказывая тип с помощью чего-то вроде public function deletePost(Post $post) и используя ParamConverter, Symfony будет знать, что $post относится к объекту Post с идентификатором из заполнителя URL-адреса id.

Из документа:

Normally, you'd expect a $id argument to show(). Instead, by creating a new argument ($post) and type-hinting it with the Post class (which is a Doctrine entity), the ParamConverter automatically queries for an object whose $id property matches the {id} value. It will also show a 404 page if no Post can be found.

Тогда избиратель также узнает, что такое $post и как с ним лечить.


Теперь, поскольку вы не используете Doctrine, у вас нет ParamConverter по умолчанию, и, как мы только что видели, это ключевой элемент здесь. Итак, что вам нужно сделать, это просто определить свой собственный ParamConverter.

Эта страница документации Symfony расскажет вам больше о том, как это сделать, особенно последний раздел «Создание конвертера». Вам нужно будет рассказать ему, как преобразовать строку "id" в объект Post, используя логику вашей модели. Сначала вы можете сделать его очень специфичным для объектов Post (и вы можете явно указать этот ParamConverter в аннотации, используя опцию converter = "name"). Позже, когда у вас будет рабочая версия, вы можете сделать ее более общей.

Другие вопросы по теме