28 use Psr\Log\LoggerAwareInterface;
29 use Psr\Log\LoggerInterface;
147 if ( self::$instance ===
null ) {
148 self::$instance =
new self(
187 $this->logger->warning(
"Overriding AuthManager primary authn because $why" );
189 if ( $this->primaryAuthenticationProviders !==
null ) {
190 $this->logger->warning(
191 'PrimaryAuthenticationProviders have already been accessed! I hope nothing breaks.'
194 $this->allAuthenticationProviders = array_diff_key(
195 $this->allAuthenticationProviders,
196 $this->primaryAuthenticationProviders
198 $session = $this->
request->getSession();
199 $session->remove(
'AuthManager::authnState' );
200 $session->remove(
'AuthManager::accountCreationState' );
201 $session->remove(
'AuthManager::accountLinkState' );
202 $this->createdAccountAuthenticationRequests = [];
205 $this->primaryAuthenticationProviders = [];
206 foreach ( $providers
as $provider ) {
208 throw new \RuntimeException(
209 'Expected instance of MediaWiki\\Auth\\PrimaryAuthenticationProvider, got ' .
210 get_class( $provider )
213 $provider->setLogger( $this->logger );
214 $provider->setManager( $this );
215 $provider->setConfig( $this->config );
216 $id = $provider->getUniqueId();
217 if ( isset( $this->allAuthenticationProviders[$id] ) ) {
218 throw new \RuntimeException(
219 "Duplicate specifications for id $id (classes " .
220 get_class( $provider ) .
' and ' .
221 get_class( $this->allAuthenticationProviders[$id] ) .
')'
224 $this->allAuthenticationProviders[$id] = $provider;
225 $this->primaryAuthenticationProviders[$id] = $provider;
242 return call_user_func_array( [ $wgAuth, $method ],
$params );
262 return $this->
request->getSession()->canSetUser();
284 $session = $this->
request->getSession();
285 if ( !$session->canSetUser() ) {
287 $session->remove(
'AuthManager::authnState' );
288 throw new \LogicException(
'Authentication is not possible now' );
291 $guessUserName =
null;
292 foreach ( $reqs
as $req ) {
293 $req->returnToUrl = $returnToUrl;
295 if (
$req->username !==
null &&
$req->username !==
'' ) {
296 if ( $guessUserName ===
null ) {
297 $guessUserName =
$req->username;
298 } elseif ( $guessUserName !==
$req->username ) {
299 $guessUserName =
null;
311 if ( !in_array(
$req, $this->createdAccountAuthenticationRequests,
true ) ) {
312 throw new \LogicException(
313 'CreatedAccountAuthenticationRequests are only valid on ' .
314 'the same AuthManager that created the account'
321 throw new \UnexpectedValueException(
322 "CreatedAccountAuthenticationRequest had invalid username \"{$req->username}\""
324 } elseif (
$user->getId() !=
$req->id ) {
325 throw new \UnexpectedValueException(
326 "ID for \"{$req->username}\" was {$user->getId()}, expected {$req->id}"
331 $this->logger->info(
'Logging in {user} after account creation', [
332 'user' =>
$user->getName(),
337 $session->remove(
'AuthManager::authnState' );
345 $status = $provider->testForAuthentication( $reqs );
347 $this->logger->debug(
'Login failed in pre-authentication by ' . $provider->getUniqueId() );
354 \Hooks::run(
'AuthManagerLoginAuthenticateAudit', [
$ret,
null, $guessUserName ] );
361 'returnToUrl' => $returnToUrl,
362 'guessUserName' => $guessUserName,
364 'primaryResponse' =>
null,
367 'continueRequests' => [],
375 $state[
'maybeLink'] =
$req->maybeLink;
378 $session = $this->
request->getSession();
379 $session->setSecret(
'AuthManager::authnState', $state );
408 $session = $this->
request->getSession();
410 if ( !$session->canSetUser() ) {
413 throw new \LogicException(
'Authentication is not possible now' );
417 $state = $session->getSecret(
'AuthManager::authnState' );
418 if ( !is_array( $state ) ) {
420 wfMessage(
'authmanager-authn-not-in-progress' )
423 $state[
'continueRequests'] = [];
425 $guessUserName = $state[
'guessUserName'];
427 foreach ( $reqs
as $req ) {
428 $req->returnToUrl = $state[
'returnToUrl'];
433 if ( $state[
'primary'] ===
null ) {
436 $guessUserName =
null;
437 foreach ( $reqs
as $req ) {
438 if (
$req->username !==
null &&
$req->username !==
'' ) {
439 if ( $guessUserName ===
null ) {
440 $guessUserName =
$req->username;
441 } elseif ( $guessUserName !==
$req->username ) {
442 $guessUserName =
null;
447 $state[
'guessUserName'] = $guessUserName;
449 $state[
'reqs'] = $reqs;
452 $res = $provider->beginPrimaryAuthentication( $reqs );
453 switch (
$res->status ) {
455 $state[
'primary'] = $id;
456 $state[
'primaryResponse'] =
$res;
457 $this->logger->debug(
"Primary login with $id succeeded" );
460 $this->logger->debug(
"Login failed in primary authentication by $id" );
461 if (
$res->createRequest || $state[
'maybeLink'] ) {
463 $res->createRequest, $state[
'maybeLink']
469 $session->remove(
'AuthManager::authnState' );
470 \Hooks::run(
'AuthManagerLoginAuthenticateAudit', [
$res,
null, $guessUserName ] );
477 $this->logger->debug(
"Primary login with $id returned $res->status" );
478 $this->
fillRequests(
$res->neededRequests, self::ACTION_LOGIN, $guessUserName );
479 $state[
'primary'] = $id;
480 $state[
'continueRequests'] =
$res->neededRequests;
481 $session->setSecret(
'AuthManager::authnState', $state );
486 throw new \DomainException(
487 get_class( $provider ) .
"::beginPrimaryAuthentication() returned $res->status"
492 if ( $state[
'primary'] ===
null ) {
493 $this->logger->debug(
'Login failed in primary authentication because no provider accepted' );
495 wfMessage(
'authmanager-authn-no-primary' )
500 $session->remove(
'AuthManager::authnState' );
503 } elseif ( $state[
'primaryResponse'] ===
null ) {
509 wfMessage(
'authmanager-authn-not-in-progress' )
514 $session->remove(
'AuthManager::authnState' );
518 $id = $provider->getUniqueId();
519 $res = $provider->continuePrimaryAuthentication( $reqs );
520 switch (
$res->status ) {
522 $state[
'primaryResponse'] =
$res;
523 $this->logger->debug(
"Primary login with $id succeeded" );
526 $this->logger->debug(
"Login failed in primary authentication by $id" );
527 if (
$res->createRequest || $state[
'maybeLink'] ) {
529 $res->createRequest, $state[
'maybeLink']
535 $session->remove(
'AuthManager::authnState' );
536 \Hooks::run(
'AuthManagerLoginAuthenticateAudit', [
$res,
null, $guessUserName ] );
540 $this->logger->debug(
"Primary login with $id returned $res->status" );
541 $this->
fillRequests(
$res->neededRequests, self::ACTION_LOGIN, $guessUserName );
542 $state[
'continueRequests'] =
$res->neededRequests;
543 $session->setSecret(
'AuthManager::authnState', $state );
546 throw new \DomainException(
547 get_class( $provider ) .
"::continuePrimaryAuthentication() returned $res->status"
552 $res = $state[
'primaryResponse'];
553 if (
$res->username ===
null ) {
559 wfMessage(
'authmanager-authn-not-in-progress' )
564 $session->remove(
'AuthManager::authnState' );
574 $state[
'maybeLink'][
$res->linkRequest->getUniqueId()] =
$res->linkRequest;
575 $msg =
'authmanager-authn-no-local-user-link';
577 $msg =
'authmanager-authn-no-local-user';
579 $this->logger->debug(
580 "Primary login with {$provider->getUniqueId()} succeeded, but returned no user"
588 if (
$res->createRequest || $state[
'maybeLink'] ) {
590 $res->createRequest, $state[
'maybeLink']
592 $ret->neededRequests[] =
$ret->createRequest;
594 $this->
fillRequests(
$ret->neededRequests, self::ACTION_LOGIN,
null,
true );
595 $session->setSecret(
'AuthManager::authnState', [
598 'primaryResponse' =>
null,
600 'continueRequests' =>
$ret->neededRequests,
611 throw new \DomainException(
612 get_class( $provider ) .
" returned an invalid username: {$res->username}"
615 if (
$user->getId() === 0 ) {
617 $this->logger->info(
'Auto-creating {user} on login', [
618 'user' =>
$user->getName(),
626 $session->remove(
'AuthManager::authnState' );
634 $beginReqs = $state[
'reqs'];
637 if ( !isset( $state[
'secondary'][$id] ) ) {
641 $func =
'beginSecondaryAuthentication';
642 $res = $provider->beginSecondaryAuthentication(
$user, $beginReqs );
643 } elseif ( !$state[
'secondary'][$id] ) {
644 $func =
'continueSecondaryAuthentication';
645 $res = $provider->continueSecondaryAuthentication(
$user, $reqs );
649 switch (
$res->status ) {
651 $this->logger->debug(
"Secondary login with $id succeeded" );
654 $state[
'secondary'][$id] =
true;
657 $this->logger->debug(
"Login failed in secondary authentication by $id" );
659 $session->remove(
'AuthManager::authnState' );
664 $this->logger->debug(
"Secondary login with $id returned " .
$res->status );
665 $this->
fillRequests( $res->neededRequests, self::ACTION_LOGIN,
$user->getName() );
666 $state[
'secondary'][$id] =
false;
667 $state[
'continueRequests'] =
$res->neededRequests;
668 $session->setSecret(
'AuthManager::authnState', $state );
673 throw new \DomainException(
674 get_class( $provider ) .
"::{$func}() returned $res->status"
683 $this->logger->info(
'Login for {user} succeeded', [
684 'user' =>
$user->getName(),
693 $session->remove(
'AuthManager::authnState' );
697 }
catch ( \Exception $ex ) {
698 $session->remove(
'AuthManager::authnState' );
717 $this->logger->debug( __METHOD__ .
": Checking $operation" );
719 $session = $this->
request->getSession();
720 $aId = $session->getUser()->getId();
724 $this->logger->info( __METHOD__ .
": Not logged in! $operation is $status" );
728 if ( $session->canSetUser() ) {
729 $id = $session->get(
'AuthManager:lastAuthId' );
730 $last = $session->get(
'AuthManager:lastAuthTimestamp' );
731 if ( $id !== $aId ||
$last ===
null ) {
732 $timeSinceLogin = PHP_INT_MAX;
734 $timeSinceLogin = max( 0, time() -
$last );
737 $thresholds = $this->config->get(
'ReauthenticateTime' );
738 if ( isset( $thresholds[$operation] ) ) {
739 $threshold = $thresholds[$operation];
740 } elseif ( isset( $thresholds[
'default'] ) ) {
741 $threshold = $thresholds[
'default'];
743 throw new \UnexpectedValueException(
'$wgReauthenticateTime lacks a default' );
746 if ( $threshold >= 0 && $timeSinceLogin > $threshold ) {
750 $timeSinceLogin = -1;
752 $pass = $this->config->get(
'AllowSecuritySensitiveOperationIfCannotReauthenticate' );
753 if ( isset( $pass[$operation] ) ) {
755 } elseif ( isset( $pass[
'default'] ) ) {
758 throw new \UnexpectedValueException(
759 '$wgAllowSecuritySensitiveOperationIfCannotReauthenticate lacks a default'
765 &
$status, $operation, $session, $timeSinceLogin
773 $this->logger->info( __METHOD__ .
": $operation is $status" );
789 if ( $provider->testUserCanAuthenticate(
$username ) ) {
813 $normalized = $provider->providerNormalizeUsername(
$username );
814 if ( $normalized !==
null ) {
815 $ret[$normalized] =
true;
818 return array_keys(
$ret );
836 $this->logger->info(
'Revoking access for {user}', [
855 foreach ( $providers
as $provider ) {
856 $status = $provider->providerAllowsAuthenticationDataChange(
$req, $checkData );
860 $any = $any ||
$status->value !==
'ignored';
864 $status->warning(
'authmanager-change-not-supported' );
885 $this->logger->info(
'Changing authentication data for {user} class {what}', [
886 'user' => is_string(
$req->username ) ?
$req->username :
'<no name>',
887 'what' => get_class(
$req ),
910 switch ( $provider->accountCreationType() ) {
933 'flags' => User::READ_NORMAL,
947 if ( !is_object(
$user ) ) {
951 if (
$user->getId() !== 0 ) {
960 foreach ( $providers
as $provider ) {
983 ->getUserPermissionsErrors(
'createaccount', $creator,
'secure' );
986 foreach ( $permErrors
as $args ) {
996 $block->mReason ?:
wfMessage(
'blockednoreason' )->text(),
1001 $errorMessage =
'cantcreateaccount-range-text';
1002 $errorParams[] = $this->
getRequest()->getIP();
1004 $errorMessage =
'cantcreateaccount-text';
1038 $session = $this->
request->getSession();
1041 $session->remove(
'AuthManager::accountCreationState' );
1042 throw new \LogicException(
'Account creation is not possible' );
1047 }
catch ( \UnexpectedValueException $ex ) {
1051 $this->logger->debug( __METHOD__ .
': No username provided' );
1058 $this->logger->debug( __METHOD__ .
': {creator} cannot create users: {reason}', [
1060 'creator' => $creator->
getName(),
1061 'reason' =>
$status->getWikiText(
null,
null,
'en' )
1070 $this->logger->debug( __METHOD__ .
': {user} cannot be created: {reason}', [
1072 'creator' => $creator->
getName(),
1073 'reason' =>
$status->getWikiText(
null,
null,
'en' )
1079 foreach ( $reqs
as $req ) {
1081 $req->returnToUrl = $returnToUrl;
1086 $session->remove(
'AuthManager::accountCreationState' );
1087 $this->logger->debug( __METHOD__ .
': UserData is invalid: {reason}', [
1088 'user' =>
$user->getName(),
1089 'creator' => $creator->
getName(),
1090 'reason' =>
$status->getWikiText(
null,
null,
'en' ),
1102 'creatorid' => $creator->
getId(),
1103 'creatorname' => $creator->
getName(),
1105 'returnToUrl' => $returnToUrl,
1107 'primaryResponse' =>
null,
1109 'continueRequests' => [],
1111 'ranPreTests' =>
false,
1119 $state[
'maybeLink'] =
$req->maybeLink;
1121 if (
$req->createRequest ) {
1122 $reqs[] =
$req->createRequest;
1123 $state[
'reqs'][] =
$req->createRequest;
1127 $session->setSecret(
'AuthManager::accountCreationState', $state );
1128 $session->persist();
1139 $session = $this->
request->getSession();
1143 $session->remove(
'AuthManager::accountCreationState' );
1144 throw new \LogicException(
'Account creation is not possible' );
1147 $state = $session->getSecret(
'AuthManager::accountCreationState' );
1148 if ( !is_array( $state ) ) {
1150 wfMessage(
'authmanager-create-not-in-progress' )
1153 $state[
'continueRequests'] = [];
1158 if ( !is_object(
$user ) ) {
1159 $session->remove(
'AuthManager::accountCreationState' );
1160 $this->logger->debug( __METHOD__ .
': Invalid username', [
1161 'user' => $state[
'username'],
1166 if ( $state[
'creatorid'] ) {
1169 $creator =
new User;
1170 $creator->setName( $state[
'creatorname'] );
1175 $lock =
$cache->getScopedLock(
$cache->makeGlobalKey(
'account', md5(
$user->getName() ) ) );
1179 $this->logger->debug( __METHOD__ .
': Could not acquire account creation lock', [
1180 'user' =>
$user->getName(),
1181 'creator' => $creator->getName(),
1189 $this->logger->debug( __METHOD__ .
': {creator} cannot create users: {reason}', [
1190 'user' =>
$user->getName(),
1191 'creator' => $creator->getName(),
1192 'reason' =>
$status->getWikiText(
null,
null,
'en' )
1196 $session->remove(
'AuthManager::accountCreationState' );
1203 if ( $state[
'userid'] === 0 ) {
1204 if (
$user->getId() != 0 ) {
1205 $this->logger->debug( __METHOD__ .
': User exists locally', [
1206 'user' =>
$user->getName(),
1207 'creator' => $creator->getName(),
1211 $session->remove(
'AuthManager::accountCreationState' );
1215 if (
$user->getId() == 0 ) {
1216 $this->logger->debug( __METHOD__ .
': User does not exist locally when it should', [
1217 'user' =>
$user->getName(),
1218 'creator' => $creator->getName(),
1219 'expected_id' => $state[
'userid'],
1221 throw new \UnexpectedValueException(
1222 "User \"{$state['username']}\" should exist now, but doesn't!"
1225 if (
$user->getId() != $state[
'userid'] ) {
1226 $this->logger->debug( __METHOD__ .
': User ID/name mismatch', [
1227 'user' =>
$user->getName(),
1228 'creator' => $creator->getName(),
1229 'expected_id' => $state[
'userid'],
1230 'actual_id' =>
$user->getId(),
1232 throw new \UnexpectedValueException(
1233 "User \"{$state['username']}\" exists, but " .
1234 "ID {$user->getId()} != {$state['userid']}!"
1238 foreach ( $state[
'reqs']
as $req ) {
1244 $this->logger->debug( __METHOD__ .
': UserData is invalid: {reason}', [
1245 'user' =>
$user->getName(),
1246 'creator' => $creator->getName(),
1247 'reason' =>
$status->getWikiText(
null,
null,
'en' ),
1251 $session->remove(
'AuthManager::accountCreationState' );
1257 foreach ( $reqs
as $req ) {
1258 $req->returnToUrl = $state[
'returnToUrl'];
1259 $req->username = $state[
'username'];
1263 if ( !$state[
'ranPreTests'] ) {
1267 foreach ( $providers
as $id => $provider ) {
1268 $status = $provider->testForAccountCreation(
$user, $creator, $reqs );
1270 $this->logger->debug( __METHOD__ .
": Fail in pre-authentication by $id", [
1271 'user' =>
$user->getName(),
1272 'creator' => $creator->getName(),
1278 $session->remove(
'AuthManager::accountCreationState' );
1283 $state[
'ranPreTests'] =
true;
1288 if ( $state[
'primary'] ===
null ) {
1294 $res = $provider->beginPrimaryAccountCreation(
$user, $creator, $reqs );
1295 switch (
$res->status ) {
1297 $this->logger->debug( __METHOD__ .
": Primary creation passed by $id", [
1298 'user' =>
$user->getName(),
1299 'creator' => $creator->getName(),
1301 $state[
'primary'] = $id;
1302 $state[
'primaryResponse'] =
$res;
1305 $this->logger->debug( __METHOD__ .
": Primary creation failed by $id", [
1306 'user' =>
$user->getName(),
1307 'creator' => $creator->getName(),
1310 $session->remove(
'AuthManager::accountCreationState' );
1317 $this->logger->debug( __METHOD__ .
": Primary creation $res->status by $id", [
1318 'user' =>
$user->getName(),
1319 'creator' => $creator->getName(),
1322 $state[
'primary'] = $id;
1323 $state[
'continueRequests'] =
$res->neededRequests;
1324 $session->setSecret(
'AuthManager::accountCreationState', $state );
1329 throw new \DomainException(
1330 get_class( $provider ) .
"::beginPrimaryAccountCreation() returned $res->status"
1335 if ( $state[
'primary'] ===
null ) {
1336 $this->logger->debug( __METHOD__ .
': Primary creation failed because no provider accepted', [
1337 'user' =>
$user->getName(),
1338 'creator' => $creator->getName(),
1341 wfMessage(
'authmanager-create-no-primary' )
1344 $session->remove(
'AuthManager::accountCreationState' );
1347 } elseif ( $state[
'primaryResponse'] ===
null ) {
1353 wfMessage(
'authmanager-create-not-in-progress' )
1356 $session->remove(
'AuthManager::accountCreationState' );
1360 $id = $provider->getUniqueId();
1361 $res = $provider->continuePrimaryAccountCreation(
$user, $creator, $reqs );
1362 switch (
$res->status ) {
1364 $this->logger->debug( __METHOD__ .
": Primary creation passed by $id", [
1365 'user' =>
$user->getName(),
1366 'creator' => $creator->getName(),
1368 $state[
'primaryResponse'] =
$res;
1371 $this->logger->debug( __METHOD__ .
": Primary creation failed by $id", [
1372 'user' =>
$user->getName(),
1373 'creator' => $creator->getName(),
1376 $session->remove(
'AuthManager::accountCreationState' );
1380 $this->logger->debug( __METHOD__ .
": Primary creation $res->status by $id", [
1381 'user' =>
$user->getName(),
1382 'creator' => $creator->getName(),
1385 $state[
'continueRequests'] =
$res->neededRequests;
1386 $session->setSecret(
'AuthManager::accountCreationState', $state );
1389 throw new \DomainException(
1390 get_class( $provider ) .
"::continuePrimaryAccountCreation() returned $res->status"
1398 if ( $state[
'userid'] === 0 ) {
1399 $this->logger->info(
'Creating user {user} during account creation', [
1400 'user' =>
$user->getName(),
1401 'creator' => $creator->getName(),
1408 $session->remove(
'AuthManager::accountCreationState' );
1414 $user->saveSettings();
1415 $state[
'userid'] =
$user->getId();
1424 $logSubtype = $provider->finishAccountCreation(
$user, $creator, $state[
'primaryResponse'] );
1427 if ( $this->config->get(
'NewUserLog' ) ) {
1428 $isAnon = $creator->isAnon();
1429 $logEntry = new \ManualLogEntry(
1431 $logSubtype ?: ( $isAnon ?
'create' :
'create2' )
1433 $logEntry->setPerformer( $isAnon ?
$user : $creator );
1434 $logEntry->setTarget(
$user->getUserPage() );
1439 $logEntry->setComment(
$req ?
$req->reason :
'' );
1440 $logEntry->setParameters( [
1441 '4::userid' =>
$user->getId(),
1443 $logid = $logEntry->insert();
1444 $logEntry->publish( $logid );
1450 $beginReqs = $state[
'reqs'];
1453 if ( !isset( $state[
'secondary'][$id] ) ) {
1457 $func =
'beginSecondaryAccountCreation';
1458 $res = $provider->beginSecondaryAccountCreation(
$user, $creator, $beginReqs );
1459 } elseif ( !$state[
'secondary'][$id] ) {
1460 $func =
'continueSecondaryAccountCreation';
1461 $res = $provider->continueSecondaryAccountCreation(
$user, $creator, $reqs );
1465 switch (
$res->status ) {
1467 $this->logger->debug( __METHOD__ .
": Secondary creation passed by $id", [
1468 'user' =>
$user->getName(),
1469 'creator' => $creator->getName(),
1473 $state[
'secondary'][$id] =
true;
1477 $this->logger->debug( __METHOD__ .
": Secondary creation $res->status by $id", [
1478 'user' =>
$user->getName(),
1479 'creator' => $creator->getName(),
1482 $state[
'secondary'][$id] =
false;
1483 $state[
'continueRequests'] =
$res->neededRequests;
1484 $session->setSecret(
'AuthManager::accountCreationState', $state );
1487 throw new \DomainException(
1488 get_class( $provider ) .
"::{$func}() returned $res->status." .
1489 ' Secondary providers are not allowed to fail account creation, that' .
1490 ' should have been done via testForAccountCreation().'
1494 throw new \DomainException(
1495 get_class( $provider ) .
"::{$func}() returned $res->status"
1501 $id =
$user->getId();
1506 $this->createdAccountAuthenticationRequests[] =
$req;
1508 $this->logger->info( __METHOD__ .
': Account creation succeeded for {user}', [
1509 'user' =>
$user->getName(),
1510 'creator' => $creator->getName(),
1514 $session->remove(
'AuthManager::accountCreationState' );
1517 }
catch ( \Exception $ex ) {
1518 $session->remove(
'AuthManager::accountCreationState' );
1539 if (
$source !== self::AUTOCREATE_SOURCE_SESSION &&
1542 throw new \InvalidArgumentException(
"Unknown auto-creation source: $source" );
1549 $flags = User::READ_NORMAL;
1554 if ( !$localId &&
wfGetLB()->getReaderIndex() != 0 ) {
1556 $flags = User::READ_LATEST;
1561 $this->logger->debug( __METHOD__ .
': {username} already exists locally', [
1564 $user->setId( $localId );
1570 $status->warning(
'userexists' );
1576 $this->logger->debug( __METHOD__ .
': denied by wfReadOnly(): {reason}', [
1581 $user->loadFromId();
1587 $session = $this->
request->getSession();
1588 if ( $session->get(
'AuthManager::AutoCreateBlacklist' ) ) {
1589 $this->logger->debug( __METHOD__ .
': blacklisted in session {sessionid}', [
1591 'sessionid' => $session->getId(),
1594 $user->loadFromId();
1595 $reason = $session->get(
'AuthManager::AutoCreateBlacklist' );
1605 $this->logger->debug( __METHOD__ .
': name "{username}" is not creatable', [
1608 $session->set(
'AuthManager::AutoCreateBlacklist',
'noname' );
1610 $user->loadFromId();
1616 if ( !$anon->isAllowedAny(
'createaccount',
'autocreateaccount' ) ) {
1617 $this->logger->debug( __METHOD__ .
': IP lacks the ability to create or autocreate accounts', [
1619 'ip' => $anon->getName(),
1621 $session->set(
'AuthManager::AutoCreateBlacklist',
'authmanager-autocreate-noperm' );
1622 $session->persist();
1624 $user->loadFromId();
1632 $this->logger->debug( __METHOD__ .
': Could not acquire account creation lock', [
1636 $user->loadFromId();
1642 'flags' => User::READ_LATEST,
1648 foreach ( $providers
as $provider ) {
1652 $this->logger->debug( __METHOD__ .
': Provider denied creation of {username}: {reason}', [
1654 'reason' =>
$ret->getWikiText(
null,
null,
'en' ),
1656 $session->set(
'AuthManager::AutoCreateBlacklist',
$status );
1658 $user->loadFromId();
1664 if (
$cache->get( $backoffKey ) ) {
1665 $this->logger->debug( __METHOD__ .
': {username} denied by prior creation attempt failures', [
1669 $user->loadFromId();
1674 $from = isset( $_SERVER[
'REQUEST_URI'] ) ? $_SERVER[
'REQUEST_URI'] :
'CLI';
1675 $this->logger->info( __METHOD__ .
': creating new user ({username}) - from: {from}', [
1682 $old = $trxProfiler->setSilenced(
true );
1688 if (
$user->getId() ) {
1689 $this->logger->info( __METHOD__ .
': {username} already exists locally (race)', [
1696 $status->warning(
'userexists' );
1698 $this->logger->error( __METHOD__ .
': {username} failed with message {msg}', [
1700 'msg' =>
$status->getWikiText(
null,
null,
'en' )
1703 $user->loadFromId();
1707 }
catch ( \Exception $ex ) {
1708 $trxProfiler->setSilenced( $old );
1709 $this->logger->error( __METHOD__ .
': {username} failed with exception {exception}', [
1714 $cache->set( $backoffKey, 1, 600 );
1726 $user->saveSettings();
1736 if ( $this->config->get(
'NewUserLog' ) ) {
1737 $logEntry = new \ManualLogEntry(
'newusers',
'autocreate' );
1738 $logEntry->setPerformer(
$user );
1739 $logEntry->setTarget(
$user->getUserPage() );
1740 $logEntry->setComment(
'' );
1741 $logEntry->setParameters( [
1742 '4::userid' =>
$user->getId(),
1744 $logEntry->insert();
1747 $trxProfiler->setSilenced( $old );
1786 $session = $this->
request->getSession();
1787 $session->remove(
'AuthManager::accountLinkState' );
1791 throw new \LogicException(
'Account linking is not possible' );
1794 if (
$user->getId() === 0 ) {
1798 $msg =
wfMessage(
'authmanager-userdoesnotexist',
$user->getName() );
1802 foreach ( $reqs
as $req ) {
1804 $req->returnToUrl = $returnToUrl;
1810 foreach ( $providers
as $id => $provider ) {
1813 $this->logger->debug( __METHOD__ .
": Account linking pre-check failed by $id", [
1814 'user' =>
$user->getName(),
1825 'username' =>
$user->getName(),
1826 'userid' =>
$user->getId(),
1827 'returnToUrl' => $returnToUrl,
1829 'continueRequests' => [],
1833 foreach ( $providers
as $id => $provider ) {
1838 $res = $provider->beginPrimaryAccountLink(
$user, $reqs );
1839 switch (
$res->status ) {
1841 $this->logger->info(
"Account linked to {user} by $id", [
1842 'user' =>
$user->getName(),
1848 $this->logger->debug( __METHOD__ .
": Account linking failed by $id", [
1849 'user' =>
$user->getName(),
1860 $this->logger->debug( __METHOD__ .
": Account linking $res->status by $id", [
1861 'user' =>
$user->getName(),
1864 $state[
'primary'] = $id;
1865 $state[
'continueRequests'] =
$res->neededRequests;
1866 $session->setSecret(
'AuthManager::accountLinkState', $state );
1867 $session->persist();
1872 throw new \DomainException(
1873 get_class( $provider ) .
"::beginPrimaryAccountLink() returned $res->status"
1879 $this->logger->debug( __METHOD__ .
': Account linking failed because no provider accepted', [
1880 'user' =>
$user->getName(),
1883 wfMessage(
'authmanager-link-no-primary' )
1895 $session = $this->
request->getSession();
1899 $session->remove(
'AuthManager::accountLinkState' );
1900 throw new \LogicException(
'Account linking is not possible' );
1903 $state = $session->getSecret(
'AuthManager::accountLinkState' );
1904 if ( !is_array( $state ) ) {
1906 wfMessage(
'authmanager-link-not-in-progress' )
1909 $state[
'continueRequests'] = [];
1914 if ( !is_object(
$user ) ) {
1915 $session->remove(
'AuthManager::accountLinkState' );
1918 if (
$user->getId() != $state[
'userid'] ) {
1919 throw new \UnexpectedValueException(
1920 "User \"{$state['username']}\" is valid, but " .
1921 "ID {$user->getId()} != {$state['userid']}!"
1925 foreach ( $reqs
as $req ) {
1926 $req->username = $state[
'username'];
1927 $req->returnToUrl = $state[
'returnToUrl'];
1937 wfMessage(
'authmanager-link-not-in-progress' )
1940 $session->remove(
'AuthManager::accountLinkState' );
1944 $id = $provider->getUniqueId();
1945 $res = $provider->continuePrimaryAccountLink(
$user, $reqs );
1946 switch (
$res->status ) {
1948 $this->logger->info(
"Account linked to {user} by $id", [
1949 'user' =>
$user->getName(),
1952 $session->remove(
'AuthManager::accountLinkState' );
1955 $this->logger->debug( __METHOD__ .
": Account linking failed by $id", [
1956 'user' =>
$user->getName(),
1959 $session->remove(
'AuthManager::accountLinkState' );
1963 $this->logger->debug( __METHOD__ .
": Account linking $res->status by $id", [
1964 'user' =>
$user->getName(),
1967 $state[
'continueRequests'] =
$res->neededRequests;
1968 $session->setSecret(
'AuthManager::accountLinkState', $state );
1971 throw new \DomainException(
1972 get_class( $provider ) .
"::continuePrimaryAccountLink() returned $res->status"
1975 }
catch ( \Exception $ex ) {
1976 $session->remove(
'AuthManager::accountLinkState' );
2020 $state = $this->
request->getSession()->getSecret(
'AuthManager::authnState' );
2021 return is_array( $state ) ? $state[
'continueRequests'] : [];
2024 $state = $this->
request->getSession()->getSecret(
'AuthManager::accountCreationState' );
2025 return is_array( $state ) ? $state[
'continueRequests'] : [];
2043 $state = $this->
request->getSession()->getSecret(
'AuthManager::accountLinkState' );
2044 return is_array( $state ) ? $state[
'continueRequests'] : [];
2054 throw new \DomainException( __METHOD__ .
": Invalid action \"$action\"" );
2078 foreach ( $providers
as $provider ) {
2080 foreach ( $provider->getAuthenticationRequests( $providerAction,
$options )
as $req ) {
2081 $id =
$req->getUniqueId();
2085 if (
$req->required ) {
2091 !isset( $reqs[$id] )
2101 switch ( $providerAction ) {
2109 if (
$options[
'username'] !==
null ) {
2120 if ( $providerAction === self::ACTION_CHANGE || $providerAction === self::ACTION_REMOVE ) {
2121 $reqs = array_filter( $reqs,
function (
$req ) {
2126 return array_values( $reqs );
2137 foreach ( $reqs
as $req ) {
2138 if ( !
$req->action || $forceAction ) {
2141 if (
$req->username ===
null ) {
2177 foreach ( $providers
as $provider ) {
2178 if ( !$provider->providerAllowsPropertyChange(
$property ) ) {
2195 if ( isset( $this->allAuthenticationProviders[$id] ) ) {
2196 return $this->allAuthenticationProviders[$id];
2201 if ( isset( $providers[$id] ) ) {
2202 return $providers[$id];
2205 if ( isset( $providers[$id] ) ) {
2206 return $providers[$id];
2209 if ( isset( $providers[$id] ) ) {
2210 return $providers[$id];
2230 $session = $this->
request->getSession();
2231 $arr = $session->getSecret(
'authData' );
2232 if ( !is_array( $arr ) ) {
2236 $session->setSecret(
'authData', $arr );
2247 $arr = $this->
request->getSession()->getSecret(
'authData' );
2248 if ( is_array( $arr ) && array_key_exists( $key, $arr ) ) {
2261 $session = $this->
request->getSession();
2262 if ( $key ===
null ) {
2263 $session->remove(
'authData' );
2265 $arr = $session->getSecret(
'authData' );
2266 if ( is_array( $arr ) && array_key_exists( $key, $arr ) ) {
2267 unset( $arr[$key] );
2268 $session->setSecret(
'authData', $arr );
2281 foreach ( $specs
as &$spec ) {
2282 $spec = [
'sort2' => $i++ ] + $spec + [
'sort' => 0 ];
2285 usort( $specs,
function ( $a, $b ) {
2286 return ( (
int)$a[
'sort'] ) - ( (
int)$b[
'sort'] )
2287 ?: $a[
'sort2'] - $b[
'sort2'];
2291 foreach ( $specs
as $spec ) {
2293 if ( !$provider instanceof $class ) {
2294 throw new \RuntimeException(
2295 "Expected instance of $class, got " . get_class( $provider )
2298 $provider->setLogger( $this->logger );
2299 $provider->setManager( $this );
2300 $provider->setConfig( $this->config );
2301 $id = $provider->getUniqueId();
2302 if ( isset( $this->allAuthenticationProviders[$id] ) ) {
2303 throw new \RuntimeException(
2304 "Duplicate specifications for id $id (classes " .
2305 get_class( $provider ) .
' and ' .
2306 get_class( $this->allAuthenticationProviders[$id] ) .
')'
2309 $this->allAuthenticationProviders[$id] = $provider;
2310 $ret[$id] = $provider;
2320 return $this->config->get(
'AuthManagerConfig' ) ?: $this->config->get(
'AuthManagerAutoConfig' );
2328 if ( $this->preAuthenticationProviders ===
null ) {
2342 if ( $this->primaryAuthenticationProviders ===
null ) {
2356 if ( $this->secondaryAuthenticationProviders ===
null ) {
2371 $session = $this->
request->getSession();
2372 $delay = $session->delaySave();
2374 $session->resetId();
2375 $session->resetAllTokens();
2376 if ( $session->canSetUser() ) {
2377 $session->setUser(
$user );
2379 if ( $remember !==
null ) {
2380 $session->setRememberUser( $remember );
2382 $session->set(
'AuthManager:lastAuthId',
$user->getId() );
2383 $session->set(
'AuthManager:lastAuthTimestamp', time() );
2384 $session->persist();
2386 \Wikimedia\ScopedCallback::consume( $delay );
2401 $user->setOption(
'language',
$lang->getPreferredVariant() );
2424 foreach ( $providers
as $provider ) {
2425 call_user_func_array( [ $provider, $method ],
$args );
2434 if ( !defined(
'MW_PHPUNIT_TEST' ) ) {
2436 throw new \MWException( __METHOD__ .
' may only be called from unit tests!' );
2440 self::$instance =
null;