diff --git a/lib/composer/composer/autoload_classmap.php b/lib/composer/composer/autoload_classmap.php index d80e425b285e4..57e6d854882a3 100644 --- a/lib/composer/composer/autoload_classmap.php +++ b/lib/composer/composer/autoload_classmap.php @@ -2428,6 +2428,7 @@ 'OC\\User\\Database' => $baseDir . '/lib/private/User/Database.php', 'OC\\User\\DisabledUserException' => $baseDir . '/lib/private/User/DisabledUserException.php', 'OC\\User\\DisplayNameCache' => $baseDir . '/lib/private/User/DisplayNameCache.php', + 'OC\\User\\LastInteractiveLogin' => $baseDir . '/lib/private/User/LastInteractiveLogin.php', 'OC\\User\\LazyUser' => $baseDir . '/lib/private/User/LazyUser.php', 'OC\\User\\Listeners\\BeforeUserDeletedListener' => $baseDir . '/lib/private/User/Listeners/BeforeUserDeletedListener.php', 'OC\\User\\Listeners\\UserChangedListener' => $baseDir . '/lib/private/User/Listeners/UserChangedListener.php', diff --git a/lib/composer/composer/autoload_static.php b/lib/composer/composer/autoload_static.php index ee95df28b9974..61bbf623b2acf 100644 --- a/lib/composer/composer/autoload_static.php +++ b/lib/composer/composer/autoload_static.php @@ -2469,6 +2469,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2 'OC\\User\\Database' => __DIR__ . '/../../..' . '/lib/private/User/Database.php', 'OC\\User\\DisabledUserException' => __DIR__ . '/../../..' . '/lib/private/User/DisabledUserException.php', 'OC\\User\\DisplayNameCache' => __DIR__ . '/../../..' . '/lib/private/User/DisplayNameCache.php', + 'OC\\User\\LastInteractiveLogin' => __DIR__ . '/../../..' . '/lib/private/User/LastInteractiveLogin.php', 'OC\\User\\LazyUser' => __DIR__ . '/../../..' . '/lib/private/User/LazyUser.php', 'OC\\User\\Listeners\\BeforeUserDeletedListener' => __DIR__ . '/../../..' . '/lib/private/User/Listeners/BeforeUserDeletedListener.php', 'OC\\User\\Listeners\\UserChangedListener' => __DIR__ . '/../../..' . '/lib/private/User/Listeners/UserChangedListener.php', diff --git a/lib/private/Security/VerificationToken/VerificationToken.php b/lib/private/Security/VerificationToken/VerificationToken.php index 13eec2b95bfbf..d03e52996ab41 100644 --- a/lib/private/Security/VerificationToken/VerificationToken.php +++ b/lib/private/Security/VerificationToken/VerificationToken.php @@ -8,6 +8,7 @@ namespace OC\Security\VerificationToken; +use OC\User\LastInteractiveLogin; use OCP\AppFramework\Utility\ITimeFactory; use OCP\BackgroundJob\IJobList; use OCP\IConfig; @@ -27,6 +28,7 @@ public function __construct( private ITimeFactory $timeFactory, private ISecureRandom $secureRandom, private IJobList $jobList, + private LastInteractiveLogin $lastInteractiveLogin, ) { } @@ -71,7 +73,7 @@ public function check( } if ($splitToken[0] < ($this->timeFactory->getTime() - self::TOKEN_LIFETIME) - || ($expiresWithLogin && $user->getLastLogin() > $splitToken[0])) { + || ($expiresWithLogin && $this->lastInteractiveLogin->get($user) > $splitToken[0])) { $this->throwInvalidTokenException(InvalidTokenException::TOKEN_EXPIRED); } diff --git a/lib/private/Server.php b/lib/private/Server.php index f2d5c731f3f70..12510bcacd7dd 100644 --- a/lib/private/Server.php +++ b/lib/private/Server.php @@ -152,6 +152,7 @@ use OC\Translation\TranslationManager; use OC\User\AvailabilityCoordinator; use OC\User\DisplayNameCache; +use OC\User\LastInteractiveLogin; use OC\User\Listeners\BeforeUserDeletedListener; use OC\User\Listeners\UserChangedListener; use OC\User\Session; @@ -454,6 +455,7 @@ public function __construct( $c->get(ILockdownManager::class), $c->get(LoggerInterface::class), $c->get(IEventDispatcher::class), + $c->get(LastInteractiveLogin::class), ); /** @deprecated 21.0.0 use BeforeUserCreatedEvent event with the IEventDispatcher instead */ $userSession->listen('\OC\User', 'preCreateUser', function ($uid, $password): void { diff --git a/lib/private/User/LastInteractiveLogin.php b/lib/private/User/LastInteractiveLogin.php new file mode 100644 index 0000000000000..1711fffafc382 --- /dev/null +++ b/lib/private/User/LastInteractiveLogin.php @@ -0,0 +1,50 @@ +userConfig->setValueInt( + $user->getUID(), + self::CONFIG_APP, + self::CONFIG_KEY, + $this->timeFactory->getTime(), + ); + } + + public function get(IUser $user): int { + return $this->userConfig->getValueInt( + $user->getUID(), + self::CONFIG_APP, + self::CONFIG_KEY, + ); + } +} diff --git a/lib/private/User/Session.php b/lib/private/User/Session.php index df25c257a61df..c0e21c45f9505 100644 --- a/lib/private/User/Session.php +++ b/lib/private/User/Session.php @@ -82,6 +82,7 @@ public function __construct( private ILockdownManager $lockdownManager, private LoggerInterface $logger, private IEventDispatcher $dispatcher, + private LastInteractiveLogin $lastInteractiveLogin, ) { } @@ -371,6 +372,9 @@ public function completeLogin(IUser $user, array $loginDetails, $regenerateSessi $isToken, ]); if ($this->isLoggedIn()) { + if (!$isToken) { + $this->lastInteractiveLogin->record($user); + } $this->prepareUserLogin($firstTimeLogin, $regenerateSessionId); return true; } @@ -963,6 +967,7 @@ public function loginWithCookie($uid, $currentToken, $oldSessionId) { $this->setToken($token->getId()); $this->lockdownManager->setToken($token); $user->updateLastLoginTimestamp(); + $this->lastInteractiveLogin->record($user); $password = null; try { $password = $this->tokenProvider->getPassword($token, $sessionId); diff --git a/tests/lib/Security/VerificationToken/VerificationTokenTest.php b/tests/lib/Security/VerificationToken/VerificationTokenTest.php index ed5890afba5a0..7976580617fe6 100644 --- a/tests/lib/Security/VerificationToken/VerificationTokenTest.php +++ b/tests/lib/Security/VerificationToken/VerificationTokenTest.php @@ -10,6 +10,7 @@ namespace Test\Security\VerificationToken; use OC\Security\VerificationToken\VerificationToken; +use OC\User\LastInteractiveLogin; use OCP\AppFramework\Utility\ITimeFactory; use OCP\BackgroundJob\IJobList; use OCP\IConfig; @@ -33,6 +34,8 @@ class VerificationTokenTest extends TestCase { protected $timeFactory; /** @var IJobList|MockObject */ protected $jobList; + /** @var LastInteractiveLogin|MockObject */ + protected $lastInteractiveLogin; #[\Override] protected function setUp(): void { @@ -43,16 +46,24 @@ protected function setUp(): void { $this->timeFactory = $this->createMock(ITimeFactory::class); $this->secureRandom = $this->createMock(ISecureRandom::class); $this->jobList = $this->createMock(IJobList::class); + $this->lastInteractiveLogin = $this->createMock(LastInteractiveLogin::class); $this->token = new VerificationToken( $this->config, $this->crypto, $this->timeFactory, $this->secureRandom, - $this->jobList + $this->jobList, + $this->lastInteractiveLogin ); } + protected function mockLastInteractiveLogin(int $timestamp): void { + $this->lastInteractiveLogin->expects($this->atLeastOnce()) + ->method('get') + ->willReturn($timestamp); + } + public function testTokenUserUnknown(): void { $this->expectException(InvalidTokenException::class); $this->expectExceptionCode(InvalidTokenException::USER_UNKNOWN); @@ -148,9 +159,6 @@ public function testTokenExpired(): void { $user->expects($this->atLeastOnce()) ->method('getUID') ->willReturn('alice'); - $user->expects($this->any()) - ->method('getLastLogin') - ->willReturn(604803); $this->config->expects($this->atLeastOnce()) ->method('getUserValue') @@ -182,9 +190,7 @@ public function testTokenExpiredByLogin(): void { $user->expects($this->atLeastOnce()) ->method('getUID') ->willReturn('alice'); - $user->expects($this->any()) - ->method('getLastLogin') - ->willReturn(604803); + $this->mockLastInteractiveLogin(604803); $this->config->expects($this->atLeastOnce()) ->method('getUserValue') @@ -208,6 +214,39 @@ public function testTokenExpiredByLogin(): void { $this->token->check('encryptedToken', $user, 'fingerprintToken', 'foobar', true); } + public function testTokenNotExpiredBySessionRevalidation(): void { + $user = $this->createMock(IUser::class); + $user->expects($this->atLeastOnce()) + ->method('isEnabled') + ->willReturn(true); + $user->expects($this->atLeastOnce()) + ->method('getUID') + ->willReturn('alice'); + $user->expects($this->never()) + ->method('getLastLogin'); + // last actual authentication predates the token + $this->mockLastInteractiveLogin(604700); + + $this->config->expects($this->atLeastOnce()) + ->method('getUserValue') + ->with('alice', 'core', 'fingerprintToken', null) + ->willReturn('encryptedToken'); + $this->config->expects($this->any()) + ->method('getSystemValueString') + ->with('secret') + ->willReturn('357111317'); + + $this->crypto->method('decrypt') + ->with('encryptedToken', 'foobar' . '357111317') + ->willReturn('604800:barfoo'); + + $this->timeFactory->expects($this->any()) + ->method('getTime') + ->willReturn(604801); + + $this->token->check('barfoo', $user, 'fingerprintToken', 'foobar', true); + } + public function testTokenMismatch(): void { $user = $this->createMock(IUser::class); $user->expects($this->atLeastOnce()) @@ -216,9 +255,6 @@ public function testTokenMismatch(): void { $user->expects($this->atLeastOnce()) ->method('getUID') ->willReturn('alice'); - $user->expects($this->any()) - ->method('getLastLogin') - ->willReturn(604703); $this->config->expects($this->atLeastOnce()) ->method('getUserValue') @@ -250,9 +286,6 @@ public function testTokenSuccess(): void { $user->expects($this->atLeastOnce()) ->method('getUID') ->willReturn('alice'); - $user->expects($this->any()) - ->method('getLastLogin') - ->willReturn(604703); $this->config->expects($this->atLeastOnce()) ->method('getUserValue') diff --git a/tests/lib/User/SessionTest.php b/tests/lib/User/SessionTest.php index f466569eb9b3c..a8e8a881df798 100644 --- a/tests/lib/User/SessionTest.php +++ b/tests/lib/User/SessionTest.php @@ -18,6 +18,7 @@ use OC\Authentication\Token\PublicKeyToken; use OC\Security\CSRF\CsrfTokenManager; use OC\Session\Memory; +use OC\User\LastInteractiveLogin; use OC\User\LoginException; use OC\User\Manager; use OC\User\Session; @@ -68,6 +69,8 @@ class SessionTest extends \Test\TestCase { private $logger; /** @var IEventDispatcher|MockObject */ private $dispatcher; + /** @var LastInteractiveLogin|MockObject */ + private $lastInteractiveLogin; #[\Override] protected function setUp(): void { @@ -86,6 +89,7 @@ protected function setUp(): void { $this->lockdownManager = $this->createMock(ILockdownManager::class); $this->logger = $this->createMock(LoggerInterface::class); $this->dispatcher = $this->createMock(IEventDispatcher::class); + $this->lastInteractiveLogin = $this->createMock(LastInteractiveLogin::class); $this->userSession = $this->getMockBuilder(Session::class) ->setConstructorArgs([ $this->manager, @@ -96,7 +100,8 @@ protected function setUp(): void { $this->random, $this->lockdownManager, $this->logger, - $this->dispatcher + $this->dispatcher, + $this->lastInteractiveLogin ]) ->onlyMethods([ 'setMagicInCookie', @@ -120,7 +125,7 @@ public function testIsLoggedIn($isLoggedIn): void { $manager = $this->createMock(Manager::class); $userSession = $this->getMockBuilder(Session::class) - ->setConstructorArgs([$manager, $session, $this->timeFactory, $this->tokenProvider, $this->config, $this->random, $this->lockdownManager, $this->logger, $this->dispatcher]) + ->setConstructorArgs([$manager, $session, $this->timeFactory, $this->tokenProvider, $this->config, $this->random, $this->lockdownManager, $this->logger, $this->dispatcher, $this->lastInteractiveLogin]) ->onlyMethods([ 'getUser' ]) @@ -147,7 +152,7 @@ public function testSetUser(): void { ->method('getUID') ->willReturn('foo'); - $userSession = new Session($manager, $session, $this->timeFactory, $this->tokenProvider, $this->config, $this->random, $this->lockdownManager, $this->logger, $this->dispatcher); + $userSession = new Session($manager, $session, $this->timeFactory, $this->tokenProvider, $this->config, $this->random, $this->lockdownManager, $this->logger, $this->dispatcher, $this->lastInteractiveLogin); $userSession->setUser($user); } @@ -198,13 +203,17 @@ public function testLoginValidPasswordEnabled(): void { $user->expects($this->once()) ->method('updateLastLoginTimestamp'); + $this->lastInteractiveLogin->expects($this->once()) + ->method('record') + ->with($user); + $manager->expects($this->once()) ->method('checkPasswordNoLogging') ->with('foo', 'bar') ->willReturn($user); $userSession = $this->getMockBuilder(Session::class) - ->setConstructorArgs([$manager, $session, $this->timeFactory, $this->tokenProvider, $this->config, $this->random, $this->lockdownManager, $this->logger, $this->dispatcher]) + ->setConstructorArgs([$manager, $session, $this->timeFactory, $this->tokenProvider, $this->config, $this->random, $this->lockdownManager, $this->logger, $this->dispatcher, $this->lastInteractiveLogin]) ->onlyMethods([ 'prepareUserLogin' ]) @@ -226,6 +235,37 @@ public function testLoginValidPasswordEnabled(): void { $this->assertEquals($user, $userSession->getUser()); } + public function testCompleteLoginWithTokenDoesNotRecordInteractiveLogin(): void { + $manager = $this->createMock(Manager::class); + $session = $this->createMock(ISession::class); + + $user = $this->createMock(IUser::class); + $user->expects($this->any()) + ->method('isEnabled') + ->willReturn(true); + $user->expects($this->any()) + ->method('getUID') + ->willReturn('foo'); + + $this->lastInteractiveLogin->expects($this->never()) + ->method('record'); + + $userSession = $this->getMockBuilder(Session::class) + ->setConstructorArgs([$manager, $session, $this->timeFactory, $this->tokenProvider, $this->config, $this->random, $this->lockdownManager, $this->logger, $this->dispatcher, $this->lastInteractiveLogin]) + ->onlyMethods(['prepareUserLogin']) + ->getMock(); + + $this->assertTrue($userSession->completeLogin( + $user, + [ + 'loginName' => 'foo', + 'password' => 'bar', + 'token' => $this->createMock(IToken::class), + ], + false + )); + } + public function testLoginValidPasswordDisabled(): void { $this->expectException(LoginException::class); @@ -267,7 +307,7 @@ public function testLoginValidPasswordDisabled(): void { $this->dispatcher->expects($this->never()) ->method('dispatch'); - $userSession = new Session($manager, $session, $this->timeFactory, $this->tokenProvider, $this->config, $this->random, $this->lockdownManager, $this->logger, $this->dispatcher); + $userSession = new Session($manager, $session, $this->timeFactory, $this->tokenProvider, $this->config, $this->random, $this->lockdownManager, $this->logger, $this->dispatcher, $this->lastInteractiveLogin); $userSession->login('foo', 'bar'); } @@ -286,7 +326,7 @@ public function testLoginInvalidPassword(): void { ]) ->getMock(); $backend = $this->createMock(\Test\Util\User\Dummy::class); - $userSession = new Session($manager, $session, $this->timeFactory, $this->tokenProvider, $this->config, $this->random, $this->lockdownManager, $this->logger, $this->dispatcher); + $userSession = new Session($manager, $session, $this->timeFactory, $this->tokenProvider, $this->config, $this->random, $this->lockdownManager, $this->logger, $this->dispatcher, $this->lastInteractiveLogin); $user = $this->createMock(IUser::class); @@ -329,7 +369,7 @@ public function testPasswordlessLoginNoLastCheckUpdate(): void { $this->createMock(LoggerInterface::class), ]) ->getMock(); - $userSession = new Session($manager, $session, $this->timeFactory, $this->tokenProvider, $this->config, $this->random, $this->lockdownManager, $this->logger, $this->dispatcher); + $userSession = new Session($manager, $session, $this->timeFactory, $this->tokenProvider, $this->config, $this->random, $this->lockdownManager, $this->logger, $this->dispatcher, $this->lastInteractiveLogin); $user = $this->createMock(IUser::class); $user->method('getUID')->willReturn('foo'); @@ -373,7 +413,7 @@ public function testLoginLastCheckUpdate(): void { $this->createMock(LoggerInterface::class), ]) ->getMock(); - $userSession = new Session($manager, $session, $this->timeFactory, $this->tokenProvider, $this->config, $this->random, $this->lockdownManager, $this->logger, $this->dispatcher); + $userSession = new Session($manager, $session, $this->timeFactory, $this->tokenProvider, $this->config, $this->random, $this->lockdownManager, $this->logger, $this->dispatcher, $this->lastInteractiveLogin); $user = $this->createMock(IUser::class); $user->method('getUID')->willReturn('foo'); @@ -406,7 +446,7 @@ public function testLoginLastCheckUpdate(): void { public function testLoginNonExisting(): void { $session = $this->createMock(Memory::class); $manager = $this->createMock(Manager::class); - $userSession = new Session($manager, $session, $this->timeFactory, $this->tokenProvider, $this->config, $this->random, $this->lockdownManager, $this->logger, $this->dispatcher); + $userSession = new Session($manager, $session, $this->timeFactory, $this->tokenProvider, $this->config, $this->random, $this->lockdownManager, $this->logger, $this->dispatcher, $this->lastInteractiveLogin); $session->expects($this->never()) ->method('set'); @@ -434,7 +474,7 @@ public function testLogClientInNoTokenPasswordWith2fa(): void { /** @var Session $userSession */ $userSession = $this->getMockBuilder(Session::class) - ->setConstructorArgs([$manager, $session, $this->timeFactory, $this->tokenProvider, $this->config, $this->random, $this->lockdownManager, $this->logger, $this->dispatcher]) + ->setConstructorArgs([$manager, $session, $this->timeFactory, $this->tokenProvider, $this->config, $this->random, $this->lockdownManager, $this->logger, $this->dispatcher, $this->lastInteractiveLogin]) ->onlyMethods(['login', 'supportsCookies', 'createSessionToken', 'getUser']) ->getMock(); @@ -479,7 +519,7 @@ public function testLogClientInUnexist(): void { /** @var Session $userSession */ $userSession = $this->getMockBuilder(Session::class) - ->setConstructorArgs([$manager, $session, $this->timeFactory, $this->tokenProvider, $this->config, $this->random, $this->lockdownManager, $this->logger, $this->dispatcher]) + ->setConstructorArgs([$manager, $session, $this->timeFactory, $this->tokenProvider, $this->config, $this->random, $this->lockdownManager, $this->logger, $this->dispatcher, $this->lastInteractiveLogin]) ->onlyMethods(['login', 'supportsCookies', 'createSessionToken', 'getUser']) ->getMock(); @@ -505,7 +545,7 @@ public function testLogClientInWithTokenPassword(): void { /** @var Session $userSession */ $userSession = $this->getMockBuilder(Session::class) - ->setConstructorArgs([$manager, $session, $this->timeFactory, $this->tokenProvider, $this->config, $this->random, $this->lockdownManager, $this->logger, $this->dispatcher]) + ->setConstructorArgs([$manager, $session, $this->timeFactory, $this->tokenProvider, $this->config, $this->random, $this->lockdownManager, $this->logger, $this->dispatcher, $this->lastInteractiveLogin]) ->onlyMethods(['login', 'supportsCookies', 'createSessionToken', 'getUser']) ->getMock(); @@ -547,7 +587,7 @@ public function testLogClientInNoTokenPasswordNo2fa(): void { /** @var Session $userSession */ $userSession = $this->getMockBuilder(Session::class) - ->setConstructorArgs([$manager, $session, $this->timeFactory, $this->tokenProvider, $this->config, $this->random, $this->lockdownManager, $this->logger, $this->dispatcher]) + ->setConstructorArgs([$manager, $session, $this->timeFactory, $this->tokenProvider, $this->config, $this->random, $this->lockdownManager, $this->logger, $this->dispatcher, $this->lastInteractiveLogin]) ->onlyMethods(['login', 'isTwoFactorEnforced']) ->getMock(); @@ -750,7 +790,7 @@ public function testRememberLoginValidToken(): void { $userSession = $this->getMockBuilder(Session::class) //override, otherwise tests will fail because of setcookie() ->onlyMethods(['setMagicInCookie', 'setLoginName']) - ->setConstructorArgs([$manager, $session, $this->timeFactory, $this->tokenProvider, $this->config, $this->random, $this->lockdownManager, $this->logger, $this->dispatcher]) + ->setConstructorArgs([$manager, $session, $this->timeFactory, $this->tokenProvider, $this->config, $this->random, $this->lockdownManager, $this->logger, $this->dispatcher, $this->lastInteractiveLogin]) ->getMock(); $user = $this->createMock(IUser::class); @@ -846,7 +886,7 @@ public function testRememberLoginInvalidSessionToken(): void { $userSession = $this->getMockBuilder(Session::class) //override, otherwise tests will fail because of setcookie() ->onlyMethods(['setMagicInCookie']) - ->setConstructorArgs([$manager, $session, $this->timeFactory, $this->tokenProvider, $this->config, $this->random, $this->lockdownManager, $this->logger, $this->dispatcher]) + ->setConstructorArgs([$manager, $session, $this->timeFactory, $this->tokenProvider, $this->config, $this->random, $this->lockdownManager, $this->logger, $this->dispatcher, $this->lastInteractiveLogin]) ->getMock(); $user = $this->createMock(IUser::class); @@ -920,7 +960,7 @@ public function testRememberLoginInvalidToken(): void { $userSession = $this->getMockBuilder(Session::class) //override, otherwise tests will fail because of setcookie() ->onlyMethods(['setMagicInCookie']) - ->setConstructorArgs([$manager, $session, $this->timeFactory, $this->tokenProvider, $this->config, $this->random, $this->lockdownManager, $this->logger, $this->dispatcher]) + ->setConstructorArgs([$manager, $session, $this->timeFactory, $this->tokenProvider, $this->config, $this->random, $this->lockdownManager, $this->logger, $this->dispatcher, $this->lastInteractiveLogin]) ->getMock(); $user = $this->createMock(IUser::class); @@ -973,7 +1013,7 @@ public function testRememberLoginInvalidUser(): void { $userSession = $this->getMockBuilder(Session::class) //override, otherwise tests will fail because of setcookie() ->onlyMethods(['setMagicInCookie']) - ->setConstructorArgs([$manager, $session, $this->timeFactory, $this->tokenProvider, $this->config, $this->random, $this->lockdownManager, $this->logger, $this->dispatcher]) + ->setConstructorArgs([$manager, $session, $this->timeFactory, $this->tokenProvider, $this->config, $this->random, $this->lockdownManager, $this->logger, $this->dispatcher, $this->lastInteractiveLogin]) ->getMock(); $token = 'goodToken'; $oldSessionId = 'sess321'; @@ -1021,7 +1061,7 @@ public function testActiveUserAfterSetSession(): void { $session = new Memory(); $session->set('user_id', 'foo'); $userSession = $this->getMockBuilder(Session::class) - ->setConstructorArgs([$manager, $session, $this->timeFactory, $this->tokenProvider, $this->config, $this->random, $this->lockdownManager, $this->logger, $this->dispatcher]) + ->setConstructorArgs([$manager, $session, $this->timeFactory, $this->tokenProvider, $this->config, $this->random, $this->lockdownManager, $this->logger, $this->dispatcher, $this->lastInteractiveLogin]) ->onlyMethods([ 'validateSession' ]) @@ -1041,7 +1081,7 @@ public function testCreateSessionToken(): void { $manager = $this->createMock(Manager::class); $session = $this->createMock(ISession::class); $user = $this->createMock(IUser::class); - $userSession = new Session($manager, $session, $this->timeFactory, $this->tokenProvider, $this->config, $this->random, $this->lockdownManager, $this->logger, $this->dispatcher); + $userSession = new Session($manager, $session, $this->timeFactory, $this->tokenProvider, $this->config, $this->random, $this->lockdownManager, $this->logger, $this->dispatcher, $this->lastInteractiveLogin); $requestId = $this->createMock(IRequestId::class); $config = $this->createMock(IConfig::class); @@ -1082,7 +1122,7 @@ public function testCreateRememberedSessionToken(): void { $manager = $this->createMock(Manager::class); $session = $this->createMock(ISession::class); $user = $this->createMock(IUser::class); - $userSession = new Session($manager, $session, $this->timeFactory, $this->tokenProvider, $this->config, $this->random, $this->lockdownManager, $this->logger, $this->dispatcher); + $userSession = new Session($manager, $session, $this->timeFactory, $this->tokenProvider, $this->config, $this->random, $this->lockdownManager, $this->logger, $this->dispatcher, $this->lastInteractiveLogin); $requestId = $this->createMock(IRequestId::class); $config = $this->createMock(IConfig::class); @@ -1126,7 +1166,7 @@ public function testCreateSessionTokenWithTokenPassword(): void { $session = $this->createMock(ISession::class); $token = $this->createMock(IToken::class); $user = $this->createMock(IUser::class); - $userSession = new Session($manager, $session, $this->timeFactory, $this->tokenProvider, $this->config, $this->random, $this->lockdownManager, $this->logger, $this->dispatcher); + $userSession = new Session($manager, $session, $this->timeFactory, $this->tokenProvider, $this->config, $this->random, $this->lockdownManager, $this->logger, $this->dispatcher, $this->lastInteractiveLogin); $requestId = $this->createMock(IRequestId::class); $config = $this->createMock(IConfig::class); @@ -1173,7 +1213,7 @@ public function testCreateSessionTokenWithNonExistentUser(): void { ->disableOriginalConstructor() ->getMock(); $session = $this->createMock(ISession::class); - $userSession = new Session($manager, $session, $this->timeFactory, $this->tokenProvider, $this->config, $this->random, $this->lockdownManager, $this->logger, $this->dispatcher); + $userSession = new Session($manager, $session, $this->timeFactory, $this->tokenProvider, $this->config, $this->random, $this->lockdownManager, $this->logger, $this->dispatcher, $this->lastInteractiveLogin); $request = $this->createMock(IRequest::class); $uid = 'user123'; @@ -1246,7 +1286,8 @@ public function testTryBasicAuthLoginValid(): void { $this->random, $this->lockdownManager, $this->logger, - $this->dispatcher + $this->dispatcher, + $this->lastInteractiveLogin ]) ->onlyMethods([ 'logClientIn', @@ -1297,7 +1338,8 @@ public function testTryBasicAuthLoginNoLogin(): void { $this->random, $this->lockdownManager, $this->logger, - $this->dispatcher + $this->dispatcher, + $this->lastInteractiveLogin ]) ->onlyMethods([ 'logClientIn', @@ -1326,7 +1368,7 @@ public function testLogClientInThrottlerUsername(): void { /** @var Session $userSession */ $userSession = $this->getMockBuilder(Session::class) - ->setConstructorArgs([$manager, $session, $this->timeFactory, $this->tokenProvider, $this->config, $this->random, $this->lockdownManager, $this->logger, $this->dispatcher]) + ->setConstructorArgs([$manager, $session, $this->timeFactory, $this->tokenProvider, $this->config, $this->random, $this->lockdownManager, $this->logger, $this->dispatcher, $this->lastInteractiveLogin]) ->onlyMethods(['login', 'supportsCookies', 'createSessionToken', 'getUser']) ->getMock(); @@ -1373,7 +1415,7 @@ public function testLogClientInThrottlerEmail(): void { /** @var Session $userSession */ $userSession = $this->getMockBuilder(Session::class) - ->setConstructorArgs([$manager, $session, $this->timeFactory, $this->tokenProvider, $this->config, $this->random, $this->lockdownManager, $this->logger, $this->dispatcher]) + ->setConstructorArgs([$manager, $session, $this->timeFactory, $this->tokenProvider, $this->config, $this->random, $this->lockdownManager, $this->logger, $this->dispatcher, $this->lastInteractiveLogin]) ->onlyMethods(['login', 'supportsCookies', 'createSessionToken', 'getUser']) ->getMock();