BotPasswords: Indicate when a password needs reset
authorBrad Jorsch <bjorsch@wikimedia.org>
Fri, 4 May 2018 13:35:55 +0000 (09:35 -0400)
committerReedy <reedy@wikimedia.org>
Sat, 7 Jul 2018 18:19:34 +0000 (18:19 +0000)
Certain things, such as changing the account's main login credentials,
causes all bot passwords to be invalidated. This state should be
indicated in Special:BotPasswords, and the API when login fails.

Bug: T193829
Change-Id: Ib12929fed861742c9f2f76702c9ac3254e8a5d97
(cherry picked from commit ff6b4cb35c1944870fcd3cc525884790c20819b3)

RELEASE-NOTES-1.31
includes/api/ApiLogin.php
includes/specials/SpecialBotPasswords.php
includes/user/BotPassword.php
languages/i18n/en.json
languages/i18n/qqq.json

index 4331e4f..f64c8fa 100644 (file)
@@ -10,6 +10,7 @@ This is a security and maintenance release of the MediaWiki 1.31 branch.
 * (T198687) Fix various selectFields methods to use the string 'NULL', not null.
 * Special:BotPasswords now requires reauthentication.
 * (T191608, T187638) Add 'logid' parameter to Special:Log.
 * (T198687) Fix various selectFields methods to use the string 'NULL', not null.
 * Special:BotPasswords now requires reauthentication.
 * (T191608, T187638) Add 'logid' parameter to Special:Log.
+* (T193829) Indicate when a Bot Password needs reset.
 
 == MediaWiki 1.31 ==
 
 
 == MediaWiki 1.31 ==
 
index e4c4429..0248f25 100644 (file)
@@ -130,7 +130,10 @@ class ApiLogin extends ApiBase {
                                $session = $status->getValue();
                                $authRes = 'Success';
                                $loginType = 'BotPassword';
                                $session = $status->getValue();
                                $authRes = 'Success';
                                $loginType = 'BotPassword';
-                       } elseif ( !$botLoginData[2] || $status->hasMessage( 'login-throttled' ) ) {
+                       } elseif ( !$botLoginData[2] ||
+                               $status->hasMessage( 'login-throttled' ) ||
+                               $status->hasMessage( 'botpasswords-needs-reset' )
+                       ) {
                                $authRes = 'Failed';
                                $message = $status->getMessage();
                                LoggerFactory::getInstance( 'authentication' )->info(
                                $authRes = 'Failed';
                                $message = $status->getMessage();
                                LoggerFactory::getInstance( 'authentication' )->info(
index c912e83..961ee1c 100644 (file)
@@ -111,6 +111,9 @@ class SpecialBotPasswords extends FormSpecialPage {
                                        'type' => 'check',
                                        'label-message' => 'botpasswords-label-resetpassword',
                                ];
                                        'type' => 'check',
                                        'label-message' => 'botpasswords-label-resetpassword',
                                ];
+                               if ( $this->botPassword->isInvalid() ) {
+                                       $fields['resetPassword']['default'] = true;
+                               }
                        }
 
                        $lang = $this->getLanguage();
                        }
 
                        $lang = $this->getLanguage();
