Skip to content

Commit 1b72bef

Browse files
authored
Merge pull request #241 from nextcloud/feat/dav
feat: Provide limit via dav property
2 parents e655a79 + 2962467 commit 1b72bef

7 files changed

Lines changed: 277 additions & 2 deletions

File tree

README.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,41 @@ jsonpath "$.ocs.data.limit" exists
4040
jsonpath "$.ocs.data.count" exists
4141
```
4242

43+
#### PropFind
44+
45+
```sh
46+
hurl propfind.hurl --variable owner=admin --variable path=/welcome.txt
47+
```
48+
49+
> propfind.hurl
50+
````hurl
51+
PROPFIND https://nextcloud.local/remote.php/dav/files/{{owner}}/{{path}}
52+
53+
[BasicAuth]
54+
{{owner}}: {{owner}}
55+
56+
```xml
57+
<?xml version="1.0"?>
58+
<d:propfind xmlns:d="DAV:" xmlns:oc="http://owncloud.org/ns" xmlns:nc="http://nextcloud.org/ns">
59+
<d:prop>
60+
<oc:fileid />
61+
<d:displayname />
62+
<d:getlastmodified />
63+
<d:getcontenttype />
64+
<oc:size />
65+
<oc:owner-id />
66+
<oc:share-types />
67+
<nc:sharees />
68+
<nc:share-download-limits />
69+
</d:prop>
70+
</d:propfind>
71+
```
72+
73+
HTTP 207
74+
[Asserts]
75+
xpath "//nc:share-download-limits" isCollection
76+
````
77+
4378
#### Set
4479

4580
```sh

lib/AppInfo/Application.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,12 @@
2727

2828
namespace OCA\Files_DownloadLimit\AppInfo;
2929

30+
use OCA\DAV\Events\SabrePluginAddEvent;
3031
use OCA\Files\Event\LoadSidebar;
3132
use OCA\Files_DownloadLimit\Capabilities;
3233
use OCA\Files_DownloadLimit\Listener\BeforeTemplateRenderedListener;
3334
use OCA\Files_DownloadLimit\Listener\LoadSidebarListener;
35+
use OCA\Files_DownloadLimit\Listener\SabrePluginAddListener;
3436
use OCA\Files_DownloadLimit\Listener\ShareLinkAccessedListener;
3537
use OCA\Files_Sharing\Event\BeforeTemplateRenderedEvent;
3638
use OCA\Files_Sharing\Event\ShareLinkAccessedEvent;
@@ -47,6 +49,8 @@ public function __construct(array $urlParams = []) {
4749
}
4850

