<?php
namespace App\Controller\Admin;
use App\Entity\Utilisateur;
use App\Form\AdminUtilisateurType;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Tools\Pagination\Paginator;
use Psr\Log\LoggerInterface;
use Symfony\Bridge\Twig\Mime\TemplatedEmail;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Mailer\MailerInterface;
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
/**
* @method \App\Entity\Utilisateur getUser()
*/
class SecurityController extends AbstractController
{
public const UTILISATEUR_TYPES = [
'username' => Types::STRING,
'password' => Types::STRING,
'salt' => Types::STRING,
'roles' => Types::TEXT,
'civilite' => Types::STRING,
'nom' => Types::STRING,
'prenom' => Types::STRING,
'mail' => Types::STRING,
'is_active' => Types::BOOLEAN,
// 'date_creation' => Types::DATETIME_IMMUTABLE,
// 'date_connexion' => Types::DATETIME_IMMUTABLE,
// 'date_modification' => Types::DATETIME_IMMUTABLE,
// 'date_debut' => Types::DATETIME_IMMUTABLE,
// 'date_fin' => Types::DATETIME_IMMUTABLE,
'telephone' => Types::STRING,
'archived' => Types::BOOLEAN,
];
public const UTILISATEUR_STRUCTURE_TYPES = [
'idutilisateur' => Types::INTEGER,
'idstructure' => Types::INTEGER,
'isadmin' => Types::BOOLEAN,
'issuperv' => Types::BOOLEAN,
'factu_acces' => Types::BOOLEAN,
'factu_level' => Types::INTEGER,
'responsable' => Types::BOOLEAN,
];
private Connection $db;
private EntityManagerInterface $em;
private LoggerInterface $usersLogger;
private MailerInterface $mailer;
/** @var \Doctrine\ORM\EntityRepository<Utilisateur> */
private $repository;
public function __construct(Connection $connection, EntityManagerInterface $manager, LoggerInterface $usersLogger, MailerInterface $mailer)
{
$this->db = $connection;
$this->em = $manager;
$this->repository = $manager->getRepository(Utilisateur::class);
$this->mailer = $mailer;
$this->usersLogger = $usersLogger;
}
public function loginAction(AuthenticationUtils $authenticationUtils): Response
{
// get the login error if there is one
$error = $authenticationUtils->getLastAuthenticationError();
// last username entered by the user
$lastUsername = $authenticationUtils->getLastUsername();
return $this->render('Admin/Security/login.html.twig', [
'last_username' => $lastUsername,
'error' => $error,
]);
}
public function checkAction()
{
throw new \RuntimeException('You must configure the check_path to be handled by the firewall using form_login in your security firewall configuration.');
}
/**
* @see https://symfony.com/doc/current/security.html#logging-out
*/
public function logoutAction()
{
// controller can be blank: it will never be executed!
throw new \Exception('Don\'t forget to activate logout in security.yaml');
}
public function lostPasswordAction(Request $request, UserPasswordHasherInterface $passwordHasher): Response
{
if ($request->get('email')) {
$utilisateur = $this->repository->findOneByMail($request->get('email'));
if (!$utilisateur) {
$this->addFlash('error', 'Cette adresse e-mail n\'est pas enregistrée.');
return $this->redirectToRoute('admin_lostPassword');
} else {
$newpass = $this->randomPassword();
//$url = 'http://numix-uce.com';
$url = 'http://' . $_SERVER['HTTP_HOST'] . '/';
$password = $passwordHasher->hashPassword($utilisateur, $newpass);
$utilisateur->setPassword($password);
$this->em->persist($utilisateur);
$this->em->flush();
// Envoi du mail
$messagemail = (new TemplatedEmail())
->to($request->get('email'))
->subject('Numix : Nouveau mot de passe')
->htmlTemplate('Admin/Mail/mailNewPass.html.twig')
->textTemplate('Admin/Mail/mailNewPass.txt.twig')
->context([
'newpass' => $newpass,
'url' => $url
]);
$this->mailer->send($messagemail);
$this->addFlash('notice', 'Votre mot de passe a été modifié. Le nouveau mot de passe a été envoyé à l\'adresse <strong>' . $request->get('email') . '</strong>');
return $this->redirectToRoute('admin_lostPassword');
}
}
return $this->render('Admin/Security/lostPassword.html.twig');
}
private function randomPassword()
{
$alphabet = "abcdefghijklmnopqrstuwxyzABCDEFGHIJKLMNOPQRSTUWXYZ0123456789";
$pass = [];
$alphaLength = strlen($alphabet) - 1;
for ($i = 0; $i < 8; $i++) {
$n = rand(0, $alphaLength);
$pass[] = $alphabet[$n];
}
return implode($pass);
}
public function listUsersAction(Request $request): Response
{
$qb = $this->em->createQueryBuilder();
$search = $request->get('search');
if (isset($search) && !empty($search)) {
$argsSearch = [];
foreach ($search['user'] as $var => $val) {
if ($val != '') {
$argsSearch[$var] = $search['user'][$var];
}
}
$request->getSession()->set('searchUtilisateur', $argsSearch);
return $this->redirectToRoute('admin_users');
}
$sortOrder = strtoupper($request->get('sortOrder', 'asc'));
$order = $request->get('order', 'username');
$page = $request->get('page', 1);
// $nbparpage = $this->getParameter('listes_nbparpage');
$nbparpage = 999999;
$limit = 0;
if ($page != '') {
$limit = ($page - 1) * $nbparpage;
} else {
$page = 1;
}
$start = ($page - 1) * $nbparpage;
$queryWhere = "";
$sessionSearch = $request->getSession()->get('searchUtilisateur');
if (isset($sessionSearch) && !empty($sessionSearch)) {
foreach ($sessionSearch as $var => $val) {
if ($var == 'mail') {
$var = 'u.mail';
}
if ($var == 'archived') {
$queryWhere .= " AND " . $var . " = '" . $val . "' ";
} else {
$queryWhere .= " AND lower(" . $var . ") LIKE lower('%" . addslashes($val) . "%')";
}
}
} else {
$queryWhere .= " AND archived = 'f' ";
$request->getSession()->set('searchUtilisateur', ['archived' => 'f']);
}
$listStructQuery = 'SELECT s.* FROM structure s WHERE s.niveau < 30';
$listStruct = $this->db->fetchAllAssociative($listStructQuery);
$listLinksQuery = 'SELECT us.* FROM utilisateur_structure us';
$listLinks = $this->db->fetchAllAssociative($listLinksQuery);
$activeStructure = $request->getSession()->get('activeStructure');
$listIdsQuery = '
SELECT us.idutilisateur
FROM utilisateur_structure us, structure s, utilisateur u
WHERE u.id = us.idutilisateur
AND s.idstructure = us.idstructure
AND (s.idstructure = ' . $activeStructure->getIdstructure() . ' OR s.idparent = ' . $activeStructure->getIdstructure() . ')
AND s.niveau < 100 ' . $queryWhere;
if ($activeStructure->getNiveau() > 10 && $activeStructure->getNiveau() < 100) {
$listIdsQuery .= ' AND u.id NOT IN (SELECT idutilisateur FROM utilisateur_structure WHERE idstructure = 1)';
}
$listIds = $this->db->fetchAllAssociative($listIdsQuery);
if (count($listIds) > 0) {
$listIdsStr = '';
foreach ($listIds as $id) {
$listIdsStr .= ($listIdsStr == '') ? $id['idutilisateur'] : ',' . $id['idutilisateur'];
}
$qb->select('u')
->from(Utilisateur::class, 'u')
->where('u.id IN (' . $listIdsStr . ')')
// ->groupBy('u.id')
->orderBy('u.' . $order, $sortOrder)
->setMaxResults($nbparpage)
->setFirstResult($start);
$utilisateurs = new Paginator($qb);
} else {
$utilisateurs = [];
}
$nbpages = ceil(count($utilisateurs) / $nbparpage);
$route = $request->get('_route');
$pagination = ($nbpages > 1) ? $this->setPagination($order, $sortOrder, $page, $nbpages, $route) : '';
return $this->render('Admin/Security/listUsers.html.twig', ['utilisateurs' => $utilisateurs, 'pagination' => $pagination, 'route' => $route, 'order' => $order, 'sortOrder' => $request->get('sortOrder'), 'listStruct' => $listStruct, 'listLinks' => $listLinks]);
}
public function editUserAction(Request $request, UserPasswordHasherInterface $passwordHasher): Response
{
$utilisateur = null;
$originalpass = null;
$activeStructure = $request->getSession()->get('activeStructure');
$query = 'SELECT * FROM structure s WHERE (s.idstructure = ' . $activeStructure->getIdstructure() . ' OR s.idparent = ' . $activeStructure->getIdstructure() . ') AND niveau < 100 ORDER BY libelle';
$structures = $this->db->fetchAllAssociative($query);
foreach ($structures as $k => $str) {
$query = 'SELECT count(*) FROM licence l WHERE l.id_structure = ' . $str['idstructure'] . ' AND now() between l.date_debut AND l.date_fin';
$licences = $this->db->fetchAssociative($query);
if ($str['niveau'] < 20) {
$structures[$k]['licences'] = 1;
} else {
$structures[$k]['licences'] = $licences['count'];
}
}
if ($request->get('id') != '') {
$query = 'SELECT * FROM utilisateur_structure us WHERE us.idutilisateur = ' . $request->get('id');
$userStructure = $this->db->fetchAllAssociative($query);
} else {
$userStructure = [];
}
// Verification de l'action en cours : POST = le formulaire a été soumis, pas POST = formulaire à créer
if ($request->getMethod() == 'POST') {
$action = ($request->get('id') != '') ? 'update' : 'create';
} else {
$action = ($request->get('id') != '') ? 'edit' : 'new';
}
// On crée un nouvel objet Utilisateur ou on charge l'existant suivant qu'on est en mode edition ou creation
if ($action == 'new' || $action == 'create') {
$utilisateur = new Utilisateur();
} elseif ($action == 'edit' || $action == 'update') {
$utilisateur = $this->repository->findOneById($request->get('id'));
if (!$utilisateur) {
throw $this->createNotFoundException('Utilisateur inconnu');
}
$originalpass = $utilisateur->getPassword();
}
// On crée le formulaire : le pass est obligatoire uniquement en mode création
$form = $this->createForm(AdminUtilisateurType::class, $utilisateur);
// On gère l'enregistrement uniquement en cas de soumission du formulaire
if ($action == 'create' || $action == 'update') {
$form->handleRequest($request);
if ($form->isValid()) {
$plainPassword = $form->get('password')->getData();
if (!empty($plainPassword)) {
$utilisateur->setSalt(md5(uniqid('', true)));
$password = $passwordHasher->hashPassword($utilisateur, $plainPassword);
$utilisateur->setPassword($password);
} else {
$utilisateur->setPassword($originalpass);
}
$role = ['ROLE_ADMIN'];
$utilisateur->setRoles($role);
$utilisateur->setDateCreation(new \DateTime());
$utilisateur->setArchived('f');
if ($utilisateur->getId() && $utilisateur->getIsActive()) {
$this->db->executeStatement("UPDATE users_jail SET freed = 't' WHERE id_user = " . $utilisateur->getId());
}
$this->em->persist($utilisateur);
$this->em->flush();
// Gestion du message flash
$listIdsStruct = '';
foreach ($structures as $struc) {
$listIdsStruct .= ($listIdsStruct == '') ? $struc['idstructure'] : ',' . $struc['idstructure'];
}
$this->db->executeStatement("DELETE FROM utilisateur_structure WHERE idutilisateur = " . $utilisateur->getId() . " AND idstructure IN (" . $listIdsStruct . ")");
if ($request->get('structures')) {
foreach ($request->get('structures') as $ids => $link) {
if (isset($link['membre']) && $link['membre'] == 'membre') {
$userStructure = ['idutilisateur' => $utilisateur->getId(), 'idstructure' => $ids];
if (isset($link['droit'])) {
switch ($link['droit']) {
case 'admin':
$userStructure['isadmin'] = true;
break;
case 'superv':
$userStructure['issuperv'] = true;
break;
}
}
if (isset($link['responsable'])) {
$userStructure['responsable'] = ($link['responsable'] == 'responsable') ? true : false;
} else {
$userStructure['responsable'] = false;
}
//check licence
$hasLicence = false;
$libStruct = '';
foreach ($structures as $struc) {
if ($struc['idstructure'] == $ids) {
if ($struc['niveau'] > 10) {
if ($struc['licences'] > 0) {
$hasLicence = true;
$countUsersLicence = 0;
$query = 'SELECT id_licence, nb_utilisateur FROM licence l WHERE l.id_structure = ' . $struc['idstructure'] . ' AND now() between l.date_debut AND l.date_fin';
$licence = $this->db->fetchAssociative($query);
if ($licence['nb_utilisateur']) {
$countUsersLicence += $licence['nb_utilisateur'];
}
$query = 'SELECT * FROM option_licence o WHERE o.id_licence = ' . $licence['id_licence'] . ' AND nb_utilisateur is not null';
$options = $this->db->fetchAllAssociative($query);
if (count($options) > 0) {
foreach ($options as $opt) {
$countUsersLicence += $opt['nb_utilisateur'];
}
}
$query = "SELECT count(*) FROM utilisateur_structure WHERE idstructure = " . $struc['idstructure'] . " AND factu_acces = 't'";
$countUsersFactu = $this->db->fetchAssociative($query);
}
$libStruct = $struc['libelle'];
} else {
$hasLicence = true;
$countUsersLicence = 1;
$countUsersFactu = ['count' => 0];
}
}
}
if (isset($link['factu']) && $link['factu'] != '') {
if ($hasLicence == true) {
if ($countUsersFactu['count'] < $countUsersLicence) {
$userStructure['factu_acces'] = true;
$userStructure['factu_level'] = $link['factu'];
} else {
$message = 'Le nombre maximum d\'utilisateurs pour la licence Facturation disponible pour la structure ' . $libStruct . ' est déjà atteint.';
$this->addFlash('notice', $message);
}
} else {
$message = 'Aucune licence Facturation disponible pour la structure ' . $libStruct . '.';
$this->addFlash('notice', $message);
}
}
$res = $this->db->insert('utilisateur_structure', $userStructure, self::UTILISATEUR_STRUCTURE_TYPES);
}
}
}
$message = ($action == 'create') ? 'Utilisateur créé avec succés' : 'Utilisateur modifié avec succés';
$this->addFlash('notice', $message);
$this->usersLogger->info('Numix parcours - editUser - $message : ' . $message . ' - User created : ' . $utilisateur->getId() . ' - User created by : ' . $this->getUser()->getId());
// Redirection
if ($request->get('savestay')) {
return $this->redirectToRoute('admin_user_edit', ['id' => $utilisateur->getId()]);
} else {
return $this->redirectToRoute('admin_users');
}
}
}
return $this->render('Admin/Security/editUser.html.twig', ['form' => $form->createView(), 'action' => $action, 'structures' => $structures, 'userStructure' => $userStructure, 'activeStructure' => $activeStructure]);
}
public function deleteUserAction(Request $request)
{
$utilisateur = $this->repository->findOneById($request->get('id'));
if (!$utilisateur) {
throw $this->createNotFoundException('Utilisateur inconnu');
}
$idUser = $utilisateur->getId();
$this->em->remove($utilisateur);
$this->em->flush();
$message = 'Utilisateur supprimé avec succés';
$this->addFlash('notice', $message);
$this->usersLogger->info('Numix parcours - deleteUser - $message : ' . $message . ' - User deleted : ' . $idUser . ' - User deleted by : ' . $this->getUser()->getId());
return $this->redirectToRoute('admin_users');
}
public function archiveUserAction(Request $request)
{
$utilisateur = $this->repository->findOneById($request->get('id'));
if (!$utilisateur) {
throw $this->createNotFoundException('Utilisateur inconnu');
}
$idUser = $utilisateur->getId();
$utilisateur->setIsActive(FALSE);
$utilisateur->setArchived(TRUE);
$this->em->persist($utilisateur);
$this->em->flush();
$message = 'Utilisateur archivé avec succés';
$this->addFlash('notice', $message);
$this->usersLogger->info('Numix parcours - archiveUser - $message : ' . $message . ' - User archived : ' . $idUser . ' - User archived by : ' . $this->getUser()->getId());
return $this->redirectToRoute('admin_users');
}
public function unarchiveUserAction(Request $request)
{
$utilisateur = $this->repository->findOneById($request->get('id'));
if (!$utilisateur) {
throw $this->createNotFoundException('Utilisateur inconnu');
}
$idUser = $utilisateur->getId();
$utilisateur->setIsActive(FALSE);
$utilisateur->setArchived(FALSE);
$this->em->persist($utilisateur);
$this->em->flush();
$message = 'Utilisateur restauré avec succés';
$this->addFlash('notice', $message);
$this->usersLogger->info('Numix parcours - unarchiveUser - $message : ' . $message . ' - User unarchived : ' . $idUser . ' - User unarchived by : ' . $this->getUser()->getId());
return $this->redirectToRoute('admin_users');
}
public function exportUsersAction(Request $request): Response
{
$qb = $this->em->createQueryBuilder();
$sortOrder = 'ASC';
$order = 'username';
$queryWhere = "";
$sessionSearch = $request->getSession()->get('searchUtilisateur');
if (isset($sessionSearch) && !empty($sessionSearch)) {
foreach ($sessionSearch as $var => $val) {
if ($var = 'archived') {
$queryWhere .= " AND (u." . $var . ") = ('" . $val . "')";
} else {
$queryWhere .= " AND lower(" . $var . ") LIKE lower('%" . $val . "%')";
}
}
}
$listStructQuery = 'SELECT s.* FROM structure s WHERE s.niveau < 30';
$listStruct = $this->db->fetchAllAssociative($listStructQuery);
$listLinksQuery = 'SELECT us.* FROM utilisateur_structure us';
$listLinks = $this->db->fetchAllAssociative($listLinksQuery);
$activeStructure = $request->getSession()->get('activeStructure');
$listIdsQuery = 'SELECT us.idutilisateur FROM utilisateur_structure us, structure s, utilisateur u WHERE u.id = us.idutilisateur AND s.idstructure = us.idstructure AND (s.idstructure = ' . $activeStructure->getIdstructure() . ' OR s.idparent = ' . $activeStructure->getIdstructure() . ')' . $queryWhere;
$listIds = $this->db->fetchAllAssociative($listIdsQuery);
$listIdsStr = '';
foreach ($listIds as $id) {
$listIdsStr .= ($listIdsStr == '') ? $id['idutilisateur'] : ',' . $id['idutilisateur'];
}
$qb->select('u')
->from(\App\Entity\Utilisateur::class, 'u')
->where('u.id IN (' . $listIdsStr . ')')
->orderBy('u.' . $order, $sortOrder);
$utilisateurs = new Paginator($qb, $fetchJoinCollection = true);
$tmpPath = $this->getParameter('kernel.project_dir') . '/var/tmp/';
$filename = 'exportUtilisateurs' . $this->getUser()->getUsername() . date('YmdHis') . '.csv';
$f = fopen($tmpPath . $filename, "w");
$entetes = ["Civilité", "Nom", "Prénom", "Mail", "Téléphone", "Structure"];
array_walk($entetes, [$this, 'encodeCSV']);
fputcsv($f, $entetes, ';');
foreach ($utilisateurs as $u) {
$structlib = '';
foreach ($listLinks as $link) {
if ($link['idutilisateur'] == $u->getId()) {
foreach ($listStruct as $struct) {
if ($link['idstructure'] == $struct['idstructure']) {
$structlib .= $struct['libelle'] . "\n";
}
}
}
}
$line = [
$u->getCivilite(),
//attente H ou F
$u->getNom(),
$u->getPrenom(),
$u->getMail(),
$u->getTelephone(),
$structlib,
];
array_walk($line, [$this, 'encodeCSV']);
fputcsv($f, $line, ';', '"');
}
// Generate response
$response = new Response();
// Set headers
$response->headers->set('Cache-Control', 'private');
$response->headers->set('Content-Type', 'text/csv');
$response->headers->set('Content-Disposition', 'attachment; filename="' . basename($filename) . '";');
$response->headers->set('Content-length', filesize($tmpPath . $filename));
// Send headers before outputting anything
$response->sendHeaders();
$response->setContent(readfile($tmpPath . $filename));
return $response;
}
private function encodeCSV(&$value, $key)
{
$value = iconv("UTF-8", "WINDOWS-1252", $value);
}
private function setPagination($order, $sortOrder, $page, $nbpages, $route)
{
$args = '';
$args .= isset($order) ? '&order=' . $order : '';
$args .= isset($sortOrder) ? '&sortOrder=' . $sortOrder : '';
$active = ($page == 1) ? 'class="active"' : '';
$url = $this->generateUrl($route);
$html_return = '<ul class="pagination pagination-sm">';
$html_return .= '<li ' . $active . '><a href="' . $url . '?page=1' . $args . '">1</a></li>';
if ($nbpages > 7) {
if ($page >= 5) {
echo '<li>. . .</li>';
}
$min = ($page >= 4) ? $page - 2 : 2;
$max = ($page <= $nbpages - 3) ? $page + 2 : $nbpages - 1;
for ($i = $min; $i <= $max; $i++) {
$active = ($i == $page) ? 'class="active"' : '';
$html_return .= '<li ' . $active . '><a href="' . $url . '?page=' . $i . $args . '">' . $i . '</a></li>';
}
if ($page <= $nbpages - 4) {
echo '<li>. . .</li>';
}
} else {
for ($i = 2; $i <= $nbpages - 1; $i++) {
$active = ($i == $page) ? 'class="active"' : '';
$html_return .= '<li ' . $active . '><a href="' . $url . '?page=' . $i . $args . '">' . $i . '</a></li>';
}
}
$active = ($page == $nbpages) ? 'class="active"' : '';
$html_return .= '<li ' . $active . '><a href="' . $url . '?page=' . $nbpages . $args . '">' . $nbpages . '</a></li>';
$html_return .= '</ul>';
return $html_return;
}
public function listUsersStructureAction(Request $request, $structure)
{
$activeStructure = $request->getSession()->get('activeStructure');
$query = 'SELECT * FROM utilisateur_structure us, utilisateur u WHERE us.idutilisateur = u.id AND us.idstructure = ' . $structure . ' AND us.idutilisateur NOT IN ( SELECT us.idutilisateur FROM utilisateur_structure us, structure s WHERE us.idstructure = s.idstructure AND s.niveau = 10 )';
if ($activeStructure->getNiveau() > 10 && $activeStructure->getNiveau() < 100) {
$query .= ' AND u.id NOT IN (SELECT idutilisateur FROM utilisateur_structure WHERE idstructure = 1)';
}
$users = $this->db->fetchAllAssociative($query);
return $this->render('Admin/Security/listUsersStructure.html.twig', ['users' => $users]);
}
}