I'm coming to you because i have an issue and i can't resolve it.
On my website, an user can upload pictures and videos. He can also delete them. But when he tries to delete a picture or a video, he got this error:
"No user token or you forgot to put your controller behind a firewall while using a @Security tag."
I already use @Security with other functions and it works. It doesn't work only for functions pictureDelete and videoDelete.
pictureDelete function :
/** Page de suppression d'une image
*
* @Route("/images/suppression/{id}/", name="delete_picture")
* @Security("is_granted('ROLE_USER')")
*/
public function pictureDelete(Picture $picture, Request $request): Response
{
if(!$this->isCsrfTokenValid('delete_picture_' . $picture->getId(), $request->query->get('csrf_token') )){
$this->AddFlash('error', 'Token sécurité invalide, veuillez ré-essayer.');
} else {
$em = $this->getDoctrine()->getManager();
$em->remove($picture);
$em->flush();
//TODO: le message flash ne s'affiche pas sur la bonne page
$this->addFlash('success', 'L\'image a été supprimée avec succès');
}
return $this->redirectToRoute('pictures');
}
videoDelete function :
/** Page de suppression d'une vidéo
*
* @Route("/videos/suppression/{id}/", name="delete_video")
* @Security("is_granted('ROLE_USER')")
*/
public function videoDelete(Video $video, Request $request): Response
{
if(!$this->isCsrfTokenValid('delete_video_' . $video->getId(), $request->query->get('csrf_token') )){
$this->AddFlash('error', 'Token sécurité invalide, veuillez ré-essayer.');
} else {
$em = $this->getDoctrine()->getManager();
$em->remove($video);
$em->flush();
//TODO: le message flash ne s'affiche pas sur la bonne page
$this->addFlash('success', 'La vidéo a été supprimée avec succès');
}
return $this->redirectToRoute('videos');
}
security.yaml :
security:
encoders:
App\Entity\User:
algorithm: auto
# https://symfony.com/doc/current/security.html#where-do-users-come-from-user-providers
providers:
# used to reload user from session & other features (e.g. switch_user)
app_user_provider:
entity:
class: App\Entity\User
property: email
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
main:
anonymous: true
lazy: true
provider: app_user_provider
guard:
authenticators:
- App\Security\LoginFormAuthenticator
logout:
path: app_logout
# where to redirect after logout
target: home
# activate different ways to authenticate
# https://symfony.com/doc/current/security.html#firewalls-authentication
# https://symfony.com/doc/current/security/impersonating_user.html
# switch_user: true
role_hierarchy:
ROLE_ADMIN: ROLE_USER
# Easy way to control access for large sections of your site
# Note: Only the *first* access control that matches will be used
access_control:
# - { path: ^/admin, roles: ROLE_ADMIN }
# - { path: ^/profile, roles: ROLE_USER }
My controller (with function addPicture and addVideo) :
<?php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;
use App\Entity\User;
use App\Entity\Picture;
use App\Form\PictureType;
use App\Entity\Video;
use App\Form\VideoType;
use \DateTime;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
use App\Form\UserType;
use Knp\Component\Pager\PaginatorInterface;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
use Symfony\Component\Security\Core\Validator\Constraints\UserPassword;
use Symfony\Component\Security\Core\Validator\Constraints\UserPasswordValidator;
class MainController extends AbstractController
{
/** Page d'accueil
*
* @Route("/", name="home")
*/
public function index(): Response
{
$pictureRepository = $this->getDoctrine()->getRepository(Picture::class);
$pictureFound = $pictureRepository->LastThreePic();
$videoRepository = $this->getDoctrine()->getRepository(Video::class);
$videoFound = $videoRepository->LastThreeVid();
return $this->render('main/home.html.twig', [
'picture' => $pictureFound,
'video' => $videoFound
]);
}
/** Page de profil
*
* @Route("/mon-profil", name="profil")
* @Security("is_granted('ROLE_USER')")
*/
public function profil(): Response
{
return $this->render('main/profil.html.twig');
}
/** Page de modification de profil
*
* @Route("/modifier-mon-profil/", name="edit_profil")
* @Security("is_granted('ROLE_USER')")
*/
public function editProfil(Request $request, UserPasswordEncoderInterface $encoder): Response
{
$user = $this->getUser();
$form = $this->createForm(UserType::class, $user);
$form->handleRequest($request);
if($form->isSubmitted() && $form->isValid()){
$em = $this->getDoctrine()->getManager();
$newPassword = $form->get('plainPassword')['first']->getData();
// Grâce au service, on génère un nouveau hash de notre nouveau mot de passe
$hashOfNewPassword = $encoder->encodePassword($user, $newPassword);
// On change l'ancien mot de passe hashé par le nouveau que l'on a généré juste au dessus
$user->setPassword( $hashOfNewPassword );
$em->flush();
$this->addFlash('success', 'Profil modifié avec succès.');
return $this->redirectToRoute('profil');
}
// Pour que la vue puisse afficher le formulaire, on doit lui envoyer le formulaire généré, avec $form->createView()
return $this->render('main/editProfil.html.twig', [
'editProfilForm' => $form->createView()
]);
}
/** Fonction permettant de supprimer son compte depuis la page de profil
*
* @Route("/delete-account", name="delete_account")
* @Security("is_granted('ROLE_USER')")
*/
public function deleteAccount(): Response
{
$user = $this->getUser();
$this->container->get('security.token_storage')->setToken(null);
$em = $this->getDoctrine()->getManager();
$em->remove($user);
$em->flush();
$this->addFlash('success', 'Votre compte a bien été supprimé.');
return $this->redirectToRoute('home');
}
/** Page des catégories d'images
*
* @Route("/categorie/images", name="pictures")
*/
public function pictures(): Response
{
return $this->render('main/pictures.html.twig');
}
/** Page des catégories de vidéos
*
* @Route("/categorie/videos", name="videos")
*/
public function videos(): Response
{
return $this->render('main/videos.html.twig');
}
/** Page des images favorites
*
* @Route("/favoris/images", name="fav_pic")
*/
public function favPic(): Response
{
$pictureRepository = $this->getDoctrine()->getRepository(Picture::class);
$picturesFound = $pictureRepository->findAll();
return $this->render('main/favPic.html.twig', [
'pictures' => $picturesFound
]);
}
/** Page des vidéos favorites
*
* @Route("/favoris/videos", name="fav_vid")
*/
public function favVid(): Response
{
$videoRepository = $this->getDoctrine()->getRepository(Video::class);
$videosFound = $videoRepository->findAll();
return $this->render('main/favVid.html.twig', [
'videos' => $videosFound
]);
}
/** Page d'ajout d'image en favoris
*
* @Route("/favoris/ajout/{id}", name="add_fav_pic")
*/
public function addFavPic(Picture $picture): Response
{
if(!$picture){
throw new NotFoundHttpException('Pas d\'image trouvée.');
}
$picture->addFav($this->getUser());
$em = $this->getDoctrine()->getManager();
$em->persist($picture);
$em->flush();
$this->addFlash('success', 'Image ajoutée aux favoris avec succès.');
return $this->redirectToRoute('fav_pic');
}
/** Page d'ajout de vidéo en favoris
*
* @Route("/favoris/videos/ajout/{id}", name="add_fav_vid")
*/
public function addFavVid(Video $video): Response
{
if(!$video){
throw new NotFoundHttpException('Pas de vidéo trouvée.');
}
$video->addFav($this->getUser());
$em = $this->getDoctrine()->getManager();
$em->persist($video);
$em->flush();
$this->addFlash('success', 'Vidéo ajoutée aux favoris avec succès.');
return $this->redirectToRoute('fav_vid');
}
/** Fonction pour supprimer une image de ses favoris
*
* @Route("/favoris/images/suppression/{id}", name="del_fav_pic")
*/
public function deleteFavPic(Picture $picture): Response
{
if(!$picture){
throw new NotFoundHttpException('Pas d\'image trouvée.');
}
$picture->removeFav($this->getUser());
$em = $this->getDoctrine()->getManager();
$em->persist($picture);
$em->flush();
$this->addFlash('success', 'Image supprimée des favoris avec succès.');
return $this->redirectToRoute('fav_pic');
}
/** Fonction pour supprimer une vidéo de ses favoris
*
* @Route("/favoris/videos/suppression/{id}", name="del_fav_vid")
*/
public function deleteFavVid(Video $video): Response
{
if(!$video){
throw new NotFoundHttpException('Pas de vidéo trouvée.');
}
$video->removeFav($this->getUser());
$em = $this->getDoctrine()->getManager();
$em->persist($video);
$em->flush();
$this->addFlash('success', 'Vidéo supprimée des favoris avec succès.');
return $this->redirectToRoute('fav_vid');
}
/** Page de suppression d'une image
*
* @Route("/images/suppression/{id}/", name="delete_picture")
* @Security("is_granted('ROLE_USER')")
*/
//TODO: @Security fait tout planter
public function pictureDelete(Picture $picture, Request $request): Response
{
if(!$this->isCsrfTokenValid('delete_picture_' . $picture->getId(), $request->query->get('csrf_token') )){
$this->AddFlash('error', 'Token sécurité invalide, veuillez ré-essayer.');
} else {
$em = $this->getDoctrine()->getManager();
$em->remove($picture);
$em->flush();
//TODO: le message flash ne s'affiche pas sur la bonne page
$this->addFlash('success', 'L\'image a été supprimée avec succès');
}
return $this->redirectToRoute('pictures');
}
/** Page de suppression d'une vidéo
*
* @Route("/videos/suppression/{id}/", name="delete_video")
* @Security("is_granted('ROLE_USER')")
*/
//TODO: @Security fait tout planter
public function videoDelete(Video $video, Request $request): Response
{
if(!$this->isCsrfTokenValid('delete_video_' . $video->getId(), $request->query->get('csrf_token') )){
$this->AddFlash('error', 'Token sécurité invalide, veuillez ré-essayer.');
} else {
$em = $this->getDoctrine()->getManager();
$em->remove($video);
$em->flush();
//TODO: le message flash ne s'affiche pas sur la bonne page
$this->addFlash('success', 'La vidéo a été supprimée avec succès');
}
return $this->redirectToRoute('videos');
}
/** Page d'ajout d'une image
*
* @Route("/ajouter-une-image", name="add_pic")
* @Security("is_granted('ROLE_USER')")
*/
public function addPicture(Request $request): Response
{
// Création d'un nouvel objet de la classe Article, vide pour le moment
$newPicture = new Picture();
// Création d'un nouveau formulaire à partir de notre formulaire ArticleType et de notre nouvel article encore vide
$form = $this->createForm(PictureType::class, $newPicture);
$form->handleRequest($request);
// Si le formulaire est envoyé et sans erreur
if($form->isSubmitted() && $form->isValid()){
$newPicture->setPublicationDate(new DateTime());
$newPicture->setAuthor($this->getUser());
// Extraction de l'objet de la photo envoyée dans le formulaire
$picture = $form->get('image')->getData();
// Création d'un nouveau nom aléatoire pour la photo avec son extension (récupérée via la méthode guessExtension() )
$newFileName = md5(time() . rand() . uniqid() ) . '.' . $picture->guessExtension();
$em = $this->getDoctrine()->getManager();
$newPicture->setImage($newFileName);
$em->persist($newPicture);
$em->flush();
// Déplacement de la photo dans le dossier que l'on avait paramétré dans le fichier services.yaml, avec le nouveau nom qu'on lui a généré
$picture->move(
$this->getParameter('app.user.pictures.directory'), // Emplacement de sauvegarde du fichier
$newFileName // Nouveau nom du fichier
);
// Petit message de succès
$this->addFlash('success', 'Image envoyée avec succès !');
// Redirection sur une autre page du site
return $this->redirectToRoute('home');
}
return $this->render('main/addPicture.html.twig', [
'addPicForm' => $form->createView()
]);
}
/** Page d'ajout d'une vidéo
*
* @Route("/ajouter-une-video", name="add_vid")
* @Security("is_granted('ROLE_USER')")
*/
public function addVideo(Request $request): Response
{
// Création d'un nouvel objet de la classe Article, vide pour le moment
$newVideo = new Video();
// Création d'un nouveau formulaire à partir de notre formulaire ArticleType et de notre nouvel article encore vide
$form = $this->createForm(VideoType::class, $newVideo);
$form->handleRequest($request);
// Si le formulaire est envoyé et sans erreur
if($form->isSubmitted() && $form->isValid()){
$newVideo->setPublicationDate(new DateTime());
$newVideo->setAuthor($this->getUser());
// Extraction de l'objet de la photo envoyée dans le formulaire
$video = $form->get('vid')->getData();
// Création d'un nouveau nom aléatoire pour la photo avec son extension (récupérée via la méthode guessExtension() )
$newFileName = md5(time() . rand() . uniqid() ) . '.' . $video->guessExtension();
$em = $this->getDoctrine()->getManager();
$newVideo->setVid($newFileName);
$em->persist($newVideo);
$em->flush();
// Déplacement de la photo dans le dossier que l'on avait paramétré dans le fichier services.yaml, avec le nouveau nom qu'on lui a généré
$video->move(
$this->getParameter('app.user.videos.directory'), // Emplacement de sauvegarde du fichier
$newFileName // Nouveau nom du fichier
);
// Petit message de succès
$this->addFlash('success', 'Vidéo envoyée avec succès !');
// Redirection sur une autre page du site
return $this->redirectToRoute('home');
}
return $this->render('main/addVideo.html.twig', [
'addVidForm' => $form->createView()
]);
}
}