<?php
namespace App\Controller;
// begin token-guard ---------------------------------------------------------------------------------
use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
use Symfony\Component\Security\Csrf\CsrfToken;
use Symfony\Component\Security\Core\Exception\InvalidCsrfTokenException;
use Symfony\Component\Security\Guard\Authenticator\AbstractFormLoginAuthenticator;
// end token-guard ---------------------------------------------------------------------------------
use App\Service\LOGDEFService;
use App\Service\DIVService;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
use Doctrine\Persistence\ManagerRegistry;
use Symfony\Component\Validator\Validator\ValidatorInterface; // contrôleur de contraintes
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface; // l'encodeur ofcourse
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
use App\Entity\usr;
use App\Entity\usl;
class SecurityController extends AbstractController
{
#[Route(path: '/identification', name: 'identification')]
public function identification
(
ManagerRegistry $doctrine,
AuthenticationUtils $authenticationUtils,
UserPasswordHasherInterface $passwordHasher,
DIVService $divservice,
): Response
{
$em = $doctrine->getManager();
// activer si besoin de coder / définir un pwd pour un glups
/*
$usr = $em->getRepository(usr::class)->findOneBy(array('username'=>'stephy.deveaux'));
$plaintextPassword = 'agagagagag:';
$hashedPassword = $passwordHasher->hashPassword(
$usr,
$plaintextPassword
);
$usr->setPassword($hashedPassword);
$em->persist($usr);
$em->flush();
*/
// =============== CTRL LOGIN'S =====================
//var_dump($_SERVER);
// vérif occurences demandes : combien de fois en combien de temps pour quel login
$delaymin = 1800; // délai maxi pour une série de tentative de login pour une IP (30')
$nbtryipmax = 16; // nb tentatives max pour une même IP en moins de 'delaymin'
$nbtrylogmax = 10; // nb tentatives échouées max pour un login en moins de 'delaymin'
$ip = '';
if(isset($_SERVER['HTTP_X_FORWARDED_FOR'])){ // récup IP origine dde
$ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
}else{
$ip = $_SERVER['REMOTE_ADDR'];
}
$datnow = time();
$perioddde = date('Y-m-d H:i:s', mktime(date('H', $datnow),date('i', $datnow),date('s', $datnow),date('m', $datnow),date('d', $datnow),date('Y', $datnow)));
$datlimit = $datnow - $delaymin;
$periodlimit = date('Y-m-d H:i:s', mktime(date('H', $datlimit),date('i', $datlimit),date('s', $datlimit),date('m', $datlimit),date('d', $datlimit),date('Y', $datlimit)));
// 1° niveau CTRL : affichage répété page login pour rien ================================
$ctx = '';$nbtry=0;
if((isset($_SERVER['HTTP_REFERER'])) && (isset($_SERVER['HTTP_COOKIE']))){ // logout possible, pas de vérif
$ctx = 'logout';
// dd('http referer isset et cookie');
}
// nb ddes pour cette IP dans les dernières 'delaymin' secondes avec ou sans login
if($ctx!='logout'){
// dd($ctx.', '.$ip);
$query = $em->getRepository(usl::class)->createQueryBuilder('usl')
->select("COUNT(usl.uslip) AS nbtry")
->where('usl.uslip = :uslip')
->setParameter('uslip', $ip)
->andwhere('usl.uslip NOT IN (:iplocale)')
// ->setParameter('iplocale' , array('127.0.0.1','192.168.26.50')) // pour audit cq + locloc
->setParameter('iplocale' , array('127.0.0.1')) // locloc ou cq
->andwhere('usl.usldat BETWEEN :periodlimit AND :perioddde')
->setParameter('perioddde' ,$perioddde)
->setParameter('periodlimit' ,$periodlimit)
;
$nbtry = $query->getQuery()->getSingleScalarResult();
}
// 1° blocage : affichage excessif de la page de login pour rien
if($nbtry>=$nbtryipmax){ // abus
$this->addFlash('Erreur', "IP Reconnectez-vous dans 1 heure. Tentatives de connexion excessives : ".$nbtryipmax);
// renvoi page erreur
return $this->render('security/erreur.html.twig',
array(
'ip' => $ip
)
);
}
// 2° niveau CTRL : tentative de login répétée avec échec => error ========================
// vérif si même IP a tenté de se loguer plus de nbtrylogmax pour un login avec N comptes....
$query = $em->getRepository(USL::class)->createQueryBuilder('usl')
->select("COUNT(usl.uslip) AS nbtry")
->where('usl.uslctx =:uslctx')
->setParameter('uslctx', 'error')
->andwhere('usl.usldat BETWEEN :periodlimit AND :perioddde')
->setParameter('perioddde' ,$perioddde)
->setParameter('periodlimit' ,$periodlimit)
;
$nbtry = $query->getQuery()->getSingleScalarResult();
if($nbtry>=$nbtrylogmax){ // abus
$this->addFlash('Erreur', "IP Reconnectez-vous dans 1 heure. Tentatives de connexion excessives : ".$nbtrylogmax);
// renvoi page erreur
return $this->render('security/erreur.html.twig',
array(
'ip' => $ip
)
);
}else{
// enregistrement des caractéristiques de la demande de login
$error = $authenticationUtils->getLastAuthenticationError(); // get the login error if there is one
$lastUsername = $authenticationUtils->getLastUsername(); // last username entered by the user
// possible provenance logout, récup phpsessid pour id usr
$cook = '';
if(isset($_SERVER['HTTP_REFERER'])){
if(isset($_SERVER['HTTP_COOKIE'])){ // id usr possibl
$cook = str_replace('PHPSESSID=','',$_SERVER['HTTP_COOKIE']);
$pos = strpos($cook, ";");
if(($pos>1)&&($pos<255)){
$cook = substr($cook, 0, $pos-1);
}else{
$cook = substr($cook, 0, 254);
}
}
}
if($error){ // inactive renvoi erreur niveau Render (ci-dessous)
$login = $lastUsername;
$ctx = 'error';
$this->addFlash('Erreur', 'Identification invalide');
}else{
if($lastUsername!=''){
$login = $lastUsername;
$ctx = 'login';
if($cook==''){
$ctx = 'start';
}
}else{
$login = '';
if($cook==''){
$ctx = 'start';
}else{
$ctx = 'logout';
}
}
}
// enrgt dde : IP + Heure + login (0,1)
$datdde = new \DateTime();
$usl = new usl;
$usl->setUslip($ip);
$usl->setUsldat($datdde);
$usl->setUsllogin($login);
$usl->setUslctx($ctx);
$usl->setUslcook($cook);
$em->persist($usl);
$em->flush();
// 2° niveau vérif ================================
// nb échecs pour ce login dans les dernières 'delaymin' secondes
$nbtry = 0;
if($login!=''){
$query = $em->getRepository(USL::class)->createQueryBuilder('usl')
->select("COUNT(usl.uslip) AS nbtry")
->where('usl.usllogin = :usllogin')
->setParameter('usllogin', $login)
->andwhere('usl.uslctx <> :uslctx')
->setParameter('uslctx', 'error')
->andwhere('usl.usldat BETWEEN :periodlimit AND :perioddde')
->setParameter('perioddde' ,$perioddde)
->setParameter('periodlimit' ,$periodlimit)
;
$nbtry = $query->getQuery()->getSingleScalarResult();
}
if($nbtry>=$nbtrylogmax){
$this->addFlash('Erreur', "LOGIN : Reconnectez-vous dans 1 heure, nb tentatives excessives > ".$nbtrylogmax);
// renvoi page erreur
return $this->render('security/erreur.html.twig',
array(
'ip' => $ip
)
);
}else{
// situation normale ras
return $this->render('security/login.html.twig', [
'last_username' => $lastUsername,
'error' => $error,
// 'deflog' => $deflog,
]);
}
}
}
#[Route(path: '/login', name: 'app_login')]
public function login
(
AuthenticationUtils $authenticationUtils,
LOGDEFService $logdefservice,
DIVService $divservice,
): Response
{
// en cas d'erreur, enregistrement des caractéristiques de la demande de login
// TODO : tenter de réafficher identification !?
$error = $authenticationUtils->getLastAuthenticationError(); // get the login error if there is one
$lastUsername = $authenticationUtils->getLastUsername(); // last username entered by the user
// ACCUEIL : possible provenance logout, récup phpsessid pour id usr
$cook = '';
if(isset($_SERVER['HTTP_REFERER'])){
if(isset($_SERVER['HTTP_COOKIE'])){ // id usr possibl
$cook = str_replace('PHPSESSID=','',$_SERVER['HTTP_COOKIE']);
$pos = strpos($cook, ";");
if(($pos>1)&&($pos<255)){
$cook = substr($cook, 0, $pos-1);
}else{
$cook = substr($cook, 0, 254);
}
}
}
if($error){ // inactive renvoi erreur niveau Render (ci-dessous)
$login = $lastUsername;
$ctx = 'error';
$this->addFlash('Erreur', 'Identification invalide');
}else{
if($lastUsername!=''){
$login = $lastUsername;
$ctx = 'login';
if($cook==''){
$ctx = 'start';
}
}else{
$login = '';
if($cook==''){
$ctx = 'start';
}else{
$ctx = 'logout';
}
}
}
// OUTILS : infobulles fusion de la page
$lesdefs = array('ppi','pfb','mfp');
$deflog = array();
foreach($lesdefs as $ladef){
$deflog[$ladef] = $logdefservice->get_unedef($ladef);
}
// récup guide pratique CF
$gp = $divservice->crypt_cfppiid(57);
return $this->render('security\accueil.html.twig', array(
'last_username' => $lastUsername,
'deflog' => $deflog,
'error' => $error,
'gp' => $gp
)
);
}
// les cartes à jouer avec les outils financiers
#[Route(path: '/nosoutils', name: 'nosoutils')]
public function nosoutils(
LOGDEFService $logdefservice,
): Response
{
// a) les infobulles fusion de la page
// $lesdefs = array('ppi','mod','pro','eau','pfi','lis','mev');
$lesdefs = array('ppi','raf','mfp');
$deflog = array();
foreach($lesdefs as $ladef){
$deflog[$ladef] = $logdefservice->get_unedef($ladef);
}
// situation normale ras
return $this->render('security/nosoutils.html.twig', [
'deflog' => $deflog,
]);
}
// Téléchargement du guide utilisateur
#[Route(path: '/downloadguide', name: 'downloadguide')]
public function downloadguide(
Request $request,
CsrfTokenManagerInterface $csrfTokenManager,
ParameterBagInterface $parambag,
DIVService $divservice,
): Response
{
$cause = -1;
$ouv_poss = false; // protection ACL contre les lectures intempestives extérieures (saisie url directe)
// begin token-guard --------------------------------------------------------------------------------------------------------------
if($request->query->get('dou')=='accueil'){
// pas de token
}else{
if($request->query->get('dou')=='autre'){
$tokorigin = 'yoplaboum'; // provenance de ailleurs
}
$csrfToken = $request->query->get('tok'); // contient le _token
if (false === $csrfTokenManager->isTokenValid(new CsrfToken($tokorigin, $csrfToken))) {
$cause = '4';
// end token-guard --------------------------------------------------------------------------------------------------------------
}
}
if($cause==-1){ // poursuit
$data_nfid = $request->query->get('nfid'); // nfid trafiqué
$gp = $divservice->decrypt_cfppiid($data_nfid);
if($gp!='57'){ // juste pour voir si accès direct pas détourné par des zombis
$cause = '2'; // pas ok
}else{
$ouv_poss = true;
}
}
if($ouv_poss){ // pour le moment pas d'accès au guide suprême, il est pas là
$nom_fichier = 'guide_utilisateur_cf.pdf';
$chemin_fichier = $parambag->get('dochelp_web_path');
$content = file_get_contents($chemin_fichier.$nom_fichier);
$response = new Response();
//set headers
$response->headers->set('Content-Type', 'mime/type');
// $response->headers->set('Content-Disposition', 'attachment;filename="'.bin2hex(random_bytes(32)).'.pdf"');
$response->headers->set('Content-Disposition', 'attachment;filename="guide_utilisateur_cf.pdf"');
$response->setContent($content);
return $response;
}else{
$this->get('session')->getFlashBag()->add(
'error',
'Impossible d\'accéder à la fonction demandée'.' ('.$cause.')'
);
return $this->redirect($this->generateUrl('pageaccueil')); // renvoi caval
}
}
#[Route(path: '/ruralconsult', name: 'ruralconsult')]
public function ruralconsult(): Response
{
return $this->render('security\ruralconsult.html.twig', array());
}
#[Route(path: '/logout', name: 'app_logout')]
public function logout(): void
{
throw new \LogicException('This method can be blank - it will be intercepted by the logout key on your firewall.');
}
}