MediaWiki  master
AuthManager.php
Go to the documentation of this file.
1 <?php
24 namespace MediaWiki\Auth;
25 
26 use Config;
37 use Psr\Log\LoggerAwareInterface;
38 use Psr\Log\LoggerInterface;
39 use Psr\Log\NullLogger;
40 use ReadOnlyMode;
41 use SpecialPage;
42 use Status;
43 use StatusValue;
44 use User;
45 use WebRequest;
46 use Wikimedia\ObjectFactory;
47 use Wikimedia\ScopedCallback;
48 
97 class AuthManager implements LoggerAwareInterface {
99  public const ACTION_LOGIN = 'login';
103  public const ACTION_LOGIN_CONTINUE = 'login-continue';
105  public const ACTION_CREATE = 'create';
109  public const ACTION_CREATE_CONTINUE = 'create-continue';
111  public const ACTION_LINK = 'link';
115  public const ACTION_LINK_CONTINUE = 'link-continue';
117  public const ACTION_CHANGE = 'change';
119  public const ACTION_REMOVE = 'remove';
121  public const ACTION_UNLINK = 'unlink';
122 
124  public const SEC_OK = 'ok';
126  public const SEC_REAUTH = 'reauth';
128  public const SEC_FAIL = 'fail';
129 
131  public const AUTOCREATE_SOURCE_SESSION = \MediaWiki\Session\SessionManager::class;
132 
134  public const AUTOCREATE_SOURCE_MAINT = '::Maintenance::';
135 
137  private static $instance = null;
138 
140  private $request;
141 
143  private $config;
144 
146  private $objectFactory;
147 
149  private $logger;
150 
152  private $userNameUtils;
153 
156 
159 
162 
165 
168 
170  private $hookContainer;
171 
173  private $hookRunner;
174 
176  private $readOnlyMode;
177 
179  private $blockManager;
180 
183 
186 
193  public static function singleton() {
194  wfDeprecated( __METHOD__, '1.35' );
195  return MediaWikiServices::getInstance()->getAuthManager();
196  }
197 
209  public function __construct(
211  Config $config,
212  ObjectFactory $objectFactory,
219  ) {
220  $this->request = $request;
221  $this->config = $config;
222  $this->objectFactory = $objectFactory;
223  $this->hookContainer = $hookContainer;
224  $this->hookRunner = new HookRunner( $hookContainer );
225  $this->setLogger( new NullLogger() );
226  $this->readOnlyMode = $readOnlyMode;
227  $this->userNameUtils = $userNameUtils;
228  $this->blockManager = $blockManager;
229  $this->blockErrorFormatter = $blockErrorFormatter;
230  $this->watchlistManager = $watchlistManager;
231  }
232 
236  public function setLogger( LoggerInterface $logger ) {
237  $this->logger = $logger;
238  }
239 
243  public function getRequest() {
244  return $this->request;
245  }
246 
253  public function forcePrimaryAuthenticationProviders( array $providers, $why ) {
254  $this->logger->warning( "Overriding AuthManager primary authn because $why" );
255 
256  if ( $this->primaryAuthenticationProviders !== null ) {
257  $this->logger->warning(
258  'PrimaryAuthenticationProviders have already been accessed! I hope nothing breaks.'
259  );
260 
261  $this->allAuthenticationProviders = array_diff_key(
262  $this->allAuthenticationProviders,
263  $this->primaryAuthenticationProviders
264  );
265  $session = $this->request->getSession();
266  $session->remove( 'AuthManager::authnState' );
267  $session->remove( 'AuthManager::accountCreationState' );
268  $session->remove( 'AuthManager::accountLinkState' );
269  $this->createdAccountAuthenticationRequests = [];
270  }
271 
272  $this->primaryAuthenticationProviders = [];
273  foreach ( $providers as $provider ) {
274  if ( !$provider instanceof AbstractPrimaryAuthenticationProvider ) {
275  throw new \RuntimeException(
276  'Expected instance of MediaWiki\\Auth\\AbstractPrimaryAuthenticationProvider, got ' .
277  get_class( $provider )
278  );
279  }
280  $provider->init( $this->logger, $this, $this->hookContainer, $this->config, $this->userNameUtils );
281  $id = $provider->getUniqueId();
282  if ( isset( $this->allAuthenticationProviders[$id] ) ) {
283  throw new \RuntimeException(
284  "Duplicate specifications for id $id (classes " .
285  get_class( $provider ) . ' and ' .
286  get_class( $this->allAuthenticationProviders[$id] ) . ')'
287  );
288  }
289  $this->allAuthenticationProviders[$id] = $provider;
290  $this->primaryAuthenticationProviders[$id] = $provider;
291  }
292  }
293 
294  /***************************************************************************/
295  // region Authentication
306  public function canAuthenticateNow() {
307  return $this->request->getSession()->canSetUser();
308  }
309 
328  public function beginAuthentication( array $reqs, $returnToUrl ) {
329  $session = $this->request->getSession();
330  if ( !$session->canSetUser() ) {
331  // Caller should have called canAuthenticateNow()
332  $session->remove( 'AuthManager::authnState' );
333  throw new \LogicException( 'Authentication is not possible now' );
334  }
335 
336  $guessUserName = null;
337  foreach ( $reqs as $req ) {
338  $req->returnToUrl = $returnToUrl;
339  // @codeCoverageIgnoreStart
340  if ( $req->username !== null && $req->username !== '' ) {
341  if ( $guessUserName === null ) {
342  $guessUserName = $req->username;
343  } elseif ( $guessUserName !== $req->username ) {
344  $guessUserName = null;
345  break;
346  }
347  }
348  // @codeCoverageIgnoreEnd
349  }
350 
351  // Check for special-case login of a just-created account
353  $reqs, CreatedAccountAuthenticationRequest::class
354  );
355  if ( $req ) {
356  if ( !in_array( $req, $this->createdAccountAuthenticationRequests, true ) ) {
357  throw new \LogicException(
358  'CreatedAccountAuthenticationRequests are only valid on ' .
359  'the same AuthManager that created the account'
360  );
361  }
362 
363  $user = User::newFromName( $req->username );
364  // @codeCoverageIgnoreStart
365  if ( !$user ) {
366  throw new \UnexpectedValueException(
367  "CreatedAccountAuthenticationRequest had invalid username \"{$req->username}\""
368  );
369  } elseif ( $user->getId() != $req->id ) {
370  throw new \UnexpectedValueException(
371  "ID for \"{$req->username}\" was {$user->getId()}, expected {$req->id}"
372  );
373  }
374  // @codeCoverageIgnoreEnd
375 
376  $this->logger->info( 'Logging in {user} after account creation', [
377  'user' => $user->getName(),
378  ] );
379  $ret = AuthenticationResponse::newPass( $user->getName() );
380  $this->setSessionDataForUser( $user );
381  $this->callMethodOnProviders( 7, 'postAuthentication', [ $user, $ret ] );
382  $session->remove( 'AuthManager::authnState' );
383  $this->getHookRunner()->onAuthManagerLoginAuthenticateAudit(
384  $ret, $user, $user->getName(), [] );
385  return $ret;
386  }
387 
388  $this->removeAuthenticationSessionData( null );
389 
390  foreach ( $this->getPreAuthenticationProviders() as $provider ) {
391  $status = $provider->testForAuthentication( $reqs );
392  if ( !$status->isGood() ) {
393  $this->logger->debug( 'Login failed in pre-authentication by ' . $provider->getUniqueId() );
395  Status::wrap( $status )->getMessage()
396  );
397  $this->callMethodOnProviders( 7, 'postAuthentication',
398  [ User::newFromName( $guessUserName ) ?: null, $ret ]
399  );
400  $this->getHookRunner()->onAuthManagerLoginAuthenticateAudit( $ret, null, $guessUserName, [] );
401  return $ret;
402  }
403  }
404 
405  $state = [
406  'reqs' => $reqs,
407  'returnToUrl' => $returnToUrl,
408  'guessUserName' => $guessUserName,
409  'primary' => null,
410  'primaryResponse' => null,
411  'secondary' => [],
412  'maybeLink' => [],
413  'continueRequests' => [],
414  ];
415 
416  // Preserve state from a previous failed login
418  $reqs, CreateFromLoginAuthenticationRequest::class
419  );
420  if ( $req ) {
421  $state['maybeLink'] = $req->maybeLink;
422  }
423 
424  $session = $this->request->getSession();
425  $session->setSecret( 'AuthManager::authnState', $state );
426  $session->persist();
427 
428  return $this->continueAuthentication( $reqs );
429  }
430 
453  public function continueAuthentication( array $reqs ) {
454  $session = $this->request->getSession();
455  try {
456  if ( !$session->canSetUser() ) {
457  // Caller should have called canAuthenticateNow()
458  // @codeCoverageIgnoreStart
459  throw new \LogicException( 'Authentication is not possible now' );
460  // @codeCoverageIgnoreEnd
461  }
462 
463  $state = $session->getSecret( 'AuthManager::authnState' );
464  if ( !is_array( $state ) ) {
466  wfMessage( 'authmanager-authn-not-in-progress' )
467  );
468  }
469  $state['continueRequests'] = [];
470 
471  $guessUserName = $state['guessUserName'];
472 
473  foreach ( $reqs as $req ) {
474  $req->returnToUrl = $state['returnToUrl'];
475  }
476 
477  // Step 1: Choose an primary authentication provider, and call it until it succeeds.
478 
479  if ( $state['primary'] === null ) {
480  // We haven't picked a PrimaryAuthenticationProvider yet
481  // @codeCoverageIgnoreStart
482  $guessUserName = null;
483  foreach ( $reqs as $req ) {
484  if ( $req->username !== null && $req->username !== '' ) {
485  if ( $guessUserName === null ) {
486  $guessUserName = $req->username;
487  } elseif ( $guessUserName !== $req->username ) {
488  $guessUserName = null;
489  break;
490  }
491  }
492  }
493  $state['guessUserName'] = $guessUserName;
494  // @codeCoverageIgnoreEnd
495  $state['reqs'] = $reqs;
496 
497  foreach ( $this->getPrimaryAuthenticationProviders() as $id => $provider ) {
498  $res = $provider->beginPrimaryAuthentication( $reqs );
499  switch ( $res->status ) {
501  $state['primary'] = $id;
502  $state['primaryResponse'] = $res;
503  $this->logger->debug( "Primary login with $id succeeded" );
504  break 2;
506  $this->logger->debug( "Login failed in primary authentication by $id" );
507  if ( $res->createRequest || $state['maybeLink'] ) {
508  $res->createRequest = new CreateFromLoginAuthenticationRequest(
509  $res->createRequest, $state['maybeLink']
510  );
511  }
512  $this->callMethodOnProviders( 7, 'postAuthentication',
513  [ User::newFromName( $guessUserName ) ?: null, $res ]
514  );
515  $session->remove( 'AuthManager::authnState' );
516  $this->getHookRunner()->onAuthManagerLoginAuthenticateAudit(
517  $res, null, $guessUserName, [] );
518  return $res;
520  // Continue loop
521  break;
524  $this->logger->debug( "Primary login with $id returned $res->status" );
525  $this->fillRequests( $res->neededRequests, self::ACTION_LOGIN, $guessUserName );
526  $state['primary'] = $id;
527  $state['continueRequests'] = $res->neededRequests;
528  $session->setSecret( 'AuthManager::authnState', $state );
529  return $res;
530 
531  // @codeCoverageIgnoreStart
532  default:
533  throw new \DomainException(
534  get_class( $provider ) . "::beginPrimaryAuthentication() returned $res->status"
535  );
536  // @codeCoverageIgnoreEnd
537  }
538  }
539  if ( $state['primary'] === null ) {
540  $this->logger->debug( 'Login failed in primary authentication because no provider accepted' );
542  wfMessage( 'authmanager-authn-no-primary' )
543  );
544  $this->callMethodOnProviders( 7, 'postAuthentication',
545  [ User::newFromName( $guessUserName ) ?: null, $ret ]
546  );
547  $session->remove( 'AuthManager::authnState' );
548  return $ret;
549  }
550  } elseif ( $state['primaryResponse'] === null ) {
551  $provider = $this->getAuthenticationProvider( $state['primary'] );
552  if ( !$provider instanceof PrimaryAuthenticationProvider ) {
553  // Configuration changed? Force them to start over.
554  // @codeCoverageIgnoreStart
556  wfMessage( 'authmanager-authn-not-in-progress' )
557  );
558  $this->callMethodOnProviders( 7, 'postAuthentication',
559  [ User::newFromName( $guessUserName ) ?: null, $ret ]
560  );
561  $session->remove( 'AuthManager::authnState' );
562  return $ret;
563  // @codeCoverageIgnoreEnd
564  }
565  $id = $provider->getUniqueId();
566  $res = $provider->continuePrimaryAuthentication( $reqs );
567  switch ( $res->status ) {
569  $state['primaryResponse'] = $res;
570  $this->logger->debug( "Primary login with $id succeeded" );
571  break;
573  $this->logger->debug( "Login failed in primary authentication by $id" );
574  if ( $res->createRequest || $state['maybeLink'] ) {
575  $res->createRequest = new CreateFromLoginAuthenticationRequest(
576  $res->createRequest, $state['maybeLink']
577  );
578  }
579  $this->callMethodOnProviders( 7, 'postAuthentication',
580  [ User::newFromName( $guessUserName ) ?: null, $res ]
581  );
582  $session->remove( 'AuthManager::authnState' );
583  $this->getHookRunner()->onAuthManagerLoginAuthenticateAudit(
584  $res, null, $guessUserName, [] );
585  return $res;
588  $this->logger->debug( "Primary login with $id returned $res->status" );
589  $this->fillRequests( $res->neededRequests, self::ACTION_LOGIN, $guessUserName );
590  $state['continueRequests'] = $res->neededRequests;
591  $session->setSecret( 'AuthManager::authnState', $state );
592  return $res;
593  default:
594  throw new \DomainException(
595  get_class( $provider ) . "::continuePrimaryAuthentication() returned $res->status"
596  );
597  }
598  }
599 
600  $res = $state['primaryResponse'];
601  if ( $res->username === null ) {
602  $provider = $this->getAuthenticationProvider( $state['primary'] );
603  if ( !$provider instanceof PrimaryAuthenticationProvider ) {
604  // Configuration changed? Force them to start over.
605  // @codeCoverageIgnoreStart
607  wfMessage( 'authmanager-authn-not-in-progress' )
608  );
609  $this->callMethodOnProviders( 7, 'postAuthentication',
610  [ User::newFromName( $guessUserName ) ?: null, $ret ]
611  );
612  $session->remove( 'AuthManager::authnState' );
613  return $ret;
614  // @codeCoverageIgnoreEnd
615  }
616 
617  if ( $provider->accountCreationType() === PrimaryAuthenticationProvider::TYPE_LINK &&
618  $res->linkRequest &&
619  // don't confuse the user with an incorrect message if linking is disabled
620  $this->getAuthenticationProvider( ConfirmLinkSecondaryAuthenticationProvider::class )
621  ) {
622  $state['maybeLink'][$res->linkRequest->getUniqueId()] = $res->linkRequest;
623  $msg = 'authmanager-authn-no-local-user-link';
624  } else {
625  $msg = 'authmanager-authn-no-local-user';
626  }
627  $this->logger->debug(
628  "Primary login with {$provider->getUniqueId()} succeeded, but returned no user"
629  );
631  $ret->neededRequests = $this->getAuthenticationRequestsInternal(
632  self::ACTION_LOGIN,
633  [],
635  );
636  if ( $res->createRequest || $state['maybeLink'] ) {
637  $ret->createRequest = new CreateFromLoginAuthenticationRequest(
638  $res->createRequest, $state['maybeLink']
639  );
640  $ret->neededRequests[] = $ret->createRequest;
641  }
642  $this->fillRequests( $ret->neededRequests, self::ACTION_LOGIN, null, true );
643  $session->setSecret( 'AuthManager::authnState', [
644  'reqs' => [], // Will be filled in later
645  'primary' => null,
646  'primaryResponse' => null,
647  'secondary' => [],
648  'continueRequests' => $ret->neededRequests,
649  ] + $state );
650  return $ret;
651  }
652 
653  // Step 2: Primary authentication succeeded, create the User object
654  // (and add the user locally if necessary)
655 
656  $user = User::newFromName( $res->username, 'usable' );
657  if ( !$user ) {
658  $provider = $this->getAuthenticationProvider( $state['primary'] );
659  throw new \DomainException(
660  get_class( $provider ) . " returned an invalid username: {$res->username}"
661  );
662  }
663  if ( $user->getId() === 0 ) {
664  // User doesn't exist locally. Create it.
665  $this->logger->info( 'Auto-creating {user} on login', [
666  'user' => $user->getName(),
667  ] );
668  $status = $this->autoCreateUser( $user, $state['primary'], false );
669  if ( !$status->isGood() ) {
671  Status::wrap( $status )->getMessage( 'authmanager-authn-autocreate-failed' )
672  );
673  $this->callMethodOnProviders( 7, 'postAuthentication', [ $user, $ret ] );
674  $session->remove( 'AuthManager::authnState' );
675  $this->getHookRunner()->onAuthManagerLoginAuthenticateAudit(
676  $ret, $user, $user->getName(), [] );
677  return $ret;
678  }
679  }
680 
681  // Step 3: Iterate over all the secondary authentication providers.
682 
683  $beginReqs = $state['reqs'];
684 
685  foreach ( $this->getSecondaryAuthenticationProviders() as $id => $provider ) {
686  if ( !isset( $state['secondary'][$id] ) ) {
687  // This provider isn't started yet, so we pass it the set
688  // of reqs from beginAuthentication instead of whatever
689  // might have been used by a previous provider in line.
690  $func = 'beginSecondaryAuthentication';
691  $res = $provider->beginSecondaryAuthentication( $user, $beginReqs );
692  } elseif ( !$state['secondary'][$id] ) {
693  $func = 'continueSecondaryAuthentication';
694  $res = $provider->continueSecondaryAuthentication( $user, $reqs );
695  } else {
696  continue;
697  }
698  switch ( $res->status ) {
700  $this->logger->debug( "Secondary login with $id succeeded" );
701  // fall through
703  $state['secondary'][$id] = true;
704  break;
706  $this->logger->debug( "Login failed in secondary authentication by $id" );
707  $this->callMethodOnProviders( 7, 'postAuthentication', [ $user, $res ] );
708  $session->remove( 'AuthManager::authnState' );
709  $this->getHookRunner()->onAuthManagerLoginAuthenticateAudit(
710  $res, $user, $user->getName(), [] );
711  return $res;
714  $this->logger->debug( "Secondary login with $id returned " . $res->status );
715  $this->fillRequests( $res->neededRequests, self::ACTION_LOGIN, $user->getName() );
716  $state['secondary'][$id] = false;
717  $state['continueRequests'] = $res->neededRequests;
718  $session->setSecret( 'AuthManager::authnState', $state );
719  return $res;
720 
721  // @codeCoverageIgnoreStart
722  default:
723  throw new \DomainException(
724  get_class( $provider ) . "::{$func}() returned $res->status"
725  );
726  // @codeCoverageIgnoreEnd
727  }
728  }
729 
730  // Step 4: Authentication complete! Set the user in the session and
731  // clean up.
732 
733  $this->logger->info( 'Login for {user} succeeded from {clientip}', [
734  'user' => $user->getName(),
735  'clientip' => $this->request->getIP(),
736  ] );
737  $rememberMeConfig = $this->config->get( 'RememberMe' );
738  if ( $rememberMeConfig === RememberMeAuthenticationRequest::ALWAYS_REMEMBER ) {
739  $rememberMe = true;
740  } elseif ( $rememberMeConfig === RememberMeAuthenticationRequest::NEVER_REMEMBER ) {
741  $rememberMe = false;
742  } else {
745  $beginReqs, RememberMeAuthenticationRequest::class
746  );
747  $rememberMe = $req && $req->rememberMe;
748  }
749  $this->setSessionDataForUser( $user, $rememberMe );
750  $ret = AuthenticationResponse::newPass( $user->getName() );
751  $this->callMethodOnProviders( 7, 'postAuthentication', [ $user, $ret ] );
752  $session->remove( 'AuthManager::authnState' );
753  $this->removeAuthenticationSessionData( null );
754  $this->getHookRunner()->onAuthManagerLoginAuthenticateAudit(
755  $ret, $user, $user->getName(), [] );
756  return $ret;
757  } catch ( \Exception $ex ) {
758  $session->remove( 'AuthManager::authnState' );
759  throw $ex;
760  }
761  }
762 
774  public function securitySensitiveOperationStatus( $operation ) {
775  $status = self::SEC_OK;
776 
777  $this->logger->debug( __METHOD__ . ": Checking $operation" );
778 
779  $session = $this->request->getSession();
780  $aId = $session->getUser()->getId();
781  if ( $aId === 0 ) {
782  // User isn't authenticated. DWIM?
783  $status = $this->canAuthenticateNow() ? self::SEC_REAUTH : self::SEC_FAIL;
784  $this->logger->info( __METHOD__ . ": Not logged in! $operation is $status" );
785  return $status;
786  }
787 
788  if ( $session->canSetUser() ) {
789  $id = $session->get( 'AuthManager:lastAuthId' );
790  $last = $session->get( 'AuthManager:lastAuthTimestamp' );
791  if ( $id !== $aId || $last === null ) {
792  $timeSinceLogin = PHP_INT_MAX; // Forever ago
793  } else {
794  $timeSinceLogin = max( 0, time() - $last );
795  }
796 
797  $thresholds = $this->config->get( 'ReauthenticateTime' );
798  if ( isset( $thresholds[$operation] ) ) {
799  $threshold = $thresholds[$operation];
800  } elseif ( isset( $thresholds['default'] ) ) {
801  $threshold = $thresholds['default'];
802  } else {
803  throw new \UnexpectedValueException( '$wgReauthenticateTime lacks a default' );
804  }
805 
806  if ( $threshold >= 0 && $timeSinceLogin > $threshold ) {
807  $status = self::SEC_REAUTH;
808  }
809  } else {
810  $timeSinceLogin = -1;
811 
812  $pass = $this->config->get( 'AllowSecuritySensitiveOperationIfCannotReauthenticate' );
813  if ( isset( $pass[$operation] ) ) {
814  $status = $pass[$operation] ? self::SEC_OK : self::SEC_FAIL;
815  } elseif ( isset( $pass['default'] ) ) {
816  $status = $pass['default'] ? self::SEC_OK : self::SEC_FAIL;
817  } else {
818  throw new \UnexpectedValueException(
819  '$wgAllowSecuritySensitiveOperationIfCannotReauthenticate lacks a default'
820  );
821  }
822  }
823 
824  $this->getHookRunner()->onSecuritySensitiveOperationStatus(
825  $status, $operation, $session, $timeSinceLogin );
826 
827  // If authentication is not possible, downgrade from "REAUTH" to "FAIL".
828  if ( !$this->canAuthenticateNow() && $status === self::SEC_REAUTH ) {
829  $status = self::SEC_FAIL;
830  }
831 
832  $this->logger->info( __METHOD__ . ": $operation is $status for '{user}'",
833  [
834  'user' => $session->getUser()->getName(),
835  'clientip' => $this->getRequest()->getIP(),
836  ]
837  );
838 
839  return $status;
840  }
841 
851  public function userCanAuthenticate( $username ) {
852  foreach ( $this->getPrimaryAuthenticationProviders() as $provider ) {
853  if ( $provider->testUserCanAuthenticate( $username ) ) {
854  return true;
855  }
856  }
857  return false;
858  }
859 
874  public function normalizeUsername( $username ) {
875  $ret = [];
876  foreach ( $this->getPrimaryAuthenticationProviders() as $provider ) {
877  $normalized = $provider->providerNormalizeUsername( $username );
878  if ( $normalized !== null ) {
879  $ret[$normalized] = true;
880  }
881  }
882  return array_keys( $ret );
883  }
884 
885  // endregion -- end of Authentication
886 
887  /***************************************************************************/
888  // region Authentication data changing
898  public function revokeAccessForUser( $username ) {
899  $this->logger->info( 'Revoking access for {user}', [
900  'user' => $username,
901  ] );
902  $this->callMethodOnProviders( 6, 'providerRevokeAccessForUser', [ $username ] );
903  }
904 
914  public function allowsAuthenticationDataChange( AuthenticationRequest $req, $checkData = true ) {
915  $any = false;
916  $providers = $this->getPrimaryAuthenticationProviders() +
918 
919  foreach ( $providers as $provider ) {
920  $status = $provider->providerAllowsAuthenticationDataChange( $req, $checkData );
921  if ( !$status->isGood() ) {
922  // If status is not good because reset email password last attempt was within
923  // $wgPasswordReminderResendTime then return good status with throttled-mailpassword value;
924  // otherwise, return the $status wrapped.
925  return $status->hasMessage( 'throttled-mailpassword' )
926  ? Status::newGood( 'throttled-mailpassword' )
927  : Status::wrap( $status );
928  }
929  $any = $any || $status->value !== 'ignored';
930  }
931  if ( !$any ) {
932  $status = Status::newGood( 'ignored' );
933  $status->warning( 'authmanager-change-not-supported' );
934  return $status;
935  }
936  return Status::newGood();
937  }
938 
956  public function changeAuthenticationData( AuthenticationRequest $req, $isAddition = false ) {
957  $this->logger->info( 'Changing authentication data for {user} class {what}', [
958  'user' => is_string( $req->username ) ? $req->username : '<no name>',
959  'what' => get_class( $req ),
960  ] );
961 
962  $this->callMethodOnProviders( 6, 'providerChangeAuthenticationData', [ $req ] );
963 
964  // When the main account's authentication data is changed, invalidate
965  // all BotPasswords too.
966  if ( !$isAddition ) {
968  }
969  }
970 
971  // endregion -- end of Authentication data changing
972 
973  /***************************************************************************/
974  // region Account creation
981  public function canCreateAccounts() {
982  foreach ( $this->getPrimaryAuthenticationProviders() as $provider ) {
983  switch ( $provider->accountCreationType() ) {
986  return true;
987  }
988  }
989  return false;
990  }
991 
1000  public function canCreateAccount( $username, $options = [] ) {
1001  // Back compat
1002  if ( is_int( $options ) ) {
1003  $options = [ 'flags' => $options ];
1004  }
1005  $options += [
1006  'flags' => User::READ_NORMAL,
1007  'creating' => false,
1008  ];
1009  $flags = $options['flags'];
1010 
1011  if ( !$this->canCreateAccounts() ) {
1012  return Status::newFatal( 'authmanager-create-disabled' );
1013  }
1014 
1015  if ( $this->userExists( $username, $flags ) ) {
1016  return Status::newFatal( 'userexists' );
1017  }
1018 
1019  $user = User::newFromName( $username, 'creatable' );
1020  if ( !is_object( $user ) ) {
1021  return Status::newFatal( 'noname' );
1022  } else {
1023  $user->load( $flags ); // Explicitly load with $flags, auto-loading always uses READ_NORMAL
1024  if ( $user->getId() !== 0 ) {
1025  return Status::newFatal( 'userexists' );
1026  }
1027  }
1028 
1029  // Denied by providers?
1030  $providers = $this->getPreAuthenticationProviders() +
1033  foreach ( $providers as $provider ) {
1034  $status = $provider->testUserForCreation( $user, false, $options );
1035  if ( !$status->isGood() ) {
1036  return Status::wrap( $status );
1037  }
1038  }
1039 
1040  return Status::newGood();
1041  }
1042 
1048  public function checkAccountCreatePermissions( Authority $creator ) {
1049  // Wiki is read-only?
1050  if ( $this->readOnlyMode->isReadOnly() ) {
1051  return Status::newFatal( wfMessage( 'readonlytext', $this->readOnlyMode->getReason() ) );
1052  }
1053 
1054  $permStatus = new PermissionStatus();
1055  if ( !$creator->authorizeWrite(
1056  'createaccount',
1057  SpecialPage::getTitleFor( 'CreateAccount' ),
1058  $permStatus
1059  ) ) {
1060  return Status::wrap( $permStatus );
1061  }
1062 
1063  $ip = $this->getRequest()->getIP();
1064  if ( $this->blockManager->isDnsBlacklisted( $ip, true /* check $wgProxyWhitelist */ ) ) {
1065  return Status::newFatal( 'sorbs_create_account_reason' );
1066  }
1067 
1068  return Status::newGood();
1069  }
1070 
1090  public function beginAccountCreation( Authority $creator, array $reqs, $returnToUrl ) {
1091  $session = $this->request->getSession();
1092  if ( !$this->canCreateAccounts() ) {
1093  // Caller should have called canCreateAccounts()
1094  $session->remove( 'AuthManager::accountCreationState' );
1095  throw new \LogicException( 'Account creation is not possible' );
1096  }
1097 
1098  try {
1100  } catch ( \UnexpectedValueException $ex ) {
1101  $username = null;
1102  }
1103  if ( $username === null ) {
1104  $this->logger->debug( __METHOD__ . ': No username provided' );
1105  return AuthenticationResponse::newFail( wfMessage( 'noname' ) );
1106  }
1107 
1108  // Permissions check
1109  $status = $this->checkAccountCreatePermissions( $creator );
1110  if ( !$status->isGood() ) {
1111  $this->logger->debug( __METHOD__ . ': {creator} cannot create users: {reason}', [
1112  'user' => $username,
1113  'creator' => $creator->getUser()->getName(),
1114  'reason' => $status->getWikiText( null, null, 'en' )
1115  ] );
1116  return AuthenticationResponse::newFail( $status->getMessage() );
1117  }
1118 
1119  $status = $this->canCreateAccount(
1120  $username, [ 'flags' => User::READ_LOCKING, 'creating' => true ]
1121  );
1122  if ( !$status->isGood() ) {
1123  $this->logger->debug( __METHOD__ . ': {user} cannot be created: {reason}', [
1124  'user' => $username,
1125  'creator' => $creator->getUser()->getName(),
1126  'reason' => $status->getWikiText( null, null, 'en' )
1127  ] );
1128  return AuthenticationResponse::newFail( $status->getMessage() );
1129  }
1130 
1131  $user = User::newFromName( $username, 'creatable' );
1132  foreach ( $reqs as $req ) {
1133  $req->username = $username;
1134  $req->returnToUrl = $returnToUrl;
1135  if ( $req instanceof UserDataAuthenticationRequest ) {
1136  $status = $req->populateUser( $user );
1137  if ( !$status->isGood() ) {
1138  $status = Status::wrap( $status );
1139  $session->remove( 'AuthManager::accountCreationState' );
1140  $this->logger->debug( __METHOD__ . ': UserData is invalid: {reason}', [
1141  'user' => $user->getName(),
1142  'creator' => $creator->getUser()->getName(),
1143  'reason' => $status->getWikiText( null, null, 'en' ),
1144  ] );
1145  return AuthenticationResponse::newFail( $status->getMessage() );
1146  }
1147  }
1148  }
1149 
1150  $this->removeAuthenticationSessionData( null );
1151 
1152  $state = [
1153  'username' => $username,
1154  'userid' => 0,
1155  'creatorid' => $creator->getUser()->getId(),
1156  'creatorname' => $creator->getUser()->getName(),
1157  'reqs' => $reqs,
1158  'returnToUrl' => $returnToUrl,
1159  'primary' => null,
1160  'primaryResponse' => null,
1161  'secondary' => [],
1162  'continueRequests' => [],
1163  'maybeLink' => [],
1164  'ranPreTests' => false,
1165  ];
1166 
1167  // Special case: converting a login to an account creation
1169  $reqs, CreateFromLoginAuthenticationRequest::class
1170  );
1171  if ( $req ) {
1172  $state['maybeLink'] = $req->maybeLink;
1173 
1174  if ( $req->createRequest ) {
1175  $reqs[] = $req->createRequest;
1176  $state['reqs'][] = $req->createRequest;
1177  }
1178  }
1179 
1180  $session->setSecret( 'AuthManager::accountCreationState', $state );
1181  $session->persist();
1182 
1183  return $this->continueAccountCreation( $reqs );
1184  }
1185 
1191  public function continueAccountCreation( array $reqs ) {
1192  $session = $this->request->getSession();
1193  try {
1194  if ( !$this->canCreateAccounts() ) {
1195  // Caller should have called canCreateAccounts()
1196  $session->remove( 'AuthManager::accountCreationState' );
1197  throw new \LogicException( 'Account creation is not possible' );
1198  }
1199 
1200  $state = $session->getSecret( 'AuthManager::accountCreationState' );
1201  if ( !is_array( $state ) ) {
1203  wfMessage( 'authmanager-create-not-in-progress' )
1204  );
1205  }
1206  $state['continueRequests'] = [];
1207 
1208  // Step 0: Prepare and validate the input
1209 
1210  $user = User::newFromName( $state['username'], 'creatable' );
1211  if ( !is_object( $user ) ) {
1212  $session->remove( 'AuthManager::accountCreationState' );
1213  $this->logger->debug( __METHOD__ . ': Invalid username', [
1214  'user' => $state['username'],
1215  ] );
1216  return AuthenticationResponse::newFail( wfMessage( 'noname' ) );
1217  }
1218 
1219  if ( $state['creatorid'] ) {
1220  $creator = User::newFromId( $state['creatorid'] );
1221  } else {
1222  $creator = new User;
1223  $creator->setName( $state['creatorname'] );
1224  }
1225 
1226  // Avoid account creation races on double submissions
1228  $lock = $cache->getScopedLock( $cache->makeGlobalKey( 'account', md5( $user->getName() ) ) );
1229  if ( !$lock ) {
1230  // Don't clear AuthManager::accountCreationState for this code
1231  // path because the process that won the race owns it.
1232  $this->logger->debug( __METHOD__ . ': Could not acquire account creation lock', [
1233  'user' => $user->getName(),
1234  'creator' => $creator->getName(),
1235  ] );
1236  return AuthenticationResponse::newFail( wfMessage( 'usernameinprogress' ) );
1237  }
1238 
1239  // Permissions check
1240  $status = $this->checkAccountCreatePermissions( $creator );
1241  if ( !$status->isGood() ) {
1242  $this->logger->debug( __METHOD__ . ': {creator} cannot create users: {reason}', [
1243  'user' => $user->getName(),
1244  'creator' => $creator->getName(),
1245  'reason' => $status->getWikiText( null, null, 'en' )
1246  ] );
1247  $ret = AuthenticationResponse::newFail( $status->getMessage() );
1248  $this->callMethodOnProviders( 7, 'postAccountCreation', [ $user, $creator, $ret ] );
1249  $session->remove( 'AuthManager::accountCreationState' );
1250  return $ret;
1251  }
1252 
1253  // Load from master for existence check
1254  $user->load( User::READ_LOCKING );
1255 
1256  if ( $state['userid'] === 0 ) {
1257  if ( $user->getId() !== 0 ) {
1258  $this->logger->debug( __METHOD__ . ': User exists locally', [
1259  'user' => $user->getName(),
1260  'creator' => $creator->getName(),
1261  ] );
1262  $ret = AuthenticationResponse::newFail( wfMessage( 'userexists' ) );
1263  $this->callMethodOnProviders( 7, 'postAccountCreation', [ $user, $creator, $ret ] );
1264  $session->remove( 'AuthManager::accountCreationState' );
1265  return $ret;
1266  }
1267  } else {
1268  if ( $user->getId() === 0 ) {
1269  $this->logger->debug( __METHOD__ . ': User does not exist locally when it should', [
1270  'user' => $user->getName(),
1271  'creator' => $creator->getName(),
1272  'expected_id' => $state['userid'],
1273  ] );
1274  throw new \UnexpectedValueException(
1275  "User \"{$state['username']}\" should exist now, but doesn't!"
1276  );
1277  }
1278  if ( $user->getId() !== $state['userid'] ) {
1279  $this->logger->debug( __METHOD__ . ': User ID/name mismatch', [
1280  'user' => $user->getName(),
1281  'creator' => $creator->getName(),
1282  'expected_id' => $state['userid'],
1283  'actual_id' => $user->getId(),
1284  ] );
1285  throw new \UnexpectedValueException(
1286  "User \"{$state['username']}\" exists, but " .
1287  "ID {$user->getId()} !== {$state['userid']}!"
1288  );
1289  }
1290  }
1291  foreach ( $state['reqs'] as $req ) {
1292  if ( $req instanceof UserDataAuthenticationRequest ) {
1293  $status = $req->populateUser( $user );
1294  if ( !$status->isGood() ) {
1295  // This should never happen...
1296  $status = Status::wrap( $status );
1297  $this->logger->debug( __METHOD__ . ': UserData is invalid: {reason}', [
1298  'user' => $user->getName(),
1299  'creator' => $creator->getName(),
1300  'reason' => $status->getWikiText( null, null, 'en' ),
1301  ] );
1302  $ret = AuthenticationResponse::newFail( $status->getMessage() );
1303  $this->callMethodOnProviders( 7, 'postAccountCreation', [ $user, $creator, $ret ] );
1304  $session->remove( 'AuthManager::accountCreationState' );
1305  return $ret;
1306  }
1307  }
1308  }
1309 
1310  foreach ( $reqs as $req ) {
1311  $req->returnToUrl = $state['returnToUrl'];
1312  $req->username = $state['username'];
1313  }
1314 
1315  // Run pre-creation tests, if we haven't already
1316  if ( !$state['ranPreTests'] ) {
1317  $providers = $this->getPreAuthenticationProviders() +
1320  foreach ( $providers as $id => $provider ) {
1321  $status = $provider->testForAccountCreation( $user, $creator, $reqs );
1322  if ( !$status->isGood() ) {
1323  $this->logger->debug( __METHOD__ . ": Fail in pre-authentication by $id", [
1324  'user' => $user->getName(),
1325  'creator' => $creator->getName(),
1326  ] );
1328  Status::wrap( $status )->getMessage()
1329  );
1330  $this->callMethodOnProviders( 7, 'postAccountCreation', [ $user, $creator, $ret ] );
1331  $session->remove( 'AuthManager::accountCreationState' );
1332  return $ret;
1333  }
1334  }
1335 
1336  $state['ranPreTests'] = true;
1337  }
1338 
1339  // Step 1: Choose a primary authentication provider and call it until it succeeds.
1340 
1341  if ( $state['primary'] === null ) {
1342  // We haven't picked a PrimaryAuthenticationProvider yet
1343  foreach ( $this->getPrimaryAuthenticationProviders() as $id => $provider ) {
1344  if ( $provider->accountCreationType() === PrimaryAuthenticationProvider::TYPE_NONE ) {
1345  continue;
1346  }
1347  $res = $provider->beginPrimaryAccountCreation( $user, $creator, $reqs );
1348  switch ( $res->status ) {
1350  $this->logger->debug( __METHOD__ . ": Primary creation passed by $id", [
1351  'user' => $user->getName(),
1352  'creator' => $creator->getName(),
1353  ] );
1354  $state['primary'] = $id;
1355  $state['primaryResponse'] = $res;
1356  break 2;
1358  $this->logger->debug( __METHOD__ . ": Primary creation failed by $id", [
1359  'user' => $user->getName(),
1360  'creator' => $creator->getName(),
1361  ] );
1362  $this->callMethodOnProviders( 7, 'postAccountCreation', [ $user, $creator, $res ] );
1363  $session->remove( 'AuthManager::accountCreationState' );
1364  return $res;
1366  // Continue loop
1367  break;
1370  $this->logger->debug( __METHOD__ . ": Primary creation $res->status by $id", [
1371  'user' => $user->getName(),
1372  'creator' => $creator->getName(),
1373  ] );
1374  $this->fillRequests( $res->neededRequests, self::ACTION_CREATE, null );
1375  $state['primary'] = $id;
1376  $state['continueRequests'] = $res->neededRequests;
1377  $session->setSecret( 'AuthManager::accountCreationState', $state );
1378  return $res;
1379 
1380  // @codeCoverageIgnoreStart
1381  default:
1382  throw new \DomainException(
1383  get_class( $provider ) . "::beginPrimaryAccountCreation() returned $res->status"
1384  );
1385  // @codeCoverageIgnoreEnd
1386  }
1387  }
1388  if ( $state['primary'] === null ) {
1389  $this->logger->debug( __METHOD__ . ': Primary creation failed because no provider accepted', [
1390  'user' => $user->getName(),
1391  'creator' => $creator->getName(),
1392  ] );
1394  wfMessage( 'authmanager-create-no-primary' )
1395  );
1396  $this->callMethodOnProviders( 7, 'postAccountCreation', [ $user, $creator, $ret ] );
1397  $session->remove( 'AuthManager::accountCreationState' );
1398  return $ret;
1399  }
1400  } elseif ( $state['primaryResponse'] === null ) {
1401  $provider = $this->getAuthenticationProvider( $state['primary'] );
1402  if ( !$provider instanceof PrimaryAuthenticationProvider ) {
1403  // Configuration changed? Force them to start over.
1404  // @codeCoverageIgnoreStart
1406  wfMessage( 'authmanager-create-not-in-progress' )
1407  );
1408  $this->callMethodOnProviders( 7, 'postAccountCreation', [ $user, $creator, $ret ] );
1409  $session->remove( 'AuthManager::accountCreationState' );
1410  return $ret;
1411  // @codeCoverageIgnoreEnd
1412  }
1413  $id = $provider->getUniqueId();
1414  $res = $provider->continuePrimaryAccountCreation( $user, $creator, $reqs );
1415  switch ( $res->status ) {
1417  $this->logger->debug( __METHOD__ . ": Primary creation passed by $id", [
1418  'user' => $user->getName(),
1419  'creator' => $creator->getName(),
1420  ] );
1421  $state['primaryResponse'] = $res;
1422  break;
1424  $this->logger->debug( __METHOD__ . ": Primary creation failed by $id", [
1425  'user' => $user->getName(),
1426  'creator' => $creator->getName(),
1427  ] );
1428  $this->callMethodOnProviders( 7, 'postAccountCreation', [ $user, $creator, $res ] );
1429  $session->remove( 'AuthManager::accountCreationState' );
1430  return $res;
1433  $this->logger->debug( __METHOD__ . ": Primary creation $res->status by $id", [
1434  'user' => $user->getName(),
1435  'creator' => $creator->getName(),
1436  ] );
1437  $this->fillRequests( $res->neededRequests, self::ACTION_CREATE, null );
1438  $state['continueRequests'] = $res->neededRequests;
1439  $session->setSecret( 'AuthManager::accountCreationState', $state );
1440  return $res;
1441  default:
1442  throw new \DomainException(
1443  get_class( $provider ) . "::continuePrimaryAccountCreation() returned $res->status"
1444  );
1445  }
1446  }
1447 
1448  // Step 2: Primary authentication succeeded, create the User object
1449  // and add the user locally.
1450 
1451  if ( $state['userid'] === 0 ) {
1452  $this->logger->info( 'Creating user {user} during account creation', [
1453  'user' => $user->getName(),
1454  'creator' => $creator->getName(),
1455  ] );
1456  $status = $user->addToDatabase();
1457  if ( !$status->isOK() ) {
1458  // @codeCoverageIgnoreStart
1459  $ret = AuthenticationResponse::newFail( $status->getMessage() );
1460  $this->callMethodOnProviders( 7, 'postAccountCreation', [ $user, $creator, $ret ] );
1461  $session->remove( 'AuthManager::accountCreationState' );
1462  return $ret;
1463  // @codeCoverageIgnoreEnd
1464  }
1465  $this->setDefaultUserOptions( $user, $creator->isAnon() );
1466  $this->getHookRunner()->onLocalUserCreated( $user, false );
1467  $user->saveSettings();
1468  $state['userid'] = $user->getId();
1469 
1470  // Update user count
1471  \DeferredUpdates::addUpdate( \SiteStatsUpdate::factory( [ 'users' => 1 ] ) );
1472 
1473  // Watch user's userpage and talk page
1474  $this->watchlistManager->addWatchIgnoringRights( $user, $user->getUserPage() );
1475 
1476  // Inform the provider
1477  $logSubtype = $provider->finishAccountCreation( $user, $creator, $state['primaryResponse'] );
1478 
1479  // Log the creation
1480  if ( $this->config->get( 'NewUserLog' ) ) {
1481  $isAnon = $creator->isAnon();
1482  $logEntry = new \ManualLogEntry(
1483  'newusers',
1484  $logSubtype ?: ( $isAnon ? 'create' : 'create2' )
1485  );
1486  $logEntry->setPerformer( $isAnon ? $user : $creator );
1487  $logEntry->setTarget( $user->getUserPage() );
1490  $state['reqs'], CreationReasonAuthenticationRequest::class
1491  );
1492  $logEntry->setComment( $req ? $req->reason : '' );
1493  $logEntry->setParameters( [
1494  '4::userid' => $user->getId(),
1495  ] );
1496  $logid = $logEntry->insert();
1497  $logEntry->publish( $logid );
1498  }
1499  }
1500 
1501  // Step 3: Iterate over all the secondary authentication providers.
1502 
1503  $beginReqs = $state['reqs'];
1504 
1505  foreach ( $this->getSecondaryAuthenticationProviders() as $id => $provider ) {
1506  if ( !isset( $state['secondary'][$id] ) ) {
1507  // This provider isn't started yet, so we pass it the set
1508  // of reqs from beginAuthentication instead of whatever
1509  // might have been used by a previous provider in line.
1510  $func = 'beginSecondaryAccountCreation';
1511  $res = $provider->beginSecondaryAccountCreation( $user, $creator, $beginReqs );
1512  } elseif ( !$state['secondary'][$id] ) {
1513  $func = 'continueSecondaryAccountCreation';
1514  $res = $provider->continueSecondaryAccountCreation( $user, $creator, $reqs );
1515  } else {
1516  continue;
1517  }
1518  switch ( $res->status ) {
1520  $this->logger->debug( __METHOD__ . ": Secondary creation passed by $id", [
1521  'user' => $user->getName(),
1522  'creator' => $creator->getName(),
1523  ] );
1524  // fall through
1526  $state['secondary'][$id] = true;
1527  break;
1530  $this->logger->debug( __METHOD__ . ": Secondary creation $res->status by $id", [
1531  'user' => $user->getName(),
1532  'creator' => $creator->getName(),
1533  ] );
1534  $this->fillRequests( $res->neededRequests, self::ACTION_CREATE, null );
1535  $state['secondary'][$id] = false;
1536  $state['continueRequests'] = $res->neededRequests;
1537  $session->setSecret( 'AuthManager::accountCreationState', $state );
1538  return $res;
1540  throw new \DomainException(
1541  get_class( $provider ) . "::{$func}() returned $res->status." .
1542  ' Secondary providers are not allowed to fail account creation, that' .
1543  ' should have been done via testForAccountCreation().'
1544  );
1545  // @codeCoverageIgnoreStart
1546  default:
1547  throw new \DomainException(
1548  get_class( $provider ) . "::{$func}() returned $res->status"
1549  );
1550  // @codeCoverageIgnoreEnd
1551  }
1552  }
1553 
1554  $id = $user->getId();
1555  $name = $user->getName();
1556  $req = new CreatedAccountAuthenticationRequest( $id, $name );
1557  $ret = AuthenticationResponse::newPass( $name );
1558  $ret->loginRequest = $req;
1559  $this->createdAccountAuthenticationRequests[] = $req;
1560 
1561  $this->logger->info( __METHOD__ . ': Account creation succeeded for {user}', [
1562  'user' => $user->getName(),
1563  'creator' => $creator->getName(),
1564  ] );
1565 
1566  $this->callMethodOnProviders( 7, 'postAccountCreation', [ $user, $creator, $ret ] );
1567  $session->remove( 'AuthManager::accountCreationState' );
1568  $this->removeAuthenticationSessionData( null );
1569  return $ret;
1570  } catch ( \Exception $ex ) {
1571  $session->remove( 'AuthManager::accountCreationState' );
1572  throw $ex;
1573  }
1574  }
1575 
1594  public function autoCreateUser( User $user, $source, $login = true, $log = true ) {
1595  if ( $source !== self::AUTOCREATE_SOURCE_SESSION &&
1596  $source !== self::AUTOCREATE_SOURCE_MAINT &&
1598  ) {
1599  throw new \InvalidArgumentException( "Unknown auto-creation source: $source" );
1600  }
1601 
1602  $username = $user->getName();
1603 
1604  // Try the local user from the replica DB
1605  $localId = User::idFromName( $username );
1606  $flags = User::READ_NORMAL;
1607 
1608  // Fetch the user ID from the primary, so that we don't try to create the user
1609  // when they already exist, due to replication lag
1610  // @codeCoverageIgnoreStart
1611  if (
1612  !$localId &&
1613  MediaWikiServices::getInstance()->getDBLoadBalancer()->getReaderIndex() !== 0
1614  ) {
1615  $localId = User::idFromName( $username, User::READ_LATEST );
1616  $flags = User::READ_LATEST;
1617  }
1618  // @codeCoverageIgnoreEnd
1619 
1620  if ( $localId ) {
1621  $this->logger->debug( __METHOD__ . ': {username} already exists locally', [
1622  'username' => $username,
1623  ] );
1624  $user->setId( $localId );
1625  $user->loadFromId( $flags );
1626  if ( $login ) {
1627  $this->setSessionDataForUser( $user );
1628  }
1629  $status = Status::newGood();
1630  $status->warning( 'userexists' );
1631  return $status;
1632  }
1633 
1634  // Wiki is read-only?
1635  if ( $this->readOnlyMode->isReadOnly() ) {
1636  $reason = $this->readOnlyMode->getReason();
1637  $this->logger->debug( __METHOD__ . ': denied because of read only mode: {reason}', [
1638  'username' => $username,
1639  'reason' => $reason,
1640  ] );
1641  $user->setId( 0 );
1642  $user->loadFromId();
1643  return Status::newFatal( wfMessage( 'readonlytext', $reason ) );
1644  }
1645 
1646  // Check the session, if we tried to create this user already there's
1647  // no point in retrying.
1648  $session = $this->request->getSession();
1649  if ( $session->get( 'AuthManager::AutoCreateBlacklist' ) ) {
1650  $this->logger->debug( __METHOD__ . ': blacklisted in session {sessionid}', [
1651  'username' => $username,
1652  'sessionid' => $session->getId(),
1653  ] );
1654  $user->setId( 0 );
1655  $user->loadFromId();
1656  $reason = $session->get( 'AuthManager::AutoCreateBlacklist' );
1657  if ( $reason instanceof StatusValue ) {
1658  return Status::wrap( $reason );
1659  } else {
1660  return Status::newFatal( $reason );
1661  }
1662  }
1663 
1664  // Is the username creatable?
1665  if ( !$this->userNameUtils->isCreatable( $username ) ) {
1666  $this->logger->debug( __METHOD__ . ': name "{username}" is not creatable', [
1667  'username' => $username,
1668  ] );
1669  $session->set( 'AuthManager::AutoCreateBlacklist', 'noname' );
1670  $user->setId( 0 );
1671  $user->loadFromId();
1672  return Status::newFatal( 'noname' );
1673  }
1674 
1675  // Is the IP user able to create accounts?
1676  $anon = new User;
1677  if ( $source !== self::AUTOCREATE_SOURCE_MAINT &&
1678  !$anon->isAllowedAny( 'createaccount', 'autocreateaccount' )
1679  ) {
1680  $this->logger->debug( __METHOD__ . ': IP lacks the ability to create or autocreate accounts', [
1681  'username' => $username,
1682  'clientip' => $anon->getName(),
1683  ] );
1684  $session->set( 'AuthManager::AutoCreateBlacklist', 'authmanager-autocreate-noperm' );
1685  $session->persist();
1686  $user->setId( 0 );
1687  $user->loadFromId();
1688  return Status::newFatal( 'authmanager-autocreate-noperm' );
1689  }
1690 
1691  // Avoid account creation races on double submissions
1693  $lock = $cache->getScopedLock( $cache->makeGlobalKey( 'account', md5( $username ) ) );
1694  if ( !$lock ) {
1695  $this->logger->debug( __METHOD__ . ': Could not acquire account creation lock', [
1696  'user' => $username,
1697  ] );
1698  $user->setId( 0 );
1699  $user->loadFromId();
1700  return Status::newFatal( 'usernameinprogress' );
1701  }
1702 
1703  // Denied by providers?
1704  $options = [
1705  'flags' => User::READ_LATEST,
1706  'creating' => true,
1707  ];
1708  $providers = $this->getPreAuthenticationProviders() +
1711  foreach ( $providers as $provider ) {
1712  $status = $provider->testUserForCreation( $user, $source, $options );
1713  if ( !$status->isGood() ) {
1714  $ret = Status::wrap( $status );
1715  $this->logger->debug( __METHOD__ . ': Provider denied creation of {username}: {reason}', [
1716  'username' => $username,
1717  'reason' => $ret->getWikiText( null, null, 'en' ),
1718  ] );
1719  $session->set( 'AuthManager::AutoCreateBlacklist', $status );
1720  $user->setId( 0 );
1721  $user->loadFromId();
1722  return $ret;
1723  }
1724  }
1725 
1726  $backoffKey = $cache->makeKey( 'AuthManager', 'autocreate-failed', md5( $username ) );
1727  if ( $cache->get( $backoffKey ) ) {
1728  $this->logger->debug( __METHOD__ . ': {username} denied by prior creation attempt failures', [
1729  'username' => $username,
1730  ] );
1731  $user->setId( 0 );
1732  $user->loadFromId();
1733  return Status::newFatal( 'authmanager-autocreate-exception' );
1734  }
1735 
1736  // Checks passed, create the user...
1737  $from = $_SERVER['REQUEST_URI'] ?? 'CLI';
1738  $this->logger->info( __METHOD__ . ': creating new user ({username}) - from: {from}', [
1739  'username' => $username,
1740  'from' => $from,
1741  ] );
1742 
1743  // Ignore warnings about master connections/writes...hard to avoid here
1744  $trxProfilerSilencedScope = \Profiler::instance()->getTransactionProfiler()->silenceForScope();
1745  try {
1746  $status = $user->addToDatabase();
1747  if ( !$status->isOK() ) {
1748  // Double-check for a race condition (T70012). We make use of the fact that when
1749  // addToDatabase fails due to the user already existing, the user object gets loaded.
1750  if ( $user->getId() ) {
1751  $this->logger->info( __METHOD__ . ': {username} already exists locally (race)', [
1752  'username' => $username,
1753  ] );
1754  if ( $login ) {
1755  $this->setSessionDataForUser( $user );
1756  }
1757  $status = Status::newGood();
1758  $status->warning( 'userexists' );
1759  } else {
1760  $this->logger->error( __METHOD__ . ': {username} failed with message {msg}', [
1761  'username' => $username,
1762  'msg' => $status->getWikiText( null, null, 'en' )
1763  ] );
1764  $user->setId( 0 );
1765  $user->loadFromId();
1766  }
1767  return $status;
1768  }
1769  } catch ( \Exception $ex ) {
1770  $this->logger->error( __METHOD__ . ': {username} failed with exception {exception}', [
1771  'username' => $username,
1772  'exception' => $ex,
1773  ] );
1774  // Do not keep throwing errors for a while
1775  $cache->set( $backoffKey, 1, 600 );
1776  // Bubble up error; which should normally trigger DB rollbacks
1777  throw $ex;
1778  }
1779 
1780  $this->setDefaultUserOptions( $user, false );
1781 
1782  // Inform the providers
1783  $this->callMethodOnProviders( 6, 'autoCreatedAccount', [ $user, $source ] );
1784 
1785  $this->getHookRunner()->onLocalUserCreated( $user, true );
1786  $user->saveSettings();
1787 
1788  // Update user count
1789  \DeferredUpdates::addUpdate( \SiteStatsUpdate::factory( [ 'users' => 1 ] ) );
1790  // Watch user's userpage and talk page
1791  \DeferredUpdates::addCallableUpdate( function () use ( $user ) {
1792  $this->watchlistManager->addWatchIgnoringRights( $user, $user->getUserPage() );
1793  } );
1794 
1795  // Log the creation
1796  if ( $this->config->get( 'NewUserLog' ) && $log ) {
1797  $logEntry = new \ManualLogEntry( 'newusers', 'autocreate' );
1798  $logEntry->setPerformer( $user );
1799  $logEntry->setTarget( $user->getUserPage() );
1800  $logEntry->setComment( '' );
1801  $logEntry->setParameters( [
1802  '4::userid' => $user->getId(),
1803  ] );
1804  $logEntry->insert();
1805  }
1806 
1807  ScopedCallback::consume( $trxProfilerSilencedScope );
1808 
1809  if ( $login ) {
1810  $this->setSessionDataForUser( $user );
1811  }
1812 
1813  return Status::newGood();
1814  }
1815 
1816  // endregion -- end of Account creation
1817 
1818  /***************************************************************************/
1819  // region Account linking
1826  public function canLinkAccounts() {
1827  foreach ( $this->getPrimaryAuthenticationProviders() as $provider ) {
1828  if ( $provider->accountCreationType() === PrimaryAuthenticationProvider::TYPE_LINK ) {
1829  return true;
1830  }
1831  }
1832  return false;
1833  }
1834 
1844  public function beginAccountLink( User $user, array $reqs, $returnToUrl ) {
1845  $session = $this->request->getSession();
1846  $session->remove( 'AuthManager::accountLinkState' );
1847 
1848  if ( !$this->canLinkAccounts() ) {
1849  // Caller should have called canLinkAccounts()
1850  throw new \LogicException( 'Account linking is not possible' );
1851  }
1852 
1853  if ( $user->getId() === 0 ) {
1854  if ( !$this->userNameUtils->isUsable( $user->getName() ) ) {
1855  $msg = wfMessage( 'noname' );
1856  } else {
1857  $msg = wfMessage( 'authmanager-userdoesnotexist', $user->getName() );
1858  }
1859  return AuthenticationResponse::newFail( $msg );
1860  }
1861  foreach ( $reqs as $req ) {
1862  $req->username = $user->getName();
1863  $req->returnToUrl = $returnToUrl;
1864  }
1865 
1866  $this->removeAuthenticationSessionData( null );
1867 
1868  $providers = $this->getPreAuthenticationProviders();
1869  foreach ( $providers as $id => $provider ) {
1870  $status = $provider->testForAccountLink( $user );
1871  if ( !$status->isGood() ) {
1872  $this->logger->debug( __METHOD__ . ": Account linking pre-check failed by $id", [
1873  'user' => $user->getName(),
1874  ] );
1876  Status::wrap( $status )->getMessage()
1877  );
1878  $this->callMethodOnProviders( 3, 'postAccountLink', [ $user, $ret ] );
1879  return $ret;
1880  }
1881  }
1882 
1883  $state = [
1884  'username' => $user->getName(),
1885  'userid' => $user->getId(),
1886  'returnToUrl' => $returnToUrl,
1887  'primary' => null,
1888  'continueRequests' => [],
1889  ];
1890 
1891  $providers = $this->getPrimaryAuthenticationProviders();
1892  foreach ( $providers as $id => $provider ) {
1893  if ( $provider->accountCreationType() !== PrimaryAuthenticationProvider::TYPE_LINK ) {
1894  continue;
1895  }
1896 
1897  $res = $provider->beginPrimaryAccountLink( $user, $reqs );
1898  switch ( $res->status ) {
1900  $this->logger->info( "Account linked to {user} by $id", [
1901  'user' => $user->getName(),
1902  ] );
1903  $this->callMethodOnProviders( 3, 'postAccountLink', [ $user, $res ] );
1904  return $res;
1905 
1907  $this->logger->debug( __METHOD__ . ": Account linking failed by $id", [
1908  'user' => $user->getName(),
1909  ] );
1910  $this->callMethodOnProviders( 3, 'postAccountLink', [ $user, $res ] );
1911  return $res;
1912 
1914  // Continue loop
1915  break;
1916 
1919  $this->logger->debug( __METHOD__ . ": Account linking $res->status by $id", [
1920  'user' => $user->getName(),
1921  ] );
1922  $this->fillRequests( $res->neededRequests, self::ACTION_LINK, $user->getName() );
1923  $state['primary'] = $id;
1924  $state['continueRequests'] = $res->neededRequests;
1925  $session->setSecret( 'AuthManager::accountLinkState', $state );
1926  $session->persist();
1927  return $res;
1928 
1929  // @codeCoverageIgnoreStart
1930  default:
1931  throw new \DomainException(
1932  get_class( $provider ) . "::beginPrimaryAccountLink() returned $res->status"
1933  );
1934  // @codeCoverageIgnoreEnd
1935  }
1936  }
1937 
1938  $this->logger->debug( __METHOD__ . ': Account linking failed because no provider accepted', [
1939  'user' => $user->getName(),
1940  ] );
1942  wfMessage( 'authmanager-link-no-primary' )
1943  );
1944  $this->callMethodOnProviders( 3, 'postAccountLink', [ $user, $ret ] );
1945  return $ret;
1946  }
1947 
1953  public function continueAccountLink( array $reqs ) {
1954  $session = $this->request->getSession();
1955  try {
1956  if ( !$this->canLinkAccounts() ) {
1957  // Caller should have called canLinkAccounts()
1958  $session->remove( 'AuthManager::accountLinkState' );
1959  throw new \LogicException( 'Account linking is not possible' );
1960  }
1961 
1962  $state = $session->getSecret( 'AuthManager::accountLinkState' );
1963  if ( !is_array( $state ) ) {
1965  wfMessage( 'authmanager-link-not-in-progress' )
1966  );
1967  }
1968  $state['continueRequests'] = [];
1969 
1970  // Step 0: Prepare and validate the input
1971 
1972  $user = User::newFromName( $state['username'], 'usable' );
1973  if ( !is_object( $user ) ) {
1974  $session->remove( 'AuthManager::accountLinkState' );
1975  return AuthenticationResponse::newFail( wfMessage( 'noname' ) );
1976  }
1977  if ( $user->getId() !== $state['userid'] ) {
1978  throw new \UnexpectedValueException(
1979  "User \"{$state['username']}\" is valid, but " .
1980  "ID {$user->getId()} !== {$state['userid']}!"
1981  );
1982  }
1983 
1984  foreach ( $reqs as $req ) {
1985  $req->username = $state['username'];
1986  $req->returnToUrl = $state['returnToUrl'];
1987  }
1988 
1989  // Step 1: Call the primary again until it succeeds
1990 
1991  $provider = $this->getAuthenticationProvider( $state['primary'] );
1992  if ( !$provider instanceof PrimaryAuthenticationProvider ) {
1993  // Configuration changed? Force them to start over.
1994  // @codeCoverageIgnoreStart
1996  wfMessage( 'authmanager-link-not-in-progress' )
1997  );
1998  $this->callMethodOnProviders( 3, 'postAccountLink', [ $user, $ret ] );
1999  $session->remove( 'AuthManager::accountLinkState' );
2000  return $ret;
2001  // @codeCoverageIgnoreEnd
2002  }
2003  $id = $provider->getUniqueId();
2004  $res = $provider->continuePrimaryAccountLink( $user, $reqs );
2005  switch ( $res->status ) {
2007  $this->logger->info( "Account linked to {user} by $id", [
2008  'user' => $user->getName(),
2009  ] );
2010  $this->callMethodOnProviders( 3, 'postAccountLink', [ $user, $res ] );
2011  $session->remove( 'AuthManager::accountLinkState' );
2012  return $res;
2014  $this->logger->debug( __METHOD__ . ": Account linking failed by $id", [
2015  'user' => $user->getName(),
2016  ] );
2017  $this->callMethodOnProviders( 3, 'postAccountLink', [ $user, $res ] );
2018  $session->remove( 'AuthManager::accountLinkState' );
2019  return $res;
2022  $this->logger->debug( __METHOD__ . ": Account linking $res->status by $id", [
2023  'user' => $user->getName(),
2024  ] );
2025  $this->fillRequests( $res->neededRequests, self::ACTION_LINK, $user->getName() );
2026  $state['continueRequests'] = $res->neededRequests;
2027  $session->setSecret( 'AuthManager::accountLinkState', $state );
2028  return $res;
2029  default:
2030  throw new \DomainException(
2031  get_class( $provider ) . "::continuePrimaryAccountLink() returned $res->status"
2032  );
2033  }
2034  } catch ( \Exception $ex ) {
2035  $session->remove( 'AuthManager::accountLinkState' );
2036  throw $ex;
2037  }
2038  }
2039 
2040  // endregion -- end of Account linking
2041 
2042  /***************************************************************************/
2043  // region Information methods
2064  public function getAuthenticationRequests( $action, UserIdentity $user = null ) {
2065  $options = [];
2066  $providerAction = $action;
2067 
2068  // Figure out which providers to query
2069  switch ( $action ) {
2070  case self::ACTION_LOGIN:
2071  case self::ACTION_CREATE:
2072  $providers = $this->getPreAuthenticationProviders() +
2075  break;
2076 
2078  $state = $this->request->getSession()->getSecret( 'AuthManager::authnState' );
2079  return is_array( $state ) ? $state['continueRequests'] : [];
2080 
2082  $state = $this->request->getSession()->getSecret( 'AuthManager::accountCreationState' );
2083  return is_array( $state ) ? $state['continueRequests'] : [];
2084 
2085  case self::ACTION_LINK:
2086  $providers = [];
2087  foreach ( $this->getPrimaryAuthenticationProviders() as $p ) {
2088  if ( $p->accountCreationType() === PrimaryAuthenticationProvider::TYPE_LINK ) {
2089  $providers[] = $p;
2090  }
2091  }
2092  break;
2093 
2094  case self::ACTION_UNLINK:
2095  $providers = [];
2096  foreach ( $this->getPrimaryAuthenticationProviders() as $p ) {
2097  if ( $p->accountCreationType() === PrimaryAuthenticationProvider::TYPE_LINK ) {
2098  $providers[] = $p;
2099  }
2100  }
2101 
2102  // To providers, unlink and remove are identical.
2103  $providerAction = self::ACTION_REMOVE;
2104  break;
2105 
2107  $state = $this->request->getSession()->getSecret( 'AuthManager::accountLinkState' );
2108  return is_array( $state ) ? $state['continueRequests'] : [];
2109 
2110  case self::ACTION_CHANGE:
2111  case self::ACTION_REMOVE:
2112  $providers = $this->getPrimaryAuthenticationProviders() +
2114  break;
2115 
2116  // @codeCoverageIgnoreStart
2117  default:
2118  throw new \DomainException( __METHOD__ . ": Invalid action \"$action\"" );
2119  }
2120  // @codeCoverageIgnoreEnd
2121 
2122  return $this->getAuthenticationRequestsInternal( $providerAction, $options, $providers, $user );
2123  }
2124 
2135  $providerAction, array $options, array $providers, UserIdentity $user = null
2136  ) {
2137  $user = $user ?: \RequestContext::getMain()->getUser();
2138  $options['username'] = $user->isRegistered() ? $user->getName() : null;
2139 
2140  // Query them and merge results
2141  $reqs = [];
2142  foreach ( $providers as $provider ) {
2143  $isPrimary = $provider instanceof PrimaryAuthenticationProvider;
2144  foreach ( $provider->getAuthenticationRequests( $providerAction, $options ) as $req ) {
2145  $id = $req->getUniqueId();
2146 
2147  // If a required request if from a Primary, mark it as "primary-required" instead
2148  if ( $isPrimary && $req->required ) {
2149  $req->required = AuthenticationRequest::PRIMARY_REQUIRED;
2150  }
2151 
2152  if (
2153  !isset( $reqs[$id] )
2154  || $req->required === AuthenticationRequest::REQUIRED
2155  || $reqs[$id] === AuthenticationRequest::OPTIONAL
2156  ) {
2157  $reqs[$id] = $req;
2158  }
2159  }
2160  }
2161 
2162  // AuthManager has its own req for some actions
2163  switch ( $providerAction ) {
2164  case self::ACTION_LOGIN:
2165  $reqs[] = new RememberMeAuthenticationRequest( $this->config->get( 'RememberMe' ) );
2166  break;
2167 
2168  case self::ACTION_CREATE:
2169  $reqs[] = new UsernameAuthenticationRequest;
2170  $reqs[] = new UserDataAuthenticationRequest;
2171  if ( $options['username'] !== null ) {
2173  $options['username'] = null; // Don't fill in the username below
2174  }
2175  break;
2176  }
2177 
2178  // Fill in reqs data
2179  $this->fillRequests( $reqs, $providerAction, $options['username'], true );
2180 
2181  // For self::ACTION_CHANGE, filter out any that something else *doesn't* allow changing
2182  if ( $providerAction === self::ACTION_CHANGE || $providerAction === self::ACTION_REMOVE ) {
2183  $reqs = array_filter( $reqs, function ( $req ) {
2184  return $this->allowsAuthenticationDataChange( $req, false )->isGood();
2185  } );
2186  }
2187 
2188  return array_values( $reqs );
2189  }
2190 
2198  private function fillRequests( array &$reqs, $action, $username, $forceAction = false ) {
2199  foreach ( $reqs as $req ) {
2200  if ( !$req->action || $forceAction ) {
2201  $req->action = $action;
2202  }
2203  if ( $req->username === null ) {
2204  $req->username = $username;
2205  }
2206  }
2207  }
2208 
2215  public function userExists( $username, $flags = User::READ_NORMAL ) {
2216  foreach ( $this->getPrimaryAuthenticationProviders() as $provider ) {
2217  if ( $provider->testUserExists( $username, $flags ) ) {
2218  return true;
2219  }
2220  }
2221 
2222  return false;
2223  }
2224 
2236  public function allowsPropertyChange( $property ) {
2237  $providers = $this->getPrimaryAuthenticationProviders() +
2239  foreach ( $providers as $provider ) {
2240  if ( !$provider->providerAllowsPropertyChange( $property ) ) {
2241  return false;
2242  }
2243  }
2244  return true;
2245  }
2246 
2255  public function getAuthenticationProvider( $id ) {
2256  // Fast version
2257  if ( isset( $this->allAuthenticationProviders[$id] ) ) {
2258  return $this->allAuthenticationProviders[$id];
2259  }
2260 
2261  // Slow version: instantiate each kind and check
2262  $providers = $this->getPrimaryAuthenticationProviders();
2263  if ( isset( $providers[$id] ) ) {
2264  return $providers[$id];
2265  }
2266  $providers = $this->getSecondaryAuthenticationProviders();
2267  if ( isset( $providers[$id] ) ) {
2268  return $providers[$id];
2269  }
2270  $providers = $this->getPreAuthenticationProviders();
2271  if ( isset( $providers[$id] ) ) {
2272  return $providers[$id];
2273  }
2274 
2275  return null;
2276  }
2277 
2278  // endregion -- end of Information methods
2279 
2280  /***************************************************************************/
2281  // region Internal methods
2290  public function setAuthenticationSessionData( $key, $data ) {
2291  $session = $this->request->getSession();
2292  $arr = $session->getSecret( 'authData' );
2293  if ( !is_array( $arr ) ) {
2294  $arr = [];
2295  }
2296  $arr[$key] = $data;
2297  $session->setSecret( 'authData', $arr );
2298  }
2299 
2307  public function getAuthenticationSessionData( $key, $default = null ) {
2308  $arr = $this->request->getSession()->getSecret( 'authData' );
2309  if ( is_array( $arr ) && array_key_exists( $key, $arr ) ) {
2310  return $arr[$key];
2311  } else {
2312  return $default;
2313  }
2314  }
2315 
2321  public function removeAuthenticationSessionData( $key ) {
2322  $session = $this->request->getSession();
2323  if ( $key === null ) {
2324  $session->remove( 'authData' );
2325  } else {
2326  $arr = $session->getSecret( 'authData' );
2327  if ( is_array( $arr ) && array_key_exists( $key, $arr ) ) {
2328  unset( $arr[$key] );
2329  $session->setSecret( 'authData', $arr );
2330  }
2331  }
2332  }
2333 
2340  protected function providerArrayFromSpecs( $class, array $specs ) {
2341  $i = 0;
2342  foreach ( $specs as &$spec ) {
2343  $spec = [ 'sort2' => $i++ ] + $spec + [ 'sort' => 0 ];
2344  }
2345  unset( $spec );
2346  // Sort according to the 'sort' field, and if they are equal, according to 'sort2'
2347  usort( $specs, static function ( $a, $b ) {
2348  return $a['sort'] <=> $b['sort']
2349  ?: $a['sort2'] <=> $b['sort2'];
2350  } );
2351 
2352  $ret = [];
2353  foreach ( $specs as $spec ) {
2355  $provider = $this->objectFactory->createObject( $spec, [ 'assertClass' => $class ] );
2356  $provider->init( $this->logger, $this, $this->getHookContainer(), $this->config, $this->userNameUtils );
2357  $id = $provider->getUniqueId();
2358  if ( isset( $this->allAuthenticationProviders[$id] ) ) {
2359  throw new \RuntimeException(
2360  "Duplicate specifications for id $id (classes " .
2361  get_class( $provider ) . ' and ' .
2362  get_class( $this->allAuthenticationProviders[$id] ) . ')'
2363  );
2364  }
2365  $this->allAuthenticationProviders[$id] = $provider;
2366  $ret[$id] = $provider;
2367  }
2368  return $ret;
2369  }
2370 
2374  private function getConfiguration() {
2375  return $this->config->get( 'AuthManagerConfig' ) ?: $this->config->get( 'AuthManagerAutoConfig' );
2376  }
2377 
2382  protected function getPreAuthenticationProviders() {
2383  if ( $this->preAuthenticationProviders === null ) {
2384  $conf = $this->getConfiguration();
2385  $this->preAuthenticationProviders = $this->providerArrayFromSpecs(
2386  PreAuthenticationProvider::class, $conf['preauth']
2387  );
2388  }
2390  }
2391 
2396  protected function getPrimaryAuthenticationProviders() {
2397  if ( $this->primaryAuthenticationProviders === null ) {
2398  $conf = $this->getConfiguration();
2399  $this->primaryAuthenticationProviders = $this->providerArrayFromSpecs(
2400  PrimaryAuthenticationProvider::class, $conf['primaryauth']
2401  );
2402  }
2404  }
2405 
2411  if ( $this->secondaryAuthenticationProviders === null ) {
2412  $conf = $this->getConfiguration();
2413  $this->secondaryAuthenticationProviders = $this->providerArrayFromSpecs(
2414  SecondaryAuthenticationProvider::class, $conf['secondaryauth']
2415  );
2416  }
2418  }
2419 
2425  private function setSessionDataForUser( $user, $remember = null ) {
2426  $session = $this->request->getSession();
2427  $delay = $session->delaySave();
2428 
2429  $session->resetId();
2430  $session->resetAllTokens();
2431  if ( $session->canSetUser() ) {
2432  $session->setUser( $user );
2433  }
2434  if ( $remember !== null ) {
2435  $session->setRememberUser( $remember );
2436  }
2437  $session->set( 'AuthManager:lastAuthId', $user->getId() );
2438  $session->set( 'AuthManager:lastAuthTimestamp', time() );
2439  $session->persist();
2440 
2441  \Wikimedia\ScopedCallback::consume( $delay );
2442 
2443  $this->getHookRunner()->onUserLoggedIn( $user );
2444  }
2445 
2450  private function setDefaultUserOptions( User $user, $useContextLang ) {
2451  $user->setToken();
2452 
2453  $contLang = MediaWikiServices::getInstance()->getContentLanguage();
2454 
2455  $lang = $useContextLang ? \RequestContext::getMain()->getLanguage() : $contLang;
2456  $user->setOption( 'language', $lang->getPreferredVariant() );
2457 
2458  $contLangConverter = MediaWikiServices::getInstance()->getLanguageConverterFactory()
2459  ->getLanguageConverter();
2460  if ( $contLangConverter->hasVariants() ) {
2461  $user->setOption( 'variant', $contLangConverter->getPreferredVariant() );
2462  }
2463  }
2464 
2470  private function callMethodOnProviders( $which, $method, array $args ) {
2471  $providers = [];
2472  if ( $which & 1 ) {
2473  $providers += $this->getPreAuthenticationProviders();
2474  }
2475  if ( $which & 2 ) {
2476  $providers += $this->getPrimaryAuthenticationProviders();
2477  }
2478  if ( $which & 4 ) {
2479  $providers += $this->getSecondaryAuthenticationProviders();
2480  }
2481  foreach ( $providers as $provider ) {
2482  $provider->$method( ...$args );
2483  }
2484  }
2485 
2491  public static function resetCache() {
2492  if ( !defined( 'MW_PHPUNIT_TEST' ) ) {
2493  // @codeCoverageIgnoreStart
2494  throw new \MWException( __METHOD__ . ' may only be called from unit tests!' );
2495  // @codeCoverageIgnoreEnd
2496  }
2497 
2498  self::$instance = null;
2499  }
2500 
2504  private function getHookContainer() {
2505  return $this->hookContainer;
2506  }
2507 
2511  private function getHookRunner() {
2512  return $this->hookRunner;
2513  }
2514 
2515  // endregion -- end of Internal methods
2516 
2517 }
2518 
2519 /*
2520  * This file uses VisualStudio style region/endregion fold markers which are
2521  * recognised by PHPStorm. If modelines are enabled, the following editor
2522  * configuration will also enable folding in vim, if it is in the last 5 lines
2523  * of the file. We also use "@name" which creates sections in Doxygen.
2524  *
2525  * vim: foldmarker=//\ region,//\ endregion foldmethod=marker
2526  */
MediaWiki\Auth\AuthManager\autoCreateUser
autoCreateUser(User $user, $source, $login=true, $log=true)
Auto-create an account, and optionally log into that account.
Definition: AuthManager.php:1594
MediaWiki\Auth\AuthManager\continueAccountLink
continueAccountLink(array $reqs)
Continue an account linking flow.
Definition: AuthManager.php:1953
User\loadFromId
loadFromId( $flags=self::READ_NORMAL)
Load user table data, given mId has already been set.
Definition: User.php:452
MediaWiki\Auth\AuthManager\continueAuthentication
continueAuthentication(array $reqs)
Continue an authentication flow.
Definition: AuthManager.php:453
MediaWiki\Auth\AuthenticationRequest\OPTIONAL
const OPTIONAL
Indicates that the request is not required for authentication to proceed.
Definition: AuthenticationRequest.php:41
MediaWiki\Auth\AuthManager\getRequest
getRequest()
Definition: AuthManager.php:243
MediaWiki\Auth\PrimaryAuthenticationProvider\TYPE_CREATE
const TYPE_CREATE
Provider can create accounts.
Definition: PrimaryAuthenticationProvider.php:77
MediaWiki\Auth\AuthManager\$preAuthenticationProviders
PreAuthenticationProvider[] $preAuthenticationProviders
Definition: AuthManager.php:158
User\newFromId
static newFromId( $id)
Static factory method for creation from a given user ID.
Definition: User.php:633
StatusValue
Generic operation result class Has warning/error list, boolean status and arbitrary value.
Definition: StatusValue.php:43
MediaWiki\Auth\AuthManager\SEC_REAUTH
const SEC_REAUTH
Security-sensitive operations should re-authenticate.
Definition: AuthManager.php:126
MediaWiki\Auth\PrimaryAuthenticationProvider\TYPE_NONE
const TYPE_NONE
Provider cannot create or link to accounts.
Definition: PrimaryAuthenticationProvider.php:81
StatusValue\newFatal
static newFatal( $message,... $parameters)
Factory function for fatal errors.
Definition: StatusValue.php:70
MediaWiki\Auth\AuthenticationProvider\getUniqueId
getUniqueId()
Return a unique identifier for this instance.
ObjectCache\getLocalClusterInstance
static getLocalClusterInstance()
Get the main cluster-local cache object.
Definition: ObjectCache.php:272
MediaWiki\Auth\AuthManager\fillRequests
fillRequests(array &$reqs, $action, $username, $forceAction=false)
Set values in an array of requests.
Definition: AuthManager.php:2198
MediaWiki\Auth\RememberMeAuthenticationRequest\ALWAYS_REMEMBER
const ALWAYS_REMEMBER
Indicates that the user will always be remembered.
Definition: RememberMeAuthenticationRequest.php:53
MediaWiki\Auth\AuthManager\changeAuthenticationData
changeAuthenticationData(AuthenticationRequest $req, $isAddition=false)
Change authentication data (e.g.
Definition: AuthManager.php:956
MediaWiki\Auth\AuthManager\getPrimaryAuthenticationProviders
getPrimaryAuthenticationProviders()
Get the list of PrimaryAuthenticationProviders.
Definition: AuthManager.php:2396
MediaWiki\Block\BlockManager
A service class for checking blocks.
Definition: BlockManager.php:47
Profiler\instance
static instance()
Singleton.
Definition: Profiler.php:69
MediaWiki\MediaWikiServices
MediaWikiServices is the service locator for the application scope of MediaWiki.
Definition: MediaWikiServices.php:180
MediaWiki\Auth\AuthManager\ACTION_UNLINK
const ACTION_UNLINK
Like ACTION_REMOVE but for linking providers only.
Definition: AuthManager.php:121
$lang
if(!isset( $args[0])) $lang
Definition: testCompression.php:37
MediaWiki\Auth\AuthManager\AUTOCREATE_SOURCE_SESSION
const AUTOCREATE_SOURCE_SESSION
Auto-creation is due to SessionManager.
Definition: AuthManager.php:131
MediaWiki\Auth\AuthManager\getHookContainer
getHookContainer()
Definition: AuthManager.php:2504
MediaWiki\Auth\AuthManager\revokeAccessForUser
revokeAccessForUser( $username)
Revoke any authentication credentials for a user.
Definition: AuthManager.php:898
MediaWiki\Auth\AuthManager\$userNameUtils
UserNameUtils $userNameUtils
Definition: AuthManager.php:152
MediaWiki\Block\BlockErrorFormatter
A service class for getting formatted information about a block.
Definition: BlockErrorFormatter.php:35
ReadOnlyMode
A service class for fetching the wiki's current read-only mode.
Definition: ReadOnlyMode.php:11
MediaWiki\Auth\AuthManager\$instance
static AuthManager null $instance
Definition: AuthManager.php:137
DeferredUpdates\addUpdate
static addUpdate(DeferrableUpdate $update, $stage=self::POSTSEND)
Add an update to the pending update queue for execution at the appropriate time.
Definition: DeferredUpdates.php:119
MediaWiki\Auth\PrimaryAuthenticationProvider\TYPE_LINK
const TYPE_LINK
Provider can link to existing accounts elsewhere.
Definition: PrimaryAuthenticationProvider.php:79
MediaWiki\Auth\CreatedAccountAuthenticationRequest
Returned from account creation to allow for logging into the created account.
Definition: CreatedAccountAuthenticationRequest.php:30
MediaWiki\Auth\AuthManager\beginAuthentication
beginAuthentication(array $reqs, $returnToUrl)
Start an authentication flow.
Definition: AuthManager.php:328
User\newFromName
static newFromName( $name, $validate='valid')
Definition: User.php:592
wfMessage
wfMessage( $key,... $params)
This is the function for getting translated interface messages.
Definition: GlobalFunctions.php:1182
User\getUserPage
getUserPage()
Get this user's personal page title.
Definition: User.php:3611
MediaWiki\Auth\AuthManager\userExists
userExists( $username, $flags=User::READ_NORMAL)
Determine whether a username exists.
Definition: AuthManager.php:2215
SpecialPage\getTitleFor
static getTitleFor( $name, $subpage=false, $fragment='')
Get a localised Title object for a specified special page name If you don't need a full Title object,...
Definition: SpecialPage.php:107
MediaWiki\Auth\RememberMeAuthenticationRequest\NEVER_REMEMBER
const NEVER_REMEMBER
Indicates that the user will never be rememberd.
Definition: RememberMeAuthenticationRequest.php:58
MediaWiki\Auth\AuthManager\$allAuthenticationProviders
AuthenticationProvider[] $allAuthenticationProviders
Definition: AuthManager.php:155
MediaWiki\Auth\AuthManager\AUTOCREATE_SOURCE_MAINT
const AUTOCREATE_SOURCE_MAINT
Auto-creation is due to a Maintenance script.
Definition: AuthManager.php:134
MediaWiki\Auth\AuthManager\getPreAuthenticationProviders
getPreAuthenticationProviders()
Get the list of PreAuthenticationProviders.
Definition: AuthManager.php:2382
MediaWiki\Auth\AuthManager\getAuthenticationSessionData
getAuthenticationSessionData( $key, $default=null)
Fetch authentication data from the current session.
Definition: AuthManager.php:2307
$res
$res
Definition: testCompression.php:57
MediaWiki\Auth\AuthManager\ACTION_LOGIN_CONTINUE
const ACTION_LOGIN_CONTINUE
Continue a login process that was interrupted by the need for user input or communication with an ext...
Definition: AuthManager.php:103
MediaWiki\Auth\AuthManager\$blockManager
BlockManager $blockManager
Definition: AuthManager.php:179
MediaWiki\Permissions\Authority\getUser
getUser()
Returns the performer of the actions associated with this authority.
MediaWiki\User\UserIdentity
Interface for objects representing user identity.
Definition: UserIdentity.php:39
MediaWiki\Auth\AuthManager\SEC_FAIL
const SEC_FAIL
Security-sensitive should not be performed.
Definition: AuthManager.php:128
MediaWiki\Auth\AuthManager\getAuthenticationRequests
getAuthenticationRequests( $action, UserIdentity $user=null)
Return the applicable list of AuthenticationRequests.
Definition: AuthManager.php:2064
MediaWiki\Auth\AuthenticationRequest\getRequestByClass
static getRequestByClass(array $reqs, $class, $allowSubclasses=false)
Select a request by class name.
Definition: AuthenticationRequest.php:272
MediaWiki\Auth\AuthManager\$hookContainer
HookContainer $hookContainer
Definition: AuthManager.php:170
Status
Generic operation result class Has warning/error list, boolean status and arbitrary value.
Definition: Status.php:44
MediaWiki\Auth\AuthManager\removeAuthenticationSessionData
removeAuthenticationSessionData( $key)
Remove authentication data.
Definition: AuthManager.php:2321
MediaWiki\MediaWikiServices\getInstance
static getInstance()
Returns the global default instance of the top level service locator.
Definition: MediaWikiServices.php:241
Config
Interface for configuration instances.
Definition: Config.php:30
MediaWiki\Auth\AuthenticationRequest\getUsernameFromRequests
static getUsernameFromRequests(array $reqs)
Get the username from the set of requests.
Definition: AuthenticationRequest.php:292
User\addToDatabase
addToDatabase()
Add this existing user object to the database.
Definition: User.php:3451
MediaWiki\Auth\AuthenticationRequest\PRIMARY_REQUIRED
const PRIMARY_REQUIRED
Indicates that the request is required by a primary authentication provider.
Definition: AuthenticationRequest.php:53
MediaWiki\Auth\AuthenticationResponse\UI
const UI
Indicates that the authentication needs further user input of some sort.
Definition: AuthenticationResponse.php:55
MediaWiki\Auth\AuthManager\getAuthenticationProvider
getAuthenticationProvider( $id)
Get a provider by ID.
Definition: AuthManager.php:2255
MediaWiki\Auth\AuthManager\ACTION_LINK_CONTINUE
const ACTION_LINK_CONTINUE
Continue a user linking process that was interrupted by the need for user input or communication with...
Definition: AuthManager.php:115
MediaWiki\Auth\CreationReasonAuthenticationRequest
Authentication request for the reason given for account creation.
Definition: CreationReasonAuthenticationRequest.php:10
wfDeprecated
wfDeprecated( $function, $version=false, $component=false, $callerOffset=2)
Logs a warning that $function is deprecated.
Definition: GlobalFunctions.php:996
MediaWiki\Auth\AuthManager\canCreateAccounts
canCreateAccounts()
Determine whether accounts can be created.
Definition: AuthManager.php:981
MediaWiki\Watchlist\WatchlistManager
WatchlistManager service.
Definition: WatchlistManager.php:52
MediaWiki\Auth\AuthenticationResponse\REDIRECT
const REDIRECT
Indicates that the authentication needs to be redirected to a third party to proceed.
Definition: AuthenticationResponse.php:58
MediaWiki\Auth\AuthManager\$readOnlyMode
ReadOnlyMode $readOnlyMode
Definition: AuthManager.php:176
MediaWiki\Auth\PreAuthenticationProvider
A pre-authentication provider can prevent authentication early on.
Definition: PreAuthenticationProvider.php:44
Status\wrap
static wrap( $sv)
Succinct helper method to wrap a StatusValue.
Definition: Status.php:62
MediaWiki\Auth\CreateFromLoginAuthenticationRequest
This transfers state between the login and account creation flows.
Definition: CreateFromLoginAuthenticationRequest.php:35
MediaWiki\Auth\AuthManager\getConfiguration
getConfiguration()
Definition: AuthManager.php:2374
$args
if( $line===false) $args
Definition: mcc.php:124
MediaWiki\Auth\AuthenticationResponse\ABSTAIN
const ABSTAIN
Indicates that the authentication provider does not handle this request.
Definition: AuthenticationResponse.php:52
MediaWiki\Auth\AuthManager\checkAccountCreatePermissions
checkAccountCreatePermissions(Authority $creator)
Basic permissions checks on whether a user can create accounts.
Definition: AuthManager.php:1048
MediaWiki\Auth\AuthManager\canAuthenticateNow
canAuthenticateNow()
Indicate whether user authentication is possible.
Definition: AuthManager.php:306
MediaWiki\Auth\AuthManager\setAuthenticationSessionData
setAuthenticationSessionData( $key, $data)
Store authentication in the current session.
Definition: AuthManager.php:2290
SiteStatsUpdate\factory
static factory(array $deltas)
Definition: SiteStatsUpdate.php:71
MediaWiki\Auth\AuthManager\getAuthenticationRequestsInternal
getAuthenticationRequestsInternal( $providerAction, array $options, array $providers, UserIdentity $user=null)
Internal request lookup for self::getAuthenticationRequests.
Definition: AuthManager.php:2134
MediaWiki\Permissions\Authority\authorizeWrite
authorizeWrite(string $action, PageIdentity $target, PermissionStatus $status=null)
Authorize write access.
MediaWiki\Auth\SecondaryAuthenticationProvider
A secondary provider mostly acts when the submitted authentication data has already been associated t...
Definition: SecondaryAuthenticationProvider.php:52
User\getId
getId( $wikiId=self::LOCAL)
Get the user's ID.
Definition: User.php:2049
MediaWiki\Auth\UsernameAuthenticationRequest
AuthenticationRequest to ensure something with a username is present.
Definition: UsernameAuthenticationRequest.php:30
User\setName
setName( $str)
Set the user name.
Definition: User.php:2110
MediaWiki\Auth\AuthManager\setDefaultUserOptions
setDefaultUserOptions(User $user, $useContextLang)
Definition: AuthManager.php:2450
MediaWiki\Auth\UserDataAuthenticationRequest
This represents additional user data requested on the account creation form.
Definition: UserDataAuthenticationRequest.php:35
MediaWiki\Auth\AuthenticationResponse\FAIL
const FAIL
Indicates that the authentication failed.
Definition: AuthenticationResponse.php:42
MediaWiki\Auth\AuthManager\resetCache
static resetCache()
Reset the internal caching for unit testing.
Definition: AuthManager.php:2491
User\saveSettings
saveSettings()
Save this user's settings into the database.
Definition: User.php:3253
MediaWiki\Auth\AuthManager\$request
WebRequest $request
Definition: AuthManager.php:140
MediaWiki\Auth\AuthManager\beginAccountLink
beginAccountLink(User $user, array $reqs, $returnToUrl)
Start an account linking flow.
Definition: AuthManager.php:1844
MediaWiki\Auth\AuthManager\normalizeUsername
normalizeUsername( $username)
Provide normalized versions of the username for security checks.
Definition: AuthManager.php:874
MediaWiki\Auth\AuthManager\canLinkAccounts
canLinkAccounts()
Determine whether accounts can be linked.
Definition: AuthManager.php:1826
MediaWiki\Permissions\Authority
This interface represents the authority associated the current execution context, such as a web reque...
Definition: Authority.php:35
MediaWiki\Auth\AuthManager\ACTION_CREATE_CONTINUE
const ACTION_CREATE_CONTINUE
Continue a user creation process that was interrupted by the need for user input or communication wit...
Definition: AuthManager.php:109
MediaWiki\Auth\AuthManager\ACTION_CREATE
const ACTION_CREATE
Create a new user.
Definition: AuthManager.php:105
MediaWiki\Auth\AuthManager\$watchlistManager
WatchlistManager $watchlistManager
Definition: AuthManager.php:185
MediaWiki\Auth\AuthManager\$logger
LoggerInterface $logger
Definition: AuthManager.php:149
MediaWiki\Auth\AuthManager\$primaryAuthenticationProviders
PrimaryAuthenticationProvider[] $primaryAuthenticationProviders
Definition: AuthManager.php:161
StatusValue\newGood
static newGood( $value=null)
Factory function for good results.
Definition: StatusValue.php:82
SpecialPage
Parent class for all special pages.
Definition: SpecialPage.php:43
MediaWiki\Auth\AuthManager\continueAccountCreation
continueAccountCreation(array $reqs)
Continue an account creation flow.
Definition: AuthManager.php:1191
BotPassword\invalidateAllPasswordsForUser
static invalidateAllPasswordsForUser( $username)
Invalidate all passwords for a user, by name.
Definition: BotPassword.php:386
MediaWiki\Auth\AuthManager\$secondaryAuthenticationProviders
SecondaryAuthenticationProvider[] $secondaryAuthenticationProviders
Definition: AuthManager.php:164
MediaWiki\Auth\AuthManager\forcePrimaryAuthenticationProviders
forcePrimaryAuthenticationProviders(array $providers, $why)
Force certain PrimaryAuthenticationProviders.
Definition: AuthManager.php:253
MediaWiki\Auth\AuthManager\getHookRunner
getHookRunner()
Definition: AuthManager.php:2511
MediaWiki\Auth\AuthManager\ACTION_CHANGE
const ACTION_CHANGE
Change a user's credentials.
Definition: AuthManager.php:117
MediaWiki\Auth\AuthManager\__construct
__construct(WebRequest $request, Config $config, ObjectFactory $objectFactory, HookContainer $hookContainer, ReadOnlyMode $readOnlyMode, UserNameUtils $userNameUtils, BlockManager $blockManager, BlockErrorFormatter $blockErrorFormatter, WatchlistManager $watchlistManager)
Definition: AuthManager.php:209
MediaWiki\Auth\AuthManager\beginAccountCreation
beginAccountCreation(Authority $creator, array $reqs, $returnToUrl)
Start an account creation flow.
Definition: AuthManager.php:1090
MediaWiki\Auth\AuthManager\callMethodOnProviders
callMethodOnProviders( $which, $method, array $args)
Definition: AuthManager.php:2470
MediaWiki\Auth\AuthManager\ACTION_LINK
const ACTION_LINK
Link an existing user to a third-party account.
Definition: AuthManager.php:111
MediaWiki\Auth\AuthManager\allowsPropertyChange
allowsPropertyChange( $property)
Determine whether a user property should be allowed to be changed.
Definition: AuthManager.php:2236
MediaWiki\Auth\RememberMeAuthenticationRequest
This is an authentication request added by AuthManager to show a "remember me" checkbox.
Definition: RememberMeAuthenticationRequest.php:35
RequestContext\getMain
static getMain()
Get the RequestContext object associated with the main request.
Definition: RequestContext.php:483
MediaWiki\Auth\AuthManager\securitySensitiveOperationStatus
securitySensitiveOperationStatus( $operation)
Whether security-sensitive operations should proceed.
Definition: AuthManager.php:774
MediaWiki\Auth\AuthManager
This serves as the entry point to the authentication system.
Definition: AuthManager.php:97
WebRequest
The WebRequest class encapsulates getting at data passed in the URL or via a POSTed form stripping il...
Definition: WebRequest.php:42
MediaWiki\Permissions\PermissionStatus
A StatusValue for permission errors.
Definition: PermissionStatus.php:34
MediaWiki\Auth\AuthManager\$hookRunner
HookRunner $hookRunner
Definition: AuthManager.php:173
MediaWiki\Auth\AuthManager\ACTION_REMOVE
const ACTION_REMOVE
Remove a user's credentials.
Definition: AuthManager.php:119
MediaWiki\Auth\AuthManager\SEC_OK
const SEC_OK
Security-sensitive operations are ok.
Definition: AuthManager.php:124
User\setId
setId( $v)
Set the user and reload all fields according to a given ID.
Definition: User.php:2073
MediaWiki\$action
string $action
Cache what action this request is.
Definition: MediaWiki.php:45
MediaWiki\Auth\AuthManager\$objectFactory
ObjectFactory $objectFactory
Definition: AuthManager.php:146
$cache
$cache
Definition: mcc.php:33
MediaWiki\Auth\AuthManager\singleton
static singleton()
Get the global AuthManager.
Definition: AuthManager.php:193
User\setToken
setToken( $token=false)
Set the random token (used for persistent authentication) Called from loadDefaults() among other plac...
Definition: User.php:2383
User\idFromName
static idFromName( $name, $flags=self::READ_NORMAL)
Get database id given a user name.
Definition: User.php:923
MediaWiki\User\UserNameUtils
UserNameUtils service.
Definition: UserNameUtils.php:42
MediaWiki\Auth\AuthenticationResponse\newFail
static newFail(Message $msg)
Definition: AuthenticationResponse.php:146
MediaWiki\Auth\AuthManager\setSessionDataForUser
setSessionDataForUser( $user, $remember=null)
Log the user in.
Definition: AuthManager.php:2425
MediaWiki\Auth\AuthManager\$createdAccountAuthenticationRequests
CreatedAccountAuthenticationRequest[] $createdAccountAuthenticationRequests
Definition: AuthManager.php:167
MediaWiki\Auth\AuthManager\$config
Config $config
Definition: AuthManager.php:143
MediaWiki\Auth\AuthManager\canCreateAccount
canCreateAccount( $username, $options=[])
Determine whether a particular account can be created.
Definition: AuthManager.php:1000
$source
$source
Definition: mwdoc-filter.php:34
MediaWiki\Auth\AuthManager\ACTION_LOGIN
const ACTION_LOGIN
Log in with an existing (not necessarily local) user.
Definition: AuthManager.php:99
MediaWiki\Auth\AbstractPrimaryAuthenticationProvider
A base class that implements some of the boilerplate for a PrimaryAuthenticationProvider.
Definition: AbstractPrimaryAuthenticationProvider.php:33
MediaWiki\HookContainer\HookContainer
HookContainer class.
Definition: HookContainer.php:45
MediaWiki\Auth\AuthManager\getSecondaryAuthenticationProviders
getSecondaryAuthenticationProviders()
Get the list of SecondaryAuthenticationProviders.
Definition: AuthManager.php:2410
MediaWiki\HookContainer\HookRunner
This class provides an implementation of the core hook interfaces, forwarding hook calls to HookConta...
Definition: HookRunner.php:558
MediaWiki\Auth\AuthManager\userCanAuthenticate
userCanAuthenticate( $username)
Determine whether a username can authenticate.
Definition: AuthManager.php:851
MediaWiki\Auth\AuthenticationResponse\PASS
const PASS
Indicates that the authentication succeeded.
Definition: AuthenticationResponse.php:39
MediaWiki\Auth\AuthManager\allowsAuthenticationDataChange
allowsAuthenticationDataChange(AuthenticationRequest $req, $checkData=true)
Validate a change of authentication data (e.g.
Definition: AuthManager.php:914
MediaWiki\Auth\AuthenticationResponse\newRestart
static newRestart(Message $msg)
Definition: AuthenticationResponse.php:159
User
The User object encapsulates all of the user-specific settings (user_id, name, rights,...
Definition: User.php:67
MediaWiki\Auth\PrimaryAuthenticationProvider
A primary authentication provider is responsible for associating the submitted authentication data wi...
Definition: PrimaryAuthenticationProvider.php:75
DeferredUpdates\addCallableUpdate
static addCallableUpdate( $callable, $stage=self::POSTSEND, $dbw=null)
Add an update to the pending update queue that invokes the specified callback when run.
Definition: DeferredUpdates.php:145
User\setOption
setOption( $oname, $val)
Set the given option for a user.
Definition: User.php:2589
MediaWiki\Auth
Definition: AbstractAuthenticationProvider.php:22
MediaWiki\Auth\AuthManager\setLogger
setLogger(LoggerInterface $logger)
Definition: AuthManager.php:236
MediaWiki\Auth\AuthManager\providerArrayFromSpecs
providerArrayFromSpecs( $class, array $specs)
Create an array of AuthenticationProviders from an array of ObjectFactory specs.
Definition: AuthManager.php:2340
MediaWiki\Auth\AuthenticationResponse\newPass
static newPass( $username=null)
Definition: AuthenticationResponse.php:134
User\getName
getName()
Get the user name, or the IP of an anonymous user.
Definition: User.php:2082
MediaWiki\Auth\AuthenticationRequest
This is a value object for authentication requests.
Definition: AuthenticationRequest.php:38
MediaWiki\Auth\AuthenticationRequest\REQUIRED
const REQUIRED
Indicates that the request is required for authentication to proceed.
Definition: AuthenticationRequest.php:47
MediaWiki\Auth\AuthManager\$blockErrorFormatter
BlockErrorFormatter $blockErrorFormatter
Definition: AuthManager.php:182
MediaWiki\Auth\AuthenticationProvider
An AuthenticationProvider is used by AuthManager when authenticating users.
Definition: AuthenticationProvider.php:39