Merge pull request #17939 from nextcloud/fix/token-insert-conflict-handling

Handle token insert conflicts
This commit is contained in:
Roeland Jago Douma 2019-11-26 19:47:59 +01:00 committed by GitHub
commit d09f8c7423
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 68 additions and 19 deletions

View File

@ -23,6 +23,7 @@ declare(strict_types=1);
namespace OC\Authentication\Token;
use Doctrine\DBAL\Exception\UniqueConstraintViolationException;
use OC\Authentication\Exceptions\ExpiredTokenException;
use OC\Authentication\Exceptions\InvalidTokenException;
use OC\Authentication\Exceptions\PasswordlessTokenException;
@ -60,6 +61,7 @@ class Manager implements IProvider {
string $name,
int $type = IToken::TEMPORARY_TOKEN,
int $remember = IToken::DO_NOT_REMEMBER): IToken {
try {
return $this->publicKeyTokenProvider->generateToken(
$token,
$uid,
@ -69,6 +71,19 @@ class Manager implements IProvider {
$type,
$remember
);
} catch (UniqueConstraintViolationException $e) {
// It's rare, but if two requests of the same session (e.g. env-based SAML)
// try to create the session token they might end up here at the same time
// because we use the session ID as token and the db token is created anew
// with every request.
//
// If the UIDs match, then this should be fine.
$existing = $this->getToken($token);
if ($existing->getUID() !== $uid) {
throw new \Exception('Token conflict handled, but UIDs do not match. This should not happen', 0, $e);
}
return $existing;
}
}
/**

View File

@ -1,4 +1,5 @@
<?php
<?php declare(strict_types=1);
/**
* @copyright Copyright (c) 2018 Roeland Jago Douma <roeland@famdouma.nl>
*
@ -23,6 +24,7 @@
namespace Test\Authentication\Token;
use Doctrine\DBAL\Exception\UniqueConstraintViolationException;
use OC\Authentication\Exceptions\InvalidTokenException;
use OC\Authentication\Exceptions\PasswordlessTokenException;
use OC\Authentication\Token\DefaultToken;
@ -31,21 +33,15 @@ use OC\Authentication\Token\ExpiredTokenException;
use OC\Authentication\Token\IToken;
use OC\Authentication\Token\Manager;
use OC\Authentication\Token\PublicKeyToken;
use OC\Authentication\Token\PublicKeyTokenMapper;
use OC\Authentication\Token\PublicKeyTokenProvider;
use OCP\AppFramework\Db\DoesNotExistException;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\IConfig;
use OCP\ILogger;
use OCP\IUser;
use OCP\Security\ICrypto;
use PHPUnit\Framework\MockObject\MockObject;
use Test\TestCase;
class ManagerTest extends TestCase {
/** @var PublicKeyTokenProvider|\PHPUnit_Framework_MockObject_MockObject */
/** @var PublicKeyTokenProvider|MockObject */
private $publicKeyTokenProvider;
/** @var DefaultTokenProvider|\PHPUnit_Framework_MockObject_MockObject */
/** @var DefaultTokenProvider|MockObject */
private $defaultTokenProvider;
/** @var Manager */
private $manager;
@ -92,6 +88,44 @@ class ManagerTest extends TestCase {
$this->assertSame($token, $actual);
}
public function testGenerateConflictingToken() {
/** @var MockObject|UniqueConstraintViolationException $exception */
$exception = $this->createMock(UniqueConstraintViolationException::class);
$this->defaultTokenProvider->expects($this->never())
->method('generateToken');
$token = new PublicKeyToken();
$token->setUid('uid');
$this->publicKeyTokenProvider->expects($this->once())
->method('generateToken')
->with(
'token',
'uid',
'loginName',
'password',
'name',
IToken::TEMPORARY_TOKEN,
IToken::REMEMBER
)->willThrowException($exception);
$this->publicKeyTokenProvider->expects($this->once())
->method('getToken')
->with('token')
->willReturn($token);
$actual = $this->manager->generateToken(
'token',
'uid',
'loginName',
'password',
'name',
IToken::TEMPORARY_TOKEN,
IToken::REMEMBER
);
$this->assertSame($token, $actual);
}
public function tokenData(): array {
return [
[new DefaultToken()],