41use Psr\Log\LoggerAwareInterface;
42use Psr\Log\LoggerInterface;
43use Psr\Log\NullLogger;
50use Wikimedia\ObjectFactory;
52use Wikimedia\ScopedCallback;
281 $this->logger->warning(
"Overriding AuthManager primary authn because $why" );
283 if ( $this->primaryAuthenticationProviders !==
null ) {
284 $this->logger->warning(
285 'PrimaryAuthenticationProviders have already been accessed! I hope nothing breaks.'
288 $this->allAuthenticationProviders = array_diff_key(
289 $this->allAuthenticationProviders,
290 $this->primaryAuthenticationProviders
292 $session = $this->request->getSession();
293 $session->remove(
'AuthManager::authnState' );
294 $session->remove(
'AuthManager::accountCreationState' );
295 $session->remove(
'AuthManager::accountLinkState' );
296 $this->createdAccountAuthenticationRequests = [];
299 $this->primaryAuthenticationProviders = [];
300 foreach ( $providers as $provider ) {
302 throw new \RuntimeException(
303 'Expected instance of MediaWiki\\Auth\\AbstractPrimaryAuthenticationProvider, got ' .
304 get_class( $provider )
307 $provider->init( $this->logger, $this, $this->hookContainer, $this->config, $this->userNameUtils );
308 $id = $provider->getUniqueId();
309 if ( isset( $this->allAuthenticationProviders[$id] ) ) {
310 throw new \RuntimeException(
311 "Duplicate specifications for id $id (classes " .
312 get_class( $provider ) .
' and ' .
313 get_class( $this->allAuthenticationProviders[$id] ) .
')'
316 $this->allAuthenticationProviders[$id] = $provider;
317 $this->primaryAuthenticationProviders[$id] = $provider;
334 return $this->request->getSession()->canSetUser();
356 $session = $this->request->getSession();
357 if ( !$session->canSetUser() ) {
359 $session->remove(
'AuthManager::authnState' );
360 throw new \LogicException(
'Authentication is not possible now' );
363 $guessUserName =
null;
364 foreach ( $reqs as $req ) {
365 $req->returnToUrl = $returnToUrl;
367 if ( $req->username !==
null && $req->username !==
'' ) {
368 if ( $guessUserName ===
null ) {
369 $guessUserName = $req->username;
370 } elseif ( $guessUserName !== $req->username ) {
371 $guessUserName =
null;
380 $reqs, CreatedAccountAuthenticationRequest::class
383 if ( !in_array( $req, $this->createdAccountAuthenticationRequests,
true ) ) {
384 throw new \LogicException(
385 'CreatedAccountAuthenticationRequests are only valid on ' .
386 'the same AuthManager that created the account'
390 $user = $this->userFactory->newFromName( (
string)$req->username );
393 throw new \UnexpectedValueException(
394 "CreatedAccountAuthenticationRequest had invalid username \"{$req->username}\""
396 } elseif ( $user->getId() != $req->id ) {
397 throw new \UnexpectedValueException(
398 "ID for \"{$req->username}\" was {$user->getId()}, expected {$req->id}"
403 $this->logger->info(
'Logging in {user} after account creation', [
404 'user' => $user->getName(),
409 $session->remove(
'AuthManager::authnState' );
411 $ret, $user, $user->getName(), [] );
418 $status = $provider->testForAuthentication( $reqs );
419 if ( !$status->isGood() ) {
420 $this->logger->debug(
'Login failed in pre-authentication by ' . $provider->getUniqueId() );
422 Status::wrap( $status )->getMessage()
425 [ $this->userFactory->newFromName( (
string)$guessUserName ), $ret ]
427 $this->
getHookRunner()->onAuthManagerLoginAuthenticateAudit( $ret,
null, $guessUserName, [] );
434 'returnToUrl' => $returnToUrl,
435 'guessUserName' => $guessUserName,
437 'primaryResponse' =>
null,
440 'continueRequests' => [],
445 $reqs, CreateFromLoginAuthenticationRequest::class
448 $state[
'maybeLink'] = $req->maybeLink;
451 $session = $this->request->getSession();
452 $session->setSecret(
'AuthManager::authnState', $state );
481 $session = $this->request->getSession();
483 if ( !$session->canSetUser() ) {
486 throw new \LogicException(
'Authentication is not possible now' );
490 $state = $session->getSecret(
'AuthManager::authnState' );
491 if ( !is_array( $state ) ) {
493 wfMessage(
'authmanager-authn-not-in-progress' )
496 $state[
'continueRequests'] = [];
498 $guessUserName = $state[
'guessUserName'];
500 foreach ( $reqs as $req ) {
501 $req->returnToUrl = $state[
'returnToUrl'];
506 if ( $state[
'primary'] ===
null ) {
509 $guessUserName =
null;
510 foreach ( $reqs as $req ) {
511 if ( $req->username !==
null && $req->username !==
'' ) {
512 if ( $guessUserName ===
null ) {
513 $guessUserName = $req->username;
514 } elseif ( $guessUserName !== $req->username ) {
515 $guessUserName =
null;
520 $state[
'guessUserName'] = $guessUserName;
522 $state[
'reqs'] = $reqs;
525 $res = $provider->beginPrimaryAuthentication( $reqs );
526 switch (
$res->status ) {
528 $state[
'primary'] = $id;
529 $state[
'primaryResponse'] =
$res;
530 $this->logger->debug(
"Primary login with $id succeeded" );
533 $this->logger->debug(
"Login failed in primary authentication by $id" );
534 if (
$res->createRequest || $state[
'maybeLink'] ) {
536 $res->createRequest, $state[
'maybeLink']
541 'postAuthentication',
543 $this->userFactory->newFromName( (
string)$guessUserName ),
547 $session->remove(
'AuthManager::authnState' );
549 $res,
null, $guessUserName, [] );
556 $this->logger->debug(
"Primary login with $id returned $res->status" );
557 $this->
fillRequests(
$res->neededRequests, self::ACTION_LOGIN, $guessUserName );
558 $state[
'primary'] = $id;
559 $state[
'continueRequests'] =
$res->neededRequests;
560 $session->setSecret(
'AuthManager::authnState', $state );
565 throw new \DomainException(
566 get_class( $provider ) .
"::beginPrimaryAuthentication() returned $res->status"
571 if ( $state[
'primary'] ===
null ) {
572 $this->logger->debug(
'Login failed in primary authentication because no provider accepted' );
574 wfMessage(
'authmanager-authn-no-primary' )
577 [ $this->userFactory->newFromName( (
string)$guessUserName ), $ret ]
579 $session->remove(
'AuthManager::authnState' );
582 } elseif ( $state[
'primaryResponse'] ===
null ) {
588 wfMessage(
'authmanager-authn-not-in-progress' )
591 [ $this->userFactory->newFromName( (
string)$guessUserName ), $ret ]
593 $session->remove(
'AuthManager::authnState' );
597 $id = $provider->getUniqueId();
598 $res = $provider->continuePrimaryAuthentication( $reqs );
599 switch (
$res->status ) {
601 $state[
'primaryResponse'] =
$res;
602 $this->logger->debug(
"Primary login with $id succeeded" );
605 $this->logger->debug(
"Login failed in primary authentication by $id" );
606 if (
$res->createRequest || $state[
'maybeLink'] ) {
608 $res->createRequest, $state[
'maybeLink']
612 [ $this->userFactory->newFromName( (
string)$guessUserName ),
$res ]
614 $session->remove(
'AuthManager::authnState' );
616 $res,
null, $guessUserName, [] );
620 $this->logger->debug(
"Primary login with $id returned $res->status" );
621 $this->
fillRequests(
$res->neededRequests, self::ACTION_LOGIN, $guessUserName );
622 $state[
'continueRequests'] =
$res->neededRequests;
623 $session->setSecret(
'AuthManager::authnState', $state );
626 throw new \DomainException(
627 get_class( $provider ) .
"::continuePrimaryAuthentication() returned $res->status"
632 $res = $state[
'primaryResponse'];
633 if (
$res->username ===
null ) {
639 wfMessage(
'authmanager-authn-not-in-progress' )
642 [ $this->userFactory->newFromName( (
string)$guessUserName ), $ret ]
644 $session->remove(
'AuthManager::authnState' );
652 $this->getAuthenticationProvider( ConfirmLinkSecondaryAuthenticationProvider::class )
654 $state[
'maybeLink'][
$res->linkRequest->getUniqueId()] =
$res->linkRequest;
655 $msg =
'authmanager-authn-no-local-user-link';
657 $msg =
'authmanager-authn-no-local-user';
659 $this->logger->debug(
660 "Primary login with {$provider->getUniqueId()} succeeded, but returned no user"
668 if (
$res->createRequest || $state[
'maybeLink'] ) {
670 $res->createRequest, $state[
'maybeLink']
672 $ret->neededRequests[] = $ret->createRequest;
674 $this->
fillRequests( $ret->neededRequests, self::ACTION_LOGIN,
null,
true );
675 $session->setSecret(
'AuthManager::authnState', [
678 'primaryResponse' =>
null,
680 'continueRequests' => $ret->neededRequests,
688 $user = $this->userFactory->newFromName(
689 (
string)
$res->username,
690 UserFactory::RIGOR_USABLE
694 throw new \DomainException(
695 get_class( $provider ) .
" returned an invalid username: {$res->username}"
698 if ( $user->getId() === 0 ) {
700 $this->logger->info(
'Auto-creating {user} on login', [
701 'user' => $user->getName(),
703 $status = $this->
autoCreateUser( $user, $state[
'primary'],
false );
704 if ( !$status->isGood() ) {
706 Status::wrap( $status )->getMessage(
'authmanager-authn-autocreate-failed' )
709 $session->remove(
'AuthManager::authnState' );
711 $ret, $user, $user->getName(), [] );
718 $beginReqs = $state[
'reqs'];
721 if ( !isset( $state[
'secondary'][$id] ) ) {
725 $func =
'beginSecondaryAuthentication';
726 $res = $provider->beginSecondaryAuthentication( $user, $beginReqs );
727 } elseif ( !$state[
'secondary'][$id] ) {
728 $func =
'continueSecondaryAuthentication';
729 $res = $provider->continueSecondaryAuthentication( $user, $reqs );
733 switch (
$res->status ) {
735 $this->logger->debug(
"Secondary login with $id succeeded" );
738 $state[
'secondary'][$id] =
true;
741 $this->logger->debug(
"Login failed in secondary authentication by $id" );
743 $session->remove(
'AuthManager::authnState' );
745 $res, $user, $user->getName(), [] );
749 $this->logger->debug(
"Secondary login with $id returned " .
$res->status );
750 $this->
fillRequests(
$res->neededRequests, self::ACTION_LOGIN, $user->getName() );
751 $state[
'secondary'][$id] =
false;
752 $state[
'continueRequests'] =
$res->neededRequests;
753 $session->setSecret(
'AuthManager::authnState', $state );
758 throw new \DomainException(
759 get_class( $provider ) .
"::{$func}() returned $res->status"
768 $this->logger->info(
'Login for {user} succeeded from {clientip}', [
769 'user' => $user->getName(),
770 'clientip' => $this->request->getIP(),
772 $rememberMeConfig = $this->config->get(
'RememberMe' );
780 $beginReqs, RememberMeAuthenticationRequest::class
782 $rememberMe = $req && $req->rememberMe;
787 $session->remove(
'AuthManager::authnState' );
790 $ret, $user, $user->getName(), [] );
792 }
catch ( \Exception $ex ) {
793 $session->remove(
'AuthManager::authnState' );
812 $this->logger->debug( __METHOD__ .
": Checking $operation" );
814 $session = $this->request->getSession();
815 $aId = $session->getUser()->getId();
819 $this->logger->info( __METHOD__ .
": Not logged in! $operation is $status" );
823 if ( $session->canSetUser() ) {
824 $id = $session->get(
'AuthManager:lastAuthId' );
825 $last = $session->get(
'AuthManager:lastAuthTimestamp' );
826 if ( $id !== $aId || $last ===
null ) {
827 $timeSinceLogin = PHP_INT_MAX;
829 $timeSinceLogin = max( 0, time() - $last );
832 $thresholds = $this->config->get(
'ReauthenticateTime' );
833 if ( isset( $thresholds[$operation] ) ) {
834 $threshold = $thresholds[$operation];
835 } elseif ( isset( $thresholds[
'default'] ) ) {
836 $threshold = $thresholds[
'default'];
838 throw new \UnexpectedValueException(
'$wgReauthenticateTime lacks a default' );
841 if ( $threshold >= 0 && $timeSinceLogin > $threshold ) {
845 $timeSinceLogin = -1;
847 $pass = $this->config->get(
'AllowSecuritySensitiveOperationIfCannotReauthenticate' );
848 if ( isset( $pass[$operation] ) ) {
850 } elseif ( isset( $pass[
'default'] ) ) {
853 throw new \UnexpectedValueException(
854 '$wgAllowSecuritySensitiveOperationIfCannotReauthenticate lacks a default'
860 $status, $operation, $session, $timeSinceLogin );
867 $this->logger->info( __METHOD__ .
": $operation is $status for '{user}'",
869 'user' => $session->getUser()->getName(),
870 'clientip' => $this->getRequest()->getIP(),
888 if ( $provider->testUserCanAuthenticate( $username ) ) {
912 $normalized = $provider->providerNormalizeUsername( $username );
913 if ( $normalized !==
null ) {
914 $ret[$normalized] =
true;
917 return array_keys( $ret );
934 $this->logger->info(
'Revoking access for {user}', [
954 foreach ( $providers as $provider ) {
955 $status = $provider->providerAllowsAuthenticationDataChange( $req, $checkData );
956 if ( !$status->isGood() ) {
960 return $status->hasMessage(
'throttled-mailpassword' )
961 ? Status::newGood(
'throttled-mailpassword' )
962 : Status::wrap( $status );
964 $any = $any || $status->value !==
'ignored';
967 return Status::newGood(
'ignored' )
968 ->warning(
'authmanager-change-not-supported' );
970 return Status::newGood();
991 $this->logger->info(
'Changing authentication data for {user} class {what}', [
992 'user' => is_string( $req->username ) ? $req->username :
'<no name>',
993 'what' => get_class( $req ),
1000 if ( !$isAddition ) {
1001 $this->botPasswordStore->invalidateUserPasswords( (
string)$req->username );
1017 switch ( $provider->accountCreationType() ) {
1036 if ( is_int( $options ) ) {
1037 $options = [
'flags' => $options ];
1040 'flags' => User::READ_NORMAL,
1041 'creating' =>
false,
1043 $flags = $options[
'flags'];
1046 return Status::newFatal(
'authmanager-create-disabled' );
1049 if ( $this->
userExists( $username, $flags ) ) {
1050 return Status::newFatal(
'userexists' );
1053 $user = $this->userFactory->newFromName( (
string)$username, UserFactory::RIGOR_CREATABLE );
1054 if ( !is_object( $user ) ) {
1055 return Status::newFatal(
'noname' );
1057 $user->load( $flags );
1058 if ( $user->getId() !== 0 ) {
1059 return Status::newFatal(
'userexists' );
1067 foreach ( $providers as $provider ) {
1068 $status = $provider->testUserForCreation( $user,
false, $options );
1069 if ( !$status->isGood() ) {
1070 return Status::wrap( $status );
1074 return Status::newGood();
1084 if ( $this->readOnlyMode->isReadOnly() ) {
1085 return Status::newFatal(
wfMessage(
'readonlytext', $this->readOnlyMode->getReason() ) );
1094 return Status::wrap( $permStatus );
1098 if ( $this->blockManager->isDnsBlacklisted( $ip,
true ) ) {
1099 return Status::newFatal(
'sorbs_create_account_reason' );
1102 return Status::newGood();
1125 $session = $this->request->getSession();
1128 $session->remove(
'AuthManager::accountCreationState' );
1129 throw new \LogicException(
'Account creation is not possible' );
1134 }
catch ( \UnexpectedValueException $ex ) {
1137 if ( $username ===
null ) {
1138 $this->logger->debug( __METHOD__ .
': No username provided' );
1144 if ( !$status->isGood() ) {
1145 $this->logger->debug( __METHOD__ .
': {creator} cannot create users: {reason}', [
1146 'user' => $username,
1147 'creator' => $creator->
getUser()->getName(),
1148 'reason' => $status->getWikiText(
null,
null,
'en' )
1154 $username, [
'flags' => User::READ_LOCKING,
'creating' =>
true ]
1156 if ( !$status->isGood() ) {
1157 $this->logger->debug( __METHOD__ .
': {user} cannot be created: {reason}', [
1158 'user' => $username,
1159 'creator' => $creator->
getUser()->getName(),
1160 'reason' => $status->getWikiText(
null,
null,
'en' )
1165 $user = $this->userFactory->newFromName( (
string)$username, UserFactory::RIGOR_CREATABLE );
1166 foreach ( $reqs as $req ) {
1167 $req->username = $username;
1168 $req->returnToUrl = $returnToUrl;
1170 $status = $req->populateUser( $user );
1171 if ( !$status->isGood() ) {
1172 $status = Status::wrap( $status );
1173 $session->remove(
'AuthManager::accountCreationState' );
1174 $this->logger->debug( __METHOD__ .
': UserData is invalid: {reason}', [
1175 'user' => $user->getName(),
1176 'creator' => $creator->
getUser()->getName(),
1177 'reason' => $status->getWikiText(
null,
null,
'en' ),
1187 'username' => $username,
1189 'creatorid' => $creator->
getUser()->getId(),
1190 'creatorname' => $creator->
getUser()->getName(),
1192 'returnToUrl' => $returnToUrl,
1194 'primaryResponse' =>
null,
1196 'continueRequests' => [],
1198 'ranPreTests' =>
false,
1203 $reqs, CreateFromLoginAuthenticationRequest::class
1206 $state[
'maybeLink'] = $req->maybeLink;
1208 if ( $req->createRequest ) {
1209 $reqs[] = $req->createRequest;
1210 $state[
'reqs'][] = $req->createRequest;
1214 $session->setSecret(
'AuthManager::accountCreationState', $state );
1215 $session->persist();
1226 $session = $this->request->getSession();
1230 $session->remove(
'AuthManager::accountCreationState' );
1231 throw new \LogicException(
'Account creation is not possible' );
1234 $state = $session->getSecret(
'AuthManager::accountCreationState' );
1235 if ( !is_array( $state ) ) {
1237 wfMessage(
'authmanager-create-not-in-progress' )
1240 $state[
'continueRequests'] = [];
1244 $user = $this->userFactory->newFromName(
1245 (
string)$state[
'username'],
1246 UserFactory::RIGOR_CREATABLE
1248 if ( !is_object( $user ) ) {
1249 $session->remove(
'AuthManager::accountCreationState' );
1250 $this->logger->debug( __METHOD__ .
': Invalid username', [
1251 'user' => $state[
'username'],
1256 if ( $state[
'creatorid'] ) {
1257 $creator = $this->userFactory->newFromId( (
int)$state[
'creatorid'] );
1259 $creator = $this->userFactory->newAnonymous();
1260 $creator->setName( $state[
'creatorname'] );
1264 $cache = \ObjectCache::getLocalClusterInstance();
1265 $lock =
$cache->getScopedLock(
$cache->makeGlobalKey(
'account', md5( $user->getName() ) ) );
1269 $this->logger->debug( __METHOD__ .
': Could not acquire account creation lock', [
1270 'user' => $user->getName(),
1271 'creator' => $creator->getName(),
1278 if ( !$status->isGood() ) {
1279 $this->logger->debug( __METHOD__ .
': {creator} cannot create users: {reason}', [
1280 'user' => $user->getName(),
1281 'creator' => $creator->getName(),
1282 'reason' => $status->getWikiText(
null,
null,
'en' )
1286 $session->remove(
'AuthManager::accountCreationState' );
1291 $user->load( User::READ_LOCKING );
1293 if ( $state[
'userid'] === 0 ) {
1294 if ( $user->getId() !== 0 ) {
1295 $this->logger->debug( __METHOD__ .
': User exists locally', [
1296 'user' => $user->getName(),
1297 'creator' => $creator->getName(),
1301 $session->remove(
'AuthManager::accountCreationState' );
1305 if ( $user->getId() === 0 ) {
1306 $this->logger->debug( __METHOD__ .
': User does not exist locally when it should', [
1307 'user' => $user->getName(),
1308 'creator' => $creator->getName(),
1309 'expected_id' => $state[
'userid'],
1311 throw new \UnexpectedValueException(
1312 "User \"{$state['username']}\" should exist now, but doesn't!"
1315 if ( $user->getId() !== $state[
'userid'] ) {
1316 $this->logger->debug( __METHOD__ .
': User ID/name mismatch', [
1317 'user' => $user->getName(),
1318 'creator' => $creator->getName(),
1319 'expected_id' => $state[
'userid'],
1320 'actual_id' => $user->getId(),
1322 throw new \UnexpectedValueException(
1323 "User \"{$state['username']}\" exists, but " .
1324 "ID {$user->getId()} !== {$state['userid']}!"
1328 foreach ( $state[
'reqs'] as $req ) {
1330 $status = $req->populateUser( $user );
1331 if ( !$status->isGood() ) {
1333 $status = Status::wrap( $status );
1334 $this->logger->debug( __METHOD__ .
': UserData is invalid: {reason}', [
1335 'user' => $user->getName(),
1336 'creator' => $creator->getName(),
1337 'reason' => $status->getWikiText(
null,
null,
'en' ),
1341 $session->remove(
'AuthManager::accountCreationState' );
1347 foreach ( $reqs as $req ) {
1348 $req->returnToUrl = $state[
'returnToUrl'];
1349 $req->username = $state[
'username'];
1353 if ( !$state[
'ranPreTests'] ) {
1357 foreach ( $providers as $id => $provider ) {
1358 $status = $provider->testForAccountCreation( $user, $creator, $reqs );
1359 if ( !$status->isGood() ) {
1360 $this->logger->debug( __METHOD__ .
": Fail in pre-authentication by $id", [
1361 'user' => $user->getName(),
1362 'creator' => $creator->getName(),
1365 Status::wrap( $status )->getMessage()
1368 $session->remove(
'AuthManager::accountCreationState' );
1373 $state[
'ranPreTests'] =
true;
1378 if ( $state[
'primary'] ===
null ) {
1384 $res = $provider->beginPrimaryAccountCreation( $user, $creator, $reqs );
1385 switch (
$res->status ) {
1387 $this->logger->debug( __METHOD__ .
": Primary creation passed by $id", [
1388 'user' => $user->getName(),
1389 'creator' => $creator->getName(),
1391 $state[
'primary'] = $id;
1392 $state[
'primaryResponse'] =
$res;
1395 $this->logger->debug( __METHOD__ .
": Primary creation failed by $id", [
1396 'user' => $user->getName(),
1397 'creator' => $creator->getName(),
1400 $session->remove(
'AuthManager::accountCreationState' );
1407 $this->logger->debug( __METHOD__ .
": Primary creation $res->status by $id", [
1408 'user' => $user->getName(),
1409 'creator' => $creator->getName(),
1412 $state[
'primary'] = $id;
1413 $state[
'continueRequests'] =
$res->neededRequests;
1414 $session->setSecret(
'AuthManager::accountCreationState', $state );
1419 throw new \DomainException(
1420 get_class( $provider ) .
"::beginPrimaryAccountCreation() returned $res->status"
1425 if ( $state[
'primary'] ===
null ) {
1426 $this->logger->debug( __METHOD__ .
': Primary creation failed because no provider accepted', [
1427 'user' => $user->getName(),
1428 'creator' => $creator->getName(),
1431 wfMessage(
'authmanager-create-no-primary' )
1434 $session->remove(
'AuthManager::accountCreationState' );
1437 } elseif ( $state[
'primaryResponse'] ===
null ) {
1443 wfMessage(
'authmanager-create-not-in-progress' )
1446 $session->remove(
'AuthManager::accountCreationState' );
1450 $id = $provider->getUniqueId();
1451 $res = $provider->continuePrimaryAccountCreation( $user, $creator, $reqs );
1452 switch (
$res->status ) {
1454 $this->logger->debug( __METHOD__ .
": Primary creation passed by $id", [
1455 'user' => $user->getName(),
1456 'creator' => $creator->getName(),
1458 $state[
'primaryResponse'] =
$res;
1461 $this->logger->debug( __METHOD__ .
": Primary creation failed by $id", [
1462 'user' => $user->getName(),
1463 'creator' => $creator->getName(),
1466 $session->remove(
'AuthManager::accountCreationState' );
1470 $this->logger->debug( __METHOD__ .
": Primary creation $res->status by $id", [
1471 'user' => $user->getName(),
1472 'creator' => $creator->getName(),
1475 $state[
'continueRequests'] =
$res->neededRequests;
1476 $session->setSecret(
'AuthManager::accountCreationState', $state );
1479 throw new \DomainException(
1480 get_class( $provider ) .
"::continuePrimaryAccountCreation() returned $res->status"
1488 if ( $state[
'userid'] === 0 ) {
1489 $this->logger->info(
'Creating user {user} during account creation', [
1490 'user' => $user->getName(),
1491 'creator' => $creator->getName(),
1493 $status = $user->addToDatabase();
1494 if ( !$status->isOK() ) {
1498 $session->remove(
'AuthManager::accountCreationState' );
1504 $user->saveSettings();
1505 $state[
'userid'] = $user->getId();
1508 \DeferredUpdates::addUpdate( \SiteStatsUpdate::factory( [
'users' => 1 ] ) );
1511 $this->watchlistManager->addWatchIgnoringRights( $user, $user->getUserPage() );
1514 $logSubtype = $provider->finishAccountCreation( $user, $creator, $state[
'primaryResponse'] );
1517 if ( $this->config->get(
'NewUserLog' ) ) {
1518 $isAnon = $creator->isAnon();
1519 $logEntry = new \ManualLogEntry(
1521 $logSubtype ?: ( $isAnon ?
'create' :
'create2' )
1523 $logEntry->setPerformer( $isAnon ? $user : $creator );
1524 $logEntry->setTarget( $user->getUserPage() );
1527 $state[
'reqs'], CreationReasonAuthenticationRequest::class
1529 $logEntry->setComment( $req ? $req->reason :
'' );
1530 $logEntry->setParameters( [
1531 '4::userid' => $user->getId(),
1533 $logid = $logEntry->insert();
1534 $logEntry->publish( $logid );
1540 $beginReqs = $state[
'reqs'];
1543 if ( !isset( $state[
'secondary'][$id] ) ) {
1547 $func =
'beginSecondaryAccountCreation';
1548 $res = $provider->beginSecondaryAccountCreation( $user, $creator, $beginReqs );
1549 } elseif ( !$state[
'secondary'][$id] ) {
1550 $func =
'continueSecondaryAccountCreation';
1551 $res = $provider->continueSecondaryAccountCreation( $user, $creator, $reqs );
1555 switch (
$res->status ) {
1557 $this->logger->debug( __METHOD__ .
": Secondary creation passed by $id", [
1558 'user' => $user->getName(),
1559 'creator' => $creator->getName(),
1563 $state[
'secondary'][$id] =
true;
1567 $this->logger->debug( __METHOD__ .
": Secondary creation $res->status by $id", [
1568 'user' => $user->getName(),
1569 'creator' => $creator->getName(),
1572 $state[
'secondary'][$id] =
false;
1573 $state[
'continueRequests'] =
$res->neededRequests;
1574 $session->setSecret(
'AuthManager::accountCreationState', $state );
1577 throw new \DomainException(
1578 get_class( $provider ) .
"::{$func}() returned $res->status." .
1579 ' Secondary providers are not allowed to fail account creation, that' .
1580 ' should have been done via testForAccountCreation().'
1584 throw new \DomainException(
1585 get_class( $provider ) .
"::{$func}() returned $res->status"
1591 $id = $user->getId();
1592 $name = $user->getName();
1595 $ret->loginRequest = $req;
1596 $this->createdAccountAuthenticationRequests[] = $req;
1598 $this->logger->info( __METHOD__ .
': Account creation succeeded for {user}', [
1599 'user' => $user->getName(),
1600 'creator' => $creator->getName(),
1604 $session->remove(
'AuthManager::accountCreationState' );
1607 }
catch ( \Exception $ex ) {
1608 $session->remove(
'AuthManager::accountCreationState' );
1632 if (
$source !== self::AUTOCREATE_SOURCE_SESSION &&
1633 $source !== self::AUTOCREATE_SOURCE_MAINT &&
1636 throw new \InvalidArgumentException(
"Unknown auto-creation source: $source" );
1642 $localUserIdentity = $this->userIdentityLookup->getUserIdentityByName( $username );
1643 $localId = ( $localUserIdentity && $localUserIdentity->getId() )
1644 ? $localUserIdentity->getId()
1646 $flags = User::READ_NORMAL;
1653 $this->loadBalancer->getReaderIndex() !== 0
1655 $localUserIdentity = $this->userIdentityLookup->getUserIdentityByName(
1657 UserIdentityLookup::READ_LATEST
1659 $localId = ( $localUserIdentity && $localUserIdentity->getId() )
1660 ? $localUserIdentity->getId()
1662 $flags = User::READ_LATEST;
1667 $this->logger->debug( __METHOD__ .
': {username} already exists locally', [
1668 'username' => $username,
1670 $user->
setId( $localId );
1675 return Status::newGood()->warning(
'userexists' );
1679 if ( $this->readOnlyMode->isReadOnly() ) {
1680 $reason = $this->readOnlyMode->getReason();
1681 $this->logger->debug( __METHOD__ .
': denied because of read only mode: {reason}', [
1682 'username' => $username,
1683 'reason' => $reason,
1687 return Status::newFatal(
wfMessage(
'readonlytext', $reason ) );
1692 $session = $this->request->getSession();
1693 if ( $session->get(
'AuthManager::AutoCreateBlacklist' ) ) {
1694 $this->logger->debug( __METHOD__ .
': blacklisted in session {sessionid}', [
1695 'username' => $username,
1696 'sessionid' => $session->getId(),
1700 $reason = $session->get(
'AuthManager::AutoCreateBlacklist' );
1702 return Status::wrap( $reason );
1704 return Status::newFatal( $reason );
1709 if ( !$this->userNameUtils->isCreatable( $username ) ) {
1710 $this->logger->debug( __METHOD__ .
': name "{username}" is not creatable', [
1711 'username' => $username,
1713 $session->set(
'AuthManager::AutoCreateBlacklist',
'noname' );
1716 return Status::newFatal(
'noname' );
1720 $anon = $this->userFactory->newAnonymous();
1721 if (
$source !== self::AUTOCREATE_SOURCE_MAINT &&
1722 !$anon->isAllowedAny(
'createaccount',
'autocreateaccount' )
1724 $this->logger->debug( __METHOD__ .
': IP lacks the ability to create or autocreate accounts', [
1725 'username' => $username,
1726 'clientip' => $anon->getName(),
1728 $session->set(
'AuthManager::AutoCreateBlacklist',
'authmanager-autocreate-noperm' );
1729 $session->persist();
1732 return Status::newFatal(
'authmanager-autocreate-noperm' );
1736 $cache = \ObjectCache::getLocalClusterInstance();
1737 $lock =
$cache->getScopedLock(
$cache->makeGlobalKey(
'account', md5( $username ) ) );
1739 $this->logger->debug( __METHOD__ .
': Could not acquire account creation lock', [
1740 'user' => $username,
1744 return Status::newFatal(
'usernameinprogress' );
1749 'flags' => User::READ_LATEST,
1755 foreach ( $providers as $provider ) {
1756 $status = $provider->testUserForCreation( $user,
$source, $options );
1757 if ( !$status->isGood() ) {
1758 $ret = Status::wrap( $status );
1759 $this->logger->debug( __METHOD__ .
': Provider denied creation of {username}: {reason}', [
1760 'username' => $username,
1761 'reason' => $ret->getWikiText(
null,
null,
'en' ),
1763 $session->set(
'AuthManager::AutoCreateBlacklist', $status );
1770 $backoffKey =
$cache->makeKey(
'AuthManager',
'autocreate-failed', md5( $username ) );
1771 if (
$cache->get( $backoffKey ) ) {
1772 $this->logger->debug( __METHOD__ .
': {username} denied by prior creation attempt failures', [
1773 'username' => $username,
1777 return Status::newFatal(
'authmanager-autocreate-exception' );
1781 $from = $_SERVER[
'REQUEST_URI'] ??
'CLI';
1782 $this->logger->info( __METHOD__ .
': creating new user ({username}) - from: {from}', [
1783 'username' => $username,
1788 $trxProfilerSilencedScope = \Profiler::instance()->getTransactionProfiler()->silenceForScope();
1791 if ( !$status->isOK() ) {
1794 if ( $user->
getId() ) {
1795 $this->logger->info( __METHOD__ .
': {username} already exists locally (race)', [
1796 'username' => $username,
1801 $status = Status::newGood()->warning(
'userexists' );
1803 $this->logger->error( __METHOD__ .
': {username} failed with message {msg}', [
1804 'username' => $username,
1805 'msg' => $status->getWikiText(
null,
null,
'en' )
1812 }
catch ( \Exception $ex ) {
1813 $this->logger->error( __METHOD__ .
': {username} failed with exception {exception}', [
1814 'username' => $username,
1818 $cache->set( $backoffKey, 1, 600 );
1832 \DeferredUpdates::addUpdate( \SiteStatsUpdate::factory( [
'users' => 1 ] ) );
1834 \DeferredUpdates::addCallableUpdate(
function () use ( $user ) {
1835 $this->watchlistManager->addWatchIgnoringRights( $user, $user->
getUserPage() );
1839 if ( $this->config->get(
'NewUserLog' ) && $log ) {
1840 $logEntry = new \ManualLogEntry(
'newusers',
'autocreate' );
1841 $logEntry->setPerformer( $user );
1843 $logEntry->setComment(
'' );
1844 $logEntry->setParameters( [
1845 '4::userid' => $user->
getId(),
1847 $logEntry->insert();
1850 ScopedCallback::consume( $trxProfilerSilencedScope );
1856 return Status::newGood();
1888 $session = $this->request->getSession();
1889 $session->remove(
'AuthManager::accountLinkState' );
1893 throw new \LogicException(
'Account linking is not possible' );
1896 if ( $user->
getId() === 0 ) {
1897 if ( !$this->userNameUtils->isUsable( $user->
getName() ) ) {
1904 foreach ( $reqs as $req ) {
1905 $req->username = $user->
getName();
1906 $req->returnToUrl = $returnToUrl;
1912 foreach ( $providers as $id => $provider ) {
1913 $status = $provider->testForAccountLink( $user );
1914 if ( !$status->isGood() ) {
1915 $this->logger->debug( __METHOD__ .
": Account linking pre-check failed by $id", [
1919 Status::wrap( $status )->getMessage()
1927 'username' => $user->
getName(),
1928 'userid' => $user->
getId(),
1929 'returnToUrl' => $returnToUrl,
1931 'continueRequests' => [],
1935 foreach ( $providers as $id => $provider ) {
1940 $res = $provider->beginPrimaryAccountLink( $user, $reqs );
1941 switch (
$res->status ) {
1943 $this->logger->info(
"Account linked to {user} by $id", [
1950 $this->logger->debug( __METHOD__ .
": Account linking failed by $id", [
1962 $this->logger->debug( __METHOD__ .
": Account linking $res->status by $id", [
1966 $state[
'primary'] = $id;
1967 $state[
'continueRequests'] =
$res->neededRequests;
1968 $session->setSecret(
'AuthManager::accountLinkState', $state );
1969 $session->persist();
1974 throw new \DomainException(
1975 get_class( $provider ) .
"::beginPrimaryAccountLink() returned $res->status"
1981 $this->logger->debug( __METHOD__ .
': Account linking failed because no provider accepted', [
1985 wfMessage(
'authmanager-link-no-primary' )
1997 $session = $this->request->getSession();
2001 $session->remove(
'AuthManager::accountLinkState' );
2002 throw new \LogicException(
'Account linking is not possible' );
2005 $state = $session->getSecret(
'AuthManager::accountLinkState' );
2006 if ( !is_array( $state ) ) {
2008 wfMessage(
'authmanager-link-not-in-progress' )
2011 $state[
'continueRequests'] = [];
2015 $user = $this->userFactory->newFromName(
2016 (
string)$state[
'username'],
2017 UserFactory::RIGOR_USABLE
2019 if ( !is_object( $user ) ) {
2020 $session->remove(
'AuthManager::accountLinkState' );
2023 if ( $user->getId() !== $state[
'userid'] ) {
2024 throw new \UnexpectedValueException(
2025 "User \"{$state['username']}\" is valid, but " .
2026 "ID {$user->getId()} !== {$state['userid']}!"
2030 foreach ( $reqs as $req ) {
2031 $req->username = $state[
'username'];
2032 $req->returnToUrl = $state[
'returnToUrl'];
2042 wfMessage(
'authmanager-link-not-in-progress' )
2045 $session->remove(
'AuthManager::accountLinkState' );
2049 $id = $provider->getUniqueId();
2050 $res = $provider->continuePrimaryAccountLink( $user, $reqs );
2051 switch (
$res->status ) {
2053 $this->logger->info(
"Account linked to {user} by $id", [
2054 'user' => $user->getName(),
2057 $session->remove(
'AuthManager::accountLinkState' );
2060 $this->logger->debug( __METHOD__ .
": Account linking failed by $id", [
2061 'user' => $user->getName(),
2064 $session->remove(
'AuthManager::accountLinkState' );
2068 $this->logger->debug( __METHOD__ .
": Account linking $res->status by $id", [
2069 'user' => $user->getName(),
2071 $this->
fillRequests(
$res->neededRequests, self::ACTION_LINK, $user->getName() );
2072 $state[
'continueRequests'] =
$res->neededRequests;
2073 $session->setSecret(
'AuthManager::accountLinkState', $state );
2076 throw new \DomainException(
2077 get_class( $provider ) .
"::continuePrimaryAccountLink() returned $res->status"
2080 }
catch ( \Exception $ex ) {
2081 $session->remove(
'AuthManager::accountLinkState' );
2112 $providerAction = $action;
2115 switch ( $action ) {
2124 $state = $this->request->getSession()->getSecret(
'AuthManager::authnState' );
2125 return is_array( $state ) ? $state[
'continueRequests'] : [];
2128 $state = $this->request->getSession()->getSecret(
'AuthManager::accountCreationState' );
2129 return is_array( $state ) ? $state[
'continueRequests'] : [];
2153 $state = $this->request->getSession()->getSecret(
'AuthManager::accountLinkState' );
2154 return is_array( $state ) ? $state[
'continueRequests'] : [];
2164 throw new \DomainException( __METHOD__ .
": Invalid action \"$action\"" );
2181 $providerAction, array $options, array $providers,
UserIdentity $user =
null
2183 $user = $user ?: \RequestContext::getMain()->getUser();
2184 $options[
'username'] = $user->isRegistered() ? $user->getName() :
null;
2188 foreach ( $providers as $provider ) {
2190 foreach ( $provider->getAuthenticationRequests( $providerAction, $options ) as $req ) {
2194 if ( $isPrimary && $req->required ) {
2199 !isset( $reqs[$id] )
2209 switch ( $providerAction ) {
2217 if ( $options[
'username'] !==
null ) {
2219 $options[
'username'] =
null;
2225 $this->
fillRequests( $reqs, $providerAction, $options[
'username'],
true );
2228 if ( $providerAction === self::ACTION_CHANGE || $providerAction === self::ACTION_REMOVE ) {
2229 $reqs = array_filter( $reqs,
function ( $req ) {
2234 return array_values( $reqs );
2244 private function fillRequests( array &$reqs, $action, $username, $forceAction =
false ) {
2245 foreach ( $reqs as $req ) {
2246 if ( !$req->action || $forceAction ) {
2247 $req->action = $action;
2249 if ( $req->username ===
null ) {
2250 $req->username = $username;
2261 public function userExists( $username, $flags = User::READ_NORMAL ) {
2263 if ( $provider->testUserExists( $username, $flags ) ) {
2285 foreach ( $providers as $provider ) {
2286 if ( !$provider->providerAllowsPropertyChange( $property ) ) {
2303 if ( isset( $this->allAuthenticationProviders[$id] ) ) {
2304 return $this->allAuthenticationProviders[$id];
2309 if ( isset( $providers[$id] ) ) {
2310 return $providers[$id];
2313 if ( isset( $providers[$id] ) ) {
2314 return $providers[$id];
2317 if ( isset( $providers[$id] ) ) {
2318 return $providers[$id];
2337 $session = $this->request->getSession();
2338 $arr = $session->getSecret(
'authData' );
2339 if ( !is_array( $arr ) ) {
2343 $session->setSecret(
'authData', $arr );
2354 $arr = $this->request->getSession()->getSecret(
'authData' );
2355 if ( is_array( $arr ) && array_key_exists( $key, $arr ) ) {
2368 $session = $this->request->getSession();
2369 if ( $key ===
null ) {
2370 $session->remove(
'authData' );
2372 $arr = $session->getSecret(
'authData' );
2373 if ( is_array( $arr ) && array_key_exists( $key, $arr ) ) {
2374 unset( $arr[$key] );
2375 $session->setSecret(
'authData', $arr );
2388 foreach ( $specs as &$spec ) {
2389 $spec = [
'sort2' => $i++ ] + $spec + [
'sort' => 0 ];
2393 usort( $specs,
static function ( $a, $b ) {
2394 return $a[
'sort'] <=> $b[
'sort']
2395 ?: $a[
'sort2'] <=> $b[
'sort2'];
2399 foreach ( $specs as $spec ) {
2401 $provider = $this->objectFactory->createObject( $spec, [
'assertClass' => $class ] );
2402 $provider->init( $this->logger, $this, $this->
getHookContainer(), $this->config, $this->userNameUtils );
2403 $id = $provider->getUniqueId();
2404 if ( isset( $this->allAuthenticationProviders[$id] ) ) {
2405 throw new \RuntimeException(
2406 "Duplicate specifications for id $id (classes " .
2407 get_class( $provider ) .
' and ' .
2408 get_class( $this->allAuthenticationProviders[$id] ) .
')'
2411 $this->allAuthenticationProviders[$id] = $provider;
2412 $ret[$id] = $provider;
2421 return $this->config->get(
'AuthManagerConfig' ) ?: $this->config->get(
'AuthManagerAutoConfig' );
2429 if ( $this->preAuthenticationProviders ===
null ) {
2432 PreAuthenticationProvider::class, $conf[
'preauth']
2443 if ( $this->primaryAuthenticationProviders ===
null ) {
2446 PrimaryAuthenticationProvider::class, $conf[
'primaryauth']
2457 if ( $this->secondaryAuthenticationProviders ===
null ) {
2460 SecondaryAuthenticationProvider::class, $conf[
'secondaryauth']
2472 $session = $this->request->getSession();
2473 $delay = $session->delaySave();
2475 $session->resetId();
2476 $session->resetAllTokens();
2477 if ( $session->canSetUser() ) {
2478 $session->setUser( $user );
2480 if ( $remember !==
null ) {
2481 $session->setRememberUser( $remember );
2483 $session->set(
'AuthManager:lastAuthId', $user->getId() );
2484 $session->set(
'AuthManager:lastAuthTimestamp', time() );
2485 $session->persist();
2487 \Wikimedia\ScopedCallback::consume( $delay );
2500 $this->userOptionsManager->setOption(
2503 $this->languageConverterFactory->getLanguageConverter(
$lang )->getPreferredVariant()
2506 $contLangConverter = $this->languageConverterFactory->getLanguageConverter( $this->contentLanguage );
2507 if ( $contLangConverter->hasVariants() ) {
2508 $this->userOptionsManager->setOption(
2511 $contLangConverter->getPreferredVariant()
2532 foreach ( $providers as $provider ) {
2533 $provider->$method( ...
$args );
wfMessage( $key,... $params)
This is the function for getting translated interface messages.
Internationalisation code See https://www.mediawiki.org/wiki/Special:MyLanguage/Localisation for more...
A service class for fetching the wiki's current read-only mode.
Parent class for all special pages.
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.
loadFromId( $flags=self::READ_NORMAL)
Load user table data, given mId has already been set.
setId( $v)
Set the user and reload all fields according to a given ID.
getId( $wikiId=self::LOCAL)
Get the user's ID.
getUserPage()
Get this user's personal page title.
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.
if(!isset( $args[0])) $lang