aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBartosz Dziewoński <dziewonski@fastmail.fm>2025-02-21 22:07:19 +0100
committerBartosz Dziewoński <dziewonski@fastmail.fm>2025-04-05 23:36:58 +0200
commit5b9d45cdc04e96ddc97b1610fe45ae56f41a60c0 (patch)
treeac8efa9f85a46c6df5c0171ec875a0c721532898
parent2a5cf3fde93263156557bc1efd21c5a74ce67725 (diff)
downloadmediawikicore-5b9d45cdc04e96ddc97b1610fe45ae56f41a60c0.tar.gz
mediawikicore-5b9d45cdc04e96ddc97b1610fe45ae56f41a60c0.zip
editpage: Refactor user right, rate limit and block checks to use Authority
Replace EditRightConstraint, UserBlockConstraint, and most of UserRateLimitConstraint with the new AuthorizationConstraint. Instead of many separate checks, everything is now handled by one authorizeWrite() call. Move 'editcontentmodel' rate limit to ContentModelChangeConstraint (by making it use authorizeWrite()). Keep 'linkpurge' rate limit in a separate check, renamed from UserRateLimitConstraint to LinkPurgeRateLimitConstraint, since the way it works in unusual and not portable to Authority without more refactoring in EditPage. AuthorizationConstraint needs some special handling to produce the idiosyncratic result codes required by EditPage, but luckily PermissionStatus gives us everything we need for that. Bug: T271975 Bug: T386346 Change-Id: Ic9f6f2fbd29efa3e349517013da540a363c263b5
-rw-r--r--autoload.php5
-rw-r--r--includes/ServiceWiring.php5
-rw-r--r--includes/editpage/Constraint/AuthorizationConstraint.php88
-rw-r--r--includes/editpage/Constraint/ContentModelChangeConstraint.php35
-rw-r--r--includes/editpage/Constraint/EditConstraintFactory.php63
-rw-r--r--includes/editpage/Constraint/EditRightConstraint.php105
-rw-r--r--includes/editpage/Constraint/LinkPurgeRateLimitConstraint.php (renamed from includes/editpage/Constraint/UserRateLimitConstraint.php)23
-rw-r--r--includes/editpage/Constraint/UserBlockConstraint.php79
-rw-r--r--includes/editpage/EditPage.php44
-rw-r--r--tests/phpunit/includes/editpage/EditPageConstraintsTest.php59
-rw-r--r--tests/phpunit/mocks/permissions/MockAuthorityTrait.php1
-rw-r--r--tests/phpunit/unit/includes/editpage/Constraint/AuthorizationConstraintTest.php111
-rw-r--r--tests/phpunit/unit/includes/editpage/Constraint/EditConstraintFactoryTest.php7
-rw-r--r--tests/phpunit/unit/includes/editpage/Constraint/EditRightConstraintTest.php170
-rw-r--r--tests/phpunit/unit/includes/editpage/Constraint/LinkPurgeRateLimitConstraintTest.php (renamed from tests/phpunit/unit/includes/editpage/Constraint/UserRateLimitConstraintTest.php)27
-rw-r--r--tests/phpunit/unit/includes/editpage/Constraint/UserBlockConstraintTest.php72
16 files changed, 292 insertions, 602 deletions
diff --git a/autoload.php b/autoload.php
index ffcec57a34b9..42d2dfac6e1a 100644
--- a/autoload.php
+++ b/autoload.php
@@ -1294,6 +1294,7 @@ $wgAutoloadLocalClasses = [
'MediaWiki\\DomainEvent\\EventSubscriberBase' => __DIR__ . '/includes/DomainEvent/DomainEventIngress.php',
'MediaWiki\\DomainEvent\\InitializableDomainEventSubscriber' => __DIR__ . '/includes/DomainEvent/InitializableDomainEventSubscriber.php',
'MediaWiki\\EditPage\\Constraint\\AccidentalRecreationConstraint' => __DIR__ . '/includes/editpage/Constraint/AccidentalRecreationConstraint.php',
+ 'MediaWiki\\EditPage\\Constraint\\AuthorizationConstraint' => __DIR__ . '/includes/editpage/Constraint/AuthorizationConstraint.php',
'MediaWiki\\EditPage\\Constraint\\BrokenRedirectConstraint' => __DIR__ . '/includes/editpage/Constraint/BrokenRedirectConstraint.php',
'MediaWiki\\EditPage\\Constraint\\ChangeTagsConstraint' => __DIR__ . '/includes/editpage/Constraint/ChangeTagsConstraint.php',
'MediaWiki\\EditPage\\Constraint\\ContentModelChangeConstraint' => __DIR__ . '/includes/editpage/Constraint/ContentModelChangeConstraint.php',
@@ -1302,10 +1303,10 @@ $wgAutoloadLocalClasses = [
'MediaWiki\\EditPage\\Constraint\\EditConstraintFactory' => __DIR__ . '/includes/editpage/Constraint/EditConstraintFactory.php',
'MediaWiki\\EditPage\\Constraint\\EditConstraintRunner' => __DIR__ . '/includes/editpage/Constraint/EditConstraintRunner.php',
'MediaWiki\\EditPage\\Constraint\\EditFilterMergedContentHookConstraint' => __DIR__ . '/includes/editpage/Constraint/EditFilterMergedContentHookConstraint.php',
- 'MediaWiki\\EditPage\\Constraint\\EditRightConstraint' => __DIR__ . '/includes/editpage/Constraint/EditRightConstraint.php',
'MediaWiki\\EditPage\\Constraint\\ExistingSectionEditConstraint' => __DIR__ . '/includes/editpage/Constraint/ExistingSectionEditConstraint.php',
'MediaWiki\\EditPage\\Constraint\\IEditConstraint' => __DIR__ . '/includes/editpage/Constraint/IEditConstraint.php',
'MediaWiki\\EditPage\\Constraint\\ImageRedirectConstraint' => __DIR__ . '/includes/editpage/Constraint/ImageRedirectConstraint.php',
+ 'MediaWiki\\EditPage\\Constraint\\LinkPurgeRateLimitConstraint' => __DIR__ . '/includes/editpage/Constraint/LinkPurgeRateLimitConstraint.php',
'MediaWiki\\EditPage\\Constraint\\MissingCommentConstraint' => __DIR__ . '/includes/editpage/Constraint/MissingCommentConstraint.php',
'MediaWiki\\EditPage\\Constraint\\NewSectionMissingSubjectConstraint' => __DIR__ . '/includes/editpage/Constraint/NewSectionMissingSubjectConstraint.php',
'MediaWiki\\EditPage\\Constraint\\PageSizeConstraint' => __DIR__ . '/includes/editpage/Constraint/PageSizeConstraint.php',
@@ -1314,8 +1315,6 @@ $wgAutoloadLocalClasses = [
'MediaWiki\\EditPage\\Constraint\\SimpleAntiSpamConstraint' => __DIR__ . '/includes/editpage/Constraint/SimpleAntiSpamConstraint.php',
'MediaWiki\\EditPage\\Constraint\\SpamRegexConstraint' => __DIR__ . '/includes/editpage/Constraint/SpamRegexConstraint.php',
'MediaWiki\\EditPage\\Constraint\\UnicodeConstraint' => __DIR__ . '/includes/editpage/Constraint/UnicodeConstraint.php',
- 'MediaWiki\\EditPage\\Constraint\\UserBlockConstraint' => __DIR__ . '/includes/editpage/Constraint/UserBlockConstraint.php',
- 'MediaWiki\\EditPage\\Constraint\\UserRateLimitConstraint' => __DIR__ . '/includes/editpage/Constraint/UserRateLimitConstraint.php',
'MediaWiki\\EditPage\\EditPage' => __DIR__ . '/includes/editpage/EditPage.php',
'MediaWiki\\EditPage\\IEditObject' => __DIR__ . '/includes/editpage/IEditObject.php',
'MediaWiki\\EditPage\\IntroMessageBuilder' => __DIR__ . '/includes/editpage/IntroMessageBuilder.php',
diff --git a/includes/ServiceWiring.php b/includes/ServiceWiring.php
index f82bb3598935..68ec604820dd 100644
--- a/includes/ServiceWiring.php
+++ b/includes/ServiceWiring.php
@@ -2793,9 +2793,6 @@ return [
),
LoggerFactory::getProvider(),
- // UserBlockConstraint
- $services->getPermissionManager(),
-
// EditFilterMergedContentHookConstraint
$services->getHookContainer(),
@@ -2805,7 +2802,7 @@ return [
// SpamRegexConstraint
$services->getSpamChecker(),
- // UserRateLimitConstraint
+ // LinkPurgeRateLimitConstraint
$services->getRateLimiter()
);
},
diff --git a/includes/editpage/Constraint/AuthorizationConstraint.php b/includes/editpage/Constraint/AuthorizationConstraint.php
new file mode 100644
index 000000000000..73247160fa7b
--- /dev/null
+++ b/includes/editpage/Constraint/AuthorizationConstraint.php
@@ -0,0 +1,88 @@
+<?php
+/**
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ */
+
+namespace MediaWiki\EditPage\Constraint;
+
+use MediaWiki\Page\PageIdentity;
+use MediaWiki\Permissions\Authority;
+use MediaWiki\Permissions\PermissionStatus;
+use StatusValue;
+
+/**
+ * Verify authorization to edit the page (user rights, rate limits, blocks).
+ *
+ * @since 1.44
+ * @internal
+ */
+class AuthorizationConstraint implements IEditConstraint {
+
+ private PermissionStatus $status;
+
+ private Authority $performer;
+ private PageIdentity $target;
+ private bool $new;
+
+ public function __construct(
+ Authority $performer,
+ PageIdentity $target,
+ bool $new
+ ) {
+ $this->performer = $performer;
+ $this->target = $target;
+ $this->new = $new;
+ }
+
+ public function checkConstraint(): string {
+ $this->status = PermissionStatus::newEmpty();
+
+ if ( $this->new && !$this->performer->authorizeWrite( 'create', $this->target, $this->status ) ) {
+ return self::CONSTRAINT_FAILED;
+ }
+
+ if ( !$this->performer->authorizeWrite( 'edit', $this->target, $this->status ) ) {
+ return self::CONSTRAINT_FAILED;
+ }
+
+ return self::CONSTRAINT_PASSED;
+ }
+
+ public function getLegacyStatus(): StatusValue {
+ $statusValue = StatusValue::newGood();
+
+ if ( !$this->status->isGood() ) {
+ // Report the most specific errors first
+ if ( $this->status->isBlocked() ) {
+ $statusValue->setResult( false, self::AS_BLOCKED_PAGE_FOR_USER );
+ } elseif ( $this->status->isRateLimitExceeded() ) {
+ $statusValue->setResult( false, self::AS_RATE_LIMITED );
+ } elseif ( $this->status->getPermission() === 'create' ) {
+ $statusValue->setResult( false, self::AS_NO_CREATE_PERMISSION );
+ } elseif ( !$this->performer->isRegistered() ) {
+ $statusValue->setResult( false, self::AS_READ_ONLY_PAGE_ANON );
+ } else {
+ $statusValue->setResult( false, self::AS_READ_ONLY_PAGE_LOGGED );
+ }
+ }
+
+ // TODO: Use error messages from the PermissionStatus ($this->status) here - T384399
+ return $statusValue;
+ }
+
+}
diff --git a/includes/editpage/Constraint/ContentModelChangeConstraint.php b/includes/editpage/Constraint/ContentModelChangeConstraint.php
index 8ed2e56debde..2581ebab3c14 100644
--- a/includes/editpage/Constraint/ContentModelChangeConstraint.php
+++ b/includes/editpage/Constraint/ContentModelChangeConstraint.php
@@ -21,6 +21,7 @@
namespace MediaWiki\EditPage\Constraint;
use MediaWiki\Permissions\Authority;
+use MediaWiki\Permissions\PermissionStatus;
use MediaWiki\Title\Title;
use StatusValue;
@@ -28,6 +29,7 @@ use StatusValue;
* Verify user permissions if changing content model:
* Must have editcontentmodel rights
* Must be able to edit under the new content model
+ * Must not have exceeded the rate limit
*
* @since 1.36
* @internal
@@ -35,10 +37,11 @@ use StatusValue;
*/
class ContentModelChangeConstraint implements IEditConstraint {
+ private PermissionStatus $status;
+
private Authority $performer;
private Title $title;
private string $newContentModel;
- private string $result;
/**
* @param Authority $performer
@@ -56,45 +59,43 @@ class ContentModelChangeConstraint implements IEditConstraint {
}
public function checkConstraint(): string {
+ $this->status = PermissionStatus::newEmpty();
+
if ( $this->newContentModel === $this->title->getContentModel() ) {
// No change
- $this->result = self::CONSTRAINT_PASSED;
return self::CONSTRAINT_PASSED;
}
- if ( !$this->performer->isAllowed( 'editcontentmodel' ) ) {
- $this->result = self::CONSTRAINT_FAILED;
+ if ( !$this->performer->authorizeWrite( 'editcontentmodel', $this->title, $this->status ) ) {
return self::CONSTRAINT_FAILED;
}
- // Make sure the user can edit the page under the new content model too
+ // Make sure the user can edit the page under the new content model too.
+ // We rely on caching in UserAuthority to avoid bumping the rate limit counter twice.
$titleWithNewContentModel = clone $this->title;
$titleWithNewContentModel->setContentModel( $this->newContentModel );
-
- $canEditModel = $this->performer->authorizeWrite(
- 'editcontentmodel',
- $titleWithNewContentModel
- );
-
if (
- !$canEditModel
- || !$this->performer->authorizeWrite( 'edit', $titleWithNewContentModel )
+ !$this->performer->authorizeWrite( 'editcontentmodel', $titleWithNewContentModel, $this->status )
+ || !$this->performer->authorizeWrite( 'edit', $titleWithNewContentModel, $this->status )
) {
- $this->result = self::CONSTRAINT_FAILED;
return self::CONSTRAINT_FAILED;
}
- $this->result = self::CONSTRAINT_PASSED;
return self::CONSTRAINT_PASSED;
}
public function getLegacyStatus(): StatusValue {
$statusValue = StatusValue::newGood();
- if ( $this->result === self::CONSTRAINT_FAILED ) {
- $statusValue->setResult( false, self::AS_NO_CHANGE_CONTENT_MODEL );
+ if ( !$this->status->isGood() ) {
+ if ( $this->status->isRateLimitExceeded() ) {
+ $statusValue->setResult( false, self::AS_RATE_LIMITED );
+ } else {
+ $statusValue->setResult( false, self::AS_NO_CHANGE_CONTENT_MODEL );
+ }
}
+ // TODO: Use error messages from the PermissionStatus ($this->status) here - T384399
return $statusValue;
}
diff --git a/includes/editpage/Constraint/EditConstraintFactory.php b/includes/editpage/Constraint/EditConstraintFactory.php
index 750c977d2312..e9fede9f256c 100644
--- a/includes/editpage/Constraint/EditConstraintFactory.php
+++ b/includes/editpage/Constraint/EditConstraintFactory.php
@@ -26,10 +26,8 @@ use MediaWiki\Context\IContextSource;
use MediaWiki\EditPage\SpamChecker;
use MediaWiki\HookContainer\HookContainer;
use MediaWiki\Language\Language;
-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;
@@ -54,7 +52,6 @@ class EditConstraintFactory {
private ServiceOptions $options;
private Spi $loggerFactory;
- private PermissionManager $permissionManager;
private HookContainer $hookContainer;
private ReadOnlyMode $readOnlyMode;
private SpamChecker $spamRegexChecker;
@@ -74,7 +71,6 @@ class EditConstraintFactory {
*
* @param ServiceOptions $options
* @param Spi $loggerFactory
- * @param PermissionManager $permissionManager
* @param HookContainer $hookContainer
* @param ReadOnlyMode $readOnlyMode
* @param SpamChecker $spamRegexChecker
@@ -83,7 +79,6 @@ class EditConstraintFactory {
public function __construct(
ServiceOptions $options,
Spi $loggerFactory,
- PermissionManager $permissionManager,
HookContainer $hookContainer,
ReadOnlyMode $readOnlyMode,
SpamChecker $spamRegexChecker,
@@ -95,9 +90,6 @@ class EditConstraintFactory {
$this->options = $options;
$this->loggerFactory = $loggerFactory;
- // UserBlockConstraint
- $this->permissionManager = $permissionManager;
-
// EditFilterMergedContentHookConstraint
$this->hookContainer = $hookContainer;
@@ -107,7 +99,7 @@ class EditConstraintFactory {
// SpamRegexConstraint
$this->spamRegexChecker = $spamRegexChecker;
- // UserRateLimitConstraint
+ // LinkPurgeRateLimitConstraint
$this->rateLimiter = $rateLimiter;
}
@@ -163,21 +155,15 @@ class EditConstraintFactory {
/**
* @param RateLimitSubject $subject
- * @param string $oldModel
- * @param string $newModel
*
- * @return UserRateLimitConstraint
+ * @return LinkPurgeRateLimitConstraint
*/
- public function newUserRateLimitConstraint(
- RateLimitSubject $subject,
- string $oldModel,
- string $newModel
- ): UserRateLimitConstraint {
- return new UserRateLimitConstraint(
+ public function newLinkPurgeRateLimitConstraint(
+ RateLimitSubject $subject
+ ): LinkPurgeRateLimitConstraint {
+ return new LinkPurgeRateLimitConstraint(
$this->rateLimiter,
- $subject,
- $oldModel,
- $newModel
+ $subject
);
}
@@ -226,39 +212,4 @@ class EditConstraintFactory {
);
}
- /**
- * @param LinkTarget $title
- * @param User $user
- * @return UserBlockConstraint
- */
- public function newUserBlockConstraint(
- LinkTarget $title,
- User $user
- ): UserBlockConstraint {
- return new UserBlockConstraint(
- $this->permissionManager,
- $title,
- $user
- );
- }
-
- /**
- * @param User $performer
- * @param Title $title
- * @param bool $new
- * @return EditRightConstraint
- */
- public function newEditRightConstraint(
- User $performer,
- Title $title,
- bool $new
- ): EditRightConstraint {
- return new EditRightConstraint(
- $performer,
- $this->permissionManager,
- $title,
- $new
- );
- }
-
}
diff --git a/includes/editpage/Constraint/EditRightConstraint.php b/includes/editpage/Constraint/EditRightConstraint.php
deleted file mode 100644
index 47be037e4dad..000000000000
--- a/includes/editpage/Constraint/EditRightConstraint.php
+++ /dev/null
@@ -1,105 +0,0 @@
-<?php
-/**
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- * @file
- */
-
-namespace MediaWiki\EditPage\Constraint;
-
-use MediaWiki\Permissions\PermissionManager;
-use MediaWiki\Title\Title;
-use MediaWiki\User\User;
-use StatusValue;
-
-/**
- * Verify user permissions:
- * Must have edit rights
- *
- * @since 1.36
- * @internal
- * @author DannyS712
- */
-class EditRightConstraint implements IEditConstraint {
-
- private User $performer;
- private PermissionManager $permManager;
- private Title $title;
- private string $result;
- private bool $new;
-
- /**
- * @param User $performer
- * @param PermissionManager $permManager
- * @param Title $title
- * @param bool $new
- */
- public function __construct(
- User $performer,
- PermissionManager $permManager,
- Title $title,
- bool $new
- ) {
- $this->performer = $performer;
- $this->permManager = $permManager;
- $this->title = $title;
- $this->new = $new;
- }
-
- public function checkConstraint(): string {
- if ( $this->new ) {
- // Check isn't simple enough to just repeat when getting the status
- if ( !$this->performer->authorizeWrite( 'create', $this->title ) ) {
- $this->result = (string)self::AS_NO_CREATE_PERMISSION;
- return self::CONSTRAINT_FAILED;
- }
- }
-
- // Check isn't simple enough to just repeat when getting the status
- // Prior to 1.41 this checked if the user had edit rights in general
- // instead of for the specific page in question.
- if ( !$this->permManager->userCan(
- 'edit',
- $this->performer,
- $this->title
- ) ) {
- $this->result = self::CONSTRAINT_FAILED;
- return self::CONSTRAINT_FAILED;
- }
-
- $this->result = self::CONSTRAINT_PASSED;
- return self::CONSTRAINT_PASSED;
- }
-
- public function getLegacyStatus(): StatusValue {
- $statusValue = StatusValue::newGood();
-
- if ( $this->result === self::CONSTRAINT_FAILED ) {
- if ( !$this->performer->isRegistered() ) {
- $statusValue->setResult( false, self::AS_READ_ONLY_PAGE_ANON );
- } else {
- $statusValue->fatal( 'readonlytext' );
- $statusValue->value = self::AS_READ_ONLY_PAGE_LOGGED;
- }
- } elseif ( $this->result === (string)self::AS_NO_CREATE_PERMISSION ) {
- $statusValue->fatal( 'nocreatetext' );
- $statusValue->value = self::AS_NO_CREATE_PERMISSION;
- }
-
- return $statusValue;
- }
-
-}
diff --git a/includes/editpage/Constraint/UserRateLimitConstraint.php b/includes/editpage/Constraint/LinkPurgeRateLimitConstraint.php
index 8f35dd5879d0..d25cf9336d88 100644
--- a/includes/editpage/Constraint/UserRateLimitConstraint.php
+++ b/includes/editpage/Constraint/LinkPurgeRateLimitConstraint.php
@@ -25,31 +25,26 @@ use MediaWiki\Permissions\RateLimitSubject;
use StatusValue;
/**
- * Verify user doesn't exceed rate limits
+ * Verify that the user doesn't exceed 'linkpurge' limits, which are weird and special.
+ * Other rate limits have been integrated into their respective permission checks.
*
- * @since 1.36
+ * @since 1.44
* @internal
* @author DannyS712
*/
-class UserRateLimitConstraint implements IEditConstraint {
+class LinkPurgeRateLimitConstraint implements IEditConstraint {
private RateLimitSubject $subject;
- private string $oldContentModel;
- private string $newContentModel;
private RateLimiter $limiter;
private string $result;
public function __construct(
RateLimiter $limiter,
- RateLimitSubject $subject,
- string $oldContentModel,
- string $newContentModel
+ RateLimitSubject $subject
) {
$this->limiter = $limiter;
$this->subject = $subject;
- $this->oldContentModel = $oldContentModel;
- $this->newContentModel = $newContentModel;
}
private function limit( string $action, int $inc = 1 ): bool {
@@ -57,16 +52,10 @@ class UserRateLimitConstraint implements IEditConstraint {
}
public function checkConstraint(): string {
- // Need to check for rate limits on `editcontentmodel` if it is changing
- $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->limit( 'edit' )
- || $this->limit( 'linkpurge', 0 ) // only counted after the fact
- || ( $contentModelChange && $this->limit( 'editcontentmodel' ) )
- ) {
+ if ( $this->limit( 'linkpurge', /* only counted after the fact */ 0 ) ) {
$this->result = self::CONSTRAINT_FAILED;
} else {
$this->result = self::CONSTRAINT_PASSED;
diff --git a/includes/editpage/Constraint/UserBlockConstraint.php b/includes/editpage/Constraint/UserBlockConstraint.php
deleted file mode 100644
index bdd33262d434..000000000000
--- a/includes/editpage/Constraint/UserBlockConstraint.php
+++ /dev/null
@@ -1,79 +0,0 @@
-<?php
-/**
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- * @file
- */
-
-namespace MediaWiki\EditPage\Constraint;
-
-use MediaWiki\Linker\LinkTarget;
-use MediaWiki\Permissions\PermissionManager;
-use MediaWiki\User\User;
-use StatusValue;
-
-/**
- * Verify user permissions:
- * Must not be blocked from the page
- *
- * @since 1.36
- * @internal
- * @author DannyS712
- */
-class UserBlockConstraint implements IEditConstraint {
-
- private PermissionManager $permissionManager;
- private LinkTarget $title;
- private User $user;
- private string $result;
-
- /**
- * @param PermissionManager $permissionManager
- * @param LinkTarget $title
- * @param User $user
- */
- public function __construct(
- PermissionManager $permissionManager,
- LinkTarget $title,
- User $user
- ) {
- $this->permissionManager = $permissionManager;
- $this->title = $title;
- $this->user = $user;
- }
-
- public function checkConstraint(): string {
- // Check isn't simple enough to just repeat when getting the status
- if ( $this->permissionManager->isBlockedFrom( $this->user, $this->title ) ) {
- $this->result = self::CONSTRAINT_FAILED;
- return self::CONSTRAINT_FAILED;
- }
-
- $this->result = self::CONSTRAINT_PASSED;
- return self::CONSTRAINT_PASSED;
- }
-
- public function getLegacyStatus(): StatusValue {
- $statusValue = StatusValue::newGood();
-
- if ( $this->result === self::CONSTRAINT_FAILED ) {
- $statusValue->setResult( false, self::AS_BLOCKED_PAGE_FOR_USER );
- }
-
- return $statusValue;
- }
-
-}
diff --git a/includes/editpage/EditPage.php b/includes/editpage/EditPage.php
index 93a3ad5a28e8..f08196894d88 100644
--- a/includes/editpage/EditPage.php
+++ b/includes/editpage/EditPage.php
@@ -37,6 +37,7 @@ use MediaWiki\Context\IContextSource;
use MediaWiki\Debug\DeprecationHelper;
use MediaWiki\Deferred\DeferredUpdates;
use MediaWiki\EditPage\Constraint\AccidentalRecreationConstraint;
+use MediaWiki\EditPage\Constraint\AuthorizationConstraint;
use MediaWiki\EditPage\Constraint\BrokenRedirectConstraint;
use MediaWiki\EditPage\Constraint\ChangeTagsConstraint;
use MediaWiki\EditPage\Constraint\ContentModelChangeConstraint;
@@ -54,7 +55,6 @@ use MediaWiki\EditPage\Constraint\PageSizeConstraint;
use MediaWiki\EditPage\Constraint\SelfRedirectConstraint;
use MediaWiki\EditPage\Constraint\SpamRegexConstraint;
use MediaWiki\EditPage\Constraint\UnicodeConstraint;
-use MediaWiki\EditPage\Constraint\UserBlockConstraint;
use MediaWiki\Exception\ErrorPageError;
use MediaWiki\Exception\MWContentSerializationException;
use MediaWiki\Exception\MWException;
@@ -2189,24 +2189,31 @@ class EditPage implements IEditObject {
)
);
$constraintRunner->addConstraint(
- $constraintFactory->newUserBlockConstraint( $this->mTitle, $requestUser )
+ $constraintFactory->newReadOnlyConstraint()
);
+
+ // Load the page data from the primary DB. If anything changes in the meantime,
+ // we detect it by using page_latest like a token in a 1 try compare-and-swap.
+ $this->page->loadPageData( IDBAccessObject::READ_LATEST );
+ $new = !$this->page->exists();
+
$constraintRunner->addConstraint(
- new ContentModelChangeConstraint(
+ new AuthorizationConstraint(
$authority,
$this->mTitle,
- $this->contentModel
+ $new
)
);
-
$constraintRunner->addConstraint(
- $constraintFactory->newReadOnlyConstraint()
+ new ContentModelChangeConstraint(
+ $authority,
+ $this->mTitle,
+ $this->contentModel
+ )
);
$constraintRunner->addConstraint(
- $constraintFactory->newUserRateLimitConstraint(
- $requestUser->toRateLimitSubject(),
- $this->mTitle->getContentModel(),
- $this->contentModel
+ $constraintFactory->newLinkPurgeRateLimitConstraint(
+ $requestUser->toRateLimitSubject()
)
);
$constraintRunner->addConstraint(
@@ -2230,16 +2237,6 @@ class EditPage implements IEditObject {
)
);
- // Load the page data from the primary DB. If anything changes in the meantime,
- // we detect it by using page_latest like a token in a 1 try compare-and-swap.
- $this->page->loadPageData( IDBAccessObject::READ_LATEST );
- $new = !$this->page->exists();
-
- // We do this last, as some of the other constraints are more specific
- $constraintRunner->addConstraint(
- $constraintFactory->newEditRightConstraint( $this->getUserForPermissions(), $this->mTitle, $new )
- );
-
// Check the constraints
if ( !$constraintRunner->checkConstraints() ) {
$failed = $constraintRunner->getFailedConstraint();
@@ -2625,9 +2622,12 @@ class EditPage implements IEditObject {
* result from the backend.
*/
private function handleFailedConstraint( IEditConstraint $failed ): void {
- if ( $failed instanceof UserBlockConstraint ) {
+ if ( $failed instanceof AuthorizationConstraint ) {
// Auto-block user's IP if the account was "hard" blocked
- if ( !MediaWikiServices::getInstance()->getReadOnlyMode()->isReadOnly() ) {
+ if (
+ !MediaWikiServices::getInstance()->getReadOnlyMode()->isReadOnly()
+ && $failed->getLegacyStatus()->value === self::AS_BLOCKED_PAGE_FOR_USER
+ ) {
$this->context->getUser()->spreadAnyEditBlock();
}
} elseif ( $failed instanceof DefaultTextConstraint ) {
diff --git a/tests/phpunit/includes/editpage/EditPageConstraintsTest.php b/tests/phpunit/includes/editpage/EditPageConstraintsTest.php
index 2ca607066035..84dbbc7c7723 100644
--- a/tests/phpunit/includes/editpage/EditPageConstraintsTest.php
+++ b/tests/phpunit/includes/editpage/EditPageConstraintsTest.php
@@ -7,7 +7,9 @@ use MediaWiki\EditPage\EditPage;
use MediaWiki\EditPage\SpamChecker;
use MediaWiki\MainConfigNames;
use MediaWiki\Permissions\PermissionManager;
+use MediaWiki\Permissions\PermissionStatus;
use MediaWiki\Request\FauxRequest;
+use MediaWiki\Tests\Unit\MockBlockTrait;
use MediaWiki\Tests\User\TempUser\TempUserTestTrait;
use MediaWiki\Title\Title;
use MediaWiki\User\User;
@@ -27,6 +29,7 @@ use Wikimedia\Rdbms\ReadOnlyMode;
class EditPageConstraintsTest extends MediaWikiLangTestCase {
use TempUserTestTrait;
+ use MockBlockTrait;
protected function setUp(): void {
parent::setUp();
@@ -183,8 +186,8 @@ class EditPageConstraintsTest extends MediaWikiLangTestCase {
$user = $this->getTestUser()->getUser();
$permissionManager = $this->getServiceContainer()->getPermissionManager();
- // Needs edit rights to pass EditRightConstraint and reach AccidentalRecreationConstraint
- $permissionManager->overrideUserRightsForTesting( $user, [ 'edit' ] );
+ // Needs these rights to pass AuthorizationConstraint and reach AccidentalRecreationConstraint
+ $permissionManager->overrideUserRightsForTesting( $user, [ 'edit', 'createpage' ] );
// Started the edit on 1 January 2019, page was deleted on 1 January 2020
$edit = [
@@ -215,7 +218,7 @@ class EditPageConstraintsTest extends MediaWikiLangTestCase {
$user = $this->getTestUser()->getUser();
$permissionManager = $this->getServiceContainer()->getPermissionManager();
- // Needs edit rights to pass EditRightConstraint and reach NewSectionMissingSubjectConstraint
+ // Needs these rights to pass AuthorizationConstraint and reach NewSectionMissingSubjectConstraint
$permissionManager->overrideUserRightsForTesting( $user, [ 'edit' ] );
$edit = [
@@ -258,7 +261,7 @@ class EditPageConstraintsTest extends MediaWikiLangTestCase {
public function testContentModelChangeConstraint() {
$user = $this->getTestUser()->getUser();
$permissionManager = $this->getServiceContainer()->getPermissionManager();
- // Needs edit rights to pass EditRightConstraint and reach ContentModelChangeConstraint
+ // Needs these rights to pass AuthorizationConstraint and reach ContentModelChangeConstraint
$permissionManager->overrideUserRightsForTesting( $user, [ 'edit' ] );
$edit = [
@@ -285,14 +288,13 @@ class EditPageConstraintsTest extends MediaWikiLangTestCase {
);
}
- /** CreationPermissionConstraint integration */
- public function testCreationPermissionConstraint() {
- $page = $this->getNonexistingTestPage( 'CreationPermissionConstraint page does not exist' );
+ /** AuthorizationConstraint integration - 'create' rights */
+ public function testAuthorizationConstraint_create() {
+ $page = $this->getNonexistingTestPage( 'AuthorizationConstraint_create page does not exist' );
$title = $page->getTitle();
$user = $this->getTestUser()->getUser();
$permissionManager = $this->getServiceContainer()->getPermissionManager();
- // Needs edit rights to pass EditRightConstraint and reach CreationPermissionConstraint
$permissionManager->overrideUserRightsForTesting( $user, [ 'edit' ] );
$edit = [
@@ -316,7 +318,7 @@ class EditPageConstraintsTest extends MediaWikiLangTestCase {
$user = $this->getTestUser()->getUser();
$permissionManager = $this->getServiceContainer()->getPermissionManager();
- // Needs edit and createpage rights to pass EditRightConstraint and CreationPermissionConstraint
+ // Needs these rights to pass AuthorizationConstraint
$permissionManager->overrideUserRightsForTesting( $user, [ 'edit', 'createpage' ] );
$edit = [
@@ -366,7 +368,7 @@ class EditPageConstraintsTest extends MediaWikiLangTestCase {
$user = $this->getTestUser()->getUser();
$permissionManager = $this->getServiceContainer()->getPermissionManager();
- // Needs edit and createpage rights to pass EditRightConstraint and CreationPermissionConstraint
+ // Needs these rights to pass AuthorizationConstraint
$permissionManager->overrideUserRightsForTesting( $user, [ 'edit', 'createpage' ] );
$edit = [
@@ -399,12 +401,12 @@ class EditPageConstraintsTest extends MediaWikiLangTestCase {
}
/**
- * EditRightConstraint integration
- * @dataProvider provideTestEditRightConstraint
+ * AuthorizationConstraint integration - 'edit' rights
+ * @dataProvider provideTestAuthorizationConstraint_edit
* @param bool $anon
* @param int $expectedErrorCode
*/
- public function testEditRightConstraint( $anon, $expectedErrorCode ) {
+ public function testAuthorizationConstraint_edit( $anon, $expectedErrorCode ) {
if ( $anon ) {
$this->disableAutoCreateTempUser();
$user = $this->getServiceContainer()->getUserFactory()->newAnonymous( '127.0.0.1' );
@@ -428,7 +430,7 @@ class EditPageConstraintsTest extends MediaWikiLangTestCase {
);
}
- public static function provideTestEditRightConstraint() {
+ public static function provideTestAuthorizationConstraint_edit() {
yield 'Anonymous user' => [ true, EditPage::AS_READ_ONLY_PAGE_ANON ];
yield 'Registered user' => [ false, EditPage::AS_READ_ONLY_PAGE_LOGGED ];
}
@@ -448,7 +450,7 @@ class EditPageConstraintsTest extends MediaWikiLangTestCase {
}
$permissionManager = $this->getServiceContainer()->getPermissionManager();
- // Needs edit rights to pass EditRightConstraint and reach ImageRedirectConstraint
+ // Needs these rights to pass AuthorizationConstraint and reach ImageRedirectConstraint
$permissionManager->overrideUserRightsForTesting( $user, [ 'edit' ] );
$edit = [
@@ -480,7 +482,7 @@ class EditPageConstraintsTest extends MediaWikiLangTestCase {
$user = $this->getTestUser()->getUser();
$permissionManager = $this->getServiceContainer()->getPermissionManager();
- // Needs edit rights to pass EditRightConstraint and reach MissingCommentConstraint
+ // Needs these rights to pass AuthorizationConstraint and reach MissingCommentConstraint
$permissionManager->overrideUserRightsForTesting( $user, [ 'edit' ] );
$edit = [
@@ -512,7 +514,7 @@ class EditPageConstraintsTest extends MediaWikiLangTestCase {
$user = $this->getTestUser()->getUser();
$permissionManager = $this->getServiceContainer()->getPermissionManager();
- // Needs edit rights to pass EditRightConstraint and reach NewSectionMissingSubjectConstraint
+ // Needs these rights to pass AuthorizationConstraint and reach NewSectionMissingSubjectConstraint
$permissionManager->overrideUserRightsForTesting( $user, [ 'edit' ] );
$edit = [
@@ -646,21 +648,14 @@ class EditPageConstraintsTest extends MediaWikiLangTestCase {
);
}
- /** UserBlockConstraint integration */
- public function testUserBlockConstraint() {
- $user = $this->createMock( User::class );
- $user->method( 'getName' )->willReturn( 'NameGoesHere' );
- $user->method( 'getId' )->willReturn( 12345 );
-
+ /** AuthorizationConstraint integration - user blocks */
+ public function testAuthorizationConstraint_block() {
$permissionManager = $this->createMock( PermissionManager::class );
- // Needs edit rights to pass EditRightConstraint and reach UserBlockConstraint
- $permissionManager->method( 'userHasRight' )->willReturn( true );
- $permissionManager->method( 'userCan' )->willReturn( true );
-
+ $permissionStatus = PermissionStatus::newEmpty();
+ $permissionStatus->setBlock( $this->makeMockBlock() );
// Not worried about the specifics of the method call, those are tested in
- // the UserBlockConstraintTest
- $permissionManager->method( 'isBlockedFrom' )->willReturn( true );
-
+ // the AuthorizationConstraintTest
+ $permissionManager->method( 'getPermissionStatus' )->willReturn( $permissionStatus );
$this->setService( 'PermissionManager', $permissionManager );
$edit = [
@@ -677,8 +672,8 @@ class EditPageConstraintsTest extends MediaWikiLangTestCase {
);
}
- /** UserRateLimitConstraint integration */
- public function testUserRateLimitConstraint() {
+ /** LinkPurgeRateLimitConstraint integration */
+ public function testLinkPurgeRateLimitConstraint() {
$this->setTemporaryHook(
'PingLimiter',
static function ( $user, $action, &$result, $incrBy ) {
diff --git a/tests/phpunit/mocks/permissions/MockAuthorityTrait.php b/tests/phpunit/mocks/permissions/MockAuthorityTrait.php
index b9b0c9350cbd..eedb4e3d7c70 100644
--- a/tests/phpunit/mocks/permissions/MockAuthorityTrait.php
+++ b/tests/phpunit/mocks/permissions/MockAuthorityTrait.php
@@ -334,6 +334,7 @@ trait MockAuthorityTrait {
return true;
} );
$mock->method( 'getBlock' )->willReturn( $block );
+ $mock->method( 'isRegistered' )->willReturn( $user->isRegistered() );
$mock->method( 'isTemp' )->willReturn( $isTemp );
$mock->method( 'isNamed' )->willReturn( $user->isRegistered() && !$isTemp );
return $mock;
diff --git a/tests/phpunit/unit/includes/editpage/Constraint/AuthorizationConstraintTest.php b/tests/phpunit/unit/includes/editpage/Constraint/AuthorizationConstraintTest.php
new file mode 100644
index 000000000000..68d8c6541858
--- /dev/null
+++ b/tests/phpunit/unit/includes/editpage/Constraint/AuthorizationConstraintTest.php
@@ -0,0 +1,111 @@
+<?php
+/**
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ */
+
+use MediaWiki\EditPage\Constraint\AuthorizationConstraint;
+use MediaWiki\EditPage\Constraint\IEditConstraint;
+use MediaWiki\Page\PageIdentity;
+use MediaWiki\Page\PageIdentityValue;
+use MediaWiki\Permissions\Authority;
+use MediaWiki\Tests\Unit\MockBlockTrait;
+use MediaWiki\Tests\Unit\Permissions\MockAuthorityTrait;
+use MediaWiki\User\UserIdentityValue;
+
+/**
+ * @covers \MediaWiki\EditPage\Constraint\AuthorizationConstraint
+ */
+class AuthorizationConstraintTest extends MediaWikiUnitTestCase {
+ use EditConstraintTestTrait;
+ use MockAuthorityTrait;
+ use MockTitleTrait;
+ use MockBlockTrait;
+
+ /**
+ * @dataProvider provideTestPass
+ */
+ public function testPass( Authority $performer, PageIdentity $page, bool $new ): void {
+ $constraint = new AuthorizationConstraint(
+ $performer,
+ $page,
+ $new
+ );
+ $this->assertConstraintPassed( $constraint );
+ }
+
+ public function provideTestPass(): iterable {
+ yield 'Edit existing page' => [
+ 'performer' => $this->mockAnonAuthorityWithPermissions( [ 'edit' ] ),
+ 'page' => PageIdentityValue::localIdentity( 123, NS_MAIN, 'AuthorizationConstraintTest' ),
+ 'new' => false,
+ ];
+ yield 'Create a new page' => [
+ 'performer' => $this->mockAnonAuthorityWithPermissions( [ 'edit', 'create' ] ),
+ 'page' => PageIdentityValue::localIdentity( 0, NS_MAIN, 'AuthorizationConstraintTest' ),
+ 'new' => true,
+ ];
+ }
+
+ /**
+ * @dataProvider provideTestFailure
+ */
+ public function testFailure(
+ Authority $performer, PageIdentity $page, bool $new, int $expectedValue
+ ): void {
+ $constraint = new AuthorizationConstraint(
+ $performer,
+ $page,
+ $new
+ );
+ $this->assertConstraintFailed( $constraint, $expectedValue );
+ }
+
+ public function provideTestFailure(): iterable {
+ yield 'Anonymous user' => [
+ 'performer' => $this->mockAnonAuthorityWithoutPermissions( [ 'edit' ] ),
+ 'page' => PageIdentityValue::localIdentity( 123, NS_MAIN, 'AuthorizationConstraintTest' ),
+ 'new' => false,
+ 'expectedValue' => IEditConstraint::AS_READ_ONLY_PAGE_ANON,
+ ];
+ yield 'Registered user' => [
+ 'performer' => $this->mockRegisteredAuthorityWithoutPermissions( [ 'edit' ] ),
+ 'page' => PageIdentityValue::localIdentity( 123, NS_MAIN, 'AuthorizationConstraintTest' ),
+ 'new' => false,
+ 'expectedValue' => IEditConstraint::AS_READ_ONLY_PAGE_LOGGED,
+ ];
+ yield 'User without create permission creates a page' => [
+ 'performer' => $this->mockAnonAuthorityWithoutPermissions( [ 'create' ] ),
+ 'page' => PageIdentityValue::localIdentity( 0, NS_MAIN, 'AuthorizationConstraintTest' ),
+ 'new' => true,
+ 'expectedValue' => IEditConstraint::AS_NO_CREATE_PERMISSION,
+ ];
+ yield 'Blocked user' => [
+ 'performer' => $this->mockUserAuthorityWithBlock(
+ UserIdentityValue::newRegistered( 42, 'AuthorizationConstraintTest User' ),
+ $this->makeMockBlock( [
+ 'decodedExpiry' => 'infinity',
+ ] ),
+ [ 'edit' ]
+ ),
+ 'page' => PageIdentityValue::localIdentity( 123, NS_MAIN, 'AuthorizationConstraintTest' ),
+ 'new' => false,
+ 'expectedValue' => IEditConstraint::AS_BLOCKED_PAGE_FOR_USER,
+ ];
+ }
+
+}
diff --git a/tests/phpunit/unit/includes/editpage/Constraint/EditConstraintFactoryTest.php b/tests/phpunit/unit/includes/editpage/Constraint/EditConstraintFactoryTest.php
index dabb27a15ff1..76ce13cb71d5 100644
--- a/tests/phpunit/unit/includes/editpage/Constraint/EditConstraintFactoryTest.php
+++ b/tests/phpunit/unit/includes/editpage/Constraint/EditConstraintFactoryTest.php
@@ -27,13 +27,11 @@ use MediaWiki\EditPage\Constraint\PageSizeConstraint;
use MediaWiki\EditPage\Constraint\ReadOnlyConstraint;
use MediaWiki\EditPage\Constraint\SimpleAntiSpamConstraint;
use MediaWiki\EditPage\Constraint\SpamRegexConstraint;
-use MediaWiki\EditPage\Constraint\UserBlockConstraint;
use MediaWiki\EditPage\SpamChecker;
use MediaWiki\HookContainer\HookContainer;
use MediaWiki\Language\Language;
use MediaWiki\Logger\Spi;
use MediaWiki\MainConfigNames;
-use MediaWiki\Permissions\PermissionManager;
use MediaWiki\Permissions\RateLimiter;
use MediaWiki\Title\Title;
use MediaWiki\User\User;
@@ -61,7 +59,6 @@ class EditConstraintFactoryTest extends MediaWikiUnitTestCase {
$factory = new EditConstraintFactory(
$options,
$loggerFactory,
- $this->createMock( PermissionManager::class ),
$this->createMock( HookContainer::class ),
$this->createMock( ReadOnlyMode::class ),
$this->createMock( SpamChecker::class ),
@@ -107,9 +104,5 @@ class EditConstraintFactoryTest extends MediaWikiUnitTestCase {
$title
)
);
- $this->assertInstanceOf(
- UserBlockConstraint::class,
- $factory->newUserBlockConstraint( $title, $user )
- );
}
}
diff --git a/tests/phpunit/unit/includes/editpage/Constraint/EditRightConstraintTest.php b/tests/phpunit/unit/includes/editpage/Constraint/EditRightConstraintTest.php
deleted file mode 100644
index 9d3346e87d01..000000000000
--- a/tests/phpunit/unit/includes/editpage/Constraint/EditRightConstraintTest.php
+++ /dev/null
@@ -1,170 +0,0 @@
-<?php
-/**
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- * @file
- */
-
-use MediaWiki\EditPage\Constraint\EditRightConstraint;
-use MediaWiki\EditPage\Constraint\IEditConstraint;
-use MediaWiki\Permissions\PermissionManager;
-use MediaWiki\Tests\Unit\Permissions\MockAuthorityTrait;
-use MediaWiki\Title\Title;
-use MediaWiki\User\User;
-
-/**
- * Tests the EditRightConstraint
- *
- * @author DannyS712
- *
- * @covers \MediaWiki\EditPage\Constraint\EditRightConstraint
- */
-class EditRightConstraintTest extends MediaWikiUnitTestCase {
- use EditConstraintTestTrait;
- use MockAuthorityTrait;
- use MockTitleTrait;
-
- /**
- * @dataProvider provideTestPass
- * @param User $performer
- * @param bool $new
- * @param PermissionManager $permissionManager
- * @return void
- */
- public function testPass( User $performer, bool $new, PermissionManager $permissionManager ) {
- $constraint = new EditRightConstraint(
- $performer,
- $permissionManager,
- $this->createMock( Title::class ),
- $new
- );
- $this->assertConstraintPassed( $constraint );
- }
-
- public function provideTestPass() {
- $title = $this->createMock( Title::class );
- $userEdit = $this->createMock( User::class );
- $permissionManagerEdit = $this->createMock( PermissionManager::class );
- $permissionManagerEdit->expects( $this->once() )
- ->method( 'userCan' )
- ->with(
- 'edit',
- $userEdit,
- $title
- )
- ->willReturn( true );
- $userCreateAndEdit = $this->createMock( User::class );
- $userCreateAndEdit->expects( $this->once() )
- ->method( 'authorizeWrite' )
- ->with(
- 'create',
- $title
- )
- ->willReturn( true );
- $permissionManagerCreateAndEdit = $this->createMock( PermissionManager::class );
- $permissionManagerCreateAndEdit->expects( $this->once() )
- ->method( 'userCan' )
- ->with(
- 'edit',
- $userCreateAndEdit,
- $title
- )
- ->willReturn( true );
- yield 'Edit existing page' => [
- 'performer' => $userEdit,
- 'new' => false,
- 'permissionManager' => $permissionManagerEdit
- ];
- yield 'Create a new page' => [
- 'performer' => $userCreateAndEdit,
- 'new' => true,
- 'permissionManager' => $permissionManagerCreateAndEdit
- ];
- }
-
- /**
- * @dataProvider provideTestFailure
- * @param User $performer
- * @param bool $new
- * @param PermissionManager $permissionManager
- * @param int $expectedValue
- */
- public function testFailure(
- User $performer, bool $new, PermissionManager $permissionManager, int $expectedValue
- ) {
- $title = $this->createMock( Title::class );
- $constraint = new EditRightConstraint(
- $performer,
- $permissionManager,
- $title,
- $new
- );
- $this->assertConstraintFailed( $constraint, $expectedValue );
- }
-
- public function provideTestFailure() {
- $title = $this->createMock( Title::class );
- $anon = $this->createMock( User::class );
- $anon->expects( $this->once() )->method( 'isRegistered' )->willReturn( false );
- $permissionManagerAnon = $this->createMock( PermissionManager::class );
- $permissionManagerAnon->expects( $this->once() )
- ->method( 'userCan' )
- ->with(
- 'edit',
- $anon,
- $title
- )
- ->willReturn( false );
- $reg = $this->createMock( User::class );
- $reg->expects( $this->once() )->method( 'isRegistered' )->willReturn( true );
- $permissionManagerReg = $this->createMock( PermissionManager::class );
- $permissionManagerReg->expects( $this->once() )
- ->method( 'userCan' )
- ->with(
- 'edit',
- $reg,
- $title
- )
- ->willReturn( false );
- $userWithoutCreatePerm = $this->createMock( User::class );
- $userWithoutCreatePerm->expects( $this->once() )
- ->method( 'authorizeWrite' )
- ->with(
- 'create',
- $title
- )
- ->willReturn( false );
- yield 'Anonymous user' => [
- 'performer' => $anon,
- 'new' => false,
- 'permissionManager' => $permissionManagerAnon,
- 'expectedValue' => IEditConstraint::AS_READ_ONLY_PAGE_ANON,
- ];
- yield 'Registered user' => [
- 'performer' => $reg,
- 'new' => false,
- 'permissionManager' => $permissionManagerReg,
- 'expectedValue' => IEditConstraint::AS_READ_ONLY_PAGE_LOGGED,
- ];
- yield 'User without create permission creates a page' => [
- 'performer' => $userWithoutCreatePerm,
- 'new' => true,
- 'permissionManager' => $this->createMock( PermissionManager::class ),
- 'expectedValue' => IEditConstraint::AS_NO_CREATE_PERMISSION,
- ];
- }
-
-}
diff --git a/tests/phpunit/unit/includes/editpage/Constraint/UserRateLimitConstraintTest.php b/tests/phpunit/unit/includes/editpage/Constraint/LinkPurgeRateLimitConstraintTest.php
index b832aa7ee5e5..7363601e0f72 100644
--- a/tests/phpunit/unit/includes/editpage/Constraint/UserRateLimitConstraintTest.php
+++ b/tests/phpunit/unit/includes/editpage/Constraint/LinkPurgeRateLimitConstraintTest.php
@@ -19,20 +19,20 @@
*/
use MediaWiki\EditPage\Constraint\IEditConstraint;
-use MediaWiki\EditPage\Constraint\UserRateLimitConstraint;
+use MediaWiki\EditPage\Constraint\LinkPurgeRateLimitConstraint;
use MediaWiki\Permissions\RateLimiter;
use MediaWiki\Permissions\RateLimitSubject;
use MediaWiki\User\UserIdentityValue;
use PHPUnit\Framework\MockObject\MockObject;
/**
- * Tests the UserRateLimitConstraint
+ * Tests the LinkPurgeRateLimitConstraint
*
* @author DannyS712
*
- * @covers \MediaWiki\EditPage\Constraint\UserRateLimitConstraint
+ * @covers \MediaWiki\EditPage\Constraint\LinkPurgeRateLimitConstraint
*/
-class UserRateLimitConstraintTest extends MediaWikiUnitTestCase {
+class LinkPurgeRateLimitConstraintTest extends MediaWikiUnitTestCase {
use EditConstraintTestTrait;
/**
@@ -42,19 +42,10 @@ class UserRateLimitConstraintTest extends MediaWikiUnitTestCase {
*/
private function getRateLimiter( $fail ) {
$mock = $this->createNoOpMock( RateLimiter::class, [ 'limit' ] );
- $expectedArgs = [
- [ 'edit', 1, false ],
- [ 'linkpurge', 0, false ],
- [ 'editcontentmodel', 1, $fail ]
- ];
- $mock->expects( $this->exactly( 3 ) )
+ $mock->expects( $this->once() )
->method( 'limit' )
- ->willReturnCallback( function ( $_, $action, $incrBy ) use ( &$expectedArgs ) {
- $curExpectedArgs = array_shift( $expectedArgs );
- $this->assertSame( $curExpectedArgs[0], $action );
- $this->assertSame( $curExpectedArgs[1], $incrBy );
- return $curExpectedArgs[2];
- } );
+ ->with( self::anything(), 'linkpurge', 0 )
+ ->willReturn( $fail );
return $mock;
}
@@ -63,7 +54,7 @@ class UserRateLimitConstraintTest extends MediaWikiUnitTestCase {
$subject = new RateLimitSubject( new UserIdentityValue( 1, 'test' ), null, [] );
- $constraint = new UserRateLimitConstraint( $limiter, $subject, 'OldContentModel', 'NewContentModel' );
+ $constraint = new LinkPurgeRateLimitConstraint( $limiter, $subject );
$this->assertConstraintPassed( $constraint );
}
@@ -72,7 +63,7 @@ class UserRateLimitConstraintTest extends MediaWikiUnitTestCase {
$subject = new RateLimitSubject( new UserIdentityValue( 1, 'test' ), null, [] );
- $constraint = new UserRateLimitConstraint( $limiter, $subject, 'OldContentModel', 'NewContentModel' );
+ $constraint = new LinkPurgeRateLimitConstraint( $limiter, $subject );
$this->assertConstraintFailed( $constraint, IEditConstraint::AS_RATE_LIMITED );
}
diff --git a/tests/phpunit/unit/includes/editpage/Constraint/UserBlockConstraintTest.php b/tests/phpunit/unit/includes/editpage/Constraint/UserBlockConstraintTest.php
deleted file mode 100644
index e231e4a50c66..000000000000
--- a/tests/phpunit/unit/includes/editpage/Constraint/UserBlockConstraintTest.php
+++ /dev/null
@@ -1,72 +0,0 @@
-<?php
-/**
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- * @file
- */
-
-use MediaWiki\EditPage\Constraint\IEditConstraint;
-use MediaWiki\EditPage\Constraint\UserBlockConstraint;
-use MediaWiki\Permissions\PermissionManager;
-use MediaWiki\Title\Title;
-use MediaWiki\User\User;
-
-/**
- * Tests the UserBlockConstraint
- *
- * @author DannyS712
- *
- * @covers \MediaWiki\EditPage\Constraint\UserBlockConstraint
- */
-class UserBlockConstraintTest extends MediaWikiUnitTestCase {
- use EditConstraintTestTrait;
-
- public function testPass() {
- $title = $this->createMock( Title::class );
- $user = $this->createMock( User::class );
- $permissionManager = $this->createMock( PermissionManager::class );
- $permissionManager->expects( $this->once() )
- ->method( 'isBlockedFrom' )
- ->with(
- $user,
- $title
- )
- ->willReturn( false );
-
- $constraint = new UserBlockConstraint( $permissionManager, $title, $user );
- $this->assertConstraintPassed( $constraint );
- }
-
- public function testFailure() {
- $title = $this->createMock( Title::class );
- $user = $this->createMock( User::class );
- $permissionManager = $this->createMock( PermissionManager::class );
- $permissionManager->expects( $this->once() )
- ->method( 'isBlockedFrom' )
- ->with(
- $user,
- $title
- )
- ->willReturn( true );
-
- $constraint = new UserBlockConstraint( $permissionManager, $title, $user );
- $this->assertConstraintFailed(
- $constraint,
- IEditConstraint::AS_BLOCKED_PAGE_FOR_USER
- );
- }
-
-}