MediaWiki  REL1_23
SpecialChangePassword.php
Go to the documentation of this file.
00001 <?php
00029 class SpecialChangePassword extends FormSpecialPage {
00030 
00031     protected $mUserName, $mDomain;
00032 
00033     // Optional Wikitext Message to show above the password change form
00034     protected $mPreTextMessage = null;
00035 
00036     // label for old password input
00037     protected $mOldPassMsg = null;
00038 
00039     public function __construct() {
00040         parent::__construct( 'ChangePassword', 'editmyprivateinfo' );
00041         $this->listed( false );
00042     }
00043 
00047     function execute( $par ) {
00048         $this->getOutput()->disallowUserJs();
00049 
00050         parent::execute( $par );
00051     }
00052 
00053     protected function checkExecutePermissions( User $user ) {
00054         parent::checkExecutePermissions( $user );
00055 
00056         if ( !$this->getRequest()->wasPosted() ) {
00057             $this->requireLogin( 'resetpass-no-info' );
00058         }
00059     }
00060 
00066     public function setChangeMessage( Message $msg ) {
00067         $this->mPreTextMessage = $msg;
00068     }
00069 
00075     public function setOldPasswordMessage( $msg ) {
00076         $this->mOldPassMsg = $msg;
00077     }
00078 
00079     protected function getFormFields() {
00080         global $wgCookieExpiration;
00081 
00082         $user = $this->getUser();
00083         $request = $this->getRequest();
00084 
00085         $oldpassMsg = $this->mOldPassMsg;
00086         if ( !isset( $oldpassMsg ) ) {
00087             $oldpassMsg = $user->isLoggedIn() ? 'oldpassword' : 'resetpass-temp-password';
00088         }
00089 
00090         $fields = array(
00091             'Name' => array(
00092                 'type' => 'info',
00093                 'label-message' => 'username',
00094                 'default' => $request->getVal( 'wpName', $user->getName() ),
00095             ),
00096             'Password' => array(
00097                 'type' => 'password',
00098                 'label-message' => $oldpassMsg,
00099             ),
00100             'NewPassword' => array(
00101                 'type' => 'password',
00102                 'label-message' => 'newpassword',
00103             ),
00104             'Retype' => array(
00105                 'type' => 'password',
00106                 'label-message' => 'retypenew',
00107             ),
00108         );
00109 
00110         if ( !$this->getUser()->isLoggedIn() ) {
00111             if ( !LoginForm::getLoginToken() ) {
00112                 LoginForm::setLoginToken();
00113             }
00114             $fields['LoginOnChangeToken'] = array(
00115                 'type' => 'hidden',
00116                 'label' => 'Change Password Token',
00117                 'default' => LoginForm::getLoginToken(),
00118             );
00119         }
00120 
00121         $extraFields = array();
00122         wfRunHooks( 'ChangePasswordForm', array( &$extraFields ) );
00123         foreach ( $extraFields as $extra ) {
00124             list( $name, $label, $type, $default ) = $extra;
00125             $fields[$name] = array(
00126                 'type' => $type,
00127                 'name' => $name,
00128                 'label-message' => $label,
00129                 'default' => $default,
00130             );
00131         }
00132 
00133         if ( !$user->isLoggedIn() ) {
00134             $fields['Remember'] = array(
00135                 'type' => 'check',
00136                 'label' => $this->msg( 'remembermypassword' )
00137                         ->numParams( ceil( $wgCookieExpiration / ( 3600 * 24 ) ) )
00138                         ->text(),
00139                 'default' => $request->getVal( 'wpRemember' ),
00140             );
00141         }
00142 
00143         return $fields;
00144     }
00145 
00146     protected function alterForm( HTMLForm $form ) {
00147         $form->setId( 'mw-resetpass-form' );
00148         $form->setTableId( 'mw-resetpass-table' );
00149         $form->setWrapperLegendMsg( 'resetpass_header' );
00150         $form->setSubmitTextMsg(
00151             $this->getUser()->isLoggedIn()
00152                 ? 'resetpass-submit-loggedin'
00153                 : 'resetpass_submit'
00154         );
00155         $form->addButton( 'wpCancel', $this->msg( 'resetpass-submit-cancel' )->text() );
00156         $form->setHeaderText( $this->msg( 'resetpass_text' )->parseAsBlock() );
00157         if ( $this->mPreTextMessage instanceof Message ) {
00158             $form->addPreText( $this->mPreTextMessage->parseAsBlock() );
00159         }
00160         $form->addHiddenFields(
00161             $this->getRequest()->getValues( 'wpName', 'wpDomain', 'returnto', 'returntoquery' ) );
00162     }
00163 
00164     public function onSubmit( array $data ) {
00165         global $wgAuth;
00166 
00167         $request = $this->getRequest();
00168 
00169         if ( $request->getCheck( 'wpLoginToken' ) ) {
00170             // This comes from Special:Userlogin when logging in with a temporary password
00171             return false;
00172         }
00173 
00174         if ( !$this->getUser()->isLoggedIn()
00175             && $request->getVal( 'wpLoginOnChangeToken' ) !== LoginForm::getLoginToken()
00176         ) {
00177             // Potential CSRF (bug 62497)
00178             return false;
00179         }
00180 
00181 
00182         if ( $request->getCheck( 'wpCancel' ) ) {
00183             $titleObj = Title::newFromText( $request->getVal( 'returnto' ) );
00184             if ( !$titleObj instanceof Title ) {
00185                 $titleObj = Title::newMainPage();
00186             }
00187             $query = $request->getVal( 'returntoquery' );
00188             $this->getOutput()->redirect( $titleObj->getFullURL( $query ) );
00189 
00190             return true;
00191         }
00192 
00193         try {
00194             $this->mUserName = $request->getVal( 'wpName', $this->getUser()->getName() );
00195             $this->mDomain = $wgAuth->getDomain();
00196 
00197             if ( !$wgAuth->allowPasswordChange() ) {
00198                 throw new ErrorPageError( 'changepassword', 'resetpass_forbidden' );
00199             }
00200 
00201             $this->attemptReset( $data['Password'], $data['NewPassword'], $data['Retype'] );
00202 
00203             return true;
00204         } catch ( PasswordError $e ) {
00205             return $e->getMessage();
00206         }
00207     }
00208 
00209     public function onSuccess() {
00210         if ( $this->getUser()->isLoggedIn() ) {
00211             $this->getOutput()->wrapWikiMsg(
00212                 "<div class=\"successbox\">\n$1\n</div>",
00213                 'changepassword-success'
00214             );
00215             $this->getOutput()->returnToMain();
00216         } else {
00217             $request = $this->getRequest();
00218             LoginForm::setLoginToken();
00219             $token = LoginForm::getLoginToken();
00220             $data = array(
00221                 'action' => 'submitlogin',
00222                 'wpName' => $this->mUserName,
00223                 'wpDomain' => $this->mDomain,
00224                 'wpLoginToken' => $token,
00225                 'wpPassword' => $request->getVal( 'wpNewPassword' ),
00226             ) + $request->getValues( 'wpRemember', 'returnto', 'returntoquery' );
00227             $login = new LoginForm( new DerivativeRequest( $request, $data, true ) );
00228             $login->setContext( $this->getContext() );
00229             $login->execute( null );
00230         }
00231     }
00232 
00236     protected function attemptReset( $oldpass, $newpass, $retype ) {
00237         global $wgPasswordAttemptThrottle;
00238 
00239         $isSelf = ( $this->mUserName === $this->getUser()->getName() );
00240         if ( $isSelf ) {
00241             $user = $this->getUser();
00242         } else {
00243             $user = User::newFromName( $this->mUserName );
00244         }
00245 
00246         if ( !$user || $user->isAnon() ) {
00247             throw new PasswordError( $this->msg( 'nosuchusershort', $this->mUserName )->text() );
00248         }
00249 
00250         if ( $newpass !== $retype ) {
00251             wfRunHooks( 'PrefsPasswordAudit', array( $user, $newpass, 'badretype' ) );
00252             throw new PasswordError( $this->msg( 'badretype' )->text() );
00253         }
00254 
00255         $throttleCount = LoginForm::incLoginThrottle( $this->mUserName );
00256         if ( $throttleCount === true ) {
00257             $lang = $this->getLanguage();
00258             throw new PasswordError( $this->msg( 'changepassword-throttled' )
00259                 ->params( $lang->formatDuration( $wgPasswordAttemptThrottle['seconds'] ) )
00260                 ->text()
00261             );
00262         }
00263 
00264         // @TODO Make these separate messages, since the message is written for both cases
00265         if ( !$user->checkTemporaryPassword( $oldpass ) && !$user->checkPassword( $oldpass ) ) {
00266             wfRunHooks( 'PrefsPasswordAudit', array( $user, $newpass, 'wrongpassword' ) );
00267             throw new PasswordError( $this->msg( 'resetpass-wrong-oldpass' )->text() );
00268         }
00269 
00270         // User is resetting their password to their old password
00271         if ( $oldpass === $newpass ) {
00272             throw new PasswordError( $this->msg( 'resetpass-recycled' )->text() );
00273         }
00274 
00275         // Do AbortChangePassword after checking mOldpass, so we don't leak information
00276         // by possibly aborting a new password before verifying the old password.
00277         $abortMsg = 'resetpass-abort-generic';
00278         if ( !wfRunHooks( 'AbortChangePassword', array( $user, $oldpass, $newpass, &$abortMsg ) ) ) {
00279             wfRunHooks( 'PrefsPasswordAudit', array( $user, $newpass, 'abortreset' ) );
00280             throw new PasswordError( $this->msg( $abortMsg )->text() );
00281         }
00282 
00283         // Please reset throttle for successful logins, thanks!
00284         if ( $throttleCount ) {
00285             LoginForm::clearLoginThrottle( $this->mUserName );
00286         }
00287 
00288         try {
00289             $user->setPassword( $newpass );
00290             wfRunHooks( 'PrefsPasswordAudit', array( $user, $newpass, 'success' ) );
00291         } catch ( PasswordError $e ) {
00292             wfRunHooks( 'PrefsPasswordAudit', array( $user, $newpass, 'error' ) );
00293             throw new PasswordError( $e->getMessage() );
00294         }
00295 
00296         if ( $isSelf ) {
00297             // This is needed to keep the user connected since
00298             // changing the password also modifies the user's token.
00299             $user->setCookies();
00300         }
00301         $user->resetPasswordExpiration();
00302         $user->saveSettings();
00303     }
00304 
00305     public function requiresUnblock() {
00306         return false;
00307     }
00308 
00309     protected function getGroupName() {
00310         return 'users';
00311     }
00312 }