31use Psr\Log\LoggerAwareInterface;
32use Psr\Log\LoggerInterface;
33use Psr\Log\NullLogger;
38use Wikimedia\ObjectFactory;
219 $this->logger->warning(
"Overriding AuthManager primary authn because $why" );
221 if ( $this->primaryAuthenticationProviders !==
null ) {
222 $this->logger->warning(
223 'PrimaryAuthenticationProviders have already been accessed! I hope nothing breaks.'
226 $this->allAuthenticationProviders = array_diff_key(
227 $this->allAuthenticationProviders,
228 $this->primaryAuthenticationProviders
230 $session = $this->request->getSession();
231 $session->remove(
'AuthManager::authnState' );
232 $session->remove(
'AuthManager::accountCreationState' );
233 $session->remove(
'AuthManager::accountLinkState' );
234 $this->createdAccountAuthenticationRequests = [];
237 $this->primaryAuthenticationProviders = [];
238 foreach ( $providers as $provider ) {
240 throw new \RuntimeException(
241 'Expected instance of MediaWiki\\Auth\\PrimaryAuthenticationProvider, got ' .
242 get_class( $provider )
245 $provider->setLogger( $this->logger );
246 $provider->setManager( $this );
247 $provider->setConfig( $this->config );
248 $provider->setHookContainer( $this->hookContainer );
249 $id = $provider->getUniqueId();
250 if ( isset( $this->allAuthenticationProviders[$id] ) ) {
251 throw new \RuntimeException(
252 "Duplicate specifications for id $id (classes " .
253 get_class( $provider ) .
' and ' .
254 get_class( $this->allAuthenticationProviders[$id] ) .
')'
257 $this->allAuthenticationProviders[$id] = $provider;
258 $this->primaryAuthenticationProviders[$id] = $provider;
276 return $this->request->getSession()->canSetUser();
298 $session = $this->request->getSession();
299 if ( !$session->canSetUser() ) {
301 $session->remove(
'AuthManager::authnState' );
302 throw new \LogicException(
'Authentication is not possible now' );
305 $guessUserName =
null;
306 foreach ( $reqs as $req ) {
307 $req->returnToUrl = $returnToUrl;
309 if ( $req->username !==
null && $req->username !==
'' ) {
310 if ( $guessUserName ===
null ) {
311 $guessUserName = $req->username;
312 } elseif ( $guessUserName !== $req->username ) {
313 $guessUserName =
null;
322 $reqs, CreatedAccountAuthenticationRequest::class
325 if ( !in_array( $req, $this->createdAccountAuthenticationRequests,
true ) ) {
326 throw new \LogicException(
327 'CreatedAccountAuthenticationRequests are only valid on ' .
328 'the same AuthManager that created the account'
335 throw new \UnexpectedValueException(
336 "CreatedAccountAuthenticationRequest had invalid username \"{$req->username}\""
338 } elseif ( $user->getId() != $req->id ) {
339 throw new \UnexpectedValueException(
340 "ID for \"{$req->username}\" was {$user->getId()}, expected {$req->id}"
345 $this->logger->info(
'Logging in {user} after account creation', [
346 'user' => $user->getName(),
351 $session->remove(
'AuthManager::authnState' );
353 $ret, $user, $user->getName(), [] );
360 $status = $provider->testForAuthentication( $reqs );
361 if ( !$status->isGood() ) {
362 $this->logger->debug(
'Login failed in pre-authentication by ' . $provider->getUniqueId() );
364 Status::wrap( $status )->getMessage()
369 $this->
getHookRunner()->onAuthManagerLoginAuthenticateAudit( $ret,
null, $guessUserName, [] );
376 'returnToUrl' => $returnToUrl,
377 'guessUserName' => $guessUserName,
379 'primaryResponse' =>
null,
382 'continueRequests' => [],
387 $reqs, CreateFromLoginAuthenticationRequest::class
390 $state[
'maybeLink'] = $req->maybeLink;
393 $session = $this->request->getSession();
394 $session->setSecret(
'AuthManager::authnState', $state );
423 $session = $this->request->getSession();
425 if ( !$session->canSetUser() ) {
428 throw new \LogicException(
'Authentication is not possible now' );
432 $state = $session->getSecret(
'AuthManager::authnState' );
433 if ( !is_array( $state ) ) {
435 wfMessage(
'authmanager-authn-not-in-progress' )
438 $state[
'continueRequests'] = [];
440 $guessUserName = $state[
'guessUserName'];
442 foreach ( $reqs as $req ) {
443 $req->returnToUrl = $state[
'returnToUrl'];
448 if ( $state[
'primary'] ===
null ) {
451 $guessUserName =
null;
452 foreach ( $reqs as $req ) {
453 if ( $req->username !==
null && $req->username !==
'' ) {
454 if ( $guessUserName ===
null ) {
455 $guessUserName = $req->username;
456 } elseif ( $guessUserName !== $req->username ) {
457 $guessUserName =
null;
462 $state[
'guessUserName'] = $guessUserName;
464 $state[
'reqs'] = $reqs;
467 $res = $provider->beginPrimaryAuthentication( $reqs );
468 switch (
$res->status ) {
470 $state[
'primary'] = $id;
471 $state[
'primaryResponse'] =
$res;
472 $this->logger->debug(
"Primary login with $id succeeded" );
475 $this->logger->debug(
"Login failed in primary authentication by $id" );
476 if (
$res->createRequest || $state[
'maybeLink'] ) {
478 $res->createRequest, $state[
'maybeLink']
484 $session->remove(
'AuthManager::authnState' );
486 $res,
null, $guessUserName, [] );
493 $this->logger->debug(
"Primary login with $id returned $res->status" );
494 $this->
fillRequests(
$res->neededRequests, self::ACTION_LOGIN, $guessUserName );
495 $state[
'primary'] = $id;
496 $state[
'continueRequests'] =
$res->neededRequests;
497 $session->setSecret(
'AuthManager::authnState', $state );
502 throw new \DomainException(
503 get_class( $provider ) .
"::beginPrimaryAuthentication() returned $res->status"
508 if ( $state[
'primary'] ===
null ) {
509 $this->logger->debug(
'Login failed in primary authentication because no provider accepted' );
511 wfMessage(
'authmanager-authn-no-primary' )
516 $session->remove(
'AuthManager::authnState' );
519 } elseif ( $state[
'primaryResponse'] ===
null ) {
525 wfMessage(
'authmanager-authn-not-in-progress' )
530 $session->remove(
'AuthManager::authnState' );
534 $id = $provider->getUniqueId();
535 $res = $provider->continuePrimaryAuthentication( $reqs );
536 switch (
$res->status ) {
538 $state[
'primaryResponse'] =
$res;
539 $this->logger->debug(
"Primary login with $id succeeded" );
542 $this->logger->debug(
"Login failed in primary authentication by $id" );
543 if (
$res->createRequest || $state[
'maybeLink'] ) {
545 $res->createRequest, $state[
'maybeLink']
551 $session->remove(
'AuthManager::authnState' );
553 $res,
null, $guessUserName, [] );
557 $this->logger->debug(
"Primary login with $id returned $res->status" );
558 $this->
fillRequests(
$res->neededRequests, self::ACTION_LOGIN, $guessUserName );
559 $state[
'continueRequests'] =
$res->neededRequests;
560 $session->setSecret(
'AuthManager::authnState', $state );
563 throw new \DomainException(
564 get_class( $provider ) .
"::continuePrimaryAuthentication() returned $res->status"
569 $res = $state[
'primaryResponse'];
570 if (
$res->username ===
null ) {
576 wfMessage(
'authmanager-authn-not-in-progress' )
581 $session->remove(
'AuthManager::authnState' );
591 $state[
'maybeLink'][
$res->linkRequest->getUniqueId()] =
$res->linkRequest;
592 $msg =
'authmanager-authn-no-local-user-link';
594 $msg =
'authmanager-authn-no-local-user';
596 $this->logger->debug(
597 "Primary login with {$provider->getUniqueId()} succeeded, but returned no user"
605 if (
$res->createRequest || $state[
'maybeLink'] ) {
607 $res->createRequest, $state[
'maybeLink']
609 $ret->neededRequests[] = $ret->createRequest;
611 $this->
fillRequests( $ret->neededRequests, self::ACTION_LOGIN,
null,
true );
612 $session->setSecret(
'AuthManager::authnState', [
615 'primaryResponse' =>
null,
617 'continueRequests' => $ret->neededRequests,
628 throw new \DomainException(
629 get_class( $provider ) .
" returned an invalid username: {$res->username}"
632 if ( $user->getId() === 0 ) {
634 $this->logger->info(
'Auto-creating {user} on login', [
635 'user' => $user->getName(),
637 $status = $this->
autoCreateUser( $user, $state[
'primary'],
false );
638 if ( !$status->isGood() ) {
640 Status::wrap( $status )->getMessage(
'authmanager-authn-autocreate-failed' )
643 $session->remove(
'AuthManager::authnState' );
645 $ret, $user, $user->getName(), [] );
652 $beginReqs = $state[
'reqs'];
655 if ( !isset( $state[
'secondary'][$id] ) ) {
659 $func =
'beginSecondaryAuthentication';
660 $res = $provider->beginSecondaryAuthentication( $user, $beginReqs );
661 } elseif ( !$state[
'secondary'][$id] ) {
662 $func =
'continueSecondaryAuthentication';
663 $res = $provider->continueSecondaryAuthentication( $user, $reqs );
667 switch (
$res->status ) {
669 $this->logger->debug(
"Secondary login with $id succeeded" );
672 $state[
'secondary'][$id] =
true;
675 $this->logger->debug(
"Login failed in secondary authentication by $id" );
677 $session->remove(
'AuthManager::authnState' );
679 $res, $user, $user->getName(), [] );
683 $this->logger->debug(
"Secondary login with $id returned " .
$res->status );
684 $this->
fillRequests(
$res->neededRequests, self::ACTION_LOGIN, $user->getName() );
685 $state[
'secondary'][$id] =
false;
686 $state[
'continueRequests'] =
$res->neededRequests;
687 $session->setSecret(
'AuthManager::authnState', $state );
692 throw new \DomainException(
693 get_class( $provider ) .
"::{$func}() returned $res->status"
702 $this->logger->info(
'Login for {user} succeeded from {clientip}', [
703 'user' => $user->getName(),
704 'clientip' => $this->request->getIP(),
708 $beginReqs, RememberMeAuthenticationRequest::class
713 $session->remove(
'AuthManager::authnState' );
716 $ret, $user, $user->getName(), [] );
718 }
catch ( \Exception $ex ) {
719 $session->remove(
'AuthManager::authnState' );
738 $this->logger->debug( __METHOD__ .
": Checking $operation" );
740 $session = $this->request->getSession();
741 $aId = $session->getUser()->getId();
745 $this->logger->info( __METHOD__ .
": Not logged in! $operation is $status" );
749 if ( $session->canSetUser() ) {
750 $id = $session->get(
'AuthManager:lastAuthId' );
751 $last = $session->get(
'AuthManager:lastAuthTimestamp' );
752 if ( $id !== $aId || $last ===
null ) {
753 $timeSinceLogin = PHP_INT_MAX;
755 $timeSinceLogin = max( 0, time() - $last );
758 $thresholds = $this->config->get(
'ReauthenticateTime' );
759 if ( isset( $thresholds[$operation] ) ) {
760 $threshold = $thresholds[$operation];
761 } elseif ( isset( $thresholds[
'default'] ) ) {
762 $threshold = $thresholds[
'default'];
764 throw new \UnexpectedValueException(
'$wgReauthenticateTime lacks a default' );
767 if ( $threshold >= 0 && $timeSinceLogin > $threshold ) {
771 $timeSinceLogin = -1;
773 $pass = $this->config->get(
'AllowSecuritySensitiveOperationIfCannotReauthenticate' );
774 if ( isset( $pass[$operation] ) ) {
776 } elseif ( isset( $pass[
'default'] ) ) {
779 throw new \UnexpectedValueException(
780 '$wgAllowSecuritySensitiveOperationIfCannotReauthenticate lacks a default'
786 $status, $operation, $session, $timeSinceLogin );
793 $this->logger->info( __METHOD__ .
": $operation is $status for '{user}'",
795 'user' => $session->getUser()->getName(),
796 'clientip' => $this->getRequest()->getIP(),
814 if ( $provider->testUserCanAuthenticate( $username ) ) {
838 $normalized = $provider->providerNormalizeUsername( $username );
839 if ( $normalized !==
null ) {
840 $ret[$normalized] =
true;
843 return array_keys( $ret );
861 $this->logger->info(
'Revoking access for {user}', [
881 foreach ( $providers as $provider ) {
882 $status = $provider->providerAllowsAuthenticationDataChange( $req, $checkData );
883 if ( !$status->isGood() ) {
887 return $status->hasMessage(
'throttled-mailpassword' )
888 ? Status::newGood(
'throttled-mailpassword' )
889 : Status::wrap( $status );
891 $any = $any || $status->value !==
'ignored';
894 $status = Status::newGood(
'ignored' );
895 $status->warning(
'authmanager-change-not-supported' );
898 return Status::newGood();
919 $this->logger->info(
'Changing authentication data for {user} class {what}', [
920 'user' => is_string( $req->username ) ? $req->username :
'<no name>',
921 'what' => get_class( $req ),
928 if ( !$isAddition ) {
929 \BotPassword::invalidateAllPasswordsForUser( $req->username );
946 switch ( $provider->accountCreationType() ) {
965 if ( is_int( $options ) ) {
966 $options = [
'flags' => $options ];
969 'flags' => User::READ_NORMAL,
972 $flags = $options[
'flags'];
975 return Status::newFatal(
'authmanager-create-disabled' );
978 if ( $this->
userExists( $username, $flags ) ) {
979 return Status::newFatal(
'userexists' );
983 if ( !is_object( $user ) ) {
984 return Status::newFatal(
'noname' );
986 $user->load( $flags );
987 if ( $user->getId() !== 0 ) {
988 return Status::newFatal(
'userexists' );
996 foreach ( $providers as $provider ) {
997 $status = $provider->testUserForCreation( $user,
false, $options );
998 if ( !$status->isGood() ) {
999 return Status::wrap( $status );
1003 return Status::newGood();
1017 $permErrors = $this->permManager->getPermissionErrors(
1022 if ( $permErrors ) {
1023 $status = Status::newGood();
1024 foreach ( $permErrors as
$args ) {
1025 $status->fatal( ...
$args );
1034 $language = \RequestContext::getMain()->getLanguage();
1036 return Status::newFatal(
1037 $formatter->getMessage( $block, $creator, $language, $ip )
1043 ->isDnsBlacklisted( $ip,
true )
1045 return Status::newFatal(
'sorbs_create_account_reason' );
1048 return Status::newGood();
1071 $session = $this->request->getSession();
1074 $session->remove(
'AuthManager::accountCreationState' );
1075 throw new \LogicException(
'Account creation is not possible' );
1080 }
catch ( \UnexpectedValueException $ex ) {
1083 if ( $username ===
null ) {
1084 $this->logger->debug( __METHOD__ .
': No username provided' );
1090 if ( !$status->isGood() ) {
1091 $this->logger->debug( __METHOD__ .
': {creator} cannot create users: {reason}', [
1092 'user' => $username,
1093 'creator' => $creator->
getName(),
1094 'reason' => $status->getWikiText(
null,
null,
'en' )
1102 if ( !$status->isGood() ) {
1103 $this->logger->debug( __METHOD__ .
': {user} cannot be created: {reason}', [
1104 'user' => $username,
1105 'creator' => $creator->
getName(),
1106 'reason' => $status->getWikiText(
null,
null,
'en' )
1112 foreach ( $reqs as $req ) {
1113 $req->username = $username;
1114 $req->returnToUrl = $returnToUrl;
1116 $status = $req->populateUser( $user );
1117 if ( !$status->isGood() ) {
1118 $status = Status::wrap( $status );
1119 $session->remove(
'AuthManager::accountCreationState' );
1120 $this->logger->debug( __METHOD__ .
': UserData is invalid: {reason}', [
1121 'user' => $user->getName(),
1122 'creator' => $creator->
getName(),
1123 'reason' => $status->getWikiText(
null,
null,
'en' ),
1133 'username' => $username,
1135 'creatorid' => $creator->
getId(),
1136 'creatorname' => $creator->
getName(),
1138 'returnToUrl' => $returnToUrl,
1140 'primaryResponse' =>
null,
1142 'continueRequests' => [],
1144 'ranPreTests' =>
false,
1149 $reqs, CreateFromLoginAuthenticationRequest::class
1152 $state[
'maybeLink'] = $req->maybeLink;
1154 if ( $req->createRequest ) {
1155 $reqs[] = $req->createRequest;
1156 $state[
'reqs'][] = $req->createRequest;
1160 $session->setSecret(
'AuthManager::accountCreationState', $state );
1161 $session->persist();
1172 $session = $this->request->getSession();
1176 $session->remove(
'AuthManager::accountCreationState' );
1177 throw new \LogicException(
'Account creation is not possible' );
1180 $state = $session->getSecret(
'AuthManager::accountCreationState' );
1181 if ( !is_array( $state ) ) {
1183 wfMessage(
'authmanager-create-not-in-progress' )
1186 $state[
'continueRequests'] = [];
1191 if ( !is_object( $user ) ) {
1192 $session->remove(
'AuthManager::accountCreationState' );
1193 $this->logger->debug( __METHOD__ .
': Invalid username', [
1194 'user' => $state[
'username'],
1199 if ( $state[
'creatorid'] ) {
1202 $creator =
new User;
1203 $creator->
setName( $state[
'creatorname'] );
1207 $cache = \ObjectCache::getLocalClusterInstance();
1208 $lock =
$cache->getScopedLock(
$cache->makeGlobalKey(
'account', md5( $user->getName() ) ) );
1212 $this->logger->debug( __METHOD__ .
': Could not acquire account creation lock', [
1213 'user' => $user->getName(),
1214 'creator' => $creator->getName(),
1221 if ( !$status->isGood() ) {
1222 $this->logger->debug( __METHOD__ .
': {creator} cannot create users: {reason}', [
1223 'user' => $user->getName(),
1224 'creator' => $creator->getName(),
1225 'reason' => $status->getWikiText(
null,
null,
'en' )
1229 $session->remove(
'AuthManager::accountCreationState' );
1236 if ( $state[
'userid'] === 0 ) {
1237 if ( $user->getId() !== 0 ) {
1238 $this->logger->debug( __METHOD__ .
': User exists locally', [
1239 'user' => $user->getName(),
1240 'creator' => $creator->getName(),
1244 $session->remove(
'AuthManager::accountCreationState' );
1248 if ( $user->getId() === 0 ) {
1249 $this->logger->debug( __METHOD__ .
': User does not exist locally when it should', [
1250 'user' => $user->getName(),
1251 'creator' => $creator->getName(),
1252 'expected_id' => $state[
'userid'],
1254 throw new \UnexpectedValueException(
1255 "User \"{$state['username']}\" should exist now, but doesn't!"
1258 if ( $user->getId() !== $state[
'userid'] ) {
1259 $this->logger->debug( __METHOD__ .
': User ID/name mismatch', [
1260 'user' => $user->getName(),
1261 'creator' => $creator->getName(),
1262 'expected_id' => $state[
'userid'],
1263 'actual_id' => $user->getId(),
1265 throw new \UnexpectedValueException(
1266 "User \"{$state['username']}\" exists, but " .
1267 "ID {$user->getId()} !== {$state['userid']}!"
1271 foreach ( $state[
'reqs'] as $req ) {
1273 $status = $req->populateUser( $user );
1274 if ( !$status->isGood() ) {
1276 $status = Status::wrap( $status );
1277 $this->logger->debug( __METHOD__ .
': UserData is invalid: {reason}', [
1278 'user' => $user->getName(),
1279 'creator' => $creator->getName(),
1280 'reason' => $status->getWikiText(
null,
null,
'en' ),
1284 $session->remove(
'AuthManager::accountCreationState' );
1290 foreach ( $reqs as $req ) {
1291 $req->returnToUrl = $state[
'returnToUrl'];
1292 $req->username = $state[
'username'];
1296 if ( !$state[
'ranPreTests'] ) {
1300 foreach ( $providers as $id => $provider ) {
1301 $status = $provider->testForAccountCreation( $user, $creator, $reqs );
1302 if ( !$status->isGood() ) {
1303 $this->logger->debug( __METHOD__ .
": Fail in pre-authentication by $id", [
1304 'user' => $user->getName(),
1305 'creator' => $creator->getName(),
1308 Status::wrap( $status )->getMessage()
1311 $session->remove(
'AuthManager::accountCreationState' );
1316 $state[
'ranPreTests'] =
true;
1321 if ( $state[
'primary'] ===
null ) {
1327 $res = $provider->beginPrimaryAccountCreation( $user, $creator, $reqs );
1328 switch (
$res->status ) {
1330 $this->logger->debug( __METHOD__ .
": Primary creation passed by $id", [
1331 'user' => $user->getName(),
1332 'creator' => $creator->getName(),
1334 $state[
'primary'] = $id;
1335 $state[
'primaryResponse'] =
$res;
1338 $this->logger->debug( __METHOD__ .
": Primary creation failed by $id", [
1339 'user' => $user->getName(),
1340 'creator' => $creator->getName(),
1343 $session->remove(
'AuthManager::accountCreationState' );
1350 $this->logger->debug( __METHOD__ .
": Primary creation $res->status by $id", [
1351 'user' => $user->getName(),
1352 'creator' => $creator->getName(),
1355 $state[
'primary'] = $id;
1356 $state[
'continueRequests'] =
$res->neededRequests;
1357 $session->setSecret(
'AuthManager::accountCreationState', $state );
1362 throw new \DomainException(
1363 get_class( $provider ) .
"::beginPrimaryAccountCreation() returned $res->status"
1368 if ( $state[
'primary'] ===
null ) {
1369 $this->logger->debug( __METHOD__ .
': Primary creation failed because no provider accepted', [
1370 'user' => $user->getName(),
1371 'creator' => $creator->getName(),
1374 wfMessage(
'authmanager-create-no-primary' )
1377 $session->remove(
'AuthManager::accountCreationState' );
1380 } elseif ( $state[
'primaryResponse'] ===
null ) {
1386 wfMessage(
'authmanager-create-not-in-progress' )
1389 $session->remove(
'AuthManager::accountCreationState' );
1393 $id = $provider->getUniqueId();
1394 $res = $provider->continuePrimaryAccountCreation( $user, $creator, $reqs );
1395 switch (
$res->status ) {
1397 $this->logger->debug( __METHOD__ .
": Primary creation passed by $id", [
1398 'user' => $user->getName(),
1399 'creator' => $creator->getName(),
1401 $state[
'primaryResponse'] =
$res;
1404 $this->logger->debug( __METHOD__ .
": Primary creation failed by $id", [
1405 'user' => $user->getName(),
1406 'creator' => $creator->getName(),
1409 $session->remove(
'AuthManager::accountCreationState' );
1413 $this->logger->debug( __METHOD__ .
": Primary creation $res->status by $id", [
1414 'user' => $user->getName(),
1415 'creator' => $creator->getName(),
1418 $state[
'continueRequests'] =
$res->neededRequests;
1419 $session->setSecret(
'AuthManager::accountCreationState', $state );
1422 throw new \DomainException(
1423 get_class( $provider ) .
"::continuePrimaryAccountCreation() returned $res->status"
1431 if ( $state[
'userid'] === 0 ) {
1432 $this->logger->info(
'Creating user {user} during account creation', [
1433 'user' => $user->getName(),
1434 'creator' => $creator->getName(),
1436 $status = $user->addToDatabase();
1437 if ( !$status->isOK() ) {
1441 $session->remove(
'AuthManager::accountCreationState' );
1447 $user->saveSettings();
1448 $state[
'userid'] = $user->getId();
1451 \DeferredUpdates::addUpdate( \SiteStatsUpdate::factory( [
'users' => 1 ] ) );
1457 $logSubtype = $provider->finishAccountCreation( $user, $creator, $state[
'primaryResponse'] );
1460 if ( $this->config->get(
'NewUserLog' ) ) {
1461 $isAnon = $creator->isAnon();
1462 $logEntry = new \ManualLogEntry(
1464 $logSubtype ?: ( $isAnon ?
'create' :
'create2' )
1466 $logEntry->setPerformer( $isAnon ? $user : $creator );
1467 $logEntry->setTarget( $user->getUserPage() );
1470 $state[
'reqs'], CreationReasonAuthenticationRequest::class
1472 $logEntry->setComment( $req ? $req->reason :
'' );
1473 $logEntry->setParameters( [
1474 '4::userid' => $user->getId(),
1476 $logid = $logEntry->insert();
1477 $logEntry->publish( $logid );
1483 $beginReqs = $state[
'reqs'];
1486 if ( !isset( $state[
'secondary'][$id] ) ) {
1490 $func =
'beginSecondaryAccountCreation';
1491 $res = $provider->beginSecondaryAccountCreation( $user, $creator, $beginReqs );
1492 } elseif ( !$state[
'secondary'][$id] ) {
1493 $func =
'continueSecondaryAccountCreation';
1494 $res = $provider->continueSecondaryAccountCreation( $user, $creator, $reqs );
1498 switch (
$res->status ) {
1500 $this->logger->debug( __METHOD__ .
": Secondary creation passed by $id", [
1501 'user' => $user->getName(),
1502 'creator' => $creator->getName(),
1506 $state[
'secondary'][$id] =
true;
1510 $this->logger->debug( __METHOD__ .
": Secondary creation $res->status by $id", [
1511 'user' => $user->getName(),
1512 'creator' => $creator->getName(),
1515 $state[
'secondary'][$id] =
false;
1516 $state[
'continueRequests'] =
$res->neededRequests;
1517 $session->setSecret(
'AuthManager::accountCreationState', $state );
1520 throw new \DomainException(
1521 get_class( $provider ) .
"::{$func}() returned $res->status." .
1522 ' Secondary providers are not allowed to fail account creation, that' .
1523 ' should have been done via testForAccountCreation().'
1527 throw new \DomainException(
1528 get_class( $provider ) .
"::{$func}() returned $res->status"
1534 $id = $user->getId();
1535 $name = $user->getName();
1538 $ret->loginRequest = $req;
1539 $this->createdAccountAuthenticationRequests[] = $req;
1541 $this->logger->info( __METHOD__ .
': Account creation succeeded for {user}', [
1542 'user' => $user->getName(),
1543 'creator' => $creator->getName(),
1547 $session->remove(
'AuthManager::accountCreationState' );
1550 }
catch ( \Exception $ex ) {
1551 $session->remove(
'AuthManager::accountCreationState' );
1574 if (
$source !== self::AUTOCREATE_SOURCE_SESSION &&
1575 $source !== self::AUTOCREATE_SOURCE_MAINT &&
1578 throw new \InvalidArgumentException(
"Unknown auto-creation source: $source" );
1585 $flags = User::READ_NORMAL;
1595 $flags = User::READ_LATEST;
1600 $this->logger->debug( __METHOD__ .
': {username} already exists locally', [
1601 'username' => $username,
1603 $user->
setId( $localId );
1608 $status = Status::newGood();
1609 $status->warning(
'userexists' );
1615 $this->logger->debug( __METHOD__ .
': denied by wfReadOnly(): {reason}', [
1616 'username' => $username,
1626 $session = $this->request->getSession();
1627 if ( $session->get(
'AuthManager::AutoCreateBlacklist' ) ) {
1628 $this->logger->debug( __METHOD__ .
': blacklisted in session {sessionid}', [
1629 'username' => $username,
1630 'sessionid' => $session->getId(),
1634 $reason = $session->get(
'AuthManager::AutoCreateBlacklist' );
1636 return Status::wrap( $reason );
1638 return Status::newFatal( $reason );
1644 $this->logger->debug( __METHOD__ .
': name "{username}" is not creatable', [
1645 'username' => $username,
1647 $session->set(
'AuthManager::AutoCreateBlacklist',
'noname' );
1650 return Status::newFatal(
'noname' );
1657 ->userHasAnyRight( $anon,
'createaccount',
'autocreateaccount' )
1659 $this->logger->debug( __METHOD__ .
': IP lacks the ability to create or autocreate accounts', [
1660 'username' => $username,
1661 'clientip' => $anon->getName(),
1663 $session->set(
'AuthManager::AutoCreateBlacklist',
'authmanager-autocreate-noperm' );
1664 $session->persist();
1667 return Status::newFatal(
'authmanager-autocreate-noperm' );
1671 $cache = \ObjectCache::getLocalClusterInstance();
1672 $lock =
$cache->getScopedLock(
$cache->makeGlobalKey(
'account', md5( $username ) ) );
1674 $this->logger->debug( __METHOD__ .
': Could not acquire account creation lock', [
1675 'user' => $username,
1679 return Status::newFatal(
'usernameinprogress' );
1684 'flags' => User::READ_LATEST,
1690 foreach ( $providers as $provider ) {
1691 $status = $provider->testUserForCreation( $user,
$source, $options );
1692 if ( !$status->isGood() ) {
1693 $ret = Status::wrap( $status );
1694 $this->logger->debug( __METHOD__ .
': Provider denied creation of {username}: {reason}', [
1695 'username' => $username,
1696 'reason' => $ret->getWikiText(
null,
null,
'en' ),
1698 $session->set(
'AuthManager::AutoCreateBlacklist', $status );
1705 $backoffKey =
$cache->makeKey(
'AuthManager',
'autocreate-failed', md5( $username ) );
1706 if (
$cache->get( $backoffKey ) ) {
1707 $this->logger->debug( __METHOD__ .
': {username} denied by prior creation attempt failures', [
1708 'username' => $username,
1712 return Status::newFatal(
'authmanager-autocreate-exception' );
1716 $from = $_SERVER[
'REQUEST_URI'] ??
'CLI';
1717 $this->logger->info( __METHOD__ .
': creating new user ({username}) - from: {from}', [
1718 'username' => $username,
1723 $trxProfiler = \Profiler::instance()->getTransactionProfiler();
1724 $old = $trxProfiler->setSilenced(
true );
1727 if ( !$status->isOK() ) {
1730 if ( $user->
getId() ) {
1731 $this->logger->info( __METHOD__ .
': {username} already exists locally (race)', [
1732 'username' => $username,
1737 $status = Status::newGood();
1738 $status->warning(
'userexists' );
1740 $this->logger->error( __METHOD__ .
': {username} failed with message {msg}', [
1741 'username' => $username,
1742 'msg' => $status->getWikiText(
null,
null,
'en' )
1749 }
catch ( \Exception $ex ) {
1750 $trxProfiler->setSilenced( $old );
1751 $this->logger->error( __METHOD__ .
': {username} failed with exception {exception}', [
1752 'username' => $username,
1756 $cache->set( $backoffKey, 1, 600 );
1770 \DeferredUpdates::addUpdate( \SiteStatsUpdate::factory( [
'users' => 1 ] ) );
1772 \DeferredUpdates::addCallableUpdate(
function () use ( $user ) {
1777 if ( $this->config->get(
'NewUserLog' ) ) {
1778 $logEntry = new \ManualLogEntry(
'newusers',
'autocreate' );
1779 $logEntry->setPerformer( $user );
1781 $logEntry->setComment(
'' );
1782 $logEntry->setParameters( [
1783 '4::userid' => $user->
getId(),
1785 $logEntry->insert();
1788 $trxProfiler->setSilenced( $old );
1794 return Status::newGood();
1827 $session = $this->request->getSession();
1828 $session->remove(
'AuthManager::accountLinkState' );
1832 throw new \LogicException(
'Account linking is not possible' );
1835 if ( $user->
getId() === 0 ) {
1843 foreach ( $reqs as $req ) {
1844 $req->username = $user->
getName();
1845 $req->returnToUrl = $returnToUrl;
1851 foreach ( $providers as $id => $provider ) {
1852 $status = $provider->testForAccountLink( $user );
1853 if ( !$status->isGood() ) {
1854 $this->logger->debug( __METHOD__ .
": Account linking pre-check failed by $id", [
1858 Status::wrap( $status )->getMessage()
1866 'username' => $user->
getName(),
1867 'userid' => $user->
getId(),
1868 'returnToUrl' => $returnToUrl,
1870 'continueRequests' => [],
1874 foreach ( $providers as $id => $provider ) {
1879 $res = $provider->beginPrimaryAccountLink( $user, $reqs );
1880 switch (
$res->status ) {
1882 $this->logger->info(
"Account linked to {user} by $id", [
1889 $this->logger->debug( __METHOD__ .
": Account linking failed by $id", [
1901 $this->logger->debug( __METHOD__ .
": Account linking $res->status by $id", [
1905 $state[
'primary'] = $id;
1906 $state[
'continueRequests'] =
$res->neededRequests;
1907 $session->setSecret(
'AuthManager::accountLinkState', $state );
1908 $session->persist();
1913 throw new \DomainException(
1914 get_class( $provider ) .
"::beginPrimaryAccountLink() returned $res->status"
1920 $this->logger->debug( __METHOD__ .
': Account linking failed because no provider accepted', [
1924 wfMessage(
'authmanager-link-no-primary' )
1936 $session = $this->request->getSession();
1940 $session->remove(
'AuthManager::accountLinkState' );
1941 throw new \LogicException(
'Account linking is not possible' );
1944 $state = $session->getSecret(
'AuthManager::accountLinkState' );
1945 if ( !is_array( $state ) ) {
1947 wfMessage(
'authmanager-link-not-in-progress' )
1950 $state[
'continueRequests'] = [];
1955 if ( !is_object( $user ) ) {
1956 $session->remove(
'AuthManager::accountLinkState' );
1959 if ( $user->getId() !== $state[
'userid'] ) {
1960 throw new \UnexpectedValueException(
1961 "User \"{$state['username']}\" is valid, but " .
1962 "ID {$user->getId()} !== {$state['userid']}!"
1966 foreach ( $reqs as $req ) {
1967 $req->username = $state[
'username'];
1968 $req->returnToUrl = $state[
'returnToUrl'];
1978 wfMessage(
'authmanager-link-not-in-progress' )
1981 $session->remove(
'AuthManager::accountLinkState' );
1985 $id = $provider->getUniqueId();
1986 $res = $provider->continuePrimaryAccountLink( $user, $reqs );
1987 switch (
$res->status ) {
1989 $this->logger->info(
"Account linked to {user} by $id", [
1990 'user' => $user->getName(),
1993 $session->remove(
'AuthManager::accountLinkState' );
1996 $this->logger->debug( __METHOD__ .
": Account linking failed by $id", [
1997 'user' => $user->getName(),
2000 $session->remove(
'AuthManager::accountLinkState' );
2004 $this->logger->debug( __METHOD__ .
": Account linking $res->status by $id", [
2005 'user' => $user->getName(),
2007 $this->
fillRequests(
$res->neededRequests, self::ACTION_LINK, $user->getName() );
2008 $state[
'continueRequests'] =
$res->neededRequests;
2009 $session->setSecret(
'AuthManager::accountLinkState', $state );
2012 throw new \DomainException(
2013 get_class( $provider ) .
"::continuePrimaryAccountLink() returned $res->status"
2016 }
catch ( \Exception $ex ) {
2017 $session->remove(
'AuthManager::accountLinkState' );
2049 $providerAction = $action;
2052 switch ( $action ) {
2061 $state = $this->request->getSession()->getSecret(
'AuthManager::authnState' );
2062 return is_array( $state ) ? $state[
'continueRequests'] : [];
2065 $state = $this->request->getSession()->getSecret(
'AuthManager::accountCreationState' );
2066 return is_array( $state ) ? $state[
'continueRequests'] : [];
2090 $state = $this->request->getSession()->getSecret(
'AuthManager::accountLinkState' );
2091 return is_array( $state ) ? $state[
'continueRequests'] : [];
2101 throw new \DomainException( __METHOD__ .
": Invalid action \"$action\"" );
2118 $providerAction, array $options, array $providers,
User $user =
null
2120 $user = $user ?: \RequestContext::getMain()->getUser();
2121 $options[
'username'] = $user->isAnon() ? null : $user->getName();
2125 foreach ( $providers as $provider ) {
2127 foreach ( $provider->getAuthenticationRequests( $providerAction, $options ) as $req ) {
2131 if ( $isPrimary && $req->required ) {
2136 !isset( $reqs[$id] )
2146 switch ( $providerAction ) {
2154 if ( $options[
'username'] !==
null ) {
2156 $options[
'username'] =
null;
2162 $this->
fillRequests( $reqs, $providerAction, $options[
'username'],
true );
2165 if ( $providerAction === self::ACTION_CHANGE || $providerAction === self::ACTION_REMOVE ) {
2166 $reqs = array_filter( $reqs,
function ( $req ) {
2171 return array_values( $reqs );
2181 private function fillRequests( array &$reqs, $action, $username, $forceAction =
false ) {
2182 foreach ( $reqs as $req ) {
2183 if ( !$req->action || $forceAction ) {
2184 $req->action = $action;
2186 if ( $req->username ===
null ) {
2187 $req->username = $username;
2198 public function userExists( $username, $flags = User::READ_NORMAL ) {
2200 if ( $provider->testUserExists( $username, $flags ) ) {
2222 foreach ( $providers as $provider ) {
2223 if ( !$provider->providerAllowsPropertyChange( $property ) ) {
2240 if ( isset( $this->allAuthenticationProviders[$id] ) ) {
2241 return $this->allAuthenticationProviders[$id];
2246 if ( isset( $providers[$id] ) ) {
2247 return $providers[$id];
2250 if ( isset( $providers[$id] ) ) {
2251 return $providers[$id];
2254 if ( isset( $providers[$id] ) ) {
2255 return $providers[$id];
2275 $session = $this->request->getSession();
2276 $arr = $session->getSecret(
'authData' );
2277 if ( !is_array( $arr ) ) {
2281 $session->setSecret(
'authData', $arr );
2292 $arr = $this->request->getSession()->getSecret(
'authData' );
2293 if ( is_array( $arr ) && array_key_exists( $key, $arr ) ) {
2306 $session = $this->request->getSession();
2307 if ( $key ===
null ) {
2308 $session->remove(
'authData' );
2310 $arr = $session->getSecret(
'authData' );
2311 if ( is_array( $arr ) && array_key_exists( $key, $arr ) ) {
2312 unset( $arr[$key] );
2313 $session->setSecret(
'authData', $arr );
2326 foreach ( $specs as &$spec ) {
2327 $spec = [
'sort2' => $i++ ] + $spec + [
'sort' => 0 ];
2331 usort( $specs,
function ( $a, $b ) {
2332 return $a[
'sort'] <=> $b[
'sort']
2333 ?: $a[
'sort2'] <=> $b[
'sort2'];
2337 foreach ( $specs as $spec ) {
2339 $provider = $this->objectFactory->createObject( $spec, [
'assertClass' => $class ] );
2340 $provider->setLogger( $this->logger );
2341 $provider->setManager( $this );
2342 $provider->setConfig( $this->config );
2344 $id = $provider->getUniqueId();
2345 if ( isset( $this->allAuthenticationProviders[$id] ) ) {
2346 throw new \RuntimeException(
2347 "Duplicate specifications for id $id (classes " .
2348 get_class( $provider ) .
' and ' .
2349 get_class( $this->allAuthenticationProviders[$id] ) .
')'
2352 $this->allAuthenticationProviders[$id] = $provider;
2353 $ret[$id] = $provider;
2363 return $this->config->get(
'AuthManagerConfig' ) ?: $this->config->get(
'AuthManagerAutoConfig' );
2371 if ( $this->preAuthenticationProviders ===
null ) {
2374 PreAuthenticationProvider::class, $conf[
'preauth']
2385 if ( $this->primaryAuthenticationProviders ===
null ) {
2388 PrimaryAuthenticationProvider::class, $conf[
'primaryauth']
2399 if ( $this->secondaryAuthenticationProviders ===
null ) {
2402 SecondaryAuthenticationProvider::class, $conf[
'secondaryauth']
2414 $session = $this->request->getSession();
2415 $delay = $session->delaySave();
2417 $session->resetId();
2418 $session->resetAllTokens();
2419 if ( $session->canSetUser() ) {
2420 $session->setUser( $user );
2422 if ( $remember !==
null ) {
2423 $session->setRememberUser( $remember );
2425 $session->set(
'AuthManager:lastAuthId', $user->getId() );
2426 $session->set(
'AuthManager:lastAuthTimestamp', time() );
2427 $session->persist();
2429 \Wikimedia\ScopedCallback::consume( $delay );
2443 $lang = $useContextLang ? \RequestContext::getMain()->getLanguage() : $contLang;
2447 ->getLanguageConverter();
2448 if ( $contLangConverter->hasVariants() ) {
2449 $user->
setOption(
'variant', $contLangConverter->getPreferredVariant() );
2469 foreach ( $providers as $provider ) {
2470 $provider->$method( ...
$args );
2480 if ( !defined(
'MW_PHPUNIT_TEST' ) ) {
2482 throw new \MWException( __METHOD__ .
' may only be called from unit tests!' );
2486 self::$instance =
null;
wfReadOnly()
Check whether the wiki is in read-only mode.
wfReadOnlyReason()
Check if the site is in read-only mode and return the message if so.
wfMessage( $key,... $params)
This is the function for getting translated interface messages.
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,...
Generic operation result class Has warning/error list, boolean status and arbitrary value.
Generic operation result class Has warning/error list, boolean status and arbitrary value.
The User object encapsulates all of the user-specific settings (user_id, name, rights,...
getName()
Get the user name, or the IP of an anonymous user.
addToDatabase()
Add this existing user object to the database.
static newFromName( $name, $validate='valid')
Static factory method for creation from username.
static isCreatableName( $name)
Usernames which fail to pass this function will be blocked from new account registrations,...
loadFromId( $flags=self::READ_NORMAL)
Load user table data, given mId has already been set.
setName( $str)
Set the user name.
getId()
Get the user's ID.
static newFromId( $id)
Static factory method for creation from a given user ID.
setId( $v)
Set the user and reload all fields according to a given ID.
static isUsableName( $name)
Usernames which fail to pass this function will be blocked from user login and new account registrati...
setOption( $oname, $val)
Set the given option for a user.
isBlockedFromCreateAccount()
Get whether the user is explicitly blocked from account creation.
getUserPage()
Get this user's personal page title.
addWatch( $title, $checkRights=self::CHECK_USER_RIGHTS, ?string $expiry=null)
Watch an article.
static idFromName( $name, $flags=self::READ_NORMAL)
Get database id given a user name.
saveSettings()
Save this user's settings into the database.
setToken( $token=false)
Set the random token (used for persistent authentication) Called from loadDefaults() among other plac...
The WebRequest class encapsulates getting at data passed in the URL or via a POSTed form stripping il...
Interface for configuration instances.
const READ_LOCKING
Constants for object loading bitfield flags (higher => higher QoS)
if(!isset( $args[0])) $lang