public function testGetGlobalSession() { $context = \RequestContext::getMain(); if (!PHPSessionHandler::isInstalled()) { PHPSessionHandler::install(SessionManager::singleton()); } $rProp = new \ReflectionProperty(PHPSessionHandler::class, 'instance'); $rProp->setAccessible(true); $handler = \TestingAccessWrapper::newFromObject($rProp->getValue()); $oldEnable = $handler->enable; $reset[] = new \Wikimedia\ScopedCallback(function () use($handler, $oldEnable) { if ($handler->enable) { session_write_close(); } $handler->enable = $oldEnable; }); $reset[] = TestUtils::setSessionManagerSingleton($this->getManager()); $handler->enable = true; $request = new \FauxRequest(); $context->setRequest($request); $id = $request->getSession()->getId(); session_id(''); $session = SessionManager::getGlobalSession(); $this->assertSame($id, $session->getId()); session_id('xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'); $session = SessionManager::getGlobalSession(); $this->assertSame('xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', $session->getId()); $this->assertSame('xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', $request->getSession()->getId()); session_write_close(); $handler->enable = false; $request = new \FauxRequest(); $context->setRequest($request); $id = $request->getSession()->getId(); session_id(''); $session = SessionManager::getGlobalSession(); $this->assertSame($id, $session->getId()); session_id('xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'); $session = SessionManager::getGlobalSession(); $this->assertSame($id, $session->getId()); $this->assertSame($id, $request->getSession()->getId()); }
public function testClear() { $session = TestUtils::getDummySession(); $priv = \TestingAccessWrapper::newFromObject($session); $backend = $this->getMock('MediaWiki\\Session\\DummySessionBackend', array('canSetUser', 'setUser', 'save')); $backend->expects($this->once())->method('canSetUser')->will($this->returnValue(true)); $backend->expects($this->once())->method('setUser')->with($this->callback(function ($user) { return $user instanceof User && $user->isAnon(); })); $backend->expects($this->once())->method('save'); $priv->backend = $backend; $session->clear(); $this->assertSame(array(), $backend->data); $this->assertTrue($backend->dirty); $backend = $this->getMock('MediaWiki\\Session\\DummySessionBackend', array('canSetUser', 'setUser', 'save')); $backend->data = array(); $backend->expects($this->once())->method('canSetUser')->will($this->returnValue(true)); $backend->expects($this->once())->method('setUser')->with($this->callback(function ($user) { return $user instanceof User && $user->isAnon(); })); $backend->expects($this->once())->method('save'); $priv->backend = $backend; $session->clear(); $this->assertFalse($backend->dirty); $backend = $this->getMock('MediaWiki\\Session\\DummySessionBackend', array('canSetUser', 'setUser', 'save')); $backend->expects($this->once())->method('canSetUser')->will($this->returnValue(false)); $backend->expects($this->never())->method('setUser'); $backend->expects($this->once())->method('save'); $priv->backend = $backend; $session->clear(); $this->assertSame(array(), $backend->data); $this->assertTrue($backend->dirty); }
public function testGetAllowedUserRights() { $provider = $this->getMockForAbstractClass('MediaWiki\\Session\\SessionProvider'); $backend = TestUtils::getDummySessionBackend(); try { $provider->getAllowedUserRights($backend); $this->fail('Expected exception not thrown'); } catch (\InvalidArgumentException $ex) { $this->assertSame('Backend\'s provider isn\'t $this', $ex->getMessage()); } \TestingAccessWrapper::newFromObject($backend)->provider = $provider; $this->assertNull($provider->getAllowedUserRights($backend)); }
/** * Setup SessionManager with a mock session provider * @param bool|null $canChangeUser If non-null, canChangeUser will be mocked to return this * @param array $methods Additional methods to mock * @return array (MediaWiki\Session\SessionProvider, ScopedCallback) */ protected function getMockSessionProvider($canChangeUser = null, array $methods = []) { if (!$this->config) { $this->config = new \HashConfig(); $this->initializeConfig(); } $this->config->set('ObjectCacheSessionExpiry', 100); $methods[] = '__toString'; $methods[] = 'describe'; if ($canChangeUser !== null) { $methods[] = 'canChangeUser'; } $provider = $this->getMockBuilder('DummySessionProvider')->setMethods($methods)->getMock(); $provider->expects($this->any())->method('__toString')->will($this->returnValue('MockSessionProvider')); $provider->expects($this->any())->method('describe')->will($this->returnValue('MockSessionProvider sessions')); if ($canChangeUser !== null) { $provider->expects($this->any())->method('canChangeUser')->will($this->returnValue($canChangeUser)); } $this->config->set('SessionProviders', [['factory' => function () use($provider) { return $provider; }]]); $manager = new \MediaWiki\Session\SessionManager(['config' => $this->config, 'logger' => new \Psr\Log\NullLogger(), 'store' => new \HashBagOStuff()]); \TestingAccessWrapper::newFromObject($manager)->getProvider((string) $provider); $reset = \MediaWiki\Session\TestUtils::setSessionManagerSingleton($manager); if ($this->request) { $manager->getSessionForRequest($this->request); } return [$provider, $reset]; }
public function testAutoCreateUser() { global $wgGroupPermissions; $that = $this; \ObjectCache::$instances[__METHOD__] = new \HashBagOStuff(); $this->setMwGlobals(array('wgMainCacheType' => __METHOD__)); $this->stashMwGlobals(array('wgGroupPermissions')); $wgGroupPermissions['*']['createaccount'] = true; $wgGroupPermissions['*']['autocreateaccount'] = false; // Replace the global singleton with one configured for testing $manager = $this->getManager(); $reset = TestUtils::setSessionManagerSingleton($manager); $logger = new \TestLogger(true, function ($m) { if (substr($m, 0, 15) === 'SessionBackend ') { // Don't care. return null; } $m = str_replace('MediaWiki\\Session\\SessionManager::autoCreateUser: '******'', $m); $m = preg_replace('/ - from: .*$/', ' - from: XXX', $m); return $m; }); $manager->setLogger($logger); $session = SessionManager::getGlobalSession(); // Can't create an already-existing user $user = User::newFromName('UTSysop'); $id = $user->getId(); $this->assertFalse($manager->autoCreateUser($user)); $this->assertSame($id, $user->getId()); $this->assertSame('UTSysop', $user->getName()); $this->assertSame(array(), $logger->getBuffer()); $logger->clearBuffer(); // Sanity check that creation works at all $user = User::newFromName('UTSessionAutoCreate1'); $this->assertSame(0, $user->getId(), 'sanity check'); $this->assertTrue($manager->autoCreateUser($user)); $this->assertNotEquals(0, $user->getId()); $this->assertSame('UTSessionAutoCreate1', $user->getName()); $this->assertEquals($user->getId(), User::idFromName('UTSessionAutoCreate1', User::READ_LATEST)); $this->assertSame(array(array(LogLevel::INFO, 'creating new user (UTSessionAutoCreate1) - from: XXX')), $logger->getBuffer()); $logger->clearBuffer(); // Check lack of permissions $wgGroupPermissions['*']['createaccount'] = false; $wgGroupPermissions['*']['autocreateaccount'] = false; $user = User::newFromName('UTDoesNotExist'); $this->assertFalse($manager->autoCreateUser($user)); $this->assertSame(0, $user->getId()); $this->assertNotSame('UTDoesNotExist', $user->getName()); $this->assertEquals(0, User::idFromName('UTDoesNotExist', User::READ_LATEST)); $session->clear(); $this->assertSame(array(array(LogLevel::DEBUG, 'user is blocked from this wiki, blacklisting')), $logger->getBuffer()); $logger->clearBuffer(); // Check other permission $wgGroupPermissions['*']['createaccount'] = false; $wgGroupPermissions['*']['autocreateaccount'] = true; $user = User::newFromName('UTSessionAutoCreate2'); $this->assertSame(0, $user->getId(), 'sanity check'); $this->assertTrue($manager->autoCreateUser($user)); $this->assertNotEquals(0, $user->getId()); $this->assertSame('UTSessionAutoCreate2', $user->getName()); $this->assertEquals($user->getId(), User::idFromName('UTSessionAutoCreate2', User::READ_LATEST)); $this->assertSame(array(array(LogLevel::INFO, 'creating new user (UTSessionAutoCreate2) - from: XXX')), $logger->getBuffer()); $logger->clearBuffer(); // Test account-creation block $anon = new User(); $block = new \Block(array('address' => $anon->getName(), 'user' => $id, 'reason' => __METHOD__, 'expiry' => time() + 100500, 'createAccount' => true)); $block->insert(); $this->assertInstanceOf('Block', $anon->isBlockedFromCreateAccount(), 'sanity check'); $reset2 = new \ScopedCallback(array($block, 'delete')); $user = User::newFromName('UTDoesNotExist'); $this->assertFalse($manager->autoCreateUser($user)); $this->assertSame(0, $user->getId()); $this->assertNotSame('UTDoesNotExist', $user->getName()); $this->assertEquals(0, User::idFromName('UTDoesNotExist', User::READ_LATEST)); \ScopedCallback::consume($reset2); $session->clear(); $this->assertSame(array(array(LogLevel::DEBUG, 'user is blocked from this wiki, blacklisting')), $logger->getBuffer()); $logger->clearBuffer(); // Sanity check that creation still works $user = User::newFromName('UTSessionAutoCreate3'); $this->assertSame(0, $user->getId(), 'sanity check'); $this->assertTrue($manager->autoCreateUser($user)); $this->assertNotEquals(0, $user->getId()); $this->assertSame('UTSessionAutoCreate3', $user->getName()); $this->assertEquals($user->getId(), User::idFromName('UTSessionAutoCreate3', User::READ_LATEST)); $this->assertSame(array(array(LogLevel::INFO, 'creating new user (UTSessionAutoCreate3) - from: XXX')), $logger->getBuffer()); $logger->clearBuffer(); // Test prevention by AuthPlugin global $wgAuth; $oldWgAuth = $wgAuth; $mockWgAuth = $this->getMock('AuthPlugin', array('autoCreate')); $mockWgAuth->expects($this->once())->method('autoCreate')->will($this->returnValue(false)); $this->setMwGlobals(array('wgAuth' => $mockWgAuth)); $user = User::newFromName('UTDoesNotExist'); $this->assertFalse($manager->autoCreateUser($user)); $this->assertSame(0, $user->getId()); $this->assertNotSame('UTDoesNotExist', $user->getName()); $this->assertEquals(0, User::idFromName('UTDoesNotExist', User::READ_LATEST)); $this->setMwGlobals(array('wgAuth' => $oldWgAuth)); $session->clear(); $this->assertSame(array(array(LogLevel::DEBUG, 'denied by AuthPlugin')), $logger->getBuffer()); $logger->clearBuffer(); // Test prevention by wfReadOnly() $this->setMwGlobals(array('wgReadOnly' => 'Because')); $user = User::newFromName('UTDoesNotExist'); $this->assertFalse($manager->autoCreateUser($user)); $this->assertSame(0, $user->getId()); $this->assertNotSame('UTDoesNotExist', $user->getName()); $this->assertEquals(0, User::idFromName('UTDoesNotExist', User::READ_LATEST)); $this->setMwGlobals(array('wgReadOnly' => false)); $session->clear(); $this->assertSame(array(array(LogLevel::DEBUG, 'denied by wfReadOnly()')), $logger->getBuffer()); $logger->clearBuffer(); // Test prevention by a previous session $session->set('MWSession::AutoCreateBlacklist', 'test'); $user = User::newFromName('UTDoesNotExist'); $this->assertFalse($manager->autoCreateUser($user)); $this->assertSame(0, $user->getId()); $this->assertNotSame('UTDoesNotExist', $user->getName()); $this->assertEquals(0, User::idFromName('UTDoesNotExist', User::READ_LATEST)); $session->clear(); $this->assertSame(array(array(LogLevel::DEBUG, 'blacklisted in session (test)')), $logger->getBuffer()); $logger->clearBuffer(); // Test uncreatable name $user = User::newFromName('UTDoesNotExist@'); $this->assertFalse($manager->autoCreateUser($user)); $this->assertSame(0, $user->getId()); $this->assertNotSame('UTDoesNotExist@', $user->getName()); $this->assertEquals(0, User::idFromName('UTDoesNotExist', User::READ_LATEST)); $session->clear(); $this->assertSame(array(array(LogLevel::DEBUG, 'Invalid username, blacklisting')), $logger->getBuffer()); $logger->clearBuffer(); // Test AbortAutoAccount hook $mock = $this->getMock(__CLASS__, array('onAbortAutoAccount')); $mock->expects($this->once())->method('onAbortAutoAccount')->will($this->returnCallback(function (User $user, &$msg) { $msg = 'No way!'; return false; })); $this->mergeMwGlobalArrayValue('wgHooks', array('AbortAutoAccount' => array($mock))); $user = User::newFromName('UTDoesNotExist'); $this->assertFalse($manager->autoCreateUser($user)); $this->assertSame(0, $user->getId()); $this->assertNotSame('UTDoesNotExist', $user->getName()); $this->assertEquals(0, User::idFromName('UTDoesNotExist', User::READ_LATEST)); $this->mergeMwGlobalArrayValue('wgHooks', array('AbortAutoAccount' => array())); $session->clear(); $this->assertSame(array(array(LogLevel::DEBUG, 'denied by hook: No way!')), $logger->getBuffer()); $logger->clearBuffer(); // Test AbortAutoAccount hook screwing up the name $mock = $this->getMock('stdClass', array('onAbortAutoAccount')); $mock->expects($this->once())->method('onAbortAutoAccount')->will($this->returnCallback(function (User $user) { $user->setName('UTDoesNotExistEither'); })); $this->mergeMwGlobalArrayValue('wgHooks', array('AbortAutoAccount' => array($mock))); try { $user = User::newFromName('UTDoesNotExist'); $manager->autoCreateUser($user); $this->fail('Expected exception not thrown'); } catch (\UnexpectedValueException $ex) { $this->assertSame('AbortAutoAccount hook tried to change the user name', $ex->getMessage()); } $this->assertSame(0, $user->getId()); $this->assertNotSame('UTDoesNotExist', $user->getName()); $this->assertNotSame('UTDoesNotExistEither', $user->getName()); $this->assertEquals(0, User::idFromName('UTDoesNotExist', User::READ_LATEST)); $this->assertEquals(0, User::idFromName('UTDoesNotExistEither', User::READ_LATEST)); $this->mergeMwGlobalArrayValue('wgHooks', array('AbortAutoAccount' => array())); $session->clear(); $this->assertSame(array(), $logger->getBuffer()); $logger->clearBuffer(); // Test for "exception backoff" $user = User::newFromName('UTDoesNotExist'); $cache = \ObjectCache::getLocalClusterInstance(); $backoffKey = wfMemcKey('MWSession', 'autocreate-failed', md5($user->getName())); $cache->set($backoffKey, 1, 60 * 10); $this->assertFalse($manager->autoCreateUser($user)); $this->assertSame(0, $user->getId()); $this->assertNotSame('UTDoesNotExist', $user->getName()); $this->assertEquals(0, User::idFromName('UTDoesNotExist', User::READ_LATEST)); $cache->delete($backoffKey); $session->clear(); $this->assertSame(array(array(LogLevel::DEBUG, 'denied by prior creation attempt failures')), $logger->getBuffer()); $logger->clearBuffer(); // Sanity check that creation still works, and test completion hook $cb = $this->callback(function (User $user) use($that) { $that->assertNotEquals(0, $user->getId()); $that->assertSame('UTSessionAutoCreate4', $user->getName()); $that->assertEquals($user->getId(), User::idFromName('UTSessionAutoCreate4', User::READ_LATEST)); return true; }); $mock = $this->getMock('stdClass', array('onAuthPluginAutoCreate', 'onLocalUserCreated')); $mock->expects($this->once())->method('onAuthPluginAutoCreate')->with($cb); $mock->expects($this->once())->method('onLocalUserCreated')->with($cb, $this->identicalTo(true)); $this->mergeMwGlobalArrayValue('wgHooks', array('AuthPluginAutoCreate' => array($mock), 'LocalUserCreated' => array($mock))); $user = User::newFromName('UTSessionAutoCreate4'); $this->assertSame(0, $user->getId(), 'sanity check'); $this->assertTrue($manager->autoCreateUser($user)); $this->assertNotEquals(0, $user->getId()); $this->assertSame('UTSessionAutoCreate4', $user->getName()); $this->assertEquals($user->getId(), User::idFromName('UTSessionAutoCreate4', User::READ_LATEST)); $this->mergeMwGlobalArrayValue('wgHooks', array('AuthPluginAutoCreate' => array(), 'LocalUserCreated' => array())); $this->assertSame(array(array(LogLevel::INFO, 'creating new user (UTSessionAutoCreate4) - from: XXX')), $logger->getBuffer()); $logger->clearBuffer(); }
public function testResetIdOfGlobalSession() { if (!PHPSessionHandler::isInstalled()) { PHPSessionHandler::install(SessionManager::singleton()); } if (!PHPSessionHandler::isEnabled()) { $rProp = new \ReflectionProperty('MediaWiki\\Session\\PHPSessionHandler', 'instance'); $rProp->setAccessible(true); $handler = \TestingAccessWrapper::newFromObject($rProp->getValue()); $resetHandler = new \ScopedCallback(function () use($handler) { session_write_close(); $handler->enable = false; }); $handler->enable = true; } $backend = $this->getBackend(User::newFromName('UTSysop')); \TestingAccessWrapper::newFromObject($backend)->usePhpSessionHandling = true; TestUtils::setSessionManagerSingleton($this->manager); $manager = \TestingAccessWrapper::newFromObject($this->manager); $request = \RequestContext::getMain()->getRequest(); $manager->globalSession = $backend->getSession($request); $manager->globalSessionRequest = $request; session_id(self::SESSIONID); \MediaWiki\quietCall('session_start'); $backend->resetId(); $this->assertNotEquals(self::SESSIONID, $backend->getId()); $this->assertSame($backend->getId(), session_id()); session_write_close(); session_id(''); $this->assertNotSame($backend->getId(), session_id(), 'sanity check'); $backend->persist(); $this->assertSame($backend->getId(), session_id()); session_write_close(); }
public function testGetAllowedUserRights() { $logger = new \TestLogger(true); $provider = $this->getProvider(); $provider->setLogger($logger); $backend = TestUtils::getDummySessionBackend(); $backendPriv = \TestingAccessWrapper::newFromObject($backend); try { $provider->getAllowedUserRights($backend); $this->fail('Expected exception not thrown'); } catch (\InvalidArgumentException $ex) { $this->assertSame('Backend\'s provider isn\'t $this', $ex->getMessage()); } $backendPriv->provider = $provider; $backendPriv->providerMetadata = ['rights' => ['foo', 'bar', 'baz']]; $this->assertSame(['foo', 'bar', 'baz'], $provider->getAllowedUserRights($backend)); $this->assertSame([], $logger->getBuffer()); $backendPriv->providerMetadata = ['foo' => 'bar']; $this->assertSame([], $provider->getAllowedUserRights($backend)); $this->assertSame([[LogLevel::DEBUG, 'MediaWiki\\Session\\BotPasswordSessionProvider::getAllowedUserRights: ' . 'No provider metadata, returning no rights allowed']], $logger->getBuffer()); $logger->clearBuffer(); $backendPriv->providerMetadata = ['rights' => 'bar']; $this->assertSame([], $provider->getAllowedUserRights($backend)); $this->assertSame([[LogLevel::DEBUG, 'MediaWiki\\Session\\BotPasswordSessionProvider::getAllowedUserRights: ' . 'No provider metadata, returning no rights allowed']], $logger->getBuffer()); $logger->clearBuffer(); $backendPriv->providerMetadata = null; $this->assertSame([], $provider->getAllowedUserRights($backend)); $this->assertSame([[LogLevel::DEBUG, 'MediaWiki\\Session\\BotPasswordSessionProvider::getAllowedUserRights: ' . 'No provider metadata, returning no rights allowed']], $logger->getBuffer()); $logger->clearBuffer(); }
public function testSecrets() { $logger = new \TestLogger(); $session = TestUtils::getDummySession(null, -1, $logger); // Simple defaulting $this->assertEquals('defaulted', $session->getSecret('test', 'defaulted')); // Bad encrypted data $session->set('test', 'foobar'); $logger->setCollect(true); $this->assertEquals('defaulted', $session->getSecret('test', 'defaulted')); $logger->setCollect(false); $this->assertSame([[LogLevel::WARNING, 'Invalid sealed-secret format']], $logger->getBuffer()); $logger->clearBuffer(); // Tampered data $session->setSecret('test', 'foobar'); $encrypted = $session->get('test'); $session->set('test', $encrypted . 'x'); $logger->setCollect(true); $this->assertEquals('defaulted', $session->getSecret('test', 'defaulted')); $logger->setCollect(false); $this->assertSame([[LogLevel::WARNING, 'Sealed secret has been tampered with, aborting.']], $logger->getBuffer()); $logger->clearBuffer(); // Unserializable data $iv = \MWCryptRand::generate(16, true); list($encKey, $hmacKey) = \TestingAccessWrapper::newFromObject($session)->getSecretKeys(); $ciphertext = openssl_encrypt('foobar', 'aes-256-ctr', $encKey, OPENSSL_RAW_DATA, $iv); $sealed = base64_encode($iv) . '.' . base64_encode($ciphertext); $hmac = hash_hmac('sha256', $sealed, $hmacKey, true); $encrypted = base64_encode($hmac) . '.' . $sealed; $session->set('test', $encrypted); \MediaWiki\suppressWarnings(); $this->assertEquals('defaulted', $session->getSecret('test', 'defaulted')); \MediaWiki\restoreWarnings(); }