MediaWiki  1.30.0
AuthManager.php
Go to the documentation of this file.
1 <?php
24 namespace MediaWiki\Auth;
25 
26 use Config;
28 use Psr\Log\LoggerAwareInterface;
29 use Psr\Log\LoggerInterface;
30 use Status;
32 use User;
34 
82 class AuthManager implements LoggerAwareInterface {
84  const ACTION_LOGIN = 'login';
87  const ACTION_LOGIN_CONTINUE = 'login-continue';
89  const ACTION_CREATE = 'create';
92  const ACTION_CREATE_CONTINUE = 'create-continue';
94  const ACTION_LINK = 'link';
97  const ACTION_LINK_CONTINUE = 'link-continue';
99  const ACTION_CHANGE = 'change';
101  const ACTION_REMOVE = 'remove';
103  const ACTION_UNLINK = 'unlink';
104 
106  const SEC_OK = 'ok';
108  const SEC_REAUTH = 'reauth';
110  const SEC_FAIL = 'fail';
111 
114 
116  private static $instance = null;
117 
119  private $request;
120 
122  private $config;
123 
125  private $logger;
126 
129 
132 
135 
138 
141 
146  public static function singleton() {
147  if ( self::$instance === null ) {
148  self::$instance = new self(
149  \RequestContext::getMain()->getRequest(),
150  MediaWikiServices::getInstance()->getMainConfig()
151  );
152  }
153  return self::$instance;
154  }
155 
161  $this->request = $request;
162  $this->config = $config;
163  $this->setLogger( \MediaWiki\Logger\LoggerFactory::getInstance( 'authentication' ) );
164  }
165 
169  public function setLogger( LoggerInterface $logger ) {
170  $this->logger = $logger;
171  }
172 
176  public function getRequest() {
177  return $this->request;
178  }
179 
186  public function forcePrimaryAuthenticationProviders( array $providers, $why ) {
187  $this->logger->warning( "Overriding AuthManager primary authn because $why" );
188 
189  if ( $this->primaryAuthenticationProviders !== null ) {
190  $this->logger->warning(
191  'PrimaryAuthenticationProviders have already been accessed! I hope nothing breaks.'
192  );
193 
194  $this->allAuthenticationProviders = array_diff_key(
195  $this->allAuthenticationProviders,
196  $this->primaryAuthenticationProviders
197  );
198  $session = $this->request->getSession();
199  $session->remove( 'AuthManager::authnState' );
200  $session->remove( 'AuthManager::accountCreationState' );
201  $session->remove( 'AuthManager::accountLinkState' );
202  $this->createdAccountAuthenticationRequests = [];
203  }
204 
205  $this->primaryAuthenticationProviders = [];
206  foreach ( $providers as $provider ) {
207  if ( !$provider instanceof PrimaryAuthenticationProvider ) {
208  throw new \RuntimeException(
209  'Expected instance of MediaWiki\\Auth\\PrimaryAuthenticationProvider, got ' .
210  get_class( $provider )
211  );
212  }
213  $provider->setLogger( $this->logger );
214  $provider->setManager( $this );
215  $provider->setConfig( $this->config );
216  $id = $provider->getUniqueId();
217  if ( isset( $this->allAuthenticationProviders[$id] ) ) {
218  throw new \RuntimeException(
219  "Duplicate specifications for id $id (classes " .
220  get_class( $provider ) . ' and ' .
221  get_class( $this->allAuthenticationProviders[$id] ) . ')'
222  );
223  }
224  $this->allAuthenticationProviders[$id] = $provider;
225  $this->primaryAuthenticationProviders[$id] = $provider;
226  }
227  }
228 
238  public static function callLegacyAuthPlugin( $method, array $params, $return = null ) {
239  global $wgAuth;
240 
241  if ( $wgAuth && !$wgAuth instanceof AuthManagerAuthPlugin ) {
242  return call_user_func_array( [ $wgAuth, $method ], $params );
243  } else {
244  return $return;
245  }
246  }
247 
261  public function canAuthenticateNow() {
262  return $this->request->getSession()->canSetUser();
263  }
264 
283  public function beginAuthentication( array $reqs, $returnToUrl ) {
284  $session = $this->request->getSession();
285  if ( !$session->canSetUser() ) {
286  // Caller should have called canAuthenticateNow()
287  $session->remove( 'AuthManager::authnState' );
288  throw new \LogicException( 'Authentication is not possible now' );
289  }
290 
291  $guessUserName = null;
292  foreach ( $reqs as $req ) {
293  $req->returnToUrl = $returnToUrl;
294  // @codeCoverageIgnoreStart
295  if ( $req->username !== null && $req->username !== '' ) {
296  if ( $guessUserName === null ) {
297  $guessUserName = $req->username;
298  } elseif ( $guessUserName !== $req->username ) {
299  $guessUserName = null;
300  break;
301  }
302  }
303  // @codeCoverageIgnoreEnd
304  }
305 
306  // Check for special-case login of a just-created account
309  );
310  if ( $req ) {
311  if ( !in_array( $req, $this->createdAccountAuthenticationRequests, true ) ) {
312  throw new \LogicException(
313  'CreatedAccountAuthenticationRequests are only valid on ' .
314  'the same AuthManager that created the account'
315  );
316  }
317 
318  $user = User::newFromName( $req->username );
319  // @codeCoverageIgnoreStart
320  if ( !$user ) {
321  throw new \UnexpectedValueException(
322  "CreatedAccountAuthenticationRequest had invalid username \"{$req->username}\""
323  );
324  } elseif ( $user->getId() != $req->id ) {
325  throw new \UnexpectedValueException(
326  "ID for \"{$req->username}\" was {$user->getId()}, expected {$req->id}"
327  );
328  }
329  // @codeCoverageIgnoreEnd
330 
331  $this->logger->info( 'Logging in {user} after account creation', [
332  'user' => $user->getName(),
333  ] );
335  $this->setSessionDataForUser( $user );
336  $this->callMethodOnProviders( 7, 'postAuthentication', [ $user, $ret ] );
337  $session->remove( 'AuthManager::authnState' );
338  \Hooks::run( 'AuthManagerLoginAuthenticateAudit', [ $ret, $user, $user->getName() ] );
339  return $ret;
340  }
341 
342  $this->removeAuthenticationSessionData( null );
343 
344  foreach ( $this->getPreAuthenticationProviders() as $provider ) {
345  $status = $provider->testForAuthentication( $reqs );
346  if ( !$status->isGood() ) {
347  $this->logger->debug( 'Login failed in pre-authentication by ' . $provider->getUniqueId() );
349  Status::wrap( $status )->getMessage()
350  );
351  $this->callMethodOnProviders( 7, 'postAuthentication',
352  [ User::newFromName( $guessUserName ) ?: null, $ret ]
353  );
354  \Hooks::run( 'AuthManagerLoginAuthenticateAudit', [ $ret, null, $guessUserName ] );
355  return $ret;
356  }
357  }
358 
359  $state = [
360  'reqs' => $reqs,
361  'returnToUrl' => $returnToUrl,
362  'guessUserName' => $guessUserName,
363  'primary' => null,
364  'primaryResponse' => null,
365  'secondary' => [],
366  'maybeLink' => [],
367  'continueRequests' => [],
368  ];
369 
370  // Preserve state from a previous failed login
373  );
374  if ( $req ) {
375  $state['maybeLink'] = $req->maybeLink;
376  }
377 
378  $session = $this->request->getSession();
379  $session->setSecret( 'AuthManager::authnState', $state );
380  $session->persist();
381 
382  return $this->continueAuthentication( $reqs );
383  }
384 
407  public function continueAuthentication( array $reqs ) {
408  $session = $this->request->getSession();
409  try {
410  if ( !$session->canSetUser() ) {
411  // Caller should have called canAuthenticateNow()
412  // @codeCoverageIgnoreStart
413  throw new \LogicException( 'Authentication is not possible now' );
414  // @codeCoverageIgnoreEnd
415  }
416 
417  $state = $session->getSecret( 'AuthManager::authnState' );
418  if ( !is_array( $state ) ) {
420  wfMessage( 'authmanager-authn-not-in-progress' )
421  );
422  }
423  $state['continueRequests'] = [];
424 
425  $guessUserName = $state['guessUserName'];
426 
427  foreach ( $reqs as $req ) {
428  $req->returnToUrl = $state['returnToUrl'];
429  }
430 
431  // Step 1: Choose an primary authentication provider, and call it until it succeeds.
432 
433  if ( $state['primary'] === null ) {
434  // We haven't picked a PrimaryAuthenticationProvider yet
435  // @codeCoverageIgnoreStart
436  $guessUserName = null;
437  foreach ( $reqs as $req ) {
438  if ( $req->username !== null && $req->username !== '' ) {
439  if ( $guessUserName === null ) {
440  $guessUserName = $req->username;
441  } elseif ( $guessUserName !== $req->username ) {
442  $guessUserName = null;
443  break;
444  }
445  }
446  }
447  $state['guessUserName'] = $guessUserName;
448  // @codeCoverageIgnoreEnd
449  $state['reqs'] = $reqs;
450 
451  foreach ( $this->getPrimaryAuthenticationProviders() as $id => $provider ) {
452  $res = $provider->beginPrimaryAuthentication( $reqs );
453  switch ( $res->status ) {
455  $state['primary'] = $id;
456  $state['primaryResponse'] = $res;
457  $this->logger->debug( "Primary login with $id succeeded" );
458  break 2;
460  $this->logger->debug( "Login failed in primary authentication by $id" );
461  if ( $res->createRequest || $state['maybeLink'] ) {
462  $res->createRequest = new CreateFromLoginAuthenticationRequest(
463  $res->createRequest, $state['maybeLink']
464  );
465  }
466  $this->callMethodOnProviders( 7, 'postAuthentication',
467  [ User::newFromName( $guessUserName ) ?: null, $res ]
468  );
469  $session->remove( 'AuthManager::authnState' );
470  \Hooks::run( 'AuthManagerLoginAuthenticateAudit', [ $res, null, $guessUserName ] );
471  return $res;
473  // Continue loop
474  break;
477  $this->logger->debug( "Primary login with $id returned $res->status" );
478  $this->fillRequests( $res->neededRequests, self::ACTION_LOGIN, $guessUserName );
479  $state['primary'] = $id;
480  $state['continueRequests'] = $res->neededRequests;
481  $session->setSecret( 'AuthManager::authnState', $state );
482  return $res;
483 
484  // @codeCoverageIgnoreStart
485  default:
486  throw new \DomainException(
487  get_class( $provider ) . "::beginPrimaryAuthentication() returned $res->status"
488  );
489  // @codeCoverageIgnoreEnd
490  }
491  }
492  if ( $state['primary'] === null ) {
493  $this->logger->debug( 'Login failed in primary authentication because no provider accepted' );
495  wfMessage( 'authmanager-authn-no-primary' )
496  );
497  $this->callMethodOnProviders( 7, 'postAuthentication',
498  [ User::newFromName( $guessUserName ) ?: null, $ret ]
499  );
500  $session->remove( 'AuthManager::authnState' );
501  return $ret;
502  }
503  } elseif ( $state['primaryResponse'] === null ) {
504  $provider = $this->getAuthenticationProvider( $state['primary'] );
505  if ( !$provider instanceof PrimaryAuthenticationProvider ) {
506  // Configuration changed? Force them to start over.
507  // @codeCoverageIgnoreStart
509  wfMessage( 'authmanager-authn-not-in-progress' )
510  );
511  $this->callMethodOnProviders( 7, 'postAuthentication',
512  [ User::newFromName( $guessUserName ) ?: null, $ret ]
513  );
514  $session->remove( 'AuthManager::authnState' );
515  return $ret;
516  // @codeCoverageIgnoreEnd
517  }
518  $id = $provider->getUniqueId();
519  $res = $provider->continuePrimaryAuthentication( $reqs );
520  switch ( $res->status ) {
522  $state['primaryResponse'] = $res;
523  $this->logger->debug( "Primary login with $id succeeded" );
524  break;
526  $this->logger->debug( "Login failed in primary authentication by $id" );
527  if ( $res->createRequest || $state['maybeLink'] ) {
528  $res->createRequest = new CreateFromLoginAuthenticationRequest(
529  $res->createRequest, $state['maybeLink']
530  );
531  }
532  $this->callMethodOnProviders( 7, 'postAuthentication',
533  [ User::newFromName( $guessUserName ) ?: null, $res ]
534  );
535  $session->remove( 'AuthManager::authnState' );
536  \Hooks::run( 'AuthManagerLoginAuthenticateAudit', [ $res, null, $guessUserName ] );
537  return $res;
540  $this->logger->debug( "Primary login with $id returned $res->status" );
541  $this->fillRequests( $res->neededRequests, self::ACTION_LOGIN, $guessUserName );
542  $state['continueRequests'] = $res->neededRequests;
543  $session->setSecret( 'AuthManager::authnState', $state );
544  return $res;
545  default:
546  throw new \DomainException(
547  get_class( $provider ) . "::continuePrimaryAuthentication() returned $res->status"
548  );
549  }
550  }
551 
552  $res = $state['primaryResponse'];
553  if ( $res->username === null ) {
554  $provider = $this->getAuthenticationProvider( $state['primary'] );
555  if ( !$provider instanceof PrimaryAuthenticationProvider ) {
556  // Configuration changed? Force them to start over.
557  // @codeCoverageIgnoreStart
559  wfMessage( 'authmanager-authn-not-in-progress' )
560  );
561  $this->callMethodOnProviders( 7, 'postAuthentication',
562  [ User::newFromName( $guessUserName ) ?: null, $ret ]
563  );
564  $session->remove( 'AuthManager::authnState' );
565  return $ret;
566  // @codeCoverageIgnoreEnd
567  }
568 
569  if ( $provider->accountCreationType() === PrimaryAuthenticationProvider::TYPE_LINK &&
570  $res->linkRequest &&
571  // don't confuse the user with an incorrect message if linking is disabled
573  ) {
574  $state['maybeLink'][$res->linkRequest->getUniqueId()] = $res->linkRequest;
575  $msg = 'authmanager-authn-no-local-user-link';
576  } else {
577  $msg = 'authmanager-authn-no-local-user';
578  }
579  $this->logger->debug(
580  "Primary login with {$provider->getUniqueId()} succeeded, but returned no user"
581  );
583  $ret->neededRequests = $this->getAuthenticationRequestsInternal(
584  self::ACTION_LOGIN,
585  [],
587  );
588  if ( $res->createRequest || $state['maybeLink'] ) {
589  $ret->createRequest = new CreateFromLoginAuthenticationRequest(
590  $res->createRequest, $state['maybeLink']
591  );
592  $ret->neededRequests[] = $ret->createRequest;
593  }
594  $this->fillRequests( $ret->neededRequests, self::ACTION_LOGIN, null, true );
595  $session->setSecret( 'AuthManager::authnState', [
596  'reqs' => [], // Will be filled in later
597  'primary' => null,
598  'primaryResponse' => null,
599  'secondary' => [],
600  'continueRequests' => $ret->neededRequests,
601  ] + $state );
602  return $ret;
603  }
604 
605  // Step 2: Primary authentication succeeded, create the User object
606  // (and add the user locally if necessary)
607 
608  $user = User::newFromName( $res->username, 'usable' );
609  if ( !$user ) {
610  $provider = $this->getAuthenticationProvider( $state['primary'] );
611  throw new \DomainException(
612  get_class( $provider ) . " returned an invalid username: {$res->username}"
613  );
614  }
615  if ( $user->getId() === 0 ) {
616  // User doesn't exist locally. Create it.
617  $this->logger->info( 'Auto-creating {user} on login', [
618  'user' => $user->getName(),
619  ] );
620  $status = $this->autoCreateUser( $user, $state['primary'], false );
621  if ( !$status->isGood() ) {
623  Status::wrap( $status )->getMessage( 'authmanager-authn-autocreate-failed' )
624  );
625  $this->callMethodOnProviders( 7, 'postAuthentication', [ $user, $ret ] );
626  $session->remove( 'AuthManager::authnState' );
627  \Hooks::run( 'AuthManagerLoginAuthenticateAudit', [ $ret, $user, $user->getName() ] );
628  return $ret;
629  }
630  }
631 
632  // Step 3: Iterate over all the secondary authentication providers.
633 
634  $beginReqs = $state['reqs'];
635 
636  foreach ( $this->getSecondaryAuthenticationProviders() as $id => $provider ) {
637  if ( !isset( $state['secondary'][$id] ) ) {
638  // This provider isn't started yet, so we pass it the set
639  // of reqs from beginAuthentication instead of whatever
640  // might have been used by a previous provider in line.
641  $func = 'beginSecondaryAuthentication';
642  $res = $provider->beginSecondaryAuthentication( $user, $beginReqs );
643  } elseif ( !$state['secondary'][$id] ) {
644  $func = 'continueSecondaryAuthentication';
645  $res = $provider->continueSecondaryAuthentication( $user, $reqs );
646  } else {
647  continue;
648  }
649  switch ( $res->status ) {
651  $this->logger->debug( "Secondary login with $id succeeded" );
652  // fall through
654  $state['secondary'][$id] = true;
655  break;
657  $this->logger->debug( "Login failed in secondary authentication by $id" );
658  $this->callMethodOnProviders( 7, 'postAuthentication', [ $user, $res ] );
659  $session->remove( 'AuthManager::authnState' );
660  \Hooks::run( 'AuthManagerLoginAuthenticateAudit', [ $res, $user, $user->getName() ] );
661  return $res;
664  $this->logger->debug( "Secondary login with $id returned " . $res->status );
665  $this->fillRequests( $res->neededRequests, self::ACTION_LOGIN, $user->getName() );
666  $state['secondary'][$id] = false;
667  $state['continueRequests'] = $res->neededRequests;
668  $session->setSecret( 'AuthManager::authnState', $state );
669  return $res;
670 
671  // @codeCoverageIgnoreStart
672  default:
673  throw new \DomainException(
674  get_class( $provider ) . "::{$func}() returned $res->status"
675  );
676  // @codeCoverageIgnoreEnd
677  }
678  }
679 
680  // Step 4: Authentication complete! Set the user in the session and
681  // clean up.
682 
683  $this->logger->info( 'Login for {user} succeeded', [
684  'user' => $user->getName(),
685  ] );
689  );
690  $this->setSessionDataForUser( $user, $req && $req->rememberMe );
692  $this->callMethodOnProviders( 7, 'postAuthentication', [ $user, $ret ] );
693  $session->remove( 'AuthManager::authnState' );
694  $this->removeAuthenticationSessionData( null );
695  \Hooks::run( 'AuthManagerLoginAuthenticateAudit', [ $ret, $user, $user->getName() ] );
696  return $ret;
697  } catch ( \Exception $ex ) {
698  $session->remove( 'AuthManager::authnState' );
699  throw $ex;
700  }
701  }
702 
714  public function securitySensitiveOperationStatus( $operation ) {
716 
717  $this->logger->debug( __METHOD__ . ": Checking $operation" );
718 
719  $session = $this->request->getSession();
720  $aId = $session->getUser()->getId();
721  if ( $aId === 0 ) {
722  // User isn't authenticated. DWIM?
724  $this->logger->info( __METHOD__ . ": Not logged in! $operation is $status" );
725  return $status;
726  }
727 
728  if ( $session->canSetUser() ) {
729  $id = $session->get( 'AuthManager:lastAuthId' );
730  $last = $session->get( 'AuthManager:lastAuthTimestamp' );
731  if ( $id !== $aId || $last === null ) {
732  $timeSinceLogin = PHP_INT_MAX; // Forever ago
733  } else {
734  $timeSinceLogin = max( 0, time() - $last );
735  }
736 
737  $thresholds = $this->config->get( 'ReauthenticateTime' );
738  if ( isset( $thresholds[$operation] ) ) {
739  $threshold = $thresholds[$operation];
740  } elseif ( isset( $thresholds['default'] ) ) {
741  $threshold = $thresholds['default'];
742  } else {
743  throw new \UnexpectedValueException( '$wgReauthenticateTime lacks a default' );
744  }
745 
746  if ( $threshold >= 0 && $timeSinceLogin > $threshold ) {
748  }
749  } else {
750  $timeSinceLogin = -1;
751 
752  $pass = $this->config->get( 'AllowSecuritySensitiveOperationIfCannotReauthenticate' );
753  if ( isset( $pass[$operation] ) ) {
754  $status = $pass[$operation] ? self::SEC_OK : self::SEC_FAIL;
755  } elseif ( isset( $pass['default'] ) ) {
756  $status = $pass['default'] ? self::SEC_OK : self::SEC_FAIL;
757  } else {
758  throw new \UnexpectedValueException(
759  '$wgAllowSecuritySensitiveOperationIfCannotReauthenticate lacks a default'
760  );
761  }
762  }
763 
764  \Hooks::run( 'SecuritySensitiveOperationStatus', [
765  &$status, $operation, $session, $timeSinceLogin
766  ] );
767 
768  // If authentication is not possible, downgrade from "REAUTH" to "FAIL".
769  if ( !$this->canAuthenticateNow() && $status === self::SEC_REAUTH ) {
771  }
772 
773  $this->logger->info( __METHOD__ . ": $operation is $status" );
774 
775  return $status;
776  }
777 
787  public function userCanAuthenticate( $username ) {
788  foreach ( $this->getPrimaryAuthenticationProviders() as $provider ) {
789  if ( $provider->testUserCanAuthenticate( $username ) ) {
790  return true;
791  }
792  }
793  return false;
794  }
795 
810  public function normalizeUsername( $username ) {
811  $ret = [];
812  foreach ( $this->getPrimaryAuthenticationProviders() as $provider ) {
813  $normalized = $provider->providerNormalizeUsername( $username );
814  if ( $normalized !== null ) {
815  $ret[$normalized] = true;
816  }
817  }
818  return array_keys( $ret );
819  }
820 
835  public function revokeAccessForUser( $username ) {
836  $this->logger->info( 'Revoking access for {user}', [
837  'user' => $username,
838  ] );
839  $this->callMethodOnProviders( 6, 'providerRevokeAccessForUser', [ $username ] );
840  }
841 
851  public function allowsAuthenticationDataChange( AuthenticationRequest $req, $checkData = true ) {
852  $any = false;
853  $providers = $this->getPrimaryAuthenticationProviders() +
855  foreach ( $providers as $provider ) {
856  $status = $provider->providerAllowsAuthenticationDataChange( $req, $checkData );
857  if ( !$status->isGood() ) {
858  return Status::wrap( $status );
859  }
860  $any = $any || $status->value !== 'ignored';
861  }
862  if ( !$any ) {
863  $status = Status::newGood( 'ignored' );
864  $status->warning( 'authmanager-change-not-supported' );
865  return $status;
866  }
867  return Status::newGood();
868  }
869 
885  $this->logger->info( 'Changing authentication data for {user} class {what}', [
886  'user' => is_string( $req->username ) ? $req->username : '<no name>',
887  'what' => get_class( $req ),
888  ] );
889 
890  $this->callMethodOnProviders( 6, 'providerChangeAuthenticationData', [ $req ] );
891 
892  // When the main account's authentication data is changed, invalidate
893  // all BotPasswords too.
895  }
896 
908  public function canCreateAccounts() {
909  foreach ( $this->getPrimaryAuthenticationProviders() as $provider ) {
910  switch ( $provider->accountCreationType() ) {
913  return true;
914  }
915  }
916  return false;
917  }
918 
927  public function canCreateAccount( $username, $options = [] ) {
928  // Back compat
929  if ( is_int( $options ) ) {
930  $options = [ 'flags' => $options ];
931  }
932  $options += [
933  'flags' => User::READ_NORMAL,
934  'creating' => false,
935  ];
936  $flags = $options['flags'];
937 
938  if ( !$this->canCreateAccounts() ) {
939  return Status::newFatal( 'authmanager-create-disabled' );
940  }
941 
942  if ( $this->userExists( $username, $flags ) ) {
943  return Status::newFatal( 'userexists' );
944  }
945 
946  $user = User::newFromName( $username, 'creatable' );
947  if ( !is_object( $user ) ) {
948  return Status::newFatal( 'noname' );
949  } else {
950  $user->load( $flags ); // Explicitly load with $flags, auto-loading always uses READ_NORMAL
951  if ( $user->getId() !== 0 ) {
952  return Status::newFatal( 'userexists' );
953  }
954  }
955 
956  // Denied by providers?
957  $providers = $this->getPreAuthenticationProviders() +
960  foreach ( $providers as $provider ) {
961  $status = $provider->testUserForCreation( $user, false, $options );
962  if ( !$status->isGood() ) {
963  return Status::wrap( $status );
964  }
965  }
966 
967  return Status::newGood();
968  }
969 
975  public function checkAccountCreatePermissions( User $creator ) {
976  // Wiki is read-only?
977  if ( wfReadOnly() ) {
978  return Status::newFatal( wfMessage( 'readonlytext', wfReadOnlyReason() ) );
979  }
980 
981  // This is awful, this permission check really shouldn't go through Title.
982  $permErrors = \SpecialPage::getTitleFor( 'CreateAccount' )
983  ->getUserPermissionsErrors( 'createaccount', $creator, 'secure' );
984  if ( $permErrors ) {
986  foreach ( $permErrors as $args ) {
987  call_user_func_array( [ $status, 'fatal' ], $args );
988  }
989  return $status;
990  }
991 
992  $block = $creator->isBlockedFromCreateAccount();
993  if ( $block ) {
994  $errorParams = [
995  $block->getTarget(),
996  $block->mReason ?: wfMessage( 'blockednoreason' )->text(),
997  $block->getByName()
998  ];
999 
1000  if ( $block->getType() === \Block::TYPE_RANGE ) {
1001  $errorMessage = 'cantcreateaccount-range-text';
1002  $errorParams[] = $this->getRequest()->getIP();
1003  } else {
1004  $errorMessage = 'cantcreateaccount-text';
1005  }
1006 
1007  return Status::newFatal( wfMessage( $errorMessage, $errorParams ) );
1008  }
1009 
1010  $ip = $this->getRequest()->getIP();
1011  if ( $creator->isDnsBlacklisted( $ip, true /* check $wgProxyWhitelist */ ) ) {
1012  return Status::newFatal( 'sorbs_create_account_reason' );
1013  }
1014 
1015  return Status::newGood();
1016  }
1017 
1037  public function beginAccountCreation( User $creator, array $reqs, $returnToUrl ) {
1038  $session = $this->request->getSession();
1039  if ( !$this->canCreateAccounts() ) {
1040  // Caller should have called canCreateAccounts()
1041  $session->remove( 'AuthManager::accountCreationState' );
1042  throw new \LogicException( 'Account creation is not possible' );
1043  }
1044 
1045  try {
1047  } catch ( \UnexpectedValueException $ex ) {
1048  $username = null;
1049  }
1050  if ( $username === null ) {
1051  $this->logger->debug( __METHOD__ . ': No username provided' );
1052  return AuthenticationResponse::newFail( wfMessage( 'noname' ) );
1053  }
1054 
1055  // Permissions check
1056  $status = $this->checkAccountCreatePermissions( $creator );
1057  if ( !$status->isGood() ) {
1058  $this->logger->debug( __METHOD__ . ': {creator} cannot create users: {reason}', [
1059  'user' => $username,
1060  'creator' => $creator->getName(),
1061  'reason' => $status->getWikiText( null, null, 'en' )
1062  ] );
1063  return AuthenticationResponse::newFail( $status->getMessage() );
1064  }
1065 
1066  $status = $this->canCreateAccount(
1067  $username, [ 'flags' => User::READ_LOCKING, 'creating' => true ]
1068  );
1069  if ( !$status->isGood() ) {
1070  $this->logger->debug( __METHOD__ . ': {user} cannot be created: {reason}', [
1071  'user' => $username,
1072  'creator' => $creator->getName(),
1073  'reason' => $status->getWikiText( null, null, 'en' )
1074  ] );
1075  return AuthenticationResponse::newFail( $status->getMessage() );
1076  }
1077 
1078  $user = User::newFromName( $username, 'creatable' );
1079  foreach ( $reqs as $req ) {
1080  $req->username = $username;
1081  $req->returnToUrl = $returnToUrl;
1082  if ( $req instanceof UserDataAuthenticationRequest ) {
1083  $status = $req->populateUser( $user );
1084  if ( !$status->isGood() ) {
1086  $session->remove( 'AuthManager::accountCreationState' );
1087  $this->logger->debug( __METHOD__ . ': UserData is invalid: {reason}', [
1088  'user' => $user->getName(),
1089  'creator' => $creator->getName(),
1090  'reason' => $status->getWikiText( null, null, 'en' ),
1091  ] );
1092  return AuthenticationResponse::newFail( $status->getMessage() );
1093  }
1094  }
1095  }
1096 
1097  $this->removeAuthenticationSessionData( null );
1098 
1099  $state = [
1100  'username' => $username,
1101  'userid' => 0,
1102  'creatorid' => $creator->getId(),
1103  'creatorname' => $creator->getName(),
1104  'reqs' => $reqs,
1105  'returnToUrl' => $returnToUrl,
1106  'primary' => null,
1107  'primaryResponse' => null,
1108  'secondary' => [],
1109  'continueRequests' => [],
1110  'maybeLink' => [],
1111  'ranPreTests' => false,
1112  ];
1113 
1114  // Special case: converting a login to an account creation
1117  );
1118  if ( $req ) {
1119  $state['maybeLink'] = $req->maybeLink;
1120 
1121  if ( $req->createRequest ) {
1122  $reqs[] = $req->createRequest;
1123  $state['reqs'][] = $req->createRequest;
1124  }
1125  }
1126 
1127  $session->setSecret( 'AuthManager::accountCreationState', $state );
1128  $session->persist();
1129 
1130  return $this->continueAccountCreation( $reqs );
1131  }
1132 
1138  public function continueAccountCreation( array $reqs ) {
1139  $session = $this->request->getSession();
1140  try {
1141  if ( !$this->canCreateAccounts() ) {
1142  // Caller should have called canCreateAccounts()
1143  $session->remove( 'AuthManager::accountCreationState' );
1144  throw new \LogicException( 'Account creation is not possible' );
1145  }
1146 
1147  $state = $session->getSecret( 'AuthManager::accountCreationState' );
1148  if ( !is_array( $state ) ) {
1150  wfMessage( 'authmanager-create-not-in-progress' )
1151  );
1152  }
1153  $state['continueRequests'] = [];
1154 
1155  // Step 0: Prepare and validate the input
1156 
1157  $user = User::newFromName( $state['username'], 'creatable' );
1158  if ( !is_object( $user ) ) {
1159  $session->remove( 'AuthManager::accountCreationState' );
1160  $this->logger->debug( __METHOD__ . ': Invalid username', [
1161  'user' => $state['username'],
1162  ] );
1163  return AuthenticationResponse::newFail( wfMessage( 'noname' ) );
1164  }
1165 
1166  if ( $state['creatorid'] ) {
1167  $creator = User::newFromId( $state['creatorid'] );
1168  } else {
1169  $creator = new User;
1170  $creator->setName( $state['creatorname'] );
1171  }
1172 
1173  // Avoid account creation races on double submissions
1175  $lock = $cache->getScopedLock( $cache->makeGlobalKey( 'account', md5( $user->getName() ) ) );
1176  if ( !$lock ) {
1177  // Don't clear AuthManager::accountCreationState for this code
1178  // path because the process that won the race owns it.
1179  $this->logger->debug( __METHOD__ . ': Could not acquire account creation lock', [
1180  'user' => $user->getName(),
1181  'creator' => $creator->getName(),
1182  ] );
1183  return AuthenticationResponse::newFail( wfMessage( 'usernameinprogress' ) );
1184  }
1185 
1186  // Permissions check
1187  $status = $this->checkAccountCreatePermissions( $creator );
1188  if ( !$status->isGood() ) {
1189  $this->logger->debug( __METHOD__ . ': {creator} cannot create users: {reason}', [
1190  'user' => $user->getName(),
1191  'creator' => $creator->getName(),
1192  'reason' => $status->getWikiText( null, null, 'en' )
1193  ] );
1194  $ret = AuthenticationResponse::newFail( $status->getMessage() );
1195  $this->callMethodOnProviders( 7, 'postAccountCreation', [ $user, $creator, $ret ] );
1196  $session->remove( 'AuthManager::accountCreationState' );
1197  return $ret;
1198  }
1199 
1200  // Load from master for existence check
1201  $user->load( User::READ_LOCKING );
1202 
1203  if ( $state['userid'] === 0 ) {
1204  if ( $user->getId() != 0 ) {
1205  $this->logger->debug( __METHOD__ . ': User exists locally', [
1206  'user' => $user->getName(),
1207  'creator' => $creator->getName(),
1208  ] );
1209  $ret = AuthenticationResponse::newFail( wfMessage( 'userexists' ) );
1210  $this->callMethodOnProviders( 7, 'postAccountCreation', [ $user, $creator, $ret ] );
1211  $session->remove( 'AuthManager::accountCreationState' );
1212  return $ret;
1213  }
1214  } else {
1215  if ( $user->getId() == 0 ) {
1216  $this->logger->debug( __METHOD__ . ': User does not exist locally when it should', [
1217  'user' => $user->getName(),
1218  'creator' => $creator->getName(),
1219  'expected_id' => $state['userid'],
1220  ] );
1221  throw new \UnexpectedValueException(
1222  "User \"{$state['username']}\" should exist now, but doesn't!"
1223  );
1224  }
1225  if ( $user->getId() != $state['userid'] ) {
1226  $this->logger->debug( __METHOD__ . ': User ID/name mismatch', [
1227  'user' => $user->getName(),
1228  'creator' => $creator->getName(),
1229  'expected_id' => $state['userid'],
1230  'actual_id' => $user->getId(),
1231  ] );
1232  throw new \UnexpectedValueException(
1233  "User \"{$state['username']}\" exists, but " .
1234  "ID {$user->getId()} != {$state['userid']}!"
1235  );
1236  }
1237  }
1238  foreach ( $state['reqs'] as $req ) {
1239  if ( $req instanceof UserDataAuthenticationRequest ) {
1240  $status = $req->populateUser( $user );
1241  if ( !$status->isGood() ) {
1242  // This should never happen...
1244  $this->logger->debug( __METHOD__ . ': UserData is invalid: {reason}', [
1245  'user' => $user->getName(),
1246  'creator' => $creator->getName(),
1247  'reason' => $status->getWikiText( null, null, 'en' ),
1248  ] );
1249  $ret = AuthenticationResponse::newFail( $status->getMessage() );
1250  $this->callMethodOnProviders( 7, 'postAccountCreation', [ $user, $creator, $ret ] );
1251  $session->remove( 'AuthManager::accountCreationState' );
1252  return $ret;
1253  }
1254  }
1255  }
1256 
1257  foreach ( $reqs as $req ) {
1258  $req->returnToUrl = $state['returnToUrl'];
1259  $req->username = $state['username'];
1260  }
1261 
1262  // Run pre-creation tests, if we haven't already
1263  if ( !$state['ranPreTests'] ) {
1264  $providers = $this->getPreAuthenticationProviders() +
1267  foreach ( $providers as $id => $provider ) {
1268  $status = $provider->testForAccountCreation( $user, $creator, $reqs );
1269  if ( !$status->isGood() ) {
1270  $this->logger->debug( __METHOD__ . ": Fail in pre-authentication by $id", [
1271  'user' => $user->getName(),
1272  'creator' => $creator->getName(),
1273  ] );
1275  Status::wrap( $status )->getMessage()
1276  );
1277  $this->callMethodOnProviders( 7, 'postAccountCreation', [ $user, $creator, $ret ] );
1278  $session->remove( 'AuthManager::accountCreationState' );
1279  return $ret;
1280  }
1281  }
1282 
1283  $state['ranPreTests'] = true;
1284  }
1285 
1286  // Step 1: Choose a primary authentication provider and call it until it succeeds.
1287 
1288  if ( $state['primary'] === null ) {
1289  // We haven't picked a PrimaryAuthenticationProvider yet
1290  foreach ( $this->getPrimaryAuthenticationProviders() as $id => $provider ) {
1291  if ( $provider->accountCreationType() === PrimaryAuthenticationProvider::TYPE_NONE ) {
1292  continue;
1293  }
1294  $res = $provider->beginPrimaryAccountCreation( $user, $creator, $reqs );
1295  switch ( $res->status ) {
1297  $this->logger->debug( __METHOD__ . ": Primary creation passed by $id", [
1298  'user' => $user->getName(),
1299  'creator' => $creator->getName(),
1300  ] );
1301  $state['primary'] = $id;
1302  $state['primaryResponse'] = $res;
1303  break 2;
1305  $this->logger->debug( __METHOD__ . ": Primary creation failed by $id", [
1306  'user' => $user->getName(),
1307  'creator' => $creator->getName(),
1308  ] );
1309  $this->callMethodOnProviders( 7, 'postAccountCreation', [ $user, $creator, $res ] );
1310  $session->remove( 'AuthManager::accountCreationState' );
1311  return $res;
1313  // Continue loop
1314  break;
1317  $this->logger->debug( __METHOD__ . ": Primary creation $res->status by $id", [
1318  'user' => $user->getName(),
1319  'creator' => $creator->getName(),
1320  ] );
1321  $this->fillRequests( $res->neededRequests, self::ACTION_CREATE, null );
1322  $state['primary'] = $id;
1323  $state['continueRequests'] = $res->neededRequests;
1324  $session->setSecret( 'AuthManager::accountCreationState', $state );
1325  return $res;
1326 
1327  // @codeCoverageIgnoreStart
1328  default:
1329  throw new \DomainException(
1330  get_class( $provider ) . "::beginPrimaryAccountCreation() returned $res->status"
1331  );
1332  // @codeCoverageIgnoreEnd
1333  }
1334  }
1335  if ( $state['primary'] === null ) {
1336  $this->logger->debug( __METHOD__ . ': Primary creation failed because no provider accepted', [
1337  'user' => $user->getName(),
1338  'creator' => $creator->getName(),
1339  ] );
1341  wfMessage( 'authmanager-create-no-primary' )
1342  );
1343  $this->callMethodOnProviders( 7, 'postAccountCreation', [ $user, $creator, $ret ] );
1344  $session->remove( 'AuthManager::accountCreationState' );
1345  return $ret;
1346  }
1347  } elseif ( $state['primaryResponse'] === null ) {
1348  $provider = $this->getAuthenticationProvider( $state['primary'] );
1349  if ( !$provider instanceof PrimaryAuthenticationProvider ) {
1350  // Configuration changed? Force them to start over.
1351  // @codeCoverageIgnoreStart
1353  wfMessage( 'authmanager-create-not-in-progress' )
1354  );
1355  $this->callMethodOnProviders( 7, 'postAccountCreation', [ $user, $creator, $ret ] );
1356  $session->remove( 'AuthManager::accountCreationState' );
1357  return $ret;
1358  // @codeCoverageIgnoreEnd
1359  }
1360  $id = $provider->getUniqueId();
1361  $res = $provider->continuePrimaryAccountCreation( $user, $creator, $reqs );
1362  switch ( $res->status ) {
1364  $this->logger->debug( __METHOD__ . ": Primary creation passed by $id", [
1365  'user' => $user->getName(),
1366  'creator' => $creator->getName(),
1367  ] );
1368  $state['primaryResponse'] = $res;
1369  break;
1371  $this->logger->debug( __METHOD__ . ": Primary creation failed by $id", [
1372  'user' => $user->getName(),
1373  'creator' => $creator->getName(),
1374  ] );
1375  $this->callMethodOnProviders( 7, 'postAccountCreation', [ $user, $creator, $res ] );
1376  $session->remove( 'AuthManager::accountCreationState' );
1377  return $res;
1380  $this->logger->debug( __METHOD__ . ": Primary creation $res->status by $id", [
1381  'user' => $user->getName(),
1382  'creator' => $creator->getName(),
1383  ] );
1384  $this->fillRequests( $res->neededRequests, self::ACTION_CREATE, null );
1385  $state['continueRequests'] = $res->neededRequests;
1386  $session->setSecret( 'AuthManager::accountCreationState', $state );
1387  return $res;
1388  default:
1389  throw new \DomainException(
1390  get_class( $provider ) . "::continuePrimaryAccountCreation() returned $res->status"
1391  );
1392  }
1393  }
1394 
1395  // Step 2: Primary authentication succeeded, create the User object
1396  // and add the user locally.
1397 
1398  if ( $state['userid'] === 0 ) {
1399  $this->logger->info( 'Creating user {user} during account creation', [
1400  'user' => $user->getName(),
1401  'creator' => $creator->getName(),
1402  ] );
1403  $status = $user->addToDatabase();
1404  if ( !$status->isOK() ) {
1405  // @codeCoverageIgnoreStart
1406  $ret = AuthenticationResponse::newFail( $status->getMessage() );
1407  $this->callMethodOnProviders( 7, 'postAccountCreation', [ $user, $creator, $ret ] );
1408  $session->remove( 'AuthManager::accountCreationState' );
1409  return $ret;
1410  // @codeCoverageIgnoreEnd
1411  }
1412  $this->setDefaultUserOptions( $user, $creator->isAnon() );
1413  \Hooks::run( 'LocalUserCreated', [ $user, false ] );
1414  $user->saveSettings();
1415  $state['userid'] = $user->getId();
1416 
1417  // Update user count
1418  \DeferredUpdates::addUpdate( new \SiteStatsUpdate( 0, 0, 0, 0, 1 ) );
1419 
1420  // Watch user's userpage and talk page
1421  $user->addWatch( $user->getUserPage(), User::IGNORE_USER_RIGHTS );
1422 
1423  // Inform the provider
1424  $logSubtype = $provider->finishAccountCreation( $user, $creator, $state['primaryResponse'] );
1425 
1426  // Log the creation
1427  if ( $this->config->get( 'NewUserLog' ) ) {
1428  $isAnon = $creator->isAnon();
1429  $logEntry = new \ManualLogEntry(
1430  'newusers',
1431  $logSubtype ?: ( $isAnon ? 'create' : 'create2' )
1432  );
1433  $logEntry->setPerformer( $isAnon ? $user : $creator );
1434  $logEntry->setTarget( $user->getUserPage() );
1438  );
1439  $logEntry->setComment( $req ? $req->reason : '' );
1440  $logEntry->setParameters( [
1441  '4::userid' => $user->getId(),
1442  ] );
1443  $logid = $logEntry->insert();
1444  $logEntry->publish( $logid );
1445  }
1446  }
1447 
1448  // Step 3: Iterate over all the secondary authentication providers.
1449 
1450  $beginReqs = $state['reqs'];
1451 
1452  foreach ( $this->getSecondaryAuthenticationProviders() as $id => $provider ) {
1453  if ( !isset( $state['secondary'][$id] ) ) {
1454  // This provider isn't started yet, so we pass it the set
1455  // of reqs from beginAuthentication instead of whatever
1456  // might have been used by a previous provider in line.
1457  $func = 'beginSecondaryAccountCreation';
1458  $res = $provider->beginSecondaryAccountCreation( $user, $creator, $beginReqs );
1459  } elseif ( !$state['secondary'][$id] ) {
1460  $func = 'continueSecondaryAccountCreation';
1461  $res = $provider->continueSecondaryAccountCreation( $user, $creator, $reqs );
1462  } else {
1463  continue;
1464  }
1465  switch ( $res->status ) {
1467  $this->logger->debug( __METHOD__ . ": Secondary creation passed by $id", [
1468  'user' => $user->getName(),
1469  'creator' => $creator->getName(),
1470  ] );
1471  // fall through
1473  $state['secondary'][$id] = true;
1474  break;
1477  $this->logger->debug( __METHOD__ . ": Secondary creation $res->status by $id", [
1478  'user' => $user->getName(),
1479  'creator' => $creator->getName(),
1480  ] );
1481  $this->fillRequests( $res->neededRequests, self::ACTION_CREATE, null );
1482  $state['secondary'][$id] = false;
1483  $state['continueRequests'] = $res->neededRequests;
1484  $session->setSecret( 'AuthManager::accountCreationState', $state );
1485  return $res;
1487  throw new \DomainException(
1488  get_class( $provider ) . "::{$func}() returned $res->status." .
1489  ' Secondary providers are not allowed to fail account creation, that' .
1490  ' should have been done via testForAccountCreation().'
1491  );
1492  // @codeCoverageIgnoreStart
1493  default:
1494  throw new \DomainException(
1495  get_class( $provider ) . "::{$func}() returned $res->status"
1496  );
1497  // @codeCoverageIgnoreEnd
1498  }
1499  }
1500 
1501  $id = $user->getId();
1502  $name = $user->getName();
1505  $ret->loginRequest = $req;
1506  $this->createdAccountAuthenticationRequests[] = $req;
1507 
1508  $this->logger->info( __METHOD__ . ': Account creation succeeded for {user}', [
1509  'user' => $user->getName(),
1510  'creator' => $creator->getName(),
1511  ] );
1512 
1513  $this->callMethodOnProviders( 7, 'postAccountCreation', [ $user, $creator, $ret ] );
1514  $session->remove( 'AuthManager::accountCreationState' );
1515  $this->removeAuthenticationSessionData( null );
1516  return $ret;
1517  } catch ( \Exception $ex ) {
1518  $session->remove( 'AuthManager::accountCreationState' );
1519  throw $ex;
1520  }
1521  }
1522 
1538  public function autoCreateUser( User $user, $source, $login = true ) {
1539  if ( $source !== self::AUTOCREATE_SOURCE_SESSION &&
1541  ) {
1542  throw new \InvalidArgumentException( "Unknown auto-creation source: $source" );
1543  }
1544 
1545  $username = $user->getName();
1546 
1547  // Try the local user from the replica DB
1548  $localId = User::idFromName( $username );
1549  $flags = User::READ_NORMAL;
1550 
1551  // Fetch the user ID from the master, so that we don't try to create the user
1552  // when they already exist, due to replication lag
1553  // @codeCoverageIgnoreStart
1554  if ( !$localId && wfGetLB()->getReaderIndex() != 0 ) {
1555  $localId = User::idFromName( $username, User::READ_LATEST );
1556  $flags = User::READ_LATEST;
1557  }
1558  // @codeCoverageIgnoreEnd
1559 
1560  if ( $localId ) {
1561  $this->logger->debug( __METHOD__ . ': {username} already exists locally', [
1562  'username' => $username,
1563  ] );
1564  $user->setId( $localId );
1565  $user->loadFromId( $flags );
1566  if ( $login ) {
1567  $this->setSessionDataForUser( $user );
1568  }
1570  $status->warning( 'userexists' );
1571  return $status;
1572  }
1573 
1574  // Wiki is read-only?
1575  if ( wfReadOnly() ) {
1576  $this->logger->debug( __METHOD__ . ': denied by wfReadOnly(): {reason}', [
1577  'username' => $username,
1578  'reason' => wfReadOnlyReason(),
1579  ] );
1580  $user->setId( 0 );
1581  $user->loadFromId();
1582  return Status::newFatal( wfMessage( 'readonlytext', wfReadOnlyReason() ) );
1583  }
1584 
1585  // Check the session, if we tried to create this user already there's
1586  // no point in retrying.
1587  $session = $this->request->getSession();
1588  if ( $session->get( 'AuthManager::AutoCreateBlacklist' ) ) {
1589  $this->logger->debug( __METHOD__ . ': blacklisted in session {sessionid}', [
1590  'username' => $username,
1591  'sessionid' => $session->getId(),
1592  ] );
1593  $user->setId( 0 );
1594  $user->loadFromId();
1595  $reason = $session->get( 'AuthManager::AutoCreateBlacklist' );
1596  if ( $reason instanceof StatusValue ) {
1597  return Status::wrap( $reason );
1598  } else {
1599  return Status::newFatal( $reason );
1600  }
1601  }
1602 
1603  // Is the username creatable?
1604  if ( !User::isCreatableName( $username ) ) {
1605  $this->logger->debug( __METHOD__ . ': name "{username}" is not creatable', [
1606  'username' => $username,
1607  ] );
1608  $session->set( 'AuthManager::AutoCreateBlacklist', 'noname' );
1609  $user->setId( 0 );
1610  $user->loadFromId();
1611  return Status::newFatal( 'noname' );
1612  }
1613 
1614  // Is the IP user able to create accounts?
1615  $anon = new User;
1616  if ( !$anon->isAllowedAny( 'createaccount', 'autocreateaccount' ) ) {
1617  $this->logger->debug( __METHOD__ . ': IP lacks the ability to create or autocreate accounts', [
1618  'username' => $username,
1619  'ip' => $anon->getName(),
1620  ] );
1621  $session->set( 'AuthManager::AutoCreateBlacklist', 'authmanager-autocreate-noperm' );
1622  $session->persist();
1623  $user->setId( 0 );
1624  $user->loadFromId();
1625  return Status::newFatal( 'authmanager-autocreate-noperm' );
1626  }
1627 
1628  // Avoid account creation races on double submissions
1630  $lock = $cache->getScopedLock( $cache->makeGlobalKey( 'account', md5( $username ) ) );
1631  if ( !$lock ) {
1632  $this->logger->debug( __METHOD__ . ': Could not acquire account creation lock', [
1633  'user' => $username,
1634  ] );
1635  $user->setId( 0 );
1636  $user->loadFromId();
1637  return Status::newFatal( 'usernameinprogress' );
1638  }
1639 
1640  // Denied by providers?
1641  $options = [
1642  'flags' => User::READ_LATEST,
1643  'creating' => true,
1644  ];
1645  $providers = $this->getPreAuthenticationProviders() +
1648  foreach ( $providers as $provider ) {
1649  $status = $provider->testUserForCreation( $user, $source, $options );
1650  if ( !$status->isGood() ) {
1651  $ret = Status::wrap( $status );
1652  $this->logger->debug( __METHOD__ . ': Provider denied creation of {username}: {reason}', [
1653  'username' => $username,
1654  'reason' => $ret->getWikiText( null, null, 'en' ),
1655  ] );
1656  $session->set( 'AuthManager::AutoCreateBlacklist', $status );
1657  $user->setId( 0 );
1658  $user->loadFromId();
1659  return $ret;
1660  }
1661  }
1662 
1663  $backoffKey = $cache->makeKey( 'AuthManager', 'autocreate-failed', md5( $username ) );
1664  if ( $cache->get( $backoffKey ) ) {
1665  $this->logger->debug( __METHOD__ . ': {username} denied by prior creation attempt failures', [
1666  'username' => $username,
1667  ] );
1668  $user->setId( 0 );
1669  $user->loadFromId();
1670  return Status::newFatal( 'authmanager-autocreate-exception' );
1671  }
1672 
1673  // Checks passed, create the user...
1674  $from = isset( $_SERVER['REQUEST_URI'] ) ? $_SERVER['REQUEST_URI'] : 'CLI';
1675  $this->logger->info( __METHOD__ . ': creating new user ({username}) - from: {from}', [
1676  'username' => $username,
1677  'from' => $from,
1678  ] );
1679 
1680  // Ignore warnings about master connections/writes...hard to avoid here
1681  $trxProfiler = \Profiler::instance()->getTransactionProfiler();
1682  $old = $trxProfiler->setSilenced( true );
1683  try {
1684  $status = $user->addToDatabase();
1685  if ( !$status->isOK() ) {
1686  // Double-check for a race condition (T70012). We make use of the fact that when
1687  // addToDatabase fails due to the user already existing, the user object gets loaded.
1688  if ( $user->getId() ) {
1689  $this->logger->info( __METHOD__ . ': {username} already exists locally (race)', [
1690  'username' => $username,
1691  ] );
1692  if ( $login ) {
1693  $this->setSessionDataForUser( $user );
1694  }
1696  $status->warning( 'userexists' );
1697  } else {
1698  $this->logger->error( __METHOD__ . ': {username} failed with message {msg}', [
1699  'username' => $username,
1700  'msg' => $status->getWikiText( null, null, 'en' )
1701  ] );
1702  $user->setId( 0 );
1703  $user->loadFromId();
1704  }
1705  return $status;
1706  }
1707  } catch ( \Exception $ex ) {
1708  $trxProfiler->setSilenced( $old );
1709  $this->logger->error( __METHOD__ . ': {username} failed with exception {exception}', [
1710  'username' => $username,
1711  'exception' => $ex,
1712  ] );
1713  // Do not keep throwing errors for a while
1714  $cache->set( $backoffKey, 1, 600 );
1715  // Bubble up error; which should normally trigger DB rollbacks
1716  throw $ex;
1717  }
1718 
1719  $this->setDefaultUserOptions( $user, false );
1720 
1721  // Inform the providers
1722  $this->callMethodOnProviders( 6, 'autoCreatedAccount', [ $user, $source ] );
1723 
1724  \Hooks::run( 'AuthPluginAutoCreate', [ $user ], '1.27' );
1725  \Hooks::run( 'LocalUserCreated', [ $user, true ] );
1726  $user->saveSettings();
1727 
1728  // Update user count
1729  \DeferredUpdates::addUpdate( new \SiteStatsUpdate( 0, 0, 0, 0, 1 ) );
1730  // Watch user's userpage and talk page
1731  \DeferredUpdates::addCallableUpdate( function () use ( $user ) {
1732  $user->addWatch( $user->getUserPage(), User::IGNORE_USER_RIGHTS );
1733  } );
1734 
1735  // Log the creation
1736  if ( $this->config->get( 'NewUserLog' ) ) {
1737  $logEntry = new \ManualLogEntry( 'newusers', 'autocreate' );
1738  $logEntry->setPerformer( $user );
1739  $logEntry->setTarget( $user->getUserPage() );
1740  $logEntry->setComment( '' );
1741  $logEntry->setParameters( [
1742  '4::userid' => $user->getId(),
1743  ] );
1744  $logEntry->insert();
1745  }
1746 
1747  $trxProfiler->setSilenced( $old );
1748 
1749  if ( $login ) {
1750  $this->setSessionDataForUser( $user );
1751  }
1752 
1753  return Status::newGood();
1754  }
1755 
1767  public function canLinkAccounts() {
1768  foreach ( $this->getPrimaryAuthenticationProviders() as $provider ) {
1769  if ( $provider->accountCreationType() === PrimaryAuthenticationProvider::TYPE_LINK ) {
1770  return true;
1771  }
1772  }
1773  return false;
1774  }
1775 
1785  public function beginAccountLink( User $user, array $reqs, $returnToUrl ) {
1786  $session = $this->request->getSession();
1787  $session->remove( 'AuthManager::accountLinkState' );
1788 
1789  if ( !$this->canLinkAccounts() ) {
1790  // Caller should have called canLinkAccounts()
1791  throw new \LogicException( 'Account linking is not possible' );
1792  }
1793 
1794  if ( $user->getId() === 0 ) {
1795  if ( !User::isUsableName( $user->getName() ) ) {
1796  $msg = wfMessage( 'noname' );
1797  } else {
1798  $msg = wfMessage( 'authmanager-userdoesnotexist', $user->getName() );
1799  }
1800  return AuthenticationResponse::newFail( $msg );
1801  }
1802  foreach ( $reqs as $req ) {
1803  $req->username = $user->getName();
1804  $req->returnToUrl = $returnToUrl;
1805  }
1806 
1807  $this->removeAuthenticationSessionData( null );
1808 
1809  $providers = $this->getPreAuthenticationProviders();
1810  foreach ( $providers as $id => $provider ) {
1811  $status = $provider->testForAccountLink( $user );
1812  if ( !$status->isGood() ) {
1813  $this->logger->debug( __METHOD__ . ": Account linking pre-check failed by $id", [
1814  'user' => $user->getName(),
1815  ] );
1817  Status::wrap( $status )->getMessage()
1818  );
1819  $this->callMethodOnProviders( 3, 'postAccountLink', [ $user, $ret ] );
1820  return $ret;
1821  }
1822  }
1823 
1824  $state = [
1825  'username' => $user->getName(),
1826  'userid' => $user->getId(),
1827  'returnToUrl' => $returnToUrl,
1828  'primary' => null,
1829  'continueRequests' => [],
1830  ];
1831 
1832  $providers = $this->getPrimaryAuthenticationProviders();
1833  foreach ( $providers as $id => $provider ) {
1834  if ( $provider->accountCreationType() !== PrimaryAuthenticationProvider::TYPE_LINK ) {
1835  continue;
1836  }
1837 
1838  $res = $provider->beginPrimaryAccountLink( $user, $reqs );
1839  switch ( $res->status ) {
1841  $this->logger->info( "Account linked to {user} by $id", [
1842  'user' => $user->getName(),
1843  ] );
1844  $this->callMethodOnProviders( 3, 'postAccountLink', [ $user, $res ] );
1845  return $res;
1846 
1848  $this->logger->debug( __METHOD__ . ": Account linking failed by $id", [
1849  'user' => $user->getName(),
1850  ] );
1851  $this->callMethodOnProviders( 3, 'postAccountLink', [ $user, $res ] );
1852  return $res;
1853 
1855  // Continue loop
1856  break;
1857 
1860  $this->logger->debug( __METHOD__ . ": Account linking $res->status by $id", [
1861  'user' => $user->getName(),
1862  ] );
1863  $this->fillRequests( $res->neededRequests, self::ACTION_LINK, $user->getName() );
1864  $state['primary'] = $id;
1865  $state['continueRequests'] = $res->neededRequests;
1866  $session->setSecret( 'AuthManager::accountLinkState', $state );
1867  $session->persist();
1868  return $res;
1869 
1870  // @codeCoverageIgnoreStart
1871  default:
1872  throw new \DomainException(
1873  get_class( $provider ) . "::beginPrimaryAccountLink() returned $res->status"
1874  );
1875  // @codeCoverageIgnoreEnd
1876  }
1877  }
1878 
1879  $this->logger->debug( __METHOD__ . ': Account linking failed because no provider accepted', [
1880  'user' => $user->getName(),
1881  ] );
1883  wfMessage( 'authmanager-link-no-primary' )
1884  );
1885  $this->callMethodOnProviders( 3, 'postAccountLink', [ $user, $ret ] );
1886  return $ret;
1887  }
1888 
1894  public function continueAccountLink( array $reqs ) {
1895  $session = $this->request->getSession();
1896  try {
1897  if ( !$this->canLinkAccounts() ) {
1898  // Caller should have called canLinkAccounts()
1899  $session->remove( 'AuthManager::accountLinkState' );
1900  throw new \LogicException( 'Account linking is not possible' );
1901  }
1902 
1903  $state = $session->getSecret( 'AuthManager::accountLinkState' );
1904  if ( !is_array( $state ) ) {
1906  wfMessage( 'authmanager-link-not-in-progress' )
1907  );
1908  }
1909  $state['continueRequests'] = [];
1910 
1911  // Step 0: Prepare and validate the input
1912 
1913  $user = User::newFromName( $state['username'], 'usable' );
1914  if ( !is_object( $user ) ) {
1915  $session->remove( 'AuthManager::accountLinkState' );
1916  return AuthenticationResponse::newFail( wfMessage( 'noname' ) );
1917  }
1918  if ( $user->getId() != $state['userid'] ) {
1919  throw new \UnexpectedValueException(
1920  "User \"{$state['username']}\" is valid, but " .
1921  "ID {$user->getId()} != {$state['userid']}!"
1922  );
1923  }
1924 
1925  foreach ( $reqs as $req ) {
1926  $req->username = $state['username'];
1927  $req->returnToUrl = $state['returnToUrl'];
1928  }
1929 
1930  // Step 1: Call the primary again until it succeeds
1931 
1932  $provider = $this->getAuthenticationProvider( $state['primary'] );
1933  if ( !$provider instanceof PrimaryAuthenticationProvider ) {
1934  // Configuration changed? Force them to start over.
1935  // @codeCoverageIgnoreStart
1937  wfMessage( 'authmanager-link-not-in-progress' )
1938  );
1939  $this->callMethodOnProviders( 3, 'postAccountLink', [ $user, $ret ] );
1940  $session->remove( 'AuthManager::accountLinkState' );
1941  return $ret;
1942  // @codeCoverageIgnoreEnd
1943  }
1944  $id = $provider->getUniqueId();
1945  $res = $provider->continuePrimaryAccountLink( $user, $reqs );
1946  switch ( $res->status ) {
1948  $this->logger->info( "Account linked to {user} by $id", [
1949  'user' => $user->getName(),
1950  ] );
1951  $this->callMethodOnProviders( 3, 'postAccountLink', [ $user, $res ] );
1952  $session->remove( 'AuthManager::accountLinkState' );
1953  return $res;
1955  $this->logger->debug( __METHOD__ . ": Account linking failed by $id", [
1956  'user' => $user->getName(),
1957  ] );
1958  $this->callMethodOnProviders( 3, 'postAccountLink', [ $user, $res ] );
1959  $session->remove( 'AuthManager::accountLinkState' );
1960  return $res;
1963  $this->logger->debug( __METHOD__ . ": Account linking $res->status by $id", [
1964  'user' => $user->getName(),
1965  ] );
1966  $this->fillRequests( $res->neededRequests, self::ACTION_LINK, $user->getName() );
1967  $state['continueRequests'] = $res->neededRequests;
1968  $session->setSecret( 'AuthManager::accountLinkState', $state );
1969  return $res;
1970  default:
1971  throw new \DomainException(
1972  get_class( $provider ) . "::continuePrimaryAccountLink() returned $res->status"
1973  );
1974  }
1975  } catch ( \Exception $ex ) {
1976  $session->remove( 'AuthManager::accountLinkState' );
1977  throw $ex;
1978  }
1979  }
1980 
2006  public function getAuthenticationRequests( $action, User $user = null ) {
2007  $options = [];
2008  $providerAction = $action;
2009 
2010  // Figure out which providers to query
2011  switch ( $action ) {
2012  case self::ACTION_LOGIN:
2013  case self::ACTION_CREATE:
2014  $providers = $this->getPreAuthenticationProviders() +
2017  break;
2018 
2020  $state = $this->request->getSession()->getSecret( 'AuthManager::authnState' );
2021  return is_array( $state ) ? $state['continueRequests'] : [];
2022 
2024  $state = $this->request->getSession()->getSecret( 'AuthManager::accountCreationState' );
2025  return is_array( $state ) ? $state['continueRequests'] : [];
2026 
2027  case self::ACTION_LINK:
2028  $providers = array_filter( $this->getPrimaryAuthenticationProviders(), function ( $p ) {
2029  return $p->accountCreationType() === PrimaryAuthenticationProvider::TYPE_LINK;
2030  } );
2031  break;
2032 
2033  case self::ACTION_UNLINK:
2034  $providers = array_filter( $this->getPrimaryAuthenticationProviders(), function ( $p ) {
2035  return $p->accountCreationType() === PrimaryAuthenticationProvider::TYPE_LINK;
2036  } );
2037 
2038  // To providers, unlink and remove are identical.
2039  $providerAction = self::ACTION_REMOVE;
2040  break;
2041 
2043  $state = $this->request->getSession()->getSecret( 'AuthManager::accountLinkState' );
2044  return is_array( $state ) ? $state['continueRequests'] : [];
2045 
2046  case self::ACTION_CHANGE:
2047  case self::ACTION_REMOVE:
2048  $providers = $this->getPrimaryAuthenticationProviders() +
2050  break;
2051 
2052  // @codeCoverageIgnoreStart
2053  default:
2054  throw new \DomainException( __METHOD__ . ": Invalid action \"$action\"" );
2055  }
2056  // @codeCoverageIgnoreEnd
2057 
2058  return $this->getAuthenticationRequestsInternal( $providerAction, $options, $providers, $user );
2059  }
2060 
2071  $providerAction, array $options, array $providers, User $user = null
2072  ) {
2073  $user = $user ?: \RequestContext::getMain()->getUser();
2074  $options['username'] = $user->isAnon() ? null : $user->getName();
2075 
2076  // Query them and merge results
2077  $reqs = [];
2078  foreach ( $providers as $provider ) {
2079  $isPrimary = $provider instanceof PrimaryAuthenticationProvider;
2080  foreach ( $provider->getAuthenticationRequests( $providerAction, $options ) as $req ) {
2081  $id = $req->getUniqueId();
2082 
2083  // If a required request if from a Primary, mark it as "primary-required" instead
2084  if ( $isPrimary ) {
2085  if ( $req->required ) {
2087  }
2088  }
2089 
2090  if (
2091  !isset( $reqs[$id] )
2092  || $req->required === AuthenticationRequest::REQUIRED
2093  || $reqs[$id] === AuthenticationRequest::OPTIONAL
2094  ) {
2095  $reqs[$id] = $req;
2096  }
2097  }
2098  }
2099 
2100  // AuthManager has its own req for some actions
2101  switch ( $providerAction ) {
2102  case self::ACTION_LOGIN:
2103  $reqs[] = new RememberMeAuthenticationRequest;
2104  break;
2105 
2106  case self::ACTION_CREATE:
2107  $reqs[] = new UsernameAuthenticationRequest;
2108  $reqs[] = new UserDataAuthenticationRequest;
2109  if ( $options['username'] !== null ) {
2111  $options['username'] = null; // Don't fill in the username below
2112  }
2113  break;
2114  }
2115 
2116  // Fill in reqs data
2117  $this->fillRequests( $reqs, $providerAction, $options['username'], true );
2118 
2119  // For self::ACTION_CHANGE, filter out any that something else *doesn't* allow changing
2120  if ( $providerAction === self::ACTION_CHANGE || $providerAction === self::ACTION_REMOVE ) {
2121  $reqs = array_filter( $reqs, function ( $req ) {
2122  return $this->allowsAuthenticationDataChange( $req, false )->isGood();
2123  } );
2124  }
2125 
2126  return array_values( $reqs );
2127  }
2128 
2136  private function fillRequests( array &$reqs, $action, $username, $forceAction = false ) {
2137  foreach ( $reqs as $req ) {
2138  if ( !$req->action || $forceAction ) {
2139  $req->action = $action;
2140  }
2141  if ( $req->username === null ) {
2142  $req->username = $username;
2143  }
2144  }
2145  }
2146 
2153  public function userExists( $username, $flags = User::READ_NORMAL ) {
2154  foreach ( $this->getPrimaryAuthenticationProviders() as $provider ) {
2155  if ( $provider->testUserExists( $username, $flags ) ) {
2156  return true;
2157  }
2158  }
2159 
2160  return false;
2161  }
2162 
2174  public function allowsPropertyChange( $property ) {
2175  $providers = $this->getPrimaryAuthenticationProviders() +
2177  foreach ( $providers as $provider ) {
2178  if ( !$provider->providerAllowsPropertyChange( $property ) ) {
2179  return false;
2180  }
2181  }
2182  return true;
2183  }
2184 
2193  public function getAuthenticationProvider( $id ) {
2194  // Fast version
2195  if ( isset( $this->allAuthenticationProviders[$id] ) ) {
2196  return $this->allAuthenticationProviders[$id];
2197  }
2198 
2199  // Slow version: instantiate each kind and check
2200  $providers = $this->getPrimaryAuthenticationProviders();
2201  if ( isset( $providers[$id] ) ) {
2202  return $providers[$id];
2203  }
2204  $providers = $this->getSecondaryAuthenticationProviders();
2205  if ( isset( $providers[$id] ) ) {
2206  return $providers[$id];
2207  }
2208  $providers = $this->getPreAuthenticationProviders();
2209  if ( isset( $providers[$id] ) ) {
2210  return $providers[$id];
2211  }
2212 
2213  return null;
2214  }
2215 
2229  public function setAuthenticationSessionData( $key, $data ) {
2230  $session = $this->request->getSession();
2231  $arr = $session->getSecret( 'authData' );
2232  if ( !is_array( $arr ) ) {
2233  $arr = [];
2234  }
2235  $arr[$key] = $data;
2236  $session->setSecret( 'authData', $arr );
2237  }
2238 
2246  public function getAuthenticationSessionData( $key, $default = null ) {
2247  $arr = $this->request->getSession()->getSecret( 'authData' );
2248  if ( is_array( $arr ) && array_key_exists( $key, $arr ) ) {
2249  return $arr[$key];
2250  } else {
2251  return $default;
2252  }
2253  }
2254 
2260  public function removeAuthenticationSessionData( $key ) {
2261  $session = $this->request->getSession();
2262  if ( $key === null ) {
2263  $session->remove( 'authData' );
2264  } else {
2265  $arr = $session->getSecret( 'authData' );
2266  if ( is_array( $arr ) && array_key_exists( $key, $arr ) ) {
2267  unset( $arr[$key] );
2268  $session->setSecret( 'authData', $arr );
2269  }
2270  }
2271  }
2272 
2279  protected function providerArrayFromSpecs( $class, array $specs ) {
2280  $i = 0;
2281  foreach ( $specs as &$spec ) {
2282  $spec = [ 'sort2' => $i++ ] + $spec + [ 'sort' => 0 ];
2283  }
2284  unset( $spec );
2285  usort( $specs, function ( $a, $b ) {
2286  return ( (int)$a['sort'] ) - ( (int)$b['sort'] )
2287  ?: $a['sort2'] - $b['sort2'];
2288  } );
2289 
2290  $ret = [];
2291  foreach ( $specs as $spec ) {
2292  $provider = \ObjectFactory::getObjectFromSpec( $spec );
2293  if ( !$provider instanceof $class ) {
2294  throw new \RuntimeException(
2295  "Expected instance of $class, got " . get_class( $provider )
2296  );
2297  }
2298  $provider->setLogger( $this->logger );
2299  $provider->setManager( $this );
2300  $provider->setConfig( $this->config );
2301  $id = $provider->getUniqueId();
2302  if ( isset( $this->allAuthenticationProviders[$id] ) ) {
2303  throw new \RuntimeException(
2304  "Duplicate specifications for id $id (classes " .
2305  get_class( $provider ) . ' and ' .
2306  get_class( $this->allAuthenticationProviders[$id] ) . ')'
2307  );
2308  }
2309  $this->allAuthenticationProviders[$id] = $provider;
2310  $ret[$id] = $provider;
2311  }
2312  return $ret;
2313  }
2314 
2319  private function getConfiguration() {
2320  return $this->config->get( 'AuthManagerConfig' ) ?: $this->config->get( 'AuthManagerAutoConfig' );
2321  }
2322 
2327  protected function getPreAuthenticationProviders() {
2328  if ( $this->preAuthenticationProviders === null ) {
2329  $conf = $this->getConfiguration();
2330  $this->preAuthenticationProviders = $this->providerArrayFromSpecs(
2331  PreAuthenticationProvider::class, $conf['preauth']
2332  );
2333  }
2335  }
2336 
2341  protected function getPrimaryAuthenticationProviders() {
2342  if ( $this->primaryAuthenticationProviders === null ) {
2343  $conf = $this->getConfiguration();
2344  $this->primaryAuthenticationProviders = $this->providerArrayFromSpecs(
2345  PrimaryAuthenticationProvider::class, $conf['primaryauth']
2346  );
2347  }
2349  }
2350 
2356  if ( $this->secondaryAuthenticationProviders === null ) {
2357  $conf = $this->getConfiguration();
2358  $this->secondaryAuthenticationProviders = $this->providerArrayFromSpecs(
2359  SecondaryAuthenticationProvider::class, $conf['secondaryauth']
2360  );
2361  }
2363  }
2364 
2370  private function setSessionDataForUser( $user, $remember = null ) {
2371  $session = $this->request->getSession();
2372  $delay = $session->delaySave();
2373 
2374  $session->resetId();
2375  $session->resetAllTokens();
2376  if ( $session->canSetUser() ) {
2377  $session->setUser( $user );
2378  }
2379  if ( $remember !== null ) {
2380  $session->setRememberUser( $remember );
2381  }
2382  $session->set( 'AuthManager:lastAuthId', $user->getId() );
2383  $session->set( 'AuthManager:lastAuthTimestamp', time() );
2384  $session->persist();
2385 
2386  \Wikimedia\ScopedCallback::consume( $delay );
2387 
2388  \Hooks::run( 'UserLoggedIn', [ $user ] );
2389  }
2390 
2395  private function setDefaultUserOptions( User $user, $useContextLang ) {
2397 
2398  $user->setToken();
2399 
2400  $lang = $useContextLang ? \RequestContext::getMain()->getLanguage() : $wgContLang;
2401  $user->setOption( 'language', $lang->getPreferredVariant() );
2402 
2403  if ( $wgContLang->hasVariants() ) {
2404  $user->setOption( 'variant', $wgContLang->getPreferredVariant() );
2405  }
2406  }
2407 
2413  private function callMethodOnProviders( $which, $method, array $args ) {
2414  $providers = [];
2415  if ( $which & 1 ) {
2416  $providers += $this->getPreAuthenticationProviders();
2417  }
2418  if ( $which & 2 ) {
2419  $providers += $this->getPrimaryAuthenticationProviders();
2420  }
2421  if ( $which & 4 ) {
2422  $providers += $this->getSecondaryAuthenticationProviders();
2423  }
2424  foreach ( $providers as $provider ) {
2425  call_user_func_array( [ $provider, $method ], $args );
2426  }
2427  }
2428 
2433  public static function resetCache() {
2434  if ( !defined( 'MW_PHPUNIT_TEST' ) ) {
2435  // @codeCoverageIgnoreStart
2436  throw new \MWException( __METHOD__ . ' may only be called from unit tests!' );
2437  // @codeCoverageIgnoreEnd
2438  }
2439 
2440  self::$instance = null;
2441  }
2442 
2445 }
2446 
MediaWiki\Auth\AuthManager\continueAccountLink
continueAccountLink(array $reqs)
Continue an account linking flow.
Definition: AuthManager.php:1894
MediaWiki\Auth\AuthManager\continueAuthentication
continueAuthentication(array $reqs)
Continue an authentication flow.
Definition: AuthManager.php:407
MediaWiki\Auth\AuthenticationRequest\OPTIONAL
const OPTIONAL
Indicates that the request is not required for authentication to proceed.
Definition: AuthenticationRequest.php:40
$user
please add to it if you re going to add events to the MediaWiki code where normally authentication against an external auth plugin would be creating a account $user
Definition: hooks.txt:244
MediaWiki\Auth\AuthManager\getRequest
getRequest()
Definition: AuthManager.php:176
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:131
User\newFromId
static newFromId( $id)
Static factory method for creation from a given user ID.
Definition: User.php:573
StatusValue
Generic operation result class Has warning/error list, boolean status and arbitrary value.
Definition: StatusValue.php:42
MediaWiki\$action
String $action
Cache what action this request is.
Definition: MediaWiki.php:47
MediaWiki\Auth\AuthManager\SEC_REAUTH
const SEC_REAUTH
Security-sensitive operations should re-authenticate.
Definition: AuthManager.php:108
MediaWiki\Auth\PrimaryAuthenticationProvider\TYPE_NONE
const TYPE_NONE
Provider cannot create or link to accounts.
Definition: PrimaryAuthenticationProvider.php:81
ObjectCache\getLocalClusterInstance
static getLocalClusterInstance()
Get the main cluster-local cache object.
Definition: ObjectCache.php:357
User\getId
getId()
Get the user's ID.
Definition: User.php:2224
MediaWiki\Auth\AuthManager\fillRequests
fillRequests(array &$reqs, $action, $username, $forceAction=false)
Set values in an array of requests.
Definition: AuthManager.php:2136
MediaWiki\Auth\AuthManager\getPrimaryAuthenticationProviders
getPrimaryAuthenticationProviders()
Get the list of PrimaryAuthenticationProviders.
Definition: AuthManager.php:2341
Profiler\instance
static instance()
Singleton.
Definition: Profiler.php:62
MediaWiki\Auth\AuthManager\ACTION_UNLINK
const ACTION_UNLINK
Like ACTION_REMOVE but for linking providers only.
Definition: AuthManager.php:103
$lang
if(!isset( $args[0])) $lang
Definition: testCompression.php:33
MediaWiki\Auth\AuthManager\AUTOCREATE_SOURCE_SESSION
const AUTOCREATE_SOURCE_SESSION
Auto-creation is due to SessionManager.
Definition: AuthManager.php:113
wfGetLB
wfGetLB( $wiki=false)
Get a load balancer object.
Definition: GlobalFunctions.php:2869
Block\TYPE_RANGE
const TYPE_RANGE
Definition: Block.php:85
MediaWiki\Auth\AuthManager\revokeAccessForUser
revokeAccessForUser( $username)
Revoke any authentication credentials for a user.
Definition: AuthManager.php:835
$last
$last
Definition: profileinfo.php:415
MediaWiki\Logger\LoggerFactory\getInstance
static getInstance( $channel)
Get a named logger instance from the currently configured logger factory.
Definition: LoggerFactory.php:92
MediaWiki\Auth\AuthManagerAuthPlugin
Backwards-compatibility wrapper for AuthManager via $wgAuth.
Definition: AuthManagerAuthPlugin.php:30
MediaWiki\Auth\AuthManager\autoCreateUser
autoCreateUser(User $user, $source, $login=true)
Auto-create an account, and log into that account.
Definition: AuthManager.php:1538
$status
Status::newGood()` to allow deletion, and then `return false` from the hook function. Ensure you consume the 'ChangeTagAfterDelete' hook to carry out custom deletion actions. $tag:name of the tag $user:user initiating the action & $status:Status object. See above. 'ChangeTagsListActive':Allows you to nominate which of the tags your extension uses are in active use. & $tags:list of all active tags. Append to this array. 'ChangeTagsAfterUpdateTags':Called after tags have been updated with the ChangeTags::updateTags function. Params:$addedTags:tags effectively added in the update $removedTags:tags effectively removed in the update $prevTags:tags that were present prior to the update $rc_id:recentchanges table id $rev_id:revision table id $log_id:logging table id $params:tag params $rc:RecentChange being tagged when the tagging accompanies the action or null $user:User who performed the tagging when the tagging is subsequent to the action or null 'ChangeTagsAllowedAdd':Called when checking if a user can add tags to a change. & $allowedTags:List of all the tags the user is allowed to add. Any tags the user wants to add( $addTags) that are not in this array will cause it to fail. You may add or remove tags to this array as required. $addTags:List of tags user intends to add. $user:User who is adding the tags. 'ChangeUserGroups':Called before user groups are changed. $performer:The User who will perform the change $user:The User whose groups will be changed & $add:The groups that will be added & $remove:The groups that will be removed 'Collation::factory':Called if $wgCategoryCollation is an unknown collation. $collationName:Name of the collation in question & $collationObject:Null. Replace with a subclass of the Collation class that implements the collation given in $collationName. 'ConfirmEmailComplete':Called after a user 's email has been confirmed successfully. $user:user(object) whose email is being confirmed 'ContentAlterParserOutput':Modify parser output for a given content object. Called by Content::getParserOutput after parsing has finished. Can be used for changes that depend on the result of the parsing but have to be done before LinksUpdate is called(such as adding tracking categories based on the rendered HTML). $content:The Content to render $title:Title of the page, as context $parserOutput:ParserOutput to manipulate 'ContentGetParserOutput':Customize parser output for a given content object, called by AbstractContent::getParserOutput. May be used to override the normal model-specific rendering of page content. $content:The Content to render $title:Title of the page, as context $revId:The revision ID, as context $options:ParserOptions for rendering. To avoid confusing the parser cache, the output can only depend on parameters provided to this hook function, not on global state. $generateHtml:boolean, indicating whether full HTML should be generated. If false, generation of HTML may be skipped, but other information should still be present in the ParserOutput object. & $output:ParserOutput, to manipulate or replace 'ContentHandlerDefaultModelFor':Called when the default content model is determined for a given title. May be used to assign a different model for that title. $title:the Title in question & $model:the model name. Use with CONTENT_MODEL_XXX constants. 'ContentHandlerForModelID':Called when a ContentHandler is requested for a given content model name, but no entry for that model exists in $wgContentHandlers. Note:if your extension implements additional models via this hook, please use GetContentModels hook to make them known to core. $modeName:the requested content model name & $handler:set this to a ContentHandler object, if desired. 'ContentModelCanBeUsedOn':Called to determine whether that content model can be used on a given page. This is especially useful to prevent some content models to be used in some special location. $contentModel:ID of the content model in question $title:the Title in question. & $ok:Output parameter, whether it is OK to use $contentModel on $title. Handler functions that modify $ok should generally return false to prevent further hooks from further modifying $ok. 'ContribsPager::getQueryInfo':Before the contributions query is about to run & $pager:Pager object for contributions & $queryInfo:The query for the contribs Pager 'ContribsPager::reallyDoQuery':Called before really executing the query for My Contributions & $data:an array of results of all contribs queries $pager:The ContribsPager object hooked into $offset:Index offset, inclusive $limit:Exact query limit $descending:Query direction, false for ascending, true for descending 'ContributionsLineEnding':Called before a contributions HTML line is finished $page:SpecialPage object for contributions & $ret:the HTML line $row:the DB row for this line & $classes:the classes to add to the surrounding< li > & $attribs:associative array of other HTML attributes for the< li > element. Currently only data attributes reserved to MediaWiki are allowed(see Sanitizer::isReservedDataAttribute). 'ContributionsToolLinks':Change tool links above Special:Contributions $id:User identifier $title:User page title & $tools:Array of tool links $specialPage:SpecialPage instance for context and services. Can be either SpecialContributions or DeletedContributionsPage. Extensions should type hint against a generic SpecialPage though. 'ConvertContent':Called by AbstractContent::convert when a conversion to another content model is requested. Handler functions that modify $result should generally return false to disable further attempts at conversion. $content:The Content object to be converted. $toModel:The ID of the content model to convert to. $lossy:boolean indicating whether lossy conversion is allowed. & $result:Output parameter, in case the handler function wants to provide a converted Content object. Note that $result->getContentModel() must return $toModel. 'CustomEditor':When invoking the page editor Return true to allow the normal editor to be used, or false if implementing a custom editor, e.g. for a special namespace, etc. $article:Article being edited $user:User performing the edit 'DatabaseOraclePostInit':Called after initialising an Oracle database $db:the DatabaseOracle object 'DeletedContribsPager::reallyDoQuery':Called before really executing the query for Special:DeletedContributions Similar to ContribsPager::reallyDoQuery & $data:an array of results of all contribs queries $pager:The DeletedContribsPager object hooked into $offset:Index offset, inclusive $limit:Exact query limit $descending:Query direction, false for ascending, true for descending 'DeletedContributionsLineEnding':Called before a DeletedContributions HTML line is finished. Similar to ContributionsLineEnding $page:SpecialPage object for DeletedContributions & $ret:the HTML line $row:the DB row for this line & $classes:the classes to add to the surrounding< li > & $attribs:associative array of other HTML attributes for the< li > element. Currently only data attributes reserved to MediaWiki are allowed(see Sanitizer::isReservedDataAttribute). 'DifferenceEngineAfterLoadNewText':called in DifferenceEngine::loadNewText() after the new revision 's content has been loaded into the class member variable $differenceEngine->mNewContent but before returning true from this function. $differenceEngine:DifferenceEngine object 'DifferenceEngineLoadTextAfterNewContentIsLoaded':called in DifferenceEngine::loadText() after the new revision 's content has been loaded into the class member variable $differenceEngine->mNewContent but before checking if the variable 's value is null. This hook can be used to inject content into said class member variable. $differenceEngine:DifferenceEngine object 'DifferenceEngineMarkPatrolledLink':Allows extensions to change the "mark as patrolled" link which is shown both on the diff header as well as on the bottom of a page, usually wrapped in a span element which has class="patrollink". $differenceEngine:DifferenceEngine object & $markAsPatrolledLink:The "mark as patrolled" link HTML(string) $rcid:Recent change ID(rc_id) for this change(int) 'DifferenceEngineMarkPatrolledRCID':Allows extensions to possibly change the rcid parameter. For example the rcid might be set to zero due to the user being the same as the performer of the change but an extension might still want to show it under certain conditions. & $rcid:rc_id(int) of the change or 0 $differenceEngine:DifferenceEngine object $change:RecentChange object $user:User object representing the current user 'DifferenceEngineNewHeader':Allows extensions to change the $newHeader variable, which contains information about the new revision, such as the revision 's author, whether the revision was marked as a minor edit or not, etc. $differenceEngine:DifferenceEngine object & $newHeader:The string containing the various #mw-diff-otitle[1-5] divs, which include things like revision author info, revision comment, RevisionDelete link and more $formattedRevisionTools:Array containing revision tools, some of which may have been injected with the DiffRevisionTools hook $nextlink:String containing the link to the next revision(if any) $status
Definition: hooks.txt:1245
use
as see the revision history and available at free of to any person obtaining a copy of this software and associated documentation to deal in the Software without including without limitation the rights to use
Definition: MIT-LICENSE.txt:10
MediaWiki\Auth\AuthManager\$instance
static AuthManager null $instance
Definition: AuthManager.php:116
$req
this hook is for auditing only $req
Definition: hooks.txt:988
DeferredUpdates\addUpdate
static addUpdate(DeferrableUpdate $update, $stage=self::POSTSEND)
Add an update to the deferred list to be run later by execute()
Definition: DeferredUpdates.php:76
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:29
StatusValue\newFatal
static newFatal( $message)
Factory function for fatal errors.
Definition: StatusValue.php:68
$params
$params
Definition: styleTest.css.php:40
MediaWiki\Auth\AuthManager\beginAuthentication
beginAuthentication(array $reqs, $returnToUrl)
Start an authentication flow.
Definition: AuthManager.php:283
wfReadOnly
wfReadOnly()
Check whether the wiki is in read-only mode.
Definition: GlobalFunctions.php:1324
User\newFromName
static newFromName( $name, $validate='valid')
Static factory method for creation from username.
Definition: User.php:550
MediaWiki\Auth\AuthManager\userExists
userExists( $username, $flags=User::READ_NORMAL)
Determine whether a username exists.
Definition: AuthManager.php:2153
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:82
$res
$res
Definition: database.txt:21
MediaWiki\Auth\AuthManager\$allAuthenticationProviders
AuthenticationProvider[] $allAuthenticationProviders
Definition: AuthManager.php:128
$name
Allows to change the fields on the form that will be generated $name
Definition: hooks.txt:302
User
User
Definition: All_system_messages.txt:425
MediaWiki\Auth\AuthManager\getPreAuthenticationProviders
getPreAuthenticationProviders()
Get the list of PreAuthenticationProviders.
Definition: AuthManager.php:2327
MediaWiki\Auth\AuthManager\getAuthenticationSessionData
getAuthenticationSessionData( $key, $default=null)
Fetch authentication data from the current session.
Definition: AuthManager.php:2246
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:87
MediaWiki\Auth\AuthManager\__construct
__construct(WebRequest $request, Config $config)
Definition: AuthManager.php:160
MediaWiki\Auth\AuthManager\SEC_FAIL
const SEC_FAIL
Security-sensitive should not be performed.
Definition: AuthManager.php:110
MediaWiki\Auth\AuthenticationRequest\getRequestByClass
static getRequestByClass(array $reqs, $class, $allowSubclasses=false)
Select a request by class name.
Definition: AuthenticationRequest.php:253
php
injection txt This is an overview of how MediaWiki makes use of dependency injection The design described here grew from the discussion of RFC T384 The term dependency this means that anything an object needs to operate should be injected from the the object itself should only know narrow no concrete implementation of the logic it relies on The requirement to inject everything typically results in an architecture that based on two main types of and essentially stateless service objects that use other service objects to operate on the value objects As of the beginning MediaWiki is only starting to use the DI approach Much of the code still relies on global state or direct resulting in a highly cyclical dependency which acts as the top level factory for services in MediaWiki which can be used to gain access to default instances of various services MediaWikiServices however also allows new services to be defined and default services to be redefined Services are defined or redefined by providing a callback the instantiator that will return a new instance of the service When it will create an instance of MediaWikiServices and populate it with the services defined in the files listed by thereby bootstrapping the DI framework Per $wgServiceWiringFiles lists includes ServiceWiring php
Definition: injection.txt:35
IDBAccessObject\READ_LOCKING
const READ_LOCKING
Constants for object loading bitfield flags (higher => higher QoS)
Definition: IDBAccessObject.php:62
Status
Generic operation result class Has warning/error list, boolean status and arbitrary value.
Definition: Status.php:40
MediaWiki\Auth\AuthManager\removeAuthenticationSessionData
removeAuthenticationSessionData( $key)
Remove authentication data.
Definition: AuthManager.php:2260
Config
Interface for configuration instances.
Definition: Config.php:28
MediaWiki\Auth\AuthenticationRequest\getUsernameFromRequests
static getUsernameFromRequests(array $reqs)
Get the username from the set of requests.
Definition: AuthenticationRequest.php:273
DeferredUpdates\addCallableUpdate
static addCallableUpdate( $callable, $stage=self::POSTSEND, IDatabase $dbw=null)
Add a callable update.
Definition: DeferredUpdates.php:111
MediaWiki\Auth\AuthenticationRequest\PRIMARY_REQUIRED
const PRIMARY_REQUIRED
Indicates that the request is required by a primary authentication provider.
Definition: AuthenticationRequest.php:51
MediaWiki\Auth\AuthenticationResponse\UI
const UI
Indicates that the authentication needs further user input of some sort.
Definition: AuthenticationResponse.php:55
MediaWiki\Auth\AuthManager\beginAccountCreation
beginAccountCreation(User $creator, array $reqs, $returnToUrl)
Start an account creation flow.
Definition: AuthManager.php:1037
MediaWiki\Auth\AuthManager\getAuthenticationProvider
getAuthenticationProvider( $id)
Get a provider by ID.
Definition: AuthManager.php:2193
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:97
MediaWiki\Auth\CreationReasonAuthenticationRequest
Authentication request for the reason given for account creation.
Definition: CreationReasonAuthenticationRequest.php:9
$property
$property
Definition: styleTest.css.php:44
MediaWiki\Auth\AuthManager\canCreateAccounts
canCreateAccounts()
Determine whether accounts can be created.
Definition: AuthManager.php:908
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\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:55
MediaWiki\Auth\AuthManager\changeAuthenticationData
changeAuthenticationData(AuthenticationRequest $req)
Change authentication data (e.g.
Definition: AuthManager.php:884
MediaWiki\Auth\CreateFromLoginAuthenticationRequest
This transfers state between the login and account creation flows.
Definition: CreateFromLoginAuthenticationRequest.php:34
MediaWiki
A helper class for throttling authentication attempts.
MediaWiki\Auth\AuthManager\getConfiguration
getConfiguration()
Get the configuration.
Definition: AuthManager.php:2319
MediaWiki\Auth\AuthenticationResponse\ABSTAIN
const ABSTAIN
Indicates that the authentication provider does not handle this request.
Definition: AuthenticationResponse.php:52
request
div flags Integer display flags(NO_ACTION_LINK, NO_EXTRA_USER_LINKS) 'LogException' returning false will NOT prevent logging a wrapping ErrorException instead of letting the login form give the generic error message that the account does not exist For when the account has been renamed or deleted or an array to pass a message key and parameters create2 Corresponds to logging log_action database field and which is displayed in the UI similar to $comment this hook should only be used to add variables that depend on the current page request
Definition: hooks.txt:2141
MediaWiki\Auth\AuthManager\canAuthenticateNow
canAuthenticateNow()
Indicate whether user authentication is possible.
Definition: AuthManager.php:261
MediaWiki\Auth\AuthManager\setAuthenticationSessionData
setAuthenticationSessionData( $key, $data)
Store authentication in the current session.
Definition: AuthManager.php:2229
global
when a variable name is used in a it is silently declared as a new masking the global
Definition: design.txt:93
MediaWiki\Auth\SecondaryAuthenticationProvider
A secondary provider mostly acts when the submitted authentication data has already been associated t...
Definition: SecondaryAuthenticationProvider.php:52
SiteStatsUpdate
Class for handling updates to the site_stats table.
Definition: SiteStatsUpdate.php:27
MediaWiki\Auth\UsernameAuthenticationRequest
AuthenticationRequest to ensure something with a username is present.
Definition: UsernameAuthenticationRequest.php:29
MediaWiki\Auth\AuthManager\setDefaultUserOptions
setDefaultUserOptions(User $user, $useContextLang)
Definition: AuthManager.php:2395
MediaWiki\Auth\UserDataAuthenticationRequest
This represents additional user data requested on the account creation form.
Definition: UserDataAuthenticationRequest.php:34
MediaWiki\Auth\AuthManager\callLegacyAuthPlugin
static callLegacyAuthPlugin( $method, array $params, $return=null)
Call a legacy AuthPlugin method, if necessary.
Definition: AuthManager.php:238
MediaWiki\Auth\AuthenticationResponse\FAIL
const FAIL
Indicates that the authentication failed.
Definition: AuthenticationResponse.php:42
MediaWiki\Auth\AuthManager\resetCache
static resetCache()
Reset the internal caching for unit testing.
Definition: AuthManager.php:2433
MediaWiki\Auth\AuthManager\$request
WebRequest $request
Definition: AuthManager.php:119
MediaWiki\Auth\AuthManager\beginAccountLink
beginAccountLink(User $user, array $reqs, $returnToUrl)
Start an account linking flow.
Definition: AuthManager.php:1785
MediaWiki\Auth\AuthManager\normalizeUsername
normalizeUsername( $username)
Provide normalized versions of the username for security checks.
Definition: AuthManager.php:810
MediaWiki\Auth\AuthManager\canLinkAccounts
canLinkAccounts()
Determine whether accounts can be linked.
Definition: AuthManager.php:1767
MediaWiki\MediaWikiServices\getInstance
static getInstance()
Returns the global default instance of the top level service locator.
Definition: MediaWikiServices.php:99
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:92
MediaWiki\Auth\AuthManager\ACTION_CREATE
const ACTION_CREATE
Create a new user.
Definition: AuthManager.php:89
MediaWiki\Auth\AuthManager\$logger
LoggerInterface $logger
Definition: AuthManager.php:125
MediaWiki\Auth\AuthManager\$primaryAuthenticationProviders
PrimaryAuthenticationProvider[] $primaryAuthenticationProviders
Definition: AuthManager.php:134
StatusValue\newGood
static newGood( $value=null)
Factory function for good results.
Definition: StatusValue.php:81
User\isDnsBlacklisted
isDnsBlacklisted( $ip, $checkWhitelist=false)
Whether the given IP is in a DNS blacklist.
Definition: User.php:1788
MediaWiki\Auth\AuthManager\continueAccountCreation
continueAccountCreation(array $reqs)
Continue an account creation flow.
Definition: AuthManager.php:1138
BotPassword\invalidateAllPasswordsForUser
static invalidateAllPasswordsForUser( $username)
Invalidate all passwords for a user, by name.
Definition: BotPassword.php:330
MediaWiki\Auth\AuthManager\$secondaryAuthenticationProviders
SecondaryAuthenticationProvider[] $secondaryAuthenticationProviders
Definition: AuthManager.php:137
MediaWiki\Auth\AuthManager\forcePrimaryAuthenticationProviders
forcePrimaryAuthenticationProviders(array $providers, $why)
Force certain PrimaryAuthenticationProviders.
Definition: AuthManager.php:186
MediaWiki\Auth\AuthManager\ACTION_CHANGE
const ACTION_CHANGE
Change a user's credentials.
Definition: AuthManager.php:99
MediaWiki\Auth\AuthManager\callMethodOnProviders
callMethodOnProviders( $which, $method, array $args)
Definition: AuthManager.php:2413
$ret
null means default in associative array with keys and values unescaped Should be merged with default with a value of false meaning to suppress the attribute in associative array with keys and values unescaped noclasses & $ret
Definition: hooks.txt:1965
MediaWiki\Auth\AuthManager\ACTION_LINK
const ACTION_LINK
Link an existing user to a third-party account.
Definition: AuthManager.php:94
ObjectFactory\getObjectFromSpec
static getObjectFromSpec( $spec)
Instantiate an object based on a specification array.
Definition: ObjectFactory.php:58
MediaWiki\Auth\AuthManager\allowsPropertyChange
allowsPropertyChange( $property)
Determine whether a user property should be allowed to be changed.
Definition: AuthManager.php:2174
MediaWiki\Auth\RememberMeAuthenticationRequest
This is an authentication request added by AuthManager to show a "remember me" checkbox.
Definition: RememberMeAuthenticationRequest.php:33
RequestContext\getMain
static getMain()
Static methods.
Definition: RequestContext.php:470
MediaWiki\Auth\AuthManager\securitySensitiveOperationStatus
securitySensitiveOperationStatus( $operation)
Whether security-sensitive operations should proceed.
Definition: AuthManager.php:714
MediaWiki\Auth\AuthManager
This serves as the entry point to the authentication system.
Definition: AuthManager.php:82
WebRequest
The WebRequest class encapsulates getting at data passed in the URL or via a POSTed form stripping il...
Definition: WebRequest.php:38
MediaWiki\Auth\AuthManager\ACTION_REMOVE
const ACTION_REMOVE
Remove a user's credentials.
Definition: AuthManager.php:101
MediaWiki\Auth\AuthManager\SEC_OK
const SEC_OK
Security-sensitive operations are ok.
Definition: AuthManager.php:106
$args
if( $line===false) $args
Definition: cdb.php:63
MediaWiki\Auth\AuthManager\getAuthenticationRequests
getAuthenticationRequests( $action, User $user=null)
Return the applicable list of AuthenticationRequests.
Definition: AuthManager.php:2006
wfReadOnlyReason
wfReadOnlyReason()
Check if the site is in read-only mode and return the message if so.
Definition: GlobalFunctions.php:1337
$cache
$cache
Definition: mcc.php:33
$options
null means default in associative array with keys and values unescaped Should be merged with default with a value of false meaning to suppress the attribute in associative array with keys and values unescaped & $options
Definition: hooks.txt:1965
MediaWiki\Auth\AuthManager\singleton
static singleton()
Get the global AuthManager.
Definition: AuthManager.php:146
User\idFromName
static idFromName( $name, $flags=self::READ_NORMAL)
Get database id given a user name.
Definition: User.php:765
MediaWiki\Auth\AuthenticationResponse\newFail
static newFail(Message $msg)
Definition: AuthenticationResponse.php:146
as
This document is intended to provide useful advice for parties seeking to redistribute MediaWiki to end users It s targeted particularly at maintainers for Linux since it s been observed that distribution packages of MediaWiki often break We ve consistently had to recommend that users seeking support use official tarballs instead of their distribution s and this often solves whatever problem the user is having It would be nice if this could such as
Definition: distributors.txt:9
MediaWiki\Auth\AuthManager\setSessionDataForUser
setSessionDataForUser( $user, $remember=null)
Log the user in.
Definition: AuthManager.php:2370
MediaWiki\Auth\AuthManager\$createdAccountAuthenticationRequests
CreatedAccountAuthenticationRequest[] $createdAccountAuthenticationRequests
Definition: AuthManager.php:140
MediaWiki\Auth\AuthManager\$config
Config $config
Definition: AuthManager.php:122
MediaWiki\Auth\AuthManager\canCreateAccount
canCreateAccount( $username, $options=[])
Determine whether a particular account can be created.
Definition: AuthManager.php:927
$source
$source
Definition: mwdoc-filter.php:46
MediaWiki\Auth\AuthManager\ACTION_LOGIN
const ACTION_LOGIN
Log in with an existing (not necessarily local) user.
Definition: AuthManager.php:84
wfMessage
either a unescaped string or a HtmlArmor object after in associative array form externallinks including delete and has completed for all link tables whether this was an auto creation default is conds Array Extra conditions for the No matching items in log is displayed if loglist is empty msgKey Array If you want a nice box with a set this to the key of the message First element is the message additional optional elements are parameters for the key that are processed with wfMessage() -> params() ->parseAsBlock() - offset Set to overwrite offset parameter in $wgRequest set to '' to unset offset - wrap String Wrap the message in html(usually something like "&lt
MediaWiki\Auth\AuthManager\getSecondaryAuthenticationProviders
getSecondaryAuthenticationProviders()
Get the list of SecondaryAuthenticationProviders.
Definition: AuthManager.php:2355
class
you have access to all of the normal MediaWiki so you can get a DB use the etc For full docs on the Maintenance class
Definition: maintenance.txt:52
$wgAuth
$wgAuth $wgAuth
Authentication plugin.
Definition: DefaultSettings.php:7337
MediaWiki\Auth\AuthManager\userCanAuthenticate
userCanAuthenticate( $username)
Determine whether a username can authenticate.
Definition: AuthManager.php:787
User\IGNORE_USER_RIGHTS
const IGNORE_USER_RIGHTS
Definition: User.php:88
MediaWikiServices
injection txt This is an overview of how MediaWiki makes use of dependency injection The design described here grew from the discussion of RFC T384 The term dependency this means that anything an object needs to operate should be injected from the the object itself should only know narrow no concrete implementation of the logic it relies on The requirement to inject everything typically results in an architecture that based on two main types of and essentially stateless service objects that use other service objects to operate on the value objects As of the beginning MediaWiki is only starting to use the DI approach Much of the code still relies on global state or direct resulting in a highly cyclical dependency MediaWikiServices
Definition: injection.txt:23
MediaWiki\Auth\AuthenticationResponse\PASS
const PASS
Indicates that the authentication succeeded.
Definition: AuthenticationResponse.php:39
User\isUsableName
static isUsableName( $name)
Usernames which fail to pass this function will be blocked from user login and new account registrati...
Definition: User.php:900
MediaWiki\Auth\AuthManager\allowsAuthenticationDataChange
allowsAuthenticationDataChange(AuthenticationRequest $req, $checkData=true)
Validate a change of authentication data (e.g.
Definition: AuthManager.php:851
MediaWiki\Auth\AuthenticationResponse\newRestart
static newRestart(Message $msg)
Definition: AuthenticationResponse.php:159
User\isBlockedFromCreateAccount
isBlockedFromCreateAccount()
Get whether the user is explicitly blocked from account creation.
Definition: User.php:4273
User
The User object encapsulates all of the user-specific settings (user_id, name, rights,...
Definition: User.php:51
MediaWiki\Auth\PrimaryAuthenticationProvider
A primary authentication provider is responsible for associating the submitted authentication data wi...
Definition: PrimaryAuthenticationProvider.php:75
Hooks\run
static run( $event, array $args=[], $deprecatedVersion=null)
Call hook functions defined in Hooks::register and $wgHooks.
Definition: Hooks.php:203
$username
this hook is for auditing only or null if authentication failed before getting that far $username
Definition: hooks.txt:781
MediaWiki\Auth
Definition: AbstractAuthenticationProvider.php:22
MediaWiki\Auth\AuthManager\setLogger
setLogger(LoggerInterface $logger)
Definition: AuthManager.php:169
MediaWiki\Auth\AuthManager\providerArrayFromSpecs
providerArrayFromSpecs( $class, array $specs)
Create an array of AuthenticationProviders from an array of ObjectFactory specs.
Definition: AuthManager.php:2279
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:2249
User\isCreatableName
static isCreatableName( $name)
Usernames which fail to pass this function will be blocked from new account registrations,...
Definition: User.php:975
MediaWiki\Auth\AuthManager\checkAccountCreatePermissions
checkAccountCreatePermissions(User $creator)
Basic permissions checks on whether a user can create accounts.
Definition: AuthManager.php:975
$flags
it s the revision text itself In either if gzip is the revision text is gzipped $flags
Definition: hooks.txt:2801
MediaWiki\Auth\AuthenticationRequest
This is a value object for authentication requests.
Definition: AuthenticationRequest.php:37
MediaWiki\Auth\AuthenticationRequest\REQUIRED
const REQUIRED
Indicates that the request is required for authentication to proceed.
Definition: AuthenticationRequest.php:46
array
the array() calling protocol came about after MediaWiki 1.4rc1.
$wgContLang
this class mediates it Skin Encapsulates a look and feel for the wiki All of the functions that render HTML and make choices about how to render it are here and are called from various other places when and is meant to be subclassed with other skins that may override some of its functions The User object contains a reference to a and so rather than having a global skin object we just rely on the global User and get the skin with $wgUser and also has some character encoding functions and other locale stuff The current user interface language is instantiated as and the content language as $wgContLang
Definition: design.txt:56
MediaWiki\Auth\AuthManager\getAuthenticationRequestsInternal
getAuthenticationRequestsInternal( $providerAction, array $options, array $providers, User $user=null)
Internal request lookup for self::getAuthenticationRequests.
Definition: AuthManager.php:2070
MediaWiki\Auth\AuthenticationProvider
An AuthenticationProvider is used by AuthManager when authenticating users.
Definition: AuthenticationProvider.php:39