src/Controller/SecurityController.php line 385

  1. <?php
  2. namespace App\Controller;
  3. use App\Entity\ResetPassword;
  4. use App\Entity\Users;
  5. use App\Entity\UserAdditionalDetails;
  6. use App\Entity\ProfileClassification;
  7. use App\Entity\UsersCategories;
  8. use App\Entity\VehicleClients;
  9. use App\Form\ResetPasswordType;
  10. use App\Form\UserRegistrationType;
  11. use Gregwar\Captcha\CaptchaBuilder;
  12. use Gregwar\Captcha\PhraseBuilder;
  13. use Psr\Log\LoggerInterface;
  14. use Symfony\Component\Routing\Annotation\Route;
  15. use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
  16. use Symfony\Component\HttpFoundation\Response;
  17. use Symfony\Component\HttpFoundation\JsonResponse;
  18. use Symfony\Component\HttpFoundation\Request;
  19. //use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
  20. use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
  21. use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
  22. use Unirest;
  23. use Symfony\Component\Form\FormError;
  24. use App\Service\SMSHelper;
  25. //use Symfony\Component\Security\Core\Security;
  26. use Symfony\Bundle\SecurityBundle\Security;
  27. use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
  28. use Symfony\Contracts\Translation\TranslatorInterface;
  29. use Doctrine\Persistence\ManagerRegistry;
  30. /**
  31.  * Class SecurityController
  32.  * @package App\Controller
  33.  */
  34. class SecurityController extends AbstractController
  35. {
  36.     /**
  37.      * @var CsrfTokenManagerInterface
  38.      */
  39.     private CsrfTokenManagerInterface $csrfTokenManager;
  40.     /**
  41.      * Constructor for SecurityController.
  42.      * Injects the CsrfTokenManagerInterface.
  43.      *
  44.      * @param CsrfTokenManagerInterface $csrfTokenManager The CSRF token manager service.
  45.      */
  46.     public function __construct(CsrfTokenManagerInterface $csrfTokenManager)
  47.     {
  48.         $this->csrfTokenManager $csrfTokenManager;
  49.     }
  50.     /**
  51.      * @param Request $request
  52.      *
  53.      */
  54.     #[Route('/login'name'login')]
  55.     public function loginAction(Security $securityAuthenticationUtils $authenticationUtils): Response
  56.     {
  57.         // redirect to profile if user logged 
  58.         if($security->getUser() instanceof \Symfony\Component\Security\Core\User\UserInterface){
  59.             return $this->redirectToRoute('my_profile');
  60.         }
  61.         // get the login error if there is one
  62.         $error        $authenticationUtils->getLastAuthenticationError();
  63.         $lastUsername $authenticationUtils->getLastUsername();
  64.         return $this->render('security/login.html.twig', array(
  65.             'last_username' => $lastUsername,
  66.             'error'         => $error
  67.         ));
  68.     }
  69.     /**
  70.      * @throws \Exception
  71.      */
  72.     #[Route('/logout'name'logout')]
  73.     public function logoutAction()
  74.     {
  75.         throw new \Exception('This should never be reached!');
  76.     }
  77.     /**
  78.      * @return \Symfony\Component\HttpFoundation\RedirectResponse|\Symfony\Component\HttpFoundation\Response
  79.      * @throws Unirest\Exception
  80.      */
  81.     #[Route('/reg/getCaptcha'name'get_captcha_ajax'methods:["POST"])]
  82.     public function regenerateCaptchaAjaxAction(Request $request)
  83.     {
  84.         if ($request->isXMLHttpRequest()) {
  85.             $session   $request->getSession();
  86.             $builder $this->createCaptcha($session);
  87.             return new JsonResponse(['builder' => $builder->inline()]);
  88.         }
  89.         return null;
  90.     }
  91.     /**
  92.      *
  93.      * @return \Symfony\Component\HttpFoundation\RedirectResponse|\Symfony\Component\HttpFoundation\Response
  94.      * @throws Unirest\Exception
  95.      */
  96.     #[Route('/register'name'user_registration'methods:["GET","POST"])]
  97.     #[Route('/reg'name'user_reg'methods:["GET","POST"])]
  98.     public function registerAction(Request $requestUserPasswordHasherInterface $passwordEncoder
  99.             LoggerInterface $loggerTranslatorInterface $translatorManagerRegistry $doctrine)
  100.     {
  101.         // redirect to profile if user logged
  102.         if($this->getUser() instanceof \Symfony\Component\Security\Core\User\UserInterface){
  103.             return $this->redirectToRoute('my_profile');
  104.         }
  105.         $session   $request->getSession();
  106.         $locale    $request->getLocale();
  107.         $errorCaptcha false;
  108.         // 1) build the form
  109.         $user = new Users();
  110.         $form $this->createForm(UserRegistrationType::class, $user, ['locale' => $locale]);
  111.         // 2) handle the submit (will only happen on POST)
  112.         $form->handleRequest($request);
  113.         if ($form->isSubmitted() && $form->isValid()) {
  114.             // refresh CSRF token (form_intention)
  115.             $newToken $this->csrfTokenManager->refreshToken('form_intention');
  116.             $postedData  $request->request->all();
  117.             $code        $postedData['sms_code'];
  118.             $userCaptcha $postedData['userCaptcha'];
  119.             if ($userCaptcha == $session->get('captchaPhrase')) {
  120.                 if ($code == $session->get('verificationCode')) {
  121.                     $password     rand(10019998);
  122.                     $mobileNumber $user->getMobileNumber();
  123.                     preg_match('/^(?:0|966)*(5\d{8})$/'$mobileNumber$mobNumChunks);
  124.                     $user->setMobileNumber($mobNumChunks[1]); //remove leading zero or KSA country code if any exists
  125.                     $username $user->getUserIdentityInfo()->getIdentificationNumber();
  126.                     // 3) Encode the password (you could also do this via Doctrine listener)
  127.                     $hashedPassword $passwordEncoder->hashPassword($user$password);
  128.                     $user->setPassword($hashedPassword);
  129.                     // 4) save the User!
  130.                     $em       $doctrine->getManager();
  131.                     $category $em->getRepository(UsersCategories::class)->find(1);
  132.                     $userIdentityInfo $user->getUserIdentityInfo();
  133.                     $user->setUserIdentityInfo(null);
  134.                     $user->setCategory($category);
  135.                     $user->setIsReceiveSms(true); //the self-registered users by default accept to receive our SMS
  136.                     $user->setUsername($userIdentityInfo->getIdentificationNumber());
  137.                     $user->setProfileOrigin("self registered");
  138.                     $em->persist($user);
  139.                     //if image uploading is required, so this is the place where to add upload code
  140.                     $em->flush($user);
  141.                     // saving user identity info
  142.                     $user->setUserIdentityInfo($userIdentityInfo);
  143.                     $userIdentityInfo->setUser($user);
  144.                     $userAdditionalDetails = new UserAdditionalDetails();
  145.                     $registeredClassification $em->getRepository(ProfileClassification::class)->findBy(['nameEn'=> 'Registered']);
  146.                     $userAdditionalDetails->setClassification($registeredClassification[0]);
  147.                     $userAdditionalDetails->setUser($user);
  148.                     $user->setUserAdditionalDetails($userAdditionalDetails);
  149.                     $em->persist($userAdditionalDetails);
  150.                     $em->flush($userAdditionalDetails);
  151.                     // Create vehicle client account
  152.                     $vehicleClient = new VehicleClients();
  153.                     $vehicleClient->setUser($user);
  154.                     $vehicleClient->setClientId($user->getUserId());
  155.                     $vehicleClient->setUser($user);
  156.                     $vehicleClient->setIsActive(1);
  157.                     $em->persist($vehicleClient);
  158.                     $em->persist($userIdentityInfo);
  159.                     $em->flush();
  160.                     
  161.                     $message $translator->trans('your login info ID and password are',
  162.                             ['%{username}'=> $username'%{password}'=> $password'%{newline}'=> "\n"], 'clients');
  163.                     if ($this->sendSMSToUser($message$mobileNumber'randum generated password'$logger)) {
  164.                         return $this->redirectToRoute('user_thanking');
  165.                     } else {
  166.                         // password did not sent to customer
  167.                         $session->getFlashBag()->add('info',
  168.                             $translator->trans('no password sent', array(), 'clients')
  169.                         );
  170.                         $logger->info('Register Action: no password sent to mobile num: '$user->getMobileNumber());
  171.                         return $this->redirectToRoute('login');
  172.                     }
  173.                 } else {
  174.                     // activation code is wrong
  175.                     $errorMessage $translator->trans('you have entered a wrong verification code please enter the correct one', array(''), 'validators');
  176.                     $form->addError(new FormError($errorMessage)); //this is how to add an error to Symfony form
  177.                     $logger->info('Register Action: you have entered a wrong verification code please enter the correct one, mobile num is '$user->getMobileNumber());
  178.                 }
  179.             } else {
  180.                 // captcha code is wrong
  181.                 $errorMessage $translator->trans('captcha code is wrong', array(''), 'validators');
  182.                 $form->addError(new FormError($errorMessage)); //this is how to add an error to Symfony form
  183.                 $logger->info('Register Action: you have entered a wrong captcha code, mobile num is '$user->getMobileNumber());
  184.                 $errorCaptcha $translator->trans('captcha code is wrong', array(''), 'validators');
  185.             }
  186.         }
  187.         $builder $this->createCaptcha($session);
  188.         return $this->render('security/register.html.twig', array(
  189.             'form'      => $form->createView(),
  190.             'locale'    => $locale,
  191.             'builder'   => $builder,
  192.             'errorCaptcha'   => $errorCaptcha,
  193.         ));
  194.     }
  195.     /**
  196.      *
  197.      * @return \Symfony\Component\HttpFoundation\RedirectResponse|\Symfony\Component\HttpFoundation\Response
  198.      * @throws Unirest\Exception
  199.      */
  200.     #[Route('/forgotPassword'name'forgot_password'methods:["GET","POST"])]
  201.     #[Route('/getCaptchaForgot'name'get_captcha_forgot_ajax'methods:["GET","POST"])]
  202.     public function forgotPasswordAction(Request $requestLoggerInterface $loggerTranslatorInterface $translatorManagerRegistry $doctrine)
  203.     {
  204.         $session $request->getSession();
  205.         //for ajax request
  206.         if ($request->isXMLHttpRequest()) {
  207.             $builder      $this->createCaptcha($session);
  208.             return new JsonResponse(['builder' => $builder->inline()]);
  209.         }
  210.         if ($request->isMethod('post')) {
  211.             //if post, validate then send verification code
  212.             $postedData   $request->request->all();
  213.             $userCaptcha  $postedData['userCaptcha'];
  214.             $mobileNumber $postedData['mobileNumber'];
  215.             // if the mobile number starts 0 , remove it
  216.              if(substr($mobileNumber,0,1) == 0){
  217.                 $mobileNumber =  substr($mobileNumber,1);
  218.             }
  219.             if ($userCaptcha == $session->get('captchaPhrase')) {
  220.                 $em   $doctrine->getManager();
  221.                 $user $em->getRepository(Users::class)->findOneBy(['mobileNumber' => $mobileNumber]);
  222.                 if ($user) {
  223.                     $code rand(101998);
  224.                     $session->set('activationCode'$code);
  225.                     
  226.                     $message $translator->trans('to reset your password enter this code', array(), 'clients') . ': '$code.
  227.                         "\n" $translator->trans('your ID is', [], 'clients') . ': ' $user->getUsername();
  228.                     if ($this->sendSMSToUser($message$mobileNumber'forgot password code'$logger)) {
  229.                         $session->set('mobileNumber'$mobileNumber);
  230.                         return $this->redirectToRoute('reset_password');
  231.                     } else {
  232.                         // code did not sent to customer
  233.                         $session->getFlashBag()->add('info',
  234.                             $translator->trans('no code sent', array(), 'clients')
  235.                         );
  236.                         return $this->redirectToRoute('forgot_password');
  237.                     }
  238.                 } else {
  239.                     // no user registered with this mobile number
  240.                     $errorCode $translator->trans('no user registered by this mobile number', array(''), 'validators');
  241.                     $builder   $this->createCaptcha($session);
  242.                 }
  243.             } else {
  244.                 // captcha code is wrong
  245.                 $errorCode $translator->trans('you have enterd a wrong captcha code', array(''), 'validators');
  246.                 $builder   $this->createCaptcha($session);
  247.             }
  248.         } else {
  249.             $builder      $this->createCaptcha($session);
  250.             $errorCode    null;
  251.             $mobileNumber '';
  252.         }
  253.         return $this->render('security/forgot_password.html.twig', ['builder' => $builder'errorCode' => $errorCode'mobileNumber' => $mobileNumber]);
  254.     }
  255.     /**
  256.      * @param $mobileNumber
  257.      *
  258.      * @return \Symfony\Component\HttpFoundation\RedirectResponse|\Symfony\Component\HttpFoundation\Response
  259.      */
  260.     #[Route('/resetPassword'name'reset_password'methods:["GET","POST"])]
  261.     public function resetPasswordAction(Request $requestUserPasswordHasherInterface $passwordEncoder
  262.             TranslatorInterface $translatorManagerRegistry $doctrine)
  263.     {
  264.         $session       $request->getSession();
  265.         $resetPassword = new ResetPassword();
  266.         $errorCode     '';
  267.         $mobileNumber$session->get('mobileNumber');
  268.         $form $this->createForm(ResetPasswordType::class, $resetPassword, ['capital' => false]);
  269.         $form->handleRequest($request);
  270.         if ($form->isSubmitted() && $form->isValid()) {
  271.             $em   $doctrine->getManager();
  272.             $user $em->getRepository(Users::class)->findOneBy(['mobileNumber' => $mobileNumber]);
  273.             if ($user) {
  274.                 $code $resetPassword->getSmsCode();
  275.                 if ($code == $session->get('activationCode')) {
  276.                     $newPassword    $resetPassword->getNewPassword();
  277.                     //$user           = $this->getUser();
  278.                     $hashedPassword $passwordEncoder->encodePassword($user$newPassword);
  279.                     $user->setPassword($hashedPassword);
  280.                     $em->flush($user);
  281.                     $session->getFlashBag()->add('success',
  282.                         $translator->trans('password changed successfully', array(), 'clients')
  283.                     );
  284.                     return $this->redirectToRoute('login');
  285.                 } else {
  286.                     // activation code is wrong
  287.                     $errorCode $translator->trans('you have entered a wrong activation code please enter the correct one', array(''), 'validators');
  288.                 }
  289.             } else {
  290.                 // no user registered by his mobile number
  291.                 $errorCode $translator->trans('no user registered by this mobile number', array(''), 'validators');
  292.                 $builder   $this->createCaptcha($session);
  293.             }
  294.         }
  295.         return $this->render('security/reset_password.html.twig', array(
  296.             'form'      => $form->createView(),
  297.             'errorCode' => $errorCode
  298.         ));
  299.     }
  300.     /**
  301.      * @param $passwordOrCode
  302.      * @param $mobileNumber
  303.      * @param $smsType
  304.      * @param null $username
  305.      *
  306.      * @return bool
  307.      * @throws Unirest\Exception
  308.      */
  309.     private function sendSMSToUser($message$mobileNumber$smsTypeLoggerInterface $logger)
  310.     {
  311.         if ( !preg_match('/^(?:0|966)*(5\d{8})$/'$mobileNumber$mobNumChunks) ) {
  312.             $logger->info"Unable to send $smsType SMS due to wrong mobile num :"$mobileNumber);
  313.             return false;
  314.         }
  315.         $mobileNumber '966'$mobNumChunks[1]; //add KSA country code
  316.         //send the code into an SMS
  317.         $smsHelper =   new  SMSHelper();
  318.         $sendStatus$smsHelper->sendSms$message $mobileNumber);
  319.         $logger->info(\Doctrine\Common\Util\Debug::dump($smsHelper->getRawResponse(), 2truefalse));
  320.         switch ($sendStatus){
  321.             case $smsHelper::STATUS['sms_seems_sent'];
  322.                 $logger->info"sending $smsType SMS to mobile num :"$mobileNumber" sent!");
  323.                 return true;
  324.             case $smsHelper::STATUS['sms_send_failed'];
  325.             case $smsHelper::STATUS['request_failed'];
  326.                 $logger->info"Failed sending $smsType SMS to mobile num :"$mobileNumber" due to this exception: "$smsHelper->getException());
  327.                 return false;
  328.         }
  329.         return null;
  330.             
  331.     }
  332.     /**
  333.      * @return CaptchaBuilder
  334.      */
  335.     private function createCaptcha($session)
  336.     {
  337.         // Will build phrases of 4 characters, only digits
  338.         $phraseBuilder = new PhraseBuilder(4'0123456789');
  339.         $builder       = new CaptchaBuilder(null$phraseBuilder);
  340.         $builder->build(11550);
  341.         $session->set('captchaPhrase'$builder->getPhrase());
  342.         return $builder;
  343.     }
  344. }