<?php
namespace App\Controller\Security;
use App\Entity\Users\LoginAttempt;
use App\Entity\Users\User;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
class SecurityController extends AbstractController
{
protected $em;
protected $encoder;
protected $tokenStorage;
protected $session;
protected $eventDispatcher;
public function __construct(EntityManagerInterface $entityManager, UserPasswordEncoderInterface $encoder, TokenStorageInterface $tokenStorage, SessionInterface $session, EventDispatcherInterface $eventDispatcher)
{
$this->em = $entityManager;
$this->encoder = $encoder;
$this->tokenStorage = $tokenStorage;
$this->session = $session;
$this->eventDispatcher = $eventDispatcher;
}
/**
* @Route("/", name="index")
*/
public function index(AuthenticationUtils $authUtils)
{
// Redirect
return $this->redirect($this->generateUrl("security_login"));
}
/**
* @Route("/login", name="security_login")
*/
public function login(AuthenticationUtils $authUtils, Request $request)
{
// Logged in?
if($this->container->get('security.authorization_checker')->isGranted('IS_AUTHENTICATED_REMEMBERED') )
{
// Redirect
return $this->redirect($this->generateUrl("dashboard_index"));
}
// Grab login method
$loginMethod = in_array($request->query->get('method'), ["pin", "email"]) ? $request->query->get('method') : "pin";
// Get the login error
$authenticationError = $authUtils->getLastAuthenticationError();
if($authenticationError)
{
// Flash message
$this->get('session')->getFlashBag()->add('error', $authenticationError->getMessage());
}
return $this->render('Security/login.html.twig', [
'loginMethod' => $loginMethod
]);
}
/**
* @Route("/login_pin", name="security_loginpin")
*/
public function pinLogin(AuthenticationUtils $authUtils, Request $request)
{
$ipAddress = $request->getClientIp();
// Too many attempts
$loginAttempts = $this->em->getRepository(LoginAttempt::class)->findFiltered([
"ipAddress" => $ipAddress,
"created" => ["gte", new \DateTime("-15 minutes")]
]);
if(count($loginAttempts) >= 5)
die("Too many failed login attempts!");
// Logging in via PIN?
if($request->request->has('pin') && strlen($request->request->get('pin')) == 6)
{
// Grab stuff
$pin = $request->request->get('pin');
// Find a user with this PIN
$user = $this->em->getRepository(User::class)->findOneFiltered([
"pin" => $pin
]);
// No user?
if(!$user)
{
// Record it
$loginAttempt = new LoginAttempt([
"ipAddress" => $ipAddress
]);
// Persist & flush
$this->em->persist($loginAttempt);
$this->em->flush();
// Flash message
$this->get('session')->getFlashBag()->add('error', "Invalid login details");
// Redirect
return $this->redirectToRoute('security_login');
}
else
{
// Grab a token
$token = new UsernamePasswordToken($user, $user->getPassword(), "app", $user->getRoles());
// Set the token
$this->tokenStorage->setToken($token);
$this->session->set('_security_common', serialize($token));
// Fire the login event
$event = new InteractiveLoginEvent($request, $token);
$this->eventDispatcher->dispatch($event, "security.interactive_login");
// Redirect
return $this->redirectToRoute('dashboard_index');
}
}
else
{
// Record it
$loginAttempt = new LoginAttempt([
"ipAddress" => $ipAddress
]);
// Persist & flush
$this->em->persist($loginAttempt);
$this->em->flush();
// Redirect
return $this->redirectToRoute('security_login');
}
}
/**
* @Route("/login_check", name="security_logincheck")
*/
public function loginCheck()
{
}
/**
* @Route("/logout", name="security_logout")
*/
public function logout()
{
}
/**
* @Route("/forgot_password/{reset_token}", name="security_forgotpassword")
*/
public function forgotPassword(Request $request, AuthenticationUtils $authUtils, $reset_token = '')
{
// Is it sent?
if($request->query->get('sent'))
{
return $this->render('Security/forgotPassword.html.twig', array(
'sent' => true
));
}
// Resetting?
elseif(strlen($reset_token))
{
// Find the member
$member = $this->em->getRepository('App:Members\Member')->findOneFiltered(array(
"resetToken" => $reset_token
));
// Found it?
if(!$member)
{
// Flash message
$this->get('session')->getFlashBag()->add('error', "The reset link was invalid");
// Redirect
return $this->redirect($this->generateUrl("security_login"));
}
// Submitted the form?
if($request->isMethod('post') && $request->request->get('action') == "do_reset")
{
// Grab the data
$password = $request->request->get('password');
$confirmPassword = $request->request->get('confirm');
// Not long enough
if(strlen($password) < 6)
{
// Flash message
$this->get('session')->getFlashBag()->add('error', "Your new password must be at least 6 characters");
}
elseif($password != $confirmPassword)
{
// Flash message
$this->get('session')->getFlashBag()->add('error', "Your passwords do not match");
}
else
{
// Encode password
$encodedPassword = $this->encoder->encodePassword($member, $password);
$member->setPassword($encodedPassword);
$member->setResetToken(md5(uniqid()));
// Persist & flush
$this->em->persist($member);
$this->em->flush($member);
// Flash message
$this->get('session')->getFlashBag()->add('success', "Your password has been changed. Please login below.");
// Redirect
return $this->redirect($this->generateUrl("security_login"));
}
}
return $this->render('Frontend/Account/forgotPassword.html.twig', array(
'sent' => false,
'reset_token' => $reset_token
));
}
else
{
// Are we posting?
if($request->isMethod('post') && $request->request->get('action') == "do_reset")
{
// Grab email
$email = trim($request->request->get('email', ''));
// If it's valid
if(filter_var($email, FILTER_VALIDATE_EMAIL))
{
// Find the member
$member = $this->em->getRepository('App:Members\Member')->findOneFiltered(array(
"email" => $email
));
// Found it?
if($member)
{
// Get the email template
$emailTemplate = $this->em->getRepository('App:Settings\EmailTemplate')->findOneFiltered(array(
"identifier" => "reset_password"
));
// Not found?
if(!$emailTemplate)
throw new \Exception("Cannot find the email template!");
// Generate a new reset token
$member->setResetToken(md5(uniqid()));
// Persist & flush
$this->em->persist($member);
$this->em->flush($member);
// Send the email
$this->utilHelper->sendEmail(array(
"recipients" => array($member->getEmail()),
"subject" => $emailTemplate->getSubject(),
"template" => $emailTemplate
), array(
'RESETLINK' => $this->generateUrl("security_forgotpassword", array(
'reset_token' => $member->getResetToken()
), UrlGeneratorInterface::ABSOLUTE_URL)
));
}
// Redirect
return $this->redirect($this->generateUrl("security_forgotpassword") . '?sent=1');
}
else
{
// Flash message
$this->get('session')->getFlashBag()->add('error', "The specified email address is invalid");
// Redirect
return $this->redirect($this->generateUrl("security_login"));
}
}
else
{
// Redirect
return $this->redirect($this->generateUrl("security_login"));
}
}
}
}