Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(session):remove dup queries,refactorcode #5773

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
216 changes: 150 additions & 66 deletions lib/Middleware/SessionMiddleware.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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,
) {
}

Expand All @@ -48,108 +55,185 @@ public function beforeController(Controller $controller, string $methodName): vo
return;
}

$reflectionMethod = new \ReflectionMethod($controller, $methodName);
//ASSERTION
$documentId = $this->getDocumentId();
$this->document = $this->documentService->getDocument($documentId);
Comment on lines +59 to +60
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This always runs - even if non of the Require... attributes were set.

Before we'd only get the document if any of the attributes was there.
Is this intentional?

I don't think it matters in practice as I assume all ISessionAwareControllers require at least one of them. Just want to point out a change in behavior that i am seeing here.


$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();
}
}

/**
* @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);
}
}
}
Loading