From 99d16d9e474e70a648ce16b1a7ec5f5a97165ae4 Mon Sep 17 00:00:00 2001 From: Vincent Petry Date: Mon, 6 Dec 2021 11:18:59 +0100 Subject: Add option to disallow creation of local storages Introduce a new config option to prevent web UI admins to create or edit external storages of type "local". Signed-off-by: Vincent Petry --- apps/files_external/js/settings.js | 13 +++++++--- .../lib/Controller/GlobalStoragesController.php | 18 +++++++++++-- .../lib/Controller/StoragesController.php | 20 ++++++++++++++- .../Controller/UserGlobalStoragesController.php | 7 +++-- .../lib/Controller/UserStoragesController.php | 16 ++++++++++-- apps/files_external/templates/settings.php | 7 +++-- .../Controller/GlobalStoragesControllerTest.php | 19 +++++++++++++- .../tests/Controller/StoragesControllerTest.php | 30 ++++++++++++++++++++++ .../Controller/UserStoragesControllerTest.php | 20 +++++++++++++-- config/config.sample.php | 13 ++++++++++ 10 files changed, 148 insertions(+), 15 deletions(-) diff --git a/apps/files_external/js/settings.js b/apps/files_external/js/settings.js index 32c4b1bc03b..12d121afa9a 100644 --- a/apps/files_external/js/settings.js +++ b/apps/files_external/js/settings.js @@ -659,6 +659,7 @@ MountConfigListView.prototype = _.extend({ } this._encryptionEnabled = options.encryptionEnabled; + this._canCreateLocal = options.canCreateLocal; // read the backend config that was carefully crammed // into the data-configurations attribute of the select @@ -825,10 +826,13 @@ MountConfigListView.prototype = _.extend({ $tr.addClass(backend.identifier); $tr.find('.backend').data('identifier', backend.identifier); - if (backend.invalid) { + if (backend.invalid || (backend.identifier === 'local' && !this._canCreateLocal)) { $tr.find('[name=mountPoint]').prop('disabled', true); $tr.find('.applicable,.mountOptionsToggle').empty(); - this.updateStatus($tr, false, 'Unknown backend: ' + backend.name); + $tr.find('.save').empty(); + if (backend.invalid) { + this.updateStatus($tr, false, 'Unknown backend: ' + backend.name); + } return $tr; } @@ -970,6 +974,7 @@ MountConfigListView.prototype = _.extend({ var storageConfig = new self._storageConfigClass(); _.extend(storageConfig, storageParams); var $tr = self.newStorage(storageConfig, onCompletion); + self.recheckStorageConfig($tr); }); onCompletion.resolve(); @@ -1313,9 +1318,11 @@ MountConfigListView.prototype = _.extend({ window.addEventListener('DOMContentLoaded', function() { var enabled = $('#files_external').attr('data-encryption-enabled'); + var canCreateLocal = $('#files_external').attr('data-can-create-local'); var encryptionEnabled = (enabled ==='true')? true: false; var mountConfigListView = new MountConfigListView($('#externalStorage'), { - encryptionEnabled: encryptionEnabled + encryptionEnabled: encryptionEnabled, + canCreateLocal: (canCreateLocal === 'true') ? true: false, }); mountConfigListView.loadStorages(); diff --git a/apps/files_external/lib/Controller/GlobalStoragesController.php b/apps/files_external/lib/Controller/GlobalStoragesController.php index 53173f88ee5..89939640acc 100644 --- a/apps/files_external/lib/Controller/GlobalStoragesController.php +++ b/apps/files_external/lib/Controller/GlobalStoragesController.php @@ -31,6 +31,7 @@ use OCA\Files_External\NotFoundException; use OCA\Files_External\Service\GlobalStoragesService; use OCP\AppFramework\Http; use OCP\AppFramework\Http\DataResponse; +use OCP\IConfig; use OCP\IGroupManager; use OCP\IL10N; use OCP\ILogger; @@ -51,6 +52,7 @@ class GlobalStoragesController extends StoragesController { * @param ILogger $logger * @param IUserSession $userSession * @param IGroupManager $groupManager + * @param IConfig $config */ public function __construct( $AppName, @@ -59,7 +61,8 @@ class GlobalStoragesController extends StoragesController { GlobalStoragesService $globalStoragesService, ILogger $logger, IUserSession $userSession, - IGroupManager $groupManager + IGroupManager $groupManager, + IConfig $config ) { parent::__construct( $AppName, @@ -68,7 +71,8 @@ class GlobalStoragesController extends StoragesController { $globalStoragesService, $logger, $userSession, - $groupManager + $groupManager, + $config ); } @@ -96,6 +100,16 @@ class GlobalStoragesController extends StoragesController { $applicableGroups, $priority ) { + $canCreateNewLocalStorage = $this->config->getSystemValue('files_external_allow_create_new_local', true); + if (!$canCreateNewLocalStorage && $backend === 'local') { + return new DataResponse( + [ + 'message' => $this->l10n->t('Forbidden to manage local mounts') + ], + Http::STATUS_FORBIDDEN + ); + } + $newStorage = $this->createStorage( $mountPoint, $backend, diff --git a/apps/files_external/lib/Controller/StoragesController.php b/apps/files_external/lib/Controller/StoragesController.php index 47021bd7d11..b82d2a4a4c7 100644 --- a/apps/files_external/lib/Controller/StoragesController.php +++ b/apps/files_external/lib/Controller/StoragesController.php @@ -39,6 +39,7 @@ use OCP\AppFramework\Controller; use OCP\AppFramework\Http; use OCP\AppFramework\Http\DataResponse; use OCP\Files\StorageNotAvailableException; +use OCP\IConfig; use OCP\IGroupManager; use OCP\IL10N; use OCP\ILogger; @@ -79,6 +80,11 @@ abstract class StoragesController extends Controller { */ protected $groupManager; + /** + * @var IConfig + */ + protected $config; + /** * Creates a new storages controller. * @@ -95,7 +101,8 @@ abstract class StoragesController extends Controller { StoragesService $storagesService, ILogger $logger, IUserSession $userSession, - IGroupManager $groupManager + IGroupManager $groupManager, + IConfig $config ) { parent::__construct($AppName, $request); $this->l10n = $l10n; @@ -103,6 +110,7 @@ abstract class StoragesController extends Controller { $this->logger = $logger; $this->userSession = $userSession; $this->groupManager = $groupManager; + $this->config = $config; } /** @@ -129,6 +137,16 @@ abstract class StoragesController extends Controller { $applicableGroups = null, $priority = null ) { + $canCreateNewLocalStorage = $this->config->getSystemValue('files_external_allow_create_new_local', true); + if (!$canCreateNewLocalStorage && $backend === 'local') { + return new DataResponse( + [ + 'message' => $this->l10n->t('Forbidden to manage local mounts') + ], + Http::STATUS_FORBIDDEN + ); + } + try { return $this->service->createStorage( $mountPoint, diff --git a/apps/files_external/lib/Controller/UserGlobalStoragesController.php b/apps/files_external/lib/Controller/UserGlobalStoragesController.php index 02fd563df0f..74424bce006 100644 --- a/apps/files_external/lib/Controller/UserGlobalStoragesController.php +++ b/apps/files_external/lib/Controller/UserGlobalStoragesController.php @@ -36,6 +36,7 @@ use OCA\Files_External\NotFoundException; use OCA\Files_External\Service\UserGlobalStoragesService; use OCP\AppFramework\Http; use OCP\AppFramework\Http\DataResponse; +use OCP\IConfig; use OCP\IGroupManager; use OCP\IL10N; use OCP\ILogger; @@ -64,7 +65,8 @@ class UserGlobalStoragesController extends StoragesController { UserGlobalStoragesService $userGlobalStoragesService, ILogger $logger, IUserSession $userSession, - IGroupManager $groupManager + IGroupManager $groupManager, + IConfig $config ) { parent::__construct( $AppName, @@ -73,7 +75,8 @@ class UserGlobalStoragesController extends StoragesController { $userGlobalStoragesService, $logger, $userSession, - $groupManager + $groupManager, + $config ); } diff --git a/apps/files_external/lib/Controller/UserStoragesController.php b/apps/files_external/lib/Controller/UserStoragesController.php index 4032ba1d96c..c0a460fd8e3 100644 --- a/apps/files_external/lib/Controller/UserStoragesController.php +++ b/apps/files_external/lib/Controller/UserStoragesController.php @@ -35,6 +35,7 @@ use OCA\Files_External\NotFoundException; use OCA\Files_External\Service\UserStoragesService; use OCP\AppFramework\Http; use OCP\AppFramework\Http\DataResponse; +use OCP\IConfig; use OCP\IGroupManager; use OCP\IL10N; use OCP\ILogger; @@ -63,7 +64,8 @@ class UserStoragesController extends StoragesController { UserStoragesService $userStoragesService, ILogger $logger, IUserSession $userSession, - IGroupManager $groupManager + IGroupManager $groupManager, + IConfig $config ) { parent::__construct( $AppName, @@ -72,7 +74,8 @@ class UserStoragesController extends StoragesController { $userStoragesService, $logger, $userSession, - $groupManager + $groupManager, + $config ); } @@ -127,6 +130,15 @@ class UserStoragesController extends StoragesController { $backendOptions, $mountOptions ) { + $canCreateNewLocalStorage = $this->config->getSystemValue('files_external_allow_create_new_local', true); + if (!$canCreateNewLocalStorage && $backend === 'local') { + return new DataResponse( + [ + 'message' => $this->l10n->t('Forbidden to manage local mounts') + ], + Http::STATUS_FORBIDDEN + ); + } $newStorage = $this->createStorage( $mountPoint, $backend, diff --git a/apps/files_external/templates/settings.php b/apps/files_external/templates/settings.php index f63cab07560..316467c5563 100644 --- a/apps/files_external/templates/settings.php +++ b/apps/files_external/templates/settings.php @@ -100,7 +100,10 @@ $canCreateMounts = $_['visibilityType'] === BackendService::VISIBILITY_ADMIN ||

t('No external storage configured or you don\'t have the permission to configure them')); ?>

