diff --git a/lib/Middleware/SessionMiddleware.php b/lib/Middleware/SessionMiddleware.php index e72646e404d..0f5db0831ac 100644 --- a/lib/Middleware/SessionMiddleware.php +++ b/lib/Middleware/SessionMiddleware.php @@ -2,8 +2,11 @@ namespace OCA\Text\Middleware; +use Exception; use OC\User\NoUserException; use OCA\Text\Controller\ISessionAwareController; +use OCA\Text\Db\Document; +use OCA\Text\Db\Session; use OCA\Text\Exception\InvalidDocumentBaseVersionEtagException; use OCA\Text\Exception\InvalidSessionException; use OCA\Text\Middleware\Attribute\RequireDocumentBaseVersionEtag; @@ -16,6 +19,7 @@ use OCP\AppFramework\Http\JSONResponse; use OCP\AppFramework\Http\Response; use OCP\AppFramework\Middleware; +use OCP\Files\Folder; use OCP\Files\IRootFolder; use OCP\Files\NotPermittedException; use OCP\IL10N; @@ -24,17 +28,20 @@ use OCP\Share\Exceptions\ShareNotFound; use OCP\Share\IManager as ShareManager; use ReflectionException; +use ReflectionMethod; class SessionMiddleware extends Middleware { - public function __construct( - private IRequest $request, - private SessionService $sessionService, + private IRequest $request, + private SessionService $sessionService, private DocumentService $documentService, - private IUserSession $userSession, - private IRootFolder $rootFolder, - private ShareManager $shareManager, - private IL10N $l10n, + private IUserSession $userSession, + private IRootFolder $rootFolder, + private ShareManager $shareManager, + private IL10N $l10n, + private ?Document $document = null, + private ?Session $session = null, + private ?string $userId = null, ) { } @@ -48,34 +55,63 @@ public function beforeController(Controller $controller, string $methodName): vo return; } - $reflectionMethod = new \ReflectionMethod($controller, $methodName); + //ASSERTION + $documentId = $this->getDocumentId(); + $this->document = $this->documentService->getDocument($documentId); + + $reflectionMethod = new ReflectionMethod($controller, $methodName); + $requiresDocumentBaseVersionEtag = !empty($reflectionMethod->getAttributes(RequireDocumentBaseVersionEtag::class)); + + if ($requiresDocumentBaseVersionEtag) { + $this->assertDocumentBaseVersionEtag(); + } + + $requiresDocumentSession = !empty($reflectionMethod->getAttributes(RequireDocumentSession::class)); + $requiresDocumentSessionOrUserOrShareToken = !empty($reflectionMethod->getAttributes(RequireDocumentSessionOrUserOrShareToken::class)); + + if (!$requiresDocumentSession && !$requiresDocumentSessionOrUserOrShareToken) { + return; + } + + $this->session = $this->sessionService->getValidSession($documentId, $this->getSessionId(), $this->getSessionToken()); + + try { + $this->assertDocumentSession(); - if (!empty($reflectionMethod->getAttributes(RequireDocumentSessionOrUserOrShareToken::class))) { - try { - $this->assertDocumentSession($controller); - } catch (InvalidSessionException) { - $this->assertUserOrShareToken($controller); + if (!$this->getToken()) { + $this->userId = $this->session->getUserId(); } + } catch (InvalidSessionException) { + if (!$requiresDocumentSessionOrUserOrShareToken) { + throw new InvalidSessionException(); + } + + $this->assertUserOrShareToken(); } - if (!empty($reflectionMethod->getAttributes(RequireDocumentBaseVersionEtag::class))) { - $this->assertDocumentBaseVersionEtag(); + //OTHERS + $this->setControllerData($controller); + } + + public function afterException($controller, $methodName, Exception $exception): JSONResponse|Response { + if ($exception instanceof InvalidDocumentBaseVersionEtagException) { + return new JSONResponse(['error' => $this->l10n->t('Editing session has expired. Please reload the page.')], Http::STATUS_PRECONDITION_FAILED); } - if (!empty($reflectionMethod->getAttributes(RequireDocumentSession::class))) { - $this->assertDocumentSession($controller); + if ($exception instanceof InvalidSessionException) { + return new JSONResponse([], 403); } + + return parent::afterException($controller, $methodName, $exception); } /** * @throws InvalidDocumentBaseVersionEtagException */ private function assertDocumentBaseVersionEtag(): void { - $documentId = (int)$this->request->getParam('documentId'); - $baseVersionEtag = $this->request->getParam('baseVersionEtag'); + $baseVersionEtag = $this->getBaseVersionEtag(); - $document = $this->documentService->getDocument($documentId); - if ($baseVersionEtag && $document?->getBaseVersionEtag() !== $baseVersionEtag) { + if ($baseVersionEtag && $this->document?->getBaseVersionEtag() !== $baseVersionEtag) { throw new InvalidDocumentBaseVersionEtagException(); } } @@ -83,73 +119,121 @@ private function assertDocumentBaseVersionEtag(): void { /** * @throws InvalidSessionException */ - private function assertDocumentSession(ISessionAwareController $controller): void { - $documentId = (int)$this->request->getParam('documentId'); - $sessionId = (int)$this->request->getParam('sessionId'); - $token = (string)$this->request->getParam('sessionToken'); - $shareToken = (string)$this->request->getParam('token'); - - $session = $this->sessionService->getValidSession($documentId, $sessionId, $token); - if (!$session) { + private function assertDocumentSession(): void { + if (!$this->document || !$this->session) { throw new InvalidSessionException(); } + } - $document = $this->documentService->getDocument($documentId); - if (!$document) { + + /** + * @throws InvalidSessionException + */ + private function assertUserOrShareToken(): void { + if (!$this->document) { throw new InvalidSessionException(); } - $controller->setSession($session); - $controller->setDocument($document); - if (!$shareToken) { - $controller->setUserId($session->getUserId()); + $documentId = $this->getDocumentId(); + + if (null !== ($userId = $this->getSessionUserId())) { + $this->assertUserHasAccessToDocument($userId, $documentId); + + $this->userId = $userId; + + return; } + + if (null !== ($shareToken = $this->getShareToken())) { + $this->assertShareTokenHasAccessToDocument($shareToken, $documentId); + + return; + } + + throw new InvalidSessionException(); } /** - * @throws NotPermittedException - * @throws NoUserException * @throws InvalidSessionException */ - private function assertUserOrShareToken(ISessionAwareController $controller): void { - $documentId = (int)$this->request->getParam('documentId'); - if (null !== $userId = $this->userSession->getUser()?->getUID()) { - // Check if user has access to document - if (count($this->rootFolder->getUserFolder($userId)->getById($documentId)) === 0) { - throw new InvalidSessionException(); - } - $controller->setUserId($userId); - } elseif ('' !== $shareToken = (string)$this->request->getParam('shareToken')) { - try { - $share = $this->shareManager->getShareByToken($shareToken); - } catch (ShareNotFound) { - throw new InvalidSessionException(); - } - // Check if shareToken has access to document - if (count($this->rootFolder->getUserFolder($share->getShareOwner())->getById($documentId)) === 0) { - throw new InvalidSessionException(); - } - } else { + private function assertUserHasAccessToDocument(string $userId, int $documentId): void { + try { + $userFolder = $this->getUserFolder($userId); + } catch (NoUserException|NotPermittedException) { throw new InvalidSessionException(); } - $document = $this->documentService->getDocument($documentId); - if (!$document) { + if (count($userFolder->getById($documentId)) === 0) { throw new InvalidSessionException(); } - - $controller->setDocument($document); } - public function afterException($controller, $methodName, \Exception $exception): JSONResponse|Response { - if ($exception instanceof InvalidDocumentBaseVersionEtagException) { - return new JSONResponse(['error' => $this->l10n->t('Editing session has expired. Please reload the page.')], Http::STATUS_PRECONDITION_FAILED); + /** + * @throws InvalidSessionException + */ + private function assertShareTokenHasAccessToDocument(string $shareToken, int $documentId): void { + try { + $share = $this->shareManager->getShareByToken($shareToken); + } catch (ShareNotFound) { + throw new InvalidSessionException(); } - if ($exception instanceof InvalidSessionException) { - return new JSONResponse([], 403); + try { + $userFolder = $this->getUserFolder($share->getShareOwner()); + } catch (NoUserException|NotPermittedException) { + throw new InvalidSessionException(); } - return parent::afterException($controller, $methodName, $exception); + if (count($userFolder->getById($documentId)) === 0) { + throw new InvalidSessionException(); + } + } + + private function getDocumentId(): int { + return (int)$this->request->getParam('documentId'); + } + + private function getSessionId(): int { + return (int)$this->request->getParam('sessionId'); + } + + private function getSessionToken(): string { + return (string)$this->request->getParam('sessionToken'); + } + + private function getToken(): string { + return (string)$this->request->getParam('token'); + } + + private function getShareToken(): ?string { + return $this->request->getParam('shareToken'); + } + + private function getBaseVersionEtag(): string { + return (string)$this->request->getParam('baseVersionEtag'); + } + + private function getSessionUserId(): ?string { + return $this->userSession->getUser()?->getUID(); + } + + /** + * @throws NotPermittedException + * @throws NoUserException + */ + private function getUserFolder(string $userId): Folder { + return $this->rootFolder->getUserFolder($userId); + } + + private function setControllerData(ISessionAwareController $controller): void { + if ($this->document) { + $controller->setDocument($this->document); + } + if ($this->session) { + $controller->setSession($this->session); + } + if ($this->userId !== null) { + $controller->setUserId($this->userId); + } } }