<?php
namespace App\Service;
use App\Entity\AccountOrganization;
use App\Entity\Organization;
use App\Entity\Praticien;
use App\Entity\Programme\Programme;
use App\Entity\UserInterface;
use App\Enum\PermissionEnum;
use App\Repository\LicenceRepository;
use App\Repository\OrganizationRepository;
use Doctrine\ORM\EntityManagerInterface;
use Exception;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;
use function get_class;
use function password_hash;
use function password_verify;
use const PASSWORD_DEFAULT;
class Security
{
protected EntityManagerInterface $entityManager;
private UserInterface|Organization|null $user = null;
private RequestStack $requestStack;
private ConnexionLoginService $connexionLoginService;
private OrganizationRepository $organizationRepository;
private PermissionEnum $permissionEnum;
private LicenceRepository $licenceRepository;
public function __construct(
RequestStack $requestStack,
EntityManagerInterface $entityManager,
ConnexionLoginService $connexionLoginService,
OrganizationRepository $organizationRepository,
PermissionEnum $permissionEnum,
LicenceRepository $licenceRepository
) {
$this->requestStack = $requestStack;
$this->entityManager = $entityManager;
$this->connexionLoginService = $connexionLoginService;
$this->organizationRepository = $organizationRepository;
$this->permissionEnum = $permissionEnum;
$this->licenceRepository = $licenceRepository;
}
/**
* Retourne une version hashée d'un mot de passe
*/
public static function passwordhash(string $password):string
{
return password_hash($password, PASSWORD_DEFAULT);
}
/**
* Vérifie un mot de passe par rapport à un hash
* @param $password
* @param $hash
* @return bool
*/
public static function isValidePassword($password, $hash):bool
{
return password_verify($password, $hash);
}
/**
* Permet de générer un token aléatoire
* @return string
*/
public static function generateToken():string
{
try {
return bin2hex(random_bytes(32));
} catch (Exception) {
return md5(uniqid('token_', true));
}
}
/**
* Monte un utilisateur en session
*/
public function login(
UserInterface|AccountOrganization $user,
Request $request
):null|UserInterface|Organization {
$this->connexionLoginService->log($user, $request);
if ($user instanceof AccountOrganization) {
$this->requestStack->getSession()->set('Organization', $user->getOrganization());
$this->requestStack->getSession()->remove('User');
return $this->getOrganization();
}
$this->requestStack->getSession()->remove('Organization');
$this->requestStack->getSession()->set('User', $user);
return $this->getUser();
}
/**
* Monte une session Organisation en conservant la session admin de côté
*/
public function logtoOrganization(Organization $organization): void
{
$this->requestStack->getSession()->set('Organization', $organization);
// Stocke la session admin afin de pouvoir la restaurer.
$this->requestStack->getSession()->set('previousSessionUser',
$this->requestStack->getSession()->get('User')
);
$this->requestStack->getSession()->remove('User');
}
/**
* Restaure une session admin après une prise de contrôle
*/
public function restore(): void
{
$this->requestStack->getSession()->set('User',
$this->requestStack->getSession()->get('previousSessionUser')
);
$this->requestStack->getSession()->remove('previousSessionUser');
$this->requestStack->getSession()->remove('Organization');
}
/**
* Vérifie si on est en prise de contrôle
*/
public function hasPreviousSession():bool
{
return $this->requestStack->getSession()->has('previousSessionUser');
}
/**
* @return UserInterface|null Récupère l'utilisateur identifié
*/
public function getUser():?UserInterface
{
// Si on l'a déjà synchronisé avec l'entityManager
if ($this->user instanceof UserInterface) {
return $this->user;
}
// Si on a un utilisateur, on récupère via l'entitymanager pour permettre des requêtes doctrine (impossible avec l'objet en session)
$user = $this->requestStack->getSession()->get('User');
if ($user instanceof UserInterface) {
$this->user = $this->entityManager->getRepository(get_class($user))->find($this->requestStack->getSession()
->get('User')
->getId());
$this->requestStack->getSession()->set('User', $this->user);
return $this->user;
}
return null;
}
public function getOrganization():?Organization
{
// Si on l'a déjà synchronisé avec l'entityManager
if ($this->user instanceof Organization) {
return $this->user;
}
$organization = $this->requestStack->getSession()->get('Organization');
if ($organization instanceof Organization) {
// Récupère depuis Doctrine pour que l'entité et ses dépendances soient dans l'EntityManager
$organization = $this->organizationRepository->find($organization->getId());
$this->user = $organization;
return $this->user;
}
return null;
}
/**
* @return void Déconnecte la session en cours
*/
public function logout():void
{
unset($this->user);
$this->requestStack->getSession()->clear();
}
public function isGranted(string $permission):bool
{
$permissionList = $this->requestStack->getSession()->get('Permission');
// Si la permission n'est pas dans la liste
if (!in_array($permission, $permissionList, true)) {
return false;
}
// Sinon, autorisé
return true;
}
public function getAllowedPathologieSlugs():array
{
$pathologiesPermissions = $this->permissionEnum->getPathologieSlugsPermissions();
$userPermissions = $this->requestStack->getSession()->get('Permission');
return array_flip(array_intersect($pathologiesPermissions, $userPermissions));
}
public function isAllowPathology(string $pathologySlug):bool
{
return in_array($pathologySlug, $this->getAllowedPathologieSlugs());
}
public function isAllowProgram(Programme $programme):bool
{
return $this->isAllowPathology($programme->getPathologie()->getSlug());
}
public function isAllowAccessAffiliatedPatient(Praticien $praticien):bool
{
return $this->organizationRepository->isAllowAccessAffiliatedPatient($praticien);
}
public function isOrganization():bool
{
return $this->requestStack->getSession()->has('Organization');
}
}