2004-02-18 02:15:00 +00:00
|
|
|
<?php
|
2004-09-02 23:28:24 +00:00
|
|
|
/**
|
Separate Block into AbstractBlock, Block and SystemBlock
This commit splits the existing Block class into AbstractBlock, Block
and SystemBlock.
Before this patch, the Block class represents several types of
blocks, which can be separated into blocks stored in the database,
and temporary blocks created by the system. These are now
represented by Block and SystemBlock, which inherit from
AbstractBlock.
This lays the foundations for:
* enforcing block parameters from multiple blocks that apply to a
user/IP address
* improvements to the Block API, including the addition of services
Breaking changes: functions expecting a Block object should still
expect a Block object if it came from the database, but other
functions may now need to expect an AbstractBlock or SystemBlock
object. (Note that an alternative naming scheme, in which the
abstract class is called Block and the subclasses are DatabaseBlock
and SystemBlock, avoids this breakage. However, it introduces more
breakages to calls to static Block methods and new Block
instantiations.)
Changes to tests: system blocks don't set the $blockCreateAccount or
$mExipry block properties, so remove/change any tests that assume
they do.
Bug: T222737
Change-Id: I83bceb5e5049e254c90ace060f8f8fad44696c67
2019-03-18 22:09:49 +00:00
|
|
|
* Class for blocks stored in the database.
|
2005-08-02 13:35:19 +00:00
|
|
|
*
|
2011-03-22 17:18:15 +00:00
|
|
|
* 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
|
2004-09-03 23:00:01 +00:00
|
|
|
*
|
2011-03-22 17:18:15 +00:00
|
|
|
* @file
|
2004-09-02 23:28:24 +00:00
|
|
|
*/
|
2016-09-22 02:52:06 +00:00
|
|
|
|
2019-05-13 14:18:07 +00:00
|
|
|
namespace MediaWiki\Block;
|
|
|
|
|
|
|
|
|
|
use CommentStore;
|
|
|
|
|
use Hooks;
|
|
|
|
|
use Html;
|
2021-09-26 19:51:13 +00:00
|
|
|
use InvalidArgumentException;
|
Introduce infrastructure for partial blocks for actions
This adds a new type of block restriction for actions, which extends
AbstractRestriction. Like page and namespace restrictions, action
restrictions are stored in the ipblocks_restrictions table.
Blockable actions are defined in a BlockActionInfo service, with a
method for getting all the blockable actions, getAllBlockActions.
Action blocks are checked for in PermissionManager::checkUserBlock
using DatabaseBlock::appliesToRight. To make this work, this patch
also removes the 'edit' case from AbstractBlock::appliesToRight,
which always returned true. This was incorrect, as blocks do not
always apply to edit, so cases that called appliesToRight('edit')
were fixed before this commit. appliesToRight('edit') now returns
null (i.e. unsure), which is correct because it is not possible to
determine whether a block applies to editing a particular page
without knowing what that page is, and appliesToRight doesn't know
that page.
There are some flags on sitewide blocks that predate partial blocks,
which block particular actions: 'createaccount' and 'sendemail'.
These are still handled in AbstractBlock::appliesToRight, and are
still checked for separately in the peripheral components.
The feature flag $wgEnablePartialActionBlocks must set to true to
enable partial action blocks.
Bug: T279556
Bug: T6995
Change-Id: I17962bb7c4247a12c722e7bc6bcaf8c36efd8600
2021-04-26 23:07:17 +00:00
|
|
|
use MediaWiki\Block\Restriction\ActionRestriction;
|
2018-10-30 18:19:22 +00:00
|
|
|
use MediaWiki\Block\Restriction\NamespaceRestriction;
|
|
|
|
|
use MediaWiki\Block\Restriction\PageRestriction;
|
2019-05-13 14:18:07 +00:00
|
|
|
use MediaWiki\Block\Restriction\Restriction;
|
2019-12-20 11:57:40 +00:00
|
|
|
use MediaWiki\Logger\LoggerFactory;
|
2016-09-22 02:52:06 +00:00
|
|
|
use MediaWiki\MediaWikiServices;
|
2021-03-05 16:44:14 +00:00
|
|
|
use MediaWiki\User\UserIdentity;
|
2019-05-13 14:18:07 +00:00
|
|
|
use MWException;
|
|
|
|
|
use stdClass;
|
|
|
|
|
use Title;
|
2019-06-25 18:53:15 +00:00
|
|
|
use Wikimedia\IPUtils;
|
2019-05-13 14:18:07 +00:00
|
|
|
use Wikimedia\Rdbms\Database;
|
|
|
|
|
use Wikimedia\Rdbms\IDatabase;
|
2016-09-22 02:52:06 +00:00
|
|
|
|
Separate Block into AbstractBlock, Block and SystemBlock
This commit splits the existing Block class into AbstractBlock, Block
and SystemBlock.
Before this patch, the Block class represents several types of
blocks, which can be separated into blocks stored in the database,
and temporary blocks created by the system. These are now
represented by Block and SystemBlock, which inherit from
AbstractBlock.
This lays the foundations for:
* enforcing block parameters from multiple blocks that apply to a
user/IP address
* improvements to the Block API, including the addition of services
Breaking changes: functions expecting a Block object should still
expect a Block object if it came from the database, but other
functions may now need to expect an AbstractBlock or SystemBlock
object. (Note that an alternative naming scheme, in which the
abstract class is called Block and the subclasses are DatabaseBlock
and SystemBlock, avoids this breakage. However, it introduces more
breakages to calls to static Block methods and new Block
instantiations.)
Changes to tests: system blocks don't set the $blockCreateAccount or
$mExipry block properties, so remove/change any tests that assume
they do.
Bug: T222737
Change-Id: I83bceb5e5049e254c90ace060f8f8fad44696c67
2019-03-18 22:09:49 +00:00
|
|
|
/**
|
2019-05-13 14:18:07 +00:00
|
|
|
* A DatabaseBlock (unlike a SystemBlock) is stored in the database, may
|
|
|
|
|
* give rise to autoblocks and may be tracked with cookies. Such blocks
|
|
|
|
|
* are more customizable than system blocks: they may be hardblocks, and
|
Separate Block into AbstractBlock, Block and SystemBlock
This commit splits the existing Block class into AbstractBlock, Block
and SystemBlock.
Before this patch, the Block class represents several types of
blocks, which can be separated into blocks stored in the database,
and temporary blocks created by the system. These are now
represented by Block and SystemBlock, which inherit from
AbstractBlock.
This lays the foundations for:
* enforcing block parameters from multiple blocks that apply to a
user/IP address
* improvements to the Block API, including the addition of services
Breaking changes: functions expecting a Block object should still
expect a Block object if it came from the database, but other
functions may now need to expect an AbstractBlock or SystemBlock
object. (Note that an alternative naming scheme, in which the
abstract class is called Block and the subclasses are DatabaseBlock
and SystemBlock, avoids this breakage. However, it introduces more
breakages to calls to static Block methods and new Block
instantiations.)
Changes to tests: system blocks don't set the $blockCreateAccount or
$mExipry block properties, so remove/change any tests that assume
they do.
Bug: T222737
Change-Id: I83bceb5e5049e254c90ace060f8f8fad44696c67
2019-03-18 22:09:49 +00:00
|
|
|
* they may be sitewide or partial.
|
2019-05-13 14:18:07 +00:00
|
|
|
*
|
|
|
|
|
* @since 1.34 Renamed from Block.
|
Separate Block into AbstractBlock, Block and SystemBlock
This commit splits the existing Block class into AbstractBlock, Block
and SystemBlock.
Before this patch, the Block class represents several types of
blocks, which can be separated into blocks stored in the database,
and temporary blocks created by the system. These are now
represented by Block and SystemBlock, which inherit from
AbstractBlock.
This lays the foundations for:
* enforcing block parameters from multiple blocks that apply to a
user/IP address
* improvements to the Block API, including the addition of services
Breaking changes: functions expecting a Block object should still
expect a Block object if it came from the database, but other
functions may now need to expect an AbstractBlock or SystemBlock
object. (Note that an alternative naming scheme, in which the
abstract class is called Block and the subclasses are DatabaseBlock
and SystemBlock, avoids this breakage. However, it introduces more
breakages to calls to static Block methods and new Block
instantiations.)
Changes to tests: system blocks don't set the $blockCreateAccount or
$mExipry block properties, so remove/change any tests that assume
they do.
Bug: T222737
Change-Id: I83bceb5e5049e254c90ace060f8f8fad44696c67
2019-03-18 22:09:49 +00:00
|
|
|
*/
|
2019-05-13 14:18:07 +00:00
|
|
|
class DatabaseBlock extends AbstractBlock {
|
2019-07-29 20:10:27 +00:00
|
|
|
/**
|
|
|
|
|
* @deprecated since 1.34. Use getType to check whether a block is autoblocking.
|
|
|
|
|
* @var bool
|
|
|
|
|
*/
|
2014-05-12 14:42:51 +00:00
|
|
|
public $mAuto;
|
|
|
|
|
|
2019-07-29 20:10:27 +00:00
|
|
|
/**
|
|
|
|
|
* @deprecated since 1.34. Use getParentBlockId instead.
|
|
|
|
|
* @var int
|
|
|
|
|
*/
|
2014-05-12 14:42:51 +00:00
|
|
|
public $mParentBlockId;
|
|
|
|
|
|
|
|
|
|
/** @var int */
|
2017-08-04 14:57:27 +00:00
|
|
|
private $mId;
|
2014-05-12 14:42:51 +00:00
|
|
|
|
|
|
|
|
/** @var bool */
|
2021-04-19 01:02:08 +00:00
|
|
|
private $mFromPrimary;
|
2014-05-12 14:42:51 +00:00
|
|
|
|
2014-04-23 09:25:56 +00:00
|
|
|
/** @var bool */
|
2017-08-04 14:57:27 +00:00
|
|
|
private $isAutoblocking;
|
2011-03-22 17:18:15 +00:00
|
|
|
|
2018-08-16 04:55:55 +00:00
|
|
|
/** @var Restriction[] */
|
|
|
|
|
private $restrictions;
|
|
|
|
|
|
2021-03-05 16:44:14 +00:00
|
|
|
/** @var UserIdentity|null */
|
2019-09-05 21:25:33 +00:00
|
|
|
private $blocker;
|
|
|
|
|
|
2011-03-21 19:12:41 +00:00
|
|
|
/**
|
2019-04-29 22:26:03 +00:00
|
|
|
* Create a new block with specified option parameters on a user, IP or IP range.
|
2014-05-22 14:45:46 +00:00
|
|
|
*
|
2019-08-29 12:18:50 +00:00
|
|
|
* @param array $options Parameters of the block, with options supported by
|
|
|
|
|
* `AbstractBlock::__construct`, and also:
|
|
|
|
|
* - user: (int) Override target user ID (for foreign users)
|
|
|
|
|
* - auto: (bool) Is this an automatic block?
|
|
|
|
|
* - expiry: (string) Timestamp of expiration of the block or 'infinity'
|
|
|
|
|
* - anonOnly: (bool) Only disallow anonymous actions
|
|
|
|
|
* - createAccount: (bool) Disallow creation of new accounts
|
|
|
|
|
* - enableAutoblock: (bool) Enable automatic blocking
|
|
|
|
|
* - blockEmail: (bool) Disallow sending emails
|
|
|
|
|
* - allowUsertalk: (bool) Allow the target to edit its own talk page
|
|
|
|
|
* - sitewide: (bool) Disallow editing all pages and all contribution actions,
|
|
|
|
|
* except those specifically allowed by other block flags
|
2021-06-03 11:00:19 +00:00
|
|
|
* - by: (UserIdentity) UserIdentity object of the blocker.
|
2014-05-22 14:45:46 +00:00
|
|
|
*
|
2019-04-29 22:26:03 +00:00
|
|
|
* @since 1.26 $options array
|
2011-03-21 19:12:41 +00:00
|
|
|
*/
|
2019-04-29 22:26:03 +00:00
|
|
|
public function __construct( array $options = [] ) {
|
Separate Block into AbstractBlock, Block and SystemBlock
This commit splits the existing Block class into AbstractBlock, Block
and SystemBlock.
Before this patch, the Block class represents several types of
blocks, which can be separated into blocks stored in the database,
and temporary blocks created by the system. These are now
represented by Block and SystemBlock, which inherit from
AbstractBlock.
This lays the foundations for:
* enforcing block parameters from multiple blocks that apply to a
user/IP address
* improvements to the Block API, including the addition of services
Breaking changes: functions expecting a Block object should still
expect a Block object if it came from the database, but other
functions may now need to expect an AbstractBlock or SystemBlock
object. (Note that an alternative naming scheme, in which the
abstract class is called Block and the subclasses are DatabaseBlock
and SystemBlock, avoids this breakage. However, it introduces more
breakages to calls to static Block methods and new Block
instantiations.)
Changes to tests: system blocks don't set the $blockCreateAccount or
$mExipry block properties, so remove/change any tests that assume
they do.
Bug: T222737
Change-Id: I83bceb5e5049e254c90ace060f8f8fad44696c67
2019-03-18 22:09:49 +00:00
|
|
|
parent::__construct( $options );
|
|
|
|
|
|
2016-02-17 09:09:32 +00:00
|
|
|
$defaults = [
|
2014-05-22 14:45:46 +00:00
|
|
|
'user' => null,
|
|
|
|
|
'auto' => false,
|
|
|
|
|
'expiry' => '',
|
|
|
|
|
'createAccount' => false,
|
|
|
|
|
'enableAutoblock' => false,
|
|
|
|
|
'blockEmail' => false,
|
|
|
|
|
'allowUsertalk' => false,
|
2018-08-16 04:55:55 +00:00
|
|
|
'sitewide' => true,
|
2019-09-05 21:25:33 +00:00
|
|
|
'by' => null,
|
2016-02-17 09:09:32 +00:00
|
|
|
];
|
2011-03-21 19:12:41 +00:00
|
|
|
|
2014-05-22 14:45:46 +00:00
|
|
|
$options += $defaults;
|
|
|
|
|
|
2021-06-03 11:00:19 +00:00
|
|
|
if ( $options['by'] && $options['by'] instanceof UserIdentity ) {
|
|
|
|
|
$this->setBlocker( $options['by'] );
|
2019-09-05 21:25:33 +00:00
|
|
|
}
|
|
|
|
|
|
2021-09-26 19:51:13 +00:00
|
|
|
$this->setExpiry( $this->getDBConnection( DB_REPLICA )->decodeExpiry( $options['expiry'] ) );
|
2014-05-22 14:45:46 +00:00
|
|
|
|
|
|
|
|
# Boolean settings
|
|
|
|
|
$this->mAuto = (bool)$options['auto'];
|
|
|
|
|
$this->isAutoblocking( (bool)$options['enableAutoblock'] );
|
2018-08-16 04:55:55 +00:00
|
|
|
$this->isSitewide( (bool)$options['sitewide'] );
|
Separate out different functionalities of Block::prevents
Block::prevents plays several different roles:
* acts as get/setter for Boolean properties that correspond to
ipb_create_account, ipb_block_email and ipb_allow_usertalk
* calculates whether a block blocks a given right, based on Block
properties, global configs, white/blacklists and anonymous user
rights
* decides whether a block prevents editing of the target's own
user talk page (listed separately because 'editownusertalk' is
not a right)
This patch:
* renames mDisableUsertalk to allowEditUsertalk (and reverses the
value), to match the field ipb_allow_usertalk and make this logic
easier to follow
* renames mCreateAccount to blockCreateAccount, to make it clear
that the flag blocks account creation when true, and make this
logic easier to follow
* decouples the block that is stored in the database (which now
reflects the form that the admin submitted) and the behaviour of
the block on enforcement (since the properties set by the admin
can be overridden by global configs) - so if the global configs
change, the block behaviour could too
* creates get/setters for blockCreateAccount, mBlockEmail and
allowEditUsertalk properties
* creates appliesToRight, exclusively for checking whether the
block blocks a given right, taking into account the block
properties, global configs and anonymous user rights
* creates appliesToUsertalk, for checking whether the block
blocks a user from editing their own talk page. The block is
unaware of the user trying to make the edit, and this user is not
always the same as the block target, e.g. if the block target is
an IP range. Therefore the user's talk page is passed in to this
method. appliesToUsertalk can be called from anywhere where the
user is known
* uses the get/setters wherever Block::prevents was being used as
such
* uses appliesToRight whenever Block::prevents was being used to
determine if the block blocks a given right
* uses appliesToUsertalk in User::isBlockedFrom
Bug: T211578
Bug: T214508
Change-Id: I0e131696419211319082cb454f4f05297e55d22e
2019-02-09 12:17:54 +00:00
|
|
|
$this->isEmailBlocked( (bool)$options['blockEmail'] );
|
|
|
|
|
$this->isCreateAccountBlocked( (bool)$options['createAccount'] );
|
|
|
|
|
$this->isUsertalkEditAllowed( (bool)$options['allowUsertalk'] );
|
2011-03-22 17:18:15 +00:00
|
|
|
|
2021-04-19 01:02:08 +00:00
|
|
|
$this->mFromPrimary = false;
|
2004-02-14 12:37:25 +00:00
|
|
|
}
|
2005-08-02 13:35:19 +00:00
|
|
|
|
2008-09-21 14:22:23 +00:00
|
|
|
/**
|
2019-02-15 19:24:58 +00:00
|
|
|
* Load a block from the block id.
|
2008-09-22 04:52:51 +00:00
|
|
|
*
|
2019-05-13 14:18:07 +00:00
|
|
|
* @param int $id id to search for
|
|
|
|
|
* @return DatabaseBlock|null
|
2008-09-21 14:22:23 +00:00
|
|
|
*/
|
2008-11-23 09:52:29 +00:00
|
|
|
public static function newFromID( $id ) {
|
2016-09-05 19:55:19 +00:00
|
|
|
$dbr = wfGetDB( DB_REPLICA );
|
2017-10-06 17:03:55 +00:00
|
|
|
$blockQuery = self::getQueryInfo();
|
2011-03-22 17:18:15 +00:00
|
|
|
$res = $dbr->selectRow(
|
2017-10-06 17:03:55 +00:00
|
|
|
$blockQuery['tables'],
|
|
|
|
|
$blockQuery['fields'],
|
2016-02-17 09:09:32 +00:00
|
|
|
[ 'ipb_id' => $id ],
|
2017-10-06 17:03:55 +00:00
|
|
|
__METHOD__,
|
|
|
|
|
[],
|
|
|
|
|
$blockQuery['joins']
|
2011-03-22 17:18:15 +00:00
|
|
|
);
|
2011-06-15 19:05:25 +00:00
|
|
|
if ( $res ) {
|
2012-05-25 14:50:57 +00:00
|
|
|
return self::newFromRow( $res );
|
2011-06-15 19:05:25 +00:00
|
|
|
} else {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
2003-09-01 13:13:56 +00:00
|
|
|
}
|
2009-10-19 11:15:51 +00:00
|
|
|
|
2017-10-06 17:03:55 +00:00
|
|
|
/**
|
|
|
|
|
* Return the tables, fields, and join conditions to be selected to create
|
|
|
|
|
* a new block object.
|
2021-04-19 01:00:56 +00:00
|
|
|
*
|
|
|
|
|
* Since 1.34, ipb_by and ipb_by_text have not been present in the
|
|
|
|
|
* database, but they continue to be available in query results as
|
|
|
|
|
* aliases.
|
|
|
|
|
*
|
2017-10-06 17:03:55 +00:00
|
|
|
* @since 1.31
|
|
|
|
|
* @return array With three keys:
|
|
|
|
|
* - tables: (string[]) to include in the `$table` to `IDatabase->select()`
|
|
|
|
|
* - fields: (string[]) to include in the `$vars` to `IDatabase->select()`
|
|
|
|
|
* - joins: (array) to include in the `$join_conds` to `IDatabase->select()`
|
|
|
|
|
*/
|
|
|
|
|
public static function getQueryInfo() {
|
2018-01-24 23:41:01 +00:00
|
|
|
$commentQuery = CommentStore::getStore()->getJoin( 'ipb_reason' );
|
2017-10-06 17:03:55 +00:00
|
|
|
return [
|
2021-04-19 01:00:56 +00:00
|
|
|
'tables' => [
|
|
|
|
|
'ipblocks',
|
|
|
|
|
'ipblocks_actor' => 'actor'
|
|
|
|
|
] + $commentQuery['tables'],
|
2017-10-06 17:03:55 +00:00
|
|
|
'fields' => [
|
|
|
|
|
'ipb_id',
|
|
|
|
|
'ipb_address',
|
|
|
|
|
'ipb_timestamp',
|
|
|
|
|
'ipb_auto',
|
|
|
|
|
'ipb_anon_only',
|
|
|
|
|
'ipb_create_account',
|
|
|
|
|
'ipb_enable_autoblock',
|
|
|
|
|
'ipb_expiry',
|
|
|
|
|
'ipb_deleted',
|
|
|
|
|
'ipb_block_email',
|
|
|
|
|
'ipb_allow_usertalk',
|
|
|
|
|
'ipb_parent_block_id',
|
2018-08-16 04:55:55 +00:00
|
|
|
'ipb_sitewide',
|
2021-04-19 01:00:56 +00:00
|
|
|
'ipb_by_actor',
|
|
|
|
|
'ipb_by' => 'ipblocks_actor.actor_user',
|
|
|
|
|
'ipb_by_text' => 'ipblocks_actor.actor_name'
|
|
|
|
|
] + $commentQuery['fields'],
|
|
|
|
|
'joins' => [
|
|
|
|
|
'ipblocks_actor' => [ 'JOIN', 'actor_id=ipb_by_actor' ]
|
|
|
|
|
] + $commentQuery['joins'],
|
2017-10-06 17:03:55 +00:00
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
2008-11-23 09:52:29 +00:00
|
|
|
/**
|
2011-03-22 17:18:15 +00:00
|
|
|
* Check if two blocks are effectively equal. Doesn't check irrelevant things like
|
2012-10-26 16:18:59 +00:00
|
|
|
* the blocking user or the block timestamp, only things which affect the blocked user
|
2011-05-28 18:58:51 +00:00
|
|
|
*
|
2019-05-13 14:18:07 +00:00
|
|
|
* @param DatabaseBlock $block
|
2011-05-28 18:58:51 +00:00
|
|
|
* @return bool
|
2008-11-23 09:52:29 +00:00
|
|
|
*/
|
2019-05-13 14:18:07 +00:00
|
|
|
public function equals( DatabaseBlock $block ) {
|
2009-10-19 11:15:51 +00:00
|
|
|
return (
|
2011-03-22 17:18:15 +00:00
|
|
|
(string)$this->target == (string)$block->target
|
|
|
|
|
&& $this->type == $block->type
|
2008-11-23 09:52:29 +00:00
|
|
|
&& $this->mAuto == $block->mAuto
|
2011-03-22 17:18:15 +00:00
|
|
|
&& $this->isHardblock() == $block->isHardblock()
|
Separate out different functionalities of Block::prevents
Block::prevents plays several different roles:
* acts as get/setter for Boolean properties that correspond to
ipb_create_account, ipb_block_email and ipb_allow_usertalk
* calculates whether a block blocks a given right, based on Block
properties, global configs, white/blacklists and anonymous user
rights
* decides whether a block prevents editing of the target's own
user talk page (listed separately because 'editownusertalk' is
not a right)
This patch:
* renames mDisableUsertalk to allowEditUsertalk (and reverses the
value), to match the field ipb_allow_usertalk and make this logic
easier to follow
* renames mCreateAccount to blockCreateAccount, to make it clear
that the flag blocks account creation when true, and make this
logic easier to follow
* decouples the block that is stored in the database (which now
reflects the form that the admin submitted) and the behaviour of
the block on enforcement (since the properties set by the admin
can be overridden by global configs) - so if the global configs
change, the block behaviour could too
* creates get/setters for blockCreateAccount, mBlockEmail and
allowEditUsertalk properties
* creates appliesToRight, exclusively for checking whether the
block blocks a given right, taking into account the block
properties, global configs and anonymous user rights
* creates appliesToUsertalk, for checking whether the block
blocks a user from editing their own talk page. The block is
unaware of the user trying to make the edit, and this user is not
always the same as the block target, e.g. if the block target is
an IP range. Therefore the user's talk page is passed in to this
method. appliesToUsertalk can be called from anywhere where the
user is known
* uses the get/setters wherever Block::prevents was being used as
such
* uses appliesToRight whenever Block::prevents was being used to
determine if the block blocks a given right
* uses appliesToUsertalk in User::isBlockedFrom
Bug: T211578
Bug: T214508
Change-Id: I0e131696419211319082cb454f4f05297e55d22e
2019-02-09 12:17:54 +00:00
|
|
|
&& $this->isCreateAccountBlocked() == $block->isCreateAccountBlocked()
|
2019-03-22 15:16:40 +00:00
|
|
|
&& $this->getExpiry() == $block->getExpiry()
|
2011-03-22 17:18:15 +00:00
|
|
|
&& $this->isAutoblocking() == $block->isAutoblocking()
|
2019-03-22 15:16:40 +00:00
|
|
|
&& $this->getHideName() == $block->getHideName()
|
Separate out different functionalities of Block::prevents
Block::prevents plays several different roles:
* acts as get/setter for Boolean properties that correspond to
ipb_create_account, ipb_block_email and ipb_allow_usertalk
* calculates whether a block blocks a given right, based on Block
properties, global configs, white/blacklists and anonymous user
rights
* decides whether a block prevents editing of the target's own
user talk page (listed separately because 'editownusertalk' is
not a right)
This patch:
* renames mDisableUsertalk to allowEditUsertalk (and reverses the
value), to match the field ipb_allow_usertalk and make this logic
easier to follow
* renames mCreateAccount to blockCreateAccount, to make it clear
that the flag blocks account creation when true, and make this
logic easier to follow
* decouples the block that is stored in the database (which now
reflects the form that the admin submitted) and the behaviour of
the block on enforcement (since the properties set by the admin
can be overridden by global configs) - so if the global configs
change, the block behaviour could too
* creates get/setters for blockCreateAccount, mBlockEmail and
allowEditUsertalk properties
* creates appliesToRight, exclusively for checking whether the
block blocks a given right, taking into account the block
properties, global configs and anonymous user rights
* creates appliesToUsertalk, for checking whether the block
blocks a user from editing their own talk page. The block is
unaware of the user trying to make the edit, and this user is not
always the same as the block target, e.g. if the block target is
an IP range. Therefore the user's talk page is passed in to this
method. appliesToUsertalk can be called from anywhere where the
user is known
* uses the get/setters wherever Block::prevents was being used as
such
* uses appliesToRight whenever Block::prevents was being used to
determine if the block blocks a given right
* uses appliesToUsertalk in User::isBlockedFrom
Bug: T211578
Bug: T214508
Change-Id: I0e131696419211319082cb454f4f05297e55d22e
2019-02-09 12:17:54 +00:00
|
|
|
&& $this->isEmailBlocked() == $block->isEmailBlocked()
|
|
|
|
|
&& $this->isUsertalkEditAllowed() == $block->isUsertalkEditAllowed()
|
2019-10-20 00:04:00 +00:00
|
|
|
&& $this->getReasonComment()->text == $block->getReasonComment()->text
|
2018-08-16 04:55:55 +00:00
|
|
|
&& $this->isSitewide() == $block->isSitewide()
|
2019-05-13 14:18:07 +00:00
|
|
|
// DatabaseBlock::getRestrictions() may perform a database query, so
|
|
|
|
|
// keep it at the end.
|
2019-04-11 19:54:10 +00:00
|
|
|
&& $this->getBlockRestrictionStore()->equals(
|
|
|
|
|
$this->getRestrictions(), $block->getRestrictions()
|
|
|
|
|
)
|
2008-11-23 09:52:29 +00:00
|
|
|
);
|
|
|
|
|
}
|
2005-08-02 13:35:19 +00:00
|
|
|
|
2011-03-21 19:12:41 +00:00
|
|
|
/**
|
2019-05-24 13:02:32 +00:00
|
|
|
* Load blocks from the database which target the specific target exactly, or which cover the
|
|
|
|
|
* vague target.
|
|
|
|
|
*
|
2021-03-19 13:56:13 +00:00
|
|
|
* @param UserIdentity|string|null $specificTarget
|
2019-05-24 13:02:32 +00:00
|
|
|
* @param int|null $specificType
|
2021-04-19 01:02:08 +00:00
|
|
|
* @param bool $fromPrimary
|
2021-03-19 13:56:13 +00:00
|
|
|
* @param UserIdentity|string|null $vagueTarget Also search for blocks affecting this target.
|
|
|
|
|
* Doesn't make any sense to use TYPE_AUTO / TYPE_ID here. Leave blank to skip IP lookups.
|
2012-10-07 23:35:26 +00:00
|
|
|
* @throws MWException
|
2019-05-24 13:02:32 +00:00
|
|
|
* @return DatabaseBlock[] Any relevant blocks
|
2011-03-21 19:12:41 +00:00
|
|
|
*/
|
2019-05-24 13:02:32 +00:00
|
|
|
protected static function newLoad(
|
|
|
|
|
$specificTarget,
|
|
|
|
|
$specificType,
|
2021-04-19 01:02:08 +00:00
|
|
|
$fromPrimary,
|
2019-05-24 13:02:32 +00:00
|
|
|
$vagueTarget = null
|
|
|
|
|
) {
|
2021-04-19 01:02:08 +00:00
|
|
|
$db = wfGetDB( $fromPrimary ? DB_PRIMARY : DB_REPLICA );
|
2019-05-24 13:02:32 +00:00
|
|
|
|
|
|
|
|
if ( $specificType !== null ) {
|
2020-03-23 00:39:02 +00:00
|
|
|
$conds = [ 'ipb_address' => [ (string)$specificTarget ] ];
|
2011-03-21 19:12:41 +00:00
|
|
|
} else {
|
2016-02-17 09:09:32 +00:00
|
|
|
$conds = [ 'ipb_address' => [] ];
|
2003-09-01 13:13:56 +00:00
|
|
|
}
|
|
|
|
|
|
2011-07-11 16:53:31 +00:00
|
|
|
# Be aware that the != '' check is explicit, since empty values will be
|
2017-02-20 22:44:19 +00:00
|
|
|
# passed by some callers (T31116)
|
2013-04-20 22:49:30 +00:00
|
|
|
if ( $vagueTarget != '' ) {
|
2021-03-17 17:48:43 +00:00
|
|
|
list( $target, $type ) = MediaWikiServices::getInstance()
|
|
|
|
|
->getBlockUtils()
|
|
|
|
|
->parseBlockTarget( $vagueTarget );
|
2013-04-26 14:42:31 +00:00
|
|
|
switch ( $type ) {
|
2011-03-21 19:12:41 +00:00
|
|
|
case self::TYPE_USER:
|
2013-03-13 07:42:41 +00:00
|
|
|
# Slightly weird, but who are we to argue?
|
2011-03-21 19:12:41 +00:00
|
|
|
$conds['ipb_address'][] = (string)$target;
|
2021-07-15 21:57:52 +00:00
|
|
|
$conds = $db->makeList( $conds, LIST_OR );
|
2011-03-21 19:12:41 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case self::TYPE_IP:
|
|
|
|
|
$conds['ipb_address'][] = (string)$target;
|
2020-03-23 00:39:02 +00:00
|
|
|
$conds['ipb_address'] = array_unique( $conds['ipb_address'] );
|
2019-06-25 18:53:15 +00:00
|
|
|
$conds[] = self::getRangeCond( IPUtils::toHex( $target ) );
|
2021-11-25 16:37:36 +00:00
|
|
|
// @phan-suppress-next-line SecurityCheck-SQLInjection
|
2011-03-21 19:12:41 +00:00
|
|
|
$conds = $db->makeList( $conds, LIST_OR );
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case self::TYPE_RANGE:
|
2019-06-25 18:53:15 +00:00
|
|
|
list( $start, $end ) = IPUtils::parseRange( $target );
|
2011-03-21 19:12:41 +00:00
|
|
|
$conds['ipb_address'][] = (string)$target;
|
|
|
|
|
$conds[] = self::getRangeCond( $start, $end );
|
2021-11-25 16:37:36 +00:00
|
|
|
// @phan-suppress-next-line SecurityCheck-SQLInjection
|
2011-03-21 19:12:41 +00:00
|
|
|
$conds = $db->makeList( $conds, LIST_OR );
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
throw new MWException( "Tried to load block with invalid type" );
|
2006-07-10 06:30:03 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-10-06 17:03:55 +00:00
|
|
|
$blockQuery = self::getQueryInfo();
|
|
|
|
|
$res = $db->select(
|
2020-03-23 00:39:02 +00:00
|
|
|
$blockQuery['tables'],
|
|
|
|
|
$blockQuery['fields'],
|
|
|
|
|
$conds,
|
|
|
|
|
__METHOD__,
|
|
|
|
|
[],
|
|
|
|
|
$blockQuery['joins']
|
2017-10-06 17:03:55 +00:00
|
|
|
);
|
2010-05-30 14:48:30 +00:00
|
|
|
|
2019-05-24 13:02:32 +00:00
|
|
|
$blocks = [];
|
|
|
|
|
$blockIds = [];
|
|
|
|
|
$autoBlocks = [];
|
2013-04-20 22:49:30 +00:00
|
|
|
foreach ( $res as $row ) {
|
2012-05-25 14:50:57 +00:00
|
|
|
$block = self::newFromRow( $row );
|
2010-02-14 22:07:30 +00:00
|
|
|
|
2011-03-21 19:12:41 +00:00
|
|
|
# Don't use expired blocks
|
2015-12-30 04:29:10 +00:00
|
|
|
if ( $block->isExpired() ) {
|
2011-03-21 19:12:41 +00:00
|
|
|
continue;
|
2006-07-11 05:30:35 +00:00
|
|
|
}
|
2010-02-14 22:07:30 +00:00
|
|
|
|
2011-03-21 19:12:41 +00:00
|
|
|
# Don't use anon only blocks on users
|
2019-05-24 13:02:32 +00:00
|
|
|
if ( $specificType == self::TYPE_USER && !$block->isHardblock() ) {
|
2011-03-21 19:12:41 +00:00
|
|
|
continue;
|
|
|
|
|
}
|
2010-02-14 22:07:30 +00:00
|
|
|
|
2019-05-24 13:02:32 +00:00
|
|
|
// Check for duplicate autoblocks
|
|
|
|
|
if ( $block->getType() === self::TYPE_AUTO ) {
|
|
|
|
|
$autoBlocks[] = $block;
|
|
|
|
|
} else {
|
|
|
|
|
$blocks[] = $block;
|
|
|
|
|
$blockIds[] = $block->getId();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Only add autoblocks that aren't duplicates
|
|
|
|
|
foreach ( $autoBlocks as $block ) {
|
|
|
|
|
if ( !in_array( $block->mParentBlockId, $blockIds ) ) {
|
|
|
|
|
$blocks[] = $block;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $blocks;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Choose the most specific block from some combination of user, IP and IP range
|
|
|
|
|
* blocks. Decreasing order of specificity: user > IP > narrower IP range > wider IP
|
|
|
|
|
* range. A range that encompasses one IP address is ranked equally to a singe IP.
|
|
|
|
|
*
|
|
|
|
|
* This is refactored out from DatabaseBlock::newLoad.
|
|
|
|
|
*
|
|
|
|
|
* @param DatabaseBlock[] $blocks These should not include autoblocks or ID blocks
|
|
|
|
|
* @return DatabaseBlock|null The block with the most specific target
|
|
|
|
|
*/
|
2019-06-27 20:42:54 +00:00
|
|
|
protected static function chooseMostSpecificBlock( array $blocks ) {
|
2019-05-24 13:02:32 +00:00
|
|
|
if ( count( $blocks ) === 1 ) {
|
|
|
|
|
return $blocks[0];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# This result could contain a block on the user, a block on the IP, and a russian-doll
|
|
|
|
|
# set of rangeblocks. We want to choose the most specific one, so keep a leader board.
|
|
|
|
|
$bestBlock = null;
|
|
|
|
|
|
|
|
|
|
# Lower will be better
|
|
|
|
|
$bestBlockScore = 100;
|
|
|
|
|
foreach ( $blocks as $block ) {
|
2013-04-20 22:49:30 +00:00
|
|
|
if ( $block->getType() == self::TYPE_RANGE ) {
|
2011-03-21 19:12:41 +00:00
|
|
|
# This is the number of bits that are allowed to vary in the block, give
|
|
|
|
|
# or take some floating point errors
|
2021-04-16 12:55:24 +00:00
|
|
|
$target = $block->getTargetName();
|
2019-06-25 18:53:15 +00:00
|
|
|
$max = IPUtils::isIPv6( $target ) ? 128 : 32;
|
|
|
|
|
list( $network, $bits ) = IPUtils::parseCIDR( $target );
|
2019-05-08 17:49:18 +00:00
|
|
|
$size = $max - $bits;
|
2011-03-21 19:12:41 +00:00
|
|
|
|
2019-05-01 17:28:55 +00:00
|
|
|
# Rank a range block covering a single IP equally with a single-IP block
|
2019-05-08 17:49:18 +00:00
|
|
|
$score = self::TYPE_RANGE - 1 + ( $size / $max );
|
2011-06-02 19:32:45 +00:00
|
|
|
|
2011-03-21 19:12:41 +00:00
|
|
|
} else {
|
|
|
|
|
$score = $block->getType();
|
|
|
|
|
}
|
|
|
|
|
|
2013-04-20 22:49:30 +00:00
|
|
|
if ( $score < $bestBlockScore ) {
|
2011-03-21 19:12:41 +00:00
|
|
|
$bestBlockScore = $score;
|
2019-05-24 13:02:32 +00:00
|
|
|
$bestBlock = $block;
|
2006-07-11 05:30:35 +00:00
|
|
|
}
|
|
|
|
|
}
|
2008-04-14 07:45:50 +00:00
|
|
|
|
2019-05-24 13:02:32 +00:00
|
|
|
return $bestBlock;
|
2006-07-10 06:30:03 +00:00
|
|
|
}
|
|
|
|
|
|
2011-03-19 23:47:08 +00:00
|
|
|
/**
|
2013-03-13 07:42:41 +00:00
|
|
|
* Get a set of SQL conditions which will select rangeblocks encompassing a given range
|
2013-03-11 17:15:01 +00:00
|
|
|
* @param string $start Hexadecimal IP representation
|
2018-06-26 21:14:43 +00:00
|
|
|
* @param string|null $end Hexadecimal IP representation, or null to use $start = $end
|
2014-04-23 09:25:56 +00:00
|
|
|
* @return string
|
2011-03-19 23:47:08 +00:00
|
|
|
*/
|
2011-03-20 17:43:17 +00:00
|
|
|
public static function getRangeCond( $start, $end = null ) {
|
|
|
|
|
if ( $end === null ) {
|
2011-03-19 23:47:08 +00:00
|
|
|
$end = $start;
|
|
|
|
|
}
|
2017-02-20 22:44:19 +00:00
|
|
|
# Per T16634, we want to include relevant active rangeblocks; for
|
2011-03-19 23:47:08 +00:00
|
|
|
# rangeblocks, we want to include larger ranges which enclose the given
|
|
|
|
|
# range. We know that all blocks must be smaller than $wgBlockCIDRLimit,
|
|
|
|
|
# so we can improve performance by filtering on a LIKE clause
|
|
|
|
|
$chunk = self::getIpFragment( $start );
|
2016-09-05 19:55:19 +00:00
|
|
|
$dbr = wfGetDB( DB_REPLICA );
|
2011-03-19 23:47:08 +00:00
|
|
|
$like = $dbr->buildLike( $chunk, $dbr->anyString() );
|
|
|
|
|
|
|
|
|
|
# Fairly hard to make a malicious SQL statement out of hex characters,
|
|
|
|
|
# but stranger things have happened...
|
|
|
|
|
$safeStart = $dbr->addQuotes( $start );
|
|
|
|
|
$safeEnd = $dbr->addQuotes( $end );
|
|
|
|
|
|
|
|
|
|
return $dbr->makeList(
|
2016-02-17 09:09:32 +00:00
|
|
|
[
|
2011-03-19 23:47:08 +00:00
|
|
|
"ipb_range_start $like",
|
|
|
|
|
"ipb_range_start <= $safeStart",
|
|
|
|
|
"ipb_range_end >= $safeEnd",
|
2016-02-17 09:09:32 +00:00
|
|
|
],
|
2011-03-19 23:47:08 +00:00
|
|
|
LIST_AND
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get the component of an IP address which is certain to be the same between an IP
|
|
|
|
|
* address and a rangeblock containing that IP address.
|
2014-04-23 09:25:56 +00:00
|
|
|
* @param string $hex Hexadecimal IP representation
|
|
|
|
|
* @return string
|
2011-03-19 23:47:08 +00:00
|
|
|
*/
|
2011-03-20 17:43:17 +00:00
|
|
|
protected static function getIpFragment( $hex ) {
|
2022-01-06 18:44:56 +00:00
|
|
|
$blockCIDRLimit = MediaWikiServices::getInstance()->getMainConfig()->get( 'BlockCIDRLimit' );
|
2011-03-20 17:43:17 +00:00
|
|
|
if ( substr( $hex, 0, 3 ) == 'v6-' ) {
|
2022-03-05 20:05:01 +00:00
|
|
|
return 'v6-' . substr( substr( $hex, 3 ), 0, (int)floor( $blockCIDRLimit['IPv6'] / 4 ) );
|
2011-03-19 23:47:08 +00:00
|
|
|
} else {
|
2022-03-05 20:05:01 +00:00
|
|
|
return substr( $hex, 0, (int)floor( $blockCIDRLimit['IPv4'] / 4 ) );
|
2011-03-19 23:47:08 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2008-09-21 14:22:23 +00:00
|
|
|
/**
|
|
|
|
|
* Given a database row from the ipblocks table, initialize
|
|
|
|
|
* member variables
|
2014-07-13 03:55:43 +00:00
|
|
|
* @param stdClass $row A row from the ipblocks table
|
2008-09-21 14:22:23 +00:00
|
|
|
*/
|
2011-03-19 17:44:01 +00:00
|
|
|
protected function initFromRow( $row ) {
|
2011-03-21 19:12:41 +00:00
|
|
|
$this->setTarget( $row->ipb_address );
|
2011-07-18 22:23:42 +00:00
|
|
|
|
2019-03-25 15:11:26 +00:00
|
|
|
$this->setTimestamp( wfTimestamp( TS_MW, $row->ipb_timestamp ) );
|
2019-09-15 15:12:06 +00:00
|
|
|
$this->mAuto = (bool)$row->ipb_auto;
|
2020-04-26 17:53:59 +00:00
|
|
|
$this->setHideName( (bool)$row->ipb_deleted );
|
2015-05-06 15:33:08 +00:00
|
|
|
$this->mId = (int)$row->ipb_id;
|
2020-04-26 17:53:59 +00:00
|
|
|
$this->mParentBlockId = (int)$row->ipb_parent_block_id;
|
2011-07-18 22:23:42 +00:00
|
|
|
|
2021-03-05 16:44:14 +00:00
|
|
|
$this->setBlocker( MediaWikiServices::getInstance()
|
|
|
|
|
->getActorNormalization()
|
|
|
|
|
->newActorFromRowFields( $row->ipb_by, $row->ipb_by_text, $row->ipb_by_actor ) );
|
2019-12-20 11:57:40 +00:00
|
|
|
|
2011-07-18 21:48:56 +00:00
|
|
|
// I wish I didn't have to do this
|
2021-09-26 19:51:13 +00:00
|
|
|
$db = $this->getDBConnection( DB_REPLICA );
|
2019-03-22 15:16:40 +00:00
|
|
|
$this->setExpiry( $db->decodeExpiry( $row->ipb_expiry ) );
|
|
|
|
|
$this->setReason(
|
|
|
|
|
CommentStore::getStore()
|
2017-10-06 17:03:55 +00:00
|
|
|
// Legacy because $row may have come from self::selectFields()
|
2019-10-20 00:04:00 +00:00
|
|
|
->getCommentLegacy( $db, 'ipb_reason', $row )
|
2019-03-22 15:16:40 +00:00
|
|
|
);
|
2011-03-22 17:18:15 +00:00
|
|
|
|
|
|
|
|
$this->isHardblock( !$row->ipb_anon_only );
|
2020-04-26 17:53:59 +00:00
|
|
|
$this->isAutoblocking( (bool)$row->ipb_enable_autoblock );
|
2018-08-16 04:55:55 +00:00
|
|
|
$this->isSitewide( (bool)$row->ipb_sitewide );
|
2011-03-22 17:18:15 +00:00
|
|
|
|
2020-04-26 17:53:59 +00:00
|
|
|
$this->isCreateAccountBlocked( (bool)$row->ipb_create_account );
|
|
|
|
|
$this->isEmailBlocked( (bool)$row->ipb_block_email );
|
|
|
|
|
$this->isUsertalkEditAllowed( (bool)$row->ipb_allow_usertalk );
|
2005-08-02 13:35:19 +00:00
|
|
|
}
|
2003-09-01 13:13:56 +00:00
|
|
|
|
2011-03-21 19:12:41 +00:00
|
|
|
/**
|
2019-05-13 14:18:07 +00:00
|
|
|
* Create a new DatabaseBlock object from a database row
|
2014-07-13 03:55:43 +00:00
|
|
|
* @param stdClass $row Row from the ipblocks table
|
2019-05-13 14:18:07 +00:00
|
|
|
* @return DatabaseBlock
|
2011-03-21 19:12:41 +00:00
|
|
|
*/
|
2012-12-20 15:09:25 +00:00
|
|
|
public static function newFromRow( $row ) {
|
2019-05-13 14:18:07 +00:00
|
|
|
$block = new DatabaseBlock;
|
2011-03-21 19:12:41 +00:00
|
|
|
$block->initFromRow( $row );
|
|
|
|
|
return $block;
|
|
|
|
|
}
|
|
|
|
|
|
2008-09-21 14:22:23 +00:00
|
|
|
/**
|
|
|
|
|
* Delete the row from the IP blocks table.
|
2008-09-22 04:52:51 +00:00
|
|
|
*
|
2020-08-27 09:27:10 +00:00
|
|
|
* @deprecated since 1.36 Use DatabaseBlockStore::deleteBlock instead.
|
2012-10-07 23:35:26 +00:00
|
|
|
* @throws MWException
|
2014-04-23 09:25:56 +00:00
|
|
|
* @return bool
|
2008-09-21 14:22:23 +00:00
|
|
|
*/
|
2008-11-30 13:09:19 +00:00
|
|
|
public function delete() {
|
2020-05-08 06:29:23 +00:00
|
|
|
return MediaWikiServices::getInstance()
|
|
|
|
|
->getDatabaseBlockStore()
|
|
|
|
|
->deleteBlock( $this );
|
2003-09-01 13:13:56 +00:00
|
|
|
}
|
|
|
|
|
|
2006-11-08 09:54:06 +00:00
|
|
|
/**
|
2008-09-22 04:52:51 +00:00
|
|
|
* Insert a block into the block table. Will fail if there is a conflicting
|
|
|
|
|
* block (same name and options) already in the database.
|
|
|
|
|
*
|
2020-08-27 09:27:10 +00:00
|
|
|
* @deprecated since 1.36 Use DatabaseBlockStore::insertBlock instead.
|
2018-06-26 21:14:43 +00:00
|
|
|
* @param IDatabase|null $dbw If you have one available
|
2014-04-23 09:25:56 +00:00
|
|
|
* @return bool|array False on failure, assoc array on success:
|
2018-05-19 20:46:54 +00:00
|
|
|
* ('id' => block ID, 'autoIds' => array of autoblock IDs)
|
2008-09-22 04:52:51 +00:00
|
|
|
*/
|
2019-06-27 20:42:54 +00:00
|
|
|
public function insert( IDatabase $dbw = null ) {
|
2020-05-08 06:29:23 +00:00
|
|
|
return MediaWikiServices::getInstance()
|
|
|
|
|
->getDatabaseBlockStore()
|
|
|
|
|
->insertBlock( $this, $dbw );
|
2003-09-01 13:13:56 +00:00
|
|
|
}
|
|
|
|
|
|
2008-09-21 15:16:32 +00:00
|
|
|
/**
|
2008-09-22 04:52:51 +00:00
|
|
|
* Update a block in the DB with new parameters.
|
|
|
|
|
* The ID field needs to be loaded first.
|
2011-06-26 23:01:29 +00:00
|
|
|
*
|
2020-08-27 09:27:10 +00:00
|
|
|
* @deprecated since 1.36 Use DatabaseBlockStore::updateBlock instead.
|
2014-03-23 01:28:57 +00:00
|
|
|
* @return bool|array False on failure, array on success:
|
|
|
|
|
* ('id' => block ID, 'autoIds' => array of autoblock IDs)
|
2008-09-21 15:16:32 +00:00
|
|
|
*/
|
|
|
|
|
public function update() {
|
2020-05-08 06:29:23 +00:00
|
|
|
return MediaWikiServices::getInstance()
|
|
|
|
|
->getDatabaseBlockStore()
|
|
|
|
|
->updateBlock( $this );
|
2006-11-08 09:54:06 +00:00
|
|
|
}
|
2009-10-19 11:15:51 +00:00
|
|
|
|
2021-03-20 03:39:26 +00:00
|
|
|
/**
|
|
|
|
|
* Checks whether a given IP is on the autoblock exemption list.
|
|
|
|
|
* TODO: this probably belongs somewhere else, but not sure where...
|
|
|
|
|
*
|
|
|
|
|
* @since 1.36
|
|
|
|
|
*
|
|
|
|
|
* @param string $ip The IP to check
|
|
|
|
|
* @return bool
|
|
|
|
|
*/
|
|
|
|
|
public static function isExemptedFromAutoblocks( $ip ) {
|
|
|
|
|
// Try to get the ip-autoblock_exemption from the cache, as it's faster
|
2008-09-21 14:22:23 +00:00
|
|
|
// than getting the msg raw and explode()'ing it.
|
2016-10-14 09:17:25 +00:00
|
|
|
$cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
|
2015-10-19 17:52:19 +00:00
|
|
|
$lines = $cache->getWithSetCallback(
|
2021-03-20 03:39:26 +00:00
|
|
|
$cache->makeKey( 'ip-autoblock', 'exemption' ),
|
2015-10-19 17:52:19 +00:00
|
|
|
$cache::TTL_DAY,
|
2021-02-10 22:31:02 +00:00
|
|
|
static function ( $curValue, &$ttl, array &$setOpts ) {
|
2016-10-14 09:17:25 +00:00
|
|
|
$setOpts += Database::getCacheSetOptions( wfGetDB( DB_REPLICA ) );
|
|
|
|
|
|
2015-10-15 02:45:03 +00:00
|
|
|
return explode( "\n",
|
2021-03-20 03:39:26 +00:00
|
|
|
wfMessage( 'block-autoblock-exemptionlist' )->inContentLanguage()->plain()
|
|
|
|
|
);
|
2015-10-15 02:45:03 +00:00
|
|
|
}
|
|
|
|
|
);
|
2006-11-22 23:42:39 +00:00
|
|
|
|
2021-03-20 03:39:26 +00:00
|
|
|
wfDebug( "Checking the autoblock exemption list.." );
|
2006-11-22 23:42:39 +00:00
|
|
|
|
2010-02-14 22:07:30 +00:00
|
|
|
foreach ( $lines as $line ) {
|
2006-11-22 23:42:39 +00:00
|
|
|
# List items only
|
|
|
|
|
if ( substr( $line, 0, 1 ) !== '*' ) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2009-10-19 11:15:51 +00:00
|
|
|
$wlEntry = substr( $line, 1 );
|
|
|
|
|
$wlEntry = trim( $wlEntry );
|
2006-11-22 23:42:39 +00:00
|
|
|
|
2010-02-14 22:07:30 +00:00
|
|
|
wfDebug( "Checking $ip against $wlEntry..." );
|
2006-11-22 23:42:39 +00:00
|
|
|
|
|
|
|
|
# Is the IP in this range?
|
2019-06-25 18:53:15 +00:00
|
|
|
if ( IPUtils::isInRange( $ip, $wlEntry ) ) {
|
2020-06-01 05:00:39 +00:00
|
|
|
wfDebug( " IP $ip matches $wlEntry, not autoblocking" );
|
2008-08-08 05:56:43 +00:00
|
|
|
return true;
|
2006-12-08 10:30:50 +00:00
|
|
|
} else {
|
2020-06-01 05:00:39 +00:00
|
|
|
wfDebug( " No match" );
|
2006-11-22 23:42:39 +00:00
|
|
|
}
|
2006-11-22 11:51:49 +00:00
|
|
|
}
|
2009-10-19 11:15:51 +00:00
|
|
|
|
2008-08-08 05:56:43 +00:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2019-05-13 14:18:07 +00:00
|
|
|
* Autoblocks the given IP, referring to this block.
|
2008-09-22 04:52:51 +00:00
|
|
|
*
|
2014-04-23 09:25:56 +00:00
|
|
|
* @param string $autoblockIP The IP to autoblock.
|
2019-05-13 14:18:07 +00:00
|
|
|
* @return int|bool ID if an autoblock was inserted, false if not.
|
2008-09-22 04:52:51 +00:00
|
|
|
*/
|
2011-03-23 00:10:46 +00:00
|
|
|
public function doAutoblock( $autoblockIP ) {
|
2008-08-08 05:56:43 +00:00
|
|
|
# If autoblocks are disabled, go away.
|
2011-03-22 17:18:15 +00:00
|
|
|
if ( !$this->isAutoblocking() ) {
|
2011-03-22 11:22:15 +00:00
|
|
|
return false;
|
2008-08-08 05:56:43 +00:00
|
|
|
}
|
|
|
|
|
|
2021-03-20 03:39:26 +00:00
|
|
|
# Check if autoblock exempt.
|
|
|
|
|
if ( self::isExemptedFromAutoblocks( $autoblockIP ) ) {
|
2011-03-22 11:22:15 +00:00
|
|
|
return false;
|
2008-08-08 05:56:43 +00:00
|
|
|
}
|
2010-02-14 22:07:30 +00:00
|
|
|
|
2011-06-18 00:29:32 +00:00
|
|
|
# Allow hooks to cancel the autoblock.
|
Hooks::run() call site migration
Migrate all callers of Hooks::run() to use the new
HookContainer/HookRunner system.
General principles:
* Use DI if it is already used. We're not changing the way state is
managed in this patch.
* HookContainer is always injected, not HookRunner. HookContainer
is a service, it's a more generic interface, it is the only
thing that provides isRegistered() which is needed in some cases,
and a HookRunner can be efficiently constructed from it
(confirmed by benchmark). Because HookContainer is needed
for object construction, it is also needed by all factories.
* "Ask your friendly local base class". Big hierarchies like
SpecialPage and ApiBase have getHookContainer() and getHookRunner()
methods in the base class, and classes that extend that base class
are not expected to know or care where the base class gets its
HookContainer from.
* ProtectedHookAccessorTrait provides protected getHookContainer() and
getHookRunner() methods, getting them from the global service
container. The point of this is to ease migration to DI by ensuring
that call sites ask their local friendly base class rather than
getting a HookRunner from the service container directly.
* Private $this->hookRunner. In some smaller classes where accessor
methods did not seem warranted, there is a private HookRunner property
which is accessed directly. Very rarely (two cases), there is a
protected property, for consistency with code that conventionally
assumes protected=private, but in cases where the class might actually
be overridden, a protected accessor is preferred over a protected
property.
* The last resort: Hooks::runner(). Mostly for static, file-scope and
global code. In a few cases it was used for objects with broken
construction schemes, out of horror or laziness.
Constructors with new required arguments:
* AuthManager
* BadFileLookup
* BlockManager
* ClassicInterwikiLookup
* ContentHandlerFactory
* ContentSecurityPolicy
* DefaultOptionsManager
* DerivedPageDataUpdater
* FullSearchResultWidget
* HtmlCacheUpdater
* LanguageFactory
* LanguageNameUtils
* LinkRenderer
* LinkRendererFactory
* LocalisationCache
* MagicWordFactory
* MessageCache
* NamespaceInfo
* PageEditStash
* PageHandlerFactory
* PageUpdater
* ParserFactory
* PermissionManager
* RevisionStore
* RevisionStoreFactory
* SearchEngineConfig
* SearchEngineFactory
* SearchFormWidget
* SearchNearMatcher
* SessionBackend
* SpecialPageFactory
* UserNameUtils
* UserOptionsManager
* WatchedItemQueryService
* WatchedItemStore
Constructors with new optional arguments:
* DefaultPreferencesFactory
* Language
* LinkHolderArray
* MovePage
* Parser
* ParserCache
* PasswordReset
* Router
setHookContainer() now required after construction:
* AuthenticationProvider
* ResourceLoaderModule
* SearchEngine
Change-Id: Id442b0dbe43aba84bd5cf801d86dedc768b082c7
2020-03-19 02:42:09 +00:00
|
|
|
if ( !Hooks::runner()->onAbortAutoblock( $autoblockIP, $this ) ) {
|
2020-06-01 05:00:39 +00:00
|
|
|
wfDebug( "Autoblock aborted by hook." );
|
2008-05-23 10:34:11 +00:00
|
|
|
return false;
|
|
|
|
|
}
|
2006-11-22 11:51:49 +00:00
|
|
|
|
2011-06-18 00:29:32 +00:00
|
|
|
# It's okay to autoblock. Go ahead and insert/update the block...
|
2006-11-22 11:51:49 +00:00
|
|
|
|
2011-06-18 00:29:32 +00:00
|
|
|
# Do not add a *new* block if the IP is already blocked.
|
2017-07-23 01:24:09 +00:00
|
|
|
$ipblock = self::newFromTarget( $autoblockIP );
|
2006-11-08 09:54:06 +00:00
|
|
|
if ( $ipblock ) {
|
2011-06-18 00:29:32 +00:00
|
|
|
# Check if the block is an autoblock and would exceed the user block
|
|
|
|
|
# if renewed. If so, do nothing, otherwise prolong the block time...
|
2013-05-15 01:12:35 +00:00
|
|
|
if ( $ipblock->mAuto && // @todo Why not compare $ipblock->mExpiry?
|
2019-03-22 15:16:40 +00:00
|
|
|
$this->getExpiry() > self::getAutoblockExpiry( $ipblock->getTimestamp() )
|
2010-05-30 14:48:30 +00:00
|
|
|
) {
|
2011-06-18 00:29:32 +00:00
|
|
|
# Reset block timestamp to now and its expiry to
|
|
|
|
|
# $wgAutoblockExpiry in the future
|
2007-03-31 17:23:10 +00:00
|
|
|
$ipblock->updateTimestamp();
|
|
|
|
|
}
|
2011-03-22 11:22:15 +00:00
|
|
|
return false;
|
2006-11-08 09:54:06 +00:00
|
|
|
}
|
2022-02-26 09:35:10 +00:00
|
|
|
$blocker = $this->getBlocker();
|
|
|
|
|
if ( !$blocker ) {
|
|
|
|
|
throw new \RuntimeException( __METHOD__ . ': this block does not have a blocker' );
|
|
|
|
|
}
|
2006-11-08 09:54:06 +00:00
|
|
|
|
2011-06-18 00:29:32 +00:00
|
|
|
# Make a new block object with the desired properties.
|
2019-05-13 14:18:07 +00:00
|
|
|
$autoblock = new DatabaseBlock;
|
2021-04-16 12:55:24 +00:00
|
|
|
wfDebug( "Autoblocking {$this->getTargetName()}@" . $autoblockIP );
|
2011-03-23 00:10:46 +00:00
|
|
|
$autoblock->setTarget( $autoblockIP );
|
2022-02-26 09:35:10 +00:00
|
|
|
$autoblock->setBlocker( $blocker );
|
2019-03-22 15:16:40 +00:00
|
|
|
$autoblock->setReason(
|
2019-10-25 16:39:16 +00:00
|
|
|
wfMessage(
|
|
|
|
|
'autoblocker',
|
2021-04-16 12:55:24 +00:00
|
|
|
$this->getTargetName(),
|
2019-10-30 13:52:12 +00:00
|
|
|
$this->getReasonComment()->text
|
2019-11-18 17:14:57 +00:00
|
|
|
)->inContentLanguage()->plain()
|
2019-03-22 15:16:40 +00:00
|
|
|
);
|
2011-06-24 10:00:35 +00:00
|
|
|
$timestamp = wfTimestampNow();
|
2019-03-25 15:11:26 +00:00
|
|
|
$autoblock->setTimestamp( $timestamp );
|
2019-09-15 15:12:06 +00:00
|
|
|
$autoblock->mAuto = true;
|
Separate out different functionalities of Block::prevents
Block::prevents plays several different roles:
* acts as get/setter for Boolean properties that correspond to
ipb_create_account, ipb_block_email and ipb_allow_usertalk
* calculates whether a block blocks a given right, based on Block
properties, global configs, white/blacklists and anonymous user
rights
* decides whether a block prevents editing of the target's own
user talk page (listed separately because 'editownusertalk' is
not a right)
This patch:
* renames mDisableUsertalk to allowEditUsertalk (and reverses the
value), to match the field ipb_allow_usertalk and make this logic
easier to follow
* renames mCreateAccount to blockCreateAccount, to make it clear
that the flag blocks account creation when true, and make this
logic easier to follow
* decouples the block that is stored in the database (which now
reflects the form that the admin submitted) and the behaviour of
the block on enforcement (since the properties set by the admin
can be overridden by global configs) - so if the global configs
change, the block behaviour could too
* creates get/setters for blockCreateAccount, mBlockEmail and
allowEditUsertalk properties
* creates appliesToRight, exclusively for checking whether the
block blocks a given right, taking into account the block
properties, global configs and anonymous user rights
* creates appliesToUsertalk, for checking whether the block
blocks a user from editing their own talk page. The block is
unaware of the user trying to make the edit, and this user is not
always the same as the block target, e.g. if the block target is
an IP range. Therefore the user's talk page is passed in to this
method. appliesToUsertalk can be called from anywhere where the
user is known
* uses the get/setters wherever Block::prevents was being used as
such
* uses appliesToRight whenever Block::prevents was being used to
determine if the block blocks a given right
* uses appliesToUsertalk in User::isBlockedFrom
Bug: T211578
Bug: T214508
Change-Id: I0e131696419211319082cb454f4f05297e55d22e
2019-02-09 12:17:54 +00:00
|
|
|
$autoblock->isCreateAccountBlocked( $this->isCreateAccountBlocked() );
|
2007-03-14 05:24:06 +00:00
|
|
|
# Continue suppressing the name if needed
|
2019-03-22 15:16:40 +00:00
|
|
|
$autoblock->setHideName( $this->getHideName() );
|
Separate out different functionalities of Block::prevents
Block::prevents plays several different roles:
* acts as get/setter for Boolean properties that correspond to
ipb_create_account, ipb_block_email and ipb_allow_usertalk
* calculates whether a block blocks a given right, based on Block
properties, global configs, white/blacklists and anonymous user
rights
* decides whether a block prevents editing of the target's own
user talk page (listed separately because 'editownusertalk' is
not a right)
This patch:
* renames mDisableUsertalk to allowEditUsertalk (and reverses the
value), to match the field ipb_allow_usertalk and make this logic
easier to follow
* renames mCreateAccount to blockCreateAccount, to make it clear
that the flag blocks account creation when true, and make this
logic easier to follow
* decouples the block that is stored in the database (which now
reflects the form that the admin submitted) and the behaviour of
the block on enforcement (since the properties set by the admin
can be overridden by global configs) - so if the global configs
change, the block behaviour could too
* creates get/setters for blockCreateAccount, mBlockEmail and
allowEditUsertalk properties
* creates appliesToRight, exclusively for checking whether the
block blocks a given right, taking into account the block
properties, global configs and anonymous user rights
* creates appliesToUsertalk, for checking whether the block
blocks a user from editing their own talk page. The block is
unaware of the user trying to make the edit, and this user is not
always the same as the block target, e.g. if the block target is
an IP range. Therefore the user's talk page is passed in to this
method. appliesToUsertalk can be called from anywhere where the
user is known
* uses the get/setters wherever Block::prevents was being used as
such
* uses appliesToRight whenever Block::prevents was being used to
determine if the block blocks a given right
* uses appliesToUsertalk in User::isBlockedFrom
Bug: T211578
Bug: T214508
Change-Id: I0e131696419211319082cb454f4f05297e55d22e
2019-02-09 12:17:54 +00:00
|
|
|
$autoblock->isUsertalkEditAllowed( $this->isUsertalkEditAllowed() );
|
2012-03-28 02:44:32 +00:00
|
|
|
$autoblock->mParentBlockId = $this->mId;
|
2018-08-16 04:55:55 +00:00
|
|
|
$autoblock->isSitewide( $this->isSitewide() );
|
|
|
|
|
$autoblock->setRestrictions( $this->getRestrictions() );
|
2010-05-30 14:48:30 +00:00
|
|
|
|
2019-03-22 15:16:40 +00:00
|
|
|
if ( $this->getExpiry() == 'infinity' ) {
|
2011-03-23 00:10:46 +00:00
|
|
|
# Original block was indefinite, start an autoblock now
|
2019-03-22 15:16:40 +00:00
|
|
|
$autoblock->setExpiry( self::getAutoblockExpiry( $timestamp ) );
|
2006-11-08 09:54:06 +00:00
|
|
|
} else {
|
2011-03-23 00:10:46 +00:00
|
|
|
# If the user is already blocked with an expiry date, we don't
|
|
|
|
|
# want to pile on top of that.
|
2019-03-22 15:16:40 +00:00
|
|
|
$autoblock->setExpiry( min( $this->getExpiry(), self::getAutoblockExpiry( $timestamp ) ) );
|
2006-11-08 09:54:06 +00:00
|
|
|
}
|
2010-05-30 14:48:30 +00:00
|
|
|
|
2011-06-18 00:29:32 +00:00
|
|
|
# Insert the block...
|
2020-08-27 09:27:10 +00:00
|
|
|
$status = MediaWikiServices::getInstance()->getDatabaseBlockStore()->insertBlock(
|
|
|
|
|
$autoblock
|
|
|
|
|
);
|
2011-03-23 00:10:46 +00:00
|
|
|
return $status
|
|
|
|
|
? $status['id']
|
|
|
|
|
: false;
|
2006-11-08 09:54:06 +00:00
|
|
|
}
|
|
|
|
|
|
2008-09-21 14:22:23 +00:00
|
|
|
/**
|
|
|
|
|
* Has the block expired?
|
2014-04-23 09:25:56 +00:00
|
|
|
* @return bool
|
2008-09-21 14:22:23 +00:00
|
|
|
*/
|
2008-11-30 13:09:19 +00:00
|
|
|
public function isExpired() {
|
2011-06-24 10:00:35 +00:00
|
|
|
$timestamp = wfTimestampNow();
|
2020-06-01 05:00:39 +00:00
|
|
|
wfDebug( __METHOD__ . " checking current " . $timestamp . " vs $this->mExpiry" );
|
2010-05-30 14:48:30 +00:00
|
|
|
|
2021-08-17 21:15:57 +00:00
|
|
|
return $this->getExpiry() && $timestamp > $this->getExpiry();
|
2003-09-01 13:13:56 +00:00
|
|
|
}
|
|
|
|
|
|
2008-09-21 14:22:23 +00:00
|
|
|
/**
|
2010-02-14 22:07:30 +00:00
|
|
|
* Update the timestamp on autoblocks.
|
2008-09-21 14:22:23 +00:00
|
|
|
*/
|
2008-11-30 13:09:19 +00:00
|
|
|
public function updateTimestamp() {
|
2004-02-27 08:25:56 +00:00
|
|
|
if ( $this->mAuto ) {
|
2019-03-25 15:11:26 +00:00
|
|
|
$this->setTimestamp( wfTimestamp() );
|
2019-03-22 15:16:40 +00:00
|
|
|
$this->setExpiry( self::getAutoblockExpiry( $this->getTimestamp() ) );
|
2004-02-27 08:25:56 +00:00
|
|
|
|
2021-09-26 19:51:13 +00:00
|
|
|
$dbw = $this->getDBConnection( DB_PRIMARY );
|
2005-08-02 13:35:19 +00:00
|
|
|
$dbw->update( 'ipblocks',
|
2016-02-17 09:09:32 +00:00
|
|
|
[ /* SET */
|
2019-03-22 15:16:40 +00:00
|
|
|
'ipb_timestamp' => $dbw->timestamp( $this->getTimestamp() ),
|
|
|
|
|
'ipb_expiry' => $dbw->timestamp( $this->getExpiry() ),
|
2016-02-17 09:09:32 +00:00
|
|
|
],
|
|
|
|
|
[ /* WHERE */
|
2021-09-26 19:51:13 +00:00
|
|
|
'ipb_id' => $this->getId( $this->getWikiId() ),
|
2016-02-17 09:09:32 +00:00
|
|
|
],
|
2011-03-22 17:18:15 +00:00
|
|
|
__METHOD__
|
2004-07-10 03:09:26 +00:00
|
|
|
);
|
2004-02-14 12:37:25 +00:00
|
|
|
}
|
|
|
|
|
}
|
2005-08-02 13:35:19 +00:00
|
|
|
|
2011-03-19 23:47:08 +00:00
|
|
|
/**
|
|
|
|
|
* Get the IP address at the start of the range in Hex form
|
2012-10-07 23:35:26 +00:00
|
|
|
* @throws MWException
|
2014-04-23 09:25:56 +00:00
|
|
|
* @return string IP in Hex form
|
2011-03-19 23:47:08 +00:00
|
|
|
*/
|
2011-03-20 17:43:17 +00:00
|
|
|
public function getRangeStart() {
|
2013-04-26 14:42:31 +00:00
|
|
|
switch ( $this->type ) {
|
2011-03-19 23:47:08 +00:00
|
|
|
case self::TYPE_USER:
|
2011-06-07 18:13:21 +00:00
|
|
|
return '';
|
2011-03-19 23:47:08 +00:00
|
|
|
case self::TYPE_IP:
|
2019-06-25 18:53:15 +00:00
|
|
|
return IPUtils::toHex( $this->target );
|
2011-03-19 23:47:08 +00:00
|
|
|
case self::TYPE_RANGE:
|
2019-06-25 18:53:15 +00:00
|
|
|
list( $start, /*...*/ ) = IPUtils::parseRange( $this->target );
|
2011-03-22 17:18:15 +00:00
|
|
|
return $start;
|
2013-04-11 05:29:05 +00:00
|
|
|
default:
|
|
|
|
|
throw new MWException( "Block with invalid type" );
|
2011-03-19 23:47:08 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2013-06-23 01:01:26 +00:00
|
|
|
* Get the IP address at the end of the range in Hex form
|
2012-10-07 23:35:26 +00:00
|
|
|
* @throws MWException
|
2014-04-23 09:25:56 +00:00
|
|
|
* @return string IP in Hex form
|
2011-03-19 23:47:08 +00:00
|
|
|
*/
|
2011-03-20 17:43:17 +00:00
|
|
|
public function getRangeEnd() {
|
2013-04-26 14:42:31 +00:00
|
|
|
switch ( $this->type ) {
|
2011-03-19 23:47:08 +00:00
|
|
|
case self::TYPE_USER:
|
2011-06-07 18:13:21 +00:00
|
|
|
return '';
|
2011-03-19 23:47:08 +00:00
|
|
|
case self::TYPE_IP:
|
2019-06-25 18:53:15 +00:00
|
|
|
return IPUtils::toHex( $this->target );
|
2011-03-19 23:47:08 +00:00
|
|
|
case self::TYPE_RANGE:
|
2019-06-25 18:53:15 +00:00
|
|
|
list( /*...*/, $end ) = IPUtils::parseRange( $this->target );
|
2011-03-22 17:18:15 +00:00
|
|
|
return $end;
|
2013-04-11 05:29:05 +00:00
|
|
|
default:
|
|
|
|
|
throw new MWException( "Block with invalid type" );
|
2011-03-19 23:47:08 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-20 00:04:00 +00:00
|
|
|
/**
|
|
|
|
|
* @inheritDoc
|
|
|
|
|
* @deprecated since 1.35. Use getReasonComment instead.
|
|
|
|
|
*/
|
|
|
|
|
public function getReason() {
|
|
|
|
|
if ( $this->getType() === self::TYPE_AUTO ) {
|
|
|
|
|
return $this->reason->message->inContentLanguage()->plain();
|
|
|
|
|
}
|
|
|
|
|
return $this->reason->text;
|
|
|
|
|
}
|
|
|
|
|
|
2006-11-21 18:26:55 +00:00
|
|
|
/**
|
Separate Block into AbstractBlock, Block and SystemBlock
This commit splits the existing Block class into AbstractBlock, Block
and SystemBlock.
Before this patch, the Block class represents several types of
blocks, which can be separated into blocks stored in the database,
and temporary blocks created by the system. These are now
represented by Block and SystemBlock, which inherit from
AbstractBlock.
This lays the foundations for:
* enforcing block parameters from multiple blocks that apply to a
user/IP address
* improvements to the Block API, including the addition of services
Breaking changes: functions expecting a Block object should still
expect a Block object if it came from the database, but other
functions may now need to expect an AbstractBlock or SystemBlock
object. (Note that an alternative naming scheme, in which the
abstract class is called Block and the subclasses are DatabaseBlock
and SystemBlock, avoids this breakage. However, it introduces more
breakages to calls to static Block methods and new Block
instantiations.)
Changes to tests: system blocks don't set the $blockCreateAccount or
$mExipry block properties, so remove/change any tests that assume
they do.
Bug: T222737
Change-Id: I83bceb5e5049e254c90ace060f8f8fad44696c67
2019-03-18 22:09:49 +00:00
|
|
|
* @inheritDoc
|
2011-03-21 19:12:41 +00:00
|
|
|
*/
|
2021-09-26 19:51:13 +00:00
|
|
|
public function getId( $wikiId = self::LOCAL ): ?int {
|
|
|
|
|
// TODO: Enable deprecation warnings once cross-wiki accesses have been removed, see T274817
|
|
|
|
|
// $this->deprecateInvalidCrossWiki( $wikiId, '1.38' );
|
2011-03-21 19:12:41 +00:00
|
|
|
return $this->mId;
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-16 04:55:55 +00:00
|
|
|
/**
|
|
|
|
|
* Set the block ID
|
|
|
|
|
*
|
2020-05-08 06:29:23 +00:00
|
|
|
* @internal Only for use in DatabaseBlockStore; private until 1.36
|
2018-08-16 04:55:55 +00:00
|
|
|
* @param int $blockId
|
2019-05-22 19:50:52 +00:00
|
|
|
* @return self
|
2018-08-16 04:55:55 +00:00
|
|
|
*/
|
2020-05-08 06:29:23 +00:00
|
|
|
public function setId( $blockId ) {
|
2018-08-16 04:55:55 +00:00
|
|
|
$this->mId = (int)$blockId;
|
|
|
|
|
|
|
|
|
|
if ( is_array( $this->restrictions ) ) {
|
2019-04-11 19:54:10 +00:00
|
|
|
$this->restrictions = $this->getBlockRestrictionStore()->setBlockId(
|
|
|
|
|
$blockId, $this->restrictions
|
|
|
|
|
);
|
2018-08-16 04:55:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $this;
|
|
|
|
|
}
|
|
|
|
|
|
2019-06-20 10:29:01 +00:00
|
|
|
/**
|
|
|
|
|
* @since 1.34
|
|
|
|
|
* @return int|null If this is an autoblock, ID of the parent block; otherwise null
|
|
|
|
|
*/
|
|
|
|
|
public function getParentBlockId() {
|
|
|
|
|
return $this->mParentBlockId;
|
|
|
|
|
}
|
|
|
|
|
|
2011-03-19 23:47:08 +00:00
|
|
|
/**
|
2019-05-13 14:18:07 +00:00
|
|
|
* Get/set whether the block is a hardblock (affects logged-in users on a given IP/range)
|
2014-09-16 23:00:19 +00:00
|
|
|
* @param bool|null $x
|
2014-04-23 09:25:56 +00:00
|
|
|
* @return bool
|
2011-03-19 23:47:08 +00:00
|
|
|
*/
|
2021-07-22 03:11:47 +00:00
|
|
|
public function isHardblock( $x = null ): bool {
|
2011-03-22 17:18:15 +00:00
|
|
|
wfSetVar( $this->isHardblock, $x );
|
|
|
|
|
|
|
|
|
|
# You can't *not* hardblock a user
|
|
|
|
|
return $this->getType() == self::TYPE_USER
|
|
|
|
|
? true
|
|
|
|
|
: $this->isHardblock;
|
2011-03-19 23:47:08 +00:00
|
|
|
}
|
|
|
|
|
|
2014-09-16 23:00:19 +00:00
|
|
|
/**
|
|
|
|
|
* @param null|bool $x
|
|
|
|
|
* @return bool
|
|
|
|
|
*/
|
2011-03-21 19:12:41 +00:00
|
|
|
public function isAutoblocking( $x = null ) {
|
2011-03-22 17:18:15 +00:00
|
|
|
wfSetVar( $this->isAutoblocking, $x );
|
|
|
|
|
|
|
|
|
|
# You can't put an autoblock on an IP or range as we don't have any history to
|
|
|
|
|
# look over to get more IPs from
|
|
|
|
|
return $this->getType() == self::TYPE_USER
|
|
|
|
|
? $this->isAutoblocking
|
|
|
|
|
: false;
|
2011-03-21 19:12:41 +00:00
|
|
|
}
|
|
|
|
|
|
2008-09-22 04:52:51 +00:00
|
|
|
/**
|
|
|
|
|
* Get the block name, but with autoblocked IPs hidden as per standard privacy policy
|
2014-04-23 09:25:56 +00:00
|
|
|
* @return string Text is escaped
|
2008-09-22 04:52:51 +00:00
|
|
|
*/
|
2008-11-30 13:09:19 +00:00
|
|
|
public function getRedactedName() {
|
2006-07-10 06:30:03 +00:00
|
|
|
if ( $this->mAuto ) {
|
2018-09-20 13:16:11 +00:00
|
|
|
return Html::element(
|
2011-03-13 15:14:33 +00:00
|
|
|
'span',
|
2016-02-17 09:09:32 +00:00
|
|
|
[ 'class' => 'mw-autoblockid' ],
|
2018-09-20 13:16:11 +00:00
|
|
|
wfMessage( 'autoblockid', $this->mId )->text()
|
2011-03-13 15:14:33 +00:00
|
|
|
);
|
2006-07-10 06:30:03 +00:00
|
|
|
} else {
|
2021-04-16 12:55:24 +00:00
|
|
|
return htmlspecialchars( $this->getTargetName() );
|
2006-07-10 06:30:03 +00:00
|
|
|
}
|
|
|
|
|
}
|
2008-04-14 07:45:50 +00:00
|
|
|
|
2008-09-21 14:22:23 +00:00
|
|
|
/**
|
|
|
|
|
* Get a timestamp of the expiry for autoblocks
|
2008-09-22 04:52:51 +00:00
|
|
|
*
|
2014-04-23 09:25:56 +00:00
|
|
|
* @param string|int $timestamp
|
|
|
|
|
* @return string
|
2008-09-21 14:22:23 +00:00
|
|
|
*/
|
2008-11-30 13:09:19 +00:00
|
|
|
public static function getAutoblockExpiry( $timestamp ) {
|
2022-01-06 18:44:56 +00:00
|
|
|
$autoblockExpiry = MediaWikiServices::getInstance()->getMainConfig()->get( 'AutoblockExpiry' );
|
2010-05-30 14:48:30 +00:00
|
|
|
|
2022-01-06 18:44:56 +00:00
|
|
|
return wfTimestamp( TS_MW, (int)wfTimestamp( TS_UNIX, $timestamp ) + $autoblockExpiry );
|
2004-02-14 12:37:25 +00:00
|
|
|
}
|
2008-04-14 07:45:50 +00:00
|
|
|
|
|
|
|
|
/**
|
2006-07-10 06:30:03 +00:00
|
|
|
* Purge expired blocks from the ipblocks table
|
2020-08-27 09:27:10 +00:00
|
|
|
*
|
2021-09-28 16:35:42 +00:00
|
|
|
* @deprecated since 1.36, hard deprecated since 1.38
|
|
|
|
|
* Use DatabaseBlockStore::purgeExpiredBlocks instead.
|
2006-07-10 06:30:03 +00:00
|
|
|
*/
|
2008-11-30 13:09:19 +00:00
|
|
|
public static function purgeExpired() {
|
2021-09-28 16:35:42 +00:00
|
|
|
wfDeprecated( __METHOD__, '1.36' );
|
2020-05-08 06:29:23 +00:00
|
|
|
MediaWikiServices::getInstance()->getDatabaseBlockStore()->purgeExpiredBlocks();
|
2006-07-10 06:30:03 +00:00
|
|
|
}
|
|
|
|
|
|
2011-03-13 14:47:34 +00:00
|
|
|
/**
|
2019-05-13 14:18:07 +00:00
|
|
|
* Given a target and the target's type, get an existing block object if possible.
|
2021-03-19 13:56:13 +00:00
|
|
|
* @param string|UserIdentity|int|null $specificTarget A block target, which may be one of several types:
|
2011-03-13 14:47:34 +00:00
|
|
|
* * A user to block, in which case $target will be a User
|
|
|
|
|
* * An IP to block, in which case $target will be a User generated by using
|
|
|
|
|
* User::newFromName( $ip, false ) to turn off name validation
|
|
|
|
|
* * An IP range, in which case $target will be a String "123.123.123.123/18" etc
|
2011-03-21 19:12:41 +00:00
|
|
|
* * The ID of an existing block, in the format "#12345" (since pure numbers are valid
|
|
|
|
|
* usernames
|
|
|
|
|
* Calling this with a user, IP address or range will not select autoblocks, and will
|
|
|
|
|
* only select a block where the targets match exactly (so looking for blocks on
|
|
|
|
|
* 1.2.3.4 will not select 1.2.0.0/16 or even 1.2.3.4/32)
|
2021-03-19 13:56:13 +00:00
|
|
|
* @param string|UserIdentity|int|null $vagueTarget As above, but we will search for *any* block which
|
2011-03-21 19:12:41 +00:00
|
|
|
* affects that target (so for an IP address, get ranges containing that IP; and also
|
2011-05-24 21:04:50 +00:00
|
|
|
* get any relevant autoblocks). Leave empty or blank to skip IP-based lookups.
|
2021-04-19 01:02:08 +00:00
|
|
|
* @param bool $fromPrimary Whether to use the DB_PRIMARY database
|
2019-05-13 14:18:07 +00:00
|
|
|
* @return DatabaseBlock|null (null if no relevant block could be found). The target and type
|
|
|
|
|
* of the returned block will refer to the actual block which was found, which might
|
2011-03-21 19:12:41 +00:00
|
|
|
* not be the same as the target you gave if you used $vagueTarget!
|
2011-03-13 14:47:34 +00:00
|
|
|
*/
|
2021-04-19 01:02:08 +00:00
|
|
|
public static function newFromTarget( $specificTarget, $vagueTarget = null, $fromPrimary = false ) {
|
|
|
|
|
$blocks = self::newListFromTarget( $specificTarget, $vagueTarget, $fromPrimary );
|
2019-03-19 18:56:10 +00:00
|
|
|
return self::chooseMostSpecificBlock( $blocks );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* This is similar to DatabaseBlock::newFromTarget, but it returns all the relevant blocks.
|
|
|
|
|
*
|
|
|
|
|
* @since 1.34
|
2021-03-19 13:56:13 +00:00
|
|
|
* @param string|UserIdentity|int|null $specificTarget
|
|
|
|
|
* @param string|UserIdentity|int|null $vagueTarget
|
2021-04-19 01:02:08 +00:00
|
|
|
* @param bool $fromPrimary
|
2019-03-19 18:56:10 +00:00
|
|
|
* @return DatabaseBlock[] Any relevant blocks
|
|
|
|
|
*/
|
|
|
|
|
public static function newListFromTarget(
|
|
|
|
|
$specificTarget,
|
|
|
|
|
$vagueTarget = null,
|
2021-04-19 01:02:08 +00:00
|
|
|
$fromPrimary = false
|
2019-03-19 18:56:10 +00:00
|
|
|
) {
|
2021-03-17 17:48:43 +00:00
|
|
|
list( $target, $type ) = MediaWikiServices::getInstance()
|
|
|
|
|
->getBlockUtils()
|
|
|
|
|
->parseBlockTarget( $specificTarget );
|
2017-07-23 01:24:09 +00:00
|
|
|
if ( $type == self::TYPE_ID || $type == self::TYPE_AUTO ) {
|
2019-03-19 18:56:10 +00:00
|
|
|
$block = self::newFromID( $target );
|
|
|
|
|
return $block ? [ $block ] : [];
|
2013-04-20 22:49:30 +00:00
|
|
|
} elseif ( $target === null && $vagueTarget == '' ) {
|
2011-03-22 11:45:18 +00:00
|
|
|
# We're not going to find anything useful here
|
2011-06-27 13:48:01 +00:00
|
|
|
# Be aware that the == '' check is explicit, since empty values will be
|
2017-02-20 22:44:19 +00:00
|
|
|
# passed by some callers (T31116)
|
2019-03-19 18:56:10 +00:00
|
|
|
return [];
|
2014-03-23 01:28:57 +00:00
|
|
|
} elseif ( in_array(
|
|
|
|
|
$type,
|
2017-07-23 01:24:09 +00:00
|
|
|
[ self::TYPE_USER, self::TYPE_IP, self::TYPE_RANGE, null ] )
|
2014-03-23 01:28:57 +00:00
|
|
|
) {
|
2021-04-19 01:02:08 +00:00
|
|
|
return self::newLoad( $target, $type, $fromPrimary, $vagueTarget );
|
2011-03-13 14:47:34 +00:00
|
|
|
}
|
2019-03-19 18:56:10 +00:00
|
|
|
return [];
|
2011-03-13 14:47:34 +00:00
|
|
|
}
|
|
|
|
|
|
2012-11-17 23:23:09 +00:00
|
|
|
/**
|
|
|
|
|
* Get all blocks that match any IP from an array of IP addresses
|
|
|
|
|
*
|
2014-04-23 09:25:56 +00:00
|
|
|
* @param array $ipChain List of IPs (strings), usually retrieved from the
|
2017-02-25 21:53:36 +00:00
|
|
|
* X-Forwarded-For header of the request
|
2014-04-23 09:25:56 +00:00
|
|
|
* @param bool $isAnon Exclude anonymous-only blocks if false
|
2021-04-19 01:02:08 +00:00
|
|
|
* @param bool $fromPrimary Whether to query the primary or replica DB
|
2020-10-28 10:01:33 +00:00
|
|
|
* @return self[]
|
2013-05-20 05:52:14 +00:00
|
|
|
* @since 1.22
|
2012-11-17 23:23:09 +00:00
|
|
|
*/
|
2021-04-19 01:02:08 +00:00
|
|
|
public static function getBlocksForIPList( array $ipChain, $isAnon, $fromPrimary = false ) {
|
2019-01-09 16:24:36 +00:00
|
|
|
if ( $ipChain === [] ) {
|
2016-02-17 09:09:32 +00:00
|
|
|
return [];
|
2012-11-17 23:23:09 +00:00
|
|
|
}
|
|
|
|
|
|
2016-02-17 09:09:32 +00:00
|
|
|
$conds = [];
|
2016-09-22 02:52:06 +00:00
|
|
|
$proxyLookup = MediaWikiServices::getInstance()->getProxyLookup();
|
2012-11-17 23:23:09 +00:00
|
|
|
foreach ( array_unique( $ipChain ) as $ipaddr ) {
|
|
|
|
|
# Discard invalid IP addresses. Since XFF can be spoofed and we do not
|
|
|
|
|
# necessarily trust the header given to us, make sure that we are only
|
|
|
|
|
# checking for blocks on well-formatted IP addresses (IPv4 and IPv6).
|
|
|
|
|
# Do not treat private IP spaces as special as it may be desirable for wikis
|
|
|
|
|
# to block those IP ranges in order to stop misbehaving proxies that spoof XFF.
|
2019-06-25 18:53:15 +00:00
|
|
|
if ( !IPUtils::isValid( $ipaddr ) ) {
|
2012-11-17 23:23:09 +00:00
|
|
|
continue;
|
|
|
|
|
}
|
2017-11-01 20:55:24 +00:00
|
|
|
# Don't check trusted IPs (includes local CDNs which will be in every request)
|
2016-09-22 02:52:06 +00:00
|
|
|
if ( $proxyLookup->isTrustedProxy( $ipaddr ) ) {
|
2012-11-17 23:23:09 +00:00
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
# Check both the original IP (to check against single blocks), as well as build
|
|
|
|
|
# the clause to check for rangeblocks for the given IP.
|
|
|
|
|
$conds['ipb_address'][] = $ipaddr;
|
2019-06-25 18:53:15 +00:00
|
|
|
$conds[] = self::getRangeCond( IPUtils::toHex( $ipaddr ) );
|
2012-11-17 23:23:09 +00:00
|
|
|
}
|
|
|
|
|
|
2019-01-09 16:24:36 +00:00
|
|
|
if ( $conds === [] ) {
|
2016-02-17 09:09:32 +00:00
|
|
|
return [];
|
2012-11-17 23:23:09 +00:00
|
|
|
}
|
|
|
|
|
|
2021-04-19 01:02:08 +00:00
|
|
|
if ( $fromPrimary ) {
|
2021-04-29 02:37:11 +00:00
|
|
|
$db = wfGetDB( DB_PRIMARY );
|
2012-11-17 23:23:09 +00:00
|
|
|
} else {
|
2016-09-05 19:55:19 +00:00
|
|
|
$db = wfGetDB( DB_REPLICA );
|
2012-11-17 23:23:09 +00:00
|
|
|
}
|
|
|
|
|
$conds = $db->makeList( $conds, LIST_OR );
|
|
|
|
|
if ( !$isAnon ) {
|
2016-02-17 09:09:32 +00:00
|
|
|
$conds = [ $conds, 'ipb_anon_only' => 0 ];
|
2012-11-17 23:23:09 +00:00
|
|
|
}
|
2017-10-06 17:03:55 +00:00
|
|
|
$blockQuery = self::getQueryInfo();
|
|
|
|
|
$rows = $db->select(
|
|
|
|
|
$blockQuery['tables'],
|
|
|
|
|
array_merge( [ 'ipb_range_start', 'ipb_range_end' ], $blockQuery['fields'] ),
|
2012-11-17 23:23:09 +00:00
|
|
|
$conds,
|
2017-10-06 17:03:55 +00:00
|
|
|
__METHOD__,
|
|
|
|
|
[],
|
|
|
|
|
$blockQuery['joins']
|
2012-11-17 23:23:09 +00:00
|
|
|
);
|
|
|
|
|
|
2016-02-17 09:09:32 +00:00
|
|
|
$blocks = [];
|
2012-11-17 23:23:09 +00:00
|
|
|
foreach ( $rows as $row ) {
|
|
|
|
|
$block = self::newFromRow( $row );
|
2015-12-30 04:29:10 +00:00
|
|
|
if ( !$block->isExpired() ) {
|
2012-11-17 23:23:09 +00:00
|
|
|
$blocks[] = $block;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $blocks;
|
|
|
|
|
}
|
|
|
|
|
|
2011-03-13 14:47:34 +00:00
|
|
|
/**
|
Separate Block into AbstractBlock, Block and SystemBlock
This commit splits the existing Block class into AbstractBlock, Block
and SystemBlock.
Before this patch, the Block class represents several types of
blocks, which can be separated into blocks stored in the database,
and temporary blocks created by the system. These are now
represented by Block and SystemBlock, which inherit from
AbstractBlock.
This lays the foundations for:
* enforcing block parameters from multiple blocks that apply to a
user/IP address
* improvements to the Block API, including the addition of services
Breaking changes: functions expecting a Block object should still
expect a Block object if it came from the database, but other
functions may now need to expect an AbstractBlock or SystemBlock
object. (Note that an alternative naming scheme, in which the
abstract class is called Block and the subclasses are DatabaseBlock
and SystemBlock, avoids this breakage. However, it introduces more
breakages to calls to static Block methods and new Block
instantiations.)
Changes to tests: system blocks don't set the $blockCreateAccount or
$mExipry block properties, so remove/change any tests that assume
they do.
Bug: T222737
Change-Id: I83bceb5e5049e254c90ace060f8f8fad44696c67
2019-03-18 22:09:49 +00:00
|
|
|
* @inheritDoc
|
2011-06-26 23:01:29 +00:00
|
|
|
*
|
Separate Block into AbstractBlock, Block and SystemBlock
This commit splits the existing Block class into AbstractBlock, Block
and SystemBlock.
Before this patch, the Block class represents several types of
blocks, which can be separated into blocks stored in the database,
and temporary blocks created by the system. These are now
represented by Block and SystemBlock, which inherit from
AbstractBlock.
This lays the foundations for:
* enforcing block parameters from multiple blocks that apply to a
user/IP address
* improvements to the Block API, including the addition of services
Breaking changes: functions expecting a Block object should still
expect a Block object if it came from the database, but other
functions may now need to expect an AbstractBlock or SystemBlock
object. (Note that an alternative naming scheme, in which the
abstract class is called Block and the subclasses are DatabaseBlock
and SystemBlock, avoids this breakage. However, it introduces more
breakages to calls to static Block methods and new Block
instantiations.)
Changes to tests: system blocks don't set the $blockCreateAccount or
$mExipry block properties, so remove/change any tests that assume
they do.
Bug: T222737
Change-Id: I83bceb5e5049e254c90ace060f8f8fad44696c67
2019-03-18 22:09:49 +00:00
|
|
|
* Autoblocks have whichever type corresponds to their target, so to detect if a block is an
|
|
|
|
|
* autoblock, we have to check the mAuto property instead.
|
2011-03-21 19:12:41 +00:00
|
|
|
*/
|
2021-07-22 03:11:47 +00:00
|
|
|
public function getType(): ?int {
|
2011-03-21 19:12:41 +00:00
|
|
|
return $this->mAuto
|
|
|
|
|
? self::TYPE_AUTO
|
Separate Block into AbstractBlock, Block and SystemBlock
This commit splits the existing Block class into AbstractBlock, Block
and SystemBlock.
Before this patch, the Block class represents several types of
blocks, which can be separated into blocks stored in the database,
and temporary blocks created by the system. These are now
represented by Block and SystemBlock, which inherit from
AbstractBlock.
This lays the foundations for:
* enforcing block parameters from multiple blocks that apply to a
user/IP address
* improvements to the Block API, including the addition of services
Breaking changes: functions expecting a Block object should still
expect a Block object if it came from the database, but other
functions may now need to expect an AbstractBlock or SystemBlock
object. (Note that an alternative naming scheme, in which the
abstract class is called Block and the subclasses are DatabaseBlock
and SystemBlock, avoids this breakage. However, it introduces more
breakages to calls to static Block methods and new Block
instantiations.)
Changes to tests: system blocks don't set the $blockCreateAccount or
$mExipry block properties, so remove/change any tests that assume
they do.
Bug: T222737
Change-Id: I83bceb5e5049e254c90ace060f8f8fad44696c67
2019-03-18 22:09:49 +00:00
|
|
|
: parent::getType();
|
2011-03-13 21:33:52 +00:00
|
|
|
}
|
2013-04-03 21:44:00 +00:00
|
|
|
|
|
|
|
|
/**
|
Separate Block into AbstractBlock, Block and SystemBlock
This commit splits the existing Block class into AbstractBlock, Block
and SystemBlock.
Before this patch, the Block class represents several types of
blocks, which can be separated into blocks stored in the database,
and temporary blocks created by the system. These are now
represented by Block and SystemBlock, which inherit from
AbstractBlock.
This lays the foundations for:
* enforcing block parameters from multiple blocks that apply to a
user/IP address
* improvements to the Block API, including the addition of services
Breaking changes: functions expecting a Block object should still
expect a Block object if it came from the database, but other
functions may now need to expect an AbstractBlock or SystemBlock
object. (Note that an alternative naming scheme, in which the
abstract class is called Block and the subclasses are DatabaseBlock
and SystemBlock, avoids this breakage. However, it introduces more
breakages to calls to static Block methods and new Block
instantiations.)
Changes to tests: system blocks don't set the $blockCreateAccount or
$mExipry block properties, so remove/change any tests that assume
they do.
Bug: T222737
Change-Id: I83bceb5e5049e254c90ace060f8f8fad44696c67
2019-03-18 22:09:49 +00:00
|
|
|
* @inheritDoc
|
2013-04-03 21:44:00 +00:00
|
|
|
*/
|
2021-09-26 19:51:13 +00:00
|
|
|
public function getIdentifier( $wikiId = self::LOCAL ) {
|
|
|
|
|
return $this->getId( $wikiId );
|
2018-08-27 01:45:18 +00:00
|
|
|
}
|
|
|
|
|
|
2018-08-16 04:55:55 +00:00
|
|
|
/**
|
|
|
|
|
* Getting the restrictions will perform a database query if the restrictions
|
|
|
|
|
* are not already loaded.
|
|
|
|
|
*
|
2019-01-08 12:44:33 +00:00
|
|
|
* @since 1.33
|
2018-08-16 04:55:55 +00:00
|
|
|
* @return Restriction[]
|
|
|
|
|
*/
|
|
|
|
|
public function getRestrictions() {
|
|
|
|
|
if ( $this->restrictions === null ) {
|
|
|
|
|
// If the block id has not been set, then do not attempt to load the
|
|
|
|
|
// restrictions.
|
|
|
|
|
if ( !$this->mId ) {
|
|
|
|
|
return [];
|
|
|
|
|
}
|
2019-04-11 19:54:10 +00:00
|
|
|
$this->restrictions = $this->getBlockRestrictionStore()->loadByBlockId( $this->mId );
|
2018-08-16 04:55:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $this->restrictions;
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-08 06:29:23 +00:00
|
|
|
/**
|
|
|
|
|
* Get restrictions without loading from database if not yet loaded
|
|
|
|
|
*
|
|
|
|
|
* @internal
|
|
|
|
|
* @return ?Restriction[]
|
|
|
|
|
*/
|
2021-07-22 03:11:47 +00:00
|
|
|
public function getRawRestrictions(): ?array {
|
2020-05-08 06:29:23 +00:00
|
|
|
return $this->restrictions;
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-16 04:55:55 +00:00
|
|
|
/**
|
2019-01-08 12:44:33 +00:00
|
|
|
* @since 1.33
|
2018-08-16 04:55:55 +00:00
|
|
|
* @param Restriction[] $restrictions
|
|
|
|
|
* @return self
|
|
|
|
|
*/
|
|
|
|
|
public function setRestrictions( array $restrictions ) {
|
2021-02-10 22:31:02 +00:00
|
|
|
$this->restrictions = array_filter( $restrictions, static function ( $restriction ) {
|
2018-08-16 04:55:55 +00:00
|
|
|
return $restriction instanceof Restriction;
|
|
|
|
|
} );
|
|
|
|
|
|
|
|
|
|
return $this;
|
|
|
|
|
}
|
2018-08-27 01:45:18 +00:00
|
|
|
|
Separate out different functionalities of Block::prevents
Block::prevents plays several different roles:
* acts as get/setter for Boolean properties that correspond to
ipb_create_account, ipb_block_email and ipb_allow_usertalk
* calculates whether a block blocks a given right, based on Block
properties, global configs, white/blacklists and anonymous user
rights
* decides whether a block prevents editing of the target's own
user talk page (listed separately because 'editownusertalk' is
not a right)
This patch:
* renames mDisableUsertalk to allowEditUsertalk (and reverses the
value), to match the field ipb_allow_usertalk and make this logic
easier to follow
* renames mCreateAccount to blockCreateAccount, to make it clear
that the flag blocks account creation when true, and make this
logic easier to follow
* decouples the block that is stored in the database (which now
reflects the form that the admin submitted) and the behaviour of
the block on enforcement (since the properties set by the admin
can be overridden by global configs) - so if the global configs
change, the block behaviour could too
* creates get/setters for blockCreateAccount, mBlockEmail and
allowEditUsertalk properties
* creates appliesToRight, exclusively for checking whether the
block blocks a given right, taking into account the block
properties, global configs and anonymous user rights
* creates appliesToUsertalk, for checking whether the block
blocks a user from editing their own talk page. The block is
unaware of the user trying to make the edit, and this user is not
always the same as the block target, e.g. if the block target is
an IP range. Therefore the user's talk page is passed in to this
method. appliesToUsertalk can be called from anywhere where the
user is known
* uses the get/setters wherever Block::prevents was being used as
such
* uses appliesToRight whenever Block::prevents was being used to
determine if the block blocks a given right
* uses appliesToUsertalk in User::isBlockedFrom
Bug: T211578
Bug: T214508
Change-Id: I0e131696419211319082cb454f4f05297e55d22e
2019-02-09 12:17:54 +00:00
|
|
|
/**
|
Separate Block into AbstractBlock, Block and SystemBlock
This commit splits the existing Block class into AbstractBlock, Block
and SystemBlock.
Before this patch, the Block class represents several types of
blocks, which can be separated into blocks stored in the database,
and temporary blocks created by the system. These are now
represented by Block and SystemBlock, which inherit from
AbstractBlock.
This lays the foundations for:
* enforcing block parameters from multiple blocks that apply to a
user/IP address
* improvements to the Block API, including the addition of services
Breaking changes: functions expecting a Block object should still
expect a Block object if it came from the database, but other
functions may now need to expect an AbstractBlock or SystemBlock
object. (Note that an alternative naming scheme, in which the
abstract class is called Block and the subclasses are DatabaseBlock
and SystemBlock, avoids this breakage. However, it introduces more
breakages to calls to static Block methods and new Block
instantiations.)
Changes to tests: system blocks don't set the $blockCreateAccount or
$mExipry block properties, so remove/change any tests that assume
they do.
Bug: T222737
Change-Id: I83bceb5e5049e254c90ace060f8f8fad44696c67
2019-03-18 22:09:49 +00:00
|
|
|
* @inheritDoc
|
2018-08-27 01:45:18 +00:00
|
|
|
*/
|
2018-11-01 14:11:03 +00:00
|
|
|
public function appliesToTitle( Title $title ) {
|
|
|
|
|
if ( $this->isSitewide() ) {
|
|
|
|
|
return true;
|
2018-08-27 01:45:18 +00:00
|
|
|
}
|
|
|
|
|
|
2018-11-01 14:11:03 +00:00
|
|
|
$restrictions = $this->getRestrictions();
|
|
|
|
|
foreach ( $restrictions as $restriction ) {
|
|
|
|
|
if ( $restriction->matches( $title ) ) {
|
|
|
|
|
return true;
|
2018-08-27 01:45:18 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-01 14:11:03 +00:00
|
|
|
return false;
|
2018-08-27 01:45:18 +00:00
|
|
|
}
|
2018-10-30 18:19:22 +00:00
|
|
|
|
|
|
|
|
/**
|
Separate Block into AbstractBlock, Block and SystemBlock
This commit splits the existing Block class into AbstractBlock, Block
and SystemBlock.
Before this patch, the Block class represents several types of
blocks, which can be separated into blocks stored in the database,
and temporary blocks created by the system. These are now
represented by Block and SystemBlock, which inherit from
AbstractBlock.
This lays the foundations for:
* enforcing block parameters from multiple blocks that apply to a
user/IP address
* improvements to the Block API, including the addition of services
Breaking changes: functions expecting a Block object should still
expect a Block object if it came from the database, but other
functions may now need to expect an AbstractBlock or SystemBlock
object. (Note that an alternative naming scheme, in which the
abstract class is called Block and the subclasses are DatabaseBlock
and SystemBlock, avoids this breakage. However, it introduces more
breakages to calls to static Block methods and new Block
instantiations.)
Changes to tests: system blocks don't set the $blockCreateAccount or
$mExipry block properties, so remove/change any tests that assume
they do.
Bug: T222737
Change-Id: I83bceb5e5049e254c90ace060f8f8fad44696c67
2019-03-18 22:09:49 +00:00
|
|
|
* @inheritDoc
|
2018-10-30 18:19:22 +00:00
|
|
|
*/
|
|
|
|
|
public function appliesToNamespace( $ns ) {
|
|
|
|
|
if ( $this->isSitewide() ) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Blocks do not apply to virtual namespaces.
|
|
|
|
|
if ( $ns < 0 ) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$restriction = $this->findRestriction( NamespaceRestriction::TYPE, $ns );
|
|
|
|
|
|
|
|
|
|
return (bool)$restriction;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
Separate Block into AbstractBlock, Block and SystemBlock
This commit splits the existing Block class into AbstractBlock, Block
and SystemBlock.
Before this patch, the Block class represents several types of
blocks, which can be separated into blocks stored in the database,
and temporary blocks created by the system. These are now
represented by Block and SystemBlock, which inherit from
AbstractBlock.
This lays the foundations for:
* enforcing block parameters from multiple blocks that apply to a
user/IP address
* improvements to the Block API, including the addition of services
Breaking changes: functions expecting a Block object should still
expect a Block object if it came from the database, but other
functions may now need to expect an AbstractBlock or SystemBlock
object. (Note that an alternative naming scheme, in which the
abstract class is called Block and the subclasses are DatabaseBlock
and SystemBlock, avoids this breakage. However, it introduces more
breakages to calls to static Block methods and new Block
instantiations.)
Changes to tests: system blocks don't set the $blockCreateAccount or
$mExipry block properties, so remove/change any tests that assume
they do.
Bug: T222737
Change-Id: I83bceb5e5049e254c90ace060f8f8fad44696c67
2019-03-18 22:09:49 +00:00
|
|
|
* @inheritDoc
|
2018-10-30 18:19:22 +00:00
|
|
|
*/
|
|
|
|
|
public function appliesToPage( $pageId ) {
|
|
|
|
|
if ( $this->isSitewide() ) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If the pageId is not over zero, the block cannot apply to it.
|
|
|
|
|
if ( $pageId <= 0 ) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$restriction = $this->findRestriction( PageRestriction::TYPE, $pageId );
|
|
|
|
|
|
|
|
|
|
return (bool)$restriction;
|
|
|
|
|
}
|
|
|
|
|
|
Introduce infrastructure for partial blocks for actions
This adds a new type of block restriction for actions, which extends
AbstractRestriction. Like page and namespace restrictions, action
restrictions are stored in the ipblocks_restrictions table.
Blockable actions are defined in a BlockActionInfo service, with a
method for getting all the blockable actions, getAllBlockActions.
Action blocks are checked for in PermissionManager::checkUserBlock
using DatabaseBlock::appliesToRight. To make this work, this patch
also removes the 'edit' case from AbstractBlock::appliesToRight,
which always returned true. This was incorrect, as blocks do not
always apply to edit, so cases that called appliesToRight('edit')
were fixed before this commit. appliesToRight('edit') now returns
null (i.e. unsure), which is correct because it is not possible to
determine whether a block applies to editing a particular page
without knowing what that page is, and appliesToRight doesn't know
that page.
There are some flags on sitewide blocks that predate partial blocks,
which block particular actions: 'createaccount' and 'sendemail'.
These are still handled in AbstractBlock::appliesToRight, and are
still checked for separately in the peripheral components.
The feature flag $wgEnablePartialActionBlocks must set to true to
enable partial action blocks.
Bug: T279556
Bug: T6995
Change-Id: I17962bb7c4247a12c722e7bc6bcaf8c36efd8600
2021-04-26 23:07:17 +00:00
|
|
|
/**
|
|
|
|
|
* @inheritDoc
|
|
|
|
|
*/
|
|
|
|
|
public function appliesToRight( $right ) {
|
|
|
|
|
// Temporarily access service container until the feature flag is removed: T280532
|
|
|
|
|
$config = MediaWikiServices::getInstance()->getMainConfig();
|
|
|
|
|
|
|
|
|
|
$res = parent::appliesToRight( $right );
|
|
|
|
|
|
|
|
|
|
if ( !$res && $config->get( 'EnablePartialActionBlocks' ) ) {
|
|
|
|
|
$blockActions = MediaWikiServices::getInstance()->getBlockActionInfo()
|
|
|
|
|
->getAllBlockActions();
|
|
|
|
|
|
|
|
|
|
if ( isset( $blockActions[$right] ) ) {
|
|
|
|
|
$restriction = $this->findRestriction(
|
|
|
|
|
ActionRestriction::TYPE,
|
|
|
|
|
$blockActions[$right]
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// $res may be null or false. This should be preserved if there is no restriction.
|
|
|
|
|
if ( $restriction ) {
|
|
|
|
|
$res = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $res;
|
|
|
|
|
}
|
|
|
|
|
|
2018-10-30 18:19:22 +00:00
|
|
|
/**
|
|
|
|
|
* Find Restriction by type and value.
|
|
|
|
|
*
|
|
|
|
|
* @param string $type
|
|
|
|
|
* @param int $value
|
|
|
|
|
* @return Restriction|null
|
|
|
|
|
*/
|
|
|
|
|
private function findRestriction( $type, $value ) {
|
|
|
|
|
$restrictions = $this->getRestrictions();
|
|
|
|
|
foreach ( $restrictions as $restriction ) {
|
|
|
|
|
if ( $restriction->getType() !== $type ) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ( $restriction->getValue() === $value ) {
|
|
|
|
|
return $restriction;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return null;
|
|
|
|
|
}
|
2019-03-13 16:08:15 +00:00
|
|
|
|
2019-04-11 19:54:10 +00:00
|
|
|
/**
|
|
|
|
|
* Get a BlockRestrictionStore instance
|
|
|
|
|
*
|
|
|
|
|
* @return BlockRestrictionStore
|
|
|
|
|
*/
|
2021-07-22 03:11:47 +00:00
|
|
|
private function getBlockRestrictionStore(): BlockRestrictionStore {
|
2021-09-28 17:21:37 +00:00
|
|
|
// TODO: get rid of global state here
|
|
|
|
|
return MediaWikiServices::getInstance()
|
|
|
|
|
->getBlockRestrictionStoreFactory()
|
|
|
|
|
->getBlockRestrictionStore( $this->getWikiId() );
|
2019-04-11 19:54:10 +00:00
|
|
|
}
|
2019-09-05 21:25:33 +00:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @inheritDoc
|
|
|
|
|
*/
|
2021-09-26 19:51:13 +00:00
|
|
|
public function getBy( $wikiId = self::LOCAL ): int {
|
|
|
|
|
$this->deprecateInvalidCrossWiki( $wikiId, '1.38' );
|
|
|
|
|
return ( $this->blocker ) ? $this->blocker->getId( $wikiId ) : 0;
|
2019-09-05 21:25:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @inheritDoc
|
|
|
|
|
*/
|
|
|
|
|
public function getByName() {
|
|
|
|
|
return ( $this->blocker ) ? $this->blocker->getName() : '';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get the user who implemented this block
|
|
|
|
|
*
|
2021-03-05 16:44:14 +00:00
|
|
|
* @return UserIdentity|null user object or null. May be a foreign user.
|
2019-09-05 21:25:33 +00:00
|
|
|
*/
|
2021-03-05 16:44:14 +00:00
|
|
|
public function getBlocker(): ?UserIdentity {
|
2019-09-05 21:25:33 +00:00
|
|
|
return $this->blocker;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Set the user who implemented (or will implement) this block
|
|
|
|
|
*
|
2021-10-03 20:46:20 +00:00
|
|
|
* @param UserIdentity $user
|
2019-09-05 21:25:33 +00:00
|
|
|
*/
|
|
|
|
|
public function setBlocker( $user ) {
|
2021-03-05 16:44:14 +00:00
|
|
|
if ( !$user->isRegistered() &&
|
2020-08-05 20:11:30 +00:00
|
|
|
MediaWikiServices::getInstance()->getUserNameUtils()->isUsable( $user->getName() )
|
|
|
|
|
) {
|
2019-12-20 11:57:40 +00:00
|
|
|
// Temporarily log some block details to debug T192964
|
|
|
|
|
$logger = LoggerFactory::getInstance( 'BlockManager' );
|
|
|
|
|
$logger->warning(
|
|
|
|
|
'Blocker is neither a local user nor an invalid username',
|
|
|
|
|
[
|
|
|
|
|
'blocker' => (string)$user,
|
2021-09-26 19:51:13 +00:00
|
|
|
'blockId' => $this->getId( $this->getWikiId() ),
|
2019-12-20 11:57:40 +00:00
|
|
|
]
|
|
|
|
|
);
|
2021-09-26 19:51:13 +00:00
|
|
|
throw new InvalidArgumentException(
|
2019-09-05 21:25:33 +00:00
|
|
|
'Blocker must be a local user or a name that cannot be a local user'
|
|
|
|
|
);
|
|
|
|
|
}
|
2022-01-22 23:14:33 +00:00
|
|
|
$this->assertWiki( $user->getWikiId() );
|
2019-09-05 21:25:33 +00:00
|
|
|
$this->blocker = $user;
|
|
|
|
|
}
|
2021-09-26 19:51:13 +00:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param int $i Specific or virtual (DB_PRIMARY/DB_REPLICA) server index
|
|
|
|
|
* @return IDatabase
|
|
|
|
|
*/
|
|
|
|
|
private function getDBConnection( int $i ) {
|
|
|
|
|
return MediaWikiServices::getInstance()
|
|
|
|
|
->getDBLoadBalancerFactory()
|
|
|
|
|
->getMainLB( $this->getWikiId() )
|
2022-01-20 18:38:30 +00:00
|
|
|
->getConnectionRef( $i, [], $this->getWikiId() );
|
2021-09-26 19:51:13 +00:00
|
|
|
}
|
2003-09-01 13:13:56 +00:00
|
|
|
}
|
2019-05-13 14:18:07 +00:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @deprecated since 1.34
|
|
|
|
|
*/
|
|
|
|
|
class_alias( DatabaseBlock::class, 'Block' );
|