Aaron Schulz has uploaded a new change for review.

  https://gerrit.wikimedia.org/r/258729

Change subject: Avoid "CAS updated failed" errors on Special:Preferences double 
post
......................................................................

Avoid "CAS updated failed" errors on Special:Preferences double post

* This does the same thing ApiOptions does to avoid these errors.
  A new getInstanceForUpdate() method is now in the User class to
  simplify this pattern.
* Avoid overriding $user in ApiOptions for code readability.
* Fixed IDEA errors around Preferences::getFormObject() return type.

Bug: T95839
Change-Id: If2385b7486c043bd70d7031ff35e37dfb079a4d2
---
M includes/Preferences.php
M includes/api/ApiOptions.php
M includes/specials/SpecialPreferences.php
M includes/user/User.php
4 files changed, 33 insertions(+), 14 deletions(-)


  git pull ssh://gerrit.wikimedia.org:29418/mediawiki/core 
refs/changes/29/258729/1

diff --git a/includes/Preferences.php b/includes/Preferences.php
index c7ab9cd..7d1f35e 100644
--- a/includes/Preferences.php
+++ b/includes/Preferences.php
@@ -1274,7 +1274,7 @@
         * @param IContextSource $context
         * @param string $formClass
         * @param array $remove Array of items to remove
-        * @return HtmlForm
+        * @return PreferencesForm|HtmlForm
         */
        static function getFormObject(
                $user,
diff --git a/includes/api/ApiOptions.php b/includes/api/ApiOptions.php
index 74ce053..7a90527 100644
--- a/includes/api/ApiOptions.php
+++ b/includes/api/ApiOptions.php
@@ -35,14 +35,10 @@
         * Changes preferences of the current user.
         */
        public function execute() {
-               $user = $this->getUser();
-
-               if ( $user->isAnon() ) {
+               if ( $this->getUser()->isAnon() ) {
                        $this->dieUsage( 'Anonymous users cannot change 
preferences', 'notloggedin' );
-               }
-
-               if ( !$user->isAllowed( 'editmyoptions' ) ) {
-                       $this->dieUsage( 'You don\'t have permission to edit 
your options', 'permissiondenied' );
+               } elseif ( !$this->getUser()->isAllowed( 'editmyoptions' ) ) {
+                       $this->dieUsage( "You don't have permission to edit 
your options", 'permissiondenied' );
                }
 
                $params = $this->extractRequestParams();
@@ -53,11 +49,9 @@
                }
 
                // Load the user from the master to reduce CAS errors on double 
post (T95839)
-               if ( wfGetLB()->getServerCount() > 1 ) {
-                       $user = User::newFromId( $user->getId() );
-                       if ( !$user->loadFromId( User::READ_EXCLUSIVE ) ) {
-                               $this->dieUsage( 'Anonymous users cannot change 
preferences', 'notloggedin' );
-                       }
+               $user = $this->getUser()->getInstanceForUpdate();
+               if ( !$user ) {
+                       $this->dieUsage( 'Anonymous users cannot change 
preferences', 'notloggedin' );
                }
 
                if ( $params['reset'] ) {
diff --git a/includes/specials/SpecialPreferences.php 
b/includes/specials/SpecialPreferences.php
index 3b5b9c9..49ab6d5 100644
--- a/includes/specials/SpecialPreferences.php
+++ b/includes/specials/SpecialPreferences.php
@@ -65,7 +65,10 @@
 
                $this->addHelpLink( 'Help:Preferences' );
 
-               $htmlForm = Preferences::getFormObject( $this->getUser(), 
$this->getContext() );
+               // Load the user from the master to reduce CAS errors on double 
post (T95839)
+               $user = $this->getUser()->getInstanceForUpdate() ?: 
$this->getUser();
+
+               $htmlForm = Preferences::getFormObject( $user, 
$this->getContext() );
                $htmlForm->setSubmitCallback( array( 'Preferences', 
'tryUISubmit' ) );
                $sectionTitles = $htmlForm->getPreferenceSections();
 
diff --git a/includes/user/User.php b/includes/user/User.php
index 3d1aa7e..84b8bdc 100644
--- a/includes/user/User.php
+++ b/includes/user/User.php
@@ -5322,6 +5322,28 @@
        }
 
        /**
+        * Get a new instance of this user that was loaded from the master via 
a locking read
+        *
+        * Use this instead of the main context User when updating that user. 
This avoids races
+        * where that user was loaded from a slave or even the master but 
without proper locks.
+        *
+        * @return User|null Returns null if the user was not found in the DB
+        * @since 1.27
+        */
+       public function getInstanceForUpdate() {
+               if ( !$this->getId() ) {
+                       return null; // anon
+               }
+
+               $user = self::newFromId( $this->getId() );
+               if ( !$user->loadFromId( self::READ_EXCLUSIVE ) ) {
+                       return null;
+               }
+
+               return $user;
+       }
+
+       /**
         * Checks if two user objects point to the same user.
         *
         * @since 1.25

-- 
To view, visit https://gerrit.wikimedia.org/r/258729
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: If2385b7486c043bd70d7031ff35e37dfb079a4d2
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/core
Gerrit-Branch: master
Gerrit-Owner: Aaron Schulz <[email protected]>

_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to