/** * @covers FauxRequest::setHeader * @covers FauxRequest::getHeader */ public function testGetSetHeader() { $value = 'test/test'; $request = new FauxRequest(); $request->setHeader('Content-Type', $value); $this->assertEquals($request->getHeader('Content-Type'), $value); $this->assertEquals($request->getHeader('CONTENT-TYPE'), $value); $this->assertEquals($request->getHeader('content-type'), $value); }
/** * @covers FauxRequest::setHeader * @covers FauxRequest::getHeader */ public function testGetSetHeader() { $value = 'text/plain, text/html'; $request = new FauxRequest(); $request->setHeader('Accept', $value); $this->assertEquals($request->getHeader('Nonexistent'), false); $this->assertEquals($request->getHeader('Accept'), $value); $this->assertEquals($request->getHeader('ACCEPT'), $value); $this->assertEquals($request->getHeader('accept'), $value); $this->assertEquals($request->getHeader('Accept', WebRequest::GETHEADER_LIST), array('text/plain', 'text/html')); }
/** * Import an client IP address, HTTP headers, user ID, and session ID * * This sets the current session, $wgUser, and $wgRequest from $params. * Once the return value falls out of scope, the old context is restored. * This method should only be called in contexts where there is no session * ID or end user receiving the response (CLI or HTTP job runners). This * is partly enforced, and is done so to avoid leaking cookies if certain * error conditions arise. * * This is useful when background scripts inherit context when acting on * behalf of a user. In general the 'sessionId' parameter should be set * to an empty string unless session importing is *truly* needed. This * feature is somewhat deprecated. * * @note suhosin.session.encrypt may interfere with this method. * * @param array $params Result of RequestContext::exportSession() * @return ScopedCallback * @throws MWException * @since 1.21 */ public static function importScopedSession(array $params) { if (strlen($params['sessionId']) && MediaWiki\Session\SessionManager::getGlobalSession()->isPersistent()) { // Sanity check to avoid sending random cookies for the wrong users. // This method should only called by CLI scripts or by HTTP job runners. throw new MWException("Sessions can only be imported when none is active."); } elseif (!IP::isValid($params['ip'])) { throw new MWException("Invalid client IP address '{$params['ip']}'."); } if ($params['userId']) { // logged-in user $user = User::newFromId($params['userId']); $user->load(); if (!$user->getId()) { throw new MWException("No user with ID '{$params['userId']}'."); } } else { // anon user $user = User::newFromName($params['ip'], false); } $importSessionFunc = function (User $user, array $params) { global $wgRequest, $wgUser; $context = RequestContext::getMain(); // Commit and close any current session if (MediaWiki\Session\PHPSessionHandler::isEnabled()) { session_write_close(); // persist session_id(''); // detach $_SESSION = []; // clear in-memory array } // Get new session, if applicable $session = null; if (strlen($params['sessionId'])) { // don't make a new random ID $manager = MediaWiki\Session\SessionManager::singleton(); $session = $manager->getSessionById($params['sessionId'], true) ?: $manager->getEmptySession(); } // Remove any user IP or agent information, and attach the request // with the new session. $context->setRequest(new FauxRequest([], false, $session)); $wgRequest = $context->getRequest(); // b/c // Now that all private information is detached from the user, it should // be safe to load the new user. If errors occur or an exception is thrown // and caught (leaving the main context in a mixed state), there is no risk // of the User object being attached to the wrong IP, headers, or session. $context->setUser($user); $wgUser = $context->getUser(); // b/c if ($session && MediaWiki\Session\PHPSessionHandler::isEnabled()) { session_id($session->getId()); MediaWiki\quietCall('session_start'); } $request = new FauxRequest([], false, $session); $request->setIP($params['ip']); foreach ($params['headers'] as $name => $value) { $request->setHeader($name, $value); } // Set the current context to use the new WebRequest $context->setRequest($request); $wgRequest = $context->getRequest(); // b/c }; // Stash the old session and load in the new one $oUser = self::getMain()->getUser(); $oParams = self::getMain()->exportSession(); $oRequest = self::getMain()->getRequest(); $importSessionFunc($user, $params); // Set callback to save and close the new session and reload the old one return new ScopedCallback(function () use($importSessionFunc, $oUser, $oParams, $oRequest) { global $wgRequest; $importSessionFunc($oUser, $oParams); // Restore the exact previous Request object (instead of leaving FauxRequest) RequestContext::getMain()->setRequest($oRequest); $wgRequest = RequestContext::getMain()->getRequest(); // b/c }); }
/** * @covers ApiMain::lacksSameOriginSecurity */ public function testLacksSameOriginSecurity() { // Basic test $main = new ApiMain(new FauxRequest(array('action' => 'query', 'meta' => 'siteinfo'))); $this->assertFalse($main->lacksSameOriginSecurity(), 'Basic test, should have security'); // JSONp $main = new ApiMain(new FauxRequest(array('action' => 'query', 'format' => 'xml', 'callback' => 'foo'))); $this->assertTrue($main->lacksSameOriginSecurity(), 'JSONp, should lack security'); // Header $request = new FauxRequest(array('action' => 'query', 'meta' => 'siteinfo')); $request->setHeader('TrEaT-As-UnTrUsTeD', ''); // With falsey value! $main = new ApiMain($request); $this->assertTrue($main->lacksSameOriginSecurity(), 'Header supplied, should lack security'); // Hook $this->mergeMwGlobalArrayValue('wgHooks', array('RequestHasSameOriginSecurity' => array(function () { return false; }))); $main = new ApiMain(new FauxRequest(array('action' => 'query', 'meta' => 'siteinfo'))); $this->assertTrue($main->lacksSameOriginSecurity(), 'Hook, should lack security'); }
/** * Import the resolved user IP, HTTP headers, user ID, and session ID. * This sets the current session and sets $wgUser and $wgRequest. * Once the return value falls out of scope, the old context is restored. * This function can only be called within CLI mode scripts. * * This will setup the session from the given ID. This is useful when * background scripts inherit context when acting on behalf of a user. * * $param array $params Result of RequestContext::exportSession() * @return ScopedCallback * @throws MWException * @since 1.21 */ public static function importScopedSession(array $params) { if (PHP_SAPI !== 'cli') { // Don't send random private cookies or turn $wgRequest into FauxRequest throw new MWException("Sessions can only be imported in cli mode."); } elseif (!strlen($params['sessionId'])) { throw new MWException("No session ID was specified."); } if ($params['userId']) { // logged-in user $user = User::newFromId($params['userId']); if (!$user) { throw new MWException("No user with ID '{$params['userId']}'."); } } elseif (!IP::isValid($params['ip'])) { throw new MWException("Could not load user '{$params['ip']}'."); } else { // anon user $user = User::newFromName($params['ip'], false); } $importSessionFunction = function (User $user, array $params) { global $wgRequest, $wgUser; $context = RequestContext::getMain(); // Commit and close any current session session_write_close(); // persist session_id(''); // detach $_SESSION = array(); // clear in-memory array // Remove any user IP or agent information $context->setRequest(new FauxRequest()); $wgRequest = $context->getRequest(); // b/c // Now that all private information is detached from the user, it should // be safe to load the new user. If errors occur or an exception is thrown // and caught (leaving the main context in a mixed state), there is no risk // of the User object being attached to the wrong IP, headers, or session. $context->setUser($user); $wgUser = $context->getUser(); // b/c if (strlen($params['sessionId'])) { // don't make a new random ID wfSetupSession($params['sessionId']); // sets $_SESSION } $request = new FauxRequest(array(), false, $_SESSION); $request->setIP($params['ip']); foreach ($params['headers'] as $name => $value) { $request->setHeader($name, $value); } // Set the current context to use the new WebRequest $context->setRequest($request); $wgRequest = $context->getRequest(); // b/c }; // Stash the old session and load in the new one $oUser = self::getMain()->getUser(); $oParams = self::getMain()->exportSession(); $importSessionFunction($user, $params); // Set callback to save and close the new session and reload the old one return new ScopedCallback(function () use($importSessionFunction, $oUser, $oParams) { $importSessionFunction($oUser, $oParams); }); }