MediaWiki
REL1_19
|
00001 <?php 00029 class SpecialPasswordReset extends FormSpecialPage { 00030 00034 private $email; 00035 00039 private $result; 00040 00041 public function __construct() { 00042 parent::__construct( 'PasswordReset' ); 00043 } 00044 00045 public function userCanExecute( User $user ) { 00046 return $this->canChangePassword( $user ) === true && parent::userCanExecute( $user ); 00047 } 00048 00049 public function checkExecutePermissions( User $user ) { 00050 $error = $this->canChangePassword( $user ); 00051 if ( is_string( $error ) ) { 00052 throw new ErrorPageError( 'internalerror', $error ); 00053 } elseif ( !$error ) { 00054 throw new ErrorPageError( 'internalerror', 'resetpass_forbidden' ); 00055 } 00056 00057 return parent::checkExecutePermissions( $user ); 00058 } 00059 00060 protected function getFormFields() { 00061 global $wgPasswordResetRoutes, $wgAuth; 00062 $a = array(); 00063 if ( isset( $wgPasswordResetRoutes['username'] ) && $wgPasswordResetRoutes['username'] ) { 00064 $a['Username'] = array( 00065 'type' => 'text', 00066 'label-message' => 'passwordreset-username', 00067 ); 00068 } 00069 00070 if ( isset( $wgPasswordResetRoutes['email'] ) && $wgPasswordResetRoutes['email'] ) { 00071 $a['Email'] = array( 00072 'type' => 'email', 00073 'label-message' => 'passwordreset-email', 00074 ); 00075 } 00076 00077 if ( isset( $wgPasswordResetRoutes['domain'] ) && $wgPasswordResetRoutes['domain'] ) { 00078 $domains = $wgAuth->domainList(); 00079 $a['Domain'] = array( 00080 'type' => 'select', 00081 'options' => $domains, 00082 'label-message' => 'passwordreset-domain', 00083 ); 00084 } 00085 00086 if( $this->getUser()->isAllowed( 'passwordreset' ) ){ 00087 $a['Capture'] = array( 00088 'type' => 'check', 00089 'label-message' => 'passwordreset-capture', 00090 'help-message' => 'passwordreset-capture-help', 00091 ); 00092 } 00093 00094 return $a; 00095 } 00096 00097 public function alterForm( HTMLForm $form ) { 00098 $form->setSubmitText( wfMessage( "mailmypassword" ) ); 00099 } 00100 00101 protected function preText() { 00102 global $wgPasswordResetRoutes; 00103 $i = 0; 00104 if ( isset( $wgPasswordResetRoutes['username'] ) && $wgPasswordResetRoutes['username'] ) { 00105 $i++; 00106 } 00107 if ( isset( $wgPasswordResetRoutes['email'] ) && $wgPasswordResetRoutes['email'] ) { 00108 $i++; 00109 } 00110 if ( isset( $wgPasswordResetRoutes['domain'] ) && $wgPasswordResetRoutes['domain'] ) { 00111 $i++; 00112 } 00113 return wfMessage( 'passwordreset-pretext', $i )->parseAsBlock(); 00114 } 00115 00123 public function onSubmit( array $data ) { 00124 global $wgAuth; 00125 00126 if ( isset( $data['Domain'] ) ) { 00127 if ( $wgAuth->validDomain( $data['Domain'] ) ) { 00128 $wgAuth->setDomain( $data['Domain'] ); 00129 } else { 00130 $wgAuth->setDomain( 'invaliddomain' ); 00131 } 00132 } 00133 00134 if( isset( $data['Capture'] ) && !$this->getUser()->isAllowed( 'passwordreset' ) ){ 00135 // The user knows they don't have the passwordreset permission, but they tried to spoof the form. That's naughty 00136 throw new PermissionsError( 'passwordreset' ); 00137 } 00138 00144 if ( isset( $data['Username'] ) && $data['Username'] !== '' ) { 00145 $method = 'username'; 00146 $users = array( User::newFromName( $data['Username'] ) ); 00147 } elseif ( isset( $data['Email'] ) 00148 && $data['Email'] !== '' 00149 && Sanitizer::validateEmail( $data['Email'] ) ) 00150 { 00151 $method = 'email'; 00152 $res = wfGetDB( DB_SLAVE )->select( 00153 'user', 00154 '*', 00155 array( 'user_email' => $data['Email'] ), 00156 __METHOD__ 00157 ); 00158 if ( $res ) { 00159 $users = array(); 00160 foreach( $res as $row ){ 00161 $users[] = User::newFromRow( $row ); 00162 } 00163 } else { 00164 // Some sort of database error, probably unreachable 00165 throw new MWException( 'Unknown database error in ' . __METHOD__ ); 00166 } 00167 } else { 00168 // The user didn't supply any data 00169 return false; 00170 } 00171 00172 // Check for hooks (captcha etc), and allow them to modify the users list 00173 $error = array(); 00174 if ( !wfRunHooks( 'SpecialPasswordResetOnSubmit', array( &$users, $data, &$error ) ) ) { 00175 return array( $error ); 00176 } 00177 00178 if( count( $users ) == 0 ){ 00179 if( $method == 'email' ){ 00180 // Don't reveal whether or not an email address is in use 00181 return true; 00182 } else { 00183 return array( 'noname' ); 00184 } 00185 } 00186 00187 $firstUser = $users[0]; 00188 00189 if ( !$firstUser instanceof User || !$firstUser->getID() ) { 00190 // Don't parse username as wikitext (bug 65501) 00191 return array( array( 'nosuchuser', wfEscapeWikiText( $data['Username'] ) ) ); 00192 } 00193 00194 // Check against the rate limiter 00195 if ( $this->getUser()->pingLimiter( 'mailpassword' ) ) { 00196 throw new ThrottledError; 00197 } 00198 00199 // Check against password throttle 00200 foreach ( $users as $user ) { 00201 if ( $user->isPasswordReminderThrottled() ) { 00202 global $wgPasswordReminderResendTime; 00203 # Round the time in hours to 3 d.p., in case someone is specifying 00204 # minutes or seconds. 00205 return array( array( 'throttled-mailpassword', round( $wgPasswordReminderResendTime, 3 ) ) ); 00206 } 00207 } 00208 00209 global $wgNewPasswordExpiry; 00210 00211 // All the users will have the same email address 00212 if ( $firstUser->getEmail() == '' ) { 00213 // This won't be reachable from the email route, so safe to expose the username 00214 return array( array( 'noemail', wfEscapeWikiText( $firstUser->getName() ) ) ); 00215 } 00216 00217 // We need to have a valid IP address for the hook, but per bug 18347, we should 00218 // send the user's name if they're logged in. 00219 $ip = $this->getRequest()->getIP(); 00220 if ( !$ip ) { 00221 return array( 'badipaddress' ); 00222 } 00223 $caller = $this->getUser(); 00224 wfRunHooks( 'User::mailPasswordInternal', array( &$caller, &$ip, &$firstUser ) ); 00225 $username = $caller->getName(); 00226 $msg = IP::isValid( $username ) 00227 ? 'passwordreset-emailtext-ip' 00228 : 'passwordreset-emailtext-user'; 00229 00230 // Send in the user's language; which should hopefully be the same 00231 $userLanguage = $firstUser->getOption( 'language' ); 00232 00233 $passwords = array(); 00234 foreach ( $users as $user ) { 00235 $password = $user->randomPassword(); 00236 $user->setNewpassword( $password ); 00237 $user->saveSettings(); 00238 $passwords[] = wfMessage( 'passwordreset-emailelement', $user->getName(), $password 00239 )->inLanguage( $userLanguage )->plain(); // We'll escape the whole thing later 00240 } 00241 $passwordBlock = implode( "\n\n", $passwords ); 00242 00243 $this->email = wfMessage( $msg )->inLanguage( $userLanguage ); 00244 $this->email->params( 00245 $username, 00246 $passwordBlock, 00247 count( $passwords ), 00248 Title::newMainPage()->getCanonicalUrl(), 00249 round( $wgNewPasswordExpiry / 86400 ) 00250 ); 00251 00252 $title = wfMessage( 'passwordreset-emailtitle' ); 00253 00254 $this->result = $firstUser->sendMail( $title->escaped(), $this->email->escaped() ); 00255 00256 // Blank the email if the user is not supposed to see it 00257 if( !isset( $data['Capture'] ) || !$data['Capture'] ) { 00258 $this->email = null; 00259 } 00260 00261 if ( $this->result->isGood() ) { 00262 return true; 00263 } elseif( isset( $data['Capture'] ) && $data['Capture'] ){ 00264 // The email didn't send, but maybe they knew that and that's why they captured it 00265 return true; 00266 } else { 00267 // @todo FIXME: The email didn't send, but we have already set the password throttle 00268 // timestamp, so they won't be able to try again until it expires... :( 00269 return array( array( 'mailerror', $this->result->getMessage() ) ); 00270 } 00271 } 00272 00273 public function onSuccess() { 00274 if( $this->getUser()->isAllowed( 'passwordreset' ) && $this->email != null ){ 00275 // @todo: Logging 00276 00277 if( $this->result->isGood() ){ 00278 $this->getOutput()->addWikiMsg( 'passwordreset-emailsent-capture' ); 00279 } else { 00280 $this->getOutput()->addWikiMsg( 'passwordreset-emailerror-capture', $this->result->getMessage() ); 00281 } 00282 00283 $this->getOutput()->addHTML( Html::rawElement( 'pre', array(), $this->email->escaped() ) ); 00284 } 00285 00286 $this->getOutput()->addWikiMsg( 'passwordreset-emailsent' ); 00287 $this->getOutput()->returnToMain(); 00288 } 00289 00290 protected function canChangePassword( User $user ) { 00291 global $wgPasswordResetRoutes, $wgAuth; 00292 00293 // Maybe password resets are disabled, or there are no allowable routes 00294 if ( !is_array( $wgPasswordResetRoutes ) || 00295 !in_array( true, array_values( $wgPasswordResetRoutes ) ) ) 00296 { 00297 return 'passwordreset-disabled'; 00298 } 00299 00300 // Maybe the external auth plugin won't allow local password changes 00301 if ( !$wgAuth->allowPasswordChange() ) { 00302 return 'resetpass_forbidden'; 00303 } 00304 00305 // Maybe the user is blocked (check this here rather than relying on the parent 00306 // method as we have a more specific error message to use here 00307 if ( $user->isBlocked() ) { 00308 return 'blocked-mailpassword'; 00309 } 00310 00311 return true; 00312 } 00313 00318 function isListed() { 00319 if ( $this->canChangePassword( $this->getUser() ) === true ) { 00320 return parent::isListed(); 00321 } 00322 00323 return false; 00324 } 00325 }