summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRoeland Douma <rullzer@users.noreply.github.com>2016-07-15 21:26:07 +0200
committerGitHub <noreply@github.com>2016-07-15 21:26:07 +0200
commitf8167c0f9a9869a32398702ed03db46101f2cc4b (patch)
tree215d124368b0919688facfad61cbba8798a63a23
parente577ef87280788b788f6c38daf8e38043bbfbc8c (diff)
parentaaf2be4c3d47395155bdd7c508efc14a54dee512 (diff)
downloadnextcloud-server-f8167c0f9a9869a32398702ed03db46101f2cc4b.tar.gz
nextcloud-server-f8167c0f9a9869a32398702ed03db46101f2cc4b.zip
Merge pull request #395 from nextcloud/user-preferences-occ
Allow to change user preferences via occ
-rw-r--r--core/Command/User/Setting.php226
-rw-r--r--core/register_command.php1
-rw-r--r--tests/Core/Command/User/SettingTest.php464
3 files changed, 691 insertions, 0 deletions
diff --git a/core/Command/User/Setting.php b/core/Command/User/Setting.php
new file mode 100644
index 00000000000..7a62b5529b3
--- /dev/null
+++ b/core/Command/User/Setting.php
@@ -0,0 +1,226 @@
+<?php
+/**
+ * @author Joas Schilling <coding@schilljs.com>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @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 OC\Core\Command\User;
+
+use OC\Core\Command\Base;
+use OCP\IConfig;
+use OCP\IDBConnection;
+use OCP\IUserManager;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Output\OutputInterface;
+use Symfony\Component\Console\Input\InputArgument;
+
+class Setting extends Base {
+ /** @var IUserManager */
+ protected $userManager;
+
+ /** @var IConfig */
+ protected $config;
+
+ /** @var IDBConnection */
+ protected $connection;
+
+ /**
+ * @param IUserManager $userManager
+ * @param IConfig $config
+ * @param IDBConnection $connection
+ */
+ public function __construct(IUserManager $userManager, IConfig $config, IDBConnection $connection) {
+ parent::__construct();
+ $this->userManager = $userManager;
+ $this->config = $config;
+ $this->connection = $connection;
+ }
+
+ protected function configure() {
+ parent::configure();
+ $this
+ ->setName('user:setting')
+ ->setDescription('Read and modify user settings')
+ ->addArgument(
+ 'uid',
+ InputArgument::REQUIRED,
+ 'User ID used to login'
+ )
+ ->addArgument(
+ 'app',
+ InputArgument::OPTIONAL,
+ 'Restrict the settings to a given app',
+ ''
+ )
+ ->addArgument(
+ 'key',
+ InputArgument::OPTIONAL,
+ 'Setting key to set, get or delete',
+ ''
+ )
+ ->addOption(
+ 'ignore-missing-user',
+ null,
+ InputOption::VALUE_NONE,
+ 'Use this option to ignore errors when the user does not exist'
+ )
+
+ // Get
+ ->addOption(
+ 'default-value',
+ null,
+ InputOption::VALUE_REQUIRED,
+ '(Only applicable on get) If no default value is set and the config does not exist, the command will exit with 1'
+ )
+
+ // Set
+ ->addArgument(
+ 'value',
+ InputArgument::OPTIONAL,
+ 'The new value of the setting',
+ null
+ )
+ ->addOption(
+ 'update-only',
+ null,
+ InputOption::VALUE_NONE,
+ 'Only updates the value, if it is not set before, it is not being added'
+ )
+
+ // Delete
+ ->addOption(
+ 'delete',
+ null,
+ InputOption::VALUE_NONE,
+ 'Specify this option to delete the config'
+ )
+ ->addOption(
+ 'error-if-not-exists',
+ null,
+ InputOption::VALUE_NONE,
+ 'Checks whether the setting exists before deleting it'
+ )
+ ;
+ }
+
+ protected function checkInput(InputInterface $input) {
+ $uid = $input->getArgument('uid');
+ if (!$input->getOption('ignore-missing-user') && !$this->userManager->userExists($uid)) {
+ throw new \InvalidArgumentException('The user "' . $uid . '" does not exists.');
+ }
+
+ if ($input->getArgument('key') === '' && $input->hasParameterOption('--default-value')) {
+ throw new \InvalidArgumentException('The "default-value" option can only be used when specifying a key.');
+ }
+
+ if ($input->getArgument('key') === '' && $input->getArgument('value') !== null) {
+ throw new \InvalidArgumentException('The value argument can only be used when specifying a key.');
+ }
+ if ($input->getArgument('value') !== null && $input->hasParameterOption('--default-value')) {
+ throw new \InvalidArgumentException('The value argument can not be used together with "default-value".');
+ }
+ if ($input->getOption('update-only') && $input->getArgument('value') === null) {
+ throw new \InvalidArgumentException('The "update-only" option can only be used together with "value".');
+ }
+
+ if ($input->getArgument('key') === '' && $input->getOption('delete')) {
+ throw new \InvalidArgumentException('The "delete" option can only be used when specifying a key.');
+ }
+ if ($input->getOption('delete') && $input->hasParameterOption('--default-value')) {
+ throw new \InvalidArgumentException('The "delete" option can not be used together with "default-value".');
+ }
+ if ($input->getOption('delete') && $input->getArgument('value') !== null) {
+ throw new \InvalidArgumentException('The "delete" option can not be used together with "value".');
+ }
+ if ($input->getOption('error-if-not-exists') && !$input->getOption('delete')) {
+ throw new \InvalidArgumentException('The "error-if-not-exists" option can only be used together with "delete".');
+ }
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output) {
+ try {
+ $this->checkInput($input);
+ } catch (\InvalidArgumentException $e) {
+ $output->writeln('<error>' . $e->getMessage() . '</error>');
+ return 1;
+ }
+
+ $uid = $input->getArgument('uid');
+ $app = $input->getArgument('app');
+ $key = $input->getArgument('key');
+
+ if ($key !== '') {
+ $value = $this->config->getUserValue($uid, $app, $key, null);
+ if ($input->getArgument('value') !== null) {
+ if ($input->hasParameterOption('--update-only') && $value === null) {
+ $output->writeln('<error>The setting does not exist for user "' . $uid . '".</error>');
+ return 1;
+ }
+
+ $this->config->setUserValue($uid, $app, $key, $input->getArgument('value'));
+ return 0;
+
+ } else if ($input->hasParameterOption('--delete')) {
+ if ($input->hasParameterOption('--error-if-not-exists') && $value === null) {
+ $output->writeln('<error>The setting does not exist for user "' . $uid . '".</error>');
+ return 1;
+ }
+
+ $this->config->deleteUserValue($uid, $app, $key);
+ return 0;
+
+ } else if ($value !== null) {
+ $output->writeln($value);
+ return 0;
+ } else {
+ if ($input->hasParameterOption('--default-value')) {
+ $output->writeln($input->getOption('default-value'));
+ return 0;
+ } else {
+ $output->writeln('<error>The setting does not exist for user "' . $uid . '".</error>');
+ return 1;
+ }
+ }
+ } else {
+ $settings = $this->getUserSettings($uid, $app);
+ $this->writeArrayInOutputFormat($input, $output, $settings);
+ return 0;
+ }
+ }
+
+ protected function getUserSettings($uid, $app) {
+ $query = $this->connection->getQueryBuilder();
+ $query->select('*')
+ ->from('preferences')
+ ->where($query->expr()->eq('userid', $query->createNamedParameter($uid)));
+
+ if ($app !== '') {
+ $query->andWhere($query->expr()->eq('appid', $query->createNamedParameter($app)));
+ }
+
+ $result = $query->execute();
+ $settings = [];
+ while ($row = $result->fetch()) {
+ $settings[$row['appid']][$row['configkey']] = $row['configvalue'];
+ }
+ $result->closeCursor();
+
+ return $settings;
+ }
+}
diff --git a/core/register_command.php b/core/register_command.php
index ebb6ce8b723..91b00df20f1 100644
--- a/core/register_command.php
+++ b/core/register_command.php
@@ -136,6 +136,7 @@ if (\OC::$server->getConfig()->getSystemValue('installed', false)) {
$application->add(new OC\Core\Command\User\LastSeen(\OC::$server->getUserManager()));
$application->add(new OC\Core\Command\User\Report(\OC::$server->getUserManager()));
$application->add(new OC\Core\Command\User\ResetPassword(\OC::$server->getUserManager()));
+ $application->add(new OC\Core\Command\User\Setting(\OC::$server->getUserManager(), \OC::$server->getConfig(), \OC::$server->getDatabaseConnection()));
$application->add(new OC\Core\Command\Security\ListCertificates(\OC::$server->getCertificateManager(null), \OC::$server->getL10N('core')));
$application->add(new OC\Core\Command\Security\ImportCertificate(\OC::$server->getCertificateManager(null)));
diff --git a/tests/Core/Command/User/SettingTest.php b/tests/Core/Command/User/SettingTest.php
new file mode 100644
index 00000000000..002e8dfef2f
--- /dev/null
+++ b/tests/Core/Command/User/SettingTest.php
@@ -0,0 +1,464 @@
+<?php
+/**
+ * @author Joas Schilling <coding@schilljs.com>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @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 Tests\Core\Command\User;
+
+
+use OC\Core\Command\User\Setting;
+use Test\TestCase;
+
+class SettingTest extends TestCase {
+ /** @var \OCP\IUserManager|\PHPUnit_Framework_MockObject_MockObject */
+ protected $userManager;
+ /** @var \OCP\IConfig|\PHPUnit_Framework_MockObject_MockObject */
+ protected $config;
+ /** @var \OCP\IDBConnection|\PHPUnit_Framework_MockObject_MockObject */
+ protected $connection;
+ /** @var \Symfony\Component\Console\Input\InputInterface|\PHPUnit_Framework_MockObject_MockObject */
+ protected $consoleInput;
+ /** @var \Symfony\Component\Console\Output\OutputInterface|\PHPUnit_Framework_MockObject_MockObject */
+ protected $consoleOutput;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->userManager = $this->getMockBuilder('OCP\IUserManager')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $this->config = $this->getMockBuilder('OCP\IConfig')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $this->connection = $this->getMockBuilder('OCP\IDBConnection')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $this->consoleInput = $this->getMockBuilder('Symfony\Component\Console\Input\InputInterface')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $this->consoleOutput = $this->getMockBuilder('Symfony\Component\Console\Output\OutputInterface')
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
+ public function getCommand(array $methods = []) {
+ if (empty($methods)) {
+ return new Setting($this->userManager, $this->config, $this->connection);
+ } else {
+ $mock = $this->getMockBuilder('OC\Core\Command\User\Setting')
+ ->setConstructorArgs([
+ $this->userManager,
+ $this->config,
+ $this->connection,
+ ])
+ ->setMethods($methods)
+ ->getMock();
+ return $mock;
+ }
+
+ }
+
+ public function dataCheckInput() {
+ return [
+ [
+ [['uid', 'username']],
+ [['ignore-missing-user', true]],
+ [],
+ false,
+ false,
+ ],
+ [
+ [['uid', 'username']],
+ [['ignore-missing-user', false]],
+ [],
+ null,
+ 'The user "username" does not exists.',
+ ],
+
+ [
+ [['uid', 'username'], ['key', 'configkey']],
+ [['ignore-missing-user', true]],
+ [['--default-value', true]],
+ false,
+ false,
+ ],
+ [
+ [['uid', 'username'], ['key', '']],
+ [['ignore-missing-user', true]],
+ [['--default-value', true]],
+ false,
+ 'The "default-value" option can only be used when specifying a key.',
+ ],
+
+ [
+ [['uid', 'username'], ['key', 'configkey'], ['value', '']],
+ [['ignore-missing-user', true]],
+ [],
+ false,
+ false,
+ ],
+ [
+ [['uid', 'username'], ['key', ''], ['value', '']],
+ [['ignore-missing-user', true]],
+ [],
+ false,
+ 'The value argument can only be used when specifying a key.',
+ ],
+ [
+ [['uid', 'username'], ['key', 'configkey'], ['value', '']],
+ [['ignore-missing-user', true]],
+ [['--default-value', true]],
+ false,
+ 'The value argument can not be used together with "default-value".',
+ ],
+ [
+ [['uid', 'username'], ['key', 'configkey'], ['value', '']],
+ [['ignore-missing-user', true], ['update-only', true]],
+ [],
+ false,
+ false,
+ ],
+ [
+ [['uid', 'username'], ['key', 'configkey'], ['value', null]],
+ [['ignore-missing-user', true], ['update-only', true]],
+ [],
+ false,
+ 'The "update-only" option can only be used together with "value".',
+ ],
+
+ [
+ [['uid', 'username'], ['key', 'configkey']],
+ [['ignore-missing-user', true], ['delete', true]],
+ [],
+ false,
+ false,
+ ],
+ [
+ [['uid', 'username'], ['key', '']],
+ [['ignore-missing-user', true], ['delete', true]],
+ [],
+ false,
+ 'The "delete" option can only be used when specifying a key.',
+ ],
+ [
+ [['uid', 'username'], ['key', 'configkey']],
+ [['ignore-missing-user', true], ['delete', true]],
+ [['--default-value', true]],
+ false,
+ 'The "delete" option can not be used together with "default-value".',
+ ],
+ [
+ [['uid', 'username'], ['key', 'configkey'], ['value', '']],
+ [['ignore-missing-user', true], ['delete', true]],
+ [],
+ false,
+ 'The "delete" option can not be used together with "value".',
+ ],
+ [
+ [['uid', 'username'], ['key', 'configkey']],
+ [['ignore-missing-user', true], ['delete', true], ['error-if-not-exists', true]],
+ [],
+ false,
+ false,
+ ],
+ [
+ [['uid', 'username'], ['key', 'configkey']],
+ [['ignore-missing-user', true], ['delete', false], ['error-if-not-exists', true]],
+ [],
+ false,
+ 'The "error-if-not-exists" option can only be used together with "delete".',
+ ],
+ ];
+ }
+
+ /**
+ * @dataProvider dataCheckInput
+ *
+ * @param array $arguments
+ * @param array $options
+ * @param array $parameterOptions
+ * @param mixed $user
+ * @param string $expectedException
+ */
+ public function testCheckInput($arguments, $options, $parameterOptions, $user, $expectedException) {
+ $this->consoleInput->expects($this->any())
+ ->method('getArgument')
+ ->willReturnMap($arguments);
+ $this->consoleInput->expects($this->any())
+ ->method('getOption')
+ ->willReturnMap($options);
+ $this->consoleInput->expects($this->any())
+ ->method('hasParameterOption')
+ ->willReturnMap($parameterOptions);
+
+ if ($user !== false) {
+ $this->userManager->expects($this->once())
+ ->method('userExists')
+ ->willReturn($user);
+ }
+
+ $command = $this->getCommand();
+ try {
+ $this->invokePrivate($command, 'checkInput', [$this->consoleInput]);
+ $this->assertFalse($expectedException);
+ } catch (\InvalidArgumentException $e) {
+ $this->assertEquals($expectedException, $e->getMessage());
+ }
+ }
+
+ public function testCheckInputExceptionCatch() {
+ $command = $this->getCommand(['checkInput']);
+ $command->expects($this->once())
+ ->method('checkInput')
+ ->willThrowException(new \InvalidArgumentException('test'));
+
+ $this->consoleOutput->expects($this->once())
+ ->method('writeln')
+ ->with('<error>test</error>');
+
+ $this->assertEquals(1, $this->invokePrivate($command, 'execute', [$this->consoleInput, $this->consoleOutput]));
+ }
+
+ public function dataExecuteDelete() {
+ return [
+ ['config', false, null, 0],
+ ['config', true, null, 0],
+ [null, false, null, 0],
+ [null, true, '<error>The setting does not exist for user "username".</error>', 1],
+ ];
+ }
+
+ /**
+ * @dataProvider dataExecuteDelete
+ *
+ * @param string|null $value
+ * @param bool $errorIfNotExists
+ * @param string $expectedLine
+ * @param int $expectedReturn
+ */
+ public function testExecuteDelete($value, $errorIfNotExists, $expectedLine, $expectedReturn) {
+ $command = $this->getCommand([
+ 'writeArrayInOutputFormat',
+ 'checkInput',
+ 'getUserSettings',
+ ]);
+
+ $this->consoleInput->expects($this->any())
+ ->method('getArgument')
+ ->willReturnMap([
+ ['uid', 'username'],
+ ['app', 'appname'],
+ ['key', 'configkey'],
+ ]);
+
+ $command->expects($this->once())
+ ->method('checkInput');
+
+ $this->config->expects($this->once())
+ ->method('getUserValue')
+ ->with('username', 'appname', 'configkey', null)
+ ->willReturn($value);
+
+ $this->consoleInput->expects($this->atLeastOnce())
+ ->method('hasParameterOption')
+ ->willReturnMap([
+ ['--delete', true],
+ ['--error-if-not-exists', $errorIfNotExists],
+ ]);
+
+ if ($expectedLine === null) {
+ $this->consoleOutput->expects($this->never())
+ ->method('writeln');
+ $this->config->expects($this->once())
+ ->method('deleteUserValue')
+ ->with('username', 'appname', 'configkey');
+
+ } else {
+ $this->consoleOutput->expects($this->once())
+ ->method('writeln')
+ ->with($expectedLine);
+ $this->config->expects($this->never())
+ ->method('deleteUserValue');
+ }
+
+ $this->assertEquals($expectedReturn, $this->invokePrivate($command, 'execute', [$this->consoleInput, $this->consoleOutput]));
+ }
+
+ public function dataExecuteSet() {
+ return [
+ ['config', false, null, 0],
+ ['config', true, null, 0],
+ [null, false, null, 0],
+ [null, true, '<error>The setting does not exist for user "username".</error>', 1],
+ ];
+ }
+
+ /**
+ * @dataProvider dataExecuteSet
+ *
+ * @param string|null $value
+ * @param bool $updateOnly
+ * @param string $expectedLine
+ * @param int $expectedReturn
+ */
+ public function testExecuteSet($value, $updateOnly, $expectedLine, $expectedReturn) {
+ $command = $this->getCommand([
+ 'writeArrayInOutputFormat',
+ 'checkInput',
+ 'getUserSettings',
+ ]);
+
+ $this->consoleInput->expects($this->atLeast(4))
+ ->method('getArgument')
+ ->willReturnMap([
+ ['uid', 'username'],
+ ['app', 'appname'],
+ ['key', 'configkey'],
+ ['value', 'setValue'],
+ ]);
+
+ $command->expects($this->once())
+ ->method('checkInput');
+
+ $this->config->expects($this->once())
+ ->method('getUserValue')
+ ->with('username', 'appname', 'configkey', null)
+ ->willReturn($value);
+
+ $this->consoleInput->expects($this->atLeastOnce())
+ ->method('hasParameterOption')
+ ->willReturnMap([
+ ['--update-only', $updateOnly],
+ ]);
+
+ if ($expectedLine === null) {
+ $this->consoleOutput->expects($this->never())
+ ->method('writeln');
+
+ $this->consoleInput->expects($this->never())
+ ->method('getOption');
+
+ $this->config->expects($this->once())
+ ->method('setUserValue')
+ ->with('username', 'appname', 'configkey', 'setValue');
+ } else {
+ $this->consoleOutput->expects($this->once())
+ ->method('writeln')
+ ->with($expectedLine);
+ $this->config->expects($this->never())
+ ->method('setUserValue');
+ }
+
+ $this->assertEquals($expectedReturn, $this->invokePrivate($command, 'execute', [$this->consoleInput, $this->consoleOutput]));
+ }
+
+ public function dataExecuteGet() {
+ return [
+ ['config', null, 'config', 0],
+ [null, 'config', 'config', 0],
+ [null, null, '<error>The setting does not exist for user "username".</error>', 1],
+ ];
+ }
+
+ /**
+ * @dataProvider dataExecuteGet
+ *
+ * @param string|null $value
+ * @param string|null $defaultValue
+ * @param string $expectedLine
+ * @param int $expectedReturn
+ */
+ public function testExecuteGet($value, $defaultValue, $expectedLine, $expectedReturn) {
+ $command = $this->getCommand([
+ 'writeArrayInOutputFormat',
+ 'checkInput',
+ 'getUserSettings',
+ ]);
+
+ $this->consoleInput->expects($this->any())
+ ->method('getArgument')
+ ->willReturnMap([
+ ['uid', 'username'],
+ ['app', 'appname'],
+ ['key', 'configkey'],
+ ]);
+
+ $command->expects($this->once())
+ ->method('checkInput');
+
+ $this->config->expects($this->once())
+ ->method('getUserValue')
+ ->with('username', 'appname', 'configkey', null)
+ ->willReturn($value);
+
+ if ($value === null) {
+ if ($defaultValue === null) {
+ $this->consoleInput->expects($this->atLeastOnce())
+ ->method('hasParameterOption')
+ ->willReturnMap([
+ ['--default-value', false],
+ ]);
+ } else {
+ $this->consoleInput->expects($this->atLeastOnce())
+ ->method('hasParameterOption')
+ ->willReturnMap([
+ ['--default-value', true],
+ ]);
+ $this->consoleInput->expects($this->once())
+ ->method('getOption')
+ ->with('default-value')
+ ->willReturn($defaultValue);
+ }
+ }
+
+ $this->consoleOutput->expects($this->once())
+ ->method('writeln')
+ ->with($expectedLine);
+
+ $this->assertEquals($expectedReturn, $this->invokePrivate($command, 'execute', [$this->consoleInput, $this->consoleOutput]));
+ }
+
+ public function testExecuteList() {
+ $command = $this->getCommand([
+ 'writeArrayInOutputFormat',
+ 'checkInput',
+ 'getUserSettings',
+ ]);
+
+ $this->consoleInput->expects($this->any())
+ ->method('getArgument')
+ ->willReturnMap([
+ ['uid', 'username'],
+ ['app', 'appname'],
+ ['key', ''],
+ ]);
+
+ $command->expects($this->once())
+ ->method('checkInput');
+ $command->expects($this->once())
+ ->method('getUserSettings')
+ ->willReturn(['settings']);
+ $command->expects($this->once())
+ ->method('writeArrayInOutputFormat')
+ ->with($this->consoleInput, $this->consoleOutput, ['settings']);
+
+
+ $this->assertEquals(0, $this->invokePrivate($command, 'execute', [$this->consoleInput, $this->consoleOutput]));
+ }
+}