<name>password</name> | <name>password</name> | ||||
<type>clob</type> | <type>clob</type> | ||||
<default></default> | <default></default> | ||||
<notnull>true</notnull> | |||||
<length>4000</length> | |||||
<notnull>false</notnull> | |||||
</field> | </field> | ||||
<field> | <field> |
<?php | |||||
/** | |||||
* @author Christoph Wurst <christoph@owncloud.com> | |||||
* | |||||
* @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 <http://www.gnu.org/licenses/> | |||||
* | |||||
*/ | |||||
namespace OC\Authentication\Exceptions; | |||||
use Exception; | |||||
class PasswordlessTokenException extends Exception { | |||||
} |
* @method void setId(int $id) | * @method void setId(int $id) | ||||
* @method void setUid(string $uid); | * @method void setUid(string $uid); | ||||
* @method void setLoginName(string $loginName) | * @method void setLoginName(string $loginName) | ||||
* @method string getLoginName() | |||||
* @method void setPassword(string $password) | * @method void setPassword(string $password) | ||||
* @method void setName(string $name) | * @method void setName(string $name) | ||||
* @method string getName() | * @method string getName() |
use Exception; | use Exception; | ||||
use OC\Authentication\Exceptions\InvalidTokenException; | use OC\Authentication\Exceptions\InvalidTokenException; | ||||
use OC\Authentication\Exceptions\PasswordlessTokenException; | |||||
use OCP\AppFramework\Db\DoesNotExistException; | use OCP\AppFramework\Db\DoesNotExistException; | ||||
use OCP\AppFramework\Utility\ITimeFactory; | use OCP\AppFramework\Utility\ITimeFactory; | ||||
use OCP\IConfig; | use OCP\IConfig; | ||||
* @param string $token | * @param string $token | ||||
* @param string $uid | * @param string $uid | ||||
* @param string $loginName | * @param string $loginName | ||||
* @param string $password | |||||
* @param string|null $password | |||||
* @param string $name | * @param string $name | ||||
* @param int $type token type | * @param int $type token type | ||||
* @return IToken | * @return IToken | ||||
$dbToken = new DefaultToken(); | $dbToken = new DefaultToken(); | ||||
$dbToken->setUid($uid); | $dbToken->setUid($uid); | ||||
$dbToken->setLoginName($loginName); | $dbToken->setLoginName($loginName); | ||||
$dbToken->setPassword($this->encryptPassword($password, $token)); | |||||
if (!is_null($password)) { | |||||
$dbToken->setPassword($this->encryptPassword($password, $token)); | |||||
} | |||||
$dbToken->setName($name); | $dbToken->setName($name); | ||||
$dbToken->setToken($this->hashToken($token)); | $dbToken->setToken($this->hashToken($token)); | ||||
$dbToken->setType($type); | $dbToken->setType($type); | ||||
* @param IToken $savedToken | * @param IToken $savedToken | ||||
* @param string $tokenId session token | * @param string $tokenId session token | ||||
* @throws InvalidTokenException | * @throws InvalidTokenException | ||||
* @throws PasswordlessTokenException | |||||
* @return string | * @return string | ||||
*/ | */ | ||||
public function getPassword(IToken $savedToken, $tokenId) { | public function getPassword(IToken $savedToken, $tokenId) { | ||||
return $this->decryptPassword($savedToken->getPassword(), $tokenId); | |||||
$password = $savedToken->getPassword(); | |||||
if (is_null($password)) { | |||||
throw new PasswordlessTokenException(); | |||||
} | |||||
return $this->decryptPassword($password, $tokenId); | |||||
} | } | ||||
/** | /** |
namespace OC\Authentication\Token; | namespace OC\Authentication\Token; | ||||
use OC\Authentication\Exceptions\InvalidTokenException; | use OC\Authentication\Exceptions\InvalidTokenException; | ||||
use OC\Authentication\Exceptions\PasswordlessTokenException; | |||||
use OCP\IUser; | use OCP\IUser; | ||||
interface IProvider { | interface IProvider { | ||||
* @param string $token | * @param string $token | ||||
* @param string $uid | * @param string $uid | ||||
* @param string $loginName | * @param string $loginName | ||||
* @param string $password | |||||
* @param string|null $password | |||||
* @param string $name | * @param string $name | ||||
* @param int $type token type | * @param int $type token type | ||||
* @return IToken | * @return IToken | ||||
* @param IToken $token | * @param IToken $token | ||||
* @param string $tokenId | * @param string $tokenId | ||||
* @throws InvalidTokenException | * @throws InvalidTokenException | ||||
* @throws PasswordlessTokenException | |||||
* @return string | * @return string | ||||
*/ | */ | ||||
public function getPassword(IToken $token, $tokenId); | public function getPassword(IToken $token, $tokenId); |
use OC; | use OC; | ||||
use OC\Authentication\Exceptions\InvalidTokenException; | use OC\Authentication\Exceptions\InvalidTokenException; | ||||
use OC\Authentication\Exceptions\PasswordlessTokenException; | |||||
use OC\Authentication\Token\IProvider; | use OC\Authentication\Token\IProvider; | ||||
use OC\Authentication\Token\IToken; | use OC\Authentication\Token\IToken; | ||||
use OC\Hooks\Emitter; | use OC\Hooks\Emitter; | ||||
use OCP\IUserManager; | use OCP\IUserManager; | ||||
use OCP\IUserSession; | use OCP\IUserSession; | ||||
use OCP\Session\Exceptions\SessionNotAvailableException; | use OCP\Session\Exceptions\SessionNotAvailableException; | ||||
use OCP\Util; | |||||
/** | /** | ||||
* Class Session | * Class Session | ||||
// An invalid token password was used -> log user out | // An invalid token password was used -> log user out | ||||
$this->logout(); | $this->logout(); | ||||
return; | return; | ||||
} catch (PasswordlessTokenException $ex) { | |||||
// Token has no password, nothing to check | |||||
$this->session->set('last_login_check', $now); | |||||
return; | |||||
} | } | ||||
if ($this->manager->checkPassword($token->getLoginName(), $pwd) === false | if ($this->manager->checkPassword($token->getLoginName(), $pwd) === false | ||||
// When logging in with token, the password must be decrypted first before passing to login hook | // When logging in with token, the password must be decrypted first before passing to login hook | ||||
try { | try { | ||||
$token = $this->tokenProvider->getToken($password); | $token = $this->tokenProvider->getToken($password); | ||||
$password = $this->tokenProvider->getPassword($token, $password); | |||||
$this->manager->emit('\OC\User', 'preLogin', array($uid, $password)); | |||||
try { | |||||
$password = $this->tokenProvider->getPassword($token, $password); | |||||
$this->manager->emit('\OC\User', 'preLogin', array($uid, $password)); | |||||
} catch (PasswordlessTokenException $ex) { | |||||
$this->manager->emit('\OC\User', 'preLogin', array($uid, '')); | |||||
} | |||||
} catch (InvalidTokenException $ex) { | } catch (InvalidTokenException $ex) { | ||||
// Invalid token, nothing to do | // Invalid token, nothing to do | ||||
} | } | ||||
} | } | ||||
protected function isTwoFactorEnforced($username) { | protected function isTwoFactorEnforced($username) { | ||||
\OCP\Util::emitHook( | |||||
Util::emitHook( | |||||
'\OCA\Files_Sharing\API\Server2Server', | '\OCA\Files_Sharing\API\Server2Server', | ||||
'preLoginNameUsedAsUserName', | 'preLoginNameUsedAsUserName', | ||||
array('uid' => &$username) | array('uid' => &$username) | ||||
* @param string $password | * @param string $password | ||||
* @return boolean | * @return boolean | ||||
*/ | */ | ||||
public function createSessionToken(IRequest $request, $uid, $loginName, $password) { | |||||
public function createSessionToken(IRequest $request, $uid, $loginName, $password = null) { | |||||
if (is_null($this->manager->get($uid))) { | if (is_null($this->manager->get($uid))) { | ||||
// User does not exist | // User does not exist | ||||
return false; | return false; |
self::setUserId($uid); | self::setUserId($uid); | ||||
self::setDisplayName($uid); | self::setDisplayName($uid); | ||||
self::getUserSession()->setLoginName($uid); | self::getUserSession()->setLoginName($uid); | ||||
$request = OC::$server->getRequest(); | |||||
self::getUserSession()->createSessionToken($request, $uid, $uid); | |||||
// setup the filesystem | // setup the filesystem | ||||
OC_Util::setupFS($uid); | OC_Util::setupFS($uid); | ||||
// first call the post_login hooks, the login-process needs to be | // first call the post_login hooks, the login-process needs to be |
use OC\AppFramework\Http; | use OC\AppFramework\Http; | ||||
use OC\Authentication\Exceptions\InvalidTokenException; | use OC\Authentication\Exceptions\InvalidTokenException; | ||||
use OC\Authentication\Exceptions\PasswordlessTokenException; | |||||
use OC\Authentication\Token\IProvider; | use OC\Authentication\Token\IProvider; | ||||
use OC\Authentication\Token\IToken; | use OC\Authentication\Token\IToken; | ||||
use OCP\AppFramework\Controller; | use OCP\AppFramework\Controller; | ||||
try { | try { | ||||
$sessionToken = $this->tokenProvider->getToken($sessionId); | $sessionToken = $this->tokenProvider->getToken($sessionId); | ||||
$loginName = $sessionToken->getLoginName(); | $loginName = $sessionToken->getLoginName(); | ||||
$password = $this->tokenProvider->getPassword($sessionToken, $sessionId); | |||||
try { | |||||
$password = $this->tokenProvider->getPassword($sessionToken, $sessionId); | |||||
} catch (PasswordlessTokenException $ex) { | |||||
$password = null; | |||||
} | |||||
} catch (InvalidTokenException $ex) { | } catch (InvalidTokenException $ex) { | ||||
$resp = new JSONResponse(); | $resp = new JSONResponse(); | ||||
$resp->setStatus(Http::STATUS_SERVICE_UNAVAILABLE); | $resp->setStatus(Http::STATUS_SERVICE_UNAVAILABLE); |
$this->assertEquals('passme', $actual); | $this->assertEquals('passme', $actual); | ||||
} | } | ||||
/** | |||||
* @expectedException \OC\Authentication\Exceptions\PasswordlessTokenException | |||||
*/ | |||||
public function testGetPasswordPasswordLessToken() { | |||||
$token = 'token1234'; | |||||
$tk = new DefaultToken(); | |||||
$tk->setPassword(null); | |||||
$this->tokenProvider->getPassword($tk, $token); | |||||
} | |||||
/** | /** | ||||
* @expectedException \OC\Authentication\Exceptions\InvalidTokenException | * @expectedException \OC\Authentication\Exceptions\InvalidTokenException | ||||
*/ | */ |
$this->invokePrivate($userSession, 'validateSession', [$user]); | $this->invokePrivate($userSession, 'validateSession', [$user]); | ||||
} | } | ||||
public function testValidateSessionNoPassword() { | |||||
$userManager = $this->getMock('\OCP\IUserManager'); | |||||
$session = $this->getMock('\OCP\ISession'); | |||||
$timeFactory = $this->getMock('\OCP\AppFramework\Utility\ITimeFactory'); | |||||
$tokenProvider = $this->getMock('\OC\Authentication\Token\IProvider'); | |||||
$userSession = $this->getMockBuilder('\OC\User\Session') | |||||
->setConstructorArgs([$userManager, $session, $timeFactory, $tokenProvider, $this->config]) | |||||
->setMethods(['logout']) | |||||
->getMock(); | |||||
$user = $this->getMock('\OCP\IUser'); | |||||
$token = $this->getMock('\OC\Authentication\Token\IToken'); | |||||
$session->expects($this->once()) | |||||
->method('getId') | |||||
->will($this->returnValue('sessionid')); | |||||
$tokenProvider->expects($this->once()) | |||||
->method('getToken') | |||||
->with('sessionid') | |||||
->will($this->returnValue($token)); | |||||
$session->expects($this->once()) | |||||
->method('get') | |||||
->with('last_login_check') | |||||
->will($this->returnValue(1000)); | |||||
$timeFactory->expects($this->once()) | |||||
->method('getTime') | |||||
->will($this->returnValue(5000)); | |||||
$tokenProvider->expects($this->once()) | |||||
->method('getPassword') | |||||
->with($token, 'sessionid') | |||||
->will($this->throwException(new \OC\Authentication\Exceptions\PasswordlessTokenException())); | |||||
$session->expects($this->once()) | |||||
->method('set') | |||||
->with('last_login_check', 5000); | |||||
$this->invokePrivate($userSession, 'validateSession', [$user]); | |||||
} | |||||
} | } |
// We only can count up. The 4. digit is only for the internal patchlevel to trigger DB upgrades | // We only can count up. The 4. digit is only for the internal patchlevel to trigger DB upgrades | ||||
// between betas, final and RCs. This is _not_ the public version number. Reset minor/patchlevel | // between betas, final and RCs. This is _not_ the public version number. Reset minor/patchlevel | ||||
// when updating major/minor version number. | // when updating major/minor version number. | ||||
$OC_Version = array(9, 1, 0, 6); | |||||
$OC_Version = array(9, 1, 0, 7); | |||||
// The human readable string | // The human readable string | ||||
$OC_VersionString = '9.1.0 beta 1'; | $OC_VersionString = '9.1.0 beta 1'; |