/** * Unblocks the specified user or provides the reason the unblock failed. */ public function execute() { $user = $this->getUser(); $params = $this->extractRequestParams(); if (is_null($params['id']) && is_null($params['user'])) { $this->dieUsageMsg('unblock-notarget'); } if (!is_null($params['id']) && !is_null($params['user'])) { $this->dieUsageMsg('unblock-idanduser'); } if (!$user->isAllowed('block')) { $this->dieUsageMsg('cantunblock'); } # bug 15810: blocked admins should have limited access here if ($user->isBlocked()) { $status = SpecialBlock::checkUnblockSelf($params['user'], $user); if ($status !== true) { $msg = $this->parseMsg($status); $this->dieUsage($msg['info'], $msg['code'], 0, array('blockinfo' => ApiQueryUserInfo::getBlockInfo($user->getBlock()))); } } $data = array('Target' => is_null($params['id']) ? $params['user'] : "******", 'Reason' => $params['reason']); $block = Block::newFromTarget($data['Target']); $retval = SpecialUnblock::processUnblock($data, $this->getContext()); if ($retval !== true) { $this->dieUsageMsg($retval[0]); } $res['id'] = $block->getId(); $target = $block->getType() == Block::TYPE_AUTO ? '' : $block->getTarget(); $res['user'] = $target instanceof User ? $target->getName() : $target; $res['userid'] = $target instanceof User ? $target->getId() : 0; $res['reason'] = $params['reason']; $this->getResult()->addValue(null, $this->getModuleName(), $res); }
/** * Blocks the user specified in the parameters for the given expiry, with the * given reason, and with all other settings provided in the params. If the block * succeeds, produces a result containing the details of the block and notice * of success. If it fails, the result will specify the nature of the error. */ public function execute() { global $wgContLang; $user = $this->getUser(); $params = $this->extractRequestParams(); if (!$user->isAllowed('block')) { $this->dieUsageMsg('cantblock'); } # bug 15810: blocked admins should have limited access here if ($user->isBlocked()) { $status = SpecialBlock::checkUnblockSelf($params['user'], $user); if ($status !== true) { $msg = $this->parseMsg($status); $this->dieUsage($msg['info'], $msg['code'], 0, ['blockinfo' => ApiQueryUserInfo::getBlockInfo($user->getBlock())]); } } $target = User::newFromName($params['user']); // Bug 38633 - if the target is a user (not an IP address), but it // doesn't exist or is unusable, error. if ($target instanceof User && ($target->isAnon() || !User::isUsableName($target->getName()))) { $this->dieUsageMsg(['nosuchuser', $params['user']]); } if ($params['hidename'] && !$user->isAllowed('hideuser')) { $this->dieUsageMsg('canthide'); } if ($params['noemail'] && !SpecialBlock::canBlockEmail($user)) { $this->dieUsageMsg('cantblock-email'); } $data = ['PreviousTarget' => $params['user'], 'Target' => $params['user'], 'Reason' => [$params['reason'], 'other', $params['reason']], 'Expiry' => $params['expiry'], 'HardBlock' => !$params['anononly'], 'CreateAccount' => $params['nocreate'], 'AutoBlock' => $params['autoblock'], 'DisableEmail' => $params['noemail'], 'HideUser' => $params['hidename'], 'DisableUTEdit' => !$params['allowusertalk'], 'Reblock' => $params['reblock'], 'Watch' => $params['watchuser'], 'Confirm' => true]; $retval = SpecialBlock::processForm($data, $this->getContext()); if ($retval !== true) { // We don't care about multiple errors, just report one of them $this->dieUsageMsg($retval); } list($target, ) = SpecialBlock::getTargetAndType($params['user']); $res['user'] = $params['user']; $res['userID'] = $target instanceof User ? $target->getId() : 0; $block = Block::newFromTarget($target, null, true); if ($block instanceof Block) { $res['expiry'] = $wgContLang->formatExpiry($block->mExpiry, TS_ISO_8601, 'infinite'); $res['id'] = $block->getId(); } else { # should be unreachable $res['expiry'] = ''; $res['id'] = ''; } $res['reason'] = $params['reason']; $res['anononly'] = $params['anononly']; $res['nocreate'] = $params['nocreate']; $res['autoblock'] = $params['autoblock']; $res['noemail'] = $params['noemail']; $res['hidename'] = $params['hidename']; $res['allowusertalk'] = $params['allowusertalk']; $res['watchuser'] = $params['watchuser']; $this->getResult()->addValue(null, $this->getModuleName(), $res); }
public function execute() { $this->useTransactionalTimeLimit(); $params = $this->extractRequestParams(); if (!$this->getUser()->isAllowed('undelete')) { $this->dieUsageMsg('permdenied-undelete'); } if ($this->getUser()->isBlocked()) { $this->dieUsage('You have been blocked from editing', 'blocked', 0, array('blockinfo' => ApiQueryUserInfo::getBlockInfo($this->getUser()->getBlock()))); } $titleObj = Title::newFromText($params['title']); if (!$titleObj || $titleObj->isExternal()) { $this->dieUsageMsg(array('invalidtitle', $params['title'])); } // Convert timestamps if (!isset($params['timestamps'])) { $params['timestamps'] = array(); } if (!is_array($params['timestamps'])) { $params['timestamps'] = array($params['timestamps']); } foreach ($params['timestamps'] as $i => $ts) { $params['timestamps'][$i] = wfTimestamp(TS_MW, $ts); } $pa = new PageArchive($titleObj, $this->getConfig()); $retval = $pa->undelete(isset($params['timestamps']) ? $params['timestamps'] : array(), $params['reason'], $params['fileids'], false, $this->getUser()); if (!is_array($retval)) { $this->dieUsageMsg('cannotundelete'); } if ($retval[1]) { Hooks::run('FileUndeleteComplete', array($titleObj, $params['fileids'], $this->getUser(), $params['reason'])); } $this->setWatch($params['watchlist'], $titleObj); $info['title'] = $titleObj->getPrefixedText(); $info['revisions'] = intval($retval[0]); $info['fileversions'] = intval($retval[1]); $info['reason'] = $retval[2]; $this->getResult()->addValue(null, $this->getModuleName(), $info); }
public function execute() { $params = $this->extractRequestParams(); $user = $this->getUser(); // make sure the user is allowed if (!$user->isAllowed('changetags')) { $this->dieUsage("You don't have permission to add or remove change tags from individual edits", 'permissiondenied'); } if ($user->isBlocked()) { $block = $user->getBlock(); // Die using the appropriate message depending on block type if ($block->getType() == TYPE_AUTO) { $this->dieUsage('Your IP address has been blocked automatically, because it was used by a blocked user', 'autoblocked', 0, array('blockinfo' => ApiQueryUserInfo::getBlockInfo($block))); } else { $this->dieUsage('You have been blocked from editing', 'blocked', 0, array('blockinfo' => ApiQueryUserInfo::getBlockInfo($block))); } } // validate and process each revid, rcid and logid $this->requireAtLeastOneParameter($params, 'revid', 'rcid', 'logid'); $ret = array(); if ($params['revid']) { foreach ($params['revid'] as $id) { $ret[] = $this->processIndividual('revid', $params, $id); } } if ($params['rcid']) { foreach ($params['rcid'] as $id) { $ret[] = $this->processIndividual('rcid', $params, $id); } } if ($params['logid']) { foreach ($params['logid'] as $id) { $ret[] = $this->processIndividual('logid', $params, $id); } } ApiResult::setIndexedTagName($ret, 'result'); $this->getResult()->addValue(null, $this->getModuleName(), $ret); }
public function execute() { $user = $this->getUser(); $params = $this->extractRequestParams(); if (is_null($params['text']) && is_null($params['appendtext']) && is_null($params['prependtext']) && $params['undo'] == 0) { $this->dieUsageMsg('missingtext'); } $pageObj = $this->getTitleOrPageId($params); $titleObj = $pageObj->getTitle(); $apiResult = $this->getResult(); if ($params['redirect']) { if ($params['prependtext'] === null && $params['appendtext'] === null && $params['section'] !== 'new') { $this->dieUsage('You have attempted to edit using the "redirect"-following' . ' mode, which must be used in conjuction with section=new, prependtext' . ', or appendtext.', 'redirect-appendonly'); } if ($titleObj->isRedirect()) { $oldTitle = $titleObj; $titles = Revision::newFromTitle($oldTitle, false, Revision::READ_LATEST)->getContent(Revision::FOR_THIS_USER, $user)->getRedirectChain(); // array_shift( $titles ); $redirValues = array(); /** @var $newTitle Title */ foreach ($titles as $id => $newTitle) { if (!isset($titles[$id - 1])) { $titles[$id - 1] = $oldTitle; } $redirValues[] = array('from' => $titles[$id - 1]->getPrefixedText(), 'to' => $newTitle->getPrefixedText()); $titleObj = $newTitle; } ApiResult::setIndexedTagName($redirValues, 'r'); $apiResult->addValue(null, 'redirects', $redirValues); // Since the page changed, update $pageObj $pageObj = WikiPage::factory($titleObj); } } if (!isset($params['contentmodel']) || $params['contentmodel'] == '') { $contentHandler = $pageObj->getContentHandler(); } else { $contentHandler = ContentHandler::getForModelID($params['contentmodel']); } $name = $titleObj->getPrefixedDBkey(); $model = $contentHandler->getModelID(); if ($contentHandler->supportsDirectApiEditing() === false) { $this->dieUsage("Direct editing via API is not supported for content model {$model} used by {$name}", 'no-direct-editing'); } if (!isset($params['contentformat']) || $params['contentformat'] == '') { $params['contentformat'] = $contentHandler->getDefaultFormat(); } $contentFormat = $params['contentformat']; if (!$contentHandler->isSupportedFormat($contentFormat)) { $this->dieUsage("The requested format {$contentFormat} is not supported for content model " . " {$model} used by {$name}", 'badformat'); } if ($params['createonly'] && $titleObj->exists()) { $this->dieUsageMsg('createonly-exists'); } if ($params['nocreate'] && !$titleObj->exists()) { $this->dieUsageMsg('nocreate-missing'); } // Now let's check whether we're even allowed to do this $errors = $titleObj->getUserPermissionsErrors('edit', $user); if (!$titleObj->exists()) { $errors = array_merge($errors, $titleObj->getUserPermissionsErrors('create', $user)); } if (count($errors)) { if (is_array($errors[0])) { switch ($errors[0][0]) { case 'blockedtext': $this->dieUsage('You have been blocked from editing', 'blocked', 0, array('blockinfo' => ApiQueryUserInfo::getBlockInfo($user->getBlock()))); break; case 'autoblockedtext': $this->dieUsage('Your IP address has been blocked automatically, because it was used by a blocked user', 'autoblocked', 0, array('blockinfo' => ApiQueryUserInfo::getBlockInfo($user->getBlock()))); break; default: $this->dieUsageMsg($errors[0]); } } else { $this->dieUsageMsg($errors[0]); } } $toMD5 = $params['text']; if (!is_null($params['appendtext']) || !is_null($params['prependtext'])) { $content = $pageObj->getContent(); if (!$content) { if ($titleObj->getNamespace() == NS_MEDIAWIKI) { # If this is a MediaWiki:x message, then load the messages # and return the message value for x. $text = $titleObj->getDefaultMessageText(); if ($text === false) { $text = ''; } try { $content = ContentHandler::makeContent($text, $this->getTitle()); } catch (MWContentSerializationException $ex) { $this->dieUsage($ex->getMessage(), 'parseerror'); return; } } else { # Otherwise, make a new empty content. $content = $contentHandler->makeEmptyContent(); } } // @todo Add support for appending/prepending to the Content interface if (!$content instanceof TextContent) { $mode = $contentHandler->getModelID(); $this->dieUsage("Can't append to pages using content model {$mode}", 'appendnotsupported'); } if (!is_null($params['section'])) { if (!$contentHandler->supportsSections()) { $modelName = $contentHandler->getModelID(); $this->dieUsage("Sections are not supported for this content model: {$modelName}.", 'sectionsnotsupported'); } if ($params['section'] == 'new') { // DWIM if they're trying to prepend/append to a new section. $content = null; } else { // Process the content for section edits $section = $params['section']; $content = $content->getSection($section); if (!$content) { $this->dieUsage("There is no section {$section}.", 'nosuchsection'); } } } if (!$content) { $text = ''; } else { $text = $content->serialize($contentFormat); } $params['text'] = $params['prependtext'] . $text . $params['appendtext']; $toMD5 = $params['prependtext'] . $params['appendtext']; } if ($params['undo'] > 0) { if ($params['undoafter'] > 0) { if ($params['undo'] < $params['undoafter']) { list($params['undo'], $params['undoafter']) = array($params['undoafter'], $params['undo']); } $undoafterRev = Revision::newFromId($params['undoafter']); } $undoRev = Revision::newFromId($params['undo']); if (is_null($undoRev) || $undoRev->isDeleted(Revision::DELETED_TEXT)) { $this->dieUsageMsg(array('nosuchrevid', $params['undo'])); } if ($params['undoafter'] == 0) { $undoafterRev = $undoRev->getPrevious(); } if (is_null($undoafterRev) || $undoafterRev->isDeleted(Revision::DELETED_TEXT)) { $this->dieUsageMsg(array('nosuchrevid', $params['undoafter'])); } if ($undoRev->getPage() != $pageObj->getID()) { $this->dieUsageMsg(array('revwrongpage', $undoRev->getID(), $titleObj->getPrefixedText())); } if ($undoafterRev->getPage() != $pageObj->getID()) { $this->dieUsageMsg(array('revwrongpage', $undoafterRev->getID(), $titleObj->getPrefixedText())); } $newContent = $contentHandler->getUndoContent($pageObj->getRevision(), $undoRev, $undoafterRev); if (!$newContent) { $this->dieUsageMsg('undo-failure'); } $params['text'] = $newContent->serialize($params['contentformat']); // If no summary was given and we only undid one rev, // use an autosummary if (is_null($params['summary']) && $titleObj->getNextRevisionID($undoafterRev->getID()) == $params['undo']) { $params['summary'] = wfMessage('undo-summary')->params($params['undo'], $undoRev->getUserText())->inContentLanguage()->text(); } } // See if the MD5 hash checks out if (!is_null($params['md5']) && md5($toMD5) !== $params['md5']) { $this->dieUsageMsg('hashcheckfailed'); } // EditPage wants to parse its stuff from a WebRequest // That interface kind of sucks, but it's workable $requestArray = array('wpTextbox1' => $params['text'], 'format' => $contentFormat, 'model' => $contentHandler->getModelID(), 'wpEditToken' => $params['token'], 'wpIgnoreBlankSummary' => true, 'wpIgnoreBlankArticle' => true, 'wpIgnoreSelfRedirect' => true, 'bot' => $params['bot']); if (!is_null($params['summary'])) { $requestArray['wpSummary'] = $params['summary']; } if (!is_null($params['sectiontitle'])) { $requestArray['wpSectionTitle'] = $params['sectiontitle']; } // TODO: Pass along information from 'undoafter' as well if ($params['undo'] > 0) { $requestArray['wpUndidRevision'] = $params['undo']; } // Watch out for basetimestamp == '' or '0' // It gets treated as NOW, almost certainly causing an edit conflict if ($params['basetimestamp'] !== null && (bool) $this->getMain()->getVal('basetimestamp')) { $requestArray['wpEdittime'] = $params['basetimestamp']; } else { $requestArray['wpEdittime'] = $pageObj->getTimestamp(); } if ($params['starttimestamp'] !== null) { $requestArray['wpStarttime'] = $params['starttimestamp']; } else { $requestArray['wpStarttime'] = wfTimestampNow(); // Fake wpStartime } if ($params['minor'] || !$params['notminor'] && $user->getOption('minordefault')) { $requestArray['wpMinoredit'] = ''; } if ($params['recreate']) { $requestArray['wpRecreate'] = ''; } if (!is_null($params['section'])) { $section = $params['section']; if (!preg_match('/^((T-)?\\d+|new)$/', $section)) { $this->dieUsage("The section parameter must be a valid section id or 'new'", "invalidsection"); } $content = $pageObj->getContent(); if ($section !== '0' && $section != 'new' && (!$content || !$content->getSection($section))) { $this->dieUsage("There is no section {$section}.", 'nosuchsection'); } $requestArray['wpSection'] = $params['section']; } else { $requestArray['wpSection'] = ''; } $watch = $this->getWatchlistValue($params['watchlist'], $titleObj); // Deprecated parameters if ($params['watch']) { $this->logFeatureUsage('action=edit&watch'); $watch = true; } elseif ($params['unwatch']) { $this->logFeatureUsage('action=edit&unwatch'); $watch = false; } if ($watch) { $requestArray['wpWatchthis'] = ''; } // Apply change tags if (count($params['tags'])) { if ($user->isAllowed('applychangetags')) { $requestArray['wpChangeTags'] = implode(',', $params['tags']); } else { $this->dieUsage('You don\'t have permission to set change tags.', 'taggingnotallowed'); } } // Pass through anything else we might have been given, to support extensions // This is kind of a hack but it's the best we can do to make extensions work $requestArray += $this->getRequest()->getValues(); global $wgTitle, $wgRequest; $req = new DerivativeRequest($this->getRequest(), $requestArray, true); // Some functions depend on $wgTitle == $ep->mTitle // TODO: Make them not or check if they still do $wgTitle = $titleObj; $articleContext = new RequestContext(); $articleContext->setRequest($req); $articleContext->setWikiPage($pageObj); $articleContext->setUser($this->getUser()); /** @var $articleObject Article */ $articleObject = Article::newFromWikiPage($pageObj, $articleContext); $ep = new EditPage($articleObject); $ep->setApiEditOverride(true); $ep->setContextTitle($titleObj); $ep->importFormData($req); $content = $ep->textbox1; // The following is needed to give the hook the full content of the // new revision rather than just the current section. (Bug 52077) if (!is_null($params['section']) && $contentHandler->supportsSections() && $titleObj->exists()) { // If sectiontitle is set, use it, otherwise use the summary as the section title (for // backwards compatibility with old forms/bots). if ($ep->sectiontitle !== '') { $sectionTitle = $ep->sectiontitle; } else { $sectionTitle = $ep->summary; } $contentObj = $contentHandler->unserializeContent($content, $contentFormat); $fullContentObj = $articleObject->replaceSectionContent($params['section'], $contentObj, $sectionTitle); if ($fullContentObj) { $content = $fullContentObj->serialize($contentFormat); } else { // This most likely means we have an edit conflict which means that the edit // wont succeed anyway. $this->dieUsageMsg('editconflict'); } } // Run hooks // Handle APIEditBeforeSave parameters $r = array(); if (!Hooks::run('APIEditBeforeSave', array($ep, $content, &$r))) { if (count($r)) { $r['result'] = 'Failure'; $apiResult->addValue(null, $this->getModuleName(), $r); return; } $this->dieUsageMsg('hookaborted'); } // Do the actual save $oldRevId = $articleObject->getRevIdFetched(); $result = null; // Fake $wgRequest for some hooks inside EditPage // @todo FIXME: This interface SUCKS $oldRequest = $wgRequest; $wgRequest = $req; $status = $ep->attemptSave($result); $wgRequest = $oldRequest; switch ($status->value) { case EditPage::AS_HOOK_ERROR: case EditPage::AS_HOOK_ERROR_EXPECTED: if (isset($status->apiHookResult)) { $r = $status->apiHookResult; $r['result'] = 'Failure'; $apiResult->addValue(null, $this->getModuleName(), $r); return; } else { $this->dieUsageMsg('hookaborted'); } case EditPage::AS_PARSE_ERROR: $this->dieUsage($status->getMessage(), 'parseerror'); case EditPage::AS_IMAGE_REDIRECT_ANON: $this->dieUsageMsg('noimageredirect-anon'); case EditPage::AS_IMAGE_REDIRECT_LOGGED: $this->dieUsageMsg('noimageredirect-logged'); case EditPage::AS_SPAM_ERROR: $this->dieUsageMsg(array('spamdetected', $result['spam'])); case EditPage::AS_BLOCKED_PAGE_FOR_USER: $this->dieUsage('You have been blocked from editing', 'blocked', 0, array('blockinfo' => ApiQueryUserInfo::getBlockInfo($user->getBlock()))); case EditPage::AS_MAX_ARTICLE_SIZE_EXCEEDED: case EditPage::AS_CONTENT_TOO_BIG: $this->dieUsageMsg(array('contenttoobig', $this->getConfig()->get('MaxArticleSize'))); case EditPage::AS_READ_ONLY_PAGE_ANON: $this->dieUsageMsg('noedit-anon'); case EditPage::AS_READ_ONLY_PAGE_LOGGED: $this->dieUsageMsg('noedit'); case EditPage::AS_READ_ONLY_PAGE: $this->dieReadOnly(); case EditPage::AS_RATE_LIMITED: $this->dieUsageMsg('actionthrottledtext'); case EditPage::AS_ARTICLE_WAS_DELETED: $this->dieUsageMsg('wasdeleted'); case EditPage::AS_NO_CREATE_PERMISSION: $this->dieUsageMsg('nocreate-loggedin'); case EditPage::AS_NO_CHANGE_CONTENT_MODEL: $this->dieUsageMsg('cantchangecontentmodel'); case EditPage::AS_BLANK_ARTICLE: $this->dieUsageMsg('blankpage'); case EditPage::AS_CONFLICT_DETECTED: $this->dieUsageMsg('editconflict'); case EditPage::AS_TEXTBOX_EMPTY: $this->dieUsageMsg('emptynewsection'); case EditPage::AS_CHANGE_TAG_ERROR: $this->dieStatus($status); case EditPage::AS_SUCCESS_NEW_ARTICLE: $r['new'] = true; // fall-through // fall-through case EditPage::AS_SUCCESS_UPDATE: $r['result'] = 'Success'; $r['pageid'] = intval($titleObj->getArticleID()); $r['title'] = $titleObj->getPrefixedText(); $r['contentmodel'] = $articleObject->getContentModel(); $newRevId = $articleObject->getLatest(); if ($newRevId == $oldRevId) { $r['nochange'] = true; } else { $r['oldrevid'] = intval($oldRevId); $r['newrevid'] = intval($newRevId); $r['newtimestamp'] = wfTimestamp(TS_ISO_8601, $pageObj->getTimestamp()); } break; case EditPage::AS_SUMMARY_NEEDED: // Shouldn't happen since we set wpIgnoreBlankSummary, but just in case $this->dieUsageMsg('summaryrequired'); case EditPage::AS_END: default: // $status came from WikiPage::doEdit() $errors = $status->getErrorsArray(); $this->dieUsageMsg($errors[0]); // TODO: Add new errors to message map break; } $apiResult->addValue(null, $this->getModuleName(), $r); }
/** * Throw a UsageException, which will (if uncaught) call the main module's * error handler and die with an error message including block info. * * @since 1.27 * @param Block $block The block used to generate the UsageException * @throws UsageException always */ public function dieBlocked(Block $block) { // Die using the appropriate message depending on block type if ($block->getType() == Block::TYPE_AUTO) { $this->dieUsage('Your IP address has been blocked automatically, because it was used by a blocked user', 'autoblocked', 0, array('blockinfo' => ApiQueryUserInfo::getBlockInfo($block))); } else { $this->dieUsage('You have been blocked from editing', 'blocked', 0, array('blockinfo' => ApiQueryUserInfo::getBlockInfo($block))); } }
/** * Executes the log-in attempt using the parameters passed. If * the log-in succeeds, it attaches a cookie to the session * and outputs the user id, username, and session token. If a * log-in fails, as the result of a bad password, a nonexistent * user, or any other reason, the host is cached with an expiry * and no log-in attempts will be accepted until that expiry * is reached. The expiry is $this->mLoginThrottle. */ public function execute() { // If we're in a mode that breaks the same-origin policy, no tokens can // be obtained if ($this->lacksSameOriginSecurity()) { $this->getResult()->addValue(null, 'login', array('result' => 'Aborted', 'reason' => 'Cannot log in when the same-origin policy is not applied')); return; } $params = $this->extractRequestParams(); $result = array(); // Init session if necessary if (session_id() == '') { wfSetupSession(); } $context = new DerivativeContext($this->getContext()); $context->setRequest(new DerivativeRequest($this->getContext()->getRequest(), array('wpName' => $params['name'], 'wpPassword' => $params['password'], 'wpDomain' => $params['domain'], 'wpLoginToken' => $params['token'], 'wpRemember' => ''))); $loginForm = new LoginForm(); $loginForm->setContext($context); $authRes = $loginForm->authenticateUserData(); switch ($authRes) { case LoginForm::SUCCESS: $user = $context->getUser(); $this->getContext()->setUser($user); $user->setCookies($this->getRequest(), null, true); ApiQueryInfo::resetTokenCache(); // Run hooks. // @todo FIXME: Split back and frontend from this hook. // @todo FIXME: This hook should be placed in the backend $injected_html = ''; Hooks::run('UserLoginComplete', array(&$user, &$injected_html)); $result['result'] = 'Success'; $result['lguserid'] = intval($user->getId()); $result['lgusername'] = $user->getName(); $result['lgtoken'] = $user->getToken(); $result['cookieprefix'] = $this->getConfig()->get('CookiePrefix'); $result['sessionid'] = session_id(); break; case LoginForm::NEED_TOKEN: $result['result'] = 'NeedToken'; $result['token'] = $loginForm->getLoginToken(); $result['cookieprefix'] = $this->getConfig()->get('CookiePrefix'); $result['sessionid'] = session_id(); break; case LoginForm::WRONG_TOKEN: $result['result'] = 'WrongToken'; break; case LoginForm::NO_NAME: $result['result'] = 'NoName'; break; case LoginForm::ILLEGAL: $result['result'] = 'Illegal'; break; case LoginForm::WRONG_PLUGIN_PASS: $result['result'] = 'WrongPluginPass'; break; case LoginForm::NOT_EXISTS: $result['result'] = 'NotExists'; break; // bug 20223 - Treat a temporary password as wrong. Per SpecialUserLogin: // The e-mailed temporary password should not be used for actual logins. // bug 20223 - Treat a temporary password as wrong. Per SpecialUserLogin: // The e-mailed temporary password should not be used for actual logins. case LoginForm::RESET_PASS: case LoginForm::WRONG_PASS: $result['result'] = 'WrongPass'; break; case LoginForm::EMPTY_PASS: $result['result'] = 'EmptyPass'; break; case LoginForm::CREATE_BLOCKED: $result['result'] = 'CreateBlocked'; $result['details'] = 'Your IP address is blocked from account creation'; $block = $context->getUser()->getBlock(); if ($block) { $result = array_merge($result, ApiQueryUserInfo::getBlockInfo($block)); } break; case LoginForm::THROTTLED: $result['result'] = 'Throttled'; $throttle = $this->getConfig()->get('PasswordAttemptThrottle'); $result['wait'] = intval($throttle['seconds']); break; case LoginForm::USER_BLOCKED: $result['result'] = 'Blocked'; $block = User::newFromName($params['name'])->getBlock(); if ($block) { $result = array_merge($result, ApiQueryUserInfo::getBlockInfo($block)); } break; case LoginForm::ABORTED: $result['result'] = 'Aborted'; $result['reason'] = $loginForm->mAbortLoginErrorMsg; break; default: ApiBase::dieDebug(__METHOD__, "Unhandled case value: {$authRes}"); } $this->getResult()->addValue(null, 'login', $result); LoggerFactory::getInstance('authmanager')->info('Login attempt', array('event' => 'login', 'successful' => $authRes === LoginForm::SUCCESS, 'status' => LoginForm::$statusCodes[$authRes])); }
public function execute() { $this->useTransactionalTimeLimit(); $params = $this->extractRequestParams(); $user = $this->getUser(); if (!$user->isAllowed(RevisionDeleter::getRestriction($params['type']))) { $this->dieUsageMsg('badaccess-group0'); } if ($user->isBlocked()) { $block = $user->getBlock(); // Die using the appropriate message depending on block type if ($block->getType() == TYPE_AUTO) { $this->dieUsage('Your IP address has been blocked automatically, because it was used by a blocked user', 'autoblocked', 0, array('blockinfo' => ApiQueryUserInfo::getBlockInfo($block))); } else { $this->dieUsage('You have been blocked from editing', 'blocked', 0, array('blockinfo' => ApiQueryUserInfo::getBlockInfo($block))); } } if (!$params['ids']) { $this->dieUsage("At least one value is required for 'ids'", 'badparams'); } $hide = $params['hide'] ?: array(); $show = $params['show'] ?: array(); if (array_intersect($hide, $show)) { $this->dieUsage("Mutually exclusive values for 'hide' and 'show'", 'badparams'); } elseif (!$hide && !$show) { $this->dieUsage("At least one value is required for 'hide' or 'show'", 'badparams'); } $bits = array('content' => RevisionDeleter::getRevdelConstant($params['type']), 'comment' => Revision::DELETED_COMMENT, 'user' => Revision::DELETED_USER); $bitfield = array(); foreach ($bits as $key => $bit) { if (in_array($key, $hide)) { $bitfield[$bit] = 1; } elseif (in_array($key, $show)) { $bitfield[$bit] = 0; } else { $bitfield[$bit] = -1; } } if ($params['suppress'] === 'yes') { if (!$user->isAllowed('suppressrevision')) { $this->dieUsageMsg('badaccess-group0'); } $bitfield[Revision::DELETED_RESTRICTED] = 1; } elseif ($params['suppress'] === 'no') { $bitfield[Revision::DELETED_RESTRICTED] = 0; } else { $bitfield[Revision::DELETED_RESTRICTED] = -1; } $targetObj = null; if ($params['target']) { $targetObj = Title::newFromText($params['target']); } $targetObj = RevisionDeleter::suggestTarget($params['type'], $targetObj, $params['ids']); if ($targetObj === null) { $this->dieUsage('A target title is required for this RevDel type', 'needtarget'); } $list = RevisionDeleter::createList($params['type'], $this->getContext(), $targetObj, $params['ids']); $status = $list->setVisibility(array('value' => $bitfield, 'comment' => $params['reason'], 'perItemStatus' => true)); $result = $this->getResult(); $data = $this->extractStatusInfo($status); $data['target'] = $targetObj->getFullText(); $data['items'] = array(); foreach ($status->itemStatuses as $id => $s) { $data['items'][$id] = $this->extractStatusInfo($s); $data['items'][$id]['id'] = $id; } $list->reloadFromMaster(); // @codingStandardsIgnoreStart Avoid function calls in a FOR loop test part for ($item = $list->reset(); $list->current(); $item = $list->next()) { $data['items'][$item->getId()] += $item->getApiData($this->getResult()); } // @codingStandardsIgnoreEnd $data['items'] = array_values($data['items']); ApiResult::setIndexedTagName($data['items'], 'i'); $result->addValue(null, $this->getModuleName(), $data); }
public function execute() { // If we're in a mode that breaks the same-origin policy, no tokens can // be obtained if ($this->lacksSameOriginSecurity()) { $this->dieUsage('Cannot create account when the same-origin policy is not applied', 'aborted'); } // $loginForm->addNewaccountInternal will throw exceptions // if wiki is read only (already handled by api), user is blocked or does not have rights. // Use userCan in order to hit GlobalBlock checks (according to Special:userlogin) $loginTitle = SpecialPage::getTitleFor('Userlogin'); if (!$loginTitle->userCan('createaccount', $this->getUser())) { $this->dieUsage('You do not have the right to create a new account', 'permdenied-createaccount'); } if ($this->getUser()->isBlockedFromCreateAccount()) { $this->dieUsage('You cannot create a new account because you are blocked', 'blocked', 0, array('blockinfo' => ApiQueryUserInfo::getBlockInfo($this->getUser()->getBlock()))); } $params = $this->extractRequestParams(); // Init session if necessary if (session_id() == '') { wfSetupSession(); } if ($params['mailpassword'] && !$params['email']) { $this->dieUsageMsg('noemail'); } if ($params['language'] && !Language::isSupportedLanguage($params['language'])) { $this->dieUsage('Invalid language parameter', 'langinvalid'); } $context = new DerivativeContext($this->getContext()); $context->setRequest(new DerivativeRequest($this->getContext()->getRequest(), array('type' => 'signup', 'uselang' => $params['language'], 'wpName' => $params['name'], 'wpPassword' => $params['password'], 'wpRetype' => $params['password'], 'wpDomain' => $params['domain'], 'wpEmail' => $params['email'], 'wpRealName' => $params['realname'], 'wpCreateaccountToken' => $params['token'], 'wpCreateaccount' => $params['mailpassword'] ? null : '1', 'wpCreateaccountMail' => $params['mailpassword'] ? '1' : null))); $loginForm = new LoginForm(); $loginForm->setContext($context); Hooks::run('AddNewAccountApiForm', array($this, $loginForm)); $loginForm->load(); $status = $loginForm->addNewaccountInternal(); $result = array(); if ($status->isGood()) { // Success! $user = $status->getValue(); if ($params['language']) { $user->setOption('language', $params['language']); } if ($params['mailpassword']) { // If mailpassword was set, disable the password and send an email. $user->setPassword(null); $status->merge($loginForm->mailPasswordInternal($user, false, 'createaccount-title', 'createaccount-text')); } elseif ($this->getConfig()->get('EmailAuthentication') && Sanitizer::validateEmail($user->getEmail())) { // Send out an email authentication message if needed $status->merge($user->sendConfirmationMail()); } // Save settings (including confirmation token) $user->saveSettings(); Hooks::run('AddNewAccount', array($user, $params['mailpassword'])); if ($params['mailpassword']) { $logAction = 'byemail'; } elseif ($this->getUser()->isLoggedIn()) { $logAction = 'create2'; } else { $logAction = 'create'; } $user->addNewUserLogEntry($logAction, (string) $params['reason']); // Add username, id, and token to result. $result['username'] = $user->getName(); $result['userid'] = $user->getId(); $result['token'] = $user->getToken(); } $apiResult = $this->getResult(); if ($status->hasMessage('sessionfailure') || $status->hasMessage('nocookiesfornew')) { // Token was incorrect, so add it to result, but don't throw an exception // since not having the correct token is part of the normal // flow of events. $result['token'] = LoginForm::getCreateaccountToken(); $result['result'] = 'NeedToken'; } elseif (!$status->isOK()) { // There was an error. Die now. $this->dieStatus($status); } elseif (!$status->isGood()) { // Status is not good, but OK. This means warnings. $result['result'] = 'Warning'; // Add any warnings to the result $warnings = $status->getErrorsByType('warning'); if ($warnings) { foreach ($warnings as &$warning) { ApiResult::setIndexedTagName($warning['params'], 'param'); } ApiResult::setIndexedTagName($warnings, 'warning'); $result['warnings'] = $warnings; } } else { // Everything was fine. $result['result'] = 'Success'; } // Give extensions a chance to modify the API result data Hooks::run('AddNewAccountApiResult', array($this, $loginForm, &$result)); $apiResult->addValue(null, 'createaccount', $result); }
/** * Executes the log-in attempt using the parameters passed. If * the log-in succeeds, it attaches a cookie to the session * and outputs the user id, username, and session token. If a * log-in fails, as the result of a bad password, a nonexistent * user, or any other reason, the host is cached with an expiry * and no log-in attempts will be accepted until that expiry * is reached. The expiry is $this->mLoginThrottle. */ public function execute() { // If we're in a mode that breaks the same-origin policy, no tokens can // be obtained if ($this->lacksSameOriginSecurity()) { $this->getResult()->addValue(null, 'login', array('result' => 'Aborted', 'reason' => 'Cannot log in when the same-origin policy is not applied')); return; } $params = $this->extractRequestParams(); $result = array(); // Make sure session is persisted $session = MediaWiki\Session\SessionManager::getGlobalSession(); $session->persist(); // Make sure it's possible to log in if (!$session->canSetUser()) { $this->getResult()->addValue(null, 'login', array('result' => 'Aborted', 'reason' => 'Cannot log in when using ' . $session->getProvider()->describe(Language::factory('en')))); return; } $authRes = false; $context = new DerivativeContext($this->getContext()); $loginType = 'N/A'; // Check login token $token = LoginForm::getLoginToken(); if (!$token) { LoginForm::setLoginToken(); $authRes = LoginForm::NEED_TOKEN; } elseif (!$params['token']) { $authRes = LoginForm::NEED_TOKEN; } elseif ($token !== $params['token']) { $authRes = LoginForm::WRONG_TOKEN; } // Try bot passwords if ($authRes === false && $this->getConfig()->get('EnableBotPasswords') && strpos($params['name'], BotPassword::getSeparator()) !== false) { $status = BotPassword::login($params['name'], $params['password'], $this->getRequest()); if ($status->isOk()) { $session = $status->getValue(); $authRes = LoginForm::SUCCESS; $loginType = 'BotPassword'; } else { LoggerFactory::getInstance('authmanager')->info('BotPassword login failed: ' . $status->getWikiText()); } } // Normal login if ($authRes === false) { $context->setRequest(new DerivativeRequest($this->getContext()->getRequest(), array('wpName' => $params['name'], 'wpPassword' => $params['password'], 'wpDomain' => $params['domain'], 'wpLoginToken' => $params['token'], 'wpRemember' => ''))); $loginForm = new LoginForm(); $loginForm->setContext($context); $authRes = $loginForm->authenticateUserData(); $loginType = 'LoginForm'; } switch ($authRes) { case LoginForm::SUCCESS: $user = $context->getUser(); $this->getContext()->setUser($user); $user->setCookies($this->getRequest(), null, true); ApiQueryInfo::resetTokenCache(); // Run hooks. // @todo FIXME: Split back and frontend from this hook. // @todo FIXME: This hook should be placed in the backend $injected_html = ''; Hooks::run('UserLoginComplete', array(&$user, &$injected_html)); $result['result'] = 'Success'; $result['lguserid'] = intval($user->getId()); $result['lgusername'] = $user->getName(); // @todo: These are deprecated, and should be removed at some // point (1.28 at the earliest, and see T121527). They were ok // when the core cookie-based login was the only thing, but // CentralAuth broke that a while back and // SessionManager/AuthManager are *really* going to break it. $result['lgtoken'] = $user->getToken(); $result['cookieprefix'] = $this->getConfig()->get('CookiePrefix'); $result['sessionid'] = $session->getId(); break; case LoginForm::NEED_TOKEN: $result['result'] = 'NeedToken'; $result['token'] = LoginForm::getLoginToken(); // @todo: See above about deprecation $result['cookieprefix'] = $this->getConfig()->get('CookiePrefix'); $result['sessionid'] = $session->getId(); break; case LoginForm::WRONG_TOKEN: $result['result'] = 'WrongToken'; break; case LoginForm::NO_NAME: $result['result'] = 'NoName'; break; case LoginForm::ILLEGAL: $result['result'] = 'Illegal'; break; case LoginForm::WRONG_PLUGIN_PASS: $result['result'] = 'WrongPluginPass'; break; case LoginForm::NOT_EXISTS: $result['result'] = 'NotExists'; break; // bug 20223 - Treat a temporary password as wrong. Per SpecialUserLogin: // The e-mailed temporary password should not be used for actual logins. // bug 20223 - Treat a temporary password as wrong. Per SpecialUserLogin: // The e-mailed temporary password should not be used for actual logins. case LoginForm::RESET_PASS: case LoginForm::WRONG_PASS: $result['result'] = 'WrongPass'; break; case LoginForm::EMPTY_PASS: $result['result'] = 'EmptyPass'; break; case LoginForm::CREATE_BLOCKED: $result['result'] = 'CreateBlocked'; $result['details'] = 'Your IP address is blocked from account creation'; $block = $context->getUser()->getBlock(); if ($block) { $result = array_merge($result, ApiQueryUserInfo::getBlockInfo($block)); } break; case LoginForm::THROTTLED: $result['result'] = 'Throttled'; $throttle = $this->getConfig()->get('PasswordAttemptThrottle'); $result['wait'] = intval($throttle['seconds']); break; case LoginForm::USER_BLOCKED: $result['result'] = 'Blocked'; $block = User::newFromName($params['name'])->getBlock(); if ($block) { $result = array_merge($result, ApiQueryUserInfo::getBlockInfo($block)); } break; case LoginForm::ABORTED: $result['result'] = 'Aborted'; $result['reason'] = $loginForm->mAbortLoginErrorMsg; break; default: ApiBase::dieDebug(__METHOD__, "Unhandled case value: {$authRes}"); } $this->getResult()->addValue(null, 'login', $result); LoggerFactory::getInstance('authmanager')->info('Login attempt', array('event' => 'login', 'successful' => $authRes === LoginForm::SUCCESS, 'loginType' => $loginType, 'status' => LoginForm::$statusCodes[$authRes])); }
/** * Executes the log-in attempt using the parameters passed. If * the log-in succeeds, it attaches a cookie to the session * and outputs the user id, username, and session token. If a * log-in fails, as the result of a bad password, a nonexistent * user, or any other reason, the host is cached with an expiry * and no log-in attempts will be accepted until that expiry * is reached. The expiry is $this->mLoginThrottle. */ public function execute() { // If we're in a mode that breaks the same-origin policy, no tokens can // be obtained if ($this->lacksSameOriginSecurity()) { $this->getResult()->addValue(null, 'login', ['result' => 'Aborted', 'reason' => 'Cannot log in when the same-origin policy is not applied']); return; } $params = $this->extractRequestParams(); $result = []; // Make sure session is persisted $session = MediaWiki\Session\SessionManager::getGlobalSession(); $session->persist(); // Make sure it's possible to log in if (!$session->canSetUser()) { $this->getResult()->addValue(null, 'login', ['result' => 'Aborted', 'reason' => 'Cannot log in when using ' . $session->getProvider()->describe(Language::factory('en'))]); return; } $authRes = false; $context = new DerivativeContext($this->getContext()); $loginType = 'N/A'; // Check login token $token = $session->getToken('', 'login'); if ($token->wasNew() || !$params['token']) { $authRes = 'NeedToken'; } elseif (!$token->match($params['token'])) { $authRes = 'WrongToken'; } // Try bot passwords if ($authRes === false && $this->getConfig()->get('EnableBotPasswords') && strpos($params['name'], BotPassword::getSeparator()) !== false) { $status = BotPassword::login($params['name'], $params['password'], $this->getRequest()); if ($status->isOK()) { $session = $status->getValue(); $authRes = 'Success'; $loginType = 'BotPassword'; } else { $authRes = 'Failed'; $message = $status->getMessage(); LoggerFactory::getInstance('authmanager')->info('BotPassword login failed: ' . $status->getWikiText(false, false, 'en')); } } if ($authRes === false) { if ($this->getConfig()->get('DisableAuthManager')) { // Non-AuthManager login $context->setRequest(new DerivativeRequest($this->getContext()->getRequest(), ['wpName' => $params['name'], 'wpPassword' => $params['password'], 'wpDomain' => $params['domain'], 'wpLoginToken' => $params['token'], 'wpRemember' => ''])); $loginForm = new LoginForm(); $loginForm->setContext($context); $authRes = $loginForm->authenticateUserData(); $loginType = 'LoginForm'; switch ($authRes) { case LoginForm::SUCCESS: $authRes = 'Success'; break; case LoginForm::NEED_TOKEN: $authRes = 'NeedToken'; break; } } else { // Simplified AuthManager login, for backwards compatibility $manager = AuthManager::singleton(); $reqs = AuthenticationRequest::loadRequestsFromSubmission($manager->getAuthenticationRequests(AuthManager::ACTION_LOGIN, $this->getUser()), ['username' => $params['name'], 'password' => $params['password'], 'domain' => $params['domain'], 'rememberMe' => true]); $res = AuthManager::singleton()->beginAuthentication($reqs, 'null:'); switch ($res->status) { case AuthenticationResponse::PASS: if ($this->getConfig()->get('EnableBotPasswords')) { $warn = 'Main-account login via action=login is deprecated and may stop working ' . 'without warning.'; $warn .= ' To continue login with action=login, see [[Special:BotPasswords]].'; $warn .= ' To safely continue using main-account login, see action=clientlogin.'; } else { $warn = 'Login via action=login is deprecated and may stop working without warning.'; $warn .= ' To safely log in, see action=clientlogin.'; } $this->setWarning($warn); $authRes = 'Success'; $loginType = 'AuthManager'; break; case AuthenticationResponse::FAIL: // Hope it's not a PreAuthenticationProvider that failed... $authRes = 'Failed'; $message = $res->message; \MediaWiki\Logger\LoggerFactory::getInstance('authentication')->info(__METHOD__ . ': Authentication failed: ' . $message->plain()); break; default: $authRes = 'Aborted'; break; } } } $result['result'] = $authRes; switch ($authRes) { case 'Success': if ($this->getConfig()->get('DisableAuthManager')) { $user = $context->getUser(); $this->getContext()->setUser($user); $user->setCookies($this->getRequest(), null, true); } else { $user = $session->getUser(); } ApiQueryInfo::resetTokenCache(); // Deprecated hook $injected_html = ''; Hooks::run('UserLoginComplete', [&$user, &$injected_html]); $result['lguserid'] = intval($user->getId()); $result['lgusername'] = $user->getName(); // @todo: These are deprecated, and should be removed at some // point (1.28 at the earliest, and see T121527). They were ok // when the core cookie-based login was the only thing, but // CentralAuth broke that a while back and // SessionManager/AuthManager *really* break it. $result['lgtoken'] = $user->getToken(); $result['cookieprefix'] = $this->getConfig()->get('CookiePrefix'); $result['sessionid'] = $session->getId(); break; case 'NeedToken': $result['token'] = $token->toString(); $this->setWarning('Fetching a token via action=login is deprecated. ' . 'Use action=query&meta=tokens&type=login instead.'); $this->logFeatureUsage('action=login&!lgtoken'); // @todo: See above about deprecation $result['cookieprefix'] = $this->getConfig()->get('CookiePrefix'); $result['sessionid'] = $session->getId(); break; case 'WrongToken': break; case 'Failed': $result['reason'] = $message->useDatabase('false')->inLanguage('en')->text(); break; case 'Aborted': $result['reason'] = 'Authentication requires user interaction, ' . 'which is not supported by action=login.'; if ($this->getConfig()->get('EnableBotPasswords')) { $result['reason'] .= ' To be able to login with action=login, see [[Special:BotPasswords]].'; $result['reason'] .= ' To continue using main-account login, see action=clientlogin.'; } else { $result['reason'] .= ' To log in, see action=clientlogin.'; } break; // Results from LoginForm for when $wgDisableAuthManager is true // Results from LoginForm for when $wgDisableAuthManager is true case LoginForm::WRONG_TOKEN: $result['result'] = 'WrongToken'; break; case LoginForm::NO_NAME: $result['result'] = 'NoName'; break; case LoginForm::ILLEGAL: $result['result'] = 'Illegal'; break; case LoginForm::WRONG_PLUGIN_PASS: $result['result'] = 'WrongPluginPass'; break; case LoginForm::NOT_EXISTS: $result['result'] = 'NotExists'; break; // bug 20223 - Treat a temporary password as wrong. Per SpecialUserLogin: // The e-mailed temporary password should not be used for actual logins. // bug 20223 - Treat a temporary password as wrong. Per SpecialUserLogin: // The e-mailed temporary password should not be used for actual logins. case LoginForm::RESET_PASS: case LoginForm::WRONG_PASS: $result['result'] = 'WrongPass'; break; case LoginForm::EMPTY_PASS: $result['result'] = 'EmptyPass'; break; case LoginForm::CREATE_BLOCKED: $result['result'] = 'CreateBlocked'; $result['details'] = 'Your IP address is blocked from account creation'; $block = $context->getUser()->getBlock(); if ($block) { $result = array_merge($result, ApiQueryUserInfo::getBlockInfo($block)); } break; case LoginForm::THROTTLED: $result['result'] = 'Throttled'; $result['wait'] = intval($loginForm->mThrottleWait); break; case LoginForm::USER_BLOCKED: $result['result'] = 'Blocked'; $block = User::newFromName($params['name'])->getBlock(); if ($block) { $result = array_merge($result, ApiQueryUserInfo::getBlockInfo($block)); } break; case LoginForm::ABORTED: $result['result'] = 'Aborted'; $result['reason'] = $loginForm->mAbortLoginErrorMsg; break; default: ApiBase::dieDebug(__METHOD__, "Unhandled case value: {$authRes}"); } $this->getResult()->addValue(null, 'login', $result); if ($loginType === 'LoginForm' && isset(LoginForm::$statusCodes[$authRes])) { $authRes = LoginForm::$statusCodes[$authRes]; } LoggerFactory::getInstance('authmanager')->info('Login attempt', ['event' => 'login', 'successful' => $authRes === 'Success', 'loginType' => $loginType, 'status' => $authRes]); }