Skip to content

Commit d26c098

Browse files
authored
Merge pull request #33416 from nextcloud/backport/32482/stable24
[stable24] Add share attributes + prevent download permission
2 parents 6088f72 + 843b54e commit d26c098

File tree

59 files changed

+1935
-136
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

59 files changed

+1935
-136
lines changed

apps/dav/appinfo/v1/publicwebdav.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@
2929
* along with this program. If not, see <http://www.gnu.org/licenses/>
3030
*
3131
*/
32+
33+
use Psr\Log\LoggerInterface;
34+
3235
// load needed apps
3336
$RUNTIME_APPTYPES = ['filesystem', 'authentication', 'logging'];
3437

@@ -49,6 +52,7 @@
4952
$serverFactory = new OCA\DAV\Connector\Sabre\ServerFactory(
5053
\OC::$server->getConfig(),
5154
\OC::$server->getLogger(),
55+
\OC::$server->get(LoggerInterface::class),
5256
\OC::$server->getDatabaseConnection(),
5357
\OC::$server->getUserSession(),
5458
\OC::$server->getMountManager(),

apps/dav/appinfo/v1/webdav.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@
2828
* along with this program. If not, see <http://www.gnu.org/licenses/>
2929
*
3030
*/
31+
32+
use Psr\Log\LoggerInterface;
33+
3134
// no php execution timeout for webdav
3235
if (strpos(@ini_get('disable_functions'), 'set_time_limit') === false) {
3336
@set_time_limit(0);
@@ -40,6 +43,7 @@
4043
$serverFactory = new \OCA\DAV\Connector\Sabre\ServerFactory(
4144
\OC::$server->getConfig(),
4245
\OC::$server->getLogger(),
46+
\OC::$server->get(LoggerInterface::class),
4347
\OC::$server->getDatabaseConnection(),
4448
\OC::$server->getUserSession(),
4549
\OC::$server->getMountManager(),

apps/dav/composer/composer/autoload_classmap.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,7 @@
191191
'OCA\\DAV\\DAV\\Sharing\\Xml\\Invite' => $baseDir . '/../lib/DAV/Sharing/Xml/Invite.php',
192192
'OCA\\DAV\\DAV\\Sharing\\Xml\\ShareRequest' => $baseDir . '/../lib/DAV/Sharing/Xml/ShareRequest.php',
193193
'OCA\\DAV\\DAV\\SystemPrincipalBackend' => $baseDir . '/../lib/DAV/SystemPrincipalBackend.php',
194+
'OCA\\DAV\\DAV\\ViewOnlyPlugin' => $baseDir . '/../lib/DAV/ViewOnlyPlugin.php',
194195
'OCA\\DAV\\Db\\Direct' => $baseDir . '/../lib/Db/Direct.php',
195196
'OCA\\DAV\\Db\\DirectMapper' => $baseDir . '/../lib/Db/DirectMapper.php',
196197
'OCA\\DAV\\Direct\\DirectFile' => $baseDir . '/../lib/Direct/DirectFile.php',

apps/dav/composer/composer/autoload_static.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,7 @@ class ComposerStaticInitDAV
206206
'OCA\\DAV\\DAV\\Sharing\\Xml\\Invite' => __DIR__ . '/..' . '/../lib/DAV/Sharing/Xml/Invite.php',
207207
'OCA\\DAV\\DAV\\Sharing\\Xml\\ShareRequest' => __DIR__ . '/..' . '/../lib/DAV/Sharing/Xml/ShareRequest.php',
208208
'OCA\\DAV\\DAV\\SystemPrincipalBackend' => __DIR__ . '/..' . '/../lib/DAV/SystemPrincipalBackend.php',
209+
'OCA\\DAV\\DAV\\ViewOnlyPlugin' => __DIR__ . '/..' . '/../lib/DAV/ViewOnlyPlugin.php',
209210
'OCA\\DAV\\Db\\Direct' => __DIR__ . '/..' . '/../lib/Db/Direct.php',
210211
'OCA\\DAV\\Db\\DirectMapper' => __DIR__ . '/..' . '/../lib/Db/DirectMapper.php',
211212
'OCA\\DAV\\Direct\\DirectFile' => __DIR__ . '/..' . '/../lib/Direct/DirectFile.php',

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ class FilesPlugin extends ServerPlugin {
6464
public const PERMISSIONS_PROPERTYNAME = '{http://owncloud.org/ns}permissions';
6565
public const SHARE_PERMISSIONS_PROPERTYNAME = '{http://open-collaboration-services.org/ns}share-permissions';
6666
public const OCM_SHARE_PERMISSIONS_PROPERTYNAME = '{http://open-cloud-mesh.org/ns}share-permissions';
67+
public const SHARE_ATTRIBUTES_PROPERTYNAME = '{http://nextcloud.org/ns}share-attributes';
6768
public const DOWNLOADURL_PROPERTYNAME = '{http://owncloud.org/ns}downloadURL';
6869
public const SIZE_PROPERTYNAME = '{http://owncloud.org/ns}size';
6970
public const GETETAG_PROPERTYNAME = '{DAV:}getetag';
@@ -172,6 +173,7 @@ public function initialize(\Sabre\DAV\Server $server) {
172173
$server->protectedProperties[] = self::PERMISSIONS_PROPERTYNAME;
173174
$server->protectedProperties[] = self::SHARE_PERMISSIONS_PROPERTYNAME;
174175
$server->protectedProperties[] = self::OCM_SHARE_PERMISSIONS_PROPERTYNAME;
176+
$server->protectedProperties[] = self::SHARE_ATTRIBUTES_PROPERTYNAME;
175177
$server->protectedProperties[] = self::SIZE_PROPERTYNAME;
176178
$server->protectedProperties[] = self::DOWNLOADURL_PROPERTYNAME;
177179
$server->protectedProperties[] = self::OWNER_ID_PROPERTYNAME;
@@ -359,6 +361,10 @@ public function handleGetProperties(PropFind $propFind, \Sabre\DAV\INode $node)
359361
return json_encode($ocmPermissions);
360362
});
361363

364+
$propFind->handle(self::SHARE_ATTRIBUTES_PROPERTYNAME, function () use ($node, $httpRequest): string {
365+
return json_encode($node->getShareAttributes());
366+
});
367+
362368
$propFind->handle(self::GETETAG_PROPERTYNAME, function () use ($node) {
363369
return $node->getETag();
364370
});

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

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
use OC\Files\Mount\MoveableMount;
3939
use OC\Files\Node\File;
4040
use OC\Files\Node\Folder;
41+
use OC\Files\Storage\Wrapper\Wrapper;
4142
use OC\Files\View;
4243
use OCA\DAV\Connector\Sabre\Exception\InvalidPath;
4344
use OCP\Files\FileInfo;
@@ -322,6 +323,31 @@ public function getSharePermissions($user) {
322323
return $permissions;
323324
}
324325

326+
/**
327+
* @return array
328+
*/
329+
public function getShareAttributes(): array {
330+
$attributes = [];
331+
332+
try {
333+
$storage = $this->info->getStorage();
334+
} catch (StorageNotAvailableException $e) {
335+
$storage = null;
336+
}
337+
338+
if ($storage && $storage->instanceOfStorage(\OCA\Files_Sharing\SharedStorage::class)) {
339+
/** @var \OCA\Files_Sharing\SharedStorage $storage */
340+
$attributes = $storage->getShare()->getAttributes();
341+
if ($attributes === null) {
342+
return [];
343+
} else {
344+
return $attributes->toArray();
345+
}
346+
}
347+
348+
return $attributes;
349+
}
350+
325351
/**
326352
* @param string $user
327353
* @return string

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333

3434
use OCP\Files\Folder;
3535
use OCA\DAV\AppInfo\PluginManager;
36+
use OCA\DAV\DAV\ViewOnlyPlugin;
3637
use OCA\DAV\Files\BrowserErrorPagePlugin;
3738
use OCP\Files\Mount\IMountManager;
3839
use OCP\IConfig;
@@ -44,6 +45,7 @@
4445
use OCP\ITagManager;
4546
use OCP\IUserSession;
4647
use OCP\SabrePluginEvent;
48+
use Psr\Log\LoggerInterface;
4749
use Sabre\DAV\Auth\Plugin;
4850
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
4951

@@ -52,6 +54,8 @@ class ServerFactory {
5254
private $config;
5355
/** @var ILogger */
5456
private $logger;
57+
/** @var LoggerInterface */
58+
private $psrLogger;
5559
/** @var IDBConnection */
5660
private $databaseConnection;
5761
/** @var IUserSession */
@@ -72,6 +76,7 @@ class ServerFactory {
7276
/**
7377
* @param IConfig $config
7478
* @param ILogger $logger
79+
* @param LoggerInterface $psrLogger
7580
* @param IDBConnection $databaseConnection
7681
* @param IUserSession $userSession
7782
* @param IMountManager $mountManager
@@ -82,6 +87,7 @@ class ServerFactory {
8287
public function __construct(
8388
IConfig $config,
8489
ILogger $logger,
90+
LoggerInterface $psrLogger,
8591
IDBConnection $databaseConnection,
8692
IUserSession $userSession,
8793
IMountManager $mountManager,
@@ -93,6 +99,7 @@ public function __construct(
9399
) {
94100
$this->config = $config;
95101
$this->logger = $logger;
102+
$this->psrLogger = $psrLogger;
96103
$this->databaseConnection = $databaseConnection;
97104
$this->userSession = $userSession;
98105
$this->mountManager = $mountManager;
@@ -182,6 +189,11 @@ public function createServer($baseUri,
182189
$server->addPlugin(new \OCA\DAV\Connector\Sabre\QuotaPlugin($view, true));
183190
$server->addPlugin(new \OCA\DAV\Connector\Sabre\ChecksumUpdatePlugin());
184191

192+
// Allow view-only plugin for webdav requests
193+
$server->addPlugin(new ViewOnlyPlugin(
194+
$this->psrLogger
195+
));
196+
185197
if ($this->userSession->isLoggedIn()) {
186198
$server->addPlugin(new \OCA\DAV\Connector\Sabre\TagsPlugin($objectTree, $this->tagManager));
187199
$server->addPlugin(new \OCA\DAV\Connector\Sabre\SharesPlugin(

apps/dav/lib/Controller/DirectController.php

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,12 @@
3131
use OCP\AppFramework\Http\DataResponse;
3232
use OCP\AppFramework\OCS\OCSBadRequestException;
3333
use OCP\AppFramework\OCS\OCSNotFoundException;
34+
use OCP\AppFramework\OCS\OCSForbiddenException;
3435
use OCP\AppFramework\OCSController;
3536
use OCP\AppFramework\Utility\ITimeFactory;
37+
use OCP\EventDispatcher\GenericEvent;
38+
use OCP\EventDispatcher\IEventDispatcher;
39+
use OCP\Files\Events\BeforeDirectFileDownloadEvent;
3640
use OCP\Files\File;
3741
use OCP\Files\IRootFolder;
3842
use OCP\IRequest;
@@ -59,6 +63,8 @@ class DirectController extends OCSController {
5963
/** @var IURLGenerator */
6064
private $urlGenerator;
6165

66+
/** @var IEventDispatcher */
67+
private $eventDispatcher;
6268

6369
public function __construct(string $appName,
6470
IRequest $request,
@@ -67,7 +73,8 @@ public function __construct(string $appName,
6773
DirectMapper $mapper,
6874
ISecureRandom $random,
6975
ITimeFactory $timeFactory,
70-
IURLGenerator $urlGenerator) {
76+
IURLGenerator $urlGenerator,
77+
IEventDispatcher $eventDispatcher) {
7178
parent::__construct($appName, $request);
7279

7380
$this->rootFolder = $rootFolder;
@@ -76,6 +83,7 @@ public function __construct(string $appName,
7683
$this->random = $random;
7784
$this->timeFactory = $timeFactory;
7885
$this->urlGenerator = $urlGenerator;
86+
$this->eventDispatcher = $eventDispatcher;
7987
}
8088

8189
/**
@@ -99,6 +107,13 @@ public function getUrl(int $fileId, int $expirationTime = 60 * 60 * 8): DataResp
99107
throw new OCSBadRequestException('Direct download only works for files');
100108
}
101109

110+
$event = new BeforeDirectFileDownloadEvent($userFolder->getRelativePath($file->getPath()));
111+
$this->eventDispatcher->dispatchTyped($event);
112+
113+
if ($event->isSuccessful() === false) {
114+
throw new OCSForbiddenException('Permission denied to download file');
115+
}
116+
102117
//TODO: at some point we should use the directdownlaod function of storages
103118
$direct = new Direct();
104119
$direct->setUserId($this->userId);
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
<?php
2+
/**
3+
* @author Piotr Mrowczynski piotr@owncloud.com
4+
*
5+
* @copyright Copyright (c) 2019, ownCloud GmbH
6+
* @license AGPL-3.0
7+
*
8+
* This code is free software: you can redistribute it and/or modify
9+
* it under the terms of the GNU Affero General Public License, version 3,
10+
* as published by the Free Software Foundation.
11+
*
12+
* This program is distributed in the hope that it will be useful,
13+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+
* GNU Affero General Public License for more details.
16+
*
17+
* You should have received a copy of the GNU Affero General Public License, version 3,
18+
* along with this program. If not, see <http://www.gnu.org/licenses/>
19+
*
20+
*/
21+
22+
namespace OCA\DAV\DAV;
23+
24+
use OCA\DAV\Connector\Sabre\Exception\Forbidden;
25+
use OCA\DAV\Connector\Sabre\File as DavFile;
26+
use OCA\DAV\Meta\MetaFile;
27+
use OCP\Files\FileInfo;
28+
use OCP\Files\NotFoundException;
29+
use Psr\Log\LoggerInterface;
30+
use Sabre\DAV\Server;
31+
use Sabre\DAV\ServerPlugin;
32+
use Sabre\HTTP\RequestInterface;
33+
use Sabre\DAV\Exception\NotFound;
34+
35+
/**
36+
* Sabre plugin for restricting file share receiver download:
37+
*/
38+
class ViewOnlyPlugin extends ServerPlugin {
39+
40+
private ?Server $server = null;
41+
private LoggerInterface $logger;
42+
43+
public function __construct(LoggerInterface $logger) {
44+
$this->logger = $logger;
45+
}
46+
47+
/**
48+
* This initializes the plugin.
49+
*
50+
* This function is called by Sabre\DAV\Server, after
51+
* addPlugin is called.
52+
*
53+
* This method should set up the required event subscriptions.
54+
*/
55+
public function initialize(Server $server): void {
56+
$this->server = $server;
57+
//priority 90 to make sure the plugin is called before
58+
//Sabre\DAV\CorePlugin::httpGet
59+
$this->server->on('method:GET', [$this, 'checkViewOnly'], 90);
60+
}
61+
62+
/**
63+
* Disallow download via DAV Api in case file being received share
64+
* and having special permission
65+
*
66+
* @throws Forbidden
67+
* @throws NotFoundException
68+
*/
69+
public function checkViewOnly(RequestInterface $request): bool {
70+
$path = $request->getPath();
71+
72+
try {
73+
assert($this->server !== null);
74+
$davNode = $this->server->tree->getNodeForPath($path);
75+
if (!($davNode instanceof DavFile)) {
76+
return true;
77+
}
78+
// Restrict view-only to nodes which are shared
79+
$node = $davNode->getNode();
80+
81+
$storage = $node->getStorage();
82+
83+
if (!$storage->instanceOfStorage(\OCA\Files_Sharing\SharedStorage::class)) {
84+
return true;
85+
}
86+
// Extract extra permissions
87+
/** @var \OCA\Files_Sharing\SharedStorage $storage */
88+
$share = $storage->getShare();
89+
90+
$attributes = $share->getAttributes();
91+
if ($attributes === null) {
92+
return true;
93+
}
94+
95+
// Check if read-only and on whether permission can download is both set and disabled.
96+
$canDownload = $attributes->getAttribute('permissions', 'download');
97+
if ($canDownload !== null && !$canDownload) {
98+
throw new Forbidden('Access to this resource has been denied because it is in view-only mode.');
99+
}
100+
} catch (NotFound $e) {
101+
$this->logger->warning($e->getMessage(), [
102+
'exception' => $e,
103+
]);
104+
}
105+
106+
return true;
107+
}
108+
}

apps/dav/lib/Server.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@
6666
use OCA\DAV\Connector\Sabre\TagsPlugin;
6767
use OCA\DAV\DAV\CustomPropertiesBackend;
6868
use OCA\DAV\DAV\PublicAuth;
69+
use OCA\DAV\DAV\ViewOnlyPlugin;
6970
use OCA\DAV\Events\SabrePluginAuthInitEvent;
7071
use OCA\DAV\Files\BrowserErrorPagePlugin;
7172
use OCA\DAV\Files\LazySearchBackend;
@@ -229,6 +230,11 @@ public function __construct(IRequest $request, string $baseUri) {
229230
$this->server->addPlugin(new FakeLockerPlugin());
230231
}
231232

233+
// Allow view-only plugin for webdav requests
234+
$this->server->addPlugin(new ViewOnlyPlugin(
235+
\OC::$server->get(LoggerInterface::class)
236+
));
237+
232238
if (BrowserErrorPagePlugin::isBrowserRequest($request)) {
233239
$this->server->addPlugin(new BrowserErrorPagePlugin());
234240
}

0 commit comments

Comments
 (0)