summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristoph Wurst <christoph@owncloud.com>2016-06-17 13:59:15 +0200
committerChristoph Wurst <christoph@owncloud.com>2016-06-17 15:42:28 +0200
commit0c0a216f42bb004380efca1fd665711f938579d9 (patch)
tree341a9fbf2d7cc9b155bf99498150400073574093
parentc4149c59c2cfe83b5e4cd2b20b8ad4caf2341ca9 (diff)
downloadnextcloud-server-0c0a216f42bb004380efca1fd665711f938579d9.tar.gz
nextcloud-server-0c0a216f42bb004380efca1fd665711f938579d9.zip
store last check timestamp in token instead of session
-rw-r--r--db_structure.xml9
-rw-r--r--lib/private/Authentication/Token/DefaultToken.php23
-rw-r--r--lib/private/Authentication/Token/DefaultTokenMapper.php4
-rw-r--r--lib/private/Authentication/Token/DefaultTokenProvider.php27
-rw-r--r--lib/private/Authentication/Token/IProvider.php14
-rw-r--r--lib/private/Authentication/Token/IToken.php14
-rw-r--r--lib/private/User/Session.php144
-rw-r--r--version.php2
8 files changed, 160 insertions, 77 deletions
diff --git a/db_structure.xml b/db_structure.xml
index b7dacc05d92..6b91c3c4c5d 100644
--- a/db_structure.xml
+++ b/db_structure.xml
@@ -1120,6 +1120,15 @@
<length>4</length>
</field>
+ <field>
+ <name>last_check</name>
+ <type>integer</type>
+ <default>0</default>
+ <notnull>true</notnull>
+ <unsigned>true</unsigned>
+ <length>4</length>
+ </field>
+
<index>
<name>authtoken_token_index</name>
<unique>true</unique>
diff --git a/lib/private/Authentication/Token/DefaultToken.php b/lib/private/Authentication/Token/DefaultToken.php
index 299291e34af..79b03eed27f 100644
--- a/lib/private/Authentication/Token/DefaultToken.php
+++ b/lib/private/Authentication/Token/DefaultToken.php
@@ -74,6 +74,11 @@ class DefaultToken extends Entity implements IToken {
*/
protected $lastActivity;
+ /**
+ * @var int
+ */
+ protected $lastCheck;
+
public function getId() {
return $this->id;
}
@@ -109,4 +114,22 @@ class DefaultToken extends Entity implements IToken {
];
}
+ /**
+ * Get the timestamp of the last password check
+ *
+ * @return int
+ */
+ public function getLastCheck() {
+ return parent::getLastCheck();
+ }
+
+ /**
+ * Get the timestamp of the last password check
+ *
+ * @param int $time
+ */
+ public function setLastCheck($time) {
+ return parent::setLastCheck($time);
+ }
+
}
diff --git a/lib/private/Authentication/Token/DefaultTokenMapper.php b/lib/private/Authentication/Token/DefaultTokenMapper.php
index c56a513b94c..b02220976cf 100644
--- a/lib/private/Authentication/Token/DefaultTokenMapper.php
+++ b/lib/private/Authentication/Token/DefaultTokenMapper.php
@@ -70,7 +70,7 @@ class DefaultTokenMapper extends Mapper {
public function getToken($token) {
/* @var $qb IQueryBuilder */
$qb = $this->db->getQueryBuilder();
- $result = $qb->select('id', 'uid', 'login_name', 'password', 'name', 'type', 'token', 'last_activity')
+ $result = $qb->select('id', 'uid', 'login_name', 'password', 'name', 'type', 'token', 'last_activity', 'last_check')
->from('authtoken')
->where($qb->expr()->eq('token', $qb->createParameter('token')))
->setParameter('token', $token)
@@ -95,7 +95,7 @@ class DefaultTokenMapper extends Mapper {
public function getTokenByUser(IUser $user) {
/* @var $qb IQueryBuilder */
$qb = $this->db->getQueryBuilder();
- $qb->select('id', 'uid', 'login_name', 'password', 'name', 'type', 'token', 'last_activity')
+ $qb->select('id', 'uid', 'login_name', 'password', 'name', 'type', 'token', 'last_activity', 'last_check')
->from('authtoken')
->where($qb->expr()->eq('uid', $qb->createNamedParameter($user->getUID())))
->setMaxResults(1000);
diff --git a/lib/private/Authentication/Token/DefaultTokenProvider.php b/lib/private/Authentication/Token/DefaultTokenProvider.php
index 03b8bb5da28..2467b8d836f 100644
--- a/lib/private/Authentication/Token/DefaultTokenProvider.php
+++ b/lib/private/Authentication/Token/DefaultTokenProvider.php
@@ -92,6 +92,18 @@ class DefaultTokenProvider implements IProvider {
}
/**
+ * Save the updated token
+ *
+ * @param IToken $token
+ */
+ public function updateToken(IToken $token) {
+ if (!($token instanceof DefaultToken)) {
+ throw new InvalidTokenException();
+ }
+ $this->mapper->update($token);
+ }
+
+ /**
* Update token activity timestamp
*
* @throws InvalidTokenException
@@ -183,21 +195,6 @@ class DefaultTokenProvider implements IProvider {
/**
* @param string $token
- * @throws InvalidTokenException
- * @return DefaultToken user UID
- */
- public function validateToken($token) {
- try {
- $dbToken = $this->mapper->getToken($this->hashToken($token));
- $this->logger->debug('valid default token for ' . $dbToken->getUID());
- return $dbToken;
- } catch (DoesNotExistException $ex) {
- throw new InvalidTokenException();
- }
- }
-
- /**
- * @param string $token
* @return string
*/
private function hashToken($token) {
diff --git a/lib/private/Authentication/Token/IProvider.php b/lib/private/Authentication/Token/IProvider.php
index e79ba8b30e5..97f8ababbbe 100644
--- a/lib/private/Authentication/Token/IProvider.php
+++ b/lib/private/Authentication/Token/IProvider.php
@@ -50,13 +50,6 @@ interface IProvider {
public function getToken($tokenId) ;
/**
- * @param string $token
- * @throws InvalidTokenException
- * @return IToken
- */
- public function validateToken($token);
-
- /**
* Invalidate (delete) the given session token
*
* @param string $token
@@ -72,6 +65,13 @@ interface IProvider {
public function invalidateTokenById(IUser $user, $id);
/**
+ * Save the updated token
+ *
+ * @param IToken $token
+ */
+ public function updateToken(IToken $token);
+
+ /**
* Update token activity timestamp
*
* @param IToken $token
diff --git a/lib/private/Authentication/Token/IToken.php b/lib/private/Authentication/Token/IToken.php
index a34bdc2c43d..096550fd091 100644
--- a/lib/private/Authentication/Token/IToken.php
+++ b/lib/private/Authentication/Token/IToken.php
@@ -55,4 +55,18 @@ interface IToken extends JsonSerializable {
* @return string
*/
public function getPassword();
+
+ /**
+ * Get the timestamp of the last password check
+ *
+ * @return int
+ */
+ public function getLastCheck();
+
+ /**
+ * Get the timestamp of the last password check
+ *
+ * @param int $time
+ */
+ public function setLastCheck($time);
}
diff --git a/lib/private/User/Session.php b/lib/private/User/Session.php
index 89148dcf8ec..ccae72ed35a 100644
--- a/lib/private/User/Session.php
+++ b/lib/private/User/Session.php
@@ -192,52 +192,22 @@ class Session implements IUserSession, Emitter {
if (is_null($this->activeUser)) {
return null;
}
- $this->validateSession($this->activeUser);
+ $this->validateSession();
}
return $this->activeUser;
}
- protected function validateSession(IUser $user) {
+ protected function validateSession() {
try {
$sessionId = $this->session->getId();
} catch (SessionNotAvailableException $ex) {
return;
}
- try {
- $token = $this->tokenProvider->getToken($sessionId);
- } catch (InvalidTokenException $ex) {
+
+ if (!$this->validateToken($sessionId)) {
// Session was invalidated
$this->logout();
- return;
}
-
- // Check whether login credentials are still valid and the user was not disabled
- // This check is performed each 5 minutes
- $lastCheck = $this->session->get('last_login_check') ? : 0;
- $now = $this->timeFacory->getTime();
- if ($lastCheck < ($now - 60 * 5)) {
- try {
- $pwd = $this->tokenProvider->getPassword($token, $sessionId);
- } catch (InvalidTokenException $ex) {
- // An invalid token password was used -> log user out
- $this->logout();
- 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
- || !$user->isEnabled()) {
- // Password has changed or user was disabled -> log user out
- $this->logout();
- return;
- }
- $this->session->set('last_login_check', $now);
- }
-
- $this->tokenProvider->updateTokenActivity($token);
}
/**
@@ -297,20 +267,22 @@ class Session implements IUserSession, Emitter {
public function login($uid, $password) {
$this->session->regenerateId();
if ($this->validateToken($password)) {
- $user = $this->getUser();
-
// When logging in with token, the password must be decrypted first before passing to login hook
try {
$token = $this->tokenProvider->getToken($password);
try {
- $password = $this->tokenProvider->getPassword($token, $password);
- $this->manager->emit('\OC\User', 'preLogin', array($uid, $password));
+ $loginPassword = $this->tokenProvider->getPassword($token, $password);
+ $this->manager->emit('\OC\User', 'preLogin', array($uid, $loginPassword));
} catch (PasswordlessTokenException $ex) {
$this->manager->emit('\OC\User', 'preLogin', array($uid, ''));
}
} catch (InvalidTokenException $ex) {
// Invalid token, nothing to do
}
+
+ $this->loginWithToken($password);
+ $user = $this->getUser();
+ $this->tokenProvider->updateTokenActivity($token);
} else {
$this->manager->emit('\OC\User', 'preLogin', array($uid, $password));
$user = $this->manager->checkPassword($uid, $password);
@@ -459,8 +431,21 @@ class Session implements IUserSession, Emitter {
return false;
}
- private function loginWithToken($uid) {
- // TODO: $this->manager->emit('\OC\User', 'preTokenLogin', array($uid));
+ private function loginWithToken($token) {
+ try {
+ $dbToken = $this->tokenProvider->getToken($token);
+ } catch (InvalidTokenException $ex) {
+ return false;
+ }
+ $uid = $dbToken->getUID();
+
+ try {
+ $password = $this->tokenProvider->getPassword($dbToken, $token);
+ $this->manager->emit('\OC\User', 'preLogin', array($uid, $password));
+ } catch (PasswordlessTokenException $ex) {
+ $this->manager->emit('\OC\User', 'preLogin', array($uid, ''));
+ }
+
$user = $this->manager->get($uid);
if (is_null($user)) {
// user does not exist
@@ -473,7 +458,9 @@ class Session implements IUserSession, Emitter {
//login
$this->setUser($user);
- // TODO: $this->manager->emit('\OC\User', 'postTokenLogin', array($user));
+ $this->tokenProvider->updateTokenActivity($dbToken);
+
+ $this->manager->emit('\OC\User', 'postLogin', array($user, $password));
return true;
}
@@ -530,24 +517,72 @@ class Session implements IUserSession, Emitter {
}
/**
+ * @param IToken $dbToken
* @param string $token
* @return boolean
*/
- private function validateToken($token) {
+ private function checkTokenCredentials(IToken $dbToken, $token) {
+ // Check whether login credentials are still valid and the user was not disabled
+ // This check is performed each 5 minutes
+ $lastCheck = $dbToken->getLastCheck() ? : 0;
+ $now = $this->timeFacory->getTime();
+ if ($lastCheck > ($now - 60 * 5)) {
+ // Checked performed recently, nothing to do now
+ return true;
+ }
+
try {
- $token = $this->tokenProvider->validateToken($token);
- if (!is_null($token)) {
- $result = $this->loginWithToken($token->getUID());
- if ($result) {
- // Login success
- $this->tokenProvider->updateTokenActivity($token);
- return true;
- }
+ $pwd = $this->tokenProvider->getPassword($dbToken, $token);
+ } catch (InvalidTokenException $ex) {
+ // An invalid token password was used -> log user out
+ $this->logout();
+ return false;
+ } catch (PasswordlessTokenException $ex) {
+ // Token has no password
+
+ if (!is_null($this->activeUser) && !$this->activeUser->isEnabled()) {
+ $this->tokenProvider->invalidateToken($token);
+ $this->logout();
+ return false;
}
+
+ $dbToken->setLastCheck($now);
+ $this->tokenProvider->updateToken($dbToken);
+ return true;
+ }
+
+ if ($this->manager->checkPassword($dbToken->getLoginName(), $pwd) === false
+ || (!is_null($this->activeUser) && !$this->activeUser->isEnabled())) {
+ $this->tokenProvider->invalidateToken($token);
+ // Password has changed or user was disabled -> log user out
+ $this->logout();
+ return false;
+ }
+ $dbToken->setLastCheck($now);
+ $this->tokenProvider->updateToken($dbToken);
+ return true;
+ }
+
+ /**
+ * Check if the given token exists and performs password/user-enabled checks
+ *
+ * Invalidates the token if checks fail
+ *
+ * @param string $token
+ * @return boolean
+ */
+ private function validateToken($token) {
+ try {
+ $dbToken = $this->tokenProvider->getToken($token);
} catch (InvalidTokenException $ex) {
+ return false;
+ }
+ if (!$this->checkTokenCredentials($dbToken, $token)) {
+ return false;
}
- return false;
+
+ return true;
}
/**
@@ -562,10 +597,15 @@ class Session implements IUserSession, Emitter {
// No auth header, let's try session id
try {
$sessionId = $this->session->getId();
- return $this->validateToken($sessionId);
} catch (SessionNotAvailableException $ex) {
return false;
}
+
+ if (!$this->validateToken($sessionId)) {
+ return false;
+ }
+
+ return $this->loginWithToken($sessionId);
} else {
$token = substr($authHeader, 6);
return $this->validateToken($token);
diff --git a/version.php b/version.php
index 698636a2196..3015d976e53 100644
--- a/version.php
+++ b/version.php
@@ -25,7 +25,7 @@
// 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
// when updating major/minor version number.
-$OC_Version = array(9, 1, 0, 8);
+$OC_Version = array(9, 1, 0, 9);
// The human readable string
$OC_VersionString = '9.1.0 beta 2';