aboutsummaryrefslogtreecommitdiffstats
path: root/apps/dav/lib/Connector
diff options
context:
space:
mode:
Diffstat (limited to 'apps/dav/lib/Connector')
-rw-r--r--apps/dav/lib/Connector/Sabre/AnonymousOptionsPlugin.php1
-rw-r--r--apps/dav/lib/Connector/Sabre/AppleQuirksPlugin.php1
-rw-r--r--apps/dav/lib/Connector/Sabre/Auth.php24
-rw-r--r--apps/dav/lib/Connector/Sabre/BearerAuth.php1
-rw-r--r--apps/dav/lib/Connector/Sabre/CachingTree.php1
-rw-r--r--apps/dav/lib/Connector/Sabre/ChecksumUpdatePlugin.php14
-rw-r--r--apps/dav/lib/Connector/Sabre/DavAclPlugin.php2
-rw-r--r--apps/dav/lib/Connector/Sabre/Directory.php22
-rw-r--r--apps/dav/lib/Connector/Sabre/DummyGetResponsePlugin.php4
-rw-r--r--apps/dav/lib/Connector/Sabre/Exception/BadGateway.php1
-rw-r--r--apps/dav/lib/Connector/Sabre/FakeLockerPlugin.php4
-rw-r--r--apps/dav/lib/Connector/Sabre/File.php7
-rw-r--r--apps/dav/lib/Connector/Sabre/FilesPlugin.php52
-rw-r--r--apps/dav/lib/Connector/Sabre/MtimeSanitizer.php1
-rw-r--r--apps/dav/lib/Connector/Sabre/Principal.php12
-rw-r--r--apps/dav/lib/Connector/Sabre/PublicAuth.php44
-rw-r--r--apps/dav/lib/Connector/Sabre/ServerFactory.php78
-rw-r--r--apps/dav/lib/Connector/Sabre/SharesPlugin.php4
-rw-r--r--apps/dav/lib/Connector/Sabre/TagsPlugin.php44
19 files changed, 224 insertions, 93 deletions
diff --git a/apps/dav/lib/Connector/Sabre/AnonymousOptionsPlugin.php b/apps/dav/lib/Connector/Sabre/AnonymousOptionsPlugin.php
index b39dc7197b0..0e2b1c58748 100644
--- a/apps/dav/lib/Connector/Sabre/AnonymousOptionsPlugin.php
+++ b/apps/dav/lib/Connector/Sabre/AnonymousOptionsPlugin.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/apps/dav/lib/Connector/Sabre/AppleQuirksPlugin.php b/apps/dav/lib/Connector/Sabre/AppleQuirksPlugin.php
index 88fff5e6a5a..9cff113140a 100644
--- a/apps/dav/lib/Connector/Sabre/AppleQuirksPlugin.php
+++ b/apps/dav/lib/Connector/Sabre/AppleQuirksPlugin.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/apps/dav/lib/Connector/Sabre/Auth.php b/apps/dav/lib/Connector/Sabre/Auth.php
index d977721bdfa..a174920946a 100644
--- a/apps/dav/lib/Connector/Sabre/Auth.php
+++ b/apps/dav/lib/Connector/Sabre/Auth.php
@@ -55,8 +55,8 @@ class Auth extends AbstractBasic {
* @see https://github.com/owncloud/core/issues/13245
*/
public function isDavAuthenticated(string $username): bool {
- return !is_null($this->session->get(self::DAV_AUTHENTICATED)) &&
- $this->session->get(self::DAV_AUTHENTICATED) === $username;
+ return !is_null($this->session->get(self::DAV_AUTHENTICATED))
+ && $this->session->get(self::DAV_AUTHENTICATED) === $username;
}
/**
@@ -71,8 +71,8 @@ class Auth extends AbstractBasic {
* @throws PasswordLoginForbidden
*/
protected function validateUserPass($username, $password) {
- if ($this->userSession->isLoggedIn() &&
- $this->isDavAuthenticated($this->userSession->getUser()->getUID())
+ if ($this->userSession->isLoggedIn()
+ && $this->isDavAuthenticated($this->userSession->getUser()->getUID())
) {
$this->session->close();
return true;
@@ -118,7 +118,7 @@ class Auth extends AbstractBasic {
* Checks whether a CSRF check is required on the request
*/
private function requiresCSRFCheck(): bool {
-
+
$methodsWithoutCsrf = ['GET', 'HEAD', 'OPTIONS'];
if (in_array($this->request->getMethod(), $methodsWithoutCsrf)) {
return false;
@@ -144,8 +144,8 @@ class Auth extends AbstractBasic {
}
// If logged-in AND DAV authenticated no check is required
- if ($this->userSession->isLoggedIn() &&
- $this->isDavAuthenticated($this->userSession->getUser()->getUID())) {
+ if ($this->userSession->isLoggedIn()
+ && $this->isDavAuthenticated($this->userSession->getUser()->getUID())) {
return false;
}
@@ -159,8 +159,8 @@ class Auth extends AbstractBasic {
private function auth(RequestInterface $request, ResponseInterface $response): array {
$forcedLogout = false;
- if (!$this->request->passesCSRFCheck() &&
- $this->requiresCSRFCheck()) {
+ if (!$this->request->passesCSRFCheck()
+ && $this->requiresCSRFCheck()) {
// In case of a fail with POST we need to recheck the credentials
if ($this->request->getMethod() === 'POST') {
$forcedLogout = true;
@@ -178,10 +178,10 @@ class Auth extends AbstractBasic {
}
if (
//Fix for broken webdav clients
- ($this->userSession->isLoggedIn() && is_null($this->session->get(self::DAV_AUTHENTICATED))) ||
+ ($this->userSession->isLoggedIn() && is_null($this->session->get(self::DAV_AUTHENTICATED)))
//Well behaved clients that only send the cookie are allowed
- ($this->userSession->isLoggedIn() && $this->session->get(self::DAV_AUTHENTICATED) === $this->userSession->getUser()->getUID() && empty($request->getHeader('Authorization'))) ||
- \OC_User::handleApacheAuth()
+ || ($this->userSession->isLoggedIn() && $this->session->get(self::DAV_AUTHENTICATED) === $this->userSession->getUser()->getUID() && empty($request->getHeader('Authorization')))
+ || \OC_User::handleApacheAuth()
) {
$user = $this->userSession->getUser()->getUID();
$this->currentUser = $user;
diff --git a/apps/dav/lib/Connector/Sabre/BearerAuth.php b/apps/dav/lib/Connector/Sabre/BearerAuth.php
index e189d8fa128..23453ae8efb 100644
--- a/apps/dav/lib/Connector/Sabre/BearerAuth.php
+++ b/apps/dav/lib/Connector/Sabre/BearerAuth.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/apps/dav/lib/Connector/Sabre/CachingTree.php b/apps/dav/lib/Connector/Sabre/CachingTree.php
index 86e102677c1..5d72b530f58 100644
--- a/apps/dav/lib/Connector/Sabre/CachingTree.php
+++ b/apps/dav/lib/Connector/Sabre/CachingTree.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/apps/dav/lib/Connector/Sabre/ChecksumUpdatePlugin.php b/apps/dav/lib/Connector/Sabre/ChecksumUpdatePlugin.php
index 64a61a43a9b..18009080585 100644
--- a/apps/dav/lib/Connector/Sabre/ChecksumUpdatePlugin.php
+++ b/apps/dav/lib/Connector/Sabre/ChecksumUpdatePlugin.php
@@ -27,20 +27,6 @@ class ChecksumUpdatePlugin extends ServerPlugin {
}
/** @return string[] */
- public function getHTTPMethods($path): array {
- $tree = $this->server->tree;
-
- if ($tree->nodeExists($path)) {
- $node = $tree->getNodeForPath($path);
- if ($node instanceof File) {
- return ['PATCH'];
- }
- }
-
- return [];
- }
-
- /** @return string[] */
public function getFeatures(): array {
return ['nextcloud-checksum-update'];
}
diff --git a/apps/dav/lib/Connector/Sabre/DavAclPlugin.php b/apps/dav/lib/Connector/Sabre/DavAclPlugin.php
index 7846896182f..100d719ef01 100644
--- a/apps/dav/lib/Connector/Sabre/DavAclPlugin.php
+++ b/apps/dav/lib/Connector/Sabre/DavAclPlugin.php
@@ -62,7 +62,7 @@ class DavAclPlugin extends \Sabre\DAVACL\Plugin {
)
);
}
-
+
}
return $access;
diff --git a/apps/dav/lib/Connector/Sabre/Directory.php b/apps/dav/lib/Connector/Sabre/Directory.php
index 7f8fe3a84de..fe09c3f423f 100644
--- a/apps/dav/lib/Connector/Sabre/Directory.php
+++ b/apps/dav/lib/Connector/Sabre/Directory.php
@@ -13,7 +13,9 @@ use OCA\DAV\AppInfo\Application;
use OCA\DAV\Connector\Sabre\Exception\FileLocked;
use OCA\DAV\Connector\Sabre\Exception\Forbidden;
use OCA\DAV\Connector\Sabre\Exception\InvalidPath;
+use OCA\DAV\Storage\PublicShareWrapper;
use OCP\App\IAppManager;
+use OCP\Constants;
use OCP\Files\FileInfo;
use OCP\Files\Folder;
use OCP\Files\ForbiddenException;
@@ -172,7 +174,20 @@ class Directory extends Node implements \Sabre\DAV\ICollection, \Sabre\DAV\IQuot
* @throws \Sabre\DAV\Exception\ServiceUnavailable
*/
public function getChild($name, $info = null, ?IRequest $request = null, ?IL10N $l10n = null) {
- if (!$this->info->isReadable()) {
+ $storage = $this->info->getStorage();
+ $allowDirectory = false;
+
+ // Checking if we're in a file drop
+ // If we are, then only PUT and MKCOL are allowed (see plugin)
+ // so we are safe to return the directory without a risk of
+ // leaking files and folders structure.
+ if ($storage instanceof PublicShareWrapper) {
+ $share = $storage->getShare();
+ $allowDirectory = ($share->getPermissions() & Constants::PERMISSION_READ) !== Constants::PERMISSION_READ;
+ }
+
+ // For file drop we need to be allowed to read the directory with the nickname
+ if (!$allowDirectory && !$this->info->isReadable()) {
// avoid detecting files through this way
throw new NotFound();
}
@@ -198,6 +213,11 @@ class Directory extends Node implements \Sabre\DAV\ICollection, \Sabre\DAV\IQuot
if ($info->getMimeType() === FileInfo::MIMETYPE_FOLDER) {
$node = new \OCA\DAV\Connector\Sabre\Directory($this->fileView, $info, $this->tree, $this->shareManager);
} else {
+ // In case reading a directory was allowed but it turns out the node was a not a directory, reject it now.
+ if (!$this->info->isReadable()) {
+ throw new NotFound();
+ }
+
$node = new File($this->fileView, $info, $this->shareManager, $request, $l10n);
}
if ($this->tree) {
diff --git a/apps/dav/lib/Connector/Sabre/DummyGetResponsePlugin.php b/apps/dav/lib/Connector/Sabre/DummyGetResponsePlugin.php
index 4a7e30caa10..f6baceb748b 100644
--- a/apps/dav/lib/Connector/Sabre/DummyGetResponsePlugin.php
+++ b/apps/dav/lib/Connector/Sabre/DummyGetResponsePlugin.php
@@ -43,8 +43,8 @@ class DummyGetResponsePlugin extends \Sabre\DAV\ServerPlugin {
* @return false
*/
public function httpGet(RequestInterface $request, ResponseInterface $response) {
- $string = 'This is the WebDAV interface. It can only be accessed by ' .
- 'WebDAV clients such as the Nextcloud desktop sync client.';
+ $string = 'This is the WebDAV interface. It can only be accessed by '
+ . 'WebDAV clients such as the Nextcloud desktop sync client.';
$stream = fopen('php://memory', 'r+');
fwrite($stream, $string);
rewind($stream);
diff --git a/apps/dav/lib/Connector/Sabre/Exception/BadGateway.php b/apps/dav/lib/Connector/Sabre/Exception/BadGateway.php
index 41ace002660..1e1e4aaed04 100644
--- a/apps/dav/lib/Connector/Sabre/Exception/BadGateway.php
+++ b/apps/dav/lib/Connector/Sabre/Exception/BadGateway.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-only
diff --git a/apps/dav/lib/Connector/Sabre/FakeLockerPlugin.php b/apps/dav/lib/Connector/Sabre/FakeLockerPlugin.php
index 61ecfaf845c..b0c5a079ce1 100644
--- a/apps/dav/lib/Connector/Sabre/FakeLockerPlugin.php
+++ b/apps/dav/lib/Connector/Sabre/FakeLockerPlugin.php
@@ -117,8 +117,8 @@ class FakeLockerPlugin extends ServerPlugin {
$lockInfo->timeout = 1800;
$body = $this->server->xml->write('{DAV:}prop', [
- '{DAV:}lockdiscovery' =>
- new LockDiscovery([$lockInfo])
+ '{DAV:}lockdiscovery'
+ => new LockDiscovery([$lockInfo])
]);
$response->setStatus(Http::STATUS_OK);
diff --git a/apps/dav/lib/Connector/Sabre/File.php b/apps/dav/lib/Connector/Sabre/File.php
index 045b9d7e784..218d38e1c4b 100644
--- a/apps/dav/lib/Connector/Sabre/File.php
+++ b/apps/dav/lib/Connector/Sabre/File.php
@@ -19,6 +19,7 @@ use OCA\DAV\Connector\Sabre\Exception\Forbidden as DAVForbiddenException;
use OCA\DAV\Connector\Sabre\Exception\UnsupportedMediaType;
use OCP\App\IAppManager;
use OCP\Encryption\Exceptions\GenericEncryptionException;
+use OCP\Files;
use OCP\Files\EntityTooLargeException;
use OCP\Files\FileInfo;
use OCP\Files\ForbiddenException;
@@ -215,7 +216,9 @@ class File extends Node implements IFile {
try {
/** @var IWriteStreamStorage $partStorage */
$count = $partStorage->writeStream($internalPartPath, $wrappedData);
- } catch (GenericFileException) {
+ } catch (GenericFileException $e) {
+ $logger = Server::get(LoggerInterface::class);
+ $logger->error('Error while writing stream to storage: ' . $e->getMessage(), ['exception' => $e, 'app' => 'webdav']);
$result = $isEOF;
if (is_resource($wrappedData)) {
$result = feof($wrappedData);
@@ -229,7 +232,7 @@ class File extends Node implements IFile {
// because we have no clue about the cause we can only throw back a 500/Internal Server Error
throw new Exception($this->l10n->t('Could not write file contents'));
}
- [$count, $result] = \OC_Helper::streamCopy($data, $target);
+ [$count, $result] = Files::streamCopy($data, $target, true);
fclose($target);
}
diff --git a/apps/dav/lib/Connector/Sabre/FilesPlugin.php b/apps/dav/lib/Connector/Sabre/FilesPlugin.php
index 34f48384e12..843383a0452 100644
--- a/apps/dav/lib/Connector/Sabre/FilesPlugin.php
+++ b/apps/dav/lib/Connector/Sabre/FilesPlugin.php
@@ -9,8 +9,10 @@ namespace OCA\DAV\Connector\Sabre;
use OC\AppFramework\Http\Request;
use OC\FilesMetadata\Model\FilesMetadata;
+use OC\User\NoUserException;
use OCA\DAV\Connector\Sabre\Exception\InvalidPath;
use OCA\Files_Sharing\External\Mount as SharingExternalMount;
+use OCP\Accounts\IAccountManager;
use OCP\Constants;
use OCP\Files\ForbiddenException;
use OCP\Files\IFilenameValidator;
@@ -91,6 +93,7 @@ class FilesPlugin extends ServerPlugin {
private IPreview $previewManager,
private IUserSession $userSession,
private IFilenameValidator $validator,
+ private IAccountManager $accountManager,
private bool $isPublic = false,
private bool $downloadAttachment = true,
) {
@@ -254,8 +257,8 @@ class FilesPlugin extends ServerPlugin {
// adds a 'Content-Disposition: attachment' header in case no disposition
// header has been set before
- if ($this->downloadAttachment &&
- $response->getHeader('Content-Disposition') === null) {
+ if ($this->downloadAttachment
+ && $response->getHeader('Content-Disposition') === null) {
$filename = $node->getName();
if ($this->request->isUserAgent(
[
@@ -361,9 +364,32 @@ class FilesPlugin extends ServerPlugin {
$owner = $node->getOwner();
if (!$owner) {
return null;
- } else {
+ }
+
+ // Get current user to see if we're in a public share or not
+ $user = $this->userSession->getUser();
+
+ // If the user is logged in, we can return the display name
+ if ($user !== null) {
return $owner->getDisplayName();
}
+
+ // Check if the user published their display name
+ try {
+ $ownerAccount = $this->accountManager->getAccount($owner);
+ } catch (NoUserException) {
+ // do not lock process if owner is not local
+ return null;
+ }
+
+ $ownerNameProperty = $ownerAccount->getProperty(IAccountManager::PROPERTY_DISPLAYNAME);
+
+ // Since we are not logged in, we need to have at least the published scope
+ if ($ownerNameProperty->getScope() === IAccountManager::SCOPE_PUBLISHED) {
+ return $owner->getDisplayName();
+ }
+
+ return null;
});
$propFind->handle(self::HAS_PREVIEW_PROPERTYNAME, function () use ($node) {
@@ -515,8 +541,8 @@ class FilesPlugin extends ServerPlugin {
$ocmPermissions[] = 'read';
}
- if (($ncPermissions & Constants::PERMISSION_CREATE) ||
- ($ncPermissions & Constants::PERMISSION_UPDATE)) {
+ if (($ncPermissions & Constants::PERMISSION_CREATE)
+ || ($ncPermissions & Constants::PERMISSION_UPDATE)) {
$ocmPermissions[] = 'write';
}
@@ -701,15 +727,15 @@ class FilesPlugin extends ServerPlugin {
*/
public function sendFileIdHeader($filePath, ?\Sabre\DAV\INode $node = null) {
// we get the node for the given $filePath here because in case of afterCreateFile $node is the parent folder
- if (!$this->server->tree->nodeExists($filePath)) {
- return;
- }
- $node = $this->server->tree->getNodeForPath($filePath);
- if ($node instanceof Node) {
- $fileId = $node->getFileId();
- if (!is_null($fileId)) {
- $this->server->httpResponse->setHeader('OC-FileId', $fileId);
+ try {
+ $node = $this->server->tree->getNodeForPath($filePath);
+ if ($node instanceof Node) {
+ $fileId = $node->getFileId();
+ if (!is_null($fileId)) {
+ $this->server->httpResponse->setHeader('OC-FileId', $fileId);
+ }
}
+ } catch (NotFound) {
}
}
}
diff --git a/apps/dav/lib/Connector/Sabre/MtimeSanitizer.php b/apps/dav/lib/Connector/Sabre/MtimeSanitizer.php
index efed6ce09f8..e18ef58149a 100644
--- a/apps/dav/lib/Connector/Sabre/MtimeSanitizer.php
+++ b/apps/dav/lib/Connector/Sabre/MtimeSanitizer.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-only
diff --git a/apps/dav/lib/Connector/Sabre/Principal.php b/apps/dav/lib/Connector/Sabre/Principal.php
index 515ef807a25..b61cabedf5f 100644
--- a/apps/dav/lib/Connector/Sabre/Principal.php
+++ b/apps/dav/lib/Connector/Sabre/Principal.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
@@ -41,9 +42,6 @@ class Principal implements BackendInterface {
/** @var bool */
private $hasCircles;
- /** @var ProxyMapper */
- private $proxyMapper;
-
/** @var KnownUserService */
private $knownUserService;
@@ -54,7 +52,7 @@ class Principal implements BackendInterface {
private IShareManager $shareManager,
private IUserSession $userSession,
private IAppManager $appManager,
- ProxyMapper $proxyMapper,
+ private ProxyMapper $proxyMapper,
KnownUserService $knownUserService,
private IConfig $config,
private IFactory $languageFactory,
@@ -62,7 +60,6 @@ class Principal implements BackendInterface {
) {
$this->principalPrefix = trim($principalPrefix, '/');
$this->hasGroups = $this->hasCircles = ($principalPrefix === 'principals/users/');
- $this->proxyMapper = $proxyMapper;
$this->knownUserService = $knownUserService;
}
@@ -155,6 +152,11 @@ class Principal implements BackendInterface {
'uri' => 'principals/system/' . $name,
'{DAV:}displayname' => $this->languageFactory->get('dav')->t('Accounts'),
];
+ } elseif ($prefix === 'principals/shares') {
+ return [
+ 'uri' => 'principals/shares/' . $name,
+ '{DAV:}displayname' => $name,
+ ];
}
return null;
}
diff --git a/apps/dav/lib/Connector/Sabre/PublicAuth.php b/apps/dav/lib/Connector/Sabre/PublicAuth.php
index ea59d9efc8f..2ca1c25e2f6 100644
--- a/apps/dav/lib/Connector/Sabre/PublicAuth.php
+++ b/apps/dav/lib/Connector/Sabre/PublicAuth.php
@@ -14,7 +14,9 @@ namespace OCA\DAV\Connector\Sabre;
use OCP\Defaults;
use OCP\IRequest;
use OCP\ISession;
+use OCP\IURLGenerator;
use OCP\Security\Bruteforce\IThrottler;
+use OCP\Security\Bruteforce\MaxDelayReached;
use OCP\Share\Exceptions\ShareNotFound;
use OCP\Share\IManager;
use OCP\Share\IShare;
@@ -22,6 +24,7 @@ use Psr\Log\LoggerInterface;
use Sabre\DAV\Auth\Backend\AbstractBasic;
use Sabre\DAV\Exception\NotAuthenticated;
use Sabre\DAV\Exception\NotFound;
+use Sabre\DAV\Exception\PreconditionFailed;
use Sabre\DAV\Exception\ServiceUnavailable;
use Sabre\HTTP;
use Sabre\HTTP\RequestInterface;
@@ -44,6 +47,7 @@ class PublicAuth extends AbstractBasic {
private ISession $session,
private IThrottler $throttler,
private LoggerInterface $logger,
+ private IURLGenerator $urlGenerator,
) {
// setup realm
$defaults = new Defaults();
@@ -51,17 +55,18 @@ class PublicAuth extends AbstractBasic {
}
/**
- * @param RequestInterface $request
- * @param ResponseInterface $response
- *
- * @return array
* @throws NotAuthenticated
+ * @throws MaxDelayReached
* @throws ServiceUnavailable
*/
public function check(RequestInterface $request, ResponseInterface $response): array {
try {
$this->throttler->sleepDelayOrThrowOnMax($this->request->getRemoteAddress(), self::BRUTEFORCE_ACTION);
+ if (count($_COOKIE) > 0 && !$this->request->passesStrictCookieCheck() && $this->getShare()->getPassword() !== null) {
+ throw new PreconditionFailed('Strict cookie check failed');
+ }
+
$auth = new HTTP\Auth\Basic(
$this->realm,
$request,
@@ -75,7 +80,17 @@ class PublicAuth extends AbstractBasic {
}
return $this->checkToken();
- } catch (NotAuthenticated $e) {
+ } catch (NotAuthenticated|MaxDelayReached $e) {
+ $this->throttler->registerAttempt(self::BRUTEFORCE_ACTION, $this->request->getRemoteAddress());
+ throw $e;
+ } catch (PreconditionFailed $e) {
+ $response->setHeader(
+ 'Location',
+ $this->urlGenerator->linkToRoute(
+ 'files_sharing.share.showShare',
+ [ 'token' => $this->getToken() ],
+ ),
+ );
throw $e;
} catch (\Exception $e) {
$class = get_class($e);
@@ -87,14 +102,13 @@ class PublicAuth extends AbstractBasic {
/**
* Extract token from request url
- * @return string
* @throws NotFound
*/
private function getToken(): string {
$path = $this->request->getPathInfo() ?: '';
// ['', 'dav', 'files', 'token']
$splittedPath = explode('/', $path);
-
+
if (count($splittedPath) < 4 || $splittedPath[3] === '') {
throw new NotFound();
}
@@ -104,7 +118,7 @@ class PublicAuth extends AbstractBasic {
/**
* Check token validity
- * @return array
+ *
* @throws NotFound
* @throws NotAuthenticated
*/
@@ -152,15 +166,13 @@ class PublicAuth extends AbstractBasic {
protected function validateUserPass($username, $password) {
$this->throttler->sleepDelayOrThrowOnMax($this->request->getRemoteAddress(), self::BRUTEFORCE_ACTION);
- $token = $this->getToken();
try {
- $share = $this->shareManager->getShareByToken($token);
+ $share = $this->getShare();
} catch (ShareNotFound $e) {
$this->throttler->registerAttempt(self::BRUTEFORCE_ACTION, $this->request->getRemoteAddress());
return false;
}
- $this->share = $share;
\OC_User::setIncognitoMode(true);
// check if the share is password protected
@@ -176,7 +188,7 @@ class PublicAuth extends AbstractBasic {
}
return true;
}
-
+
if ($this->session->exists(PublicAuth::DAV_AUTHENTICATED)
&& $this->session->get(PublicAuth::DAV_AUTHENTICATED) === $share->getId()) {
return true;
@@ -203,7 +215,13 @@ class PublicAuth extends AbstractBasic {
}
public function getShare(): IShare {
- assert($this->share !== null);
+ $token = $this->getToken();
+
+ if ($this->share === null) {
+ $share = $this->shareManager->getShareByToken($token);
+ $this->share = $share;
+ }
+
return $this->share;
}
}
diff --git a/apps/dav/lib/Connector/Sabre/ServerFactory.php b/apps/dav/lib/Connector/Sabre/ServerFactory.php
index f035ff8d625..214412e1744 100644
--- a/apps/dav/lib/Connector/Sabre/ServerFactory.php
+++ b/apps/dav/lib/Connector/Sabre/ServerFactory.php
@@ -8,17 +8,23 @@
namespace OCA\DAV\Connector\Sabre;
use OC\Files\View;
+use OC\KnownUser\KnownUserService;
use OCA\DAV\AppInfo\PluginManager;
use OCA\DAV\CalDAV\DefaultCalendarValidator;
+use OCA\DAV\CalDAV\Proxy\ProxyMapper;
use OCA\DAV\DAV\CustomPropertiesBackend;
use OCA\DAV\DAV\ViewOnlyPlugin;
use OCA\DAV\Files\BrowserErrorPagePlugin;
+use OCA\DAV\Files\Sharing\RootCollection;
+use OCA\DAV\Upload\CleanupService;
use OCA\Theming\ThemingDefaults;
+use OCP\Accounts\IAccountManager;
use OCP\App\IAppManager;
use OCP\Comments\ICommentsManager;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\Files\Folder;
use OCP\Files\IFilenameValidator;
+use OCP\Files\IRootFolder;
use OCP\Files\Mount\IMountManager;
use OCP\IConfig;
use OCP\IDBConnection;
@@ -27,12 +33,14 @@ use OCP\IL10N;
use OCP\IPreview;
use OCP\IRequest;
use OCP\ITagManager;
+use OCP\IUserManager;
use OCP\IUserSession;
use OCP\SabrePluginEvent;
use OCP\SystemTag\ISystemTagManager;
use OCP\SystemTag\ISystemTagObjectMapper;
use Psr\Log\LoggerInterface;
use Sabre\DAV\Auth\Plugin;
+use Sabre\DAV\SimpleCollection;
class ServerFactory {
@@ -53,13 +61,22 @@ class ServerFactory {
/**
* @param callable $viewCallBack callback that should return the view for the dav endpoint
*/
- public function createServer(string $baseUri,
+ public function createServer(
+ bool $isPublicShare,
+ string $baseUri,
string $requestUri,
Plugin $authPlugin,
- callable $viewCallBack): Server {
+ callable $viewCallBack,
+ ): Server {
// Fire up server
- $objectTree = new ObjectTree();
- $server = new Server($objectTree);
+ if ($isPublicShare) {
+ $rootCollection = new SimpleCollection('root');
+ $tree = new CachingTree($rootCollection);
+ } else {
+ $rootCollection = null;
+ $tree = new ObjectTree();
+ }
+ $server = new Server($tree);
// Set URL explicitly due to reverse-proxy situations
$server->httpRequest->setUrl($requestUri);
$server->setBaseUri($baseUri);
@@ -80,7 +97,7 @@ class ServerFactory {
$server->addPlugin(new RequestIdHeaderPlugin($this->request));
$server->addPlugin(new ZipFolderPlugin(
- $objectTree,
+ $tree,
$this->logger,
$this->eventDispatcher,
));
@@ -100,7 +117,7 @@ class ServerFactory {
}
// wait with registering these until auth is handled and the filesystem is setup
- $server->on('beforeMethod:*', function () use ($server, $objectTree, $viewCallBack): void {
+ $server->on('beforeMethod:*', function () use ($server, $tree, $viewCallBack, $isPublicShare, $rootCollection): void {
// ensure the skeleton is copied
$userFolder = \OC::$server->getUserFolder();
@@ -114,20 +131,55 @@ class ServerFactory {
// Create Nextcloud Dir
if ($rootInfo->getType() === 'dir') {
- $root = new Directory($view, $rootInfo, $objectTree);
+ $root = new Directory($view, $rootInfo, $tree);
} else {
$root = new File($view, $rootInfo);
}
- $objectTree->init($root, $view, $this->mountManager);
+
+ if ($isPublicShare) {
+ $userPrincipalBackend = new Principal(
+ \OCP\Server::get(IUserManager::class),
+ \OCP\Server::get(IGroupManager::class),
+ \OCP\Server::get(IAccountManager::class),
+ \OCP\Server::get(\OCP\Share\IManager::class),
+ \OCP\Server::get(IUserSession::class),
+ \OCP\Server::get(IAppManager::class),
+ \OCP\Server::get(ProxyMapper::class),
+ \OCP\Server::get(KnownUserService::class),
+ \OCP\Server::get(IConfig::class),
+ \OC::$server->getL10NFactory(),
+ );
+
+ // Mount the share collection at /public.php/dav/shares/<share token>
+ $rootCollection->addChild(new RootCollection(
+ $root,
+ $userPrincipalBackend,
+ 'principals/shares',
+ ));
+
+ // Mount the upload collection at /public.php/dav/uploads/<share token>
+ $rootCollection->addChild(new \OCA\DAV\Upload\RootCollection(
+ $userPrincipalBackend,
+ 'principals/shares',
+ \OCP\Server::get(CleanupService::class),
+ \OCP\Server::get(IRootFolder::class),
+ \OCP\Server::get(IUserSession::class),
+ \OCP\Server::get(\OCP\Share\IManager::class),
+ ));
+ } else {
+ /** @var ObjectTree $tree */
+ $tree->init($root, $view, $this->mountManager);
+ }
$server->addPlugin(
new FilesPlugin(
- $objectTree,
+ $tree,
$this->config,
$this->request,
$this->previewManager,
$this->userSession,
\OCP\Server::get(IFilenameValidator::class),
+ \OCP\Server::get(IAccountManager::class),
false,
!$this->config->getSystemValue('debug', false)
)
@@ -141,16 +193,16 @@ class ServerFactory {
));
if ($this->userSession->isLoggedIn()) {
- $server->addPlugin(new TagsPlugin($objectTree, $this->tagManager, $this->eventDispatcher, $this->userSession));
+ $server->addPlugin(new TagsPlugin($tree, $this->tagManager, $this->eventDispatcher, $this->userSession));
$server->addPlugin(new SharesPlugin(
- $objectTree,
+ $tree,
$this->userSession,
$userFolder,
\OCP\Server::get(\OCP\Share\IManager::class)
));
$server->addPlugin(new CommentPropertiesPlugin(\OCP\Server::get(ICommentsManager::class), $this->userSession));
$server->addPlugin(new FilesReportPlugin(
- $objectTree,
+ $tree,
$view,
\OCP\Server::get(ISystemTagManager::class),
\OCP\Server::get(ISystemTagObjectMapper::class),
@@ -165,7 +217,7 @@ class ServerFactory {
new \Sabre\DAV\PropertyStorage\Plugin(
new CustomPropertiesBackend(
$server,
- $objectTree,
+ $tree,
$this->databaseConnection,
$this->userSession->getUser(),
\OCP\Server::get(DefaultCalendarValidator::class),
diff --git a/apps/dav/lib/Connector/Sabre/SharesPlugin.php b/apps/dav/lib/Connector/Sabre/SharesPlugin.php
index 088cf33d85f..f49e85333f3 100644
--- a/apps/dav/lib/Connector/Sabre/SharesPlugin.php
+++ b/apps/dav/lib/Connector/Sabre/SharesPlugin.php
@@ -176,8 +176,8 @@ class SharesPlugin extends \Sabre\DAV\ServerPlugin {
if ($sabreNode instanceof Directory
&& $propFind->getDepth() !== 0
&& (
- !is_null($propFind->getStatus(self::SHARETYPES_PROPERTYNAME)) ||
- !is_null($propFind->getStatus(self::SHAREES_PROPERTYNAME))
+ !is_null($propFind->getStatus(self::SHARETYPES_PROPERTYNAME))
+ || !is_null($propFind->getStatus(self::SHAREES_PROPERTYNAME))
)
) {
$folderNode = $sabreNode->getNode();
diff --git a/apps/dav/lib/Connector/Sabre/TagsPlugin.php b/apps/dav/lib/Connector/Sabre/TagsPlugin.php
index eb06fa5cef6..25c1633df36 100644
--- a/apps/dav/lib/Connector/Sabre/TagsPlugin.php
+++ b/apps/dav/lib/Connector/Sabre/TagsPlugin.php
@@ -94,6 +94,7 @@ class TagsPlugin extends \Sabre\DAV\ServerPlugin {
$this->server = $server;
$this->server->on('propFind', [$this, 'handleGetProperties']);
$this->server->on('propPatch', [$this, 'handleUpdateProperties']);
+ $this->server->on('preloadProperties', [$this, 'handlePreloadProperties']);
}
/**
@@ -150,6 +151,24 @@ class TagsPlugin extends \Sabre\DAV\ServerPlugin {
}
/**
+ * Prefetches tags for a list of file IDs and caches the results
+ *
+ * @param array $fileIds List of file IDs to prefetch tags for
+ * @return void
+ */
+ private function prefetchTagsForFileIds(array $fileIds) {
+ $tags = $this->getTagger()->getTagsForObjects($fileIds);
+ if ($tags === false) {
+ // the tags API returns false on error...
+ $tags = [];
+ }
+
+ foreach ($fileIds as $fileId) {
+ $this->cachedTags[$fileId] = $tags[$fileId] ?? [];
+ }
+ }
+
+ /**
* Updates the tags of the given file id
*
* @param int $fileId
@@ -199,22 +218,11 @@ class TagsPlugin extends \Sabre\DAV\ServerPlugin {
)) {
// note: pre-fetching only supported for depth <= 1
$folderContent = $node->getChildren();
- $fileIds[] = (int)$node->getId();
+ $fileIds = [(int)$node->getId()];
foreach ($folderContent as $info) {
$fileIds[] = (int)$info->getId();
}
- $tags = $this->getTagger()->getTagsForObjects($fileIds);
- if ($tags === false) {
- // the tags API returns false on error...
- $tags = [];
- }
-
- $this->cachedTags = $this->cachedTags + $tags;
- $emptyFileIds = array_diff($fileIds, array_keys($tags));
- // also cache the ones that were not found
- foreach ($emptyFileIds as $fileId) {
- $this->cachedTags[$fileId] = [];
- }
+ $this->prefetchTagsForFileIds($fileIds);
}
$isFav = null;
@@ -270,4 +278,14 @@ class TagsPlugin extends \Sabre\DAV\ServerPlugin {
return 200;
});
}
+
+ public function handlePreloadProperties(array $nodes, array $requestProperties): void {
+ if (
+ !in_array(self::FAVORITE_PROPERTYNAME, $requestProperties, true)
+ && !in_array(self::TAGS_PROPERTYNAME, $requestProperties, true)
+ ) {
+ return;
+ }
+ $this->prefetchTagsForFileIds(array_map(fn ($node) => $node->getId(), $nodes));
+ }
}