<?php
namespace AdminBundle\Security;
use AdminBundle\Entity\User;
use AdminBundle\Entity\Driver;
use AdminBundle\Entity\Settings;
use AdminBundle\Security\ApiKeyUserProvider;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\Exception\BadCredentialsException;
use Symfony\Component\Security\Core\Exception\CustomUserMessageAuthenticationException;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Guard\AbstractGuardAuthenticator;
class ApiKeyGuard extends AbstractGuardAuthenticator
{
private $em;
private $keyParam = 'apikey';
public function __construct(
ContainerInterface $container,
EntityManagerInterface $em
) {
$this->em = $em;
$this->container = $container;
}
private function recursiveVerifyDriver(
array $verify,
$driver,
$limit
) {
$response = null;
foreach ($verify as $function => $object) {
if (is_array($object)) {
$response = $this->recursiveVerifyDriver(
$object,
$driver->$function(),
$limit
);
}
if ($object == 'Insurance') {
$insurance_time_lock = $this->em->getRepository(
Settings::class
)->findOneBy([
'key' => 'vehicle_insurance_time_lock'
]);
if (!empty($insurance_time_lock)) {
$db_insurance = $driver->$function();
$diff = $db_insurance->diff($limit);
$hours = $diff->h + $diff->days * 24;
$expiresIn = $limit > $db_insurance ? -$hours : $hours;
if ($expiresIn <= $insurance_time_lock->getValue()) {
return $object;
}
} else {
if ($driver->$function() <= $limit) {
return $object;
}
}
} else {
if ($driver->$function() <= $limit) {
return $object;
}
}
}
return $response;
}
/**
* Called on every request to decide if this authenticator should be
* used for the request. Returning `false` will cause this authenticator
* to be skipped.
*/
public function supports(Request $request): bool {
$targetUrl = '/api';
if (
strpos($targetUrl, $request->getPathInfo()) == 0 &&
strpos($targetUrl, $request->getPathInfo()) !== false
) {
return false;
}
if ($request->get('_route') === 'api_reset_password') {
return false;
};
// 1. check if exist header apikey
// 2. if not exist header check if exist _GET parameter apiKey
if ($request->headers->has($this->keyParam)) {
return true;
} else {
return $request->query->has($this->keyParam);
}
}
/**
* Called on every request. Return whatever credentials you want to
* be passed to getUser() as $credentials.
*/
public function getCredentials(Request $request) {
// 1. check if exist header apikey
// 2. if not exist header check if exist _GET parameter apiKey
return $request->headers->get(
$this->keyParam,
$request->query->get(
$this->keyParam
)
);
}
public function getUser(
$credentials,
UserProviderInterface $userProvider
): ?UserInterface {
if (!$userProvider instanceof ApiKeyUserProvider) {
throw new \InvalidArgumentException(
sprintf(
'The user provider must be an instance of ApiKeyUserProvider (%s was given).',
get_class($userProvider)
)
);
}
if (null === $credentials) {
throw new BadCredentialsException();
// Authentication fails with HTTP Status
// Code 401 "Unauthorized"
// return null;
}
$user = $this->em->getRepository(User::class)->findOneBy([
'apiKey' => $credentials
]);
if (!$user) {
throw new CustomUserMessageAuthenticationException(
sprintf(
'Invalid api key. API Key "%s" does not exist.',
$credentials
)
);
}
$user->addRole('ROLE_API');
// The "username" in this case is the apiToken, see the key `property`
// of `your_db_provider` in `security.yaml`.
// If this returns a user, checkCredentials() is called next:
// return $userProvider->loadUserByUsername($credentials);
return $user;
}
public function checkCredentials(
$credentials,
UserInterface $user
): bool {
if (!$user->isEnabled()) {
throw new CustomUserMessageAuthenticationException(
'Your account is disabled. '.
'Please contact office to restore your access.Thank you'
);
}
if (in_array(User::ROLE_DRIVER, $user->getRoles())) {
$driver = $user->getDriver();
if ($driver->getStatus() == Driver::STATUS_SUSPENDED) {
throw new CustomUserMessageAuthenticationException(
'Invalid driver status.'
);
}
if ($driver->isDeactivated()) {
$deactivatedDate = $driver->getDeactivatedUntil()->format('Y-m-d H:i:s');
throw new CustomUserMessageAuthenticationException(
'Your account was deactiaved until '. $deactivatedDate. '. '.
'Please contact office to restore your access.Thank you'
);
}
$main_location = $this->container->getParameter('main_location');
$verifyExpiring = [];
if ($main_location == 'UK') {
$verifyExpiring = [
'getCar' => [
'getInsuranceExpirationDate' => 'Insurance',
'getMotExpiryDate' => 'MOT',
'getPhvExpireDate' => 'PHV',
],
'getLicenseExpireDate' => 'Driving License',
'getPublicCarriageOfficeExpiryDate' => 'PCO Licence',
];
}
if ($main_location == 'US') {
$verifyExpiring = [
'getCar' => [
'getInsuranceExpirationDate' => 'Insurance',
'getVehicleInspectionExpiryDate' => 'Vehicle Inspection',
'getLimoLicenseExpiryDate' => 'Limo License',
],
'getLicenseExpireDate' => 'Driving License',
'getHackLicenseExpirationDate' => 'Hack Licence',
];
}
/** @var Driver $driver */
if ($driver->getCar()) {
$expired = $this->recursiveVerifyDriver(
$verifyExpiring, $driver, new \DateTime()
);
if (!empty($expired)) {
throw new CustomUserMessageAuthenticationException(
'Expired '. $expired. '. '.
'Please contact Office and upload documents in your account.'
);
}
}
}
// Return `true` to cause authentication success
return true;
}
public function onAuthenticationSuccess(
Request $request,
TokenInterface $token,
$providerKey
): ?Response {
// on success, let the request continue
return null;
}
public function onAuthenticationFailure(
Request $request,
AuthenticationException $exception
): ?Response {
$data = [
// you may want to customize or obfuscate the message first
'message' => strtr(
$exception->getMessageKey(),
$exception->getMessageData()
)
// or to translate this message
// $this->translator->trans(
// $exception->getMessageKey(),
// $exception->getMessageData()
// )
];
return new JsonResponse($data, Response::HTTP_UNAUTHORIZED);
}
/**
* Called when authentication is needed, but it's not sent
*/
public function start(
Request $request,
?AuthenticationException $authException = null
): Response {
$data = [
// you might translate this message
'message' => 'Authentication Required'
];
return new JsonResponse($data, Response::HTTP_UNAUTHORIZED);
}
public function supportsRememberMe(): bool {
return false;
}
}