diff --git a/apps/encryption/appinfo/application.php b/apps/encryption/appinfo/application.php index dbeb1171bd..417b944c02 100644 --- a/apps/encryption/appinfo/application.php +++ b/apps/encryption/appinfo/application.php @@ -167,6 +167,20 @@ class Application extends \OCP\AppFramework\App { ); }); + $container->registerService('SettingsController', function (IAppContainer $c) { + $server = $c->getServer(); + return new \OCA\Encryption\Controller\SettingsController( + $c->getAppName(), + $server->getRequest(), + $server->getL10N($c->getAppName()), + $server->getUserManager(), + $server->getUserSession(), + $c->query('KeyManager'), + $c->query('Crypt'), + $c->query('Session') + ); + }); + $container->registerService('UserSetup', function (IAppContainer $c) { $server = $c->getServer(); diff --git a/apps/encryption/appinfo/routes.php b/apps/encryption/appinfo/routes.php index 4194308a0c..8fa163d075 100644 --- a/apps/encryption/appinfo/routes.php +++ b/apps/encryption/appinfo/routes.php @@ -30,6 +30,11 @@ namespace OCA\Encryption\AppInfo; 'url' => '/ajax/adminRecovery', 'verb' => 'POST' ], + [ + 'name' => 'Settings#updatePrivateKeyPassword', + 'url' => '/ajax/updatePrivateKeyPassword', + 'verb' => 'POST' + ], [ 'name' => 'Recovery#changeRecoveryPassword', 'url' => '/ajax/changeRecoveryPassword', diff --git a/apps/encryption/controller/settingscontroller.php b/apps/encryption/controller/settingscontroller.php new file mode 100644 index 0000000000..ec45b0596c --- /dev/null +++ b/apps/encryption/controller/settingscontroller.php @@ -0,0 +1,144 @@ + + * + * @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 + * + */ + + +namespace OCA\Encryption\Controller; + + +use OCA\Encryption\Crypto\Crypt; +use OCA\Encryption\KeyManager; +use OCA\Encryption\Session; +use OCP\AppFramework\Controller; +use OCP\AppFramework\Http\DataResponse; +use OCP\IL10N; +use OCP\IRequest; +use OCP\IUserManager; +use OCP\IUserSession; + +class SettingsController extends Controller { + + /** @var IL10N */ + private $l; + + /** @var IUserManager */ + private $userManager; + + /** @var IUserSession */ + private $userSession; + + /** @var KeyManager */ + private $keyManager; + + /** @var Crypt */ + private $crypt; + + /** @var Session */ + private $session; + + /** + * @param string $AppName + * @param IRequest $request + * @param IL10N $l10n + * @param IUserManager $userManager + * @param IUserSession $userSession + * @param KeyManager $keyManager + * @param Crypt $crypt + * @param Session $session + */ + public function __construct($AppName, + IRequest $request, + IL10N $l10n, + IUserManager $userManager, + IUserSession $userSession, + KeyManager $keyManager, + Crypt $crypt, + Session $session) { + parent::__construct($AppName, $request); + $this->l = $l10n; + $this->userSession = $userSession; + $this->userManager = $userManager; + $this->keyManager = $keyManager; + $this->crypt = $crypt; + $this->session = $session; + } + + + /** + * @NoAdminRequired + * @UseSession + * + * @param string $oldPassword + * @param string $newPassword + * @return DataResponse + */ + public function updatePrivateKeyPassword($oldPassword, $newPassword) { + $result = false; + $uid = $this->userSession->getUser()->getUID(); + $errorMessage = $this->l->t('Could not update the private key password.'); + + //check if password is correct + $passwordCorrect = $this->userManager->checkPassword($uid, $newPassword); + + if ($passwordCorrect !== false) { + $encryptedKey = $this->keyManager->getPrivateKey($uid); + $decryptedKey = $this->crypt->decryptPrivateKey($encryptedKey, $oldPassword); + + if ($decryptedKey) { + $encryptedKey = $this->crypt->symmetricEncryptFileContent($decryptedKey, $newPassword); + $header = $this->crypt->generateHeader(); + if ($encryptedKey) { + $this->keyManager->setPrivateKey($uid, $header . $encryptedKey); + $this->session->setPrivateKey($decryptedKey); + $result = true; + } + } else { + $result = false; + $errorMessage = $this->l->t( + 'The old password was not correct, please try again.'); + } + } else { + $result = false; + $errorMessage = $this->l->t( + 'The current log-in password was not correct, please try again.'); + } + + if ($result === true) { + $this->session->setStatus(Session::INIT_SUCCESSFUL); + return new DataResponse( + array( + 'status' => 'success', + 'data' => array( + 'message' => (string) $this->l->t('Private key password successfully updated.')) + ) + ); + } else { + return new DataResponse( + array( + 'data' => array + ('message' => (string) $errorMessage) + ) + ); + } + + } + + +} diff --git a/apps/encryption/tests/controller/SettingsControllerTest.php b/apps/encryption/tests/controller/SettingsControllerTest.php new file mode 100644 index 0000000000..37a67652a1 --- /dev/null +++ b/apps/encryption/tests/controller/SettingsControllerTest.php @@ -0,0 +1,221 @@ + + * + * @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 + * + */ + + +namespace OCA\Encryption\Tests\Controller; + + +use OCA\Encryption\Controller\SettingsController; +use OCA\Encryption\Session; +use Test\TestCase; + +class SettingsControllerTest extends TestCase { + + /** @var SettingsController */ + private $controller; + + /** @var \PHPUnit_Framework_MockObject_MockObject */ + private $requestMock; + + /** @var \PHPUnit_Framework_MockObject_MockObject */ + private $l10nMock; + + /** @var \PHPUnit_Framework_MockObject_MockObject */ + private $userManagerMock; + + /** @var \PHPUnit_Framework_MockObject_MockObject */ + private $userSessionMock; + + /** @var \PHPUnit_Framework_MockObject_MockObject */ + private $keyManagerMock; + + /** @var \PHPUnit_Framework_MockObject_MockObject */ + private $cryptMock; + + /** @var \PHPUnit_Framework_MockObject_MockObject */ + private $sessionMock; + + public function setUp() { + + parent::setUp(); + + $this->requestMock = $this->getMock('OCP\IRequest'); + + $this->l10nMock = $this->getMockBuilder('OCP\IL10N') + ->disableOriginalConstructor()->getMock(); + + $this->l10nMock->expects($this->any()) + ->method('t') + ->will($this->returnCallback(function($message) { + return $message; })); + + $this->userManagerMock = $this->getMockBuilder('OCP\IUserManager') + ->disableOriginalConstructor()->getMock(); + + $this->keyManagerMock = $this->getMockBuilder('OCA\Encryption\KeyManager') + ->disableOriginalConstructor()->getMock(); + + $this->cryptMock = $this->getMockBuilder('OCA\Encryption\Crypto\Crypt') + ->disableOriginalConstructor()->getMock(); + + $this->userSessionMock = $this->getMockBuilder('OCP\IUserSession') + ->disableOriginalConstructor() + ->setMethods([ + 'isLoggedIn', + 'getUID', + 'login', + 'logout', + 'setUser', + 'getUser', + 'canChangePassword' + ]) + ->getMock(); + + $this->userSessionMock->expects($this->any()) + ->method('getUID') + ->willReturn('testUser'); + + $this->userSessionMock->expects($this->any()) + ->method($this->anything()) + ->will($this->returnSelf()); + + $this->sessionMock = $this->getMockBuilder('OCA\Encryption\Session') + ->disableOriginalConstructor()->getMock(); + + $this->controller = new SettingsController( + 'encryption', + $this->requestMock, + $this->l10nMock, + $this->userManagerMock, + $this->userSessionMock, + $this->keyManagerMock, + $this->cryptMock, + $this->sessionMock + ); + } + + /** + * test updatePrivateKeyPassword() if wrong new password was entered + */ + public function testUpdatePrivateKeyPasswordWrongNewPassword() { + + $oldPassword = 'old'; + $newPassword = 'new'; + + $this->userManagerMock + ->expects($this->once()) + ->method('checkPassword') + ->willReturn(false); + + $result = $this->controller->updatePrivateKeyPassword($oldPassword, $newPassword); + + $data = $result->getData(); + + $this->assertSame('The current log-in password was not correct, please try again.', + $data['data']['message']); + } + + /** + * test updatePrivateKeyPassword() if wrong old password was entered + */ + public function testUpdatePrivateKeyPasswordWrongOldPassword() { + + $oldPassword = 'old'; + $newPassword = 'new'; + + $this->userManagerMock + ->expects($this->once()) + ->method('checkPassword') + ->willReturn(true); + + $this->cryptMock + ->expects($this->once()) + ->method('decryptPrivateKey') + ->willReturn(false); + + $result = $this->controller->updatePrivateKeyPassword($oldPassword, $newPassword); + + $data = $result->getData(); + + $this->assertSame('The old password was not correct, please try again.', + $data['data']['message']); + } + + /** + * test updatePrivateKeyPassword() with the correct old and new password + */ + public function testUpdatePrivateKeyPassword() { + + $oldPassword = 'old'; + $newPassword = 'new'; + + $this->userSessionMock + ->expects($this->once()) + ->method('getUID') + ->willReturn('testUser'); + + $this->userManagerMock + ->expects($this->once()) + ->method('checkPassword') + ->willReturn(true); + + $this->cryptMock + ->expects($this->once()) + ->method('decryptPrivateKey') + ->willReturn('decryptedKey'); + + $this->cryptMock + ->expects($this->once()) + ->method('symmetricEncryptFileContent') + ->willReturn('encryptedKey'); + + $this->cryptMock + ->expects($this->once()) + ->method('generateHeader') + ->willReturn('header.'); + + // methods which must be called after successful changing the key password + $this->keyManagerMock + ->expects($this->once()) + ->method('setPrivateKey') + ->with($this->equalTo('testUser'), $this->equalTo('header.encryptedKey')); + + $this->sessionMock + ->expects($this->once()) + ->method('setPrivateKey') + ->with($this->equalTo('decryptedKey')); + + $this->sessionMock + ->expects($this->once()) + ->method('setStatus') + ->with($this->equalTo(Session::INIT_SUCCESSFUL)); + + $result = $this->controller->updatePrivateKeyPassword($oldPassword, $newPassword); + + $data = $result->getData(); + + $this->assertSame('success', $data['status']); + + $this->assertSame('Private key password successfully updated.', + $data['data']['message']); + } + +}