diff options
Diffstat (limited to 'apps')
34 files changed, 422 insertions, 399 deletions
diff --git a/apps/accessibility/lib/Controller/AccessibilityController.php b/apps/accessibility/lib/Controller/AccessibilityController.php index 91c110de868..e17b206cdfd 100644 --- a/apps/accessibility/lib/Controller/AccessibilityController.php +++ b/apps/accessibility/lib/Controller/AccessibilityController.php @@ -260,7 +260,15 @@ class AccessibilityController extends Controller { * @return string */ private function invertSvgIconsColor(string $css) { - return str_replace(['color=000', 'color=fff', 'color=***'], ['color=***', 'color=000', 'color=fff'], $css); + return str_replace( + ['color=000&', 'color=fff&', 'color=***&'], + ['color=***&', 'color=000&', 'color=fff&'], + str_replace( + ['color=000000&', 'color=ffffff&', 'color=******&'], + ['color=******&', 'color=000000&', 'color=ffffff&'], + $css + ) + ); } /** diff --git a/apps/dav/lib/Connector/Sabre/File.php b/apps/dav/lib/Connector/Sabre/File.php index 8aa7d66ba34..3c42f66a04a 100644 --- a/apps/dav/lib/Connector/Sabre/File.php +++ b/apps/dav/lib/Connector/Sabre/File.php @@ -169,9 +169,12 @@ class File extends Node implements IFile { if ($partStorage->instanceOfStorage(Storage\IWriteStreamStorage::class)) { if (!is_resource($data)) { - $data = fopen('php://temp', 'r+'); - fwrite($data, 'foobar'); - rewind($data); + $tmpData = fopen('php://temp', 'r+'); + if ($data !== null) { + fwrite($tmpData, $data); + rewind($tmpData); + } + $data = $tmpData; } $isEOF = false; @@ -205,7 +208,9 @@ class File extends Node implements IFile { if (isset($_SERVER['CONTENT_LENGTH'])) { $expected = $_SERVER['CONTENT_LENGTH']; } - throw new Exception('Error while copying file to target location (copied bytes: ' . $count . ', expected filesize: ' . $expected . ' )'); + if ($expected !== "0") { + throw new Exception('Error while copying file to target location (copied bytes: ' . $count . ', expected filesize: ' . $expected . ' )'); + } } // if content length is sent by client: diff --git a/apps/dav/lib/Connector/Sabre/FilesPlugin.php b/apps/dav/lib/Connector/Sabre/FilesPlugin.php index f53f13c5687..99317f2bc1c 100644 --- a/apps/dav/lib/Connector/Sabre/FilesPlugin.php +++ b/apps/dav/lib/Connector/Sabre/FilesPlugin.php @@ -70,6 +70,7 @@ class FilesPlugin extends ServerPlugin { const HAS_PREVIEW_PROPERTYNAME = '{http://nextcloud.org/ns}has-preview'; const MOUNT_TYPE_PROPERTYNAME = '{http://nextcloud.org/ns}mount-type'; const IS_ENCRYPTED_PROPERTYNAME = '{http://nextcloud.org/ns}is-encrypted'; + const SHARE_NOTE = '{http://nextcloud.org/ns}note'; /** * Reference to main server object @@ -161,6 +162,7 @@ class FilesPlugin extends ServerPlugin { $server->protectedProperties[] = self::HAS_PREVIEW_PROPERTYNAME; $server->protectedProperties[] = self::MOUNT_TYPE_PROPERTYNAME; $server->protectedProperties[] = self::IS_ENCRYPTED_PROPERTYNAME; + $server->protectedProperties[] = self::SHARE_NOTE; // normally these cannot be changed (RFC4918), but we want them modifiable through PROPPATCH $allowedProperties = ['{DAV:}getetag']; @@ -359,6 +361,12 @@ class FilesPlugin extends ServerPlugin { $propFind->handle(self::MOUNT_TYPE_PROPERTYNAME, function () use ($node) { return $node->getFileInfo()->getMountPoint()->getMountType(); }); + + $propFind->handle(self::SHARE_NOTE, function() use ($node, $httpRequest) { + return $node->getNoteFromShare( + $httpRequest->getRawServerValue('PHP_AUTH_USER') + ); + }); } if ($node instanceof \OCA\DAV\Connector\Sabre\Node) { diff --git a/apps/dav/lib/Connector/Sabre/Node.php b/apps/dav/lib/Connector/Sabre/Node.php index 38d0ff57fb2..32ba6c53a0c 100644 --- a/apps/dav/lib/Connector/Sabre/Node.php +++ b/apps/dav/lib/Connector/Sabre/Node.php @@ -41,6 +41,8 @@ use OCP\Files\FileInfo; use OCP\Files\StorageNotAvailableException; use OCP\Share\Exceptions\ShareNotFound; use OCP\Share\IManager; +use OCP\Share; +use OCP\Share\IShare; abstract class Node implements \Sabre\DAV\INode { @@ -291,6 +293,35 @@ abstract class Node implements \Sabre\DAV\INode { } /** + * @param string $user + * @return string + */ + public function getNoteFromShare($user) { + if ($user === null) { + return ''; + } + + $types = [ + Share::SHARE_TYPE_USER, + Share::SHARE_TYPE_GROUP, + Share::SHARE_TYPE_CIRCLE, + Share::SHARE_TYPE_ROOM + ]; + + foreach ($types as $shareType) { + $shares = $this->shareManager->getSharedWith($user, $shareType, $this, -1); + foreach ($shares as $share) { + $note = $share->getNote(); + if($share->getShareOwner() !== $user && !empty($note)) { + return $note; + } + } + } + + return ''; + } + + /** * @return string */ public function getDavPermissions() { diff --git a/apps/files/appinfo/info.xml b/apps/files/appinfo/info.xml index d6a0959eebe..79926afe83e 100644 --- a/apps/files/appinfo/info.xml +++ b/apps/files/appinfo/info.xml @@ -35,10 +35,6 @@ <command>OCA\Files\Command\ScanAppData</command> </commands> - <settings> - <admin>OCA\Files\Settings\Admin</admin> - </settings> - <activity> <settings> <setting>OCA\Files\Activity\Settings\FavoriteAction</setting> diff --git a/apps/files/appinfo/routes.php b/apps/files/appinfo/routes.php index 06806309cac..b085d79b8c8 100644 --- a/apps/files/appinfo/routes.php +++ b/apps/files/appinfo/routes.php @@ -77,11 +77,6 @@ $application->registerRoutes( 'verb' => 'GET', ], [ - 'name' => 'settings#setMaxUploadSize', - 'url' => '/settings/maxUpload', - 'verb' => 'POST', - ], - [ 'name' => 'ajax#getStorageStats', 'url' => '/ajax/getstoragestats.php', 'verb' => 'GET', diff --git a/apps/files/composer/composer/autoload_classmap.php b/apps/files/composer/composer/autoload_classmap.php index b664b3fa227..dd0bea25644 100644 --- a/apps/files/composer/composer/autoload_classmap.php +++ b/apps/files/composer/composer/autoload_classmap.php @@ -31,9 +31,7 @@ return array( 'OCA\\Files\\Command\\TransferOwnership' => $baseDir . '/../lib/Command/TransferOwnership.php', 'OCA\\Files\\Controller\\AjaxController' => $baseDir . '/../lib/Controller/AjaxController.php', 'OCA\\Files\\Controller\\ApiController' => $baseDir . '/../lib/Controller/ApiController.php', - 'OCA\\Files\\Controller\\SettingsController' => $baseDir . '/../lib/Controller/SettingsController.php', 'OCA\\Files\\Controller\\ViewController' => $baseDir . '/../lib/Controller/ViewController.php', 'OCA\\Files\\Helper' => $baseDir . '/../lib/Helper.php', 'OCA\\Files\\Service\\TagService' => $baseDir . '/../lib/Service/TagService.php', - 'OCA\\Files\\Settings\\Admin' => $baseDir . '/../lib/Settings/Admin.php', ); diff --git a/apps/files/composer/composer/autoload_static.php b/apps/files/composer/composer/autoload_static.php index ef1ecdcc0ce..ae748b5d4d8 100644 --- a/apps/files/composer/composer/autoload_static.php +++ b/apps/files/composer/composer/autoload_static.php @@ -46,11 +46,9 @@ class ComposerStaticInitFiles 'OCA\\Files\\Command\\TransferOwnership' => __DIR__ . '/..' . '/../lib/Command/TransferOwnership.php', 'OCA\\Files\\Controller\\AjaxController' => __DIR__ . '/..' . '/../lib/Controller/AjaxController.php', 'OCA\\Files\\Controller\\ApiController' => __DIR__ . '/..' . '/../lib/Controller/ApiController.php', - 'OCA\\Files\\Controller\\SettingsController' => __DIR__ . '/..' . '/../lib/Controller/SettingsController.php', 'OCA\\Files\\Controller\\ViewController' => __DIR__ . '/..' . '/../lib/Controller/ViewController.php', 'OCA\\Files\\Helper' => __DIR__ . '/..' . '/../lib/Helper.php', 'OCA\\Files\\Service\\TagService' => __DIR__ . '/..' . '/../lib/Service/TagService.php', - 'OCA\\Files\\Settings\\Admin' => __DIR__ . '/..' . '/../lib/Settings/Admin.php', ); public static function getInitializer(ClassLoader $loader) diff --git a/apps/files/js/admin.js b/apps/files/js/admin.js deleted file mode 100644 index 6afd77eb03e..00000000000 --- a/apps/files/js/admin.js +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (c) 2014 - * - * This file is licensed under the Affero General Public License version 3 - * or later. - * - * See the COPYING-README file. - * - */ - -(function() { - if (!OCA.Files) { - /** - * Namespace for the files app - * @namespace OCA.Files - */ - OCA.Files = {}; - } - - /** - * @namespace OCA.Files.Admin - */ - OCA.Files.Admin = { - initialize: function() { - $('#submitMaxUpload').on('click', _.bind(this._onClickSubmitMaxUpload, this)); - }, - - _onClickSubmitMaxUpload: function () { - OC.msg.startSaving('#maxUploadSizeSettingsMsg'); - - var request = $.ajax({ - url: OC.generateUrl('/apps/files/settings/maxUpload'), - type: 'POST', - data: { - maxUploadSize: $('#maxUploadSize').val() - } - }); - - request.done(function (data) { - $('#maxUploadSize').val(data.maxUploadSize); - OC.msg.finishedSuccess('#maxUploadSizeSettingsMsg', 'Saved'); - }); - - request.fail(function () { - OC.msg.finishedError('#maxUploadSizeSettingsMsg', 'Error'); - }); - } - } -})(); - -function switchPublicFolder() { - var publicEnable = $('#publicEnable').is(':checked'); - // find all radiobuttons of that group - var sharingaimGroup = $('input:radio[name=sharingaim]'); - $.each(sharingaimGroup, function(index, sharingaimItem) { - // set all buttons to the correct state - sharingaimItem.disabled = !publicEnable; - }); -} - -$(document).ready(function() { - OCA.Files.Admin.initialize(); - - // Execute the function after loading DOM tree - switchPublicFolder(); - $('#publicEnable').click(function() { - // To get rid of onClick() - switchPublicFolder(); - }); -}); diff --git a/apps/files/lib/Controller/SettingsController.php b/apps/files/lib/Controller/SettingsController.php deleted file mode 100644 index 0b2dc9c2dd1..00000000000 --- a/apps/files/lib/Controller/SettingsController.php +++ /dev/null @@ -1,51 +0,0 @@ -<?php -/** - * - * - * @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\Files\Controller; - -use OCP\AppFramework\Controller; -use OCP\AppFramework\Http; -use OCP\AppFramework\Http\JSONResponse; -use OCP\IRequest; -use OCP\Util; - -class SettingsController extends Controller { - public function __construct($appName, IRequest $request) { - parent::__construct($appName, $request); - } - - /** - * @param string $maxUploadSize - * @return JSONResponse - */ - public function setMaxUploadSize($maxUploadSize) { - $setMaxSize = \OC_Files::setUploadLimit(Util::computerFileSize($maxUploadSize)); - - if ($setMaxSize === false) { - return new JSONResponse([], Http::STATUS_BAD_REQUEST); - } else { - return new JSONResponse([ - 'maxUploadSize' => Util::humanFileSize($setMaxSize) - ]); - } - } -} diff --git a/apps/files/lib/Settings/Admin.php b/apps/files/lib/Settings/Admin.php deleted file mode 100644 index 11b66dab846..00000000000 --- a/apps/files/lib/Settings/Admin.php +++ /dev/null @@ -1,87 +0,0 @@ -<?php -/** - * @copyright Copyright (c) 2016 Arthur Schiwon <blizzz@arthur-schiwon.de> - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Joas Schilling <coding@schilljs.com> - * - * @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\Files\Settings; - -use bantu\IniGetWrapper\IniGetWrapper; -use OCP\AppFramework\Http\TemplateResponse; -use OCP\IRequest; -use OCP\Settings\ISettings; -use OCP\Util; - -class Admin implements ISettings { - - /** @var IniGetWrapper */ - private $iniWrapper; - - /** @var IRequest */ - private $request; - - public function __construct(IniGetWrapper $iniWrapper, IRequest $request) { - $this->iniWrapper = $iniWrapper; - $this->request = $request; - } - - /** - * @return TemplateResponse - */ - public function getForm() { - $htaccessWorking = (getenv('htaccessWorking') === 'true'); - $htaccessWritable = is_writable(\OC::$SERVERROOT.'/.htaccess'); - $userIniWritable = is_writable(\OC::$SERVERROOT.'/.user.ini'); - - $upload_max_filesize = $this->iniWrapper->getBytes('upload_max_filesize'); - $post_max_size = $this->iniWrapper->getBytes('post_max_size'); - $maxUploadFilesize = Util::humanFileSize(min($upload_max_filesize, $post_max_size)); - - $parameters = [ - 'uploadChangable' => ($htaccessWorking and $htaccessWritable) or $userIniWritable, - 'uploadMaxFilesize' => $maxUploadFilesize, - // max possible makes only sense on a 32 bit system - 'displayMaxPossibleUploadSize' => PHP_INT_SIZE === 4, - 'maxPossibleUploadSize' => Util::humanFileSize(PHP_INT_MAX), - ]; - - return new TemplateResponse('files', 'admin', $parameters, ''); - } - - /** - * @return string the section ID, e.g. 'sharing' - */ - public function getSection() { - return 'server'; - } - - /** - * @return int whether the form should be rather on the top or bottom of - * the admin section. The forms are arranged in ascending order of the - * priority values. It is required to return a value between 0 and 100. - * - * E.g.: 70 - */ - public function getPriority() { - return 5; - } - -} diff --git a/apps/files/templates/admin.php b/apps/files/templates/admin.php deleted file mode 100644 index 1cbb339fee7..00000000000 --- a/apps/files/templates/admin.php +++ /dev/null @@ -1,20 +0,0 @@ - <?php OCP\Util::addScript('files', 'admin'); ?> - - <div class="section"> - <h2><?php p($l->t('File handling')); ?></h2> - <label for="maxUploadSize"><?php p($l->t( 'Maximum upload size' )); ?> </label> - <span id="maxUploadSizeSettingsMsg" class="msg"></span> - <br /> - <input type="text" name='maxUploadSize' id="maxUploadSize" value='<?php p($_['uploadMaxFilesize']) ?>' <?php if(!$_['uploadChangable']) { p('disabled'); } ?> /> - <?php if($_['displayMaxPossibleUploadSize']):?> - (<?php p($l->t('max. possible: ')); p($_['maxPossibleUploadSize']) ?>) - <?php endif;?> - <input type="hidden" value="<?php p($_['requesttoken']); ?>" name="requesttoken" /> - <?php if($_['uploadChangable']): ?> - <input type="submit" id="submitMaxUpload" - value="<?php p($l->t( 'Save' )); ?>"/> - <p><em><?php p($l->t('With PHP-FPM it might take 5 minutes for changes to be applied.')); ?></em></p> - <?php else: ?> - <p><em><?php p($l->t('Missing permissions to edit from here.')); ?></em></p> - <?php endif; ?> - </div> diff --git a/apps/files/tests/Settings/AdminTest.php b/apps/files/tests/Settings/AdminTest.php deleted file mode 100644 index 027a37d7b2f..00000000000 --- a/apps/files/tests/Settings/AdminTest.php +++ /dev/null @@ -1,84 +0,0 @@ -<?php -/** - * @copyright Copyright (c) 2016 Lukas Reschke <lukas@statuscode.ch> - * - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Morris Jobke <hey@morrisjobke.de> - * - * @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\Files\Tests\Settings; - -use bantu\IniGetWrapper\IniGetWrapper; -use OCA\Files\Settings\Admin; -use OCP\AppFramework\Http\TemplateResponse; -use OCP\IRequest; -use OCP\Util; -use Test\TestCase; - -class AdminTest extends TestCase { - /** @var Admin */ - private $admin; - /** @var IniGetWrapper */ - private $iniGetWrapper; - /** @var IRequest */ - private $request; - - public function setUp() { - parent::setUp(); - $this->iniGetWrapper = $this->getMockBuilder('\bantu\IniGetWrapper\IniGetWrapper')->disableOriginalConstructor()->getMock(); - $this->request = $this->getMockBuilder(IRequest::class)->getMock(); - $this->admin = new Admin( - $this->iniGetWrapper, - $this->request - ); - } - - public function testGetForm() { - $htaccessWorking = (getenv('htaccessWorking') == 'true'); - $htaccessWritable = is_writable(\OC::$SERVERROOT.'/.htaccess'); - $userIniWritable = is_writable(\OC::$SERVERROOT.'/.user.ini'); - - $this->iniGetWrapper - ->expects($this->at(0)) - ->method('getBytes') - ->with('upload_max_filesize') - ->willReturn(1234); - $this->iniGetWrapper - ->expects($this->at(1)) - ->method('getBytes') - ->with('post_max_size') - ->willReturn(1234); - $params = [ - 'uploadChangable' => (($htaccessWorking and $htaccessWritable) or $userIniWritable ), - 'uploadMaxFilesize' => '1 KB', - 'displayMaxPossibleUploadSize' => PHP_INT_SIZE === 4, - 'maxPossibleUploadSize' => Util::humanFileSize(PHP_INT_MAX), - ]; - $expected = new TemplateResponse('files', 'admin', $params, ''); - $this->assertEquals($expected, $this->admin->getForm()); - } - - public function testGetSection() { - $this->assertSame('server', $this->admin->getSection()); - } - - public function testGetPriority() { - $this->assertSame(5, $this->admin->getPriority()); - } -} diff --git a/apps/files_external/3rdparty/composer.json b/apps/files_external/3rdparty/composer.json index 20e4b8a4d38..897234dfce0 100644 --- a/apps/files_external/3rdparty/composer.json +++ b/apps/files_external/3rdparty/composer.json @@ -9,6 +9,6 @@ }, "require": { "icewind/streams": "0.6.1", - "icewind/smb": "3.1.0" + "icewind/smb": "3.1.1" } } diff --git a/apps/files_external/3rdparty/composer.lock b/apps/files_external/3rdparty/composer.lock index 33c975a7d77..69da480d19f 100644 --- a/apps/files_external/3rdparty/composer.lock +++ b/apps/files_external/3rdparty/composer.lock @@ -4,20 +4,20 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "ea11a8c6e4979d35dd3bdf1f057ef0e8", + "content-hash": "dd320be9cd87742d53b4384bf3df9c42", "packages": [ { "name": "icewind/smb", - "version": "v3.1.0", + "version": "v3.1.1", "source": { "type": "git", "url": "https://github.com/icewind1991/SMB.git", - "reference": "db16d4430cb75f0196eaa6377c5766b19f744c8d" + "reference": "26b7b8780342d0e61313b464b880d50a2ea898e2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/icewind1991/SMB/zipball/db16d4430cb75f0196eaa6377c5766b19f744c8d", - "reference": "db16d4430cb75f0196eaa6377c5766b19f744c8d", + "url": "https://api.github.com/repos/icewind1991/SMB/zipball/26b7b8780342d0e61313b464b880d50a2ea898e2", + "reference": "26b7b8780342d0e61313b464b880d50a2ea898e2", "shasum": "" }, "require": { @@ -46,7 +46,7 @@ } ], "description": "php wrapper for smbclient and libsmbclient-php", - "time": "2019-02-06T14:17:35+00:00" + "time": "2019-03-04T15:02:42+00:00" }, { "name": "icewind/streams", diff --git a/apps/files_external/3rdparty/composer/autoload_classmap.php b/apps/files_external/3rdparty/composer/autoload_classmap.php index b92bd22b4f0..9eb83e893bd 100644 --- a/apps/files_external/3rdparty/composer/autoload_classmap.php +++ b/apps/files_external/3rdparty/composer/autoload_classmap.php @@ -76,21 +76,6 @@ return array( 'Icewind\\Streams\\PathWrapper' => $vendorDir . '/icewind/streams/src/PathWrapper.php', 'Icewind\\Streams\\RetryWrapper' => $vendorDir . '/icewind/streams/src/RetryWrapper.php', 'Icewind\\Streams\\SeekableWrapper' => $vendorDir . '/icewind/streams/src/SeekableWrapper.php', - 'Icewind\\Streams\\Tests\\CallbackWrapperTest' => $vendorDir . '/icewind/streams/tests/CallbackWrapperTest.php', - 'Icewind\\Streams\\Tests\\CountWrapperTest' => $vendorDir . '/icewind/streams/tests/CountWrapperTest.php', - 'Icewind\\Streams\\Tests\\DirectoryFilter' => $vendorDir . '/icewind/streams/tests/DirectoryFilter.php', - 'Icewind\\Streams\\Tests\\DirectoryWrapper' => $vendorDir . '/icewind/streams/tests/DirectoryWrapper.php', - 'Icewind\\Streams\\Tests\\DirectoryWrapperDummy' => $vendorDir . '/icewind/streams/tests/DirectoryWrapper.php', - 'Icewind\\Streams\\Tests\\DirectoryWrapperNull' => $vendorDir . '/icewind/streams/tests/DirectoryWrapper.php', - 'Icewind\\Streams\\Tests\\FailWrapper' => $vendorDir . '/icewind/streams/tests/RetryWrapper.php', - 'Icewind\\Streams\\Tests\\IteratorDirectory' => $vendorDir . '/icewind/streams/tests/IteratorDirectory.php', - 'Icewind\\Streams\\Tests\\NullWrapperTest' => $vendorDir . '/icewind/streams/tests/NullWrapperTest.php', - 'Icewind\\Streams\\Tests\\PartialWrapper' => $vendorDir . '/icewind/streams/tests/RetryWrapper.php', - 'Icewind\\Streams\\Tests\\PathWrapper' => $vendorDir . '/icewind/streams/tests/PathWrapper.php', - 'Icewind\\Streams\\Tests\\RetryWrapperTest' => $vendorDir . '/icewind/streams/tests/RetryWrapper.php', - 'Icewind\\Streams\\Tests\\SeekableWrapper' => $vendorDir . '/icewind/streams/tests/SeekableWrapper.php', - 'Icewind\\Streams\\Tests\\UrlCallBack' => $vendorDir . '/icewind/streams/tests/UrlCallBack.php', - 'Icewind\\Streams\\Tests\\WrapperTest' => $vendorDir . '/icewind/streams/tests/WrapperTest.php', 'Icewind\\Streams\\Url' => $vendorDir . '/icewind/streams/src/Url.php', 'Icewind\\Streams\\UrlCallback' => $vendorDir . '/icewind/streams/src/UrlCallBack.php', 'Icewind\\Streams\\Wrapper' => $vendorDir . '/icewind/streams/src/Wrapper.php', diff --git a/apps/files_external/3rdparty/composer/autoload_static.php b/apps/files_external/3rdparty/composer/autoload_static.php index 6d7ed9d8536..eb2329b2b05 100644 --- a/apps/files_external/3rdparty/composer/autoload_static.php +++ b/apps/files_external/3rdparty/composer/autoload_static.php @@ -106,21 +106,6 @@ class ComposerStaticInit98fe9b281934250b3a93f69a5ce843b3 'Icewind\\Streams\\PathWrapper' => __DIR__ . '/..' . '/icewind/streams/src/PathWrapper.php', 'Icewind\\Streams\\RetryWrapper' => __DIR__ . '/..' . '/icewind/streams/src/RetryWrapper.php', 'Icewind\\Streams\\SeekableWrapper' => __DIR__ . '/..' . '/icewind/streams/src/SeekableWrapper.php', - 'Icewind\\Streams\\Tests\\CallbackWrapperTest' => __DIR__ . '/..' . '/icewind/streams/tests/CallbackWrapperTest.php', - 'Icewind\\Streams\\Tests\\CountWrapperTest' => __DIR__ . '/..' . '/icewind/streams/tests/CountWrapperTest.php', - 'Icewind\\Streams\\Tests\\DirectoryFilter' => __DIR__ . '/..' . '/icewind/streams/tests/DirectoryFilter.php', - 'Icewind\\Streams\\Tests\\DirectoryWrapper' => __DIR__ . '/..' . '/icewind/streams/tests/DirectoryWrapper.php', - 'Icewind\\Streams\\Tests\\DirectoryWrapperDummy' => __DIR__ . '/..' . '/icewind/streams/tests/DirectoryWrapper.php', - 'Icewind\\Streams\\Tests\\DirectoryWrapperNull' => __DIR__ . '/..' . '/icewind/streams/tests/DirectoryWrapper.php', - 'Icewind\\Streams\\Tests\\FailWrapper' => __DIR__ . '/..' . '/icewind/streams/tests/RetryWrapper.php', - 'Icewind\\Streams\\Tests\\IteratorDirectory' => __DIR__ . '/..' . '/icewind/streams/tests/IteratorDirectory.php', - 'Icewind\\Streams\\Tests\\NullWrapperTest' => __DIR__ . '/..' . '/icewind/streams/tests/NullWrapperTest.php', - 'Icewind\\Streams\\Tests\\PartialWrapper' => __DIR__ . '/..' . '/icewind/streams/tests/RetryWrapper.php', - 'Icewind\\Streams\\Tests\\PathWrapper' => __DIR__ . '/..' . '/icewind/streams/tests/PathWrapper.php', - 'Icewind\\Streams\\Tests\\RetryWrapperTest' => __DIR__ . '/..' . '/icewind/streams/tests/RetryWrapper.php', - 'Icewind\\Streams\\Tests\\SeekableWrapper' => __DIR__ . '/..' . '/icewind/streams/tests/SeekableWrapper.php', - 'Icewind\\Streams\\Tests\\UrlCallBack' => __DIR__ . '/..' . '/icewind/streams/tests/UrlCallBack.php', - 'Icewind\\Streams\\Tests\\WrapperTest' => __DIR__ . '/..' . '/icewind/streams/tests/WrapperTest.php', 'Icewind\\Streams\\Url' => __DIR__ . '/..' . '/icewind/streams/src/Url.php', 'Icewind\\Streams\\UrlCallback' => __DIR__ . '/..' . '/icewind/streams/src/UrlCallBack.php', 'Icewind\\Streams\\Wrapper' => __DIR__ . '/..' . '/icewind/streams/src/Wrapper.php', diff --git a/apps/files_external/3rdparty/composer/installed.json b/apps/files_external/3rdparty/composer/installed.json index ff953699268..b6c0b4d751c 100644 --- a/apps/files_external/3rdparty/composer/installed.json +++ b/apps/files_external/3rdparty/composer/installed.json @@ -1,17 +1,17 @@ [ { "name": "icewind/smb", - "version": "v3.1.0", - "version_normalized": "3.1.0.0", + "version": "v3.1.1", + "version_normalized": "3.1.1.0", "source": { "type": "git", "url": "https://github.com/icewind1991/SMB.git", - "reference": "db16d4430cb75f0196eaa6377c5766b19f744c8d" + "reference": "26b7b8780342d0e61313b464b880d50a2ea898e2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/icewind1991/SMB/zipball/db16d4430cb75f0196eaa6377c5766b19f744c8d", - "reference": "db16d4430cb75f0196eaa6377c5766b19f744c8d", + "url": "https://api.github.com/repos/icewind1991/SMB/zipball/26b7b8780342d0e61313b464b880d50a2ea898e2", + "reference": "26b7b8780342d0e61313b464b880d50a2ea898e2", "shasum": "" }, "require": { @@ -22,7 +22,7 @@ "friendsofphp/php-cs-fixer": "^2.13", "phpunit/phpunit": "^5.7" }, - "time": "2019-02-06T14:17:35+00:00", + "time": "2019-03-04T15:02:42+00:00", "type": "library", "installation-source": "dist", "autoload": { diff --git a/apps/files_external/3rdparty/icewind/smb/src/Wrapped/Connection.php b/apps/files_external/3rdparty/icewind/smb/src/Wrapped/Connection.php index de0a1de40ee..830f6fb17b0 100644 --- a/apps/files_external/3rdparty/icewind/smb/src/Wrapped/Connection.php +++ b/apps/files_external/3rdparty/icewind/smb/src/Wrapped/Connection.php @@ -66,7 +66,11 @@ class Connection extends RawConnection { $this->parser->checkConnectionError($promptLine); $output = []; - $line = $this->readLine(); + if (!$this->isPrompt($promptLine)) { + $line = $promptLine; + } else { + $line = $this->readLine(); + } if ($line === false) { $this->unknownError($promptLine); } diff --git a/apps/files_external/lib/Command/Notify.php b/apps/files_external/lib/Command/Notify.php index b859a825c80..556687e6930 100644 --- a/apps/files_external/lib/Command/Notify.php +++ b/apps/files_external/lib/Command/Notify.php @@ -2,6 +2,7 @@ /** * @copyright Copyright (c) 2016 Robin Appelman <robin@icewind.nl> * + * @author Ari Selseng <ari@selseng.net> * @author Robin Appelman <robin@icewind.nl> * @author Roeland Jago Douma <roeland@famdouma.nl> * @@ -24,6 +25,7 @@ namespace OCA\Files_External\Command; +use Doctrine\DBAL\Exception\DriverException; use OC\Core\Command\Base; use OCA\Files_External\Lib\InsufficientDataForMeaningfulAnswerException; use OCA\Files_External\Lib\StorageConfig; @@ -35,6 +37,7 @@ use OCP\Files\Storage\INotifyStorage; use OCP\Files\Storage\IStorage; use OCP\Files\StorageNotAvailableException; use OCP\IDBConnection; +use OCP\ILogger; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; @@ -47,17 +50,15 @@ class Notify extends Base { private $connection; /** @var \OCP\DB\QueryBuilder\IQueryBuilder */ private $updateQuery; + /** @var ILogger */ + private $logger; - function __construct(GlobalStoragesService $globalService, IDBConnection $connection) { + function __construct(GlobalStoragesService $globalService, IDBConnection $connection, ILogger $logger) { parent::__construct(); $this->globalService = $globalService; $this->connection = $connection; - // the query builder doesn't really like subqueries with parameters - $this->updateQuery = $this->connection->prepare( - 'UPDATE *PREFIX*filecache SET size = -1 - WHERE `path` = ? - AND `storage` IN (SELECT storage_id FROM *PREFIX*mounts WHERE mount_id = ?)' - ); + $this->logger = $logger; + $this->updateQuery = $this->getUpdateQuery($this->connection); } protected function configure() { @@ -143,9 +144,9 @@ class Notify extends Base { $this->logUpdate($change, $output); } if ($change instanceof IRenameChange) { - $this->markParentAsOutdated($mount->getId(), $change->getTargetPath()); + $this->markParentAsOutdated($mount->getId(), $change->getTargetPath(), $output); } - $this->markParentAsOutdated($mount->getId(), $change->getPath()); + $this->markParentAsOutdated($mount->getId(), $change->getPath(), $output); }); } @@ -154,12 +155,21 @@ class Notify extends Base { return new $class($mount->getBackendOptions()); } - private function markParentAsOutdated($mountId, $path) { + private function markParentAsOutdated($mountId, $path, OutputInterface $output) { $parent = ltrim(dirname($path), '/'); if ($parent === '.') { $parent = ''; } - $this->updateQuery->execute([$parent, $mountId]); + + try { + $this->updateQuery->execute([$parent, $mountId]); + } catch (DriverException $ex) { + $this->logger->logException($ex, ['app' => 'files_external', 'message' => 'Error while trying to mark folder as outdated', 'level' => ILogger::WARN]); + $this->connection = $this->reconnectToDatabase($this->connection, $output); + $output->writeln('<info>Needed to reconnect to the database</info>'); + $this->updateQuery = $this->getUpdateQuery($this->connection); + $this->updateQuery->execute([$parent, $mountId]); + } } private function logUpdate(IChange $change, OutputInterface $output) { @@ -188,6 +198,41 @@ class Notify extends Base { $output->writeln($text); } + /** + * @return \Doctrine\DBAL\Statement + */ + private function getUpdateQuery(IDBConnection $connection) { + // the query builder doesn't really like subqueries with parameters + return $connection->prepare( + 'UPDATE *PREFIX*filecache SET size = -1 + WHERE `path` = ? + AND `storage` IN (SELECT storage_id FROM *PREFIX*mounts WHERE mount_id = ?)' + ); + } + + /** + * @return \OCP\IDBConnection + */ + private function reconnectToDatabase(IDBConnection $connection, OutputInterface $output) { + try { + $connection->close(); + } catch (\Exception $ex) { + $this->logger->logException($ex, ['app' => 'files_external', 'message' => 'Error while disconnecting from DB', 'level' => ILogger::WARN]); + $output->writeln("<info>Error while disconnecting from database: {$ex->getMessage()}</info>"); + } + while (!$connection->isConnected()) { + try { + $connection->connect(); + } catch (\Exception $ex) { + $this->logger->logException($ex, ['app' => 'files_external', 'message' => 'Error while re-connecting to database', 'level' => ILogger::WARN]); + $output->writeln("<info>Error while re-connecting to database: {$ex->getMessage()}</info>"); + sleep(60); + } + } + return $connection; + } + + private function selfTest(IStorage $storage, INotifyHandler $notifyHandler, $verbose, OutputInterface $output) { usleep(100 * 1000); //give time for the notify to start $storage->file_put_contents('/.nc_test_file.txt', 'test content'); diff --git a/apps/files_sharing/.l10nignore b/apps/files_sharing/.l10nignore index dfb7d6c4e6d..f5aa2c6a868 100644 --- a/apps/files_sharing/.l10nignore +++ b/apps/files_sharing/.l10nignore @@ -1,2 +1,6 @@ #webpack bundled files js/additionalScripts.js +js/dist/additionalScripts.js +js/dist/files_sharing.js +js/dist/files_sharing2.js +js/dist/files_sharing3.js diff --git a/apps/files_sharing/lib/External/Manager.php b/apps/files_sharing/lib/External/Manager.php index 6a036b663d9..0cf7d899234 100644 --- a/apps/files_sharing/lib/External/Manager.php +++ b/apps/files_sharing/lib/External/Manager.php @@ -522,7 +522,7 @@ class Manager { $share = $getShare->fetch(); $getShare->closeCursor(); - if ($result && (int)$share['share_type'] === Share::SHARE_TYPE_USER) { + if ($result && $share !== false && (int)$share['share_type'] === Share::SHARE_TYPE_USER) { try { $this->sendFeedbackToRemote($share['remote'], $share['share_token'], $share['remote_id'], 'decline'); } catch (\Exception $e) { diff --git a/apps/files_versions/lib/Storage.php b/apps/files_versions/lib/Storage.php index e2e4888cbce..c09633be3c1 100644 --- a/apps/files_versions/lib/Storage.php +++ b/apps/files_versions/lib/Storage.php @@ -750,13 +750,16 @@ class Storage { // subtract size of files and current versions size from quota if ($quota >= 0) { if ($softQuota) { - $files_view = new View('/' . $uid . '/files'); - $rootInfo = $files_view->getFileInfo('/', false); - $free = $quota - $rootInfo['size']; // remaining free space for user - if ($free > 0) { - $availableSpace = ($free * self::DEFAULTMAXSIZE / 100) - $versionsSize; // how much space can be used for versions + $userFolder = \OC::$server->getUserFolder($uid); + if(is_null($userFolder)) { + $availableSpace = 0; } else { - $availableSpace = $free - $versionsSize; + $free = $quota - $userFolder->getSize(false); // remaining free space for user + if ($free > 0) { + $availableSpace = ($free * self::DEFAULTMAXSIZE / 100) - $versionsSize; // how much space can be used for versions + } else { + $availableSpace = $free - $versionsSize; + } } } else { $availableSpace = $quota; diff --git a/apps/lookup_server_connector/lib/BackgroundJobs/RetryJob.php b/apps/lookup_server_connector/lib/BackgroundJobs/RetryJob.php index 5a7212a6522..242b0c713fe 100644 --- a/apps/lookup_server_connector/lib/BackgroundJobs/RetryJob.php +++ b/apps/lookup_server_connector/lib/BackgroundJobs/RetryJob.php @@ -36,8 +36,10 @@ class RetryJob extends Job { private $jobList; /** @var string */ private $lookupServer; - /** @var int how much time should be between two tries (10 minutes) */ - private $interval = 600; + /** @var int how much time should be between two, will be increased for each retry */ + private $interval = 100; + /** @var IConfig */ + private $config; /** * @param IClientService $clientService @@ -49,6 +51,7 @@ class RetryJob extends Job { IConfig $config) { $this->clientService = $clientService; $this->jobList = $jobList; + $this->config = $config; if ($config->getSystemValue('has_internet_connection', true) === false) { return; @@ -75,7 +78,7 @@ class RetryJob extends Job { } protected function run($argument) { - if ($argument['retryNo'] === 5 || empty($this->lookupServer)) { + if ($this->killBackgroundJob((int)$argument['retryNo'])) { return; } @@ -108,6 +111,27 @@ class RetryJob extends Job { * @return bool */ protected function shouldRun($argument) { - return !isset($argument['lastRun']) || ((time() - $argument['lastRun']) > $this->interval); + $retryNo = (int)$argument['retryNo']; + $delay = $this->interval * 6 ** $retryNo; + return !isset($argument['lastRun']) || ((time() - $argument['lastRun']) > $delay); + } + + /** + * check if we should kill the background job + * + * The lookup server should no longer be contacted if: + * + * - max retries are reached (set to 5) + * - lookup server was disabled by the admin + * - no valid lookup server URL given + * + * @param int $retryCount + * @return bool + */ + protected function killBackgroundJob($retryCount) { + $maxTriesReached = $retryCount >= 5; + $lookupServerDisabled = $this->config->getAppValue('files_sharing', 'lookupServerUploadEnabled', 'yes') !== 'yes'; + + return $maxTriesReached || $lookupServerDisabled || empty($this->lookupServer); } } diff --git a/apps/lookup_server_connector/lib/UpdateLookupServer.php b/apps/lookup_server_connector/lib/UpdateLookupServer.php index 7902ede8816..f96b3d787f2 100644 --- a/apps/lookup_server_connector/lib/UpdateLookupServer.php +++ b/apps/lookup_server_connector/lib/UpdateLookupServer.php @@ -46,6 +46,8 @@ class UpdateLookupServer { private $jobList; /** @var string URL point to lookup server */ private $lookupServer; + /** @var bool */ + private $lookupServerEnabled; /** * @param AccountManager $accountManager @@ -68,6 +70,8 @@ class UpdateLookupServer { return; } + $this->lookupServerEnabled = $config->getAppValue('files_sharing', 'lookupServerUploadEnabled', 'yes') === 'yes'; + $this->lookupServer = $config->getSystemValue('lookup_server', 'https://lookup.nextcloud.com'); if(!empty($this->lookupServer)) { $this->lookupServer = rtrim($this->lookupServer, '/'); @@ -79,7 +83,8 @@ class UpdateLookupServer { * @param IUser $user */ public function userUpdated(IUser $user) { - if(empty($this->lookupServer)) { + + if (!$this->shouldUpdateLookupServer()) { return; } @@ -150,4 +155,17 @@ class UpdateLookupServer { ); } } + + /** + * check if we should update the lookup server, we only do it if + * + * * we have a valid URL + * * the lookup server update was enabled by the admin + * + * @return bool + */ + private function shouldUpdateLookupServer() { + return $this->lookupServerEnabled || !empty($this->lookupServer); + } + } diff --git a/apps/provisioning_api/lib/Controller/UsersController.php b/apps/provisioning_api/lib/Controller/UsersController.php index a034ff6d77a..c7625a26f59 100644 --- a/apps/provisioning_api/lib/Controller/UsersController.php +++ b/apps/provisioning_api/lib/Controller/UsersController.php @@ -513,6 +513,9 @@ class UsersController extends AUserData { break; case 'password': try { + if (!$targetUser->canChangePassword()) { + throw new OCSException('Setting the password is not supported by the users backend', 103); + } $targetUser->setPassword($value); } catch (HintException $e) { // password policy error throw new OCSException($e->getMessage(), 103); diff --git a/apps/provisioning_api/tests/Controller/UsersControllerTest.php b/apps/provisioning_api/tests/Controller/UsersControllerTest.php index fcdc68d353b..c6e0082c4b0 100644 --- a/apps/provisioning_api/tests/Controller/UsersControllerTest.php +++ b/apps/provisioning_api/tests/Controller/UsersControllerTest.php @@ -1272,6 +1272,10 @@ class UsersControllerTest extends TestCase { ->will($this->returnValue($targetUser)); $targetUser ->expects($this->once()) + ->method('canChangePassword') + ->will($this->returnValue(true)); + $targetUser + ->expects($this->once()) ->method('setPassword') ->with('NewPassword'); $targetUser diff --git a/apps/twofactor_backupcodes/composer/composer/autoload_classmap.php b/apps/twofactor_backupcodes/composer/composer/autoload_classmap.php index c093a1112a3..2038421a3a2 100644 --- a/apps/twofactor_backupcodes/composer/composer/autoload_classmap.php +++ b/apps/twofactor_backupcodes/composer/composer/autoload_classmap.php @@ -17,6 +17,7 @@ return array( 'OCA\\TwoFactorBackupCodes\\Listener\\ActivityPublisher' => $baseDir . '/../lib/Listener/ActivityPublisher.php', 'OCA\\TwoFactorBackupCodes\\Listener\\ClearNotifications' => $baseDir . '/../lib/Listener/ClearNotifications.php', 'OCA\\TwoFactorBackupCodes\\Listener\\IListener' => $baseDir . '/../lib/Listener/IListener.php', + 'OCA\\TwoFactorBackupCodes\\Listener\\ProviderDisabled' => $baseDir . '/../lib/Listener/ProviderDisabled.php', 'OCA\\TwoFactorBackupCodes\\Listener\\ProviderEnabled' => $baseDir . '/../lib/Listener/ProviderEnabled.php', 'OCA\\TwoFactorBackupCodes\\Listener\\RegistryUpdater' => $baseDir . '/../lib/Listener/RegistryUpdater.php', 'OCA\\TwoFactorBackupCodes\\Migration\\CheckBackupCodes' => $baseDir . '/../lib/Migration/CheckBackupCodes.php', diff --git a/apps/twofactor_backupcodes/composer/composer/autoload_static.php b/apps/twofactor_backupcodes/composer/composer/autoload_static.php index baad28d711a..d1f124a407d 100644 --- a/apps/twofactor_backupcodes/composer/composer/autoload_static.php +++ b/apps/twofactor_backupcodes/composer/composer/autoload_static.php @@ -32,6 +32,7 @@ class ComposerStaticInitTwoFactorBackupCodes 'OCA\\TwoFactorBackupCodes\\Listener\\ActivityPublisher' => __DIR__ . '/..' . '/../lib/Listener/ActivityPublisher.php', 'OCA\\TwoFactorBackupCodes\\Listener\\ClearNotifications' => __DIR__ . '/..' . '/../lib/Listener/ClearNotifications.php', 'OCA\\TwoFactorBackupCodes\\Listener\\IListener' => __DIR__ . '/..' . '/../lib/Listener/IListener.php', + 'OCA\\TwoFactorBackupCodes\\Listener\\ProviderDisabled' => __DIR__ . '/..' . '/../lib/Listener/ProviderDisabled.php', 'OCA\\TwoFactorBackupCodes\\Listener\\ProviderEnabled' => __DIR__ . '/..' . '/../lib/Listener/ProviderEnabled.php', 'OCA\\TwoFactorBackupCodes\\Listener\\RegistryUpdater' => __DIR__ . '/..' . '/../lib/Listener/RegistryUpdater.php', 'OCA\\TwoFactorBackupCodes\\Migration\\CheckBackupCodes' => __DIR__ . '/..' . '/../lib/Migration/CheckBackupCodes.php', diff --git a/apps/twofactor_backupcodes/lib/AppInfo/Application.php b/apps/twofactor_backupcodes/lib/AppInfo/Application.php index f5d0139dbd9..fc6c94d5b7a 100644 --- a/apps/twofactor_backupcodes/lib/AppInfo/Application.php +++ b/apps/twofactor_backupcodes/lib/AppInfo/Application.php @@ -30,6 +30,7 @@ use OCA\TwoFactorBackupCodes\Event\CodesGenerated; use OCA\TwoFactorBackupCodes\Listener\ActivityPublisher; use OCA\TwoFactorBackupCodes\Listener\ClearNotifications; use OCA\TwoFactorBackupCodes\Listener\IListener; +use OCA\TwoFactorBackupCodes\Listener\ProviderDisabled; use OCA\TwoFactorBackupCodes\Listener\ProviderEnabled; use OCA\TwoFactorBackupCodes\Listener\RegistryUpdater; use OCA\TwoFactorBackupCodes\Notifications\Notifier; @@ -81,6 +82,12 @@ class Application extends App { $listener = $container->query(ProviderEnabled::class); $listener->handle($event); }); + + $eventDispatcher->addListener(IRegistry::EVENT_PROVIDER_DISABLED, function(RegistryEvent $event) use ($container) { + /** @var IListener $listener */ + $listener = $container->query(ProviderDisabled::class); + $listener->handle($event); + }); } public function registerNotification() { diff --git a/apps/twofactor_backupcodes/lib/BackgroundJob/RememberBackupCodesJob.php b/apps/twofactor_backupcodes/lib/BackgroundJob/RememberBackupCodesJob.php index b04a8c3a29f..1a7f5aca3eb 100644 --- a/apps/twofactor_backupcodes/lib/BackgroundJob/RememberBackupCodesJob.php +++ b/apps/twofactor_backupcodes/lib/BackgroundJob/RememberBackupCodesJob.php @@ -70,7 +70,15 @@ class RememberBackupCodesJob extends TimedJob { } $providers = $this->registry->getProviderStates($user); - if (isset($providers['backup_codes']) && $providers['backup_codes'] === true) { + $state2fa = array_reduce($providers, function(bool $carry, bool $state) { + return $carry || $state; + }, false); + + /* + * If no provider is active or if the backup codes are already generate + * we can remove the job + */ + if ($state2fa === false || (isset($providers['backup_codes']) && $providers['backup_codes'] === true)) { // Backup codes already generated lets remove this job $this->jobList->remove(self::class, $argument); return; diff --git a/apps/twofactor_backupcodes/lib/Listener/ProviderDisabled.php b/apps/twofactor_backupcodes/lib/Listener/ProviderDisabled.php new file mode 100644 index 00000000000..835eb0394f9 --- /dev/null +++ b/apps/twofactor_backupcodes/lib/Listener/ProviderDisabled.php @@ -0,0 +1,65 @@ +<?php +declare(strict_types=1); +/** + * @copyright Copyright (c) 2019, 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\TwoFactorBackupCodes\Listener; + +use OCA\TwoFactorBackupCodes\BackgroundJob\RememberBackupCodesJob; +use OCP\Authentication\TwoFactorAuth\IRegistry; +use OCP\Authentication\TwoFactorAuth\RegistryEvent; +use OCP\BackgroundJob\IJobList; +use Symfony\Component\EventDispatcher\Event; + +class ProviderDisabled implements IListener { + + /** @var IRegistry */ + private $registry; + + /** @var IJobList */ + private $jobList; + + public function __construct(IRegistry $registry, + IJobList $jobList) { + $this->registry = $registry; + $this->jobList = $jobList; + } + + public function handle(Event $event) { + if (!($event instanceof RegistryEvent)) { + return; + } + + $providers = $this->registry->getProviderStates($event->getUser()); + + // Loop over all providers. If all are disabled we remove the job + $state = array_reduce($providers, function (bool $carry, bool $enabled) { + return $carry || $enabled; + }, false); + + if ($state === false) { + $this->jobList->remove(RememberBackupCodesJob::class, ['uid' => $event->getUser()->getUID()]); + } + } + +} diff --git a/apps/twofactor_backupcodes/tests/Unit/BackgroundJob/RememberBackupCodesJobTest.php b/apps/twofactor_backupcodes/tests/Unit/BackgroundJob/RememberBackupCodesJobTest.php index 0e23e032bd8..fe68da8ebf5 100644 --- a/apps/twofactor_backupcodes/tests/Unit/BackgroundJob/RememberBackupCodesJobTest.php +++ b/apps/twofactor_backupcodes/tests/Unit/BackgroundJob/RememberBackupCodesJobTest.php @@ -114,6 +114,34 @@ class RememberBackupCodesJobTest extends TestCase { $this->invokePrivate($this->job, 'run', [['uid' => 'validUID']]); } + public function testNoActiveProvider() { + $user = $this->createMock(IUser::class); + $user->method('getUID') + ->willReturn('validUID'); + $this->userManager->method('get') + ->with('validUID') + ->willReturn($user); + + $this->registry->method('getProviderStates') + ->with($user) + ->willReturn([ + 'backup_codes' => false, + 'foo' => false, + ]); + + $this->jobList->expects($this->once()) + ->method('remove') + ->with( + RememberBackupCodesJob::class, + ['uid' => 'validUID'] + ); + + $this->notificationManager->expects($this->never()) + ->method($this->anything()); + + $this->invokePrivate($this->job, 'run', [['uid' => 'validUID']]); + } + public function testNotificationSend() { $user = $this->createMock(IUser::class); $user->method('getUID') @@ -125,7 +153,8 @@ class RememberBackupCodesJobTest extends TestCase { $this->registry->method('getProviderStates') ->with($user) ->willReturn([ - 'backup_codes' => false + 'backup_codes' => false, + 'foo' => true, ]); $this->jobList->expects($this->never()) diff --git a/apps/twofactor_backupcodes/tests/Unit/Listener/ProviderDisabledTest.php b/apps/twofactor_backupcodes/tests/Unit/Listener/ProviderDisabledTest.php new file mode 100644 index 00000000000..1bd5a7ccab0 --- /dev/null +++ b/apps/twofactor_backupcodes/tests/Unit/Listener/ProviderDisabledTest.php @@ -0,0 +1,110 @@ +<?php +declare(strict_types=1); +/** + * @copyright Copyright (c) 2019, 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\TwoFactorBackupCodes\Tests\Unit\Listener; + +use OCA\TwoFactorBackupCodes\BackgroundJob\RememberBackupCodesJob; +use OCA\TwoFactorBackupCodes\Listener\ProviderDisabled; +use OCP\Authentication\TwoFactorAuth\IRegistry; +use OCP\Authentication\TwoFactorAuth\RegistryEvent; +use OCP\BackgroundJob\IJobList; +use OCP\IUser; +use Symfony\Component\EventDispatcher\Event; +use Test\TestCase; + +class ProviderDisabledTest extends TestCase { + + /** @var IRegistry|\PHPUnit\Framework\MockObject\MockObject */ + private $registy; + + /** @var IJobList|\PHPUnit\Framework\MockObject\MockObject */ + private $jobList; + + /** @var ProviderDisabled */ + private $listener; + + protected function setUp() { + parent::setUp(); + + $this->registy = $this->createMock(IRegistry::class); + $this->jobList = $this->createMock(IJobList::class); + + $this->listener = new ProviderDisabled($this->registy, $this->jobList); + } + + public function testHandleGenericEvent() { + $event = $this->createMock(Event::class); + $this->jobList->expects($this->never()) + ->method($this->anything()); + + $this->listener->handle($event); + } + + public function testHandleStillActiveProvider() { + $user = $this->createMock(IUser::class); + $user->method('getUID') + ->willReturn('myUID'); + $event = $this->createMock(RegistryEvent::class); + $event->method('getUser') + ->willReturn($user); + + $this->registy->method('getProviderStates') + ->with($user) + ->willReturn([ + 'backup_codes' => false, + 'foo' => true, + ]); + + $this->jobList->expects($this->never()) + ->method($this->anything()); + + $this->listener->handle($event); + } + + public function testHandleNoActiveProvider() { + $user = $this->createMock(IUser::class); + $user->method('getUID') + ->willReturn('myUID'); + $event = $this->createMock(RegistryEvent::class); + $event->method('getUser') + ->willReturn($user); + + $this->registy->method('getProviderStates') + ->with($user) + ->willReturn([ + 'backup_codes' => false, + 'foo' => false, + ]); + + $this->jobList->expects($this->once()) + ->method('remove') + ->with( + $this->equalTo(RememberBackupCodesJob::class), + $this->equalTo(['uid' => 'myUID']) + ); + + $this->listener->handle($event); + } +} |