From 34296e78411dbf38b85f435d9d2f9d6c9bc7689a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Tue, 10 Jan 2023 20:26:44 +0100 Subject: [PATCH] Feature: [wopi] Implements the new required AuthorizationManager and UserManager for wopi --- src/Bundle/ChillMainBundle/Entity/User.php | 14 ++- .../src/Resources/config/services.php | 18 +++- .../src/Service/Wopi/AuthorizationManager.php | 88 +++++++++++++++++++ .../src/Service/Wopi/ChillDocumentManager.php | 10 ++- .../src/Service/Wopi/ChillWopi.php | 70 +-------------- .../src/Service/Wopi/UserManager.php | 53 +++++++++++ 6 files changed, 172 insertions(+), 81 deletions(-) create mode 100644 src/Bundle/ChillWopiBundle/src/Service/Wopi/AuthorizationManager.php create mode 100644 src/Bundle/ChillWopiBundle/src/Service/Wopi/UserManager.php diff --git a/src/Bundle/ChillMainBundle/Entity/User.php b/src/Bundle/ChillMainBundle/Entity/User.php index 80a0b5b2a..1317edf83 100644 --- a/src/Bundle/ChillMainBundle/Entity/User.php +++ b/src/Bundle/ChillMainBundle/Entity/User.php @@ -15,7 +15,7 @@ use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\Collection; use Doctrine\ORM\Mapping as ORM; use RuntimeException; -use Symfony\Component\Security\Core\User\AdvancedUserInterface; +use Symfony\Component\Security\Core\User\UserInterface; use Symfony\Component\Serializer\Annotation as Serializer; use Symfony\Component\Validator\Context\ExecutionContextInterface; @@ -31,7 +31,7 @@ use function in_array; * "user": User::class * }) */ -class User implements AdvancedUserInterface +class User implements UserInterface { /** * @ORM\Id @@ -58,8 +58,6 @@ class User implements AdvancedUserInterface private ?Location $currentLocation = null; /** - * @var string - * * @ORM\Column(type="string", length=150, nullable=true) */ private ?string $email = null; @@ -216,7 +214,7 @@ class User implements AdvancedUserInterface } /** - * @return GroupCenter + * @return Collection */ public function getGroupCenters() { @@ -225,10 +223,8 @@ class User implements AdvancedUserInterface /** * Get id. - * - * @return int */ - public function getId() + public function getId(): int { return $this->id; } @@ -487,7 +483,7 @@ class User implements AdvancedUserInterface * * @param string $name * - * @return Agent + * @return User */ public function setUsername($name) { diff --git a/src/Bundle/ChillWopiBundle/src/Resources/config/services.php b/src/Bundle/ChillWopiBundle/src/Resources/config/services.php index ffd1a7947..e7ca90dbc 100644 --- a/src/Bundle/ChillWopiBundle/src/Resources/config/services.php +++ b/src/Bundle/ChillWopiBundle/src/Resources/config/services.php @@ -12,12 +12,15 @@ declare(strict_types=1); namespace Symfony\Component\DependencyInjection\Loader\Configurator; use ChampsLibres\AsyncUploaderBundle\TempUrl\TempUrlGeneratorInterface; +use ChampsLibres\WopiBundle\Contracts\AuthorizationManagerInterface; +use ChampsLibres\WopiBundle\Contracts\UserManagerInterface; use ChampsLibres\WopiBundle\Service\Wopi as CLWopi; -use ChampsLibres\WopiLib\Contract\Service\DocumentLockManagerInterface; use ChampsLibres\WopiLib\Contract\Service\DocumentManagerInterface; +use Chill\WopiBundle\Service\Wopi\AuthorizationManager; use Chill\WopiBundle\Service\Wopi\ChillDocumentLockManager; use Chill\WopiBundle\Service\Wopi\ChillDocumentManager; use Chill\WopiBundle\Service\Wopi\ChillWopi; +use Chill\WopiBundle\Service\Wopi\UserManager; return static function (ContainerConfigurator $container) { $services = $container @@ -44,8 +47,17 @@ return static function (ContainerConfigurator $container) { ->alias(DocumentManagerInterface::class, ChillDocumentManager::class); $services - ->set(ChillDocumentLockManager::class) - ->decorate(DocumentLockManagerInterface::class); + ->set(ChillDocumentLockManager::class); + + $services + ->set(AuthorizationManager::class); + + $services->alias(AuthorizationManagerInterface::class, AuthorizationManager::class); + + $services + ->set(UserManager::class); + + $services->alias(UserManagerInterface::class, UserManager::class); // TODO: Move this into the async bundle (low priority) $services diff --git a/src/Bundle/ChillWopiBundle/src/Service/Wopi/AuthorizationManager.php b/src/Bundle/ChillWopiBundle/src/Service/Wopi/AuthorizationManager.php new file mode 100644 index 000000000..24ea00982 --- /dev/null +++ b/src/Bundle/ChillWopiBundle/src/Service/Wopi/AuthorizationManager.php @@ -0,0 +1,88 @@ +tokenManager = $tokenManager; + $this->security = $security; + } + + public function isRestrictedWebViewOnly(string $accessToken, Document $document, RequestInterface $request): bool + { + return false; + } + + public function isTokenValid(string $accessToken, Document $document, RequestInterface $request): bool + { + $metadata = $this->tokenManager->parse($accessToken); + + if (false === $metadata) { + return false; + } + + $user = $this->security->getUser(); + + if (!$user instanceof User) { + return false; + } + + return $document->getWopiDocId() === $metadata['fileId']; + } + + public function userCanAttend(string $accessToken, Document $document, RequestInterface $request): bool + { + return $this->isTokenValid($accessToken, $document, $request); + } + + public function userCanDelete(string $accessToken, Document $document, RequestInterface $request): bool + { + return false; + } + + public function userCannotWriteRelative(string $accessToken, Document $document, RequestInterface $request): bool + { + return true; + } + + public function userCanPresent(string $accessToken, Document $document, RequestInterface $request): bool + { + return $this->isTokenValid($accessToken, $document, $request); + } + + public function userCanRead(string $accessToken, Document $document, RequestInterface $request): bool + { + return $this->isTokenValid($accessToken, $document, $request); + } + + public function userCanRename(string $accessToken, Document $document, RequestInterface $request): bool + { + return false; + } + + public function userCanWrite(string $accessToken, Document $document, RequestInterface $request): bool + { + return $this->isTokenValid($accessToken, $document, $request); + } +} diff --git a/src/Bundle/ChillWopiBundle/src/Service/Wopi/ChillDocumentManager.php b/src/Bundle/ChillWopiBundle/src/Service/Wopi/ChillDocumentManager.php index 1ae6b395a..8b6e3f846 100644 --- a/src/Bundle/ChillWopiBundle/src/Service/Wopi/ChillDocumentManager.php +++ b/src/Bundle/ChillWopiBundle/src/Service/Wopi/ChillDocumentManager.php @@ -24,11 +24,12 @@ use loophp\psr17\Psr17Interface; use Psr\Http\Message\RequestInterface; use Psr\Http\Message\StreamInterface; use Ramsey\Uuid\Uuid; +use RuntimeException; use Symfony\Bridge\PsrHttpMessage\HttpMessageFactoryInterface; use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\Mime\MimeTypes; -use Symfony\Component\Serializer\Normalizer\ObjectNormalizer; +use Symfony\Component\Serializer\Normalizer\ObjectNormalizer; use function strlen; final class ChillDocumentManager implements DocumentManagerInterface @@ -190,7 +191,12 @@ final class ChillDocumentManager implements DocumentManagerInterface public function remove(Document $document): void { - // TODO: To implement when we have a clearer view and API. + throw new RuntimeException('this is not implemented and should not happens'); + } + + public function rename(Document $document, string $requestedName): void + { + throw new RuntimeException('this is not implemented and should not happens'); } public function write(Document $document, array $properties = []): void diff --git a/src/Bundle/ChillWopiBundle/src/Service/Wopi/ChillWopi.php b/src/Bundle/ChillWopiBundle/src/Service/Wopi/ChillWopi.php index 3629c6126..e0e57e078 100644 --- a/src/Bundle/ChillWopiBundle/src/Service/Wopi/ChillWopi.php +++ b/src/Bundle/ChillWopiBundle/src/Service/Wopi/ChillWopi.php @@ -13,43 +13,20 @@ namespace Chill\WopiBundle\Service\Wopi; use ChampsLibres\WopiLib\Contract\Service\DocumentManagerInterface; use ChampsLibres\WopiLib\Contract\Service\WopiInterface; -use Chill\MainBundle\Entity\User; -use DateTimeInterface; use loophp\psr17\Psr17Interface; use Psr\Cache\CacheItemPoolInterface; use Psr\Http\Message\RequestInterface; use Psr\Http\Message\ResponseInterface; -use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; use Symfony\Component\Security\Core\Security; use Symfony\Component\Security\Core\User\UserProviderInterface; final class ChillWopi implements WopiInterface { - private CacheItemPoolInterface $cache; - - private DocumentManagerInterface $documentManager; - - private Psr17Interface $psr17; - - private Security $security; - - private UserProviderInterface $userProvider; - private WopiInterface $wopi; public function __construct( - CacheItemPoolInterface $cache, - DocumentManagerInterface $documentManager, - Psr17Interface $psr17, - Security $security, - UserProviderInterface $userProvider, WopiInterface $wopi ) { - $this->cache = $cache; - $this->documentManager = $documentManager; - $this->psr17 = $psr17; - $this->security = $security; - $this->userProvider = $userProvider; $this->wopi = $wopi; } @@ -58,50 +35,9 @@ final class ChillWopi implements WopiInterface ?string $accessToken, RequestInterface $request ): ResponseInterface { - $user = $this->security->getUser(); - - if (!$user instanceof User) { - throw new AccessDeniedHttpException('User must be authenticated'); - } - $document = $this->documentManager->findByDocumentId($fileId); - $userCacheKey = sprintf('wopi_putUserInfo_%s', $user->getId()); - - return $this - ->psr17 - ->createResponse() - ->withHeader('Content-Type', 'application/json') - ->withBody($this->psr17->createStream((string) json_encode( - [ - 'BaseFileName' => $this->documentManager->getBasename($document), - 'OwnerId' => 'Symfony', - 'Size' => $this->documentManager->getSize($document), - 'UserId' => $user->getId(), - 'ReadOnly' => false, - 'UserCanAttend' => true, - 'UserCanPresent' => true, - 'UserCanRename' => true, - 'UserCanWrite' => true, - 'UserCanNotWriteRelative' => false, - 'SupportsUserInfo' => true, - 'SupportsDeleteFile' => true, - 'SupportsLocks' => true, - 'SupportsGetLock' => true, - 'SupportsExtendedLockLength' => true, - 'UserFriendlyName' => $user->getLabel(), - 'SupportsUpdate' => true, - 'SupportsRename' => true, - 'SupportsFolder' => false, - 'DisablePrint' => false, - 'AllowExternalMarketplace' => true, - 'SupportedShareUrlTypes' => [ - 'ReadOnly', - ], - 'LastModifiedTime' => $this->documentManager->getLastModifiedDate($document) - ->format(DateTimeInterface::ATOM), - 'SHA256' => $this->documentManager->getSha256($document), - 'UserInfo' => (string) $this->cache->getItem($userCacheKey)->get(), - ] - ))); + return $this->wopi->checkFileInfo($fileId, $accessToken, $request, [ + 'SupportsRename' => false, + ]); } public function deleteFile(string $fileId, ?string $accessToken, RequestInterface $request): ResponseInterface diff --git a/src/Bundle/ChillWopiBundle/src/Service/Wopi/UserManager.php b/src/Bundle/ChillWopiBundle/src/Service/Wopi/UserManager.php new file mode 100644 index 000000000..dc030192b --- /dev/null +++ b/src/Bundle/ChillWopiBundle/src/Service/Wopi/UserManager.php @@ -0,0 +1,53 @@ +security = $security; + } + + public function getUserFriendlyName(string $accessToken, string $fileId, RequestInterface $request): ?string + { + $user = $this->security->getUser(); + + if (!$user instanceof User) { + return null; + } + + return (string) $user->getLabel(); + } + + public function getUserId(string $accessToken, string $fileId, RequestInterface $request): ?string + { + $user = $this->security->getUser(); + + if (!$user instanceof User) { + return null; + } + + return (string) $user->getId(); + } + + public function isAnonymousUser(string $accessToken, string $fileId, RequestInterface $request): bool + { + return false; + } +}