From b9e3ed14680bea2626d90c5dfc48d7f61d7e437f Mon Sep 17 00:00:00 2001 From: Lukas Reschke Date: Tue, 9 Feb 2016 19:58:29 +0100 Subject: [PATCH] Add SSO for updater application Allows logging-in into the updater application by visiting the admin panel and pressing "Open updater". --- .gitignore | 1 + apps/updatenotification/admin.php | 25 +++++ apps/updatenotification/appinfo/app.php | 2 + .../appinfo/application.php | 46 +++++++++ apps/updatenotification/appinfo/info.xml | 2 +- apps/updatenotification/appinfo/routes.php | 27 +++++ .../controller/admincontroller.php | 85 ++++++++++++++++ apps/updatenotification/js/admin.js | 42 ++++++++ .../lib/resettokenbackgroundjob.php | 75 ++++++++++++++ apps/updatenotification/templates/admin.php | 8 ++ .../tests/ResetTokenBackgroundJobTest.php | 81 +++++++++++++++ .../tests/controller/AdminControllerTest.php | 98 +++++++++++++++++++ 12 files changed, 491 insertions(+), 1 deletion(-) create mode 100644 apps/updatenotification/admin.php create mode 100644 apps/updatenotification/appinfo/application.php create mode 100644 apps/updatenotification/appinfo/routes.php create mode 100644 apps/updatenotification/controller/admincontroller.php create mode 100644 apps/updatenotification/js/admin.js create mode 100644 apps/updatenotification/lib/resettokenbackgroundjob.php create mode 100644 apps/updatenotification/templates/admin.php create mode 100644 apps/updatenotification/tests/ResetTokenBackgroundJobTest.php create mode 100644 apps/updatenotification/tests/controller/AdminControllerTest.php diff --git a/.gitignore b/.gitignore index a2fb67ef42..73f57989a8 100644 --- a/.gitignore +++ b/.gitignore @@ -22,6 +22,7 @@ !/apps/user_ldap !/apps/provisioning_api !/apps/systemtags +!/apps/updatenotification /apps/files_external/3rdparty/irodsphp/PHPUnitTest /apps/files_external/3rdparty/irodsphp/web /apps/files_external/3rdparty/irodsphp/prods/test diff --git a/apps/updatenotification/admin.php b/apps/updatenotification/admin.php new file mode 100644 index 0000000000..768d887800 --- /dev/null +++ b/apps/updatenotification/admin.php @@ -0,0 +1,25 @@ + + * + * @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 + * + */ + +$app = new \OCA\UpdateNotification\AppInfo\Application(); +/** @var OCA\UpdateNotification\Controller\AdminController $controller */ +$controller = $app->getContainer()->query('AdminController'); +return $controller->displayPanel()->render(); diff --git a/apps/updatenotification/appinfo/app.php b/apps/updatenotification/appinfo/app.php index d5e973be52..99df99ac7c 100644 --- a/apps/updatenotification/appinfo/app.php +++ b/apps/updatenotification/appinfo/app.php @@ -36,4 +36,6 @@ if(\OC::$server->getConfig()->getSystemValue('updatechecker', true) === true) { OC_Hook::connect('\OCP\Config', 'js', $updateChecker, 'getJavaScript'); } } + + \OC_App::registerAdmin('updatenotification', 'admin'); } diff --git a/apps/updatenotification/appinfo/application.php b/apps/updatenotification/appinfo/application.php new file mode 100644 index 0000000000..ae3317c1b5 --- /dev/null +++ b/apps/updatenotification/appinfo/application.php @@ -0,0 +1,46 @@ + + * + * @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 + * + */ + +namespace OCA\UpdateNotification\AppInfo; + +use OC\AppFramework\Utility\TimeFactory; +use OCA\UpdateNotification\Controller\AdminController; +use OCP\AppFramework\App; +use OCP\AppFramework\IAppContainer; + +class Application extends App { + public function __construct (array $urlParams = array()) { + parent::__construct('updatenotification', $urlParams); + $container = $this->getContainer(); + + $container->registerService('AdminController', function(IAppContainer $c) { + return new AdminController( + $c->query('AppName'), + $c->query('Request'), + $c->getServer()->getJobList(), + $c->getServer()->getSecureRandom(), + $c->getServer()->getConfig(), + new TimeFactory() + ); + }); + } + +} diff --git a/apps/updatenotification/appinfo/info.xml b/apps/updatenotification/appinfo/info.xml index 0bfdd861a2..2991829e19 100644 --- a/apps/updatenotification/appinfo/info.xml +++ b/apps/updatenotification/appinfo/info.xml @@ -2,7 +2,7 @@ updatenotification Update notification - Displays update notifications for ownCloud. + Displays update notifications for ownCloud and provides the SSO for the updater. AGPL Lukas Reschke 0.1.0 diff --git a/apps/updatenotification/appinfo/routes.php b/apps/updatenotification/appinfo/routes.php new file mode 100644 index 0000000000..2cf43c8976 --- /dev/null +++ b/apps/updatenotification/appinfo/routes.php @@ -0,0 +1,27 @@ + + * + * @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 + * + */ + +namespace OCA\UpdateNotification\AppInfo; + +$application = new Application(); +$application->registerRoutes($this, ['routes' => [ + ['name' => 'Admin#createCredentials', 'url' => '/credentials', 'verb' => 'GET'], +]]); diff --git a/apps/updatenotification/controller/admincontroller.php b/apps/updatenotification/controller/admincontroller.php new file mode 100644 index 0000000000..ec1cc45075 --- /dev/null +++ b/apps/updatenotification/controller/admincontroller.php @@ -0,0 +1,85 @@ + + * + * @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 + * + */ + +namespace OCA\UpdateNotification\Controller; + +use OCP\AppFramework\Controller; +use OCP\AppFramework\Http\DataResponse; +use OCP\AppFramework\Http\TemplateResponse; +use OCP\AppFramework\Utility\ITimeFactory; +use OCP\BackgroundJob\IJobList; +use OCP\IConfig; +use OCP\IRequest; +use OCP\Security\ISecureRandom; + +class AdminController extends Controller { + /** @var IJobList */ + private $jobList; + /** @var ISecureRandom */ + private $secureRandom; + /** @var IConfig */ + private $config; + /** @var ITimeFactory */ + private $timeFactory; + + /** + * @param string $appName + * @param IRequest $request + * @param IJobList $jobList + * @param ISecureRandom $secureRandom + * @param IConfig $config + * @param ITimeFactory $timeFactory + */ + public function __construct($appName, + IRequest $request, + IJobList $jobList, + ISecureRandom $secureRandom, + IConfig $config, + ITimeFactory $timeFactory) { + parent::__construct($appName, $request); + $this->jobList = $jobList; + $this->secureRandom = $secureRandom; + $this->config = $config; + $this->timeFactory = $timeFactory; + } + + /** + * @return TemplateResponse + */ + public function displayPanel() { + return new TemplateResponse($this->appName, 'admin', [], ''); + } + + /** + * @return DataResponse + */ + public function createCredentials() { + // Create a new job and store the creation date + $this->jobList->add('OCA\UpdateNotification\ResetTokenBackgroundJob'); + $this->config->setAppValue('core', 'updater.secret.created', $this->timeFactory->getTime()); + + // Create a new token + $newToken = $this->secureRandom->generate(32); + $this->config->setSystemValue('updater.secret', $newToken); + + return new DataResponse($newToken); + } +} diff --git a/apps/updatenotification/js/admin.js b/apps/updatenotification/js/admin.js new file mode 100644 index 0000000000..df021fe2e9 --- /dev/null +++ b/apps/updatenotification/js/admin.js @@ -0,0 +1,42 @@ +/** + * Copyright (c) 2016 ownCloud Inc + * + * @author Lukas Reschke + * + * This file is licensed under the Affero General Public License version 3 + * or later. + * + * See the COPYING-README file. + * + */ + +/** + * Creates a new authentication token and loads the updater URL + */ +var loginToken = ''; +$(document).ready(function(){ + $('#oca_updatenotification').click(function() { + // Load the new token + $.ajax({ + url: OC.generateUrl('/apps/updatenotification/credentials') + }).success(function(data) { + loginToken = data; + $.ajax({ + url: OC.webroot+'/updater/', + headers: { + 'Authorization': loginToken + }, + method: 'POST', + success: function(data){ + if(data !== 'false') { + var body = $('body'); + $('head').remove(); + body.html(data); + body.removeAttr('id'); + body.attr('id', 'body-settings'); + } + } + }); + }); + }); +}); diff --git a/apps/updatenotification/lib/resettokenbackgroundjob.php b/apps/updatenotification/lib/resettokenbackgroundjob.php new file mode 100644 index 0000000000..0b737f681b --- /dev/null +++ b/apps/updatenotification/lib/resettokenbackgroundjob.php @@ -0,0 +1,75 @@ + + * + * @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 + * + */ + +namespace OCA\UpdateNotification; + +use OC\AppFramework\Utility\TimeFactory; +use OC\BackgroundJob\TimedJob; +use OCP\AppFramework\Utility\ITimeFactory; +use OCP\IConfig; + +/** + * Class ResetTokenBackgroundJob deletes any configured token all 24 hours for + * + * + * @package OCA\UpdateNotification + */ +class ResetTokenBackgroundJob extends TimedJob { + /** @var IConfig */ + private $config; + /** @var ITimeFactory */ + private $timeFactory; + + /** + * @param IConfig|null $config + * @param ITimeFactory|null $timeFactory + */ + public function __construct(IConfig $config = null, + ITimeFactory $timeFactory = null) { + // Run all 10 minutes + $this->setInterval(60 * 10); + + if ($config instanceof IConfig && $timeFactory instanceof ITimeFactory) { + $this->config = $config; + $this->timeFactory = $timeFactory; + } else { + $this->fixDIForJobs(); + } + } + + /** + * DI for jobs + */ + private function fixDIForJobs() { + $this->config = \OC::$server->getConfig(); + $this->timeFactory = new TimeFactory(); + } + + /** + * @param $argument + */ + protected function run($argument) { + if($this->timeFactory->getTime() - $this->config->getAppValue('core', 'updater.secret.created', $this->timeFactory->getTime()) >= 86400) { + $this->config->deleteSystemValue('updater.secret'); + } + } + +} diff --git a/apps/updatenotification/templates/admin.php b/apps/updatenotification/templates/admin.php new file mode 100644 index 0000000000..647c88dea1 --- /dev/null +++ b/apps/updatenotification/templates/admin.php @@ -0,0 +1,8 @@ + +
+

t('Updater')); ?>

+

+ t('For security reasons the built-in ownCloud updater is using additional credentials. To visit the updater page please click the following button.')) ?> +

