Merge pull request #17939 from nextcloud/fix/token-insert-conflict-handling
Handle token insert conflicts
This commit is contained in:
commit
d09f8c7423
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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()],
|
||||
|
|
Loading…
Reference in New Issue