diff options
author | daniel <dkinzler@wikimedia.org> | 2021-04-16 14:55:24 +0200 |
---|---|---|
committer | daniel <dkinzler@wikimedia.org> | 2021-05-11 11:36:11 +0200 |
commit | 753b1bcaffa3b575d91b31ec20ba4fa420009813 (patch) | |
tree | 0ee7fd54706a4edc2979cac5e5a4f3c99f7c195d /includes/block | |
parent | b6fea9934146a76fea1f887ab6022d53f7d30995 (diff) | |
download | mediawikicore-753b1bcaffa3b575d91b31ec20ba4fa420009813.tar.gz mediawikicore-753b1bcaffa3b575d91b31ec20ba4fa420009813.zip |
Introduce Block interface and replace AbstractBlock.
In order to allow Authority to know about user blocks,
we need a narrow interface to represent such blocks.
This deprecates some methods on AbstractBlocks in favor
of new methods on the Block interface that avoid binding to
the User class.
Bug: T271494
Change-Id: I7bb950533970984a014de0434518fbbefb695131
Diffstat (limited to 'includes/block')
-rw-r--r-- | includes/block/AbstractBlock.php | 58 | ||||
-rw-r--r-- | includes/block/Block.php | 150 | ||||
-rw-r--r-- | includes/block/BlockErrorFormatter.php | 29 | ||||
-rw-r--r-- | includes/block/BlockPermissionChecker.php | 5 | ||||
-rw-r--r-- | includes/block/BlockUser.php | 6 | ||||
-rw-r--r-- | includes/block/CompositeBlock.php | 8 | ||||
-rw-r--r-- | includes/block/DatabaseBlock.php | 8 | ||||
-rw-r--r-- | includes/block/DatabaseBlockStore.php | 20 | ||||
-rw-r--r-- | includes/block/SystemBlock.php | 9 | ||||
-rw-r--r-- | includes/block/UnblockUser.php | 17 |
10 files changed, 248 insertions, 62 deletions
diff --git a/includes/block/AbstractBlock.php b/includes/block/AbstractBlock.php index c56859b5f02c..49280ef4f168 100644 --- a/includes/block/AbstractBlock.php +++ b/includes/block/AbstractBlock.php @@ -34,7 +34,7 @@ use User; * @note Extensions should not subclass this, as MediaWiki currently does not support custom block types. * @since 1.34 Factored out from DatabaseBlock (previously Block). */ -abstract class AbstractBlock { +abstract class AbstractBlock implements Block { /** @var CommentStoreComment */ protected $reason; @@ -80,14 +80,6 @@ abstract class AbstractBlock { /** @var bool */ protected $isSitewide = true; - # TYPE constants - # Do not introduce negative constants without changing BlockUser command object. - public const TYPE_USER = 1; - public const TYPE_IP = 2; - public const TYPE_RANGE = 3; - public const TYPE_AUTO = 4; - public const TYPE_ID = 5; - /** * Create a new block with specified parameters on a user, IP or IP range. * @@ -139,15 +131,6 @@ abstract class AbstractBlock { } /** - * Get the information that identifies this block, such that a user could - * look up everything that can be found about this block. May be an ID, - * array of IDs, type, etc. - * - * @return mixed Identifying information - */ - abstract public function getIdentifier(); - - /** * Get the reason given for creating the block, as a string. * * Deprecated, since this gives the caller no control over the language @@ -350,6 +333,9 @@ abstract class AbstractBlock { * * If the type is not null, it will be an AbstractBlock::TYPE_ constant. * + * @deprecated since 1.37, use getTargetName() and getTargetUserIdentity() + * together with getType() + * * @return array [ User|String|null, int|null ] * @todo FIXME: This should be an integral part of the block member variables */ @@ -358,9 +344,11 @@ abstract class AbstractBlock { } /** - * Get the target for this particular block. Note that for autoblocks, + * Get the target for this particular block. Note that for autoblocks, * this returns the unredacted name; frontend functions need to call $block->getRedactedName() * in this situation. + * @deprecated since 1.37, use getTargetName() and getTargetUserIdentity() + * together with getType() * @return User|string|null */ public function getTarget() { @@ -368,6 +356,38 @@ abstract class AbstractBlock { } /** + * @since 1.37 + * @return ?UserIdentity + */ + public function getTargetUserIdentity(): ?UserIdentity { + return $this->target instanceof UserIdentity ? $this->target : null; + } + + /** + * @since 1.37 + * @return string + */ + public function getTargetName(): string { + return $this->target instanceof UserIdentity + ? $this->target->getName() + : (string)$this->target; + } + + /** + * @param UserIdentity|string $target + * + * @return bool + * @since 1.37 + */ + public function isBlocking( $target ): bool { + $targetName = $target instanceof UserIdentity + ? $target->getName() + : (string)$target; + + return $targetName === $this->getTargetName(); + } + + /** * Get the block expiry time * * @since 1.19 diff --git a/includes/block/Block.php b/includes/block/Block.php new file mode 100644 index 000000000000..523a23d3166a --- /dev/null +++ b/includes/block/Block.php @@ -0,0 +1,150 @@ +<?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\Block; + +use CommentStoreComment; +use MediaWiki\User\UserIdentity; + +/** + * Represents a block that may prevent users from performing specific operations. + * The block may apply to a specific user, to a network address, network range, + * or some other aspect of a web request. + * The block may apply to the entire site, or may be limited to specific pages + * or namespaces. + * + * @since 1.37 Extracted from the AbstractBlock base class, + * which was in turn factored out of DatabaseBlock in 1.34. + */ +interface Block { + + # TYPE constants + # Do not introduce negative constants without changing BlockUser command object. + public const TYPE_USER = 1; + public const TYPE_IP = 2; + public const TYPE_RANGE = 3; + public const TYPE_AUTO = 4; + public const TYPE_ID = 5; + + /** + * Get the block ID + * @return ?int + */ + public function getId(); + + /** + * Get the information that identifies this block, such that a user could + * look up everything that can be found about this block. Typically a scalar ID (integer + * or string), but can also return a list of IDs, or an associative array encoding a composite + * ID. Must be safe to serialize as JSON. + * + * @return mixed Identifying information + */ + public function getIdentifier(); + + /** + * Get the user who applied this block + * + * @return UserIdentity|null user identity or null. May be an external user. + */ + public function getBlocker(): ?UserIdentity; + + /** + * Get the reason for creating the block. + * + * @return CommentStoreComment + */ + public function getReasonComment(); + + /** + * Get the UserIdentity identifying the blocked user, + * if the target is indeed a user (that is, if getType() returns TYPE_USER). + * + * @return ?UserIdentity + */ + public function getTargetUserIdentity(): ?UserIdentity; + + /** + * Return the name of the block target as a string. + * Depending on the type returned by get Type(), this could be a user name, + * an IP address or range, an internal ID, etc. + * + * @return string + */ + public function getTargetName(): string; + + /** + * Determines whether this block is blocking the given target (and only that target). + * + * @param UserIdentity|string $target + * + * @return bool + */ + public function isBlocking( $target ): bool; + + /** + * Get the block expiry time + * + * @return string + */ + public function getExpiry(); + + /** + * Get the type of target for this particular block. + * @return int|null Block::TYPE_ constant, will never be TYPE_ID + */ + public function getType(); + + /** + * Get the timestamp indicating when the block was created + * + * @return string + */ + public function getTimestamp(); + + /** + * Indicates that the block is a sitewide block. This means the user is + * prohibited from editing any page on the site (other than their own talk + * page). + * + * @return bool + */ + public function isSitewide(); + + /** + * Get the flag indicating whether this block blocks the target from + * creating an account. (Note that the flag may be overridden depending on + * global configs.) + * + * @return bool + */ + public function isCreateAccountBlocked(); + + /** + * Returns whether the block is a hardblock (affects logged-in users on a given IP/range) + * + * Note that users are always hardblocked, since they're logged in by definition. + * + * @return bool + */ + public function isHardblock(); + +} diff --git a/includes/block/BlockErrorFormatter.php b/includes/block/BlockErrorFormatter.php index 9382f1edd54b..ca224f47a9a9 100644 --- a/includes/block/BlockErrorFormatter.php +++ b/includes/block/BlockErrorFormatter.php @@ -49,14 +49,14 @@ class BlockErrorFormatter { * block features. Message parameters are formatted for the specified user and * language. * - * @param AbstractBlock $block + * @param Block $block * @param UserIdentity $user * @param Language $language * @param string $ip * @return Message */ public function getMessage( - AbstractBlock $block, + Block $block, UserIdentity $user, Language $language, $ip @@ -69,7 +69,7 @@ class BlockErrorFormatter { /** * Get a standard set of block details for building a block error message. * - * @param AbstractBlock $block + * @param Block $block * @return mixed[] * - identifier: Information for looking up the block * - targetName: The target, as a string @@ -79,12 +79,13 @@ class BlockErrorFormatter { * - expiry: Expiry time * - timestamp: Time the block was created */ - private function getBlockErrorInfo( AbstractBlock $block ) { + private function getBlockErrorInfo( Block $block ) { + $blocker = $block->getBlocker(); return [ 'identifier' => $block->getIdentifier(), - 'targetName' => (string)$block->getTarget(), - 'blockerName' => $block->getByName(), - 'blockerId' => $block->getBy(), + 'targetName' => $block->getTargetName(), + 'blockerName' => $blocker ? $blocker->getName() : '', + 'blockerId' => $blocker ? $blocker->getId() : 0, 'reason' => $block->getReasonComment(), 'expiry' => $block->getExpiry(), 'timestamp' => $block->getTimestamp(), @@ -96,13 +97,13 @@ class BlockErrorFormatter { * formatted for a specified user and language. * * @since 1.35 - * @param AbstractBlock $block + * @param Block $block * @param UserIdentity $user * @param Language $language * @return mixed[] See getBlockErrorInfo */ private function getFormattedBlockErrorInfo( - AbstractBlock $block, + Block $block, UserIdentity $user, Language $language ) { @@ -158,13 +159,13 @@ class BlockErrorFormatter { /** * Determine the block error message key by examining the block. * - * @param AbstractBlock $block + * @param Block $block * @return string Message key */ - private function getBlockErrorMessageKey( AbstractBlock $block ) { + private function getBlockErrorMessageKey( Block $block ) { $key = 'blockedtext'; if ( $block instanceof DatabaseBlock ) { - if ( $block->getType() === AbstractBlock::TYPE_AUTO ) { + if ( $block->getType() === Block::TYPE_AUTO ) { $key = 'autoblockedtext'; } elseif ( !$block->isSitewide() ) { $key = 'blockedtext-partial'; @@ -181,7 +182,7 @@ class BlockErrorFormatter { * Get the formatted parameters needed to build the block error messages handled by * getBlockErrorMessageKey. * - * @param AbstractBlock $block + * @param Block $block * @param UserIdentity $user * @param Language $language * @param string $ip @@ -196,7 +197,7 @@ class BlockErrorFormatter { * - timestamp: Time the block was created, in the specified language */ private function getBlockErrorMessageParams( - AbstractBlock $block, + Block $block, UserIdentity $user, Language $language, $ip diff --git a/includes/block/BlockPermissionChecker.php b/includes/block/BlockPermissionChecker.php index f2cb746e8d4e..9bd91540a619 100644 --- a/includes/block/BlockPermissionChecker.php +++ b/includes/block/BlockPermissionChecker.php @@ -141,7 +141,7 @@ class BlockPermissionChecker { // Blocked admin is trying to alter their own block // Self-blocked admins can always remove or alter their block - if ( $block->getByName() === $performerIdentity->getName() ) { + if ( $block->getBlocker() && $performerIdentity->equals( $block->getBlocker() ) ) { return true; } @@ -155,7 +155,8 @@ class BlockPermissionChecker { if ( $this->target instanceof UserIdentity && - $block->getByName() === $this->target->getName() + $block->getBlocker() && + $this->target->equals( $block->getBlocker() ) ) { // T150826: Blocked admins can always block the admin who blocked them return true; diff --git a/includes/block/BlockUser.php b/includes/block/BlockUser.php index 8555f8a2dfb4..04f5f90c5057 100644 --- a/includes/block/BlockUser.php +++ b/includes/block/BlockUser.php @@ -559,12 +559,12 @@ class BlockUser { if ( $priorBlock === null ) { $this->logger->warning( 'Block could not be inserted. No existing block was found.' ); - return Status::newFatal( 'ipb-block-not-found', $block->getTarget() ); + return Status::newFatal( 'ipb-block-not-found', $block->getTargetName() ); } if ( $block->equals( $priorBlock ) ) { // Block settings are equal => user is already blocked - return Status::newFatal( 'ipb_already_blocked', $block->getTarget() ); + return Status::newFatal( 'ipb_already_blocked', $block->getTargetName() ); } $currentBlock = $this->configureBlock( $priorBlock ); @@ -572,7 +572,7 @@ class BlockUser { $isReblock = true; $block = $currentBlock; } else { - return Status::newFatal( 'ipb_already_blocked', $block->getTarget() ); + return Status::newFatal( 'ipb_already_blocked', $block->getTargetName() ); } } diff --git a/includes/block/CompositeBlock.php b/includes/block/CompositeBlock.php index b333b26ac189..0f898b829640 100644 --- a/includes/block/CompositeBlock.php +++ b/includes/block/CompositeBlock.php @@ -22,6 +22,7 @@ namespace MediaWiki\Block; +use MediaWiki\User\UserIdentity; use Title; /** @@ -206,4 +207,11 @@ class CompositeBlock extends AbstractBlock { public function getByName() { return ''; } + + /** + * @inheritDoc + */ + public function getBlocker(): ?UserIdentity { + return null; + } } diff --git a/includes/block/DatabaseBlock.php b/includes/block/DatabaseBlock.php index 949c8d1528db..761aa836ecea 100644 --- a/includes/block/DatabaseBlock.php +++ b/includes/block/DatabaseBlock.php @@ -391,7 +391,7 @@ class DatabaseBlock extends AbstractBlock { if ( $block->getType() == self::TYPE_RANGE ) { # This is the number of bits that are allowed to vary in the block, give # or take some floating point errors - $target = $block->getTarget(); + $target = $block->getTargetName(); $max = IPUtils::isIPv6( $target ) ? 128 : 32; list( $network, $bits ) = IPUtils::parseCIDR( $target ); $size = $max - $bits; @@ -655,13 +655,13 @@ class DatabaseBlock extends AbstractBlock { # Make a new block object with the desired properties. $autoblock = new DatabaseBlock; - wfDebug( "Autoblocking {$this->getTarget()}@" . $autoblockIP ); + wfDebug( "Autoblocking {$this->getTargetName()}@" . $autoblockIP ); $autoblock->setTarget( $autoblockIP ); $autoblock->setBlocker( $this->getBlocker() ); $autoblock->setReason( wfMessage( 'autoblocker', - (string)$this->getTarget(), + $this->getTargetName(), $this->getReasonComment()->text )->inContentLanguage()->plain() ); @@ -854,7 +854,7 @@ class DatabaseBlock extends AbstractBlock { wfMessage( 'autoblockid', $this->mId )->text() ); } else { - return htmlspecialchars( $this->getTarget() ); + return htmlspecialchars( $this->getTargetName() ); } } diff --git a/includes/block/DatabaseBlockStore.php b/includes/block/DatabaseBlockStore.php index 191579ca17c9..ea5404399715 100644 --- a/includes/block/DatabaseBlockStore.php +++ b/includes/block/DatabaseBlockStore.php @@ -29,7 +29,6 @@ use MediaWiki\Config\ServiceOptions; use MediaWiki\HookContainer\HookContainer; use MediaWiki\HookContainer\HookRunner; use MediaWiki\User\ActorStoreFactory; -use MediaWiki\User\UserIdentity; use MWException; use Psr\Log\LoggerInterface; use ReadOnlyMode; @@ -342,12 +341,11 @@ class DatabaseBlockStore { ) : array { $expiry = $dbw->encodeExpiry( $block->getExpiry() ); - $target = $block->getTarget(); - $forcedTargetId = $block->getForcedTargetId(); + $forcedTargetId = $block->getForcedTargetID(); if ( $forcedTargetId ) { $userId = $forcedTargetId; - } elseif ( $target instanceof User ) { - $userId = $target->getId(); + } elseif ( $block->getTargetUserIdentity() ) { + $userId = $block->getTargetUserIdentity()->getId(); } else { $userId = 0; } @@ -360,7 +358,7 @@ class DatabaseBlockStore { ->acquireActorId( $block->getBlocker(), $dbw ); $blockArray = [ - 'ipb_address' => (string)$target, + 'ipb_address' => $block->getTargetName(), 'ipb_user' => $userId, 'ipb_by_actor' => $blockerActor, 'ipb_timestamp' => $dbw->timestamp( $block->getTimestamp() ), @@ -431,7 +429,7 @@ class DatabaseBlockStore { // If autoblock is enabled, autoblock the LAST IP(s) used if ( $block->isAutoblocking() && $block->getType() == AbstractBlock::TYPE_USER ) { $this->logger->debug( - 'Doing retroactive autoblocks for ' . $block->getTarget() + 'Doing retroactive autoblocks for ' . $block->getTargetName() ); $hookAutoBlocked = []; @@ -463,12 +461,11 @@ class DatabaseBlockStore { return []; } - list( $target, $type ) = $block->getTargetAndType(); + $type = $block->getType(); if ( $type !== AbstractBlock::TYPE_USER ) { // Autoblocks only apply to users return []; } - /** @var UserIdentity $target */ $dbr = $this->loadBalancer->getConnectionRef( DB_REPLICA ); @@ -477,7 +474,10 @@ class DatabaseBlockStore { 'LIMIT' => 1, ]; - $actor = $this->actorStoreFactory->getActorNormalization()->findActorId( $target, $dbr ); + $actor = $this->actorStoreFactory->getActorNormalization()->findActorId( + $block->getTargetUserIdentity(), + $dbr + ); if ( !$actor ) { $this->logger->debug( 'No actor found to retroactively autoblock' ); return []; diff --git a/includes/block/SystemBlock.php b/includes/block/SystemBlock.php index e99d076f09fb..318cc1ab05d9 100644 --- a/includes/block/SystemBlock.php +++ b/includes/block/SystemBlock.php @@ -22,6 +22,8 @@ namespace MediaWiki\Block; +use MediaWiki\User\UserIdentity; + /** * System blocks are temporary blocks that are created on enforcement (e.g. * from IP lists) and are not saved to the database. The target of a @@ -107,4 +109,11 @@ class SystemBlock extends AbstractBlock { public function getByName() { return ''; } + + /** + * @inheritDoc + */ + public function getBlocker(): ?UserIdentity { + return null; + } } diff --git a/includes/block/UnblockUser.php b/includes/block/UnblockUser.php index 85a69864c450..92b0e9263b7b 100644 --- a/includes/block/UnblockUser.php +++ b/includes/block/UnblockUser.php @@ -31,7 +31,6 @@ use MediaWiki\User\UserIdentity; use RevisionDeleteUser; use Status; use TitleValue; -use User; /** * Backend class for unblocking users @@ -175,7 +174,7 @@ class UnblockUser { $this->block->getType() === AbstractBlock::TYPE_RANGE && $this->targetType === AbstractBlock::TYPE_IP ) { - $status->fatal( 'ipb_blocked_as_range', $this->target, $this->block->getTarget() ); + $status->fatal( 'ipb_blocked_as_range', $this->target, $this->block->getTargetName() ); return $status; } @@ -190,17 +189,17 @@ class UnblockUser { $deleteBlockStatus = $this->blockStore->deleteBlock( $this->block ); if ( !$deleteBlockStatus ) { - $status->fatal( 'ipb_cant_unblock', $this->block->getTarget() ); + $status->fatal( 'ipb_cant_unblock', $this->block->getTargetName() ); return $status; } $this->hookRunner->onUnblockUserComplete( $this->block, $legacyUser ); // Unset _deleted fields as needed - if ( $this->block->getHideName() && $this->block->getTarget() instanceof User ) { - // Something is deeply FUBAR if this is not a User object, but who knows? - $id = $this->block->getTarget()->getId(); - RevisionDeleteUser::unsuppressUserName( $this->block->getTarget(), $id ); + $user = $this->block->getTargetUserIdentity(); + if ( $this->block->getHideName() && $user ) { + $id = $user->getId(); + RevisionDeleteUser::unsuppressUserName( $user->getName(), $id ); } $this->log(); @@ -217,9 +216,7 @@ class UnblockUser { if ( $this->block->getType() === DatabaseBlock::TYPE_AUTO ) { $page = TitleValue::tryNew( NS_USER, '#' . $this->block->getId() ); } else { - $page = $this->block->getTarget() instanceof UserIdentity - ? $this->block->getTarget()->getUserPage() - : TitleValue::tryNew( NS_USER, $this->block->getTarget() ); + $page = TitleValue::tryNew( NS_USER, $this->block->getTargetName() ); } $logEntry = new ManualLogEntry( 'block', 'unblock' ); |