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;
90 $this->restrictions = MWRestrictions::newFromJson( $row->bp_restrictions );
91 $this->grants = FormatJson::decode( $row->bp_grants );
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() {
207 list( $index, $options ) = DBAccessObjectUtils::getDBOptions( $this->flags );
209 $password = $db->selectField(
212 [
'bp_user' => $this->centralId,
'bp_app_id' => $this->appId ],
216 if ( $password ===
false ) {
217 return PasswordFactory::newInvalidPassword();
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();
263 return Status::newGood();
268 return Status::wrap( $statusValue );
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 ) {
318 [
'bp_password' => PasswordFactory::newInvalidPassword()->toString() ],
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();
369 return PasswordFactory::generateRandomPasswordString( max(
370 self::PASSWORD_MINLENGTH, $config->get( MainConfigNames::MinimalPasswordLength ) ) );
383 $sep = self::getSeparator();
385 if ( strlen( $password ) >= self::PASSWORD_MINLENGTH && strpos( $username, $sep ) !==
false ) {
387 if ( preg_match(
'/^[0-9a-w]{' . self::PASSWORD_MINLENGTH .
',}$/', $password ) ) {
388 return [ $username, $password ];
390 } elseif ( strlen( $password ) > self::PASSWORD_MINLENGTH && strpos( $password, $sep ) !==
false ) {
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 ) {
414 return Status::newFatal(
'botpasswords-disabled' );
417 $provider = SessionManager::singleton()->getProvider( BotPasswordSessionProvider::class );
419 return Status::newFatal(
'botpasswords-no-provider' );
423 $sep = self::getSeparator();
424 if ( strpos( $username, $sep ) ===
false ) {
425 return self::loginHook( $username,
null, Status::newFatal(
'botpasswords-invalid-name', $sep ) );
427 list( $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() ) {
436 return Status::newFatal(
'botpasswords-locked' );
440 if ( !empty( $passwordAttemptThrottle ) ) {
441 $throttle =
new Throttler( $passwordAttemptThrottle, [
442 'type' =>
'botpassword',
443 'cache' => ObjectCache::getLocalClusterInstance(),
445 $result = $throttle->increase( $user->getName(), $request->
getIP(), __METHOD__ );
447 $msg =
wfMessage(
'login-throttled' )->durationParams( $result[
'wait'] );
448 return self::loginHook( $user,
null, Status::newFatal( $msg ) );
453 $bp = self::newFromUser( $user, $appId );
455 return self::loginHook( $user, $bp,
456 Status::newFatal(
'botpasswords-not-exist', $name, $appId ) );
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,
469 Status::newFatal(
'botpasswords-needs-reset', $name, $appId ) );
471 if ( !$passwordObj->verify( $password ) ) {
472 return self::loginHook( $user, $bp, Status::newFatal(
'wrongpassword' ) );
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();
500 $extraData[
'appId'] = $name . self::getSeparator() . $bp->getAppId();
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 runner()
Get a HookRunner instance for calling hooks using the new interfaces.
Represents an invalid password hash.
A class to check request restrictions expressed as a JSON object.
A class containing constants representing the names of configuration variables.
Show an error when any operation involving passwords fails to run.
static newInvalidPassword()
Create an InvalidPassword.
Represents a password hash for use in authentication.
isOK()
Returns whether the operation completed.
isGood()
Returns whether the operation completed and didn't have any error or warnings.
Generic operation result class Has warning/error list, boolean status and arbitrary value.
getMessage( $shortContext=false, $longContext=false, $lang=null)
Get a bullet list of the errors as a Message object.
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.