* Update last_activity timestamp of the session token * Check user backend credentials once in 5 minutestags/v9.1.0beta1
@@ -26,7 +26,6 @@ namespace OC\Core\Controller; | |||
use OC; | |||
use OC\User\Session; | |||
use OC_App; | |||
use OC_User; | |||
use OC_Util; | |||
use OCP\AppFramework\Controller; | |||
use OCP\AppFramework\Http\RedirectResponse; |
@@ -1057,10 +1057,10 @@ | |||
<field> | |||
<name>password</name> | |||
<type>text</type> | |||
<type>clob</type> | |||
<default></default> | |||
<notnull>true</notnull> | |||
<length>100</length> | |||
<length>4000</length> | |||
</field> | |||
<field> |
@@ -51,13 +51,8 @@ class DefaultToken extends Entity implements IToken { | |||
*/ | |||
protected $lastActivity; | |||
/** | |||
* Get the token ID | |||
* | |||
* @return string | |||
*/ | |||
public function getId() { | |||
return $this->token; | |||
return $this->id; | |||
} | |||
} |
@@ -61,10 +61,10 @@ class DefaultTokenMapper extends Mapper { | |||
* | |||
* @param string $token | |||
* @throws DoesNotExistException | |||
* @return string | |||
* @return DefaultToken | |||
*/ | |||
public function getTokenUser($token) { | |||
$sql = 'SELECT `uid` ' | |||
public function getToken($token) { | |||
$sql = 'SELECT `id`, `uid`, `password`, `name`, `token`, `last_activity` ' | |||
. 'FROM `' . $this->getTableName() . '` ' | |||
. 'WHERE `token` = ?'; | |||
return $this->findEntity($sql, [ |
@@ -48,8 +48,7 @@ class DefaultTokenProvider implements IProvider { | |||
* @param IConfig $config | |||
* @param ILogger $logger | |||
*/ | |||
public function __construct(DefaultTokenMapper $mapper, ICrypto $crypto, | |||
IConfig $config, ILogger $logger) { | |||
public function __construct(DefaultTokenMapper $mapper, ICrypto $crypto, IConfig $config, ILogger $logger) { | |||
$this->mapper = $mapper; | |||
$this->crypto = $crypto; | |||
$this->config = $config; | |||
@@ -67,8 +66,7 @@ class DefaultTokenProvider implements IProvider { | |||
public function generateToken($token, $uid, $password, $name) { | |||
$dbToken = new DefaultToken(); | |||
$dbToken->setUid($uid); | |||
$secret = $this->config->getSystemValue('secret'); | |||
$dbToken->setPassword($this->crypto->encrypt($password . $secret)); | |||
$dbToken->setPassword($this->encryptPassword($password, $token)); | |||
$dbToken->setName($name); | |||
$dbToken->setToken($this->hashToken($token)); | |||
$dbToken->setLastActivity(time()); | |||
@@ -78,6 +76,37 @@ class DefaultTokenProvider implements IProvider { | |||
return $dbToken; | |||
} | |||
/** | |||
* Update token activity timestamp | |||
* | |||
* @param DefaultToken $token | |||
*/ | |||
public function updateToken(DefaultToken $token) { | |||
$token->setLastActivity(time()); | |||
$this->mapper->update($token); | |||
} | |||
/** | |||
* @param string $token | |||
* @throws InvalidTokenException | |||
*/ | |||
public function getToken($token) { | |||
try { | |||
return $this->mapper->getToken($this->hashToken($token)); | |||
} catch (DoesNotExistException $ex) { | |||
throw new InvalidTokenException(); | |||
} | |||
} | |||
/** | |||
* @param DefaultToken $savedToken | |||
* @param string $token session token | |||
*/ | |||
public function getPassword(DefaultToken $savedToken, $token) { | |||
return $this->decryptPassword($savedToken->getPassword(), $token); | |||
} | |||
/** | |||
* Invalidate (delete) the given session token | |||
* | |||
@@ -104,7 +133,7 @@ class DefaultTokenProvider implements IProvider { | |||
public function validateToken($token) { | |||
$this->logger->debug('validating default token <' . $token . '>'); | |||
try { | |||
$dbToken = $this->mapper->getTokenUser($this->hashToken($token)); | |||
$dbToken = $this->mapper->getToken($this->hashToken($token)); | |||
$this->logger->debug('valid token for ' . $dbToken->getUid()); | |||
return $dbToken->getUid(); | |||
} catch (DoesNotExistException $ex) { | |||
@@ -121,4 +150,32 @@ class DefaultTokenProvider implements IProvider { | |||
return hash('sha512', $token); | |||
} | |||
/** | |||
* Encrypt the given password | |||
* | |||
* The token is used as key | |||
* | |||
* @param string $password | |||
* @param string $token | |||
* @return string encrypted password | |||
*/ | |||
private function encryptPassword($password, $token) { | |||
$secret = $this->config->getSystemValue('secret'); | |||
return $this->crypto->encrypt($password, $token . $secret); | |||
} | |||
/** | |||
* Decrypt the given password | |||
* | |||
* The token is used as key | |||
* | |||
* @param string $password | |||
* @param string $token | |||
* @return string the decrypted key | |||
*/ | |||
private function decryptPassword($password, $token) { | |||
$secret = $this->config->getSystemValue('secret'); | |||
return $this->crypto->decrypt($password, $token . $secret); | |||
} | |||
} |
@@ -96,8 +96,7 @@ class Session implements IUserSession, Emitter { | |||
* @param ISession $session | |||
* @param IProvider[] $tokenProviders | |||
*/ | |||
public function __construct(IUserManager $manager, ISession $session, | |||
DefaultTokenProvider $tokenProvider, array $tokenProviders = []) { | |||
public function __construct(IUserManager $manager, ISession $session, DefaultTokenProvider $tokenProvider, array $tokenProviders = []) { | |||
$this->manager = $manager; | |||
$this->session = $session; | |||
$this->tokenProvider = $tokenProvider; | |||
@@ -118,8 +117,7 @@ class Session implements IUserSession, Emitter { | |||
* @param string $method optional | |||
* @param callable $callback optional | |||
*/ | |||
public function removeListener($scope = null, $method = null, | |||
callable $callback = null) { | |||
public function removeListener($scope = null, $method = null, callable $callback = null) { | |||
$this->manager->removeListener($scope, $method, $callback); | |||
} | |||
@@ -183,8 +181,7 @@ class Session implements IUserSession, Emitter { | |||
return $this->activeUser; | |||
} else { | |||
$uid = $this->session->get('user_id'); | |||
if ($uid !== null) { | |||
$this->activeUser = $this->manager->get($uid); | |||
if ($uid !== null && $this->isValidSession($uid)) { | |||
return $this->activeUser; | |||
} else { | |||
return null; | |||
@@ -192,6 +189,41 @@ class Session implements IUserSession, Emitter { | |||
} | |||
} | |||
private function isValidSession($uid) { | |||
$this->activeUser = $this->manager->get($uid); | |||
if (is_null($this->activeUser)) { | |||
// User does not exist | |||
return false; | |||
} | |||
// TODO: use ISession::getId(), https://github.com/owncloud/core/pull/24229 | |||
$sessionId = session_id(); | |||
try { | |||
$token = $this->tokenProvider->getToken($sessionId); | |||
} catch (InvalidTokenException $ex) { | |||
// Session was inalidated | |||
$this->logout(); | |||
return false; | |||
} | |||
// Check whether login credentials are still valid | |||
// This check is performed each 5 minutes | |||
$lastCheck = $this->session->get('last_login_check') ? : 0; | |||
if ($lastCheck < (time() - 60 * 5)) { | |||
$pwd = $this->tokenProvider->getPassword($token, $sessionId); | |||
if ($this->manager->checkPassword($uid, $pwd) === false) { | |||
// Password has changed -> log user out | |||
$this->logout(); | |||
return false; | |||
} | |||
$this->session->set('last_login_check', time()); | |||
} | |||
// Session is valid, so the token can be refreshed | |||
$this->tokenProvider->updateToken($token); | |||
return true; | |||
} | |||
/** | |||
* Checks whether the user is logged in | |||
* | |||
@@ -334,7 +366,6 @@ class Session implements IUserSession, Emitter { | |||
* @return boolean | |||
*/ | |||
private function validateToken(IRequest $request, $token) { | |||
// TODO: hash token | |||
foreach ($this->tokenProviders as $provider) { | |||
try { | |||
$user = $provider->validateToken($token); |
@@ -68,7 +68,7 @@ class OC_User { | |||
private static $_setupedBackends = array(); | |||
// bool, stores if a user want to access a resource anonymously, e.g if he opens a public link | |||
// bool, stores if a user want to access a resource anonymously, e.g if they open a public link | |||
private static $incognitoMode = false; | |||
/** |