src/AdminBundle/Security/ApiKeyGuard.php line 34

Open in your IDE?
  1. <?php
  2. namespace AdminBundle\Security;
  3. use AdminBundle\Entity\User;
  4. use AdminBundle\Entity\Driver;
  5. use AdminBundle\Entity\Settings;
  6. use AdminBundle\Security\ApiKeyUserProvider;
  7. use Doctrine\ORM\EntityManagerInterface;
  8. use Symfony\Component\DependencyInjection\ContainerInterface;
  9. use Symfony\Component\HttpFoundation\JsonResponse;
  10. use Symfony\Component\HttpFoundation\Request;
  11. use Symfony\Component\HttpFoundation\Response;
  12. use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
  13. use Symfony\Component\Security\Core\Exception\AuthenticationException;
  14. use Symfony\Component\Security\Core\Exception\BadCredentialsException;
  15. use Symfony\Component\Security\Core\Exception\CustomUserMessageAuthenticationException;
  16. use Symfony\Component\Security\Core\User\UserInterface;
  17. use Symfony\Component\Security\Core\User\UserProviderInterface;
  18. use Symfony\Component\Security\Guard\AbstractGuardAuthenticator;
  19. class ApiKeyGuard extends AbstractGuardAuthenticator
  20. {
  21. private $em;
  22. private $keyParam = 'apikey';
  23. public function __construct(
  24. ContainerInterface $container,
  25. EntityManagerInterface $em
  26. ) {
  27. $this->em = $em;
  28. $this->container = $container;
  29. }
  30. private function recursiveVerifyDriver(
  31. array $verify,
  32. $driver,
  33. $limit
  34. ) {
  35. $response = null;
  36. foreach ($verify as $function => $object) {
  37. if (is_array($object)) {
  38. $response = $this->recursiveVerifyDriver(
  39. $object,
  40. $driver->$function(),
  41. $limit
  42. );
  43. }
  44. if ($object == 'Insurance') {
  45. $insurance_time_lock = $this->em->getRepository(
  46. Settings::class
  47. )->findOneBy([
  48. 'key' => 'vehicle_insurance_time_lock'
  49. ]);
  50. if (!empty($insurance_time_lock)) {
  51. $db_insurance = $driver->$function();
  52. $diff = $db_insurance->diff($limit);
  53. $hours = $diff->h + $diff->days * 24;
  54. $expiresIn = $limit > $db_insurance ? -$hours : $hours;
  55. if ($expiresIn <= $insurance_time_lock->getValue()) {
  56. return $object;
  57. }
  58. } else {
  59. if ($driver->$function() <= $limit) {
  60. return $object;
  61. }
  62. }
  63. } else {
  64. if ($driver->$function() <= $limit) {
  65. return $object;
  66. }
  67. }
  68. }
  69. return $response;
  70. }
  71. /**
  72. * Called on every request to decide if this authenticator should be
  73. * used for the request. Returning `false` will cause this authenticator
  74. * to be skipped.
  75. */
  76. public function supports(Request $request): bool {
  77. $targetUrl = '/api';
  78. if (
  79. strpos($targetUrl, $request->getPathInfo()) == 0 &&
  80. strpos($targetUrl, $request->getPathInfo()) !== false
  81. ) {
  82. return false;
  83. }
  84. if ($request->get('_route') === 'api_reset_password') {
  85. return false;
  86. };
  87. // 1. check if exist header apikey
  88. // 2. if not exist header check if exist _GET parameter apiKey
  89. if ($request->headers->has($this->keyParam)) {
  90. return true;
  91. } else {
  92. return $request->query->has($this->keyParam);
  93. }
  94. }
  95. /**
  96. * Called on every request. Return whatever credentials you want to
  97. * be passed to getUser() as $credentials.
  98. */
  99. public function getCredentials(Request $request) {
  100. // 1. check if exist header apikey
  101. // 2. if not exist header check if exist _GET parameter apiKey
  102. return $request->headers->get(
  103. $this->keyParam,
  104. $request->query->get(
  105. $this->keyParam
  106. )
  107. );
  108. }
  109. public function getUser(
  110. $credentials,
  111. UserProviderInterface $userProvider
  112. ): ?UserInterface {
  113. if (!$userProvider instanceof ApiKeyUserProvider) {
  114. throw new \InvalidArgumentException(
  115. sprintf(
  116. 'The user provider must be an instance of ApiKeyUserProvider (%s was given).',
  117. get_class($userProvider)
  118. )
  119. );
  120. }
  121. if (null === $credentials) {
  122. throw new BadCredentialsException();
  123. // Authentication fails with HTTP Status
  124. // Code 401 "Unauthorized"
  125. // return null;
  126. }
  127. $user = $this->em->getRepository(User::class)->findOneBy([
  128. 'apiKey' => $credentials
  129. ]);
  130. if (!$user) {
  131. throw new CustomUserMessageAuthenticationException(
  132. sprintf(
  133. 'Invalid api key. API Key "%s" does not exist.',
  134. $credentials
  135. )
  136. );
  137. }
  138. $user->addRole('ROLE_API');
  139. // The "username" in this case is the apiToken, see the key `property`
  140. // of `your_db_provider` in `security.yaml`.
  141. // If this returns a user, checkCredentials() is called next:
  142. // return $userProvider->loadUserByUsername($credentials);
  143. return $user;
  144. }
  145. public function checkCredentials(
  146. $credentials,
  147. UserInterface $user
  148. ): bool {
  149. if (!$user->isEnabled()) {
  150. throw new CustomUserMessageAuthenticationException(
  151. 'Your account is disabled. '.
  152. 'Please contact office to restore your access.Thank you'
  153. );
  154. }
  155. if (in_array(User::ROLE_DRIVER, $user->getRoles())) {
  156. $driver = $user->getDriver();
  157. if ($driver->getStatus() == Driver::STATUS_SUSPENDED) {
  158. throw new CustomUserMessageAuthenticationException(
  159. 'Invalid driver status.'
  160. );
  161. }
  162. if ($driver->isDeactivated()) {
  163. $deactivatedDate = $driver->getDeactivatedUntil()->format('Y-m-d H:i:s');
  164. throw new CustomUserMessageAuthenticationException(
  165. 'Your account was deactiaved until '. $deactivatedDate. '. '.
  166. 'Please contact office to restore your access.Thank you'
  167. );
  168. }
  169. $main_location = $this->container->getParameter('main_location');
  170. $verifyExpiring = [];
  171. if ($main_location == 'UK') {
  172. $verifyExpiring = [
  173. 'getCar' => [
  174. 'getInsuranceExpirationDate' => 'Insurance',
  175. 'getMotExpiryDate' => 'MOT',
  176. 'getPhvExpireDate' => 'PHV',
  177. ],
  178. 'getLicenseExpireDate' => 'Driving License',
  179. 'getPublicCarriageOfficeExpiryDate' => 'PCO Licence',
  180. ];
  181. }
  182. if ($main_location == 'US') {
  183. $verifyExpiring = [
  184. 'getCar' => [
  185. 'getInsuranceExpirationDate' => 'Insurance',
  186. 'getVehicleInspectionExpiryDate' => 'Vehicle Inspection',
  187. 'getLimoLicenseExpiryDate' => 'Limo License',
  188. ],
  189. 'getLicenseExpireDate' => 'Driving License',
  190. 'getHackLicenseExpirationDate' => 'Hack Licence',
  191. ];
  192. }
  193. /** @var Driver $driver */
  194. if ($driver->getCar()) {
  195. $expired = $this->recursiveVerifyDriver(
  196. $verifyExpiring, $driver, new \DateTime()
  197. );
  198. if (!empty($expired)) {
  199. throw new CustomUserMessageAuthenticationException(
  200. 'Expired '. $expired. '. '.
  201. 'Please contact Office and upload documents in your account.'
  202. );
  203. }
  204. }
  205. }
  206. // Return `true` to cause authentication success
  207. return true;
  208. }
  209. public function onAuthenticationSuccess(
  210. Request $request,
  211. TokenInterface $token,
  212. $providerKey
  213. ): ?Response {
  214. // on success, let the request continue
  215. return null;
  216. }
  217. public function onAuthenticationFailure(
  218. Request $request,
  219. AuthenticationException $exception
  220. ): ?Response {
  221. $data = [
  222. // you may want to customize or obfuscate the message first
  223. 'message' => strtr(
  224. $exception->getMessageKey(),
  225. $exception->getMessageData()
  226. )
  227. // or to translate this message
  228. // $this->translator->trans(
  229. // $exception->getMessageKey(),
  230. // $exception->getMessageData()
  231. // )
  232. ];
  233. return new JsonResponse($data, Response::HTTP_UNAUTHORIZED);
  234. }
  235. /**
  236. * Called when authentication is needed, but it's not sent
  237. */
  238. public function start(
  239. Request $request,
  240. ?AuthenticationException $authException = null
  241. ): Response {
  242. $data = [
  243. // you might translate this message
  244. 'message' => 'Authentication Required'
  245. ];
  246. return new JsonResponse($data, Response::HTTP_UNAUTHORIZED);
  247. }
  248. public function supportsRememberMe(): bool {
  249. return false;
  250. }
  251. }