aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFerdinand Thiessen <opensource@fthiessen.de>2024-09-28 22:16:42 +0200
committerFerdinand Thiessen <opensource@fthiessen.de>2024-09-29 13:29:04 +0200
commite0c00af3bf63b41d4c47a8d0ae8ae137b05d5fd9 (patch)
treef52c6a1cd095b704359304a4e568a1e7b881e9c8
parenta0b2297c536923867ed3f8e489dabe8a278ac0be (diff)
downloadnextcloud-server-e0c00af3bf63b41d4c47a8d0ae8ae137b05d5fd9.tar.gz
nextcloud-server-e0c00af3bf63b41d4c47a8d0ae8ae137b05d5fd9.zip
fix(dav): Emit `BeforeZipCreatedEvent` when creating folder zip archivefix/bring-back-zip-event
This is required to not break behavior on zip download (apps should be able to react to zip download especially for shares). Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
-rw-r--r--apps/dav/lib/Connector/Sabre/ServerFactory.php45
-rw-r--r--apps/dav/lib/Connector/Sabre/ZipFolderPlugin.php28
-rw-r--r--apps/dav/lib/Server.php15
-rw-r--r--lib/public/Files/Events/BeforeZipCreatedEvent.php30
4 files changed, 71 insertions, 47 deletions
diff --git a/apps/dav/lib/Connector/Sabre/ServerFactory.php b/apps/dav/lib/Connector/Sabre/ServerFactory.php
index 4235a02831a..617ba60f80d 100644
--- a/apps/dav/lib/Connector/Sabre/ServerFactory.php
+++ b/apps/dav/lib/Connector/Sabre/ServerFactory.php
@@ -28,39 +28,19 @@ use Psr\Log\LoggerInterface;
use Sabre\DAV\Auth\Plugin;
class ServerFactory {
- private IConfig $config;
- private LoggerInterface $logger;
- private IDBConnection $databaseConnection;
- private IUserSession $userSession;
- private IMountManager $mountManager;
- private ITagManager $tagManager;
- private IRequest $request;
- private IPreview $previewManager;
- private IEventDispatcher $eventDispatcher;
- private IL10N $l10n;
public function __construct(
- IConfig $config,
- LoggerInterface $logger,
- IDBConnection $databaseConnection,
- IUserSession $userSession,
- IMountManager $mountManager,
- ITagManager $tagManager,
- IRequest $request,
- IPreview $previewManager,
- IEventDispatcher $eventDispatcher,
- IL10N $l10n,
+ private IConfig $config,
+ private LoggerInterface $logger,
+ private IDBConnection $databaseConnection,
+ private IUserSession $userSession,
+ private IMountManager $mountManager,
+ private ITagManager $tagManager,
+ private IRequest $request,
+ private IPreview $previewManager,
+ private IEventDispatcher $eventDispatcher,
+ private IL10N $l10n,
) {
- $this->config = $config;
- $this->logger = $logger;
- $this->databaseConnection = $databaseConnection;
- $this->userSession = $userSession;
- $this->mountManager = $mountManager;
- $this->tagManager = $tagManager;
- $this->request = $request;
- $this->previewManager = $previewManager;
- $this->eventDispatcher = $eventDispatcher;
- $this->l10n = $l10n;
}
/**
@@ -80,7 +60,7 @@ class ServerFactory {
// Load plugins
$server->addPlugin(new \OCA\DAV\Connector\Sabre\MaintenancePlugin($this->config, $this->l10n));
$server->addPlugin(new BlockLegacyClientPlugin(
- \OCP\Server::get(IConfig::class),
+ $this->config,
\OCP\Server::get(ThemingDefaults::class),
));
$server->addPlugin(new \OCA\DAV\Connector\Sabre\AnonymousOptionsPlugin());
@@ -90,11 +70,12 @@ class ServerFactory {
$server->addPlugin(new \OCA\DAV\Connector\Sabre\ExceptionLoggerPlugin('webdav', $this->logger));
$server->addPlugin(new \OCA\DAV\Connector\Sabre\LockPlugin());
- $server->addPlugin(new RequestIdHeaderPlugin(\OC::$server->get(IRequest::class)));
+ $server->addPlugin(new RequestIdHeaderPlugin($this->request));
$server->addPlugin(new ZipFolderPlugin(
$objectTree,
$this->logger,
+ $this->eventDispatcher,
));
// Some WebDAV clients do require Class 2 WebDAV support (locking), since
diff --git a/apps/dav/lib/Connector/Sabre/ZipFolderPlugin.php b/apps/dav/lib/Connector/Sabre/ZipFolderPlugin.php
index a5820f80538..8484dfa1935 100644
--- a/apps/dav/lib/Connector/Sabre/ZipFolderPlugin.php
+++ b/apps/dav/lib/Connector/Sabre/ZipFolderPlugin.php
@@ -9,6 +9,9 @@ declare(strict_types=1);
namespace OCA\DAV\Connector\Sabre;
use OC\Streamer;
+use OCA\DAV\Connector\Sabre\Exception\Forbidden;
+use OCP\EventDispatcher\IEventDispatcher;
+use OCP\Files\Events\BeforeZipCreatedEvent;
use OCP\Files\File as NcFile;
use OCP\Files\Folder as NcFolder;
use OCP\Files\Node as NcNode;
@@ -37,6 +40,7 @@ class ZipFolderPlugin extends ServerPlugin {
public function __construct(
private Tree $tree,
private LoggerInterface $logger,
+ private IEventDispatcher $eventDispatcher,
) {
}
@@ -114,17 +118,33 @@ class ZipFolderPlugin extends ServerPlugin {
if ($filesParam !== '') {
$files = json_decode($filesParam);
if (!is_array($files)) {
- if (!is_string($files)) {
+ $files = [$files];
+ }
+
+ foreach ($files as $file) {
+ if (!is_string($file)) {
+ // we log this as this means either we - or an app - have a bug somewhere or a user is trying invalid things
+ $this->logger->notice('Invalid files filter parameter for ZipFolderPlugin', ['filter' => $filesParam]);
// no valid parameter so continue with Sabre behavior
- $this->logger->debug('Invalid files filter parameter for ZipFolderPlugin', ['filter' => $filesParam]);
return null;
}
-
- $files = [$files];
}
}
$folder = $node->getNode();
+ $event = new BeforeZipCreatedEvent($folder, $files);
+ $this->eventDispatcher->dispatchTyped($event);
+ if ((!$event->isSuccessful()) || $event->getErrorMessage() !== null) {
+ $errorMessage = $event->getErrorMessage();
+ if ($errorMessage === null) {
+ // Not allowed to download but also no explaining error
+ // so we abort the ZIP creation and fall back to Sabre default behavior.
+ return null;
+ }
+ // Downloading was denied by an app
+ throw new Forbidden($errorMessage);
+ }
+
$content = empty($files) ? $folder->getDirectoryListing() : [];
foreach ($files as $path) {
$child = $node->getChild($path);
diff --git a/apps/dav/lib/Server.php b/apps/dav/lib/Server.php
index 69f05395cb5..4bfc8019218 100644
--- a/apps/dav/lib/Server.php
+++ b/apps/dav/lib/Server.php
@@ -86,9 +86,9 @@ class Server {
$this->request = $request;
$this->baseUri = $baseUri;
- $logger = \OC::$server->get(LoggerInterface::class);
- /** @var IEventDispatcher $dispatcher */
- $dispatcher = \OC::$server->get(IEventDispatcher::class);
+
+ $logger = \OCP\Server::get(LoggerInterface::class);
+ $eventDispatcher = \OCP\Server::get(IEventDispatcher::class);
$root = new RootCollection();
$this->server = new \OCA\DAV\Connector\Sabre\Server(new CachingTree($root));
@@ -123,10 +123,10 @@ class Server {
// allow setup of additional auth backends
$event = new SabrePluginEvent($this->server);
- $dispatcher->dispatch('OCA\DAV\Connector\Sabre::authInit', $event);
+ $eventDispatcher->dispatch('OCA\DAV\Connector\Sabre::authInit', $event);
$newAuthEvent = new SabrePluginAuthInitEvent($this->server);
- $dispatcher->dispatchTyped($newAuthEvent);
+ $eventDispatcher->dispatchTyped($newAuthEvent);
$bearerAuthBackend = new BearerAuth(
\OC::$server->getUserSession(),
@@ -213,12 +213,13 @@ class Server {
$this->server->addPlugin(new ZipFolderPlugin(
$this->server->tree,
$logger,
+ $eventDispatcher,
));
// allow setup of additional plugins
- $dispatcher->dispatch('OCA\DAV\Connector\Sabre::addPlugin', $event);
+ $eventDispatcher->dispatch('OCA\DAV\Connector\Sabre::addPlugin', $event);
$typedEvent = new SabrePluginAddEvent($this->server);
- $dispatcher->dispatchTyped($typedEvent);
+ $eventDispatcher->dispatchTyped($typedEvent);
// Some WebDAV clients do require Class 2 WebDAV support (locking), since
// we do not provide locking we emulate it using a fake locking plugin.
diff --git a/lib/public/Files/Events/BeforeZipCreatedEvent.php b/lib/public/Files/Events/BeforeZipCreatedEvent.php
index b55b36d3968..0363d385d36 100644
--- a/lib/public/Files/Events/BeforeZipCreatedEvent.php
+++ b/lib/public/Files/Events/BeforeZipCreatedEvent.php
@@ -10,23 +10,45 @@ declare(strict_types=1);
namespace OCP\Files\Events;
use OCP\EventDispatcher\Event;
+use OCP\Files\Folder;
/**
+ * This event is triggered before a archive is created when a user requested
+ * downloading a folder or multiple files.
+ *
+ * By setting `successful` to false the tar creation can be aborted and the download denied.
+ *
* @since 25.0.0
*/
class BeforeZipCreatedEvent extends Event {
private string $directory;
- private array $files;
private bool $successful = true;
private ?string $errorMessage = null;
+ private ?Folder $folder = null;
/**
+ * @param list<string> $files
* @since 25.0.0
+ * @since 31.0.0 support `OCP\Files\Folder` as `$directory` parameter - passing a string is deprecated now
*/
- public function __construct(string $directory, array $files) {
+ public function __construct(
+ string|Folder $directory,
+ private array $files,
+ ) {
parent::__construct();
- $this->directory = $directory;
- $this->files = $files;
+ if ($directory instanceof Folder) {
+ $this->directory = $directory->getPath();
+ $this->folder = $directory;
+ } else {
+ $this->directory = $directory;
+ }
+ }
+
+ /**
+ * @since 31.0.0
+ */
+ public function getFolder(): ?Folder {
+ return $this->folder;
}
/**