27use Psr\Log\LoggerAwareInterface;
28use Psr\Log\LoggerInterface;
146 if ( self::$instance ===
null ) {
147 self::$instance =
new self(
148 \RequestContext::getMain()->getRequest(),
149 \ConfigFactory::getDefaultInstance()->makeConfig(
'main' )
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;
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;
307 $reqs, CreatedAccountAuthenticationRequest::class
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'
317 $user = User::newFromName(
$req->username );
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' );
337 \Hooks::run(
'AuthManagerLoginAuthenticateAudit', [
$ret,
$user,
$user->getName() ] );
344 $status = $provider->testForAuthentication( $reqs );
346 $this->logger->debug(
'Login failed in pre-authentication by ' . $provider->getUniqueId() );
348 Status::wrap(
$status )->getMessage()
351 [ User::newFromName( $guessUserName ) ?:
null,
$ret ]
353 \Hooks::run(
'AuthManagerLoginAuthenticateAudit', [
$ret,
null, $guessUserName ] );
360 'returnToUrl' => $returnToUrl,
361 'guessUserName' => $guessUserName,
363 'primaryResponse' =>
null,
366 'continueRequests' => [],
371 $reqs, CreateFromLoginAuthenticationRequest::class
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']
466 [ User::newFromName( $guessUserName ) ?:
null,
$res ]
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' )
497 [ User::newFromName( $guessUserName ) ?:
null,
$ret ]
499 $session->remove(
'AuthManager::authnState' );
502 } elseif ( $state[
'primaryResponse'] ===
null ) {
508 wfMessage(
'authmanager-authn-not-in-progress' )
511 [ User::newFromName( $guessUserName ) ?:
null,
$ret ]
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']
532 [ User::newFromName( $guessUserName ) ?:
null,
$res ]
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' )
561 [ User::newFromName( $guessUserName ) ?:
null,
$ret ]
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,
607 $user = User::newFromName(
$res->username,
'usable' );
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(),
622 Status::wrap(
$status )->getMessage(
'authmanager-authn-autocreate-failed' )
625 $session->remove(
'AuthManager::authnState' );
626 \Hooks::run(
'AuthManagerLoginAuthenticateAudit', [
$ret,
$user,
$user->getName() ] );
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' );
659 \Hooks::run(
'AuthManagerLoginAuthenticateAudit', [
$res,
$user,
$user->getName() ] );
663 $this->logger->debug(
"Secondary login with $id returned " .
$res->status );
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(),
687 $beginReqs, RememberMeAuthenticationRequest::class
692 $session->remove(
'AuthManager::authnState' );
694 \Hooks::run(
'AuthManagerLoginAuthenticateAudit', [
$ret,
$user,
$user->getName() ] );
696 }
catch ( \Exception $ex ) {
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] ) ) {
754 } elseif ( isset( $pass[
'default'] ) ) {
757 throw new \UnexpectedValueException(
758 '$wgAllowSecuritySensitiveOperationIfCannotReauthenticate lacks a default'
763 \Hooks::run(
'SecuritySensitiveOperationStatus', [
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 );
857 return Status::wrap(
$status );
859 $any = $any ||
$status->value !==
'ignored';
862 $status = Status::newGood(
'ignored' );
863 $status->warning(
'authmanager-change-not-supported' );
866 return Status::newGood();
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 ),
893 \BotPassword::invalidateAllPasswordsForUser(
$req->username );
909 switch ( $provider->accountCreationType() ) {
932 'flags' => User::READ_NORMAL,
938 return Status::newFatal(
'authmanager-create-disabled' );
942 return Status::newFatal(
'userexists' );
946 if ( !is_object(
$user ) ) {
947 return Status::newFatal(
'noname' );
950 if (
$user->getId() !== 0 ) {
951 return Status::newFatal(
'userexists' );
959 foreach ( $providers
as $provider ) {
962 return Status::wrap(
$status );
966 return Status::newGood();
981 $permErrors = \SpecialPage::getTitleFor(
'CreateAccount' )
982 ->getUserPermissionsErrors(
'createaccount', $creator,
'secure' );
985 foreach ( $permErrors
as $args ) {
995 $block->mReason ?:
wfMessage(
'blockednoreason' )->text(),
1000 $errorMessage =
'cantcreateaccount-range-text';
1001 $errorParams[] = $this->
getRequest()->getIP();
1003 $errorMessage =
'cantcreateaccount-text';
1006 return Status::newFatal(
wfMessage( $errorMessage, $errorParams ) );
1011 return Status::newFatal(
'sorbs_create_account_reason' );
1014 return Status::newGood();
1037 $session = $this->
request->getSession();
1040 $session->remove(
'AuthManager::accountCreationState' );
1041 throw new \LogicException(
'Account creation is not possible' );
1046 }
catch ( \UnexpectedValueException $ex ) {
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' )
1066 $username, [
'flags' => User::READ_LOCKING,
'creating' =>
true ]
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,
1115 $reqs, CreateFromLoginAuthenticationRequest::class
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'] = [];
1156 $user = User::newFromName( $state[
'username'],
'creatable' );
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'] ) {
1166 $creator = User::newFromId( $state[
'creatorid'] );
1168 $creator =
new User;
1169 $creator->
setName( $state[
'creatorname'] );
1173 $cache = \ObjectCache::getLocalClusterInstance();
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' );
1200 $user->load( User::READ_LOCKING );
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(),
1274 Status::wrap(
$status )->getMessage()
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' );
1412 \Hooks::run(
'LocalUserCreated', [
$user,
false ] );
1413 $user->saveSettings();
1414 $state[
'userid'] =
$user->getId();
1420 $user->addWatch(
$user->getUserPage(), User::IGNORE_USER_RIGHTS );
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() );
1436 $state[
'reqs'], CreationReasonAuthenticationRequest::class
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' );
1516 }
catch ( \Exception $ex ) {
1517 $session->remove(
'AuthManager::accountCreationState' );
1538 if (
$source !== self::AUTOCREATE_SOURCE_SESSION &&
1541 throw new \InvalidArgumentException(
"Unknown auto-creation source: $source" );
1547 $localId = User::idFromName(
$username );
1548 $flags = User::READ_NORMAL;
1553 if ( !$localId &&
wfGetLB()->getReaderIndex() != 0 ) {
1554 $localId = User::idFromName(
$username, User::READ_LATEST );
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}', [
1580 $user->loadFromId();
1586 $session = $this->
request->getSession();
1587 if ( $session->get(
'AuthManager::AutoCreateBlacklist' ) ) {
1588 $this->logger->debug( __METHOD__ .
': blacklisted in session {sessionid}', [
1590 'sessionid' => $session->getId(),
1593 $user->loadFromId();
1594 $reason = $session->get(
'AuthManager::AutoCreateBlacklist' );
1596 return Status::wrap( $reason );
1598 return Status::newFatal( $reason );
1603 if ( !User::isCreatableName(
$username ) ) {
1604 $this->logger->debug( __METHOD__ .
': name "{username}" is not creatable', [
1607 $session->set(
'AuthManager::AutoCreateBlacklist',
'noname' );
1609 $user->loadFromId();
1610 return Status::newFatal(
'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();
1623 $user->loadFromId();
1624 return Status::newFatal(
'authmanager-autocreate-noperm' );
1628 $cache = \ObjectCache::getLocalClusterInstance();
1631 $this->logger->debug( __METHOD__ .
': Could not acquire account creation lock', [
1635 $user->loadFromId();
1636 return Status::newFatal(
'usernameinprogress' );
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 );
1657 $user->loadFromId();
1663 if (
$cache->get( $backoffKey ) ) {
1664 $this->logger->debug( __METHOD__ .
': {username} denied by prior creation attempt failures', [
1668 $user->loadFromId();
1669 return Status::newFatal(
'authmanager-autocreate-exception' );
1673 $from = isset( $_SERVER[
'REQUEST_URI'] ) ? $_SERVER[
'REQUEST_URI'] :
'CLI';
1674 $this->logger->info( __METHOD__ .
': creating new user ({username}) - from: {from}', [
1680 $trxProfiler = \Profiler::instance()->getTransactionProfiler();
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' )
1702 $user->loadFromId();
1706 }
catch ( \Exception $ex ) {
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 ] );
1725 $user->saveSettings();
1730 \DeferredUpdates::addCallableUpdate(
function ()
use (
$user ) {
1731 $user->addWatch(
$user->getUserPage(), User::IGNORE_USER_RIGHTS );
1735 if ( $this->config->get(
'NewUserLog' ) ) {
1736 $logEntry = new \ManualLogEntry(
'newusers',
'autocreate' );
1737 $logEntry->setPerformer(
$user );
1738 $logEntry->setTarget(
$user->getUserPage() );
1739 $logEntry->setComment(
'' );
1740 $logEntry->setParameters( [
1741 '4::userid' =>
$user->getId(),
1743 $logEntry->insert();
1746 $trxProfiler->setSilenced( $old );
1752 return Status::newGood();
1785 $session = $this->
request->getSession();
1786 $session->remove(
'AuthManager::accountLinkState' );
1790 throw new \LogicException(
'Account linking is not possible' );
1793 if (
$user->getId() === 0 ) {
1794 if ( !User::isUsableName(
$user->getName() ) ) {
1797 $msg =
wfMessage(
'authmanager-userdoesnotexist',
$user->getName() );
1801 foreach ( $reqs
as $req ) {
1803 $req->returnToUrl = $returnToUrl;
1809 foreach ( $providers
as $id => $provider ) {
1812 $this->logger->debug( __METHOD__ .
": Account linking pre-check failed by $id", [
1813 'user' =>
$user->getName(),
1816 Status::wrap(
$status )->getMessage()
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", [
1841 'user' =>
$user->getName(),
1847 $this->logger->debug( __METHOD__ .
": Account linking failed by $id", [
1848 'user' =>
$user->getName(),
1859 $this->logger->debug( __METHOD__ .
": Account linking $res->status by $id", [
1860 'user' =>
$user->getName(),
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', [
1879 'user' =>
$user->getName(),
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'] = [];
1912 $user = User::newFromName( $state[
'username'],
'usable' );
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"
1974 }
catch ( \Exception $ex ) {
1975 $session->remove(
'AuthManager::accountLinkState' );
2007 $providerAction = $action;
2010 switch ( $action ) {
2019 $state = $this->
request->getSession()->getSecret(
'AuthManager::authnState' );
2020 return is_array( $state ) ? $state[
'continueRequests'] : [];
2023 $state = $this->
request->getSession()->getSecret(
'AuthManager::accountCreationState' );
2024 return is_array( $state ) ? $state[
'continueRequests'] : [];
2042 $state = $this->
request->getSession()->getSecret(
'AuthManager::accountLinkState' );
2043 return is_array( $state ) ? $state[
'continueRequests'] : [];
2053 throw new \DomainException( __METHOD__ .
": Invalid action \"$action\"" );
2072 $user =
$user ?: \RequestContext::getMain()->getUser();
2077 foreach ( $providers
as $provider ) {
2079 foreach ( $provider->getAuthenticationRequests( $providerAction,
$options )
as $req ) {
2084 if (
$req->required ) {
2090 !isset( $reqs[$id] )
2100 switch ( $providerAction ) {
2108 if (
$options[
'username'] !==
null ) {
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 ) {
2138 $req->action = $action;
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 ) {
2291 $provider = \ObjectFactory::getObjectFromSpec( $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 ) {
2330 PreAuthenticationProvider::class, $conf[
'preauth']
2341 if ( $this->primaryAuthenticationProviders ===
null ) {
2344 PrimaryAuthenticationProvider::class, $conf[
'primaryauth']
2355 if ( $this->secondaryAuthenticationProviders ===
null ) {
2358 SecondaryAuthenticationProvider::class, $conf[
'secondaryauth']
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 );
2387 \Hooks::run(
'UserLoggedIn', [
$user ] );
2399 $lang = $useContextLang ? \RequestContext::getMain()->getLanguage() :
$wgContLang;
2400 $user->setOption(
'language',
$lang->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;
Apache License January AND DISTRIBUTION Definitions License shall mean the terms and conditions for use
$wgAuth $wgAuth
Authentication plugin.
wfGetLB( $wiki=false)
Get a load balancer object.
wfReadOnly()
Check whether the wiki is in read-only mode.
wfMemcKey()
Make a cache key for the local wiki.
wfReadOnlyReason()
Check if the site is in read-only mode and return the message if so.
Class for handling updates to the site_stats table.
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.
isDnsBlacklisted( $ip, $checkWhitelist=false)
Whether the given IP is in a DNS blacklist.
setName( $str)
Set the user name.
getId()
Get the user's ID.
isBlockedFromCreateAccount()
Get whether the user is explicitly blocked from account creation.
The WebRequest class encapsulates getting at data passed in the URL or via a POSTed form stripping il...
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
when a variable name is used in a it is silently declared as a new local masking the global
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
this hook is for auditing only $req
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
the array() calling protocol came about after MediaWiki 1.4rc1.
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
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
either a unescaped string or a HtmlArmor object after in associative array form externallinks including delete and has completed for all link tables whether this was an auto creation default is conds Array Extra conditions for the No matching items in log is displayed if loglist is empty msgKey Array If you want a nice box with a set this to the key of the message First element is the message additional optional elements are parameters for the key that are processed with wfMessage() -> params() ->parseAsBlock() - offset Set to overwrite offset parameter in $wgRequest set to '' to unset offset - wrap String Wrap the message in html(usually something like "<div ...>$1</div>"). - flags Integer display flags(NO_ACTION_LINK, NO_EXTRA_USER_LINKS) 'LogException':Called before an exception(or PHP error) is logged. This is meant for integration with external error aggregation services
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
it s the revision text itself In either if gzip is the revision text is gzipped $flags
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
this hook is for auditing only or null if authentication failed before getting that far $username
Allows to change the fields on the form that will be generated $name
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
Interface for configuration instances.
if(!isset( $args[0])) $lang