src/Security/EmpireAuthenticator.php line 54

  1. <?php
  2. namespace App\Security;
  3. use Empire\Core\User;
  4. use Doctrine\ORM\EntityManagerInterface;
  5. use Symfony\Component\HttpFoundation\JsonResponse;
  6. use Symfony\Component\HttpFoundation\Request;
  7. use Symfony\Component\HttpFoundation\Response;
  8. use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
  9. use Symfony\Component\Security\Core\Exception\AuthenticationException;
  10. use Symfony\Component\Security\Core\Exception\UserNotFoundException;
  11. use Symfony\Component\Security\Core\User\UserInterface;
  12. use Symfony\Component\Security\Core\User\UserProviderInterface;
  13. use Symfony\Component\Security\Http\Authenticator\AbstractAuthenticator;
  14. use Symfony\Component\Security\Core\Exception\CustomUserMessageAuthenticationException;
  15. use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
  16. use Symfony\Component\Security\Http\Authenticator\Passport\Badge\PreAuthenticatedUserBadge;
  17. use Symfony\Component\Security\Http\Authenticator\Passport\Passport;
  18. use Symfony\Component\Security\Http\Authenticator\Passport\SelfValidatingPassport;
  19. use Symfony\Component\Security\Http\Authenticator\Passport\Credentials\CustomCredentials;
  20. use Symfony\Component\HttpFoundation\RedirectResponse;
  21. use Symfony\Component\HttpFoundation\RequestStack;
  22. use App\Security\UserProvider;
  23. use Empire\Core\Core;
  24. use Empire\Core\DatabaseHelper;
  25. class EmpireAuthenticator extends AbstractAuthenticator
  26. {
  27. private $entityManager;
  28. private $requestStack;
  29. private $userProvider;
  30. public function __construct(EntityManagerInterface $entityManager, RequestStack $requestStack, UserProvider $userProvider)
  31. {
  32. $this->entityManager = $entityManager;
  33. $this->requestStack = $requestStack;
  34. $this->userProvider = $userProvider;
  35. }
  36. /**
  37. * Called on every request to decide if this authenticator should be
  38. * used for the request. Returning `false` will cause this authenticator
  39. * to be skipped.
  40. */
  41. public function supports(Request $request): ?bool
  42. {
  43. $cook = false;
  44. if ($request->cookies->has('irms_token')){
  45. $tok = $request->cookies->get('irms_token');
  46. if ($tok != '' || !is_null($tok)) {
  47. $cook = true;
  48. }
  49. }
  50. return $cook || $this->requestStack->getSession()->has('member_id');
  51. }
  52. public function getCredentials(Request $request)
  53. {
  54. if ($request->cookies->has('irms_token')){
  55. list($token, $signature) = explode(":", $request->cookies->get('irms_token'), 2);
  56. if ($signature != hash_hmac('md5', $token, $_ENV['APP_SECRET'])) {
  57. throw new \Exception('Invalid cookie!');
  58. }
  59. return $token;
  60. }
  61. return "_no_cookie"; // special val, to show no cookie usage and to check session
  62. }
  63. public function getUser($credentials, UserProviderInterface $userProvider)
  64. {
  65. if (null === $credentials) {
  66. // The token header was empty, authentication fails with HTTP Status
  67. // Code 401 "Unauthorized"
  68. return null;
  69. }
  70. if ($credentials == "_no_cookie"){
  71. $membid = $this->requestStack->getSession()->get('member_id');
  72. return $userProvider->loadUserByUsername($membid);
  73. }
  74. // no session, so a remember_me
  75. return $userProvider->loadUserByIrmsToken($credentials);
  76. }
  77. public function checkCredentials($credentials, UserInterface $user)
  78. {
  79. return true;
  80. }
  81. public function authenticate(Request $request): Passport
  82. {
  83. $cookie = $request->cookies->get('irms_token');
  84. if (is_null($cookie)){
  85. $membid = $this->requestStack->getSession()->get('member_id');
  86. if(is_null($membid)){
  87. throw new CustomUserMessageAuthenticationException('No token provided');
  88. }
  89. return new SelfValidatingPassport(new UserBadge($this->userProvider->loadUserByUsername($membid)), [new PreAuthenticatedUserBadge()]);
  90. } else {
  91. list($token, $signature) = explode(":", $cookie, 2);
  92. if ($signature != hash_hmac('md5', $token, $_ENV['APP_SECRET'])) {
  93. throw new \Exception('Invalid cookie!');
  94. }
  95. Core::setDB($this->entityManager->getConnection());
  96. $sql = "SELECT MEMB_ID FROM COOKIE WHERE COOK_TOKEN = '$token';";
  97. $stmt = DatabaseHelper::prepare($sql);
  98. $id = DatabaseHelper::fetchObjectFlexible($stmt);
  99. if(is_null($id)){
  100. throw new UserNotFoundException();
  101. } else {
  102. $id = $id->MEMB_ID;
  103. }
  104. $usql = "SELECT * FROM MEMBER WHERE MEMB_ID = '$id';";
  105. $stmt = DatabaseHelper::prepare($usql);
  106. $user = DatabaseHelper::fetchObject($stmt, User::class);
  107. if (is_null($user)) {
  108. throw new UserNotFoundException();
  109. }
  110. return new SelfValidatingPassport(new UserBadge($user));
  111. }
  112. }
  113. public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $firewallName): ?Response
  114. {
  115. // on success, let the request continue
  116. return null;
  117. }
  118. public function onAuthenticationFailure(Request $request, AuthenticationException $exception): ?Response
  119. {
  120. $data = [
  121. // you may want to customize or obfuscate the message first
  122. 'message' => strtr($exception->getMessageKey(), $exception->getMessageData())
  123. // or to translate this message
  124. // $this->translator->trans($exception->getMessageKey(), $exception->getMessageData())
  125. ];
  126. return new JsonResponse($data, Response::HTTP_UNAUTHORIZED);
  127. }
  128. /**
  129. * Called when authentication is needed, but it's not sent
  130. */
  131. public function start(Request $request, AuthenticationException $authException = null)
  132. {
  133. // send to login
  134. //var_dump($request->server->all());
  135. $reqUri = urlencode($request->server->get('REQUEST_URI'));
  136. return new RedirectResponse("/login?returnUrl={$reqUri}&client=Forum");
  137. }
  138. public function supportsRememberMe()
  139. {
  140. return true;
  141. }
  142. }