aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRoeland Jago Douma <roeland@famdouma.nl>2018-05-16 12:39:00 +0200
committerRoeland Jago Douma <roeland@famdouma.nl>2018-05-23 17:02:15 +0200
commitf2a31151576d00a6452bdfa8646adf6ba892b526 (patch)
tree1d855d397be51e161a44c6ebc7b48d25bac59236
parent0885bd4ee543d8d9e4f3e561b44fcc90376dd45a (diff)
downloadnextcloud-server-f2a31151576d00a6452bdfa8646adf6ba892b526.tar.gz
nextcloud-server-f2a31151576d00a6452bdfa8646adf6ba892b526.zip
Certain tokens can expire
However due to the nature of what we store in the token (encrypted passwords etc). We can't just delete the tokens because that would make the oauth refresh useless. Signed-off-by: Roeland Jago Douma <roeland@famdouma.nl>
-rw-r--r--db_structure.xml9
-rw-r--r--lib/composer/composer/autoload_classmap.php1
-rw-r--r--lib/composer/composer/autoload_static.php1
-rw-r--r--lib/private/Authentication/Exceptions/ExpiredTokenException.php40
-rw-r--r--lib/private/Authentication/Token/DefaultToken.php16
-rw-r--r--lib/private/Authentication/Token/DefaultTokenMapper.php6
-rw-r--r--lib/private/Authentication/Token/DefaultTokenProvider.php19
-rw-r--r--lib/private/Authentication/Token/IProvider.php2
-rw-r--r--lib/private/Authentication/Token/IToken.php7
-rw-r--r--tests/lib/Authentication/Token/DefaultTokenProviderTest.php75
-rw-r--r--version.php2
11 files changed, 172 insertions, 6 deletions
diff --git a/db_structure.xml b/db_structure.xml
index 583e9bb8522..72d7ebc647d 100644
--- a/db_structure.xml
+++ b/db_structure.xml
@@ -1181,6 +1181,15 @@
<notnull>false</notnull>
</field>
+ <field>
+ <name>expires</name>
+ <type>integer</type>
+ <default></default>
+ <notnull>false</notnull>
+ <unsigned>true</unsigned>
+ <length>4</length>
+ </field>
+
<index>
<name>authtoken_token_index</name>
<unique>true</unique>
diff --git a/lib/composer/composer/autoload_classmap.php b/lib/composer/composer/autoload_classmap.php
index 93e792a7f4c..39e96f3341d 100644
--- a/lib/composer/composer/autoload_classmap.php
+++ b/lib/composer/composer/autoload_classmap.php
@@ -363,6 +363,7 @@ return array(
'OC\\Authentication\\Token\\DefaultTokenCleanupJob' => $baseDir . '/lib/private/Authentication/Token/DefaultTokenCleanupJob.php',
'OC\\Authentication\\Token\\DefaultTokenMapper' => $baseDir . '/lib/private/Authentication/Token/DefaultTokenMapper.php',
'OC\\Authentication\\Token\\DefaultTokenProvider' => $baseDir . '/lib/private/Authentication/Token/DefaultTokenProvider.php',
+ 'OC\\Authentication\\Token\\ExpiredTokenException' => $baseDir . '/lib/private/Authentication/Exceptions/ExpiredTokenException.php',
'OC\\Authentication\\Token\\IProvider' => $baseDir . '/lib/private/Authentication/Token/IProvider.php',
'OC\\Authentication\\Token\\IToken' => $baseDir . '/lib/private/Authentication/Token/IToken.php',
'OC\\Authentication\\TwoFactorAuth\\Manager' => $baseDir . '/lib/private/Authentication/TwoFactorAuth/Manager.php',
diff --git a/lib/composer/composer/autoload_static.php b/lib/composer/composer/autoload_static.php
index 88bb9c65ffa..1242f67deba 100644
--- a/lib/composer/composer/autoload_static.php
+++ b/lib/composer/composer/autoload_static.php
@@ -393,6 +393,7 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c
'OC\\Authentication\\Token\\DefaultTokenCleanupJob' => __DIR__ . '/../../..' . '/lib/private/Authentication/Token/DefaultTokenCleanupJob.php',
'OC\\Authentication\\Token\\DefaultTokenMapper' => __DIR__ . '/../../..' . '/lib/private/Authentication/Token/DefaultTokenMapper.php',
'OC\\Authentication\\Token\\DefaultTokenProvider' => __DIR__ . '/../../..' . '/lib/private/Authentication/Token/DefaultTokenProvider.php',
+ 'OC\\Authentication\\Token\\ExpiredTokenException' => __DIR__ . '/../../..' . '/lib/private/Authentication/Exceptions/ExpiredTokenException.php',
'OC\\Authentication\\Token\\IProvider' => __DIR__ . '/../../..' . '/lib/private/Authentication/Token/IProvider.php',
'OC\\Authentication\\Token\\IToken' => __DIR__ . '/../../..' . '/lib/private/Authentication/Token/IToken.php',
'OC\\Authentication\\TwoFactorAuth\\Manager' => __DIR__ . '/../../..' . '/lib/private/Authentication/TwoFactorAuth/Manager.php',
diff --git a/lib/private/Authentication/Exceptions/ExpiredTokenException.php b/lib/private/Authentication/Exceptions/ExpiredTokenException.php
new file mode 100644
index 00000000000..8abf01bae09
--- /dev/null
+++ b/lib/private/Authentication/Exceptions/ExpiredTokenException.php
@@ -0,0 +1,40 @@
+<?php
+/**
+ * @copyright Copyright (c) 2018 Roeland Jago Douma <roeland@famdouma.nl>
+ *
+ * @author Roeland Jago Douma <roeland@famdouma.nl>
+ *
+ * @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\Token;
+
+use OC\Authentication\Exceptions\InvalidTokenException;
+
+class ExpiredTokenException extends InvalidTokenException {
+ /** @var IToken */
+ private $token;
+
+ public function __construct(IToken $token) {
+ parent::__construct();
+
+ $this->token = $token;
+ }
+
+ public function getToken() {
+ return $this->token;
+ }
+}
diff --git a/lib/private/Authentication/Token/DefaultToken.php b/lib/private/Authentication/Token/DefaultToken.php
index 024a6097fa8..583423166dd 100644
--- a/lib/private/Authentication/Token/DefaultToken.php
+++ b/lib/private/Authentication/Token/DefaultToken.php
@@ -90,10 +90,15 @@ class DefaultToken extends Entity implements IToken {
*/
protected $scope;
+ /** @var int */
+ protected $expires;
+
public function __construct() {
$this->addType('type', 'int');
$this->addType('lastActivity', 'int');
$this->addType('lastCheck', 'int');
+ $this->addType('scope', 'string');
+ $this->addType('expires', 'int');
}
public function getId() {
@@ -179,4 +184,15 @@ class DefaultToken extends Entity implements IToken {
public function setPassword($password = null) {
parent::setPassword($password);
}
+
+ public function setExpires($expires) {
+ parent::setExpires($expires);
+ }
+
+ /**
+ * @return int|null
+ */
+ public function getExpires() {
+ return parent::getExpires();
+ }
}
diff --git a/lib/private/Authentication/Token/DefaultTokenMapper.php b/lib/private/Authentication/Token/DefaultTokenMapper.php
index 00e367801e8..763fd118f4c 100644
--- a/lib/private/Authentication/Token/DefaultTokenMapper.php
+++ b/lib/private/Authentication/Token/DefaultTokenMapper.php
@@ -72,7 +72,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', 'remember', 'token', 'last_activity', 'last_check', 'scope')
+ $result = $qb->select('*')
->from('authtoken')
->where($qb->expr()->eq('token', $qb->createNamedParameter($token)))
->execute();
@@ -96,7 +96,7 @@ class DefaultTokenMapper extends Mapper {
public function getTokenById($id) {
/* @var $qb IQueryBuilder */
$qb = $this->db->getQueryBuilder();
- $result = $qb->select('id', 'uid', 'login_name', 'password', 'name', 'type', 'token', 'last_activity', 'last_check', 'scope')
+ $result = $qb->select('*')
->from('authtoken')
->where($qb->expr()->eq('id', $qb->createNamedParameter($id)))
->execute();
@@ -121,7 +121,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', 'remember', 'token', 'last_activity', 'last_check', 'scope')
+ $qb->select('*')
->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 4f435e80e75..eee4db20af9 100644
--- a/lib/private/Authentication/Token/DefaultTokenProvider.php
+++ b/lib/private/Authentication/Token/DefaultTokenProvider.php
@@ -150,13 +150,20 @@ class DefaultTokenProvider implements IProvider {
* @param string $tokenId
* @throws InvalidTokenException
* @return DefaultToken
+ * @throws ExpiredTokenException
*/
public function getToken($tokenId) {
try {
- return $this->mapper->getToken($this->hashToken($tokenId));
+ $token = $this->mapper->getToken($this->hashToken($tokenId));
} catch (DoesNotExistException $ex) {
throw new InvalidTokenException();
}
+
+ if ($token->getExpires() !== null && $token->getExpires() < $this->time->getTime()) {
+ throw new ExpiredTokenException($token);
+ }
+
+ return $token;
}
/**
@@ -165,13 +172,21 @@ class DefaultTokenProvider implements IProvider {
* @param string $tokenId
* @throws InvalidTokenException
* @return DefaultToken
+ * @throws ExpiredTokenException
+ * @return IToken
*/
public function getTokenById($tokenId) {
try {
- return $this->mapper->getTokenById($tokenId);
+ $token = $this->mapper->getTokenById($tokenId);
} catch (DoesNotExistException $ex) {
throw new InvalidTokenException();
}
+
+ if ($token->getExpires() !== null && $token->getExpires() < $this->time->getTime()) {
+ throw new ExpiredTokenException($token);
+ }
+
+ return $token;
}
/**
diff --git a/lib/private/Authentication/Token/IProvider.php b/lib/private/Authentication/Token/IProvider.php
index 5951cabad7f..22a0994755b 100644
--- a/lib/private/Authentication/Token/IProvider.php
+++ b/lib/private/Authentication/Token/IProvider.php
@@ -48,6 +48,7 @@ interface IProvider {
*
* @param string $tokenId
* @throws InvalidTokenException
+ * @throws ExpiredTokenException
* @return IToken
*/
public function getToken($tokenId);
@@ -58,6 +59,7 @@ interface IProvider {
* @param string $tokenId
* @throws InvalidTokenException
* @return DefaultToken
+ * @throws ExpiredTokenException
*/
public function getTokenById($tokenId);
diff --git a/lib/private/Authentication/Token/IToken.php b/lib/private/Authentication/Token/IToken.php
index 16d97e65567..0abdf643c1d 100644
--- a/lib/private/Authentication/Token/IToken.php
+++ b/lib/private/Authentication/Token/IToken.php
@@ -107,4 +107,11 @@ interface IToken extends JsonSerializable {
* @param string $password
*/
public function setPassword($password);
+
+ /**
+ * Set the expiration time of the token
+ *
+ * @param int|null $expires
+ */
+ public function setExpires($expires);
}
diff --git a/tests/lib/Authentication/Token/DefaultTokenProviderTest.php b/tests/lib/Authentication/Token/DefaultTokenProviderTest.php
index eb69005655f..31b2d8a6208 100644
--- a/tests/lib/Authentication/Token/DefaultTokenProviderTest.php
+++ b/tests/lib/Authentication/Token/DefaultTokenProviderTest.php
@@ -25,6 +25,7 @@ namespace Test\Authentication\Token;
use OC\Authentication\Exceptions\InvalidTokenException;
use OC\Authentication\Token\DefaultToken;
use OC\Authentication\Token\DefaultTokenProvider;
+use OC\Authentication\Token\ExpiredTokenException;
use OC\Authentication\Token\IToken;
use OCP\AppFramework\Db\DoesNotExistException;
use OCP\AppFramework\Db\Mapper;
@@ -396,6 +397,63 @@ class DefaultTokenProviderTest extends TestCase {
$this->tokenProvider->renewSessionToken('oldId', 'newId');
}
+ public function testGetToken() {
+ $token = new DefaultToken();
+
+ $this->config->method('getSystemValue')
+ ->with('secret')
+ ->willReturn('mysecret');
+
+ $this->mapper->method('getToken')
+ ->with(
+ $this->callback(function ($token) {
+ return hash('sha512', 'unhashedTokenmysecret') === $token;
+ })
+ )->willReturn($token);
+
+ $this->assertSame($token, $this->tokenProvider->getToken('unhashedToken'));
+ }
+
+ public function testGetInvalidToken() {
+ $this->expectException(InvalidTokenException::class);
+
+ $this->config->method('getSystemValue')
+ ->with('secret')
+ ->willReturn('mysecret');
+
+ $this->mapper->method('getToken')
+ ->with(
+ $this->callback(function ($token) {
+ return hash('sha512', 'unhashedTokenmysecret') === $token;
+ })
+ )->willThrowException(new InvalidTokenException());
+
+ $this->tokenProvider->getToken('unhashedToken');
+ }
+
+ public function testGetExpiredToken() {
+ $token = new DefaultToken();
+ $token->setExpires(42);
+
+ $this->config->method('getSystemValue')
+ ->with('secret')
+ ->willReturn('mysecret');
+
+ $this->mapper->method('getToken')
+ ->with(
+ $this->callback(function ($token) {
+ return hash('sha512', 'unhashedTokenmysecret') === $token;
+ })
+ )->willReturn($token);
+
+ try {
+ $this->tokenProvider->getToken('unhashedToken');
+ } catch (ExpiredTokenException $e) {
+ $this->assertSame($token, $e->getToken());
+ }
+
+ }
+
public function testGetTokenById() {
$token = $this->createMock(DefaultToken::class);
@@ -418,6 +476,23 @@ class DefaultTokenProviderTest extends TestCase {
$this->tokenProvider->getTokenById(42);
}
+ public function testGetExpiredTokenById() {
+ $token = new DefaultToken();
+ $token->setExpires(42);
+
+ $this->mapper->expects($this->once())
+ ->method('getTokenById')
+ ->with($this->equalTo(42))
+ ->willReturn($token);
+
+ try {
+ $this->tokenProvider->getTokenById(42);
+ $this->fail();
+ } catch (ExpiredTokenException $e) {
+ $this->assertSame($token, $e->getToken());
+ }
+ }
+
public function testRotate() {
$token = new DefaultToken();
$token->setPassword('oldencryptedpassword');
diff --git a/version.php b/version.php
index e59ffed5f2b..ed1abb314cc 100644
--- a/version.php
+++ b/version.php
@@ -26,7 +26,7 @@
// between betas, final and RCs. This is _not_ the public version number. Reset minor/patchlevel
// when updating major/minor version number.
-$OC_Version = array(12, 0, 7, 1);
+$OC_Version = array(12, 0, 7, 2);
// The human readable string
$OC_VersionString = '12.0.7';