69 $this->options = $options;
70 $this->centralIdLookup = $centralIdLookup;
71 $this->lbFactory = $lbFactory;
82 $loadBalancer = $this->lbFactory->getExternalLB(
86 $loadBalancer = $this->lbFactory->getMainLB(
90 return $loadBalancer->getConnectionRef(
107 int $flags = self::READ_NORMAL
113 $centralId = $this->centralIdLookup->centralIdFromLocalUser(
115 CentralIdLookup::AUDIENCE_RAW,
118 return $centralId ? $this->getByCentralId( $centralId, $appId, $flags ) : null;
131 int $flags = self::READ_NORMAL
137 [ $index, $options ] = DBAccessObjectUtils::getDBOptions( $flags );
138 $db = $this->getDatabase( $index );
139 $row = $db->newSelectQueryBuilder()
140 ->select( [
'bp_user',
'bp_app_id',
'bp_token',
'bp_restrictions',
'bp_grants' ] )
141 ->from(
'bot_passwords' )
142 ->where( [
'bp_user' => $centralId,
'bp_app_id' => $appId ] )
143 ->options( $options )
144 ->caller( __METHOD__ )->fetchRow();
145 return $row ?
new BotPassword( $row,
true, $flags ) : null;
162 int $flags = self::READ_NORMAL
164 if ( isset( $data[
'user'] ) && ( !$data[
'user'] instanceof
UserIdentity ) ) {
170 'bp_app_id' => trim( $data[
'appId'] ??
'' ),
171 'bp_token' =>
'**unsaved**',
172 'bp_restrictions' => $data[
'restrictions'] ?? MWRestrictions::newDefault(),
173 'bp_grants' => $data[
'grants'] ?? [],
177 $row->bp_app_id ===
'' ||
178 strlen( $row->bp_app_id ) > BotPassword::APPID_MAXLENGTH ||
180 !is_array( $row->bp_grants )
185 $row->bp_restrictions = $row->bp_restrictions->toJson();
186 $row->bp_grants = FormatJson::encode( $row->bp_grants );
188 if ( isset( $data[
'user'] ) ) {
190 $row->bp_user = $this->centralIdLookup->centralIdFromLocalUser(
192 CentralIdLookup::AUDIENCE_RAW,
195 } elseif ( isset( $data[
'username'] ) ) {
196 $row->bp_user = $this->centralIdLookup->centralIdFromName(
198 CentralIdLookup::AUDIENCE_RAW,
201 } elseif ( isset( $data[
'centralId'] ) ) {
202 $row->bp_user = $data[
'centralId'];
204 if ( !$row->bp_user ) {
208 return new BotPassword( $row,
false, $flags );
224 $res = $this->validateBotPassword( $botPassword );
225 if ( !$res->isGood() ) {
229 if ( $password ===
null ) {
230 $password = PasswordFactory::newInvalidPassword();
234 $dbw->newInsertQueryBuilder()
235 ->insertInto(
'bot_passwords' )
239 'bp_app_id' => $botPassword->
getAppId(),
240 'bp_token' => MWCryptRand::generateHex( User::TOKEN_LENGTH ),
242 'bp_grants' => FormatJson::encode( $botPassword->
getGrants() ),
243 'bp_password' => $password->toString(),
245 ->caller( __METHOD__ )->execute();
247 $ok = (bool)$dbw->affectedRows();
249 $token = $dbw->newSelectQueryBuilder()
250 ->select(
'bp_token' )
251 ->from(
'bot_passwords' )
253 ->caller( __METHOD__ )->fetchField();
254 return StatusValue::newGood( $token );
256 return StatusValue::newFatal(
'botpasswords-insert-failed', $botPassword->
getAppId() );
272 $res = $this->validateBotPassword( $botPassword );
273 if ( !$res->isGood() ) {
279 'bp_app_id' => $botPassword->
getAppId(),
282 'bp_token' => MWCryptRand::generateHex( User::TOKEN_LENGTH ),
284 'bp_grants' => FormatJson::encode( $botPassword->
getGrants() ),
286 if ( $password !==
null ) {
287 $fields[
'bp_password'] = $password->toString();
291 $dbw->newUpdateQueryBuilder()
292 ->update(
'bot_passwords' )
295 ->caller( __METHOD__ )->execute();
297 $ok = (bool)$dbw->affectedRows();
299 $token = $dbw->newSelectQueryBuilder()
300 ->select(
'bp_token' )
301 ->from(
'bot_passwords' )
303 ->caller( __METHOD__ )->fetchField();
304 return StatusValue::newGood( $token );
306 return StatusValue::newFatal(
'botpasswords-update-failed', $botPassword->
getAppId() );
316 private function validateBotPassword( BotPassword $botPassword ):
StatusValue {
319 $restrictions = $botPassword->getRestrictions()->toJson();
320 if ( strlen( $restrictions ) > BotPassword::RESTRICTIONS_MAXLENGTH ) {
321 $res->
fatal(
'botpasswords-toolong-restrictions' );
325 if ( strlen( $grants ) > BotPassword::GRANTS_MAXLENGTH ) {
326 $res->fatal(
'botpasswords-toolong-grants' );
340 $dbw->newDeleteQueryBuilder()
341 ->deleteFrom(
'bot_passwords' )
343 ->andWhere( [
'bp_app_id' => $botPassword->
getAppId() ] )
344 ->caller( __METHOD__ )->execute();
346 return (
bool)$dbw->affectedRows();
359 $centralId = $this->centralIdLookup->centralIdFromName(
361 CentralIdLookup::AUDIENCE_RAW,
362 CentralIdLookup::READ_LATEST
369 $dbw->newUpdateQueryBuilder()
370 ->update(
'bot_passwords' )
371 ->set( [
'bp_password' => PasswordFactory::newInvalidPassword()->toString() ] )
372 ->where( [
'bp_user' => $centralId ] )
373 ->caller( __METHOD__ )->execute();
374 return (
bool)$dbw->affectedRows();
387 $centralId = $this->centralIdLookup->centralIdFromName(
389 CentralIdLookup::AUDIENCE_RAW,
390 CentralIdLookup::READ_LATEST
397 $dbw->newDeleteQueryBuilder()
398 ->deleteFrom(
'bot_passwords' )
399 ->where( [
'bp_user' => $centralId ] )
400 ->caller( __METHOD__ )->execute();
401 return (
bool)$dbw->affectedRows();
if(!defined('MW_SETUP_CALLBACK'))
Helper class for DAO classes.
A class to check request restrictions expressed as a JSON object.
A class containing constants representing the names of configuration variables.
const BotPasswordsDatabase
Name constant for the BotPasswordsDatabase setting, for use with Config::get()
const EnableBotPasswords
Name constant for the EnableBotPasswords setting, for use with Config::get()
const BotPasswordsCluster
Name constant for the BotPasswordsCluster setting, for use with Config::get()
Factory class for creating and checking Password objects.
Represents a password hash for use in authentication.
Generic operation result class Has warning/error list, boolean status and arbitrary value.
fatal( $message,... $parameters)
Add an error and set OK to false, indicating that the operation as a whole was fatal.
Interface for database access objects.