X Tutup
Skip to content

Commit 9fc9ea8

Browse files
author
David Christofas
committed
pre-signed download urls for password protected public links
To support clients which don't use cookies I implemented pre-signed urls for password protected public links. The share password is used as the signing key and the signed url is then added to the propfind response in the field `downloadURL` which was added before but never used. This change allows owncloud Web to implement a more efficient download mechanism for password protected link shares.
1 parent d98b71c commit 9fc9ea8

File tree

5 files changed

+128
-0
lines changed

5 files changed

+128
-0
lines changed

apps/dav/lib/Files/PublicFiles/PublicFilesPlugin.php

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,25 @@ public function propFind(PropFind $propFind, INode $node) {
148148
return $node->getNode()->getSize();
149149
});
150150
}
151+
$server = $this->server;
152+
$propFind->handle(FilesPlugin::DOWNLOADURL_PROPERTYNAME, static function () use ($node, $server) {
153+
$path = $server->getBaseUri() . $server->getRequestUri();
154+
$nodeName = $node->getName();
155+
if (\substr($path, -\strlen($nodeName)) !== $nodeName) {
156+
$path .= '/' . $nodeName;
157+
}
158+
159+
$share = $node->getShare();
160+
if ($share->getPassword() === null) {
161+
return $path;
162+
}
163+
164+
$validUntil = new \DateTime();
165+
$validUntil->add(new \DateInterval("PT1H")); // valid for 1 hour
166+
$key = \hash_hkdf('sha256', $share->getPassword());
167+
$s = new PublicShareSignature($share->getToken(), $node->getName(), $validUntil, $key);
168+
return $path . '?signature=' . $s->get() . '&expires=' . \urlencode($validUntil->format(\DateTime::ATOM));
169+
});
151170
}
152171

153172
/**
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<?php
2+
/**
3+
* @author David Christofas <dchristofas@owncloud.com>
4+
*
5+
* @copyright Copyright (c) 2021, 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\Files\PublicFiles;
23+
24+
class PublicShareSignature {
25+
private $token;
26+
private $fileName;
27+
private $validUntil;
28+
private $signingKey;
29+
30+
public function __construct(String $token, String $fileName, \DateTime $validUntil, String $signingKey) {
31+
$this->token = $token;
32+
$this->fileName = $fileName;
33+
$this->validUntil = $validUntil->format(\DateTime::ATOM);
34+
$this->signingKey = $signingKey;
35+
}
36+
37+
public function get() {
38+
return \hash_hmac('sha512/256', \implode('|', [$this->token, $this->fileName, $this->validUntil]), $this->signingKey, false);
39+
}
40+
41+
public function verify($signature) {
42+
$sig = $this->get();
43+
return $sig === $signature;
44+
}
45+
}

apps/dav/lib/Files/PublicFiles/PublicSharingAuth.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,24 @@ public function check(RequestInterface $request, ResponseInterface $response) {
101101
return [true, 'principals/system/public'];
102102
}
103103

104+
// Clients which don't use cookie based session authentication and want
105+
// to use anchor tags `<a href...` to download password protected files
106+
// can't add the basic authentication header.
107+
// They can use pre-signed urls instead.
108+
$query = $request->getQueryParameters();
109+
if (isset($query['signature'], $query['expires'])) {
110+
$sig = $query['signature'];
111+
$validUntil = \DateTime::createFromFormat(\DateTime::ATOM, $query['expires']);
112+
$now = new \DateTime();
113+
if ($now < $validUntil) {
114+
$key = \hash_hkdf('sha256', $this->share->getPassword());
115+
$s = new PublicShareSignature($this->share->getToken(), $this->share->getNode()->getName(), $validUntil, $key);
116+
if ($s->verify($sig)) {
117+
return [true, 'principals/system/public'];
118+
}
119+
}
120+
}
121+
104122
try {
105123
return parent::check($request, $response);
106124
} catch (LoginException $e) {
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<?php
2+
/**
3+
* @author David Christofas <dchristofas@owncloud.com>
4+
*
5+
* @copyright Copyright (c) 2021, 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\Tests\Unit\Files\PublicFiles;
23+
24+
use OCA\DAV\Files\PublicFiles\PublicShareSignature;
25+
use Test\TestCase;
26+
27+
class PublicShareSignatureTest extends TestCase {
28+
public function testGet() {
29+
$s = new PublicShareSignature('someToken', 'someFileName', new \DateTime(), 'somekey');
30+
$hash = $s->get();
31+
self::assertIsString($hash);
32+
self::assertEquals(64, \strlen($hash));
33+
}
34+
35+
public function testVerify() {
36+
$expectedHash = 'd67966402971bd3eb18aea62faf122a30e2dd5c9101aa9e106a56574cc535c6c';
37+
$date = \DateTime::createFromFormat(\DateTime::ATOM, '2009-01-03T18:15:05Z');
38+
$s = new PublicShareSignature('someToken', 'someFileName', $date, 'somekey');
39+
self::assertEquals($expectedHash, $s->get());
40+
}
41+
}

changelog/unreleased/38376

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
Enhancement: Implement pre-signed download urls for public links
2+
3+
Added pre-signed download urls for password protected public links to support clients which don't use cookies.
4+
5+
https://github.com/owncloud/core/pull/38376

0 commit comments

Comments
 (0)
X Tutup