From dd32764efec19b136747edc29ea06ad691cf4b3b Mon Sep 17 00:00:00 2001 From: root Date: Mon, 9 Mar 2026 12:14:05 +0000 Subject: [PATCH] fix(dav): return 401 instead of 503 for disabled user login attempts When a disabled user's device tries to sync via WebDAV, a LoginException was caught by the generic Exception handler in Auth::check(), resulting in a 503 ServiceUnavailable response logged at error level. This caused constant fatal errors in logs for disabled users whose devices continue to attempt sync. Catch LoginException specifically and return 401 NotAuthenticated instead, logging at info level since this is expected behavior. Fixes #22758 --- apps/dav/lib/Connector/Sabre/Auth.php | 4 +++ .../tests/unit/Connector/Sabre/AuthTest.php | 30 +++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/apps/dav/lib/Connector/Sabre/Auth.php b/apps/dav/lib/Connector/Sabre/Auth.php index 5ef33958c9b0a..64f83d2ed991c 100644 --- a/apps/dav/lib/Connector/Sabre/Auth.php +++ b/apps/dav/lib/Connector/Sabre/Auth.php @@ -10,6 +10,7 @@ use Exception; use OC\Authentication\Exceptions\PasswordLoginForbiddenException; use OC\Authentication\TwoFactorAuth\Manager; +use OC\User\LoginException; use OC\User\Session; use OCA\DAV\Connector\Sabre\Exception\PasswordLoginForbidden; use OCA\DAV\Connector\Sabre\Exception\TooManyRequests; @@ -109,6 +110,9 @@ public function check(RequestInterface $request, ResponseInterface $response) { return $this->auth($request, $response); } catch (NotAuthenticated $e) { throw $e; + } catch (LoginException $e) { + Server::get(LoggerInterface::class)->info($e->getMessage(), ['exception' => $e]); + throw new NotAuthenticated($e->getMessage(), 0, $e); } catch (Exception $e) { $class = get_class($e); $msg = $e->getMessage(); diff --git a/apps/dav/tests/unit/Connector/Sabre/AuthTest.php b/apps/dav/tests/unit/Connector/Sabre/AuthTest.php index e05c46705652c..5e81fde9bad56 100644 --- a/apps/dav/tests/unit/Connector/Sabre/AuthTest.php +++ b/apps/dav/tests/unit/Connector/Sabre/AuthTest.php @@ -10,6 +10,7 @@ use OC\Authentication\Exceptions\PasswordLoginForbiddenException; use OC\Authentication\TwoFactorAuth\Manager; +use OC\User\LoginException; use OC\User\Session; use OCA\DAV\Connector\Sabre\Auth; use OCA\DAV\Connector\Sabre\Exception\PasswordLoginForbidden; @@ -586,6 +587,35 @@ public function testAuthenticateValidCredentials(): void { $this->assertEquals([true, 'principals/users/MyTestUser'], $response); } + public function testCheckWithLoginExceptionThrowsNotAuthenticated(): void { + $this->expectException(\Sabre\DAV\Exception\NotAuthenticated::class); + $this->expectExceptionMessage('User disabled'); + + $httpRequest = $this->createMock(RequestInterface::class); + $httpResponse = $this->createMock(ResponseInterface::class); + $this->userSession + ->expects($this->any()) + ->method('isLoggedIn') + ->willReturn(false); + $httpRequest + ->expects($this->any()) + ->method('getHeader') + ->willReturnMap([ + ['Authorization', 'basic dXNlcm5hbWU6cGFzc3dvcmQ='], + ['X-Requested-With', null], + ]); + $this->userSession + ->expects($this->once()) + ->method('logClientIn') + ->with('username', 'password') + ->willThrowException(new LoginException('User disabled')); + $this->session + ->expects($this->once()) + ->method('close'); + + $this->auth->check($httpRequest, $httpResponse); + } + public function testAuthenticateInvalidCredentials(): void { $server = $this->createMock(Server::class); $server->httpRequest = $this->createMock(Request::class);