diff options
author | Morris Jobke <hey@morrisjobke.de> | 2018-05-01 14:54:44 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-05-01 14:54:44 +0200 |
commit | f212c692ac5204fbea4639727db8e464152dba5d (patch) | |
tree | b12f4670807486983f82e9527791b89c8ee78625 /apps/dav/lib | |
parent | 7f194be3ab4ad0ee8edf095bf6d79a17f116f782 (diff) | |
parent | 1c75ddac45845d9e91997e6253e36b9ae2556e91 (diff) | |
download | nextcloud-server-f212c692ac5204fbea4639727db8e464152dba5d.tar.gz nextcloud-server-f212c692ac5204fbea4639727db8e464152dba5d.zip |
Merge pull request #9178 from nextcloud/feature/9142/directdownload
Add directdownload endpoint
Diffstat (limited to 'apps/dav/lib')
-rw-r--r-- | apps/dav/lib/BackgroundJob/CleanupDirectLinksJob.php | 50 | ||||
-rw-r--r-- | apps/dav/lib/Controller/DirectController.php | 113 | ||||
-rw-r--r-- | apps/dav/lib/Db/Direct.php | 58 | ||||
-rw-r--r-- | apps/dav/lib/Db/DirectMapper.php | 72 | ||||
-rw-r--r-- | apps/dav/lib/Direct/DirectFile.php | 110 | ||||
-rw-r--r-- | apps/dav/lib/Direct/DirectHome.php | 118 | ||||
-rw-r--r-- | apps/dav/lib/Direct/Server.php | 33 | ||||
-rw-r--r-- | apps/dav/lib/Direct/ServerFactory.php | 61 | ||||
-rw-r--r-- | apps/dav/lib/Migration/Version1005Date20180413093149.php | 80 |
9 files changed, 695 insertions, 0 deletions
diff --git a/apps/dav/lib/BackgroundJob/CleanupDirectLinksJob.php b/apps/dav/lib/BackgroundJob/CleanupDirectLinksJob.php new file mode 100644 index 00000000000..ba1049071ad --- /dev/null +++ b/apps/dav/lib/BackgroundJob/CleanupDirectLinksJob.php @@ -0,0 +1,50 @@ +<?php +declare(strict_types=1); +/** + * @copyright 2018, Roeland Jago Douma <roeland@famdouma.nl> + * + * @author Roeland Jago Douma <roeland@famdouma.nl> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +namespace OCA\DAV\BackgroundJob; + +use OC\BackgroundJob\TimedJob; +use OCA\DAV\Db\DirectMapper; +use OCP\AppFramework\Utility\ITimeFactory; + +class CleanupDirectLinksJob extends TimedJob { + /** @var ITimeFactory */ + private $timeFactory; + + /** @var DirectMapper */ + private $mapper; + + public function __construct(ITimeFactory $timeFactory, DirectMapper $mapper) { + $this->setInterval(60*60*24); + + $this->timeFactory = $timeFactory; + $this->mapper = $mapper; + } + + protected function run($argument) { + // Delete all shares expired 24 hours ago + $this->mapper->deleteExpired($this->timeFactory->getTime() - 60*60*24); + } + +} diff --git a/apps/dav/lib/Controller/DirectController.php b/apps/dav/lib/Controller/DirectController.php new file mode 100644 index 00000000000..2a14e4db2c7 --- /dev/null +++ b/apps/dav/lib/Controller/DirectController.php @@ -0,0 +1,113 @@ +<?php +declare(strict_types=1); +/** + * @copyright 2018, Roeland Jago Douma <roeland@famdouma.nl> + * + * @author Roeland Jago Douma <roeland@famdouma.nl> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +namespace OCA\DAV\Controller; + +use OCA\DAV\Db\Direct; +use OCA\DAV\Db\DirectMapper; +use OCP\AppFramework\Http\DataResponse; +use OCP\AppFramework\OCS\OCSBadRequestException; +use OCP\AppFramework\OCS\OCSNotFoundException; +use OCP\AppFramework\OCSController; +use OCP\AppFramework\Utility\ITimeFactory; +use OCP\Files\File; +use OCP\Files\IRootFolder; +use OCP\IRequest; +use OCP\IURLGenerator; +use OCP\Security\ISecureRandom; + +class DirectController extends OCSController { + + /** @var IRootFolder */ + private $rootFolder; + + /** @var string */ + private $userId; + + /** @var DirectMapper */ + private $mapper; + + /** @var ISecureRandom */ + private $random; + + /** @var ITimeFactory */ + private $timeFactory; + + /** @var IURLGenerator */ + private $urlGenerator; + + + public function __construct(string $appName, + IRequest $request, + IRootFolder $rootFolder, + string $userId, + DirectMapper $mapper, + ISecureRandom $random, + ITimeFactory $timeFactory, + IURLGenerator $urlGenerator) { + parent::__construct($appName, $request); + + $this->rootFolder = $rootFolder; + $this->userId = $userId; + $this->mapper = $mapper; + $this->random = $random; + $this->timeFactory = $timeFactory; + $this->urlGenerator = $urlGenerator; + } + + /** + * @NoAdminRequired + */ + public function getUrl(int $fileId): DataResponse { + $userFolder = $this->rootFolder->getUserFolder($this->userId); + + $files = $userFolder->getById($fileId); + + if ($files === []) { + throw new OCSNotFoundException(); + } + + $file = array_shift($files); + if (!($file instanceof File)) { + throw new OCSBadRequestException('Direct download only works for files'); + } + + //TODO: at some point we should use the directdownlaod function of storages + $direct = new Direct(); + $direct->setUserId($this->userId); + $direct->setFileId($fileId); + + $token = $this->random->generate(60, ISecureRandom::CHAR_UPPER . ISecureRandom::CHAR_LOWER . ISecureRandom::CHAR_DIGITS); + $direct->setToken($token); + $direct->setExpiration($this->timeFactory->getTime() + 60 * 60 * 8); + + $this->mapper->insert($direct); + + $url = $this->urlGenerator->getAbsoluteURL('remote.php/direct/'.$token); + + return new DataResponse([ + 'url' => $url, + ]); + } +} diff --git a/apps/dav/lib/Db/Direct.php b/apps/dav/lib/Db/Direct.php new file mode 100644 index 00000000000..ef588b1ec3a --- /dev/null +++ b/apps/dav/lib/Db/Direct.php @@ -0,0 +1,58 @@ +<?php +declare(strict_types=1); +/** + * @copyright 2018, Roeland Jago Douma <roeland@famdouma.nl> + * + * @author Roeland Jago Douma <roeland@famdouma.nl> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +namespace OCA\DAV\Db; + +use OCP\AppFramework\Db\Entity; + +/** + * @method string getUserId() + * @method void setUserId(string $userId) + * @method int getFileId() + * @method void setFileId(int $fileId) + * @method string getToken() + * @method void setToken(string $token) + * @method int getExpiration() + * @method void setExpiration(int $expiration) + */ +class Direct extends Entity { + /** @var string */ + protected $userId; + + /** @var int */ + protected $fileId; + + /** @var string */ + protected $token; + + /** @var int */ + protected $expiration; + + public function __construct() { + $this->addType('userId', 'string'); + $this->addType('fileId', 'int'); + $this->addType('token', 'string'); + $this->addType('expiration', 'int'); + } +} diff --git a/apps/dav/lib/Db/DirectMapper.php b/apps/dav/lib/Db/DirectMapper.php new file mode 100644 index 00000000000..d0db4b82879 --- /dev/null +++ b/apps/dav/lib/Db/DirectMapper.php @@ -0,0 +1,72 @@ +<?php +declare(strict_types=1); +/** + * @copyright 2018, Roeland Jago Douma <roeland@famdouma.nl> + * + * @author Roeland Jago Douma <roeland@famdouma.nl> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +namespace OCA\DAV\Db; + +use OCP\AppFramework\Db\DoesNotExistException; +use OCP\AppFramework\Db\Mapper; +use OCP\IDBConnection; + +class DirectMapper extends Mapper { + + public function __construct(IDBConnection $db) { + parent::__construct($db, 'directlink', Direct::class); + } + + /** + * @param string $token + * @return Direct + * @throws DoesNotExistException + */ + public function getByToken(string $token): Direct { + $qb = $this->db->getQueryBuilder(); + + $qb->select('*') + ->from('directlink') + ->where( + $qb->expr()->eq('token', $qb->createNamedParameter($token)) + ); + + $cursor = $qb->execute(); + $data = $cursor->fetch(); + $cursor->closeCursor(); + + if ($data === false) { + throw new DoesNotExistException('Direct link with token does not exist'); + } + + return Direct::fromRow($data); + } + + public function deleteExpired(int $expiration) { + $qb = $this->db->getQueryBuilder(); + + $qb->delete('directlink') + ->where( + $qb->expr()->lt('expiration', $qb->createNamedParameter($expiration)) + ); + + $qb->execute(); + } +} diff --git a/apps/dav/lib/Direct/DirectFile.php b/apps/dav/lib/Direct/DirectFile.php new file mode 100644 index 00000000000..947352c5148 --- /dev/null +++ b/apps/dav/lib/Direct/DirectFile.php @@ -0,0 +1,110 @@ +<?php +declare(strict_types=1); +/** + * @copyright 2018, Roeland Jago Douma <roeland@famdouma.nl> + * + * @author Roeland Jago Douma <roeland@famdouma.nl> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +namespace OCA\DAV\Direct; + +use OCA\DAV\Db\Direct; +use OCP\Files\File; +use OCP\Files\IRootFolder; +use Sabre\DAV\Exception\Forbidden; +use Sabre\DAV\Exception\NotFound; +use Sabre\DAV\IFile; + +class DirectFile implements IFile { + /** @var Direct */ + private $direct; + + /** @var IRootFolder */ + private $rootFolder; + + /** @var File */ + private $file; + + public function __construct(Direct $direct, IRootFolder $rootFolder) { + $this->direct = $direct; + $this->rootFolder = $rootFolder; + } + + public function put($data) { + throw new Forbidden(); + } + + public function get() { + $this->getFile(); + + return $this->file->fopen('rb'); + } + + public function getContentType() { + $this->getFile(); + + return $this->file->getMimeType(); + } + + public function getETag() { + $this->getFile(); + + return $this->file->getEtag(); + } + + public function getSize() { + $this->getFile(); + + return $this->file->getSize(); + } + + public function delete() { + throw new Forbidden(); + } + + public function getName() { + return $this->direct->getToken(); + } + + public function setName($name) { + throw new Forbidden(); + } + + public function getLastModified() { + $this->getFile(); + + return $this->file->getMTime(); + } + + private function getFile() { + if ($this->file === null) { + $userFolder = $this->rootFolder->getUserFolder($this->direct->getUserId()); + $files = $userFolder->getById($this->direct->getFileId()); + + if ($files === []) { + throw new NotFound(); + } + + $this->file = array_shift($files); + } + + return $this->file; + } + +} diff --git a/apps/dav/lib/Direct/DirectHome.php b/apps/dav/lib/Direct/DirectHome.php new file mode 100644 index 00000000000..e0246c83de9 --- /dev/null +++ b/apps/dav/lib/Direct/DirectHome.php @@ -0,0 +1,118 @@ +<?php +declare(strict_types=1); +/** + * @copyright 2018, Roeland Jago Douma <roeland@famdouma.nl> + * + * @author Roeland Jago Douma <roeland@famdouma.nl> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +namespace OCA\DAV\Direct; + +use OC\Security\Bruteforce\Throttler; +use OCA\DAV\Db\DirectMapper; +use OCP\AppFramework\Db\DoesNotExistException; +use OCP\AppFramework\Utility\ITimeFactory; +use OCP\Files\IRootFolder; +use OCP\IRequest; +use Sabre\DAV\Exception\Forbidden; +use Sabre\DAV\Exception\MethodNotAllowed; +use Sabre\DAV\Exception\NotFound; +use Sabre\DAV\ICollection; + +class DirectHome implements ICollection { + + /** @var IRootFolder */ + private $rootFolder; + + /** @var DirectMapper */ + private $mapper; + + /** @var ITimeFactory */ + private $timeFactory; + + /** @var Throttler */ + private $throttler; + + /** @var IRequest */ + private $request; + + public function __construct(IRootFolder $rootFolder, + DirectMapper $mapper, + ITimeFactory $timeFactory, + Throttler $throttler, + IRequest $request) { + $this->rootFolder = $rootFolder; + $this->mapper = $mapper; + $this->timeFactory = $timeFactory; + $this->throttler = $throttler; + $this->request = $request; + } + + public function createFile($name, $data = null) { + throw new Forbidden(); + } + + public function createDirectory($name) { + throw new Forbidden(); + } + + public function getChild($name): DirectFile { + try { + $direct = $this->mapper->getByToken($name); + + // Expired + if ($direct->getExpiration() < $this->timeFactory->getTime()) { + throw new NotFound(); + } + + return new DirectFile($direct, $this->rootFolder); + } catch (DoesNotExistException $e) { + // Since the token space is so huge only throttle on non exsisting token + $this->throttler->registerAttempt('directlink', $this->request->getRemoteAddress()); + $this->throttler->sleepDelay($this->request->getRemoteAddress(), 'directlink'); + + throw new NotFound(); + } + } + + public function getChildren() { + throw new MethodNotAllowed('Listing members of this collection is disabled'); + } + + public function childExists($name): bool { + return false; + } + + public function delete() { + throw new Forbidden(); + } + + public function getName(): string { + return 'direct'; + } + + public function setName($name) { + throw new Forbidden(); + } + + public function getLastModified(): int { + return 0; + } + +} diff --git a/apps/dav/lib/Direct/Server.php b/apps/dav/lib/Direct/Server.php new file mode 100644 index 00000000000..373dc2dca50 --- /dev/null +++ b/apps/dav/lib/Direct/Server.php @@ -0,0 +1,33 @@ +<?php +declare(strict_types=1); +/** + * @copyright 2018, Roeland Jago Douma <roeland@famdouma.nl> + * + * @author Roeland Jago Douma <roeland@famdouma.nl> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +namespace OCA\DAV\Direct; + +class Server extends \Sabre\DAV\Server { + public function __construct($treeOrNode = null) { + parent::__construct($treeOrNode); + self::$exposeVersion = false; + $this->enablePropfindDepthInfinityf = false; + } +} diff --git a/apps/dav/lib/Direct/ServerFactory.php b/apps/dav/lib/Direct/ServerFactory.php new file mode 100644 index 00000000000..618f6889fd0 --- /dev/null +++ b/apps/dav/lib/Direct/ServerFactory.php @@ -0,0 +1,61 @@ +<?php +declare(strict_types=1); +/** + * @copyright 2018, Roeland Jago Douma <roeland@famdouma.nl> + * + * @author Roeland Jago Douma <roeland@famdouma.nl> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +namespace OCA\DAV\Direct; + +use OC\Security\Bruteforce\Throttler; +use OCA\DAV\Db\DirectMapper; +use OCP\AppFramework\Utility\ITimeFactory; +use OCP\Files\IRootFolder; +use OCP\IConfig; +use OCP\IRequest; + +class ServerFactory { + /** @var IConfig */ + private $config; + + public function __construct(IConfig $config) { + $this->config = $config; + } + + public function createServer(string $baseURI, + string $requestURI, + IRootFolder $rootFolder, + DirectMapper $mapper, + ITimeFactory $timeFactory, + Throttler $throttler, + IRequest $request): Server { + $home = new DirectHome($rootFolder, $mapper, $timeFactory, $throttler, $request); + $server = new Server($home); + + $server->httpRequest->setUrl($requestURI); + $server->setBaseUri($baseURI); + + $server->addPlugin(new \OCA\DAV\Connector\Sabre\MaintenancePlugin($this->config)); + + return $server; + + + } +} diff --git a/apps/dav/lib/Migration/Version1005Date20180413093149.php b/apps/dav/lib/Migration/Version1005Date20180413093149.php new file mode 100644 index 00000000000..cd6aeca6b48 --- /dev/null +++ b/apps/dav/lib/Migration/Version1005Date20180413093149.php @@ -0,0 +1,80 @@ +<?php +declare(strict_types=1); +/** + * @copyright 2018, Roeland Jago Douma <roeland@famdouma.nl> + * + * @author Roeland Jago Douma <roeland@famdouma.nl> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +namespace OCA\DAV\Migration; + +use Doctrine\DBAL\Types\Type; +use OCP\DB\ISchemaWrapper; +use OCP\Migration\SimpleMigrationStep; +use OCP\Migration\IOutput; + +class Version1005Date20180413093149 extends SimpleMigrationStep { + + /** + * @param IOutput $output + * @param \Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper` + * @param array $options + * @return null|ISchemaWrapper + */ + public function changeSchema(IOutput $output, \Closure $schemaClosure, array $options) { + + /** @var ISchemaWrapper $schema */ + $schema = $schemaClosure(); + + if (!$schema->hasTable('directlink')) { + $table = $schema->createTable('directlink'); + + $table->addColumn('id',Type::BIGINT, [ + 'autoincrement' => true, + 'notnull' => true, + 'length' => 11, + 'unsigned' => true, + ]); + $table->addColumn('user_id', Type::STRING, [ + 'notnull' => false, + 'length' => 64, + ]); + $table->addColumn('file_id', Type::BIGINT, [ + 'notnull' => true, + 'length' => 11, + 'unsigned' => true, + ]); + $table->addColumn('token', Type::STRING, [ + 'notnull' => false, + 'length' => 60, + ]); + $table->addColumn('expiration', Type::BIGINT, [ + 'notnull' => true, + 'length' => 11, + 'unsigned' => true, + ]); + + $table->setPrimaryKey(['id'], 'directlink_id_idx'); + $table->addIndex(['token'], 'directlink_token_idx'); + $table->addIndex(['expiration'], 'directlink_expiration_idx'); + + return $schema; + } + } +} |