From ec7035452674da7061163a3e4b2003db248838f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20Molakvo=C3=A6?= Date: Wed, 1 Apr 2026 14:02:14 +0200 Subject: [PATCH 1/2] fix(dav): adjust password session check on legacy dav auth MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Follow-up https://github.com/nextcloud/server/pull/55955/ Signed-off-by: John Molakvoæ --- apps/dav/lib/Connector/LegacyPublicAuth.php | 53 +++++++++++++++---- .../unit/Connector/LegacyPublicAuthTest.php | 6 +-- 2 files changed, 45 insertions(+), 14 deletions(-) diff --git a/apps/dav/lib/Connector/LegacyPublicAuth.php b/apps/dav/lib/Connector/LegacyPublicAuth.php index aad2313bdf795..20904958c4f0e 100644 --- a/apps/dav/lib/Connector/LegacyPublicAuth.php +++ b/apps/dav/lib/Connector/LegacyPublicAuth.php @@ -17,6 +17,7 @@ use OCP\Share\IManager; use OCP\Share\IShare; use Sabre\DAV\Auth\Backend\AbstractBasic; +use Sabre\DAV\Exception\NotAuthenticated; /** * Class PublicAuth @@ -71,22 +72,29 @@ protected function validateUserPass($username, $password) { if ($share->getShareType() === IShare::TYPE_LINK || $share->getShareType() === IShare::TYPE_EMAIL || $share->getShareType() === IShare::TYPE_CIRCLE) { + // Validate password if provided if ($this->shareManager->checkPassword($share, $password)) { + // If not set, set authenticated session cookie + if (!$this->isShareInSession($share)) { + $this->addShareToSession($share); + } return true; - } elseif ($this->session->exists(PublicAuth::DAV_AUTHENTICATED) - && $this->session->get(PublicAuth::DAV_AUTHENTICATED) === $share->getId()) { + } + + // We are already authenticated for this share in the session + if ($this->isShareInSession($share)) { return true; - } else { - if (in_array('XMLHttpRequest', explode(',', $this->request->getHeader('X-Requested-With')))) { - // do not re-authenticate over ajax, use dummy auth name to prevent browser popup - http_response_code(401); - header('WWW-Authenticate: DummyBasic realm="' . $this->realm . '"'); - throw new \Sabre\DAV\Exception\NotAuthenticated('Cannot authenticate over ajax calls'); - } + } - $this->throttler->registerAttempt(self::BRUTEFORCE_ACTION, $this->request->getRemoteAddress()); - return false; + if (in_array('XMLHttpRequest', explode(',', $this->request->getHeader('X-Requested-With')))) { + // do not re-authenticate over ajax, use dummy auth name to prevent browser popup + http_response_code(401); + header('WWW-Authenticate: DummyBasic realm="' . $this->realm . '"'); + throw new NotAuthenticated('Cannot authenticate over ajax calls'); } + + $this->throttler->registerAttempt(self::BRUTEFORCE_ACTION, $this->request->getRemoteAddress()); + return false; } elseif ($share->getShareType() === IShare::TYPE_REMOTE) { return true; } else { @@ -97,6 +105,29 @@ protected function validateUserPass($username, $password) { return true; } + private function addShareToSession(IShare $share): void { + $allowedShareIds = $this->session->get(PublicAuth::DAV_AUTHENTICATED) ?? []; + if (!is_array($allowedShareIds)) { + $allowedShareIds = []; + } + + $allowedShareIds[] = $share->getId(); + $this->session->set(PublicAuth::DAV_AUTHENTICATED, $allowedShareIds); + } + + private function isShareInSession(IShare $share): bool { + if (!$this->session->exists(PublicAuth::DAV_AUTHENTICATED)) { + return false; + } + + $allowedShareIds = $this->session->get(PublicAuth::DAV_AUTHENTICATED); + if (!is_array($allowedShareIds)) { + return false; + } + + return in_array($share->getId(), $allowedShareIds); + } + public function getShare(): IShare { assert($this->share !== null); return $this->share; diff --git a/apps/dav/tests/unit/Connector/LegacyPublicAuthTest.php b/apps/dav/tests/unit/Connector/LegacyPublicAuthTest.php index 57cd8bc96a27e..88ccc68cc9393 100644 --- a/apps/dav/tests/unit/Connector/LegacyPublicAuthTest.php +++ b/apps/dav/tests/unit/Connector/LegacyPublicAuthTest.php @@ -174,7 +174,7 @@ public function testInvalidSharePasswordLinkValidSession(): void { )->willReturn(false); $this->session->method('exists')->with('public_link_authenticated')->willReturn(true); - $this->session->method('get')->with('public_link_authenticated')->willReturn('42'); + $this->session->method('get')->with('public_link_authenticated')->willReturn(['42']); $result = $this->invokePrivate($this->auth, 'validateUserPass', ['username', 'password']); @@ -199,7 +199,7 @@ public function testSharePasswordLinkInvalidSession(): void { )->willReturn(false); $this->session->method('exists')->with('public_link_authenticated')->willReturn(true); - $this->session->method('get')->with('public_link_authenticated')->willReturn('43'); + $this->session->method('get')->with('public_link_authenticated')->willReturn(['43']); $result = $this->invokePrivate($this->auth, 'validateUserPass', ['username', 'password']); @@ -224,7 +224,7 @@ public function testSharePasswordMailInvalidSession(): void { )->willReturn(false); $this->session->method('exists')->with('public_link_authenticated')->willReturn(true); - $this->session->method('get')->with('public_link_authenticated')->willReturn('43'); + $this->session->method('get')->with('public_link_authenticated')->willReturn(['43']); $result = $this->invokePrivate($this->auth, 'validateUserPass', ['username', 'password']); From de69a88f5104884454e9d8cc372e57d12a002935 Mon Sep 17 00:00:00 2001 From: Ferdinand Thiessen Date: Thu, 20 Aug 2026 14:22:56 +0200 Subject: [PATCH 2/2] chore: use strict `in_array` Co-authored-by: Carl Schwan Signed-off-by: Ferdinand Thiessen --- apps/dav/lib/Connector/LegacyPublicAuth.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/dav/lib/Connector/LegacyPublicAuth.php b/apps/dav/lib/Connector/LegacyPublicAuth.php index 20904958c4f0e..b65ed5d1cf3cd 100644 --- a/apps/dav/lib/Connector/LegacyPublicAuth.php +++ b/apps/dav/lib/Connector/LegacyPublicAuth.php @@ -125,7 +125,7 @@ private function isShareInSession(IShare $share): bool { return false; } - return in_array($share->getId(), $allowedShareIds); + return in_array($share->getId(), $allowedShareIds, true); } public function getShare(): IShare {