Merge pull request #2044 from nextcloud/login-credential-store
Login credential storepull/3333/head
commit
5bad417e57
@ -0,0 +1,72 @@ |
||||
<?php |
||||
|
||||
/** |
||||
* @copyright 2016 Christoph Wurst <christoph@winzerhof-wurst.at> |
||||
* |
||||
* @author 2016 Christoph Wurst <christoph@winzerhof-wurst.at> |
||||
* |
||||
* @license GNU AGPL version 3 or any later version |
||||
* |
||||
* This program is free software: you can redistribute it and/or modify |
||||
* it under the terms of the GNU Affero General Public License as |
||||
* published by the Free Software Foundation, either version 3 of the |
||||
* License, or (at your option) any later version. |
||||
* |
||||
* 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 |
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||
* |
||||
*/ |
||||
|
||||
namespace OC\Authentication\LoginCredentials; |
||||
|
||||
use OCP\Authentication\LoginCredentials\ICredentials; |
||||
|
||||
class Credentials implements ICredentials { |
||||
|
||||
/** @var string */ |
||||
private $uid; |
||||
|
||||
/** @var string */ |
||||
private $loginName; |
||||
|
||||
/** @var string */ |
||||
private $password; |
||||
|
||||
/** |
||||
* @param string $uid |
||||
* @param string $loginName |
||||
* @param string $password |
||||
*/ |
||||
public function __construct($uid, $loginName, $password) { |
||||
$this->uid = $uid; |
||||
$this->loginName = $loginName; |
||||
$this->password = $password; |
||||
} |
||||
|
||||
/** |
||||
* @return string |
||||
*/ |
||||
public function getUID() { |
||||
return $this->uid; |
||||
} |
||||
|
||||
/** |
||||
* @return string |
||||
*/ |
||||
public function getLoginName() { |
||||
return $this->loginName; |
||||
} |
||||
|
||||
/** |
||||
* @return string |
||||
*/ |
||||
public function getPassword() { |
||||
return $this->password; |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,120 @@ |
||||
<?php |
||||
|
||||
/** |
||||
* @copyright 2016 Christoph Wurst <christoph@winzerhof-wurst.at> |
||||
* |
||||
* @author 2016 Christoph Wurst <christoph@winzerhof-wurst.at> |
||||
* |
||||
* @license GNU AGPL version 3 or any later version |
||||
* |
||||
* This program is free software: you can redistribute it and/or modify |
||||
* it under the terms of the GNU Affero General Public License as |
||||
* published by the Free Software Foundation, either version 3 of the |
||||
* License, or (at your option) any later version. |
||||
* |
||||
* 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 |
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||
* |
||||
*/ |
||||
|
||||
namespace OC\Authentication\LoginCredentials; |
||||
|
||||
use OC\Authentication\Exceptions\InvalidTokenException; |
||||
use OC\Authentication\Exceptions\PasswordlessTokenException; |
||||
use OC\Authentication\Token\IProvider; |
||||
use OCP\Authentication\Exceptions\CredentialsUnavailableException; |
||||
use OCP\Authentication\LoginCredentials\ICredentials; |
||||
use OCP\Authentication\LoginCredentials\IStore; |
||||
use OCP\ILogger; |
||||
use OCP\ISession; |
||||
use OCP\Session\Exceptions\SessionNotAvailableException; |
||||
use OCP\Util; |
||||
|
||||
class Store implements IStore { |
||||
|
||||
/** @var ISession */ |
||||
private $session; |
||||
|
||||
/** @var ILogger */ |
||||
private $logger; |
||||
|
||||
/** @var IProvider|null */ |
||||
private $tokenProvider; |
||||
|
||||
/** |
||||
* @param ISession $session |
||||
* @param ILogger $logger |
||||
* @param IProvider $tokenProvider |
||||
*/ |
||||
public function __construct(ISession $session, ILogger $logger, IProvider $tokenProvider = null) { |
||||
$this->session = $session; |
||||
$this->logger = $logger; |
||||
$this->tokenProvider = $tokenProvider; |
||||
|
||||
Util::connectHook('OC_User', 'post_login', $this, 'authenticate'); |
||||
} |
||||
|
||||
/** |
||||
* Hook listener on post login |
||||
* |
||||
* @param array $params |
||||
*/ |
||||
public function authenticate(array $params) { |
||||
$this->session->set('login_credentials', json_encode($params)); |
||||
} |
||||
|
||||
/** |
||||
* Replace the session implementation |
||||
* |
||||
* @param ISession $session |
||||
*/ |
||||
public function setSession(ISession $session) { |
||||
$this->session = $session; |
||||
} |
||||
|
||||
/** |
||||
* @since 12 |
||||
* |
||||
* @return ICredentials the login credentials of the current user |
||||
* @throws CredentialsUnavailableException |
||||
*/ |
||||
public function getLoginCredentials() { |
||||
if (is_null($this->tokenProvider)) { |
||||
throw new CredentialsUnavailableException(); |
||||
} |
||||
|
||||
$trySession = false; |
||||
try { |
||||
$sessionId = $this->session->getId(); |
||||
$token = $this->tokenProvider->getToken($sessionId); |
||||
|
||||
$uid = $token->getUID(); |
||||
$user = $token->getLoginName(); |
||||
$password = $this->tokenProvider->getPassword($token, $sessionId); |
||||
|
||||
return new Credentials($uid, $user, $password); |
||||
} catch (SessionNotAvailableException $ex) { |
||||
$this->logger->debug('could not get login credentials because session is unavailable', ['app' => 'core']); |
||||
} catch (InvalidTokenException $ex) { |
||||
$this->logger->debug('could not get login credentials because the token is invalid', ['app' => 'core']); |
||||
$trySession = true; |
||||
} catch (PasswordlessTokenException $ex) { |
||||
$this->logger->debug('could not get login credentials because the token has no password', ['app' => 'core']); |
||||
$trySession = true; |
||||
} |
||||
|
||||
if ($trySession && $this->session->exists('login_credentials')) { |
||||
$creds = json_decode($this->session->get('login_credentials')); |
||||
return new Credentials($creds->uid, $creds->uid, $creds->password); |
||||
} |
||||
|
||||
// If we reach this line, an exception was thrown. |
||||
throw new CredentialsUnavailableException(); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,34 @@ |
||||
<?php |
||||
|
||||
/** |
||||
* @copyright 2016 Christoph Wurst <christoph@winzerhof-wurst.at> |
||||
* |
||||
* @author 2016 Christoph Wurst <christoph@winzerhof-wurst.at> |
||||
* |
||||
* @license GNU AGPL version 3 or any later version |
||||
* |
||||
* This program is free software: you can redistribute it and/or modify |
||||
* it under the terms of the GNU Affero General Public License as |
||||
* published by the Free Software Foundation, either version 3 of the |
||||
* License, or (at your option) any later version. |
||||
* |
||||
* 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 |
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||
* |
||||
*/ |
||||
|
||||
namespace OCP\Authentication\Exceptions; |
||||
|
||||
use Exception; |
||||
|
||||
/** |
||||
* @since 12 |
||||
*/ |
||||
class CredentialsUnavailableException extends Exception { |
||||
|
||||
} |
||||
@ -0,0 +1,58 @@ |
||||
<?php |
||||
|
||||
/** |
||||
* @copyright 2016 Christoph Wurst <christoph@winzerhof-wurst.at> |
||||
* |
||||
* @author 2016 Christoph Wurst <christoph@winzerhof-wurst.at> |
||||
* |
||||
* @license GNU AGPL version 3 or any later version |
||||
* |
||||
* This program is free software: you can redistribute it and/or modify |
||||
* it under the terms of the GNU Affero General Public License as |
||||
* published by the Free Software Foundation, either version 3 of the |
||||
* License, or (at your option) any later version. |
||||
* |
||||
* 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 |
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||
* |
||||
*/ |
||||
|
||||
namespace OCP\Authentication\LoginCredentials; |
||||
|
||||
/** |
||||
* @since 12 |
||||
*/ |
||||
interface ICredentials { |
||||
|
||||
/** |
||||
* Get the user UID |
||||
* |
||||
* @since 12 |
||||
* |
||||
* @return string |
||||
*/ |
||||
public function getUID(); |
||||
|
||||
/** |
||||
* Get the login name the users used to login |
||||
* |
||||
* @since 12 |
||||
* |
||||
* @return string |
||||
*/ |
||||
public function getLoginName(); |
||||
|
||||
/** |
||||
* Get the password |
||||
* |
||||
* @since 12 |
||||
* |
||||
* @return string |
||||
*/ |
||||
public function getPassword(); |
||||
} |
||||
@ -0,0 +1,44 @@ |
||||
<?php |
||||
|
||||
/** |
||||
* @copyright 2016 Christoph Wurst <christoph@winzerhof-wurst.at> |
||||
* |
||||
* @author 2016 Christoph Wurst <christoph@winzerhof-wurst.at> |
||||
* |
||||
* @license GNU AGPL version 3 or any later version |
||||
* |
||||
* This program is free software: you can redistribute it and/or modify |
||||
* it under the terms of the GNU Affero General Public License as |
||||
* published by the Free Software Foundation, either version 3 of the |
||||
* License, or (at your option) any later version. |
||||
* |
||||
* 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 |
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||
* |
||||
*/ |
||||
|
||||
namespace OCP\Authentication\LoginCredentials; |
||||
|
||||
use OCP\Authentication\Exceptions\CredentialsUnavailableException; |
||||
|
||||
/** |
||||
* @since 12 |
||||
*/ |
||||
interface IStore { |
||||
|
||||
/** |
||||
* Get login credentials of the currently logged in user |
||||
* |
||||
* @since 12 |
||||
* |
||||
* @throws CredentialsUnavailableException |
||||
* @return ICredentials the login credentials of the current user |
||||
*/ |
||||
public function getLoginCredentials(); |
||||
|
||||
} |
||||
@ -0,0 +1,66 @@ |
||||
<?php |
||||
|
||||
/** |
||||
* @copyright 2016 Christoph Wurst <christoph@winzerhof-wurst.at> |
||||
* |
||||
* @author 2016 Christoph Wurst <christoph@winzerhof-wurst.at> |
||||
* |
||||
* @license GNU AGPL version 3 or any later version |
||||
* |
||||
* This program is free software: you can redistribute it and/or modify |
||||
* it under the terms of the GNU Affero General Public License as |
||||
* published by the Free Software Foundation, either version 3 of the |
||||
* License, or (at your option) any later version. |
||||
* |
||||
* 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 |
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||
* |
||||
*/ |
||||
|
||||
namespace Test\Authentication\LoginCredentials; |
||||
|
||||
use OC\Authentication\LoginCredentials\Credentials; |
||||
use Test\TestCase; |
||||
|
||||
class CredentialsTest extends TestCase { |
||||
|
||||
/** @var string */ |
||||
private $uid; |
||||
|
||||
/** @var string */ |
||||
private $user; |
||||
|
||||
/** @var string */ |
||||
private $password; |
||||
|
||||
/** @var Credentials */ |
||||
private $credentials; |
||||
|
||||
protected function setUp() { |
||||
parent::setUp(); |
||||
|
||||
$this->uid = 'user123'; |
||||
$this->user = 'User123'; |
||||
$this->password = '123456'; |
||||
|
||||
$this->credentials = new Credentials($this->uid, $this->user, $this->password); |
||||
} |
||||
|
||||
public function testGetUID() { |
||||
$this->assertEquals($this->uid, $this->credentials->getUID()); |
||||
} |
||||
|
||||
public function testGetUserName() { |
||||
$this->assertEquals($this->user, $this->credentials->getLoginName()); |
||||
} |
||||
|
||||
public function testGetPassword() { |
||||
$this->assertEquals($this->password, $this->credentials->getPassword()); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,182 @@ |
||||
<?php |
||||
|
||||
/** |
||||
* @copyright 2016 Christoph Wurst <christoph@winzerhof-wurst.at> |
||||
* |
||||
* @author 2016 Christoph Wurst <christoph@winzerhof-wurst.at> |
||||
* |
||||
* @license GNU AGPL version 3 or any later version |
||||
* |
||||
* This program is free software: you can redistribute it and/or modify |
||||
* it under the terms of the GNU Affero General Public License as |
||||
* published by the Free Software Foundation, either version 3 of the |
||||
* License, or (at your option) any later version. |
||||
* |
||||
* 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 |
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||
* |
||||
*/ |
||||
|
||||
namespace Test\Authentication\LoginCredentials; |
||||
|
||||
use OC\Authentication\Exceptions\InvalidTokenException; |
||||
use OC\Authentication\Exceptions\PasswordlessTokenException; |
||||
use OC\Authentication\LoginCredentials\Credentials; |
||||
use OC\Authentication\LoginCredentials\Store; |
||||
use OC\Authentication\Token\IProvider; |
||||
use OC\Authentication\Token\IToken; |
||||
use OCP\Authentication\Exceptions\CredentialsUnavailableException; |
||||
use OCP\ILogger; |
||||
use OCP\ISession; |
||||
use OCP\Session\Exceptions\SessionNotAvailableException; |
||||
use PHPUnit_Framework_MockObject_MockObject; |
||||
use Test\TestCase; |
||||
|
||||
class StoreTest extends TestCase { |
||||
|
||||
/** @var ISession|PHPUnit_Framework_MockObject_MockObject */ |
||||
private $session; |
||||
|
||||
/** @var IProvider|PHPUnit_Framework_MockObject_MockObject */ |
||||
private $tokenProvider; |
||||
|
||||
/** @var ILogger|PHPUnit_Framework_MockObject_MockObject */ |
||||
private $logger; |
||||
|
||||
/** @var Store */ |
||||
private $store; |
||||
|
||||
protected function setUp() { |
||||
parent::setUp(); |
||||
|
||||
$this->session = $this->createMock(ISession::class); |
||||
$this->tokenProvider = $this->createMock(IProvider::class); |
||||
$this->logger = $this->createMock(ILogger::class); |
||||
|
||||
$this->store = new Store($this->session, $this->logger, $this->tokenProvider); |
||||
} |
||||
|
||||
public function testAuthenticate() { |
||||
$params = [ |
||||
'run' => true, |
||||
'uid' => 'user123', |
||||
'password' => 123456, |
||||
]; |
||||
|
||||
$this->session->expects($this->once()) |
||||
->method('set') |
||||
->with($this->equalTo('login_credentials'), $this->equalTo(json_encode($params))); |
||||
|
||||
$this->store->authenticate($params); |
||||
} |
||||
|
||||
public function testSetSession() { |
||||
$session = $this->createMock(ISession::class); |
||||
|
||||
$this->store->setSession($session); |
||||
} |
||||
|
||||
public function testGetLoginCredentialsNoTokenProvider() { |
||||
$this->store = new Store($this->session, $this->logger, null); |
||||
|
||||
$this->expectException(CredentialsUnavailableException::class); |
||||
|
||||
$this->store->getLoginCredentials(); |
||||
} |
||||
|
||||
public function testGetLoginCredentials() { |
||||
$uid = 'uid'; |
||||
$user = 'user123'; |
||||
$password = 'passme'; |
||||
$token = $this->createMock(IToken::class); |
||||
$this->session->expects($this->once()) |
||||
->method('getId') |
||||
->willReturn('sess2233'); |
||||
$this->tokenProvider->expects($this->once()) |
||||
->method('getToken') |
||||
->with('sess2233') |
||||
->willReturn($token); |
||||
$token->expects($this->once()) |
||||
->method('getUID') |
||||
->willReturn($uid); |
||||
$token->expects($this->once()) |
||||
->method('getLoginName') |
||||
->willReturn($user); |
||||
$this->tokenProvider->expects($this->once()) |
||||
->method('getPassword') |
||||
->with($token, 'sess2233') |
||||
->willReturn($password); |
||||
$expected = new Credentials($uid, $user, $password); |
||||
|
||||
$creds = $this->store->getLoginCredentials(); |
||||
|
||||
$this->assertEquals($expected, $creds); |
||||
} |
||||
|
||||
public function testGetLoginCredentialsSessionNotAvailable() { |
||||
$this->session->expects($this->once()) |
||||
->method('getId') |
||||
->will($this->throwException(new SessionNotAvailableException())); |
||||
$this->expectException(CredentialsUnavailableException::class); |
||||
|
||||
$this->store->getLoginCredentials(); |
||||
} |
||||
|
||||
public function testGetLoginCredentialsInvalidToken() { |
||||
$this->session->expects($this->once()) |
||||
->method('getId') |
||||
->willReturn('sess2233'); |
||||
$this->tokenProvider->expects($this->once()) |
||||
->method('getToken') |
||||
->with('sess2233') |
||||
->will($this->throwException(new InvalidTokenException())); |
||||
$this->expectException(CredentialsUnavailableException::class); |
||||
|
||||
$this->store->getLoginCredentials(); |
||||
} |
||||
|
||||
public function testGetLoginCredentialsInvalidTokenLoginCredentials() { |
||||
$uid = 'user987'; |
||||
$password = '7389374'; |
||||
|
||||
$this->session->expects($this->once()) |
||||
->method('getId') |
||||
->willReturn('sess2233'); |
||||
$this->tokenProvider->expects($this->once()) |
||||
->method('getToken') |
||||
->with('sess2233') |
||||
->will($this->throwException(new InvalidTokenException())); |
||||
$this->session->expects($this->once()) |
||||
->method('exists') |
||||
->with($this->equalTo('login_credentials')) |
||||
->willReturn(true); |
||||
$this->session->expects($this->once()) |
||||
->method('get') |
||||
->with($this->equalTo('login_credentials')) |
||||
->willReturn('{"run":true,"uid":"user987","password":"7389374"}'); |
||||
$expected = new Credentials('user987', 'user987', '7389374'); |
||||
|
||||
$actual = $this->store->getLoginCredentials(); |
||||
|
||||
$this->assertEquals($expected, $actual); |
||||
} |
||||
|
||||
public function testGetLoginCredentialsPasswordlessToken() { |
||||
$this->session->expects($this->once()) |
||||
->method('getId') |
||||
->willReturn('sess2233'); |
||||
$this->tokenProvider->expects($this->once()) |
||||
->method('getToken') |
||||
->with('sess2233') |
||||
->will($this->throwException(new PasswordlessTokenException())); |
||||
$this->expectException(CredentialsUnavailableException::class); |
||||
|
||||
$this->store->getLoginCredentials(); |
||||
} |
||||
|
||||
} |
||||
Loading…
Reference in new issue