@@ -157,22 +160,39 @@ class SpecialBotPasswords extends FormSpecialPage {
 
                } else {
                        $linkRenderer = $this->getLinkRenderer();
 
                } else {
                        $linkRenderer = $this->getLinkRenderer();
+                       $passwordFactory = new PasswordFactory();
+                       $passwordFactory->init( $this->getConfig() );
+
                        $dbr = BotPassword::getDB( DB_REPLICA );
                        $res = $dbr->select(
                                'bot_passwords',
                        $dbr = BotPassword::getDB( DB_REPLICA );
                        $res = $dbr->select(
                                'bot_passwords',
-                               [ 'bp_app_id' ],
+                               [ 'bp_app_id', 'bp_password' ],
                                [ 'bp_user' => $this->userId ],
                                __METHOD__
                        );
                        foreach ( $res as $row ) {
                                [ 'bp_user' => $this->userId ],
                                __METHOD__
                        );
                        foreach ( $res as $row ) {
+                               try {
+                                       $password = $passwordFactory->newFromCiphertext( $row->bp_password );
+                                       $passwordInvalid = $password instanceof InvalidPassword;
+                                       unset( $password );
+                               } catch ( PasswordError $ex ) {
+                                       $passwordInvalid = true;
+                               }
+
+                               $text = $linkRenderer->makeKnownLink(
+                                       $this->getPageTitle( $row->bp_app_id ),
+                                       $row->bp_app_id
+                               );
+                               if ( $passwordInvalid ) {
+                                       $text .= $this->msg( 'word-separator' )->escaped()
+                                               . $this->msg( 'botpasswords-label-needsreset' )->parse();
+                               }
+
                                $fields[] = [
                                        'section' => 'existing',
                                        'type' => 'info',
                                        'raw' => true,
                                $fields[] = [
                                        'section' => 'existing',
                                        'type' => 'info',
                                        'raw' => true,
-                                       'default' => $linkRenderer->makeKnownLink(
-                                               $this->getPageTitle( $row->bp_app_id ),
-                                               $row->bp_app_id
-                                       ),
+                                       'default' => $text,
                                ];
                        }
 
                                ];
                        }
 
index f102d49..6b8153c 100644 (file)
@@ -261,6 +261,15 @@ class BotPassword implements IDBAccessObject {
                }
        }
 
                }
        }
 
