Skip to content

Instantly share code, notes, and snippets.

@ahmed-bhs
Created October 22, 2020 14:40
Show Gist options
  • Save ahmed-bhs/f7117b9b24ed2622c078b7e5e0d19d25 to your computer and use it in GitHub Desktop.
Save ahmed-bhs/f7117b9b24ed2622c078b7e5e0d19d25 to your computer and use it in GitHub Desktop.
<?php
/**
* @copyright Copyright (C) eZ Systems AS. All rights reserved.
* @license For full copyright and license information view LICENSE file distributed with this source code.
*/
declare(strict_types=1);
namespace EzSystems\EzPlatformPageBuilder\Security\EditorialMode;
use eZ\Publish\API\Repository\PermissionResolver;
use eZ\Publish\Core\MVC\Symfony\Security\UserInterface;
use eZ\Publish\Core\MVC\Symfony\SiteAccess;
use EzSystems\EzPlatformAdminUi\Specification\SiteAccess\IsAdmin;
use InvalidArgumentException;
use Lexik\Bundle\JWTAuthenticationBundle\Exception\ExpiredTokenException;
use Lexik\Bundle\JWTAuthenticationBundle\Exception\InvalidTokenException;
use Lexik\Bundle\JWTAuthenticationBundle\Exception\JWTDecodeFailureException;
use Lexik\Bundle\JWTAuthenticationBundle\TokenExtractor;
use Lexik\Bundle\JWTAuthenticationBundle\TokenExtractor\TokenExtractorInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Http\Authentication\SimplePreAuthenticatorInterface;
class TokenAuthenticator implements SimplePreAuthenticatorInterface
{
/** @var \EzSystems\EzPlatformPageBuilder\Security\EditorialMode\TokenManager */
private $tokenManager;
/** @var \EzSystems\EzPlatformPageBuilder\Security\EditorialMode\TokenRevokerInterface */
private $tokenRevoker;
/** @var \eZ\Publish\API\Repository\PermissionResolver */
private $permissionResolver;
/** @var bool */
private $enabled;
/** @var array */
private $siteAccessGroups;
/** @var string */
private $tokenQueryParamName;
/**
* @param \EzSystems\EzPlatformPageBuilder\Security\EditorialMode\TokenManager $tokenManager
* @param \EzSystems\EzPlatformPageBuilder\Security\EditorialMode\TokenRevokerInterface $tokenRevoker
* @param \eZ\Publish\API\Repository\PermissionResolver $permissionResolver
* @param string $tokenQueryParamName
* @param bool $enabled
* @param array $siteAccessGroups
*/
public function __construct(
TokenManager $tokenManager,
TokenRevokerInterface $tokenRevoker,
PermissionResolver $permissionResolver,
string $tokenQueryParamName,
bool $enabled,
array $siteAccessGroups
) {
$this->tokenManager = $tokenManager;
$this->tokenRevoker = $tokenRevoker;
$this->tokenQueryParamName = $tokenQueryParamName;
$this->permissionResolver = $permissionResolver;
$this->enabled = $enabled;
$this->siteAccessGroups = $siteAccessGroups;
}
/**
* {@inheritdoc}
*/
public function authenticateToken(TokenInterface $token, UserProviderInterface $userProvider, $providerKey)
{
if (!($token instanceof PreAuthenticatedToken)) {
throw new InvalidArgumentException(sprintf(
'The token must be instance of PreAuthenticatedToken (%s was given).',
get_class($token)
));
}
$payload = $token->getCredentials();
$user = $token->getUser();
if (!($user instanceof UserInterface)) {
$user = $userProvider->loadUserByUsername($token->getUsername());
}
if (!$this->tokenRevoker->isValid($token)) {
throw new InvalidTokenException('Revoked JWT Token');
}
$this->tokenRevoker->revoke($token);
$this->permissionResolver->setCurrentUserReference(
$user->getAPIUserReference()
);
return new PreAuthenticatedToken(
$user,
$payload,
$providerKey,
$user->getRoles()
);
}
/**
* {@inheritdoc}
*/
public function supportsToken(TokenInterface $token, $providerKey)
{
return $token instanceof PreAuthenticatedToken && $token->getProviderKey() === $providerKey;
}
/**
* Returns name of query parameter used to pass token.
*
* @return string
*/
public function getTokenQueryParamName(): string
{
return $this->tokenQueryParamName;
}
/**
* {@inheritdoc}
*/
public function createToken(Request $request, $providerKey)
{
if (!$this->isEnabled()) {
return null;
}
if ($this->isAdminSiteAccess($request)) {
return null;
}
$jsonWebToken = $this->getTokenExtractor()->extract($request);
if (false === $jsonWebToken) {
return null;
}
try {
if (!($payload = $this->tokenManager->decodeFromString($jsonWebToken))) {
throw new InvalidTokenException('Invalid JWT Token');
}
} catch (JWTDecodeFailureException $e) {
if (JWTDecodeFailureException::EXPIRED_TOKEN === $e->getReason()) {
throw new ExpiredTokenException();
}
throw new InvalidTokenException('Invalid JWT Token', 0, $e);
}
$username = $payload[$this->tokenManager->getUserIdentityField()];
return new PreAuthenticatedToken(
$username,
$payload,
$providerKey
);
}
/**
* Returns true if token based authentication is enabled.
*
* @return bool
*/
public function isEnabled(): bool
{
return $this->enabled;
}
/**
* Returns true if the siteaccess of given request is administrative.
*
* @param \Symfony\Component\HttpFoundation\Request $request
*
* @return bool
*/
private function isAdminSiteAccess(Request $request): bool
{
$siteaccess = $request->attributes->get('siteaccess', null);
if ($siteaccess instanceof SiteAccess) {
return (new IsAdmin($this->siteAccessGroups))->isSatisfiedBy($siteaccess);
}
return false;
}
/**
* {@inheritdoc}
*/
private function getTokenExtractor(): TokenExtractorInterface
{
return new TokenExtractor\QueryParameterTokenExtractor($this->tokenQueryParamName);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment