aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authordaniel <dkinzler@wikimedia.org>2023-10-27 19:34:38 +0200
committerdaniel <dkinzler@wikimedia.org>2024-01-02 20:14:12 +0100
commitafbad85185329982499d79b45a6e27002eccc50e (patch)
treebe1ae5911444f2b7fd5b64d06e6324f921808437
parent58ca755ce5b7c4faab1386cfe008bba1c63a7b92 (diff)
downloadmediawikicore-afbad85185329982499d79b45a6e27002eccc50e.tar.gz
mediawikicore-afbad85185329982499d79b45a6e27002eccc50e.zip
EditPage: replace usage of User::pingLimiter
Using RateLimiter::limit instead. Change-Id: Ia27cba5023994bfdc61f6d27702eeb98502d6dd4
-rw-r--r--includes/ServiceWiring.php5
-rw-r--r--includes/editpage/Constraint/EditConstraintFactory.php30
-rw-r--r--includes/editpage/Constraint/UserRateLimitConstraint.php46
-rw-r--r--includes/editpage/EditPage.php12
-rw-r--r--tests/phpunit/unit/includes/editpage/Constraint/EditConstraintFactoryTest.php4
-rw-r--r--tests/phpunit/unit/includes/editpage/Constraint/UserRateLimitConstraintTest.php74
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 );
}