public function execute() { $userName = '******'; // <- targer username $user = new CentralAuthUser( $userName ); if ( !$user->exists() ) { echo "Cannot unsuppress non-existent user {$userName}!\n"; exit( 0 ); } $userName = $user->getName(); // sanity $wikis = $user->listAttached(); // wikis with attached accounts foreach ( $wikis as $wiki ) { $lb = wfGetLB( $wiki ); $dbw = $lb->getConnection( DB_MASTER, array(), $wiki ); # Get local ID like $user->localUserData( $wiki ) does $localUserId = $dbw->selectField( 'user', 'user_id', array( 'user_name' => $userName ), __METHOD__ ); $delUserBit = Revision::DELETED_USER; $hiddenCount = $dbw->selectField( 'revision', 'COUNT(*)', array( 'rev_user' => $localUserId, "rev_deleted & $delUserBit != 0" ), __METHOD__ ); echo "$hiddenCount edits have the username hidden on \"$wiki\"\n"; # Unsuppress username on edits if ( $hiddenCount > 0 ) { echo "Unsuppressed edits of attached account (local id $localUserId) on \"$wiki\"..."; IPBlockForm::unsuppressUserName( $userName, $localUserId, $dbw ); echo "done!\n\n"; } $lb->reuseConnection( $dbw ); // not really needed # Don't lag too bad wfWaitForSlaves( 5 ); } }
/** * */ function showLogExtract() { $user = $this->mGlobalUser->getName(); $text = ''; $numRows = LogEventsList::showLogExtract($text, array('globalauth', 'suppress'), Title::newFromText("User:{$user}@global")->getPrefixedText(), '', array('showIfEmpty' => true)); if ($numRows) { $this->getOutput()->addHTML(Xml::fieldset(wfMsg('centralauth-admin-logsnippet'), $text)); } }
public function execute() { $globalUser = new CentralAuthUser($this->getParameter('user')); if (!$this->getUser()->isAllowed('centralauth-lock')) { $this->dieUsageMsg(array('badaccess-groups')); } elseif (!$globalUser->exists()) { $this->dieUsageMsg(array('nosuchuser', $globalUser->getName())); } elseif ($globalUser->isOversighted() && !$this->getUser()->isAllowed('centralauth-oversight')) { $this->dieUsageMsg(array('nosuchuser', $globalUser->getName())); } elseif (!$this->getRequest()->getCheck('locked') && $this->getParameter('hidden') === null) { $this->dieUsage("At least one of the parameters locked, hidden is required", "missingparam"); } $setLocked = $this->getParameter('locked'); if (!$setLocked) { // Don't lock or unlock $setLocked = null; } else { $setLocked = $setLocked === 'lock'; } $setHidden = $this->getParameter('hidden'); $reason = $this->getParameter('reason'); $stateCheck = $this->getParameter('statecheck'); if ($stateCheck && $stateCheck !== $globalUser->getStateHash(true)) { $this->dieUsage('Edit conflict detected, Aborting.', 'editconflict'); } $status = $globalUser->adminLockHide($setLocked, $setHidden, $reason, $this->getContext()); // Logging etc if ($status->isGood()) { $this->getResult()->addValue(null, $this->getModuleName(), array('user' => $globalUser->getName(), 'locked' => $globalUser->isLocked(), 'hidden' => $globalUser->getHiddenLevel(), 'reason' => $reason)); } else { if (is_callable(array($this, 'getErrorFormatter'))) { $error = $this->getErrorFormatter()->arrayFromStatus($status); } else { $error = $this->getResult()->convertStatusToArray($status); } $this->getResult()->addValue('error', null, $error); $this->getResult()->addValue(null, $this->getModuleName(), array('user' => $globalUser->getName(), 'locked' => $globalUser->isLocked(), 'hidden' => $globalUser->getHiddenLevel())); } }
private function showLogExtract() { $user = $this->mGlobalUser->getName(); $text = ''; $logTypes = array('globalauth'); if ($this->mCanOversight) { $logTypes[] = 'suppress'; } $numRows = LogEventsList::showLogExtract($text, $logTypes, Title::newFromText(MWNamespace::getCanonicalName(NS_USER) . ":{$user}@global")->getPrefixedText(), '', array('showIfEmpty' => true)); if ($numRows) { $this->getOutput()->addHTML(Xml::fieldset($this->msg('centralauth-admin-logsnippet')->text(), $text)); } }
/** * @covers CentralAuthUser::exists * @covers CentralAuthUser::getId * @covers CentralAuthUser::getName * @covers CentralAuthUser::getHomeWiki * @covers CentralAuthUser::isAttached * @covers CentralAuthUser::getRegistration * @covers CentralAuthUser::getStateHash */ public function testBasicAttrs() { $caUser = new CentralAuthUser('GlobalUser'); $this->assertSame(true, $caUser->exists()); $this->assertEquals(1001, $caUser->getId()); $this->assertEquals('GlobalUser', $caUser->getName()); $this->assertEquals(wfWikiID(), $caUser->getHomeWiki()); $this->assertSame(true, $caUser->isAttached()); $this->assertSame(false, $caUser->isLocked()); $this->assertEquals('20130627183537', $caUser->getRegistration()); $this->assertEquals(CentralAuthUser::HIDDEN_NONE, $caUser->getHiddenLevel()); $this->assertEquals('2234d7949459185926a50073d174b673', $caUser->getStateHash()); }
protected function rename($row, DatabaseBase $dbw) { $wiki = $row->utr_wiki; $name = $row->utr_name; $newNamePrefix = User::getCanonicalName($name . '~' . str_replace('_', '-', $wiki), 'usable'); if (!$newNamePrefix) { $this->log("ERROR: New name '{$name}~{$wiki}' is not valid"); return; } $this->log("Beginning rename of {$newNamePrefix}"); $newCAUser = new CentralAuthUser($newNamePrefix); $count = 0; // Edge case: Someone created User:Foo~wiki manually. // So just start appending numbers to the end of the name // until we get one that isn't used. while ($newCAUser->exists()) { $count++; $newCAUser = new CentralAuthUser($newNamePrefix . (string) $count); } if ($newNamePrefix !== $newCAUser->getName()) { $this->log("WARNING: New name is now {$newCAUser->getName()}"); } $this->log("Renaming {$name} to {$newCAUser->getName()}."); $statuses = new GlobalRenameUserStatus($name); $success = $statuses->setStatuses(array(array('ru_wiki' => $wiki, 'ru_oldname' => $name, 'ru_newname' => $newCAUser->getName(), 'ru_status' => 'queued'))); if (!$success) { $this->log("WARNING: Race condition, renameuser_status already set for {$newCAUser->getName()}. Skipping."); return; } $this->log("Set renameuser_status for {$newCAUser->getName()}."); $job = new LocalRenameUserJob(Title::newFromText('Global rename job'), array('from' => $name, 'to' => $newCAUser->getName(), 'renamer' => 'Maintenance script', 'movepages' => true, 'suppressredirects' => true, 'promotetoglobal' => true, 'reason' => $this->getOption('reason'))); JobQueueGroup::singleton($row->utr_wiki)->push($job); $this->log("Submitted job for {$newCAUser->getName()}."); $updates = new UsersToRenameDatabaseUpdates($dbw); $updates->markRenamed($row->utr_name, $row->utr_wiki); }
public function execute() { if (!$this->getUser()->isAllowed('centralauth-unmerge')) { $this->dieUsageMsg(array('badaccess-groups')); } $params = $this->extractRequestParams(); $globalUser = new CentralAuthUser($params['user']); if (!$globalUser->exists()) { $this->dieUsageMsg(array('nosuchuser', $globalUser->getName())); } elseif ($globalUser->isOversighted() && !$this->getUser()->isAllowed('centralauth-oversight')) { $this->dieUsageMsg(array('nosuchuser', $globalUser->getName())); } $status = $globalUser->adminDelete($params['reason']); if ($status->isGood()) { $this->getResult()->addValue(null, $this->getModuleName(), array('user' => $globalUser->getName(), 'reason' => $params['reason'])); } else { if (is_callable(array($this, 'getErrorFormatter'))) { $error = $this->getErrorFormatter()->arrayFromStatus($status); } else { $error = $this->getResult()->convertStatusToArray($status); } $this->getResult()->addValue('error', null, $error); } }
/** * Switches a user's preferences off * @param $user User object to set preferences for * @param $global bool Whether to apply this change on all wikis in $wgPrefSwitchWikis */ public static function switchOff($user, $global = false) { self::switchOffUser($user); if ($global) { $globalUser = new CentralAuthUser($user->getName()); if (!$globalUser->exists()) { return; } $accounts = $globalUser->queryAttached(); foreach ($accounts as $account) { $remoteUser = UserRightsProxy::newFromName($account['wiki'], $globalUser->getName(), true); if ($remoteUser) { self::switchOffUser($remoteUser); } } } }
/** * @param CentralAuthUser $globalUser * @return string */ private function getUserTableRow(CentralAuthUser $globalUser) { $rowHtml = ''; // @TODO: Don't use methods from the special page directly, // rather move them somewhere sane $sca = new SpecialCentralAuth(); $sca->setContext($this->getContext()); $guName = $globalUser->getName(); $guLink = Linker::link(SpecialPage::getTitleFor('CentralAuth', $guName), htmlspecialchars($guName)); $guHidden = $sca->formatHiddenLevel($globalUser->getHiddenLevel()); $accountAge = time() - wfTimestamp(TS_UNIX, $globalUser->getRegistration()); $guRegister = $sca->prettyTimespan($accountAge); $guLocked = $this->msg('centralauth-admin-status-locked-no')->escaped(); if ($globalUser->isLocked()) { $guLocked = $this->msg('centralauth-admin-status-locked-yes')->escaped(); } $guEditCount = htmlspecialchars($this->getLanguage()->formatNum($globalUser->getGlobalEditCount())); $guAttachedLocalAccounts = htmlspecialchars($this->getLanguage()->formatNum(count($globalUser->listAttached()))); $rowHtml .= Html::rawElement('td', array(), Html::input('wpActionTarget[' . $guName . ']', $guName, 'checkbox', array('checked' => 'checked'))); $rowHtml .= Html::rawElement('td', array(), $guLink); $rowHtml .= Html::element('td', array('data-sort-value' => $accountAge), $guRegister); $rowHtml .= Html::element('td', array(), $guLocked); $rowHtml .= Html::element('td', array(), $guHidden); $rowHtml .= Html::element('td', array(), $guEditCount); $rowHtml .= Html::element('td', array(), $guAttachedLocalAccounts); return $rowHtml; }
/** * @param array $users * * @return LocalUserMergeJob */ private function getJob(array $users) { return new LocalUserMergeJob(Title::newFromText('Global merge job'), array('to' => $this->newCAUser->getName(), 'renamer' => $this->performingUser->getName(), 'from' => $users, 'session' => $this->session)); }
/** * @param User $user * @param CentralAuthUser $centralUser * @param string $inject_html * @return bool */ protected static function doCentralLoginRedirect(User $user, CentralAuthUser $centralUser, &$inject_html) { global $wgCentralAuthLoginWiki, $wgMemc, $wgSecureLogin; $context = RequestContext::getMain(); $request = $context->getRequest(); if (!$wgCentralAuthLoginWiki || defined('MW_API')) { // Mark the session to include edge login imgs on the next pageview $request->setSessionData('CentralAuthDoEdgeLogin', true); return true; } // Check that this is actually for a special login page view if ($context->getTitle()->isSpecial('Userlogin') && $request->wasPosted()) { // User will be redirected to Special:CentralLogin/start (central wiki), // then redirected back to Special:CentralLogin/complete (this wiki). // Sanity check that "returnto" is not one of the central login pages. If it // is, then clear the "returnto" options (LoginForm will use the main page). $returnTo = $request->getVal('returnto', ''); $returnToQuery = $request->getVal('returntoquery', ''); $returnToTitle = Title::newFromText($returnTo); if ($returnToTitle && $returnToTitle->isSpecial('CentralLogin')) { $returnTo = ''; $returnToQuery = ''; } // Determine the final protocol of page, after login $finalProto = $request->detectProtocol(); $secureCookies = $finalProto === 'https'; if ($wgSecureLogin) { $finalProto = 'http'; if ($request->getBool('wpForceHttps', false) || $user->getBoolOption('prefershttps') && wfCanIPUseHTTPS($request->getIP())) { $finalProto = 'https'; } $secureCookies = $finalProto === 'https' && $user->getBoolOption('prefershttps'); } // When POSTs triggered from Special:CentralLogin/start are sent back to // this wiki, the token will be checked to see if it was signed with this. // This is needed as Special:CentralLogin/start only takes a token argument // and we need to make sure an agent requesting such a URL actually initiated // the login request that spawned that token server-side. $secret = MWCryptRand::generateHex(32); $_SESSION['CentralAuth:autologin:current-attempt'] = array('secret' => $secret, 'remember' => $request->getCheck('wpRemember'), 'returnTo' => $returnTo, 'returnToQuery' => $returnToQuery, 'stickHTTPS' => $secureCookies, 'finalProto' => $finalProto, 'type' => $request->getText('type')); // Create a new token to pass to Special:CentralLogin/start (central wiki) $token = MWCryptRand::generateHex(32); $key = CentralAuthUser::memcKey('central-login-start-token', $token); $data = array('secret' => $secret, 'name' => $centralUser->getName(), 'guid' => $centralUser->getId(), 'wikiId' => wfWikiId(), 'secureCookies' => $secureCookies, 'finalProto' => $finalProto, 'currentProto' => $request->detectProtocol()); Hooks::run('CentralAuthLoginRedirectData', array($centralUser, &$data)); $wgMemc->set($key, $data, 60); $wiki = WikiMap::getWiki($wgCentralAuthLoginWiki); // Use WikiReference::getFullUrl(), returns a protocol-relative URL if needed $context->getOutput()->redirect(wfAppendQuery($wiki->getFullUrl('Special:CentralLogin/start'), "token={$token}")); // Set $inject_html to some text to bypass the LoginForm redirection $inject_html .= '<!-- do CentralAuth redirect -->'; } else { // Mark the session to include edge login imgs on the next pageview $request->setSessionData('CentralAuthDoEdgeLogin', true); } return true; }
function migrate($username, $homewiki = null) { $this->total++; $this->output("CentralAuth account migration for: " . $username . "\n"); $central = new CentralAuthUser($username); try { $unattached = $central->queryUnattached(); } catch (Exception $e) { // This might happen due to localnames inconsistencies (bug 67350) $this->output("ERROR: Fetching unattached accounts for {$username} failed."); return; } /** * Migration with an existing global account */ if ($central->exists()) { $this->output("INFO: A global account already exists for: {$username}\n"); if ($this->getOption('attachmissing', false) && !is_null($central->getEmailAuthenticationTimestamp())) { foreach ($unattached as $wiki => $local) { if ($central->getEmail() === $local['email'] && !is_null($local['emailAuthenticated'])) { $this->output("ATTACHING: {$username}@{$wiki}\n"); $central->attach($wiki, 'mail', !$this->suppressRC); } } } if ($this->getOption('attachbroken', false)) { // This option is for bug 61876 / bug 39996 where the account has // an empty password and email set, and became unattached. // Since there is no way an account can have an empty password manually // it has to be due to a CentralAuth bug. So just attach it then. // But just to be on the safe side, check that it also has 0 edits. foreach ($unattached as $wiki => $local) { if ($local['email'] === '' && $local['password'] === '' && $local['editCount'] === '0') { $this->output("ATTACHING: {$username}@{$wiki}\n"); // Ironically, the attachment is made due to lack of a password. $central->attach($wiki, 'password', !$this->suppressRC); } } } } else { if (count($unattached) == 0) { $this->output("ERROR: No local accounts found for: {$username}\n"); return; } if ($this->safe && count($unattached) !== 1) { $this->output("ERROR: More than 1 local user account found for username: {$username}\n"); foreach ($unattached as $local) { $this->output("\t" . $central->getName() . "@" . $local['wiki'] . "\n"); } return; } if ($homewiki !== null) { if (!array_key_exists($homewiki, $unattached)) { $this->output("ERROR: Unattached user not found for {$username}@{$homewiki}\n"); return; } $this->output("INFO: Setting homewiki for '{$username}' to {$homewiki}\n"); $central->mHomeWiki = $homewiki; } // Check that all unattached (i.e. ALL) accounts have a confirmed email // address and that the addresses are all the same. We are using this // to match accounts to the same user, since we can't use the password. $emailMatch = true; $email = null; foreach ($unattached as $local) { if (is_null($email)) { $email = $local['email']; } if ($local['email'] === $email && !is_null($local['emailAuthenticated'])) { continue; } $emailMatch = false; break; } // All of the emails are the same and confirmed? Merge all the accounts. // They aren't? Skip, or merge the winner if --auto was specified. if ($emailMatch) { $this->output("Email addresses match and are confirmed for: {$username}\n"); $central->storeAndMigrate(array(), !$this->suppressRC); } else { if (isset($central->mHomeWiki) || $this->autoMigrate) { $central->storeAndMigrate(array(), !$this->suppressRC); } else { $this->output("ERROR: Auto migration is disabled and email addresses do not match for: {$username}\n"); } } } $unattachedAfter = $central->queryUnattached(); if (count($unattachedAfter) == 0) { $this->migrated++; return; } elseif (count($unattachedAfter) > 0 && count($unattachedAfter) < count($unattached)) { $this->partial++; $this->output("INFO: Incomplete migration for '{$username}'\n"); } if ($this->resetToken) { $this->output("INFO: Resetting CentralAuth auth token for '{$username}'\n"); $central->resetAuthToken(); } }
public function execute() { $params = $this->extractRequestParams(); $prop = array_flip((array) $params['prop']); if (is_null($params['user'])) { $params['user'] = $this->getUser()->getName(); } $user = new CentralAuthUser($params['user']); // Add basic info $result = $this->getResult(); $data = array(); $userExists = $user->exists(); if ($userExists && ($user->getHiddenLevel() === CentralAuthUser::HIDDEN_NONE || $this->getUser()->isAllowed('centralauth-oversight'))) { // The global user exists and it's not hidden or the current user is allowed to see it $data['home'] = $user->getHomeWiki(); $data['id'] = $user->getId(); $data['registration'] = wfTimestamp(TS_ISO_8601, $user->getRegistration()); $data['name'] = $user->getName(); if ($user->isLocked()) { $data['locked'] = ''; } if ($user->isHidden()) { $data['hidden'] = ''; } } else { // The user doesn't exist or we pretend it doesn't if it's hidden $data['missing'] = ''; } $result->addValue('query', $this->getModuleName(), $data); // Add requested info if ($userExists && isset($prop['groups'])) { $groups = $user->getGlobalGroups(); $result->setIndexedTagName($groups, 'g'); $result->addValue(array('query', $this->getModuleName()), 'groups', $groups); } if ($userExists && isset($prop['rights'])) { $rights = $user->getGlobalRights(); $result->setIndexedTagName($rights, 'r'); $result->addValue(array('query', $this->getModuleName()), 'rights', $rights); } $attachedAccounts = null; if ($userExists && (isset($prop['merged']) || isset($prop['editcount']))) { $attachedAccounts = $user->queryAttached(); } if ($userExists && isset($prop['merged'])) { foreach ($attachedAccounts as $account) { $dbname = $account['wiki']; $wiki = WikiMap::getWiki($dbname); $a = array('wiki' => $dbname, 'url' => $wiki->getCanonicalServer(), 'timestamp' => wfTimestamp(TS_ISO_8601, $account['attachedTimestamp']), 'method' => $account['attachedMethod'], 'editcount' => $account['editCount']); if ($account['blocked']) { $a['blocked'] = array('expiry' => $this->getLanguage()->formatExpiry($account['block-expiry'], TS_ISO_8601), 'reason' => $account['block-reason']); } $result->addValue(array('query', $this->getModuleName(), 'merged'), null, $a); } if (defined('ApiResult::META_CONTENT')) { $result->addIndexedTagName(array('query', $this->getModuleName(), 'merged'), 'account'); } else { $result->setIndexedTagName_internal(array('query', $this->getModuleName(), 'merged'), 'account'); } } if ($userExists && isset($prop['editcount'])) { $editcount = 0; foreach ($attachedAccounts as $account) { $editcount += $account['editCount']; } $result->addValue('query', $this->getModuleName(), array('editcount' => $editcount)); } if (isset($prop['unattached'])) { $accounts = $user->queryUnattached(); foreach ($accounts as $account) { $a = array('wiki' => $account['wiki'], 'editcount' => $account['editCount']); if ($account['blocked']) { $a['blocked'] = array('expiry' => $this->getLanguage()->formatExpiry($account['block-expiry'], TS_ISO_8601), 'reason' => $account['block-reason']); } $result->addValue(array('query', $this->getModuleName(), 'unattached'), null, $a); } if (defined('ApiResult::META_CONTENT')) { $result->addIndexedTagName(array('query', $this->getModuleName(), 'unattached'), 'account'); } else { $result->setIndexedTagName_internal(array('query', $this->getModuleName(), 'unattached'), 'account'); } } }
/** * Sets up jobs to create and attach a local account for the given user on every wiki listed in * $wgCentralAuthAutoCreateWikis. * @param CentralAuthUser $centralUser */ private function autoCreateAccounts(CentralAuthUser $centralUser) { global $wgCentralAuthAutoCreateWikis; $name = $centralUser->getName(); $thisWiki = wfWikiID(); $session = RequestContext::getMain()->exportSession(); foreach ($wgCentralAuthAutoCreateWikis as $wiki) { if ($wiki === $thisWiki) { continue; } $job = Job::factory('CentralAuthCreateLocalAccountJob', Title::makeTitleSafe(NS_USER, $name), array('name' => $name, 'from' => $thisWiki, 'session' => $session)); JobQueueGroup::singleton($wiki)->push($job); } }
function execute($par) { global $wgMemc, $wgCentralAuthLoginWiki; $request = $this->getRequest(); $this->loginWiki = $wgCentralAuthLoginWiki; if (!$this->loginWiki) { // Ugh, no central wiki. If we're coming from an edge login, make // the logged-into wiki the de-facto central wiki for this request // so auto-login still works. $fromwiki = $request->getVal('from', $request->getVal('notifywiki')); if ($fromwiki !== null && WikiMap::getWiki($fromwiki)) { $this->loginWiki = $fromwiki; } } elseif ($request->getVal('from') === wfWikiId() && $wgCentralAuthLoginWiki !== wfWikiId()) { // Remote wiki must not have wgCentralAuthLoginWiki set, but we do. Redirect them. $this->do302Redirect($wgCentralAuthLoginWiki, $par, $request->getValues()); return; } $params = $request->getValues('type', 'from', 'return', 'returnto', 'returntoquery', 'proto', 'mobile'); switch (strval($par)) { case 'P3P': // Explain the bogus P3P header $this->setHeaders(); $this->getOutput()->addWikiMsg('centralauth-centralautologin-p3p-explanation'); return; case 'toolslist': // Do not cache this, we want updated Echo numbers and such. $this->getOutput()->enableClientCache(false); $user = $this->getUser(); if (!$user->isAnon()) { if (!CentralAuthHooks::isUIReloadRecommended($user)) { $html = $this->getSkin()->getPersonalToolsList(); $json = FormatJSON::encode(array('toolslist' => $html)); } else { $gender = $this->getUser()->getOption('gender'); if (strval($gender) === '') { $gender = 'unknown'; } $json = FormatJSON::encode(array('notify' => array('username' => $user->getName(), 'gender' => $gender))); } $this->doFinalOutput(true, 'OK', $json, 'json'); } else { $this->doFinalOutput(false, 'Not logged in', '', 'json'); } return; case 'refreshCookies': // Refresh central cookies (e.g. in case 'remember me' was set) // Do not cache this, we need to reset the cookies every time. $this->getOutput()->enableClientCache(false); if (!$wgCentralAuthLoginWiki || !$this->checkIsCentralWiki($wikiid)) { return; } CentralAuthUser::setP3P(); $centralUser = CentralAuthUser::getInstance($this->getUser()); if ($centralUser && $centralUser->getId()) { $centralSession = $this->getCentralSession($centralUser, $this->getUser()); // Refresh 'remember me' preference $remember = (bool) $centralSession['remember']; if ($remember != $this->getUser()->getBoolOption('rememberpassword')) { $this->getUser()->setOption('rememberpassword', $remember ? 1 : 0); $this->getUser()->saveSettings(); } $secureCookie = $centralSession['secureCookies']; $centralUser->setGlobalCookies($remember, false, $secureCookie, $centralSession); $this->doFinalOutput(true, 'success'); } else { $this->doFinalOutput(false, 'Not logged in'); } return; case 'deleteCookies': // Delete central cookies // Do not cache this, we need to reset the cookies every time. $this->getOutput()->enableClientCache(false); if ($this->getUser()->isLoggedIn()) { $this->doFinalOutput(false, 'Cannot delete cookies while still logged in'); return; } CentralAuthUser::setP3P(); CentralAuthUser::deleteGlobalCookies(); $this->doFinalOutput(true, 'success'); return; case 'start': // Main entry point // Note this is safe to cache, because the cache already varies on // the session cookies. $this->getOutput()->setSquidMaxage(1200); if (!$this->checkIsLocalWiki()) { return; } CentralAuthUser::setP3P(); $this->do302Redirect($this->loginWiki, 'checkLoggedIn', array('wikiid' => wfWikiID(), 'proto' => $request->detectProtocol()) + $params); return; case 'checkLoggedIn': // Check if we're logged in centrally // Note this is safe to cache, because the cache already varies on // the session cookies. $this->getOutput()->setSquidMaxage(1200); if (!$this->checkIsCentralWiki($wikiid)) { return; } CentralAuthUser::setP3P(); if ($this->getUser()->isLoggedIn()) { $centralUser = CentralAuthUser::getInstance($this->getUser()); } else { $this->doFinalOutput(false, 'Not centrally logged in', self::getInlineScript('anon-set.js')); return; } // We're pretty sure this user is logged in, so pass back // headers to prevent caching, just in case $this->getOutput()->enableClientCache(false); $memcData = array('gu_id' => $centralUser->getId()); $token = MWCryptRand::generateHex(32); $key = CentralAuthUser::memcKey('centralautologin-token', $token); $wgMemc->set($key, $memcData, 60); $this->do302Redirect($wikiid, 'createSession', array('token' => $token) + $params); return; case 'createSession': // Create the local session and shared memcache token if (!$this->checkIsLocalWiki()) { return; } CentralAuthUser::setP3P(); $token = $request->getVal('token', ''); $gid = $request->getVal('gu_id', ''); if ($token !== '') { // Load memc data $key = CentralAuthUser::memcKey('centralautologin-token', $token); $memcData = $wgMemc->get($key); $wgMemc->delete($key); if (!$memcData || !isset($memcData['gu_id'])) { $this->doFinalOutput(false, 'Invalid parameters'); return; } $gu_id = intval($memcData['gu_id']); } elseif ($gid !== '') { // Cached, or was logging in as we switched from gu_id to token $gu_id = intval($gid); } else { $this->doFinalOutput(false, 'Invalid parameters'); return; } if ($gu_id <= 0) { $this->doFinalOutput(false, 'Not centrally logged in', self::getInlineScript('anon-set.js')); return; } // At this point we can't cache anymore because we need to set // cookies and memc each time. $this->getOutput()->enableClientCache(false); // Ensure that a session exists if (session_id() == '') { wfSetupSession(); } // Create memc token $wikiid = wfWikiID(); $memcData = array('gu_id' => $gu_id, 'wikiid' => $wikiid); $token = MWCryptRand::generateHex(32); $key = CentralAuthUser::memcKey('centralautologin-token', $token, $wikiid); $wgMemc->set($key, $memcData, 60); // Save memc token for the 'setCookies' step $request->setSessionData('centralautologin-token', $token); $this->do302Redirect($this->loginWiki, 'validateSession', array('token' => $token, 'wikiid' => $wikiid) + $params); return; case 'validateSession': // Validate the shared memcached token // Do not cache this, we need to reset the cookies and memc every time. $this->getOutput()->enableClientCache(false); if (!$this->checkIsCentralWiki($wikiid)) { return; } if (!$this->getUser()->isLoggedIn()) { $this->doFinalOutput(false, 'Not logged in'); return; } CentralAuthUser::setP3P(); // Validate params $token = $request->getVal('token', ''); if ($token === '') { $this->doFinalOutput(false, 'Invalid parameters'); return; } // Load memc data $key = CentralAuthUser::memcKey('centralautologin-token', $token, $wikiid); $memcData = $wgMemc->get($key); $wgMemc->delete($key); // Check memc data $centralUser = CentralAuthUser::getInstance($this->getUser()); if (!$memcData || $memcData['wikiid'] !== $wikiid || !$centralUser || !$centralUser->getId() || $memcData['gu_id'] != $centralUser->getId()) { $this->doFinalOutput(false, 'Invalid parameters'); return; } // Write info for session creation into memc $centralSession = $this->getCentralSession($centralUser, $this->getUser()); $memcData += array('userName' => $centralUser->getName(), 'token' => $centralUser->getAuthToken(), 'finalProto' => $centralSession['finalProto'], 'secureCookies' => $centralSession['secureCookies'], 'remember' => $centralSession['remember'], 'sessionId' => $centralSession['sessionId']); $wgMemc->set($key, $memcData, 60); $this->do302Redirect($wikiid, 'setCookies', $params); return; case 'setCookies': // Check that memcached is validated, and set cookies // Do not cache this, we need to reset the cookies and memc every time. $this->getOutput()->enableClientCache(false); if (!$this->checkIsLocalWiki()) { return; } CentralAuthUser::setP3P(); // Check saved memc token $token = $this->getRequest()->getSessionData('centralautologin-token'); if ($token === null) { $this->doFinalOutput(false, 'Lost session'); return; } // Load memc data $wikiid = wfWikiID(); $key = CentralAuthUser::memcKey('centralautologin-token', $token, $wikiid); $memcData = $wgMemc->get($key); $wgMemc->delete($key); // Check memc data if (!$memcData || $memcData['wikiid'] !== $wikiid || !isset($memcData['userName']) || !isset($memcData['token'])) { $this->doFinalOutput(false, 'Lost session'); return; } // Load and check CentralAuthUser. But don't check if it's // attached, because then if the user is missing en.site they // won't be auto logged in to any of the non-en versions either. $centralUser = new CentralAuthUser($memcData['userName']); if (!$centralUser->getId() || $centralUser->getId() != $memcData['gu_id']) { $msg = "Wrong user: expected {$memcData['gu_id']}, got {$centralUser->getId()}"; wfDebug(__METHOD__ . ": {$msg}\n"); $this->doFinalOutput(false, 'Lost session'); return; } $loginResult = $centralUser->authenticateWithToken($memcData['token']); if ($loginResult != 'ok') { $msg = "Bad token: {$loginResult}"; wfDebug(__METHOD__ . ": {$msg}\n"); $this->doFinalOutput(false, 'Lost session'); return; } // Set a new session cookie, Just In Case™ wfResetSessionID(); // Set central cookies too, with a refreshed sessionid. Also, check if we // need to override the default cookie security policy $secureCookie = $memcData['secureCookies']; $centralUser->setGlobalCookies($memcData['remember'], $memcData['sessionId'], $secureCookie, array('finalProto' => $memcData['finalProto'], 'secureCookies' => $memcData['secureCookies'], 'remember' => $memcData['remember'])); // Now, figure out how to report this back to the user. // First, set to redo the edge login on the next pageview $request->setSessionData('CentralAuthDoEdgeLogin', true); // If it's not a script callback, just go for it. if ($request->getVal('type') !== 'script') { $this->doFinalOutput(true, 'success'); return; } // If it is a script callback, then we do want to create the user // if it doesn't already exist locally (and fail if that can't be // done). if (!User::idFromName($centralUser->getName())) { $user = new User(); $user->setName($centralUser->getName()); if (CentralAuthHooks::attemptAddUser($user)) { $centralUser->invalidateCache(); } } if (!$centralUser->isAttached()) { $this->doFinalOutput(false, 'Local user is not attached', self::getInlineScript('anon-set.js')); return; } $script = self::getInlineScript('anon-remove.js'); // If we're returning to returnto, do that if ($request->getCheck('return')) { global $wgRedirectOnLogin; if ($wgRedirectOnLogin !== null) { $returnTo = $wgRedirectOnLogin; $returnToQuery = array(); } else { $returnTo = $request->getVal('returnto', ''); $returnToQuery = wfCgiToArray($request->getVal('returntoquery', '')); } $returnToTitle = Title::newFromText($returnTo); if (!$returnToTitle) { $returnToTitle = Title::newMainPage(); $returnToQuery = array(); } $redirectUrl = $returnToTitle->getFullURL($returnToQuery); $script .= "\n" . 'location.href = ' . Xml::encodeJsVar($redirectUrl) . ';'; $this->doFinalOutput(true, 'success', $script); return; } // Otherwise, we need to rewrite p-personal and maybe notify the user too global $wgCentralAuthUseEventLogging; if ($wgCentralAuthUseEventLogging) { EventLogging::logEvent('CentralAuth', 5690875, array('version' => 1, 'userId' => $centralUser->getId(), 'action' => 'sul2-autologin-fallbacklogin')); } // Add a script to the page that will pull in the user's toolslist // via ajax, and update the UI. Don't write out the tools here (bug 57081). $code = $this->getUser()->getOption('language'); $code = RequestContext::sanitizeLangCode($code); Hooks::run('UserGetLanguageObject', array($this->getUser(), &$code, $this->getContext())); $script .= "\n" . Xml::encodeJsCall('mediaWiki.messages.set', array(array('centralauth-centralautologin-logged-in' => wfMessage('centralauth-centralautologin-logged-in')->inLanguage($code)->plain(), 'centralauth-centralautologin-logged-in-nouser' => wfMessage('centralauth-centralautologin-logged-in-nouser')->inLanguage($code)->plain(), 'centralautologin' => wfMessage('centralautologin')->inLanguage($code)->plain()))); $script .= "\n" . self::getInlineScript('autologin.js'); // And for good measure, add the edge login HTML images to the page. $script .= "\n" . Xml::encodeJsCall("jQuery( 'body' ).append", array(CentralAuthHooks::getEdgeLoginHTML())); $this->doFinalOutput(true, 'success', $script); return; default: $this->setHeaders(); $this->getOutput()->addWikiMsg('centralauth-centralautologin-desc'); return; } }