<?php
declare(strict_types=1);
namespace App\Twig\Functions;
use App\Subscriber\DownloadTokenSubscriber;
use Lcobucci\Clock\SystemClock;
use Lcobucci\JWT\Encoding\CannotDecodeContent;
use Lcobucci\JWT\Encoding\JoseEncoder;
use Lcobucci\JWT\Signer\Hmac\Sha256;
use Lcobucci\JWT\Signer\Key\InMemory;
use Lcobucci\JWT\Token\InvalidTokenStructure;
use Lcobucci\JWT\Token\Parser;
use Lcobucci\JWT\Validation\Constraint\LooseValidAt;
use Lcobucci\JWT\Validation\Constraint\SignedWith;
use Lcobucci\JWT\Validation\Validator;
use Psr\Log\LoggerInterface;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Twig\Extension\AbstractExtension;
use Twig\TwigFunction;
use TypeError;
/**
* Class DownloadExtension
*
* @package App\Twig\Functions
*/
class DownloadExtension extends AbstractExtension
{
private string $downloadBaseUrl;
private string $jwtPrivateKey;
private LoggerInterface $logger;
private SessionInterface $session;
/**
* DownloadExtension constructor.
*
* @param SessionInterface $session
* @param string $downloadBaseUrl
* @param string $jwtPrivateKey
*/
public function __construct(SessionInterface $session, string $downloadBaseUrl, string $jwtPrivateKey, LoggerInterface $logger)
{
$this->session = $session;
$this->downloadBaseUrl = $downloadBaseUrl;
$this->jwtPrivateKey = $jwtPrivateKey;
$this->logger = $logger;
}
/**
* @return TwigFunction[]
*/
public function getFunctions(): array
{
return [
new TwigFunction('isAssetDownloadAllowed', [$this, 'isAssetDownloadAllowed']),
new TwigFunction('getAssetDownloadUrl', [$this, 'getAssetDownloadUrl']),
];
}
/**
* @return bool
*/
public function isAssetDownloadAllowed(): bool
{
if (!$this->session->has(DownloadTokenSubscriber::SESSION_KEY_DOWNLOAD_TOKEN)) {
return false;
}
$downloadToken = $this->session->get(DownloadTokenSubscriber::SESSION_KEY_DOWNLOAD_TOKEN);
try {
$parsedToken = (new Parser(new JoseEncoder()))->parse($downloadToken);
} catch (TypeError $e) {
$this->logger->warning('The token is invalid. Error: TypeError', ['downloadToken' => $downloadToken]);
return false;
} catch (CannotDecodeContent $e) {
$this->logger->warning('The token is invalid. Error: CannotDecodeContent', ['downloadToken' => $downloadToken]);
return false;
} catch (InvalidTokenStructure $e) {
$this->logger->warning('The token is invalid. Error: InvalidTokenStructure', ['downloadToken' => $downloadToken]);
return false;
}
$constraints = [
new SignedWith(new Sha256(), InMemory::plainText($this->jwtPrivateKey)),
new LooseValidAt(SystemClock::fromSystemTimezone()),
];
$valid = (new Validator())->validate($parsedToken, ...$constraints);
if (!$valid) {
$this->logger->warning('The token is invalid. Maybe it\'s expired or the jwtPrivateKey ist wrong configured.');
}
return $valid;
}
/**
* @param array $asset
* @return string
*/
public function getAssetDownloadUrl(array $asset): string
{
$downloadToken = $this->session->get(DownloadTokenSubscriber::SESSION_KEY_DOWNLOAD_TOKEN);
return $this->downloadBaseUrl . $asset['id'] . '?downloadToken=' . $downloadToken;
}
}