src/Controller/SecurityController.php line 58

Open in your IDE?
  1. <?php
  2. namespace App\Controller;
  3. use App\Form\UserPasswordType;
  4. use App\Service\EmailService;
  5. use GuzzleHttp\Client;
  6. use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
  7. use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
  8. use Symfony\Component\Form\Extension\Core\Type\SubmitType;
  9. use Symfony\Component\HttpFoundation\Request;
  10. use Symfony\Component\HttpFoundation\Response;
  11. use Symfony\Component\Routing\Annotation\Route;
  12. use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
  13. use Symfony\Component\Form\Extension\Core\Type\EmailType;
  14. use Symfony\Component\Form\FormError;
  15. use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
  16. use Doctrine\ORM\EntityManagerInterface;
  17. use App\Entity\User;
  18. use Symfony\Contracts\Translation\TranslatorInterface;
  19. class SecurityController extends AbstractController
  20. {
  21.     private $em;
  22.     private $parameter;
  23.     private $encoder;
  24.     private $emailService;
  25.     private $translator;
  26.     public function __construct(
  27.         EntityManagerInterface $em,
  28.         ParameterBagInterface $parameter,
  29.         UserPasswordEncoderInterface $encoder,
  30.         EmailService $emailService,
  31.         TranslatorInterface $translator
  32.     )
  33.     {
  34.         $this->em $em;
  35.         $this->parameter $parameter;
  36.         $this->encoder $encoder;
  37.         $this->emailService $emailService;
  38.         $this->translator $translator;
  39.     }
  40.     /**
  41.      * @Route("/{_locale}/login", name="login")
  42.      */
  43.     public function login(Request $requestAuthenticationUtils $authUtils)
  44.     {
  45.         // get the login error if there is one
  46.         $error $authUtils->getLastAuthenticationError();
  47.         // last username entered by the user
  48.         $lastUsername $authUtils->getLastUsername();
  49.         return $this->render('security/login.html.twig', array(
  50.             'last_username' => $lastUsername,
  51.             'error'         => $error,
  52.             'redirect' => $request->getSession()->get('redirect_uri')
  53.         ));
  54.     }
  55.     /**
  56.      * @Route("/json_login", name="json_login", methods={"POST"})
  57.      */
  58.     public function loginJson(Request $request)
  59.     {
  60.         return $this->json([
  61.                 'user' => $this->getUser() ? $this->getUser()->getId() : null]
  62.         );
  63.     }
  64.     /**
  65.      * Oops, I forgot my password!
  66.      * @Route("/{_locale}/forgot_password", name="forgot_password")
  67.      */
  68.     public function forgotPassword(Request $request)
  69.     {
  70.         // make form
  71.         $form $this->createFormBuilder()
  72.             ->add('email'EmailType::class, array(
  73.                 'label' => 'label.email',
  74.                 'label_attr' => [
  75.                     'class' => 'visually-hidden'
  76.                 ],
  77.                 'attr' => [
  78.                     'placeholder' => 'label.email',
  79.                     'class' => 'form-control',
  80.                     'autofocus' => true
  81.                 ]
  82.             ))
  83.             ->getForm()
  84.         ;
  85.         $form->handleRequest($request);
  86.         if ($form->isSubmitted() && $form->isValid()) {
  87.             // get corresponding user
  88.             $email $form->getData()["email"];
  89.             $user $this->em->getRepository(User::class)->findOneBy(['email' => $email]);
  90.             if ($user) {
  91.                 // if user exists then send mail for resetting password
  92.                 $this->emailService->sendNouveauMDPMail($user$request->getLocale());
  93.                 return $this->render("default/simple.html.twig", array(
  94.                     "content" => "connect.mail.sent",
  95.                 ));
  96.             }
  97.             // get back to email form, maybe it's a typo
  98.             $form->get('email')->addError(new FormError($this->translator->trans('connect.no.user.found')));
  99.         }
  100.         // form rendering
  101.         return $this->render('security/forgot_password.html.twig', array(
  102.             'form' => $form->createView(),
  103.         ));
  104.     }
  105.     /**
  106.      * Reset a password, given a token and an email. (see forgotPassword function)
  107.      * @Route("/{_locale}/adduser_password", name="adduser_password")
  108.      */
  109.     public function addUserPassword(Request $request)
  110.     {
  111.         $pass $this->randomPassword();
  112.         return $this->genPassword($request,$pass, [
  113.             'route' => 'adduser_password',
  114.             'content_captcha_valide' => $this->translator->trans('connect.your.password.is') . ' : ' $pass
  115.         ]);
  116.     }
  117.     /**
  118.      * Reset a password, given a token and an email. (see forgotPassword function)
  119.      * @Route("/{_locale}/reset_password", name="reset_password")
  120.      */
  121.     public function resetPassword(Request $request)
  122.     {
  123.         $generatePassOnReset getenv("GENERATE_PASS_ON_RESET") === "false" false true;
  124.         if ($generatePassOnReset) {
  125.             // User is given a random password
  126.             $pass $this->randomPassword();
  127.             return $this->genPassword($request$pass, [
  128.                 'route' => 'reset_password',
  129.                 'content_captcha_valide' => $this->translator->trans('connect.your.new.password.is') . ' : ' $pass
  130.             ]);
  131.         } else {
  132.             // User can write its own new password
  133.             return $this->formChangePassword($request);
  134.         }
  135.     }
  136.     private function genPassword($request$password$options){
  137.         $email $request->query->get('email'); // no use urlencode if you need your url is redirect bad
  138.         $token $request->query->get('token');
  139.         $captcha_token $request->request->get("g-recaptcha-response");
  140.         if ($captcha_token && $this->isValid($captcha_token)) {
  141.             // reset password
  142.             // get user
  143.             /** @var User $user */
  144.             $user $this->em->getRepository(User::class)->findByTokenAndEmail($token$email);
  145.             // aucun utilisateur correspondant
  146.             if (!$user) {
  147.                 return $this->render("security/client-error.html.twig", ["error_message" => "connect.invalid.link"]);
  148.             }
  149.             // Changement mdp
  150.             $user->setPassword($this->encoder->encodePassword($user$password));
  151.             $user->setResetToken(null);
  152.             $user->setResetDate(new \DateTime());
  153.             if ($user->getActivatedAt() == null) {
  154.                 $user->setActivatedAt(new \DateTime());
  155.                 $user->setIsActive(true);
  156.             }
  157.             if ($options['route'] === 'adduser_password') {
  158.                 $user->setIsActive(true);
  159.             }
  160.             $this->em->flush();
  161.             // On redirige vers la page d'accueil
  162.             return $this->render("default/simple.html.twig", array(
  163.                 "content" => $options['content_captcha_valide'],
  164.                 "redirect_url" => getenv("RESET_PASS_URL_REDIRECT"),
  165.             ));
  166.         }
  167.         // show captcha
  168.         return $this->render("security/reset_password_captcha.html.twig", array(
  169.             "email" => $email,
  170.             "token" => $token,
  171.             "key" => $this->parameter->get('captcha_key'),
  172.             "route" => $options['route']
  173.         ));
  174.     }
  175.     private function formChangePassword(Request $request): Response
  176.     {
  177.         $email $request->query->get('email');
  178.         $token $request->query->get('token');
  179.         // Given a token & mail, check if an user corresponds
  180.         /** @var User $user */
  181.         $user $this->em->getRepository(User::class)->findByTokenAndEmail($token$email);
  182.         // No user found, token is invalid
  183.         if (!$user) {
  184.             return $this->render("security/client-error.html.twig", ["error_message" => "connect.invalid.link"]);
  185.         }
  186.         // Build form
  187.         $form $this->createForm(UserPasswordType::class, $user, [
  188.             "action" => $request->getUri(),
  189.         ]);
  190.         $form->handleRequest($request);
  191.         // Form processing
  192.         if ($form->isSubmitted() && $form->isValid()) {
  193.             $user->setPassword($this->encoder->encodePassword($user$user->getPlainPassword()));
  194.             if ($user->getActivatedAt() == null) {
  195.                 $user->setActivatedAt(new \DateTime());
  196.                 $user->setIsActive(true);
  197.             }
  198.             $this->em->persist($user);
  199.             $this->em->flush();
  200.             $customRedirectUrl getenv("RESET_PASS_URL_REDIRECT");
  201.             if ($customRedirectUrl) {
  202.                 // On redirige vers l'url personnalisée
  203.                 return $this->redirect($customRedirectUrl);
  204.             } else {
  205.                 // On redirige vers le login
  206.                 return $this->render("security/login.html.twig", [
  207.                     "success_msg" => "security.label.password_changed_success",
  208.                     "error" => false,
  209.                     "last_username" => "",
  210.                 ]);
  211.             }
  212.         }
  213.         return $this->render("security/change_password.html.twig", [
  214.            "form" => $form->createView(),
  215.         ]);
  216.     }
  217.     /**
  218.      * Generates random password.
  219.      * @see https://stackoverflow.com/questions/4356289/php-random-string-generator/31107425#31107425
  220.      */
  221.     private function randomPassword()
  222.     {
  223.         $keyspace "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ&*";
  224.         $pieces = [];
  225.         $max mb_strlen($keyspace'8bit') - 1;
  226.         for ($i 0$i 10; ++$i) {
  227.             $pieces []= $keyspace[random_int(0$max)];
  228.         }
  229.         return implode(''$pieces);
  230.     }
  231.     private function isValid($token)
  232.     {
  233.         $client = new Client();
  234.         $url "https://www.google.com/recaptcha/api/siteverify?secret=" $this->parameter->get('captcha_secret') . "&response=" $token;
  235.         $response $client->get($url);
  236.         $json json_decode($response->getBody(),true);
  237.         return $json["success"];
  238.     }
  239. }