Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 1 addition & 10 deletions apps/dav/appinfo/v1/caldav.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
use OCA\DAV\CalDAV\Validation\CalDavValidatePlugin;
use OCA\DAV\Connector\LegacyDAVACL;
use OCA\DAV\Connector\Sabre\Auth;
use OCA\DAV\Connector\Sabre\BearerAuth;
use OCA\DAV\Connector\Sabre\ExceptionLoggerPlugin;
use OCA\DAV\Connector\Sabre\MaintenancePlugin;
use OCA\DAV\Connector\Sabre\Principal;
Expand Down Expand Up @@ -48,12 +47,6 @@
Server::get(IThrottler::class),
'principals/'
);
$bearerAuthBackend = new BearerAuth(
Server::get(IUserSession::class),
Server::get(ISession::class),
Server::get(IRequest::class),
Server::get(IConfig::class),
);
$principalBackend = new Principal(
Server::get(IUserManager::class),
Server::get(IGroupManager::class),
Expand Down Expand Up @@ -115,9 +108,7 @@

// Add plugins
$server->addPlugin(new MaintenancePlugin(Server::get(IConfig::class), $davL10n));
$authPlugin = new \Sabre\DAV\Auth\Plugin($authBackend);
$authPlugin->addBackend($bearerAuthBackend);
$server->addPlugin($authPlugin);
$server->addPlugin(new \Sabre\DAV\Auth\Plugin($authBackend));
$server->addPlugin(new \Sabre\CalDAV\Plugin());

$server->addPlugin(new LegacyDAVACL());
Expand Down
11 changes: 1 addition & 10 deletions apps/dav/appinfo/v1/carddav.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
use OCA\DAV\CardDAV\Validation\CardDavValidatePlugin;
use OCA\DAV\Connector\LegacyDAVACL;
use OCA\DAV\Connector\Sabre\Auth;
use OCA\DAV\Connector\Sabre\BearerAuth;
use OCA\DAV\Connector\Sabre\ExceptionLoggerPlugin;
use OCA\DAV\Connector\Sabre\MaintenancePlugin;
use OCA\DAV\Connector\Sabre\Principal;
Expand Down Expand Up @@ -45,12 +44,6 @@
Server::get(IThrottler::class),
'principals/'
);
$bearerAuthBackend = new BearerAuth(
Server::get(IUserSession::class),
Server::get(ISession::class),
Server::get(IRequest::class),
Server::get(IConfig::class),
);
$principalBackend = new Principal(
Server::get(IUserManager::class),
Server::get(IGroupManager::class),
Expand Down Expand Up @@ -97,9 +90,7 @@
$server->setBaseUri($baseuri);
// Add plugins
$server->addPlugin(new MaintenancePlugin(Server::get(IConfig::class), Server::get(IL10nFactory::class)->get('dav')));
$authPlugin = new \Sabre\DAV\Auth\Plugin($authBackend);
$authPlugin->addBackend($bearerAuthBackend);
$server->addPlugin($authPlugin);
$server->addPlugin(new \Sabre\DAV\Auth\Plugin($authBackend));
$server->addPlugin(new Plugin());

$server->addPlugin(new LegacyDAVACL());
Expand Down
1 change: 1 addition & 0 deletions apps/dav/appinfo/v1/publicwebdav.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
Server::get(ISession::class),
Server::get(IRequest::class),
Server::get(IConfig::class),
allowOcmAccessToken: true,
);
$authPlugin = new \Sabre\DAV\Auth\Plugin($authBackend);
$authPlugin->addBackend($bearerAuthBackend);
Expand Down
1 change: 1 addition & 0 deletions apps/dav/appinfo/v2/publicremote.php
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@
$session,
$request,
Server::get(IConfig::class),
allowOcmAccessToken: true,
);
$authPlugin = new \Sabre\DAV\Auth\Plugin($authBackend);
$authPlugin->addBackend($bearerAuthBackend);
Expand Down
3 changes: 2 additions & 1 deletion apps/dav/lib/Connector/Sabre/BearerAuth.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ public function __construct(
private IConfig $config,
private string $principalPrefix = 'principals/users/',
private string $token = '',
private bool $allowOcmAccessToken = false,
) {
// setup realm
$defaults = new Defaults();
Expand Down Expand Up @@ -57,7 +58,7 @@ public function validateBearerToken($bearerToken) {
\OC_User::setIncognitoMode(false);

if (!$this->userSession->isLoggedIn()) {
$this->userSession->tryTokenLogin($this->request);
$this->userSession->tryTokenLogin($this->request, $this->allowOcmAccessToken);
}
if ($this->userSession->isLoggedIn()) {
return $this->setupUserFs($this->userSession->getUser()->getUID());
Expand Down
34 changes: 34 additions & 0 deletions apps/dav/tests/unit/Connector/Sabre/BearerAuthTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,40 @@ public function testValidateBearerToken(): void {
$this->assertSame('principals/users/admin', $this->bearerAuth->validateBearerToken('Token'));
}

public function testValidateBearerTokenDefaultsOcmFlagToFalse(): void {
$this->userSession
->method('isLoggedIn')
->willReturnOnConsecutiveCalls(false, false);
$this->userSession
->expects($this->once())
->method('tryTokenLogin')
->with($this->request, false);

$this->assertFalse($this->bearerAuth->validateBearerToken('Token'));
}

public function testValidateBearerTokenPassesOcmFlagWhenAllowed(): void {
$bearerAuth = new BearerAuth(
$this->userSession,
$this->session,
$this->request,
$this->config,
allowOcmAccessToken: true,
);
$this->userSession
->method('isLoggedIn')
->willReturnOnConsecutiveCalls(false, true);
$this->userSession
->expects($this->once())
->method('tryTokenLogin')
->with($this->request, true);
$user = $this->createMock(IUser::class);
$user->method('getUID')->willReturn('admin');
$this->userSession->method('getUser')->willReturn($user);

$this->assertSame('principals/users/admin', $bearerAuth->validateBearerToken('Token'));
}

public function testChallenge(): void {
/** @var RequestInterface&MockObject $request */
$request = $this->createMock(RequestInterface::class);
Expand Down
7 changes: 5 additions & 2 deletions lib/private/User/Session.php
Original file line number Diff line number Diff line change
Expand Up @@ -816,10 +816,13 @@ private function validateTokenLoginName(?string $loginName, IToken $token): bool
* Tries to login the user with auth token header
*
* @param IRequest $request
* @param bool $allowOcmAccessToken Whether an OCM access token may log in
* from a Bearer header. Only the masked
* public share endpoints may set this.
* @todo check remember me cookie
* @return boolean
*/
public function tryTokenLogin(IRequest $request) {
public function tryTokenLogin(IRequest $request, bool $allowOcmAccessToken = false) {
$authHeader = $request->getHeader('Authorization');
$tokenFromCookie = false;
if (str_starts_with($authHeader, 'Bearer ')) {
Expand Down Expand Up @@ -847,7 +850,7 @@ public function tryTokenLogin(IRequest $request) {
if ($dbToken instanceof PublicKeyToken
&& $dbToken->getType() === IToken::TEMPORARY_TOKEN
&& !$tokenFromCookie
&& $dbToken->getName() !== IToken::OCM_ACCESS_TOKEN_NAME) {
&& !($allowOcmAccessToken && $dbToken->getName() === IToken::OCM_ACCESS_TOKEN_NAME)) {
// Session token but from Bearer header, not allowed
return false;
}
Expand Down
53 changes: 53 additions & 0 deletions tests/lib/User/SessionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -662,6 +662,59 @@ public function testTryTokenLoginNotAnAppPassword(): void {
self::assertTrue($loginResult);
}

public function testTryTokenLoginOcmAccessTokenRejectedFromBearerByDefault(): void {
$request = $this->createMock(IRequest::class);
$request->method('getHeader')->with('Authorization')->willReturn('Bearer ocm-access-token');
$dbToken = new PublicKeyToken();
$dbToken->setId(42);
$dbToken->setUid('alice');
$dbToken->setLoginName('alice');
$dbToken->setLastCheck(0);
$dbToken->setType(IToken::TEMPORARY_TOKEN);
$dbToken->setName(IToken::OCM_ACCESS_TOKEN_NAME);
$this->tokenProvider->expects(self::once())
->method('getToken')
->with('ocm-access-token')
->willReturn($dbToken);
// The guard must reject before any login is attempted.
$this->manager->expects(self::never())
->method('get');

$loginResult = $this->userSession->tryTokenLogin($request);

self::assertFalse($loginResult);
}

public function testTryTokenLoginOcmAccessTokenAllowedFromBearerWhenPermitted(): void {
$request = $this->createMock(IRequest::class);
$request->method('getHeader')->with('Authorization')->willReturn('Bearer ocm-access-token');
$dbToken = new PublicKeyToken();
$dbToken->setId(42);
$dbToken->setUid('alice');
$dbToken->setLoginName('alice');
$dbToken->setLastCheck(0);
$dbToken->setType(IToken::TEMPORARY_TOKEN);
$dbToken->setName(IToken::OCM_ACCESS_TOKEN_NAME);
$this->tokenProvider->method('getToken')
->with('ocm-access-token')
->willReturn($dbToken);
$this->session->method('set')
->willReturnCallback(function ($key, $value): void {
if ($key === 'app_password') {
throw new ExpectationFailedException('app_password should not be set in session');
}
});
$user = $this->createMock(IUser::class);
$user->method('isEnabled')->willReturn(true);
$this->manager->method('get')
->with('alice')
->willReturn($user);

$loginResult = $this->userSession->tryTokenLogin($request, true);

self::assertTrue($loginResult);
}

public function testRememberLoginValidToken(): void {
$session = $this->createMock(Memory::class);
$managerMethods = get_class_methods(Manager::class);
Expand Down
Loading