186 User $performingUser,
190 if ( !$this->isAllowed( $performingUser )->isGood() ) {
191 throw new LogicException(
192 'User ' . $performingUser->
getName() .
' is not allowed to reset passwords'
198 if ( $performingUser->
pingLimiter(
'mailpassword' ) ) {
199 return StatusValue::newGood();
206 return StatusValue::newFatal(
'badipaddress' );
212 $resetRoutes = $this->config->get( MainConfigNames::PasswordResetRoutes )
213 + [
'username' =>
false,
'email' => false ];
214 if ( $resetRoutes[
'username'] && $username ) {
215 $method =
'username';
216 $users = [ $this->userFactory->newFromName( $username ) ];
217 } elseif ( $resetRoutes[
'email'] && $email ) {
218 if ( !Sanitizer::validateEmail( $email ) ) {
220 return StatusValue::newGood();
224 $users = $this->getUsersByEmail( $email );
227 if ( $this->config->get( MainConfigNames::AllowRequiringEmailForResets ) ) {
228 $optionsLookup = $this->userOptionsLookup;
229 foreach ( $users as $index => $user ) {
230 if ( $optionsLookup->getBoolOption( $user,
'requireemail' ) ) {
231 unset( $users[$index] );
237 return StatusValue::newFatal(
'passwordreset-nodata' );
241 if ( $username && !$this->userNameUtils->getCanonical( $username ) ) {
242 return StatusValue::newFatal(
'noname' );
248 'Username' => $username,
250 'Email' => $method ===
'email' ? $email :
null,
256 $users = array_values( $users );
258 if ( !$this->hookRunner->onSpecialPasswordResetOnSubmit( $users, $data, $error ) ) {
264 $firstUser = reset( $users );
266 $requireEmail = $this->config->get( MainConfigNames::AllowRequiringEmailForResets )
267 && $method ===
'username'
269 && $this->userOptionsLookup->getBoolOption( $firstUser,
'requireemail' );
270 if ( $requireEmail && ( $email ===
'' || !Sanitizer::validateEmail( $email ) ) ) {
272 return StatusValue::newGood();
276 if ( $method ===
'email' ) {
278 return StatusValue::newGood();
280 return StatusValue::newFatal(
'noname' );
288 if ( !$firstUser instanceof
User || !$firstUser->
getId() || !$firstUser->getEmail() ) {
289 return StatusValue::newGood();
293 if ( $requireEmail && $firstUser->getEmail() !== $email ) {
294 return StatusValue::newGood();
297 $this->hookRunner->onUser__mailPasswordInternal( $performingUser, $ip, $firstUser );
299 $result = StatusValue::newGood();
301 foreach ( $users as $user ) {
302 $req = TemporaryPasswordAuthenticationRequest::newRandom();
303 $req->username = $user->getName();
304 $req->mailpassword =
true;
305 $req->caller = $performingUser->
getName();
307 $status = $this->authManager->allowsAuthenticationDataChange( $req,
true );
311 if ( $status->isGood() && $status->getValue() ===
'throttled-mailpassword' ) {
312 return StatusValue::newGood();
315 if ( $status->isGood() && $status->getValue() !==
'ignored' ) {
317 } elseif ( $result->isGood() ) {
320 if ( $status->getValue() ===
'ignored' ) {
321 $status = StatusValue::newFatal(
'passwordreset-ignored' );
323 $result->merge( $status );
328 'requestingIp' => $ip,
329 'requestingUser' => $performingUser->
getName(),
330 'targetUsername' => $username,
331 'targetEmail' => $email,
334 if ( !$result->isGood() ) {
336 "{requestingUser} attempted password reset of {actualUser} but failed",
337 $logContext + [
'errors' => $result->getErrors() ]
342 DeferredUpdates::addUpdate(
344 DeferredUpdates::POSTSEND
347 return StatusValue::newGood();