Skip to content

Commit 1e9c62a

Browse files
committed
fix(dav): Try basic auth for ajax WebDAV requests
Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
1 parent a40a13c commit 1e9c62a

File tree

2 files changed

+72
-33
lines changed

2 files changed

+72
-33
lines changed

apps/dav/lib/Connector/Sabre/Auth.php

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -221,18 +221,16 @@ private function auth(RequestInterface $request, ResponseInterface $response): a
221221
}
222222
}
223223

224-
if (!$this->userSession->isLoggedIn() && in_array('XMLHttpRequest', explode(',', $request->getHeader('X-Requested-With') ?? ''))) {
225-
// do not re-authenticate over ajax, use dummy auth name to prevent browser popup
226-
$response->addHeader('WWW-Authenticate', 'DummyBasic realm="' . $this->realm . '"');
227-
$response->setStatus(401);
228-
throw new \Sabre\DAV\Exception\NotAuthenticated('Cannot authenticate over ajax calls');
229-
}
230-
231224
$data = parent::check($request, $response);
232225
if ($data[0] === true) {
233226
$startPos = strrpos($data[1], '/') + 1;
234227
$user = $this->userSession->getUser()->getUID();
235228
$data[1] = substr_replace($data[1], $user, $startPos);
229+
} elseif (in_array('XMLHttpRequest', explode(',', $request->getHeader('X-Requested-With') ?? ''))) {
230+
// For ajax requests use dummy auth name to prevent browser popup in case of invalid creditials
231+
$response->addHeader('WWW-Authenticate', 'DummyBasic realm="' . $this->realm . '"');
232+
$response->setStatus(401);
233+
throw new \Sabre\DAV\Exception\NotAuthenticated('Cannot authenticate over ajax calls');
236234
}
237235
return $data;
238236
}

apps/dav/tests/unit/Connector/Sabre/AuthTest.php

