MediaWiki  REL1_31
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 use Wikimedia\ObjectFactory;
35 
83 class AuthManager implements LoggerAwareInterface {
85  const ACTION_LOGIN = 'login';
88  const ACTION_LOGIN_CONTINUE = 'login-continue';
90  const ACTION_CREATE = 'create';
93  const ACTION_CREATE_CONTINUE = 'create-continue';
95  const ACTION_LINK = 'link';
98  const ACTION_LINK_CONTINUE = 'link-continue';
100  const ACTION_CHANGE = 'change';
102  const ACTION_REMOVE = 'remove';
104  const ACTION_UNLINK = 'unlink';
105 
107  const SEC_OK = 'ok';
109  const SEC_REAUTH = 'reauth';
111  const SEC_FAIL = 'fail';
112 
115 
117  private static $instance = null;
118 
120  private $request;
121 
123  private $config;
124 
126  private $logger;
127 
130 
133 
136 
139 
142 
147  public static function singleton() {
148  if ( self::$instance === null ) {
149  self::$instance = new self(
150  \RequestContext::getMain()->getRequest(),
151  MediaWikiServices::getInstance()->getMainConfig()
152  );
153  }
154  return self::$instance;
155  }
156 
162  $this->request = $request;
163  $this->config = $config;
164  $this->setLogger( \MediaWiki\Logger\LoggerFactory::getInstance( 'authentication' ) );
165  }
166 
170  public function setLogger( LoggerInterface $logger ) {
171  $this->logger = $logger;
172  }
173 
177  public function getRequest() {
178  return $this->request;
179  }
180 
187  public function forcePrimaryAuthenticationProviders( array $providers, $why ) {
188  $this->logger->warning( "Overriding AuthManager primary authn because $why" );
189 
190  if ( $this->primaryAuthenticationProviders !== null ) {
191  $this->logger->warning(
192  'PrimaryAuthenticationProviders have already been accessed! I hope nothing breaks.'
193  );
194 
195  $this->allAuthenticationProviders = array_diff_key(
196  $this->allAuthenticationProviders,
197  $this->primaryAuthenticationProviders
198  );
199  $session = $this->request->getSession();
200  $session->remove( 'AuthManager::authnState' );
201  $session->remove( 'AuthManager::accountCreationState' );
202  $session->remove( 'AuthManager::accountLinkState' );
203  $this->createdAccountAuthenticationRequests = [];
204  }
205 
206  $this->primaryAuthenticationProviders = [];
207  foreach ( $providers as $provider ) {
208  if ( !$provider instanceof PrimaryAuthenticationProvider ) {
209  throw new \RuntimeException(
210  'Expected instance of MediaWiki\\Auth\\PrimaryAuthenticationProvider, got ' .
211  get_class( $provider )
212  );
213  }
214  $provider->setLogger( $this->logger );
215  $provider->setManager( $this );
216  $provider->setConfig( $this->config );
217  $id = $provider->getUniqueId();
218  if ( isset( $this->allAuthenticationProviders[$id] ) ) {
219  throw new \RuntimeException(
220  "Duplicate specifications for id $id (classes " .
221  get_class( $provider ) . ' and ' .
222  get_class( $this->allAuthenticationProviders[$id] ) . ')'
223  );
224  }
225  $this->allAuthenticationProviders[$id] = $provider;
226  $this->primaryAuthenticationProviders[$id] = $provider;
227  }
228  }
229 
239  public static function callLegacyAuthPlugin( $method, array $params, $return = null ) {
240  global $wgAuth;
241 
242  if ( $wgAuth && !$wgAuth instanceof AuthManagerAuthPlugin ) {
243  return call_user_func_array( [ $wgAuth, $method ], $params );
244  } else {
245  return $return;
246  }
247  }
248 
262  public function canAuthenticateNow() {
263  return $this->request->getSession()->canSetUser();
264  }
265 
284  public function beginAuthentication( array $reqs, $returnToUrl ) {
285  $session = $this->request->getSession();
286  if ( !$session->canSetUser() ) {
287  // Caller should have called canAuthenticateNow()
288  $session->remove( 'AuthManager::authnState' );
289  throw new \LogicException( 'Authentication is not possible now' );
290  }
291 
292  $guessUserName = null;
293  foreach ( $reqs as $req ) {
294  $req->returnToUrl = $returnToUrl;
295  // @codeCoverageIgnoreStart
296  if ( $req->username !== null && $req->username !== '' ) {
297  if ( $guessUserName === null ) {
298  $guessUserName = $req->username;
299  } elseif ( $guessUserName !== $req->username ) {
300  $guessUserName = null;
301  break;
302  }
303  }
304  // @codeCoverageIgnoreEnd
305  }
306 
307  // Check for special-case login of a just-created account
310  );
311  if ( $req ) {
312  if ( !in_array( $req, $this->createdAccountAuthenticationRequests, true ) ) {
313  throw new \LogicException(
314  'CreatedAccountAuthenticationRequests are only valid on ' .
315  'the same AuthManager that created the account'
316  );
317  }
318 
319  $user = User::newFromName( $req->username );
320  // @codeCoverageIgnoreStart
321  if ( !$user ) {
322  throw new \UnexpectedValueException(
323  "CreatedAccountAuthenticationRequest had invalid username \"{$req->username}\""
324  );
325  } elseif ( $user->getId() != $req->id ) {
326  throw new \UnexpectedValueException(
327  "ID for \"{$req->username}\" was {$user->getId()}, expected {$req->id}"
328  );
329  }
330  // @codeCoverageIgnoreEnd
331 
332  $this->logger->info( 'Logging in {user} after account creation', [
333  'user' => $user->getName(),
334  ] );
336  $this->setSessionDataForUser( $user );
337  $this->callMethodOnProviders( 7, 'postAuthentication', [ $user, $ret ] );
338  $session->remove( 'AuthManager::authnState' );
339  \Hooks::run( 'AuthManagerLoginAuthenticateAudit', [ $ret, $user, $user->getName() ] );
340  return $ret;
341  }
342 
343  $this->removeAuthenticationSessionData( null );
344 
345  foreach ( $this->getPreAuthenticationProviders() as $provider ) {
346  $status = $provider->testForAuthentication( $reqs );
347  if ( !$status->isGood() ) {
348  $this->logger->debug( 'Login failed in pre-authentication by ' . $provider->getUniqueId() );
350  Status::wrap( $status )->getMessage()
351  );
352  $this->callMethodOnProviders( 7, 'postAuthentication',
353  [ User::newFromName( $guessUserName ) ?: null, $ret ]
354  );
355  \Hooks::run( 'AuthManagerLoginAuthenticateAudit', [ $ret, null, $guessUserName ] );
356  return $ret;
357  }
358  }
359 
360  $state = [
361  'reqs' => $reqs,
362  'returnToUrl' => $returnToUrl,
363  'guessUserName' => $guessUserName,
364  'primary' => null,
365  'primaryResponse' => null,
366  'secondary' => [],
367  'maybeLink' => [],
368  'continueRequests' => [],
369  ];
370 
371  // Preserve state from a previous failed login
374  );
375  if ( $req ) {
376  $state['maybeLink'] = $req->maybeLink;
377  }
378 
379  $session = $this->request->getSession();
380  $session->setSecret( 'AuthManager::authnState', $state );
381  $session->persist();
382 
383  return $this->continueAuthentication( $reqs );
384  }
385 
408  public function continueAuthentication( array $reqs ) {
409  $session = $this->request->getSession();
410  try {
411  if ( !$session->canSetUser() ) {
412  // Caller should have called canAuthenticateNow()
413  // @codeCoverageIgnoreStart
414  throw new \LogicException( 'Authentication is not possible now' );
415  // @codeCoverageIgnoreEnd
416  }
417 
418  $state = $session->getSecret( 'AuthManager::authnState' );
419  if ( !is_array( $state ) ) {
421  wfMessage( 'authmanager-authn-not-in-progress' )
422  );
423  }
424  $state['continueRequests'] = [];
425 
426  $guessUserName = $state['guessUserName'];
427 
428  foreach ( $reqs as $req ) {
429  $req->returnToUrl = $state['returnToUrl'];
430  }
431 
432  // Step 1: Choose an primary authentication provider, and call it until it succeeds.
433 
434  if ( $state['primary'] === null ) {
435  // We haven't picked a PrimaryAuthenticationProvider yet
436  // @codeCoverageIgnoreStart
437  $guessUserName = null;
438  foreach ( $reqs as $req ) {
439  if ( $req->username !== null && $req->username !== '' ) {
440  if ( $guessUserName === null ) {
441  $guessUserName = $req->username;
442  } elseif ( $guessUserName !== $req->username ) {
443  $guessUserName = null;
444  break;
445  }
446  }
447  }
448  $state['guessUserName'] = $guessUserName;
449  // @codeCoverageIgnoreEnd
450  $state['reqs'] = $reqs;
451 
452  foreach ( $this->getPrimaryAuthenticationProviders() as $id => $provider ) {
453  $res = $provider->beginPrimaryAuthentication( $reqs );
454  switch ( $res->status ) {
456  $state['primary'] = $id;
457  $state['primaryResponse'] = $res;
458  $this->logger->debug( "Primary login with $id succeeded" );
459  break 2;
461  $this->logger->debug( "Login failed in primary authentication by $id" );
462  if ( $res->createRequest || $state['maybeLink'] ) {
463  $res->createRequest = new CreateFromLoginAuthenticationRequest(
464  $res->createRequest, $state['maybeLink']
465  );
466  }
467  $this->callMethodOnProviders( 7, 'postAuthentication',
468  [ User::newFromName( $guessUserName ) ?: null, $res ]
469  );
470  $session->remove( 'AuthManager::authnState' );
471  \Hooks::run( 'AuthManagerLoginAuthenticateAudit', [ $res, null, $guessUserName ] );
472  return $res;
474  // Continue loop
475  break;
478  $this->logger->debug( "Primary login with $id returned $res->status" );
479  $this->fillRequests( $res->neededRequests, self::ACTION_LOGIN, $guessUserName );
480  $state['primary'] = $id;
481  $state['continueRequests'] = $res->neededRequests;
482  $session->setSecret( 'AuthManager::authnState', $state );
483  return $res;
484 
485  // @codeCoverageIgnoreStart
486  default:
487  throw new \DomainException(
488  get_class( $provider ) . "::beginPrimaryAuthentication() returned $res->status"
489  );
490  // @codeCoverageIgnoreEnd
491  }
492  }
493  if ( $state['primary'] === null ) {
494  $this->logger->debug( 'Login failed in primary authentication because no provider accepted' );
496  wfMessage( 'authmanager-authn-no-primary' )
497  );
498  $this->callMethodOnProviders( 7, 'postAuthentication',
499  [ User::newFromName( $guessUserName ) ?: null, $ret ]
500  );
501  $session->remove( 'AuthManager::authnState' );
502  return $ret;
503  }
504  } elseif ( $state['primaryResponse'] === null ) {
505  $provider = $this->getAuthenticationProvider( $state['primary'] );
506  if ( !$provider instanceof PrimaryAuthenticationProvider ) {
507  // Configuration changed? Force them to start over.
508  // @codeCoverageIgnoreStart
510  wfMessage( 'authmanager-authn-not-in-progress' )
511  );
512  $this->callMethodOnProviders( 7, 'postAuthentication',
513  [ User::newFromName( $guessUserName ) ?: null, $ret ]
514  );
515  $session->remove( 'AuthManager::authnState' );
516  return $ret;
517  // @codeCoverageIgnoreEnd
518  }
519  $id = $provider->getUniqueId();
520  $res = $provider->continuePrimaryAuthentication( $reqs );
521  switch ( $res->status ) {
523  $state['primaryResponse'] = $res;
524  $this->logger->debug( "Primary login with $id succeeded" );
525  break;
527  $this->logger->debug( "Login failed in primary authentication by $id" );
528  if ( $res->createRequest || $state['maybeLink'] ) {
529  $res->createRequest = new CreateFromLoginAuthenticationRequest(
530  $res->createRequest, $state['maybeLink']
531  );
532  }
533  $this->callMethodOnProviders( 7, 'postAuthentication',
534  [ User::newFromName( $guessUserName ) ?: null, $res ]
535  );
536  $session->remove( 'AuthManager::authnState' );
537  \Hooks::run( 'AuthManagerLoginAuthenticateAudit', [ $res, null, $guessUserName ] );
538  return $res;
541  $this->logger->debug( "Primary login with $id returned $res->status" );
542  $this->fillRequests( $res->neededRequests, self::ACTION_LOGIN, $guessUserName );
543  $state['continueRequests'] = $res->neededRequests;
544  $session->setSecret( 'AuthManager::authnState', $state );
545  return $res;
546  default:
547  throw new \DomainException(
548  get_class( $provider ) . "::continuePrimaryAuthentication() returned $res->status"
549  );
550  }
551  }
552 
553  $res = $state['primaryResponse'];
554  if ( $res->username === null ) {
555  $provider = $this->getAuthenticationProvider( $state['primary'] );
556  if ( !$provider instanceof PrimaryAuthenticationProvider ) {
557  // Configuration changed? Force them to start over.
558  // @codeCoverageIgnoreStart
560  wfMessage( 'authmanager-authn-not-in-progress' )
561  );
562  $this->callMethodOnProviders( 7, 'postAuthentication',
563  [ User::newFromName( $guessUserName ) ?: null, $ret ]
564  );
565  $session->remove( 'AuthManager::authnState' );
566  return $ret;
567  // @codeCoverageIgnoreEnd
568  }
569 
570  if ( $provider->accountCreationType() === PrimaryAuthenticationProvider::TYPE_LINK &&
571  $res->linkRequest &&
572  // don't confuse the user with an incorrect message if linking is disabled
573  $this->getAuthenticationProvider( ConfirmLinkSecondaryAuthenticationProvider::class )
574  ) {
575  $state['maybeLink'][$res->linkRequest->getUniqueId()] = $res->linkRequest;
576  $msg = 'authmanager-authn-no-local-user-link';
577  } else {
578  $msg = 'authmanager-authn-no-local-user';
579  }
580  $this->logger->debug(
581  "Primary login with {$provider->getUniqueId()} succeeded, but returned no user"
582  );
584  $ret->neededRequests = $this->getAuthenticationRequestsInternal(
585  self::ACTION_LOGIN,
586  [],
588  );
589  if ( $res->createRequest || $state['maybeLink'] ) {
590  $ret->createRequest = new CreateFromLoginAuthenticationRequest(
591  $res->createRequest, $state['maybeLink']
592  );
593  $ret->neededRequests[] = $ret->createRequest;
594  }
595  $this->fillRequests( $ret->neededRequests, self::ACTION_LOGIN, null, true );
596  $session->setSecret( 'AuthManager::authnState', [
597  'reqs' => [], // Will be filled in later
598  'primary' => null,
599  'primaryResponse' => null,
600  'secondary' => [],
601  'continueRequests' => $ret->neededRequests,
602  ] + $state );
603  return $ret;
604  }
605 
606  // Step 2: Primary authentication succeeded, create the User object
607  // (and add the user locally if necessary)
608 
609  $user = User::newFromName( $res->username, 'usable' );
610  if ( !$user ) {
611  $provider = $this->getAuthenticationProvider( $state['primary'] );
612  throw new \DomainException(
613  get_class( $provider ) . " returned an invalid username: {$res->username}"
614  );
615  }
616  if ( $user->getId() === 0 ) {
617  // User doesn't exist locally. Create it.
618  $this->logger->info( 'Auto-creating {user} on login', [
619  'user' => $user->getName(),
620  ] );
621  $status = $this->autoCreateUser( $user, $state['primary'], false );
622  if ( !$status->isGood() ) {
624  Status::wrap( $status )->getMessage( 'authmanager-authn-autocreate-failed' )
625  );
626  $this->callMethodOnProviders( 7, 'postAuthentication', [ $user, $ret ] );
627  $session->remove( 'AuthManager::authnState' );
628  \Hooks::run( 'AuthManagerLoginAuthenticateAudit', [ $ret, $user, $user->getName() ] );
629  return $ret;
630  }
631  }
632 
633  // Step 3: Iterate over all the secondary authentication providers.
634 
635  $beginReqs = $state['reqs'];
636 
637  foreach ( $this->getSecondaryAuthenticationProviders() as $id => $provider ) {
638  if ( !isset( $state['secondary'][$id] ) ) {
639  // This provider isn't started yet, so we pass it the set
640  // of reqs from beginAuthentication instead of whatever
641  // might have been used by a previous provider in line.
642  $func = 'beginSecondaryAuthentication';
643  $res = $provider->beginSecondaryAuthentication( $user, $beginReqs );
644  } elseif ( !$state['secondary'][$id] ) {
645  $func = 'continueSecondaryAuthentication';
646  $res = $provider->continueSecondaryAuthentication( $user, $reqs );
647  } else {
648  continue;
649  }
650  switch ( $res->status ) {
652  $this->logger->debug( "Secondary login with $id succeeded" );
653  // fall through
655  $state['secondary'][$id] = true;
656  break;
658  $this->logger->debug( "Login failed in secondary authentication by $id" );
659  $this->callMethodOnProviders( 7, 'postAuthentication', [ $user, $res ] );
660  $session->remove( 'AuthManager::authnState' );
661  \Hooks::run( 'AuthManagerLoginAuthenticateAudit', [ $res, $user, $user->getName() ] );
662  return $res;
665  $this->logger->debug( "Secondary login with $id returned " . $res->status );
666  $this->fillRequests( $res->neededRequests, self::ACTION_LOGIN, $user->getName() );
667  $state['secondary'][$id] = false;
668  $state['continueRequests'] = $res->neededRequests;
669  $session->setSecret( 'AuthManager::authnState', $state );
670  return $res;
671 
672  // @codeCoverageIgnoreStart
673  default:
674  throw new \DomainException(
675  get_class( $provider ) . "::{$func}() returned $res->status"
676  );
677  // @codeCoverageIgnoreEnd
678  }
679  }
680 
681  // Step 4: Authentication complete! Set the user in the session and
682  // clean up.
683 
684  $this->logger->info( 'Login for {user} succeeded from {clientip}', [
685  'user' => $user->getName(),
686  'clientip' => $this->request->getIP(),
687  ] );
691  );
692  $this->setSessionDataForUser( $user, $req && $req->rememberMe );
694  $this->callMethodOnProviders( 7, 'postAuthentication', [ $user, $ret ] );
695  $session->remove( 'AuthManager::authnState' );
696  $this->removeAuthenticationSessionData( null );
697  \Hooks::run( 'AuthManagerLoginAuthenticateAudit', [ $ret, $user, $user->getName() ] );
698  return $ret;
699  } catch ( \Exception $ex ) {
700  $session->remove( 'AuthManager::authnState' );
701  throw $ex;
702  }
703  }
704 
716  public function securitySensitiveOperationStatus( $operation ) {
718 
719  $this->logger->debug( __METHOD__ . ": Checking $operation" );
720 
721  $session = $this->request->getSession();
722  $aId = $session->getUser()->getId();
723  if ( $aId === 0 ) {
724  // User isn't authenticated. DWIM?
726  $this->logger->info( __METHOD__ . ": Not logged in! $operation is $status" );
727  return $status;
728  }
729 
730  if ( $session->canSetUser() ) {
731  $id = $session->get( 'AuthManager:lastAuthId' );
732  $last = $session->get( 'AuthManager:lastAuthTimestamp' );
733  if ( $id !== $aId || $last === null ) {
734  $timeSinceLogin = PHP_INT_MAX; // Forever ago
735  } else {
736  $timeSinceLogin = max( 0, time() - $last );
737  }
738 
739  $thresholds = $this->config->get( 'ReauthenticateTime' );
740  if ( isset( $thresholds[$operation] ) ) {
741  $threshold = $thresholds[$operation];
742  } elseif ( isset( $thresholds['default'] ) ) {
743  $threshold = $thresholds['default'];
744  } else {
745  throw new \UnexpectedValueException( '$wgReauthenticateTime lacks a default' );
746  }
747 
748  if ( $threshold >= 0 && $timeSinceLogin > $threshold ) {
750  }
751  } else {
752  $timeSinceLogin = -1;
753 
754  $pass = $this->config->get( 'AllowSecuritySensitiveOperationIfCannotReauthenticate' );
755  if ( isset( $pass[$operation] ) ) {
756  $status = $pass[$operation] ? self::SEC_OK : self::SEC_FAIL;
757  } elseif ( isset( $pass['default'] ) ) {
758  $status = $pass['default'] ? self::SEC_OK : self::SEC_FAIL;
759  } else {
760  throw new \UnexpectedValueException(
761  '$wgAllowSecuritySensitiveOperationIfCannotReauthenticate lacks a default'
762  );
763  }
764  }
765 
766  \Hooks::run( 'SecuritySensitiveOperationStatus', [
767  &$status, $operation, $session, $timeSinceLogin
768  ] );
769 
770  // If authentication is not possible, downgrade from "REAUTH" to "FAIL".
771  if ( !$this->canAuthenticateNow() && $status === self::SEC_REAUTH ) {
773  }
774 
775  $this->logger->info( __METHOD__ . ": $operation is $status" );
776 
777  return $status;
778  }
779 
789  public function userCanAuthenticate( $username ) {
790  foreach ( $this->getPrimaryAuthenticationProviders() as $provider ) {
791  if ( $provider->testUserCanAuthenticate( $username ) ) {
792  return true;
793  }
794  }
795  return false;
796  }
797 
812  public function normalizeUsername( $username ) {
813  $ret = [];
814  foreach ( $this->getPrimaryAuthenticationProviders() as $provider ) {
815  $normalized = $provider->providerNormalizeUsername( $username );
816  if ( $normalized !== null ) {
817  $ret[$normalized] = true;
818  }
819  }
820  return array_keys( $ret );
821  }
822 
837  public function revokeAccessForUser( $username ) {
838  $this->logger->info( 'Revoking access for {user}', [
839  'user' => $username,
840  ] );
841  $this->callMethodOnProviders( 6, 'providerRevokeAccessForUser', [ $username ] );
842  }
843 
853  public function allowsAuthenticationDataChange( AuthenticationRequest $req, $checkData = true ) {
854  $any = false;
855  $providers = $this->getPrimaryAuthenticationProviders() +
857  foreach ( $providers as $provider ) {
858  $status = $provider->providerAllowsAuthenticationDataChange( $req, $checkData );
859  if ( !$status->isGood() ) {
860  return Status::wrap( $status );
861  }
862  $any = $any || $status->value !== 'ignored';
863  }
864  if ( !$any ) {
865  $status = Status::newGood( 'ignored' );
866  $status->warning( 'authmanager-change-not-supported' );
867  return $status;
868  }
869  return Status::newGood();
870  }
871 
889  public function changeAuthenticationData( AuthenticationRequest $req, $isAddition = false ) {
890  $this->logger->info( 'Changing authentication data for {user} class {what}', [
891  'user' => is_string( $req->username ) ? $req->username : '<no name>',
892  'what' => get_class( $req ),
893  ] );
894 
895  $this->callMethodOnProviders( 6, 'providerChangeAuthenticationData', [ $req ] );
896 
897  // When the main account's authentication data is changed, invalidate
898  // all BotPasswords too.
899  if ( !$isAddition ) {
901  }
902  }
903 
915  public function canCreateAccounts() {
916  foreach ( $this->getPrimaryAuthenticationProviders() as $provider ) {
917  switch ( $provider->accountCreationType() ) {
920  return true;
921  }
922  }
923  return false;
924  }
925 
934  public function canCreateAccount( $username, $options = [] ) {
935  // Back compat
936  if ( is_int( $options ) ) {
937  $options = [ 'flags' => $options ];
938  }
939  $options += [
940  'flags' => User::READ_NORMAL,
941  'creating' => false,
942  ];
943  $flags = $options['flags'];
944 
945  if ( !$this->canCreateAccounts() ) {
946  return Status::newFatal( 'authmanager-create-disabled' );
947  }
948 
949  if ( $this->userExists( $username, $flags ) ) {
950  return Status::newFatal( 'userexists' );
951  }
952 
953  $user = User::newFromName( $username, 'creatable' );
954  if ( !is_object( $user ) ) {
955  return Status::newFatal( 'noname' );
956  } else {
957  $user->load( $flags ); // Explicitly load with $flags, auto-loading always uses READ_NORMAL
958  if ( $user->getId() !== 0 ) {
959  return Status::newFatal( 'userexists' );
960  }
961  }
962 
963  // Denied by providers?
964  $providers = $this->getPreAuthenticationProviders() +
967  foreach ( $providers as $provider ) {
968  $status = $provider->testUserForCreation( $user, false, $options );
969  if ( !$status->isGood() ) {
970  return Status::wrap( $status );
971  }
972  }
973 
974  return Status::newGood();
975  }
976 
982  public function checkAccountCreatePermissions( User $creator ) {
983  // Wiki is read-only?
984  if ( wfReadOnly() ) {
985  return Status::newFatal( wfMessage( 'readonlytext', wfReadOnlyReason() ) );
986  }
987 
988  // This is awful, this permission check really shouldn't go through Title.
989  $permErrors = \SpecialPage::getTitleFor( 'CreateAccount' )
990  ->getUserPermissionsErrors( 'createaccount', $creator, 'secure' );
991  if ( $permErrors ) {
993  foreach ( $permErrors as $args ) {
994  call_user_func_array( [ $status, 'fatal' ], $args );
995  }
996  return $status;
997  }
998 
999  $block = $creator->isBlockedFromCreateAccount();
1000  if ( $block ) {
1001  $errorParams = [
1002  $block->getTarget(),
1003  $block->mReason ?: wfMessage( 'blockednoreason' )->text(),
1004  $block->getByName()
1005  ];
1006 
1007  if ( $block->getType() === \Block::TYPE_RANGE ) {
1008  $errorMessage = 'cantcreateaccount-range-text';
1009  $errorParams[] = $this->getRequest()->getIP();
1010  } else {
1011  $errorMessage = 'cantcreateaccount-text';
1012  }
1013 
1014  return Status::newFatal( wfMessage( $errorMessage, $errorParams ) );
1015  }
1016 
1017  $ip = $this->getRequest()->getIP();
1018  if ( $creator->isDnsBlacklisted( $ip, true /* check $wgProxyWhitelist */ ) ) {
1019  return Status::newFatal( 'sorbs_create_account_reason' );
1020  }
1021 
1022  return Status::newGood();
1023  }
1024 
1044  public function beginAccountCreation( User $creator, array $reqs, $returnToUrl ) {
1045  $session = $this->request->getSession();
1046  if ( !$this->canCreateAccounts() ) {
1047  // Caller should have called canCreateAccounts()
1048  $session->remove( 'AuthManager::accountCreationState' );
1049  throw new \LogicException( 'Account creation is not possible' );
1050  }
1051 
1052  try {
1054  } catch ( \UnexpectedValueException $ex ) {
1055  $username = null;
1056  }
1057  if ( $username === null ) {
1058  $this->logger->debug( __METHOD__ . ': No username provided' );
1059  return AuthenticationResponse::newFail( wfMessage( 'noname' ) );
1060  }
1061 
1062  // Permissions check
1063  $status = $this->checkAccountCreatePermissions( $creator );
1064  if ( !$status->isGood() ) {
1065  $this->logger->debug( __METHOD__ . ': {creator} cannot create users: {reason}', [
1066  'user' => $username,
1067  'creator' => $creator->getName(),
1068  'reason' => $status->getWikiText( null, null, 'en' )
1069  ] );
1070  return AuthenticationResponse::newFail( $status->getMessage() );
1071  }
1072 
1073  $status = $this->canCreateAccount(
1074  $username, [ 'flags' => User::READ_LOCKING, 'creating' => true ]
1075  );
1076  if ( !$status->isGood() ) {
1077  $this->logger->debug( __METHOD__ . ': {user} cannot be created: {reason}', [
1078  'user' => $username,
1079  'creator' => $creator->getName(),
1080  'reason' => $status->getWikiText( null, null, 'en' )
1081  ] );
1082  return AuthenticationResponse::newFail( $status->getMessage() );
1083  }
1084 
1085  $user = User::newFromName( $username, 'creatable' );
1086  foreach ( $reqs as $req ) {
1087  $req->username = $username;
1088  $req->returnToUrl = $returnToUrl;
1089  if ( $req instanceof UserDataAuthenticationRequest ) {
1090  $status = $req->populateUser( $user );
1091  if ( !$status->isGood() ) {
1093  $session->remove( 'AuthManager::accountCreationState' );
1094  $this->logger->debug( __METHOD__ . ': UserData is invalid: {reason}', [
1095  'user' => $user->getName(),
1096  'creator' => $creator->getName(),
1097  'reason' => $status->getWikiText( null, null, 'en' ),
1098  ] );
1099  return AuthenticationResponse::newFail( $status->getMessage() );
1100  }
1101  }
1102  }
1103 
1104  $this->removeAuthenticationSessionData( null );
1105 
1106  $state = [
1107  'username' => $username,
1108  'userid' => 0,
1109  'creatorid' => $creator->getId(),
1110  'creatorname' => $creator->getName(),
1111  'reqs' => $reqs,
1112  'returnToUrl' => $returnToUrl,
1113  'primary' => null,
1114  'primaryResponse' => null,
1115  'secondary' => [],
1116  'continueRequests' => [],
1117  'maybeLink' => [],
1118  'ranPreTests' => false,
1119  ];
1120 
1121  // Special case: converting a login to an account creation
1124  );
1125  if ( $req ) {
1126  $state['maybeLink'] = $req->maybeLink;
1127 
1128  if ( $req->createRequest ) {
1129  $reqs[] = $req->createRequest;
1130  $state['reqs'][] = $req->createRequest;
1131  }
1132  }
1133 
1134  $session->setSecret( 'AuthManager::accountCreationState', $state );
1135  $session->persist();
1136 
1137  return $this->continueAccountCreation( $reqs );
1138  }
1139 
1145  public function continueAccountCreation( array $reqs ) {
1146  $session = $this->request->getSession();
1147  try {
1148  if ( !$this->canCreateAccounts() ) {
1149  // Caller should have called canCreateAccounts()
1150  $session->remove( 'AuthManager::accountCreationState' );
1151  throw new \LogicException( 'Account creation is not possible' );
1152  }
1153 
1154  $state = $session->getSecret( 'AuthManager::accountCreationState' );
1155  if ( !is_array( $state ) ) {
1157  wfMessage( 'authmanager-create-not-in-progress' )
1158  );
1159  }
1160  $state['continueRequests'] = [];
1161 
1162  // Step 0: Prepare and validate the input
1163 
1164  $user = User::newFromName( $state['username'], 'creatable' );
1165  if ( !is_object( $user ) ) {
1166  $session->remove( 'AuthManager::accountCreationState' );
1167  $this->logger->debug( __METHOD__ . ': Invalid username', [
1168  'user' => $state['username'],
1169  ] );
1170  return AuthenticationResponse::newFail( wfMessage( 'noname' ) );
1171  }
1172 
1173  if ( $state['creatorid'] ) {
1174  $creator = User::newFromId( $state['creatorid'] );
1175  } else {
1176  $creator = new User;
1177  $creator->setName( $state['creatorname'] );
1178  }
1179 
1180  // Avoid account creation races on double submissions
1182  $lock = $cache->getScopedLock( $cache->makeGlobalKey( 'account', md5( $user->getName() ) ) );
1183  if ( !$lock ) {
1184  // Don't clear AuthManager::accountCreationState for this code
1185  // path because the process that won the race owns it.
1186  $this->logger->debug( __METHOD__ . ': Could not acquire account creation lock', [
1187  'user' => $user->getName(),
1188  'creator' => $creator->getName(),
1189  ] );
1190  return AuthenticationResponse::newFail( wfMessage( 'usernameinprogress' ) );
1191  }
1192 
1193  // Permissions check
1194  $status = $this->checkAccountCreatePermissions( $creator );
1195  if ( !$status->isGood() ) {
1196  $this->logger->debug( __METHOD__ . ': {creator} cannot create users: {reason}', [
1197  'user' => $user->getName(),
1198  'creator' => $creator->getName(),
1199  'reason' => $status->getWikiText( null, null, 'en' )
1200  ] );
1201  $ret = AuthenticationResponse::newFail( $status->getMessage() );
1202  $this->callMethodOnProviders( 7, 'postAccountCreation', [ $user, $creator, $ret ] );
1203  $session->remove( 'AuthManager::accountCreationState' );
1204  return $ret;
1205  }
1206 
1207  // Load from master for existence check
1208  $user->load( User::READ_LOCKING );
1209 
1210  if ( $state['userid'] === 0 ) {
1211  if ( $user->getId() != 0 ) {
1212  $this->logger->debug( __METHOD__ . ': User exists locally', [
1213  'user' => $user->getName(),
1214  'creator' => $creator->getName(),
1215  ] );
1216  $ret = AuthenticationResponse::newFail( wfMessage( 'userexists' ) );
1217  $this->callMethodOnProviders( 7, 'postAccountCreation', [ $user, $creator, $ret ] );
1218  $session->remove( 'AuthManager::accountCreationState' );
1219  return $ret;
1220  }
1221  } else {
1222  if ( $user->getId() == 0 ) {
1223  $this->logger->debug( __METHOD__ . ': User does not exist locally when it should', [
1224  'user' => $user->getName(),
1225  'creator' => $creator->getName(),
1226  'expected_id' => $state['userid'],
1227  ] );
1228  throw new \UnexpectedValueException(
1229  "User \"{$state['username']}\" should exist now, but doesn't!"
1230  );
1231  }
1232  if ( $user->getId() != $state['userid'] ) {
1233  $this->logger->debug( __METHOD__ . ': User ID/name mismatch', [
1234  'user' => $user->getName(),
1235  'creator' => $creator->getName(),
1236  'expected_id' => $state['userid'],
1237  'actual_id' => $user->getId(),
1238  ] );
1239  throw new \UnexpectedValueException(
1240  "User \"{$state['username']}\" exists, but " .
1241  "ID {$user->getId()} != {$state['userid']}!"
1242  );
1243  }
1244  }
1245  foreach ( $state['reqs'] as $req ) {
1246  if ( $req instanceof UserDataAuthenticationRequest ) {
1247  $status = $req->populateUser( $user );
1248  if ( !$status->isGood() ) {
1249  // This should never happen...
1251  $this->logger->debug( __METHOD__ . ': UserData is invalid: {reason}', [
1252  'user' => $user->getName(),
1253  'creator' => $creator->getName(),
1254  'reason' => $status->getWikiText( null, null, 'en' ),
1255  ] );
1256  $ret = AuthenticationResponse::newFail( $status->getMessage() );
1257  $this->callMethodOnProviders( 7, 'postAccountCreation', [ $user, $creator, $ret ] );
1258  $session->remove( 'AuthManager::accountCreationState' );
1259  return $ret;
1260  }
1261  }
1262  }
1263 
1264  foreach ( $reqs as $req ) {
1265  $req->returnToUrl = $state['returnToUrl'];
1266  $req->username = $state['username'];
1267  }
1268 
1269  // Run pre-creation tests, if we haven't already
1270  if ( !$state['ranPreTests'] ) {
1271  $providers = $this->getPreAuthenticationProviders() +
1274  foreach ( $providers as $id => $provider ) {
1275  $status = $provider->testForAccountCreation( $user, $creator, $reqs );
1276  if ( !$status->isGood() ) {
1277  $this->logger->debug( __METHOD__ . ": Fail in pre-authentication by $id", [
1278  'user' => $user->getName(),
1279  'creator' => $creator->getName(),
1280  ] );
1282  Status::wrap( $status )->getMessage()
1283  );
1284  $this->callMethodOnProviders( 7, 'postAccountCreation', [ $user, $creator, $ret ] );
1285  $session->remove( 'AuthManager::accountCreationState' );
1286  return $ret;
1287  }
1288  }
1289 
1290  $state['ranPreTests'] = true;
1291  }
1292 
1293  // Step 1: Choose a primary authentication provider and call it until it succeeds.
1294 
1295  if ( $state['primary'] === null ) {
1296  // We haven't picked a PrimaryAuthenticationProvider yet
1297  foreach ( $this->getPrimaryAuthenticationProviders() as $id => $provider ) {
1298  if ( $provider->accountCreationType() === PrimaryAuthenticationProvider::TYPE_NONE ) {
1299  continue;
1300  }
1301  $res = $provider->beginPrimaryAccountCreation( $user, $creator, $reqs );
1302  switch ( $res->status ) {
1304  $this->logger->debug( __METHOD__ . ": Primary creation passed by $id", [
1305  'user' => $user->getName(),
1306  'creator' => $creator->getName(),
1307  ] );
1308  $state['primary'] = $id;
1309  $state['primaryResponse'] = $res;
1310  break 2;
1312  $this->logger->debug( __METHOD__ . ": Primary creation failed by $id", [
1313  'user' => $user->getName(),
1314  'creator' => $creator->getName(),
1315  ] );
1316  $this->callMethodOnProviders( 7, 'postAccountCreation', [ $user, $creator, $res ] );
1317  $session->remove( 'AuthManager::accountCreationState' );
1318  return $res;
1320  // Continue loop
1321  break;
1324  $this->logger->debug( __METHOD__ . ": Primary creation $res->status by $id", [
1325  'user' => $user->getName(),
1326  'creator' => $creator->getName(),
1327  ] );
1328  $this->fillRequests( $res->neededRequests, self::ACTION_CREATE, null );
1329  $state['primary'] = $id;
1330  $state['continueRequests'] = $res->neededRequests;
1331  $session->setSecret( 'AuthManager::accountCreationState', $state );
1332  return $res;
1333 
1334  // @codeCoverageIgnoreStart
1335  default:
1336  throw new \DomainException(
1337  get_class( $provider ) . "::beginPrimaryAccountCreation() returned $res->status"
1338  );
1339  // @codeCoverageIgnoreEnd
1340  }
1341  }
1342  if ( $state['primary'] === null ) {
1343  $this->logger->debug( __METHOD__ . ': Primary creation failed because no provider accepted', [
1344  'user' => $user->getName(),
1345  'creator' => $creator->getName(),
1346  ] );
1348  wfMessage( 'authmanager-create-no-primary' )
1349  );
1350  $this->callMethodOnProviders( 7, 'postAccountCreation', [ $user, $creator, $ret ] );
1351  $session->remove( 'AuthManager::accountCreationState' );
1352  return $ret;
1353  }
1354  } elseif ( $state['primaryResponse'] === null ) {
1355  $provider = $this->getAuthenticationProvider( $state['primary'] );
1356  if ( !$provider instanceof PrimaryAuthenticationProvider ) {
1357  // Configuration changed? Force them to start over.
1358  // @codeCoverageIgnoreStart
1360  wfMessage( 'authmanager-create-not-in-progress' )
1361  );
1362  $this->callMethodOnProviders( 7, 'postAccountCreation', [ $user, $creator, $ret ] );
1363  $session->remove( 'AuthManager::accountCreationState' );
1364  return $ret;
1365  // @codeCoverageIgnoreEnd
1366  }
1367  $id = $provider->getUniqueId();
1368  $res = $provider->continuePrimaryAccountCreation( $user, $creator, $reqs );
1369  switch ( $res->status ) {
1371  $this->logger->debug( __METHOD__ . ": Primary creation passed by $id", [
1372  'user' => $user->getName(),
1373  'creator' => $creator->getName(),
1374  ] );
1375  $state['primaryResponse'] = $res;
1376  break;
1378  $this->logger->debug( __METHOD__ . ": Primary creation failed by $id", [
1379  'user' => $user->getName(),
1380  'creator' => $creator->getName(),
1381  ] );
1382  $this->callMethodOnProviders( 7, 'postAccountCreation', [ $user, $creator, $res ] );
1383  $session->remove( 'AuthManager::accountCreationState' );
1384  return $res;
1387  $this->logger->debug( __METHOD__ . ": Primary creation $res->status by $id", [
1388  'user' => $user->getName(),
1389  'creator' => $creator->getName(),
1390  ] );
1391  $this->fillRequests( $res->neededRequests, self::ACTION_CREATE, null );
1392  $state['continueRequests'] = $res->neededRequests;
1393  $session->setSecret( 'AuthManager::accountCreationState', $state );
1394  return $res;
1395  default:
1396  throw new \DomainException(
1397  get_class( $provider ) . "::continuePrimaryAccountCreation() returned $res->status"
1398  );
1399  }
1400  }
1401 
1402  // Step 2: Primary authentication succeeded, create the User object
1403  // and add the user locally.
1404 
1405  if ( $state['userid'] === 0 ) {
1406  $this->logger->info( 'Creating user {user} during account creation', [
1407  'user' => $user->getName(),
1408  'creator' => $creator->getName(),
1409  ] );
1410  $status = $user->addToDatabase();
1411  if ( !$status->isOK() ) {
1412  // @codeCoverageIgnoreStart
1413  $ret = AuthenticationResponse::newFail( $status->getMessage() );
1414  $this->callMethodOnProviders( 7, 'postAccountCreation', [ $user, $creator, $ret ] );
1415  $session->remove( 'AuthManager::accountCreationState' );
1416  return $ret;
1417  // @codeCoverageIgnoreEnd
1418  }
1419  $this->setDefaultUserOptions( $user, $creator->isAnon() );
1420  \Hooks::run( 'LocalUserCreated', [ $user, false ] );
1421  $user->saveSettings();
1422  $state['userid'] = $user->getId();
1423 
1424  // Update user count
1425  \DeferredUpdates::addUpdate( \SiteStatsUpdate::factory( [ 'users' => 1 ] ) );
1426 
1427  // Watch user's userpage and talk page
1428  $user->addWatch( $user->getUserPage(), User::IGNORE_USER_RIGHTS );
1429 
1430  // Inform the provider
1431  $logSubtype = $provider->finishAccountCreation( $user, $creator, $state['primaryResponse'] );
1432 
1433  // Log the creation
1434  if ( $this->config->get( 'NewUserLog' ) ) {
1435  $isAnon = $creator->isAnon();
1436  $logEntry = new \ManualLogEntry(
1437  'newusers',
1438  $logSubtype ?: ( $isAnon ? 'create' : 'create2' )
1439  );
1440  $logEntry->setPerformer( $isAnon ? $user : $creator );
1441  $logEntry->setTarget( $user->getUserPage() );
1445  );
1446  $logEntry->setComment( $req ? $req->reason : '' );
1447  $logEntry->setParameters( [
1448  '4::userid' => $user->getId(),
1449  ] );
1450  $logid = $logEntry->insert();
1451  $logEntry->publish( $logid );
1452  }
1453  }
1454 
1455  // Step 3: Iterate over all the secondary authentication providers.
1456 
1457  $beginReqs = $state['reqs'];
1458 
1459  foreach ( $this->getSecondaryAuthenticationProviders() as $id => $provider ) {
1460  if ( !isset( $state['secondary'][$id] ) ) {
1461  // This provider isn't started yet, so we pass it the set
1462  // of reqs from beginAuthentication instead of whatever
1463  // might have been used by a previous provider in line.
1464  $func = 'beginSecondaryAccountCreation';
1465  $res = $provider->beginSecondaryAccountCreation( $user, $creator, $beginReqs );
1466  } elseif ( !$state['secondary'][$id] ) {
1467  $func = 'continueSecondaryAccountCreation';
1468  $res = $provider->continueSecondaryAccountCreation( $user, $creator, $reqs );
1469  } else {
1470  continue;
1471  }
1472  switch ( $res->status ) {
1474  $this->logger->debug( __METHOD__ . ": Secondary creation passed by $id", [
1475  'user' => $user->getName(),
1476  'creator' => $creator->getName(),
1477  ] );
1478  // fall through
1480  $state['secondary'][$id] = true;
1481  break;
1484  $this->logger->debug( __METHOD__ . ": Secondary creation $res->status by $id", [
1485  'user' => $user->getName(),
1486  'creator' => $creator->getName(),
1487  ] );
1488  $this->fillRequests( $res->neededRequests, self::ACTION_CREATE, null );
1489  $state['secondary'][$id] = false;
1490  $state['continueRequests'] = $res->neededRequests;
1491  $session->setSecret( 'AuthManager::accountCreationState', $state );
1492  return $res;
1494  throw new \DomainException(
1495  get_class( $provider ) . "::{$func}() returned $res->status." .
1496  ' Secondary providers are not allowed to fail account creation, that' .
1497  ' should have been done via testForAccountCreation().'
1498  );
1499  // @codeCoverageIgnoreStart
1500  default:
1501  throw new \DomainException(
1502  get_class( $provider ) . "::{$func}() returned $res->status"
1503  );
1504  // @codeCoverageIgnoreEnd
1505  }
1506  }
1507 
1508  $id = $user->getId();
1509  $name = $user->getName();
1512  $ret->loginRequest = $req;
1513  $this->createdAccountAuthenticationRequests[] = $req;
1514 
1515  $this->logger->info( __METHOD__ . ': Account creation succeeded for {user}', [
1516  'user' => $user->getName(),
1517  'creator' => $creator->getName(),
1518  ] );
1519 
1520  $this->callMethodOnProviders( 7, 'postAccountCreation', [ $user, $creator, $ret ] );
1521  $session->remove( 'AuthManager::accountCreationState' );
1522  $this->removeAuthenticationSessionData( null );
1523  return $ret;
1524  } catch ( \Exception $ex ) {
1525  $session->remove( 'AuthManager::accountCreationState' );
1526  throw $ex;
1527  }
1528  }
1529 
1545  public function autoCreateUser( User $user, $source, $login = true ) {
1546  if ( $source !== self::AUTOCREATE_SOURCE_SESSION &&
1548  ) {
1549  throw new \InvalidArgumentException( "Unknown auto-creation source: $source" );
1550  }
1551 
1552  $username = $user->getName();
1553 
1554  // Try the local user from the replica DB
1555  $localId = User::idFromName( $username );
1556  $flags = User::READ_NORMAL;
1557 
1558  // Fetch the user ID from the master, so that we don't try to create the user
1559  // when they already exist, due to replication lag
1560  // @codeCoverageIgnoreStart
1561  if (
1562  !$localId &&
1563  MediaWikiServices::getInstance()->getDBLoadBalancer()->getReaderIndex() != 0
1564  ) {
1565  $localId = User::idFromName( $username, User::READ_LATEST );
1566  $flags = User::READ_LATEST;
1567  }
1568  // @codeCoverageIgnoreEnd
1569 
1570  if ( $localId ) {
1571  $this->logger->debug( __METHOD__ . ': {username} already exists locally', [
1572  'username' => $username,
1573  ] );
1574  $user->setId( $localId );
1575  $user->loadFromId( $flags );
1576  if ( $login ) {
1577  $this->setSessionDataForUser( $user );
1578  }
1580  $status->warning( 'userexists' );
1581  return $status;
1582  }
1583 
1584  // Wiki is read-only?
1585  if ( wfReadOnly() ) {
1586  $this->logger->debug( __METHOD__ . ': denied by wfReadOnly(): {reason}', [
1587  'username' => $username,
1588  'reason' => wfReadOnlyReason(),
1589  ] );
1590  $user->setId( 0 );
1591  $user->loadFromId();
1592  return Status::newFatal( wfMessage( 'readonlytext', wfReadOnlyReason() ) );
1593  }
1594 
1595  // Check the session, if we tried to create this user already there's
1596  // no point in retrying.
1597  $session = $this->request->getSession();
1598  if ( $session->get( 'AuthManager::AutoCreateBlacklist' ) ) {
1599  $this->logger->debug( __METHOD__ . ': blacklisted in session {sessionid}', [
1600  'username' => $username,
1601  'sessionid' => $session->getId(),
1602  ] );
1603  $user->setId( 0 );
1604  $user->loadFromId();
1605  $reason = $session->get( 'AuthManager::AutoCreateBlacklist' );
1606  if ( $reason instanceof StatusValue ) {
1607  return Status::wrap( $reason );
1608  } else {
1609  return Status::newFatal( $reason );
1610  }
1611  }
1612 
1613  // Is the username creatable?
1614  if ( !User::isCreatableName( $username ) ) {
1615  $this->logger->debug( __METHOD__ . ': name "{username}" is not creatable', [
1616  'username' => $username,
1617  ] );
1618  $session->set( 'AuthManager::AutoCreateBlacklist', 'noname' );
1619  $user->setId( 0 );
1620  $user->loadFromId();
1621  return Status::newFatal( 'noname' );
1622  }
1623 
1624  // Is the IP user able to create accounts?
1625  $anon = new User;
1626  if ( !$anon->isAllowedAny( 'createaccount', 'autocreateaccount' ) ) {
1627  $this->logger->debug( __METHOD__ . ': IP lacks the ability to create or autocreate accounts', [
1628  'username' => $username,
1629  'ip' => $anon->getName(),
1630  ] );
1631  $session->set( 'AuthManager::AutoCreateBlacklist', 'authmanager-autocreate-noperm' );
1632  $session->persist();
1633  $user->setId( 0 );
1634  $user->loadFromId();
1635  return Status::newFatal( 'authmanager-autocreate-noperm' );
1636  }
1637 
1638  // Avoid account creation races on double submissions
1640  $lock = $cache->getScopedLock( $cache->makeGlobalKey( 'account', md5( $username ) ) );
1641  if ( !$lock ) {
1642  $this->logger->debug( __METHOD__ . ': Could not acquire account creation lock', [
1643  'user' => $username,
1644  ] );
1645  $user->setId( 0 );
1646  $user->loadFromId();
1647  return Status::newFatal( 'usernameinprogress' );
1648  }
1649 
1650  // Denied by providers?
1651  $options = [
1652  'flags' => User::READ_LATEST,
1653  'creating' => true,
1654  ];
1655  $providers = $this->getPreAuthenticationProviders() +
1658  foreach ( $providers as $provider ) {
1659  $status = $provider->testUserForCreation( $user, $source, $options );
1660  if ( !$status->isGood() ) {
1661  $ret = Status::wrap( $status );
1662  $this->logger->debug( __METHOD__ . ': Provider denied creation of {username}: {reason}', [
1663  'username' => $username,
1664  'reason' => $ret->getWikiText( null, null, 'en' ),
1665  ] );
1666  $session->set( 'AuthManager::AutoCreateBlacklist', $status );
1667  $user->setId( 0 );
1668  $user->loadFromId();
1669  return $ret;
1670  }
1671  }
1672 
1673  $backoffKey = $cache->makeKey( 'AuthManager', 'autocreate-failed', md5( $username ) );
1674  if ( $cache->get( $backoffKey ) ) {
1675  $this->logger->debug( __METHOD__ . ': {username} denied by prior creation attempt failures', [
1676  'username' => $username,
1677  ] );
1678  $user->setId( 0 );
1679  $user->loadFromId();
1680  return Status::newFatal( 'authmanager-autocreate-exception' );
1681  }
1682 
1683  // Checks passed, create the user...
1684  $from = isset( $_SERVER['REQUEST_URI'] ) ? $_SERVER['REQUEST_URI'] : 'CLI';
1685  $this->logger->info( __METHOD__ . ': creating new user ({username}) - from: {from}', [
1686  'username' => $username,
1687  'from' => $from,
1688  ] );
1689 
1690  // Ignore warnings about master connections/writes...hard to avoid here
1691  $trxProfiler = \Profiler::instance()->getTransactionProfiler();
1692  $old = $trxProfiler->setSilenced( true );
1693  try {
1694  $status = $user->addToDatabase();
1695  if ( !$status->isOK() ) {
1696  // Double-check for a race condition (T70012). We make use of the fact that when
1697  // addToDatabase fails due to the user already existing, the user object gets loaded.
1698  if ( $user->getId() ) {
1699  $this->logger->info( __METHOD__ . ': {username} already exists locally (race)', [
1700  'username' => $username,
1701  ] );
1702  if ( $login ) {
1703  $this->setSessionDataForUser( $user );
1704  }
1706  $status->warning( 'userexists' );
1707  } else {
1708  $this->logger->error( __METHOD__ . ': {username} failed with message {msg}', [
1709  'username' => $username,
1710  'msg' => $status->getWikiText( null, null, 'en' )
1711  ] );
1712  $user->setId( 0 );
1713  $user->loadFromId();
1714  }
1715  return $status;
1716  }
1717  } catch ( \Exception $ex ) {
1718  $trxProfiler->setSilenced( $old );
1719  $this->logger->error( __METHOD__ . ': {username} failed with exception {exception}', [
1720  'username' => $username,
1721  'exception' => $ex,
1722  ] );
1723  // Do not keep throwing errors for a while
1724  $cache->set( $backoffKey, 1, 600 );
1725  // Bubble up error; which should normally trigger DB rollbacks
1726  throw $ex;
1727  }
1728 
1729  $this->setDefaultUserOptions( $user, false );
1730 
1731  // Inform the providers
1732  $this->callMethodOnProviders( 6, 'autoCreatedAccount', [ $user, $source ] );
1733 
1734  \Hooks::run( 'AuthPluginAutoCreate', [ $user ], '1.27' );
1735  \Hooks::run( 'LocalUserCreated', [ $user, true ] );
1736  $user->saveSettings();
1737 
1738  // Update user count
1739  \DeferredUpdates::addUpdate( \SiteStatsUpdate::factory( [ 'users' => 1 ] ) );
1740  // Watch user's userpage and talk page
1741  \DeferredUpdates::addCallableUpdate( function () use ( $user ) {
1742  $user->addWatch( $user->getUserPage(), User::IGNORE_USER_RIGHTS );
1743  } );
1744 
1745  // Log the creation
1746  if ( $this->config->get( 'NewUserLog' ) ) {
1747  $logEntry = new \ManualLogEntry( 'newusers', 'autocreate' );
1748  $logEntry->setPerformer( $user );
1749  $logEntry->setTarget( $user->getUserPage() );
1750  $logEntry->setComment( '' );
1751  $logEntry->setParameters( [
1752  '4::userid' => $user->getId(),
1753  ] );
1754  $logEntry->insert();
1755  }
1756 
1757  $trxProfiler->setSilenced( $old );
1758 
1759  if ( $login ) {
1760  $this->setSessionDataForUser( $user );
1761  }
1762 
1763  return Status::newGood();
1764  }
1765 
1777  public function canLinkAccounts() {
1778  foreach ( $this->getPrimaryAuthenticationProviders() as $provider ) {
1779  if ( $provider->accountCreationType() === PrimaryAuthenticationProvider::TYPE_LINK ) {
1780  return true;
1781  }
1782  }
1783  return false;
1784  }
1785 
1795  public function beginAccountLink( User $user, array $reqs, $returnToUrl ) {
1796  $session = $this->request->getSession();
1797  $session->remove( 'AuthManager::accountLinkState' );
1798 
1799  if ( !$this->canLinkAccounts() ) {
1800  // Caller should have called canLinkAccounts()
1801  throw new \LogicException( 'Account linking is not possible' );
1802  }
1803 
1804  if ( $user->getId() === 0 ) {
1805  if ( !User::isUsableName( $user->getName() ) ) {
1806  $msg = wfMessage( 'noname' );
1807  } else {
1808  $msg = wfMessage( 'authmanager-userdoesnotexist', $user->getName() );
1809  }
1810  return AuthenticationResponse::newFail( $msg );
1811  }
1812  foreach ( $reqs as $req ) {
1813  $req->username = $user->getName();
1814  $req->returnToUrl = $returnToUrl;
1815  }
1816 
1817  $this->removeAuthenticationSessionData( null );
1818 
1819  $providers = $this->getPreAuthenticationProviders();
1820  foreach ( $providers as $id => $provider ) {
1821  $status = $provider->testForAccountLink( $user );
1822  if ( !$status->isGood() ) {
1823  $this->logger->debug( __METHOD__ . ": Account linking pre-check failed by $id", [
1824  'user' => $user->getName(),
1825  ] );
1827  Status::wrap( $status )->getMessage()
1828  );
1829  $this->callMethodOnProviders( 3, 'postAccountLink', [ $user, $ret ] );
1830  return $ret;
1831  }
1832  }
1833 
1834  $state = [
1835  'username' => $user->getName(),
1836  'userid' => $user->getId(),
1837  'returnToUrl' => $returnToUrl,
1838  'primary' => null,
1839  'continueRequests' => [],
1840  ];
1841 
1842  $providers = $this->getPrimaryAuthenticationProviders();
1843  foreach ( $providers as $id => $provider ) {
1844  if ( $provider->accountCreationType() !== PrimaryAuthenticationProvider::TYPE_LINK ) {
1845  continue;
1846  }
1847 
1848  $res = $provider->beginPrimaryAccountLink( $user, $reqs );
1849  switch ( $res->status ) {
1851  $this->logger->info( "Account linked to {user} by $id", [
1852  'user' => $user->getName(),
1853  ] );
1854  $this->callMethodOnProviders( 3, 'postAccountLink', [ $user, $res ] );
1855  return $res;
1856 
1858  $this->logger->debug( __METHOD__ . ": Account linking failed by $id", [
1859  'user' => $user->getName(),
1860  ] );
1861  $this->callMethodOnProviders( 3, 'postAccountLink', [ $user, $res ] );
1862  return $res;
1863 
1865  // Continue loop
1866  break;
1867 
1870  $this->logger->debug( __METHOD__ . ": Account linking $res->status by $id", [
1871  'user' => $user->getName(),
1872  ] );
1873  $this->fillRequests( $res->neededRequests, self::ACTION_LINK, $user->getName() );
1874  $state['primary'] = $id;
1875  $state['continueRequests'] = $res->neededRequests;
1876  $session->setSecret( 'AuthManager::accountLinkState', $state );
1877  $session->persist();
1878  return $res;
1879 
1880  // @codeCoverageIgnoreStart
1881  default:
1882  throw new \DomainException(
1883  get_class( $provider ) . "::beginPrimaryAccountLink() returned $res->status"
1884  );
1885  // @codeCoverageIgnoreEnd
1886  }
1887  }
1888 
1889  $this->logger->debug( __METHOD__ . ': Account linking failed because no provider accepted', [
1890  'user' => $user->getName(),
1891  ] );
1893  wfMessage( 'authmanager-link-no-primary' )
1894  );
1895  $this->callMethodOnProviders( 3, 'postAccountLink', [ $user, $ret ] );
1896  return $ret;
1897  }
1898 
1904  public function continueAccountLink( array $reqs ) {
1905  $session = $this->request->getSession();
1906  try {
1907  if ( !$this->canLinkAccounts() ) {
1908  // Caller should have called canLinkAccounts()
1909  $session->remove( 'AuthManager::accountLinkState' );
1910  throw new \LogicException( 'Account linking is not possible' );
1911  }
1912 
1913  $state = $session->getSecret( 'AuthManager::accountLinkState' );
1914  if ( !is_array( $state ) ) {
1916  wfMessage( 'authmanager-link-not-in-progress' )
1917  );
1918  }
1919  $state['continueRequests'] = [];
1920 
1921  // Step 0: Prepare and validate the input
1922 
1923  $user = User::newFromName( $state['username'], 'usable' );
1924  if ( !is_object( $user ) ) {
1925  $session->remove( 'AuthManager::accountLinkState' );
1926  return AuthenticationResponse::newFail( wfMessage( 'noname' ) );
1927  }
1928  if ( $user->getId() != $state['userid'] ) {
1929  throw new \UnexpectedValueException(
1930  "User \"{$state['username']}\" is valid, but " .
1931  "ID {$user->getId()} != {$state['userid']}!"
1932  );
1933  }
1934 
1935  foreach ( $reqs as $req ) {
1936  $req->username = $state['username'];
1937  $req->returnToUrl = $state['returnToUrl'];
1938  }
1939 
1940  // Step 1: Call the primary again until it succeeds
1941 
1942  $provider = $this->getAuthenticationProvider( $state['primary'] );
1943  if ( !$provider instanceof PrimaryAuthenticationProvider ) {
1944  // Configuration changed? Force them to start over.
1945  // @codeCoverageIgnoreStart
1947  wfMessage( 'authmanager-link-not-in-progress' )
1948  );
1949  $this->callMethodOnProviders( 3, 'postAccountLink', [ $user, $ret ] );
1950  $session->remove( 'AuthManager::accountLinkState' );
1951  return $ret;
1952  // @codeCoverageIgnoreEnd
1953  }
1954  $id = $provider->getUniqueId();
1955  $res = $provider->continuePrimaryAccountLink( $user, $reqs );
1956  switch ( $res->status ) {
1958  $this->logger->info( "Account linked to {user} by $id", [
1959  'user' => $user->getName(),
1960  ] );
1961  $this->callMethodOnProviders( 3, 'postAccountLink', [ $user, $res ] );
1962  $session->remove( 'AuthManager::accountLinkState' );
1963  return $res;
1965  $this->logger->debug( __METHOD__ . ": Account linking failed by $id", [
1966  'user' => $user->getName(),
1967  ] );
1968  $this->callMethodOnProviders( 3, 'postAccountLink', [ $user, $res ] );
1969  $session->remove( 'AuthManager::accountLinkState' );
1970  return $res;
1973  $this->logger->debug( __METHOD__ . ": Account linking $res->status by $id", [
1974  'user' => $user->getName(),
1975  ] );
1976  $this->fillRequests( $res->neededRequests, self::ACTION_LINK, $user->getName() );
1977  $state['continueRequests'] = $res->neededRequests;
1978  $session->setSecret( 'AuthManager::accountLinkState', $state );
1979  return $res;
1980  default:
1981  throw new \DomainException(
1982  get_class( $provider ) . "::continuePrimaryAccountLink() returned $res->status"
1983  );
1984  }
1985  } catch ( \Exception $ex ) {
1986  $session->remove( 'AuthManager::accountLinkState' );
1987  throw $ex;
1988  }
1989  }
1990 
2016  public function getAuthenticationRequests( $action, User $user = null ) {
2017  $options = [];
2018  $providerAction = $action;
2019 
2020  // Figure out which providers to query
2021  switch ( $action ) {
2022  case self::ACTION_LOGIN:
2023  case self::ACTION_CREATE:
2024  $providers = $this->getPreAuthenticationProviders() +
2027  break;
2028 
2030  $state = $this->request->getSession()->getSecret( 'AuthManager::authnState' );
2031  return is_array( $state ) ? $state['continueRequests'] : [];
2032 
2034  $state = $this->request->getSession()->getSecret( 'AuthManager::accountCreationState' );
2035  return is_array( $state ) ? $state['continueRequests'] : [];
2036 
2037  case self::ACTION_LINK:
2038  $providers = array_filter( $this->getPrimaryAuthenticationProviders(), function ( $p ) {
2039  return $p->accountCreationType() === PrimaryAuthenticationProvider::TYPE_LINK;
2040  } );
2041  break;
2042 
2043  case self::ACTION_UNLINK:
2044  $providers = array_filter( $this->getPrimaryAuthenticationProviders(), function ( $p ) {
2045  return $p->accountCreationType() === PrimaryAuthenticationProvider::TYPE_LINK;
2046  } );
2047 
2048  // To providers, unlink and remove are identical.
2049  $providerAction = self::ACTION_REMOVE;
2050  break;
2051 
2053  $state = $this->request->getSession()->getSecret( 'AuthManager::accountLinkState' );
2054  return is_array( $state ) ? $state['continueRequests'] : [];
2055 
2056  case self::ACTION_CHANGE:
2057  case self::ACTION_REMOVE:
2058  $providers = $this->getPrimaryAuthenticationProviders() +
2060  break;
2061 
2062  // @codeCoverageIgnoreStart
2063  default:
2064  throw new \DomainException( __METHOD__ . ": Invalid action \"$action\"" );
2065  }
2066  // @codeCoverageIgnoreEnd
2067 
2068  return $this->getAuthenticationRequestsInternal( $providerAction, $options, $providers, $user );
2069  }
2070 
2081  $providerAction, array $options, array $providers, User $user = null
2082  ) {
2083  $user = $user ?: \RequestContext::getMain()->getUser();
2084  $options['username'] = $user->isAnon() ? null : $user->getName();
2085 
2086  // Query them and merge results
2087  $reqs = [];
2088  foreach ( $providers as $provider ) {
2089  $isPrimary = $provider instanceof PrimaryAuthenticationProvider;
2090  foreach ( $provider->getAuthenticationRequests( $providerAction, $options ) as $req ) {
2091  $id = $req->getUniqueId();
2092 
2093  // If a required request if from a Primary, mark it as "primary-required" instead
2094  if ( $isPrimary ) {
2095  if ( $req->required ) {
2097  }
2098  }
2099 
2100  if (
2101  !isset( $reqs[$id] )
2102  || $req->required === AuthenticationRequest::REQUIRED
2103  || $reqs[$id] === AuthenticationRequest::OPTIONAL
2104  ) {
2105  $reqs[$id] = $req;
2106  }
2107  }
2108  }
2109 
2110  // AuthManager has its own req for some actions
2111  switch ( $providerAction ) {
2112  case self::ACTION_LOGIN:
2113  $reqs[] = new RememberMeAuthenticationRequest;
2114  break;
2115 
2116  case self::ACTION_CREATE:
2117  $reqs[] = new UsernameAuthenticationRequest;
2118  $reqs[] = new UserDataAuthenticationRequest;
2119  if ( $options['username'] !== null ) {
2121  $options['username'] = null; // Don't fill in the username below
2122  }
2123  break;
2124  }
2125 
2126  // Fill in reqs data
2127  $this->fillRequests( $reqs, $providerAction, $options['username'], true );
2128 
2129  // For self::ACTION_CHANGE, filter out any that something else *doesn't* allow changing
2130  if ( $providerAction === self::ACTION_CHANGE || $providerAction === self::ACTION_REMOVE ) {
2131  $reqs = array_filter( $reqs, function ( $req ) {
2132  return $this->allowsAuthenticationDataChange( $req, false )->isGood();
2133  } );
2134  }
2135 
2136  return array_values( $reqs );
2137  }
2138 
2146  private function fillRequests( array &$reqs, $action, $username, $forceAction = false ) {
2147  foreach ( $reqs as $req ) {
2148  if ( !$req->action || $forceAction ) {
2149  $req->action = $action;
2150  }
2151  if ( $req->username === null ) {
2152  $req->username = $username;
2153  }
2154  }
2155  }
2156 
2163  public function userExists( $username, $flags = User::READ_NORMAL ) {
2164  foreach ( $this->getPrimaryAuthenticationProviders() as $provider ) {
2165  if ( $provider->testUserExists( $username, $flags ) ) {
2166  return true;
2167  }
2168  }
2169 
2170  return false;
2171  }
2172 
2184  public function allowsPropertyChange( $property ) {
2185  $providers = $this->getPrimaryAuthenticationProviders() +
2187  foreach ( $providers as $provider ) {
2188  if ( !$provider->providerAllowsPropertyChange( $property ) ) {
2189  return false;
2190  }
2191  }
2192  return true;
2193  }
2194 
2203  public function getAuthenticationProvider( $id ) {
2204  // Fast version
2205  if ( isset( $this->allAuthenticationProviders[$id] ) ) {
2206  return $this->allAuthenticationProviders[$id];
2207  }
2208 
2209  // Slow version: instantiate each kind and check
2210  $providers = $this->getPrimaryAuthenticationProviders();
2211  if ( isset( $providers[$id] ) ) {
2212  return $providers[$id];
2213  }
2214  $providers = $this->getSecondaryAuthenticationProviders();
2215  if ( isset( $providers[$id] ) ) {
2216  return $providers[$id];
2217  }
2218  $providers = $this->getPreAuthenticationProviders();
2219  if ( isset( $providers[$id] ) ) {
2220  return $providers[$id];
2221  }
2222 
2223  return null;
2224  }
2225 
2239  public function setAuthenticationSessionData( $key, $data ) {
2240  $session = $this->request->getSession();
2241  $arr = $session->getSecret( 'authData' );
2242  if ( !is_array( $arr ) ) {
2243  $arr = [];
2244  }
2245  $arr[$key] = $data;
2246  $session->setSecret( 'authData', $arr );
2247  }
2248 
2256  public function getAuthenticationSessionData( $key, $default = null ) {
2257  $arr = $this->request->getSession()->getSecret( 'authData' );
2258  if ( is_array( $arr ) && array_key_exists( $key, $arr ) ) {
2259  return $arr[$key];
2260  } else {
2261  return $default;
2262  }
2263  }
2264 
2270  public function removeAuthenticationSessionData( $key ) {
2271  $session = $this->request->getSession();
2272  if ( $key === null ) {
2273  $session->remove( 'authData' );
2274  } else {
2275  $arr = $session->getSecret( 'authData' );
2276  if ( is_array( $arr ) && array_key_exists( $key, $arr ) ) {
2277  unset( $arr[$key] );
2278  $session->setSecret( 'authData', $arr );
2279  }
2280  }
2281  }
2282 
2289  protected function providerArrayFromSpecs( $class, array $specs ) {
2290  $i = 0;
2291  foreach ( $specs as &$spec ) {
2292  $spec = [ 'sort2' => $i++ ] + $spec + [ 'sort' => 0 ];
2293  }
2294  unset( $spec );
2295  usort( $specs, function ( $a, $b ) {
2296  return ( (int)$a['sort'] ) - ( (int)$b['sort'] )
2297  ?: $a['sort2'] - $b['sort2'];
2298  } );
2299 
2300  $ret = [];
2301  foreach ( $specs as $spec ) {
2302  $provider = ObjectFactory::getObjectFromSpec( $spec );
2303  if ( !$provider instanceof $class ) {
2304  throw new \RuntimeException(
2305  "Expected instance of $class, got " . get_class( $provider )
2306  );
2307  }
2308  $provider->setLogger( $this->logger );
2309  $provider->setManager( $this );
2310  $provider->setConfig( $this->config );
2311  $id = $provider->getUniqueId();
2312  if ( isset( $this->allAuthenticationProviders[$id] ) ) {
2313  throw new \RuntimeException(
2314  "Duplicate specifications for id $id (classes " .
2315  get_class( $provider ) . ' and ' .
2316  get_class( $this->allAuthenticationProviders[$id] ) . ')'
2317  );
2318  }
2319  $this->allAuthenticationProviders[$id] = $provider;
2320  $ret[$id] = $provider;
2321  }
2322  return $ret;
2323  }
2324 
2329  private function getConfiguration() {
2330  return $this->config->get( 'AuthManagerConfig' ) ?: $this->config->get( 'AuthManagerAutoConfig' );
2331  }
2332 
2337  protected function getPreAuthenticationProviders() {
2338  if ( $this->preAuthenticationProviders === null ) {
2339  $conf = $this->getConfiguration();
2340  $this->preAuthenticationProviders = $this->providerArrayFromSpecs(
2341  PreAuthenticationProvider::class, $conf['preauth']
2342  );
2343  }
2345  }
2346 
2351  protected function getPrimaryAuthenticationProviders() {
2352  if ( $this->primaryAuthenticationProviders === null ) {
2353  $conf = $this->getConfiguration();
2354  $this->primaryAuthenticationProviders = $this->providerArrayFromSpecs(
2355  PrimaryAuthenticationProvider::class, $conf['primaryauth']
2356  );
2357  }
2359  }
2360 
2366  if ( $this->secondaryAuthenticationProviders === null ) {
2367  $conf = $this->getConfiguration();
2368  $this->secondaryAuthenticationProviders = $this->providerArrayFromSpecs(
2369  SecondaryAuthenticationProvider::class, $conf['secondaryauth']
2370  );
2371  }
2373  }
2374 
2380  private function setSessionDataForUser( $user, $remember = null ) {
2381  $session = $this->request->getSession();
2382  $delay = $session->delaySave();
2383 
2384  $session->resetId();
2385  $session->resetAllTokens();
2386  if ( $session->canSetUser() ) {
2387  $session->setUser( $user );
2388  }
2389  if ( $remember !== null ) {
2390  $session->setRememberUser( $remember );
2391  }
2392  $session->set( 'AuthManager:lastAuthId', $user->getId() );
2393  $session->set( 'AuthManager:lastAuthTimestamp', time() );
2394  $session->persist();
2395 
2396  \Wikimedia\ScopedCallback::consume( $delay );
2397 
2398  \Hooks::run( 'UserLoggedIn', [ $user ] );
2399  }
2400 
2405  private function setDefaultUserOptions( User $user, $useContextLang ) {
2407 
2408  $user->setToken();
2409 
2410  $lang = $useContextLang ? \RequestContext::getMain()->getLanguage() : $wgContLang;
2411  $user->setOption( 'language', $lang->getPreferredVariant() );
2412 
2413  if ( $wgContLang->hasVariants() ) {
2414  $user->setOption( 'variant', $wgContLang->getPreferredVariant() );
2415  }
2416  }
2417 
2423  private function callMethodOnProviders( $which, $method, array $args ) {
2424  $providers = [];
2425  if ( $which & 1 ) {
2426  $providers += $this->getPreAuthenticationProviders();
2427  }
2428  if ( $which & 2 ) {
2429  $providers += $this->getPrimaryAuthenticationProviders();
2430  }
2431  if ( $which & 4 ) {
2432  $providers += $this->getSecondaryAuthenticationProviders();
2433  }
2434  foreach ( $providers as $provider ) {
2435  call_user_func_array( [ $provider, $method ], $args );
2436  }
2437  }
2438 
2443  public static function resetCache() {
2444  if ( !defined( 'MW_PHPUNIT_TEST' ) ) {
2445  // @codeCoverageIgnoreStart
2446  throw new \MWException( __METHOD__ . ' may only be called from unit tests!' );
2447  // @codeCoverageIgnoreEnd
2448  }
2449 
2450  self::$instance = null;
2451  }
2452 
2455 }
2456 
$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:247
MediaWiki\Auth\AuthManager\continueAccountLink
continueAccountLink(array $reqs)
Continue an account linking flow.
Definition: AuthManager.php:1904
MediaWiki\Auth\AuthManager\continueAuthentication
continueAuthentication(array $reqs)
Continue an authentication flow.
Definition: AuthManager.php:408
MediaWiki\Auth\AuthenticationRequest\OPTIONAL
const OPTIONAL
Indicates that the request is not required for authentication to proceed.
Definition: AuthenticationRequest.php:40
MediaWiki\Auth\AuthManager\getRequest
getRequest()
Definition: AuthManager.php:177
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:132
User\newFromId
static newFromId( $id)
Static factory method for creation from a given user ID.
Definition: User.php:614
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:48
MediaWiki\Auth\AuthManager\SEC_REAUTH
const SEC_REAUTH
Security-sensitive operations should re-authenticate.
Definition: AuthManager.php:109
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:367
use
Apache License January AND DISTRIBUTION Definitions License shall mean the terms and conditions for use
Definition: APACHE-LICENSE-2.0.txt:10
User\getId
getId()
Get the user's ID.
Definition: User.php:2457
MediaWiki\Auth\AuthManager\fillRequests
fillRequests(array &$reqs, $action, $username, $forceAction=false)
Set values in an array of requests.
Definition: AuthManager.php:2146
MediaWiki\Auth\AuthManager\changeAuthenticationData
changeAuthenticationData(AuthenticationRequest $req, $isAddition=false)
Change authentication data (e.g.
Definition: AuthManager.php:889
MediaWiki\Auth\AuthManager\getPrimaryAuthenticationProviders
getPrimaryAuthenticationProviders()
Get the list of PrimaryAuthenticationProviders.
Definition: AuthManager.php:2351
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
Profiler\instance
static instance()
Singleton.
Definition: Profiler.php:62
array
the array() calling protocol came about after MediaWiki 1.4rc1.
MediaWiki\Auth\AuthManager\ACTION_UNLINK
const ACTION_UNLINK
Like ACTION_REMOVE but for linking providers only.
Definition: AuthManager.php:104
$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:114
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:837
$last
$last
Definition: profileinfo.php:408
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:31
$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:2005
MediaWiki\Auth\AuthManager\autoCreateUser
autoCreateUser(User $user, $source, $login=true)
Auto-create an account, and log into that account.
Definition: AuthManager.php:1545
MediaWiki\Auth\AuthManager\$instance
static AuthManager null $instance
Definition: AuthManager.php:117
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:284
wfReadOnly
wfReadOnly()
Check whether the wiki is in read-only mode.
Definition: GlobalFunctions.php:1262
User\newFromName
static newFromName( $name, $validate='valid')
Static factory method for creation from username.
Definition: User.php:591
MediaWiki\Auth\AuthManager\userExists
userExists( $username, $flags=User::READ_NORMAL)
Determine whether a username exists.
Definition: AuthManager.php:2163
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:129
User
User
Definition: All_system_messages.txt:425
MediaWiki\Auth\AuthManager\getPreAuthenticationProviders
getPreAuthenticationProviders()
Get the list of PreAuthenticationProviders.
Definition: AuthManager.php:2337
MediaWiki\Auth\AuthManager\getAuthenticationSessionData
getAuthenticationSessionData( $key, $default=null)
Fetch authentication data from the current session.
Definition: AuthManager.php:2256
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:88
MediaWiki\Auth\AuthManager\__construct
__construct(WebRequest $request, Config $config)
Definition: AuthManager.php:161
MediaWiki\Auth\AuthManager\SEC_FAIL
const SEC_FAIL
Security-sensitive should not be performed.
Definition: AuthManager.php:111
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:37
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:2270
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
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:1044
MediaWiki\Auth\AuthManager\getAuthenticationProvider
getAuthenticationProvider( $id)
Get a provider by ID.
Definition: AuthManager.php:2203
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:98
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:915
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\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:2329
MediaWiki\Auth\AuthenticationResponse\ABSTAIN
const ABSTAIN
Indicates that the authentication provider does not handle this request.
Definition: AuthenticationResponse.php:52
MediaWiki\Auth\AuthManager\canAuthenticateNow
canAuthenticateNow()
Indicate whether user authentication is possible.
Definition: AuthManager.php:262
MediaWiki\Auth\AuthManager\setAuthenticationSessionData
setAuthenticationSessionData( $key, $data)
Store authentication in the current session.
Definition: AuthManager.php:2239
SiteStatsUpdate\factory
static factory(array $deltas)
Definition: SiteStatsUpdate.php:66
global
when a variable name is used in a it is silently declared as a new masking the global
Definition: design.txt:95
MediaWiki\Auth\SecondaryAuthenticationProvider
A secondary provider mostly acts when the submitted authentication data has already been associated t...
Definition: SecondaryAuthenticationProvider.php:52
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:2405
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:239
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:2443
MediaWiki\Auth\AuthManager\$request
WebRequest $request
Definition: AuthManager.php:120
MediaWiki\Auth\AuthManager\beginAccountLink
beginAccountLink(User $user, array $reqs, $returnToUrl)
Start an account linking flow.
Definition: AuthManager.php:1795
$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:2001
MediaWiki\Auth\AuthManager\normalizeUsername
normalizeUsername( $username)
Provide normalized versions of the username for security checks.
Definition: AuthManager.php:812
MediaWiki\Auth\AuthManager\canLinkAccounts
canLinkAccounts()
Determine whether accounts can be linked.
Definition: AuthManager.php:1777
MediaWiki\MediaWikiServices\getInstance
static getInstance()
Returns the global default instance of the top level service locator.
Definition: MediaWikiServices.php:109
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:93
MediaWiki\Auth\AuthManager\ACTION_CREATE
const ACTION_CREATE
Create a new user.
Definition: AuthManager.php:90
MediaWiki\Auth\AuthManager\$logger
LoggerInterface $logger
Definition: AuthManager.php:126
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:2224
MediaWiki\Auth\AuthManager\$primaryAuthenticationProviders
PrimaryAuthenticationProvider[] $primaryAuthenticationProviders
Definition: AuthManager.php:135
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:1932
MediaWiki\Auth\AuthManager\continueAccountCreation
continueAccountCreation(array $reqs)
Continue an account creation flow.
Definition: AuthManager.php:1145
BotPassword\invalidateAllPasswordsForUser
static invalidateAllPasswordsForUser( $username)
Invalidate all passwords for a user, by name.
Definition: BotPassword.php:380
MediaWiki\Auth\AuthManager\$secondaryAuthenticationProviders
SecondaryAuthenticationProvider[] $secondaryAuthenticationProviders
Definition: AuthManager.php:138
MediaWiki\Auth\AuthManager\forcePrimaryAuthenticationProviders
forcePrimaryAuthenticationProviders(array $providers, $why)
Force certain PrimaryAuthenticationProviders.
Definition: AuthManager.php:187
MediaWiki\Auth\AuthManager\ACTION_CHANGE
const ACTION_CHANGE
Change a user's credentials.
Definition: AuthManager.php:100
MediaWiki\Auth\AuthManager\callMethodOnProviders
callMethodOnProviders( $which, $method, array $args)
Definition: AuthManager.php:2423
MediaWiki\Auth\AuthManager\ACTION_LINK
const ACTION_LINK
Link an existing user to a third-party account.
Definition: AuthManager.php:95
MediaWiki\Auth\AuthManager\allowsPropertyChange
allowsPropertyChange( $property)
Determine whether a user property should be allowed to be changed.
Definition: AuthManager.php:2184
$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). 'DeleteUnknownPreferences':Called by the cleanupPreferences.php maintenance script to build a WHERE clause with which to delete preferences that are not known about. This hook is used by extensions that have dynamically-named preferences that should not be deleted in the usual cleanup process. For example, the Gadgets extension creates preferences prefixed with 'gadget-', and so anything with that prefix is excluded from the deletion. &where:An array that will be passed as the $cond parameter to IDatabase::select() to determine what will be deleted from the user_properties table. $db:The IDatabase object, useful for accessing $db->buildLike() etc. '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:1255
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()
Get the RequestContext object associated with the main request.
Definition: RequestContext.php:434
MediaWiki\Auth\AuthManager\securitySensitiveOperationStatus
securitySensitiveOperationStatus( $operation)
Whether security-sensitive operations should proceed.
Definition: AuthManager.php:716
MediaWiki\Auth\AuthManager
This serves as the entry point to the authentication system.
Definition: AuthManager.php:83
WebRequest
The WebRequest class encapsulates getting at data passed in the URL or via a POSTed form stripping il...
Definition: WebRequest.php:38
$req
this hook is for auditing only $req
Definition: hooks.txt:990
MediaWiki\Auth\AuthManager\ACTION_REMOVE
const ACTION_REMOVE
Remove a user's credentials.
Definition: AuthManager.php:102
MediaWiki\Auth\AuthManager\SEC_OK
const SEC_OK
Security-sensitive operations are ok.
Definition: AuthManager.php:107
$args
if( $line===false) $args
Definition: cdb.php:64
$name
Allows to change the fields on the form that will be generated $name
Definition: hooks.txt:302
MediaWiki\Auth\AuthManager\getAuthenticationRequests
getAuthenticationRequests( $action, User $user=null)
Return the applicable list of AuthenticationRequests.
Definition: AuthManager.php:2016
wfReadOnlyReason
wfReadOnlyReason()
Check if the site is in read-only mode and return the message if so.
Definition: GlobalFunctions.php:1275
$cache
$cache
Definition: mcc.php:33
MediaWiki\Auth\AuthManager\singleton
static singleton()
Get the global AuthManager.
Definition: AuthManager.php:147
User\idFromName
static idFromName( $name, $flags=self::READ_NORMAL)
Get database id given a user name.
Definition: User.php:883
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:22
MediaWiki\Auth\AuthManager\setSessionDataForUser
setSessionDataForUser( $user, $remember=null)
Log the user in.
Definition: AuthManager.php:2380
MediaWiki\Auth\AuthManager\$createdAccountAuthenticationRequests
CreatedAccountAuthenticationRequest[] $createdAccountAuthenticationRequests
Definition: AuthManager.php:141
MediaWiki\Auth\AuthManager\$config
Config $config
Definition: AuthManager.php:123
MediaWiki\Auth\AuthManager\canCreateAccount
canCreateAccount( $username, $options=[])
Determine whether a particular account can be created.
Definition: AuthManager.php:934
$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:85
$username
this hook is for auditing only or null if authentication failed before getting that far $username
Definition: hooks.txt:785
MediaWiki\Auth\AuthManager\getSecondaryAuthenticationProviders
getSecondaryAuthenticationProviders()
Get the list of SecondaryAuthenticationProviders.
Definition: AuthManager.php:2365
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:56
$wgAuth
$wgAuth $wgAuth
Authentication plugin.
Definition: DefaultSettings.php:7417
MediaWiki\Auth\AuthManager\userCanAuthenticate
userCanAuthenticate( $username)
Determine whether a username can authenticate.
Definition: AuthManager.php:789
User\IGNORE_USER_RIGHTS
const IGNORE_USER_RIGHTS
Definition: User.php:90
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:25
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:1018
MediaWiki\Auth\AuthManager\allowsAuthenticationDataChange
allowsAuthenticationDataChange(AuthenticationRequest $req, $checkData=true)
Validate a change of authentication data (e.g.
Definition: AuthManager.php:853
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:4494
User
The User object encapsulates all of the user-specific settings (user_id, name, rights,...
Definition: User.php:53
MediaWiki\Auth\PrimaryAuthenticationProvider
A primary authentication provider is responsible for associating the submitted authentication data wi...
Definition: PrimaryAuthenticationProvider.php:75
DeferredUpdates\addCallableUpdate
static addCallableUpdate( $callable, $stage=self::POSTSEND, $dbw=null)
Add a callable update.
Definition: DeferredUpdates.php:111
Hooks\run
static run( $event, array $args=[], $deprecatedVersion=null)
Call hook functions defined in Hooks::register and $wgHooks.
Definition: Hooks.php:203
MediaWiki\Auth
Definition: AbstractAuthenticationProvider.php:22
MediaWiki\Auth\AuthManager\setLogger
setLogger(LoggerInterface $logger)
Definition: AuthManager.php:170
MediaWiki\Auth\AuthManager\providerArrayFromSpecs
providerArrayFromSpecs( $class, array $specs)
Create an array of AuthenticationProviders from an array of ObjectFactory specs.
Definition: AuthManager.php:2289
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:2482
User\isCreatableName
static isCreatableName( $name)
Usernames which fail to pass this function will be blocked from new account registrations,...
Definition: User.php:1093
MediaWiki\Auth\AuthManager\checkAccountCreatePermissions
checkAccountCreatePermissions(User $creator)
Basic permissions checks on whether a user can create accounts.
Definition: AuthManager.php:982
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
$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:57
MediaWiki\Auth\AuthManager\getAuthenticationRequestsInternal
getAuthenticationRequestsInternal( $providerAction, array $options, array $providers, User $user=null)
Internal request lookup for self::getAuthenticationRequests.
Definition: AuthManager.php:2080
MediaWiki\Auth\AuthenticationProvider
An AuthenticationProvider is used by AuthManager when authenticating users.
Definition: AuthenticationProvider.php:39