43 use UnexpectedValueException;
84 private $restrictions;
99 public function __construct( $row, $isSaved, $flags = self::READ_NORMAL ) {
101 $this->flags = $flags;
103 $this->centralId = (int)$row->bp_user;
104 $this->appId = $row->bp_app_id;
105 $this->token = $row->bp_token;
115 public static function getDB( $db ) {
117 ->getBotPasswordStore()
118 ->getDatabase( $db );
130 ->getBotPasswordStore()
131 ->getByUser( $userIdentity, (
string)$appId, (
int)$flags );
141 public static function newFromCentralId( $centralId, $appId, $flags = self::READ_NORMAL ) {
143 ->getBotPasswordStore()
144 ->getByCentralId( (
int)$centralId, (
string)$appId, (
int)$flags );
159 public static function newUnsaved( array $data, $flags = self::READ_NORMAL ) {
161 ->getBotPasswordStore()
162 ->newUnsavedBotPassword( $data, (
int)$flags );
170 return $this->isSaved;
178 return $this->centralId;
199 return $this->restrictions;
206 return $this->grants;
216 return $userrightsInterwikiDelimiter;
222 private function getPassword() {
225 $password = $db->newSelectQueryBuilder()
226 ->select(
'bp_password' )
227 ->from(
'bot_passwords' )
228 ->where( [
'bp_user' => $this->centralId,
'bp_app_id' => $this->appId ] )
229 ->options( $options )
230 ->caller( __METHOD__ )->fetchField();
231 if ( $password ===
false ) {
237 return $passwordFactory->newFromCiphertext( $password );
261 if ( $operation !==
'insert' && $operation !==
'update' ) {
262 throw new UnexpectedValueException(
263 "Expected 'insert' or 'update'; got '{$operation}'."
268 if ( $operation ===
'insert' ) {
269 $statusValue = $store->insertBotPassword( $this, $password );
272 $statusValue = $store->updateBotPassword( $this, $password );
275 if ( $statusValue->isGood() ) {
276 $this->token = $statusValue->getValue();
290 public function delete() {
292 ->getBotPasswordStore()
293 ->deleteBotPassword( $this );
295 $this->token =
'**unsaved**';
308 ->getBotPasswordStore()
309 ->invalidateUserPasswords( (
string)$username );
326 if ( !$enableBotPasswords ) {
331 $dbw->newUpdateQueryBuilder()
332 ->update(
'bot_passwords' )
334 ->where( [
'bp_user' => $centralId ] )
335 ->caller( __METHOD__ )->execute();
336 return (
bool)$dbw->affectedRows();
346 ->getBotPasswordStore()
347 ->removeUserPasswords( (
string)$username );
364 if ( !$enableBotPasswords ) {
369 $dbw->newDeleteQueryBuilder()
370 ->deleteFrom(
'bot_passwords' )
371 ->where( [
'bp_user' => $centralId ] )
372 ->caller( __METHOD__ )->execute();
373 return (
bool)$dbw->affectedRows();
398 if ( strlen( $password ) >= self::PASSWORD_MINLENGTH && str_contains( $username, $sep ) ) {
400 if ( preg_match(
'/^[0-9a-w]{' . self::PASSWORD_MINLENGTH .
',}$/', $password ) ) {
401 return [ $username, $password ];
403 } elseif ( strlen( $password ) > self::PASSWORD_MINLENGTH && str_contains( $password, $sep ) ) {
404 $segments = explode( $sep, $password );
405 $password = array_pop( $segments );
406 $appId = implode( $sep, $segments );
407 if ( preg_match(
'/^[0-9a-w]{' . self::PASSWORD_MINLENGTH .
',}$/', $password ) ) {
408 return [ $username . $sep . $appId, $password ];
426 if ( !$enableBotPasswords ) {
437 if ( !str_contains( $username, $sep ) ) {
438 return self::loginHook( $username,
null,
Status::newFatal(
'botpasswords-invalid-name', $sep ) );
440 [ $name, $appId ] = explode( $sep, $username, 2 );
444 if ( !$user || $user->isAnon() ) {
445 return self::loginHook( $user ?: $name,
null,
Status::newFatal(
'nosuchuser', $name ) );
448 if ( $user->isLocked() ) {
453 if ( $passwordAttemptThrottle ) {
454 $throttle =
new Throttler( $passwordAttemptThrottle, [
455 'type' =>
'botpassword',
458 $result = $throttle->increase( $user->getName(), $request->
getIP(), __METHOD__ );
460 $msg =
wfMessage(
'login-throttled' )->durationParams( $result[
'wait'] );
468 return self::loginHook( $user, $bp,
473 $status = $bp->getRestrictions()->check( $request );
474 if ( !$status->
isOK() ) {
475 return self::loginHook( $user, $bp,
Status::newFatal(
'botpasswords-restriction-failed' ) );
479 $passwordObj = $bp->getPassword();
481 return self::loginHook( $user, $bp,
484 if ( !$passwordObj->verify( $password ) ) {
490 $throttle->clear( $user->getName(), $request->
getIP() );
492 return self::loginHook( $user, $bp,
494 Status::newGood( $provider->newSessionForRequest( $user, $bp, $request ) ) );
508 private static function loginHook( $user, $bp,
Status $status ) {
510 if ( $user instanceof
User ) {
511 $name = $user->getName();
520 if ( $status->
isGood() ) {
526 ->onAuthManagerLoginAuthenticateAudit( $response, $user, $name, $extraData );
536 class_alias( BotPassword::class,
'BotPassword' );
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.
Helper class for DAO classes.
static getDBOptions( $bitfield)
Get an appropriate DB index, options, and fallback DB index for a query.
Represents an invalid password hash.
A class to check request restrictions expressed as a JSON object.
static newFromJson( $json)
A class containing constants representing the names of configuration variables.
const MinimalPasswordLength
Name constant for the MinimalPasswordLength setting, for use with Config::get()
const PasswordAttemptThrottle
Name constant for the PasswordAttemptThrottle setting, for use with Config::get()
const UserrightsInterwikiDelimiter
Name constant for the UserrightsInterwikiDelimiter setting, for use with Config::get()
const EnableBotPasswords
Name constant for the EnableBotPasswords setting, for use with Config::get()
Functions to get cache objects.
static getLocalClusterInstance()
Get the main cluster-local cache object.
Show an error when any operation involving passwords fails to run.
Factory class for creating and checking Password objects.
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.
Interface for database access objects.