From d907666232468503ab6ed2bdac44b6500be2beb6 Mon Sep 17 00:00:00 2001 From: Christoph Wurst Date: Tue, 6 Sep 2016 21:41:15 +0200 Subject: bring back remember-me * try to reuse the old session token for remember me login * decrypt/encrypt token password and set the session id accordingly * create remember-me cookies only if checkbox is checked and 2fa solved * adjust db token cleanup to store remembered tokens longer * adjust unit tests Signed-off-by: Christoph Wurst --- tests/Core/Controller/LoginControllerTest.php | 72 ++++- .../Token/DefaultTokenMapperTest.php | 1 + .../Token/DefaultTokenProviderTest.php | 18 +- .../Authentication/TwoFactorAuth/ManagerTest.php | 29 ++- tests/lib/User/SessionTest.php | 289 ++++++++++++--------- 5 files changed, 269 insertions(+), 140 deletions(-) (limited to 'tests') diff --git a/tests/Core/Controller/LoginControllerTest.php b/tests/Core/Controller/LoginControllerTest.php index ff50ac98fbd..d16b9b114f3 100644 --- a/tests/Core/Controller/LoginControllerTest.php +++ b/tests/Core/Controller/LoginControllerTest.php @@ -322,6 +322,8 @@ class LoginControllerTest extends TestCase { $this->userSession->expects($this->never()) ->method('createSessionToken'); + $this->userSession->expects($this->never()) + ->method('createRememberMeToken'); $this->config->expects($this->never()) ->method('deleteUserValue'); @@ -363,7 +365,7 @@ class LoginControllerTest extends TestCase { ->with($user, $password); $this->userSession->expects($this->once()) ->method('createSessionToken') - ->with($this->request, $user->getUID(), $user, $password); + ->with($this->request, $user->getUID(), $user, $password, false); $this->twoFactorManager->expects($this->once()) ->method('isTwoFactorAuthenticated') ->with($user) @@ -371,11 +373,63 @@ class LoginControllerTest extends TestCase { $this->config->expects($this->once()) ->method('deleteUserValue') ->with('uid', 'core', 'lostpassword'); + $this->userSession->expects($this->never()) + ->method('createRememberMeToken'); $expected = new \OCP\AppFramework\Http\RedirectResponse($indexPageUrl); $this->assertEquals($expected, $this->loginController->tryLogin($user, $password, null)); } + public function testLoginWithValidCredentialsAndRememberMe() { + /** @var IUser | \PHPUnit_Framework_MockObject_MockObject $user */ + $user = $this->getMockBuilder('\OCP\IUser')->getMock(); + $user->expects($this->any()) + ->method('getUID') + ->will($this->returnValue('uid')); + $password = 'secret'; + $indexPageUrl = \OC_Util::getDefaultPageUrl(); + + $this->request + ->expects($this->exactly(2)) + ->method('getRemoteAddress') + ->willReturn('192.168.0.1'); + $this->request + ->expects($this->once()) + ->method('passesCSRFCheck') + ->willReturn(true); + $this->throttler + ->expects($this->once()) + ->method('sleepDelay') + ->with('192.168.0.1'); + $this->throttler + ->expects($this->once()) + ->method('getDelay') + ->with('192.168.0.1') + ->willReturn(200); + $this->userManager->expects($this->once()) + ->method('checkPassword') + ->will($this->returnValue($user)); + $this->userSession->expects($this->once()) + ->method('login') + ->with($user, $password); + $this->userSession->expects($this->once()) + ->method('createSessionToken') + ->with($this->request, $user->getUID(), $user, $password, true); + $this->twoFactorManager->expects($this->once()) + ->method('isTwoFactorAuthenticated') + ->with($user) + ->will($this->returnValue(false)); + $this->config->expects($this->once()) + ->method('deleteUserValue') + ->with('uid', 'core', 'lostpassword'); + $this->userSession->expects($this->once()) + ->method('createRememberMeToken') + ->with($user); + + $expected = new \OCP\AppFramework\Http\RedirectResponse($indexPageUrl); + $this->assertEquals($expected, $this->loginController->tryLogin($user, $password, null, true)); + } + public function testLoginWithoutPassedCsrfCheckAndNotLoggedIn() { /** @var IUser | \PHPUnit_Framework_MockObject_MockObject $user */ $user = $this->getMockBuilder('\OCP\IUser')->getMock(); @@ -408,6 +462,8 @@ class LoginControllerTest extends TestCase { ->will($this->returnValue(false)); $this->config->expects($this->never()) ->method('deleteUserValue'); + $this->userSession->expects($this->never()) + ->method('createRememberMeToken'); $expected = new \OCP\AppFramework\Http\RedirectResponse(\OC_Util::getDefaultPageUrl()); $this->assertEquals($expected, $this->loginController->tryLogin('Jane', $password, $originalUrl)); @@ -450,6 +506,8 @@ class LoginControllerTest extends TestCase { ->will($this->returnValue($redirectUrl)); $this->config->expects($this->never()) ->method('deleteUserValue'); + $this->userSession->expects($this->never()) + ->method('createRememberMeToken'); $expected = new \OCP\AppFramework\Http\RedirectResponse($redirectUrl); $this->assertEquals($expected, $this->loginController->tryLogin('Jane', $password, $originalUrl)); @@ -488,7 +546,7 @@ class LoginControllerTest extends TestCase { ->will($this->returnValue($user)); $this->userSession->expects($this->once()) ->method('createSessionToken') - ->with($this->request, $user->getUID(), 'Jane', $password); + ->with($this->request, $user->getUID(), 'Jane', $password, false); $this->userSession->expects($this->once()) ->method('isLoggedIn') ->with() @@ -540,7 +598,7 @@ class LoginControllerTest extends TestCase { ->with('john@doe.com', $password); $this->userSession->expects($this->once()) ->method('createSessionToken') - ->with($this->request, $user->getUID(), 'john@doe.com', $password); + ->with($this->request, $user->getUID(), 'john@doe.com', $password, false); $this->twoFactorManager->expects($this->once()) ->method('isTwoFactorAuthenticated') ->with($user) @@ -564,6 +622,8 @@ class LoginControllerTest extends TestCase { $this->config->expects($this->once()) ->method('deleteUserValue') ->with('john', 'core', 'lostpassword'); + $this->userSession->expects($this->never()) + ->method('createRememberMeToken'); $expected = new RedirectResponse($challengeUrl); $this->assertEquals($expected, $this->loginController->tryLogin('john@doe.com', $password, null)); @@ -605,7 +665,7 @@ class LoginControllerTest extends TestCase { ->with('john@doe.com', $password); $this->userSession->expects($this->once()) ->method('createSessionToken') - ->with($this->request, $user->getUID(), 'john@doe.com', $password); + ->with($this->request, $user->getUID(), 'john@doe.com', $password, false); $this->twoFactorManager->expects($this->once()) ->method('isTwoFactorAuthenticated') ->with($user) @@ -628,6 +688,8 @@ class LoginControllerTest extends TestCase { $this->config->expects($this->once()) ->method('deleteUserValue') ->with('john', 'core', 'lostpassword'); + $this->userSession->expects($this->never()) + ->method('createRememberMeToken'); $expected = new RedirectResponse($challengeUrl); $this->assertEquals($expected, $this->loginController->tryLogin('john@doe.com', $password, null)); @@ -680,6 +742,8 @@ class LoginControllerTest extends TestCase { ->with('login', '192.168.0.1', ['user' => 'john@doe.com']); $this->config->expects($this->never()) ->method('deleteUserValue'); + $this->userSession->expects($this->never()) + ->method('createRememberMeToken'); $expected = new RedirectResponse(''); $this->assertEquals($expected, $this->loginController->tryLogin('john@doe.com', 'just wrong', null)); diff --git a/tests/lib/Authentication/Token/DefaultTokenMapperTest.php b/tests/lib/Authentication/Token/DefaultTokenMapperTest.php index d71d9468477..418a4d14f62 100644 --- a/tests/lib/Authentication/Token/DefaultTokenMapperTest.php +++ b/tests/lib/Authentication/Token/DefaultTokenMapperTest.php @@ -130,6 +130,7 @@ class DefaultTokenMapperTest extends TestCase { $token->setName('Firefox on Android'); $token->setToken('1504445f1524fc801035448a95681a9378ba2e83930c814546c56e5d6ebde221198792fd900c88ed5ead0555780dad1ebce3370d7e154941cd5de87eb419899b'); $token->setType(IToken::TEMPORARY_TOKEN); + $token->setRemember(IToken::DO_NOT_REMEMBER); $token->setLastActivity($this->time - 60 * 60 * 24 * 3); $token->setLastCheck($this->time - 10); diff --git a/tests/lib/Authentication/Token/DefaultTokenProviderTest.php b/tests/lib/Authentication/Token/DefaultTokenProviderTest.php index 7f90cf051f4..cd6bf7bad57 100644 --- a/tests/lib/Authentication/Token/DefaultTokenProviderTest.php +++ b/tests/lib/Authentication/Token/DefaultTokenProviderTest.php @@ -25,7 +25,6 @@ namespace Test\Authentication\Token; use OC\Authentication\Token\DefaultToken; use OC\Authentication\Token\DefaultTokenProvider; use OC\Authentication\Token\IToken; -use OCP\AppFramework\Db\DoesNotExistException; use OCP\AppFramework\Utility\ITimeFactory; use OCP\IConfig; use OCP\ILogger; @@ -81,6 +80,7 @@ class DefaultTokenProviderTest extends TestCase { $toInsert->setName($name); $toInsert->setToken(hash('sha512', $token . '1f4h9s')); $toInsert->setType($type); + $toInsert->setRemember(IToken::DO_NOT_REMEMBER); $toInsert->setLastActivity($this->time); $this->config->expects($this->any()) @@ -95,7 +95,7 @@ class DefaultTokenProviderTest extends TestCase { ->method('insert') ->with($this->equalTo($toInsert)); - $actual = $this->tokenProvider->generateToken($token, $uid, $user, $password, $name, $type); + $actual = $this->tokenProvider->generateToken($token, $uid, $user, $password, $name, $type, IToken::DO_NOT_REMEMBER); $this->assertEquals($toInsert, $actual); } @@ -245,13 +245,19 @@ class DefaultTokenProviderTest extends TestCase { public function testInvalidateOldTokens() { $defaultSessionLifetime = 60 * 60 * 24; - $this->config->expects($this->once()) + $defaultRememberMeLifetime = 60 * 60 * 24 * 15; + $this->config->expects($this->exactly(2)) ->method('getSystemValue') - ->with('session_lifetime', $defaultSessionLifetime) - ->will($this->returnValue(150)); - $this->mapper->expects($this->once()) + ->will($this->returnValueMap([ + ['session_lifetime', $defaultSessionLifetime, 150], + ['remember_login_cookie_lifetime', $defaultRememberMeLifetime, 300], + ])); + $this->mapper->expects($this->at(0)) ->method('invalidateOld') ->with($this->time - 150); + $this->mapper->expects($this->at(1)) + ->method('invalidateOld') + ->with($this->time - 300); $this->tokenProvider->invalidateOldTokens(); } diff --git a/tests/lib/Authentication/TwoFactorAuth/ManagerTest.php b/tests/lib/Authentication/TwoFactorAuth/ManagerTest.php index 72b70d817d2..52f3ca28500 100644 --- a/tests/lib/Authentication/TwoFactorAuth/ManagerTest.php +++ b/tests/lib/Authentication/TwoFactorAuth/ManagerTest.php @@ -233,8 +233,15 @@ class ManagerTest extends TestCase { ->with($this->user, $challenge) ->will($this->returnValue(true)); $this->session->expects($this->once()) + ->method('get') + ->with('two_factor_remember_login') + ->will($this->returnValue(false)); + $this->session->expects($this->at(1)) ->method('remove') ->with('two_factor_auth_uid'); + $this->session->expects($this->at(2)) + ->method('remove') + ->with('two_factor_remember_login'); $this->assertTrue($this->manager->verifyChallenge('email', $this->user, $challenge)); } @@ -304,11 +311,29 @@ class ManagerTest extends TestCase { ->method('getUID') ->will($this->returnValue('ferdinand')); - $this->session->expects($this->once()) + $this->session->expects($this->at(0)) + ->method('set') + ->with('two_factor_auth_uid', 'ferdinand'); + $this->session->expects($this->at(1)) + ->method('set') + ->with('two_factor_remember_login', true); + + $this->manager->prepareTwoFactorLogin($this->user, true); + } + + public function testPrepareTwoFactorLoginDontRemember() { + $this->user->expects($this->once()) + ->method('getUID') + ->will($this->returnValue('ferdinand')); + + $this->session->expects($this->at(0)) ->method('set') ->with('two_factor_auth_uid', 'ferdinand'); + $this->session->expects($this->at(1)) + ->method('set') + ->with('two_factor_remember_login', false); - $this->manager->prepareTwoFactorLogin($this->user); + $this->manager->prepareTwoFactorLogin($this->user, false); } } diff --git a/tests/lib/User/SessionTest.php b/tests/lib/User/SessionTest.php index 268d8e10e5a..c324870a60a 100644 --- a/tests/lib/User/SessionTest.php +++ b/tests/lib/User/SessionTest.php @@ -52,6 +52,8 @@ class SessionTest extends \Test\TestCase { $this->tokenProvider = $this->createMock(IProvider::class); $this->config = $this->createMock(IConfig::class); $this->throttler = $this->createMock(Throttler::class); + + \OC_User::setIncognitoMode(false); } public function testGetUser() { @@ -100,7 +102,7 @@ class SessionTest extends \Test\TestCase { ->method('updateTokenActivity') ->with($token); - $manager->expects($this->any()) + $manager->expects($this->once()) ->method('get') ->with($expectedUser->getUID()) ->will($this->returnValue($expectedUser)); @@ -181,16 +183,9 @@ class SessionTest extends \Test\TestCase { }, 'foo')); $managerMethods = get_class_methods(Manager::class); - //keep following methods intact in order to ensure hooks are - //working - $doNotMock = array('__construct', 'emit', 'listen'); - foreach ($doNotMock as $methodName) { - $i = array_search($methodName, $managerMethods, true); - if ($i !== false) { - unset($managerMethods[$i]); - } - } - $manager = $this->getMockBuilder(Manager::class) + //keep following methods intact in order to ensure hooks are working + $mockedManagerMethods = array_diff($managerMethods, ['__construct', 'emit', 'listen']); + $manager = $this->getMockBuilder(Manager::class)->setMethods($mockedManagerMethods)->getMock(); ->setMethods($managerMethods) ->setConstructorArgs([$this->config]) ->getMock(); @@ -238,17 +233,10 @@ class SessionTest extends \Test\TestCase { ->with('bar') ->will($this->throwException(new \OC\Authentication\Exceptions\InvalidTokenException())); - $managerMethods = get_class_methods('\OC\User\Manager'); - //keep following methods intact in order to ensure hooks are - //working - $doNotMock = array('__construct', 'emit', 'listen'); - foreach ($doNotMock as $methodName) { - $i = array_search($methodName, $managerMethods, true); - if ($i !== false) { - unset($managerMethods[$i]); - } - } - $manager = $this->getMockBuilder(Manager::class) + $managerMethods = get_class_methods(\OC\User\Manager::class); + //keep following methods intact in order to ensure hooks are working + $mockedManagerMethods = array_diff($managerMethods, ['__construct', 'emit', 'listen']); + $manager = $this->getMockBuilder(Manager::class)->setMethods($mockedManagerMethods)->getMock(); ->setMethods($managerMethods) ->setConstructorArgs([$this->config]) ->getMock(); @@ -273,17 +261,10 @@ class SessionTest extends \Test\TestCase { public function testLoginInvalidPassword() { $session = $this->getMockBuilder(Memory::class)->setConstructorArgs([''])->getMock(); - $managerMethods = get_class_methods('\OC\User\Manager'); - //keep following methods intact in order to ensure hooks are - //working - $doNotMock = array('__construct', 'emit', 'listen'); - foreach ($doNotMock as $methodName) { - $i = array_search($methodName, $managerMethods, true); - if ($i !== false) { - unset($managerMethods[$i]); - } - } - $manager = $this->getMockBuilder(Manager::class) + $managerMethods = get_class_methods(\OC\User\Manager::class); + //keep following methods intact in order to ensure hooks are working + $mockedManagerMethods = array_diff($managerMethods, ['__construct', 'emit', 'listen']); + $manager = $this->getMockBuilder(Manager::class)->setMethods($mockedManagerMethods)->getMock(); ->setMethods($managerMethods) ->setConstructorArgs([$this->config]) ->getMock(); @@ -513,156 +494,208 @@ class SessionTest extends \Test\TestCase { public function testRememberLoginValidToken() { $session = $this->getMockBuilder(Memory::class)->setConstructorArgs([''])->getMock(); - $session->expects($this->exactly(1)) - ->method('set') - ->with($this->callback(function ($key) { - switch ($key) { - case 'user_id': - return true; - default: - return false; - } - }, 'foo')); - $session->expects($this->once()) - ->method('regenerateId'); + $managerMethods = get_class_methods(\OC\User\Manager::class); + //keep following methods intact in order to ensure hooks are working + $mockedManagerMethods = array_diff($managerMethods, ['__construct', 'emit', 'listen']); + $manager = $this->getMockBuilder(Manager::class)->setMethods($mockedManagerMethods)->getMock(); + $userSession = $this->getMockBuilder(Session::class) + //override, otherwise tests will fail because of setcookie() + ->setMethods(['setMagicInCookie']) + ->setConstructorArgs([$manager, $session, $this->timeFactory, $this->tokenProvider, $this->config]) + ->getMock(); - $managerMethods = get_class_methods(Manager::class); - //keep following methods intact in order to ensure hooks are - //working - $doNotMock = array('__construct', 'emit', 'listen'); - foreach ($doNotMock as $methodName) { - $i = array_search($methodName, $managerMethods, true); - if ($i !== false) { - unset($managerMethods[$i]); - } - } - $manager = $this->getMockBuilder(Manager::class) + $user = $this->createMock(IUser::class); + $token = 'goodToken'; + $oldSessionId = 'sess321'; + $sessionId = 'sess123'; ->setMethods($managerMethods) ->setConstructorArgs([$this->config]) ->getMock(); - $backend = $this->createMock(\Test\Util\User\Dummy::class); + $session->expects($this->once()) + ->method('regenerateId'); + $manager->expects($this->once()) + ->method('get') + ->with('foo') + ->will($this->returnValue($user)); + $this->config->expects($this->once()) + ->method('getUserKeys') + ->with('foo', 'login_token') + ->will($this->returnValue([$token])); + $this->config->expects($this->once()) + ->method('deleteUserValue') + ->with('foo', 'login_token', $token); + $this->config->expects($this->once()) + ->method('setUserValue'); // TODO: mock new random value - $user = $this->getMockBuilder(User::class)->setConstructorArgs(['foo', $backend])->getMock(); + $session->expects($this->once()) + ->method('getId') + ->will($this->returnValue($sessionId)); + $this->tokenProvider->expects($this->once()) + ->method('renewSessionToken') + ->with($oldSessionId, $sessionId) + ->will($this->returnValue(true)); $user->expects($this->any()) ->method('getUID') ->will($this->returnValue('foo')); + $userSession->expects($this->once()) + ->method('setMagicInCookie'); $user->expects($this->once()) ->method('updateLastLoginTimestamp'); + $session->expects($this->once()) + ->method('set') + ->with('user_id', 'foo'); - $manager->expects($this->once()) - ->method('get') - ->with('foo') - ->will($this->returnValue($user)); + $granted = $userSession->loginWithCookie('foo', $token, $oldSessionId); - //prepare login token - $token = 'goodToken'; - \OC::$server->getConfig()->setUserValue('foo', 'login_token', $token, time()); + $this->assertTrue($granted); + } + public function testRememberLoginInvalidSessionToken() { + $session = $this->getMockBuilder(Memory::class)->setConstructorArgs([''])->getMock(); + $managerMethods = get_class_methods(\OC\User\Manager::class); + //keep following methods intact in order to ensure hooks are working + $mockedManagerMethods = array_diff($managerMethods, ['__construct', 'emit', 'listen']); + $manager = $this->getMockBuilder(Manager::class)->setMethods($mockedManagerMethods)->getMock(); $userSession = $this->getMockBuilder(Session::class) //override, otherwise tests will fail because of setcookie() ->setMethods(['setMagicInCookie']) - //there are passed as parameters to the constructor ->setConstructorArgs([$manager, $session, $this->timeFactory, $this->tokenProvider, $this->config]) ->getMock(); - $granted = $userSession->loginWithCookie('foo', $token); - - $this->assertSame($granted, true); - } + $user = $this->createMock(IUser::class); + $token = 'goodToken'; + $oldSessionId = 'sess321'; + $sessionId = 'sess123'; - public function testRememberLoginInvalidToken() { - $session = $this->getMockBuilder(Memory::class)->setConstructorArgs([''])->getMock(); - $session->expects($this->never()) - ->method('set'); $session->expects($this->once()) ->method('regenerateId'); + $manager->expects($this->once()) + ->method('get') + ->with('foo') + ->will($this->returnValue($user)); + $this->config->expects($this->once()) + ->method('getUserKeys') + ->with('foo', 'login_token') + ->will($this->returnValue([$token])); + $this->config->expects($this->once()) + ->method('deleteUserValue') + ->with('foo', 'login_token', $token); + $this->config->expects($this->once()) + ->method('setUserValue'); // TODO: mock new random value - $managerMethods = get_class_methods('\OC\User\Manager'); - //keep following methods intact in order to ensure hooks are - //working - $doNotMock = array('__construct', 'emit', 'listen'); - foreach ($doNotMock as $methodName) { - $i = array_search($methodName, $managerMethods, true); - if ($i !== false) { - unset($managerMethods[$i]); - } - } - $manager = $this->getMockBuilder(Manager::class) + $session->expects($this->once()) + ->method('getId') + ->will($this->returnValue($sessionId)); + $this->tokenProvider->expects($this->once()) + ->method('renewSessionToken') + ->with($oldSessionId, $sessionId) + ->will($this->throwException(new \OC\Authentication\Exceptions\InvalidTokenException())); ->setMethods($managerMethods) ->setConstructorArgs([$this->config]) ->getMock(); - $backend = $this->createMock(\Test\Util\User\Dummy::class); - - $user = $this->getMockBuilder(User::class)->setConstructorArgs(['foo', $backend])->getMock(); - - $user->expects($this->any()) + $user->expects($this->never()) ->method('getUID') ->will($this->returnValue('foo')); + $userSession->expects($this->never()) + ->method('setMagicInCookie'); $user->expects($this->never()) ->method('updateLastLoginTimestamp'); + $session->expects($this->never()) + ->method('set') + ->with('user_id', 'foo'); + + $granted = $userSession->loginWithCookie('foo', $token, $oldSessionId); + + $this->assertFalse($granted); + } + + public function testRememberLoginInvalidToken() { + $session = $this->getMockBuilder(Memory::class)->setConstructorArgs([''])->getMock(); + $managerMethods = get_class_methods(\OC\User\Manager::class); + //keep following methods intact in order to ensure hooks are working + $mockedManagerMethods = array_diff($managerMethods, ['__construct', 'emit', 'listen']); + $manager = $this->getMockBuilder(Manager::class)->setMethods($mockedManagerMethods)->getMock(); + $userSession = $this->getMockBuilder(Session::class) + //override, otherwise tests will fail because of setcookie() + ->setMethods(['setMagicInCookie']) + ->setConstructorArgs([$manager, $session, $this->timeFactory, $this->tokenProvider, $this->config]) + ->getMock(); + + $user = $this->createMock(IUser::class); + $token = 'goodToken'; + $oldSessionId = 'sess321'; + $session->expects($this->once()) + ->method('regenerateId'); $manager->expects($this->once()) ->method('get') ->with('foo') ->will($this->returnValue($user)); + $this->config->expects($this->once()) + ->method('getUserKeys') + ->with('foo', 'login_token') + ->will($this->returnValue(['anothertoken'])); + $this->config->expects($this->never()) + ->method('deleteUserValue') + ->with('foo', 'login_token', $token); + + $this->tokenProvider->expects($this->never()) + ->method('renewSessionToken'); + $userSession->expects($this->never()) + ->method('setMagicInCookie'); + $user->expects($this->never()) + ->method('updateLastLoginTimestamp'); + $session->expects($this->never()) + ->method('set') + ->with('user_id', 'foo'); - //prepare login token - $token = 'goodToken'; - \OC::$server->getConfig()->setUserValue('foo', 'login_token', $token, time()); - - $userSession = new \OC\User\Session($manager, $session, $this->timeFactory, $this->tokenProvider, $this->config); - $granted = $userSession->loginWithCookie('foo', 'badToken'); + $granted = $userSession->loginWithCookie('foo', $token, $oldSessionId); - $this->assertSame($granted, false); + $this->assertFalse($granted); } public function testRememberLoginInvalidUser() { $session = $this->getMockBuilder(Memory::class)->setConstructorArgs([''])->getMock(); - $session->expects($this->never()) - ->method('set'); + $managerMethods = get_class_methods(\OC\User\Manager::class); + //keep following methods intact in order to ensure hooks are working + $mockedManagerMethods = array_diff($managerMethods, ['__construct', 'emit', 'listen']); + $manager = $this->getMockBuilder(Manager::class)->setMethods($mockedManagerMethods)->getMock(); + $userSession = $this->getMockBuilder(Session::class) + //override, otherwise tests will fail because of setcookie() + ->setMethods(['setMagicInCookie']) + ->setConstructorArgs([$manager, $session, $this->timeFactory, $this->tokenProvider, $this->config]) + ->getMock(); + $token = 'goodToken'; + $oldSessionId = 'sess321'; + $session->expects($this->once()) ->method('regenerateId'); - - $managerMethods = get_class_methods('\OC\User\Manager'); - //keep following methods intact in order to ensure hooks are - //working - $doNotMock = array('__construct', 'emit', 'listen'); - foreach ($doNotMock as $methodName) { - $i = array_search($methodName, $managerMethods, true); - if ($i !== false) { - unset($managerMethods[$i]); - } - } - $manager = $this->getMockBuilder(Manager::class) ->setMethods($managerMethods) ->setConstructorArgs([$this->config]) ->getMock(); - - $backend = $this->createMock(\Test\Util\User\Dummy::class); - - $user = $this->getMockBuilder(User::class)->setConstructorArgs(['foo', $backend])->getMock(); - - $user->expects($this->never()) - ->method('getUID'); - $user->expects($this->never()) - ->method('updateLastLoginTimestamp'); - $manager->expects($this->once()) ->method('get') ->with('foo') ->will($this->returnValue(null)); + $this->config->expects($this->never()) + ->method('getUserKeys') + ->with('foo', 'login_token') + ->will($this->returnValue(['anothertoken'])); + + $this->tokenProvider->expects($this->never()) + ->method('renewSessionToken'); + $userSession->expects($this->never()) + ->method('setMagicInCookie'); + $session->expects($this->never()) + ->method('set') + ->with('user_id', 'foo'); - //prepare login token - $token = 'goodToken'; - \OC::$server->getConfig()->setUserValue('foo', 'login_token', $token, time()); - - $userSession = new \OC\User\Session($manager, $session, $this->timeFactory, $this->tokenProvider, $this->config); - $granted = $userSession->loginWithCookie('foo', $token); + $granted = $userSession->loginWithCookie('foo', $token, $oldSessionId); - $this->assertSame($granted, false); + $this->assertFalse($granted); } public function testActiveUserAfterSetSession() { -- cgit v1.2.3 From 6f86e468d4d9bcf50143e37905923d74ac423a7e Mon Sep 17 00:00:00 2001 From: Christoph Wurst Date: Mon, 31 Oct 2016 09:36:55 +0100 Subject: inject ISecureRandom into user session and use injected config too Signed-off-by: Christoph Wurst --- lib/private/Server.php | 2 +- lib/private/User/Session.php | 29 ++++++----- tests/lib/User/SessionTest.php | 109 +++++++++++++++++++++++------------------ 3 files changed, 79 insertions(+), 61 deletions(-) (limited to 'tests') diff --git a/lib/private/Server.php b/lib/private/Server.php index 7931e08074b..d911d603031 100644 --- a/lib/private/Server.php +++ b/lib/private/Server.php @@ -242,7 +242,7 @@ class Server extends ServerContainer implements IServerContainer { $defaultTokenProvider = null; } - $userSession = new \OC\User\Session($manager, $session, $timeFactory, $defaultTokenProvider, $c->getConfig()); + $userSession = new \OC\User\Session($manager, $session, $timeFactory, $defaultTokenProvider, $c->getConfig(), $c->getSecureRandom()); $userSession->listen('\OC\User', 'preCreateUser', function ($uid, $password) { \OC_Hook::emit('OC_User', 'pre_createUser', array('run' => true, 'uid' => $uid, 'password' => $password)); }); diff --git a/lib/private/User/Session.php b/lib/private/User/Session.php index 68f17747a08..7215cbe4188 100644 --- a/lib/private/User/Session.php +++ b/lib/private/User/Session.php @@ -48,6 +48,7 @@ use OCP\ISession; use OCP\IUser; use OCP\IUserManager; use OCP\IUserSession; +use OCP\Security\ISecureRandom; use OCP\Session\Exceptions\SessionNotAvailableException; use OCP\Util; @@ -89,23 +90,29 @@ class Session implements IUserSession, Emitter { /** @var User $activeUser */ protected $activeUser; + /** @var ISecureRandom */ + private $random; + /** * @param IUserManager $manager * @param ISession $session * @param ITimeFactory $timeFacory * @param IProvider $tokenProvider * @param IConfig $config + * @param ISecureRandom $random */ public function __construct(IUserManager $manager, ISession $session, ITimeFactory $timeFacory, $tokenProvider, - IConfig $config) { + IConfig $config, + ISecureRandom $random) { $this->manager = $manager; $this->session = $session; $this->timeFacory = $timeFacory; $this->tokenProvider = $tokenProvider; $this->config = $config; + $this->random = $random; } /** @@ -701,7 +708,7 @@ class Session implements IUserSession, Emitter { } // replace successfully used token with a new one $this->config->deleteUserValue($uid, 'login_token', $currentToken); - $newToken = OC::$server->getSecureRandom()->generate(32); + $newToken = $this->random->generate(32); $this->config->setUserValue($uid, 'login_token', $newToken, $this->timeFacory->getTime()); try { @@ -726,8 +733,8 @@ class Session implements IUserSession, Emitter { * @param IUser $user */ public function createRememberMeToken(IUser $user) { - $token = OC::$server->getSecureRandom()->generate(32); - $this->config->setUserValue($user->getUID(), 'login_token', $token, time()); + $token = $this->random->generate(32); + $this->config->setUserValue($user->getUID(), 'login_token', $token, $this->timeFacory->getTime()); $this->setMagicInCookie($user->getUID(), $token); } @@ -763,7 +770,7 @@ class Session implements IUserSession, Emitter { $webRoot = '/'; } - $expires = $this->timeFacory->getTime() + OC::$server->getConfig()->getSystemValue('remember_login_cookie_lifetime', 60 * 60 * 24 * 15); + $expires = $this->timeFacory->getTime() + $this->config->getSystemValue('remember_login_cookie_lifetime', 60 * 60 * 24 * 15); setcookie('nc_username', $username, $expires, $webRoot, '', $secureCookie, true); setcookie('nc_token', $token, $expires, $webRoot, '', $secureCookie, true); try { @@ -783,14 +790,14 @@ class Session implements IUserSession, Emitter { unset($_COOKIE['nc_username']); //TODO: DI unset($_COOKIE['nc_token']); unset($_COOKIE['nc_session_id']); - setcookie('nc_username', '', time() - 3600, OC::$WEBROOT, '', $secureCookie, true); - setcookie('nc_token', '', time() - 3600, OC::$WEBROOT, '', $secureCookie, true); - setcookie('nc_session_id', '', time() - 3600, OC::$WEBROOT, '', $secureCookie, true); + setcookie('nc_username', '', $this->timeFacory->getTime() - 3600, OC::$WEBROOT, '', $secureCookie, true); + setcookie('nc_token', '', $this->timeFacory->getTime() - 3600, OC::$WEBROOT, '', $secureCookie, true); + setcookie('nc_session_id', '', $this->timeFacory->getTime() - 3600, OC::$WEBROOT, '', $secureCookie, true); // old cookies might be stored under /webroot/ instead of /webroot // and Firefox doesn't like it! - setcookie('nc_username', '', time() - 3600, OC::$WEBROOT . '/', '', $secureCookie, true); - setcookie('nc_token', '', time() - 3600, OC::$WEBROOT . '/', '', $secureCookie, true); - setcookie('nc_session_id', '', time() - 3600, OC::$WEBROOT . '/', '', $secureCookie, true); + setcookie('nc_username', '', $this->timeFacory->getTime() - 3600, OC::$WEBROOT . '/', '', $secureCookie, true); + setcookie('nc_token', '', $this->timeFacory->getTime() - 3600, OC::$WEBROOT . '/', '', $secureCookie, true); + setcookie('nc_session_id', '', $this->timeFacory->getTime() - 3600, OC::$WEBROOT . '/', '', $secureCookie, true); } /** diff --git a/tests/lib/User/SessionTest.php b/tests/lib/User/SessionTest.php index c324870a60a..f4237e94cde 100644 --- a/tests/lib/User/SessionTest.php +++ b/tests/lib/User/SessionTest.php @@ -39,8 +39,10 @@ class SessionTest extends \Test\TestCase { protected $tokenProvider; /** @var IConfig|\PHPUnit_Framework_MockObject_MockObject */ private $config; - /** @var Throttler */ + /** @var Throttler|\PHPUnit_Framework_MockObject_MockObject */ private $throttler; + /** @var ISecureRandom|\PHPUnit_Framework_MockObject_MockObject */ + private $random; protected function setUp() { parent::setUp(); @@ -52,6 +54,7 @@ class SessionTest extends \Test\TestCase { $this->tokenProvider = $this->createMock(IProvider::class); $this->config = $this->createMock(IConfig::class); $this->throttler = $this->createMock(Throttler::class); + $this->random = $this->createMock(ISecureRandom::class); \OC_User::setIncognitoMode(false); } @@ -107,7 +110,7 @@ class SessionTest extends \Test\TestCase { ->with($expectedUser->getUID()) ->will($this->returnValue($expectedUser)); - $userSession = new \OC\User\Session($manager, $session, $this->timeFactory, $this->tokenProvider, $this->config); + $userSession = new \OC\User\Session($manager, $session, $this->timeFactory, $this->tokenProvider, $this->config, $this->random); $user = $userSession->getUser(); $this->assertSame($expectedUser, $user); $this->assertSame(10000, $token->getLastCheck()); @@ -129,7 +132,7 @@ class SessionTest extends \Test\TestCase { $manager = $this->createMock(Manager::class); $userSession = $this->getMockBuilder(Session::class) - ->setConstructorArgs([$manager, $session, $this->timeFactory, $this->tokenProvider, $this->config]) + ->setConstructorArgs([$manager, $session, $this->timeFactory, $this->tokenProvider, $this->config, $this->random]) ->setMethods([ 'getUser' ]) @@ -156,7 +159,7 @@ class SessionTest extends \Test\TestCase { ->method('getUID') ->will($this->returnValue('foo')); - $userSession = new \OC\User\Session($manager, $session, $this->timeFactory, $this->tokenProvider, $this->config); + $userSession = new \OC\User\Session($manager, $session, $this->timeFactory, $this->tokenProvider, $this->config, $this->random); $userSession->setUser($user); } @@ -185,8 +188,8 @@ class SessionTest extends \Test\TestCase { $managerMethods = get_class_methods(Manager::class); //keep following methods intact in order to ensure hooks are working $mockedManagerMethods = array_diff($managerMethods, ['__construct', 'emit', 'listen']); - $manager = $this->getMockBuilder(Manager::class)->setMethods($mockedManagerMethods)->getMock(); - ->setMethods($managerMethods) + $manager = $this->getMockBuilder(Manager::class) + ->setMethods($mockedManagerMethods) ->setConstructorArgs([$this->config]) ->getMock(); @@ -208,7 +211,7 @@ class SessionTest extends \Test\TestCase { ->will($this->returnValue($user)); $userSession = $this->getMockBuilder(Session::class) - ->setConstructorArgs([$manager, $session, $this->timeFactory, $this->tokenProvider, $this->config]) + ->setConstructorArgs([$manager, $session, $this->timeFactory, $this->tokenProvider, $this->config, $this->random]) ->setMethods([ 'prepareUserLogin' ]) @@ -236,8 +239,8 @@ class SessionTest extends \Test\TestCase { $managerMethods = get_class_methods(\OC\User\Manager::class); //keep following methods intact in order to ensure hooks are working $mockedManagerMethods = array_diff($managerMethods, ['__construct', 'emit', 'listen']); - $manager = $this->getMockBuilder(Manager::class)->setMethods($mockedManagerMethods)->getMock(); - ->setMethods($managerMethods) + $manager = $this->getMockBuilder(Manager::class) + ->setMethods($mockedManagerMethods) ->setConstructorArgs([$this->config]) ->getMock(); @@ -255,7 +258,7 @@ class SessionTest extends \Test\TestCase { ->with('foo', 'bar') ->will($this->returnValue($user)); - $userSession = new \OC\User\Session($manager, $session, $this->timeFactory, $this->tokenProvider, $this->config); + $userSession = new \OC\User\Session($manager, $session, $this->timeFactory, $this->tokenProvider, $this->config, $this->random); $userSession->login('foo', 'bar'); } @@ -264,12 +267,12 @@ class SessionTest extends \Test\TestCase { $managerMethods = get_class_methods(\OC\User\Manager::class); //keep following methods intact in order to ensure hooks are working $mockedManagerMethods = array_diff($managerMethods, ['__construct', 'emit', 'listen']); - $manager = $this->getMockBuilder(Manager::class)->setMethods($mockedManagerMethods)->getMock(); - ->setMethods($managerMethods) + $manager = $this->getMockBuilder(Manager::class) + ->setMethods($mockedManagerMethods) ->setConstructorArgs([$this->config]) ->getMock(); $backend = $this->createMock(\Test\Util\User\Dummy::class); - $userSession = new \OC\User\Session($manager, $session, $this->timeFactory, $this->tokenProvider, $this->config); + $userSession = new \OC\User\Session($manager, $session, $this->timeFactory, $this->tokenProvider, $this->config, $this->random); $user = $this->getMockBuilder(User::class)->setConstructorArgs(['foo', $backend])->getMock(); @@ -298,7 +301,7 @@ class SessionTest extends \Test\TestCase { public function testLoginNonExisting() { $session = $this->getMockBuilder(Memory::class)->setConstructorArgs([''])->getMock(); $manager = $this->createMock(Manager::class); - $userSession = new \OC\User\Session($manager, $session, $this->timeFactory, $this->tokenProvider, $this->config); + $userSession = new \OC\User\Session($manager, $session, $this->timeFactory, $this->tokenProvider, $this->config, $this->random); $session->expects($this->never()) ->method('set'); @@ -324,7 +327,7 @@ class SessionTest extends \Test\TestCase { public function testLoginWithDifferentTokenLoginName() { $session = $this->getMockBuilder(Memory::class)->setConstructorArgs([''])->getMock(); $manager = $this->createMock(Manager::class); - $userSession = new \OC\User\Session($manager, $session, $this->timeFactory, $this->tokenProvider, $this->config); + $userSession = new \OC\User\Session($manager, $session, $this->timeFactory, $this->tokenProvider, $this->config, $this->random); $username = 'user123'; $token = new \OC\Authentication\Token\DefaultToken(); $token->setLoginName($username); @@ -356,7 +359,7 @@ class SessionTest extends \Test\TestCase { /** @var \OC\User\Session $userSession */ $userSession = $this->getMockBuilder(Session::class) - ->setConstructorArgs([$manager, $session, $this->timeFactory, $this->tokenProvider, $this->config]) + ->setConstructorArgs([$manager, $session, $this->timeFactory, $this->tokenProvider, $this->config, $this->random]) ->setMethods(['login', 'supportsCookies', 'createSessionToken', 'getUser']) ->getMock(); @@ -392,7 +395,7 @@ class SessionTest extends \Test\TestCase { /** @var Session $userSession */ $userSession = $this->getMockBuilder(Session::class) - ->setConstructorArgs([$manager, $session, $this->timeFactory, $this->tokenProvider, $this->config]) + ->setConstructorArgs([$manager, $session, $this->timeFactory, $this->tokenProvider, $this->config, $this->random]) ->setMethods(['login', 'supportsCookies', 'createSessionToken', 'getUser']) ->getMock(); @@ -415,7 +418,7 @@ class SessionTest extends \Test\TestCase { /** @var \OC\User\Session $userSession */ $userSession = $this->getMockBuilder(Session::class) - ->setConstructorArgs([$manager, $session, $this->timeFactory, $this->tokenProvider, $this->config]) + ->setConstructorArgs([$manager, $session, $this->timeFactory, $this->tokenProvider, $this->config, $this->random]) ->setMethods(['isTokenPassword', 'login', 'supportsCookies', 'createSessionToken', 'getUser']) ->getMock(); @@ -457,7 +460,7 @@ class SessionTest extends \Test\TestCase { /** @var \OC\User\Session $userSession */ $userSession = $this->getMockBuilder(Session::class) - ->setConstructorArgs([$manager, $session, $this->timeFactory, $this->tokenProvider, $this->config]) + ->setConstructorArgs([$manager, $session, $this->timeFactory, $this->tokenProvider, $this->config, $this->random]) ->setMethods(['login', 'isTwoFactorEnforced']) ->getMock(); @@ -497,20 +500,20 @@ class SessionTest extends \Test\TestCase { $managerMethods = get_class_methods(\OC\User\Manager::class); //keep following methods intact in order to ensure hooks are working $mockedManagerMethods = array_diff($managerMethods, ['__construct', 'emit', 'listen']); - $manager = $this->getMockBuilder(Manager::class)->setMethods($mockedManagerMethods)->getMock(); + $manager = $this->getMockBuilder(Manager::class) + ->setMethods($mockedManagerMethods) + ->setConstructorArgs([$this->config]) + ->getMock(); $userSession = $this->getMockBuilder(Session::class) //override, otherwise tests will fail because of setcookie() ->setMethods(['setMagicInCookie']) - ->setConstructorArgs([$manager, $session, $this->timeFactory, $this->tokenProvider, $this->config]) + ->setConstructorArgs([$manager, $session, $this->timeFactory, $this->tokenProvider, $this->config, $this->random]) ->getMock(); $user = $this->createMock(IUser::class); $token = 'goodToken'; $oldSessionId = 'sess321'; $sessionId = 'sess123'; - ->setMethods($managerMethods) - ->setConstructorArgs([$this->config]) - ->getMock(); $session->expects($this->once()) ->method('regenerateId'); @@ -525,8 +528,13 @@ class SessionTest extends \Test\TestCase { $this->config->expects($this->once()) ->method('deleteUserValue') ->with('foo', 'login_token', $token); + $this->random->expects($this->once()) + ->method('generate') + ->with(32) + ->will($this->returnValue('abcdefg123456')); $this->config->expects($this->once()) - ->method('setUserValue'); // TODO: mock new random value + ->method('setUserValue') + ->with('foo', 'login_token', 'abcdefg123456', 10000); $session->expects($this->once()) ->method('getId') @@ -557,11 +565,14 @@ class SessionTest extends \Test\TestCase { $managerMethods = get_class_methods(\OC\User\Manager::class); //keep following methods intact in order to ensure hooks are working $mockedManagerMethods = array_diff($managerMethods, ['__construct', 'emit', 'listen']); - $manager = $this->getMockBuilder(Manager::class)->setMethods($mockedManagerMethods)->getMock(); + $manager = $this->getMockBuilder(Manager::class) + ->setMethods($mockedManagerMethods) + ->setConstructorArgs([$this->config]) + ->getMock(); $userSession = $this->getMockBuilder(Session::class) //override, otherwise tests will fail because of setcookie() ->setMethods(['setMagicInCookie']) - ->setConstructorArgs([$manager, $session, $this->timeFactory, $this->tokenProvider, $this->config]) + ->setConstructorArgs([$manager, $session, $this->timeFactory, $this->tokenProvider, $this->config, $this->random]) ->getMock(); $user = $this->createMock(IUser::class); @@ -592,9 +603,6 @@ class SessionTest extends \Test\TestCase { ->method('renewSessionToken') ->with($oldSessionId, $sessionId) ->will($this->throwException(new \OC\Authentication\Exceptions\InvalidTokenException())); - ->setMethods($managerMethods) - ->setConstructorArgs([$this->config]) - ->getMock(); $user->expects($this->never()) ->method('getUID') @@ -617,11 +625,14 @@ class SessionTest extends \Test\TestCase { $managerMethods = get_class_methods(\OC\User\Manager::class); //keep following methods intact in order to ensure hooks are working $mockedManagerMethods = array_diff($managerMethods, ['__construct', 'emit', 'listen']); - $manager = $this->getMockBuilder(Manager::class)->setMethods($mockedManagerMethods)->getMock(); + $manager = $this->getMockBuilder(Manager::class) + ->setMethods($mockedManagerMethods) + ->setConstructorArgs([$this->config]) + ->getMock(); $userSession = $this->getMockBuilder(Session::class) //override, otherwise tests will fail because of setcookie() ->setMethods(['setMagicInCookie']) - ->setConstructorArgs([$manager, $session, $this->timeFactory, $this->tokenProvider, $this->config]) + ->setConstructorArgs([$manager, $session, $this->timeFactory, $this->tokenProvider, $this->config, $this->random]) ->getMock(); $user = $this->createMock(IUser::class); @@ -662,20 +673,20 @@ class SessionTest extends \Test\TestCase { $managerMethods = get_class_methods(\OC\User\Manager::class); //keep following methods intact in order to ensure hooks are working $mockedManagerMethods = array_diff($managerMethods, ['__construct', 'emit', 'listen']); - $manager = $this->getMockBuilder(Manager::class)->setMethods($mockedManagerMethods)->getMock(); + $manager = $this->getMockBuilder(Manager::class) + ->setMethods($mockedManagerMethods) + ->setConstructorArgs([$this->config]) + ->getMock(); $userSession = $this->getMockBuilder(Session::class) //override, otherwise tests will fail because of setcookie() ->setMethods(['setMagicInCookie']) - ->setConstructorArgs([$manager, $session, $this->timeFactory, $this->tokenProvider, $this->config]) + ->setConstructorArgs([$manager, $session, $this->timeFactory, $this->tokenProvider, $this->config, $this->random]) ->getMock(); $token = 'goodToken'; $oldSessionId = 'sess321'; $session->expects($this->once()) ->method('regenerateId'); - ->setMethods($managerMethods) - ->setConstructorArgs([$this->config]) - ->getMock(); $manager->expects($this->once()) ->method('get') ->with('foo') @@ -717,7 +728,7 @@ class SessionTest extends \Test\TestCase { $session = new Memory(''); $session->set('user_id', 'foo'); $userSession = $this->getMockBuilder('\OC\User\Session') - ->setConstructorArgs([$manager, $session, $this->timeFactory, $this->tokenProvider, $this->config]) + ->setConstructorArgs([$manager, $session, $this->timeFactory, $this->tokenProvider, $this->config, $this->random]) ->setMethods([ 'validateSession' ]) @@ -738,7 +749,7 @@ class SessionTest extends \Test\TestCase { $session = $this->createMock(ISession::class); $token = $this->createMock(IToken::class); $user = $this->createMock(IUser::class); - $userSession = new \OC\User\Session($manager, $session, $this->timeFactory, $this->tokenProvider, $this->config); + $userSession = new \OC\User\Session($manager, $session, $this->timeFactory, $this->tokenProvider, $this->config, $this->random); $random = $this->createMock(ISecureRandom::class); $config = $this->createMock(IConfig::class); @@ -782,7 +793,7 @@ class SessionTest extends \Test\TestCase { $session = $this->createMock(ISession::class); $token = $this->createMock(IToken::class); $user = $this->createMock(IUser::class); - $userSession = new \OC\User\Session($manager, $session, $this->timeFactory, $this->tokenProvider, $this->config); + $userSession = new \OC\User\Session($manager, $session, $this->timeFactory, $this->tokenProvider, $this->config, $this->random); $random = $this->createMock(ISecureRandom::class); $config = $this->createMock(IConfig::class); @@ -829,7 +840,7 @@ class SessionTest extends \Test\TestCase { ->disableOriginalConstructor() ->getMock(); $session = $this->createMock(ISession::class); - $userSession = new \OC\User\Session($manager, $session, $this->timeFactory, $this->tokenProvider, $this->config); + $userSession = new \OC\User\Session($manager, $session, $this->timeFactory, $this->tokenProvider, $this->config, $this->random); $request = $this->createMock(IRequest::class); $uid = 'user123'; @@ -859,7 +870,7 @@ class SessionTest extends \Test\TestCase { $user = $this->createMock(IUser::class); $userSession = $this->getMockBuilder('\OC\User\Session') ->setMethods(['logout']) - ->setConstructorArgs([$manager, $session, $this->timeFactory, $this->tokenProvider, $this->config]) + ->setConstructorArgs([$manager, $session, $this->timeFactory, $this->tokenProvider, $this->config, $this->random]) ->getMock(); $request = $this->createMock(IRequest::class); @@ -888,7 +899,7 @@ class SessionTest extends \Test\TestCase { $timeFactory = $this->createMock(ITimeFactory::class); $tokenProvider = $this->createMock(IProvider::class); $userSession = $this->getMockBuilder('\OC\User\Session') - ->setConstructorArgs([$userManager, $session, $timeFactory, $tokenProvider, $this->config]) + ->setConstructorArgs([$userManager, $session, $timeFactory, $tokenProvider, $this->config, $this->random]) ->setMethods(['logout']) ->getMock(); @@ -935,7 +946,7 @@ class SessionTest extends \Test\TestCase { $timeFactory = $this->createMock(ITimeFactory::class); $tokenProvider = $this->createMock(IProvider::class); $userSession = $this->getMockBuilder('\OC\User\Session') - ->setConstructorArgs([$userManager, $session, $timeFactory, $tokenProvider, $this->config]) + ->setConstructorArgs([$userManager, $session, $timeFactory, $tokenProvider, $this->config, $this->random]) ->setMethods(['logout']) ->getMock(); @@ -969,7 +980,7 @@ class SessionTest extends \Test\TestCase { $session = $this->createMock(ISession::class); $timeFactory = $this->createMock(ITimeFactory::class); $tokenProvider = $this->createMock(IProvider::class); - $userSession = new \OC\User\Session($userManager, $session, $timeFactory, $tokenProvider, $this->config); + $userSession = new \OC\User\Session($userManager, $session, $timeFactory, $tokenProvider, $this->config, $this->random); $password = '123456'; $sessionId = 'session1234'; @@ -994,7 +1005,7 @@ class SessionTest extends \Test\TestCase { $session = $this->createMock(ISession::class); $timeFactory = $this->createMock(ITimeFactory::class); $tokenProvider = $this->createMock(IProvider::class); - $userSession = new \OC\User\Session($userManager, $session, $timeFactory, $tokenProvider, $this->config); + $userSession = new \OC\User\Session($userManager, $session, $timeFactory, $tokenProvider, $this->config, $this->random); $session->expects($this->once()) ->method('getId') @@ -1008,7 +1019,7 @@ class SessionTest extends \Test\TestCase { $session = $this->createMock(ISession::class); $timeFactory = $this->createMock(ITimeFactory::class); $tokenProvider = $this->createMock(IProvider::class); - $userSession = new \OC\User\Session($userManager, $session, $timeFactory, $tokenProvider, $this->config); + $userSession = new \OC\User\Session($userManager, $session, $timeFactory, $tokenProvider, $this->config, $this->random); $password = '123456'; $sessionId = 'session1234'; @@ -1048,7 +1059,7 @@ class SessionTest extends \Test\TestCase { $tokenProvider = new DefaultTokenProvider($mapper, $crypto, $this->config, $logger, $this->timeFactory); /** @var \OC\User\Session $userSession */ - $userSession = new Session($manager, $session, $this->timeFactory, $tokenProvider, $this->config); + $userSession = new Session($manager, $session, $this->timeFactory, $tokenProvider, $this->config, $this->random); $mapper->expects($this->any()) ->method('getToken') @@ -1098,7 +1109,7 @@ class SessionTest extends \Test\TestCase { $tokenProvider = new DefaultTokenProvider($mapper, $crypto, $this->config, $logger, $this->timeFactory); /** @var \OC\User\Session $userSession */ - $userSession = new Session($manager, $session, $this->timeFactory, $tokenProvider, $this->config); + $userSession = new Session($manager, $session, $this->timeFactory, $tokenProvider, $this->config, $this->random); $mapper->expects($this->any()) ->method('getToken') -- cgit v1.2.3 From 9d6e01ef40f7f4d2acab653b33e1af026bcde6c7 Mon Sep 17 00:00:00 2001 From: Lukas Reschke Date: Wed, 2 Nov 2016 13:37:39 +0100 Subject: Add missing tests and fix PHPDoc Signed-off-by: Lukas Reschke --- .../Authentication/Token/DefaultTokenProvider.php | 9 +- lib/private/Authentication/Token/IProvider.php | 1 + .../Token/DefaultTokenProviderTest.php | 118 ++++++++++++++++++++- tests/lib/User/SessionTest.php | 45 +++++++- 4 files changed, 169 insertions(+), 4 deletions(-) (limited to 'tests') diff --git a/lib/private/Authentication/Token/DefaultTokenProvider.php b/lib/private/Authentication/Token/DefaultTokenProvider.php index af1d600e4c3..87f434c684c 100644 --- a/lib/private/Authentication/Token/DefaultTokenProvider.php +++ b/lib/private/Authentication/Token/DefaultTokenProvider.php @@ -1,6 +1,7 @@ * * @author Christoph Wurst * @@ -56,7 +57,11 @@ class DefaultTokenProvider implements IProvider { * @param ILogger $logger * @param ITimeFactory $time */ - public function __construct(DefaultTokenMapper $mapper, ICrypto $crypto, IConfig $config, ILogger $logger, ITimeFactory $time) { + public function __construct(DefaultTokenMapper $mapper, + ICrypto $crypto, + IConfig $config, + ILogger $logger, + ITimeFactory $time) { $this->mapper = $mapper; $this->crypto = $crypto; $this->config = $config; @@ -98,6 +103,7 @@ class DefaultTokenProvider implements IProvider { * Save the updated token * * @param IToken $token + * @throws InvalidTokenException */ public function updateToken(IToken $token) { if (!($token instanceof DefaultToken)) { @@ -156,6 +162,7 @@ class DefaultTokenProvider implements IProvider { /** * @param string $oldSessionId * @param string $sessionId + * @throws InvalidTokenException */ public function renewSessionToken($oldSessionId, $sessionId) { $token = $this->getToken($oldSessionId); diff --git a/lib/private/Authentication/Token/IProvider.php b/lib/private/Authentication/Token/IProvider.php index b8c15571df1..6260555470d 100644 --- a/lib/private/Authentication/Token/IProvider.php +++ b/lib/private/Authentication/Token/IProvider.php @@ -55,6 +55,7 @@ interface IProvider { /** * @param string $oldSessionId * @param string $sessionId + * @throws InvalidTokenException */ public function renewSessionToken($oldSessionId, $sessionId); diff --git a/tests/lib/Authentication/Token/DefaultTokenProviderTest.php b/tests/lib/Authentication/Token/DefaultTokenProviderTest.php index cd6bf7bad57..5e4d4f94366 100644 --- a/tests/lib/Authentication/Token/DefaultTokenProviderTest.php +++ b/tests/lib/Authentication/Token/DefaultTokenProviderTest.php @@ -1,8 +1,8 @@ * + * @copyright Copyright (c) 2016, Lukas Reschke * @copyright Copyright (c) 2016, ownCloud, Inc. * @license AGPL-3.0 * @@ -25,6 +25,7 @@ namespace Test\Authentication\Token; use OC\Authentication\Token\DefaultToken; use OC\Authentication\Token\DefaultTokenProvider; use OC\Authentication\Token\IToken; +use OCP\AppFramework\Db\Mapper; use OCP\AppFramework\Utility\ITimeFactory; use OCP\IConfig; use OCP\ILogger; @@ -34,13 +35,19 @@ use Test\TestCase; class DefaultTokenProviderTest extends TestCase { - /** @var DefaultTokenProvider */ + /** @var DefaultTokenProvider|\PHPUnit_Framework_MockObject_MockObject */ private $tokenProvider; + /** @var Mapper|\PHPUnit_Framework_MockObject_MockObject */ private $mapper; + /** @var ICrypto|\PHPUnit_Framework_MockObject_MockObject */ private $crypto; + /** @var IConfig|\PHPUnit_Framework_MockObject_MockObject */ private $config; + /** @var ILogger|\PHPUnit_Framework_MockObject_MockObject */ private $logger; + /** @var ITimeFactory|\PHPUnit_Framework_MockObject_MockObject */ private $timeFactory; + /** @var int */ private $time; protected function setUp() { @@ -262,4 +269,111 @@ class DefaultTokenProviderTest extends TestCase { $this->tokenProvider->invalidateOldTokens(); } + public function testRenewSessionTokenWithoutPassword() { + $token = $this->getMockBuilder(DefaultToken::class) + ->disableOriginalConstructor() + ->setMethods(['getUID', 'getLoginName', 'getPassword', 'getName']) + ->getMock(); + $token + ->expects($this->at(0)) + ->method('getUID') + ->willReturn('UserUid'); + $token + ->expects($this->at(1)) + ->method('getLoginName') + ->willReturn('UserLoginName'); + $token + ->expects($this->at(2)) + ->method('getPassword') + ->willReturn(null); + $token + ->expects($this->at(3)) + ->method('getName') + ->willReturn('MyTokenName'); + $this->config + ->expects($this->exactly(2)) + ->method('getSystemValue') + ->with('secret') + ->willReturn('MyInstanceSecret'); + $this->mapper + ->expects($this->at(0)) + ->method('getToken') + ->with(hash('sha512', 'oldId' . 'MyInstanceSecret')) + ->willReturn($token); + $newToken = new DefaultToken(); + $newToken->setUid('UserUid'); + $newToken->setLoginName('UserLoginName'); + $newToken->setName('MyTokenName'); + $newToken->setToken(hash('sha512', 'newId' . 'MyInstanceSecret')); + $newToken->setType(IToken::TEMPORARY_TOKEN); + $newToken->setLastActivity(1313131); + $this->mapper + ->expects($this->at(1)) + ->method('insert') + ->with($newToken); + + $this->tokenProvider->renewSessionToken('oldId', 'newId'); + } + + public function testRenewSessionTokenWithPassword() { + $token = $this->getMockBuilder(DefaultToken::class) + ->disableOriginalConstructor() + ->setMethods(['getUID', 'getLoginName', 'getPassword', 'getName']) + ->getMock(); + $token + ->expects($this->at(0)) + ->method('getUID') + ->willReturn('UserUid'); + $token + ->expects($this->at(1)) + ->method('getLoginName') + ->willReturn('UserLoginName'); + $token + ->expects($this->at(2)) + ->method('getPassword') + ->willReturn('EncryptedPassword'); + $token + ->expects($this->at(3)) + ->method('getPassword') + ->willReturn('EncryptedPassword'); + $token + ->expects($this->at(4)) + ->method('getName') + ->willReturn('MyTokenName'); + $this->crypto + ->expects($this->any(0)) + ->method('decrypt') + ->with('EncryptedPassword', 'oldIdMyInstanceSecret') + ->willReturn('ClearTextPassword'); + $this->crypto + ->expects($this->any(1)) + ->method('encrypt') + ->with('ClearTextPassword', 'newIdMyInstanceSecret') + ->willReturn('EncryptedPassword'); + $this->config + ->expects($this->exactly(4)) + ->method('getSystemValue') + ->with('secret') + ->willReturn('MyInstanceSecret'); + $this->mapper + ->expects($this->at(0)) + ->method('getToken') + ->with(hash('sha512', 'oldId' . 'MyInstanceSecret')) + ->willReturn($token); + $newToken = new DefaultToken(); + $newToken->setUid('UserUid'); + $newToken->setLoginName('UserLoginName'); + $newToken->setName('MyTokenName'); + $newToken->setToken(hash('sha512', 'newId' . 'MyInstanceSecret')); + $newToken->setType(IToken::TEMPORARY_TOKEN); + $newToken->setLastActivity(1313131); + $newToken->setPassword('EncryptedPassword'); + $this->mapper + ->expects($this->at(1)) + ->method('insert') + ->with($newToken); + + $this->tokenProvider->renewSessionToken('oldId', 'newId'); + } + } diff --git a/tests/lib/User/SessionTest.php b/tests/lib/User/SessionTest.php index f4237e94cde..ee9ed737cf5 100644 --- a/tests/lib/User/SessionTest.php +++ b/tests/lib/User/SessionTest.php @@ -1,5 +1,4 @@ * This file is licensed under the Affero General Public License version 3 or @@ -43,6 +42,12 @@ class SessionTest extends \Test\TestCase { private $throttler; /** @var ISecureRandom|\PHPUnit_Framework_MockObject_MockObject */ private $random; + /** @var IUserManager|\PHPUnit_Framework_MockObject_MockObject */ + private $manager; + /** @var ISession|\PHPUnit_Framework_MockObject_MockObject */ + private $session; + /** @var Session|\PHPUnit_Framework_MockObject_MockObject */ + private $userSession; protected function setUp() { parent::setUp(); @@ -55,6 +60,21 @@ class SessionTest extends \Test\TestCase { $this->config = $this->createMock(IConfig::class); $this->throttler = $this->createMock(Throttler::class); $this->random = $this->createMock(ISecureRandom::class); + $this->manager = $this->createMock(IUserManager::class); + $this->session = $this->createMock(ISession::class); + $this->userSession = $this->getMockBuilder(Session::class) + ->setConstructorArgs([ + $this->manager, + $this->session, + $this->timeFactory, + $this->tokenProvider, + $this->config, + $this->random, + ]) + ->setMethods([ + 'setMagicInCookie', + ]) + ->getMock(); \OC_User::setIncognitoMode(false); } @@ -1136,4 +1156,27 @@ class SessionTest extends \Test\TestCase { $userSession->logClientIn('john', 'doe', $request, $this->throttler); } + + public function testCreateRememberMeToken() { + $user = $this->createMock(IUser::class); + $user + ->expects($this->exactly(2)) + ->method('getUID') + ->willReturn('UserUid'); + $this->random + ->expects($this->once()) + ->method('generate') + ->with(32) + ->willReturn('LongRandomToken'); + $this->config + ->expects($this->once()) + ->method('setUserValue') + ->with('UserUid', 'login_token', 'LongRandomToken', 10000); + $this->userSession + ->expects($this->once()) + ->method('setMagicInCookie') + ->with('UserUid', 'LongRandomToken'); + + $this->userSession->createRememberMeToken($user); + } } -- cgit v1.2.3