From c1f9b1f7b1b77776192048005dcc66dcf3df2bfb Mon Sep 17 00:00:00 2001 From: Pierre Schmitz Date: Sat, 27 Dec 2014 15:41:37 +0100 Subject: Update to MediaWiki 1.24.1 --- includes/specials/SpecialChangePassword.php | 385 ++++++++++++++-------------- 1 file changed, 193 insertions(+), 192 deletions(-) (limited to 'includes/specials/SpecialChangePassword.php') diff --git a/includes/specials/SpecialChangePassword.php b/includes/specials/SpecialChangePassword.php index a75e7e83..24664edb 100644 --- a/includes/specials/SpecialChangePassword.php +++ b/includes/specials/SpecialChangePassword.php @@ -26,228 +26,216 @@ * * @ingroup SpecialPage */ -class SpecialChangePassword extends UnlistedSpecialPage { +class SpecialChangePassword extends FormSpecialPage { + protected $mUserName; + protected $mDomain; - protected $mUserName, $mOldpass, $mNewpass, $mRetype, $mDomain; + // Optional Wikitext Message to show above the password change form + protected $mPreTextMessage = null; + + // label for old password input + protected $mOldPassMsg = null; public function __construct() { parent::__construct( 'ChangePassword', 'editmyprivateinfo' ); + $this->listed( false ); } /** * Main execution point + * @param string|null $par */ function execute( $par ) { - global $wgAuth; - - $this->setHeaders(); - $this->outputHeader(); $this->getOutput()->disallowUserJs(); - $request = $this->getRequest(); - $this->mUserName = trim( $request->getVal( 'wpName' ) ); - $this->mOldpass = $request->getVal( 'wpPassword' ); - $this->mNewpass = $request->getVal( 'wpNewPassword' ); - $this->mRetype = $request->getVal( 'wpRetype' ); - $this->mDomain = $request->getVal( 'wpDomain' ); - - $user = $this->getUser(); - - if ( !$user->isLoggedIn() && !LoginForm::getLoginToken() ) { - LoginForm::setLoginToken(); - } - - if ( !$request->wasPosted() && !$user->isLoggedIn() ) { - $this->error( $this->msg( 'resetpass-no-info' )->text() ); - - return; - } - - if ( $request->wasPosted() && $request->getBool( 'wpCancel' ) ) { - $titleObj = Title::newFromText( $request->getVal( 'returnto' ) ); - if ( !$titleObj instanceof Title ) { - $titleObj = Title::newMainPage(); - } - $query = $request->getVal( 'returntoquery' ); - $this->getOutput()->redirect( $titleObj->getFullURL( $query ) ); + parent::execute( $par ); + } - return; - } + protected function checkExecutePermissions( User $user ) { + parent::checkExecutePermissions( $user ); - $this->checkReadOnly(); - $this->checkPermissions(); - - if ( $request->wasPosted() && $user->matchEditToken( $request->getVal( 'token' ) ) ) { - try { - $this->mDomain = $wgAuth->getDomain(); - if ( !$wgAuth->allowPasswordChange() ) { - $this->error( $this->msg( 'resetpass_forbidden' )->text() ); - - return; - } - - if ( !$user->isLoggedIn() - && $request->getVal( 'wpLoginOnChangeToken' ) !== LoginForm::getLoginToken() - ) { - // Potential CSRF (bug 62497) - $this->error( $this->msg( 'sessionfailure' )->text() ); - return false; - } - - $this->attemptReset( $this->mNewpass, $this->mRetype ); - - if ( $user->isLoggedIn() ) { - $this->getOutput()->wrapWikiMsg( - "
\n$1\n
", - 'changepassword-success' - ); - $this->getOutput()->returnToMain(); - } else { - LoginForm::setLoginToken(); - $token = LoginForm::getLoginToken(); - $data = array( - 'action' => 'submitlogin', - 'wpName' => $this->mUserName, - 'wpDomain' => $this->mDomain, - 'wpLoginToken' => $token, - 'wpPassword' => $request->getVal( 'wpNewPassword' ), - ) + $request->getValues( 'wpRemember', 'returnto', 'returntoquery' ); - $login = new LoginForm( new DerivativeRequest( $request, $data, true ) ); - $login->setContext( $this->getContext() ); - $login->execute( null ); - } - - return; - } catch ( PasswordError $e ) { - $this->error( $e->getMessage() ); - } + if ( !$this->getRequest()->wasPosted() ) { + $this->requireLogin( 'resetpass-no-info' ); } - $this->showForm(); } /** - * @param $msg string + * Set a message at the top of the Change Password form + * @since 1.23 + * @param Message $msg Message to parse and add to the form header */ - function error( $msg ) { - $this->getOutput()->addHTML( Xml::element( 'p', array( 'class' => 'error' ), $msg ) ); + public function setChangeMessage( Message $msg ) { + $this->mPreTextMessage = $msg; } - function showForm() { - global $wgCookieExpiration; + /** + * Set a message at the top of the Change Password form + * @since 1.23 + * @param string $msg Message label for old/temp password field + */ + public function setOldPasswordMessage( $msg ) { + $this->mOldPassMsg = $msg; + } + protected function getFormFields() { $user = $this->getUser(); - if ( !$this->mUserName ) { - $this->mUserName = $user->getName(); + $request = $this->getRequest(); + + $oldpassMsg = $this->mOldPassMsg; + if ( $oldpassMsg === null ) { + $oldpassMsg = $user->isLoggedIn() ? 'oldpassword' : 'resetpass-temp-password'; } - $rememberMe = ''; - if ( !$user->isLoggedIn() ) { - $rememberMe = '' . - '' . - '' . - Xml::checkLabel( - $this->msg( 'remembermypassword' )->numParams( ceil( $wgCookieExpiration / ( 3600 * 24 ) ) )->text(), - 'wpRemember', 'wpRemember', - $this->getRequest()->getCheck( 'wpRemember' ) ) . - '' . - ''; - $submitMsg = 'resetpass_submit'; - $oldpassMsg = 'resetpass-temp-password'; - } else { - $oldpassMsg = 'oldpassword'; - $submitMsg = 'resetpass-submit-loggedin'; + + $fields = array( + 'Name' => array( + 'type' => 'info', + 'label-message' => 'username', + 'default' => $request->getVal( 'wpName', $user->getName() ), + ), + 'Password' => array( + 'type' => 'password', + 'label-message' => $oldpassMsg, + ), + 'NewPassword' => array( + 'type' => 'password', + 'label-message' => 'newpassword', + ), + 'Retype' => array( + 'type' => 'password', + 'label-message' => 'retypenew', + ), + ); + + if ( !$this->getUser()->isLoggedIn() ) { + if ( !LoginForm::getLoginToken() ) { + LoginForm::setLoginToken(); + } + $fields['LoginOnChangeToken'] = array( + 'type' => 'hidden', + 'label' => 'Change Password Token', + 'default' => LoginForm::getLoginToken(), + ); } + $extraFields = array(); wfRunHooks( 'ChangePasswordForm', array( &$extraFields ) ); - $prettyFields = array( - array( 'wpName', 'username', 'text', $this->mUserName ), - array( 'wpPassword', $oldpassMsg, 'password', $this->mOldpass ), - array( 'wpNewPassword', 'newpassword', 'password', null ), - array( 'wpRetype', 'retypenew', 'password', null ), - ); - $prettyFields = array_merge( $prettyFields, $extraFields ); - $hiddenFields = array( - 'token' => $user->getEditToken(), - 'wpName' => $this->mUserName, - 'wpDomain' => $this->mDomain, - ) + $this->getRequest()->getValues( 'returnto', 'returntoquery' ); - if ( !$user->isLoggedIn() ) { - $hiddenFields['wpLoginOnChangeToken'] = LoginForm::getLoginToken(); + foreach ( $extraFields as $extra ) { + list( $name, $label, $type, $default ) = $extra; + $fields[$name] = array( + 'type' => $type, + 'name' => $name, + 'label-message' => $label, + 'default' => $default, + ); } - $hiddenFieldsStr = ''; - foreach ( $hiddenFields as $fieldname => $fieldvalue ) { - $hiddenFieldsStr .= Html::hidden( $fieldname, $fieldvalue ) . "\n"; + + if ( !$user->isLoggedIn() ) { + $fields['Remember'] = array( + 'type' => 'check', + 'label' => $this->msg( 'remembermypassword' ) + ->numParams( + ceil( $this->getConfig()->get( 'CookieExpiration' ) / ( 3600 * 24 ) ) + )->text(), + 'default' => $request->getVal( 'wpRemember' ), + ); } - $this->getOutput()->addHTML( - Xml::fieldset( $this->msg( 'resetpass_header' )->text() ) . - Xml::openElement( 'form', - array( - 'method' => 'post', - 'action' => $this->getTitle()->getLocalURL(), - 'id' => 'mw-resetpass-form' ) ) . "\n" . - $hiddenFieldsStr . - $this->msg( 'resetpass_text' )->parseAsBlock() . "\n" . - Xml::openElement( 'table', array( 'id' => 'mw-resetpass-table' ) ) . "\n" . - $this->pretty( $prettyFields ) . "\n" . - $rememberMe . - "\n" . - "\n" . - '' . - Xml::submitButton( $this->msg( $submitMsg )->text() ) . - Xml::submitButton( $this->msg( 'resetpass-submit-cancel' )->text(), array( 'name' => 'wpCancel' ) ) . - "\n" . - "\n" . - Xml::closeElement( 'table' ) . - Xml::closeElement( 'form' ) . - Xml::closeElement( 'fieldset' ) . "\n" + + return $fields; + } + + protected function alterForm( HTMLForm $form ) { + $form->setId( 'mw-resetpass-form' ); + $form->setTableId( 'mw-resetpass-table' ); + $form->setWrapperLegendMsg( 'resetpass_header' ); + $form->setSubmitTextMsg( + $this->getUser()->isLoggedIn() + ? 'resetpass-submit-loggedin' + : 'resetpass_submit' ); + $form->addButton( 'wpCancel', $this->msg( 'resetpass-submit-cancel' )->text() ); + $form->setHeaderText( $this->msg( 'resetpass_text' )->parseAsBlock() ); + if ( $this->mPreTextMessage instanceof Message ) { + $form->addPreText( $this->mPreTextMessage->parseAsBlock() ); + } + $form->addHiddenFields( + $this->getRequest()->getValues( 'wpName', 'wpDomain', 'returnto', 'returntoquery' ) ); } - /** - * @param $fields array - * @return string - */ - function pretty( $fields ) { - $out = ''; - foreach ( $fields as $list ) { - list( $name, $label, $type, $value ) = $list; - if ( $type == 'text' ) { - $field = htmlspecialchars( $value ); - } else { - $attribs = array( 'id' => $name ); - if ( $name == 'wpNewPassword' || $name == 'wpRetype' ) { - $attribs = array_merge( $attribs, - User::passwordChangeInputAttribs() ); - } - if ( $name == 'wpPassword' ) { - $attribs[] = 'autofocus'; - } - $field = Html::input( $name, $value, $type, $attribs ); + public function onSubmit( array $data ) { + global $wgAuth; + + $request = $this->getRequest(); + + if ( $request->getCheck( 'wpLoginToken' ) ) { + // This comes from Special:Userlogin when logging in with a temporary password + return false; + } + + if ( !$this->getUser()->isLoggedIn() + && $request->getVal( 'wpLoginOnChangeToken' ) !== LoginForm::getLoginToken() + ) { + // Potential CSRF (bug 62497) + return false; + } + + if ( $request->getCheck( 'wpCancel' ) ) { + $titleObj = Title::newFromText( $request->getVal( 'returnto' ) ); + if ( !$titleObj instanceof Title ) { + $titleObj = Title::newMainPage(); } - $out .= "\n"; - $out .= "\t"; + $query = $request->getVal( 'returntoquery' ); + $this->getOutput()->redirect( $titleObj->getFullURL( $query ) ); + + return true; + } + + try { + $this->mUserName = $request->getVal( 'wpName', $this->getUser()->getName() ); + $this->mDomain = $wgAuth->getDomain(); - if ( $type != 'text' ) { - $out .= Xml::label( $this->msg( $label )->text(), $name ); - } else { - $out .= $this->msg( $label )->escaped(); + if ( !$wgAuth->allowPasswordChange() ) { + throw new ErrorPageError( 'changepassword', 'resetpass_forbidden' ); } - $out .= "\n"; - $out .= "\t"; - $out .= $field; - $out .= "\n"; - $out .= ""; + $this->attemptReset( $data['Password'], $data['NewPassword'], $data['Retype'] ); + + return true; + } catch ( PasswordError $e ) { + return $e->getMessage(); } + } - return $out; + public function onSuccess() { + if ( $this->getUser()->isLoggedIn() ) { + $this->getOutput()->wrapWikiMsg( + "
\n$1\n
", + 'changepassword-success' + ); + $this->getOutput()->returnToMain(); + } else { + $request = $this->getRequest(); + LoginForm::setLoginToken(); + $token = LoginForm::getLoginToken(); + $data = array( + 'action' => 'submitlogin', + 'wpName' => $this->mUserName, + 'wpDomain' => $this->mDomain, + 'wpLoginToken' => $token, + 'wpPassword' => $request->getVal( 'wpNewPassword' ), + ) + $request->getValues( 'wpRemember', 'returnto', 'returntoquery' ); + $login = new LoginForm( new DerivativeRequest( $request, $data, true ) ); + $login->setContext( $this->getContext() ); + $login->execute( null ); + } } /** - * @throws PasswordError when cannot set the new password because requirements not met. + * @param string $oldpass + * @param string $newpass + * @param string $retype + * @throws PasswordError When cannot set the new password because requirements not met. */ - protected function attemptReset( $newpass, $retype ) { - global $wgPasswordAttemptThrottle; - + protected function attemptReset( $oldpass, $newpass, $retype ) { $isSelf = ( $this->mUserName === $this->getUser()->getName() ); if ( $isSelf ) { $user = $this->getUser(); @@ -267,32 +255,40 @@ class SpecialChangePassword extends UnlistedSpecialPage { $throttleCount = LoginForm::incLoginThrottle( $this->mUserName ); if ( $throttleCount === true ) { $lang = $this->getLanguage(); - throw new PasswordError( $this->msg( 'login-throttled' ) - ->params( $lang->formatDuration( $wgPasswordAttemptThrottle['seconds'] ) ) + $throttleInfo = $this->getConfig()->get( 'PasswordAttemptThrottle' ); + throw new PasswordError( $this->msg( 'changepassword-throttled' ) + ->params( $lang->formatDuration( $throttleInfo['seconds'] ) ) ->text() ); } + // @todo Make these separate messages, since the message is written for both cases + if ( !$user->checkTemporaryPassword( $oldpass ) && !$user->checkPassword( $oldpass ) ) { + wfRunHooks( 'PrefsPasswordAudit', array( $user, $newpass, 'wrongpassword' ) ); + throw new PasswordError( $this->msg( 'resetpass-wrong-oldpass' )->text() ); + } + + // User is resetting their password to their old password + if ( $oldpass === $newpass ) { + throw new PasswordError( $this->msg( 'resetpass-recycled' )->text() ); + } + + // Do AbortChangePassword after checking mOldpass, so we don't leak information + // by possibly aborting a new password before verifying the old password. $abortMsg = 'resetpass-abort-generic'; - if ( !wfRunHooks( 'AbortChangePassword', array( $user, $this->mOldpass, $newpass, &$abortMsg ) ) ) { + if ( !wfRunHooks( 'AbortChangePassword', array( $user, $oldpass, $newpass, &$abortMsg ) ) ) { wfRunHooks( 'PrefsPasswordAudit', array( $user, $newpass, 'abortreset' ) ); throw new PasswordError( $this->msg( $abortMsg )->text() ); } - if ( !$user->checkTemporaryPassword( $this->mOldpass ) && !$user->checkPassword( $this->mOldpass ) ) { - wfRunHooks( 'PrefsPasswordAudit', array( $user, $newpass, 'wrongpassword' ) ); - throw new PasswordError( $this->msg( 'resetpass-wrong-oldpass' )->text() ); - } - // Please reset throttle for successful logins, thanks! if ( $throttleCount ) { LoginForm::clearLoginThrottle( $this->mUserName ); } try { - $user->setPassword( $this->mNewpass ); + $user->setPassword( $newpass ); wfRunHooks( 'PrefsPasswordAudit', array( $user, $newpass, 'success' ) ); - $this->mNewpass = $this->mOldpass = $this->mRetype = ''; } catch ( PasswordError $e ) { wfRunHooks( 'PrefsPasswordAudit', array( $user, $newpass, 'error' ) ); throw new PasswordError( $e->getMessage() ); @@ -301,12 +297,17 @@ class SpecialChangePassword extends UnlistedSpecialPage { if ( $isSelf ) { // This is needed to keep the user connected since // changing the password also modifies the user's token. - $user->setCookies(); + $remember = $this->getRequest()->getCookie( 'Token' ) !== null; + $user->setCookies( null, null, $remember ); } - + $user->resetPasswordExpiration(); $user->saveSettings(); } + public function requiresUnblock() { + return false; + } + protected function getGroupName() { return 'users'; } -- cgit v1.2.3-54-g00ecf