* http://www.gnu.org/copyleft/gpl.html
*/
+use MediaWiki\Session\BotPasswordSessionProvider;
+
/**
* Utility class for bot passwords
* @since 1.27
/**
* Get a database connection for the bot passwords database
- * @param int $db Index of the connection to get, e.g. DB_MASTER or DB_SLAVE.
+ * @param int $db Index of the connection to get, e.g. DB_MASTER or DB_REPLICA.
* @return DatabaseBase
*/
public static function getDB( $db ) {
$lb = $wgBotPasswordsCluster
? wfGetLBFactory()->getExternalLB( $wgBotPasswordsCluster )
: wfGetLB( $wgBotPasswordsDatabase );
- return $lb->getConnectionRef( $db, array(), $wgBotPasswordsDatabase );
+ return $lb->getConnectionRef( $db, [], $wgBotPasswordsDatabase );
}
/**
$db = self::getDB( $index );
$row = $db->selectRow(
'bot_passwords',
- array( 'bp_user', 'bp_app_id', 'bp_token', 'bp_restrictions', 'bp_grants' ),
- array( 'bp_user' => $centralId, 'bp_app_id' => $appId ),
+ [ 'bp_user', 'bp_app_id', 'bp_token', 'bp_restrictions', 'bp_grants' ],
+ [ 'bp_user' => $centralId, 'bp_app_id' => $appId ],
__METHOD__,
$options
);
* @return BotPassword|null
*/
public static function newUnsaved( array $data, $flags = self::READ_NORMAL ) {
- $row = (object)array(
+ $row = (object)[
'bp_user' => 0,
'bp_app_id' => isset( $data['appId'] ) ? trim( $data['appId'] ) : '',
'bp_token' => '**unsaved**',
'bp_restrictions' => isset( $data['restrictions'] )
? $data['restrictions']
: MWRestrictions::newDefault(),
- 'bp_grants' => isset( $data['grants'] ) ? $data['grants'] : array(),
- );
+ 'bp_grants' => isset( $data['grants'] ) ? $data['grants'] : [],
+ ];
if (
$row->bp_app_id === '' || strlen( $row->bp_app_id ) > self::APPID_MAXLENGTH ||
$password = $db->selectField(
'bot_passwords',
'bp_password',
- array( 'bp_user' => $this->centralId, 'bp_app_id' => $this->appId ),
+ [ 'bp_user' => $this->centralId, 'bp_app_id' => $this->appId ],
__METHOD__,
$options
);
* @return bool Success
*/
public function save( $operation, Password $password = null ) {
- $conds = array(
+ $conds = [
'bp_user' => $this->centralId,
'bp_app_id' => $this->appId,
- );
- $fields = array(
+ ];
+ $fields = [
'bp_token' => MWCryptRand::generateHex( User::TOKEN_LENGTH ),
'bp_restrictions' => $this->restrictions->toJson(),
'bp_grants' => FormatJson::encode( $this->grants ),
- );
+ ];
if ( $password !== null ) {
$fields['bp_password'] = $password->toString();
$dbw = self::getDB( DB_MASTER );
switch ( $operation ) {
case 'insert':
- $dbw->insert( 'bot_passwords', $fields + $conds, __METHOD__, array( 'IGNORE' ) );
+ $dbw->insert( 'bot_passwords', $fields + $conds, __METHOD__, [ 'IGNORE' ] );
break;
case 'update':
* @return bool Success
*/
public function delete() {
- $conds = array(
+ $conds = [
'bp_user' => $this->centralId,
'bp_app_id' => $this->appId,
- );
+ ];
$dbw = self::getDB( DB_MASTER );
$dbw->delete( 'bot_passwords', $conds, __METHOD__ );
$ok = (bool)$dbw->affectedRows();
$dbw = self::getDB( DB_MASTER );
$dbw->update(
'bot_passwords',
- array( 'bp_password' => PasswordFactory::newInvalidPassword()->toString() ),
- array( 'bp_user' => $centralId ),
+ [ 'bp_password' => PasswordFactory::newInvalidPassword()->toString() ],
+ [ 'bp_user' => $centralId ],
__METHOD__
);
return (bool)$dbw->affectedRows();
$dbw = self::getDB( DB_MASTER );
$dbw->delete(
'bot_passwords',
- array( 'bp_user' => $centralId ),
+ [ 'bp_user' => $centralId ],
__METHOD__
);
return (bool)$dbw->affectedRows();
}
+ /**
+ * Returns a (raw, unhashed) random password string.
+ * @param Config $config
+ * @return string
+ */
+ public static function generatePassword( $config ) {
+ return PasswordFactory::generateRandomPasswordString(
+ max( 32, $config->get( 'MinimalPasswordLength' ) ) );
+ }
+
+ /**
+ * There are two ways to login with a bot password: "username@appId", "password" and
+ * "username", "appId@password". Transform it so it is always in the first form.
+ * Returns [bot username, bot password, could be normal password?] where the last one is a flag
+ * meaning this could either be a bot password or a normal password, it cannot be decided for
+ * certain (although in such cases it almost always will be a bot password).
+ * If this cannot be a bot password login just return false.
+ * @param string $username
+ * @param string $password
+ * @return array|false
+ */
+ public static function canonicalizeLoginData( $username, $password ) {
+ $sep = BotPassword::getSeparator();
+ if ( strpos( $username, $sep ) !== false ) {
+ // the separator is not valid in usernames so this must be a bot login
+ return [ $username, $password, false ];
+ } elseif ( strlen( $password ) > 32 && strpos( $password, $sep ) !== false ) {
+ // the strlen check helps minimize the password information obtainable from timing
+ $segments = explode( $sep, $password );
+ $password = array_pop( $segments );
+ $appId = implode( $sep, $segments );
+ if ( preg_match( '/^[0-9a-w]{32,}$/', $password ) ) {
+ return [ $username . $sep . $appId, $password, true ];
+ }
+ }
+ return false;
+ }
+
/**
* Try to log the user in
* @param string $username Combined user name and app ID
}
$manager = MediaWiki\Session\SessionManager::singleton();
- $provider = $manager->getProvider(
- 'MediaWiki\\Session\\BotPasswordSessionProvider'
- );
+ $provider = $manager->getProvider( BotPasswordSessionProvider::class );
if ( !$provider ) {
return Status::newFatal( 'botpasswords-no-provider' );
}
// Check restrictions
$status = $bp->getRestrictions()->check( $request );
- if ( !$status->isOk() ) {
+ if ( !$status->isOK() ) {
return Status::newFatal( 'botpasswords-restriction-failed' );
}