mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-06-07 18:44:08 +00:00
Merge branch '43-wopi-use-access-token'
This commit is contained in:
commit
a913d2820d
@ -9,18 +9,22 @@
|
|||||||
],
|
],
|
||||||
"require": {
|
"require": {
|
||||||
"php": "^7.4",
|
"php": "^7.4",
|
||||||
|
"ext-json": "*",
|
||||||
|
"ext-openssl": "*",
|
||||||
|
"ext-redis": "*",
|
||||||
"champs-libres/async-uploader-bundle": "dev-sf4#d57134aee8e504a83c902ff0cf9f8d36ac418290",
|
"champs-libres/async-uploader-bundle": "dev-sf4#d57134aee8e504a83c902ff0cf9f8d36ac418290",
|
||||||
"champs-libres/wopi-bundle": "dev-master#6dd8e0a14e00131eb4b889ecc30270ee4a0e5224",
|
"champs-libres/wopi-bundle": "dev-master@dev",
|
||||||
"champs-libres/wopi-lib": "dev-master#8615f4a45a39fc2b6a98765ea835fcfd39618787",
|
"champs-libres/wopi-lib": "dev-master@dev",
|
||||||
"doctrine/doctrine-bundle": "^2.1",
|
"doctrine/doctrine-bundle": "^2.1",
|
||||||
"doctrine/doctrine-migrations-bundle": "^3.0",
|
"doctrine/doctrine-migrations-bundle": "^3.0",
|
||||||
"doctrine/orm": "^2.7",
|
"doctrine/orm": "^2.13.0",
|
||||||
"erusev/parsedown": "^1.7",
|
"erusev/parsedown": "^1.7",
|
||||||
"graylog2/gelf-php": "^1.5",
|
"graylog2/gelf-php": "^1.5",
|
||||||
"knplabs/knp-menu-bundle": "^3.0",
|
"knplabs/knp-menu-bundle": "^3.0",
|
||||||
"knplabs/knp-time-bundle": "^1.12",
|
"knplabs/knp-time-bundle": "^1.12",
|
||||||
"knpuniversity/oauth2-client-bundle": "^2.10",
|
"knpuniversity/oauth2-client-bundle": "^2.10",
|
||||||
"league/csv": "^9.7.1",
|
"league/csv": "^9.7.1",
|
||||||
|
"lexik/jwt-authentication-bundle": "^2.16",
|
||||||
"nyholm/psr7": "^1.4",
|
"nyholm/psr7": "^1.4",
|
||||||
"ocramius/package-versions": "^1.10 || ^2",
|
"ocramius/package-versions": "^1.10 || ^2",
|
||||||
"odolbeau/phone-number-bundle": "^3.6",
|
"odolbeau/phone-number-bundle": "^3.6",
|
||||||
@ -29,12 +33,12 @@
|
|||||||
"ramsey/uuid-doctrine": "^1.7",
|
"ramsey/uuid-doctrine": "^1.7",
|
||||||
"sensio/framework-extra-bundle": "^5.5",
|
"sensio/framework-extra-bundle": "^5.5",
|
||||||
"spomky-labs/base64url": "^2.0",
|
"spomky-labs/base64url": "^2.0",
|
||||||
"symfony/asset": "^4.4",
|
|
||||||
"symfony/browser-kit": "^4.4",
|
"symfony/browser-kit": "^4.4",
|
||||||
"symfony/css-selector": "^4.4",
|
"symfony/css-selector": "^4.4",
|
||||||
"symfony/expression-language": "^4.4",
|
"symfony/expression-language": "^4.4",
|
||||||
"symfony/form": "^4.4",
|
"symfony/form": "^4.4",
|
||||||
"symfony/framework-bundle": "^4.4",
|
"symfony/framework-bundle": "^4.4",
|
||||||
|
"symfony/http-client": "^4.4 || ^5",
|
||||||
"symfony/http-foundation": "^4.4",
|
"symfony/http-foundation": "^4.4",
|
||||||
"symfony/intl": "^4.4",
|
"symfony/intl": "^4.4",
|
||||||
"symfony/mailer": "^5.4",
|
"symfony/mailer": "^5.4",
|
||||||
@ -72,8 +76,7 @@
|
|||||||
"symfony/maker-bundle": "^1.20",
|
"symfony/maker-bundle": "^1.20",
|
||||||
"symfony/phpunit-bridge": "^4.4",
|
"symfony/phpunit-bridge": "^4.4",
|
||||||
"symfony/stopwatch": "^4.4",
|
"symfony/stopwatch": "^4.4",
|
||||||
"symfony/var-dumper": "^4.4",
|
"symfony/var-dumper": "^4.4"
|
||||||
"symfony/web-profiler-bundle": "^4.4"
|
|
||||||
},
|
},
|
||||||
"conflict": {
|
"conflict": {
|
||||||
"symfony/symfony": "*"
|
"symfony/symfony": "*"
|
||||||
|
@ -60,7 +60,7 @@ final class StoredObjectManager implements StoredObjectManagerInterface
|
|||||||
$this
|
$this
|
||||||
->tempUrlGenerator
|
->tempUrlGenerator
|
||||||
->generate(
|
->generate(
|
||||||
Request::METHOD_PUT,
|
Request::METHOD_HEAD,
|
||||||
$document->getFilename()
|
$document->getFilename()
|
||||||
)
|
)
|
||||||
->url
|
->url
|
||||||
|
@ -15,7 +15,7 @@ use Doctrine\Common\Collections\ArrayCollection;
|
|||||||
use Doctrine\Common\Collections\Collection;
|
use Doctrine\Common\Collections\Collection;
|
||||||
use Doctrine\ORM\Mapping as ORM;
|
use Doctrine\ORM\Mapping as ORM;
|
||||||
use RuntimeException;
|
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\Serializer\Annotation as Serializer;
|
||||||
use Symfony\Component\Validator\Context\ExecutionContextInterface;
|
use Symfony\Component\Validator\Context\ExecutionContextInterface;
|
||||||
|
|
||||||
@ -31,7 +31,7 @@ use function in_array;
|
|||||||
* "user": User::class
|
* "user": User::class
|
||||||
* })
|
* })
|
||||||
*/
|
*/
|
||||||
class User implements AdvancedUserInterface
|
class User implements UserInterface
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @ORM\Id
|
* @ORM\Id
|
||||||
@ -58,8 +58,6 @@ class User implements AdvancedUserInterface
|
|||||||
private ?Location $currentLocation = null;
|
private ?Location $currentLocation = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var string
|
|
||||||
*
|
|
||||||
* @ORM\Column(type="string", length=150, nullable=true)
|
* @ORM\Column(type="string", length=150, nullable=true)
|
||||||
*/
|
*/
|
||||||
private ?string $email = null;
|
private ?string $email = null;
|
||||||
@ -216,7 +214,7 @@ class User implements AdvancedUserInterface
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return GroupCenter
|
* @return Collection<GroupCenter>
|
||||||
*/
|
*/
|
||||||
public function getGroupCenters()
|
public function getGroupCenters()
|
||||||
{
|
{
|
||||||
@ -225,10 +223,8 @@ class User implements AdvancedUserInterface
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Get id.
|
* Get id.
|
||||||
*
|
|
||||||
* @return int
|
|
||||||
*/
|
*/
|
||||||
public function getId()
|
public function getId(): int
|
||||||
{
|
{
|
||||||
return $this->id;
|
return $this->id;
|
||||||
}
|
}
|
||||||
@ -487,7 +483,7 @@ class User implements AdvancedUserInterface
|
|||||||
*
|
*
|
||||||
* @param string $name
|
* @param string $name
|
||||||
*
|
*
|
||||||
* @return Agent
|
* @return User
|
||||||
*/
|
*/
|
||||||
public function setUsername($name)
|
public function setUsername($name)
|
||||||
{
|
{
|
||||||
|
@ -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
|
||||||
|
@ -12,12 +12,15 @@ declare(strict_types=1);
|
|||||||
namespace Symfony\Component\DependencyInjection\Loader\Configurator;
|
namespace Symfony\Component\DependencyInjection\Loader\Configurator;
|
||||||
|
|
||||||
use ChampsLibres\AsyncUploaderBundle\TempUrl\TempUrlGeneratorInterface;
|
use ChampsLibres\AsyncUploaderBundle\TempUrl\TempUrlGeneratorInterface;
|
||||||
|
use ChampsLibres\WopiBundle\Contracts\AuthorizationManagerInterface;
|
||||||
|
use ChampsLibres\WopiBundle\Contracts\UserManagerInterface;
|
||||||
use ChampsLibres\WopiBundle\Service\Wopi as CLWopi;
|
use ChampsLibres\WopiBundle\Service\Wopi as CLWopi;
|
||||||
use ChampsLibres\WopiLib\Contract\Service\DocumentLockManagerInterface;
|
|
||||||
use ChampsLibres\WopiLib\Contract\Service\DocumentManagerInterface;
|
use ChampsLibres\WopiLib\Contract\Service\DocumentManagerInterface;
|
||||||
|
use Chill\WopiBundle\Service\Wopi\AuthorizationManager;
|
||||||
use Chill\WopiBundle\Service\Wopi\ChillDocumentLockManager;
|
use Chill\WopiBundle\Service\Wopi\ChillDocumentLockManager;
|
||||||
use Chill\WopiBundle\Service\Wopi\ChillDocumentManager;
|
use Chill\WopiBundle\Service\Wopi\ChillDocumentManager;
|
||||||
use Chill\WopiBundle\Service\Wopi\ChillWopi;
|
use Chill\WopiBundle\Service\Wopi\ChillWopi;
|
||||||
|
use Chill\WopiBundle\Service\Wopi\UserManager;
|
||||||
|
|
||||||
return static function (ContainerConfigurator $container) {
|
return static function (ContainerConfigurator $container) {
|
||||||
$services = $container
|
$services = $container
|
||||||
@ -44,8 +47,17 @@ return static function (ContainerConfigurator $container) {
|
|||||||
->alias(DocumentManagerInterface::class, ChillDocumentManager::class);
|
->alias(DocumentManagerInterface::class, ChillDocumentManager::class);
|
||||||
|
|
||||||
$services
|
$services
|
||||||
->set(ChillDocumentLockManager::class)
|
->set(ChillDocumentLockManager::class);
|
||||||
->decorate(DocumentLockManagerInterface::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)
|
// TODO: Move this into the async bundle (low priority)
|
||||||
$services
|
$services
|
||||||
|
@ -0,0 +1,88 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Chill is a software for social workers
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view
|
||||||
|
* the LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Chill\WopiBundle\Service\Wopi;
|
||||||
|
|
||||||
|
use ChampsLibres\WopiLib\Contract\Entity\Document;
|
||||||
|
use Chill\MainBundle\Entity\User;
|
||||||
|
use Lexik\Bundle\JWTAuthenticationBundle\Services\JWTTokenManagerInterface;
|
||||||
|
use Psr\Http\Message\RequestInterface;
|
||||||
|
use Symfony\Component\Security\Core\Security;
|
||||||
|
|
||||||
|
class AuthorizationManager implements \ChampsLibres\WopiBundle\Contracts\AuthorizationManagerInterface
|
||||||
|
{
|
||||||
|
private Security $security;
|
||||||
|
|
||||||
|
private JWTTokenManagerInterface $tokenManager;
|
||||||
|
|
||||||
|
public function __construct(JWTTokenManagerInterface $tokenManager, Security $security)
|
||||||
|
{
|
||||||
|
$this->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);
|
||||||
|
}
|
||||||
|
}
|
@ -24,11 +24,12 @@ use loophp\psr17\Psr17Interface;
|
|||||||
use Psr\Http\Message\RequestInterface;
|
use Psr\Http\Message\RequestInterface;
|
||||||
use Psr\Http\Message\StreamInterface;
|
use Psr\Http\Message\StreamInterface;
|
||||||
use Ramsey\Uuid\Uuid;
|
use Ramsey\Uuid\Uuid;
|
||||||
|
use RuntimeException;
|
||||||
use Symfony\Bridge\PsrHttpMessage\HttpMessageFactoryInterface;
|
use Symfony\Bridge\PsrHttpMessage\HttpMessageFactoryInterface;
|
||||||
use Symfony\Component\HttpFoundation\RequestStack;
|
use Symfony\Component\HttpFoundation\RequestStack;
|
||||||
use Symfony\Component\Mime\MimeTypes;
|
use Symfony\Component\Mime\MimeTypes;
|
||||||
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
|
|
||||||
|
|
||||||
|
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
|
||||||
use function strlen;
|
use function strlen;
|
||||||
|
|
||||||
final class ChillDocumentManager implements DocumentManagerInterface
|
final class ChillDocumentManager implements DocumentManagerInterface
|
||||||
@ -190,7 +191,12 @@ final class ChillDocumentManager implements DocumentManagerInterface
|
|||||||
|
|
||||||
public function remove(Document $document): void
|
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
|
public function write(Document $document, array $properties = []): void
|
||||||
|
@ -13,39 +13,20 @@ 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 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\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
|
||||||
{
|
{
|
||||||
private CacheItemPoolInterface $cache;
|
|
||||||
|
|
||||||
private DocumentManagerInterface $documentManager;
|
|
||||||
|
|
||||||
private Psr17Interface $psr17;
|
|
||||||
|
|
||||||
private UserProviderInterface $userProvider;
|
|
||||||
|
|
||||||
private WopiInterface $wopi;
|
private WopiInterface $wopi;
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
CacheItemPoolInterface $cache,
|
|
||||||
DocumentManagerInterface $documentManager,
|
|
||||||
Psr17Interface $psr17,
|
|
||||||
UserProviderInterface $userProvider,
|
|
||||||
WopiInterface $wopi
|
WopiInterface $wopi
|
||||||
) {
|
) {
|
||||||
$this->cache = $cache;
|
|
||||||
$this->documentManager = $documentManager;
|
|
||||||
$this->psr17 = $psr17;
|
|
||||||
$this->userProvider = $userProvider;
|
|
||||||
$this->wopi = $wopi;
|
$this->wopi = $wopi;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -54,55 +35,9 @@ final class ChillWopi implements WopiInterface
|
|||||||
?string $accessToken,
|
?string $accessToken,
|
||||||
RequestInterface $request
|
RequestInterface $request
|
||||||
): ResponseInterface {
|
): ResponseInterface {
|
||||||
try {
|
return $this->wopi->checkFileInfo($fileId, $accessToken, $request, [
|
||||||
$user = $this->userProvider->loadUserByUsername($accessToken);
|
'SupportsRename' => false,
|
||||||
} catch (UsernameNotFoundException $e) {
|
]);
|
||||||
return $this
|
|
||||||
->psr17
|
|
||||||
->createResponse(401);
|
|
||||||
}
|
|
||||||
|
|
||||||
// @ TODO : Replace this with a call to decorated object once authentication is done.
|
|
||||||
$document = $this->documentManager->findByDocumentId($fileId);
|
|
||||||
$userIdentifier = $user->getUsername();
|
|
||||||
$userCacheKey = sprintf('wopi_putUserInfo_%s', $userIdentifier);
|
|
||||||
|
|
||||||
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' => $userIdentifier,
|
|
||||||
'ReadOnly' => false,
|
|
||||||
'UserCanAttend' => true,
|
|
||||||
'UserCanPresent' => true,
|
|
||||||
'UserCanRename' => true,
|
|
||||||
'UserCanWrite' => true,
|
|
||||||
'UserCanNotWriteRelative' => false,
|
|
||||||
'SupportsUserInfo' => true,
|
|
||||||
'SupportsDeleteFile' => true,
|
|
||||||
'SupportsLocks' => true,
|
|
||||||
'SupportsGetLock' => true,
|
|
||||||
'SupportsExtendedLockLength' => true,
|
|
||||||
'UserFriendlyName' => $userIdentifier,
|
|
||||||
'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(),
|
|
||||||
]
|
|
||||||
)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function deleteFile(string $fileId, ?string $accessToken, RequestInterface $request): ResponseInterface
|
public function deleteFile(string $fileId, ?string $accessToken, RequestInterface $request): ResponseInterface
|
||||||
@ -152,97 +87,7 @@ final class ChillWopi implements WopiInterface
|
|||||||
string $xWopiEditors,
|
string $xWopiEditors,
|
||||||
RequestInterface $request
|
RequestInterface $request
|
||||||
): ResponseInterface {
|
): ResponseInterface {
|
||||||
$document = $this->documentManager->findByDocumentId($fileId);
|
return $this->wopi->putFile($fileId, $accessToken, $xWopiLock, $xWopiEditors, $request);
|
||||||
$version = $this->documentManager->getVersion($document);
|
|
||||||
|
|
||||||
// File is unlocked, we must reject the document, except if collabora is autosaving
|
|
||||||
if (false === $this->documentManager->hasLock($document) && 'true' !== ($request->getHeader('x-lool-wopi-isexitsave') ?? ['false'])[0]) {
|
|
||||||
if (0 !== $this->documentManager->getSize($document)) {
|
|
||||||
return $this
|
|
||||||
->psr17
|
|
||||||
->createResponse(409)
|
|
||||||
->withHeader(
|
|
||||||
WopiInterface::HEADER_ITEM_VERSION,
|
|
||||||
sprintf('v%s', $version)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// File is locked, we check for the lock
|
|
||||||
if ($this->documentManager->hasLock($document)) {
|
|
||||||
if ($xWopiLock !== $currentLock = $this->documentManager->getLock($document)) {
|
|
||||||
return $this
|
|
||||||
->psr17
|
|
||||||
->createResponse(409)
|
|
||||||
->withHeader(
|
|
||||||
WopiInterface::HEADER_LOCK,
|
|
||||||
$currentLock
|
|
||||||
)
|
|
||||||
->withHeader(
|
|
||||||
WopiInterface::HEADER_ITEM_VERSION,
|
|
||||||
sprintf('v%s', $version)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// for collabora online editor, check timestamp if present
|
|
||||||
/* delete because it seems that collabora send always the first wopi-timestamp, not the last known one
|
|
||||||
// example:
|
|
||||||
// load the doc: the last-wopi is 12:00 in FileInfo
|
|
||||||
// save the doc: x-cool-wopi-timestamp is 12:00, but we replace the ts with the time of save (12:05)
|
|
||||||
// save the doc again: x-cool-wopi-timestamp is still 12:00...
|
|
||||||
if ($request->hasHeader('x-cool-wopi-timestamp')) {
|
|
||||||
$date = DateTimeImmutable::createFromFormat(
|
|
||||||
DateTimeImmutable::ATOM,
|
|
||||||
$request->getHeader('x-cool-wopi-timestamp')[0]
|
|
||||||
);
|
|
||||||
|
|
||||||
if (false === $date) {
|
|
||||||
throw new RuntimeException('Error parsing date: ' . implode('', DateTimeImmutable::getLastErrors()));
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($this->documentManager->getLastModifiedDate($document)->getTimestamp() < $date->getTimestamp()) {
|
|
||||||
return $this
|
|
||||||
->psr17
|
|
||||||
->createResponse(409)
|
|
||||||
->withHeader(
|
|
||||||
WopiInterface::HEADER_LOCK,
|
|
||||||
$currentLock
|
|
||||||
)
|
|
||||||
->withHeader(
|
|
||||||
WopiInterface::HEADER_ITEM_VERSION,
|
|
||||||
sprintf('v%s', $version)
|
|
||||||
)
|
|
||||||
->withBody(
|
|
||||||
$this->psr17->createStream(
|
|
||||||
json_encode(['COOLStatusCode' => 1010])
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
$body = (string) $request->getBody();
|
|
||||||
$this->documentManager->write(
|
|
||||||
$document,
|
|
||||||
[
|
|
||||||
'content' => $body,
|
|
||||||
'size' => (string) strlen($body),
|
|
||||||
]
|
|
||||||
);
|
|
||||||
$version = $this->documentManager->getVersion($document);
|
|
||||||
|
|
||||||
return $this
|
|
||||||
->psr17
|
|
||||||
->createResponse()
|
|
||||||
->withHeader(
|
|
||||||
WopiInterface::HEADER_LOCK,
|
|
||||||
$xWopiLock
|
|
||||||
)
|
|
||||||
->withHeader(
|
|
||||||
WopiInterface::HEADER_ITEM_VERSION,
|
|
||||||
sprintf('v%s', $version)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function putRelativeFile(string $fileId, string $accessToken, ?string $suggestedTarget, ?string $relativeTarget, bool $overwriteRelativeTarget, int $size, RequestInterface $request): ResponseInterface
|
public function putRelativeFile(string $fileId, string $accessToken, ?string $suggestedTarget, ?string $relativeTarget, bool $overwriteRelativeTarget, int $size, RequestInterface $request): ResponseInterface
|
||||||
|
53
src/Bundle/ChillWopiBundle/src/Service/Wopi/UserManager.php
Normal file
53
src/Bundle/ChillWopiBundle/src/Service/Wopi/UserManager.php
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Chill is a software for social workers
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view
|
||||||
|
* the LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Chill\WopiBundle\Service\Wopi;
|
||||||
|
|
||||||
|
use Chill\MainBundle\Entity\User;
|
||||||
|
use Psr\Http\Message\RequestInterface;
|
||||||
|
use Symfony\Component\Security\Core\Security;
|
||||||
|
|
||||||
|
class UserManager implements \ChampsLibres\WopiBundle\Contracts\UserManagerInterface
|
||||||
|
{
|
||||||
|
private Security $security;
|
||||||
|
|
||||||
|
public function __construct(Security $security)
|
||||||
|
{
|
||||||
|
$this->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;
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user