diff options
author | daniel <dkinzler@wikimedia.org> | 2023-10-27 19:34:38 +0200 |
---|---|---|
committer | daniel <dkinzler@wikimedia.org> | 2024-01-02 20:14:12 +0100 |
commit | afbad85185329982499d79b45a6e27002eccc50e (patch) | |
tree | be1ae5911444f2b7fd5b64d06e6324f921808437 | |
parent | 58ca755ce5b7c4faab1386cfe008bba1c63a7b92 (diff) | |
download | mediawikicore-afbad85185329982499d79b45a6e27002eccc50e.tar.gz mediawikicore-afbad85185329982499d79b45a6e27002eccc50e.zip |
EditPage: replace usage of User::pingLimiter
Using RateLimiter::limit instead.
Change-Id: Ia27cba5023994bfdc61f6d27702eeb98502d6dd4
6 files changed, 92 insertions, 79 deletions
diff --git a/includes/ServiceWiring.php b/includes/ServiceWiring.php index 1825c28513c4..e2717561b990 100644 --- a/includes/ServiceWiring.php +++ b/includes/ServiceWiring.php @@ -2546,7 +2546,10 @@ return [ $services->getReadOnlyMode(), // SpamRegexConstraint - $services->getSpamChecker() + $services->getSpamChecker(), + + // UserRateLimitConstraint + $services->getRateLimiter() ); }, diff --git a/includes/editpage/Constraint/EditConstraintFactory.php b/includes/editpage/Constraint/EditConstraintFactory.php index 665b0b6ad5ed..023155eed5ff 100644 --- a/includes/editpage/Constraint/EditConstraintFactory.php +++ b/includes/editpage/Constraint/EditConstraintFactory.php @@ -30,6 +30,8 @@ use MediaWiki\Linker\LinkTarget; use MediaWiki\Logger\Spi; use MediaWiki\MainConfigNames; use MediaWiki\Permissions\PermissionManager; +use MediaWiki\Permissions\RateLimiter; +use MediaWiki\Permissions\RateLimitSubject; use MediaWiki\Title\Title; use MediaWiki\User\User; use MediaWiki\User\UserIdentity; @@ -67,6 +69,7 @@ class EditConstraintFactory { /** @var SpamChecker */ private $spamRegexChecker; + private RateLimiter $rateLimiter; /** * Some constraints have dependencies that need to be injected, @@ -86,6 +89,7 @@ class EditConstraintFactory { * @param HookContainer $hookContainer * @param ReadOnlyMode $readOnlyMode * @param SpamChecker $spamRegexChecker + * @param RateLimiter $rateLimiter */ public function __construct( ServiceOptions $options, @@ -93,7 +97,8 @@ class EditConstraintFactory { PermissionManager $permissionManager, HookContainer $hookContainer, ReadOnlyMode $readOnlyMode, - SpamChecker $spamRegexChecker + SpamChecker $spamRegexChecker, + RateLimiter $rateLimiter ) { $options->assertRequiredOptions( self::CONSTRUCTOR_OPTIONS ); @@ -112,6 +117,9 @@ class EditConstraintFactory { // SpamRegexConstraint $this->spamRegexChecker = $spamRegexChecker; + + // UserRateLimitConstraint + $this->rateLimiter = $rateLimiter; } /** @@ -168,6 +176,26 @@ class EditConstraintFactory { } /** + * @param RateLimitSubject $subject + * @param string $oldModel + * @param string $newModel + * + * @return UserRateLimitConstraint + */ + public function newUserRateLimitConstraint( + RateLimitSubject $subject, + string $oldModel, + string $newModel + ): UserRateLimitConstraint { + return new UserRateLimitConstraint( + $this->rateLimiter, + $subject, + $oldModel, + $newModel + ); + } + + /** * @param string $input * @param UserIdentity $user * @param Title $title diff --git a/includes/editpage/Constraint/UserRateLimitConstraint.php b/includes/editpage/Constraint/UserRateLimitConstraint.php index db064f067920..d21409a95a84 100644 --- a/includes/editpage/Constraint/UserRateLimitConstraint.php +++ b/includes/editpage/Constraint/UserRateLimitConstraint.php @@ -20,8 +20,8 @@ namespace MediaWiki\EditPage\Constraint; -use MediaWiki\Title\Title; -use MediaWiki\User\User; +use MediaWiki\Permissions\RateLimiter; +use MediaWiki\Permissions\RateLimitSubject; use StatusValue; /** @@ -33,43 +33,39 @@ use StatusValue; */ class UserRateLimitConstraint implements IEditConstraint { - /** @var User */ - private $user; + private RateLimitSubject $subject; + private string $oldContentModel; + private string $newContentModel; + private RateLimiter $limiter; - /** @var Title */ - private $title; + private string $result; - /** @var string */ - private $newContentModel; - - /** @var string|null */ - private $result; - - /** - * @param User $user - * @param Title $title - * @param string $newContentModel - */ public function __construct( - User $user, - Title $title, + RateLimiter $limiter, + RateLimitSubject $subject, + string $oldContentModel, string $newContentModel ) { - $this->user = $user; - $this->title = $title; + $this->limiter = $limiter; + $this->subject = $subject; + $this->oldContentModel = $oldContentModel; $this->newContentModel = $newContentModel; } + private function limit( string $action, int $inc = 1 ) { + return $this->limiter->limit( $this->subject, $action, $inc ); + } + public function checkConstraint(): string { // Need to check for rate limits on `editcontentmodel` if it is changing - $contentModelChange = ( $this->newContentModel !== $this->title->getContentModel() ); + $contentModelChange = ( $this->newContentModel !== $this->oldContentModel ); // TODO inject and use a ThrottleStore once available, see T261744 // Checking if the user is rate limited increments the counts, so we cannot perform // the check again when getting the status; thus, store the result - if ( $this->user->pingLimiter() - || $this->user->pingLimiter( 'linkpurge', 0 ) // only counted after the fact - || ( $contentModelChange && $this->user->pingLimiter( 'editcontentmodel' ) ) + if ( $this->limit( 'edit' ) + || $this->limit( 'linkpurge', 0 ) // only counted after the fact + || ( $contentModelChange && $this->limit( 'editcontentmodel' ) ) ) { $this->result = self::CONSTRAINT_FAILED; } else { diff --git a/includes/editpage/EditPage.php b/includes/editpage/EditPage.php index d366898dbb34..f5c951e5ca71 100644 --- a/includes/editpage/EditPage.php +++ b/includes/editpage/EditPage.php @@ -53,7 +53,6 @@ use MediaWiki\EditPage\Constraint\SelfRedirectConstraint; use MediaWiki\EditPage\Constraint\SpamRegexConstraint; use MediaWiki\EditPage\Constraint\UnicodeConstraint; use MediaWiki\EditPage\Constraint\UserBlockConstraint; -use MediaWiki\EditPage\Constraint\UserRateLimitConstraint; use MediaWiki\HookContainer\HookRunner; use MediaWiki\HookContainer\ProtectedHookAccessorTrait; use MediaWiki\Html\Html; @@ -2119,7 +2118,11 @@ class EditPage implements IEditObject { $constraintFactory->newReadOnlyConstraint() ); $constraintRunner->addConstraint( - new UserRateLimitConstraint( $requestUser, $this->mTitle, $this->contentModel ) + $constraintFactory->newUserRateLimitConstraint( + $requestUser->toRateLimitSubject(), + $this->mTitle->getContentModel(), + $this->contentModel + ) ); $constraintRunner->addConstraint( // Same constraint is used to check size before and after merging the @@ -2503,8 +2506,9 @@ class EditPage implements IEditObject { $result['nullEdit'] = !$doEditStatus->wasRevisionCreated(); if ( $result['nullEdit'] ) { - // We don't know if it was a null edit until now, so increment here - $requestUser->pingLimiter( 'linkpurge' ); + // We didn't know if it was a null edit until now, so bump the rate limit now + $limitSubject = $requestUser->toRateLimitSubject(); + MediaWikiServices::getInstance()->getRateLimiter()->limit( $limitSubject, 'linkpurge' ); } $result['redirect'] = $content->isRedirect(); diff --git a/tests/phpunit/unit/includes/editpage/Constraint/EditConstraintFactoryTest.php b/tests/phpunit/unit/includes/editpage/Constraint/EditConstraintFactoryTest.php index 6696da59a7f4..a9be685c080b 100644 --- a/tests/phpunit/unit/includes/editpage/Constraint/EditConstraintFactoryTest.php +++ b/tests/phpunit/unit/includes/editpage/Constraint/EditConstraintFactoryTest.php @@ -31,6 +31,7 @@ use MediaWiki\HookContainer\HookContainer; use MediaWiki\Logger\Spi; use MediaWiki\MainConfigNames; use MediaWiki\Permissions\PermissionManager; +use MediaWiki\Permissions\RateLimiter; use MediaWiki\Title\Title; use MediaWiki\User\User; use Psr\Log\NullLogger; @@ -60,7 +61,8 @@ class EditConstraintFactoryTest extends MediaWikiUnitTestCase { $this->createMock( PermissionManager::class ), $this->createMock( HookContainer::class ), $this->createMock( ReadOnlyMode::class ), - $this->createMock( SpamChecker::class ) + $this->createMock( SpamChecker::class ), + $this->createMock( RateLimiter::class ), ); $user = $this->createMock( User::class ); diff --git a/tests/phpunit/unit/includes/editpage/Constraint/UserRateLimitConstraintTest.php b/tests/phpunit/unit/includes/editpage/Constraint/UserRateLimitConstraintTest.php index bd87b4f065bc..c562bb877a3d 100644 --- a/tests/phpunit/unit/includes/editpage/Constraint/UserRateLimitConstraintTest.php +++ b/tests/phpunit/unit/includes/editpage/Constraint/UserRateLimitConstraintTest.php @@ -20,8 +20,10 @@ use MediaWiki\EditPage\Constraint\IEditConstraint; use MediaWiki\EditPage\Constraint\UserRateLimitConstraint; -use MediaWiki\Title\Title; -use MediaWiki\User\User; +use MediaWiki\Permissions\RateLimiter; +use MediaWiki\Permissions\RateLimitSubject; +use MediaWiki\User\UserIdentityValue; +use PHPUnit\Framework\MockObject\MockObject; /** * Tests the UserRateLimitConstraint @@ -33,61 +35,39 @@ use MediaWiki\User\User; class UserRateLimitConstraintTest extends MediaWikiUnitTestCase { use EditConstraintTestTrait; - public function testPass() { - // Cannot assert that 0 parameters are passed, since PHP fills in the default - // values before PHPUnit checks; first call uses both defaults, third call - // uses the default of 1 for the second parameter - $user = $this->getMockBuilder( User::class ) - ->onlyMethods( [ 'pingLimiter' ] ) - ->getMock(); - $user->expects( $this->exactly( 3 ) ) - ->method( 'pingLimiter' ) + /** + * @param bool $fail + * + * @return MockObject&RateLimiter + */ + private function getRateLimiter( $fail ) { + $mock = $this->createNoOpMock( RateLimiter::class, [ 'limit' ] ); + $mock->expects( $this->exactly( 3 ) ) + ->method( 'limit' ) ->withConsecutive( - [ 'edit', 1 ], - [ 'linkpurge', 0 ], - [ 'editcontentmodel', 1 ] + [ $this->anything(), 'edit', 1 ], + [ $this->anything(), 'linkpurge', 0 ], + [ $this->anything(), 'editcontentmodel', 1 ] ) - ->willReturnOnConsecutiveCalls( - false, - false, - false - ); + ->willReturnOnConsecutiveCalls( false, false, $fail ); + return $mock; + } + + public function testPass() { + $limiter = $this->getRateLimiter( false ); - $title = $this->createMock( Title::class ); - $title->expects( $this->once() ) - ->method( 'getContentModel' ) - ->willReturn( 'OldContentModel' ); + $subject = new RateLimitSubject( new UserIdentityValue( 1, 'test' ), null, [] ); - $constraint = new UserRateLimitConstraint( $user, $title, 'NewContentModel' ); + $constraint = new UserRateLimitConstraint( $limiter, $subject, 'OldContentModel', 'NewContentModel' ); $this->assertConstraintPassed( $constraint ); } public function testFailure() { - // Cannot assert that 0 parameters are passed, since PHP fills in the default - // values before PHPUnit checks; first call uses both defaults, third call - // uses the default of 1 for the second parameter - $user = $this->getMockBuilder( User::class ) - ->onlyMethods( [ 'pingLimiter' ] ) - ->getMock(); - $user->expects( $this->exactly( 3 ) ) - ->method( 'pingLimiter' ) - ->withConsecutive( - [ 'edit', 1 ], - [ 'linkpurge', 0 ], - [ 'editcontentmodel', 1 ] - ) - ->willReturnOnConsecutiveCalls( - false, - false, - true // Only die on the last check - ); + $limiter = $this->getRateLimiter( true ); - $title = $this->createMock( Title::class ); - $title->expects( $this->once() ) - ->method( 'getContentModel' ) - ->willReturn( 'OldContentModel' ); + $subject = new RateLimitSubject( new UserIdentityValue( 1, 'test' ), null, [] ); - $constraint = new UserRateLimitConstraint( $user, $title, 'NewContentModel' ); + $constraint = new UserRateLimitConstraint( $limiter, $subject, 'OldContentModel', 'NewContentModel' ); $this->assertConstraintFailed( $constraint, IEditConstraint::AS_RATE_LIMITED ); } |