+       /**
+        * Whether the password is currently invalid
+        * @since 1.32
+        * @return bool
+        */
+       public function isInvalid() {
+               return $this->getPassword() instanceof InvalidPassword;
+       }
+
        /**
         * Save the BotPassword to the database
         * @param string $operation 'update' or 'insert'
        /**
         * Save the BotPassword to the database
         * @param string $operation 'update' or 'insert'
@@ -491,7 +500,11 @@ class BotPassword implements IDBAccessObject {
                }
 
                // Check the password
                }
 
                // Check the password
-               if ( !$bp->getPassword()->equals( $password ) ) {
+               $passwordObj = $bp->getPassword();
+               if ( $passwordObj instanceof InvalidPassword ) {
+                       return Status::newFatal( 'botpasswords-needs-reset', $name, $appId );
+               }
+               if ( !$passwordObj->equals( $password ) ) {
                        return Status::newFatal( 'wrongpassword' );
                }
 
                        return Status::newFatal( 'wrongpassword' );
                }
 
index 168ce07..331dabe 100644 (file)
        "botpasswords-existing": "Existing bot passwords",
        "botpasswords-createnew": "Create a new bot password",
        "botpasswords-editexisting": "Edit an existing bot password",
        "botpasswords-existing": "Existing bot passwords",
        "botpasswords-createnew": "Create a new bot password",
        "botpasswords-editexisting": "Edit an existing bot password",
+       "botpasswords-label-needsreset": "(password needs reset)",
        "botpasswords-label-appid": "Bot name:",
        "botpasswords-label-create": "Create",
        "botpasswords-label-update": "Update",
        "botpasswords-label-appid": "Bot name:",
        "botpasswords-label-create": "Create",
        "botpasswords-label-update": "Update",
        "botpasswords-restriction-failed": "Bot password restrictions prevent this login.",
        "botpasswords-invalid-name": "The username specified does not contain the bot password separator (\"$1\").",
        "botpasswords-not-exist": "User \"$1\" does not have a bot password named \"$2\".",
        "botpasswords-restriction-failed": "Bot password restrictions prevent this login.",
        "botpasswords-invalid-name": "The username specified does not contain the bot password separator (\"$1\").",
        "botpasswords-not-exist": "User \"$1\" does not have a bot password named \"$2\".",
+       "botpasswords-needs-reset": "The bot password for bot name \"$2\" of {{GENDER:$1|user}} \"$1\" must be reset.",
        "resetpass_forbidden": "Passwords cannot be changed",
        "resetpass_forbidden-reason": "Passwords cannot be changed: $1",
        "resetpass-no-info": "You must be logged in to access this page directly.",
        "resetpass_forbidden": "Passwords cannot be changed",
        "resetpass_forbidden-reason": "Passwords cannot be changed: $1",
        "resetpass-no-info": "You must be logged in to access this page directly.",
index e7da4c6..a4927b6 100644 (file)
        "botpasswords-existing": "Form section label for the part of the form listing the user's existing bot passwords.",
        "botpasswords-createnew": "Form section label for the part of the form related to creating a new bot password.",
        "botpasswords-editexisting": "Form section label for the part of the form related to editing an existing bot password.",
        "botpasswords-existing": "Form section label for the part of the form listing the user's existing bot passwords.",
        "botpasswords-createnew": "Form section label for the part of the form related to creating a new bot password.",
        "botpasswords-editexisting": "Form section label for the part of the form related to editing an existing bot password.",
+       "botpasswords-label-needsreset": "Indicator for when an existing bot password is invalid and needs to be reset.",
        "botpasswords-label-appid": "Form field label for the \"bot name\", internally known as the \"application ID\".",
        "botpasswords-label-create": "Button label for the button to create a new bot password.\n{{Identical|Create}}",
        "botpasswords-label-update": "Button label for the button to save changes to a bot password.\n{{Identical|Update}}",
        "botpasswords-label-appid": "Form field label for the \"bot name\", internally known as the \"application ID\".",
        "botpasswords-label-create": "Button label for the button to create a new bot password.\n{{Identical|Create}}",
        "botpasswords-label-update": "Button label for the button to save changes to a bot password.\n{{Identical|Update}}",
        "botpasswords-restriction-failed": "Error message when login is rejected because the configured restrictions were not satisfied.",
        "botpasswords-invalid-name": "Error message when a username lacking the separator character is passed to BotPassword. Parameters:\n* $1 - The separator character.",
        "botpasswords-not-exist": "Error message when a username exists but does not a bot password for the given \"bot name\". Parameters:\n* $1 - username\n* $2 - bot name",
        "botpasswords-restriction-failed": "Error message when login is rejected because the configured restrictions were not satisfied.",
        "botpasswords-invalid-name": "Error message when a username lacking the separator character is passed to BotPassword. Parameters:\n* $1 - The separator character.",
        "botpasswords-not-exist": "Error message when a username exists but does not a bot password for the given \"bot name\". Parameters:\n* $1 - username\n* $2 - bot name",
+       "botpasswords-needs-reset": "Error message when a bot password exists but needs to be reset. Parameters:\n* $1 - username\n* $2 - bot name",
        "resetpass_forbidden": "Used as error message in changing password. Maybe the external auth plugin won't allow local password changes.",
        "resetpass_forbidden-reason": "Like {{msg-mw|resetpass_forbidden}} but the auth provider gave a reason.\n\nParameters:\n* $1 - reason given by auth provider",
        "resetpass-no-info": "Error message for [[Special:ChangePassword]].\n\nParameters:\n* $1 (unused) - a link to [[Special:UserLogin]] with {{msg-mw|loginreqlink}} as link description",
        "resetpass_forbidden": "Used as error message in changing password. Maybe the external auth plugin won't allow local password changes.",
        "resetpass_forbidden-reason": "Like {{msg-mw|resetpass_forbidden}} but the auth provider gave a reason.\n\nParameters:\n* $1 - reason given by auth provider",
        "resetpass-no-info": "Error message for [[Special:ChangePassword]].\n\nParameters:\n* $1 (unused) - a link to [[Special:UserLogin]] with {{msg-mw|loginreqlink}} as link description",