Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
b40b61d
feat: Bearer auth aware Sabre HTTP client
enriquepablo Dec 23, 2025
cb6c875
feat(dav): Add token endpoint to exchange refresh tokens for access t…
enriquepablo Dec 23, 2025
a51e339
feat(dav): Add Bearer auth backend for webdav requests
enriquepablo Dec 23, 2025
0a3285a
feat(dav): New method doTryTokenLogin to allow to try token login wit…
enriquepablo Dec 23, 2025
2354443
feat(federatedfilesharing): Create permanent refresh token when creat…
enriquepablo Dec 23, 2025
9cf1857
feat(federatedfilesharing): When a remote requests a share with a tok…
enriquepablo Dec 23, 2025
802e234
feat(files_sharing): When requesting a remote share with bearer auth,…
enriquepablo Dec 23, 2025
d9f1399
feat: adapt to guzzle api
enriquepablo Dec 23, 2025
f8480fb
feat(cloud_federation_api): adapt to new format for share creation
enriquepablo Dec 23, 2025
d017242
feat(cloud_federation_api): support multi protocol for share creation
enriquepablo Jan 8, 2026
e8365f7
fix(dav): data sent to token endpoint must be application/x-www-form-…
enriquepablo Jan 8, 2026
bfd6795
fix(dav): when receiving a share, account for the must-exchange-token…
enriquepablo Jan 9, 2026
ae6474a
fix(federatedfilesharing): POSTs to token endpoint should be signed
enriquepablo Jan 12, 2026
dddba03
fix(federatedfilesharing): POSTs to token endpoint MUST be signed
enriquepablo Jan 12, 2026
cefa317
fix: federated share provider tests
enriquepablo Jan 8, 2026
0dc6299
fix: share manager test
enriquepablo Jan 8, 2026
c485e0f
fix(federatedfilesharing): fix federated share provider tests
enriquepablo Jan 19, 2026
79c18dd
fix(federatedfilesharing): fixing federated share provider tests
enriquepablo Jan 19, 2026
b580689
fix(federatedfilesharing): fixing federated share provider tests
enriquepablo Jan 19, 2026
69372bb
fix: fixing code style
enriquepablo Jan 19, 2026
561ce99
fix: fixing openapi specs
enriquepablo Jan 19, 2026
c68b0b2
fix: fix psalm issues
enriquepablo Jan 19, 2026
325e24a
fix: reorder import
enriquepablo Jan 19, 2026
f4db4ac
fix(dav): do not import from NCU ns
enriquepablo Jan 19, 2026
084877a
fix: fix sqlite integration tests
enriquepablo Jan 23, 2026
83cc971
fix(federatedfilesharing): order of imports
enriquepablo Jan 23, 2026
d113724
fix: fix session tests using Session::loginWithToken
enriquepablo Jan 23, 2026
de8667c
fix: fix public key token provider test
enriquepablo Jan 23, 2026
06b1cd5
fix: Fixed undefined $request variable
enriquepablo Jan 26, 2026
26daea8
fix(files_external): Added missing doTryTokenLogin() method to implem…
enriquepablo Jan 26, 2026
4125749
fix: Fixed parent::getType() to use ->getter('type') to avoid Psalm m…
enriquepablo Jan 26, 2026
cdb2928
fix: Added getTokenEndPoint() and setTokenEndPoint() methods that sho…
enriquepablo Jan 26, 2026
67a4897
fix: fix session tests
enriquepablo Jan 28, 2026
877e02e
fix(federatedfilesharing): remove unused import
enriquepablo Jan 29, 2026
f1bafec
fix: fix user session tests
enriquepablo Jan 29, 2026
6087b34
fix: update 3rdparty submodule
enriquepablo Jan 29, 2026
a9a10a1
test: test token controller
enriquepablo Jan 29, 2026
79390a1
test: test doTryTokenLogin method
enriquepablo Jan 29, 2026
2cf4f9c
fix(dav): remove unused import in TokenController test
enriquepablo Feb 2, 2026
736b3ff
feat(dav): refresh expired tokens
enriquepablo Feb 2, 2026
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
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

namespace OCA\CloudFederationAPI\Controller;

