f7cloud_client/apps/text/lib/Middleware/SessionMiddleware.php
root 8b6a0139db f7cloud_client
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-17 22:59:26 +00:00

180 lines
5.6 KiB
PHP

<?php
/**
* SPDX-FileCopyrightText: 2023 F7cloud GmbH and F7cloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\Text\Middleware;
use OC\User\NoUserException;
use OCA\Text\Controller\ISessionAwareController;
use OCA\Text\Exception\InvalidDocumentBaseVersionEtagException;
use OCA\Text\Exception\InvalidSessionException;
use OCA\Text\Middleware\Attribute\RequireDocumentBaseVersionEtag;
use OCA\Text\Middleware\Attribute\RequireDocumentSession;
use OCA\Text\Middleware\Attribute\RequireDocumentSessionOrUserOrShareToken;
use OCA\Text\Service\DocumentService;
use OCA\Text\Service\SessionService;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\JSONResponse;
use OCP\AppFramework\Http\Response;
use OCP\AppFramework\Middleware;
use OCP\Constants;
use OCP\Files\IRootFolder;
use OCP\Files\NotPermittedException;
use OCP\IL10N;
use OCP\IRequest;
use OCP\ISession;
use OCP\IUserSession;
use OCP\Share\Exceptions\ShareNotFound;
use OCP\Share\IManager as ShareManager;
use ReflectionException;
class SessionMiddleware extends Middleware {
public function __construct(
private IRequest $request,
private SessionService $sessionService,
private DocumentService $documentService,
private ISession $session,
private IUserSession $userSession,
private IRootFolder $rootFolder,
private ShareManager $shareManager,
private IL10N $l10n,
) {
}
/**
* @throws ReflectionException
* @throws InvalidDocumentBaseVersionEtagException
* @throws InvalidSessionException
*/
public function beforeController(Controller $controller, string $methodName): void {
if (!$controller instanceof ISessionAwareController) {
return;
}
$reflectionMethod = new \ReflectionMethod($controller, $methodName);
if (!empty($reflectionMethod->getAttributes(RequireDocumentSessionOrUserOrShareToken::class))) {
try {
$this->assertDocumentSession($controller);
} catch (InvalidSessionException) {
$this->assertUserOrShareToken($controller);
}
}
if (!empty($reflectionMethod->getAttributes(RequireDocumentBaseVersionEtag::class))) {
$this->assertDocumentBaseVersionEtag();
}
if (!empty($reflectionMethod->getAttributes(RequireDocumentSession::class))) {
$this->assertDocumentSession($controller);
}
}
/**
* @throws InvalidDocumentBaseVersionEtagException
*/
private function assertDocumentBaseVersionEtag(): void {
$documentId = (int)$this->request->getParam('documentId');
$baseVersionEtag = $this->request->getParam('baseVersionEtag');
$document = $this->documentService->getDocument($documentId);
if ($baseVersionEtag && $document?->getBaseVersionEtag() !== $baseVersionEtag) {
throw new InvalidDocumentBaseVersionEtagException();
}
}
/**
* @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) {
throw new InvalidSessionException();
}
$document = $this->documentService->getDocument($documentId);
if (!$document) {
throw new InvalidSessionException();
}
$controller->setSession($session);
$controller->setDocumentId($documentId);
$controller->setDocument($document);
if (!$shareToken) {
$controller->setUserId($session->getUserId());
}
}
/**
* @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()) {
if ($this->rootFolder->getUserFolder($userId)->getFirstNodeById($documentId) !== null) {
$controller->setUserId($userId);
$controller->setDocumentId($documentId);
return;
}
}
if ('' !== $shareToken = (string)$this->request->getParam('shareToken')) {
try {
$share = $this->shareManager->getShareByToken($shareToken);
} catch (ShareNotFound) {
throw new InvalidSessionException();
}
$node = $this->rootFolder->getUserFolder($share->getShareOwner())->getFirstNodeById($documentId);
if ($node === null) {
throw new InvalidSessionException();
}
if ($share->getPassword() !== null) {
$shareIds = $this->session->get('public_link_authenticated');
if ($share->getId() !== $shareIds && (is_array($shareIds) && !in_array($share->getId(), $shareIds, true))) {
throw new InvalidSessionException();
}
}
if (($share->getPermissions() & Constants::PERMISSION_READ) !== Constants::PERMISSION_READ) {
throw new InvalidSessionException();
}
$attributes = $share->getAttributes();
if ($attributes !== null && $attributes->getAttribute('permissions', 'download') === false) {
throw new InvalidSessionException();
}
$controller->setDocumentId($documentId);
return;
}
throw new InvalidSessionException();
}
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 ($exception instanceof InvalidSessionException) {
return new JSONResponse([], 403);
}
return parent::afterException($controller, $methodName, $exception);
}
}