From ddb1d334d2c345d1b32106a85afd334be35459df 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 8f5f59aa9..362e96d0e 100644 --- a/lib/CurrentUser.php +++ b/lib/CurrentUser.php @@ -25,10 +25,10 @@ class CurrentUser { protected $sessionUser = false; 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, ) { } @@ -38,9 +38,8 @@ public function getUser(): ?IUser { /** * Get an identifier for the user, session or token - * @return string */ - public function getUserIdentifier() { + public function getUserIdentifier(): string { if ($this->identifier !== null) { return $this->identifier; } @@ -70,9 +69,8 @@ public function getUserIdentifier() { /** * Get the current user id from the session - * @return string|null */ - public function getUID() { + public function getUID(): string { if ($this->sessionUser === false) { $user = $this->userSession->getUser(); if ($user instanceof IUser) { @@ -87,9 +85,8 @@ public function getUID() { /** * Get the current user cloud id from the session - * @return string|null */ - public function getCloudId() { + public function getCloudId(): string { if ($this->cloudId === false) { $user = $this->userSession->getUser(); if ($user instanceof IUser) { @@ -123,7 +120,6 @@ public function isPublicShareToken(): bool { /** * Get the cloud ID from the sharing token - * @return string|null */ protected function getCloudIDFromToken() { /** @psalm-suppress NoInterfaceProperties */ From 6dfed5b4d1d411100b72acc88a234e8198914188 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 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 362e96d0e..24a9c211e 100644 --- a/lib/CurrentUser.php +++ b/lib/CurrentUser.php @@ -103,42 +103,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; + } + + // 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 null; + return $splittedPath[3]; } } diff --git a/tests/CurrentUserTest.php b/tests/CurrentUserTest.php index 28a28ce6a..89cf3f92e 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,14 +85,14 @@ 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'], ['cached', -1, -1, 'cached'], ]; } #[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', @@ -102,7 +104,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);