use OC\Authentication\Token\PublicKeyTokenProvider;
use OC\OCM\OCMSignatoryManager;
use OCA\CloudFederationAPI\Config;
use OCA\CloudFederationAPI\Db\FederatedInviteMapper;
Expand Down Expand Up @@ -43,6 +44,7 @@
use OCP\Security\Signature\Exceptions\SignatoryNotFoundException;
use OCP\Security\Signature\IIncomingSignedRequest;
use OCP\Security\Signature\ISignatureManager;
use OCP\Server;
use OCP\Share\Exceptions\ShareNotFound;
use OCP\Util;
use Psr\Log\LoggerInterface;
Expand Down Expand Up @@ -91,7 +93,7 @@ public function __construct(
* @param string|null $ownerDisplayName Display name of the user who shared the item
* @param string|null $sharedBy Provider specific UID of the user who shared the resource
* @param string|null $sharedByDisplayName Display name of the user who shared the resource
* @param array{name: list<string>, options: array<string, mixed>} $protocol e,.g. ['name' => 'webdav', 'options' => ['username' => 'john', 'permissions' => 31]]
* @param array{name: string, options?: array<string, mixed>, webdav?: array<string, mixed>} $protocol Old format: ['name' => 'webdav', 'options' => ['sharedSecret' => '...', 'permissions' => '...']] or New format: ['name' => 'webdav', 'webdav' => ['uri' => '...', 'sharedSecret' => '...', 'permissions' => [...]]] or Multi format: ['name' => 'multi', 'webdav' => [...]]
* @param string $shareType 'group' or 'user' share
* @param string $resourceType 'file', 'calendar',...
*
Expand Down Expand Up @@ -126,9 +128,6 @@ public function addShare($shareWith, $name, $description, $providerId, $owner, $
|| $shareType === null
|| !is_array($protocol)
|| !isset($protocol['name'])
|| !isset($protocol['options'])
|| !is_array($protocol['options'])
|| !isset($protocol['options']['sharedSecret'])
) {
return new JSONResponse(
[
Expand All @@ -139,6 +138,33 @@ public function addShare($shareWith, $name, $description, $providerId, $owner, $
);
}

$protocolName = $protocol['name'];
$hasOldFormat = isset($protocol['options']) && is_array($protocol['options']) && isset($protocol['options']['sharedSecret']);
$hasNewFormat = isset($protocol[$protocolName]) && is_array($protocol[$protocolName]) && isset($protocol[$protocolName]['sharedSecret']);

// For multi-protocol, we only consider webdav
$hasMultiFormat = false;
if ($protocolName === 'multi') {
if (isset($protocol['webdav']) && is_array($protocol['webdav']) && isset($protocol['webdav']['sharedSecret'])) {
$hasMultiFormat = true;
$protocol = [
'name' => 'webdav',
'webdav' => $protocol['webdav']
];
$protocolName = 'webdav';
}
}

if (!$hasOldFormat && !$hasNewFormat && !$hasMultiFormat) {
return new JSONResponse(
[
'message' => 'Missing sharedSecret in protocol',
'validationErrors' => [],
],
Http::STATUS_BAD_REQUEST
);
}

$supportedShareTypes = $this->config->getSupportedShareTypes($resourceType);
if (!in_array($shareType, $supportedShareTypes)) {
return new JSONResponse(
Expand Down Expand Up @@ -490,6 +516,12 @@ private function confirmNotificationIdentity(
$provider = $this->cloudFederationProviderManager->getCloudFederationProvider($resourceType);
if ($provider instanceof ISignedCloudFederationProvider) {
$identity = $provider->getFederationIdFromSharedSecret($sharedSecret, $notification);
if ($identity === '') {
$tokenProvider = Server::get(PublicKeyTokenProvider::class);
$accessTokenDb = $tokenProvider->getToken($sharedSecret);
$refreshToken = $accessTokenDb->getUID();
$identity = $provider->getFederationIdFromSharedSecret($refreshToken, $notification);
}
} else {
$this->logger->debug('cloud federation provider {provider} does not implements ISignedCloudFederationProvider', ['provider' => $provider::class]);
return;
Expand Down
16 changes: 9 additions & 7 deletions apps/cloud_federation_api/openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -161,23 +161,25 @@
},
"protocol": {
"type": "object",
"description": "e,.g. ['name' => 'webdav', 'options' => ['username' => 'john', 'permissions' => 31]]",
"description": "Old format: ['name' => 'webdav', 'options' => ['sharedSecret' => '...', 'permissions' => '...']] or New format: ['name' => 'webdav', 'webdav' => ['uri' => '...', 'sharedSecret' => '...', 'permissions' => [...]]] or Multi format: ['name' => 'multi', 'webdav' => [...]]",
"required": [
"name",
"options"
"name"
],
"properties": {
"name": {
"type": "array",
"items": {
"type": "string"
}
"type": "string"
},
"options": {
"type": "object",
"additionalProperties": {
"type": "object"
}
},
"webdav": {
"type": "object",
"additionalProperties": {
"type": "object"
}
}
}
},
Expand Down
16 changes: 14 additions & 2 deletions apps/dav/appinfo/v1/publicwebdav.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use OC\Files\Storage\Wrapper\PermissionsMask;
use OC\Files\View;
use OCA\DAV\Connector\LegacyPublicAuth;
use OCA\DAV\Connector\Sabre\BearerAuth;
use OCA\DAV\Connector\Sabre\ServerFactory;
use OCA\DAV\Files\Sharing\FilesDropPlugin;
use OCA\DAV\Files\Sharing\PublicLinkCheckPlugin;
Expand Down Expand Up @@ -49,7 +50,14 @@
Server::get(ISession::class),
Server::get(IThrottler::class)
);
$bearerAuthBackend = new BearerAuth(
Server::get(IUserSession::class),
Server::get(ISession::class),
Server::get(IRequest::class),
Server::get(IConfig::class),
);
$authPlugin = new \Sabre\DAV\Auth\Plugin($authBackend);
$authPlugin->addBackend($bearerAuthBackend);

/** @var IEventDispatcher $eventDispatcher */
$eventDispatcher = Server::get(IEventDispatcher::class);
Expand Down Expand Up @@ -80,6 +88,7 @@
$authPlugin,
function (\Sabre\DAV\Server $server) use (
$authBackend,
$bearerAuthBackend,
$linkCheckPlugin,
$filesDropPlugin
) {
Expand All @@ -90,8 +99,11 @@ function (\Sabre\DAV\Server $server) use (
// this is what is thrown when trying to access a non-existing share
throw new \Sabre\DAV\Exception\NotAuthenticated();
}

$share = $authBackend->getShare();
try {
$share = $authBackend->getShare();
} catch (AssertionError $e) {
$share = $bearerAuthBackend->getShare();
}
$owner = $share->getShareOwner();
$isReadable = $share->getPermissions() & Constants::PERMISSION_READ;
$fileId = $share->getNodeId();
Expand Down
1 change: 1 addition & 0 deletions apps/dav/composer/composer/autoload_classmap.php
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,7 @@
'OCA\\DAV\\Controller\\ExampleContentController' => $baseDir . '/../lib/Controller/ExampleContentController.php',
'OCA\\DAV\\Controller\\InvitationResponseController' => $baseDir . '/../lib/Controller/InvitationResponseController.php',
'OCA\\DAV\\Controller\\OutOfOfficeController' => $baseDir . '/../lib/Controller/OutOfOfficeController.php',
'OCA\\DAV\\Controller\\TokenController' => $baseDir . '/../lib/Controller/TokenController.php',
'OCA\\DAV\\Controller\\UpcomingEventsController' => $baseDir . '/../lib/Controller/UpcomingEventsController.php',
'OCA\\DAV\\DAV\\CustomPropertiesBackend' => $baseDir . '/../lib/DAV/CustomPropertiesBackend.php',
'OCA\\DAV\\DAV\\GroupPrincipalBackend' => $baseDir . '/../lib/DAV/GroupPrincipalBackend.php',
Expand Down
1 change: 1 addition & 0 deletions apps/dav/composer/composer/autoload_static.php
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,7 @@ class ComposerStaticInitDAV
'OCA\\DAV\\Controller\\ExampleContentController' => __DIR__ . '/..' . '/../lib/Controller/ExampleContentController.php',
'OCA\\DAV\\Controller\\InvitationResponseController' => __DIR__ . '/..' . '/../lib/Controller/InvitationResponseController.php',
'OCA\\DAV\\Controller\\OutOfOfficeController' => __DIR__ . '/..' . '/../lib/Controller/OutOfOfficeController.php',
'OCA\\DAV\\Controller\\TokenController' => __DIR__ . '/..' . '/../lib/Controller/TokenController.php',
'OCA\\DAV\\Controller\\UpcomingEventsController' => __DIR__ . '/..' . '/../lib/Controller/UpcomingEventsController.php',
'OCA\\DAV\\DAV\\CustomPropertiesBackend' => __DIR__ . '/..' . '/../lib/DAV/CustomPropertiesBackend.php',
'OCA\\DAV\\DAV\\GroupPrincipalBackend' => __DIR__ . '/..' . '/../lib/DAV/GroupPrincipalBackend.php',
Expand Down
21 changes: 19 additions & 2 deletions apps/dav/lib/Connector/Sabre/BearerAuth.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@
use OCP\IRequest;
use OCP\ISession;
use OCP\IUserSession;
use OCP\Server;
use OCP\Share\IManager;
use OCP\Share\IShare;
use Sabre\DAV\Auth\Backend\AbstractBearer;
use Sabre\HTTP\RequestInterface;
use Sabre\HTTP\ResponseInterface;
Expand All @@ -23,6 +26,7 @@ public function __construct(
private IRequest $request,
private IConfig $config,
private string $principalPrefix = 'principals/users/',
private string $token = '',
) {
// setup realm
$defaults = new Defaults();
Expand All @@ -40,17 +44,30 @@ private function setupUserFs($userId) {
*/
public function validateBearerToken($bearerToken) {
\OC_Util::setupFS();
$this->token = $bearerToken;

if (!$this->userSession->isLoggedIn()) {
$loggedIn = $this->userSession->isLoggedIn();
if (!$loggedIn) {
$this->userSession->tryTokenLogin($this->request);
$loggedIn = $this->userSession->isLoggedIn();
}
if ($this->userSession->isLoggedIn()) {
if (!$loggedIn) {
$this->userSession->doTryTokenLogin($bearerToken);
$loggedIn = $this->userSession->isLoggedIn();
}
if ($loggedIn) {
return $this->setupUserFs($this->userSession->getUser()->getUID());
}

return false;
}

public function getShare(): IShare {
$shareManager = Server::get(IManager::class);
$share = $shareManager->getShareByToken($this->token);
return $share;
}

/**
* \Sabre\DAV\Auth\Backend\AbstractBearer::challenge sets an WWW-Authenticate
* header which some DAV clients can't handle. Thus we override this function
Expand Down
Loading