-
+getConfig()->getSystemValue('files_external_allow_create_new_local', true); +?> +

t('External storage')); ?>

t('External storage enables you to mount external storage services and devices as secondary Nextcloud storage devices. You may also allow users to mount their own external storage services.')); ?>

@@ -150,7 +153,7 @@ $canCreateMounts = $_['visibilityType'] === BackendService::VISIBILITY_ADMIN || }); ?> - getDeprecateTo()) { + getDeprecateTo() || (!$canCreateNewLocalStorage && $backend->getIdentifier() == "local")) { continue; } // ignore deprecated backends?> diff --git a/apps/files_external/tests/Controller/GlobalStoragesControllerTest.php b/apps/files_external/tests/Controller/GlobalStoragesControllerTest.php index f385b1f379e..0963cd06f00 100644 --- a/apps/files_external/tests/Controller/GlobalStoragesControllerTest.php +++ b/apps/files_external/tests/Controller/GlobalStoragesControllerTest.php @@ -28,6 +28,7 @@ namespace OCA\Files_External\Tests\Controller; use OC\User\User; use OCA\Files_External\Controller\GlobalStoragesController; use OCA\Files_External\Service\BackendService; +use OCP\IConfig; use OCP\IGroupManager; use OCP\IL10N; use OCP\ILogger; @@ -38,6 +39,7 @@ use Symfony\Component\EventDispatcher\EventDispatcherInterface; class GlobalStoragesControllerTest extends StoragesControllerTest { protected function setUp(): void { parent::setUp(); + $this->service = $this->getMockBuilder('\OCA\Files_External\Service\GlobalStoragesService') ->disableOriginalConstructor() ->getMock(); @@ -45,11 +47,20 @@ class GlobalStoragesControllerTest extends StoragesControllerTest { $this->service->method('getVisibilityType') ->willReturn(BackendService::VISIBILITY_ADMIN); + $this->controller = $this->createController(true); + } + + private function createController($allowCreateLocal = true) { $session = $this->createMock(IUserSession::class); $session->method('getUser') ->willReturn(new User('test', null, $this->createMock(EventDispatcherInterface::class))); - $this->controller = new GlobalStoragesController( + $config = $this->createMock(IConfig::class); + $config->method('getSystemValue') + ->with('files_external_allow_create_new_local', true) + ->willReturn($allowCreateLocal); + + return new GlobalStoragesController( 'files_external', $this->createMock(IRequest::class), $this->createMock(IL10N::class), @@ -57,6 +68,12 @@ class GlobalStoragesControllerTest extends StoragesControllerTest { $this->createMock(ILogger::class), $session, $this->createMock(IGroupManager::class), + $config ); } + + public function testAddLocalStorageWhenDisabled() { + $this->controller = $this->createController(false); + parent::testAddLocalStorageWhenDisabled(); + } } diff --git a/apps/files_external/tests/Controller/StoragesControllerTest.php b/apps/files_external/tests/Controller/StoragesControllerTest.php index 7dc2d287b43..bb9be2f2d4a 100644 --- a/apps/files_external/tests/Controller/StoragesControllerTest.php +++ b/apps/files_external/tests/Controller/StoragesControllerTest.php @@ -130,6 +130,36 @@ abstract class StoragesControllerTest extends \Test\TestCase { $this->assertEquals($storageConfig, $data); } + public function testAddLocalStorageWhenDisabled() { + $authMech = $this->getAuthMechMock(); + $backend = $this->getBackendMock(); + + $storageConfig = new StorageConfig(1); + $storageConfig->setMountPoint('mount'); + $storageConfig->setBackend($backend); + $storageConfig->setAuthMechanism($authMech); + $storageConfig->setBackendOptions([]); + + $this->service->expects($this->never()) + ->method('createStorage'); + $this->service->expects($this->never()) + ->method('addStorage'); + + $response = $this->controller->create( + 'mount', + 'local', + '\OCA\Files_External\Lib\Auth\NullMechanism', + [], + [], + [], + [], + null + ); + + $data = $response->getData(); + $this->assertEquals(Http::STATUS_FORBIDDEN, $response->getStatus()); + } + public function testUpdateStorage() { $authMech = $this->getAuthMechMock(); $authMech->method('validateStorage') diff --git a/apps/files_external/tests/Controller/UserStoragesControllerTest.php b/apps/files_external/tests/Controller/UserStoragesControllerTest.php index c9e4ec5f985..f4de39fcd2b 100644 --- a/apps/files_external/tests/Controller/UserStoragesControllerTest.php +++ b/apps/files_external/tests/Controller/UserStoragesControllerTest.php @@ -31,6 +31,7 @@ use OCA\Files_External\Controller\UserStoragesController; use OCA\Files_External\Lib\StorageConfig; use OCA\Files_External\Service\BackendService; use OCP\AppFramework\Http; +use OCP\IConfig; use OCP\IGroupManager; use OCP\IL10N; use OCP\ILogger; @@ -54,21 +55,36 @@ class UserStoragesControllerTest extends StoragesControllerTest { $this->service->method('getVisibilityType') ->willReturn(BackendService::VISIBILITY_PERSONAL); + $this->controller = $this->createController(true); + } + + private function createController($allowCreateLocal = true) { $session = $this->createMock(IUserSession::class); $session->method('getUser') ->willReturn(new User('test', null, $this->createMock(EventDispatcherInterface::class))); - $this->controller = new UserStoragesController( + $config = $this->createMock(IConfig::class); + $config->method('getSystemValue') + ->with('files_external_allow_create_new_local', true) + ->willReturn($allowCreateLocal); + + return new UserStoragesController( 'files_external', $this->createMock(IRequest::class), $this->createMock(IL10N::class), $this->service, $this->createMock(ILogger::class), $session, - $this->createMock(IGroupManager::class) + $this->createMock(IGroupManager::class), + $config ); } + public function testAddLocalStorageWhenDisabled() { + $this->controller = $this->createController(false); + parent::testAddLocalStorageWhenDisabled(); + } + public function testAddOrUpdateStorageDisallowedBackend() { $backend = $this->getBackendMock(); $backend->method('isVisibleFor') diff --git a/config/config.sample.php b/config/config.sample.php index b099b08f78c..20534cfc66d 100644 --- a/config/config.sample.php +++ b/config/config.sample.php @@ -1805,6 +1805,19 @@ $CONFIG = [ */ 'external_storage.auth_availability_delay' => 1800, +/** + * Allows to create external storages of type "Local" in the web interface and APIs. + * + * When disable, it is still possible to create local storages with occ using + * the following command: + * + * % php occ files_external:create /mountpoint local null::null -c datadir=/path/to/data + * + * Defaults to ``true`` + * + */ +'files_external_allow_create_new_local' => true, + /** * Specifies how often the local filesystem (the Nextcloud data/ directory, and * NFS mounts in data/) is checked for changes made outside Nextcloud. This -- cgit v1.2.3