owncloud/core

View on GitHub
apps/files_external/lib/Command/ListCommand.php

Summary

Maintainability
D
2 days
Test Coverage
<?php
/**
 * @author Robin Appelman <icewind@owncloud.com>
 * @author Vincent Petry <pvince81@owncloud.com>
 *
 * @copyright Copyright (c) 2018, ownCloud GmbH
 * @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/>
 *
 */

namespace OCA\Files_External\Command;

use OC\Core\Command\Base;
use OC\User\NoUserException;
use OCP\Files\External\Auth\InvalidAuth;
use OCP\Files\External\Backend\InvalidBackend;
use OCP\Files\External\IStorageConfig;
use OCP\Files\External\Service\IGlobalStoragesService;
use OCP\Files\External\Service\IUserStoragesService;
use OCP\Files\External\DefinitionParameter;
use OCP\IUserManager;
use OCP\IUserSession;
use Symfony\Component\Console\Helper\Table;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;

class ListCommand extends Base {
    /**
     * @var IGlobalStoragesService
     */
    protected $globalService;

    /**
     * @var IUserStoragesService
     */
    protected $userService;

    /**
     * @var IUserSession
     */
    protected $userSession;

    /**
     * @var IUserManager
     */
    protected $userManager;

    public const ALL = -1;

    public function __construct(IGlobalStoragesService $globalService, IUserStoragesService $userService, IUserSession $userSession, IUserManager $userManager) {
        parent::__construct();
        $this->globalService = $globalService;
        $this->userService = $userService;
        $this->userSession = $userSession;
        $this->userManager = $userManager;
    }

    protected function configure() {
        $this
            ->setName('files_external:list')
            ->setDescription('List configured admin or personal mounts')
            ->addArgument(
                'user_id',
                InputArgument::OPTIONAL,
                'User id to list the personal mounts for, if no user is provided admin mounts will be listed'
            )->addOption(
                'show-password',
                null,
                InputOption::VALUE_NONE,
                'Show passwords and secrets'
            )->addOption(
                'mount-options',
                null,
                InputOption::VALUE_NONE,
                'Show all mount options independent if they are set to their default value or not'
            )->addOption(
                'full',
                null,
                InputOption::VALUE_NONE,
                'Don\'t truncate long values in table output'
            )->addOption(
                'all',
                'a',
                InputOption::VALUE_NONE,
                'Show mounts for all users. All has precedence over user_id'
            )->addOption(
                'short',
                's',
                InputOption::VALUE_NONE,
                'Show only a reduced mount info'
            )->addOption(
                'importable-format',
                'i',
                InputOption::VALUE_NONE,
                'Provide output values in a format compatible with files_external:import'
            );
        parent::configure();
    }

    protected function execute(InputInterface $input, OutputInterface $output): int {
        if ($input->getOption('all')) {
            /** @var  $mounts IStorageConfig[] */
            $mounts = $this->globalService->getStorageForAllUsers();
            $userId = self::ALL;
        } else {
            $userId = $input->getArgument('user_id');
            $storageService = $this->getStorageService($userId);

            /** @var  $mounts IStorageConfig[] */
            $mounts = $storageService->getAllStorages();
        }

        $this->listMounts($userId, $mounts, $input, $output);
        return 0;
    }

