From 7d6f286b53e27761b981d95214eb98231df4eec9 Mon Sep 17 00:00:00 2001 From: Louis Chmn Date: Fri, 21 Aug 2026 21:00:49 +0200 Subject: [PATCH 1/2] chore(CurrentUser): Move to php types Signed-off-by: Louis Chmn --- lib/CurrentUser.php | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/lib/CurrentUser.php b/lib/CurrentUser.php index 7b41c088c..3d8c92923 100644 --- a/lib/CurrentUser.php +++ b/lib/CurrentUser.php @@ -18,10 +18,10 @@ class CurrentUser { public function __construct( - protected IUserSession $userSession, - protected IRequest $request, - protected IManager $shareManager, - protected IFactory $l10nFactory, + protected readonly IUserSession $userSession, + protected readonly IRequest $request, + protected readonly IManager $shareManager, + protected readonly IFactory $l10nFactory, ) { } @@ -31,9 +31,8 @@ public function getUser(): ?IUser { /** * Get an identifier for the user, session or token - * @return string */ - public function getUserIdentifier() { + public function getUserIdentifier(): string { $uid = $this->getUID(); if ($uid !== null) { return $uid; @@ -55,9 +54,8 @@ public function getUserIdentifier() { /** * Get the current user id from the session - * @return string|null */ - public function getUID() { + public function getUID(): ?string { $user = $this->userSession->getUser(); if ($user instanceof IUser) { return $user->getUID(); @@ -67,9 +65,8 @@ public function getUID() { /** * Get the current user cloud id from the session - * @return string|null */ - public function getCloudId() { + public function getCloudId(): ?string { $user = $this->userSession->getUser(); if ($user instanceof IUser) { return $user->getCloudId(); @@ -99,7 +96,6 @@ public function isPublicShareToken(): bool { /** * Get the cloud ID from the sharing token - * @return string|null */ protected function getCloudIDFromToken() { /** @psalm-suppress NoInterfaceProperties */ From f95186c14cb89cb8e9f7f367e9ac394b2c398c50 Mon Sep 17 00:00:00 2001 From: Louis Chmn Date: Fri, 21 Aug 2026 22:08:40 +0200 Subject: [PATCH 2/2] fix(CurrentUser): Properly get share token from non-legacy public webdav requests Signed-off-by: Louis Chmn Assisted-by: ClaudeCode:claude-opus-5 Signed-off-by: Louis Chmn --- lib/CurrentUser.php | 73 +++++++++++++++++++++++---------------- tests/CurrentUserTest.php | 8 +++-- 2 files changed, 49 insertions(+), 32 deletions(-) diff --git a/lib/CurrentUser.php b/lib/CurrentUser.php index 3d8c92923..e9fe71f7b 100644 --- a/lib/CurrentUser.php +++ b/lib/CurrentUser.php @@ -79,42 +79,57 @@ public function getCloudId(): ?string { * Check if the current request is via a public share link */ public function isPublicShareToken(): bool { - /** @psalm-suppress NoInterfaceProperties */ - if (!empty($this->request->server['PHP_AUTH_USER'])) { - $token = $this->request->server['PHP_AUTH_USER']; - try { - $share = $this->shareManager->getShareByToken($token); - return $share->getShareType() === IShare::TYPE_LINK - || $share->getShareType() === IShare::TYPE_EMAIL; - } catch (ShareNotFound $e) { - // No share found for this token - } - } - - return false; + return $this->getPublicShare() !== null; } /** * Get the cloud ID from the sharing token */ - protected function getCloudIDFromToken() { + protected function getCloudIDFromToken(): ?string { + $share = $this->getPublicShare(); + + if ($share === null || $share->getShareType() !== IShare::TYPE_REMOTE) { + return null; + } + + return $share->getSharedWith(); + } + + protected function getPublicShare(): ?IShare { + if (basename($this->request->getScriptName()) !== 'public.php') { + return null; + } + + $token = $this->getShareToken(); + if ($token === null) { + return null; + } + + try { + return $this->shareManager->getShareByToken($token); + } catch (ShareNotFound $e) { + return null; + } + } + + protected function getShareToken(): ?string { + // The legacy public endpoint receive the share token in the HTTP basic auth header. /** @psalm-suppress NoInterfaceProperties */ - if (!empty($this->request->server['PHP_AUTH_USER'])) { - $token = $this->request->server['PHP_AUTH_USER']; - /** - * Until https://github.com/nextcloud/server/pull/26681 is merged - * @psalm-suppress InvalidCatch - */ - try { - $share = $this->shareManager->getShareByToken($token); - if ($share->getShareType() === IShare::TYPE_REMOTE) { - return $share->getSharedWith(); - } - } catch (ShareNotFound $e) { - // No share, use the fallback - } + $authUser = (string)($this->request->server['PHP_AUTH_USER'] ?? ''); + if ($authUser !== '') { + return $authUser; } - return null; + // The current public endpoint receives the share token in the path. + // Copied from apps/dav/lib/Connector/Sabre/PublicAuth::getToken() + $path = $this->request->getPathInfo() ?: ''; + // ['', 'dav', 'files', 'token'] + $splittedPath = explode('/', $path); + + if (count($splittedPath) < 4 || $splittedPath[3] === '') { + return null; + } + + return $splittedPath[3]; } } diff --git a/tests/CurrentUserTest.php b/tests/CurrentUserTest.php index d24c29f13..2c7151ea7 100644 --- a/tests/CurrentUserTest.php +++ b/tests/CurrentUserTest.php @@ -57,6 +57,8 @@ protected function setUp(): void { $this->userSession = $this->createMock(IUserSession::class); $this->shareManager = $this->createMock(IManager::class); $this->l10nFactory = $this->createMock(IFactory::class); + + $this->request->method('getScriptName')->willReturn('/public.php'); } protected function getInstance(array $methods = []): CurrentUser|MockObject { @@ -83,13 +85,13 @@ protected function getInstance(array $methods = []): CurrentUser|MockObject { public static function dataGetUserIdentifier(): array { return [ [null, null, null, ''], - [null, 'uid', -1, 'uid'], + [null, 'uid', '-1', 'uid'], [null, null, 'token', 'token'], ]; } #[DataProvider('dataGetUserIdentifier')] - public function testGetUserIdentifier(?string $cachedIdentifier, string|int|null $uidResult, string|int|null $tokenResult, string $expected): void { + public function testGetUserIdentifier(?string $cachedIdentifier, string|int|null $uidResult, ?string $tokenResult, string $expected): void { $instance = $this->getInstance([ 'getUID', 'getCloudIDFromToken', @@ -99,7 +101,7 @@ public function testGetUserIdentifier(?string $cachedIdentifier, string|int|null ->method('getUID') ->willReturn($uidResult); - $instance->expects($tokenResult !== -1 ? $this->once() : $this->never()) + $instance->expects($tokenResult !== '-1' ? $this->once() : $this->never()) ->method('getCloudIDFromToken') ->willReturn($tokenResult);