Page Menu
Home
WickedGov Phorge
Search
Configure Global Search
Log In
Files
F1432173
GlobalBlockLookup.php
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
24 KB
Referenced Files
None
Subscribers
None
GlobalBlockLookup.php
View Options
<?php
namespace
MediaWiki\Extension\GlobalBlocking\Services
;
use
InvalidArgumentException
;
use
Liuggio\StatsdClient\Factory\StatsdDataFactoryInterface
;
use
MediaWiki\Config\ServiceOptions
;
use
MediaWiki\Context\RequestContext
;
use
MediaWiki\Extension\GlobalBlocking\GlobalBlock
;
use
MediaWiki\User\CentralId\CentralIdLookup
;
use
MediaWiki\User\TempUser\TempUserConfig
;
use
MediaWiki\User\User
;
use
MediaWiki\User\UserFactory
;
use
MediaWiki\User\UserIdentity
;
use
stdClass
;
use
Wikimedia\IPUtils
;
use
Wikimedia\Rdbms\FakeResultWrapper
;
use
Wikimedia\Rdbms\IExpression
;
use
Wikimedia\Rdbms\IResultWrapper
;
use
Wikimedia\Rdbms\LikeValue
;
/**
* Allows looking up global blocks in the globalblocks table.
*
* @since 1.42
*/
class
GlobalBlockLookup
{
public
const
CONSTRUCTOR_OPTIONS
=
[
'GlobalBlockingAllowedRanges'
,
'GlobalBlockingBlockXFF'
,
'GlobalBlockingCIDRLimit'
,
'GlobalBlockingCentralWikiContentLanguage'
,
];
private
const
TYPE_USER
=
1
;
private
const
TYPE_IP
=
2
;
private
const
TYPE_RANGE
=
3
;
private
const
TYPE_AUTOBLOCK
=
4
;
/** @var int Flag to ignore blocks on IP addresses which are marked as anon-only. */
public
const
SKIP_SOFT_IP_BLOCKS
=
1
;
/** @var int Flag to ignore all blocks on IP addresses. */
public
const
SKIP_IP_BLOCKS
=
2
;
/** @var int Flag to skip checking if the blocks that affect a target are locally disabled. */
public
const
SKIP_LOCAL_DISABLE_CHECK
=
4
;
/** @var int Flag to skip the excluding of IP blocks in the GlobalBlockingAllowedRanges config. */
public
const
SKIP_ALLOWED_RANGES_CHECK
=
8
;
/** @var int Flag to ignore all autoblocks. Is implicitly set if ::SKIP_IP_BLOCKS is set. */
public
const
SKIP_AUTOBLOCKS
=
16
;
private
ServiceOptions
$options
;
private
GlobalBlockingConnectionProvider
$globalBlockingConnectionProvider
;
private
StatsdDataFactoryInterface
$statsdFactory
;
private
CentralIdLookup
$centralIdLookup
;
private
GlobalBlockLocalStatusLookup
$globalBlockLocalStatusLookup
;
private
TempUserConfig
$tempUserConfig
;
private
UserFactory
$userFactory
;
private
array
$getUserBlockDetailsCache
=
[];
public
function
__construct
(
ServiceOptions
$options
,
GlobalBlockingConnectionProvider
$globalBlockingConnectionProvider
,
StatsdDataFactoryInterface
$statsdFactory
,
CentralIdLookup
$centralIdLookup
,
GlobalBlockLocalStatusLookup
$globalBlockLocalStatusLookup
,
TempUserConfig
$tempUserConfig
,
UserFactory
$userFactory
)
{
$options
->
assertRequiredOptions
(
self
::
CONSTRUCTOR_OPTIONS
);
$this
->
options
=
$options
;
$this
->
globalBlockingConnectionProvider
=
$globalBlockingConnectionProvider
;
$this
->
statsdFactory
=
$statsdFactory
;
$this
->
centralIdLookup
=
$centralIdLookup
;
$this
->
globalBlockLocalStatusLookup
=
$globalBlockLocalStatusLookup
;
$this
->
tempUserConfig
=
$tempUserConfig
;
$this
->
userFactory
=
$userFactory
;
}
/**
* Given a target and the IP address being used to make the request, get an existing
* GlobalBlock object that applies to the target or IP address being used. If no
* block exists, then this method returns null.
*
* @param User $user Filter for GlobalBlock objects that target this user or IP address
* @param string|null $ip The IP address being used by the user, used to apply global blocks
* on IPs or IP ranges that are not anon-only. Specifying null when $user is an IP address
* and is not the session user will cause the value to be autogenerated.
* @return GlobalBlock|null The GlobalBlock that applies to the given user or IP, or null if no block applies.
*/
public
function
getUserBlock
(
User
$user
,
?
string
$ip
):
?
GlobalBlock
{
$details
=
$this
->
getUserBlockDetails
(
$user
,
$ip
);
if
(
$details
[
'block'
]
)
{
$row
=
$details
[
'block'
];
if
(
$row
->
gb_autoblock_parent_id
)
{
// If the block is an autoblock, then replace the reason used for the autoblock with the autoblock
// reason but in the content language.
// This means that the user seeing the block notice will get the reason for the block in a language
// that they understand (as opposed to seeing it in English).
$parentBlock
=
$this
->
globalBlockingConnectionProvider
->
getReplicaGlobalBlockingDatabase
()
->
newSelectQueryBuilder
()
->
select
(
[
'gb_reason'
,
'gb_target_central_id'
,
'gb_id'
]
)
->
from
(
'globalblocks'
)
->
where
(
[
'gb_id'
=>
$row
->
gb_autoblock_parent_id
]
)
->
caller
(
__METHOD__
)
->
fetchRow
();
$row
->
gb_reason
=
$this
->
getAutoblockReason
(
$parentBlock
,
true
);
}
return
GlobalBlock
::
newFromRow
(
$row
,
$details
[
'xff'
]
);
}
return
null
;
}
/**
* Get the reason for the autoblock suitable for use in a "globalblocks" table database row, or for
* display to the user in a global auto block notice.
*
* @since 1.43
* @param stdClass $block The globalblock row of the parent block that is causing the autoblock.
* Should include at least the gb_id, gb_target_central_id, and gb_reason fields.
* @param bool $forDisplayInBlockNotice Whether the autoblock reason will be displayed in a block
* notice? If so, then the reason is always set to use the content language of the wiki.
* @return string
*/
public
function
getAutoblockReason
(
stdClass
$block
,
bool
$forDisplayInBlockNotice
):
string
{
// Hide the target username of the parent block if it is not publicly viewable. This is to prevent leaking
// hidden usernames. Users can refer to the parent global block via the ID in this case.
$target
=
$this
->
centralIdLookup
->
nameFromCentralId
(
$block
->
gb_target_central_id
)
??
''
;
if
(
$target
)
{
$autoBlockReasonMsg
=
wfMessage
(
'globalblocking-autoblocker'
,
$target
,
$block
->
gb_reason
);
}
else
{
$autoBlockReasonMsg
=
wfMessage
(
'globalblocking-autoblocker-hidden-block'
,
$block
->
gb_id
);
}
if
(
$this
->
options
->
get
(
'GlobalBlockingCentralWikiContentLanguage'
)
&&
!
$forDisplayInBlockNotice
)
{
$autoBlockReasonMsg
->
inLanguage
(
$this
->
options
->
get
(
'GlobalBlockingCentralWikiContentLanguage'
)
);
}
else
{
$autoBlockReasonMsg
->
inContentLanguage
();
}
return
$autoBlockReasonMsg
->
plain
();
}
/**
* Gets a key to access ->getUserBlockDetailsCache for a given user and IP.
*
* @param UserIdentity $userIdentity
* @param string|null $ip
* @return string The associated cache key
*/
private
function
getUserBlockDetailsCacheKey
(
UserIdentity
$userIdentity
,
?
string
$ip
):
string
{
return
$userIdentity
->
getName
()
.
(
$ip
??
''
);
}
/**
* Add the $result to the instance cache under the username of the given $user.
*
* @param array $result
* @param UserIdentity $user
* @param string|null $ip
* @return array The value of $result
*/
private
function
addToUserBlockDetailsCache
(
array
$result
,
UserIdentity
$user
,
?
string
$ip
):
array
{
$this
->
getUserBlockDetailsCache
[
$this
->
getUserBlockDetailsCacheKey
(
$user
,
$ip
)]
=
$result
;
return
$result
;
}
/**
* Get the cached result of ::getUserBlockDetails for the given user and IP.
*
* @param UserIdentity $userIdentity
* @param string|null $ip
* @return array|null Array if the result is cached, null if there is no cached result
*/
protected
function
getUserBlockDetailsCacheResult
(
UserIdentity
$userIdentity
,
?
string
$ip
):
?
array
{
return
$this
->
getUserBlockDetailsCache
[
$this
->
getUserBlockDetailsCacheKey
(
$userIdentity
,
$ip
)]
??
null
;
}
/**
* Given a target and the IP address being used to make the request, get the
* most specific block that applies along with a human readable error message
* associated with the block. If no block exists, this returns an array with
* no block and an empty array of error messages.
*
* @param User $user See ::getUserBlock. Note this may not be the session user.
* @param string|null $ip See ::getUserBlock.
* @return array An array with the key 'block' for the DB row of the block that applies. May include a
* xff key if the block was applied due to the X-Forwarded-For header value.
* @phan-return array{block:stdClass|null,xff:bool}
*/
private
function
getUserBlockDetails
(
User
$user
,
?
string
$ip
):
array
{
// Check first if the instance cache has the result.
$cachedResult
=
$this
->
getUserBlockDetailsCacheResult
(
$user
,
$ip
);
if
(
$cachedResult
!==
null
)
{
return
$cachedResult
;
}
$this
->
statsdFactory
->
increment
(
'global_blocking.get_user_block'
);
// We have callers from different code paths which may leave $ip as null when providing an
// IP address as the $user where the IP address is not the session user. In this case, populate
// the $ip argument with the IP provided in $user to get all the blocks that apply to the IP.
$context
=
RequestContext
::
getMain
();
$isSessionUser
=
$user
->
equals
(
$context
->
getUser
()
);
if
(
$ip
===
null
&&
!
$isSessionUser
&&
IPUtils
::
isIPAddress
(
$user
->
getName
()
)
)
{
// Populate the IP for checking blocks against non-session users.
$ip
=
$user
->
getName
();
}
$flags
=
0
;
if
(
$user
->
isAllowedAny
(
'ipblock-exempt'
,
'globalblock-exempt'
)
)
{
// User is exempt from IP blocks.
$flags
|=
self
::
SKIP_IP_BLOCKS
;
}
if
(
$user
->
isNamed
()
)
{
// User is a named account, so skip anon-only (soft) IP blocks.
$flags
|=
self
::
SKIP_SOFT_IP_BLOCKS
;
}
$centralId
=
0
;
if
(
$user
->
isRegistered
()
)
{
$centralId
=
$this
->
centralIdLookup
->
centralIdFromLocalUser
(
$user
,
CentralIdLookup
::
AUDIENCE_RAW
);
}
$this
->
statsdFactory
->
increment
(
'global_blocking.get_user_block_db_query'
);
$block
=
$this
->
getGlobalBlockingBlock
(
$ip
,
$centralId
,
$flags
);
if
(
$block
)
{
return
$this
->
addToUserBlockDetailsCache
(
[
'block'
=>
$block
,
'xff'
=>
false
],
$user
,
$ip
);
}
// We should only check XFF blocks if we are checking blocks for the session user. The exception to this is
// that we should also check XFF blocks when the name is the temporary account placeholder, as this is used
// when a logged out user is making edit on a wiki with temporary accounts enabled (T353564).
if
(
$this
->
options
->
get
(
'GlobalBlockingBlockXFF'
)
&&
(
$isSessionUser
||
(
$this
->
tempUserConfig
->
isEnabled
()
&&
$this
->
userFactory
->
newTempPlaceholder
()->
equals
(
$user
)
)
)
)
{
$xffIps
=
$context
->
getRequest
()->
getHeader
(
'X-Forwarded-For'
);
if
(
$xffIps
)
{
$xffIps
=
array_map
(
'trim'
,
explode
(
','
,
$xffIps
)
);
// Always skip the allowed ranges check when checking the XFF IPs as the value of this header
// is easy to spoof.
$xffFlags
=
$flags
|
self
::
SKIP_ALLOWED_RANGES_CHECK
;
$block
=
$this
->
chooseMostSpecificBlock
(
$this
->
checkIpsForBlock
(
$xffIps
,
$xffFlags
),
$xffFlags
);
if
(
$block
!==
null
)
{
return
$this
->
addToUserBlockDetailsCache
(
[
'block'
=>
$block
,
'xff'
=>
true
],
$user
,
$ip
);
}
}
}
return
$this
->
addToUserBlockDetailsCache
(
[
'block'
=>
null
,
'xff'
=>
false
],
$user
,
$ip
);
}
/**
* Returns the ::TYPE_* constant for the given block.
*
* @param stdClass $block
* @return int
*/
private
function
getTargetType
(
stdClass
$block
):
int
{
if
(
$block
->
gb_autoblock_parent_id
)
{
return
self
::
TYPE_AUTOBLOCK
;
}
$target
=
$block
->
gb_address
;
if
(
IPUtils
::
isValid
(
$target
)
)
{
return
self
::
TYPE_IP
;
}
elseif
(
IPUtils
::
isValidRange
(
$target
)
)
{
return
self
::
TYPE_RANGE
;
}
else
{
return
self
::
TYPE_USER
;
}
}
/**
* Choose the most specific block from some combination of user, IP and IP range
* blocks. Decreasing order of specificity: IP > narrower IP range > wider IP
* range. A range that encompasses one IP address is ranked equally to a single IP.
*
* Note that DatabaseBlock::chooseBlocks chooses blocks in a different way.
*
* This is based on DatabaseBlock::chooseMostSpecificBlock
*
* @param IResultWrapper $blocks These should not include autoblocks or ID blocks
* @param int $flags The $flags provided. This method only checks for BLOCK_FLAG_SKIP_LOCAL_DISABLE_CHECK,
* and callers are in charge of checking for other relevant flags.
* @return stdClass|null The block with the most specific target
*/
private
function
chooseMostSpecificBlock
(
IResultWrapper
$blocks
,
int
$flags
):
?
stdClass
{
// 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
)
{
// Check for local whitelisting, unless the flag is set to skip the check.
if
(
!(
$flags
&
self
::
SKIP_LOCAL_DISABLE_CHECK
)
&&
$this
->
globalBlockLocalStatusLookup
->
isGlobalBlockLocallyDisabledForBlockApplication
(
$block
->
gb_id
)
)
{
continue
;
}
$target
=
$block
->
gb_address
;
$type
=
$this
->
getTargetType
(
$block
);
if
(
$type
==
self
::
TYPE_RANGE
)
{
// This is the number of bits that are allowed to vary in the block, give
// or take some floating point errors
$max
=
IPUtils
::
isIPv6
(
$target
)
?
128
:
32
;
[
$network
,
$bits
]
=
IPUtils
::
parseCIDR
(
$target
);
$size
=
$max
-
$bits
;
// Rank a range block covering a single IP equally with a single-IP block
$score
=
self
::
TYPE_RANGE
-
1
+
(
$size
/
$max
);
}
else
{
$score
=
$type
;
}
// Always prioritise blocks that deny account creation over all other blocks.
// This is done by adding the maximum possible score for a single block to the current block score
// (currently the score for an autoblock), such that blocks which don't disable account creation always
// have a higher score to those which disable account creation.
if
(
!
$block
->
gb_create_account
)
{
$score
+=
self
::
TYPE_AUTOBLOCK
;
}
if
(
$bestBlock
===
null
||
$score
<
$bestBlockScore
)
{
$bestBlockScore
=
$score
;
$bestBlock
=
$block
;
}
}
return
$bestBlock
;
}
/**
* Get the most specific row from the `globalblocks` table that applies to the given IP address
* or the central user.
*
* This does not check if the user is exempt from IP blocks. As such it should not be used to determine
* if a block should be applied to a user. Use ::getUserBlock for that.
*
* @param string|null $ip The IP address used by the user. If null, then no IP blocks will be checked.
* @param int $centralId The central ID of the user. 0 if the user is anonymous. Setting this as
* a boolean is soft deprecated and will be treated as 0.
* @param int $flags Flags to control the behavior of the block lookup
* @return stdClass|null The most specific row from the `globalblocks` table, or null if no row was found
*/
public
function
getGlobalBlockingBlock
(
?
string
$ip
,
int
$centralId
,
int
$flags
=
0
):
?
stdClass
{
$conds
=
$this
->
getGlobalBlockLookupConditions
(
$ip
,
$centralId
,
$flags
);
if
(
$conds
===
null
)
{
// No conditions, so don't perform the query and assume the user is not targeted by any block
return
null
;
}
$blocks
=
$this
->
globalBlockingConnectionProvider
->
getReplicaGlobalBlockingDatabase
()
->
newSelectQueryBuilder
()
->
select
(
self
::
selectFields
()
)
->
from
(
'globalblocks'
)
->
where
(
$conds
)
->
caller
(
__METHOD__
)
->
fetchResultSet
();
// Get the most specific block for the global blocks that apply to the user.
return
$this
->
chooseMostSpecificBlock
(
$blocks
,
$flags
);
}
/**
* Get the SQL WHERE conditions that allow looking up all blocks from the
* `globalblocks` table that apply to the given IP address or range.
*
* @param string $ip The IP address or range
* @deprecated Since 1.42. Use ::getGlobalBlockLookupConditions.
* @return IExpression
*/
public
function
getRangeCondition
(
string
$ip
):
IExpression
{
wfDeprecated
(
__METHOD__
,
'1.42'
);
// This method does not return null if an IP is provided and the allowed ranges check is skipped.
// @phan-suppress-next-line PhanTypeMismatchReturnNullable
return
$this
->
getGlobalBlockLookupConditions
(
$ip
,
0
,
self
::
SKIP_ALLOWED_RANGES_CHECK
);
}
/**
* Get the SQL WHERE conditions that allow looking up all blocks from the `globalblocks` table.
*
* @param ?string $ip The IP address or range. If null, then no IP blocks will be checked.
* @param int $centralId The central ID of the user. 0 if the user is anonymous and 0 will skip
* checking user specific blocks.
* @param int $flags Flags which control what conditions are returned. Ignores the
* ::BLOCK_FLAG_SKIP_LOCAL_DISABLE_CHECK flag and callers are expected to check if the block is
* locally disabled if this is needed.
* @return IExpression|null The conditions to be used in a SQL query to look up global blocks, or null if no valid
* conditions could be generated.
*/
public
function
getGlobalBlockLookupConditions
(
?
string
$ip
,
int
$centralId
=
0
,
int
$flags
=
0
):
?
IExpression
{
$dbr
=
$this
->
globalBlockingConnectionProvider
->
getReplicaGlobalBlockingDatabase
();
$ipExpr
=
null
;
$userExpr
=
null
;
if
(
$ip
!==
null
)
{
$sanitisedIp
=
IPUtils
::
sanitizeIP
(
$ip
);
if
(
!
IPUtils
::
isIPAddress
(
$ip
)
||
!
$sanitisedIp
)
{
// The provided IP is invalid, so throw.
throw
new
InvalidArgumentException
(
"Invalid IP address or range provided to GlobalBlockLookup::getGlobalBlockLookupConditions."
);
}
// Use the sanitised version of the IP address, incase an IPv4 address is provided that has leading 0s.
// If leading 0s are present, then IPUtils::parseRange will fail to parse the range properly.
$ip
=
$sanitisedIp
;
}
if
(
$ip
!==
null
&&
!(
$flags
&
self
::
SKIP_ALLOWED_RANGES_CHECK
)
)
{
$ranges
=
$this
->
options
->
get
(
'GlobalBlockingAllowedRanges'
);
foreach
(
$ranges
as
$range
)
{
if
(
IPUtils
::
isInRange
(
$ip
,
$range
)
)
{
// IP is in a range that is exempt from IP blocks, so treat the user as having
// global IP block exemption for this specific IP address
$flags
|=
self
::
SKIP_IP_BLOCKS
;
break
;
}
}
}
if
(
$ip
!==
null
&&
!(
$flags
&
self
::
SKIP_IP_BLOCKS
)
)
{
// If we have been provided an IP address or range in $ip, then
// add conditions to the query to lookup blocks that apply to the IP address / range.
[
$start
,
$end
]
=
IPUtils
::
parseRange
(
$ip
);
$chunk
=
$this
->
getIpFragment
(
$start
);
$ipExpr
=
$dbr
->
expr
(
'gb_range_start'
,
IExpression
::
LIKE
,
new
LikeValue
(
$chunk
,
$dbr
->
anyString
()
)
)
->
and
(
'gb_range_start'
,
'<='
,
$start
)
->
and
(
'gb_range_end'
,
'>='
,
$end
);
if
(
$flags
&
self
::
SKIP_SOFT_IP_BLOCKS
)
{
// If the flags say to skip soft IP blocks, then exclude blocks with gb_anon_only
// set to 1 (which should only be soft blocks on IP addresses or ranges).
$ipExpr
=
$ipExpr
->
and
(
'gb_anon_only'
,
'!='
,
1
);
}
if
(
$flags
&
self
::
SKIP_AUTOBLOCKS
)
{
$ipExpr
=
$ipExpr
->
and
(
'gb_autoblock_parent_id'
,
'='
,
0
);
}
}
if
(
$centralId
!==
0
)
{
// If we have been provided a non-zero central ID, then also look for blocks that target the
// given central ID.
$userExpr
=
$dbr
->
expr
(
'gb_target_central_id'
,
'='
,
$centralId
);
}
// Combine the IP conditions and user IExpressions
if
(
$userExpr
!==
null
&&
$ipExpr
!==
null
)
{
// If we have conditions for both the IP and the user, then combine them with an OR
// to allow selecting blocks that apply to either the IP or the user.
$targetExpr
=
$userExpr
->
orExpr
(
$ipExpr
);
}
elseif
(
$userExpr
!==
null
)
{
// If we only have conditions for the user, then use that IExpression.
$targetExpr
=
$userExpr
;
}
elseif
(
$ipExpr
!==
null
)
{
// If we only have conditions for the IP, then use that IExpression.
$targetExpr
=
$ipExpr
;
}
else
{
// No conditions, so don't perform the query otherwise we will select all blocks from the DB.
// In this case, we can assume the user or their IP is not affected by any global block.
return
null
;
}
// @todo expiry shouldn't be in this function
return
$dbr
->
expr
(
'gb_expiry'
,
'>'
,
$dbr
->
timestamp
()
)
->
andExpr
(
$targetExpr
);
}
/**
* Get the component of an IP address which is certain to be the same between an IP
* address and a range block containing that IP address.
*
* This mostly duplicates the logic in DatabaseStoreBlock::getIpFragment, but with the
* CIDR limit config being the GlobalBlocking extension specific one.
*
* @param string $hex Hexadecimal IP representation
* @return string
*/
private
function
getIpFragment
(
string
$hex
):
string
{
$blockCIDRLimit
=
$this
->
options
->
get
(
'GlobalBlockingCIDRLimit'
);
if
(
str_starts_with
(
$hex
,
'v6-'
)
)
{
return
'v6-'
.
substr
(
substr
(
$hex
,
3
),
0
,
(
int
)
floor
(
$blockCIDRLimit
[
'IPv6'
]
/
4
)
);
}
else
{
return
substr
(
$hex
,
0
,
(
int
)
floor
(
$blockCIDRLimit
[
'IPv4'
]
/
4
)
);
}
}
/**
* Find all rows from the `globalblocks` table that target at least one of
* the given IP addresses.
*
* Does not filter out locally disabled blocks. You probably want to pass the
* result to {@link self::chooseMostSpecificBlock}.
*
* @param string[] $ips The array of IP addresses to be checked
* @param int $flags Flags which control what blocks are returned.
* @return IResultWrapper Applicable blocks as rows from the `globalblocks` table
*/
private
function
checkIpsForBlock
(
array
$ips
,
int
$flags
=
0
):
IResultWrapper
{
if
(
$flags
&
self
::
SKIP_IP_BLOCKS
)
{
// If the flags say to skip IP blocks, then don't even make the query.
return
new
FakeResultWrapper
(
[]
);
}
$dbr
=
$this
->
globalBlockingConnectionProvider
->
getReplicaGlobalBlockingDatabase
();
$conds
=
[];
foreach
(
$ips
as
$ip
)
{
if
(
IPUtils
::
isValid
(
$ip
)
)
{
$ipConds
=
$this
->
getGlobalBlockLookupConditions
(
$ip
,
0
,
$flags
);
if
(
$ipConds
!==
null
)
{
$conds
[]
=
$ipConds
;
}
}
}
if
(
!
$conds
)
{
// No valid IPs provided so don't even make the query. Bug 59705
return
new
FakeResultWrapper
(
[]
);
}
return
$dbr
->
newSelectQueryBuilder
()
->
select
(
self
::
selectFields
()
)
->
from
(
'globalblocks'
)
->
where
(
$dbr
->
orExpr
(
$conds
)
)
->
caller
(
__METHOD__
)
->
fetchResultSet
();
}
/**
* Given a specific target, find the ID for the global block that applies to it.
* If no global block exists for this target, then this method returns 0.
*
* @param string $target The specific target which can be a username, IP address, range, or global block ID that
* may or may not exist. The target being specific means that if you provide a single IP which is covered by a
* range block, the range block will not be returned. This also means that autoblocks cannot be queried by the
* IP that they target. Use ::getGlobalBlockingBlock to include these blocks.
* @param int $dbtype Either DB_REPLICA or DB_PRIMARY.
* @return int
*/
public
function
getGlobalBlockId
(
string
$target
,
int
$dbtype
=
DB_REPLICA
):
int
{
if
(
$dbtype
===
DB_PRIMARY
)
{
$db
=
$this
->
globalBlockingConnectionProvider
->
getPrimaryGlobalBlockingDatabase
();
}
else
{
$db
=
$this
->
globalBlockingConnectionProvider
->
getReplicaGlobalBlockingDatabase
();
}
$queryBuilder
=
$db
->
newSelectQueryBuilder
()
->
select
(
'gb_id'
)
->
from
(
'globalblocks'
)
->
where
(
$db
->
expr
(
'gb_expiry'
,
'>'
,
$db
->
timestamp
()
)
);
$globalBlockId
=
self
::
isAGlobalBlockId
(
$target
);
if
(
$globalBlockId
)
{
$queryBuilder
->
where
(
[
'gb_id'
=>
$globalBlockId
]
);
}
elseif
(
IPUtils
::
isIPAddress
(
$target
)
)
{
$queryBuilder
->
where
(
[
'gb_address'
=>
$target
,
'gb_autoblock_parent_id'
=>
0
]
);
}
else
{
$centralId
=
$this
->
centralIdLookup
->
centralIdFromName
(
$target
,
CentralIdLookup
::
AUDIENCE_RAW
);
if
(
!
$centralId
)
{
// If we are looking up a block by a central ID of a user, then the user must have a central ID
// for a block to apply to them.
return
0
;
}
$queryBuilder
->
where
(
[
'gb_target_central_id'
=>
$centralId
]
);
}
return
(
int
)
$queryBuilder
->
caller
(
__METHOD__
)
->
fetchField
();
}
/**
* Determines if a given string is in the format of a global block ID.
*
* This method does not validate that the global block ID actually exists. Use
* {@link GlobalBlockLookup::getGlobalBlockId} for that.
*
* @param string $target The string to check
* @return int|false False if the string is not in the format of a global block ID, or the ID of the global
* block if it is in the format of a global block ID.
*/
public
static
function
isAGlobalBlockId
(
string
$target
)
{
$isTargetABlockId
=
preg_match
(
'/^#
\d
+$/'
,
$target
);
if
(
$isTargetABlockId
)
{
return
intval
(
substr
(
$target
,
1
)
);
}
return
false
;
}
/**
* @return string[] The fields needed to construct a GlobalBlock object
*/
public
static
function
selectFields
():
array
{
return
[
'gb_id'
,
'gb_address'
,
'gb_target_central_id'
,
'gb_by_central_id'
,
'gb_by_wiki'
,
'gb_reason'
,
'gb_timestamp'
,
'gb_anon_only'
,
'gb_expiry'
,
'gb_range_start'
,
'gb_range_end'
,
'gb_create_account'
,
'gb_enable_autoblock'
,
'gb_autoblock_parent_id'
,
];
}
}
File Metadata
Details
Attached
Mime Type
text/x-php
Expires
Sat, May 16, 21:29 (1 d, 5 h)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
47/8e/7496cfc8b98c2158152e6f721249
Default Alt Text
GlobalBlockLookup.php (24 KB)
Attached To
Mode
rMWPROD MediaWiki Production
Attached
Detach File
Event Timeline
Log In to Comment