src/Controller/SecurityController.php line 85

Open in your IDE?
  1. <?php
  2. namespace App\Controller;
  3. use App\Constants\Setting;
  4. use App\Constants\Setting as SettingConst;
  5. use App\Constants\Sso;
  6. use App\Entity\User;
  7. use App\Exception\EncryptionException;
  8. use App\Factory\Security\SecurityFormFactory;
  9. use App\Form\Type\LoginType;
  10. use App\Services\Common\ModuleSettingService;
  11. use App\Services\Common\SettingService;
  12. use App\Services\Common\User\WorkflowUser;
  13. use App\Services\Common\UserService;
  14. use App\Services\DTV\YamlConfig\YamlReader;
  15. use App\Services\Security\EncryptionManager;
  16. use App\Services\Security\RegisterService;
  17. use DateTime;
  18. use Doctrine\ORM\EntityManagerInterface;
  19. use Exception;
  20. use LogicException;
  21. use Psr\Container\ContainerExceptionInterface;
  22. use Psr\Container\NotFoundExceptionInterface;
  23. use Psr\Log\LoggerInterface;
  24. use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
  25. use Symfony\Component\HttpFoundation\RedirectResponse;
  26. use Symfony\Component\HttpFoundation\Request;
  27. use Symfony\Component\HttpFoundation\Response;
  28. use Symfony\Component\Routing\Annotation\Route;
  29. use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
  30. use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
  31. use Symfony\Component\Translation\TranslatableMessage;
  32. use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
  33. use SymfonyCasts\Bundle\ResetPassword\Exception\ResetPasswordExceptionInterface;
  34. /**
  35. * Controller qui gère la sécurité
  36. */
  37. class SecurityController extends AbstractController
  38. {
  39. private YamlReader $yamlReader;
  40. private SecurityFormFactory $formFactory;
  41. private SettingService $settingService;
  42. private EntityManagerInterface $em;
  43. private RegisterService $registerService;
  44. private EncryptionManager $encryptionManager;
  45. private WorkflowUser $workflowUser;
  46. private LoggerInterface $logger;
  47. private string $env;
  48. private UserService $userService;
  49. private ModuleSettingService $moduleSettingService;
  50. public function __construct(YamlReader $yamlReader, SecurityFormFactory $formFactory, SettingService $settingService, EntityManagerInterface $em, RegisterService $registerService, EncryptionManager $encryptionManager, WorkflowUser $workflowUser, LoggerInterface $logger, string $env, UserService $userService, ModuleSettingService $moduleSettingService)
  51. {
  52. $this->yamlReader = $yamlReader;
  53. $this->formFactory = $formFactory;
  54. $this->settingService = $settingService;
  55. $this->em = $em;
  56. $this->registerService = $registerService;
  57. $this->encryptionManager = $encryptionManager;
  58. $this->workflowUser = $workflowUser;
  59. $this->logger = $logger;
  60. $this->env = $env;
  61. $this->userService = $userService;
  62. $this->moduleSettingService = $moduleSettingService;
  63. }
  64. /**
  65. * Formulaire de connexion
  66. *
  67. * @Route("/login", name="app_login")
  68. *
  69. * @param AuthenticationUtils $authenticationUtils
  70. * @param Request $request
  71. *
  72. * @return Response
  73. *
  74. * @throws EncryptionException
  75. * @throws ResetPasswordExceptionInterface
  76. * @throws TransportExceptionInterface
  77. * @throws \Symfony\Component\Mailer\Exception\TransportExceptionInterface
  78. */
  79. public function login(AuthenticationUtils $authenticationUtils, Request $request): Response
  80. {
  81. return $this->processLogin($authenticationUtils, $request);
  82. }
  83. /**
  84. * Formulaire de connexion secondaire
  85. *
  86. * @Route("/login-admin", name="app_login_admin")
  87. *
  88. * @param AuthenticationUtils $authenticationUtils
  89. * @param Request $request
  90. *
  91. * @return Response
  92. * @throws EncryptionException
  93. * @throws ResetPasswordExceptionInterface
  94. * @throws TransportExceptionInterface
  95. * @throws \Symfony\Component\Mailer\Exception\TransportExceptionInterface
  96. */
  97. public function loginAdmin(AuthenticationUtils $authenticationUtils, Request $request): Response
  98. {
  99. if($this->settingService->isExist(Setting::SSO_SETTINGS) && $this->moduleSettingService->isModuleActive(Sso::MODULE_NAME))
  100. {
  101. return $this->processLogin($authenticationUtils, $request, 'security/login_admin.html.twig');
  102. }
  103. throw $this->createNotFoundException();
  104. }
  105. /**
  106. * @throws TransportExceptionInterface
  107. * @throws ResetPasswordExceptionInterface
  108. * @throws \Symfony\Component\Mailer\Exception\TransportExceptionInterface
  109. * @throws EncryptionException
  110. * @throws Exception
  111. */
  112. protected function processLogin(AuthenticationUtils $authenticationUtils, Request $request, ?string $twigPath = null): Response
  113. {
  114. // On gère ici si on est sur un Portail/enfant
  115. $setting = $this->settingService->getSettingFromName(SettingConst::PORTAL_CHILDREN);
  116. $values = $setting !== NULL ? json_decode($setting->getValue(), TRUE) : [];
  117. $hasParent = !empty($values['parent_url']);
  118. if($hasParent)
  119. {
  120. $header = $request->headers;
  121. if($header->has('q') || $request->query->get('q'))
  122. {
  123. $q = base64_decode($header->get('q') ?? $request->query->get('q'));
  124. try
  125. {
  126. $q = $this->encryptionManager->decrypt($q);
  127. } catch(Exception $e)
  128. {
  129. $this->addFlash('danger', 'Un problème est survenu lors de la connexion');
  130. $this->logger->error('Erreur lors du décryptage du token de connexion', ['error' => $e->getMessage()]);
  131. return $this->redirectToRoute('app_login');
  132. }
  133. $q = json_decode($q, TRUE);
  134. $user = $this->em->getRepository(User::class)->findOneByEmailOrExtension([
  135. 'email' => $q['email'],
  136. 'extension1' => $q['extension1'],
  137. 'extension2' => $q['extension2'],
  138. ]);
  139. if($user instanceof User)
  140. {
  141. if($q['email'] !== $user->getEmail())
  142. {
  143. $user
  144. ->setEmail($q['email'])
  145. ->setFirstname($q['firstName'])
  146. ->setLastname($q['lastName'])
  147. ->setRoles($q['roles'])
  148. ;
  149. $this->registerService->registerReplaceFakeUserBySellerCode($user, $q['extension1'], TRUE);
  150. }
  151. // Gestion du cas où l'utilisateur existe en BDD (pas en fakeUser) mais ne s'est pas encore connecté à la plateforme
  152. if($q['cguAt'] !== NULL && $user->getStatus() === 'cgu_pending')
  153. {
  154. $this->workflowUser->acceptCGU($user);
  155. $user->setCguAt(new DateTime($q['cguAt']));
  156. $this->em->flush();
  157. }
  158. // Créer un token de connexion
  159. $token = new UsernamePasswordToken($user, 'app', $user->getRoles());
  160. // Stocker le token dans le token storage
  161. try
  162. {
  163. $this->container->get('security.token_storage')->setToken($token);
  164. } catch(NotFoundExceptionInterface|ContainerExceptionInterface $e)
  165. {
  166. $this->addFlash('danger', 'Un problème est survenu lors de la connexion');
  167. $this->logger->error('Erreur lors de la récupération du token storage', ['error' => $e->getMessage()]);
  168. return $this->redirectToRoute('front_homepage');
  169. }
  170. }
  171. else
  172. {
  173. // on delog l'user
  174. $this->get('security.token_storage')->setToken(NULL);
  175. $this->logger->info('L\'utilisateur n\'existe pas en BDD', ['email' => $q['email']]);
  176. $request->getSession()->invalidate();
  177. }
  178. }
  179. }
  180. // Redirige sur la home-page si l'user est connecté
  181. if($this->getUser()) return $this->redirectToRoute('front_homepage');
  182. $user = $this->userService->initUser();
  183. $config = $this->yamlReader->getFrontSecurity();
  184. $configLogin = $config['login'];
  185. $globalRegister = $this->yamlReader->getRegister();
  186. $globalRegisterEnabled = $globalRegister['enabled'];
  187. $configRegister = $configLogin['sections']['section_register'] ?? FALSE;
  188. $hasFormRegister = FALSE;
  189. $formRegister = FALSE;
  190. if($globalRegisterEnabled && is_array($configRegister) && $configRegister['enabled'])
  191. {
  192. // Création du formulaire d'inscription
  193. try
  194. {
  195. $formRegister = $this->formFactory->generateRegisterForm($user);
  196. $hasFormRegister = TRUE;
  197. } catch(Exception $e)
  198. {
  199. throw $this->createNotFoundException($e->getMessage());
  200. }
  201. $formRegister->handleRequest($request);
  202. if($formRegister->isSubmitted())
  203. {
  204. // Validation spécifique du formulaire d'inscription
  205. try
  206. {
  207. $formRegister = $this->formFactory->postValidateRegisterForm($formRegister);
  208. } catch(Exception $e)
  209. {
  210. if($this->env != 'prod') throw $e;
  211. $this->addFlash('danger', new TranslatableMessage('impossible d\'exécuter la post validation du formulaire', []));
  212. $this->logger->error('Erreur lors de la post validation du formulaire d\'inscription', ['error' => $e->getMessage()]);
  213. $referer = $request->headers->get('referer');
  214. return $this->redirect($referer);
  215. }
  216. if($formRegister->isValid())
  217. {
  218. // Post traitement du formulaire d'inscription
  219. try
  220. {
  221. $response = $this->formFactory->postProcessingRegisterForm($formRegister, $user);
  222. } catch(Exception $e)
  223. {
  224. if($this->env != 'prod') throw $e;
  225. $this->addFlash('danger', 'impossible d\'exécuter le post traitement du formulaire');
  226. $this->logger->error('Erreur lors du post traitement du formulaire d\'inscription', ['error' => $e->getMessage()]);
  227. $referer = $request->headers->get('referer');
  228. return $this->redirect($referer);
  229. } catch(TransportExceptionInterface $e)
  230. {
  231. if($this->env != 'prod') throw $e;
  232. $this->addFlash('danger', 'impossible d\'exécuter le post traitement du formulaire');
  233. $this->logger->error('Erreur lors du post traitement du formulaire d\'inscription', ['error' => $e->getMessage()]);
  234. $referer = $request->headers->get('referer');
  235. return $this->redirect($referer);
  236. }
  237. if($response['message'] !== NULL)
  238. {
  239. $this->addFlash('success', $response['message']);
  240. }
  241. return $this->redirectToRoute($response['route']);
  242. }
  243. }
  244. }
  245. $formLogin = $this->createForm(LoginType::class);
  246. // get the login error if there is one
  247. $error = $authenticationUtils->getLastAuthenticationError();
  248. // last username entered by the user
  249. $lastUsername = $authenticationUtils->getLastUsername();
  250. if(!$twigPath)
  251. {
  252. $twigPath = 'security/login.html.twig';
  253. if(isset($configLogin['folder']) && !in_array($configLogin['folder'], [FALSE, '', NULL], TRUE))
  254. {
  255. $twigPath = 'security/' . $configLogin['folder'] . '/login.html.twig';
  256. }
  257. }
  258. return $this->render($twigPath, [
  259. 'last_username' => $lastUsername,
  260. 'error' => $error,
  261. 'loginForm' => $formLogin->createView(),
  262. 'registrationForm' => $hasFormRegister ? $formRegister->createView() : FALSE,
  263. 'hasFormRegister' => $hasFormRegister
  264. ]);
  265. }
  266. /**
  267. * Déconnexion
  268. *
  269. * @Route("/universal_logout", name="universal_logout")
  270. *
  271. * @return RedirectResponse
  272. */
  273. public function universalLogout(): RedirectResponse
  274. {
  275. if($this->isSamlSsoEnabled()) return $this->redirectToRoute('saml_logout');
  276. return $this->redirectToRoute('app_logout');
  277. }
  278. /**
  279. * Déconnexion
  280. *
  281. * @Route("/logout", name="app_logout")
  282. *
  283. * @return void
  284. */
  285. public function logout(): void
  286. {
  287. throw new LogicException('This method can be blank - it will be intercepted by the logout key on your firewall.',);
  288. }
  289. private function isSamlSsoEnabled(): bool
  290. {
  291. if(!$this->moduleSettingService->isModuleActive(Sso::MODULE_NAME) || !$this->settingService->isExist(Setting::SSO_SETTINGS)) return false;
  292. $ssoSettings = $this->settingService->getSettingFromName(Setting::SSO_SETTINGS, true, true);
  293. return is_array($ssoSettings) && !empty($ssoSettings['ssoType']) && strtolower((string)$ssoSettings['ssoType']) === Sso::SSO_SAML;
  294. }
  295. }