146 if ( self::$instance === null ) {
147 self::$instance =
new self(
152 return self::$instance;
186 $this->logger->warning(
"Overriding AuthManager primary authn because $why" );
188 if ( $this->primaryAuthenticationProviders !== null ) {
189 $this->logger->warning(
190 'PrimaryAuthenticationProviders have already been accessed! I hope nothing breaks.'
193 $this->allAuthenticationProviders = array_diff_key(
194 $this->allAuthenticationProviders,
195 $this->primaryAuthenticationProviders
197 $session = $this->
request->getSession();
198 $session->remove(
'AuthManager::authnState' );
199 $session->remove(
'AuthManager::accountCreationState' );
200 $session->remove(
'AuthManager::accountLinkState' );
201 $this->createdAccountAuthenticationRequests = [];
204 $this->primaryAuthenticationProviders = [];
205 foreach ( $providers
as $provider ) {
207 throw new \RuntimeException(
208 'Expected instance of MediaWiki\\Auth\\PrimaryAuthenticationProvider, got ' .
209 get_class( $provider )
212 $provider->setLogger( $this->logger );
213 $provider->setManager( $this );
214 $provider->setConfig( $this->config );
215 $id = $provider->getUniqueId();
216 if ( isset( $this->allAuthenticationProviders[$id] ) ) {
217 throw new \RuntimeException(
218 "Duplicate specifications for id $id (classes " .
219 get_class( $provider ) .
' and ' .
220 get_class( $this->allAuthenticationProviders[$id] ) .
')'
223 $this->allAuthenticationProviders[$id] = $provider;
224 $this->primaryAuthenticationProviders[$id] = $provider;
241 return call_user_func_array( [ $wgAuth, $method ], $params );
261 return $this->
request->getSession()->canSetUser();
283 $session = $this->
request->getSession();
284 if ( !$session->canSetUser() ) {
286 $session->remove(
'AuthManager::authnState' );
287 throw new \LogicException(
'Authentication is not possible now' );
290 $guessUserName = null;
291 foreach ( $reqs
as $req ) {
292 $req->returnToUrl = $returnToUrl;
294 if ( $req->username !== null && $req->username !==
'' ) {
295 if ( $guessUserName === null ) {
296 $guessUserName = $req->username;
297 } elseif ( $guessUserName !== $req->username ) {
298 $guessUserName = null;
310 if ( !in_array( $req, $this->createdAccountAuthenticationRequests,
true ) ) {
311 throw new \LogicException(
312 'CreatedAccountAuthenticationRequests are only valid on ' .
313 'the same AuthManager that created the account'
320 throw new \UnexpectedValueException(
321 "CreatedAccountAuthenticationRequest had invalid username \"{$req->username}\""
323 } elseif (
$user->getId() != $req->id ) {
324 throw new \UnexpectedValueException(
325 "ID for \"{$req->username}\" was {$user->getId()}, expected {$req->id}"
330 $this->logger->info(
'Logging in {user} after account creation', [
331 'user' =>
$user->getName(),
336 $session->remove(
'AuthManager::authnState' );
344 $status = $provider->testForAuthentication( $reqs );
346 $this->logger->debug(
'Login failed in pre-authentication by ' . $provider->getUniqueId() );
353 \Hooks::run(
'AuthManagerLoginAuthenticateAudit', [
$ret, null, $guessUserName ] );
360 'returnToUrl' => $returnToUrl,
361 'guessUserName' => $guessUserName,
363 'primaryResponse' => null,
366 'continueRequests' => [],
374 $state[
'maybeLink'] = $req->maybeLink;
377 $session = $this->
request->getSession();
378 $session->setSecret(
'AuthManager::authnState', $state );
407 $session = $this->
request->getSession();
409 if ( !$session->canSetUser() ) {
412 throw new \LogicException(
'Authentication is not possible now' );
416 $state = $session->getSecret(
'AuthManager::authnState' );
417 if ( !is_array( $state ) ) {
419 wfMessage(
'authmanager-authn-not-in-progress' )
422 $state[
'continueRequests'] = [];
424 $guessUserName = $state[
'guessUserName'];
426 foreach ( $reqs
as $req ) {
427 $req->returnToUrl = $state[
'returnToUrl'];
432 if ( $state[
'primary'] === null ) {
435 $guessUserName = null;
436 foreach ( $reqs
as $req ) {
437 if ( $req->username !== null && $req->username !==
'' ) {
438 if ( $guessUserName === null ) {
439 $guessUserName = $req->username;
440 } elseif ( $guessUserName !== $req->username ) {
441 $guessUserName = null;
446 $state[
'guessUserName'] = $guessUserName;
448 $state[
'reqs'] = $reqs;
451 $res = $provider->beginPrimaryAuthentication( $reqs );
452 switch (
$res->status ) {
454 $state[
'primary'] = $id;
455 $state[
'primaryResponse'] =
$res;
456 $this->logger->debug(
"Primary login with $id succeeded" );
459 $this->logger->debug(
"Login failed in primary authentication by $id" );
460 if (
$res->createRequest || $state[
'maybeLink'] ) {
462 $res->createRequest, $state[
'maybeLink']
468 $session->remove(
'AuthManager::authnState' );
469 \Hooks::run(
'AuthManagerLoginAuthenticateAudit', [
$res, null, $guessUserName ] );
476 $this->logger->debug(
"Primary login with $id returned $res->status" );
477 $this->
fillRequests(
$res->neededRequests, self::ACTION_LOGIN, $guessUserName );
478 $state[
'primary'] = $id;
479 $state[
'continueRequests'] =
$res->neededRequests;
480 $session->setSecret(
'AuthManager::authnState', $state );
485 throw new \DomainException(
486 get_class( $provider ) .
"::beginPrimaryAuthentication() returned $res->status"
491 if ( $state[
'primary'] === null ) {
492 $this->logger->debug(
'Login failed in primary authentication because no provider accepted' );
494 wfMessage(
'authmanager-authn-no-primary' )
499 $session->remove(
'AuthManager::authnState' );
502 } elseif ( $state[
'primaryResponse'] === null ) {
508 wfMessage(
'authmanager-authn-not-in-progress' )
513 $session->remove(
'AuthManager::authnState' );
517 $id = $provider->getUniqueId();
518 $res = $provider->continuePrimaryAuthentication( $reqs );
519 switch (
$res->status ) {
521 $state[
'primaryResponse'] =
$res;
522 $this->logger->debug(
"Primary login with $id succeeded" );
525 $this->logger->debug(
"Login failed in primary authentication by $id" );
526 if (
$res->createRequest || $state[
'maybeLink'] ) {
528 $res->createRequest, $state[
'maybeLink']
534 $session->remove(
'AuthManager::authnState' );
535 \Hooks::run(
'AuthManagerLoginAuthenticateAudit', [
$res, null, $guessUserName ] );
539 $this->logger->debug(
"Primary login with $id returned $res->status" );
540 $this->
fillRequests(
$res->neededRequests, self::ACTION_LOGIN, $guessUserName );
541 $state[
'continueRequests'] =
$res->neededRequests;
542 $session->setSecret(
'AuthManager::authnState', $state );
545 throw new \DomainException(
546 get_class( $provider ) .
"::continuePrimaryAuthentication() returned $res->status"
551 $res = $state[
'primaryResponse'];
552 if (
$res->username === null ) {
558 wfMessage(
'authmanager-authn-not-in-progress' )
563 $session->remove(
'AuthManager::authnState' );
573 $state[
'maybeLink'][
$res->linkRequest->getUniqueId()] =
$res->linkRequest;
574 $msg =
'authmanager-authn-no-local-user-link';
576 $msg =
'authmanager-authn-no-local-user';
578 $this->logger->debug(
579 "Primary login with {$provider->getUniqueId()} succeeded, but returned no user"
587 if (
$res->createRequest || $state[
'maybeLink'] ) {
589 $res->createRequest, $state[
'maybeLink']
591 $ret->neededRequests[] =
$ret->createRequest;
593 $this->
fillRequests(
$ret->neededRequests, self::ACTION_LOGIN, null,
true );
594 $session->setSecret(
'AuthManager::authnState', [
597 'primaryResponse' => null,
599 'continueRequests' =>
$ret->neededRequests,
610 throw new \DomainException(
611 get_class( $provider ) .
" returned an invalid username: {$res->username}"
614 if (
$user->getId() === 0 ) {
616 $this->logger->info(
'Auto-creating {user} on login', [
617 'user' =>
$user->getName(),
625 $session->remove(
'AuthManager::authnState' );
633 $beginReqs = $state[
'reqs'];
636 if ( !isset( $state[
'secondary'][$id] ) ) {
640 $func =
'beginSecondaryAuthentication';
641 $res = $provider->beginSecondaryAuthentication(
$user, $beginReqs );
642 } elseif ( !$state[
'secondary'][$id] ) {
643 $func =
'continueSecondaryAuthentication';
644 $res = $provider->continueSecondaryAuthentication(
$user, $reqs );
648 switch (
$res->status ) {
650 $this->logger->debug(
"Secondary login with $id succeeded" );
653 $state[
'secondary'][$id] =
true;
656 $this->logger->debug(
"Login failed in secondary authentication by $id" );
658 $session->remove(
'AuthManager::authnState' );
663 $this->logger->debug(
"Secondary login with $id returned " . $res->status );
664 $this->
fillRequests( $res->neededRequests, self::ACTION_LOGIN,
$user->getName() );
665 $state[
'secondary'][$id] =
false;
666 $state[
'continueRequests'] = $res->neededRequests;
667 $session->setSecret(
'AuthManager::authnState', $state );
672 throw new \DomainException(
673 get_class( $provider ) .
"::{$func}() returned $res->status"
682 $this->logger->info(
'Login for {user} succeeded', [
683 'user' =>
$user->getName(),
692 $session->remove(
'AuthManager::authnState' );
697 $session->remove(
'AuthManager::authnState' );
716 $this->logger->debug( __METHOD__ .
": Checking $operation" );
718 $session = $this->
request->getSession();
719 $aId = $session->getUser()->getId();
723 $this->logger->info( __METHOD__ .
": Not logged in! $operation is $status" );
727 if ( $session->canSetUser() ) {
728 $id = $session->get(
'AuthManager:lastAuthId' );
729 $last = $session->get(
'AuthManager:lastAuthTimestamp' );
730 if ( $id !== $aId ||
$last === null ) {
731 $timeSinceLogin = PHP_INT_MAX;
733 $timeSinceLogin = max( 0, time() -
$last );
736 $thresholds = $this->config->get(
'ReauthenticateTime' );
737 if ( isset( $thresholds[$operation] ) ) {
738 $threshold = $thresholds[$operation];
739 } elseif ( isset( $thresholds[
'default'] ) ) {
740 $threshold = $thresholds[
'default'];
742 throw new \UnexpectedValueException(
'$wgReauthenticateTime lacks a default' );
745 if ( $threshold >= 0 && $timeSinceLogin > $threshold ) {
749 $timeSinceLogin = -1;
751 $pass = $this->config->get(
'AllowSecuritySensitiveOperationIfCannotReauthenticate' );
752 if ( isset( $pass[$operation] ) ) {
753 $status = $pass[$operation] ? self::SEC_OK : self::SEC_FAIL;
754 } elseif ( isset( $pass[
'default'] ) ) {
755 $status = $pass[
'default'] ? self::SEC_OK : self::SEC_FAIL;
757 throw new \UnexpectedValueException(
758 '$wgAllowSecuritySensitiveOperationIfCannotReauthenticate lacks a default'
764 &
$status, $operation, $session, $timeSinceLogin
772 $this->logger->info( __METHOD__ .
": $operation is $status" );
788 if ( $provider->testUserCanAuthenticate(
$username ) ) {
812 $normalized = $provider->providerNormalizeUsername(
$username );
813 if ( $normalized !== null ) {
814 $ret[$normalized] =
true;
817 return array_keys(
$ret );
835 $this->logger->info(
'Revoking access for {user}', [
854 foreach ( $providers
as $provider ) {
855 $status = $provider->providerAllowsAuthenticationDataChange( $req, $checkData );
859 $any = $any ||
$status->value !==
'ignored';
863 $status->warning(
'authmanager-change-not-supported' );
884 $this->logger->info(
'Changing authentication data for {user} class {what}', [
885 'user' => is_string( $req->username ) ? $req->username :
'<no name>',
886 'what' => get_class( $req ),
909 switch ( $provider->accountCreationType() ) {
932 'flags' => User::READ_NORMAL,
946 if ( !is_object(
$user ) ) {
950 if (
$user->getId() !== 0 ) {
959 foreach ( $providers
as $provider ) {
982 ->getUserPermissionsErrors(
'createaccount', $creator,
'secure' );
985 foreach ( $permErrors
as $args ) {
986 call_user_func_array( [
$status,
'fatal' ], $args );
995 $block->mReason ?:
wfMessage(
'blockednoreason' )->text(),
1000 $errorMessage =
'cantcreateaccount-range-text';
1001 $errorParams[] = $this->
getRequest()->getIP();
1003 $errorMessage =
'cantcreateaccount-text';
1037 $session = $this->
request->getSession();
1040 $session->remove(
'AuthManager::accountCreationState' );
1041 throw new \LogicException(
'Account creation is not possible' );
1050 $this->logger->debug( __METHOD__ .
': No username provided' );
1057 $this->logger->debug( __METHOD__ .
': {creator} cannot create users: {reason}', [
1059 'creator' => $creator->
getName(),
1060 'reason' =>
$status->getWikiText( null, null,
'en' )
1069 $this->logger->debug( __METHOD__ .
': {user} cannot be created: {reason}', [
1071 'creator' => $creator->
getName(),
1072 'reason' =>
$status->getWikiText( null, null,
'en' )
1078 foreach ( $reqs
as $req ) {
1080 $req->returnToUrl = $returnToUrl;
1085 $session->remove(
'AuthManager::accountCreationState' );
1086 $this->logger->debug( __METHOD__ .
': UserData is invalid: {reason}', [
1087 'user' =>
$user->getName(),
1088 'creator' => $creator->
getName(),
1089 'reason' =>
$status->getWikiText( null, null,
'en' ),
1101 'creatorid' => $creator->
getId(),
1102 'creatorname' => $creator->
getName(),
1104 'returnToUrl' => $returnToUrl,
1106 'primaryResponse' => null,
1108 'continueRequests' => [],
1110 'ranPreTests' =>
false,
1118 $state[
'maybeLink'] = $req->maybeLink;
1120 if ( $req->createRequest ) {
1121 $reqs[] = $req->createRequest;
1122 $state[
'reqs'][] = $req->createRequest;
1126 $session->setSecret(
'AuthManager::accountCreationState', $state );
1127 $session->persist();
1138 $session = $this->
request->getSession();
1142 $session->remove(
'AuthManager::accountCreationState' );
1143 throw new \LogicException(
'Account creation is not possible' );
1146 $state = $session->getSecret(
'AuthManager::accountCreationState' );
1147 if ( !is_array( $state ) ) {
1149 wfMessage(
'authmanager-create-not-in-progress' )
1152 $state[
'continueRequests'] = [];
1157 if ( !is_object(
$user ) ) {
1158 $session->remove(
'AuthManager::accountCreationState' );
1159 $this->logger->debug( __METHOD__ .
': Invalid username', [
1160 'user' => $state[
'username'],
1165 if ( $state[
'creatorid'] ) {
1168 $creator =
new User;
1169 $creator->setName( $state[
'creatorname'] );
1174 $lock =
$cache->getScopedLock(
$cache->makeGlobalKey(
'account', md5(
$user->getName() ) ) );
1178 $this->logger->debug( __METHOD__ .
': Could not acquire account creation lock', [
1179 'user' =>
$user->getName(),
1180 'creator' => $creator->getName(),
1188 $this->logger->debug( __METHOD__ .
': {creator} cannot create users: {reason}', [
1189 'user' =>
$user->getName(),
1190 'creator' => $creator->getName(),
1191 'reason' =>
$status->getWikiText( null, null,
'en' )
1195 $session->remove(
'AuthManager::accountCreationState' );
1202 if ( $state[
'userid'] === 0 ) {
1203 if (
$user->getId() != 0 ) {
1204 $this->logger->debug( __METHOD__ .
': User exists locally', [
1205 'user' =>
$user->getName(),
1206 'creator' => $creator->getName(),
1210 $session->remove(
'AuthManager::accountCreationState' );
1214 if (
$user->getId() == 0 ) {
1215 $this->logger->debug( __METHOD__ .
': User does not exist locally when it should', [
1216 'user' =>
$user->getName(),
1217 'creator' => $creator->getName(),
1218 'expected_id' => $state[
'userid'],
1220 throw new \UnexpectedValueException(
1221 "User \"{$state['username']}\" should exist now, but doesn't!"
1224 if (
$user->getId() != $state[
'userid'] ) {
1225 $this->logger->debug( __METHOD__ .
': User ID/name mismatch', [
1226 'user' =>
$user->getName(),
1227 'creator' => $creator->getName(),
1228 'expected_id' => $state[
'userid'],
1229 'actual_id' =>
$user->getId(),
1231 throw new \UnexpectedValueException(
1232 "User \"{$state['username']}\" exists, but " .
1233 "ID {$user->getId()} != {$state['userid']}!"
1237 foreach ( $state[
'reqs']
as $req ) {
1243 $this->logger->debug( __METHOD__ .
': UserData is invalid: {reason}', [
1244 'user' =>
$user->getName(),
1245 'creator' => $creator->getName(),
1246 'reason' =>
$status->getWikiText( null, null,
'en' ),
1250 $session->remove(
'AuthManager::accountCreationState' );
1256 foreach ( $reqs
as $req ) {
1257 $req->returnToUrl = $state[
'returnToUrl'];
1258 $req->username = $state[
'username'];
1262 if ( !$state[
'ranPreTests'] ) {
1266 foreach ( $providers
as $id => $provider ) {
1267 $status = $provider->testForAccountCreation(
$user, $creator, $reqs );
1269 $this->logger->debug( __METHOD__ .
": Fail in pre-authentication by $id", [
1270 'user' =>
$user->getName(),
1271 'creator' => $creator->getName(),
1277 $session->remove(
'AuthManager::accountCreationState' );
1282 $state[
'ranPreTests'] =
true;
1287 if ( $state[
'primary'] === null ) {
1293 $res = $provider->beginPrimaryAccountCreation(
$user, $creator, $reqs );
1294 switch (
$res->status ) {
1296 $this->logger->debug( __METHOD__ .
": Primary creation passed by $id", [
1297 'user' =>
$user->getName(),
1298 'creator' => $creator->getName(),
1300 $state[
'primary'] = $id;
1301 $state[
'primaryResponse'] =
$res;
1304 $this->logger->debug( __METHOD__ .
": Primary creation failed by $id", [
1305 'user' =>
$user->getName(),
1306 'creator' => $creator->getName(),
1309 $session->remove(
'AuthManager::accountCreationState' );
1316 $this->logger->debug( __METHOD__ .
": Primary creation $res->status by $id", [
1317 'user' =>
$user->getName(),
1318 'creator' => $creator->getName(),
1321 $state[
'primary'] = $id;
1322 $state[
'continueRequests'] =
$res->neededRequests;
1323 $session->setSecret(
'AuthManager::accountCreationState', $state );
1328 throw new \DomainException(
1329 get_class( $provider ) .
"::beginPrimaryAccountCreation() returned $res->status"
1334 if ( $state[
'primary'] === null ) {
1335 $this->logger->debug( __METHOD__ .
': Primary creation failed because no provider accepted', [
1336 'user' =>
$user->getName(),
1337 'creator' => $creator->getName(),
1340 wfMessage(
'authmanager-create-no-primary' )
1343 $session->remove(
'AuthManager::accountCreationState' );
1346 } elseif ( $state[
'primaryResponse'] === null ) {
1352 wfMessage(
'authmanager-create-not-in-progress' )
1355 $session->remove(
'AuthManager::accountCreationState' );
1359 $id = $provider->getUniqueId();
1360 $res = $provider->continuePrimaryAccountCreation(
$user, $creator, $reqs );
1361 switch (
$res->status ) {
1363 $this->logger->debug( __METHOD__ .
": Primary creation passed by $id", [
1364 'user' =>
$user->getName(),
1365 'creator' => $creator->getName(),
1367 $state[
'primaryResponse'] =
$res;
1370 $this->logger->debug( __METHOD__ .
": Primary creation failed by $id", [
1371 'user' =>
$user->getName(),
1372 'creator' => $creator->getName(),
1375 $session->remove(
'AuthManager::accountCreationState' );
1379 $this->logger->debug( __METHOD__ .
": Primary creation $res->status by $id", [
1380 'user' =>
$user->getName(),
1381 'creator' => $creator->getName(),
1384 $state[
'continueRequests'] =
$res->neededRequests;
1385 $session->setSecret(
'AuthManager::accountCreationState', $state );
1388 throw new \DomainException(
1389 get_class( $provider ) .
"::continuePrimaryAccountCreation() returned $res->status"
1397 if ( $state[
'userid'] === 0 ) {
1398 $this->logger->info(
'Creating user {user} during account creation', [
1399 'user' =>
$user->getName(),
1400 'creator' => $creator->getName(),
1407 $session->remove(
'AuthManager::accountCreationState' );
1413 $user->saveSettings();
1414 $state[
'userid'] =
$user->getId();
1423 $logSubtype = $provider->finishAccountCreation(
$user, $creator, $state[
'primaryResponse'] );
1426 if ( $this->config->get(
'NewUserLog' ) ) {
1427 $isAnon = $creator->isAnon();
1428 $logEntry = new \ManualLogEntry(
1430 $logSubtype ?: ( $isAnon ?
'create' :
'create2' )
1432 $logEntry->setPerformer( $isAnon ?
$user : $creator );
1433 $logEntry->setTarget(
$user->getUserPage() );
1438 $logEntry->setComment( $req ? $req->reason :
'' );
1439 $logEntry->setParameters( [
1440 '4::userid' =>
$user->getId(),
1442 $logid = $logEntry->insert();
1443 $logEntry->publish( $logid );
1449 $beginReqs = $state[
'reqs'];
1452 if ( !isset( $state[
'secondary'][$id] ) ) {
1456 $func =
'beginSecondaryAccountCreation';
1457 $res = $provider->beginSecondaryAccountCreation(
$user, $creator, $beginReqs );
1458 } elseif ( !$state[
'secondary'][$id] ) {
1459 $func =
'continueSecondaryAccountCreation';
1460 $res = $provider->continueSecondaryAccountCreation(
$user, $creator, $reqs );
1464 switch (
$res->status ) {
1466 $this->logger->debug( __METHOD__ .
": Secondary creation passed by $id", [
1467 'user' =>
$user->getName(),
1468 'creator' => $creator->getName(),
1472 $state[
'secondary'][$id] =
true;
1476 $this->logger->debug( __METHOD__ .
": Secondary creation $res->status by $id", [
1477 'user' =>
$user->getName(),
1478 'creator' => $creator->getName(),
1481 $state[
'secondary'][$id] =
false;
1482 $state[
'continueRequests'] =
$res->neededRequests;
1483 $session->setSecret(
'AuthManager::accountCreationState', $state );
1486 throw new \DomainException(
1487 get_class( $provider ) .
"::{$func}() returned $res->status." .
1488 ' Secondary providers are not allowed to fail account creation, that' .
1489 ' should have been done via testForAccountCreation().'
1493 throw new \DomainException(
1494 get_class( $provider ) .
"::{$func}() returned $res->status"
1500 $id =
$user->getId();
1505 $this->createdAccountAuthenticationRequests[] =
$req;
1507 $this->logger->info( __METHOD__ .
': Account creation succeeded for {user}', [
1508 'user' =>
$user->getName(),
1509 'creator' => $creator->getName(),
1513 $session->remove(
'AuthManager::accountCreationState' );
1517 $session->remove(
'AuthManager::accountCreationState' );
1538 if (
$source !== self::AUTOCREATE_SOURCE_SESSION &&
1541 throw new \InvalidArgumentException(
"Unknown auto-creation source: $source" );
1548 $flags = User::READ_NORMAL;
1553 if ( !$localId &&
wfGetLB()->getReaderIndex() != 0 ) {
1555 $flags = User::READ_LATEST;
1560 $this->logger->debug( __METHOD__ .
': {username} already exists locally', [
1563 $user->
setId( $localId );
1569 $status->warning(
'userexists' );
1575 $this->logger->debug( __METHOD__ .
': denied by wfReadOnly(): {reason}', [
1586 $session = $this->
request->getSession();
1587 if ( $session->get(
'AuthManager::AutoCreateBlacklist' ) ) {
1588 $this->logger->debug( __METHOD__ .
': blacklisted in session {sessionid}', [
1590 'sessionid' => $session->getId(),
1594 $reason = $session->get(
'AuthManager::AutoCreateBlacklist' );
1604 $this->logger->debug( __METHOD__ .
': name "{username}" is not creatable', [
1607 $session->set(
'AuthManager::AutoCreateBlacklist',
'noname' );
1615 if ( !$anon->isAllowedAny(
'createaccount',
'autocreateaccount' ) ) {
1616 $this->logger->debug( __METHOD__ .
': IP lacks the ability to create or autocreate accounts', [
1618 'ip' => $anon->getName(),
1620 $session->set(
'AuthManager::AutoCreateBlacklist',
'authmanager-autocreate-noperm' );
1621 $session->persist();
1631 $this->logger->debug( __METHOD__ .
': Could not acquire account creation lock', [
1641 'flags' => User::READ_LATEST,
1647 foreach ( $providers
as $provider ) {
1651 $this->logger->debug( __METHOD__ .
': Provider denied creation of {username}: {reason}', [
1653 'reason' =>
$ret->getWikiText( null, null,
'en' ),
1655 $session->set(
'AuthManager::AutoCreateBlacklist',
$status );
1663 if (
$cache->get( $backoffKey ) ) {
1664 $this->logger->debug( __METHOD__ .
': {username} denied by prior creation attempt failures', [
1673 $from = isset( $_SERVER[
'REQUEST_URI'] ) ? $_SERVER[
'REQUEST_URI'] :
'CLI';
1674 $this->logger->info( __METHOD__ .
': creating new user ({username}) - from: {from}', [
1681 $old = $trxProfiler->setSilenced(
true );
1687 if ( $user->
getId() ) {
1688 $this->logger->info( __METHOD__ .
': {username} already exists locally (race)', [
1695 $status->warning(
'userexists' );
1697 $this->logger->error( __METHOD__ .
': {username} failed with message {msg}', [
1699 'msg' =>
$status->getWikiText( null, null,
'en' )
1707 $trxProfiler->setSilenced( $old );
1708 $this->logger->error( __METHOD__ .
': {username} failed with exception {exception}', [
1713 $cache->set( $backoffKey, 1, 600 );
1723 \Hooks::run(
'AuthPluginAutoCreate', [ $user ],
'1.27' );
1724 \Hooks::run(
'LocalUserCreated', [ $user,
true ] );
1735 if ( $this->config->get(
'NewUserLog' ) ) {
1736 $logEntry = new \ManualLogEntry(
'newusers',
'autocreate' );
1737 $logEntry->setPerformer( $user );
1739 $logEntry->setComment(
'' );
1740 $logEntry->setParameters( [
1741 '4::userid' => $user->
getId(),
1743 $logEntry->insert();
1746 $trxProfiler->setSilenced( $old );
1785 $session = $this->
request->getSession();
1786 $session->remove(
'AuthManager::accountLinkState' );
1790 throw new \LogicException(
'Account linking is not possible' );
1793 if ( $user->
getId() === 0 ) {
1801 foreach ( $reqs
as $req ) {
1802 $req->username = $user->
getName();
1803 $req->returnToUrl = $returnToUrl;
1809 foreach ( $providers
as $id => $provider ) {
1810 $status = $provider->testForAccountLink( $user );
1812 $this->logger->debug( __METHOD__ .
": Account linking pre-check failed by $id", [
1824 'username' => $user->
getName(),
1825 'userid' => $user->
getId(),
1826 'returnToUrl' => $returnToUrl,
1828 'continueRequests' => [],
1832 foreach ( $providers
as $id => $provider ) {
1837 $res = $provider->beginPrimaryAccountLink( $user, $reqs );
1838 switch (
$res->status ) {
1840 $this->logger->info(
"Account linked to {user} by $id", [
1847 $this->logger->debug( __METHOD__ .
": Account linking failed by $id", [
1859 $this->logger->debug( __METHOD__ .
": Account linking $res->status by $id", [
1863 $state[
'primary'] = $id;
1864 $state[
'continueRequests'] =
$res->neededRequests;
1865 $session->setSecret(
'AuthManager::accountLinkState', $state );
1866 $session->persist();
1871 throw new \DomainException(
1872 get_class( $provider ) .
"::beginPrimaryAccountLink() returned $res->status"
1878 $this->logger->debug( __METHOD__ .
': Account linking failed because no provider accepted', [
1882 wfMessage(
'authmanager-link-no-primary' )
1894 $session = $this->
request->getSession();
1898 $session->remove(
'AuthManager::accountLinkState' );
1899 throw new \LogicException(
'Account linking is not possible' );
1902 $state = $session->getSecret(
'AuthManager::accountLinkState' );
1903 if ( !is_array( $state ) ) {
1905 wfMessage(
'authmanager-link-not-in-progress' )
1908 $state[
'continueRequests'] = [];
1913 if ( !is_object(
$user ) ) {
1914 $session->remove(
'AuthManager::accountLinkState' );
1917 if (
$user->getId() != $state[
'userid'] ) {
1918 throw new \UnexpectedValueException(
1919 "User \"{$state['username']}\" is valid, but " .
1920 "ID {$user->getId()} != {$state['userid']}!"
1924 foreach ( $reqs
as $req ) {
1925 $req->username = $state[
'username'];
1926 $req->returnToUrl = $state[
'returnToUrl'];
1936 wfMessage(
'authmanager-link-not-in-progress' )
1939 $session->remove(
'AuthManager::accountLinkState' );
1943 $id = $provider->getUniqueId();
1944 $res = $provider->continuePrimaryAccountLink(
$user, $reqs );
1945 switch (
$res->status ) {
1947 $this->logger->info(
"Account linked to {user} by $id", [
1948 'user' =>
$user->getName(),
1951 $session->remove(
'AuthManager::accountLinkState' );
1954 $this->logger->debug( __METHOD__ .
": Account linking failed by $id", [
1955 'user' =>
$user->getName(),
1958 $session->remove(
'AuthManager::accountLinkState' );
1962 $this->logger->debug( __METHOD__ .
": Account linking $res->status by $id", [
1963 'user' =>
$user->getName(),
1966 $state[
'continueRequests'] =
$res->neededRequests;
1967 $session->setSecret(
'AuthManager::accountLinkState', $state );
1970 throw new \DomainException(
1971 get_class( $provider ) .
"::continuePrimaryAccountLink() returned $res->status"
1975 $session->remove(
'AuthManager::accountLinkState' );
2011 case self::ACTION_LOGIN:
2012 case self::ACTION_CREATE:
2018 case self::ACTION_LOGIN_CONTINUE:
2019 $state = $this->
request->getSession()->getSecret(
'AuthManager::authnState' );
2020 return is_array( $state ) ? $state[
'continueRequests'] : [];
2022 case self::ACTION_CREATE_CONTINUE:
2023 $state = $this->
request->getSession()->getSecret(
'AuthManager::accountCreationState' );
2024 return is_array( $state ) ? $state[
'continueRequests'] : [];
2026 case self::ACTION_LINK:
2032 case self::ACTION_UNLINK:
2038 $providerAction = self::ACTION_REMOVE;
2041 case self::ACTION_LINK_CONTINUE:
2042 $state = $this->
request->getSession()->getSecret(
'AuthManager::accountLinkState' );
2043 return is_array( $state ) ? $state[
'continueRequests'] : [];
2045 case self::ACTION_CHANGE:
2046 case self::ACTION_REMOVE:
2053 throw new \DomainException( __METHOD__ .
": Invalid action \"$action\"" );
2073 $options[
'username'] =
$user->isAnon() ? null :
$user->getName();
2077 foreach ( $providers
as $provider ) {
2079 foreach ( $provider->getAuthenticationRequests( $providerAction, $options )
as $req ) {
2080 $id =
$req->getUniqueId();
2084 if (
$req->required ) {
2090 !isset( $reqs[$id] )
2100 switch ( $providerAction ) {
2101 case self::ACTION_LOGIN:
2105 case self::ACTION_CREATE:
2108 if ( $options[
'username'] !== null ) {
2110 $options[
'username'] = null;
2116 $this->
fillRequests( $reqs, $providerAction, $options[
'username'],
true );
2119 if ( $providerAction === self::ACTION_CHANGE || $providerAction === self::ACTION_REMOVE ) {
2120 $reqs = array_filter( $reqs,
function (
$req ) {
2125 return array_values( $reqs );
2136 foreach ( $reqs
as $req ) {
2137 if ( !$req->action || $forceAction ) {
2140 if ( $req->username === null ) {
2176 foreach ( $providers
as $provider ) {
2177 if ( !$provider->providerAllowsPropertyChange(
$property ) ) {
2194 if ( isset( $this->allAuthenticationProviders[$id] ) ) {
2195 return $this->allAuthenticationProviders[$id];
2200 if ( isset( $providers[$id] ) ) {
2201 return $providers[$id];
2204 if ( isset( $providers[$id] ) ) {
2205 return $providers[$id];
2208 if ( isset( $providers[$id] ) ) {
2209 return $providers[$id];
2229 $session = $this->
request->getSession();
2230 $arr = $session->getSecret(
'authData' );
2231 if ( !is_array( $arr ) ) {
2235 $session->setSecret(
'authData', $arr );
2246 $arr = $this->
request->getSession()->getSecret(
'authData' );
2247 if ( is_array( $arr ) && array_key_exists( $key, $arr ) ) {
2260 $session = $this->
request->getSession();
2261 if ( $key === null ) {
2262 $session->remove(
'authData' );
2264 $arr = $session->getSecret(
'authData' );
2265 if ( is_array( $arr ) && array_key_exists( $key, $arr ) ) {
2266 unset( $arr[$key] );
2267 $session->setSecret(
'authData', $arr );
2280 foreach ( $specs
as &$spec ) {
2281 $spec = [
'sort2' => $i++ ] + $spec + [
'sort' => 0 ];
2284 usort( $specs,
function ( $a, $b ) {
2285 return ( (
int)$a[
'sort'] ) - ( (
int)$b[
'sort'] )
2286 ?: $a[
'sort2'] - $b[
'sort2'];
2290 foreach ( $specs
as $spec ) {
2292 if ( !$provider instanceof $class ) {
2293 throw new \RuntimeException(
2294 "Expected instance of $class, got " . get_class( $provider )
2297 $provider->setLogger( $this->logger );
2298 $provider->setManager( $this );
2299 $provider->setConfig( $this->config );
2300 $id = $provider->getUniqueId();
2301 if ( isset( $this->allAuthenticationProviders[$id] ) ) {
2302 throw new \RuntimeException(
2303 "Duplicate specifications for id $id (classes " .
2304 get_class( $provider ) .
' and ' .
2305 get_class( $this->allAuthenticationProviders[$id] ) .
')'
2308 $this->allAuthenticationProviders[$id] = $provider;
2309 $ret[$id] = $provider;
2319 return $this->config->get(
'AuthManagerConfig' ) ?: $this->config->get(
'AuthManagerAutoConfig' );
2327 if ( $this->preAuthenticationProviders === null ) {
2341 if ( $this->primaryAuthenticationProviders === null ) {
2355 if ( $this->secondaryAuthenticationProviders === null ) {
2370 $session = $this->
request->getSession();
2371 $delay = $session->delaySave();
2373 $session->resetId();
2374 $session->resetAllTokens();
2375 if ( $session->canSetUser() ) {
2376 $session->setUser(
$user );
2378 if ( $remember !== null ) {
2379 $session->setRememberUser( $remember );
2381 $session->set(
'AuthManager:lastAuthId',
$user->getId() );
2382 $session->set(
'AuthManager:lastAuthTimestamp', time() );
2383 $session->persist();
2385 \Wikimedia\ScopedCallback::consume( $delay );
2402 if ( $wgContLang->hasVariants() ) {
2403 $user->
setOption(
'variant', $wgContLang->getPreferredVariant() );
2423 foreach ( $providers
as $provider ) {
2424 call_user_func_array( [ $provider, $method ], $args );
2433 if ( !defined(
'MW_PHPUNIT_TEST' ) ) {
2435 throw new \MWException( __METHOD__ .
' may only be called from unit tests!' );
2439 self::$instance = null;
static newFromName($name, $validate= 'valid')
Static factory method for creation from username.
the array() calling protocol came about after MediaWiki 1.4rc1.
static wrap($sv)
Succinct helper method to wrap a StatusValue.
static getObjectFromSpec($spec)
Instantiate an object based on a specification array.
isDnsBlacklisted($ip, $checkWhitelist=false)
Whether the given IP is in a DNS blacklist.
saveSettings()
Save this user's settings into the database.
null means default in associative array with keys and values unescaped Should be merged with default with a value of false meaning to suppress the attribute in associative array with keys and values unescaped noclasses & $ret
Apache License January AND DISTRIBUTION Definitions License shall mean the terms and conditions for use
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...
static newFatal($message)
Factory function for fatal errors.
static instance()
Singleton.
if(!isset($args[0])) $lang
const READ_LOCKING
Constants for object loading bitfield flags (higher => higher QoS)
static isUsableName($name)
Usernames which fail to pass this function will be blocked from user login and new account registrati...
static newFromId($id)
Static factory method for creation from a given user ID.
static getLocalClusterInstance()
Get the main cluster-local cache object.
it s the revision text itself In either if gzip is the revision text is gzipped $flags
when a variable name is used in a it is silently declared as a new local masking the global
setToken($token=false)
Set the random token (used for persistent authentication) Called from loadDefaults() among other plac...
setOption($oname, $val)
Set the given option for a user.
getName()
Get the user name, or the IP of an anonymous user.
isBlockedFromCreateAccount()
Get whether the user is explicitly blocked from account creation.
wfGetLB($wiki=false)
Get a load balancer object.
wfReadOnly()
Check whether the wiki is in read-only mode.
static getMain()
Static methods.
either a unescaped string or a HtmlArmor object after in associative array form externallinks including delete and has completed for all link tables whether this was an auto creation default is conds Array Extra conditions for the No matching items in log is displayed if loglist is empty msgKey Array If you want a nice box with a set this to the key of the message First element is the message additional optional elements are parameters for the key that are processed with wfMessage() -> params() ->parseAsBlock()-offset Set to overwrite offset parameter in $wgRequest set to ''to unsetoffset-wrap String Wrap the message in html(usually something like"<
div flags Integer display flags(NO_ACTION_LINK, NO_EXTRA_USER_LINKS) 'LogException'returning false will NOT prevent logging a wrapping ErrorException instead of letting the login form give the generic error message that the account does not exist For when the account has been renamed or deleted or an array to pass a message key and parameters create2 Corresponds to logging log_action database field and which is displayed in the UI similar to $comment this hook should only be used to add variables that depend on the current page request
this hook is for auditing only RecentChangesLinked and Watchlist RecentChangesLinked and Watchlist e g Watchlist removed from all revisions and log entries to which it was applied This gives extensions a chance to take it off their books as the deletion has already been partly carried out by this point or something similar the user will be unable to create the tag set and then return false from the hook function Ensure you consume the ChangeTagAfterDelete hook to carry out custom deletion actions as context called by AbstractContent::getParserOutput May be used to override the normal model specific rendering of page content as context as context $options
static invalidateAllPasswordsForUser($username)
Invalidate all passwords for a user, by name.
Class for handling updates to the site_stats table.
static newGood($value=null)
Factory function for good results.
static run($event, array $args=[], $deprecatedVersion=null)
Call hook functions defined in Hooks::register and $wgHooks.
This document is intended to provide useful advice for parties seeking to redistribute MediaWiki to end users It s targeted particularly at maintainers for Linux since it s been observed that distribution packages of MediaWiki often break We ve consistently had to recommend that users seeking support use official tarballs instead of their distribution s and this often solves whatever problem the user is having It would be nice if this could such as
please add to it if you re going to add events to the MediaWiki code where normally authentication against an external auth plugin would be creating a local account $user
static getDefaultInstance()
setId($v)
Set the user and reload all fields according to a given ID.
injection txt This is an overview of how MediaWiki makes use of dependency injection The design described here grew from the discussion of RFC T384 The term dependency this means that anything an object needs to operate should be injected from the the object itself should only know narrow no concrete implementation of the logic it relies on The requirement to inject everything typically results in an architecture that based on two main types of and essentially stateless service objects that use other service objects to operate on the value objects As of the beginning MediaWiki is only starting to use the DI approach Much of the code still relies on global state or direct resulting in a highly cyclical dependency which acts as the top level factory for services in MediaWiki which can be used to gain access to default instances of various services MediaWikiServices however also allows new services to be defined and default services to be redefined Services are defined or redefined by providing a callback the instantiator that will return a new instance of the service When it will create an instance of MediaWikiServices and populate it with the services defined in the files listed by thereby bootstrapping the DI framework Per $wgServiceWiringFiles lists includes ServiceWiring php
this hook is for auditing only $req
this hook is for auditing only or null if authentication failed before getting that far $username
static addUpdate(DeferrableUpdate $update, $stage=self::POSTSEND)
Add an update to the deferred list to be run later by execute()
wfReadOnlyReason()
Check if the site is in read-only mode and return the message if so.
getId()
Get the user's ID.
addToDatabase()
Add this existing user object to the database.
you have access to all of the normal MediaWiki so you can get a DB use the etc For full docs on the Maintenance class
static isCreatableName($name)
Usernames which fail to pass this function will be blocked from new account registrations, but may be used internally either by batch processes or by user accounts which have already been created.
getUserPage()
Get this user's personal page title.
static idFromName($name, $flags=self::READ_NORMAL)
Get database id given a user name.
this class mediates it Skin Encapsulates a look and feel for the wiki All of the functions that render HTML and make choices about how to render it are here and are called from various other places when and is meant to be subclassed with other skins that may override some of its functions The User object contains a reference to a and so rather than having a global skin object we just rely on the global User and get the skin with $wgUser and also has some character encoding functions and other locale stuff The current user interface language is instantiated as and the local content language as $wgContLang
this hook is for auditing only RecentChangesLinked and Watchlist RecentChangesLinked and Watchlist e g Watchlist removed from all revisions and log entries to which it was applied This gives extensions a chance to take it off their books as the deletion has already been partly carried out by this point or something similar the user will be unable to create the tag set $status
wfMemcKey()
Make a cache key for the local wiki.
static addCallableUpdate($callable, $stage=self::POSTSEND, IDatabase $dbw=null)
Add a callable update.
loadFromId($flags=self::READ_NORMAL)
Load user table data, given mId has already been set.
addWatch($title, $checkRights=self::CHECK_USER_RIGHTS)
Watch an article.
Allows to change the fields on the form that will be generated $name