src/Controller/SecurityController.php line 97

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