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>
This commit is contained in:
parent
0885bd4ee5
commit
f2a3115157
|
@ -1181,6 +1181,15 @@
|
||||||
<notnull>false</notnull>
|
<notnull>false</notnull>
|
||||||
</field>
|
</field>
|
||||||
|
|
||||||
|
<field>
|
||||||
|
<name>expires</name>
|
||||||
|
<type>integer</type>
|
||||||
|
<default></default>
|
||||||
|
<notnull>false</notnull>
|
||||||
|
<unsigned>true</unsigned>
|
||||||
|
<length>4</length>
|
||||||
|
</field>
|
||||||
|
|
||||||
<index>
|
<index>
|
||||||
<name>authtoken_token_index</name>
|
<name>authtoken_token_index</name>
|
||||||
<unique>true</unique>
|
<unique>true</unique>
|
||||||
|
|
|
@ -363,6 +363,7 @@ return array(
|
||||||
'OC\\Authentication\\Token\\DefaultTokenCleanupJob' => $baseDir . '/lib/private/Authentication/Token/DefaultTokenCleanupJob.php',
|
'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\\DefaultTokenMapper' => $baseDir . '/lib/private/Authentication/Token/DefaultTokenMapper.php',
|
||||||
'OC\\Authentication\\Token\\DefaultTokenProvider' => $baseDir . '/lib/private/Authentication/Token/DefaultTokenProvider.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\\IProvider' => $baseDir . '/lib/private/Authentication/Token/IProvider.php',
|
||||||
'OC\\Authentication\\Token\\IToken' => $baseDir . '/lib/private/Authentication/Token/IToken.php',
|
'OC\\Authentication\\Token\\IToken' => $baseDir . '/lib/private/Authentication/Token/IToken.php',
|
||||||
'OC\\Authentication\\TwoFactorAuth\\Manager' => $baseDir . '/lib/private/Authentication/TwoFactorAuth/Manager.php',
|
'OC\\Authentication\\TwoFactorAuth\\Manager' => $baseDir . '/lib/private/Authentication/TwoFactorAuth/Manager.php',
|
||||||
|
|
|
@ -393,6 +393,7 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c
|
||||||
'OC\\Authentication\\Token\\DefaultTokenCleanupJob' => __DIR__ . '/../../..' . '/lib/private/Authentication/Token/DefaultTokenCleanupJob.php',
|
'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\\DefaultTokenMapper' => __DIR__ . '/../../..' . '/lib/private/Authentication/Token/DefaultTokenMapper.php',
|
||||||
'OC\\Authentication\\Token\\DefaultTokenProvider' => __DIR__ . '/../../..' . '/lib/private/Authentication/Token/DefaultTokenProvider.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\\IProvider' => __DIR__ . '/../../..' . '/lib/private/Authentication/Token/IProvider.php',
|
||||||
'OC\\Authentication\\Token\\IToken' => __DIR__ . '/../../..' . '/lib/private/Authentication/Token/IToken.php',
|
'OC\\Authentication\\Token\\IToken' => __DIR__ . '/../../..' . '/lib/private/Authentication/Token/IToken.php',
|
||||||
'OC\\Authentication\\TwoFactorAuth\\Manager' => __DIR__ . '/../../..' . '/lib/private/Authentication/TwoFactorAuth/Manager.php',
|
'OC\\Authentication\\TwoFactorAuth\\Manager' => __DIR__ . '/../../..' . '/lib/private/Authentication/TwoFactorAuth/Manager.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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -90,10 +90,15 @@ class DefaultToken extends Entity implements IToken {
|
||||||
*/
|
*/
|
||||||
protected $scope;
|
protected $scope;
|
||||||
|
|
||||||
|
/** @var int */
|
||||||
|
protected $expires;
|
||||||
|
|
||||||
public function __construct() {
|
public function __construct() {
|
||||||
$this->addType('type', 'int');
|
$this->addType('type', 'int');
|
||||||
$this->addType('lastActivity', 'int');
|
$this->addType('lastActivity', 'int');
|
||||||
$this->addType('lastCheck', 'int');
|
$this->addType('lastCheck', 'int');
|
||||||
|
$this->addType('scope', 'string');
|
||||||
|
$this->addType('expires', 'int');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getId() {
|
public function getId() {
|
||||||
|
@ -179,4 +184,15 @@ class DefaultToken extends Entity implements IToken {
|
||||||
public function setPassword($password = null) {
|
public function setPassword($password = null) {
|
||||||
parent::setPassword($password);
|
parent::setPassword($password);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function setExpires($expires) {
|
||||||
|
parent::setExpires($expires);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return int|null
|
||||||
|
*/
|
||||||
|
public function getExpires() {
|
||||||
|
return parent::getExpires();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -72,7 +72,7 @@ class DefaultTokenMapper extends Mapper {
|
||||||
public function getToken($token) {
|
public function getToken($token) {
|
||||||
/* @var $qb IQueryBuilder */
|
/* @var $qb IQueryBuilder */
|
||||||
$qb = $this->db->getQueryBuilder();
|
$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')
|
->from('authtoken')
|
||||||
->where($qb->expr()->eq('token', $qb->createNamedParameter($token)))
|
->where($qb->expr()->eq('token', $qb->createNamedParameter($token)))
|
||||||
->execute();
|
->execute();
|
||||||
|
@ -96,7 +96,7 @@ class DefaultTokenMapper extends Mapper {
|
||||||
public function getTokenById($id) {
|
public function getTokenById($id) {
|
||||||
/* @var $qb IQueryBuilder */
|
/* @var $qb IQueryBuilder */
|
||||||
$qb = $this->db->getQueryBuilder();
|
$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')
|
->from('authtoken')
|
||||||
->where($qb->expr()->eq('id', $qb->createNamedParameter($id)))
|
->where($qb->expr()->eq('id', $qb->createNamedParameter($id)))
|
||||||
->execute();
|
->execute();
|
||||||
|
@ -121,7 +121,7 @@ class DefaultTokenMapper extends Mapper {
|
||||||
public function getTokenByUser(IUser $user) {
|
public function getTokenByUser(IUser $user) {
|
||||||
/* @var $qb IQueryBuilder */
|
/* @var $qb IQueryBuilder */
|
||||||
$qb = $this->db->getQueryBuilder();
|
$qb = $this->db->getQueryBuilder();
|
||||||
$qb->select('id', 'uid', 'login_name', 'password', 'name', 'type', 'remember', 'token', 'last_activity', 'last_check', 'scope')
|
$qb->select('*')
|
||||||
->from('authtoken')
|
->from('authtoken')
|
||||||
->where($qb->expr()->eq('uid', $qb->createNamedParameter($user->getUID())))
|
->where($qb->expr()->eq('uid', $qb->createNamedParameter($user->getUID())))
|
||||||
->setMaxResults(1000);
|
->setMaxResults(1000);
|
||||||
|
|
|
@ -150,13 +150,20 @@ class DefaultTokenProvider implements IProvider {
|
||||||
* @param string $tokenId
|
* @param string $tokenId
|
||||||
* @throws InvalidTokenException
|
* @throws InvalidTokenException
|
||||||
* @return DefaultToken
|
* @return DefaultToken
|
||||||
|
* @throws ExpiredTokenException
|
||||||
*/
|
*/
|
||||||
public function getToken($tokenId) {
|
public function getToken($tokenId) {
|
||||||
try {
|
try {
|
||||||
return $this->mapper->getToken($this->hashToken($tokenId));
|
$token = $this->mapper->getToken($this->hashToken($tokenId));
|
||||||
} catch (DoesNotExistException $ex) {
|
} catch (DoesNotExistException $ex) {
|
||||||
throw new InvalidTokenException();
|
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
|
* @param string $tokenId
|
||||||
* @throws InvalidTokenException
|
* @throws InvalidTokenException
|
||||||
* @return DefaultToken
|
* @return DefaultToken
|
||||||
|
* @throws ExpiredTokenException
|
||||||
|
* @return IToken
|
||||||
*/
|
*/
|
||||||
public function getTokenById($tokenId) {
|
public function getTokenById($tokenId) {
|
||||||
try {
|
try {
|
||||||
return $this->mapper->getTokenById($tokenId);
|
$token = $this->mapper->getTokenById($tokenId);
|
||||||
} catch (DoesNotExistException $ex) {
|
} catch (DoesNotExistException $ex) {
|
||||||
throw new InvalidTokenException();
|
throw new InvalidTokenException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($token->getExpires() !== null && $token->getExpires() < $this->time->getTime()) {
|
||||||
|
throw new ExpiredTokenException($token);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $token;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -48,6 +48,7 @@ interface IProvider {
|
||||||
*
|
*
|
||||||
* @param string $tokenId
|
* @param string $tokenId
|
||||||
* @throws InvalidTokenException
|
* @throws InvalidTokenException
|
||||||
|
* @throws ExpiredTokenException
|
||||||
* @return IToken
|
* @return IToken
|
||||||
*/
|
*/
|
||||||
public function getToken($tokenId);
|
public function getToken($tokenId);
|
||||||
|
@ -58,6 +59,7 @@ interface IProvider {
|
||||||
* @param string $tokenId
|
* @param string $tokenId
|
||||||
* @throws InvalidTokenException
|
* @throws InvalidTokenException
|
||||||
* @return DefaultToken
|
* @return DefaultToken
|
||||||
|
* @throws ExpiredTokenException
|
||||||
*/
|
*/
|
||||||
public function getTokenById($tokenId);
|
public function getTokenById($tokenId);
|
||||||
|
|
||||||
|
|
|
@ -107,4 +107,11 @@ interface IToken extends JsonSerializable {
|
||||||
* @param string $password
|
* @param string $password
|
||||||
*/
|
*/
|
||||||
public function setPassword($password);
|
public function setPassword($password);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the expiration time of the token
|
||||||
|
*
|
||||||
|
* @param int|null $expires
|
||||||
|
*/
|
||||||
|
public function setExpires($expires);
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,7 @@ namespace Test\Authentication\Token;
|
||||||
use OC\Authentication\Exceptions\InvalidTokenException;
|
use OC\Authentication\Exceptions\InvalidTokenException;
|
||||||
use OC\Authentication\Token\DefaultToken;
|
use OC\Authentication\Token\DefaultToken;
|
||||||
use OC\Authentication\Token\DefaultTokenProvider;
|
use OC\Authentication\Token\DefaultTokenProvider;
|
||||||
|
use OC\Authentication\Token\ExpiredTokenException;
|
||||||
use OC\Authentication\Token\IToken;
|
use OC\Authentication\Token\IToken;
|
||||||
use OCP\AppFramework\Db\DoesNotExistException;
|
use OCP\AppFramework\Db\DoesNotExistException;
|
||||||
use OCP\AppFramework\Db\Mapper;
|
use OCP\AppFramework\Db\Mapper;
|
||||||
|
@ -396,6 +397,63 @@ class DefaultTokenProviderTest extends TestCase {
|
||||||
$this->tokenProvider->renewSessionToken('oldId', 'newId');
|
$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() {
|
public function testGetTokenById() {
|
||||||
$token = $this->createMock(DefaultToken::class);
|
$token = $this->createMock(DefaultToken::class);
|
||||||
|
|
||||||
|
@ -418,6 +476,23 @@ class DefaultTokenProviderTest extends TestCase {
|
||||||
$this->tokenProvider->getTokenById(42);
|
$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() {
|
public function testRotate() {
|
||||||
$token = new DefaultToken();
|
$token = new DefaultToken();
|
||||||
$token->setPassword('oldencryptedpassword');
|
$token->setPassword('oldencryptedpassword');
|
||||||
|
|
|
@ -26,7 +26,7 @@
|
||||||
// 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(12, 0, 7, 1);
|
$OC_Version = array(12, 0, 7, 2);
|
||||||
|
|
||||||
// The human readable string
|
// The human readable string
|
||||||
$OC_VersionString = '12.0.7';
|
$OC_VersionString = '12.0.7';
|
||||||
|
|
Loading…
Reference in New Issue