/** * @dataProvider provideSecuritySensitiveOperationStatus * @param bool $mutableSession */ public function testSecuritySensitiveOperationStatus($mutableSession) { $this->logger = new \Psr\Log\NullLogger(); $user = \User::newFromName('UTSysop'); $provideUser = null; $reauth = $mutableSession ? AuthManager::SEC_REAUTH : AuthManager::SEC_FAIL; list($provider, $reset) = $this->getMockSessionProvider($mutableSession, ['provideSessionInfo']); $provider->expects($this->any())->method('provideSessionInfo')->will($this->returnCallback(function () use($provider, &$provideUser) { return new SessionInfo(SessionInfo::MIN_PRIORITY, ['provider' => $provider, 'id' => \DummySessionProvider::ID, 'persisted' => true, 'userInfo' => UserInfo::newFromUser($provideUser, true)]); })); $this->initializeManager(); $this->config->set('ReauthenticateTime', []); $this->config->set('AllowSecuritySensitiveOperationIfCannotReauthenticate', []); $provideUser = new \User(); $session = $provider->getManager()->getSessionForRequest($this->request); $this->assertSame(0, $session->getUser()->getId(), 'sanity check'); // Anonymous user => reauth $session->set('AuthManager:lastAuthId', 0); $session->set('AuthManager:lastAuthTimestamp', time() - 5); $this->assertSame($reauth, $this->manager->securitySensitiveOperationStatus('foo')); $provideUser = $user; $session = $provider->getManager()->getSessionForRequest($this->request); $this->assertSame($user->getId(), $session->getUser()->getId(), 'sanity check'); // Error for no default (only gets thrown for non-anonymous user) $session->set('AuthManager:lastAuthId', $user->getId() + 1); $session->set('AuthManager:lastAuthTimestamp', time() - 5); try { $this->manager->securitySensitiveOperationStatus('foo'); $this->fail('Expected exception not thrown'); } catch (\UnexpectedValueException $ex) { $this->assertSame($mutableSession ? '$wgReauthenticateTime lacks a default' : '$wgAllowSecuritySensitiveOperationIfCannotReauthenticate lacks a default', $ex->getMessage()); } if ($mutableSession) { $this->config->set('ReauthenticateTime', ['test' => 100, 'test2' => -1, 'default' => 10]); // Mismatched user ID $session->set('AuthManager:lastAuthId', $user->getId() + 1); $session->set('AuthManager:lastAuthTimestamp', time() - 5); $this->assertSame(AuthManager::SEC_REAUTH, $this->manager->securitySensitiveOperationStatus('foo')); $this->assertSame(AuthManager::SEC_REAUTH, $this->manager->securitySensitiveOperationStatus('test')); $this->assertSame(AuthManager::SEC_OK, $this->manager->securitySensitiveOperationStatus('test2')); // Missing time $session->set('AuthManager:lastAuthId', $user->getId()); $session->set('AuthManager:lastAuthTimestamp', null); $this->assertSame(AuthManager::SEC_REAUTH, $this->manager->securitySensitiveOperationStatus('foo')); $this->assertSame(AuthManager::SEC_REAUTH, $this->manager->securitySensitiveOperationStatus('test')); $this->assertSame(AuthManager::SEC_OK, $this->manager->securitySensitiveOperationStatus('test2')); // Recent enough to pass $session->set('AuthManager:lastAuthTimestamp', time() - 5); $this->assertSame(AuthManager::SEC_OK, $this->manager->securitySensitiveOperationStatus('foo')); // Not recent enough to pass $session->set('AuthManager:lastAuthTimestamp', time() - 20); $this->assertSame(AuthManager::SEC_REAUTH, $this->manager->securitySensitiveOperationStatus('foo')); // But recent enough for the 'test' operation $this->assertSame(AuthManager::SEC_OK, $this->manager->securitySensitiveOperationStatus('test')); } else { $this->config->set('AllowSecuritySensitiveOperationIfCannotReauthenticate', ['test' => false, 'default' => true]); $this->assertEquals(AuthManager::SEC_OK, $this->manager->securitySensitiveOperationStatus('foo')); $this->assertEquals(AuthManager::SEC_FAIL, $this->manager->securitySensitiveOperationStatus('test')); } // Test hook, all three possible values foreach ([AuthManager::SEC_OK => AuthManager::SEC_OK, AuthManager::SEC_REAUTH => $reauth, AuthManager::SEC_FAIL => AuthManager::SEC_FAIL] as $hook => $expect) { $this->hook('SecuritySensitiveOperationStatus', $this->exactly(2))->with($this->anything(), $this->anything(), $this->callback(function ($s) use($session) { return $s->getId() === $session->getId(); }), $mutableSession ? $this->equalTo(500, 1) : $this->equalTo(-1))->will($this->returnCallback(function (&$v) use($hook) { $v = $hook; return true; })); $session->set('AuthManager:lastAuthTimestamp', time() - 500); $this->assertEquals($expect, $this->manager->securitySensitiveOperationStatus('test'), "hook {$hook}"); $this->assertEquals($expect, $this->manager->securitySensitiveOperationStatus('test2'), "hook {$hook}"); $this->unhook('SecuritySensitiveOperationStatus'); } \ScopedCallback::consume($reset); }