4951
public function register(IRegistrationContext $context): void {
52+
$context->registerEventListener(SabrePluginAddEvent::class, SabrePluginAddListener::class);
53+
5054
$context->registerEventListener(BeforeTemplateRenderedEvent::class, BeforeTemplateRenderedListener::class);
5155
$context->registerEventListener(LoadSidebar::class, LoadSidebarListener::class);
5256
$context->registerEventListener(ShareLinkAccessedEvent::class, ShareLinkAccessedListener::class);

lib/Dav/PropFindPlugin.php

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* @copyright 2024 Christopher Ng <[email protected]>
7+
*
8+
* @author Christopher Ng <[email protected]>
9+
*
10+
* @license GNU AGPL version 3 or any later version
11+
*
12+
* This program is free software: you can redistribute it and/or modify
13+
* it under the terms of the GNU Affero General Public License as
14+
* published by the Free Software Foundation, either version 3 of the
15+
* License, or (at your option) any later version.
16+
*
17+
* This program is distributed in the hope that it will be useful,
18+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
19+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20+
* GNU Affero General Public License for more details.
21+
*
22+
* You should have received a copy of the GNU Affero General Public License
23+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
24+
*
25+
*/
26+
27+
namespace OCA\Files_DownloadLimit\Dav;
28+
29+
use OCA\DAV\Connector\Sabre\File as SabreFile;
30+
use OCA\Files_DownloadLimit\Db\Limit;
31+
use OCA\Files_DownloadLimit\Db\LimitMapper;
32+
use OCA\Files_DownloadLimit\LimitList;
33+
use OCP\AppFramework\Db\DoesNotExistException;
34+
use OCP\Files\File;
35+
use OCP\IUser;
36+
use OCP\IUserSession;
37+
use OCP\Share\IManager as IShareManager;
38+
use OCP\Share\IShare;
39+
use Psr\Log\LoggerInterface;
40+
use Sabre\DAV\INode;
41+
use Sabre\DAV\PropFind;
42+
use Sabre\DAV\Server;
43+
use Sabre\DAV\ServerPlugin;
44+
45+
class PropFindPlugin extends ServerPlugin {
46+
public const DOWNLOAD_LIMITS_PROPERTY = '{http://nextcloud.org/ns}share-download-limits';
47+
48+
public function __construct(
49+
private IShareManager $shareManager,
50+
private IUserSession $userSession,
51+
private LimitMapper $limitMapper,
52+
private LoggerInterface $logger,
53+
) {
54+
}
55+
56+
public function initialize(Server $server): void {
57+
$server->on('propFind', [$this, 'propFind']);
58+
}
59+
60+
public function propFind(PropFind $propFind, INode $node) {
61+
if (!in_array(static::DOWNLOAD_LIMITS_PROPERTY, $propFind->getRequestedProperties(), true)) {
62+
return;
63+
}
64+
65+
if (!($node instanceof SabreFile)) { // Only allowed on files
66+
return;
67+
}
68+
69+
$propFind->handle(
70+
static::DOWNLOAD_LIMITS_PROPERTY,
71+
function () use ($node) {
72+
$user = $this->userSession->getUser();
73+
if (!($user instanceof IUser)) {
74+
return new LimitList([]);
75+
}
76+
77+
$externalShares = $this->getSharesForTypes(
78+
$user,
79+
$node->getNode(),
80+
[
81+
IShare::TYPE_LINK,
82+
IShare::TYPE_EMAIL,
83+
],
84+
);
85+
86+
/** @var Limit[] $limits */
87+
$limits = array_values(array_filter(array_map(function (IShare $share) {
88+
try {
89+
return $this->limitMapper->get($share->getToken());
90+
} catch (DoesNotExistException $e) {
91+
// Allow as no limit has been set
92+
return null;
93+
}
94+
}, $externalShares)));
95+
96+
return new LimitList($limits);
97+
},
98+
);
99+
}
100+
101+
/**
102+
* @param int[] $types
103+
* @return IShare[]
104+
*/
105+
private function getSharesForTypes(IUser $user, File $file, array $types) {
106+
/** @var IShare[] $shares */
107+
$shares = [];
108+
foreach ($types as $type) {
109+
$shares = array_merge($shares, $this->shareManager->getSharesBy($user->getUID(), $type, $file, false, -1, 0));
110+
}
111+
return $shares;
112+
}
113+
}

lib/LimitList.php

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* @copyright 2024 Christopher Ng <[email protected]>
7+
*
8+
* @author Christopher Ng <[email protected]>
9+
*
10+
* @license GNU AGPL version 3 or any later version
11+
*
12+
* This program is free software: you can redistribute it and/or modify
13+
* it under the terms of the GNU Affero General Public License as
14+
* published by the Free Software Foundation, either version 3 of the
15+
* License, or (at your option) any later version.
16+
*
17+
* This program is distributed in the hope that it will be useful,
18+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
19+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20+
* GNU Affero General Public License for more details.
21+
*
22+
* You should have received a copy of the GNU Affero General Public License
23+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
24+
*
25+
*/
26+
27+
namespace OCA\Files_DownloadLimit;
28+
29+
use OCA\Files_DownloadLimit\Db\Limit;
30+
use Sabre\Xml\Writer;
31+
use Sabre\Xml\XmlSerializable;
32+
33+
class LimitList implements XmlSerializable {
34+
public const NS_NEXTCLOUD = 'http://nextcloud.org/ns';
35+
36+
/** @var Limit[] */
37+
private $limits;
38+
39+
/**
40+
* @param Limit[] $limits
41+
*/
42+
public function __construct(
43+
array $limits,
44+
) {
45+
$this->limits = $limits;
46+
}
47+
48+
public function xmlSerialize(Writer $writer): void {
49+
foreach ($this->limits as $limit) {
50+
$writer->startElement('{' . static::NS_NEXTCLOUD . '}share-download-limit');
51+
$writer->writeElement('{' . static::NS_NEXTCLOUD . '}token', $limit->getId());
52+
$writer->writeElement('{' . static::NS_NEXTCLOUD . '}limit', $limit->getLimit());
53+
$writer->writeElement('{' . static::NS_NEXTCLOUD . '}count', $limit->getDownloads());
54+
$writer->endElement();
55+
}
56+
}
57+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* @copyright 2024 Christopher Ng <[email protected]>
7+
*
8+
* @author Christopher Ng <[email protected]>
9+
*
10+
* @license GNU AGPL version 3 or any later version
11+
*
12+
* This program is free software: you can redistribute it and/or modify
13+
* it under the terms of the GNU Affero General Public License as
14+
* published by the Free Software Foundation, either version 3 of the
15+
* License, or (at your option) any later version.
16+
*
17+
* This program is distributed in the hope that it will be useful,
18+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
19+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20+
* GNU Affero General Public License for more details.
21+
*
22+
* You should have received a copy of the GNU Affero General Public License
23+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
24+
*
25+
*/
26+
27+
namespace OCA\Files_DownloadLimit\Listener;
28+
29+
use OCA\DAV\Events\SabrePluginAddEvent;
30+
use OCA\Files_DownloadLimit\Dav\PropFindPlugin;
31+
use OCP\EventDispatcher\Event;
32+
use OCP\EventDispatcher\IEventListener;
33+
use Psr\Container\ContainerInterface;
34+
35+
/** @template-implements IEventListener<SabrePluginAddEvent> */
36+
class SabrePluginAddListener implements IEventListener {
37+
public function __construct(
38+
private ContainerInterface $container,
39+
) {
40+
}
41+
42+
public function handle(Event $event): void {
43+
if (!($event instanceof SabrePluginAddEvent)) {
44+
return;
45+
}
46+
47+
$server = $event->getServer();
48+
$plugin = $this->container->get(PropFindPlugin::class);
49+
$server->addPlugin($plugin);
50+
}
51+
}

psalm.xml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,17 +19,21 @@
1919
<issueHandlers>
2020
<UndefinedClass>
2121
<errorLevel type="suppress">
22+
<referencedClass name="Doctrine\DBAL\Types\Types" />
23+
<referencedClass name="OCA\DAV\Events\SabrePluginAddEvent" />
2224
<referencedClass name="OCA\Files_Sharing\Event\BeforeTemplateRenderedEvent" />
2325
<referencedClass name="OCA\Files_Sharing\Event\ShareLinkAccessedEvent" />
24-
<referencedClass name="Doctrine\DBAL\Types\Types" />
2526
<referencedClass name="OCA\Files\Event\LoadSidebar" />
27+
<referencedClass name="Sabre\DAV\ServerPlugin" />
28+
<referencedClass name="Sabre\Xml\XmlSerializable" />
2629
</errorLevel>
2730
</UndefinedClass>
2831
<UndefinedDocblockClass>
2932
<errorLevel type="suppress">
33+
<referencedClass name="Doctrine\DBAL\Schema\Table" />
34+
<referencedClass name="OCA\DAV\Events\SabrePluginAddEvent" />
3035
<referencedClass name="OCA\Files_Sharing\Event\BeforeTemplateRenderedEvent" />
3136
<referencedClass name="OCA\Files_Sharing\Event\ShareLinkAccessedEvent" />
32-
<referencedClass name="Doctrine\DBAL\Schema\Table" />
3337
<referencedClass name="OCA\Files\Event\LoadSidebar" />
3438
</errorLevel>
3539
</UndefinedDocblockClass>

tests/psalm-baseline.xml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,15 @@
2424
<code>$event</code>
2525
</ImplementedParamTypeMismatch>
2626
</file>
27+
<file src="lib/Listener/SabrePluginAddListener.php">
28+
<InvalidTemplateParam>
29+
<code>IEventListener</code>
30+
</InvalidTemplateParam>
31+
<ImplementedParamTypeMismatch>
32+
<code>$event</code>
33+
</ImplementedParamTypeMismatch>
34+
<MissingDependency>
35+
<code>PropFindPlugin</code>
36+
</MissingDependency>
37+
</file>
2738
</files>

0 commit comments

Comments
 (0)