123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320 |
- <?php
- /**
- * 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\Backend\Backend;
- use OCA\Files_External\Lib\InsufficientDataForMeaningfulAnswerException;
- use OCA\Files_External\Lib\StorageConfig;
- 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 {
- /**
- * Creates a new storages controller.
- *
- * @param string $AppName application name
- * @param IRequest $request request object
- * @param IL10N $l10n l10n service
- * @param StoragesService $storagesService storage service
- * @param LoggerInterface $logger
- */
- public function __construct(
- $AppName,
- IRequest $request,
- protected IL10N $l10n,
- protected StoragesService $service,
- protected LoggerInterface $logger,
- protected IUserSession $userSession,
- protected IGroupManager $groupManager,
- protected IConfig $config,
- ) {
- parent::__construct($AppName, $request);
- }
- /**
- * Create a storage from its parameters
- *
- * @param string $mountPoint storage mount point
- * @param string $backend backend identifier
- * @param string $authMechanism authentication mechanism identifier
- * @param array $backendOptions backend-specific options
- * @param array|null $mountOptions mount-specific options
- * @param array|null $applicableUsers users for which to mount the storage
- * @param array|null $applicableGroups groups for which to mount the storage
- * @param int|null $priority priority
- *
- * @return StorageConfig|DataResponse
- */
- protected function createStorage(
- $mountPoint,
- $backend,
- $authMechanism,
- $backendOptions,
- $mountOptions = null,
- $applicableUsers = null,
- $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,
- $backend,
- $authMechanism,
- $backendOptions,
- $mountOptions,
- $applicableUsers,
- $applicableGroups,
- $priority
- );
- } catch (\InvalidArgumentException $e) {
- $this->logger->error($e->getMessage(), ['exception' => $e]);
- return new DataResponse(
- [
- 'message' => $this->l10n->t('Invalid backend or authentication mechanism class')
- ],
- Http::STATUS_UNPROCESSABLE_ENTITY
- );
- }
- }
- /**
- * Validate storage config
- *
- * @param StorageConfig $storage storage config
- *1
- * @return DataResponse|null returns response in case of validation error
- */
- protected function validate(StorageConfig $storage) {
- $mountPoint = $storage->getMountPoint();
- if ($mountPoint === '') {
- return new DataResponse(
- [
- 'message' => $this->l10n->t('Invalid mount point'),
- ],
- Http::STATUS_UNPROCESSABLE_ENTITY
- );
- }
- if ($storage->getBackendOption('objectstore')) {
- // objectstore must not be sent from client side
- return new DataResponse(
- [
- 'message' => $this->l10n->t('Objectstore forbidden'),
- ],
- Http::STATUS_UNPROCESSABLE_ENTITY
- );
- }
- /** @var Backend */
- $backend = $storage->getBackend();
- /** @var AuthMechanism */
- $authMechanism = $storage->getAuthMechanism();
- if ($backend->checkDependencies()) {
- // invalid backend
- return new DataResponse(
- [
- 'message' => $this->l10n->t('Invalid storage backend "%s"', [
- $backend->getIdentifier(),
- ]),
- ],
- Http::STATUS_UNPROCESSABLE_ENTITY
- );
- }
- if (!$backend->isVisibleFor($this->service->getVisibilityType())) {
- // not permitted to use backend
- return new DataResponse(
- [
- '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(
- [
- 'message' => $this->l10n->t('Not permitted to use authentication mechanism "%s"', [
- $authMechanism->getIdentifier(),
- ]),
- ],
- Http::STATUS_UNPROCESSABLE_ENTITY
- );
- }
- if (!$backend->validateStorage($storage)) {
- // unsatisfied parameters
- return new DataResponse(
- [
- 'message' => $this->l10n->t('Unsatisfied backend parameters'),
- ],
- Http::STATUS_UNPROCESSABLE_ENTITY
- );
- }
- if (!$authMechanism->validateStorage($storage)) {
- // unsatisfied parameters
- return new DataResponse(
- [
- 'message' => $this->l10n->t('Unsatisfied authentication mechanism parameters'),
- ],
- Http::STATUS_UNPROCESSABLE_ENTITY
- );
- }
- return null;
- }
- protected function manipulateStorageConfig(StorageConfig $storage) {
- /** @var AuthMechanism */
- $authMechanism = $storage->getAuthMechanism();
- $authMechanism->manipulateStorageConfig($storage);
- /** @var Backend */
- $backend = $storage->getBackend();
- $backend->manipulateStorageConfig($storage);
- }
- /**
- * Check whether the given storage is available / valid.
- *
- * Note that this operation can be time consuming depending
- * 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) {
- try {
- $this->manipulateStorageConfig($storage);
- /** @var Backend */
- $backend = $storage->getBackend();
- // update status (can be time-consuming)
- $storage->setStatus(
- MountConfig::getBackendStatus(
- $backend->getStorageClass(),
- $storage->getBackendOptions(),
- false,
- $testOnly
- )
- );
- } catch (InsufficientDataForMeaningfulAnswerException $e) {
- $status = $e->getCode() ?: StorageNotAvailableException::STATUS_INDETERMINATE;
- $storage->setStatus(
- (int)$status,
- $this->l10n->t('Insufficient data: %s', [$e->getMessage()])
- );
- } catch (StorageNotAvailableException $e) {
- $storage->setStatus(
- (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 all storage entries
- *
- * @return DataResponse
- */
- public function index() {
- $storages = array_map(static fn ($storage) => $storage->jsonSerialize(true), $this->service->getStorages());
- return new DataResponse(
- $storages,
- Http::STATUS_OK
- );
- }
- /**
- * 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(int $id, $testOnly = true) {
- try {
- $storage = $this->service->getStorage($id);
- $this->updateStorageStatus($storage, $testOnly);
- } catch (NotFoundException $e) {
- return new DataResponse(
- [
- '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(
- $data,
- Http::STATUS_OK
- );
- }
- /**
- * Deletes the storage with the given id.
- *
- * @param int $id storage id
- *
- * @return DataResponse
- */
- #[PasswordConfirmationRequired(strict: true)]
- public function destroy(int $id) {
- try {
- $this->service->removeStorage($id);
- } catch (NotFoundException $e) {
- return new DataResponse(
- [
- 'message' => $this->l10n->t('Storage with ID "%d" not found', [$id]),
- ],
- Http::STATUS_NOT_FOUND
- );
- }
- return new DataResponse([], Http::STATUS_NO_CONTENT);
- }
- }
|