src/Controller/SecurityController.php line 385
<?php
namespace App\Controller;
use App\Entity\ResetPassword;
use App\Entity\Users;
use App\Entity\UserAdditionalDetails;
use App\Entity\ProfileClassification;
use App\Entity\UsersCategories;
use App\Entity\VehicleClients;
use App\Form\ResetPasswordType;
use App\Form\UserRegistrationType;
use Gregwar\Captcha\CaptchaBuilder;
use Gregwar\Captcha\PhraseBuilder;
use Psr\Log\LoggerInterface;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
//use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
use Unirest;
use Symfony\Component\Form\FormError;
use App\Service\SMSHelper;
//use Symfony\Component\Security\Core\Security;
use Symfony\Bundle\SecurityBundle\Security;
use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
use Doctrine\Persistence\ManagerRegistry;
/**
* Class SecurityController
* @package App\Controller
*/
class SecurityController extends AbstractController
{
/**
* @var CsrfTokenManagerInterface
*/
private CsrfTokenManagerInterface $csrfTokenManager;
/**
* Constructor for SecurityController.
* Injects the CsrfTokenManagerInterface.
*
* @param CsrfTokenManagerInterface $csrfTokenManager The CSRF token manager service.
*/
public function __construct(CsrfTokenManagerInterface $csrfTokenManager)
{
$this->csrfTokenManager = $csrfTokenManager;
}
/**
* @param Request $request
*
*/
#[Route('/login', name: 'login')]
public function loginAction(Security $security, AuthenticationUtils $authenticationUtils): Response
{
// redirect to profile if user logged
if($security->getUser() instanceof \Symfony\Component\Security\Core\User\UserInterface){
return $this->redirectToRoute('my_profile');
}
// get the login error if there is one
$error = $authenticationUtils->getLastAuthenticationError();
$lastUsername = $authenticationUtils->getLastUsername();
return $this->render('security/login.html.twig', array(
'last_username' => $lastUsername,
'error' => $error
));
}
/**
* @throws \Exception
*/
#[Route('/logout', name: 'logout')]
public function logoutAction()
{
throw new \Exception('This should never be reached!');
}
/**
* @return \Symfony\Component\HttpFoundation\RedirectResponse|\Symfony\Component\HttpFoundation\Response
* @throws Unirest\Exception
*/
#[Route('/reg/getCaptcha', name: 'get_captcha_ajax', methods:["POST"])]
public function regenerateCaptchaAjaxAction(Request $request)
{
if ($request->isXMLHttpRequest()) {
$session = $request->getSession();
$builder = $this->createCaptcha($session);
return new JsonResponse(['builder' => $builder->inline()]);
}
return null;
}
/**
*
* @return \Symfony\Component\HttpFoundation\RedirectResponse|\Symfony\Component\HttpFoundation\Response
* @throws Unirest\Exception
*/
#[Route('/register', name: 'user_registration', methods:["GET","POST"])]
#[Route('/reg', name: 'user_reg', methods:["GET","POST"])]
public function registerAction(Request $request, UserPasswordHasherInterface $passwordEncoder,
LoggerInterface $logger, TranslatorInterface $translator, ManagerRegistry $doctrine)
{
// redirect to profile if user logged
if($this->getUser() instanceof \Symfony\Component\Security\Core\User\UserInterface){
return $this->redirectToRoute('my_profile');
}
$session = $request->getSession();
$locale = $request->getLocale();
$errorCaptcha = false;
// 1) build the form
$user = new Users();
$form = $this->createForm(UserRegistrationType::class, $user, ['locale' => $locale]);
// 2) handle the submit (will only happen on POST)
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
// refresh CSRF token (form_intention)
$newToken = $this->csrfTokenManager->refreshToken('form_intention');
$postedData = $request->request->all();
$code = $postedData['sms_code'];
$userCaptcha = $postedData['userCaptcha'];
if ($userCaptcha == $session->get('captchaPhrase')) {
if ($code == $session->get('verificationCode')) {
$password = rand(1001, 9998);
$mobileNumber = $user->getMobileNumber();
preg_match('/^(?:0|966)*(5\d{8})$/', $mobileNumber, $mobNumChunks);
$user->setMobileNumber($mobNumChunks[1]); //remove leading zero or KSA country code if any exists
$username = $user->getUserIdentityInfo()->getIdentificationNumber();
// 3) Encode the password (you could also do this via Doctrine listener)
$hashedPassword = $passwordEncoder->hashPassword($user, $password);
$user->setPassword($hashedPassword);
// 4) save the User!
$em = $doctrine->getManager();
$category = $em->getRepository(UsersCategories::class)->find(1);
$userIdentityInfo = $user->getUserIdentityInfo();
$user->setUserIdentityInfo(null);
$user->setCategory($category);
$user->setIsReceiveSms(true); //the self-registered users by default accept to receive our SMS
$user->setUsername($userIdentityInfo->getIdentificationNumber());
$user->setProfileOrigin("self registered");
$em->persist($user);
//if image uploading is required, so this is the place where to add upload code
$em->flush($user);
// saving user identity info
$user->setUserIdentityInfo($userIdentityInfo);
$userIdentityInfo->setUser($user);
$userAdditionalDetails = new UserAdditionalDetails();
$registeredClassification = $em->getRepository(ProfileClassification::class)->findBy(['nameEn'=> 'Registered']);
$userAdditionalDetails->setClassification($registeredClassification[0]);
$userAdditionalDetails->setUser($user);
$user->setUserAdditionalDetails($userAdditionalDetails);
$em->persist($userAdditionalDetails);
$em->flush($userAdditionalDetails);
// Create vehicle client account
$vehicleClient = new VehicleClients();
$vehicleClient->setUser($user);
$vehicleClient->setClientId($user->getUserId());
$vehicleClient->setUser($user);
$vehicleClient->setIsActive(1);
$em->persist($vehicleClient);
$em->persist($userIdentityInfo);
$em->flush();
$message = $translator->trans('your login info ID and password are',
['%{username}'=> $username, '%{password}'=> $password, '%{newline}'=> "\n"], 'clients');
if ($this->sendSMSToUser($message, $mobileNumber, 'randum generated password', $logger)) {
return $this->redirectToRoute('user_thanking');
} else {
// password did not sent to customer
$session->getFlashBag()->add('info',
$translator->trans('no password sent', array(), 'clients')
);
$logger->info('Register Action: no password sent to mobile num: '. $user->getMobileNumber());
return $this->redirectToRoute('login');
}
} else {
// activation code is wrong
$errorMessage = $translator->trans('you have entered a wrong verification code please enter the correct one', array(''), 'validators');
$form->addError(new FormError($errorMessage)); //this is how to add an error to Symfony form
$logger->info('Register Action: you have entered a wrong verification code please enter the correct one, mobile num is '. $user->getMobileNumber());
}
} else {
// captcha code is wrong
$errorMessage = $translator->trans('captcha code is wrong', array(''), 'validators');
$form->addError(new FormError($errorMessage)); //this is how to add an error to Symfony form
$logger->info('Register Action: you have entered a wrong captcha code, mobile num is '. $user->getMobileNumber());
$errorCaptcha = $translator->trans('captcha code is wrong', array(''), 'validators');
}
}
$builder = $this->createCaptcha($session);
return $this->render('security/register.html.twig', array(
'form' => $form->createView(),
'locale' => $locale,
'builder' => $builder,
'errorCaptcha' => $errorCaptcha,
));
}
/**
*
* @return \Symfony\Component\HttpFoundation\RedirectResponse|\Symfony\Component\HttpFoundation\Response
* @throws Unirest\Exception
*/
#[Route('/forgotPassword', name: 'forgot_password', methods:["GET","POST"])]
#[Route('/getCaptchaForgot', name: 'get_captcha_forgot_ajax', methods:["GET","POST"])]
public function forgotPasswordAction(Request $request, LoggerInterface $logger, TranslatorInterface $translator, ManagerRegistry $doctrine)
{
$session = $request->getSession();
//for ajax request
if ($request->isXMLHttpRequest()) {
$builder = $this->createCaptcha($session);
return new JsonResponse(['builder' => $builder->inline()]);
}
if ($request->isMethod('post')) {
//if post, validate then send verification code
$postedData = $request->request->all();
$userCaptcha = $postedData['userCaptcha'];
$mobileNumber = $postedData['mobileNumber'];
// if the mobile number starts 0 , remove it
if(substr($mobileNumber,0,1) == 0){
$mobileNumber = substr($mobileNumber,1);
}
if ($userCaptcha == $session->get('captchaPhrase')) {
$em = $doctrine->getManager();
$user = $em->getRepository(Users::class)->findOneBy(['mobileNumber' => $mobileNumber]);
if ($user) {
$code = rand(101, 998);
$session->set('activationCode', $code);
$message = $translator->trans('to reset your password enter this code', array(), 'clients') . ': '. $code.
"\n" . $translator->trans('your ID is', [], 'clients') . ': ' . $user->getUsername();
if ($this->sendSMSToUser($message, $mobileNumber, 'forgot password code', $logger)) {
$session->set('mobileNumber', $mobileNumber);
return $this->redirectToRoute('reset_password');
} else {
// code did not sent to customer
$session->getFlashBag()->add('info',
$translator->trans('no code sent', array(), 'clients')
);
return $this->redirectToRoute('forgot_password');
}
} else {
// no user registered with this mobile number
$errorCode = $translator->trans('no user registered by this mobile number', array(''), 'validators');
$builder = $this->createCaptcha($session);
}
} else {
// captcha code is wrong
$errorCode = $translator->trans('you have enterd a wrong captcha code', array(''), 'validators');
$builder = $this->createCaptcha($session);
}
} else {
$builder = $this->createCaptcha($session);
$errorCode = null;
$mobileNumber = '';
}
return $this->render('security/forgot_password.html.twig', ['builder' => $builder, 'errorCode' => $errorCode, 'mobileNumber' => $mobileNumber]);
}
/**
* @param $mobileNumber
*
* @return \Symfony\Component\HttpFoundation\RedirectResponse|\Symfony\Component\HttpFoundation\Response
*/
#[Route('/resetPassword', name: 'reset_password', methods:["GET","POST"])]
public function resetPasswordAction(Request $request, UserPasswordHasherInterface $passwordEncoder,
TranslatorInterface $translator, ManagerRegistry $doctrine)
{
$session = $request->getSession();
$resetPassword = new ResetPassword();
$errorCode = '';
$mobileNumber= $session->get('mobileNumber');
$form = $this->createForm(ResetPasswordType::class, $resetPassword, ['capital' => false]);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$em = $doctrine->getManager();
$user = $em->getRepository(Users::class)->findOneBy(['mobileNumber' => $mobileNumber]);
if ($user) {
$code = $resetPassword->getSmsCode();
if ($code == $session->get('activationCode')) {
$newPassword = $resetPassword->getNewPassword();
//$user = $this->getUser();
$hashedPassword = $passwordEncoder->encodePassword($user, $newPassword);
$user->setPassword($hashedPassword);
$em->flush($user);
$session->getFlashBag()->add('success',
$translator->trans('password changed successfully', array(), 'clients')
);
return $this->redirectToRoute('login');
} else {
// activation code is wrong
$errorCode = $translator->trans('you have entered a wrong activation code please enter the correct one', array(''), 'validators');
}
} else {
// no user registered by his mobile number
$errorCode = $translator->trans('no user registered by this mobile number', array(''), 'validators');
$builder = $this->createCaptcha($session);
}
}
return $this->render('security/reset_password.html.twig', array(
'form' => $form->createView(),
'errorCode' => $errorCode
));
}
/**
* @param $passwordOrCode
* @param $mobileNumber
* @param $smsType
* @param null $username
*
* @return bool
* @throws Unirest\Exception
*/
private function sendSMSToUser($message, $mobileNumber, $smsType, LoggerInterface $logger)
{
if ( !preg_match('/^(?:0|966)*(5\d{8})$/', $mobileNumber, $mobNumChunks) ) {
$logger->info( "Unable to send $smsType SMS due to wrong mobile num :". $mobileNumber);
return false;
}
$mobileNumber = '966'. $mobNumChunks[1]; //add KSA country code
//send the code into an SMS
$smsHelper = new SMSHelper();
$sendStatus= $smsHelper->sendSms( $message , $mobileNumber);
$logger->info(\Doctrine\Common\Util\Debug::dump($smsHelper->getRawResponse(), 2, true, false));
switch ($sendStatus){
case $smsHelper::STATUS['sms_seems_sent'];
$logger->info( "sending $smsType SMS to mobile num :". $mobileNumber. " sent!");
return true;
case $smsHelper::STATUS['sms_send_failed'];
case $smsHelper::STATUS['request_failed'];
$logger->info( "Failed sending $smsType SMS to mobile num :". $mobileNumber. " due to this exception: ". $smsHelper->getException());
return false;
}
return null;
}
/**
* @return CaptchaBuilder
*/
private function createCaptcha($session)
{
// Will build phrases of 4 characters, only digits
$phraseBuilder = new PhraseBuilder(4, '0123456789');
$builder = new CaptchaBuilder(null, $phraseBuilder);
$builder->build(115, 50);
$session->set('captchaPhrase', $builder->getPhrase());
return $builder;
}
}