namespace OC\Authentication\Token;
+use Doctrine\DBAL\Exception\UniqueConstraintViolationException;
use OC\Authentication\Exceptions\ExpiredTokenException;
use OC\Authentication\Exceptions\InvalidTokenException;
use OC\Authentication\Exceptions\PasswordlessTokenException;
string $name,
int $type = IToken::TEMPORARY_TOKEN,
int $remember = IToken::DO_NOT_REMEMBER): IToken {
- return $this->publicKeyTokenProvider->generateToken(
- $token,
- $uid,
- $loginName,
- $password,
- $name,
- $type,
- $remember
- );
+ try {
+ return $this->publicKeyTokenProvider->generateToken(
+ $token,
+ $uid,
+ $loginName,
+ $password,
+ $name,
+ $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;
+ }
}
/**
-<?php
+<?php declare(strict_types=1);
+
/**
* @copyright Copyright (c) 2018 Roeland Jago Douma <roeland@famdouma.nl>
*
namespace Test\Authentication\Token;
+use Doctrine\DBAL\Exception\UniqueConstraintViolationException;
use OC\Authentication\Exceptions\InvalidTokenException;
use OC\Authentication\Exceptions\PasswordlessTokenException;
use OC\Authentication\Token\DefaultToken;
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;
$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()],