    /**
     * @param $userId $userId
     * @param IStorageConfig[] $mounts
     * @param InputInterface $input
     * @param OutputInterface $output
     */
    public function listMounts($userId, array $mounts, InputInterface $input, OutputInterface $output) {
        $outputType = $input->getOption('output');
        $outputIsJson = ($outputType === self::OUTPUT_FORMAT_JSON) || ($outputType === self::OUTPUT_FORMAT_JSON_PRETTY);
        $shortView = $input->getOption('short');
        $importableView = $input->getOption('importable-format');

        // check if there are any mounts present
        if (\count($mounts) === 0) {
            if ($outputType === self::OUTPUT_FORMAT_JSON || $outputType === self::OUTPUT_FORMAT_JSON_PRETTY) {
                $output->writeln('[]');
            } else {
                if ($userId === self::ALL) {
                    $output->writeln("<info>No mounts configured</info>");
                } elseif ($userId) {
                    $output->writeln("<info>No mounts configured by $userId</info>");
                } else {
                    $output->writeln("<info>No admin mounts configured</info>");
                }
            }
            return;
        }

        // set minimum columns used based on options
        if ($shortView) {
            $headers = ['Mount ID', 'Mount Point', 'Auth', 'Type'];
            // if there is no userId or option --all is set, insert additional columns
            if (!$userId || $userId === self::ALL) {
                \array_splice($headers, 2, 0, 'Applicable Users');
                \array_splice($headers, 3, 0, 'Applicable Groups');
            }
        } else {
            $headers = ['Mount ID', 'Mount Point', 'Storage', 'Authentication Type', 'Configuration', 'Options'];

            if (!$userId || $userId === self::ALL) {
                $headers[] = 'Applicable Users';
                $headers[] = 'Applicable Groups';
            }

            if (!$input->getOption('show-password')) {
                $hideKeys = ['refresh_token', 'token', 'public_key', 'private_key'];
                foreach ($mounts as $mount) {
                    $config = $mount->getBackendOptions();
                    foreach ($config as $key => $value) {
                        if (\in_array($key, $hideKeys, true) || $this->shouldHideKey($mount, $key)) {
                            $mount->setBackendOption($key, '***');
                        }
                    }
                }
            }
        }

        // default output style
        $full = $input->getOption('full');
        $showMountOptions = $input->getOption('mount-options');

        $defaultMountOptions = [
            'encrypt' => true,
            'previews' => true,
            'filesystem_check_changes' => 1,
            'read_only' => false,
            'enable_sharing' => false,
            'encoding_compatibility' => false
        ];
        $countInvalid = 0;
        // In case adding array elements, add them only after the first two (Mount ID / Mount Point)
        // and before the last one entry (Type). Necessary for option -s
        $rows = \array_map(function (IStorageConfig $config) use ($outputIsJson, $shortView, $importableView, $userId, $defaultMountOptions, $full, $showMountOptions, &$countInvalid) {
            if ($config->getBackend() instanceof InvalidBackend || $config->getAuthMechanism() instanceof InvalidAuth) {
                $countInvalid++;
            }
            $storageConfig = $config->getBackendOptions();
            $keys = \array_keys($storageConfig);
            $values = \array_values($storageConfig);

            if (!$full) {
                $values = \array_map(function ($value) {
                    if (\is_string($value) && \strlen($value) > 32) {
                        return \substr($value, 0, 6) . '...' . \substr($value, -6, 6);
                    } else {
                        return $value;
                    }
                }, $values);
            }

            $configStrings = \array_map(function ($key, $value) {
                return $key . ': ' . \json_encode($value);
            }, $keys, $values);
            $configString = \implode(', ', $configStrings);

            $mountOptions = $config->getMountOptions();
            $mountOptions = \array_merge($defaultMountOptions, $mountOptions);
            // hide defaults
            if (!$showMountOptions) {
                foreach ($mountOptions as $key => $value) {
                    if ($value === $defaultMountOptions[$key]) {
                        unset($mountOptions[$key]);
                    }
                }
            }
            $keys = \array_keys($mountOptions);
            $values = \array_values($mountOptions);

            $optionsStrings = \array_map(function ($key, $value) {
                return $key . ': ' . \json_encode($value);
            }, $keys, $values);
            $optionsString = \implode(', ', $optionsStrings);

            // output dependent on option shortview
            if ($shortView) {
                $values = [
                    $config->getId(),
                    $config->getMountPoint()
                ];
            } else {
                $configValue = $configString;
                if ($importableView) {
                    $storageValue = $config->getBackend()->getStorageClass();
                    $authValue = $config->getAuthMechanism()->getIdentifier();
                    if ($outputIsJson) {
                        $configValue = $storageConfig;
                    }
                } else {
                    $storageValue = $config->getBackend()->getText();
                    $authValue = $config->getAuthMechanism()->getText();
                }
                $values = [
                    $config->getId(),
                    $config->getMountPoint(),
                    $storageValue,
                    $authValue,
                    $configValue,
                    $optionsString
                ];
            }

            // output independent of option shortview
            if (!$userId || $userId === self::ALL) {
                if ($importableView && $outputIsJson) {
                    $applicableUsers = $config->getApplicableUsers();
                    $applicableGroups = $config->getApplicableGroups();
                } else {
                    $applicableUsers = \implode(', ', $config->getApplicableUsers());
                    $applicableGroups = \implode(', ', $config->getApplicableGroups());
                    if ($applicableUsers === '' && $applicableGroups === '') {
                        $applicableUsers = 'All';
                    }
                }
                $values[] = $applicableUsers;
                $values[] = $applicableGroups;
            }
            // This MUST stay the last entry
            if ($shortView) {
                // query the auth type
                if (\stristr($config->getBackend()->getText(), 'session') === true) {
                    $values[] =  'Session';
                } else {
                    $values[] = 'User';
                }
                // get the mount type
                $values[] = $config->getType() === IStorageConfig::MOUNT_TYPE_ADMIN ? 'Admin' : 'Personal';
            }

            return $values;
        }, $mounts);

        if ($outputIsJson) {
            $keys = \array_map(function ($header) {
                return \strtolower(\str_replace(' ', '_', $header));
            }, $headers);

            $pairs = [];
            foreach ($rows as $array_1) {
                $pairs[] = \array_combine($keys, $array_1);
            }

            if ($outputType === self::OUTPUT_FORMAT_JSON) {
                $output->writeln(\json_encode(\array_values($pairs)));
            } else {
                $output->writeln(\json_encode(\array_values($pairs), JSON_PRETTY_PRINT));
            }
        } else {
            $table = new Table($output);
            $table->setHeaders($headers);
            $table->setRows($rows);
            $table->render();
        }

        if ($countInvalid > 0) {
            $output->writeln(
                "<error>Number of invalid storages found: $countInvalid.\n" .
                "The listed configuration details are likely incomplete.\n" .
                "Please make sure that all related apps that provide these storages are enabled or delete these.</error>"
            );
        }
    }

    private function shouldHideKey(IStorageConfig $mount, $key) {
        $backend = $mount->getBackend();
        $backendParameters = $backend->getParameters();

        $auth = $mount->getAuthMechanism();
        $authParameters = $auth->getParameters();
        if (
            (
                isset($backendParameters[$key]) &&
                $backendParameters[$key]->getType() === DefinitionParameter::VALUE_PASSWORD
            ) || (
                isset($authParameters[$key]) &&
                $authParameters[$key]->getType() === DefinitionParameter::VALUE_PASSWORD
            )
        ) {
            return true;
        }
        return false;
    }

    protected function getStorageService($userId) {
        if (!empty($userId)) {
            $user = $this->userManager->get($userId);
            if ($user === null) {
                throw new NoUserException("user $userId not found");
            }
            $this->userSession->setUser($user);
            return $this->userService;
        } else {
            return $this->globalService;
        }
    }
}