Skip to content

Commit ccc00aa

Browse files
committed
fixup! feat: improve ZipFolderPlugin behaviour for different cases
Signed-off-by: Salvatore Martire <4652631+salmart-dev@users.noreply.github.com>
1 parent b251a11 commit ccc00aa

10 files changed

Lines changed: 37 additions & 29 deletions

File tree

apps/dav/lib/Connector/Sabre/ByteCounterFilter.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
* A stream filter to track how many bytes have been streamed from a stream.
1313
*/
1414
class ByteCounterFilter extends \php_user_filter {
15+
/** @psalm-suppress NonInvariantPropertyType PHP crashes without string type */
1516
public string $filtername = 'ByteCounter';
1617

1718
public function filter($in, $out, &$consumed, bool $closing): int {

apps/dav/lib/Connector/Sabre/ZipFolderPlugin.php

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
use OCP\IConfig;
1919
use OCP\IDateTimeZone;
2020
use OCP\IL10N;
21-
use OCP\L10N\IFactory;
2221
use Psr\Log\LoggerInterface;
2322
use Sabre\DAV\Server;
2423
use Sabre\DAV\ServerPlugin;
@@ -244,7 +243,7 @@ public function handleDownload(Request $request, Response $response): ?bool {
244243
$streamer->addEmptyDir($archiveName);
245244
}
246245

247-
foreach ($event->getNodes() as $path => [$node, $reason]) {
246+
foreach ($event->getNodes($rootPath) as $path => [$node, $reason]) {
248247
$filename = str_replace($rootPath, '', $path);
249248
if ($node === null) {
250249
if ($this->reportMissingFiles) {

apps/dav/lib/Server.php

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
namespace OCA\DAV;
99

1010
use OC\Files\Filesystem;
11-
use OC\L10N\L10N;
1211
use OCA\DAV\AppInfo\PluginManager;
1312
use OCA\DAV\BulkUpload\BulkUploadPlugin;
1413
use OCA\DAV\CalDAV\BirthdayCalendar\EnablePlugin;
@@ -89,7 +88,6 @@
8988
use OCP\IDateTimeZone;
9089
use OCP\IDBConnection;
9190
use OCP\IGroupManager;
92-
use OCP\IL10N;
9391
use OCP\IPreview;
9492
use OCP\IRequest;
9593
use OCP\ISession;

apps/dav/tests/unit/Connector/Sabre/ZipFolderPluginTest.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
namespace OCA\DAV\Tests\unit\Connector\Sabre;
1111

1212
use Exception;
13-
use OC\L10N\L10N;
1413
use OCA\DAV\Connector\Sabre\Directory;
1514
use OCA\DAV\Connector\Sabre\Exception\Forbidden;
1615
use OCA\DAV\Connector\Sabre\Node;

apps/files_sharing/lib/Listener/BeforeDirectFileDownloadListener.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,8 @@ public function handle(Event $event): void {
3737
// Check only for user/group shares. Don't restrict e.g. share links
3838
if (!$user) {
3939
return;
40-
4140
}
41+
4242
$userFolder = $this->rootFolder->getUserFolder($user->getUID());
4343
$node = $userFolder->get($event->getPath());
4444
if (!$this->viewOnly->isDownloadable($node)) {

apps/files_sharing/lib/Listener/BeforeZipCreatedListener.php

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -46,18 +46,20 @@ public function handle(Event $event): void {
4646
}
4747
}
4848

49-
// Check only for user/group shares. Don't restrict e.g. share links
5049
$user = $this->userSession->getUser();
51-
if (!$user) {
50+
$folder = $event->getFolder();
51+
if ($user === null && $event->getFolder() === null) {
52+
// there is no way to know if the file is downloadable or not, allow it
5253
$event->setSuccessful(true);
5354
return;
5455
}
5556

56-
$userFolder = $this->rootFolder->getUserFolder($user->getUID());
57-
$viewOnlyHandler = new ViewOnly($userFolder);
57+
// in link-shares there may be no user, in that case we check that the share folder is downloadable
58+
$userFolder = $user ? $this->rootFolder->getUserFolder($user->getUID()) : null;
59+
$folderToCheck = $userFolder ? $userFolder->get($dir) : $folder;
5860

59-
$node = $userFolder->get($dir);
60-
$isRootDownloadable = $viewOnlyHandler->isDownloadable($node);
61+
$viewOnlyHandler = new ViewOnly($userFolder);
62+
$isRootDownloadable = $viewOnlyHandler->isDownloadable($folderToCheck);
6163

6264
if (!$isRootDownloadable) {
6365
$message = $event->allowPartialArchive ? 'Access to this resource and its children has been denied.' : 'Access to this resource or one of its sub-items has been denied.';

apps/files_sharing/lib/ViewOnly.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
class ViewOnly {
2020

2121
public function __construct(
22-
private Folder $userFolder,
22+
private ?Folder $userFolder,
2323
) {
2424
}
2525

@@ -28,6 +28,10 @@ public function __construct(
2828
* @return bool
2929
*/
3030
public function check(array $pathsToCheck): bool {
31+
if ($this->userFolder === null) {
32+
throw new \LogicException('The user folder is not set but this check requires it.');
33+
}
34+
3135
// If any of elements cannot be downloaded, prevent whole download
3236
foreach ($pathsToCheck as $file) {
3337
try {

apps/files_sharing/tests/ApplicationTest.php

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ public function testCheckDirectCanBeDownloaded(
8181

8282
$userFolder = $this->createMock(Folder::class);
8383
$userFolder->method('get')->willReturn($file);
84+
$userFolder->method('getPath')->willReturn($path);
8485

8586
$user = $this->createMock(IUser::class);
8687
$user->method('getUID')->willReturn('test');
@@ -93,7 +94,7 @@ public function testCheckDirectCanBeDownloaded(
9394
$listener = new BeforeDirectFileDownloadListener(
9495
$this->userSession,
9596
$this->rootFolder,
96-
new ViewOnly(),
97+
new ViewOnly($userFolder),
9798
);
9899
$listener->handle($event);
99100

@@ -139,6 +140,7 @@ public function testCheckZipCanBeDownloaded(
139140
$secureSharedStorage->method('getShare')->willReturn($secureReceiverFileShare);
140141

141142
$folder = $this->createMock(Folder::class);
143+
$folder->method('getPath')->willReturn($dir);
142144
if ($folderStorage === 'nonSharedStorage') {
143145
$folder->method('getStorage')->willReturn($nonSharedStorage);
144146
} elseif ($folderStorage === 'secureSharedStorage') {
@@ -202,7 +204,7 @@ public function testCheckFileUserNotFound(): void {
202204
$this->userSession->method('isLoggedIn')->willReturn(false);
203205

204206
// Simulate zip download of folder folder
205-
$event = new BeforeZipCreatedEvent('/test', ['test.txt'], []);
207+
$event = new BeforeZipCreatedEvent('/test', ['test.txt']);
206208
$listener = new BeforeZipCreatedListener(
207209
$this->userSession,
208210
$this->rootFolder,

apps/files_sharing/tests/unit/Listener/BeforeZipCreatedListenerTest.php

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ public static function dataHandle(): array {
6363
'partial archive disabled, no filtering, 1 blocked 1 non-blocked file => should fail event' => [
6464
'folderPath' => $rootFromFolder,
6565
'rootDownloadable' => true,
66-
'files' => ["blocked.txt" => false, "allowed.txt" => true],
66+
'files' => ['blocked.txt' => false, 'allowed.txt' => true],
6767
'filesFilter' => [],
6868
'allowPartialArchive' => false,
6969
'expectedSuccess' => false,
@@ -186,15 +186,16 @@ public function testHandle(
186186
): void {
187187
$fileNodes = [];
188188
$fileNodesByName = [];
189+
$folderPathFromUserRoot = "/user/files{$folderPath}";
189190
foreach ($files as $relativePath => $downloadable) {
190-
$pathWithFolder = "{$folderPath}/{$relativePath}";
191+
$pathWithFolder = "{$folderPathFromUserRoot}/{$relativePath}";
191192
$file = $this->createSharedFile($downloadable, $pathWithFolder);
192-
$fileNodesByName[$pathWithFolder] = $file;
193+
$fileNodesByName[$relativePath] = $file;
193194
$fileNodes[] = $file;
194195
}
195-
$folderPathFromUserRoot = "/user/files{$folderPath}";
196196
$folder = $this->createSharedFolder($rootDownloadable, $folderPathFromUserRoot, $fileNodes);
197197
$this->userFolder->method('get')->willReturnCallback(function (string $path) use ($folderPath, $fileNodesByName, $folder) {
198+
$path = str_replace($folderPath . '/', '', $path);
198199
return match (true) {
199200
$path === $folderPath => $folder,
200201
isset($fileNodesByName[$path]) => $fileNodesByName[$path],
@@ -209,18 +210,18 @@ public function testHandle(
209210
$this->assertSame($expectedMessage, $event->getErrorMessage());
210211

211212
$event->setNodesIterable($fileNodes);
212-
$actualNodes = iterator_to_array($event->getNodes());
213+
$actualNodes = iterator_to_array($event->getNodes($folderPathFromUserRoot));
213214
$this->assertCount(count($expectedNodeList), $actualNodes);
214215
foreach ($expectedNodeList as $relativePath => $expectedValue) {
215-
$path = "{$folderPath}/{$relativePath}";
216+
$path = "$relativePath";
216217
if ($expectedValue === null) {
217218
// cannot reference the node in the data provider, add it here
218219
$node = $fileNodesByName[$path] ?? null;
219220
$this->assertNotNull($node, 'Node mock must be present for the test to be correct.');
220221
$expectedValue = [$node, null];
221222
}
222223

223-
$this->assertEquals($expectedValue, $actualNodes[$path]);
224+
$this->assertEquals($expectedValue, $actualNodes[$path] ?? []);
224225
}
225226
}
226227

lib/public/Files/Events/BeforeZipCreatedEvent.php

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ class BeforeZipCreatedEvent extends Event {
4646
public function __construct(
4747
string|Folder $directory,
4848
private array $files,
49-
public ?bool $allowPartialArchive = true,
49+
public ?bool $allowPartialArchive = false,
5050
) {
5151
parent::__construct();
5252
if ($directory instanceof Folder) {
@@ -119,8 +119,8 @@ public function setNodesIterable(iterable $iterable): void {
119119

120120
/**
121121
* @param callable(Node): array{0: bool, 1: ?string} $filter filter that
122-
* receives a Node and returns an array with a bool telling if the file is
123-
* to be included in the archive and an optional reason string.
122+
* receives a Node and returns an array with a bool telling if the file is
123+
* to be included in the archive and an optional reason string.
124124
*
125125
* @return void
126126
*/
@@ -136,7 +136,7 @@ public function addNodeFilter(callable $filter): void {
136136
*
137137
* @return iterable<string, array{0: ?Node, 1: ?string}>
138138
*/
139-
public function getNodes(): iterable {
139+
public function getNodes(string $rootPath): iterable {
140140
if (!isset($this->nodesIterable)) {
141141
throw new \LogicException('No nodes iterable set');
142142
}
@@ -145,10 +145,12 @@ public function getNodes(): iterable {
145145
return;
146146
}
147147

148-
$directory = $this->getDirectory();
149148
foreach ($this->nodesIterable as $node) {
150-
$relativePath = $directory . '/' . $node->getName();
151-
if (!empty($this->files) && !in_array($node->getName(), $this->files)) {
149+
$relativePath = trim(str_replace($rootPath, '', $node->getPath()), '/');
150+
$parent = rtrim(substr($relativePath, 0, strpos($relativePath, '/', 1) ?: 0), '/');
151+
// we need to filter by file name or first parent folder
152+
$filterName = $parent === '' ? $node->getName() : $parent;
153+
if (!empty($this->files) && !in_array($filterName, $this->files)) {
152154
// the node is supposed to be filtered out
153155
continue;
154156
}

0 commit comments

Comments
 (0)