+ +
diff --git a/apps/updatenotification/tests/ResetTokenBackgroundJobTest.php b/apps/updatenotification/tests/ResetTokenBackgroundJobTest.php new file mode 100644 index 0000000000..f3d64814ec --- /dev/null +++ b/apps/updatenotification/tests/ResetTokenBackgroundJobTest.php @@ -0,0 +1,81 @@ + + * + * @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 + * + */ + +namespace OCA\UpdateNotification\Tests; + +use OCA\UpdateNotification\ResetTokenBackgroundJob; +use OCP\AppFramework\Utility\ITimeFactory; +use OCP\IConfig; +use Test\TestCase; + +class ResetTokenBackgroundJobTest extends TestCase { + /** @var IConfig */ + private $config; + /** @var ResetTokenBackgroundJob */ + private $resetTokenBackgroundJob; + /** @var ITimeFactory */ + private $timeFactory; + + public function setUp() { + parent::setUp(); + $this->config = $this->getMock('\\OCP\\IConfig'); + $this->timeFactory = $this->getMock('\\OCP\\AppFramework\\Utility\\ITimeFactory'); + $this->resetTokenBackgroundJob = new ResetTokenBackgroundJob($this->config, $this->timeFactory); + } + + public function testRunWithNotExpiredToken() { + $this->timeFactory + ->expects($this->any()) + ->method('getTime') + ->willReturn(123); + $this->config + ->expects($this->once()) + ->method('getAppValue') + ->with('core', 'updater.secret.created', 123); + $this->config + ->expects($this->never()) + ->method('deleteSystemValue') + ->with('updater.secret'); + + $this->invokePrivate($this->resetTokenBackgroundJob, 'run', ['']); + } + + public function testRunWithExpiredToken() { + $this->timeFactory + ->expects($this->at(0)) + ->method('getTime') + ->willReturn(1455131633); + $this->timeFactory + ->expects($this->at(1)) + ->method('getTime') + ->willReturn(1455045234); + $this->config + ->expects($this->once()) + ->method('getAppValue') + ->with('core', 'updater.secret.created', 1455045234); + $this->config + ->expects($this->once()) + ->method('deleteSystemValue') + ->with('updater.secret'); + + $this->invokePrivate($this->resetTokenBackgroundJob, 'run', ['']); + } +} diff --git a/apps/updatenotification/tests/controller/AdminControllerTest.php b/apps/updatenotification/tests/controller/AdminControllerTest.php new file mode 100644 index 0000000000..0180162600 --- /dev/null +++ b/apps/updatenotification/tests/controller/AdminControllerTest.php @@ -0,0 +1,98 @@ + + * + * @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 + * + */ + +namespace OCA\UpdateNotification\Tests\Controller; + +use OCA\UpdateNotification\Controller\AdminController; +use OCP\AppFramework\Http\DataResponse; +use OCP\AppFramework\Http\TemplateResponse; +use OCP\AppFramework\Utility\ITimeFactory; +use OCP\BackgroundJob\IJobList; +use OCP\IConfig; +use OCP\IRequest; +use OCP\Security\ISecureRandom; +use Test\TestCase; + +class AdminControllerTest extends TestCase { + /** @var IRequest */ + private $request; + /** @var IJobList */ + private $jobList; + /** @var ISecureRandom */ + private $secureRandom; + /** @var IConfig */ + private $config; + /** @var AdminController */ + private $adminController; + /** @var ITimeFactory */ + private $timeFactory; + + public function setUp() { + parent::setUp(); + + $this->request = $this->getMock('\\OCP\\IRequest'); + $this->jobList = $this->getMock('\\OCP\\BackgroundJob\\IJobList'); + $this->secureRandom = $this->getMock('\\OCP\\Security\\ISecureRandom'); + $this->config = $this->getMock('\\OCP\\IConfig'); + $this->timeFactory = $this->getMock('\\OCP\\AppFramework\\Utility\\ITimeFactory'); + + $this->adminController = new AdminController( + 'updatenotification', + $this->request, + $this->jobList, + $this->secureRandom, + $this->config, + $this->timeFactory + ); + } + + public function testDisplayPanel() { + $expected = new TemplateResponse('updatenotification', 'admin', [], ''); + $this->assertEquals($expected, $this->adminController->displayPanel()); + } + + public function testCreateCredentials() { + $this->jobList + ->expects($this->once()) + ->method('add') + ->with('OCA\UpdateNotification\ResetTokenBackgroundJob'); + $this->secureRandom + ->expects($this->once()) + ->method('generate') + ->with(32) + ->willReturn('MyGeneratedToken'); + $this->config + ->expects($this->once()) + ->method('setSystemValue') + ->with('updater.secret', 'MyGeneratedToken'); + $this->timeFactory + ->expects($this->once()) + ->method('getTime') + ->willReturn(12345); + $this->config + ->expects($this->once()) + ->method('setAppValue') + ->with('core', 'updater.secret.created', 12345); + + $expected = new DataResponse('MyGeneratedToken'); + $this->assertEquals($expected, $this->adminController->createCredentials()); + } +}