Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
65 commits
Select commit Hold shift + click to select a range
fc081cd
feat: Bearer auth aware Sabre HTTP client
enriquepablo Dec 23, 2025
ecd40d1
feat(dav): Add token endpoint to exchange refresh tokens for access t…
enriquepablo Dec 23, 2025
65056d2
feat(dav): Add Bearer auth backend for webdav requests
enriquepablo Dec 23, 2025
159f94f
feat(dav): New method doTryTokenLogin to allow to try token login wit…
enriquepablo Dec 23, 2025
baae8fa
feat(federatedfilesharing): Create permanent refresh token when creat…
enriquepablo Dec 23, 2025
765a027
feat(federatedfilesharing): When a remote requests a share with a tok…
enriquepablo Dec 23, 2025
c6b625d
feat(files_sharing): When requesting a remote share with bearer auth,…
enriquepablo Dec 23, 2025
375832b
feat: adapt to guzzle api
enriquepablo Dec 23, 2025
38c307a
feat(cloud_federation_api): adapt to new format for share creation
enriquepablo Dec 23, 2025
4c98890
feat(cloud_federation_api): support multi protocol for share creation
enriquepablo Jan 8, 2026
4db9829
fix(dav): data sent to token endpoint must be application/x-www-form-…
enriquepablo Jan 8, 2026
294dbe2
fix(dav): when receiving a share, account for the must-exchange-token…
enriquepablo Jan 9, 2026
695ff45
fix(federatedfilesharing): POSTs to token endpoint should be signed
enriquepablo Jan 12, 2026
bf696d5
fix(federatedfilesharing): POSTs to token endpoint MUST be signed
enriquepablo Jan 12, 2026
315028a
fix: federated share provider tests
enriquepablo Jan 8, 2026
717d12f
fix: share manager test
enriquepablo Jan 8, 2026
15f04af
fix(federatedfilesharing): fix federated share provider tests
enriquepablo Jan 19, 2026
76b688e
fix(federatedfilesharing): fixing federated share provider tests
enriquepablo Jan 19, 2026
74e4635
fix(federatedfilesharing): fixing federated share provider tests
enriquepablo Jan 19, 2026
12352f3
fix: fixing code style
enriquepablo Jan 19, 2026
2bbb103
fix: fixing openapi specs
enriquepablo Jan 19, 2026
5b78b96
fix: fix psalm issues
enriquepablo Jan 19, 2026
71e3bdc
fix: reorder import
enriquepablo Jan 19, 2026
9fa2531
fix(dav): do not import from NCU ns
enriquepablo Jan 19, 2026
2f85002
fix: fix sqlite integration tests
enriquepablo Jan 23, 2026
c133e0c
fix(federatedfilesharing): order of imports
enriquepablo Jan 23, 2026
bbd55a1
fix: fix session tests using Session::loginWithToken
enriquepablo Jan 23, 2026
41dfd41
fix: fix public key token provider test
enriquepablo Jan 23, 2026
d72631e
fix: Fixed undefined $request variable
enriquepablo Jan 26, 2026
a9b90a0
fix(files_external): Added missing doTryTokenLogin() method to implem…
enriquepablo Jan 26, 2026
941672d
fix: Fixed parent::getType() to use ->getter('type') to avoid Psalm m…
enriquepablo Jan 26, 2026
a6f47de
fix: Added getTokenEndPoint() and setTokenEndPoint() methods that sho…
enriquepablo Jan 26, 2026
3abada0
fix: fix session tests
enriquepablo Jan 28, 2026
8323328
fix(federatedfilesharing): remove unused import
enriquepablo Jan 29, 2026
f871549
fix: fix user session tests
enriquepablo Jan 29, 2026
c95bc44
test: test token controller
enriquepablo Jan 29, 2026
77e731d
test: test doTryTokenLogin method
enriquepablo Jan 29, 2026
7d2918e
fix(dav): remove unused import in TokenController test
enriquepablo Feb 2, 2026
81aab58
feat(dav): refresh expired tokens
enriquepablo Feb 2, 2026
7e3e4cb
fix(files_sharing): refactor refreshing access tokens
enriquepablo Feb 4, 2026
fb975d1
fix(dav): keep refresh tokens in its own db table
enriquepablo Mar 6, 2026
becb5a7
fix(dav): validate token exchange response before using it
enriquepablo Mar 6, 2026
7718cb5
fix(dav): keep refresh tokens in its own db table, add migration for …
enriquepablo Mar 6, 2026
707ce82
fix(files_sharing): keep access tokens in its own field in the extern…
enriquepablo Mar 6, 2026
88cd0df
fix(files_sharing): prevent concurrent requests to refresh token inde…
enriquepablo Mar 6, 2026
5bb9c8d
fix(dav): cleanup expired access tokens
enriquepablo Mar 6, 2026
92c2242
fix(files_sharing): prevent infinite loop trying unsuccessfully to re…
enriquepablo Mar 6, 2026
4aabcda
fix(files_sharing): missing autoloads
enriquepablo Mar 10, 2026
9713750
test(dav): test bearer token login
enriquepablo Mar 20, 2026
442b04e
test(dav): test cleanup of expired access tokens
enriquepablo Mar 20, 2026
533051c
test(federatedfilesharing): test federated shares
enriquepablo Mar 20, 2026
0c2a3b7
test(files_sharing): test access tokens
enriquepablo Mar 20, 2026
cbe9010
fix(files_sharing): correct access level for appConfig
enriquepablo Mar 20, 2026
6202297
fix: backwards compatibility for shares from instances before upgrading
enriquepablo Mar 25, 2026
35d398f
fix: backwards compatibility for shares for instances before upgrading
enriquepablo Mar 25, 2026
4218026
fix: backwards compatibility for shares for instances before upgrading
enriquepablo Mar 26, 2026
d551782
fix: re-order imports
enriquepablo Mar 26, 2026
9375592
fix: avoid changing the IUserSession interface
enriquepablo Mar 26, 2026
11284cb
fix[dav]: composer autoload
enriquepablo Apr 20, 2026
2ec36ba
fix[federatedfilesharing]: replace deprecated import
enriquepablo Apr 20, 2026
0695770
chore: bump 3rdparty and update psalm baseline
mickenordin Apr 24, 2026
8d7d031
fix(dav): respect storage scheme when discovering token endpoint
mickenordin Apr 26, 2026
7412b7d
fix(auth): allow OCM access tokens via Bearer header
mickenordin Apr 26, 2026
b96e786
chore: add missing `Override` attributes flagged by psalm
mickenordin May 4, 2026
405de22
fix(dav): regenerate composer autoloader for new migration
mickenordin May 4, 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,11 +7,13 @@

