user: Move UserRightsProxy::invalidateCache to UserFactory
To remove the use of UserRightsProxy from Special:UserRights the touch update happen in UserRightsProxy::invalidateCache needs a new location, before a cross-wiki aware UserIdentityLookup can be used. Move it to the UserFactory service which is the storage layer for the user table to handle user_touched database field for cross-wiki user right changes. For compatibility call the User::invalidateCache for local identities Bug: T255309 Bug: T307301 Depends-On: I60a665de6aa8550d9bc0f5c78d54b8894ea5913e Change-Id: I0c3d36a05abaa3548e554acf8d34e8e959c26776
This commit is contained in:
parent
9a7e435e79
commit
9116c38745
6 changed files with 134 additions and 26 deletions
|
|
@ -2101,7 +2101,10 @@ return [
|
|||
|
||||
'UserFactory' => static function ( MediaWikiServices $services ): UserFactory {
|
||||
return new UserFactory(
|
||||
$services->getDBLoadBalancer(),
|
||||
new ServiceOptions(
|
||||
UserFactory::CONSTRUCTOR_OPTIONS, $services->getMainConfig()
|
||||
),
|
||||
$services->getDBLoadBalancerFactory(),
|
||||
$services->getUserNameUtils()
|
||||
);
|
||||
},
|
||||
|
|
|
|||
|
|
@ -460,7 +460,7 @@ class SpecialUserRights extends SpecialPage {
|
|||
$newUGMs = $userGroupManager->getUserGroupMemberships( $user );
|
||||
|
||||
// Ensure that caches are cleared
|
||||
$user->invalidateCache();
|
||||
$this->userFactory->invalidateCache( $user );
|
||||
|
||||
// update groups in external authentication database
|
||||
$this->getHookRunner()->onUserGroupsChanged( $user, $add, $remove,
|
||||
|
|
|
|||
|
|
@ -25,9 +25,13 @@ namespace MediaWiki\User;
|
|||
use DBAccessObjectUtils;
|
||||
use IDBAccessObject;
|
||||
use InvalidArgumentException;
|
||||
use MediaWiki\Config\ServiceOptions;
|
||||
use MediaWiki\MainConfigNames;
|
||||
use MediaWiki\Permissions\Authority;
|
||||
use stdClass;
|
||||
use User;
|
||||
use Wikimedia\Rdbms\IDatabase;
|
||||
use Wikimedia\Rdbms\ILBFactory;
|
||||
use Wikimedia\Rdbms\ILoadBalancer;
|
||||
|
||||
/**
|
||||
|
|
@ -42,6 +46,18 @@ class UserFactory implements IDBAccessObject, UserRigorOptions {
|
|||
* READ_* constants are inherited from IDBAccessObject
|
||||
*/
|
||||
|
||||
/** @internal */
|
||||
public const CONSTRUCTOR_OPTIONS = [
|
||||
MainConfigNames::SharedDB,
|
||||
MainConfigNames::SharedTables,
|
||||
];
|
||||
|
||||
/** @var ServiceOptions */
|
||||
private $options;
|
||||
|
||||
/** @var ILBFactory */
|
||||
private $loadBalancerFactory;
|
||||
|
||||
/** @var ILoadBalancer */
|
||||
private $loadBalancer;
|
||||
|
||||
|
|
@ -52,14 +68,19 @@ class UserFactory implements IDBAccessObject, UserRigorOptions {
|
|||
private $lastUserFromIdentity = null;
|
||||
|
||||
/**
|
||||
* @param ILoadBalancer $loadBalancer
|
||||
* @param ServiceOptions $options
|
||||
* @param ILBFactory $loadBalancerFactory
|
||||
* @param UserNameUtils $userNameUtils
|
||||
*/
|
||||
public function __construct(
|
||||
ILoadBalancer $loadBalancer,
|
||||
ServiceOptions $options,
|
||||
ILBFactory $loadBalancerFactory,
|
||||
UserNameUtils $userNameUtils
|
||||
) {
|
||||
$this->loadBalancer = $loadBalancer;
|
||||
$options->assertRequiredOptions( self::CONSTRUCTOR_OPTIONS );
|
||||
$this->options = $options;
|
||||
$this->loadBalancerFactory = $loadBalancerFactory;
|
||||
$this->loadBalancer = $loadBalancerFactory->getMainLB();
|
||||
$this->userNameUtils = $userNameUtils;
|
||||
}
|
||||
|
||||
|
|
@ -339,4 +360,61 @@ class UserFactory implements IDBAccessObject, UserRigorOptions {
|
|||
$user->setName( $name );
|
||||
return $user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Purge user related caches, "touch" the user table to invalidate further caches
|
||||
* @since 1.41
|
||||
* @param UserIdentity $userIdentity
|
||||
*/
|
||||
public function invalidateCache( UserIdentity $userIdentity ) {
|
||||
if ( !$userIdentity->isRegistered() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$wikiId = $userIdentity->getWikiId();
|
||||
if ( $wikiId === UserIdentity::LOCAL ) {
|
||||
$legacyUser = $this->newFromUserIdentity( $userIdentity );
|
||||
// Update user_touched within User class to manage state of User::mTouched for CAS check
|
||||
$legacyUser->invalidateCache();
|
||||
} else {
|
||||
// cross-wiki invalidation
|
||||
$userId = $userIdentity->getId( $wikiId );
|
||||
|
||||
$dbw = $this->getUserTableConnection( ILoadBalancer::DB_PRIMARY, $wikiId );
|
||||
$dbw->newUpdateQueryBuilder()
|
||||
->update( 'user' )
|
||||
->set( [ 'user_touched' => $dbw->timestamp() ] )
|
||||
->where( [ 'user_id' => $userId ] )
|
||||
->caller( __METHOD__ )->execute();
|
||||
|
||||
$dbw->onTransactionPreCommitOrIdle(
|
||||
static function () use ( $wikiId, $userId ) {
|
||||
User::purge( $wikiId, $userId );
|
||||
},
|
||||
__METHOD__
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $mode
|
||||
* @param string|false $wikiId
|
||||
* @return IDatabase
|
||||
*/
|
||||
private function getUserTableConnection( $mode, $wikiId ) {
|
||||
if ( is_string( $wikiId ) && $this->loadBalancerFactory->getLocalDomainID() === $wikiId ) {
|
||||
$wikiId = UserIdentity::LOCAL;
|
||||
}
|
||||
|
||||
if ( $this->options->get( MainConfigNames::SharedDB ) &&
|
||||
in_array( 'user', $this->options->get( MainConfigNames::SharedTables ) )
|
||||
) {
|
||||
// The main LB is aliased for the shared database in Setup.php
|
||||
$lb = $this->loadBalancer;
|
||||
} else {
|
||||
$lb = $this->loadBalancerFactory->getMainLB( $wikiId );
|
||||
}
|
||||
|
||||
return $lb->getConnection( $mode, [], $wikiId );
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,8 +39,6 @@ class UserRightsProxy implements UserIdentity {
|
|||
|
||||
/** @var IDatabase */
|
||||
private $db;
|
||||
/** @var IDatabase */
|
||||
private $userdb;
|
||||
/** @var string */
|
||||
private $dbDomain;
|
||||
/** @var string */
|
||||
|
|
@ -56,14 +54,12 @@ class UserRightsProxy implements UserIdentity {
|
|||
* @see newFromId()
|
||||
* @see newFromName()
|
||||
* @param IDatabase $db Db connection
|
||||
* @param IDatabase $userdb Db connection (user table)
|
||||
* @param string $dbDomain Database name
|
||||
* @param string $name User name
|
||||
* @param int $id User ID
|
||||
*/
|
||||
private function __construct( $db, $userdb, $dbDomain, $name, $id ) {
|
||||
private function __construct( $db, $dbDomain, $name, $id ) {
|
||||
$this->db = $db;
|
||||
$this->userdb = $userdb;
|
||||
$this->dbDomain = $dbDomain;
|
||||
$this->name = $name;
|
||||
$this->id = intval( $id );
|
||||
|
|
@ -157,7 +153,7 @@ class UserRightsProxy implements UserIdentity {
|
|||
|
||||
if ( $row !== false ) {
|
||||
return new UserRightsProxy(
|
||||
$db, $userdb, $dbDomain, $row->user_name, intval( $row->user_id ) );
|
||||
$db, $dbDomain, $row->user_name, intval( $row->user_id ) );
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
|
@ -294,21 +290,9 @@ class UserRightsProxy implements UserIdentity {
|
|||
* Replaces User::touchUser()
|
||||
*/
|
||||
public function invalidateCache() {
|
||||
$this->userdb->update(
|
||||
'user',
|
||||
[ 'user_touched' => $this->userdb->timestamp() ],
|
||||
[ 'user_id' => $this->id ],
|
||||
__METHOD__
|
||||
);
|
||||
|
||||
$domainId = $this->userdb->getDomainID();
|
||||
$userId = $this->id;
|
||||
$this->userdb->onTransactionPreCommitOrIdle(
|
||||
static function () use ( $domainId, $userId ) {
|
||||
User::purge( $domainId, $userId );
|
||||
},
|
||||
__METHOD__
|
||||
);
|
||||
MediaWikiServices::getInstance()
|
||||
->getUserFactory()
|
||||
->invalidateCache( $this );
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -5,6 +5,10 @@ use MediaWiki\Permissions\UltimateAuthority;
|
|||
use MediaWiki\User\UserFactory;
|
||||
use MediaWiki\User\UserIdentityValue;
|
||||
use Wikimedia\IPUtils;
|
||||
use Wikimedia\Rdbms\IDatabase;
|
||||
use Wikimedia\Rdbms\ILoadBalancer;
|
||||
use Wikimedia\Rdbms\LBFactory;
|
||||
use Wikimedia\Rdbms\UpdateQueryBuilder;
|
||||
|
||||
/**
|
||||
* @covers \MediaWiki\User\UserFactory
|
||||
|
|
@ -222,4 +226,38 @@ class UserFactoryTest extends MediaWikiIntegrationTestCase {
|
|||
$this->assertFalse( $user->isNamed() );
|
||||
}
|
||||
|
||||
public function testInvalidateCacheLocal() {
|
||||
$userMock = $this->createMock( User::class );
|
||||
$userMock->method( 'isRegistered' )->willReturn( true );
|
||||
$userMock->method( 'getWikiId' )->willReturn( User::LOCAL );
|
||||
$userMock->expects( $this->once() )->method( 'invalidateCache' );
|
||||
|
||||
$this->getUserFactory()->invalidateCache( $userMock );
|
||||
}
|
||||
|
||||
public function testInvalidateCacheCrossWiki() {
|
||||
$dbMock = $this->createMock( IDatabase::class );
|
||||
$dbMock->method( 'timestamp' )->willReturn( 'timestamp' );
|
||||
$dbMock->expects( $this->once() )
|
||||
->method( 'newUpdateQueryBuilder' )
|
||||
->willReturn( new UpdateQueryBuilder( $dbMock ) );
|
||||
$dbMock->expects( $this->once() )
|
||||
->method( 'update' )
|
||||
->with(
|
||||
'user',
|
||||
[ 'user_touched' => 'timestamp' ],
|
||||
[ 'user_id' => 123 ]
|
||||
);
|
||||
|
||||
$lbMock = $this->createMock( ILoadBalancer::class );
|
||||
$lbMock->method( 'getConnection' )->willReturn( $dbMock );
|
||||
|
||||
$lbFactoryMock = $this->createMock( LBFactory::class );
|
||||
$lbFactoryMock->method( 'getMainLB' )->willReturn( $lbMock );
|
||||
$this->setService( 'DBLoadBalancerFactory', $lbFactoryMock );
|
||||
|
||||
$user = new UserIdentityValue( 123, 'UserIdentityCacheUpdaterTest', 'meta' );
|
||||
$this->getUserFactory()->invalidateCache( $user );
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ use UserRightsProxy;
|
|||
use Wikimedia\Rdbms\DBConnRef;
|
||||
use Wikimedia\Rdbms\ILoadBalancer;
|
||||
use Wikimedia\Rdbms\LBFactory;
|
||||
use Wikimedia\Rdbms\UpdateQueryBuilder;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass UserRightsProxy
|
||||
|
|
@ -204,6 +205,9 @@ class UserRightsProxyTest extends MediaWikiIntegrationTestCase {
|
|||
]
|
||||
]
|
||||
);
|
||||
$dbMock->expects( $this->once() )
|
||||
->method( 'newUpdateQueryBuilder' )
|
||||
->willReturn( new UpdateQueryBuilder( $dbMock ) );
|
||||
$dbMock->expects( $this->once() )
|
||||
->method( 'update' )
|
||||
->with(
|
||||
|
|
@ -216,6 +220,7 @@ class UserRightsProxyTest extends MediaWikiIntegrationTestCase {
|
|||
|
||||
$lbMock = $this->createMock( ILoadBalancer::class );
|
||||
$lbMock->method( 'getMaintenanceConnectionRef' )->willReturn( $dbMock );
|
||||
$lbMock->method( 'getConnection' )->willReturn( $dbMock );
|
||||
|
||||
$lbFactoryMock = $this->createMock( LBFactory::class );
|
||||
$lbFactoryMock->method( 'getMainLB' )->willReturn( $lbMock );
|
||||
|
|
|
|||
Loading…
Reference in a new issue