mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-06-12 13:24:25 +00:00
Feature: use JWT access token for securing wopi endpoints
This commit is contained in:
parent
e542ebe531
commit
c1c92dc296
@ -15,11 +15,13 @@ use ChampsLibres\WopiLib\Contract\Service\Configuration\ConfigurationInterface;
|
|||||||
use ChampsLibres\WopiLib\Contract\Service\Discovery\DiscoveryInterface;
|
use ChampsLibres\WopiLib\Contract\Service\Discovery\DiscoveryInterface;
|
||||||
use ChampsLibres\WopiLib\Contract\Service\DocumentManagerInterface;
|
use ChampsLibres\WopiLib\Contract\Service\DocumentManagerInterface;
|
||||||
use Chill\DocStoreBundle\Entity\StoredObject;
|
use Chill\DocStoreBundle\Entity\StoredObject;
|
||||||
|
use Chill\MainBundle\Entity\User;
|
||||||
use Chill\WopiBundle\Service\Controller\ResponderInterface;
|
use Chill\WopiBundle\Service\Controller\ResponderInterface;
|
||||||
use Exception;
|
use Exception;
|
||||||
|
use Lexik\Bundle\JWTAuthenticationBundle\Services\JWTTokenManagerInterface;
|
||||||
use loophp\psr17\Psr17Interface;
|
use loophp\psr17\Psr17Interface;
|
||||||
use Symfony\Component\Finder\Exception\AccessDeniedException;
|
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
|
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
|
||||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||||
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
|
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
|
||||||
use Symfony\Component\Routing\RouterInterface;
|
use Symfony\Component\Routing\RouterInterface;
|
||||||
@ -33,6 +35,8 @@ final class Editor
|
|||||||
{
|
{
|
||||||
private DocumentManagerInterface $documentManager;
|
private DocumentManagerInterface $documentManager;
|
||||||
|
|
||||||
|
private JWTTokenManagerInterface $JWTTokenManager;
|
||||||
|
|
||||||
private Psr17Interface $psr17;
|
private Psr17Interface $psr17;
|
||||||
|
|
||||||
private ResponderInterface $responder;
|
private ResponderInterface $responder;
|
||||||
@ -49,12 +53,14 @@ final class Editor
|
|||||||
ConfigurationInterface $wopiConfiguration,
|
ConfigurationInterface $wopiConfiguration,
|
||||||
DiscoveryInterface $wopiDiscovery,
|
DiscoveryInterface $wopiDiscovery,
|
||||||
DocumentManagerInterface $documentManager,
|
DocumentManagerInterface $documentManager,
|
||||||
|
JWTTokenManagerInterface $JWTTokenManager,
|
||||||
ResponderInterface $responder,
|
ResponderInterface $responder,
|
||||||
Security $security,
|
Security $security,
|
||||||
Psr17Interface $psr17,
|
Psr17Interface $psr17,
|
||||||
RouterInterface $router
|
RouterInterface $router
|
||||||
) {
|
) {
|
||||||
$this->documentManager = $documentManager;
|
$this->documentManager = $documentManager;
|
||||||
|
$this->JWTTokenManager = $JWTTokenManager;
|
||||||
$this->wopiConfiguration = $wopiConfiguration;
|
$this->wopiConfiguration = $wopiConfiguration;
|
||||||
$this->wopiDiscovery = $wopiDiscovery;
|
$this->wopiDiscovery = $wopiDiscovery;
|
||||||
$this->responder = $responder;
|
$this->responder = $responder;
|
||||||
@ -65,8 +71,12 @@ final class Editor
|
|||||||
|
|
||||||
public function __invoke(string $fileId): Response
|
public function __invoke(string $fileId): Response
|
||||||
{
|
{
|
||||||
if (null === $user = $this->security->getUser()->getUsername()) {
|
if (null === $user = $this->security->getUser()) {
|
||||||
throw new AccessDeniedException('You must be logged in to access to this resource.');
|
throw new AccessDeniedHttpException('Please authenticate to access this feature');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$user instanceof User) {
|
||||||
|
throw new AccessDeniedHttpException('Please authenticate as a user to access this feature');
|
||||||
}
|
}
|
||||||
|
|
||||||
$configuration = $this->wopiConfiguration->jsonSerialize();
|
$configuration = $this->wopiConfiguration->jsonSerialize();
|
||||||
@ -82,7 +92,19 @@ final class Editor
|
|||||||
}
|
}
|
||||||
|
|
||||||
$configuration['favIconUrl'] = '';
|
$configuration['favIconUrl'] = '';
|
||||||
$configuration['access_token'] = $user;
|
$configuration['access_token'] = $this->JWTTokenManager->createFromPayload($user, [
|
||||||
|
'UserCanWrite' => true,
|
||||||
|
'UserCanAttend' => true,
|
||||||
|
'UserCanPresent' => true,
|
||||||
|
'fileId' => $fileId,
|
||||||
|
]);
|
||||||
|
|
||||||
|
// we parse the token back to get the access_token_ttl
|
||||||
|
// reminder: access_token_ttl is a javascript epoch, not a number of seconds; it is the
|
||||||
|
// time when the token will expire, not the time to live:
|
||||||
|
// https://learn.microsoft.com/en-us/microsoft-365/cloud-storage-partner-program/rest/concepts#the-access_token_ttl-property
|
||||||
|
$jwt = $this->JWTTokenManager->parse($configuration['access_token']);
|
||||||
|
$configuration['access_token_ttl'] = $jwt['exp'] * 1000;
|
||||||
|
|
||||||
$configuration['server'] = $this
|
$configuration['server'] = $this
|
||||||
->psr17
|
->psr17
|
||||||
|
@ -13,16 +13,15 @@ namespace Chill\WopiBundle\Service\Wopi;
|
|||||||
|
|
||||||
use ChampsLibres\WopiLib\Contract\Service\DocumentManagerInterface;
|
use ChampsLibres\WopiLib\Contract\Service\DocumentManagerInterface;
|
||||||
use ChampsLibres\WopiLib\Contract\Service\WopiInterface;
|
use ChampsLibres\WopiLib\Contract\Service\WopiInterface;
|
||||||
use DateTimeImmutable;
|
use Chill\MainBundle\Entity\User;
|
||||||
use DateTimeInterface;
|
use DateTimeInterface;
|
||||||
use loophp\psr17\Psr17Interface;
|
use loophp\psr17\Psr17Interface;
|
||||||
use Psr\Cache\CacheItemPoolInterface;
|
use Psr\Cache\CacheItemPoolInterface;
|
||||||
use Psr\Http\Message\RequestInterface;
|
use Psr\Http\Message\RequestInterface;
|
||||||
use Psr\Http\Message\ResponseInterface;
|
use Psr\Http\Message\ResponseInterface;
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
|
||||||
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
|
use Symfony\Component\Security\Core\Security;
|
||||||
use Symfony\Component\Security\Core\User\UserProviderInterface;
|
use Symfony\Component\Security\Core\User\UserProviderInterface;
|
||||||
use function strlen;
|
|
||||||
|
|
||||||
final class ChillWopi implements WopiInterface
|
final class ChillWopi implements WopiInterface
|
||||||
{
|
{
|
||||||
@ -32,6 +31,8 @@ final class ChillWopi implements WopiInterface
|
|||||||
|
|
||||||
private Psr17Interface $psr17;
|
private Psr17Interface $psr17;
|
||||||
|
|
||||||
|
private Security $security;
|
||||||
|
|
||||||
private UserProviderInterface $userProvider;
|
private UserProviderInterface $userProvider;
|
||||||
|
|
||||||
private WopiInterface $wopi;
|
private WopiInterface $wopi;
|
||||||
@ -40,12 +41,14 @@ final class ChillWopi implements WopiInterface
|
|||||||
CacheItemPoolInterface $cache,
|
CacheItemPoolInterface $cache,
|
||||||
DocumentManagerInterface $documentManager,
|
DocumentManagerInterface $documentManager,
|
||||||
Psr17Interface $psr17,
|
Psr17Interface $psr17,
|
||||||
|
Security $security,
|
||||||
UserProviderInterface $userProvider,
|
UserProviderInterface $userProvider,
|
||||||
WopiInterface $wopi
|
WopiInterface $wopi
|
||||||
) {
|
) {
|
||||||
$this->cache = $cache;
|
$this->cache = $cache;
|
||||||
$this->documentManager = $documentManager;
|
$this->documentManager = $documentManager;
|
||||||
$this->psr17 = $psr17;
|
$this->psr17 = $psr17;
|
||||||
|
$this->security = $security;
|
||||||
$this->userProvider = $userProvider;
|
$this->userProvider = $userProvider;
|
||||||
$this->wopi = $wopi;
|
$this->wopi = $wopi;
|
||||||
}
|
}
|
||||||
@ -55,18 +58,13 @@ final class ChillWopi implements WopiInterface
|
|||||||
?string $accessToken,
|
?string $accessToken,
|
||||||
RequestInterface $request
|
RequestInterface $request
|
||||||
): ResponseInterface {
|
): ResponseInterface {
|
||||||
try {
|
$user = $this->security->getUser();
|
||||||
$user = $this->userProvider->loadUserByUsername($accessToken);
|
|
||||||
} catch (UsernameNotFoundException $e) {
|
|
||||||
return $this
|
|
||||||
->psr17
|
|
||||||
->createResponse(401);
|
|
||||||
}
|
|
||||||
|
|
||||||
// @ TODO : Replace this with a call to decorated object once authentication is done.
|
if (!$user instanceof User) {
|
||||||
|
throw new AccessDeniedHttpException('User must be authenticated');
|
||||||
|
}
|
||||||
$document = $this->documentManager->findByDocumentId($fileId);
|
$document = $this->documentManager->findByDocumentId($fileId);
|
||||||
$userIdentifier = $user->getUsername();
|
$userCacheKey = sprintf('wopi_putUserInfo_%s', $user->getId());
|
||||||
$userCacheKey = sprintf('wopi_putUserInfo_%s', $userIdentifier);
|
|
||||||
|
|
||||||
return $this
|
return $this
|
||||||
->psr17
|
->psr17
|
||||||
@ -77,7 +75,7 @@ final class ChillWopi implements WopiInterface
|
|||||||
'BaseFileName' => $this->documentManager->getBasename($document),
|
'BaseFileName' => $this->documentManager->getBasename($document),
|
||||||
'OwnerId' => 'Symfony',
|
'OwnerId' => 'Symfony',
|
||||||
'Size' => $this->documentManager->getSize($document),
|
'Size' => $this->documentManager->getSize($document),
|
||||||
'UserId' => $userIdentifier,
|
'UserId' => $user->getId(),
|
||||||
'ReadOnly' => false,
|
'ReadOnly' => false,
|
||||||
'UserCanAttend' => true,
|
'UserCanAttend' => true,
|
||||||
'UserCanPresent' => true,
|
'UserCanPresent' => true,
|
||||||
@ -89,7 +87,7 @@ final class ChillWopi implements WopiInterface
|
|||||||
'SupportsLocks' => true,
|
'SupportsLocks' => true,
|
||||||
'SupportsGetLock' => true,
|
'SupportsGetLock' => true,
|
||||||
'SupportsExtendedLockLength' => true,
|
'SupportsExtendedLockLength' => true,
|
||||||
'UserFriendlyName' => $userIdentifier,
|
'UserFriendlyName' => $user->getLabel(),
|
||||||
'SupportsUpdate' => true,
|
'SupportsUpdate' => true,
|
||||||
'SupportsRename' => true,
|
'SupportsRename' => true,
|
||||||
'SupportsFolder' => false,
|
'SupportsFolder' => false,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user