Page Menu
Home
WickedGov Phorge
Search
Configure Global Search
Log In
Files
F1429154
CheckUserLookupUtils.php
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
8 KB
Referenced Files
None
Subscribers
None
CheckUserLookupUtils.php
View Options
<?php
namespace
MediaWiki\CheckUser\Services
;
use
LogPage
;
use
ManualLogEntry
;
use
MediaWiki\CheckUser\CheckUserQueryInterface
;
use
MediaWiki\Config\ServiceOptions
;
use
MediaWiki\Revision\ArchivedRevisionLookup
;
use
MediaWiki\Revision\RevisionLookup
;
use
MediaWiki\Revision\RevisionRecord
;
use
MediaWiki\Title\Title
;
use
MediaWiki\User\UserIdentity
;
use
Psr\Log\LoggerInterface
;
use
RuntimeException
;
use
stdClass
;
use
Wikimedia\IPUtils
;
use
Wikimedia\Rdbms\IConnectionProvider
;
use
Wikimedia\Rdbms\IExpression
;
use
Wikimedia\Rdbms\IReadableDatabase
;
class
CheckUserLookupUtils
{
public
const
CONSTRUCTOR_OPTIONS
=
[
'CheckUserCIDRLimit'
];
private
ServiceOptions
$options
;
private
IReadableDatabase
$dbr
;
private
RevisionLookup
$revisionLookup
;
private
ArchivedRevisionLookup
$archivedRevisionLookup
;
private
LoggerInterface
$logger
;
public
function
__construct
(
ServiceOptions
$options
,
IConnectionProvider
$dbProvider
,
RevisionLookup
$revisionLookup
,
ArchivedRevisionLookup
$archivedRevisionLookup
,
LoggerInterface
$logger
)
{
$options
->
assertRequiredOptions
(
self
::
CONSTRUCTOR_OPTIONS
);
$this
->
options
=
$options
;
$this
->
dbr
=
$dbProvider
->
getReplicaDatabase
();
$this
->
revisionLookup
=
$revisionLookup
;
$this
->
archivedRevisionLookup
=
$archivedRevisionLookup
;
$this
->
logger
=
$logger
;
}
/**
* Returns whether the given target IP address or IP address range is valid. This applies the range limits
* imposed by $wgCheckUserCIDRLimit.
*
* @param string $target an IP address or CIDR range
* @return bool
*/
public
function
isValidIPOrRange
(
string
$target
):
bool
{
$CIDRLimit
=
$this
->
options
->
get
(
'CheckUserCIDRLimit'
);
if
(
IPUtils
::
isValidRange
(
$target
)
)
{
[
$ip
,
$range
]
=
explode
(
'/'
,
$target
,
2
);
return
!(
(
IPUtils
::
isIPv4
(
$ip
)
&&
$range
<
$CIDRLimit
[
'IPv4'
]
)
||
(
IPUtils
::
isIPv6
(
$ip
)
&&
$range
<
$CIDRLimit
[
'IPv6'
]
)
);
}
return
IPUtils
::
isValid
(
$target
);
}
/**
* Utility for getting the CIDR limits, e.g. for error messages. Avoids the need to pass the config to
* the caller.
*
* @return int[]
*/
public
function
getRangeLimit
()
{
return
$this
->
options
->
get
(
'CheckUserCIDRLimit'
);
}
/**
* Get the WHERE conditions as an IExpression object which can be used to filter results for a provided
* IP address / range and optionally for the XFF IP.
*
* @param string $target an IP address or CIDR range
* @param bool $xfor True if searching on XFF IPs by IP address / range
* @param string $table The table which will be used in the query these WHERE conditions
* are used (array of valid options in self::RESULT_TABLES).
* @return IExpression|null IExpression for valid conditions, null if invalid
*/
public
function
getIPTargetExpr
(
string
$target
,
bool
$xfor
,
string
$table
):
?
IExpression
{
$columnName
=
$this
->
getIpHexColumn
(
$xfor
,
$table
);
return
$this
->
getIPTargetExprForColumn
(
$target
,
$columnName
);
}
/**
* Get the WHERE conditions as an IExpression object which can be used to filter results for a provided
* IP address / range, given a database column name.
*
* @param string $target an IP address or CIDR range
* @param string $columnName The column which will be used in the query these WHERE conditions
* are used (must be a database column holding an IP address in hex format).
* @return IExpression|null IExpression for valid conditions, null if invalid
*/
public
function
getIPTargetExprForColumn
(
string
$target
,
string
$columnName
):
?
IExpression
{
if
(
!
$this
->
isValidIPOrRange
(
$target
)
)
{
// Return null if the target is not a valid IP address or range
return
null
;
}
if
(
IPUtils
::
isValidRange
(
$target
)
)
{
// If the target is a range, then the conditions should include all rows where the IP hex
// is between the start and end (inclusive).
[
$start
,
$end
]
=
IPUtils
::
parseRange
(
$target
);
return
$this
->
dbr
->
expr
(
$columnName
,
'>='
,
$start
)->
and
(
$columnName
,
'<='
,
$end
);
}
else
{
// If the target is a single IP, then the ip hex column should be equal to the hex of the target IP.
return
$this
->
dbr
->
expr
(
$columnName
,
'='
,
IPUtils
::
toHex
(
$target
)
);
}
}
/**
* Gets the column name for the IP hex column based
* on the value for $xfor and a given $table.
*
* @param bool $xfor Whether the IPs being searched through are XFF IPs.
* @param string $table The table selecting results from (array of valid
* options in CheckUserQueryInterface::RESULT_TABLES).
* @return string
*/
private
function
getIpHexColumn
(
bool
$xfor
,
string
$table
):
string
{
$type
=
$xfor
?
'xff'
:
'ip'
;
return
CheckUserQueryInterface
::
RESULT_TABLE_TO_PREFIX
[
$table
]
.
$type
.
'_hex'
;
}
/**
* Gets the name for the index for a given table.
*
* @param bool|null $xfor Whether the IPs being searched through are XFF IPs. Null if the target is a username.
* @param string $table The table this index should apply to (list of valid options
* in CheckUserQueryInterface::RESULT_TABLES).
* @return string
*/
public
function
getIndexName
(
?
bool
$xfor
,
string
$table
):
string
{
// So that a code search can find existing usages:
// cuc_actor_ip_time, cule_actor_ip_time, cupe_actor_ip_time, cuc_xff_hex_time, cuc_ip_hex_time,
// cule_xff_hex_time, cule_ip_hex_time, cupe_xff_hex_time, cupe_ip_hex_time
if
(
$xfor
===
null
)
{
return
CheckUserQueryInterface
::
RESULT_TABLE_TO_PREFIX
[
$table
]
.
'actor_ip_time'
;
}
else
{
$type
=
$xfor
?
'xff'
:
'ip'
;
return
CheckUserQueryInterface
::
RESULT_TABLE_TO_PREFIX
[
$table
]
.
$type
.
'_hex_time'
;
}
}
/**
* Get a ManualLogEntry instance for the given row from either cu_log_event or
* cu_private_event table. The column names are expected to not include the table prefix and the row should include
* the following columns for this method to work:
* - log_type
* - log_action
* - log_params
* - log_deleted
* - title (or page/page_id)
* - namespace (not needed if title is not defined but page/page_id is)
* - timestamp
*
* Do not call this method for rows from cu_changes.
*
* @param stdClass $row The database row
* @param UserIdentity $user The user who is the performer for this row.
* @return ManualLogEntry Which can be used via the LogFormatter to generate action text.
*/
public
function
getManualLogEntryFromRow
(
stdClass
$row
,
UserIdentity
$user
):
ManualLogEntry
{
$logEntry
=
new
ManualLogEntry
(
$row
->
log_type
,
$row
->
log_action
);
if
(
$row
->
log_params
!==
null
)
{
// Suppress E_NOTICE from PHP's unserialize if the log parameters are legacy parameters.
// This is similar to DatabaseLogEntry::getParameters.
// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged
$parsedLogParams
=
@
ManualLogEntry
::
extractParams
(
$row
->
log_params
);
if
(
$parsedLogParams
===
false
)
{
// Use the LogPage::extractParams method to extract the log parameters as they are probably
// legacy parameters.
$parsedLogParams
=
LogPage
::
extractParams
(
$row
->
log_params
);
$logEntry
->
setLegacy
(
true
);
}
$logEntry
->
setParameters
(
$parsedLogParams
);
}
$logEntry
->
setPerformer
(
$user
);
if
(
isset
(
$row
->
title
)
&&
$row
->
title
)
{
$logEntry
->
setTarget
(
Title
::
makeTitle
(
$row
->
namespace
,
$row
->
title
)
);
}
elseif
(
// page_id is the column name for Special:CheckUser. page is the column name for the CheckUser API.
(
isset
(
$row
->
page
)
&&
$row
->
page
)
||
(
isset
(
$row
->
page_id
)
&&
$row
->
page_id
)
)
{
$logEntry
->
setTarget
(
Title
::
newFromID
(
$row
->
page
??
$row
->
page_id
)
);
}
$logEntry
->
setTimestamp
(
$row
->
timestamp
);
$logEntry
->
setDeleted
(
$row
->
log_deleted
);
return
$logEntry
;
}
/**
* Get a RevisionRecord instance for the given row from cu_changes table. This should not be called for rows from
* the cu_log_event or cu_private_event tables.
*
* @param stdClass $row
* @return ?RevisionRecord null if no revision is found, otherwise the RevisionRecord.
*/
public
function
getRevisionRecordFromRow
(
stdClass
$row
):
?
RevisionRecord
{
$revRecord
=
$this
->
revisionLookup
->
getRevisionById
(
$row
->
this_oldid
);
if
(
!
$revRecord
)
{
$revRecord
=
$this
->
archivedRevisionLookup
->
getArchivedRevisionRecord
(
null
,
$row
->
this_oldid
);
}
if
(
$revRecord
===
null
)
{
// This shouldn't happen, CheckUser points to a revision that isn't in revision nor archive table?
$this
->
logger
->
warning
(
"Couldn't fetch revision with this_oldid as {old_id}"
,
[
'old_id'
=>
$row
->
this_oldid
,
'exception'
=>
new
RuntimeException
]
);
}
return
$revRecord
;
}
}
File Metadata
Details
Attached
Mime Type
text/x-php
Expires
Sat, May 16, 17:10 (10 h, 7 m)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
9d/68/9ed01205406c824f9c0be9475dda
Default Alt Text
CheckUserLookupUtils.php (8 KB)
Attached To
Mode
rMWPROD MediaWiki Production
Attached
Detach File
Event Timeline
Log In to Comment