public function testExecute_email() { $config = new HashConfig(['PasswordResetRoutes' => ['username' => true, 'email' => true], 'EnableEmail' => true]); $authManager = $this->getMockBuilder(AuthManager::class)->disableOriginalConstructor()->getMock(); $authManager->expects($this->any())->method('allowsAuthenticationDataChange')->willReturn(Status::newGood()); $authManager->expects($this->exactly(2))->method('changeAuthenticationData'); $request = new FauxRequest(); $request->setIP('1.2.3.4'); $performingUser = $this->getMock(User::class); $performingUser->expects($this->any())->method('getRequest')->willReturn($request); $performingUser->expects($this->any())->method('isAllowed')->willReturn(true); $targetUser1 = $this->getMock(User::class); $targetUser2 = $this->getMock(User::class); $targetUser1->expects($this->any())->method('getName')->willReturn('User1'); $targetUser2->expects($this->any())->method('getName')->willReturn('User2'); $targetUser1->expects($this->any())->method('getId')->willReturn(1); $targetUser2->expects($this->any())->method('getId')->willReturn(2); $targetUser1->expects($this->any())->method('getEmail')->willReturn('*****@*****.**'); $targetUser2->expects($this->any())->method('getEmail')->willReturn('*****@*****.**'); $passwordReset = $this->getMockBuilder(PasswordReset::class)->setMethods(['getUsersByEmail'])->setConstructorArgs([$config, $authManager])->getMock(); $passwordReset->expects($this->any())->method('getUsersByEmail')->with('*****@*****.**')->willReturn([$targetUser1, $targetUser2]); $status = $passwordReset->isAllowed($performingUser); $this->assertTrue($status->isGood()); $status = $passwordReset->execute($performingUser, null, '*****@*****.**'); $this->assertTrue($status->isGood()); }
/** * @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); }
protected function doApiRequest($params, $session = null, $appendModule = false) { if (is_null($session)) { $session = array(); } $request = new FauxRequest($params, true, $session); $module = new ApiMain($request, true); $module->execute(); $results = array($module->getResultData(), $request, $request->getSessionArray()); if ($appendModule) { $results[] = $module; } return $results; }
/** * @dataProvider provideTryNormaliseRedirect * @covers MediaWiki::tryNormaliseRedirect */ public function testTryNormaliseRedirect($url, $query, $title, $expectedRedirect = false) { // Set SERVER because interpolateTitle() doesn't use getRequestURL(), // whereas tryNormaliseRedirect does(). $_SERVER['REQUEST_URI'] = $url; $req = new FauxRequest($query); $req->setRequestURL($url); // This adds a virtual 'title' query parameter. Normally called from Setup.php $req->interpolateTitle(); $titleObj = Title::newFromText($title); // Set global context since some involved code paths don't yet have context $context = RequestContext::getMain(); $context->setRequest($req); $context->setTitle($titleObj); $mw = new MediaWiki($context); $method = new ReflectionMethod($mw, 'tryNormaliseRedirect'); $method->setAccessible(true); $ret = $method->invoke($mw, $titleObj); $this->assertEquals($expectedRedirect !== false, $ret, 'Return true only when redirecting'); $this->assertEquals($expectedRedirect ?: '', $context->getOutput()->getRedirect()); }
/** * @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')); }
public function testGetSessionIdFromCookie() { $this->setMwGlobals('wgCookiePrefix', 'wgCookiePrefix'); $request = new \FauxRequest(); $request->setCookies(array('' => 'empty---------------------------', 'Foo' => 'foo-----------------------------', 'wgCookiePrefixFoo' => 'wgfoo---------------------------', 'BarFoo' => 'foobar--------------------------', 'bad' => 'bad'), ''); $provider = \TestingAccessWrapper::newFromObject($this->getProvider(null)); try { $provider->getSessionIdFromCookie($request); $this->fail('Expected exception not thrown'); } catch (\BadMethodCallException $ex) { $this->assertSame('MediaWiki\\Session\\ImmutableSessionProviderWithCookie::getSessionIdFromCookie ' . 'may not be called when $this->sessionCookieName === null', $ex->getMessage()); } $provider = \TestingAccessWrapper::newFromObject($this->getProvider('Foo')); $this->assertSame('wgfoo---------------------------', $provider->getSessionIdFromCookie($request)); $provider = \TestingAccessWrapper::newFromObject($this->getProvider('Foo', 'Bar')); $this->assertSame('foobar--------------------------', $provider->getSessionIdFromCookie($request)); $provider = \TestingAccessWrapper::newFromObject($this->getProvider('Foo', '')); $this->assertSame('foo-----------------------------', $provider->getSessionIdFromCookie($request)); $provider = \TestingAccessWrapper::newFromObject($this->getProvider('bad', '')); $this->assertSame(null, $provider->getSessionIdFromCookie($request)); $provider = \TestingAccessWrapper::newFromObject($this->getProvider('none', '')); $this->assertSame(null, $provider->getSessionIdFromCookie($request)); }
/** * Execute the requested Api actions. * @todo: Write some unit tests for API results */ public function execute() { // Logged-in users' parser options depend on preferences $this->getMain()->setCacheMode('anon-public-user-private'); // Enough '*' keys in JSON!!! $isXml = $this->getMain()->isInternalMode() || $this->getMain()->getPrinter()->getFormat() == 'XML'; $textElement = $isXml ? '*' : 'text'; $params = $this->extractRequestParams(); $prop = array_flip($params['prop']); $sectionProp = array_flip($params['sectionprop']); $this->variant = $params['variant']; $this->followRedirects = $params['redirect'] == 'yes'; $this->noHeadings = $params['noheadings']; $this->noTransform = $params['notransform']; $onlyRequestedSections = $params['onlyrequestedsections']; $this->offset = $params['offset']; $this->maxlen = $params['maxlen']; if ($this->offset === 0 && $this->maxlen === 0) { $this->offset = -1; // Disable text splitting } elseif ($this->maxlen === 0) { $this->maxlen = PHP_INT_MAX; } $title = $this->makeTitle($params['page']); // See whether the actual page (or if enabled, the redirect target) is the main page $this->mainPage = $this->isMainPage($title); if ($this->mainPage && $this->noHeadings) { $this->noHeadings = false; $this->setWarning("``noheadings'' makes no sense on the main page, ignoring"); } if (isset($prop['normalizedtitle']) && $title->getPrefixedText() != $params['page']) { $this->getResult()->addValue(null, $this->getModuleName(), array('normalizedtitle' => $title->getPageLanguage()->convert($title->getPrefixedText()))); } $data = $this->getData($title, $params['noimages']); // Bug 73109: #getData will return an empty array if the title redirects to // a page in a virtual namespace (NS_SPECIAL, NS_MEDIA), so make sure that // the requested data exists too. if (isset($prop['lastmodified']) && isset($data['lastmodified'])) { $this->getResult()->addValue(null, $this->getModuleName(), array('lastmodified' => $data['lastmodified'])); } if (isset($prop['lastmodifiedby']) && isset($data['lastmodifiedby'])) { $this->getResult()->addValue(null, $this->getModuleName(), array('lastmodifiedby' => $data['lastmodifiedby'])); } if (isset($prop['revision']) && isset($data['revision'])) { $this->getResult()->addValue(null, $this->getModuleName(), array('revision' => $data['revision'])); } if (isset($prop['id']) && isset($data['id'])) { $this->getResult()->addValue(null, $this->getModuleName(), array('id' => $data['id'])); } if (isset($prop['languagecount']) && isset($data['languagecount'])) { $this->getResult()->addValue(null, $this->getModuleName(), array('languagecount' => $data['languagecount'])); } if (isset($prop['hasvariants']) && isset($data['hasvariants'])) { $this->getResult()->addValue(null, $this->getModuleName(), array('hasvariants' => $data['hasvariants'])); } if (isset($prop['displaytitle']) && isset($data['displaytitle'])) { $this->getResult()->addValue(null, $this->getModuleName(), array('displaytitle' => $data['displaytitle'])); } if (isset($prop['pageprops'])) { $propNames = $params['pageprops']; if ($propNames == '*' && isset($data['pageprops'])) { $pageProps = $data['pageprops']; } else { $propNames = explode('|', $propNames); $pageProps = array_intersect_key($data['pageprops'], array_flip($propNames)); } $this->getResult()->addValue(null, $this->getModuleName(), array('pageprops' => $pageProps)); } if (isset($prop['description']) && isset($data['pageprops']['wikibase_item'])) { $desc = ExtMobileFrontend::getWikibaseDescription($data['pageprops']['wikibase_item']); if ($desc) { $this->getResult()->addValue(null, $this->getModuleName(), array('description' => $desc)); } } if ($this->usePageImages) { $this->addPageImage($data, $params, $prop); } $result = array(); $missingSections = array(); if ($this->mainPage) { if ($onlyRequestedSections) { $requestedSections = self::parseSections($params['sections'], $data, $missingSections); } else { $requestedSections = array(0); } $this->getResult()->addValue(null, $this->getModuleName(), array('mainpage' => '')); } elseif (isset($params['sections'])) { $requestedSections = self::parseSections($params['sections'], $data, $missingSections); } else { $requestedSections = array(); } if (isset($data['sections'])) { if (isset($prop['sections'])) { $sectionCount = count($data['sections']); for ($i = 0; $i <= $sectionCount; $i++) { if (!isset($requestedSections[$i]) && $onlyRequestedSections) { continue; } $section = array(); if ($i > 0) { $section = array_intersect_key($data['sections'][$i - 1], $sectionProp); } $section['id'] = $i; if (isset($prop['text']) && isset($requestedSections[$i]) && isset($data['text'][$i])) { $section[$textElement] = $this->stringSplitter($this->prepareSection($data['text'][$i])); unset($requestedSections[$i]); } if (isset($data['refsections'][$i])) { $section['references'] = ''; } $result[] = $section; } $missingSections = array_keys($requestedSections); } else { foreach (array_keys($requestedSections) as $index) { $section = array('id' => $index); if (isset($data['text'][$index])) { $section[$textElement] = $this->stringSplitter($this->prepareSection($data['text'][$index])); } else { $missingSections[] = $index; } $result[] = $section; } } $this->getResult()->setIndexedTagName($result, 'section'); $this->getResult()->addValue(null, $this->getModuleName(), array('sections' => $result)); } if (isset($prop['protection'])) { $this->addProtection($title); } if (isset($prop['editable'])) { $user = $this->getUser(); if ($user->isAnon()) { // HACK: Anons receive cached information, so don't check blocked status for them // to avoid them receiving false positives. Currently there is no way to check // all permissions except blocked status from the Title class. $req = new FauxRequest(); $req->setIP('127.0.0.1'); $user = User::newFromSession($req); } $editable = $title->quickUserCan('edit', $user); if ($isXml) { $editable = intval($editable); } $this->getResult()->addValue(null, $this->getModuleName(), array('editable' => $editable)); } // https://bugzilla.wikimedia.org/show_bug.cgi?id=51586 // Inform ppl if the page is infested with LiquidThreads but that's the // only thing we support about it. if (class_exists('LqtDispatch') && LqtDispatch::isLqtPage($title)) { $this->getResult()->addValue(null, $this->getModuleName(), array('liquidthreads' => '')); } if (count($missingSections) && isset($prop['text'])) { $this->setWarning('Section(s) ' . implode(', ', $missingSections) . ' not found'); } if ($this->maxlen < 0) { // There is more data available $this->getResult()->addValue(null, $this->getModuleName(), array('continue-offset' => $params['offset'] + $params['maxlen'])); } }
/** * Test conditional headers output * @dataProvider provideConditionalRequestHeadersOutput * @param array $conditions Return data for ApiBase::getConditionalRequestData * @param array $headers Expected output headers * @param bool $isError $isError flag * @param bool $post Request is a POST */ public function testConditionalRequestHeadersOutput($conditions, $headers, $isError = false, $post = false) { $request = new FauxRequest(array('action' => 'query', 'meta' => 'siteinfo'), $post); $response = $request->response(); $api = new ApiMain($request); $priv = TestingAccessWrapper::newFromObject($api); $priv->mInternalMode = false; $module = $this->getMockBuilder('ApiBase')->setConstructorArgs(array($api, 'mock'))->setMethods(array('getConditionalRequestData'))->getMockForAbstractClass(); $module->expects($this->any())->method('getConditionalRequestData')->will($this->returnCallback(function ($condition) use($conditions) { return isset($conditions[$condition]) ? $conditions[$condition] : null; })); $priv->mModule = $module; $priv->sendCacheHeaders($isError); foreach (array('Last-Modified', 'ETag') as $header) { $this->assertEquals(isset($headers[$header]) ? $headers[$header] : null, $response->getHeader($header), $header); } }
/** * @covers FauxRequest::getHeader */ public function testGetHeader() { $_SERVER['HTTP_TEST'] = 'Example'; $request = new FauxRequest(); $this->assertEquals(false, $request->getHeader('test')); }
/** * @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'); }
public function __construct($url, array $cookies = array()) { $this->url = $url; $query = array(); if ($url) { $params = wfParseUrl(wfExpandUrl($url)); if (isset($params['query'])) { $query = wfCgiToArray($params['query']); } } parent::__construct($query); $this->cookies = $cookies; $this->response = new FauxResponse(); }
/** * Depending on the requested action this method will try to store/preview * the data in mOptions or retrieve the edit form. * * The form and target page will be available in mOptions after execution of * the method. * * Errors and warnings are logged in the API result under the 'errors' key. * The general request status is maintained in mStatus. * * @global $wgRequest * @global $wgOut * @global SFFormPrinter $sfgFormPrinter * @throws MWException */ public function doAction() { global $wgOut, $wgParser, $wgRequest, $sfgFormPrinter; // if the wiki is read-only, do not save if (wfReadOnly()) { if ($this->mAction === self::ACTION_SAVE) { throw new MWException(wfMessage('sf_autoedit_readonly', wfReadOnlyReason())->parse()); } // even if not saving notify client anyway. Might want to dislay a notice $this->logMessage(wfMessage('sf_autoedit_readonly', wfReadOnlyReason())->parse(), self::NOTICE); } // find the title of the form to be used $formTitle = $this->getFormTitle(); // get the form content $formContent = StringUtils::delimiterReplace('<noinclude>', '</noinclude>', '', $this->getTextForPage($formTitle)); // signals that the form was submitted // always true, else we would not be here $isFormSubmitted = $this->mAction === self::ACTION_SAVE || $this->mAction === self::ACTION_PREVIEW || $this->mAction === self::ACTION_DIFF; // the article id of the form to be used $formArticleId = $formTitle->getArticleID(); // the name of the target page; might be empty when using the one-step-process $targetName = $this->mOptions['target']; // if the target page was not specified, try finding the page name formula // (Why is this not done in SFFormPrinter::formHTML?) if ($targetName === '') { // Parse the form to see if it has a 'page name' value set. if (preg_match('/{{{\\s*info.*page name\\s*=\\s*(.*)}}}/msU', $formContent, $matches)) { $pageNameElements = SFUtils::getFormTagComponents(trim($matches[1])); $targetNameFormula = $pageNameElements[0]; } else { throw new MWException(wfMessage('sf_autoedit_notargetspecified')->parse()); } $targetTitle = null; } else { $targetNameFormula = null; $targetTitle = Title::newFromText($targetName); } $preloadContent = ''; // save $wgRequest for later restoration $oldRequest = $wgRequest; $pageExists = false; // preload data if not explicitly excluded and if the preload page exists if (!isset($this->mOptions['preload']) || $this->mOptions['preload'] !== false) { if (isset($this->mOptions['preload']) && is_string($this->mOptions['preload'])) { $preloadTitle = Title::newFromText($this->mOptions['preload']); } else { $preloadTitle = Title::newFromText($targetName); } if ($preloadTitle !== null && $preloadTitle->exists()) { // the content of the page that was specified to be used for preloading $preloadContent = $this->getTextForPage($preloadTitle); $pageExists = true; } else { if (isset($this->mOptions['preload'])) { $this->logMessage(wfMessage('sf_autoedit_invalidpreloadspecified', $this->mOptions['preload'])->parse(), self::WARNING); } } } // Allow extensions to set/change the preload text, for new // pages. if (!$pageExists) { Hooks::run('sfEditFormPreloadText', array(&$preloadContent, $targetTitle, $formTitle)); } // Flag to keep track of formHTML() runs. $formHtmlHasRun = false; if ($preloadContent !== '') { // @HACK - we need to set this for the preload to take // effect in the form. $pageExists = true; // Spoof $wgRequest for SFFormPrinter::formHTML(). if (isset($_SESSION)) { $wgRequest = new FauxRequest($this->mOptions, true, $_SESSION); } else { $wgRequest = new FauxRequest($this->mOptions, true); } // Call SFFormPrinter::formHTML() to get at the form // HTML of the existing page. list($formHTML, $formJS, $targetContent, $form_page_title, $generatedTargetNameFormula) = $sfgFormPrinter->formHTML($formContent, $isFormSubmitted, $pageExists, $formArticleId, $preloadContent, $targetName, $targetNameFormula); // Parse the data to be preloaded from the form HTML of // the existing page. $data = $this->parseDataFromHTMLFrag($formHTML); // ...and merge/overwrite it with the new data. $this->mOptions = SFUtils::array_merge_recursive_distinct($data, $this->mOptions); } // We already preloaded stuff for saving/previewing - // do not do this again. if ($isFormSubmitted && !$wgRequest->getCheck('partial')) { $preloadContent = ''; $pageExists = false; } else { // Source of the data is a page. $pageExists = is_a($targetTitle, 'Title') && $targetTitle->exists(); } // Spoof $wgRequest for SFFormPrinter::formHTML(). if (isset($_SESSION)) { $wgRequest = new FauxRequest($this->mOptions, true, $_SESSION); } else { $wgRequest = new FauxRequest($this->mOptions, true); } // get wikitext for submitted data and form list($formHTML, $formJS, $targetContent, $generatedFormName, $generatedTargetNameFormula) = $sfgFormPrinter->formHTML($formContent, $isFormSubmitted, $pageExists, $formArticleId, $preloadContent, $targetName, $targetNameFormula); // Restore original request. $wgRequest = $oldRequest; if ($generatedFormName !== '') { $formTitle = Title::newFromText($generatedFormName); $this->mOptions['formtitle'] = $formTitle->getText(); } $this->mOptions['formHTML'] = $formHTML; $this->mOptions['formJS'] = $formJS; if ($isFormSubmitted) { // If the target page was not specified, see if // something was generated from the target name formula. if ($this->mOptions['target'] === '') { // If no name was generated, we cannot save => give up if ($generatedTargetNameFormula === '') { throw new MWException(wfMessage('sf_autoedit_notargetspecified')->parse()); } $this->mOptions['target'] = $this->generateTargetName($generatedTargetNameFormula); } // Lets other code process additional form-definition syntax Hooks::run('sfWritePageData', array($this->mOptions['form'], Title::newFromText($this->mOptions['target']), &$targetContent)); $editor = $this->setupEditPage($targetContent); // Perform the requested action. if ($this->mAction === self::ACTION_PREVIEW) { $this->doPreview($editor); } else { if ($this->mAction === self::ACTION_DIFF) { $this->doDiff($editor); } else { $this->doStore($editor); } } } else { if ($this->mAction === self::ACTION_FORMEDIT) { $parserOutput = $wgParser->getOutput(); if (method_exists($wgOut, 'addParserOutputMetadata')) { $wgOut->addParserOutputMetadata($parserOutput); } else { $wgOut->addParserOutputNoText($parserOutput); } $this->doFormEdit($formHTML, $formJS); } } }
private static function makeContext(ResourceLoaderContext $mainContext, $group, $type, array $extraQuery = []) { // Create new ResourceLoaderContext so that $extraQuery may trigger isRaw(). $req = new FauxRequest(array_merge($mainContext->getRequest()->getValues(), $extraQuery)); // Set 'only' if not combined $req->setVal('only', $type === ResourceLoaderModule::TYPE_COMBINED ? null : $type); // Remove user parameter in most cases if ($group !== 'user' && $group !== 'private') { $req->setVal('user', null); } $context = new ResourceLoaderContext($mainContext->getResourceLoader(), $req); // Allow caller to setVersion() and setModules() return new DerivativeResourceLoaderContext($context); }
public function testBug71329() { SpecialPageFactory::resetList(); RequestContext::resetMain(); $req = new FauxRequest(array('title' => 'Special:Search', 'mobileaction' => 'toggle_view_mobile')); $req->setRequestURL('/w/index.php?title=Special:Search&mobileaction=toggle_view_mobile'); RequestContext::getMain()->setRequest($req); MobileContext::setInstance(null); $this->setMwGlobals('wgTitle', null); SpecialPage::getTitleFor('Search'); $this->assertTrue(true, 'In case of failure this test just crashes'); }
/** * @dataProvider provideCheckIpLimits */ public function testCheckIpLimits($ip, $sessionData, $userData, $logLevel1, $logLevel2) { $this->setMwGlobals(array('wgSuspiciousIpPerSessionLimit' => 5, 'wgSuspiciousIpPerUserLimit' => 10, 'wgSuspiciousIpExpiry' => 600, 'wgSquidServers' => array('11.22.33.44'))); $manager = new SessionManager(); $logger = $this->getMock('\\Psr\\Log\\LoggerInterface'); $this->setLogger('session-ip', $logger); $request = new \FauxRequest(); $request->setIP($ip); $session = $manager->getSessionForRequest($request); /** @var SessionBackend $backend */ $backend = \TestingAccessWrapper::newFromObject($session)->backend; $data =& $backend->getData(); $data = array('SessionManager-ip' => $sessionData); $backend->setUser(User::newFromName('UTSysop')); $manager = \TestingAccessWrapper::newFromObject($manager); $manager->store->set('SessionManager-ip:' . md5('UTSysop'), $userData); $logger->expects($this->exactly(isset($logLevel1) + isset($logLevel2)))->method('log'); if ($logLevel1) { $logger->expects($this->at(0))->method('log')->with($logLevel1, 'Same session used from {count} IPs', $this->isType('array')); } if ($logLevel2) { $logger->expects($this->at(isset($logLevel1)))->method('log')->with($logLevel2, 'Same user had sessions from {count} IPs', $this->isType('array')); } $manager->checkIpLimits($session); }
public function testGetCookie() { $provider = new CookieSessionProvider(['priority' => 1, 'sessionName' => 'MySessionName', 'cookieOptions' => ['prefix' => 'x']]); $provider->setLogger(new \Psr\Log\NullLogger()); $provider->setConfig($this->getConfig()); $provider->setManager(SessionManager::singleton()); $provider = \TestingAccessWrapper::newFromObject($provider); $request = new \FauxRequest(); $request->setCookies(['xFoo' => 'foo!', 'xBar' => 'deleted'], ''); $this->assertSame('foo!', $provider->getCookie($request, 'Foo', 'x')); $this->assertNull($provider->getCookie($request, 'Bar', 'x')); $this->assertNull($provider->getCookie($request, 'Baz', 'x')); }
/** * 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); }); }
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')); }
public function testLogin() { // Test failure when bot passwords aren't enabled $this->setMwGlobals('wgEnableBotPasswords', false); $status = BotPassword::login("{$this->testUserName}@BotPassword", 'foobaz', new FauxRequest()); $this->assertEquals(Status::newFatal('botpasswords-disabled'), $status); $this->setMwGlobals('wgEnableBotPasswords', true); // Test failure when BotPasswordSessionProvider isn't configured $manager = new SessionManager(['logger' => new Psr\Log\NullLogger(), 'store' => new EmptyBagOStuff()]); $reset = MediaWiki\Session\TestUtils::setSessionManagerSingleton($manager); $this->assertNull($manager->getProvider(MediaWiki\Session\BotPasswordSessionProvider::class), 'sanity check'); $status = BotPassword::login("{$this->testUserName}@BotPassword", 'foobaz', new FauxRequest()); $this->assertEquals(Status::newFatal('botpasswords-no-provider'), $status); ScopedCallback::consume($reset); // Now configure BotPasswordSessionProvider for further tests... $mainConfig = RequestContext::getMain()->getConfig(); $config = new HashConfig(['SessionProviders' => $mainConfig->get('SessionProviders') + [MediaWiki\Session\BotPasswordSessionProvider::class => ['class' => MediaWiki\Session\BotPasswordSessionProvider::class, 'args' => [['priority' => 40]]]]]); $manager = new SessionManager(['config' => new MultiConfig([$config, RequestContext::getMain()->getConfig()]), 'logger' => new Psr\Log\NullLogger(), 'store' => new EmptyBagOStuff()]); $reset = MediaWiki\Session\TestUtils::setSessionManagerSingleton($manager); // No "@"-thing in the username $status = BotPassword::login($this->testUserName, 'foobaz', new FauxRequest()); $this->assertEquals(Status::newFatal('botpasswords-invalid-name', '@'), $status); // No base user $status = BotPassword::login('UTDummy@BotPassword', 'foobaz', new FauxRequest()); $this->assertEquals(Status::newFatal('nosuchuser', 'UTDummy'), $status); // No bot password $status = BotPassword::login("{$this->testUserName}@DoesNotExist", 'foobaz', new FauxRequest()); $this->assertEquals(Status::newFatal('botpasswords-not-exist', $this->testUserName, 'DoesNotExist'), $status); // Failed restriction $request = $this->getMock('FauxRequest', ['getIP']); $request->expects($this->any())->method('getIP')->will($this->returnValue('10.0.0.1')); $status = BotPassword::login("{$this->testUserName}@BotPassword", 'foobaz', $request); $this->assertEquals(Status::newFatal('botpasswords-restriction-failed'), $status); // Wrong password $status = BotPassword::login("{$this->testUserName}@BotPassword", $this->testUser->password, new FauxRequest()); $this->assertEquals(Status::newFatal('wrongpassword'), $status); // Success! $request = new FauxRequest(); $this->assertNotInstanceOf(MediaWiki\Session\BotPasswordSessionProvider::class, $request->getSession()->getProvider(), 'sanity check'); $status = BotPassword::login("{$this->testUserName}@BotPassword", 'foobaz', $request); $this->assertInstanceOf('Status', $status); $this->assertTrue($status->isGood()); $session = $status->getValue(); $this->assertInstanceOf(MediaWiki\Session\Session::class, $session); $this->assertInstanceOf(MediaWiki\Session\BotPasswordSessionProvider::class, $session->getProvider()); $this->assertSame($session->getId(), $request->getSession()->getId()); ScopedCallback::consume($reset); }
public function testContinueLinkAttempt() { $user = \User::newFromName('UTSysop'); $obj = new \stdClass(); $reqs = $this->getLinkRequests(); $done = [false, false, false]; // First, test the pass-through for not containing the ConfirmLinkAuthenticationRequest $mock = $this->getMockBuilder(ConfirmLinkSecondaryAuthenticationProvider::class)->setMethods(['beginLinkAttempt'])->getMock(); $mock->expects($this->once())->method('beginLinkAttempt')->with($this->identicalTo($user), $this->identicalTo('state'))->will($this->returnValue($obj)); $this->assertSame($obj, \TestingAccessWrapper::newFromObject($mock)->continueLinkAttempt($user, 'state', $reqs)); // Now test the actual functioning $provider = $this->getMockBuilder(ConfirmLinkSecondaryAuthenticationProvider::class)->setMethods(['beginLinkAttempt', 'providerAllowsAuthenticationDataChange', 'providerChangeAuthenticationData'])->getMock(); $provider->expects($this->never())->method('beginLinkAttempt'); $provider->expects($this->any())->method('providerAllowsAuthenticationDataChange')->will($this->returnCallback(function ($req) use($reqs) { return $req->getUniqueId() === 'Request3' ? \StatusValue::newFatal('foo') : \StatusValue::newGood(); })); $provider->expects($this->any())->method('providerChangeAuthenticationData')->will($this->returnCallback(function ($req) use(&$done) { $done[$req->id] = true; })); $config = new \HashConfig(['AuthManagerConfig' => ['preauth' => [], 'primaryauth' => [], 'secondaryauth' => [['factory' => function () use($provider) { return $provider; }]]]]); $request = new \FauxRequest(); $manager = new AuthManager($request, $config); $provider->setManager($manager); $provider = \TestingAccessWrapper::newFromObject($provider); $req = new ConfirmLinkAuthenticationRequest($reqs); $this->assertEquals(AuthenticationResponse::newAbstain(), $provider->continueLinkAttempt($user, 'state', [$req])); $request->getSession()->setSecret('state', ['maybeLink' => []]); $this->assertEquals(AuthenticationResponse::newAbstain(), $provider->continueLinkAttempt($user, 'state', [$req])); $request->getSession()->setSecret('state', ['maybeLink' => $reqs]); $this->assertEquals(AuthenticationResponse::newPass(), $res = $provider->continueLinkAttempt($user, 'state', [$req])); $this->assertSame([false, false, false], $done); $request->getSession()->setSecret('state', ['maybeLink' => [$reqs['Request2']]]); $req->confirmedLinkIDs = ['Request1', 'Request2']; $res = $provider->continueLinkAttempt($user, 'state', [$req]); $this->assertEquals(AuthenticationResponse::newPass(), $res); $this->assertSame([false, true, false], $done); $done = [false, false, false]; $request->getSession()->setSecret('state', ['maybeLink' => $reqs]); $req->confirmedLinkIDs = ['Request1', 'Request2']; $res = $provider->continueLinkAttempt($user, 'state', [$req]); $this->assertEquals(AuthenticationResponse::newPass(), $res); $this->assertSame([true, true, false], $done); $done = [false, false, false]; $request->getSession()->setSecret('state', ['maybeLink' => $reqs]); $req->confirmedLinkIDs = ['Request1', 'Request3']; $res = $provider->continueLinkAttempt($user, 'state', [$req]); $this->assertEquals(AuthenticationResponse::UI, $res->status); $this->assertCount(1, $res->neededRequests); $this->assertInstanceOf(ButtonAuthenticationRequest::class, $res->neededRequests[0]); $this->assertSame([true, false, false], $done); $done = [false, false, false]; $res = $provider->continueLinkAttempt($user, 'state', [$res->neededRequests[0]]); $this->assertEquals(AuthenticationResponse::newPass(), $res); $this->assertSame([false, false, false], $done); }
/** * 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 }); }
public function testProvideSessionInfo() { $provider = $this->getProvider(); $request = new \FauxRequest(); $request->setCookie('_BPsession', 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', 'wgCookiePrefix'); if (!defined('MW_API')) { $this->assertNull($provider->provideSessionInfo($request)); define('MW_API', 1); } $info = $provider->provideSessionInfo($request); $this->assertInstanceOf('MediaWiki\\Session\\SessionInfo', $info); $this->assertSame('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', $info->getId()); $this->config->set('EnableBotPasswords', false); $this->assertNull($provider->provideSessionInfo($request)); $this->config->set('EnableBotPasswords', true); $this->assertNull($provider->provideSessionInfo(new \FauxRequest())); }
public function __construct(WebRequest $base, $data, $wasPosted = false) { $this->base = $base; parent::__construct($data, $wasPosted); }
/** * @covers FauxRequest::getRawQueryString * @covers FauxRequest::getRawPostString * @covers FauxRequest::getRawInput */ public function testDummies() { $req = new FauxRequest(); $this->assertEquals('', $req->getRawQueryString()); $this->assertEquals('', $req->getRawPostString()); $this->assertEquals('', $req->getRawInput()); }