Lines changed: 67 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
use OCP\ISession;
3636
use OCP\IUser;
3737
use OCP\Security\Bruteforce\IThrottler;
38+
use PHPUnit\Framework\MockObject\MockObject;
3839
use Sabre\DAV\Server;
3940
use Sabre\HTTP\RequestInterface;
4041
use Sabre\HTTP\ResponseInterface;
@@ -47,17 +48,17 @@
4748
* @group DB
4849
*/
4950
class AuthTest extends TestCase {
50-
/** @var ISession */
51+
/** @var ISession&MockObject */
5152
private $session;
5253
/** @var \OCA\DAV\Connector\Sabre\Auth */
5354
private $auth;
54-
/** @var Session */
55+
/** @var Session&MockObject */
5556
private $userSession;
56-
/** @var IRequest */
57+
/** @var IRequest&MockObject */
5758
private $request;
58-
/** @var Manager */
59+
/** @var Manager&MockObject */
5960
private $twoFactorManager;
60-
/** @var IThrottler */
61+
/** @var IThrottler&MockObject */
6162
private $throttler;
6263

6364
protected function setUp(): void {
@@ -549,11 +550,11 @@ public function testAuthenticateNoBasicAuthenticateHeadersProvidedWithAjax(): vo
549550
$this->expectException(\Sabre\DAV\Exception\NotAuthenticated::class);
550551
$this->expectExceptionMessage('Cannot authenticate over ajax calls');
551552

552-
/** @var \Sabre\HTTP\RequestInterface $httpRequest */
553+
/** @var \Sabre\HTTP\RequestInterface&MockObject $httpRequest */
553554
$httpRequest = $this->getMockBuilder(RequestInterface::class)
554555
->disableOriginalConstructor()
555556
->getMock();
556-
/** @var \Sabre\HTTP\ResponseInterface $httpResponse */
557+
/** @var \Sabre\HTTP\ResponseInterface&MockObject $httpResponse */
557558
$httpResponse = $this->getMockBuilder(ResponseInterface::class)
558559
->disableOriginalConstructor()
559560
->getMock();
@@ -562,10 +563,59 @@ public function testAuthenticateNoBasicAuthenticateHeadersProvidedWithAjax(): vo
562563
->method('isLoggedIn')
563564
->willReturn(false);
564565
$httpRequest
566+
->expects($this->exactly(2))
567+
->method('getHeader')
568+
->willReturnMap([
569+
['X-Requested-With', 'XMLHttpRequest'],
570+
['Authorization', null],
571+
]);
572+
573+
$this->auth->check($httpRequest, $httpResponse);
574+
}
575+
576+
public function testAuthenticateWithBasicAuthenticateHeadersProvidedWithAjax(): void {
577+
// No CSRF
578+
$this->request
565579
->expects($this->once())
580+
->method('passesCSRFCheck')
581+
->willReturn(false);
582+
583+
/** @var \Sabre\HTTP\RequestInterface&MockObject $httpRequest */
584+
$httpRequest = $this->getMockBuilder(RequestInterface::class)
585+
->disableOriginalConstructor()
586+
->getMock();
587+
/** @var \Sabre\HTTP\ResponseInterface&MockObject $httpResponse */
588+
$httpResponse = $this->getMockBuilder(ResponseInterface::class)
589+
->disableOriginalConstructor()
590+
->getMock();
591+
$httpRequest
592+
->expects($this->any())
566593
->method('getHeader')
567-
->with('X-Requested-With')
568-
->willReturn('XMLHttpRequest');
594+
->willReturnMap([
595+
['X-Requested-With', 'XMLHttpRequest'],
596+
['Authorization', 'basic dXNlcm5hbWU6cGFzc3dvcmQ='],
597+
]);
598+
599+
$user = $this->getMockBuilder(IUser::class)
600+
->disableOriginalConstructor()
601+
->getMock();
602+
$user->expects($this->any())
603+
->method('getUID')
604+
->willReturn('MyDavUser');
605+
$this->userSession
606+
->expects($this->any())
607+
->method('isLoggedIn')
608+
->willReturn(false);
609+
$this->userSession
610+
->expects($this->once())
611+
->method('logClientIn')
612+
->with('username', 'password')
613+
->willReturn(true);
614+
$this->userSession
615+
->expects($this->any())
616+
->method('getUser')
617+
->willReturn($user);
618+
569619
$this->auth->check($httpRequest, $httpResponse);
570620
}
571621

@@ -619,16 +669,11 @@ public function testAuthenticateValidCredentials(): void {
619669
->disableOriginalConstructor()
620670
->getMock();
621671
$server->httpRequest
622-
->expects($this->exactly(2))
672+
->expects($this->once())
623673
->method('getHeader')
624-
->withConsecutive(
625-
['X-Requested-With'],
626-
['Authorization'],
627-
)
628-
->willReturnOnConsecutiveCalls(
629-
null,
630-
'basic dXNlcm5hbWU6cGFzc3dvcmQ=',
631-
);
674+
->with('Authorization')
675+
->willReturn('basic dXNlcm5hbWU6cGFzc3dvcmQ=');
676+
632677
$server->httpResponse = $this->getMockBuilder(ResponseInterface::class)
633678
->disableOriginalConstructor()
634679
->getMock();
@@ -661,14 +706,10 @@ public function testAuthenticateInvalidCredentials(): void {
661706
$server->httpRequest
662707
->expects($this->exactly(2))
663708
->method('getHeader')
664-
->withConsecutive(
665-
['X-Requested-With'],
666-
['Authorization'],
667-
)
668-
->willReturnOnConsecutiveCalls(
669-
null,
670-
'basic dXNlcm5hbWU6cGFzc3dvcmQ=',
671-
);
709+
->willReturnMap([
710+
['Authorization', 'basic dXNlcm5hbWU6cGFzc3dvcmQ='],
711+
['X-Requested-With', null],
712+
]);
672713
$server->httpResponse = $this->getMockBuilder(ResponseInterface::class)
673714
->disableOriginalConstructor()
674715
->getMock();

0 commit comments

Comments
 (0)