Restreindre l'ajout d'un message

A ce stade, si nous utilisons la route POST discussions/{id}/messages nous pouvons écrire sur n'importe qu'elle Discussion. Nous allons restreindre l'écriture à l'auteur ou au participant.

Nous allons créer un Voter que nous appellerons dans notre CreateMessageInputDataPersister.

<?php

// src/Security/Voter/DiscussionVoter.php

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

class DiscussionVoter extends Voter
{
    protected function supports($attribute, $subject)
    {
        return in_array($attribute, ['MESSAGE_CREATE'])
            && $subject instanceof \App\Entity\Discussion;
    }

    /**
     * @param $attribute
     * @param Discussion $subject
     * @param TokenInterface $token
     * @return false
     */
    protected function voteOnAttribute($attribute, $subject, TokenInterface $token)
    {
        $user = $token->getUser();
        // if the user is anonymous, do not grant access
        if (!$user instanceof UserInterface) {
            return false;
        }

        switch ($attribute) {
            case 'MESSAGE_CREATE':
                if ($subject->getAuthor() === $user) {
                    return true;
                }
                if ($subject->isUserInParticipant($user)) {
                    return true;
                }
                break;
        }

        return false;
    }
}

Dans ce voter on envoie la Discussion dans $subject. On regarde ensuite si on est auteur de la Discussion ou si on fait partie des participants, si l'un des deux est vrai alors on peut enregistrer notre Message.

Nous avons besoin de créer une nouvelle méthode qui vérifie si on est dans les participants. Cette méthode sera sur l'entité Discussion

public function isUserInParticipant(User $user): bool
{
    return $this->getParticipants()->contains($user);
}
``

Maintenant on se rend dans notre CreateMessageInputDataPersister pour appeler notre Voter en utilisant AuthorizationCheckerInterface

```php
<?php

namespace App\DataPersister;

use ApiPlatform\Core\DataPersister\ContextAwareDataPersisterInterface;
use App\Entity\Message;
use App\Repository\DiscussionRepository;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
use Symfony\Component\Security\Core\Security;

class CreateMessageInputDataPersister implements ContextAwareDataPersisterInterface
{
    /**
     * @var RequestStack
     */
    private $requestStack;
    /**
     * @var DiscussionRepository
     */
    private $discussionRepository;
    /**
     * @var EntityManagerInterface
     */
    private $entityManager;
    /**
     * @var Security
     */
    private $security;
    /**
     * @var AuthorizationCheckerInterface
     */
    private $authorizationChecker;

    public function __construct(
        RequestStack $requestStack,
        DiscussionRepository $discussionRepository,
        EntityManagerInterface $entityManager,
        Security $security,
        AuthorizationCheckerInterface $authorizationChecker
    )
    {
        $this->requestStack = $requestStack;
        $this->discussionRepository = $discussionRepository;
        $this->entityManager = $entityManager;
        $this->security = $security;
        $this->authorizationChecker = $authorizationChecker;
    }

    public function supports($data, array $context = []): bool
    {
        return $data instanceof Message
            && (array_key_exists('collection_operation_name', $context)
            && $context['collection_operation_name'] === 'createNewMessage');
    }

    /**
     * @param Message $data
     * @param array $context
     * @return void
     */
    public function persist($data, array $context = [])
    {
        $user = $this->security->getUser();
        $id = $this->requestStack->getMasterRequest()->attributes->get('id');
        $discusssion = $this->discussionRepository->find($id);

        if (!$this->authorizationChecker->isGranted('MESSAGE_CREATE', $discusssion)) {
            throw new AccessDeniedException();
        }

        $data->setDiscussion($discusssion);
        $data->setAuthor($user);

        $this->entityManager->persist($data);
        $this->entityManager->flush();

        return $data;
    }

    public function remove($data, array $context = [])
    {
    }
}

Avec la méthode isGranted on regarde si on à accès à cette Discussion, si oui on enregistre notre Message, si non alors Access Denied. est retourné avec une Réponse 403

prev next

Commentaires

Connectez-vous pour laisser un commentaire