Browse Source
Merge pull request #18369 from owncloud/occ-log
Merge pull request #18369 from owncloud/occ-log
occ commands to manage loggingremotes/origin/add-bruteforce-protection
6 changed files with 605 additions and 4 deletions
-
9config/config.sample.php
-
171core/command/log/manage.php
-
124core/command/log/owncloud.php
-
3core/register_command.php
-
181tests/core/command/log/managetest.php
-
121tests/core/command/log/owncloudtest.php
@ -0,0 +1,171 @@ |
|||
<?php |
|||
/** |
|||
* @author Robin McCorkell <rmccorkell@owncloud.com> |
|||
* |
|||
* @copyright Copyright (c) 2015, 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\Log; |
|||
|
|||
use \OCP\IConfig; |
|||
|
|||
use Symfony\Component\Console\Command\Command; |
|||
use Symfony\Component\Console\Input\InputInterface; |
|||
use Symfony\Component\Console\Input\InputArgument; |
|||
use Symfony\Component\Console\Input\InputOption; |
|||
use Symfony\Component\Console\Output\OutputInterface; |
|||
|
|||
class Manage extends Command { |
|||
|
|||
const DEFAULT_BACKEND = 'owncloud'; |
|||
const DEFAULT_LOG_LEVEL = 2; |
|||
const DEFAULT_TIMEZONE = 'UTC'; |
|||
|
|||
/** @var IConfig */ |
|||
protected $config; |
|||
|
|||
public function __construct(IConfig $config) { |
|||
$this->config = $config; |
|||
parent::__construct(); |
|||
} |
|||
|
|||
protected function configure() { |
|||
$this |
|||
->setName('log:manage') |
|||
->setDescription('manage logging configuration') |
|||
->addOption( |
|||
'backend', |
|||
null, |
|||
InputOption::VALUE_REQUIRED, |
|||
'set the logging backend [owncloud, syslog, errorlog]' |
|||
) |
|||
->addOption( |
|||
'level', |
|||
null, |
|||
InputOption::VALUE_REQUIRED, |
|||
'set the log level [debug, info, warning, error]' |
|||
) |
|||
->addOption( |
|||
'timezone', |
|||
null, |
|||
InputOption::VALUE_REQUIRED, |
|||
'set the logging timezone' |
|||
) |
|||
; |
|||
} |
|||
|
|||
protected function execute(InputInterface $input, OutputInterface $output) { |
|||
// collate config setting to the end, to avoid partial configuration
|
|||
$toBeSet = []; |
|||
|
|||
if ($backend = $input->getOption('backend')) { |
|||
$this->validateBackend($backend); |
|||
$toBeSet['log_type'] = $backend; |
|||
} |
|||
|
|||
if ($level = $input->getOption('level')) { |
|||
if (is_numeric($level)) { |
|||
$levelNum = $level; |
|||
// sanity check
|
|||
$this->convertLevelNumber($levelNum); |
|||
} else { |
|||
$levelNum = $this->convertLevelString($level); |
|||
} |
|||
$toBeSet['loglevel'] = $levelNum; |
|||
} |
|||
|
|||
if ($timezone = $input->getOption('timezone')) { |
|||
$this->validateTimezone($timezone); |
|||
$toBeSet['logtimezone'] = $timezone; |
|||
} |
|||
|
|||
// set config
|
|||
foreach ($toBeSet as $option => $value) { |
|||
$this->config->setSystemValue($option, $value); |
|||
} |
|||
|
|||
// display configuration
|
|||
$backend = $this->config->getSystemValue('log_type', self::DEFAULT_BACKEND); |
|||
$output->writeln('Enabled logging backend: '.$backend); |
|||
|
|||
$levelNum = $this->config->getSystemValue('loglevel', self::DEFAULT_LOG_LEVEL); |
|||
$level = $this->convertLevelNumber($levelNum); |
|||
$output->writeln('Log level: '.$level.' ('.$levelNum.')'); |
|||
|
|||
$timezone = $this->config->getSystemValue('logtimezone', self::DEFAULT_TIMEZONE); |
|||
$output->writeln('Log timezone: '.$timezone); |
|||
} |
|||
|
|||
/** |
|||
* @param string $backend |
|||
* @throws \InvalidArgumentException |
|||
*/ |
|||
protected function validateBackend($backend) { |
|||
if (!class_exists('OC_Log_'.$backend)) { |
|||
throw new \InvalidArgumentException('Invalid backend'); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* @param string $timezone |
|||
* @throws \Exception |
|||
*/ |
|||
protected function validateTimezone($timezone) { |
|||
new \DateTimeZone($timezone); |
|||
} |
|||
|
|||
/** |
|||
* @param string $level |
|||
* @return int |
|||
* @throws \InvalidArgumentException |
|||
*/ |
|||
protected function convertLevelString($level) { |
|||
$level = strtolower($level); |
|||
switch ($level) { |
|||
case 'debug': |
|||
return 0; |
|||
case 'info': |
|||
return 1; |
|||
case 'warning': |
|||
case 'warn': |
|||
return 2; |
|||
case 'error': |
|||
case 'err': |
|||
return 3; |
|||
} |
|||
throw new \InvalidArgumentException('Invalid log level string'); |
|||
} |
|||
|
|||
/** |
|||
* @param int $levelNum |
|||
* @return string |
|||
* @throws \InvalidArgumentException |
|||
*/ |
|||
protected function convertLevelNumber($levelNum) { |
|||
switch ($levelNum) { |
|||
case 0: |
|||
return 'Debug'; |
|||
case 1: |
|||
return 'Info'; |
|||
case 2: |
|||
return 'Warning'; |
|||
case 3: |
|||
return 'Error'; |
|||
} |
|||
throw new \InvalidArgumentException('Invalid log level number'); |
|||
} |
|||
} |
|||
@ -0,0 +1,124 @@ |
|||
<?php |
|||
/** |
|||
* @author Robin McCorkell <rmccorkell@owncloud.com> |
|||
* |
|||
* @copyright Copyright (c) 2015, 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\Log; |
|||
|
|||
use \OCP\IConfig; |
|||
|
|||
use Symfony\Component\Console\Command\Command; |
|||
use Symfony\Component\Console\Input\InputInterface; |
|||
use Symfony\Component\Console\Input\InputArgument; |
|||
use Symfony\Component\Console\Input\InputOption; |
|||
use Symfony\Component\Console\Output\OutputInterface; |
|||
|
|||
class OwnCloud extends Command { |
|||
|
|||
/** @var IConfig */ |
|||
protected $config; |
|||
|
|||
public function __construct(IConfig $config) { |
|||
$this->config = $config; |
|||
parent::__construct(); |
|||
} |
|||
|
|||
protected function configure() { |
|||
$this |
|||
->setName('log:owncloud') |
|||
->setDescription('manipulate ownCloud logging backend') |
|||
->addOption( |
|||
'enable', |
|||
null, |
|||
InputOption::VALUE_NONE, |
|||
'enable this logging backend' |
|||
) |
|||
->addOption( |
|||
'file', |
|||
null, |
|||
InputOption::VALUE_REQUIRED, |
|||
'set the log file path' |
|||
) |
|||
->addOption( |
|||
'rotate-size', |
|||
null, |
|||
InputOption::VALUE_REQUIRED, |
|||
'set the file size for log rotation, 0 = disabled' |
|||
) |
|||
; |
|||
} |
|||
|
|||
protected function execute(InputInterface $input, OutputInterface $output) { |
|||
$toBeSet = []; |
|||
|
|||
if ($input->getOption('enable')) { |
|||
$toBeSet['log_type'] = 'owncloud'; |
|||
} |
|||
|
|||
if ($file = $input->getOption('file')) { |
|||
$toBeSet['logfile'] = $file; |
|||
} |
|||
|
|||
if (($rotateSize = $input->getOption('rotate-size')) !== null) { |
|||
$rotateSize = \OCP\Util::computerFileSize($rotateSize); |
|||
$this->validateRotateSize($rotateSize); |
|||
$toBeSet['log_rotate_size'] = $rotateSize; |
|||
} |
|||
|
|||
// set config
|
|||
foreach ($toBeSet as $option => $value) { |
|||
$this->config->setSystemValue($option, $value); |
|||
} |
|||
|
|||
// display config
|
|||
if ($this->config->getSystemValue('log_type', 'owncloud') === 'owncloud') { |
|||
$enabledText = 'enabled'; |
|||
} else { |
|||
$enabledText = 'disabled'; |
|||
} |
|||
$output->writeln('Log backend ownCloud: '.$enabledText); |
|||
|
|||
$dataDir = $this->config->getSystemValue('datadirectory', \OC::$SERVERROOT.'/data'); |
|||
$defaultLogFile = rtrim($dataDir, '/').'/owncloud.log'; |
|||
$output->writeln('Log file: '.$this->config->getSystemValue('logfile', $defaultLogFile)); |
|||
|
|||
$rotateSize = $this->config->getSystemValue('log_rotate_size', 0); |
|||
if ($rotateSize) { |
|||
$rotateString = \OCP\Util::humanFileSize($rotateSize); |
|||
} else { |
|||
$rotateString = 'disabled'; |
|||
} |
|||
$output->writeln('Rotate at: '.$rotateString); |
|||
} |
|||
|
|||
/** |
|||
* @param mixed $rotateSize |
|||
* @throws \InvalidArgumentException |
|||
*/ |
|||
protected function validateRotateSize(&$rotateSize) { |
|||
if ($rotateSize === false) { |
|||
throw new \InvalidArgumentException('Error parsing log rotation file size'); |
|||
} |
|||
$rotateSize = (int) $rotateSize; |
|||
if ($rotateSize < 0) { |
|||
throw new \InvalidArgumentException('Log rotation file size must be non-negative'); |
|||
} |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,181 @@ |
|||
<?php |
|||
/** |
|||
* @author Robin McCorkell <rmccorkell@owncloud.com> |
|||
* |
|||
* @copyright Copyright (c) 2015, 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\Log; |
|||
|
|||
|
|||
use OC\Core\Command\Log\Manage; |
|||
use Test\TestCase; |
|||
|
|||
class ManageTest extends TestCase { |
|||
/** @var \PHPUnit_Framework_MockObject_MockObject */ |
|||
protected $config; |
|||
/** @var \PHPUnit_Framework_MockObject_MockObject */ |
|||
protected $consoleInput; |
|||
/** @var \PHPUnit_Framework_MockObject_MockObject */ |
|||
protected $consoleOutput; |
|||
|
|||
/** @var \Symfony\Component\Console\Command\Command */ |
|||
protected $command; |
|||
|
|||
protected function setUp() { |
|||
parent::setUp(); |
|||
|
|||
$config = $this->config = $this->getMockBuilder('OCP\IConfig') |
|||
->disableOriginalConstructor() |
|||
->getMock(); |
|||
$this->consoleInput = $this->getMock('Symfony\Component\Console\Input\InputInterface'); |
|||
$this->consoleOutput = $this->getMock('Symfony\Component\Console\Output\OutputInterface'); |
|||
|
|||
$this->command = new Manage($config); |
|||
} |
|||
|
|||
public function testChangeBackend() { |
|||
$this->consoleInput->method('getOption') |
|||
->will($this->returnValueMap([ |
|||
['backend', 'syslog'] |
|||
])); |
|||
$this->config->expects($this->once()) |
|||
->method('setSystemValue') |
|||
->with('log_type', 'syslog'); |
|||
|
|||
self::invokePrivate($this->command, 'execute', [$this->consoleInput, $this->consoleOutput]); |
|||
} |
|||
|
|||
public function testChangeLevel() { |
|||
$this->consoleInput->method('getOption') |
|||
->will($this->returnValueMap([ |
|||
['level', 'debug'] |
|||
])); |
|||
$this->config->expects($this->once()) |
|||
->method('setSystemValue') |
|||
->with('loglevel', 0); |
|||
|
|||
self::invokePrivate($this->command, 'execute', [$this->consoleInput, $this->consoleOutput]); |
|||
} |
|||
|
|||
public function testChangeTimezone() { |
|||
$this->consoleInput->method('getOption') |
|||
->will($this->returnValueMap([ |
|||
['timezone', 'UTC'] |
|||
])); |
|||
$this->config->expects($this->once()) |
|||
->method('setSystemValue') |
|||
->with('logtimezone', 'UTC'); |
|||
|
|||
self::invokePrivate($this->command, 'execute', [$this->consoleInput, $this->consoleOutput]); |
|||
} |
|||
|
|||
/** |
|||
* @expectedException \InvalidArgumentException |
|||
*/ |
|||
public function testValidateBackend() { |
|||
self::invokePrivate($this->command, 'validateBackend', ['notabackend']); |
|||
} |
|||
|
|||
/** |
|||
* @expectedException \Exception |
|||
*/ |
|||
public function testValidateTimezone() { |
|||
// this might need to be changed when humanity colonises Mars
|
|||
self::invokePrivate($this->command, 'validateTimezone', ['Mars/OlympusMons']); |
|||
} |
|||
|
|||
public function convertLevelStringProvider() { |
|||
return [ |
|||
['dEbug', 0], |
|||
['inFO', 1], |
|||
['Warning', 2], |
|||
['wArn', 2], |
|||
['error', 3], |
|||
['eRr', 3], |
|||
]; |
|||
} |
|||
|
|||
/** |
|||
* @dataProvider convertLevelStringProvider |
|||
*/ |
|||
public function testConvertLevelString($levelString, $expectedInt) { |
|||
$this->assertEquals($expectedInt, |
|||
self::invokePrivate($this->command, 'convertLevelString', [$levelString]) |
|||
); |
|||
} |
|||
|
|||
/** |
|||
* @expectedException \InvalidArgumentException |
|||
*/ |
|||
public function testConvertLevelStringInvalid() { |
|||
self::invokePrivate($this->command, 'convertLevelString', ['abc']); |
|||
} |
|||
|
|||
public function convertLevelNumberProvider() { |
|||
return [ |
|||
[0, 'Debug'], |
|||
[1, 'Info'], |
|||
[2, 'Warning'], |
|||
[3, 'Error'], |
|||
]; |
|||
} |
|||
|
|||
/** |
|||
* @dataProvider convertLevelNumberProvider |
|||
*/ |
|||
public function testConvertLevelNumber($levelNum, $expectedString) { |
|||
$this->assertEquals($expectedString, |
|||
self::invokePrivate($this->command, 'convertLevelNumber', [$levelNum]) |
|||
); |
|||
} |
|||
|
|||
/** |
|||
* @expectedException \InvalidArgumentException |
|||
*/ |
|||
public function testConvertLevelNumberInvalid() { |
|||
self::invokePrivate($this->command, 'convertLevelNumber', [11]); |
|||
} |
|||
|
|||
public function testGetConfiguration() { |
|||
$this->config->expects($this->at(0)) |
|||
->method('getSystemValue') |
|||
->with('log_type', 'owncloud') |
|||
->willReturn('log_type_value'); |
|||
$this->config->expects($this->at(1)) |
|||
->method('getSystemValue') |
|||
->with('loglevel', 2) |
|||
->willReturn(0); |
|||
$this->config->expects($this->at(2)) |
|||
->method('getSystemValue') |
|||
->with('logtimezone', 'UTC') |
|||
->willReturn('logtimezone_value'); |
|||
|
|||
$this->consoleOutput->expects($this->at(0)) |
|||
->method('writeln') |
|||
->with('Enabled logging backend: log_type_value'); |
|||
$this->consoleOutput->expects($this->at(1)) |
|||
->method('writeln') |
|||
->with('Log level: Debug (0)'); |
|||
$this->consoleOutput->expects($this->at(2)) |
|||
->method('writeln') |
|||
->with('Log timezone: logtimezone_value'); |
|||
|
|||
self::invokePrivate($this->command, 'execute', [$this->consoleInput, $this->consoleOutput]); |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,121 @@ |
|||
<?php |
|||
/** |
|||
* @author Robin McCorkell <rmccorkell@owncloud.com> |
|||
* |
|||
* @copyright Copyright (c) 2015, 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\Log; |
|||
|
|||
|
|||
use OC\Core\Command\Log\OwnCloud; |
|||
use Test\TestCase; |
|||
|
|||
class OwnCloudTest extends TestCase { |
|||
/** @var \PHPUnit_Framework_MockObject_MockObject */ |
|||
protected $config; |
|||
/** @var \PHPUnit_Framework_MockObject_MockObject */ |
|||
protected $consoleInput; |
|||
/** @var \PHPUnit_Framework_MockObject_MockObject */ |
|||
protected $consoleOutput; |
|||
|
|||
/** @var \Symfony\Component\Console\Command\Command */ |
|||
protected $command; |
|||
|
|||
protected function setUp() { |
|||
parent::setUp(); |
|||
|
|||
$config = $this->config = $this->getMockBuilder('OCP\IConfig') |
|||
->disableOriginalConstructor() |
|||
->getMock(); |
|||
$this->consoleInput = $this->getMock('Symfony\Component\Console\Input\InputInterface'); |
|||
$this->consoleOutput = $this->getMock('Symfony\Component\Console\Output\OutputInterface'); |
|||
|
|||
$this->command = new OwnCloud($config); |
|||
} |
|||
|
|||
public function testEnable() { |
|||
$this->consoleInput->method('getOption') |
|||
->will($this->returnValueMap([ |
|||
['enable', 'true'] |
|||
])); |
|||
$this->config->expects($this->once()) |
|||
->method('setSystemValue') |
|||
->with('log_type', 'owncloud'); |
|||
|
|||
self::invokePrivate($this->command, 'execute', [$this->consoleInput, $this->consoleOutput]); |
|||
} |
|||
|
|||
public function testChangeFile() { |
|||
$this->consoleInput->method('getOption') |
|||
->will($this->returnValueMap([ |
|||
['file', '/foo/bar/file.log'] |
|||
])); |
|||
$this->config->expects($this->once()) |
|||
->method('setSystemValue') |
|||
->with('logfile', '/foo/bar/file.log'); |
|||
|
|||
self::invokePrivate($this->command, 'execute', [$this->consoleInput, $this->consoleOutput]); |
|||
} |
|||
|
|||
public function changeRotateSizeProvider() { |
|||
return [ |
|||
['42', 42], |
|||
['0', 0], |
|||
['1 kB', 1024], |
|||
['5MB', 5 * 1024 * 1024], |
|||
]; |
|||
} |
|||
|
|||
/** |
|||
* @dataProvider changeRotateSizeProvider |
|||
*/ |
|||
public function testChangeRotateSize($optionValue, $configValue) { |
|||
$this->consoleInput->method('getOption') |
|||
->will($this->returnValueMap([ |
|||
['rotate-size', $optionValue] |
|||
])); |
|||
$this->config->expects($this->once()) |
|||
->method('setSystemValue') |
|||
->with('log_rotate_size', $configValue); |
|||
|
|||
self::invokePrivate($this->command, 'execute', [$this->consoleInput, $this->consoleOutput]); |
|||
} |
|||
|
|||
public function testGetConfiguration() { |
|||
$this->config->method('getSystemValue') |
|||
->will($this->returnValueMap([ |
|||
['log_type', 'owncloud', 'log_type_value'], |
|||
['datadirectory', \OC::$SERVERROOT.'/data', '/data/directory/'], |
|||
['logfile', '/data/directory/owncloud.log', '/var/log/owncloud.log'], |
|||
['log_rotate_size', 0, 5 * 1024 * 1024], |
|||
])); |
|||
|
|||
$this->consoleOutput->expects($this->at(0)) |
|||
->method('writeln') |
|||
->with('Log backend ownCloud: disabled'); |
|||
$this->consoleOutput->expects($this->at(1)) |
|||
->method('writeln') |
|||
->with('Log file: /var/log/owncloud.log'); |
|||
$this->consoleOutput->expects($this->at(2)) |
|||
->method('writeln') |
|||
->with('Rotate at: 5 MB'); |
|||
|
|||
self::invokePrivate($this->command, 'execute', [$this->consoleInput, $this->consoleOutput]); |
|||
} |
|||
|
|||
} |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue