private function getProvider($name = null, $prefix = null) { global $wgSessionProviders; $params = ['priority' => 40, 'sessionCookieName' => $name, 'sessionCookieOptions' => []]; if ($prefix !== null) { $params['sessionCookieOptions']['prefix'] = $prefix; } if (!$this->config) { $this->config = new \HashConfig(['CookiePrefix' => 'wgCookiePrefix', 'EnableBotPasswords' => true, 'BotPasswordsDatabase' => false, 'SessionProviders' => $wgSessionProviders + [BotPasswordSessionProvider::class => ['class' => BotPasswordSessionProvider::class, 'args' => [$params]]]]); } $manager = new SessionManager(['config' => new \MultiConfig([$this->config, \RequestContext::getMain()->getConfig()]), 'logger' => new \Psr\Log\NullLogger(), 'store' => new TestBagOStuff()]); return $manager->getProvider(BotPasswordSessionProvider::class); }
private function getProvider($name = null, $prefix = null) { global $wgSessionProviders; $params = array('priority' => 40, 'sessionCookieName' => $name, 'sessionCookieOptions' => array()); if ($prefix !== null) { $params['sessionCookieOptions']['prefix'] = $prefix; } if (!$this->config) { $this->config = new \HashConfig(array('CookiePrefix' => 'wgCookiePrefix', 'EnableBotPasswords' => true, 'BotPasswordsDatabase' => false, 'SessionProviders' => $wgSessionProviders + array('MediaWiki\\Session\\BotPasswordSessionProvider' => array('class' => 'MediaWiki\\Session\\BotPasswordSessionProvider', 'args' => array($params))))); } $manager = new SessionManager(array('config' => new \MultiConfig(array($this->config, \RequestContext::getMain()->getConfig())), 'logger' => new \Psr\Log\NullLogger(), 'store' => new TestBagOStuff())); return $manager->getProvider('MediaWiki\\Session\\BotPasswordSessionProvider'); }
public function execute($subPage) { $this->setHeaders(); $this->loadAuth($subPage); $this->outputHeader(); $status = $this->trySubmit(); if ($status === false || !$status->isOK()) { $this->displayForm($status); return; } /** @var AuthenticationResponse $response */ $response = $status->getValue(); if ($response->status === AuthenticationResponse::FAIL) { $this->displayForm(StatusValue::newFatal($response->message)); return; } $status = StatusValue::newGood(); $status->warning(wfMessage('unlinkaccounts-success')); $this->loadAuth($subPage, null, true); // update requests so the unlinked one doesn't show up // Reset sessions - if the user unlinked an account because it was compromised, // log attackers out from sessions obtained via that account. $session = $this->getRequest()->getSession(); $user = $this->getUser(); SessionManager::singleton()->invalidateSessionsForUser($user); $session->setUser($user); $session->resetId(); $this->displayForm($status); }
public function provideSessionInfo(WebRequest $request) { $info = array('id' => $this->getCookie($request, $this->params['sessionName'], '')); if (!SessionManager::validateSessionId($info['id'])) { unset($info['id']); } list($userId, $userName, $token) = $this->getUserInfoFromCookies($request); if ($userId !== null) { try { $userInfo = UserInfo::newFromId($userId); } catch (\InvalidArgumentException $ex) { return null; } // Sanity check if ($userName !== null && $userInfo->getName() !== $userName) { return null; } if ($token !== null) { if (!hash_equals($userInfo->getToken(), $token)) { return null; } $info['userInfo'] = $userInfo->verified(); } elseif (isset($info['id'])) { // No point if no session ID $info['userInfo'] = $userInfo; } } if (!$info) { return null; } $info += array('provider' => $this, 'persisted' => isset($info['id']), 'forceHTTPS' => $this->getCookie($request, 'forceHTTPS', '', false)); return new SessionInfo($this->priority, $info); }
/** * Get the session ID from the cookie, if any. * * Only call this if $this->sessionCookieName !== null. If * sessionCookieName is null, do some logic (probably involving a call to * $this->hashToSessionId()) to create the single session ID corresponding * to this WebRequest instead of calling this method. * * @param WebRequest $request * @return string|null */ protected function getSessionIdFromCookie(WebRequest $request) { if ($this->sessionCookieName === null) { throw new \BadMethodCallException(__METHOD__ . ' may not be called when $this->sessionCookieName === null'); } $prefix = isset($this->sessionCookieOptions['prefix']) ? $this->sessionCookieOptions['prefix'] : $this->config->get('CookiePrefix'); $id = $request->getCookie($this->sessionCookieName, $prefix); return SessionManager::validateSessionId($id) ? $id : null; }
/** * Destroy a session * @private For internal use only * @param string $id Session id * @return bool Success */ public function destroy($id) { if (self::$instance !== $this) { throw new \UnexpectedValueException(__METHOD__ . ': Wrong instance called!'); } if (!$this->enable) { throw new \BadMethodCallException('Attempt to use PHP session management'); } $session = $this->manager->getSessionById($id, false); if ($session) { $session->clear(); } return true; }
public function provideSessionInfo(WebRequest $request) { $sessionId = $this->getCookie($request, $this->params['sessionName'], ''); $info = ['provider' => $this, 'forceHTTPS' => $this->getCookie($request, 'forceHTTPS', '', false)]; if (SessionManager::validateSessionId($sessionId)) { $info['id'] = $sessionId; $info['persisted'] = true; } list($userId, $userName, $token) = $this->getUserInfoFromCookies($request); if ($userId !== null) { try { $userInfo = UserInfo::newFromId($userId); } catch (\InvalidArgumentException $ex) { return null; } // Sanity check if ($userName !== null && $userInfo->getName() !== $userName) { $this->logger->warning('Session "{session}" requested with mismatched UserID and UserName cookies.', ['session' => $sessionId, 'mismatch' => ['userid' => $userId, 'cookie_username' => $userName, 'username' => $userInfo->getName()]]); return null; } if ($token !== null) { if (!hash_equals($userInfo->getToken(), $token)) { $this->logger->warning('Session "{session}" requested with invalid Token cookie.', ['session' => $sessionId, 'userid' => $userId, 'username' => $userInfo->getName()]); return null; } $info['userInfo'] = $userInfo->verified(); $info['persisted'] = true; // If we have user+token, it should be } elseif (isset($info['id'])) { $info['userInfo'] = $userInfo; } else { // No point in returning, loadSessionInfoFromStore() will // reject it anyway. return null; } } elseif (isset($info['id'])) { // No UserID cookie, so insist that the session is anonymous. // Note: this event occurs for several normal activities: // * anon visits Special:UserLogin // * anon browsing after seeing Special:UserLogin // * anon browsing after edit or preview $this->logger->debug('Session "{session}" requested without UserID cookie', ['session' => $info['id']]); $info['userInfo'] = UserInfo::newAnonymous(); } else { // No session ID and no user is the same as an empty session, so // there's no point. return null; } return new SessionInfo($this->priority, $info); }
public function provideSessionInfo(WebRequest $request) { $info = array('id' => $this->getCookie($request, $this->params['sessionName'], ''), 'provider' => $this, 'forceHTTPS' => $this->getCookie($request, 'forceHTTPS', '', false)); if (!SessionManager::validateSessionId($info['id'])) { unset($info['id']); } $info['persisted'] = isset($info['id']); list($userId, $userName, $token) = $this->getUserInfoFromCookies($request); if ($userId !== null) { try { $userInfo = UserInfo::newFromId($userId); } catch (\InvalidArgumentException $ex) { return null; } // Sanity check if ($userName !== null && $userInfo->getName() !== $userName) { return null; } if ($token !== null) { if (!hash_equals($userInfo->getToken(), $token)) { return null; } $info['userInfo'] = $userInfo->verified(); } elseif (isset($info['id'])) { $info['userInfo'] = $userInfo; } else { // No point in returning, loadSessionInfoFromStore() will // reject it anyway. return null; } } elseif (isset($info['id'])) { // No UserID cookie, so insist that the session is anonymous. $info['userInfo'] = UserInfo::newAnonymous(); } else { // No session ID and no user is the same as an empty session, so // there's no point. return null; } return new SessionInfo($this->priority, $info); }
/** * @param array $data Array of *non*-urlencoded key => value pairs, the * fake GET/POST values * @param bool $wasPosted Whether to treat the data as POST * @param MediaWiki\\Session\\Session|array|null $session Session, session * data array, or null * @param string $protocol 'http' or 'https' * @throws MWException */ public function __construct($data = array(), $wasPosted = false, $session = null, $protocol = 'http') { $this->requestTime = microtime(true); if (is_array($data)) { $this->data = $data; } else { throw new MWException("FauxRequest() got bogus data"); } $this->wasPosted = $wasPosted; if ($session instanceof MediaWiki\Session\Session) { $this->sessionId = $session->getSessionId(); } elseif (is_array($session)) { $mwsession = SessionManager::singleton()->getEmptySession($this); $this->sessionId = $mwsession->getSessionId(); foreach ($session as $key => $value) { $mwsession->set($key, $value); } } elseif ($session !== null) { throw new MWException("FauxRequest() got bogus session"); } $this->protocol = $protocol; }
public function execute() { $username = $this->getOption('user'); $file = $this->getOption('file'); if ($username === null && $file === null) { $this->error('Either --user or --file is required', 1); } elseif ($username !== null && $file !== null) { $this->error('Cannot use both --user and --file', 1); } if ($username !== null) { $usernames = [$username]; } else { $usernames = is_readable($file) ? file($file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES) : false; if ($usernames === false) { $this->error("Could not open {$file}", 2); } } $i = 0; $lbFactory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory(); $sessionManager = SessionManager::singleton(); foreach ($usernames as $username) { $i++; $user = User::newFromName($username); try { $sessionManager->invalidateSessionsForUser($user); if ($user->getId()) { $this->output("Invalidated sessions for user {$username}\n"); } else { # session invalidation might still work if there is a central identity provider $this->output("Could not find user {$username}, tried to invalidate anyway\n"); } } catch (Exception $e) { $this->output("Failed to invalidate sessions for user {$username} | " . str_replace(["\r", "\n"], ' ', $e->getMessage()) . "\n"); } if ($i % $this->mBatchSize) { $lbFactory->waitForReplication(); } } }
protected function success() { $session = $this->getRequest()->getSession(); $user = $this->getUser(); $out = $this->getOutput(); $returnUrl = $this->getReturnUrl(); // change user token and update the session SessionManager::singleton()->invalidateSessionsForUser($user); $session->setUser($user); $session->resetId(); if ($returnUrl) { $out->redirect($returnUrl); } else { // messages used: changecredentials-success removecredentials-success $out->wrapWikiMsg("<div class=\"successbox\">\n\$1\n</div>", static::$messagePrefix . '-success'); $out->returnToMain(); } }
static function setupSession($sessionId = false) { SessionManager::getGlobalSession()->persist(); }
/** * Initialise php session * * @deprecated since 1.27, use MediaWiki\Session\SessionManager instead. * Generally, "using" SessionManager will be calling ->getSessionById() or * ::getGlobalSession() (depending on whether you were passing $sessionId * here), then calling $session->persist(). * @param bool|string $sessionId */ function wfSetupSession($sessionId = false) { wfDeprecated(__FUNCTION__, '1.27'); if ($sessionId) { session_id($sessionId); } $session = SessionManager::getGlobalSession(); $session->persist(); if (session_id() !== $session->getId()) { session_id($session->getId()); } MediaWiki\quietCall('session_start'); }
public function testGetLoginCookieExpiration() { $config = $this->getConfig(); $provider = \TestingAccessWrapper::newFromObject(new CookieSessionProvider(['priority' => 10])); $provider->setLogger(new \Psr\Log\NullLogger()); $provider->setConfig($config); $provider->setManager(SessionManager::singleton()); $this->assertSame(200, $provider->getLoginCookieExpiration('Token')); $this->assertSame(100, $provider->getLoginCookieExpiration('User')); $config->set('ExtendedLoginCookieExpiration', null); $this->assertSame(100, $provider->getLoginCookieExpiration('Token')); $this->assertSame(100, $provider->getLoginCookieExpiration('User')); }
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(); }
/** * This should kill the session as hard as possible. * It will leave the cookie behind, but everything it could possibly * reference will be gone. */ public function session_killAllEverything() { if ($this->isBatchProcessor()) { return; } SessionManager::getGlobalSession()->clear(); }
/** * @param int $priority Session priority * @param array $data * - provider: (SessionProvider|null) If not given, the provider will be * determined from the saved session data. * - id: (string|null) Session ID * - userInfo: (UserInfo|null) User known from the request. If * $provider->canChangeUser() is false, a verified user * must be provided. * - persisted: (bool) Whether this session was persisted * - remembered: (bool) Whether the verified user was remembered. * Defaults to true. * - forceHTTPS: (bool) Whether to force HTTPS for this session * - metadata: (array) Provider metadata, to be returned by * Session::getProviderMetadata(). * - idIsSafe: (bool) Set true if the 'id' did not come from the user. * Generally you'll use this from SessionProvider::newEmptySession(), * and not from any other method. * - copyFrom: (SessionInfo) SessionInfo to copy other data items from. */ public function __construct($priority, array $data) { if ($priority < self::MIN_PRIORITY || $priority > self::MAX_PRIORITY) { throw new \InvalidArgumentException('Invalid priority'); } if (isset($data['copyFrom'])) { $from = $data['copyFrom']; if (!$from instanceof SessionInfo) { throw new \InvalidArgumentException('Invalid copyFrom'); } $data += array('provider' => $from->provider, 'id' => $from->id, 'userInfo' => $from->userInfo, 'persisted' => $from->persisted, 'remembered' => $from->remembered, 'forceHTTPS' => $from->forceHTTPS, 'metadata' => $from->providerMetadata, 'idIsSafe' => $from->idIsSafe); // @codeCoverageIgnoreEnd } else { $data += array('provider' => null, 'id' => null, 'userInfo' => null, 'persisted' => false, 'remembered' => true, 'forceHTTPS' => false, 'metadata' => null, 'idIsSafe' => false); // @codeCoverageIgnoreEnd } if ($data['id'] !== null && !SessionManager::validateSessionId($data['id'])) { throw new \InvalidArgumentException('Invalid session ID'); } if ($data['userInfo'] !== null && !$data['userInfo'] instanceof UserInfo) { throw new \InvalidArgumentException('Invalid userInfo'); } if (!$data['provider'] && $data['id'] === null) { throw new \InvalidArgumentException('Must supply an ID when no provider is given'); } if ($data['metadata'] !== null && !is_array($data['metadata'])) { throw new \InvalidArgumentException('Invalid metadata'); } $this->provider = $data['provider']; if ($data['id'] !== null) { $this->id = $data['id']; $this->idIsSafe = $data['idIsSafe']; } else { $this->id = $this->provider->getManager()->generateSessionId(); $this->idIsSafe = true; } $this->priority = (int) $priority; $this->userInfo = $data['userInfo']; $this->persisted = (bool) $data['persisted']; if ($data['provider'] !== null) { if ($this->userInfo !== null && !$this->userInfo->isAnon() && $this->userInfo->isVerified()) { $this->remembered = (bool) $data['remembered']; } $this->providerMetadata = $data['metadata']; } $this->forceHTTPS = (bool) $data['forceHTTPS']; }
/** * Check if all users may be assumed to have the given permission * * We generally assume so if the right is granted to '*' and isn't revoked * on any group. It doesn't attempt to take grants or other extension * limitations on rights into account in the general case, though, as that * would require it to always return false and defeat the purpose. * Specifically, session-based rights restrictions (such as OAuth or bot * passwords) are applied based on the current session. * * @since 1.22 * @param string $right Right to check * @return bool */ public static function isEveryoneAllowed($right) { global $wgGroupPermissions, $wgRevokePermissions; static $cache = array(); // Use the cached results, except in unit tests which rely on // being able change the permission mid-request if (isset($cache[$right]) && !defined('MW_PHPUNIT_TEST')) { return $cache[$right]; } if (!isset($wgGroupPermissions['*'][$right]) || !$wgGroupPermissions['*'][$right]) { $cache[$right] = false; return false; } // If it's revoked anywhere, then everyone doesn't have it foreach ($wgRevokePermissions as $rights) { if (isset($rights[$right]) && $rights[$right]) { $cache[$right] = false; return false; } } // Remove any rights that aren't allowed to the global-session user $allowedRights = SessionManager::getGlobalSession()->getAllowedUserRights(); if ($allowedRights !== null && !in_array($right, $allowedRights, true)) { $cache[$right] = false; return false; } // Allow extensions to say false if (!Hooks::run('UserIsEveryoneAllowed', array($right))) { $cache[$right] = false; return false; } $cache[$right] = true; return true; }
/** * @param string|null $subPage */ public function execute($subPage) { $authManager = AuthManager::singleton(); $session = SessionManager::getGlobalSession(); // Session data is used for various things in the authentication process, so we must make // sure a session cookie or some equivalent mechanism is set. $session->persist(); $this->load($subPage); $this->setHeaders(); $this->checkPermissions(); // Make sure it's possible to log in if (!$this->isSignup() && !$session->canSetUser()) { throw new ErrorPageError('cannotloginnow-title', 'cannotloginnow-text', [$session->getProvider()->describe(RequestContext::getMain()->getLanguage())]); } /* * In the case where the user is already logged in, and was redirected to * the login form from a page that requires login, do not show the login * page. The use case scenario for this is when a user opens a large number * of tabs, is redirected to the login page on all of them, and then logs * in on one, expecting all the others to work properly. * * However, do show the form if it was visited intentionally (no 'returnto' * is present). People who often switch between several accounts have grown * accustomed to this behavior. * * Also make an exception when force=<level> is set in the URL, which means the user must * reauthenticate for security reasons. */ if (!$this->isSignup() && !$this->mPosted && !$this->securityLevel && ($this->mReturnTo !== '' || $this->mReturnToQuery !== '') && $this->getUser()->isLoggedIn()) { $this->successfulAction(); } // If logging in and not on HTTPS, either redirect to it or offer a link. global $wgSecureLogin; if ($this->getRequest()->getProtocol() !== 'https') { $title = $this->getFullTitle(); $query = $this->getPreservedParams(false) + ['title' => null, $this->mEntryErrorType === 'error' ? 'error' : 'warning' => $this->mEntryError] + $this->getRequest()->getQueryValues(); $url = $title->getFullURL($query, false, PROTO_HTTPS); if ($wgSecureLogin && !$this->mFromHTTP && wfCanIPUseHTTPS($this->getRequest()->getIP())) { // Avoid infinite redirect $url = wfAppendQuery($url, 'fromhttp=1'); $this->getOutput()->redirect($url); // Since we only do this redir to change proto, always vary $this->getOutput()->addVaryHeader('X-Forwarded-Proto'); return; } else { // A wiki without HTTPS login support should set $wgServer to // http://somehost, in which case the secure URL generated // above won't actually start with https:// if (substr($url, 0, 8) === 'https://') { $this->mSecureLoginUrl = $url; } } } if (!$this->isActionAllowed($this->authAction)) { // FIXME how do we explain this to the user? can we handle session loss better? // messages used: authpage-cannot-login, authpage-cannot-login-continue, // authpage-cannot-create, authpage-cannot-create-continue $this->mainLoginForm([], 'authpage-cannot-' . $this->authAction); return; } $status = $this->trySubmit(); if (!$status || !$status->isGood()) { $this->mainLoginForm($this->authRequests, $status ? $status->getMessage() : '', 'error'); return; } /** @var AuthenticationResponse $response */ $response = $status->getValue(); $returnToUrl = $this->getPageTitle('return')->getFullURL($this->getPreservedParams(true), false, PROTO_HTTPS); switch ($response->status) { case AuthenticationResponse::PASS: $this->logAuthResult(true); $this->proxyAccountCreation = $this->isSignup() && !$this->getUser()->isAnon(); $this->targetUser = User::newFromName($response->username); if (!$this->proxyAccountCreation && $response->loginRequest && $authManager->canAuthenticateNow()) { // successful registration; log the user in instantly $response2 = $authManager->beginAuthentication([$response->loginRequest], $returnToUrl); if ($response2->status !== AuthenticationResponse::PASS) { LoggerFactory::getInstance('login')->error('Could not log in after account creation'); $this->successfulAction(true, Status::newFatal('createacct-loginerror')); break; } } if (!$this->proxyAccountCreation) { // Ensure that the context user is the same as the session user. $this->setSessionUserForCurrentRequest(); } $this->successfulAction(true); break; case AuthenticationResponse::FAIL: // fall through // fall through case AuthenticationResponse::RESTART: unset($this->authForm); if ($response->status === AuthenticationResponse::FAIL) { $action = $this->getDefaultAction($subPage); $messageType = 'error'; } else { $action = $this->getContinueAction($this->authAction); $messageType = 'warning'; } $this->logAuthResult(false, $response->message ? $response->message->getKey() : '-'); $this->loadAuth($subPage, $action, true); $this->mainLoginForm($this->authRequests, $response->message, $messageType); break; case AuthenticationResponse::REDIRECT: unset($this->authForm); $this->getOutput()->redirect($response->redirectTarget); break; case AuthenticationResponse::UI: unset($this->authForm); $this->authAction = $this->isSignup() ? AuthManager::ACTION_CREATE_CONTINUE : AuthManager::ACTION_LOGIN_CONTINUE; $this->authRequests = $response->neededRequests; $this->mainLoginForm($response->neededRequests, $response->message, 'warning'); break; default: throw new LogicException('invalid AuthenticationResponse'); } }
public function testSetLoggedOutCookie() { $provider = \TestingAccessWrapper::newFromObject(new CookieSessionProvider(array('priority' => 1, 'sessionName' => 'MySessionName', 'cookieOptions' => array('prefix' => 'x')))); $provider->setLogger(new \Psr\Log\NullLogger()); $provider->setConfig($this->getConfig()); $provider->setManager(SessionManager::singleton()); $t1 = time(); $t2 = time() - 86400 * 2; // Set it $request = new \FauxRequest(); $provider->setLoggedOutCookie($t1, $request); $this->assertSame((string) $t1, $request->response()->getCookie('xLoggedOut')); // Too old $request = new \FauxRequest(); $provider->setLoggedOutCookie($t2, $request); $this->assertSame(null, $request->response()->getCookie('xLoggedOut')); // Don't reset if it's already set $request = new \FauxRequest(); $request->setCookies(array('xLoggedOut' => $t1), ''); $provider->setLoggedOutCookie($t1, $request); $this->assertSame(null, $request->response()->getCookie('xLoggedOut')); }
/** * Renew the user's session id, using strong entropy */ private function renewSessionId() { global $wgSecureLogin, $wgCookieSecure; if ($wgSecureLogin && !$this->mStickHTTPS) { $wgCookieSecure = false; } SessionManager::getGlobalSession()->resetId(); }
/** * For backwards compatibility, open the PHP session when the global * session is persisted */ private function checkPHPSession() { if (!$this->checkPHPSessionRecursionGuard) { $this->checkPHPSessionRecursionGuard = true; $reset = new \ScopedCallback(function () { $this->checkPHPSessionRecursionGuard = false; }); if ($this->usePhpSessionHandling && session_id() === '' && PHPSessionHandler::isEnabled() && SessionManager::getGlobalSession()->getId() === (string) $this->id) { $this->logger->debug('SessionBackend "{session}" Taking over PHP session', ['session' => $this->id]); session_id((string) $this->id); \MediaWiki\quietCall('session_start'); } } }
/** * For backwards compatibility, open the PHP session when the global * session is persisted */ private function checkPHPSession() { if (!$this->checkPHPSessionRecursionGuard) { $this->checkPHPSessionRecursionGuard = true; $ref =& $this->checkPHPSessionRecursionGuard; $reset = new \ScopedCallback(function () use(&$ref) { $ref = false; }); if ($this->usePhpSessionHandling && session_id() === '' && PHPSessionHandler::isEnabled() && SessionManager::getGlobalSession()->getId() === (string) $this->id) { $this->logger->debug("SessionBackend {$this->id}: Taking over PHP session"); session_id((string) $this->id); \MediaWiki\quietCall('session_cache_limiter', 'private, must-revalidate'); \MediaWiki\quietCall('session_start'); } } }
/** * @param User $user * @param bool $useContextLang Use 'uselang' to set the user's language */ private function setDefaultUserOptions(User $user, $useContextLang) { global $wgContLang; \MediaWiki\Session\SessionManager::singleton()->invalidateSessionsForUser($user); $lang = $useContextLang ? \RequestContext::getMain()->getLanguage() : $wgContLang; $user->setOption('language', $lang->getPreferredVariant()); if ($wgContLang->hasVariants()) { $user->setOption('variant', $wgContLang->getPreferredVariant()); } }
/** * Return the session for this request * @since 1.27 * @note For performance, keep the session locally if you will be making * much use of it instead of calling this method repeatedly. * @return Session */ public function getSession() { if ($this->sessionId !== null) { $session = SessionManager::singleton()->getSessionById((string) $this->sessionId, true, $this); if ($session) { return $session; } } $session = SessionManager::singleton()->getSessionForRequest($this); $this->sessionId = $session->getSessionId(); return $session; }
/** * Initialise php session * * @deprecated since 1.27, use MediaWiki\\Session\\SessionManager instead. * Generally, "using" SessionManager will be calling ->getSessionById() or * ::getGlobalSession() (depending on whether you were passing $sessionId * here), then calling $session->persist(). * @param bool|string $sessionId */ function wfSetupSession($sessionId = false) { wfDeprecated(__FUNCTION__, '1.27'); // If they're calling this, they probably want our session management even // if NO_SESSION was set for Setup.php. if (!MediaWiki\Session\PHPSessionHandler::isInstalled()) { MediaWiki\Session\PHPSessionHandler::install(SessionManager::singleton()); } if ($sessionId) { session_id($sessionId); } $session = SessionManager::getGlobalSession(); $session->persist(); if (session_id() !== $session->getId()) { session_id($session->getId()); } MediaWiki\quietCall('session_cache_limiter', 'private, must-revalidate'); MediaWiki\quietCall('session_start'); }
/** * Returns true if the request has a persistent session. * This does not necessarily mean that the user is logged in! * * @deprecated since 1.27, use * \\MediaWiki\\Session\\SessionManager::singleton()->getPersistedSessionId() * instead. * @return bool */ public function checkSessionCookie() { wfDeprecated(__METHOD__, '1.27'); return SessionManager::singleton()->getPersistedSessionId($this) !== null; }
public function testGenerateSessionId() { $manager = $this->getManager(); $id = $manager->generateSessionId(); $this->assertTrue(SessionManager::validateSessionId($id), "Generated ID: {$id}"); }
/** * Send cache control HTTP headers */ public function sendCacheControl() { $response = $this->getRequest()->response(); $config = $this->getConfig(); if ($config->get('UseETag') && $this->mETag) { $response->header("ETag: {$this->mETag}"); } $this->addVaryHeader('Cookie'); $this->addAcceptLanguage(); # don't serve compressed data to clients who can't handle it # maintain different caches for logged-in users and non-logged in ones $response->header($this->getVaryHeader()); if ($config->get('UseKeyHeader')) { $response->header($this->getKeyHeader()); } if ($this->mEnableClientCache) { if ($config->get('UseSquid') && !SessionManager::getGlobalSession()->isPersistent() && !$this->isPrintable() && $this->mCdnMaxage != 0 && !$this->haveCacheVaryCookies()) { if ($config->get('UseESI')) { # We'll purge the proxy cache explicitly, but require end user agents # to revalidate against the proxy on each visit. # Surrogate-Control controls our CDN, Cache-Control downstream caches wfDebug(__METHOD__ . ": proxy caching with ESI; {$this->mLastModified} **", 'private'); # start with a shorter timeout for initial testing # header( 'Surrogate-Control: max-age=2678400+2678400, content="ESI/1.0"'); $response->header('Surrogate-Control: max-age=' . $config->get('SquidMaxage') . '+' . $this->mCdnMaxage . ', content="ESI/1.0"'); $response->header('Cache-Control: s-maxage=0, must-revalidate, max-age=0'); } else { # We'll purge the proxy cache for anons explicitly, but require end user agents # to revalidate against the proxy on each visit. # IMPORTANT! The CDN needs to replace the Cache-Control header with # Cache-Control: s-maxage=0, must-revalidate, max-age=0 wfDebug(__METHOD__ . ": local proxy caching; {$this->mLastModified} **", 'private'); # start with a shorter timeout for initial testing # header( "Cache-Control: s-maxage=2678400, must-revalidate, max-age=0" ); $response->header('Cache-Control: s-maxage=' . $this->mCdnMaxage . ', must-revalidate, max-age=0'); } } else { # We do want clients to cache if they can, but they *must* check for updates # on revisiting the page. wfDebug(__METHOD__ . ": private caching; {$this->mLastModified} **", 'private'); $response->header('Expires: ' . gmdate('D, d M Y H:i:s', 0) . ' GMT'); $response->header("Cache-Control: private, must-revalidate, max-age=0"); } if ($this->mLastModified) { $response->header("Last-Modified: {$this->mLastModified}"); } } else { wfDebug(__METHOD__ . ": no caching **", 'private'); # In general, the absence of a last modified header should be enough to prevent # the client from using its cache. We send a few other things just to make sure. $response->header('Expires: ' . gmdate('D, d M Y H:i:s', 0) . ' GMT'); $response->header('Cache-Control: no-cache, no-store, max-age=0, must-revalidate'); $response->header('Pragma: no-cache'); } }
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(); }