diff --git a/lib/Controller/DocumentController.php b/lib/Controller/DocumentController.php index 9e4b80195..503f86b6a 100644 --- a/lib/Controller/DocumentController.php +++ b/lib/Controller/DocumentController.php @@ -395,7 +395,8 @@ class DocumentController extends Controller { $file = $this->getFileForUser($fileId); $this->session->set(self::SESSION_FILE_TARGET, [ - 'fileId' => $file->getId() + 'fileId' => $file->getId(), + 'PresentationLeader' => $file->getFileInfo()->getOwner()->getUid() ]); $filePath = $file->getPath(); @@ -425,7 +426,7 @@ class DocumentController extends Controller { } $isGuest = $guestName || !$this->userId; - $wopi = $this->getToken($file, $share, null, $isGuest); + $wopi = $this->getToken($file, $share, null, $isGuest, presentationLeader: $this->session->get(self::SESSION_FILE_TARGET)['PresentationLeader']); $this->tokenManager->setGuestName($wopi, $guestName); @@ -520,7 +521,7 @@ class DocumentController extends Controller { throw new NotFoundException(); } - private function getToken(File $file, ?IShare $share = null, ?int $version = null, bool $isGuest = false): Wopi { + private function getToken(File $file, ?IShare $share = null, ?int $version = null, bool $isGuest = false, ?string $presentationLeader = null): Wopi { // Pass through $version $templateFile = $this->templateManager->getTemplateSource($file->getId()); if ($templateFile) { @@ -540,7 +541,7 @@ class DocumentController extends Controller { } - return $this->tokenManager->generateWopiToken($this->getWopiFileId($file->getId(), $version), $share?->getToken(), $this->userId); + return $this->tokenManager->generateWopiToken($this->getWopiFileId($file->getId(), $version), $share?->getToken(), $this->userId, presentationLeader: $presentationLeader); } private function getWopiFileId(int $fileId, ?int $version = null): string { diff --git a/lib/Controller/WopiController.php b/lib/Controller/WopiController.php index 656dc139e..9ce76544a 100644 --- a/lib/Controller/WopiController.php +++ b/lib/Controller/WopiController.php @@ -64,6 +64,8 @@ use OCP\Share\IShare; use Psr\Container\ContainerExceptionInterface; use Psr\Container\NotFoundExceptionInterface; use Psr\Log\LoggerInterface; +use OCP\ICacheFactory; +use OCP\ICache; #[RestrictToWopiServer] class WopiController extends Controller { @@ -96,6 +98,7 @@ class WopiController extends Controller { private SettingsService $settingsService, private CapabilitiesService $capabilitiesService, private Helper $helper, + private ICacheFactory $cacheFactory, ) { parent::__construct($appName, $request); } @@ -149,6 +152,17 @@ class WopiController extends Controller { $userId = !$isPublic ? $wopi->getEditorUid() : $guestUserId; + $cache = $this->cacheFactory->createLocal('richdocuments_wopi'); + $extraJson = $cache->get('wopi_extra_' . $wopi->getToken()); + if ($extraJson !== false && $extraJson !== '') { + $decoded = json_decode($extraJson, true); + if (is_array($decoded) && isset($decoded['PresentationLeader'])) { + $wopi->setPresentationLeader($decoded['PresentationLeader']); + } + $cache->remove('wopi_extra_' . $wopi->getToken()); + } + + $response = [ 'BaseFileName' => $file->getName(), 'Size' => $file->getSize(), @@ -181,6 +195,7 @@ class WopiController extends Controller { 'EnableRemoteAIContent' => $isTaskProcessingEnabled, 'HasContentRange' => true, 'ServerPrivateInfo' => [], + 'PresentationLeader' => $wopi->getPresentationLeader() ]; if ($this->capabilitiesService->hasSettingIframeSupport()) { diff --git a/lib/Db/Wopi.php b/lib/Db/Wopi.php index 82aaf25ef..f28428ac6 100644 --- a/lib/Db/Wopi.php +++ b/lib/Db/Wopi.php @@ -124,6 +124,8 @@ class Wopi extends Entity implements \JsonSerializable { /** @var int */ protected $tokenType = 0; + protected ?string $presentationLeader = null; + public function __construct() { $this->addType('ownerUid', Types::STRING); $this->addType('editorUid', Types::STRING); @@ -139,6 +141,7 @@ class Wopi extends Entity implements \JsonSerializable { $this->addType('hideDownload', Types::BOOLEAN); $this->addType('direct', Types::BOOLEAN); $this->addType('tokenType', Types::INTEGER); + $this->addType('presentationLeader', Types::STRING); } public function hasTemplateId() { @@ -168,6 +171,14 @@ class Wopi extends Entity implements \JsonSerializable { return (bool)$this->direct; } + public function setPresentationLeader(?string $val): void { + $this->presentationLeader = $val; + } + + public function getPresentationLeader(): ?string { + return $this->presentationLeader; + } + #[\ReturnTypeWillChange] public function jsonSerialize() { $properties = get_object_vars($this); diff --git a/lib/Db/WopiMapper.php b/lib/Db/WopiMapper.php index 0ed77392a..05688ffab 100644 --- a/lib/Db/WopiMapper.php +++ b/lib/Db/WopiMapper.php @@ -14,6 +14,8 @@ use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\IDBConnection; use OCP\Security\ISecureRandom; use Psr\Log\LoggerInterface; +use OCP\ICacheFactory; +use OCP\ICache; /** @template-extends QBMapper */ class WopiMapper extends QBMapper { @@ -23,6 +25,7 @@ class WopiMapper extends QBMapper { private LoggerInterface $logger, private ITimeFactory $timeFactory, private AppConfig $appConfig, + private ICacheFactory $cacheFactory, ) { parent::__construct($db, 'richdocuments_wopi', Wopi::class); } @@ -38,7 +41,7 @@ class WopiMapper extends QBMapper { * @param int $templateDestination * @return Wopi */ - public function generateFileToken($fileId, $owner, $editor, $version, $updatable, $serverHost, ?string $guestDisplayname = null, $hideDownload = false, $direct = false, $templateId = 0, $share = null) { + public function generateFileToken($fileId, $owner, $editor, $version, $updatable, $serverHost, ?string $guestDisplayname = null, $hideDownload = false, $direct = false, $templateId = 0, $share = null, ?string $presentationLeader = null) { $token = $this->random->generate(32, ISecureRandom::CHAR_LOWER . ISecureRandom::CHAR_UPPER . ISecureRandom::CHAR_DIGITS); $wopi = Wopi::fromParams([ @@ -57,9 +60,21 @@ class WopiMapper extends QBMapper { 'remoteServer' => '', 'remoteServerToken' => '', 'share' => $share, - 'tokenType' => $guestDisplayname === null ? Wopi::TOKEN_TYPE_USER : Wopi::TOKEN_TYPE_GUEST + 'tokenType' => $guestDisplayname === null ? Wopi::TOKEN_TYPE_USER : Wopi::TOKEN_TYPE_GUEST, + 'presentationLeader' => $presentationLeader ]); + // store transient value in memcache keyed by token with TTL = token expiry - now + if ($presentationLeader !== null) { + /** @var ICache $cache */ + $cache = $this->cacheFactory->createLocal('richdocuments_wopi'); + $ttl = $this->calculateNewTokenExpiry() - $this->timeFactory->getTime(); + if ($ttl <= 0) { + $ttl = 60; + } + $cache->set('wopi_extra_' . $token, json_encode(['PresentationLeader' => $presentationLeader]), (int)$ttl); + } + /** @var Wopi $wopi */ $wopi = $this->insert($wopi); diff --git a/lib/TokenManager.php b/lib/TokenManager.php index b2d34749f..404d5ab1c 100644 --- a/lib/TokenManager.php +++ b/lib/TokenManager.php @@ -45,7 +45,7 @@ class TokenManager { /** * @throws Exception */ - public function generateWopiToken(string $fileId, ?string $shareToken = null, ?string $editoruid = null, bool $direct = false): Wopi { + public function generateWopiToken(string $fileId, ?string $shareToken = null, ?string $editoruid = null, bool $direct = false, ?string $presentationLeader = null): Wopi { [$fileId, , $version] = Helper::parseFileId($fileId); $owneruid = null; $hideDownload = false; @@ -121,7 +121,7 @@ class TokenManager { $serverHost = $this->urlGenerator->getAbsoluteURL('/'); $guestName = $editoruid === null ? $this->prepareGuestName($this->helper->getGuestNameFromCookie()) : null; - return $this->wopiMapper->generateFileToken($fileId, $owneruid, $editoruid, $version, $updatable, $serverHost, $guestName, $hideDownload, $direct, 0, $shareToken); + return $this->wopiMapper->generateFileToken($fileId, $owneruid, $editoruid, $version, $updatable, $serverHost, $guestName, $hideDownload, $direct, 0, $shareToken, $presentationLeader); } /**