]> source.dussan.org Git - nextcloud-server.git/commitdiff
Add option to disallow creation of local storages 30111/head
authorVincent Petry <vincent@nextcloud.com>
Mon, 6 Dec 2021 10:18:59 +0000 (11:18 +0100)
committerVincent Petry <vincent@nextcloud.com>
Wed, 15 Dec 2021 08:15:31 +0000 (09:15 +0100)
Introduce a new config option to prevent web UI admins to create
or edit external storages of type "local".

Signed-off-by: Vincent Petry <vincent@nextcloud.com>
apps/files_external/js/settings.js
apps/files_external/lib/Controller/GlobalStoragesController.php
apps/files_external/lib/Controller/StoragesController.php
apps/files_external/lib/Controller/UserGlobalStoragesController.php
apps/files_external/lib/Controller/UserStoragesController.php
apps/files_external/templates/settings.php
apps/files_external/tests/Controller/GlobalStoragesControllerTest.php
apps/files_external/tests/Controller/StoragesControllerTest.php
apps/files_external/tests/Controller/UserStoragesControllerTest.php
config/config.sample.php

index bed380bab1456e672a6720880b585e58fe4fee64..266b4f96af3ab4a58c3e749f1da70ae0bec6fcd7 100644 (file)
@@ -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;
                }
 
@@ -981,6 +985,7 @@ MountConfigListView.prototype = _.extend({
                                        var storageConfig = new self._storageConfigClass();
                                        _.extend(storageConfig, storageParams);
                                        var $tr = self.newStorage(storageConfig, onCompletion);
+
                                        self.recheckStorageConfig($tr);
                                });
                                onCompletion.resolve();
@@ -1325,9 +1330,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();
 
index 53173f88ee579fccbeabcb6038cd94af1f97a78b..89939640acc8a3f65afe39e20ec065d775a20ecd 100644 (file)
@@ -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,
index 47021bd7d11937cc75f9df979e1914f896723ccd..b82d2a4a4c71fd69105dac38f1bbe36833f54b8a 100644 (file)
@@ -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,
index 02fd563df0fd8142955d67fb8d09b0f040d00773..74424bce006ca1d2e01e91c9ade6a339a5a351e3 100644 (file)
@@ -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
                );
        }
 
index 4032ba1d96c89d02cc8efa75c01950a5587c0269..c0a460fd8e3169d9fdf42db6633cb38b794c69a3 100644 (file)
@@ -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,
index 675cce5da6e42c60bc763c2821623d04f79ebd99..2b5b1a1bcd2231f7c493e447cb11f254a6afdca5 100644 (file)
@@ -100,7 +100,10 @@ $canCreateMounts = $_['visibilityType'] === BackendService::VISIBILITY_ADMIN ||
        <h2><?php p($l->t('No external storage configured or you don\'t have the permission to configure them')); ?></h2>
 </div>
 
-<form data-can-create="<?php echo $canCreateMounts?'true':'false' ?>" id="files_external" class="section" data-encryption-enabled="<?php echo $_['encryptionEnabled']?'true': 'false'; ?>">
+<?php
+       $canCreateNewLocalStorage = \OC::$server->getConfig()->getSystemValue('files_external_allow_create_new_local', true);
+?>
+<form data-can-create="<?php echo $canCreateMounts?'true':'false' ?>" data-can-create-local="<?php echo $canCreateNewLocalStorage?'true':'false' ?>" id="files_external" class="section" data-encryption-enabled="<?php echo $_['encryptionEnabled']?'true': 'false'; ?>">
        <h2 class="inlineblock" data-anchor-name="external-storage"><?php p($l->t('External storage')); ?></h2>
        <a target="_blank" rel="noreferrer" class="icon-info" title="<?php p($l->t('Open documentation'));?>" href="<?php p(link_to_docs('admin-external-storage')); ?>"></a>
        <p class="settings-hint"><?php p($l->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.')); ?></p>
@@ -155,7 +158,7 @@ $canCreateMounts = $_['visibilityType'] === BackendService::VISIBILITY_ADMIN ||
                                                        });
                                                ?>
                                                <?php foreach ($sortedBackends as $backend): ?>
-                                                       <?php if ($backend->getDeprecateTo()) {
+                                                       <?php if ($backend->getDeprecateTo() || (!$canCreateNewLocalStorage && $backend->getIdentifier() == "local")) {
                                                        continue;
                                                } // ignore deprecated backends?>
                                                        <option value="<?php p($backend->getIdentifier()); ?>"><?php p($backend->getText()); ?></option>
index f385b1f379e2569de2794c4f95039659122807c6..0963cd06f00153d707d8624309c88d6e4cc4b608 100644 (file)
@@ -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();
+       }
 }
index 7dc2d287b439a8b2b9b56417c404d45d26177cad..bb9be2f2d4a80e2be8ae23a27d6e54c161241fd3 100644 (file)
@@ -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')
index c9e4ec5f985c8652e3809f59100c424624f7423e..f4de39fcd2b5e2c79c87149fae9949c52b7222ff 100644 (file)
@@ -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')
index 4a28748b69c4709d5f9c87440825548d8a6bf7be..daac86f42f774ac6d4eac1b7f4b8441e19ce87ed 100644 (file)
@@ -1746,6 +1746,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