MediaWiki  master
AuthManager.php
Go to the documentation of this file.
1 <?php
24 namespace MediaWiki\Auth;
25 
26 use Config;
27 use Language;
41 use Psr\Log\LoggerAwareInterface;
42 use Psr\Log\LoggerInterface;
43 use Psr\Log\NullLogger;
44 use ReadOnlyMode;
45 use SpecialPage;
46 use Status;
47 use StatusValue;
48 use User;
49 use WebRequest;
50 use Wikimedia\ObjectFactory;
52 use Wikimedia\ScopedCallback;
53 
102 class AuthManager implements LoggerAwareInterface {
104  public const ACTION_LOGIN = 'login';
108  public const ACTION_LOGIN_CONTINUE = 'login-continue';
110  public const ACTION_CREATE = 'create';
114  public const ACTION_CREATE_CONTINUE = 'create-continue';
116  public const ACTION_LINK = 'link';
120  public const ACTION_LINK_CONTINUE = 'link-continue';
122  public const ACTION_CHANGE = 'change';
124  public const ACTION_REMOVE = 'remove';
126  public const ACTION_UNLINK = 'unlink';
127 
129  public const SEC_OK = 'ok';
131  public const SEC_REAUTH = 'reauth';
133  public const SEC_FAIL = 'fail';
134 
136  public const AUTOCREATE_SOURCE_SESSION = \MediaWiki\Session\SessionManager::class;
137 
139  public const AUTOCREATE_SOURCE_MAINT = '::Maintenance::';
140 
142  private $request;
143 
145  private $config;
146 
148  private $objectFactory;
149 
151  private $logger;
152 
154  private $userNameUtils;
155 
158 
161 
164 
167 
170 
172  private $hookContainer;
173 
175  private $hookRunner;
176 
178  private $readOnlyMode;
179 
181  private $blockManager;
182 
185 
187  private $loadBalancer;
188 
191 
194 
197 
199  private $userFactory;
200 
203 
206 
224  public function __construct(
226  Config $config,
227  ObjectFactory $objectFactory,
240  ) {
241  $this->request = $request;
242  $this->config = $config;
243  $this->objectFactory = $objectFactory;
244  $this->hookContainer = $hookContainer;
245  $this->hookRunner = new HookRunner( $hookContainer );
246  $this->setLogger( new NullLogger() );
247  $this->readOnlyMode = $readOnlyMode;
248  $this->userNameUtils = $userNameUtils;
249  $this->blockManager = $blockManager;
250  $this->watchlistManager = $watchlistManager;
251  $this->loadBalancer = $loadBalancer;
252  $this->contentLanguage = $contentLanguage;
253  $this->languageConverterFactory = $languageConverterFactory;
254  $this->botPasswordStore = $botPasswordStore;
255  $this->userFactory = $userFactory;
256  $this->userIdentityLookup = $userIdentityLookup;
257  $this->userOptionsManager = $userOptionsManager;
258  }
259 
263  public function setLogger( LoggerInterface $logger ) {
264  $this->logger = $logger;
265  }
266 
270  public function getRequest() {
271  return $this->request;
272  }
273 
280  public function forcePrimaryAuthenticationProviders( array $providers, $why ) {
281  $this->logger->warning( "Overriding AuthManager primary authn because $why" );
282 
283  if ( $this->primaryAuthenticationProviders !== null ) {
284  $this->logger->warning(
285  'PrimaryAuthenticationProviders have already been accessed! I hope nothing breaks.'
286  );
287 
288  $this->allAuthenticationProviders = array_diff_key(
289  $this->allAuthenticationProviders,
290  $this->primaryAuthenticationProviders
291  );
292  $session = $this->request->getSession();
293  $session->remove( 'AuthManager::authnState' );
294  $session->remove( 'AuthManager::accountCreationState' );
295  $session->remove( 'AuthManager::accountLinkState' );
296  $this->createdAccountAuthenticationRequests = [];
297  }
298 
299  $this->primaryAuthenticationProviders = [];
300  foreach ( $providers as $provider ) {
301  if ( !$provider instanceof AbstractPrimaryAuthenticationProvider ) {
302  throw new \RuntimeException(
303  'Expected instance of MediaWiki\\Auth\\AbstractPrimaryAuthenticationProvider, got ' .
304  get_class( $provider )
305  );
306  }
307  $provider->init( $this->logger, $this, $this->hookContainer, $this->config, $this->userNameUtils );
308  $id = $provider->getUniqueId();
309  if ( isset( $this->allAuthenticationProviders[$id] ) ) {
310  throw new \RuntimeException(
311  "Duplicate specifications for id $id (classes " .
312  get_class( $provider ) . ' and ' .
313  get_class( $this->allAuthenticationProviders[$id] ) . ')'
314  );
315  }
316  $this->allAuthenticationProviders[$id] = $provider;
317  $this->primaryAuthenticationProviders[$id] = $provider;
318  }
319  }
320 
321  /***************************************************************************/
322  // region Authentication
333  public function canAuthenticateNow() {
334  return $this->request->getSession()->canSetUser();
335  }
336 
355  public function beginAuthentication( array $reqs, $returnToUrl ) {
356  $session = $this->request->getSession();
357  if ( !$session->canSetUser() ) {
358  // Caller should have called canAuthenticateNow()
359  $session->remove( 'AuthManager::authnState' );
360  throw new \LogicException( 'Authentication is not possible now' );
361  }
362 
363  $guessUserName = null;
364  foreach ( $reqs as $req ) {
365  $req->returnToUrl = $returnToUrl;
366  // @codeCoverageIgnoreStart
367  if ( $req->username !== null && $req->username !== '' ) {
368  if ( $guessUserName === null ) {
369  $guessUserName = $req->username;
370  } elseif ( $guessUserName !== $req->username ) {
371  $guessUserName = null;
372  break;
373  }
374  }
375  // @codeCoverageIgnoreEnd
376  }
377 
378  // Check for special-case login of a just-created account
380  $reqs, CreatedAccountAuthenticationRequest::class
381  );
382  if ( $req ) {
383  if ( !in_array( $req, $this->createdAccountAuthenticationRequests, true ) ) {
384  throw new \LogicException(
385  'CreatedAccountAuthenticationRequests are only valid on ' .
386  'the same AuthManager that created the account'
387  );
388  }
389 
390  $user = $this->userFactory->newFromName( (string)$req->username );
391  // @codeCoverageIgnoreStart
392  if ( !$user ) {
393  throw new \UnexpectedValueException(
394  "CreatedAccountAuthenticationRequest had invalid username \"{$req->username}\""
395  );
396  } elseif ( $user->getId() != $req->id ) {
397  throw new \UnexpectedValueException(
398  "ID for \"{$req->username}\" was {$user->getId()}, expected {$req->id}"
399  );
400  }
401  // @codeCoverageIgnoreEnd
402 
403  $this->logger->info( 'Logging in {user} after account creation', [
404  'user' => $user->getName(),
405  ] );
406  $ret = AuthenticationResponse::newPass( $user->getName() );
407  $this->setSessionDataForUser( $user );
408  $this->callMethodOnProviders( 7, 'postAuthentication', [ $user, $ret ] );
409  $session->remove( 'AuthManager::authnState' );
410  $this->getHookRunner()->onAuthManagerLoginAuthenticateAudit(
411  $ret, $user, $user->getName(), [] );
412  return $ret;
413  }
414 
415  $this->removeAuthenticationSessionData( null );
416 
417  foreach ( $this->getPreAuthenticationProviders() as $provider ) {
418  $status = $provider->testForAuthentication( $reqs );
419  if ( !$status->isGood() ) {
420  $this->logger->debug( 'Login failed in pre-authentication by ' . $provider->getUniqueId() );
422  Status::wrap( $status )->getMessage()
423  );
424  $this->callMethodOnProviders( 7, 'postAuthentication',
425  [ $this->userFactory->newFromName( (string)$guessUserName ), $ret ]
426  );
427  $this->getHookRunner()->onAuthManagerLoginAuthenticateAudit( $ret, null, $guessUserName, [] );
428  return $ret;
429  }
430  }
431 
432  $state = [
433  'reqs' => $reqs,
434  'returnToUrl' => $returnToUrl,
435  'guessUserName' => $guessUserName,
436  'primary' => null,
437  'primaryResponse' => null,
438  'secondary' => [],
439  'maybeLink' => [],
440  'continueRequests' => [],
441  ];
442 
443  // Preserve state from a previous failed login
445  $reqs, CreateFromLoginAuthenticationRequest::class
446  );
447  if ( $req ) {
448  $state['maybeLink'] = $req->maybeLink;
449  }
450 
451  $session = $this->request->getSession();
452  $session->setSecret( 'AuthManager::authnState', $state );
453  $session->persist();
454 
455  return $this->continueAuthentication( $reqs );
456  }
457 
480  public function continueAuthentication( array $reqs ) {
481  $session = $this->request->getSession();
482  try {
483  if ( !$session->canSetUser() ) {
484  // Caller should have called canAuthenticateNow()
485  // @codeCoverageIgnoreStart
486  throw new \LogicException( 'Authentication is not possible now' );
487  // @codeCoverageIgnoreEnd
488  }
489 
490  $state = $session->getSecret( 'AuthManager::authnState' );
491  if ( !is_array( $state ) ) {
493  wfMessage( 'authmanager-authn-not-in-progress' )
494  );
495  }
496  $state['continueRequests'] = [];
497 
498  $guessUserName = $state['guessUserName'];
499 
500  foreach ( $reqs as $req ) {
501  $req->returnToUrl = $state['returnToUrl'];
502  }
503 
504  // Step 1: Choose an primary authentication provider, and call it until it succeeds.
505 
506  if ( $state['primary'] === null ) {
507  // We haven't picked a PrimaryAuthenticationProvider yet
508  // @codeCoverageIgnoreStart
509  $guessUserName = null;
510  foreach ( $reqs as $req ) {
511  if ( $req->username !== null && $req->username !== '' ) {
512  if ( $guessUserName === null ) {
513  $guessUserName = $req->username;
514  } elseif ( $guessUserName !== $req->username ) {
515  $guessUserName = null;
516  break;
517  }
518  }
519  }
520  $state['guessUserName'] = $guessUserName;
521  // @codeCoverageIgnoreEnd
522  $state['reqs'] = $reqs;
523 
524  foreach ( $this->getPrimaryAuthenticationProviders() as $id => $provider ) {
525  $res = $provider->beginPrimaryAuthentication( $reqs );
526  switch ( $res->status ) {
528  $state['primary'] = $id;
529  $state['primaryResponse'] = $res;
530  $this->logger->debug( "Primary login with $id succeeded" );
531  break 2;
533  $this->logger->debug( "Login failed in primary authentication by $id" );
534  if ( $res->createRequest || $state['maybeLink'] ) {
535  $res->createRequest = new CreateFromLoginAuthenticationRequest(
536  $res->createRequest, $state['maybeLink']
537  );
538  }
539  $this->callMethodOnProviders(
540  7,
541  'postAuthentication',
542  [
543  $this->userFactory->newFromName( (string)$guessUserName ),
544  $res
545  ]
546  );
547  $session->remove( 'AuthManager::authnState' );
548  $this->getHookRunner()->onAuthManagerLoginAuthenticateAudit(
549  $res, null, $guessUserName, [] );
550  return $res;
552  // Continue loop
553  break;
556  $this->logger->debug( "Primary login with $id returned $res->status" );
557  $this->fillRequests( $res->neededRequests, self::ACTION_LOGIN, $guessUserName );
558  $state['primary'] = $id;
559  $state['continueRequests'] = $res->neededRequests;
560  $session->setSecret( 'AuthManager::authnState', $state );
561  return $res;
562 
563  // @codeCoverageIgnoreStart
564  default:
565  throw new \DomainException(
566  get_class( $provider ) . "::beginPrimaryAuthentication() returned $res->status"
567  );
568  // @codeCoverageIgnoreEnd
569  }
570  }
571  if ( $state['primary'] === null ) {
572  $this->logger->debug( 'Login failed in primary authentication because no provider accepted' );
574  wfMessage( 'authmanager-authn-no-primary' )
575  );
576  $this->callMethodOnProviders( 7, 'postAuthentication',
577  [ $this->userFactory->newFromName( (string)$guessUserName ), $ret ]
578  );
579  $session->remove( 'AuthManager::authnState' );
580  return $ret;
581  }
582  } elseif ( $state['primaryResponse'] === null ) {
583  $provider = $this->getAuthenticationProvider( $state['primary'] );
584  if ( !$provider instanceof PrimaryAuthenticationProvider ) {
585  // Configuration changed? Force them to start over.
586  // @codeCoverageIgnoreStart
588  wfMessage( 'authmanager-authn-not-in-progress' )
589  );
590  $this->callMethodOnProviders( 7, 'postAuthentication',
591  [ $this->userFactory->newFromName( (string)$guessUserName ), $ret ]
592  );
593  $session->remove( 'AuthManager::authnState' );
594  return $ret;
595  // @codeCoverageIgnoreEnd
596  }
597  $id = $provider->getUniqueId();
598  $res = $provider->continuePrimaryAuthentication( $reqs );
599  switch ( $res->status ) {
601  $state['primaryResponse'] = $res;
602  $this->logger->debug( "Primary login with $id succeeded" );
603  break;
605  $this->logger->debug( "Login failed in primary authentication by $id" );
606  if ( $res->createRequest || $state['maybeLink'] ) {
607  $res->createRequest = new CreateFromLoginAuthenticationRequest(
608  $res->createRequest, $state['maybeLink']
609  );
610  }
611  $this->callMethodOnProviders( 7, 'postAuthentication',
612  [ $this->userFactory->newFromName( (string)$guessUserName ), $res ]
613  );
614  $session->remove( 'AuthManager::authnState' );
615  $this->getHookRunner()->onAuthManagerLoginAuthenticateAudit(
616  $res, null, $guessUserName, [] );
617  return $res;
620  $this->logger->debug( "Primary login with $id returned $res->status" );
621  $this->fillRequests( $res->neededRequests, self::ACTION_LOGIN, $guessUserName );
622  $state['continueRequests'] = $res->neededRequests;
623  $session->setSecret( 'AuthManager::authnState', $state );
624  return $res;
625  default:
626  throw new \DomainException(
627  get_class( $provider ) . "::continuePrimaryAuthentication() returned $res->status"
628  );
629  }
630  }
631 
632  $res = $state['primaryResponse'];
633  if ( $res->username === null ) {
634  $provider = $this->getAuthenticationProvider( $state['primary'] );
635  if ( !$provider instanceof PrimaryAuthenticationProvider ) {
636  // Configuration changed? Force them to start over.
637  // @codeCoverageIgnoreStart
639  wfMessage( 'authmanager-authn-not-in-progress' )
640  );
641  $this->callMethodOnProviders( 7, 'postAuthentication',
642  [ $this->userFactory->newFromName( (string)$guessUserName ), $ret ]
643  );
644  $session->remove( 'AuthManager::authnState' );
645  return $ret;
646  // @codeCoverageIgnoreEnd
647  }
648 
649  if ( $provider->accountCreationType() === PrimaryAuthenticationProvider::TYPE_LINK &&
650  $res->linkRequest &&
651  // don't confuse the user with an incorrect message if linking is disabled
652  $this->getAuthenticationProvider( ConfirmLinkSecondaryAuthenticationProvider::class )
653  ) {
654  $state['maybeLink'][$res->linkRequest->getUniqueId()] = $res->linkRequest;
655  $msg = 'authmanager-authn-no-local-user-link';
656  } else {
657  $msg = 'authmanager-authn-no-local-user';
658  }
659  $this->logger->debug(
660  "Primary login with {$provider->getUniqueId()} succeeded, but returned no user"
661  );
663  $ret->neededRequests = $this->getAuthenticationRequestsInternal(
664  self::ACTION_LOGIN,
665  [],
667  );
668  if ( $res->createRequest || $state['maybeLink'] ) {
669  $ret->createRequest = new CreateFromLoginAuthenticationRequest(
670  $res->createRequest, $state['maybeLink']
671  );
672  $ret->neededRequests[] = $ret->createRequest;
673  }
674  $this->fillRequests( $ret->neededRequests, self::ACTION_LOGIN, null, true );
675  $session->setSecret( 'AuthManager::authnState', [
676  'reqs' => [], // Will be filled in later
677  'primary' => null,
678  'primaryResponse' => null,
679  'secondary' => [],
680  'continueRequests' => $ret->neededRequests,
681  ] + $state );
682  return $ret;
683  }
684 
685  // Step 2: Primary authentication succeeded, create the User object
686  // (and add the user locally if necessary)
687 
688  $user = $this->userFactory->newFromName(
689  (string)$res->username,
690  UserFactory::RIGOR_USABLE
691  );
692  if ( !$user ) {
693  $provider = $this->getAuthenticationProvider( $state['primary'] );
694  throw new \DomainException(
695  get_class( $provider ) . " returned an invalid username: {$res->username}"
696  );
697  }
698  if ( $user->getId() === 0 ) {
699  // User doesn't exist locally. Create it.
700  $this->logger->info( 'Auto-creating {user} on login', [
701  'user' => $user->getName(),
702  ] );
703  $status = $this->autoCreateUser( $user, $state['primary'], false );
704  if ( !$status->isGood() ) {
706  Status::wrap( $status )->getMessage( 'authmanager-authn-autocreate-failed' )
707  );
708  $this->callMethodOnProviders( 7, 'postAuthentication', [ $user, $ret ] );
709  $session->remove( 'AuthManager::authnState' );
710  $this->getHookRunner()->onAuthManagerLoginAuthenticateAudit(
711  $ret, $user, $user->getName(), [] );
712  return $ret;
713  }
714  }
715 
716  // Step 3: Iterate over all the secondary authentication providers.
717 
718  $beginReqs = $state['reqs'];
719 
720  foreach ( $this->getSecondaryAuthenticationProviders() as $id => $provider ) {
721  if ( !isset( $state['secondary'][$id] ) ) {
722  // This provider isn't started yet, so we pass it the set
723  // of reqs from beginAuthentication instead of whatever
724  // might have been used by a previous provider in line.
725  $func = 'beginSecondaryAuthentication';
726  $res = $provider->beginSecondaryAuthentication( $user, $beginReqs );
727  } elseif ( !$state['secondary'][$id] ) {
728  $func = 'continueSecondaryAuthentication';
729  $res = $provider->continueSecondaryAuthentication( $user, $reqs );
730  } else {
731  continue;
732  }
733  switch ( $res->status ) {
735  $this->logger->debug( "Secondary login with $id succeeded" );
736  // fall through
738  $state['secondary'][$id] = true;
739  break;
741  $this->logger->debug( "Login failed in secondary authentication by $id" );
742  $this->callMethodOnProviders( 7, 'postAuthentication', [ $user, $res ] );
743  $session->remove( 'AuthManager::authnState' );
744  $this->getHookRunner()->onAuthManagerLoginAuthenticateAudit(
745  $res, $user, $user->getName(), [] );
746  return $res;
749  $this->logger->debug( "Secondary login with $id returned " . $res->status );
750  $this->fillRequests( $res->neededRequests, self::ACTION_LOGIN, $user->getName() );
751  $state['secondary'][$id] = false;
752  $state['continueRequests'] = $res->neededRequests;
753  $session->setSecret( 'AuthManager::authnState', $state );
754  return $res;
755 
756  // @codeCoverageIgnoreStart
757  default:
758  throw new \DomainException(
759  get_class( $provider ) . "::{$func}() returned $res->status"
760  );
761  // @codeCoverageIgnoreEnd
762  }
763  }
764 
765  // Step 4: Authentication complete! Set the user in the session and
766  // clean up.
767 
768  $this->logger->info( 'Login for {user} succeeded from {clientip}', [
769  'user' => $user->getName(),
770  'clientip' => $this->request->getIP(),
771  ] );
772  $rememberMeConfig = $this->config->get( 'RememberMe' );
773  if ( $rememberMeConfig === RememberMeAuthenticationRequest::ALWAYS_REMEMBER ) {
774  $rememberMe = true;
775  } elseif ( $rememberMeConfig === RememberMeAuthenticationRequest::NEVER_REMEMBER ) {
776  $rememberMe = false;
777  } else {
780  $beginReqs, RememberMeAuthenticationRequest::class
781  );
782  $rememberMe = $req && $req->rememberMe;
783  }
784  $this->setSessionDataForUser( $user, $rememberMe );
785  $ret = AuthenticationResponse::newPass( $user->getName() );
786  $this->callMethodOnProviders( 7, 'postAuthentication', [ $user, $ret ] );
787  $session->remove( 'AuthManager::authnState' );
788  $this->removeAuthenticationSessionData( null );
789  $this->getHookRunner()->onAuthManagerLoginAuthenticateAudit(
790  $ret, $user, $user->getName(), [] );
791  return $ret;
792  } catch ( \Exception $ex ) {
793  $session->remove( 'AuthManager::authnState' );
794  throw $ex;
795  }
796  }
797 
809  public function securitySensitiveOperationStatus( $operation ) {
810  $status = self::SEC_OK;
811 
812  $this->logger->debug( __METHOD__ . ": Checking $operation" );
813 
814  $session = $this->request->getSession();
815  $aId = $session->getUser()->getId();
816  if ( $aId === 0 ) {
817  // User isn't authenticated. DWIM?
818  $status = $this->canAuthenticateNow() ? self::SEC_REAUTH : self::SEC_FAIL;
819  $this->logger->info( __METHOD__ . ": Not logged in! $operation is $status" );
820  return $status;
821  }
822 
823  if ( $session->canSetUser() ) {
824  $id = $session->get( 'AuthManager:lastAuthId' );
825  $last = $session->get( 'AuthManager:lastAuthTimestamp' );
826  if ( $id !== $aId || $last === null ) {
827  $timeSinceLogin = PHP_INT_MAX; // Forever ago
828  } else {
829  $timeSinceLogin = max( 0, time() - $last );
830  }
831 
832  $thresholds = $this->config->get( 'ReauthenticateTime' );
833  if ( isset( $thresholds[$operation] ) ) {
834  $threshold = $thresholds[$operation];
835  } elseif ( isset( $thresholds['default'] ) ) {
836  $threshold = $thresholds['default'];
837  } else {
838  throw new \UnexpectedValueException( '$wgReauthenticateTime lacks a default' );
839  }
840 
841  if ( $threshold >= 0 && $timeSinceLogin > $threshold ) {
842  $status = self::SEC_REAUTH;
843  }
844  } else {
845  $timeSinceLogin = -1;
846 
847  $pass = $this->config->get( 'AllowSecuritySensitiveOperationIfCannotReauthenticate' );
848  if ( isset( $pass[$operation] ) ) {
849  $status = $pass[$operation] ? self::SEC_OK : self::SEC_FAIL;
850  } elseif ( isset( $pass['default'] ) ) {
851  $status = $pass['default'] ? self::SEC_OK : self::SEC_FAIL;
852  } else {
853  throw new \UnexpectedValueException(
854  '$wgAllowSecuritySensitiveOperationIfCannotReauthenticate lacks a default'
855  );
856  }
857  }
858 
859  $this->getHookRunner()->onSecuritySensitiveOperationStatus(
860  $status, $operation, $session, $timeSinceLogin );
861 
862  // If authentication is not possible, downgrade from "REAUTH" to "FAIL".
863  if ( !$this->canAuthenticateNow() && $status === self::SEC_REAUTH ) {
864  $status = self::SEC_FAIL;
865  }
866 
867  $this->logger->info( __METHOD__ . ": $operation is $status for '{user}'",
868  [
869  'user' => $session->getUser()->getName(),
870  'clientip' => $this->getRequest()->getIP(),
871  ]
872  );
873 
874  return $status;
875  }
876 
886  public function userCanAuthenticate( $username ) {
887  foreach ( $this->getPrimaryAuthenticationProviders() as $provider ) {
888  if ( $provider->testUserCanAuthenticate( $username ) ) {
889  return true;
890  }
891  }
892  return false;
893  }
894 
909  public function normalizeUsername( $username ) {
910  $ret = [];
911  foreach ( $this->getPrimaryAuthenticationProviders() as $provider ) {
912  $normalized = $provider->providerNormalizeUsername( $username );
913  if ( $normalized !== null ) {
914  $ret[$normalized] = true;
915  }
916  }
917  return array_keys( $ret );
918  }
919 
920  // endregion -- end of Authentication
921 
922  /***************************************************************************/
923  // region Authentication data changing
933  public function revokeAccessForUser( $username ) {
934  $this->logger->info( 'Revoking access for {user}', [
935  'user' => $username,
936  ] );
937  $this->callMethodOnProviders( 6, 'providerRevokeAccessForUser', [ $username ] );
938  }
939 
949  public function allowsAuthenticationDataChange( AuthenticationRequest $req, $checkData = true ) {
950  $any = false;
951  $providers = $this->getPrimaryAuthenticationProviders() +
953 
954  foreach ( $providers as $provider ) {
955  $status = $provider->providerAllowsAuthenticationDataChange( $req, $checkData );
956  if ( !$status->isGood() ) {
957  // If status is not good because reset email password last attempt was within
958  // $wgPasswordReminderResendTime then return good status with throttled-mailpassword value;
959  // otherwise, return the $status wrapped.
960  return $status->hasMessage( 'throttled-mailpassword' )
961  ? Status::newGood( 'throttled-mailpassword' )
962  : Status::wrap( $status );
963  }
964  $any = $any || $status->value !== 'ignored';
965  }
966  if ( !$any ) {
967  return Status::newGood( 'ignored' )
968  ->warning( 'authmanager-change-not-supported' );
969  }
970  return Status::newGood();
971  }
972 
990  public function changeAuthenticationData( AuthenticationRequest $req, $isAddition = false ) {
991  $this->logger->info( 'Changing authentication data for {user} class {what}', [
992  'user' => is_string( $req->username ) ? $req->username : '<no name>',
993  'what' => get_class( $req ),
994  ] );
995 
996  $this->callMethodOnProviders( 6, 'providerChangeAuthenticationData', [ $req ] );
997 
998  // When the main account's authentication data is changed, invalidate
999  // all BotPasswords too.
1000  if ( !$isAddition ) {
1001  $this->botPasswordStore->invalidateUserPasswords( (string)$req->username );
1002  }
1003  }
1004 
1005  // endregion -- end of Authentication data changing
1006 
1007  /***************************************************************************/
1008  // region Account creation
1015  public function canCreateAccounts() {
1016  foreach ( $this->getPrimaryAuthenticationProviders() as $provider ) {
1017  switch ( $provider->accountCreationType() ) {
1020  return true;
1021  }
1022  }
1023  return false;
1024  }
1025 
1034  public function canCreateAccount( $username, $options = [] ) {
1035  // Back compat
1036  if ( is_int( $options ) ) {
1037  $options = [ 'flags' => $options ];
1038  }
1039  $options += [
1040  'flags' => User::READ_NORMAL,
1041  'creating' => false,
1042  ];
1043  $flags = $options['flags'];
1044 
1045  if ( !$this->canCreateAccounts() ) {
1046  return Status::newFatal( 'authmanager-create-disabled' );
1047  }
1048 
1049  if ( $this->userExists( $username, $flags ) ) {
1050  return Status::newFatal( 'userexists' );
1051  }
1052 
1053  $user = $this->userFactory->newFromName( (string)$username, UserFactory::RIGOR_CREATABLE );
1054  if ( !is_object( $user ) ) {
1055  return Status::newFatal( 'noname' );
1056  } else {
1057  $user->load( $flags ); // Explicitly load with $flags, auto-loading always uses READ_NORMAL
1058  if ( $user->getId() !== 0 ) {
1059  return Status::newFatal( 'userexists' );
1060  }
1061  }
1062 
1063  // Denied by providers?
1064  $providers = $this->getPreAuthenticationProviders() +
1067  foreach ( $providers as $provider ) {
1068  $status = $provider->testUserForCreation( $user, false, $options );
1069  if ( !$status->isGood() ) {
1070  return Status::wrap( $status );
1071  }
1072  }
1073 
1074  return Status::newGood();
1075  }
1076 
1082  public function checkAccountCreatePermissions( Authority $creator ) {
1083  // Wiki is read-only?
1084  if ( $this->readOnlyMode->isReadOnly() ) {
1085  return Status::newFatal( wfMessage( 'readonlytext', $this->readOnlyMode->getReason() ) );
1086  }
1087 
1088  $permStatus = new PermissionStatus();
1089  if ( !$creator->authorizeWrite(
1090  'createaccount',
1091  SpecialPage::getTitleFor( 'CreateAccount' ),
1092  $permStatus
1093  ) ) {
1094  return Status::wrap( $permStatus );
1095  }
1096 
1097  $ip = $this->getRequest()->getIP();
1098  if ( $this->blockManager->isDnsBlacklisted( $ip, true /* check $wgProxyWhitelist */ ) ) {
1099  return Status::newFatal( 'sorbs_create_account_reason' );
1100  }
1101 
1102  return Status::newGood();
1103  }
1104 
1124  public function beginAccountCreation( Authority $creator, array $reqs, $returnToUrl ) {
1125  $session = $this->request->getSession();
1126  if ( !$this->canCreateAccounts() ) {
1127  // Caller should have called canCreateAccounts()
1128  $session->remove( 'AuthManager::accountCreationState' );
1129  throw new \LogicException( 'Account creation is not possible' );
1130  }
1131 
1132  try {
1134  } catch ( \UnexpectedValueException $ex ) {
1135  $username = null;
1136  }
1137  if ( $username === null ) {
1138  $this->logger->debug( __METHOD__ . ': No username provided' );
1139  return AuthenticationResponse::newFail( wfMessage( 'noname' ) );
1140  }
1141 
1142  // Permissions check
1143  $status = $this->checkAccountCreatePermissions( $creator );
1144  if ( !$status->isGood() ) {
1145  $this->logger->debug( __METHOD__ . ': {creator} cannot create users: {reason}', [
1146  'user' => $username,
1147  'creator' => $creator->getUser()->getName(),
1148  'reason' => $status->getWikiText( null, null, 'en' )
1149  ] );
1150  return AuthenticationResponse::newFail( $status->getMessage() );
1151  }
1152 
1153  $status = $this->canCreateAccount(
1154  $username, [ 'flags' => User::READ_LOCKING, 'creating' => true ]
1155  );
1156  if ( !$status->isGood() ) {
1157  $this->logger->debug( __METHOD__ . ': {user} cannot be created: {reason}', [
1158  'user' => $username,
1159  'creator' => $creator->getUser()->getName(),
1160  'reason' => $status->getWikiText( null, null, 'en' )
1161  ] );
1162  return AuthenticationResponse::newFail( $status->getMessage() );
1163  }
1164 
1165  $user = $this->userFactory->newFromName( (string)$username, UserFactory::RIGOR_CREATABLE );
1166  foreach ( $reqs as $req ) {
1167  $req->username = $username;
1168  $req->returnToUrl = $returnToUrl;
1169  if ( $req instanceof UserDataAuthenticationRequest ) {
1170  $status = $req->populateUser( $user );
1171  if ( !$status->isGood() ) {
1172  $status = Status::wrap( $status );
1173  $session->remove( 'AuthManager::accountCreationState' );
1174  $this->logger->debug( __METHOD__ . ': UserData is invalid: {reason}', [
1175  'user' => $user->getName(),
1176  'creator' => $creator->getUser()->getName(),
1177  'reason' => $status->getWikiText( null, null, 'en' ),
1178  ] );
1179  return AuthenticationResponse::newFail( $status->getMessage() );
1180  }
1181  }
1182  }
1183 
1184  $this->removeAuthenticationSessionData( null );
1185 
1186  $state = [
1187  'username' => $username,
1188  'userid' => 0,
1189  'creatorid' => $creator->getUser()->getId(),
1190  'creatorname' => $creator->getUser()->getName(),
1191  'reqs' => $reqs,
1192  'returnToUrl' => $returnToUrl,
1193  'primary' => null,
1194  'primaryResponse' => null,
1195  'secondary' => [],
1196  'continueRequests' => [],
1197  'maybeLink' => [],
1198  'ranPreTests' => false,
1199  ];
1200 
1201  // Special case: converting a login to an account creation
1203  $reqs, CreateFromLoginAuthenticationRequest::class
1204  );
1205  if ( $req ) {
1206  $state['maybeLink'] = $req->maybeLink;
1207 
1208  if ( $req->createRequest ) {
1209  $reqs[] = $req->createRequest;
1210  $state['reqs'][] = $req->createRequest;
1211  }
1212  }
1213 
1214  $session->setSecret( 'AuthManager::accountCreationState', $state );
1215  $session->persist();
1216 
1217  return $this->continueAccountCreation( $reqs );
1218  }
1219 
1225  public function continueAccountCreation( array $reqs ) {
1226  $session = $this->request->getSession();
1227  try {
1228  if ( !$this->canCreateAccounts() ) {
1229  // Caller should have called canCreateAccounts()
1230  $session->remove( 'AuthManager::accountCreationState' );
1231  throw new \LogicException( 'Account creation is not possible' );
1232  }
1233 
1234  $state = $session->getSecret( 'AuthManager::accountCreationState' );
1235  if ( !is_array( $state ) ) {
1237  wfMessage( 'authmanager-create-not-in-progress' )
1238  );
1239  }
1240  $state['continueRequests'] = [];
1241 
1242  // Step 0: Prepare and validate the input
1243 
1244  $user = $this->userFactory->newFromName(
1245  (string)$state['username'],
1246  UserFactory::RIGOR_CREATABLE
1247  );
1248  if ( !is_object( $user ) ) {
1249  $session->remove( 'AuthManager::accountCreationState' );
1250  $this->logger->debug( __METHOD__ . ': Invalid username', [
1251  'user' => $state['username'],
1252  ] );
1253  return AuthenticationResponse::newFail( wfMessage( 'noname' ) );
1254  }
1255 
1256  if ( $state['creatorid'] ) {
1257  $creator = $this->userFactory->newFromId( (int)$state['creatorid'] );
1258  } else {
1259  $creator = $this->userFactory->newAnonymous();
1260  $creator->setName( $state['creatorname'] );
1261  }
1262 
1263  // Avoid account creation races on double submissions
1265  $lock = $cache->getScopedLock( $cache->makeGlobalKey( 'account', md5( $user->getName() ) ) );
1266  if ( !$lock ) {
1267  // Don't clear AuthManager::accountCreationState for this code
1268  // path because the process that won the race owns it.
1269  $this->logger->debug( __METHOD__ . ': Could not acquire account creation lock', [
1270  'user' => $user->getName(),
1271  'creator' => $creator->getName(),
1272  ] );
1273  return AuthenticationResponse::newFail( wfMessage( 'usernameinprogress' ) );
1274  }
1275 
1276  // Permissions check
1277  $status = $this->checkAccountCreatePermissions( $creator );
1278  if ( !$status->isGood() ) {
1279  $this->logger->debug( __METHOD__ . ': {creator} cannot create users: {reason}', [
1280  'user' => $user->getName(),
1281  'creator' => $creator->getName(),
1282  'reason' => $status->getWikiText( null, null, 'en' )
1283  ] );
1284  $ret = AuthenticationResponse::newFail( $status->getMessage() );
1285  $this->callMethodOnProviders( 7, 'postAccountCreation', [ $user, $creator, $ret ] );
1286  $session->remove( 'AuthManager::accountCreationState' );
1287  return $ret;
1288  }
1289 
1290  // Load from primary DB for existence check
1291  $user->load( User::READ_LOCKING );
1292 
1293  if ( $state['userid'] === 0 ) {
1294  if ( $user->getId() !== 0 ) {
1295  $this->logger->debug( __METHOD__ . ': User exists locally', [
1296  'user' => $user->getName(),
1297  'creator' => $creator->getName(),
1298  ] );
1299  $ret = AuthenticationResponse::newFail( wfMessage( 'userexists' ) );
1300  $this->callMethodOnProviders( 7, 'postAccountCreation', [ $user, $creator, $ret ] );
1301  $session->remove( 'AuthManager::accountCreationState' );
1302  return $ret;
1303  }
1304  } else {
1305  if ( $user->getId() === 0 ) {
1306  $this->logger->debug( __METHOD__ . ': User does not exist locally when it should', [
1307  'user' => $user->getName(),
1308  'creator' => $creator->getName(),
1309  'expected_id' => $state['userid'],
1310  ] );
1311  throw new \UnexpectedValueException(
1312  "User \"{$state['username']}\" should exist now, but doesn't!"
1313  );
1314  }
1315  if ( $user->getId() !== $state['userid'] ) {
1316  $this->logger->debug( __METHOD__ . ': User ID/name mismatch', [
1317  'user' => $user->getName(),
1318  'creator' => $creator->getName(),
1319  'expected_id' => $state['userid'],
1320  'actual_id' => $user->getId(),
1321  ] );
1322  throw new \UnexpectedValueException(
1323  "User \"{$state['username']}\" exists, but " .
1324  "ID {$user->getId()} !== {$state['userid']}!"
1325  );
1326  }
1327  }
1328  foreach ( $state['reqs'] as $req ) {
1329  if ( $req instanceof UserDataAuthenticationRequest ) {
1330  $status = $req->populateUser( $user );
1331  if ( !$status->isGood() ) {
1332  // This should never happen...
1333  $status = Status::wrap( $status );
1334  $this->logger->debug( __METHOD__ . ': UserData is invalid: {reason}', [
1335  'user' => $user->getName(),
1336  'creator' => $creator->getName(),
1337  'reason' => $status->getWikiText( null, null, 'en' ),
1338  ] );
1339  $ret = AuthenticationResponse::newFail( $status->getMessage() );
1340  $this->callMethodOnProviders( 7, 'postAccountCreation', [ $user, $creator, $ret ] );
1341  $session->remove( 'AuthManager::accountCreationState' );
1342  return $ret;
1343  }
1344  }
1345  }
1346 
1347  foreach ( $reqs as $req ) {
1348  $req->returnToUrl = $state['returnToUrl'];
1349  $req->username = $state['username'];
1350  }
1351 
1352  // Run pre-creation tests, if we haven't already
1353  if ( !$state['ranPreTests'] ) {
1354  $providers = $this->getPreAuthenticationProviders() +
1357  foreach ( $providers as $id => $provider ) {
1358  $status = $provider->testForAccountCreation( $user, $creator, $reqs );
1359  if ( !$status->isGood() ) {
1360  $this->logger->debug( __METHOD__ . ": Fail in pre-authentication by $id", [
1361  'user' => $user->getName(),
1362  'creator' => $creator->getName(),
1363  ] );
1365  Status::wrap( $status )->getMessage()
1366  );
1367  $this->callMethodOnProviders( 7, 'postAccountCreation', [ $user, $creator, $ret ] );
1368  $session->remove( 'AuthManager::accountCreationState' );
1369  return $ret;
1370  }
1371  }
1372 
1373  $state['ranPreTests'] = true;
1374  }
1375 
1376  // Step 1: Choose a primary authentication provider and call it until it succeeds.
1377 
1378  if ( $state['primary'] === null ) {
1379  // We haven't picked a PrimaryAuthenticationProvider yet
1380  foreach ( $this->getPrimaryAuthenticationProviders() as $id => $provider ) {
1381  if ( $provider->accountCreationType() === PrimaryAuthenticationProvider::TYPE_NONE ) {
1382  continue;
1383  }
1384  $res = $provider->beginPrimaryAccountCreation( $user, $creator, $reqs );
1385  switch ( $res->status ) {
1387  $this->logger->debug( __METHOD__ . ": Primary creation passed by $id", [
1388  'user' => $user->getName(),
1389  'creator' => $creator->getName(),
1390  ] );
1391  $state['primary'] = $id;
1392  $state['primaryResponse'] = $res;
1393  break 2;
1395  $this->logger->debug( __METHOD__ . ": Primary creation failed by $id", [
1396  'user' => $user->getName(),
1397  'creator' => $creator->getName(),
1398  ] );
1399  $this->callMethodOnProviders( 7, 'postAccountCreation', [ $user, $creator, $res ] );
1400  $session->remove( 'AuthManager::accountCreationState' );
1401  return $res;
1403  // Continue loop
1404  break;
1407  $this->logger->debug( __METHOD__ . ": Primary creation $res->status by $id", [
1408  'user' => $user->getName(),
1409  'creator' => $creator->getName(),
1410  ] );
1411  $this->fillRequests( $res->neededRequests, self::ACTION_CREATE, null );
1412  $state['primary'] = $id;
1413  $state['continueRequests'] = $res->neededRequests;
1414  $session->setSecret( 'AuthManager::accountCreationState', $state );
1415  return $res;
1416 
1417  // @codeCoverageIgnoreStart
1418  default:
1419  throw new \DomainException(
1420  get_class( $provider ) . "::beginPrimaryAccountCreation() returned $res->status"
1421  );
1422  // @codeCoverageIgnoreEnd
1423  }
1424  }
1425  if ( $state['primary'] === null ) {
1426  $this->logger->debug( __METHOD__ . ': Primary creation failed because no provider accepted', [
1427  'user' => $user->getName(),
1428  'creator' => $creator->getName(),
1429  ] );
1431  wfMessage( 'authmanager-create-no-primary' )
1432  );
1433  $this->callMethodOnProviders( 7, 'postAccountCreation', [ $user, $creator, $ret ] );
1434  $session->remove( 'AuthManager::accountCreationState' );
1435  return $ret;
1436  }
1437  } elseif ( $state['primaryResponse'] === null ) {
1438  $provider = $this->getAuthenticationProvider( $state['primary'] );
1439  if ( !$provider instanceof PrimaryAuthenticationProvider ) {
1440  // Configuration changed? Force them to start over.
1441  // @codeCoverageIgnoreStart
1443  wfMessage( 'authmanager-create-not-in-progress' )
1444  );
1445  $this->callMethodOnProviders( 7, 'postAccountCreation', [ $user, $creator, $ret ] );
1446  $session->remove( 'AuthManager::accountCreationState' );
1447  return $ret;
1448  // @codeCoverageIgnoreEnd
1449  }
1450  $id = $provider->getUniqueId();
1451  $res = $provider->continuePrimaryAccountCreation( $user, $creator, $reqs );
1452  switch ( $res->status ) {
1454  $this->logger->debug( __METHOD__ . ": Primary creation passed by $id", [
1455  'user' => $user->getName(),
1456  'creator' => $creator->getName(),
1457  ] );
1458  $state['primaryResponse'] = $res;
1459  break;
1461  $this->logger->debug( __METHOD__ . ": Primary creation failed by $id", [
1462  'user' => $user->getName(),
1463  'creator' => $creator->getName(),
1464  ] );
1465  $this->callMethodOnProviders( 7, 'postAccountCreation', [ $user, $creator, $res ] );
1466  $session->remove( 'AuthManager::accountCreationState' );
1467  return $res;
1470  $this->logger->debug( __METHOD__ . ": Primary creation $res->status by $id", [
1471  'user' => $user->getName(),
1472  'creator' => $creator->getName(),
1473  ] );
1474  $this->fillRequests( $res->neededRequests, self::ACTION_CREATE, null );
1475  $state['continueRequests'] = $res->neededRequests;
1476  $session->setSecret( 'AuthManager::accountCreationState', $state );
1477  return $res;
1478  default:
1479  throw new \DomainException(
1480  get_class( $provider ) . "::continuePrimaryAccountCreation() returned $res->status"
1481  );
1482  }
1483  }
1484 
1485  // Step 2: Primary authentication succeeded, create the User object
1486  // and add the user locally.
1487 
1488  if ( $state['userid'] === 0 ) {
1489  $this->logger->info( 'Creating user {user} during account creation', [
1490  'user' => $user->getName(),
1491  'creator' => $creator->getName(),
1492  ] );
1493  $status = $user->addToDatabase();
1494  if ( !$status->isOK() ) {
1495  // @codeCoverageIgnoreStart
1496  $ret = AuthenticationResponse::newFail( $status->getMessage() );
1497  $this->callMethodOnProviders( 7, 'postAccountCreation', [ $user, $creator, $ret ] );
1498  $session->remove( 'AuthManager::accountCreationState' );
1499  return $ret;
1500  // @codeCoverageIgnoreEnd
1501  }
1502  $this->setDefaultUserOptions( $user, $creator->isAnon() );
1503  $this->getHookRunner()->onLocalUserCreated( $user, false );
1504  $user->saveSettings();
1505  $state['userid'] = $user->getId();
1506 
1507  // Update user count
1508  \DeferredUpdates::addUpdate( \SiteStatsUpdate::factory( [ 'users' => 1 ] ) );
1509 
1510  // Watch user's userpage and talk page
1511  $this->watchlistManager->addWatchIgnoringRights( $user, $user->getUserPage() );
1512 
1513  // Inform the provider
1514  $logSubtype = $provider->finishAccountCreation( $user, $creator, $state['primaryResponse'] );
1515 
1516  // Log the creation
1517  if ( $this->config->get( 'NewUserLog' ) ) {
1518  $isAnon = $creator->isAnon();
1519  $logEntry = new \ManualLogEntry(
1520  'newusers',
1521  $logSubtype ?: ( $isAnon ? 'create' : 'create2' )
1522  );
1523  $logEntry->setPerformer( $isAnon ? $user : $creator );
1524  $logEntry->setTarget( $user->getUserPage() );
1527  $state['reqs'], CreationReasonAuthenticationRequest::class
1528  );
1529  $logEntry->setComment( $req ? $req->reason : '' );
1530  $logEntry->setParameters( [
1531  '4::userid' => $user->getId(),
1532  ] );
1533  $logid = $logEntry->insert();
1534  $logEntry->publish( $logid );
1535  }
1536  }
1537 
1538  // Step 3: Iterate over all the secondary authentication providers.
1539 
1540  $beginReqs = $state['reqs'];
1541 
1542  foreach ( $this->getSecondaryAuthenticationProviders() as $id => $provider ) {
1543  if ( !isset( $state['secondary'][$id] ) ) {
1544  // This provider isn't started yet, so we pass it the set
1545  // of reqs from beginAuthentication instead of whatever
1546  // might have been used by a previous provider in line.
1547  $func = 'beginSecondaryAccountCreation';
1548  $res = $provider->beginSecondaryAccountCreation( $user, $creator, $beginReqs );
1549  } elseif ( !$state['secondary'][$id] ) {
1550  $func = 'continueSecondaryAccountCreation';
1551  $res = $provider->continueSecondaryAccountCreation( $user, $creator, $reqs );
1552  } else {
1553  continue;
1554  }
1555  switch ( $res->status ) {
1557  $this->logger->debug( __METHOD__ . ": Secondary creation passed by $id", [
1558  'user' => $user->getName(),
1559  'creator' => $creator->getName(),
1560  ] );
1561  // fall through
1563  $state['secondary'][$id] = true;
1564  break;
1567  $this->logger->debug( __METHOD__ . ": Secondary creation $res->status by $id", [
1568  'user' => $user->getName(),
1569  'creator' => $creator->getName(),
1570  ] );
1571  $this->fillRequests( $res->neededRequests, self::ACTION_CREATE, null );
1572  $state['secondary'][$id] = false;
1573  $state['continueRequests'] = $res->neededRequests;
1574  $session->setSecret( 'AuthManager::accountCreationState', $state );
1575  return $res;
1577  throw new \DomainException(
1578  get_class( $provider ) . "::{$func}() returned $res->status." .
1579  ' Secondary providers are not allowed to fail account creation, that' .
1580  ' should have been done via testForAccountCreation().'
1581  );
1582  // @codeCoverageIgnoreStart
1583  default:
1584  throw new \DomainException(
1585  get_class( $provider ) . "::{$func}() returned $res->status"
1586  );
1587  // @codeCoverageIgnoreEnd
1588  }
1589  }
1590 
1591  $id = $user->getId();
1592  $name = $user->getName();
1593  $req = new CreatedAccountAuthenticationRequest( $id, $name );
1594  $ret = AuthenticationResponse::newPass( $name );
1595  $ret->loginRequest = $req;
1596  $this->createdAccountAuthenticationRequests[] = $req;
1597 
1598  $this->logger->info( __METHOD__ . ': Account creation succeeded for {user}', [
1599  'user' => $user->getName(),
1600  'creator' => $creator->getName(),
1601  ] );
1602 
1603  $this->callMethodOnProviders( 7, 'postAccountCreation', [ $user, $creator, $ret ] );
1604  $session->remove( 'AuthManager::accountCreationState' );
1605  $this->removeAuthenticationSessionData( null );
1606  return $ret;
1607  } catch ( \Exception $ex ) {
1608  $session->remove( 'AuthManager::accountCreationState' );
1609  throw $ex;
1610  }
1611  }
1612 
1631  public function autoCreateUser( User $user, $source, $login = true, $log = true ) {
1632  if ( $source !== self::AUTOCREATE_SOURCE_SESSION &&
1633  $source !== self::AUTOCREATE_SOURCE_MAINT &&
1635  ) {
1636  throw new \InvalidArgumentException( "Unknown auto-creation source: $source" );
1637  }
1638 
1639  $username = $user->getName();
1640 
1641  // Try the local user from the replica DB
1642  $localUserIdentity = $this->userIdentityLookup->getUserIdentityByName( $username );
1643  $localId = ( $localUserIdentity && $localUserIdentity->getId() )
1644  ? $localUserIdentity->getId()
1645  : null;
1646  $flags = User::READ_NORMAL;
1647 
1648  // Fetch the user ID from the primary, so that we don't try to create the user
1649  // when they already exist, due to replication lag
1650  // @codeCoverageIgnoreStart
1651  if (
1652  !$localId &&
1653  $this->loadBalancer->getReaderIndex() !== 0
1654  ) {
1655  $localUserIdentity = $this->userIdentityLookup->getUserIdentityByName(
1656  $username,
1657  UserIdentityLookup::READ_LATEST
1658  );
1659  $localId = ( $localUserIdentity && $localUserIdentity->getId() )
1660  ? $localUserIdentity->getId()
1661  : null;
1662  $flags = User::READ_LATEST;
1663  }
1664  // @codeCoverageIgnoreEnd
1665 
1666  if ( $localId ) {
1667  $this->logger->debug( __METHOD__ . ': {username} already exists locally', [
1668  'username' => $username,
1669  ] );
1670  $user->setId( $localId );
1671  $user->loadFromId( $flags );
1672  if ( $login ) {
1673  $this->setSessionDataForUser( $user );
1674  }
1675  return Status::newGood()->warning( 'userexists' );
1676  }
1677 
1678  // Wiki is read-only?
1679  if ( $this->readOnlyMode->isReadOnly() ) {
1680  $reason = $this->readOnlyMode->getReason();
1681  $this->logger->debug( __METHOD__ . ': denied because of read only mode: {reason}', [
1682  'username' => $username,
1683  'reason' => $reason,
1684  ] );
1685  $user->setId( 0 );
1686  $user->loadFromId();
1687  return Status::newFatal( wfMessage( 'readonlytext', $reason ) );
1688  }
1689 
1690  // Check the session, if we tried to create this user already there's
1691  // no point in retrying.
1692  $session = $this->request->getSession();
1693  if ( $session->get( 'AuthManager::AutoCreateBlacklist' ) ) {
1694  $this->logger->debug( __METHOD__ . ': blacklisted in session {sessionid}', [
1695  'username' => $username,
1696  'sessionid' => $session->getId(),
1697  ] );
1698  $user->setId( 0 );
1699  $user->loadFromId();
1700  $reason = $session->get( 'AuthManager::AutoCreateBlacklist' );
1701  if ( $reason instanceof StatusValue ) {
1702  return Status::wrap( $reason );
1703  } else {
1704  return Status::newFatal( $reason );
1705  }
1706  }
1707 
1708  // Is the username creatable?
1709  if ( !$this->userNameUtils->isCreatable( $username ) ) {
1710  $this->logger->debug( __METHOD__ . ': name "{username}" is not creatable', [
1711  'username' => $username,
1712  ] );
1713  $session->set( 'AuthManager::AutoCreateBlacklist', 'noname' );
1714  $user->setId( 0 );
1715  $user->loadFromId();
1716  return Status::newFatal( 'noname' );
1717  }
1718 
1719  // Is the IP user able to create accounts?
1720  $anon = $this->userFactory->newAnonymous();
1721  if ( $source !== self::AUTOCREATE_SOURCE_MAINT &&
1722  !$anon->isAllowedAny( 'createaccount', 'autocreateaccount' )
1723  ) {
1724  $this->logger->debug( __METHOD__ . ': IP lacks the ability to create or autocreate accounts', [
1725  'username' => $username,
1726  'clientip' => $anon->getName(),
1727  ] );
1728  $session->set( 'AuthManager::AutoCreateBlacklist', 'authmanager-autocreate-noperm' );
1729  $session->persist();
1730  $user->setId( 0 );
1731  $user->loadFromId();
1732  return Status::newFatal( 'authmanager-autocreate-noperm' );
1733  }
1734 
1735  // Avoid account creation races on double submissions
1737  $lock = $cache->getScopedLock( $cache->makeGlobalKey( 'account', md5( $username ) ) );
1738  if ( !$lock ) {
1739  $this->logger->debug( __METHOD__ . ': Could not acquire account creation lock', [
1740  'user' => $username,
1741  ] );
1742  $user->setId( 0 );
1743  $user->loadFromId();
1744  return Status::newFatal( 'usernameinprogress' );
1745  }
1746 
1747  // Denied by providers?
1748  $options = [
1749  'flags' => User::READ_LATEST,
1750  'creating' => true,
1751  ];
1752  $providers = $this->getPreAuthenticationProviders() +
1755  foreach ( $providers as $provider ) {
1756  $status = $provider->testUserForCreation( $user, $source, $options );
1757  if ( !$status->isGood() ) {
1758  $ret = Status::wrap( $status );
1759  $this->logger->debug( __METHOD__ . ': Provider denied creation of {username}: {reason}', [
1760  'username' => $username,
1761  'reason' => $ret->getWikiText( null, null, 'en' ),
1762  ] );
1763  $session->set( 'AuthManager::AutoCreateBlacklist', $status );
1764  $user->setId( 0 );
1765  $user->loadFromId();
1766  return $ret;
1767  }
1768  }
1769 
1770  $backoffKey = $cache->makeKey( 'AuthManager', 'autocreate-failed', md5( $username ) );
1771  if ( $cache->get( $backoffKey ) ) {
1772  $this->logger->debug( __METHOD__ . ': {username} denied by prior creation attempt failures', [
1773  'username' => $username,
1774  ] );
1775  $user->setId( 0 );
1776  $user->loadFromId();
1777  return Status::newFatal( 'authmanager-autocreate-exception' );
1778  }
1779 
1780  // Checks passed, create the user...
1781  $from = $_SERVER['REQUEST_URI'] ?? 'CLI';
1782  $this->logger->info( __METHOD__ . ': creating new user ({username}) - from: {from}', [
1783  'username' => $username,
1784  'from' => $from,
1785  ] );
1786 
1787  // Ignore warnings about primary connections/writes...hard to avoid here
1788  $trxProfilerSilencedScope = \Profiler::instance()->getTransactionProfiler()->silenceForScope();
1789  try {
1790  $status = $user->addToDatabase();
1791  if ( !$status->isOK() ) {
1792  // Double-check for a race condition (T70012). We make use of the fact that when
1793  // addToDatabase fails due to the user already existing, the user object gets loaded.
1794  if ( $user->getId() ) {
1795  $this->logger->info( __METHOD__ . ': {username} already exists locally (race)', [
1796  'username' => $username,
1797  ] );
1798  if ( $login ) {
1799  $this->setSessionDataForUser( $user );
1800  }
1801  $status = Status::newGood()->warning( 'userexists' );
1802  } else {
1803  $this->logger->error( __METHOD__ . ': {username} failed with message {msg}', [
1804  'username' => $username,
1805  'msg' => $status->getWikiText( null, null, 'en' )
1806  ] );
1807  $user->setId( 0 );
1808  $user->loadFromId();
1809  }
1810  return $status;
1811  }
1812  } catch ( \Exception $ex ) {
1813  $this->logger->error( __METHOD__ . ': {username} failed with exception {exception}', [
1814  'username' => $username,
1815  'exception' => $ex,
1816  ] );
1817  // Do not keep throwing errors for a while
1818  $cache->set( $backoffKey, 1, 600 );
1819  // Bubble up error; which should normally trigger DB rollbacks
1820  throw $ex;
1821  }
1822 
1823  $this->setDefaultUserOptions( $user, false );
1824 
1825  // Inform the providers
1826  $this->callMethodOnProviders( 6, 'autoCreatedAccount', [ $user, $source ] );
1827 
1828  $this->getHookRunner()->onLocalUserCreated( $user, true );
1829  $user->saveSettings();
1830 
1831  // Update user count
1832  \DeferredUpdates::addUpdate( \SiteStatsUpdate::factory( [ 'users' => 1 ] ) );
1833  // Watch user's userpage and talk page
1834  \DeferredUpdates::addCallableUpdate( function () use ( $user ) {
1835  $this->watchlistManager->addWatchIgnoringRights( $user, $user->getUserPage() );
1836  } );
1837 
1838  // Log the creation
1839  if ( $this->config->get( 'NewUserLog' ) && $log ) {
1840  $logEntry = new \ManualLogEntry( 'newusers', 'autocreate' );
1841  $logEntry->setPerformer( $user );
1842  $logEntry->setTarget( $user->getUserPage() );
1843  $logEntry->setComment( '' );
1844  $logEntry->setParameters( [
1845  '4::userid' => $user->getId(),
1846  ] );
1847  $logEntry->insert();
1848  }
1849 
1850  ScopedCallback::consume( $trxProfilerSilencedScope );
1851 
1852  if ( $login ) {
1853  $this->setSessionDataForUser( $user );
1854  }
1855 
1856  return Status::newGood();
1857  }
1858 
1859  // endregion -- end of Account creation
1860 
1861  /***************************************************************************/
1862  // region Account linking
1869  public function canLinkAccounts() {
1870  foreach ( $this->getPrimaryAuthenticationProviders() as $provider ) {
1871  if ( $provider->accountCreationType() === PrimaryAuthenticationProvider::TYPE_LINK ) {
1872  return true;
1873  }
1874  }
1875  return false;
1876  }
1877 
1887  public function beginAccountLink( User $user, array $reqs, $returnToUrl ) {
1888  $session = $this->request->getSession();
1889  $session->remove( 'AuthManager::accountLinkState' );
1890 
1891  if ( !$this->canLinkAccounts() ) {
1892  // Caller should have called canLinkAccounts()
1893  throw new \LogicException( 'Account linking is not possible' );
1894  }
1895 
1896  if ( $user->getId() === 0 ) {
1897  if ( !$this->userNameUtils->isUsable( $user->getName() ) ) {
1898  $msg = wfMessage( 'noname' );
1899  } else {
1900  $msg = wfMessage( 'authmanager-userdoesnotexist', $user->getName() );
1901  }
1902  return AuthenticationResponse::newFail( $msg );
1903  }
1904  foreach ( $reqs as $req ) {
1905  $req->username = $user->getName();
1906  $req->returnToUrl = $returnToUrl;
1907  }
1908 
1909  $this->removeAuthenticationSessionData( null );
1910 
1911  $providers = $this->getPreAuthenticationProviders();
1912  foreach ( $providers as $id => $provider ) {
1913  $status = $provider->testForAccountLink( $user );
1914  if ( !$status->isGood() ) {
1915  $this->logger->debug( __METHOD__ . ": Account linking pre-check failed by $id", [
1916  'user' => $user->getName(),
1917  ] );
1919  Status::wrap( $status )->getMessage()
1920  );
1921  $this->callMethodOnProviders( 3, 'postAccountLink', [ $user, $ret ] );
1922  return $ret;
1923  }
1924  }
1925 
1926  $state = [
1927  'username' => $user->getName(),
1928  'userid' => $user->getId(),
1929  'returnToUrl' => $returnToUrl,
1930  'primary' => null,
1931  'continueRequests' => [],
1932  ];
1933 
1934  $providers = $this->getPrimaryAuthenticationProviders();
1935  foreach ( $providers as $id => $provider ) {
1936  if ( $provider->accountCreationType() !== PrimaryAuthenticationProvider::TYPE_LINK ) {
1937  continue;
1938  }
1939 
1940  $res = $provider->beginPrimaryAccountLink( $user, $reqs );
1941  switch ( $res->status ) {
1943  $this->logger->info( "Account linked to {user} by $id", [
1944  'user' => $user->getName(),
1945  ] );
1946  $this->callMethodOnProviders( 3, 'postAccountLink', [ $user, $res ] );
1947  return $res;
1948 
1950  $this->logger->debug( __METHOD__ . ": Account linking failed by $id", [
1951  'user' => $user->getName(),
1952  ] );
1953  $this->callMethodOnProviders( 3, 'postAccountLink', [ $user, $res ] );
1954  return $res;
1955 
1957  // Continue loop
1958  break;
1959 
1962  $this->logger->debug( __METHOD__ . ": Account linking $res->status by $id", [
1963  'user' => $user->getName(),
1964  ] );
1965  $this->fillRequests( $res->neededRequests, self::ACTION_LINK, $user->getName() );
1966  $state['primary'] = $id;
1967  $state['continueRequests'] = $res->neededRequests;
1968  $session->setSecret( 'AuthManager::accountLinkState', $state );
1969  $session->persist();
1970  return $res;
1971 
1972  // @codeCoverageIgnoreStart
1973  default:
1974  throw new \DomainException(
1975  get_class( $provider ) . "::beginPrimaryAccountLink() returned $res->status"
1976  );
1977  // @codeCoverageIgnoreEnd
1978  }
1979  }
1980 
1981  $this->logger->debug( __METHOD__ . ': Account linking failed because no provider accepted', [
1982  'user' => $user->getName(),
1983  ] );
1985  wfMessage( 'authmanager-link-no-primary' )
1986  );
1987  $this->callMethodOnProviders( 3, 'postAccountLink', [ $user, $ret ] );
1988  return $ret;
1989  }
1990 
1996  public function continueAccountLink( array $reqs ) {
1997  $session = $this->request->getSession();
1998  try {
1999  if ( !$this->canLinkAccounts() ) {
2000  // Caller should have called canLinkAccounts()
2001  $session->remove( 'AuthManager::accountLinkState' );
2002  throw new \LogicException( 'Account linking is not possible' );
2003  }
2004 
2005  $state = $session->getSecret( 'AuthManager::accountLinkState' );
2006  if ( !is_array( $state ) ) {
2008  wfMessage( 'authmanager-link-not-in-progress' )
2009  );
2010  }
2011  $state['continueRequests'] = [];
2012 
2013  // Step 0: Prepare and validate the input
2014 
2015  $user = $this->userFactory->newFromName(
2016  (string)$state['username'],
2017  UserFactory::RIGOR_USABLE
2018  );
2019  if ( !is_object( $user ) ) {
2020  $session->remove( 'AuthManager::accountLinkState' );
2021  return AuthenticationResponse::newFail( wfMessage( 'noname' ) );
2022  }
2023  if ( $user->getId() !== $state['userid'] ) {
2024  throw new \UnexpectedValueException(
2025  "User \"{$state['username']}\" is valid, but " .
2026  "ID {$user->getId()} !== {$state['userid']}!"
2027  );
2028  }
2029 
2030  foreach ( $reqs as $req ) {
2031  $req->username = $state['username'];
2032  $req->returnToUrl = $state['returnToUrl'];
2033  }
2034 
2035  // Step 1: Call the primary again until it succeeds
2036 
2037  $provider = $this->getAuthenticationProvider( $state['primary'] );
2038  if ( !$provider instanceof PrimaryAuthenticationProvider ) {
2039  // Configuration changed? Force them to start over.
2040  // @codeCoverageIgnoreStart
2042  wfMessage( 'authmanager-link-not-in-progress' )
2043  );
2044  $this->callMethodOnProviders( 3, 'postAccountLink', [ $user, $ret ] );
2045  $session->remove( 'AuthManager::accountLinkState' );
2046  return $ret;
2047  // @codeCoverageIgnoreEnd
2048  }
2049  $id = $provider->getUniqueId();
2050  $res = $provider->continuePrimaryAccountLink( $user, $reqs );
2051  switch ( $res->status ) {
2053  $this->logger->info( "Account linked to {user} by $id", [
2054  'user' => $user->getName(),
2055  ] );
2056  $this->callMethodOnProviders( 3, 'postAccountLink', [ $user, $res ] );
2057  $session->remove( 'AuthManager::accountLinkState' );
2058  return $res;
2060  $this->logger->debug( __METHOD__ . ": Account linking failed by $id", [
2061  'user' => $user->getName(),
2062  ] );
2063  $this->callMethodOnProviders( 3, 'postAccountLink', [ $user, $res ] );
2064  $session->remove( 'AuthManager::accountLinkState' );
2065  return $res;
2068  $this->logger->debug( __METHOD__ . ": Account linking $res->status by $id", [
2069  'user' => $user->getName(),
2070  ] );
2071  $this->fillRequests( $res->neededRequests, self::ACTION_LINK, $user->getName() );
2072  $state['continueRequests'] = $res->neededRequests;
2073  $session->setSecret( 'AuthManager::accountLinkState', $state );
2074  return $res;
2075  default:
2076  throw new \DomainException(
2077  get_class( $provider ) . "::continuePrimaryAccountLink() returned $res->status"
2078  );
2079  }
2080  } catch ( \Exception $ex ) {
2081  $session->remove( 'AuthManager::accountLinkState' );
2082  throw $ex;
2083  }
2084  }
2085 
2086  // endregion -- end of Account linking
2087 
2088  /***************************************************************************/
2089  // region Information methods
2110  public function getAuthenticationRequests( $action, UserIdentity $user = null ) {
2111  $options = [];
2112  $providerAction = $action;
2113 
2114  // Figure out which providers to query
2115  switch ( $action ) {
2116  case self::ACTION_LOGIN:
2117  case self::ACTION_CREATE:
2118  $providers = $this->getPreAuthenticationProviders() +
2121  break;
2122 
2124  $state = $this->request->getSession()->getSecret( 'AuthManager::authnState' );
2125  return is_array( $state ) ? $state['continueRequests'] : [];
2126 
2128  $state = $this->request->getSession()->getSecret( 'AuthManager::accountCreationState' );
2129  return is_array( $state ) ? $state['continueRequests'] : [];
2130 
2131  case self::ACTION_LINK:
2132  $providers = [];
2133  foreach ( $this->getPrimaryAuthenticationProviders() as $p ) {
2134  if ( $p->accountCreationType() === PrimaryAuthenticationProvider::TYPE_LINK ) {
2135  $providers[] = $p;
2136  }
2137  }
2138  break;
2139 
2140  case self::ACTION_UNLINK:
2141  $providers = [];
2142  foreach ( $this->getPrimaryAuthenticationProviders() as $p ) {
2143  if ( $p->accountCreationType() === PrimaryAuthenticationProvider::TYPE_LINK ) {
2144  $providers[] = $p;
2145  }
2146  }
2147 
2148  // To providers, unlink and remove are identical.
2149  $providerAction = self::ACTION_REMOVE;
2150  break;
2151 
2153  $state = $this->request->getSession()->getSecret( 'AuthManager::accountLinkState' );
2154  return is_array( $state ) ? $state['continueRequests'] : [];
2155 
2156  case self::ACTION_CHANGE:
2157  case self::ACTION_REMOVE:
2158  $providers = $this->getPrimaryAuthenticationProviders() +
2160  break;
2161 
2162  // @codeCoverageIgnoreStart
2163  default:
2164  throw new \DomainException( __METHOD__ . ": Invalid action \"$action\"" );
2165  }
2166  // @codeCoverageIgnoreEnd
2167 
2168  return $this->getAuthenticationRequestsInternal( $providerAction, $options, $providers, $user );
2169  }
2170 
2181  $providerAction, array $options, array $providers, UserIdentity $user = null
2182  ) {
2183  $user = $user ?: \RequestContext::getMain()->getUser();
2184  $options['username'] = $user->isRegistered() ? $user->getName() : null;
2185 
2186  // Query them and merge results
2187  $reqs = [];
2188  foreach ( $providers as $provider ) {
2189  $isPrimary = $provider instanceof PrimaryAuthenticationProvider;
2190  foreach ( $provider->getAuthenticationRequests( $providerAction, $options ) as $req ) {
2191  $id = $req->getUniqueId();
2192 
2193  // If a required request if from a Primary, mark it as "primary-required" instead
2194  if ( $isPrimary && $req->required ) {
2195  $req->required = AuthenticationRequest::PRIMARY_REQUIRED;
2196  }
2197 
2198  if (
2199  !isset( $reqs[$id] )
2200  || $req->required === AuthenticationRequest::REQUIRED
2201  || $reqs[$id] === AuthenticationRequest::OPTIONAL
2202  ) {
2203  $reqs[$id] = $req;
2204  }
2205  }
2206  }
2207 
2208  // AuthManager has its own req for some actions
2209  switch ( $providerAction ) {
2210  case self::ACTION_LOGIN:
2211  $reqs[] = new RememberMeAuthenticationRequest( $this->config->get( 'RememberMe' ) );
2212  break;
2213 
2214  case self::ACTION_CREATE:
2215  $reqs[] = new UsernameAuthenticationRequest;
2216  $reqs[] = new UserDataAuthenticationRequest;
2217  if ( $options['username'] !== null ) {
2219  $options['username'] = null; // Don't fill in the username below
2220  }
2221  break;
2222  }
2223 
2224  // Fill in reqs data
2225  $this->fillRequests( $reqs, $providerAction, $options['username'], true );
2226 
2227  // For self::ACTION_CHANGE, filter out any that something else *doesn't* allow changing
2228  if ( $providerAction === self::ACTION_CHANGE || $providerAction === self::ACTION_REMOVE ) {
2229  $reqs = array_filter( $reqs, function ( $req ) {
2230  return $this->allowsAuthenticationDataChange( $req, false )->isGood();
2231  } );
2232  }
2233 
2234  return array_values( $reqs );
2235  }
2236 
2244  private function fillRequests( array &$reqs, $action, $username, $forceAction = false ) {
2245  foreach ( $reqs as $req ) {
2246  if ( !$req->action || $forceAction ) {
2247  $req->action = $action;
2248  }
2249  if ( $req->username === null ) {
2250  $req->username = $username;
2251  }
2252  }
2253  }
2254 
2261  public function userExists( $username, $flags = User::READ_NORMAL ) {
2262  foreach ( $this->getPrimaryAuthenticationProviders() as $provider ) {
2263  if ( $provider->testUserExists( $username, $flags ) ) {
2264  return true;
2265  }
2266  }
2267 
2268  return false;
2269  }
2270 
2282  public function allowsPropertyChange( $property ) {
2283  $providers = $this->getPrimaryAuthenticationProviders() +
2285  foreach ( $providers as $provider ) {
2286  if ( !$provider->providerAllowsPropertyChange( $property ) ) {
2287  return false;
2288  }
2289  }
2290  return true;
2291  }
2292 
2301  public function getAuthenticationProvider( $id ) {
2302  // Fast version
2303  if ( isset( $this->allAuthenticationProviders[$id] ) ) {
2304  return $this->allAuthenticationProviders[$id];
2305  }
2306 
2307  // Slow version: instantiate each kind and check
2308  $providers = $this->getPrimaryAuthenticationProviders();
2309  if ( isset( $providers[$id] ) ) {
2310  return $providers[$id];
2311  }
2312  $providers = $this->getSecondaryAuthenticationProviders();
2313  if ( isset( $providers[$id] ) ) {
2314  return $providers[$id];
2315  }
2316  $providers = $this->getPreAuthenticationProviders();
2317  if ( isset( $providers[$id] ) ) {
2318  return $providers[$id];
2319  }
2320 
2321  return null;
2322  }
2323 
2324  // endregion -- end of Information methods
2325 
2326  /***************************************************************************/
2327  // region Internal methods
2336  public function setAuthenticationSessionData( $key, $data ) {
2337  $session = $this->request->getSession();
2338  $arr = $session->getSecret( 'authData' );
2339  if ( !is_array( $arr ) ) {
2340  $arr = [];
2341  }
2342  $arr[$key] = $data;
2343  $session->setSecret( 'authData', $arr );
2344  }
2345 
2353  public function getAuthenticationSessionData( $key, $default = null ) {
2354  $arr = $this->request->getSession()->getSecret( 'authData' );
2355  if ( is_array( $arr ) && array_key_exists( $key, $arr ) ) {
2356  return $arr[$key];
2357  } else {
2358  return $default;
2359  }
2360  }
2361 
2367  public function removeAuthenticationSessionData( $key ) {
2368  $session = $this->request->getSession();
2369  if ( $key === null ) {
2370  $session->remove( 'authData' );
2371  } else {
2372  $arr = $session->getSecret( 'authData' );
2373  if ( is_array( $arr ) && array_key_exists( $key, $arr ) ) {
2374  unset( $arr[$key] );
2375  $session->setSecret( 'authData', $arr );
2376  }
2377  }
2378  }
2379 
2386  protected function providerArrayFromSpecs( $class, array $specs ) {
2387  $i = 0;
2388  foreach ( $specs as &$spec ) {
2389  $spec = [ 'sort2' => $i++ ] + $spec + [ 'sort' => 0 ];
2390  }
2391  unset( $spec );
2392  // Sort according to the 'sort' field, and if they are equal, according to 'sort2'
2393  usort( $specs, static function ( $a, $b ) {
2394  return $a['sort'] <=> $b['sort']
2395  ?: $a['sort2'] <=> $b['sort2'];
2396  } );
2397 
2398  $ret = [];
2399  foreach ( $specs as $spec ) {
2401  $provider = $this->objectFactory->createObject( $spec, [ 'assertClass' => $class ] );
2402  $provider->init( $this->logger, $this, $this->getHookContainer(), $this->config, $this->userNameUtils );
2403  $id = $provider->getUniqueId();
2404  if ( isset( $this->allAuthenticationProviders[$id] ) ) {
2405  throw new \RuntimeException(
2406  "Duplicate specifications for id $id (classes " .
2407  get_class( $provider ) . ' and ' .
2408  get_class( $this->allAuthenticationProviders[$id] ) . ')'
2409  );
2410  }
2411  $this->allAuthenticationProviders[$id] = $provider;
2412  $ret[$id] = $provider;
2413  }
2414  return $ret;
2415  }
2416 
2420  private function getConfiguration() {
2421  return $this->config->get( 'AuthManagerConfig' ) ?: $this->config->get( 'AuthManagerAutoConfig' );
2422  }
2423 
2428  protected function getPreAuthenticationProviders() {
2429  if ( $this->preAuthenticationProviders === null ) {
2430  $conf = $this->getConfiguration();
2431  $this->preAuthenticationProviders = $this->providerArrayFromSpecs(
2432  PreAuthenticationProvider::class, $conf['preauth']
2433  );
2434  }
2436  }
2437 
2442  protected function getPrimaryAuthenticationProviders() {
2443  if ( $this->primaryAuthenticationProviders === null ) {
2444  $conf = $this->getConfiguration();
2445  $this->primaryAuthenticationProviders = $this->providerArrayFromSpecs(
2446  PrimaryAuthenticationProvider::class, $conf['primaryauth']
2447  );
2448  }
2450  }
2451 
2457  if ( $this->secondaryAuthenticationProviders === null ) {
2458  $conf = $this->getConfiguration();
2459  $this->secondaryAuthenticationProviders = $this->providerArrayFromSpecs(
2460  SecondaryAuthenticationProvider::class, $conf['secondaryauth']
2461  );
2462  }
2464  }
2465 
2471  private function setSessionDataForUser( $user, $remember = null ) {
2472  $session = $this->request->getSession();
2473  $delay = $session->delaySave();
2474 
2475  $session->resetId();
2476  $session->resetAllTokens();
2477  if ( $session->canSetUser() ) {
2478  $session->setUser( $user );
2479  }
2480  if ( $remember !== null ) {
2481  $session->setRememberUser( $remember );
2482  }
2483  $session->set( 'AuthManager:lastAuthId', $user->getId() );
2484  $session->set( 'AuthManager:lastAuthTimestamp', time() );
2485  $session->persist();
2486 
2487  \Wikimedia\ScopedCallback::consume( $delay );
2488 
2489  $this->getHookRunner()->onUserLoggedIn( $user );
2490  }
2491 
2496  private function setDefaultUserOptions( User $user, $useContextLang ) {
2497  $user->setToken();
2498 
2499  $lang = $useContextLang ? \RequestContext::getMain()->getLanguage() : $this->contentLanguage;
2500  $this->userOptionsManager->setOption(
2501  $user,
2502  'language',
2503  $this->languageConverterFactory->getLanguageConverter( $lang )->getPreferredVariant()
2504  );
2505 
2506  $contLangConverter = $this->languageConverterFactory->getLanguageConverter( $this->contentLanguage );
2507  if ( $contLangConverter->hasVariants() ) {
2508  $this->userOptionsManager->setOption(
2509  $user,
2510  'variant',
2511  $contLangConverter->getPreferredVariant()
2512  );
2513  }
2514  }
2515 
2521  private function callMethodOnProviders( $which, $method, array $args ) {
2522  $providers = [];
2523  if ( $which & 1 ) {
2524  $providers += $this->getPreAuthenticationProviders();
2525  }
2526  if ( $which & 2 ) {
2527  $providers += $this->getPrimaryAuthenticationProviders();
2528  }
2529  if ( $which & 4 ) {
2530  $providers += $this->getSecondaryAuthenticationProviders();
2531  }
2532  foreach ( $providers as $provider ) {
2533  $provider->$method( ...$args );
2534  }
2535  }
2536 
2540  private function getHookContainer() {
2541  return $this->hookContainer;
2542  }
2543 
2547  private function getHookRunner() {
2548  return $this->hookRunner;
2549  }
2550 
2551  // endregion -- end of Internal methods
2552 
2553 }
2554 
2555 /*
2556  * This file uses VisualStudio style region/endregion fold markers which are
2557  * recognised by PHPStorm. If modelines are enabled, the following editor
2558  * configuration will also enable folding in vim, if it is in the last 5 lines
2559  * of the file. We also use "@name" which creates sections in Doxygen.
2560  *
2561  * vim: foldmarker=//\ region,//\ endregion foldmethod=marker
2562  */
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:1631
MediaWiki\Auth\AuthManager\continueAccountLink
continueAccountLink(array $reqs)
Continue an account linking flow.
Definition: AuthManager.php:1996
User\loadFromId
loadFromId( $flags=self::READ_NORMAL)
Load user table data, given mId has already been set.
Definition: User.php:466
MediaWiki\Auth\AuthManager\continueAuthentication
continueAuthentication(array $reqs)
Continue an authentication flow.
Definition: AuthManager.php:480
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:270
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:160
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:131
MediaWiki\Auth\PrimaryAuthenticationProvider\TYPE_NONE
const TYPE_NONE
Provider cannot create or link to accounts.
Definition: PrimaryAuthenticationProvider.php:81
MediaWiki\Auth\AuthManager\__construct
__construct(WebRequest $request, Config $config, ObjectFactory $objectFactory, HookContainer $hookContainer, ReadOnlyMode $readOnlyMode, UserNameUtils $userNameUtils, BlockManager $blockManager, WatchlistManager $watchlistManager, ILoadBalancer $loadBalancer, Language $contentLanguage, LanguageConverterFactory $languageConverterFactory, BotPasswordStore $botPasswordStore, UserFactory $userFactory, UserIdentityLookup $userIdentityLookup, UserOptionsManager $userOptionsManager)
Definition: AuthManager.php:224
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:273
MediaWiki\Auth\AuthManager\fillRequests
fillRequests(array &$reqs, $action, $username, $forceAction=false)
Set values in an array of requests.
Definition: AuthManager.php:2244
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:990
MediaWiki\Auth\AuthManager\getPrimaryAuthenticationProviders
getPrimaryAuthenticationProviders()
Get the list of PrimaryAuthenticationProviders.
Definition: AuthManager.php:2442
MediaWiki\Block\BlockManager
A service class for checking blocks.
Definition: BlockManager.php:47
Profiler\instance
static instance()
Singleton.
Definition: Profiler.php:69
MediaWiki\Auth\AuthManager\ACTION_UNLINK
const ACTION_UNLINK
Like ACTION_REMOVE but for linking providers only.
Definition: AuthManager.php:126
$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:136
MediaWiki\Auth\AuthManager\getHookContainer
getHookContainer()
Definition: AuthManager.php:2540
MediaWiki\Auth\AuthManager\revokeAccessForUser
revokeAccessForUser( $username)
Revoke any authentication credentials for a user.
Definition: AuthManager.php:933
MediaWiki\Auth\AuthManager\$userNameUtils
UserNameUtils $userNameUtils
Definition: AuthManager.php:154
ReadOnlyMode
A service class for fetching the wiki's current read-only mode.
Definition: ReadOnlyMode.php:11
MediaWiki\Auth\AuthManager\$userFactory
UserFactory $userFactory
Definition: AuthManager.php:199
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:355
wfMessage
wfMessage( $key,... $params)
This is the function for getting translated interface messages.
Definition: GlobalFunctions.php:1186
User\getUserPage
getUserPage()
Get this user's personal page title.
Definition: User.php:3654
MediaWiki\Auth\AuthManager\userExists
userExists( $username, $flags=User::READ_NORMAL)
Determine whether a username exists.
Definition: AuthManager.php:2261
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:157
MediaWiki\Auth\AuthManager\$languageConverterFactory
LanguageConverterFactory $languageConverterFactory
Definition: AuthManager.php:193
MediaWiki\Auth\AuthManager\AUTOCREATE_SOURCE_MAINT
const AUTOCREATE_SOURCE_MAINT
Auto-creation is due to a Maintenance script.
Definition: AuthManager.php:139
MediaWiki\Auth\AuthManager\getPreAuthenticationProviders
getPreAuthenticationProviders()
Get the list of PreAuthenticationProviders.
Definition: AuthManager.php:2428
MediaWiki\Auth\AuthManager\getAuthenticationSessionData
getAuthenticationSessionData( $key, $default=null)
Fetch authentication data from the current session.
Definition: AuthManager.php:2353
$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:108
MediaWiki\Auth\AuthManager\$blockManager
BlockManager $blockManager
Definition: AuthManager.php:181
MediaWiki\Languages\LanguageConverterFactory
An interface for creating language converters.
Definition: LanguageConverterFactory.php:46
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:133
MediaWiki\Auth\AuthManager\getAuthenticationRequests
getAuthenticationRequests( $action, UserIdentity $user=null)
Return the applicable list of AuthenticationRequests.
Definition: AuthManager.php:2110
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:172
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:2367
MediaWiki\Auth\AuthManager\$userOptionsManager
UserOptionsManager $userOptionsManager
Definition: AuthManager.php:205
MediaWiki\Auth\AuthManager\$loadBalancer
ILoadBalancer $loadBalancer
Definition: AuthManager.php:187
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:3494
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:2301
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:120
MediaWiki\Auth\CreationReasonAuthenticationRequest
Authentication request for the reason given for account creation.
Definition: CreationReasonAuthenticationRequest.php:10
MediaWiki\Auth\AuthManager\canCreateAccounts
canCreateAccounts()
Determine whether accounts can be created.
Definition: AuthManager.php:1015
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:178
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:2420
$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\$userIdentityLookup
UserIdentityLookup $userIdentityLookup
Definition: AuthManager.php:202
MediaWiki\Auth\AuthManager\checkAccountCreatePermissions
checkAccountCreatePermissions(Authority $creator)
Basic permissions checks on whether a user can create accounts.
Definition: AuthManager.php:1082
MediaWiki\Auth\AuthManager\canAuthenticateNow
canAuthenticateNow()
Indicate whether user authentication is possible.
Definition: AuthManager.php:333
MediaWiki\Auth\AuthManager\setAuthenticationSessionData
setAuthenticationSessionData( $key, $data)
Store authentication in the current session.
Definition: AuthManager.php:2336
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:2180
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:2082
MediaWiki\Auth\UsernameAuthenticationRequest
AuthenticationRequest to ensure something with a username is present.
Definition: UsernameAuthenticationRequest.php:30
MediaWiki\Auth\AuthManager\setDefaultUserOptions
setDefaultUserOptions(User $user, $useContextLang)
Definition: AuthManager.php:2496
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
User\saveSettings
saveSettings()
Save this user's settings into the database.
Definition: User.php:3296
MediaWiki\Auth\AuthManager\$request
WebRequest $request
Definition: AuthManager.php:142
MediaWiki\Auth\AuthManager\beginAccountLink
beginAccountLink(User $user, array $reqs, $returnToUrl)
Start an account linking flow.
Definition: AuthManager.php:1887
MediaWiki\Auth\AuthManager\normalizeUsername
normalizeUsername( $username)
Provide normalized versions of the username for security checks.
Definition: AuthManager.php:909
MediaWiki\Auth\AuthManager\canLinkAccounts
canLinkAccounts()
Determine whether accounts can be linked.
Definition: AuthManager.php:1869
MediaWiki\Permissions\Authority
This interface represents the authority associated the current execution context, such as a web reque...
Definition: Authority.php:37
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:114
MediaWiki\User\UserIdentityLookup
Definition: UserIdentityLookup.php:33
MediaWiki\Auth\AuthManager\ACTION_CREATE
const ACTION_CREATE
Create a new user.
Definition: AuthManager.php:110
MediaWiki\Auth\AuthManager\$watchlistManager
WatchlistManager $watchlistManager
Definition: AuthManager.php:184
MediaWiki\Auth\AuthManager\$logger
LoggerInterface $logger
Definition: AuthManager.php:151
MediaWiki\User\BotPasswordStore
Definition: BotPasswordStore.php:44
MediaWiki\Auth\AuthManager\$primaryAuthenticationProviders
PrimaryAuthenticationProvider[] $primaryAuthenticationProviders
Definition: AuthManager.php:163
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:1225
MediaWiki\Auth\AuthManager\$secondaryAuthenticationProviders
SecondaryAuthenticationProvider[] $secondaryAuthenticationProviders
Definition: AuthManager.php:166
MediaWiki\Auth\AuthManager\forcePrimaryAuthenticationProviders
forcePrimaryAuthenticationProviders(array $providers, $why)
Force certain PrimaryAuthenticationProviders.
Definition: AuthManager.php:280
MediaWiki\Auth\AuthManager\getHookRunner
getHookRunner()
Definition: AuthManager.php:2547
MediaWiki\Auth\AuthManager\ACTION_CHANGE
const ACTION_CHANGE
Change a user's credentials.
Definition: AuthManager.php:122
MediaWiki\Auth\AuthManager\beginAccountCreation
beginAccountCreation(Authority $creator, array $reqs, $returnToUrl)
Start an account creation flow.
Definition: AuthManager.php:1124
MediaWiki\Auth\AuthManager\callMethodOnProviders
callMethodOnProviders( $which, $method, array $args)
Definition: AuthManager.php:2521
MediaWiki\Auth\AuthManager\ACTION_LINK
const ACTION_LINK
Link an existing user to a third-party account.
Definition: AuthManager.php:116
MediaWiki\Auth\AuthManager\allowsPropertyChange
allowsPropertyChange( $property)
Determine whether a user property should be allowed to be changed.
Definition: AuthManager.php:2282
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:484
MediaWiki\Auth\AuthManager\securitySensitiveOperationStatus
securitySensitiveOperationStatus( $operation)
Whether security-sensitive operations should proceed.
Definition: AuthManager.php:809
MediaWiki\Auth\AuthManager
This serves as the entry point to the authentication system.
Definition: AuthManager.php:102
WebRequest
The WebRequest class encapsulates getting at data passed in the URL or via a POSTed form stripping il...
Definition: WebRequest.php:43
MediaWiki\Permissions\PermissionStatus
A StatusValue for permission errors.
Definition: PermissionStatus.php:35
MediaWiki\User\UserOptionsManager
A service class to control user options.
Definition: UserOptionsManager.php:43
MediaWiki\Auth\AuthManager\$hookRunner
HookRunner $hookRunner
Definition: AuthManager.php:175
MediaWiki\Auth\AuthManager\ACTION_REMOVE
const ACTION_REMOVE
Remove a user's credentials.
Definition: AuthManager.php:124
MediaWiki\Auth\AuthManager\SEC_OK
const SEC_OK
Security-sensitive operations are ok.
Definition: AuthManager.php:129
User\setId
setId( $v)
Set the user and reload all fields according to a given ID.
Definition: User.php:2106
MediaWiki\$action
string $action
Cache what action this request is.
Definition: MediaWiki.php:45
MediaWiki\Auth\AuthManager\$objectFactory
ObjectFactory $objectFactory
Definition: AuthManager.php:148
$cache
$cache
Definition: mcc.php:33
User\setToken
setToken( $token=false)
Set the random token (used for persistent authentication) Called from loadDefaults() among other plac...
Definition: User.php:2416
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:2471
MediaWiki\Auth\AuthManager\$createdAccountAuthenticationRequests
CreatedAccountAuthenticationRequest[] $createdAccountAuthenticationRequests
Definition: AuthManager.php:169
MediaWiki\Auth\AuthManager\$config
Config $config
Definition: AuthManager.php:145
MediaWiki\Auth\AuthManager\canCreateAccount
canCreateAccount( $username, $options=[])
Determine whether a particular account can be created.
Definition: AuthManager.php:1034
MediaWiki\Auth\AuthManager\$contentLanguage
Language $contentLanguage
Definition: AuthManager.php:190
$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:104
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:2456
MediaWiki\HookContainer\HookRunner
This class provides an implementation of the core hook interfaces, forwarding hook calls to HookConta...
Definition: HookRunner.php:556
MediaWiki\Auth\AuthManager\userCanAuthenticate
userCanAuthenticate( $username)
Determine whether a username can authenticate.
Definition: AuthManager.php:886
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:949
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:68
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
MediaWiki\Auth
Definition: AbstractAuthenticationProvider.php:22
MediaWiki\Auth\AuthManager\setLogger
setLogger(LoggerInterface $logger)
Definition: AuthManager.php:263
MediaWiki\Auth\AuthManager\providerArrayFromSpecs
providerArrayFromSpecs( $class, array $specs)
Create an array of AuthenticationProviders from an array of ObjectFactory specs.
Definition: AuthManager.php:2386
MediaWiki\Auth\AuthManager\$botPasswordStore
BotPasswordStore $botPasswordStore
Definition: AuthManager.php:196
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:2115
MediaWiki\User\UserFactory
Creates User objects.
Definition: UserFactory.php:41
Language
Internationalisation code See https://www.mediawiki.org/wiki/Special:MyLanguage/Localisation for more...
Definition: Language.php:42
Wikimedia\Rdbms\ILoadBalancer
Database cluster connection, tracking, load balancing, and transaction manager interface.
Definition: ILoadBalancer.php:81
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\AuthenticationProvider
An AuthenticationProvider is used by AuthManager when authenticating users.
Definition: AuthenticationProvider.php:39