68 private $restrictions;
83 public function __construct( $row, $isSaved, $flags = self::READ_NORMAL ) {
85 $this->flags = $flags;
87 $this->centralId = (int)$row->bp_user;
88 $this->appId = $row->bp_app_id;
89 $this->token = $row->bp_token;
99 public static function getDB( $db ) {
100 return MediaWikiServices::getInstance()
101 ->getBotPasswordStore()
102 ->getDatabase( $db );
113 return MediaWikiServices::getInstance()
114 ->getBotPasswordStore()
115 ->getByUser( $userIdentity, (
string)$appId, (
int)$flags );
125 public static function newFromCentralId( $centralId, $appId, $flags = self::READ_NORMAL ) {
126 return MediaWikiServices::getInstance()
127 ->getBotPasswordStore()
128 ->getByCentralId( (
int)$centralId, (
string)$appId, (
int)$flags );
143 public static function newUnsaved( array $data, $flags = self::READ_NORMAL ) {
144 return MediaWikiServices::getInstance()
145 ->getBotPasswordStore()
146 ->newUnsavedBotPassword( $data, (
int)$flags );
154 return $this->isSaved;
162 return $this->centralId;
183 return $this->restrictions;
190 return $this->grants;
198 $userrightsInterwikiDelimiter = MediaWikiServices::getInstance()
199 ->getMainConfig()->get( MainConfigNames::UserrightsInterwikiDelimiter );
200 return $userrightsInterwikiDelimiter;
206 private function getPassword() {
209 $password = $db->selectField(
212 [
'bp_user' => $this->centralId,
'bp_app_id' => $this->appId ],
216 if ( $password ===
false ) {
220 $passwordFactory = MediaWikiServices::getInstance()->getPasswordFactory();
222 return $passwordFactory->newFromCiphertext( $password );
246 if ( $operation !==
'insert' && $operation !==
'update' ) {
247 throw new UnexpectedValueException(
248 "Expected 'insert' or 'update'; got '{$operation}'."
252 $store = MediaWikiServices::getInstance()->getBotPasswordStore();
253 if ( $operation ===
'insert' ) {
254 $statusValue = $store->insertBotPassword( $this, $password );
257 $statusValue = $store->updateBotPassword( $this, $password );
260 if ( $statusValue->isGood() ) {
261 $this->token = $statusValue->getValue();
275 public function delete() {
276 $ok = MediaWikiServices::getInstance()
277 ->getBotPasswordStore()
278 ->deleteBotPassword( $this );
280 $this->token =
'**unsaved**';
292 return MediaWikiServices::getInstance()
293 ->getBotPasswordStore()
294 ->invalidateUserPasswords( (
string)$username );
308 $enableBotPasswords = MediaWikiServices::getInstance()->getMainConfig()
309 ->get( MainConfigNames::EnableBotPasswords );
311 if ( !$enableBotPasswords ) {
319 [
'bp_user' => $centralId ],
322 return (
bool)$dbw->affectedRows();
331 return MediaWikiServices::getInstance()
332 ->getBotPasswordStore()
333 ->removeUserPasswords( (
string)$username );
347 $enableBotPasswords = MediaWikiServices::getInstance()->getMainConfig()
348 ->get( MainConfigNames::EnableBotPasswords );
350 if ( !$enableBotPasswords ) {
357 [
'bp_user' => $centralId ],
360 return (
bool)$dbw->affectedRows();
370 self::PASSWORD_MINLENGTH, $config->get( MainConfigNames::MinimalPasswordLength ) ) );
385 if ( strlen( $password ) >= self::PASSWORD_MINLENGTH && str_contains( $username, $sep ) ) {
387 if ( preg_match(
'/^[0-9a-w]{' . self::PASSWORD_MINLENGTH .
',}$/', $password ) ) {
388 return [ $username, $password ];
390 } elseif ( strlen( $password ) > self::PASSWORD_MINLENGTH && str_contains( $password, $sep ) ) {
391 $segments = explode( $sep, $password );
392 $password = array_pop( $segments );
393 $appId = implode( $sep, $segments );
394 if ( preg_match(
'/^[0-9a-w]{' . self::PASSWORD_MINLENGTH .
',}$/', $password ) ) {
395 return [ $username . $sep . $appId, $password ];
409 $enableBotPasswords = MediaWikiServices::getInstance()->getMainConfig()
410 ->get( MainConfigNames::EnableBotPasswords );
411 $passwordAttemptThrottle = MediaWikiServices::getInstance()->getMainConfig()
412 ->get( MainConfigNames::PasswordAttemptThrottle );
413 if ( !$enableBotPasswords ) {
417 $provider = SessionManager::singleton()->getProvider( BotPasswordSessionProvider::class );
424 if ( !str_contains( $username, $sep ) ) {
425 return self::loginHook( $username,
null,
Status::newFatal(
'botpasswords-invalid-name', $sep ) );
427 [ $name, $appId ] = explode( $sep, $username, 2 );
431 if ( !$user || $user->isAnon() ) {
432 return self::loginHook( $user ?: $name,
null,
Status::newFatal(
'nosuchuser', $name ) );
435 if ( $user->isLocked() ) {
440 if ( !empty( $passwordAttemptThrottle ) ) {
441 $throttle =
new Throttler( $passwordAttemptThrottle, [
442 'type' =>
'botpassword',
445 $result = $throttle->increase( $user->getName(), $request->
getIP(), __METHOD__ );
447 $msg =
wfMessage(
'login-throttled' )->durationParams( $result[
'wait'] );
455 return self::loginHook( $user, $bp,
460 $status = $bp->getRestrictions()->check( $request );
461 if ( !$status->
isOK() ) {
462 return self::loginHook( $user, $bp,
Status::newFatal(
'botpasswords-restriction-failed' ) );
466 $passwordObj = $bp->getPassword();
468 return self::loginHook( $user, $bp,
471 if ( !$passwordObj->verify( $password ) ) {
477 $throttle->clear( $user->getName(), $request->
getIP() );
479 return self::loginHook( $user, $bp,
481 Status::newGood( $provider->newSessionForRequest( $user, $bp, $request ) ) );
495 private static function loginHook( $user, $bp,
Status $status ) {
497 if ( $user instanceof
User ) {
498 $name = $user->getName();
507 if ( $status->
isGood() ) {
508 $response = AuthenticationResponse::newPass( $name );
510 $response = AuthenticationResponse::newFail( $status->
getMessage() );
512 Hooks::runner()->onAuthManagerLoginAuthenticateAudit( $response, $user, $name, $extraData );
wfMessage( $key,... $params)
This is the function for getting translated interface messages.
wfDeprecated( $function, $version=false, $component=false, $callerOffset=2)
Logs a warning that a deprecated feature was used.
Utility class for bot passwords.
isInvalid()
Whether the password is currently invalid.
__construct( $row, $isSaved, $flags=self::READ_NORMAL)
static newFromUser(UserIdentity $userIdentity, $appId, $flags=self::READ_NORMAL)
Load a BotPassword from the database.
static invalidateAllPasswordsForUser( $username)
Invalidate all passwords for a user, by name.
static generatePassword( $config)
Returns a (raw, unhashed) random password string.
const PASSWORD_MINLENGTH
Minimum length for a bot password.
getUserCentralId()
Get the central user ID.
static getDB( $db)
Get a database connection for the bot passwords database.
const RESTRICTIONS_MAXLENGTH
Maximum length of the json representation of restrictions.
static newFromCentralId( $centralId, $appId, $flags=self::READ_NORMAL)
Load a BotPassword from the database.
const GRANTS_MAXLENGTH
Maximum length of the json representation of grants.
static login( $username, $password, WebRequest $request)
Try to log the user in.
static invalidateAllPasswordsForCentralId( $centralId)
Invalidate all passwords for a user, by central ID.
isSaved()
Indicate whether this is known to be saved.
static removeAllPasswordsForUser( $username)
Remove all passwords for a user, by name.
static removeAllPasswordsForCentralId( $centralId)
Remove all passwords for a user, by central ID.
static newUnsaved(array $data, $flags=self::READ_NORMAL)
Create an unsaved BotPassword.
static getSeparator()
Get the separator for combined user name + app ID.
save( $operation, Password $password=null)
Save the BotPassword to the database.
static canonicalizeLoginData( $username, $password)
There are two ways to login with a bot password: "username@appId", "password" and "username",...
static getDBOptions( $bitfield)
Get an appropriate DB index, options, and fallback DB index for a query.
static runner()
Get a HookRunner instance for calling hooks using the new interfaces.
Represents an invalid password hash.
static newFromJson( $json)
A class containing constants representing the names of configuration variables.
static getLocalClusterInstance()
Get the main cluster-local cache object.
Show an error when any operation involving passwords fails to run.
static generateRandomPasswordString(int $minLength=10)
Generate a random string suitable for a password.
static newInvalidPassword()
Create an InvalidPassword.
Represents a password hash for use in authentication.
static newFatal( $message,... $parameters)
Factory function for fatal errors.
isOK()
Returns whether the operation completed.
isGood()
Returns whether the operation completed and didn't have any error or warnings.
static newGood( $value=null)
Factory function for good results.
Generic operation result class Has warning/error list, boolean status and arbitrary value.
static wrap( $sv)
Succinct helper method to wrap a StatusValue.
getMessage( $shortContext=false, $longContext=false, $lang=null)
Get a bullet list of the errors as a Message object.
The User object encapsulates all of the user-specific settings (user_id, name, rights,...
static newFromName( $name, $validate='valid')
The WebRequest class encapsulates getting at data passed in the URL or via a POSTed form stripping il...
getIP()
Work out the IP address based on various globals For trusted proxies, use the XFF client IP (first of...
Interface for database access objects.