namespace OCA\CloudFederationAPI\Controller;

use OC\Authentication\Token\PublicKeyTokenProvider;
use OC\OCM\OCMSignatoryManager;
use OCA\CloudFederationAPI\Config;
use OCA\CloudFederationAPI\Db\FederatedInviteMapper;
use OCA\CloudFederationAPI\Events\FederatedInviteAcceptedEvent;
use OCA\CloudFederationAPI\ResponseDefinitions;
use OCA\DAV\Db\OcmTokenMapMapper;
use OCA\FederatedFileSharing\AddressHandler;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Db\DoesNotExistException;
Expand Down Expand Up @@ -43,6 +45,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 +94,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 +129,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 +139,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 All @@ -148,6 +175,7 @@ public function addShare($shareWith, $name, $description, $providerId, $owner, $
}

$cloudId = $this->cloudIdManager->resolveCloudId($shareWith);
$shareWithCloudId = $shareWith; // preserve full cloud ID for factory capability discovery
$shareWith = $cloudId->getUser();

if ($shareType === 'user') {
Expand Down Expand Up @@ -192,7 +220,10 @@ public function addShare($shareWith, $name, $description, $providerId, $owner, $

try {
$provider = $this->cloudFederationProviderManager->getCloudFederationProvider($resourceType);
$share = $this->factory->getCloudFederationShare($shareWith, $name, $description, $providerId, $owner, $ownerDisplayName, $sharedBy, $sharedByDisplayName, '', $shareType, $resourceType);
// Pass the original cloud ID so the factory can discover capabilities without warning.
// Then reset shareWith to the local username that shareReceived() needs for user lookup.
$share = $this->factory->getCloudFederationShare($shareWithCloudId, $name, $description, $providerId, $owner, $ownerDisplayName, $sharedBy, $sharedByDisplayName, '', $shareType, $resourceType);
$share->setShareWith($shareWith);
$share->setProtocol($protocol);
$provider->shareReceived($share);
} catch (ProviderDoesNotExistsException|ProviderCouldNotAddShareException $e) {
Expand Down Expand Up @@ -490,6 +521,12 @@ private function confirmNotificationIdentity(
$provider = $this->cloudFederationProviderManager->getCloudFederationProvider($resourceType);
if ($provider instanceof ISignedCloudFederationProvider || $provider instanceof \NCU\Federation\ISignedCloudFederationProvider) {
$identity = $provider->getFederationIdFromSharedSecret($sharedSecret, $notification);
if ($identity === '') {
$tokenProvider = Server::get(PublicKeyTokenProvider::class);
$accessTokenDb = $tokenProvider->getToken($sharedSecret);
$mapping = Server::get(OcmTokenMapMapper::class)->getByAccessTokenId($accessTokenDb->getId());
$identity = $provider->getFederationIdFromSharedSecret($mapping->getRefreshToken(), $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
1 change: 1 addition & 0 deletions apps/dav/appinfo/info.xml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
<job>OCA\DAV\BackgroundJob\CalendarRetentionJob</job>
<job>OCA\DAV\BackgroundJob\PruneOutdatedSyncTokensJob</job>
<job>OCA\DAV\BackgroundJob\FederatedCalendarPeriodicSyncJob</job>
<job>OCA\DAV\BackgroundJob\CleanupExpiredOcmTokensJob</job>
</background-jobs>

<repair-steps>
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\DirPermissionsMask;
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();
}
$isReadable = $share->getPermissions() & Constants::PERMISSION_READ;
$fileId = $share->getNodeId();

Expand Down
5 changes: 5 additions & 0 deletions apps/dav/composer/composer/autoload_classmap.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
'OCA\\DAV\\BackgroundJob\\BuildReminderIndexBackgroundJob' => $baseDir . '/../lib/BackgroundJob/BuildReminderIndexBackgroundJob.php',
'OCA\\DAV\\BackgroundJob\\CalendarRetentionJob' => $baseDir . '/../lib/BackgroundJob/CalendarRetentionJob.php',
'OCA\\DAV\\BackgroundJob\\CleanupDirectLinksJob' => $baseDir . '/../lib/BackgroundJob/CleanupDirectLinksJob.php',
'OCA\\DAV\\BackgroundJob\\CleanupExpiredOcmTokensJob' => $baseDir . '/../lib/BackgroundJob/CleanupExpiredOcmTokensJob.php',
'OCA\\DAV\\BackgroundJob\\CleanupInvitationTokenJob' => $baseDir . '/../lib/BackgroundJob/CleanupInvitationTokenJob.php',
'OCA\\DAV\\BackgroundJob\\CleanupOrphanedChildrenJob' => $baseDir . '/../lib/BackgroundJob/CleanupOrphanedChildrenJob.php',
'OCA\\DAV\\BackgroundJob\\DeleteOutdatedSchedulingObjects' => $baseDir . '/../lib/BackgroundJob/DeleteOutdatedSchedulingObjects.php',
Expand Down Expand Up @@ -265,6 +266,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 All @@ -284,6 +286,8 @@
'OCA\\DAV\\Db\\AbsenceMapper' => $baseDir . '/../lib/Db/AbsenceMapper.php',
'OCA\\DAV\\Db\\Direct' => $baseDir . '/../lib/Db/Direct.php',
'OCA\\DAV\\Db\\DirectMapper' => $baseDir . '/../lib/Db/DirectMapper.php',
'OCA\\DAV\\Db\\OcmTokenMap' => $baseDir . '/../lib/Db/OcmTokenMap.php',
'OCA\\DAV\\Db\\OcmTokenMapMapper' => $baseDir . '/../lib/Db/OcmTokenMapMapper.php',
'OCA\\DAV\\Db\\Property' => $baseDir . '/../lib/Db/Property.php',
'OCA\\DAV\\Db\\PropertyMapper' => $baseDir . '/../lib/Db/PropertyMapper.php',
'OCA\\DAV\\Direct\\DirectFile' => $baseDir . '/../lib/Direct/DirectFile.php',
Expand Down Expand Up @@ -393,6 +397,7 @@
'OCA\\DAV\\Migration\\Version1034Date20250605132605' => $baseDir . '/../lib/Migration/Version1034Date20250605132605.php',
'OCA\\DAV\\Migration\\Version1034Date20250813093701' => $baseDir . '/../lib/Migration/Version1034Date20250813093701.php',
'OCA\\DAV\\Migration\\Version1036Date20251202000000' => $baseDir . '/../lib/Migration/Version1036Date20251202000000.php',
'OCA\\DAV\\Migration\\Version1037Date20260306120000' => $baseDir . '/../lib/Migration/Version1037Date20260306120000.php',
'OCA\\DAV\\Migration\\Version1038Date20260302000000' => $baseDir . '/../lib/Migration/Version1038Date20260302000000.php',
'OCA\\DAV\\Migration\\Version1039Date20260408000000' => $baseDir . '/../lib/Migration/Version1039Date20260408000000.php',
'OCA\\DAV\\Model\\ExampleEvent' => $baseDir . '/../lib/Model/ExampleEvent.php',
Expand Down
9 changes: 7 additions & 2 deletions apps/dav/composer/composer/autoload_static.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@
class ComposerStaticInitDAV
{
public static $prefixLengthsPsr4 = array (
'O' =>
'O' =>
array (
'OCA\\DAV\\' => 8,
),
);

public static $prefixDirsPsr4 = array (
'OCA\\DAV\\' =>
'OCA\\DAV\\' =>
array (
0 => __DIR__ . '/..' . '/../lib',
),
Expand All @@ -30,6 +30,7 @@ class ComposerStaticInitDAV
'OCA\\DAV\\BackgroundJob\\BuildReminderIndexBackgroundJob' => __DIR__ . '/..' . '/../lib/BackgroundJob/BuildReminderIndexBackgroundJob.php',
'OCA\\DAV\\BackgroundJob\\CalendarRetentionJob' => __DIR__ . '/..' . '/../lib/BackgroundJob/CalendarRetentionJob.php',
'OCA\\DAV\\BackgroundJob\\CleanupDirectLinksJob' => __DIR__ . '/..' . '/../lib/BackgroundJob/CleanupDirectLinksJob.php',
'OCA\\DAV\\BackgroundJob\\CleanupExpiredOcmTokensJob' => __DIR__ . '/..' . '/../lib/BackgroundJob/CleanupExpiredOcmTokensJob.php',
'OCA\\DAV\\BackgroundJob\\CleanupInvitationTokenJob' => __DIR__ . '/..' . '/../lib/BackgroundJob/CleanupInvitationTokenJob.php',
'OCA\\DAV\\BackgroundJob\\CleanupOrphanedChildrenJob' => __DIR__ . '/..' . '/../lib/BackgroundJob/CleanupOrphanedChildrenJob.php',
'OCA\\DAV\\BackgroundJob\\DeleteOutdatedSchedulingObjects' => __DIR__ . '/..' . '/../lib/BackgroundJob/DeleteOutdatedSchedulingObjects.php',
Expand Down Expand Up @@ -280,6 +281,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 All @@ -299,6 +301,8 @@ class ComposerStaticInitDAV
'OCA\\DAV\\Db\\AbsenceMapper' => __DIR__ . '/..' . '/../lib/Db/AbsenceMapper.php',
'OCA\\DAV\\Db\\Direct' => __DIR__ . '/..' . '/../lib/Db/Direct.php',
'OCA\\DAV\\Db\\DirectMapper' => __DIR__ . '/..' . '/../lib/Db/DirectMapper.php',
'OCA\\DAV\\Db\\OcmTokenMap' => __DIR__ . '/..' . '/../lib/Db/OcmTokenMap.php',
'OCA\\DAV\\Db\\OcmTokenMapMapper' => __DIR__ . '/..' . '/../lib/Db/OcmTokenMapMapper.php',
'OCA\\DAV\\Db\\Property' => __DIR__ . '/..' . '/../lib/Db/Property.php',
'OCA\\DAV\\Db\\PropertyMapper' => __DIR__ . '/..' . '/../lib/Db/PropertyMapper.php',
'OCA\\DAV\\Direct\\DirectFile' => __DIR__ . '/..' . '/../lib/Direct/DirectFile.php',
Expand Down Expand Up @@ -408,6 +412,7 @@ class ComposerStaticInitDAV
'OCA\\DAV\\Migration\\Version1034Date20250605132605' => __DIR__ . '/..' . '/../lib/Migration/Version1034Date20250605132605.php',
'OCA\\DAV\\Migration\\Version1034Date20250813093701' => __DIR__ . '/..' . '/../lib/Migration/Version1034Date20250813093701.php',
'OCA\\DAV\\Migration\\Version1036Date20251202000000' => __DIR__ . '/..' . '/../lib/Migration/Version1036Date20251202000000.php',
'OCA\\DAV\\Migration\\Version1037Date20260306120000' => __DIR__ . '/..' . '/../lib/Migration/Version1037Date20260306120000.php',
'OCA\\DAV\\Migration\\Version1038Date20260302000000' => __DIR__ . '/..' . '/../lib/Migration/Version1038Date20260302000000.php',
'OCA\\DAV\\Migration\\Version1039Date20260408000000' => __DIR__ . '/..' . '/../lib/Migration/Version1039Date20260408000000.php',
'OCA\\DAV\\Model\\ExampleEvent' => __DIR__ . '/..' . '/../lib/Model/ExampleEvent.php',
Expand Down
37 changes: 37 additions & 0 deletions apps/dav/lib/BackgroundJob/CleanupExpiredOcmTokensJob.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?php

declare(strict_types=1);

/**
* SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

namespace OCA\DAV\BackgroundJob;

use OCA\DAV\Db\OcmTokenMapMapper;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\BackgroundJob\TimedJob;

/**
* Periodically purge expired OCM access token mappings from dav_ocm_token_map.
*
* The corresponding oc_authtoken entries (TEMPORARY_TOKEN with an expires
* timestamp) are cleaned up by Nextcloud's own token expiry jobs.
*/
class CleanupExpiredOcmTokensJob extends TimedJob {
public function __construct(
ITimeFactory $timeFactory,
private readonly OcmTokenMapMapper $mapper,
) {
parent::__construct($timeFactory);

$this->setInterval(6 * 60 * 60); // run every 6 hours
$this->setTimeSensitivity(self::TIME_INSENSITIVE);
}

#[\Override]
protected function run($argument): void {
$this->mapper->deleteExpired($this->time->getTime());
}
}
17 changes: 15 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 @@ -41,17 +45,26 @@ private function setupUserFs($userId) {
#[\Override]
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) {
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