diff options
Diffstat (limited to 'apps/files_external/lib/Controller')
6 files changed, 327 insertions, 405 deletions
diff --git a/apps/files_external/lib/Controller/AjaxController.php b/apps/files_external/lib/Controller/AjaxController.php index 9c2953851f5..5cee6422530 100644 --- a/apps/files_external/lib/Controller/AjaxController.php +++ b/apps/files_external/lib/Controller/AjaxController.php @@ -1,53 +1,25 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Martin Mattel <martin.mattel@diemattels.at> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Robin McCorkell <robin@mccorkell.me.uk> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Ross Nicoll <jrn@jrn.me.uk> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * 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, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2017-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ - namespace OCA\Files_External\Controller; use OCA\Files_External\Lib\Auth\Password\GlobalAuth; +use OCA\Files_External\Lib\Auth\PublicKey\RSA; use OCP\AppFramework\Controller; use OCP\AppFramework\Http; -use OCP\AppFramework\Http\Response; +use OCP\AppFramework\Http\Attribute\NoAdminRequired; +use OCP\AppFramework\Http\Attribute\PasswordConfirmationRequired; +use OCP\AppFramework\Http\JSONResponse; use OCP\IGroupManager; +use OCP\IL10N; use OCP\IRequest; -use OCP\AppFramework\Http\JSONResponse; -use OCA\Files_External\Lib\Auth\PublicKey\RSA; use OCP\IUserSession; class AjaxController extends Controller { - /** @var RSA */ - private $rsaMechanism; - /** @var GlobalAuth */ - private $globalAuth; - /** @var IUserSession */ - private $userSession; - /** @var IGroupManager */ - private $groupManager; - /** * @param string $appName * @param IRequest $request @@ -56,17 +28,16 @@ class AjaxController extends Controller { * @param IUserSession $userSession * @param IGroupManager $groupManager */ - public function __construct($appName, - IRequest $request, - RSA $rsaMechanism, - GlobalAuth $globalAuth, - IUserSession $userSession, - IGroupManager $groupManager) { + public function __construct( + $appName, + IRequest $request, + private RSA $rsaMechanism, + private GlobalAuth $globalAuth, + private IUserSession $userSession, + private IGroupManager $groupManager, + private IL10N $l10n, + ) { parent::__construct($appName, $request); - $this->rsaMechanism = $rsaMechanism; - $this->globalAuth = $globalAuth; - $this->userSession = $userSession; - $this->groupManager = $groupManager; } /** @@ -84,41 +55,53 @@ class AjaxController extends Controller { /** * Generates an SSH public/private key pair. * - * @NoAdminRequired * @param int $keyLength */ + #[NoAdminRequired] public function getSshKeys($keyLength = 1024) { $key = $this->generateSshKeys($keyLength); - return new JSONResponse( - array('data' => array( + return new JSONResponse([ + 'data' => [ 'private_key' => $key['privatekey'], 'public_key' => $key['publickey'] - ), - 'status' => 'success' - )); + ], + 'status' => 'success', + ]); } /** - * @NoAdminRequired - * * @param string $uid * @param string $user * @param string $password - * @return bool + * @return JSONResponse */ - public function saveGlobalCredentials($uid, $user, $password) { + #[NoAdminRequired] + #[PasswordConfirmationRequired(strict: true)] + public function saveGlobalCredentials($uid, $user, $password): JSONResponse { $currentUser = $this->userSession->getUser(); + if ($currentUser === null) { + return new JSONResponse([ + 'status' => 'error', + 'message' => $this->l10n->t('You are not logged in'), + ], Http::STATUS_UNAUTHORIZED); + } // Non-admins can only edit their own credentials - $allowedToEdit = ( - $this->groupManager->isAdmin($currentUser->getUID()) || $currentUser->getUID() === $uid - ) ? true : false; + // Admin can edit global credentials + $allowedToEdit = $uid === '' + ? $this->groupManager->isAdmin($currentUser->getUID()) + : $currentUser->getUID() === $uid; if ($allowedToEdit) { $this->globalAuth->saveAuth($uid, $user, $password); - return true; - } else { - return false; + return new JSONResponse([ + 'status' => 'success', + ]); } + + return new JSONResponse([ + 'status' => 'success', + 'message' => $this->l10n->t('Permission denied'), + ], Http::STATUS_FORBIDDEN); } } diff --git a/apps/files_external/lib/Controller/ApiController.php b/apps/files_external/lib/Controller/ApiController.php index 8be623c5eee..49547357e6b 100644 --- a/apps/files_external/lib/Controller/ApiController.php +++ b/apps/files_external/lib/Controller/ApiController.php @@ -1,105 +1,98 @@ <?php + declare(strict_types=1); + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Jesús Macias <jmacias@solidgear.es> - * @author Joas Schilling <coding@schilljs.com> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Robin McCorkell <robin@mccorkell.me.uk> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Vincent Petry <pvince81@owncloud.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * 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, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2017-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ - namespace OCA\Files_External\Controller; +use OCA\Files_External\Lib\StorageConfig; +use OCA\Files_External\ResponseDefinitions; +use OCA\Files_External\Service\UserGlobalStoragesService; +use OCA\Files_External\Service\UserStoragesService; +use OCP\AppFramework\Http; +use OCP\AppFramework\Http\Attribute\NoAdminRequired; use OCP\AppFramework\Http\DataResponse; use OCP\AppFramework\OCSController; +use OCP\Constants; use OCP\IRequest; -use OCP\IUserSession; +/** + * @psalm-import-type Files_ExternalMount from ResponseDefinitions + */ class ApiController extends OCSController { - /** @var IUserSession */ - private $userSession; - - public function __construct(string $appName, - IRequest $request, - IUserSession $userSession) { + public function __construct( + string $appName, + IRequest $request, + private UserGlobalStoragesService $userGlobalStoragesService, + private UserStoragesService $userStoragesService, + ) { parent::__construct($appName, $request); - - $this->userSession = $userSession; } /** * Formats the given mount config to a mount entry. * * @param string $mountPoint mount point name, relative to the data dir - * @param array $mountConfig mount config to format + * @param StorageConfig $mountConfig mount config to format * - * @return array entry + * @return Files_ExternalMount */ - private function formatMount(string $mountPoint, array $mountConfig): array { - // strip "/$user/files" from mount point - $mountPoint = explode('/', trim($mountPoint, '/'), 3); - $mountPoint = $mountPoint[2] ?? ''; - + private function formatMount(string $mountPoint, StorageConfig $mountConfig): array { // split path from mount point $path = \dirname($mountPoint); - if ($path === '.') { + if ($path === '.' || $path === '/') { $path = ''; } - $isSystemMount = !$mountConfig['personal']; + $isSystemMount = $mountConfig->getType() === StorageConfig::MOUNT_TYPE_ADMIN; - $permissions = \OCP\Constants::PERMISSION_READ; + $permissions = Constants::PERMISSION_READ; // personal mounts can be deleted if (!$isSystemMount) { - $permissions |= \OCP\Constants::PERMISSION_DELETE; + $permissions |= Constants::PERMISSION_DELETE; } - $entry = array( + $entry = [ + 'id' => $mountConfig->getId(), + 'type' => 'dir', 'name' => basename($mountPoint), 'path' => $path, - 'type' => 'dir', - 'backend' => $mountConfig['backend'], - 'scope' => $isSystemMount ? 'system' : 'personal', 'permissions' => $permissions, - 'id' => $mountConfig['id'], - 'class' => $mountConfig['class'] - ); + 'scope' => $isSystemMount ? 'system' : 'personal', + 'backend' => $mountConfig->getBackend()->getText(), + 'class' => $mountConfig->getBackend()->getIdentifier(), + 'config' => $mountConfig->jsonSerialize(true), + ]; return $entry; } /** - * @NoAdminRequired + * Get the mount points visible for this user * - * Returns the mount points visible for this user. + * @return DataResponse<Http::STATUS_OK, list<Files_ExternalMount>, array{}> * - * @return DataResponse share information + * 200: User mounts returned */ + #[NoAdminRequired] public function getUserMounts(): DataResponse { $entries = []; - $user = $this->userSession->getUser()->getUID(); + $mountPoints = []; - $mounts = \OC_Mount_Config::getAbsoluteMountPoints($user); - foreach($mounts as $mountPoint => $mount) { + foreach ($this->userGlobalStoragesService->getStorages() as $storage) { + $mountPoint = $storage->getMountPoint(); + $mountPoints[$mountPoint] = $storage; + } + + foreach ($this->userStoragesService->getStorages() as $storage) { + $mountPoint = $storage->getMountPoint(); + $mountPoints[$mountPoint] = $storage; + } + foreach ($mountPoints as $mountPoint => $mount) { $entries[] = $this->formatMount($mountPoint, $mount); } diff --git a/apps/files_external/lib/Controller/GlobalStoragesController.php b/apps/files_external/lib/Controller/GlobalStoragesController.php index 69056225f8a..e7274c9cfb6 100644 --- a/apps/files_external/lib/Controller/GlobalStoragesController.php +++ b/apps/files_external/lib/Controller/GlobalStoragesController.php @@ -1,39 +1,23 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Joas Schilling <coding@schilljs.com> - * @author Juan Pablo Villafáñez <jvillafanez@solidgear.es> - * @author Robin Appelman <robin@icewind.nl> - * @author Robin McCorkell <robin@mccorkell.me.uk> - * @author Vincent Petry <pvince81@owncloud.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * 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, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2017-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ - namespace OCA\Files_External\Controller; - -use OCP\ILogger; -use \OCP\IRequest; -use \OCP\IL10N; -use \OCP\AppFramework\Http\DataResponse; -use \OCP\AppFramework\Http; -use OCA\Files_External\Service\GlobalStoragesService; use OCA\Files_External\NotFoundException; +use OCA\Files_External\Service\GlobalStoragesService; +use OCP\AppFramework\Http; +use OCP\AppFramework\Http\Attribute\PasswordConfirmationRequired; +use OCP\AppFramework\Http\DataResponse; +use OCP\IConfig; +use OCP\IGroupManager; +use OCP\IL10N; +use OCP\IRequest; +use OCP\IUserSession; +use Psr\Log\LoggerInterface; /** * Global storages controller @@ -46,21 +30,30 @@ class GlobalStoragesController extends StoragesController { * @param IRequest $request request object * @param IL10N $l10n l10n service * @param GlobalStoragesService $globalStoragesService storage service - * @param ILogger $logger + * @param LoggerInterface $logger + * @param IUserSession $userSession + * @param IGroupManager $groupManager + * @param IConfig $config */ public function __construct( $AppName, IRequest $request, IL10N $l10n, GlobalStoragesService $globalStoragesService, - ILogger $logger + LoggerInterface $logger, + IUserSession $userSession, + IGroupManager $groupManager, + IConfig $config, ) { parent::__construct( $AppName, $request, $l10n, $globalStoragesService, - $logger + $logger, + $userSession, + $groupManager, + $config ); } @@ -78,6 +71,7 @@ class GlobalStoragesController extends StoragesController { * * @return DataResponse */ + #[PasswordConfirmationRequired(strict: true)] public function create( $mountPoint, $backend, @@ -86,8 +80,18 @@ class GlobalStoragesController extends StoragesController { $mountOptions, $applicableUsers, $applicableGroups, - $priority + $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, @@ -112,7 +116,7 @@ class GlobalStoragesController extends StoragesController { $this->updateStorageStatus($newStorage); return new DataResponse( - $newStorage, + $newStorage->jsonSerialize(true), Http::STATUS_CREATED ); } @@ -123,16 +127,16 @@ class GlobalStoragesController extends StoragesController { * @param int $id storage id * @param string $mountPoint storage mount point * @param string $backend backend identifier - * @param string $authMechanism authentication mechansim identifier + * @param string $authMechanism authentication mechanism identifier * @param array $backendOptions backend-specific options * @param array $mountOptions mount-specific options * @param array $applicableUsers users for which to mount the storage * @param array $applicableGroups groups for which to mount the storage * @param int $priority priority - * @param bool $testOnly whether to storage should only test the connection or do more things * * @return DataResponse */ + #[PasswordConfirmationRequired(strict: true)] public function update( $id, $mountPoint, @@ -143,7 +147,6 @@ class GlobalStoragesController extends StoragesController { $applicableUsers, $applicableGroups, $priority, - $testOnly = true ) { $storage = $this->createStorage( $mountPoint, @@ -170,20 +173,17 @@ class GlobalStoragesController extends StoragesController { } catch (NotFoundException $e) { return new DataResponse( [ - 'message' => (string)$this->l10n->t('Storage with ID "%d" not found', array($id)) + 'message' => $this->l10n->t('Storage with ID "%d" not found', [$id]) ], Http::STATUS_NOT_FOUND ); } - $this->updateStorageStatus($storage, $testOnly); + $this->updateStorageStatus($storage); return new DataResponse( - $storage, + $storage->jsonSerialize(true), Http::STATUS_OK ); - } - - } diff --git a/apps/files_external/lib/Controller/StoragesController.php b/apps/files_external/lib/Controller/StoragesController.php index dd2bf69a0a6..df3a4528054 100644 --- a/apps/files_external/lib/Controller/StoragesController.php +++ b/apps/files_external/lib/Controller/StoragesController.php @@ -1,71 +1,35 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Jesús Macias <jmacias@solidgear.es> - * @author Joas Schilling <coding@schilljs.com> - * @author Juan Pablo Villafáñez <jvillafanez@solidgear.es> - * @author Robin Appelman <robin@icewind.nl> - * @author Robin McCorkell <robin@mccorkell.me.uk> - * @author Vincent Petry <pvince81@owncloud.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * 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, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2017-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ - namespace OCA\Files_External\Controller; - -use OCP\ILogger; -use \OCP\IRequest; -use \OCP\IL10N; -use \OCP\AppFramework\Http\DataResponse; -use \OCP\AppFramework\Controller; -use \OCP\AppFramework\Http; -use OCA\Files_External\Service\StoragesService; -use OCA\Files_External\NotFoundException; +use OCA\Files_External\Lib\Auth\AuthMechanism; +use OCA\Files_External\Lib\Backend\Backend; +use OCA\Files_External\Lib\InsufficientDataForMeaningfulAnswerException; use OCA\Files_External\Lib\StorageConfig; -use \OCA\Files_External\Lib\Backend\Backend; -use \OCA\Files_External\Lib\Auth\AuthMechanism; -use \OCP\Files\StorageNotAvailableException; -use \OCA\Files_External\Lib\InsufficientDataForMeaningfulAnswerException; +use OCA\Files_External\MountConfig; +use OCA\Files_External\NotFoundException; +use OCA\Files_External\Service\StoragesService; +use OCP\AppFramework\Controller; +use OCP\AppFramework\Http; +use OCP\AppFramework\Http\Attribute\PasswordConfirmationRequired; +use OCP\AppFramework\Http\DataResponse; +use OCP\Files\StorageNotAvailableException; +use OCP\IConfig; +use OCP\IGroupManager; +use OCP\IL10N; +use OCP\IRequest; +use OCP\IUserSession; +use Psr\Log\LoggerInterface; /** * Base class for storages controllers */ abstract class StoragesController extends Controller { - - /** - * L10N service - * - * @var IL10N - */ - protected $l10n; - - /** - * Storages service - * - * @var StoragesService - */ - protected $service; - - /** - * @var ILogger - */ - protected $logger; - /** * Creates a new storages controller. * @@ -73,19 +37,19 @@ abstract class StoragesController extends Controller { * @param IRequest $request request object * @param IL10N $l10n l10n service * @param StoragesService $storagesService storage service - * @param ILogger $logger + * @param LoggerInterface $logger */ public function __construct( $AppName, IRequest $request, - IL10N $l10n, - StoragesService $storagesService, - ILogger $logger + protected IL10N $l10n, + protected StoragesService $service, + protected LoggerInterface $logger, + protected IUserSession $userSession, + protected IGroupManager $groupManager, + protected IConfig $config, ) { parent::__construct($AppName, $request); - $this->l10n = $l10n; - $this->service = $storagesService; - $this->logger = $logger; } /** @@ -110,8 +74,18 @@ abstract class StoragesController extends Controller { $mountOptions = null, $applicableUsers = null, $applicableGroups = null, - $priority = 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, @@ -124,10 +98,10 @@ abstract class StoragesController extends Controller { $priority ); } catch (\InvalidArgumentException $e) { - $this->logger->logException($e); + $this->logger->error($e->getMessage(), ['exception' => $e]); return new DataResponse( [ - 'message' => (string)$this->l10n->t('Invalid backend or authentication mechanism class') + 'message' => $this->l10n->t('Invalid backend or authentication mechanism class') ], Http::STATUS_UNPROCESSABLE_ENTITY ); @@ -145,9 +119,9 @@ abstract class StoragesController extends Controller { $mountPoint = $storage->getMountPoint(); if ($mountPoint === '') { return new DataResponse( - array( - 'message' => (string)$this->l10n->t('Invalid mount point') - ), + [ + 'message' => $this->l10n->t('Invalid mount point'), + ], Http::STATUS_UNPROCESSABLE_ENTITY ); } @@ -155,9 +129,9 @@ abstract class StoragesController extends Controller { if ($storage->getBackendOption('objectstore')) { // objectstore must not be sent from client side return new DataResponse( - array( - 'message' => (string)$this->l10n->t('Objectstore forbidden') - ), + [ + 'message' => $this->l10n->t('Objectstore forbidden'), + ], Http::STATUS_UNPROCESSABLE_ENTITY ); } @@ -169,11 +143,11 @@ abstract class StoragesController extends Controller { if ($backend->checkDependencies()) { // invalid backend return new DataResponse( - array( - 'message' => (string)$this->l10n->t('Invalid storage backend "%s"', [ - $backend->getIdentifier() - ]) - ), + [ + 'message' => $this->l10n->t('Invalid storage backend "%s"', [ + $backend->getIdentifier(), + ]), + ], Http::STATUS_UNPROCESSABLE_ENTITY ); } @@ -181,22 +155,22 @@ abstract class StoragesController extends Controller { if (!$backend->isVisibleFor($this->service->getVisibilityType())) { // not permitted to use backend return new DataResponse( - array( - 'message' => (string)$this->l10n->t('Not permitted to use backend "%s"', [ - $backend->getIdentifier() - ]) - ), + [ + 'message' => $this->l10n->t('Not permitted to use backend "%s"', [ + $backend->getIdentifier(), + ]), + ], Http::STATUS_UNPROCESSABLE_ENTITY ); } if (!$authMechanism->isVisibleFor($this->service->getVisibilityType())) { // not permitted to use auth mechanism return new DataResponse( - array( - 'message' => (string)$this->l10n->t('Not permitted to use authentication mechanism "%s"', [ - $authMechanism->getIdentifier() - ]) - ), + [ + 'message' => $this->l10n->t('Not permitted to use authentication mechanism "%s"', [ + $authMechanism->getIdentifier(), + ]), + ], Http::STATUS_UNPROCESSABLE_ENTITY ); } @@ -204,9 +178,9 @@ abstract class StoragesController extends Controller { if (!$backend->validateStorage($storage)) { // unsatisfied parameters return new DataResponse( - array( - 'message' => (string)$this->l10n->t('Unsatisfied backend parameters') - ), + [ + 'message' => $this->l10n->t('Unsatisfied backend parameters'), + ], Http::STATUS_UNPROCESSABLE_ENTITY ); } @@ -214,7 +188,7 @@ abstract class StoragesController extends Controller { // unsatisfied parameters return new DataResponse( [ - 'message' => (string)$this->l10n->t('Unsatisfied authentication mechanism parameters') + 'message' => $this->l10n->t('Unsatisfied authentication mechanism parameters'), ], Http::STATUS_UNPROCESSABLE_ENTITY ); @@ -239,9 +213,8 @@ abstract class StoragesController extends Controller { * on whether the remote storage is available or not. * * @param StorageConfig $storage storage configuration - * @param bool $testOnly whether to storage should only test the connection or do more things */ - protected function updateStorageStatus(StorageConfig &$storage, $testOnly = true) { + protected function updateStorageStatus(StorageConfig &$storage) { try { $this->manipulateStorageConfig($storage); @@ -249,29 +222,27 @@ abstract class StoragesController extends Controller { $backend = $storage->getBackend(); // update status (can be time-consuming) $storage->setStatus( - \OC_Mount_Config::getBackendStatus( + MountConfig::getBackendStatus( $backend->getStorageClass(), $storage->getBackendOptions(), - false, - $testOnly ) ); } catch (InsufficientDataForMeaningfulAnswerException $e) { - $status = $e->getCode() ? $e->getCode() : StorageNotAvailableException::STATUS_INDETERMINATE; + $status = $e->getCode() ?: StorageNotAvailableException::STATUS_INDETERMINATE; $storage->setStatus( - $status, + (int)$status, $this->l10n->t('Insufficient data: %s', [$e->getMessage()]) ); } catch (StorageNotAvailableException $e) { $storage->setStatus( - $e->getCode(), + (int)$e->getCode(), $this->l10n->t('%s', [$e->getMessage()]) ); } catch (\Exception $e) { // FIXME: convert storage exceptions to StorageNotAvailableException $storage->setStatus( StorageNotAvailableException::STATUS_ERROR, - get_class($e).': '.$e->getMessage() + get_class($e) . ': ' . $e->getMessage() ); } } @@ -282,7 +253,7 @@ abstract class StoragesController extends Controller { * @return DataResponse */ public function index() { - $storages = $this->service->getStorages(); + $storages = array_map(static fn ($storage) => $storage->jsonSerialize(true), $this->service->getStorages()); return new DataResponse( $storages, @@ -294,26 +265,29 @@ abstract class StoragesController extends Controller { * Get an external storage entry. * * @param int $id storage id - * @param bool $testOnly whether to storage should only test the connection or do more things * * @return DataResponse */ - public function show($id, $testOnly = true) { + public function show(int $id) { try { $storage = $this->service->getStorage($id); - $this->updateStorageStatus($storage, $testOnly); + $this->updateStorageStatus($storage); } catch (NotFoundException $e) { return new DataResponse( [ - 'message' => (string)$this->l10n->t('Storage with ID "%d" not found', array($id)) + 'message' => $this->l10n->t('Storage with ID "%d" not found', [$id]), ], Http::STATUS_NOT_FOUND ); } + $data = $storage->jsonSerialize(true); + $isAdmin = $this->groupManager->isAdmin($this->userSession->getUser()->getUID()); + $data['can_edit'] = $storage->getType() === StorageConfig::MOUNT_TYPE_PERSONAL || $isAdmin; + return new DataResponse( - $storage, + $data, Http::STATUS_OK ); } @@ -325,13 +299,14 @@ abstract class StoragesController extends Controller { * * @return DataResponse */ - public function destroy($id) { + #[PasswordConfirmationRequired(strict: true)] + public function destroy(int $id) { try { $this->service->removeStorage($id); } catch (NotFoundException $e) { return new DataResponse( [ - 'message' => (string)$this->l10n->t('Storage with ID "%d" not found', array($id)) + 'message' => $this->l10n->t('Storage with ID "%d" not found', [$id]), ], Http::STATUS_NOT_FOUND ); @@ -339,6 +314,4 @@ abstract class StoragesController extends Controller { return new DataResponse([], Http::STATUS_NO_CONTENT); } - } - diff --git a/apps/files_external/lib/Controller/UserGlobalStoragesController.php b/apps/files_external/lib/Controller/UserGlobalStoragesController.php index 22c9c867855..88a9f936401 100644 --- a/apps/files_external/lib/Controller/UserGlobalStoragesController.php +++ b/apps/files_external/lib/Controller/UserGlobalStoragesController.php @@ -1,94 +1,82 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Joas Schilling <coding@schilljs.com> - * @author Juan Pablo Villafáñez <jvillafanez@solidgear.es> - * @author Robin Appelman <robin@icewind.nl> - * @author Robin McCorkell <robin@mccorkell.me.uk> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * 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, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2017-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ - namespace OCA\Files_External\Controller; use OCA\Files_External\Lib\Auth\AuthMechanism; use OCA\Files_External\Lib\Auth\IUserProvided; +use OCA\Files_External\Lib\Auth\Password\UserGlobalAuth; +use OCA\Files_External\Lib\Backend\Backend; use OCA\Files_External\Lib\InsufficientDataForMeaningfulAnswerException; -use OCP\ILogger; -use \OCP\IRequest; -use \OCP\IL10N; -use \OCP\AppFramework\Http\DataResponse; -use \OCP\AppFramework\Http; -use OCA\Files_External\Service\UserGlobalStoragesService; -use OCA\Files_External\NotFoundException; use OCA\Files_External\Lib\StorageConfig; -use \OCA\Files_External\Lib\Backend\Backend; +use OCA\Files_External\NotFoundException; +use OCA\Files_External\Service\UserGlobalStoragesService; +use OCP\AppFramework\Http; +use OCP\AppFramework\Http\Attribute\NoAdminRequired; +use OCP\AppFramework\Http\Attribute\PasswordConfirmationRequired; +use OCP\AppFramework\Http\DataResponse; +use OCP\IConfig; +use OCP\IGroupManager; +use OCP\IL10N; +use OCP\IRequest; use OCP\IUserSession; +use Psr\Log\LoggerInterface; /** * User global storages controller */ class UserGlobalStoragesController extends StoragesController { /** - * @var IUserSession - */ - private $userSession; - - /** * Creates a new user global storages controller. * * @param string $AppName application name * @param IRequest $request request object * @param IL10N $l10n l10n service * @param UserGlobalStoragesService $userGlobalStoragesService storage service + * @param LoggerInterface $logger * @param IUserSession $userSession + * @param IGroupManager $groupManager */ public function __construct( $AppName, IRequest $request, IL10N $l10n, UserGlobalStoragesService $userGlobalStoragesService, + LoggerInterface $logger, IUserSession $userSession, - ILogger $logger + IGroupManager $groupManager, + IConfig $config, ) { parent::__construct( $AppName, $request, $l10n, $userGlobalStoragesService, - $logger + $logger, + $userSession, + $groupManager, + $config ); - $this->userSession = $userSession; } /** * Get all storage entries * * @return DataResponse - * - * @NoAdminRequired */ + #[NoAdminRequired] public function index() { - $storages = $this->service->getUniqueStorages(); - - // remove configuration data, this must be kept private - foreach ($storages as $storage) { + /** @var UserGlobalStoragesService */ + $service = $this->service; + $storages = array_map(function ($storage) { + // remove configuration data, this must be kept private $this->sanitizeStorage($storage); - } + return $storage->jsonSerialize(true); + }, $service->getUniqueStorages()); return new DataResponse( $storages, @@ -109,20 +97,18 @@ class UserGlobalStoragesController extends StoragesController { * Get an external storage entry. * * @param int $id storage id - * @param bool $testOnly whether to storage should only test the connection or do more things * @return DataResponse - * - * @NoAdminRequired */ - public function show($id, $testOnly = true) { + #[NoAdminRequired] + public function show($id) { try { $storage = $this->service->getStorage($id); - $this->updateStorageStatus($storage, $testOnly); + $this->updateStorageStatus($storage); } catch (NotFoundException $e) { return new DataResponse( [ - 'message' => (string)$this->l10n->t('Storage with ID "%d" not found', array($id)) + 'message' => $this->l10n->t('Storage with ID "%d" not found', [$id]) ], Http::STATUS_NOT_FOUND ); @@ -130,8 +116,12 @@ class UserGlobalStoragesController extends StoragesController { $this->sanitizeStorage($storage); + $data = $storage->jsonSerialize(true); + $isAdmin = $this->groupManager->isAdmin($this->userSession->getUser()->getUID()); + $data['can_edit'] = $storage->getType() === StorageConfig::MOUNT_TYPE_PERSONAL || $isAdmin; + return new DataResponse( - $storage, + $data, Http::STATUS_OK ); } @@ -142,27 +132,25 @@ class UserGlobalStoragesController extends StoragesController { * * @param int $id storage id * @param array $backendOptions backend-specific options - * @param bool $testOnly whether to storage should only test the connection or do more things * * @return DataResponse - * - * @NoAdminRequired */ + #[NoAdminRequired] + #[PasswordConfirmationRequired(strict: true)] public function update( $id, $backendOptions, - $testOnly = true ) { try { $storage = $this->service->getStorage($id); $authMechanism = $storage->getAuthMechanism(); - if ($authMechanism instanceof IUserProvided) { + if ($authMechanism instanceof IUserProvided || $authMechanism instanceof UserGlobalAuth) { $authMechanism->saveBackendOptions($this->userSession->getUser(), $id, $backendOptions); $authMechanism->manipulateStorageConfig($storage, $this->userSession->getUser()); } else { return new DataResponse( [ - 'message' => (string)$this->l10n->t('Storage with ID "%d" is not user editable', array($id)) + 'message' => $this->l10n->t('Storage with ID "%d" is not editable by non-admins', [$id]) ], Http::STATUS_FORBIDDEN ); @@ -170,20 +158,19 @@ class UserGlobalStoragesController extends StoragesController { } catch (NotFoundException $e) { return new DataResponse( [ - 'message' => (string)$this->l10n->t('Storage with ID "%d" not found', array($id)) + 'message' => $this->l10n->t('Storage with ID "%d" not found', [$id]) ], Http::STATUS_NOT_FOUND ); } - $this->updateStorageStatus($storage, $testOnly); + $this->updateStorageStatus($storage); $this->sanitizeStorage($storage); return new DataResponse( - $storage, + $storage->jsonSerialize(true), Http::STATUS_OK ); - } /** @@ -203,5 +190,4 @@ class UserGlobalStoragesController extends StoragesController { } } } - } diff --git a/apps/files_external/lib/Controller/UserStoragesController.php b/apps/files_external/lib/Controller/UserStoragesController.php index 724bdd01463..7b564d57f7e 100644 --- a/apps/files_external/lib/Controller/UserStoragesController.php +++ b/apps/files_external/lib/Controller/UserStoragesController.php @@ -1,79 +1,63 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Joas Schilling <coding@schilljs.com> - * @author Juan Pablo Villafáñez <jvillafanez@solidgear.es> - * @author Robin Appelman <robin@icewind.nl> - * @author Robin McCorkell <robin@mccorkell.me.uk> - * @author Vincent Petry <pvince81@owncloud.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * 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, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2017-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ - namespace OCA\Files_External\Controller; - use OCA\Files_External\Lib\Auth\AuthMechanism; -use OCP\ILogger; -use \OCP\IUserSession; -use \OCP\IRequest; -use \OCP\IL10N; -use \OCP\AppFramework\Http\DataResponse; -use \OCP\AppFramework\Http; -use OCA\Files_External\Service\UserStoragesService; -use OCA\Files_External\NotFoundException; +use OCA\Files_External\Lib\Backend\Backend; use OCA\Files_External\Lib\StorageConfig; -use \OCA\Files_External\Lib\Backend\Backend; +use OCA\Files_External\NotFoundException; +use OCA\Files_External\Service\UserStoragesService; +use OCP\AppFramework\Http; +use OCP\AppFramework\Http\Attribute\NoAdminRequired; +use OCP\AppFramework\Http\Attribute\PasswordConfirmationRequired; +use OCP\AppFramework\Http\DataResponse; +use OCP\IConfig; +use OCP\IGroupManager; +use OCP\IL10N; +use OCP\IRequest; +use OCP\IUserSession; +use Psr\Log\LoggerInterface; /** * User storages controller */ class UserStoragesController extends StoragesController { /** - * @var IUserSession - */ - private $userSession; - - /** * Creates a new user storages controller. * * @param string $AppName application name * @param IRequest $request request object * @param IL10N $l10n l10n service * @param UserStoragesService $userStoragesService storage service + * @param LoggerInterface $logger * @param IUserSession $userSession - * @param ILogger $logger + * @param IGroupManager $groupManager */ public function __construct( $AppName, IRequest $request, IL10N $l10n, UserStoragesService $userStoragesService, + LoggerInterface $logger, IUserSession $userSession, - ILogger $logger + IGroupManager $groupManager, + IConfig $config, ) { parent::__construct( $AppName, $request, $l10n, $userStoragesService, - $logger + $logger, + $userSession, + $groupManager, + $config ); - $this->userSession = $userSession; } protected function manipulateStorageConfig(StorageConfig $storage) { @@ -88,10 +72,9 @@ class UserStoragesController extends StoragesController { /** * Get all storage entries * - * @NoAdminRequired - * * @return DataResponse */ + #[NoAdminRequired] public function index() { return parent::index(); } @@ -99,12 +82,11 @@ class UserStoragesController extends StoragesController { /** * Return storage * - * @NoAdminRequired - * * {@inheritdoc} */ - public function show($id, $testOnly = true) { - return parent::show($id, $testOnly); + #[NoAdminRequired] + public function show(int $id) { + return parent::show($id); } /** @@ -117,16 +99,25 @@ class UserStoragesController extends StoragesController { * @param array $mountOptions backend-specific mount options * * @return DataResponse - * - * @NoAdminRequired */ + #[NoAdminRequired] + #[PasswordConfirmationRequired(strict: true)] public function create( $mountPoint, $backend, $authMechanism, $backendOptions, - $mountOptions + $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, @@ -134,7 +125,7 @@ class UserStoragesController extends StoragesController { $backendOptions, $mountOptions ); - if ($newStorage instanceOf DataResponse) { + if ($newStorage instanceof DataResponse) { return $newStorage; } @@ -147,7 +138,7 @@ class UserStoragesController extends StoragesController { $this->updateStorageStatus($newStorage); return new DataResponse( - $newStorage, + $newStorage->jsonSerialize(true), Http::STATUS_CREATED ); } @@ -161,12 +152,11 @@ class UserStoragesController extends StoragesController { * @param string $authMechanism authentication mechanism identifier * @param array $backendOptions backend-specific options * @param array $mountOptions backend-specific mount options - * @param bool $testOnly whether to storage should only test the connection or do more things * * @return DataResponse - * - * @NoAdminRequired */ + #[NoAdminRequired] + #[PasswordConfirmationRequired(strict: true)] public function update( $id, $mountPoint, @@ -174,7 +164,6 @@ class UserStoragesController extends StoragesController { $authMechanism, $backendOptions, $mountOptions, - $testOnly = true ) { $storage = $this->createStorage( $mountPoint, @@ -183,7 +172,7 @@ class UserStoragesController extends StoragesController { $backendOptions, $mountOptions ); - if ($storage instanceOf DataResponse) { + if ($storage instanceof DataResponse) { return $storage; } $storage->setId($id); @@ -198,30 +187,28 @@ class UserStoragesController extends StoragesController { } catch (NotFoundException $e) { return new DataResponse( [ - 'message' => (string)$this->l10n->t('Storage with ID "%d" not found', array($id)) + 'message' => $this->l10n->t('Storage with ID "%d" not found', [$id]) ], Http::STATUS_NOT_FOUND ); } - $this->updateStorageStatus($storage, $testOnly); + $this->updateStorageStatus($storage); return new DataResponse( - $storage, + $storage->jsonSerialize(true), Http::STATUS_OK ); - } /** * Delete storage * - * @NoAdminRequired - * * {@inheritdoc} */ - public function destroy($id) { + #[NoAdminRequired] + #[PasswordConfirmationRequired(strict: true)] + public function destroy(int $id) { return parent::destroy($id); } - } |