<?php
namespace App\Controller;
use App\Entity\utilisateur\UtilisateurProfil;
use App\Entity\utilisateur\Utilisateurs;
use App\Form\administration\ResetMdpFormType;
use App\Form\AuthentificationFormType;
use App\Form\ForgetFormType;
use App\Form\ResetFormType;
use App\Form\CreatePasswordFormType;
use App\Helpers\Helpers;
use App\Repository\administration\UtilisateurSocieteRepository;
use App\Repository\administration\UtilisateursRepository;
use App\Repository\Utilisateur\UtilisateurSocieteAssociationRepository;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Bundle\FrameworkBundle\Translation\Translator;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\Mailer\Exception\TransportExceptionInterface;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
use Symfony\Contracts\Translation\TranslatorInterface;
use Symfony\Component\Mailer\MailerInterface;
use Symfony\Component\Mime\Email;
use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
use Symfony\Component\Security\Csrf\CsrfToken;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
class AuthentificationController extends AbstractController
{
/**
* @ORM\Column(type="string")
*/
private $objectManager;
private const MAINTENANCE_FILE = __DIR__ . '/../../var/.maintenance';
public function __construct(EntityManagerInterface $objectManager) {
$this->objectManager = $objectManager;
}
public function setConnectionSessions($res, $utilisateur, $utilisateurSocieteAssociation, $repository)
{
$token = new UsernamePasswordToken($res, null, 'main', $utilisateur->getRoles());
$this->get('security.token_storage')->setToken($token);
$this->get('session')->set('_security_main', serialize($token));
$utilisateurProfil = new UtilisateurProfil($res->getFkUtilisateurProfil()->getIdUtilisateurProfil(),$res->getFkUtilisateurProfil()->getLibelle());
$this->get('session')->set('profil', $res->getFkUtilisateurProfil());
$this->get('session')->set('societe', $res->getFkUtilisateurSociete());
$arrayListeSocieteAssocie = [$res->getFkUtilisateurSociete()->getIdUtilisateurSociete()];
$listeSocieteAssocie = $utilisateurSocieteAssociation->findByFkUtilisateurSocieteIdParent($res->getFkUtilisateurSociete()->getIdUtilisateurSociete());
foreach($listeSocieteAssocie as $associe){
$arrayListeSocieteAssocie[] = $associe->getFkUtilisateurSocieteId()->getIdUtilisateurSociete();
}
$this->get('session')->set('ListeDeSesSociete', $arrayListeSocieteAssocie);
$menu = $repository->getUtilisateurMenu($res->getIdUtilisateur(),$utilisateurProfil->getIdUtilisateurProfil());
$this->get('session')->set('menu',$menu);
$this->get('session')->set('user',$res->getIdUtilisateur());
$this->get('session')->set('accesMemo', $res->getAccesMemo());
$this->get('session')->set('accesElium', $res->getAccesElium());
}
/**
* @Route("/{_locale<%app.supported_locales%>}/login", name="login")
* @param Request $request
* @param UtilisateursRepository $repository
* @param TranslatorInterface $translator
* @param UtilisateurSocieteAssociationRepository $utilisateurSocieteAssociation
* @return Response
* @throws \Doctrine\DBAL\Driver\Exception
* @throws \Doctrine\DBAL\Exception
* @param MailerInterface $mailer
*/
public function authentification(Request $request, UtilisateursRepository $repository,TranslatorInterface $translator,
UtilisateurSocieteAssociationRepository $utilisateurSocieteAssociation,
CsrfTokenManagerInterface $csrfTokenManager, MailerInterface $mailer) : Response
{
$session = $request->getSession();
$em = $this->getDoctrine()->getManager();
$remainingAttemps = 3;
if ($session->has('societe')) {
return $this->redirectToRoute('index');
}
$utilisateur = new Utilisateurs();
$form = $this->createForm(AuthentificationFormType::class, $utilisateur);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$identifiant = $request->request->get('authentification_form')['identifiant'];
$pwd = $request->request->get('authentification_form')['motdepasse'];
$submittedToken = $request->request->get('authentification_form')['_token'] ?? null;
$connectionIsAllowed = false;
if (!$csrfTokenManager->isTokenValid(new CsrfToken('task_item', $submittedToken))) {
throw $this->createAccessDeniedException('Invalid CSRF token.');
}
if($identifiant && $pwd) {
$hashedPwd = $repository->getHashedPwd($identifiant);
if(!$hashedPwd) {
$this->getLog($request, 'Fail', 'Wrong credentials - Email does not exist');
$this->addFlash('danger', $translator->trans('message.error_auth'));
return $this->render('authentification/index.html.twig', [
'form' => $form->createView(),
'remainingAttempts' => null,
'accountActivated' => null,
]);
}
}
if (password_verify($pwd, $hashedPwd['pwd'])) {
$connectionIsAllowed = true;
}
if($connectionIsAllowed) {
$resLogin = $repository->logInManagement($identifiant);
if($resLogin) {
$this->setConnectionSessions($resLogin[0], $utilisateur, $utilisateurSocieteAssociation, $repository);
$this->getLog($request, 'Success', 'Correct credentials');
return $this->redirectToRoute('index');
}
// SI USER NOT VERIFIED OU COMPTE DESACTIVE //
else {
$this->getLog($request, 'Fail', 'Account not verified / Account blocked');
$this->addFlash('danger', $translator->trans('message.error_auth'));
return $this->render('authentification/index.html.twig', [
'form' => $form->createView(),
'remainingAttempts' => null,
'accountActivated' => null,
]);
}
}
else {
$user = $repository->findOneByIdentifiant($identifiant);
if($user) {
$remainingAttemps = $user->getNbEssaiAvantBloquage();
if ($remainingAttemps > 0 && $user->getIsVerified() != 0) {
$user->setNbEssaiAvantBloquage($remainingAttemps - 1);
if (($remainingAttemps - 1) == 0) {
$user->setCompteDesactive(true);
$messageToReseller = new Email();
$messageToReseller->from($_ENV['MAILER_SENDER']);
$messageToReseller->to($identifiant);
$messageToReseller->subject($translator->trans('mail.objet.account-blocked-reseller',[],'mail+intl-icu'));
$corps = $translator->trans('mail.account-blocked-reseller',[],'mail+intl-icu');
$corps = utf8_decode($corps);
$messageToReseller->html($corps,'text/html','UTF-8');
$messageAdmin = new Email();
$messageAdmin->from($_ENV['MAILER_SENDER']);
$messageAdmin->to('administratif@planet-monetic.fr');
$messageAdmin->subject($translator->trans('mail.objet.account-blocked',[],'mail+intl-icu'));
$corps = $translator->trans('mail.account-blocked',['%$identifiant%' => $identifiant],'mail+intl-icu');
$corps = utf8_decode(str_replace('%identifiant%',$identifiant,$corps));
$messageAdmin->html($corps,'text/html','UTF-8');
$messageToDev = new Email();
$messageToDev->from($_ENV['MAILER_SENDER']);
$messageToDev->to('developpement@planet-monetic.fr');
$messageToDev->subject($translator->trans('mail.objet.account-blocked',[],'mail+intl-icu'));
$corps = $translator->trans('mail.account-blocked',['%$identifiant%' => $identifiant],'mail+intl-icu');
$corps = utf8_decode(str_replace('%identifiant%',$identifiant,$corps));
$messageToDev->html($corps,'text/html','UTF-8');
$mailer->send($messageToReseller);
$mailer->send($messageAdmin);
$mailer->send($messageToDev);
}
$em->persist($user);
$em->flush();
}
}
$this->getLog($request, 'Fail', 'Wrong credentials - Invalid Password');
$this->addFlash('danger', $translator->trans('message.error_auth'));
return $this->render('authentification/index.html.twig', [
'form' => $form->createView(),
'remainingAttempts' => null,
'accountActivated' => null,
]);
}
}
return $this->render('authentification/index.html.twig', [
'form' => $form->createView()
]);
}
/**
* @Route("/{_locale<%app.supported_locales%>}/logout", name="app_logout", methods={"GET"})
*/
public function logout()
{
}
/**
* @Route("/{_locale<%app.supported_locales%>}/forget", name="forget")
* @param Request $request
* @param UtilisateursRepository $repository
* @param TranslatorInterface $translator
* @param MailerInterface $mailer
* @return Response
*/
public function forget(Request $request, UtilisateursRepository $repository,TranslatorInterface $translator, MailerInterface $mailer) : Response
{
$utilisateur = new Utilisateurs();
$form = $this->createForm(ForgetFormType::class, $utilisateur);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$res = $repository->findOneBy(['mail' => $utilisateur->getMail()]);
if($res && $res->getIsVerified()) {
$userInfosForLog = 'User : ' . $res->getNom() . $res->getPrenom() . ' reseller : ' . $res->getFkUtilisateurSociete()->getSociete();
$lien = $this->createUrlToken($res, $request, 'reset');
$message = new Email();
$message->from($_ENV['MAILER_SENDER']);
$message->to($utilisateur->getMail());
$message->bcc('developpement@planet-monetic.fr');
$message->subject($translator->trans('mail.objet.forget',[],'mail+intl-icu'));
$corps = $translator->trans('mail.forget',['%lien%' => $lien],'mail+intl-icu');
$corps = utf8_decode(str_replace('%lien%',$lien,$corps));
$message->html($corps,'text/html','UTF-8');
try{
$mailer->send($message);
$this->addFlash('success', $translator->trans('message.forgetsuccess'));
} catch (TransportExceptionInterface $e) {
$this->addFlash('danger', $translator->trans('message.forgeterror'));
}
} else if ($res && !$res->getIsVerified()) {
$this->addFlash('danger', $translator->trans('message.account-not-activated'));
} else {
$this->addFlash('danger', $translator->trans('message.forgeterror'));
}
}
return $this->render('authentification/forget.html.twig', ['form' => $form->createView()]);
}
private function createUrlToken($user, $request, $action) {
$token = $user->getIdUtilisateur().'|'.date('Y-m-d H:i:s');
$token = strtr(base64_encode(serialize($token)), '+/=', '-_,');
$lien = $this->generateUrl(
$action,
['t' => $token, '_locale' => $request->getLocale()],
UrlGeneratorInterface::ABSOLUTE_URL
);
return $lien;
}
/**
* @Route("/{_locale<%app.supported_locales%>}/reset", name="reset")
* @param Request $request
* @param TranslatorInterface $translator
* @return Response
*/
public function reset(Request $request, Helpers $helpers, TranslatorInterface $translator) : Response
{
if(empty($_GET['t'])){
return $this->redirectToRoute('login');
} else {
$t = explode('|',unserialize(stripslashes(base64_decode(strtr($_GET['t'], '-_,','+/=')))));
$id = $t[0];
$date = $t[1];
if((strtotime(date('Y-m-d H:i')) - strtotime($date))/ 60 > 60){
throw new AccessDeniedException('');
} else {
$utilisateur = new Utilisateurs();
$utilisateur->setIdUtilisateur($id);
$form = $this->createForm(ResetFormType::class, $utilisateur);
$form->handleRequest($request);
if($form->isSubmitted() && $form->isValid()) {
$resetMdpForm = $request->get('reset_form');
//Si le mot de passe est valide
$validationMdp = $helpers->checkMdp(null, $resetMdpForm['newmotdepasse'], $resetMdpForm['confirmmotdepasse'], 14, false);
if($validationMdp) {
$entityManager = $this->getDoctrine()->getManager();
$utilisateur = $entityManager->getRepository(Utilisateurs::class)->find($form->get('idUtilisateur')->getData());
$utilisateur->setMotDePasse(sha1($form->get('confirmmotdepasse')->getData()));
$hashedPassword = password_hash($resetMdpForm['newmotdepasse'], PASSWORD_ARGON2ID, [
'memory_cost' => 65536,
'time_cost' => 4,
'threads' => 1,
]);
$utilisateur->setPwd($hashedPassword);
$entityManager->flush();
return $this->redirectToRoute('login');
}
else {
$this->addFlash('danger', $translator->trans('message.error_auth'));
}
}
return $this->render('authentification/reset.html.twig', ['form' => $form->createView()]);
}
}
}
/**
* Méthode pour reset mdp 1er connexion
*
* @Route("/{_locale<%app.supported_locales%>}/reset_mdp", name="reset_mdp")
*/
public function resetMdp(Request $request, Helpers $helpers, TranslatorInterface $translator, UtilisateursRepository $utilisateurRepo,
UtilisateurSocieteAssociationRepository $utilisateurSocieteAssociationRepo, UtilisateurSocieteRepository $utilisateurSocieteRepo)
{
$dateConnexion = new \DateTime();
$em = $this->getDoctrine()->getManager();
$form = $this->createForm(ResetMdpFormType::class);
$form->handleRequest($request);
//Si pas de connexion, mais accès direct via l'URL, on fait une redirection vers la page de login
if(empty($this->get('session')->get('userTmp'))) {
return $this->redirectToRoute('login');
}
if ($form->isSubmitted()) {
$resetMdpForm = $request->get('reset_mdp_form');
//Si le mot de passe est valide
$validationMdp = $helpers->checkMdp($resetMdpForm['identifiant'], $resetMdpForm['motdepasse'], $resetMdpForm['motdepasse2'], 14);
if($validationMdp) {
$userTmp = $this->get('session')->get('userTmp');
$user = $utilisateurRepo->findOneBy(['idUtilisateur' => $userTmp->getIdUtilisateur()]);
if (!$user) {
throw new \Exception('User not found');
}
$user->setIdentifiant($resetMdpForm['identifiant']);
$user->setMail($resetMdpForm['identifiant']);
// PASSWORD LOGIC //
$maxLength = 128;
if (mb_strlen($resetMdpForm['motdepasse']) > $maxLength) {
throw new \InvalidArgumentException("Password cannot be longer than $maxLength characters.");
}
$user->setMotDePasse(sha1($resetMdpForm['motdepasse']));
$hashedPassword = password_hash($resetMdpForm['motdepasse'], PASSWORD_ARGON2ID, [
'memory_cost' => 65536,
'time_cost' => 4,
'threads' => 1,
]);
$user->setPwd($hashedPassword);
// END PASSWORD LOGIC //
$user->setDerniereConnexion($dateConnexion);
$user->setAncienMotDePasse(NULL);
$em->persist($user);
$em->flush();
//Création tous les sessions de connexion
$utilisateur = new Utilisateurs();
$this->setConnectionSessions($user, $utilisateur, $utilisateurSocieteAssociationRepo, $utilisateurRepo);
return $this->redirectToRoute('index');
}
else {
$this->addFlash('danger', $translator->trans('message.error_auth'));
}
}
return $this->render('authentification/reset_mdp.html.twig', ['form' => $form->createView()]);
}
/**
* @Route("/{_locale<%app.supported_locales%>}/create_password", name="create_password")
* @param Request $request
* @param TranslatorInterface $translator
* @return Response
*/
public function createPassword(Request $request, Helpers $helpers, TranslatorInterface $translator,
UtilisateursRepository $utilisateurRepo,
CsrfTokenManagerInterface $csrfTokenManager) : Response
{
if (empty($_GET['t'])) {
return $this->redirectToRoute('login');
} else {
$t = explode('|', unserialize(stripslashes(base64_decode(strtr($_GET['t'], '-_,', '+/=')))));
$id = $t[0];
$date = $t[1];
if ((strtotime(date('Y-m-d H:i')) - strtotime($date)) / 60 > 60) {
throw new AccessDeniedException('');
} else {
$utilisateur = new Utilisateurs();
$utilisateur->setIdUtilisateur($id);
$form = $this->createForm(CreatePasswordFormType::class, $utilisateur);
$form->handleRequest($request);
if($form->isSubmitted() && $form->isValid()) {
$submittedToken = $request->request->get('create_password_form')['_token'] ?? null;
if (!$csrfTokenManager->isTokenValid(new CsrfToken('task_item', $submittedToken))) {
throw $this->createAccessDeniedException('Invalid CSRF token.');
}
$createPasswordForm = $request->get('create_password_form');
$validationMdp = $helpers->checkMdp($createPasswordForm['identifiant'], $createPasswordForm['newmotdepasse'], $createPasswordForm['confirmmotdepasse'], 14);
if ($validationMdp) {
$em = $this->getDoctrine()->getManager();
$utilisateur = $em->getRepository(Utilisateurs::class)->find($form->get('idUtilisateur')->getData());
if ($form->get('identifiant')->getData() == $utilisateur->getIdentifiant()) {
$utilisateur->setMotDePasse(sha1($form->get('confirmmotdepasse')->getData()));
$hashedPassword = password_hash($form->get('confirmmotdepasse')->getData(), PASSWORD_ARGON2ID, [
'memory_cost' => 65536,
'time_cost' => 4,
'threads' => 1,
]);
$utilisateur->setPwd($hashedPassword);
$utilisateur->setIsVerified(1);
$em->flush();
return $this->redirectToRoute('login');
}
else {
$this->addFlash('danger', $translator->trans('message.error_auth'));
}
}
else {
$this->addFlash('danger', $translator->trans('message.error_auth'));
}
}
}
return $this->render('authentification/create_password.html.twig', ['form' => $form->createView()]);
}
}
public function getLog($request, $status, $reason)
{
$userIp = $request->getClientIp();
$userIdentifiant = $request->request->get('authentification_form')['identifiant'];
$date = date('d-m-Y');
$month = date('F');
$year = date('Y');
$dateLog = date('d-m-Y');
$timeLog = date('H-i-s');
// create folder if not exist
if (!file_exists($this->getParameter('kernel.project_dir') . '/public/works/logs/connection_attempts_logs/' . $month . $year)) {
mkdir($this->getParameter('kernel.project_dir') . '/public/works/logs/connection_attempts_logs/' . $month . $year, 0755, true);
}
$log = fopen($this->getParameter('kernel.project_dir') . '/public/works/logs/connection_attempts_logs/' . $month . $year .'/connection-log-'. $date . '.txt', 'a+');
fwrite($log,
"\n" .
'************** Login attempt **************' . "\n" .
'IP address : ' . $userIp . "\n" .
'Status : ' . $status . "\n" .
'Login : ' . $userIdentifiant . "\n" .
'Reason : ' . $reason . "\n" .
'On the : ' . $dateLog . ' at ' . $timeLog . "\n" .
'******************************************' . "\n");
fclose($log);
}
}