mediawiki-extensions-AbuseF.../includes/KeywordsManager.php
Dreamy Jazz 85022190e5 Add user_type variable
Why:
* An AbuseFilter variable is needed that allows filters to determine
  what type the current user is. That is, whether the user is an
  IP address, temporary account, named user or external user.
* Currently filters implement this by inspecting the value in
  the 'user_name' variable, but this is likely to break when
  temporary accounts are enabled as IPs would be hidden.
* Giving a dedicated variable that indicates the type of the user
  allows filters to work out this information without having to
  know the specific username of the user before performing the
  check.

What:
* Add the 'user_type' variable which is lazily computed. It can have
  the value 'named', 'temp', 'ip' or 'external' depending on the
  type of the user. If the user does not match any of these, then
  the value is 'unknown'.
* Replace call to deprecated User::newFromIdentity with a use of the
  UserFactory service that is dependency injected.
* Add and update tests to ensure consistent test coverage.

Bug: T357615
Change-Id: Ifffa891879e7e49d2430a0330116b34c5a03049d
2024-03-07 10:38:25 +00:00

422 lines
16 KiB
PHP

<?php
namespace MediaWiki\Extension\AbuseFilter;
use MediaWiki\Extension\AbuseFilter\Hooks\AbuseFilterHookRunner;
/**
* This service can be used to manage the list of keywords recognized by the Parser
*/
class KeywordsManager {
public const SERVICE_NAME = 'AbuseFilterKeywordsManager';
private const BUILDER_VALUES = [
'op-arithmetic' => [
// Generates abusefilter-edit-builder-op-arithmetic-addition
'+' => 'addition',
// Generates abusefilter-edit-builder-op-arithmetic-subtraction
'-' => 'subtraction',
// Generates abusefilter-edit-builder-op-arithmetic-multiplication
'*' => 'multiplication',
// Generates abusefilter-edit-builder-op-arithmetic-divide
'/' => 'divide',
// Generates abusefilter-edit-builder-op-arithmetic-modulo
'%' => 'modulo',
// Generates abusefilter-edit-builder-op-arithmetic-pow
'**' => 'pow'
],
'op-comparison' => [
// Generates abusefilter-edit-builder-op-comparison-equal
'==' => 'equal',
// Generates abusefilter-edit-builder-op-comparison-equal-strict
'===' => 'equal-strict',
// Generates abusefilter-edit-builder-op-comparison-notequal
'!=' => 'notequal',
// Generates abusefilter-edit-builder-op-comparison-notequal-strict
'!==' => 'notequal-strict',
// Generates abusefilter-edit-builder-op-comparison-lt
'<' => 'lt',
// Generates abusefilter-edit-builder-op-comparison-gt
'>' => 'gt',
// Generates abusefilter-edit-builder-op-comparison-lte
'<=' => 'lte',
// Generates abusefilter-edit-builder-op-comparison-gte
'>=' => 'gte'
],
'op-bool' => [
// Generates abusefilter-edit-builder-op-bool-not
'!' => 'not',
// Generates abusefilter-edit-builder-op-bool-and
'&' => 'and',
// Generates abusefilter-edit-builder-op-bool-or
'|' => 'or',
// Generates abusefilter-edit-builder-op-bool-xor
'^' => 'xor'
],
'misc' => [
// Generates abusefilter-edit-builder-misc-in
'in' => 'in',
// Generates abusefilter-edit-builder-misc-contains
'contains' => 'contains',
// Generates abusefilter-edit-builder-misc-like
'like' => 'like',
// Generates abusefilter-edit-builder-misc-stringlit
'""' => 'stringlit',
// Generates abusefilter-edit-builder-misc-rlike
'rlike' => 'rlike',
// Generates abusefilter-edit-builder-misc-irlike
'irlike' => 'irlike',
// Generates abusefilter-edit-builder-misc-tern
'cond ? iftrue : iffalse' => 'tern',
// Generates abusefilter-edit-builder-misc-cond
'if cond then iftrue else iffalse end' => 'cond',
// Generates abusefilter-edit-builder-misc-cond-short
'if cond then iftrue end' => 'cond-short',
],
'funcs' => [
// Generates abusefilter-edit-builder-funcs-length
'length(string)' => 'length',
// Generates abusefilter-edit-builder-funcs-lcase
'lcase(string)' => 'lcase',
// Generates abusefilter-edit-builder-funcs-ucase
'ucase(string)' => 'ucase',
// Generates abusefilter-edit-builder-funcs-ccnorm
'ccnorm(string)' => 'ccnorm',
// Generates abusefilter-edit-builder-funcs-ccnorm-contains-any
'ccnorm_contains_any(haystack,needle1,needle2,..)' => 'ccnorm-contains-any',
// Generates abusefilter-edit-builder-funcs-ccnorm-contains-all
'ccnorm_contains_all(haystack,needle1,needle2,..)' => 'ccnorm-contains-all',
// Generates abusefilter-edit-builder-funcs-rmdoubles
'rmdoubles(string)' => 'rmdoubles',
// Generates abusefilter-edit-builder-funcs-specialratio
'specialratio(string)' => 'specialratio',
// Generates abusefilter-edit-builder-funcs-norm
'norm(string)' => 'norm',
// Generates abusefilter-edit-builder-funcs-count
'count(needle,haystack)' => 'count',
// Generates abusefilter-edit-builder-funcs-rcount
'rcount(needle,haystack)' => 'rcount',
// Generates abusefilter-edit-builder-funcs-get_matches
'get_matches(needle,haystack)' => 'get_matches',
// Generates abusefilter-edit-builder-funcs-rmwhitespace
'rmwhitespace(text)' => 'rmwhitespace',
// Generates abusefilter-edit-builder-funcs-rmspecials
'rmspecials(text)' => 'rmspecials',
// Generates abusefilter-edit-builder-funcs-ip_in_range
'ip_in_range(ip, range)' => 'ip_in_range',
// Generates abusefilter-edit-builder-funcs-ip_in_ranges
'ip_in_ranges(ip, range1, range2, ...)' => 'ip_in_ranges',
// Generates abusefilter-edit-builder-funcs-contains-any
'contains_any(haystack,needle1,needle2,...)' => 'contains-any',
// Generates abusefilter-edit-builder-funcs-contains-all
'contains_all(haystack,needle1,needle2,...)' => 'contains-all',
// Generates abusefilter-edit-builder-funcs-equals-to-any
'equals_to_any(haystack,needle1,needle2,...)' => 'equals-to-any',
// Generates abusefilter-edit-builder-funcs-substr
'substr(subject, offset, length)' => 'substr',
// Generates abusefilter-edit-builder-funcs-strpos
'strpos(haystack, needle)' => 'strpos',
// Generates abusefilter-edit-builder-funcs-str_replace
'str_replace(subject, search, replace)' => 'str_replace',
// Generates abusefilter-edit-builder-funcs-str_replace_regexp
'str_replace_regexp(subject, search, replace)' => 'str_replace_regexp',
// Generates abusefilter-edit-builder-funcs-rescape
'rescape(string)' => 'rescape',
// Generates abusefilter-edit-builder-funcs-set_var
'set_var(var,value)' => 'set_var',
// Generates abusefilter-edit-builder-funcs-sanitize
'sanitize(string)' => 'sanitize',
],
'vars' => [
// Generates abusefilter-edit-builder-vars-timestamp
'timestamp' => 'timestamp',
// Generates abusefilter-edit-builder-vars-accountname
'accountname' => 'accountname',
// Generates abusefilter-edit-builder-vars-action
'action' => 'action',
// Generates abusefilter-edit-builder-vars-addedlines
'added_lines' => 'addedlines',
// Generates abusefilter-edit-builder-vars-delta
'edit_delta' => 'delta',
// Generates abusefilter-edit-builder-vars-diff
'edit_diff' => 'diff',
// Generates abusefilter-edit-builder-vars-newsize
'new_size' => 'newsize',
// Generates abusefilter-edit-builder-vars-oldsize
'old_size' => 'oldsize',
// Generates abusefilter-edit-builder-vars-new-content-model
'new_content_model' => 'new-content-model',
// Generates abusefilter-edit-builder-vars-old-content-model
'old_content_model' => 'old-content-model',
// Generates abusefilter-edit-builder-vars-removedlines
'removed_lines' => 'removedlines',
// Generates abusefilter-edit-builder-vars-summary
'summary' => 'summary',
// Generates abusefilter-edit-builder-vars-page-id
'page_id' => 'page-id',
// Generates abusefilter-edit-builder-vars-page-ns
'page_namespace' => 'page-ns',
// Generates abusefilter-edit-builder-vars-page-title
'page_title' => 'page-title',
// Generates abusefilter-edit-builder-vars-page-prefixedtitle
'page_prefixedtitle' => 'page-prefixedtitle',
// Generates abusefilter-edit-builder-vars-page-age
'page_age' => 'page-age',
// Generates abusefilter-edit-builder-vars-movedfrom-id
'moved_from_id' => 'movedfrom-id',
// Generates abusefilter-edit-builder-vars-movedfrom-ns
'moved_from_namespace' => 'movedfrom-ns',
// Generates abusefilter-edit-builder-vars-movedfrom-title
'moved_from_title' => 'movedfrom-title',
// Generates abusefilter-edit-builder-vars-movedfrom-prefixedtitle
'moved_from_prefixedtitle' => 'movedfrom-prefixedtitle',
// Generates abusefilter-edit-builder-vars-movedfrom-age
'moved_from_age' => 'movedfrom-age',
// Generates abusefilter-edit-builder-vars-movedto-id
'moved_to_id' => 'movedto-id',
// Generates abusefilter-edit-builder-vars-movedto-ns
'moved_to_namespace' => 'movedto-ns',
// Generates abusefilter-edit-builder-vars-movedto-title
'moved_to_title' => 'movedto-title',
// Generates abusefilter-edit-builder-vars-movedto-prefixedtitle
'moved_to_prefixedtitle' => 'movedto-prefixedtitle',
// Generates abusefilter-edit-builder-vars-movedto-age
'moved_to_age' => 'movedto-age',
// Generates abusefilter-edit-builder-vars-user-editcount
'user_editcount' => 'user-editcount',
// Generates abusefilter-edit-builder-vars-user-age
'user_age' => 'user-age',
// Generates abusefilter-edit-builder-vars-user-name
'user_name' => 'user-name',
// Generates abusefilter-edit-builder-vars-user-type
'user_type' => 'user-type',
// Generates abusefilter-edit-builder-vars-user-groups
'user_groups' => 'user-groups',
// Generates abusefilter-edit-builder-vars-user-rights
'user_rights' => 'user-rights',
// Generates abusefilter-edit-builder-vars-user-blocked
'user_blocked' => 'user-blocked',
// Generates abusefilter-edit-builder-vars-user-emailconfirm
'user_emailconfirm' => 'user-emailconfirm',
// Generates abusefilter-edit-builder-vars-old-wikitext
'old_wikitext' => 'old-wikitext',
// Generates abusefilter-edit-builder-vars-new-wikitext
'new_wikitext' => 'new-wikitext',
// Generates abusefilter-edit-builder-vars-added-links
'added_links' => 'added-links',
// Generates abusefilter-edit-builder-vars-removed-links
'removed_links' => 'removed-links',
// Generates abusefilter-edit-builder-vars-all-links
'all_links' => 'all-links',
// Generates abusefilter-edit-builder-vars-new-pst
'new_pst' => 'new-pst',
// Generates abusefilter-edit-builder-vars-diff-pst
'edit_diff_pst' => 'diff-pst',
// Generates abusefilter-edit-builder-vars-addedlines-pst
'added_lines_pst' => 'addedlines-pst',
// Generates abusefilter-edit-builder-vars-new-text
'new_text' => 'new-text',
// Generates abusefilter-edit-builder-vars-new-html
'new_html' => 'new-html',
// Generates abusefilter-edit-builder-vars-restrictions-edit
'page_restrictions_edit' => 'restrictions-edit',
// Generates abusefilter-edit-builder-vars-restrictions-move
'page_restrictions_move' => 'restrictions-move',
// Generates abusefilter-edit-builder-vars-restrictions-create
'page_restrictions_create' => 'restrictions-create',
// Generates abusefilter-edit-builder-vars-restrictions-upload
'page_restrictions_upload' => 'restrictions-upload',
// Generates abusefilter-edit-builder-vars-recent-contributors
'page_recent_contributors' => 'recent-contributors',
// Generates abusefilter-edit-builder-vars-first-contributor
'page_first_contributor' => 'first-contributor',
// Generates abusefilter-edit-builder-vars-movedfrom-restrictions-edit
'moved_from_restrictions_edit' => 'movedfrom-restrictions-edit',
// Generates abusefilter-edit-builder-vars-movedfrom-restrictions-move
'moved_from_restrictions_move' => 'movedfrom-restrictions-move',
// Generates abusefilter-edit-builder-vars-movedfrom-restrictions-create
'moved_from_restrictions_create' => 'movedfrom-restrictions-create',
// Generates abusefilter-edit-builder-vars-movedfrom-restrictions-upload
'moved_from_restrictions_upload' => 'movedfrom-restrictions-upload',
// Generates abusefilter-edit-builder-vars-movedfrom-recent-contributors
'moved_from_recent_contributors' => 'movedfrom-recent-contributors',
// Generates abusefilter-edit-builder-vars-movedfrom-first-contributor
'moved_from_first_contributor' => 'movedfrom-first-contributor',
// Generates abusefilter-edit-builder-vars-movedto-restrictions-edit
'moved_to_restrictions_edit' => 'movedto-restrictions-edit',
// Generates abusefilter-edit-builder-vars-movedto-restrictions-move
'moved_to_restrictions_move' => 'movedto-restrictions-move',
// Generates abusefilter-edit-builder-vars-movedto-restrictions-create
'moved_to_restrictions_create' => 'movedto-restrictions-create',
// Generates abusefilter-edit-builder-vars-movedto-restrictions-upload
'moved_to_restrictions_upload' => 'movedto-restrictions-upload',
// Generates abusefilter-edit-builder-vars-movedto-recent-contributors
'moved_to_recent_contributors' => 'movedto-recent-contributors',
// Generates abusefilter-edit-builder-vars-movedto-first-contributor
'moved_to_first_contributor' => 'movedto-first-contributor',
// Generates abusefilter-edit-builder-vars-old-links
'old_links' => 'old-links',
// Generates abusefilter-edit-builder-vars-file-sha1
'file_sha1' => 'file-sha1',
// Generates abusefilter-edit-builder-vars-file-size
'file_size' => 'file-size',
// Generates abusefilter-edit-builder-vars-file-mime
'file_mime' => 'file-mime',
// Generates abusefilter-edit-builder-vars-file-mediatype
'file_mediatype' => 'file-mediatype',
// Generates abusefilter-edit-builder-vars-file-width
'file_width' => 'file-width',
// Generates abusefilter-edit-builder-vars-file-height
'file_height' => 'file-height',
// Generates abusefilter-edit-builder-vars-file-bits-per-channel
'file_bits_per_channel' => 'file-bits-per-channel',
// Generates abusefilter-edit-builder-vars-wiki-name
'wiki_name' => 'wiki-name',
// Generates abusefilter-edit-builder-vars-wiki-language
'wiki_language' => 'wiki-language',
],
];
/** @var array Old vars which aren't in use anymore */
private const DISABLED_VARS = [
'old_text' => 'old-text',
'old_html' => 'old-html',
'minor_edit' => 'minor-edit'
];
private const DEPRECATED_VARS = [
'article_text' => 'page_title',
'article_prefixedtext' => 'page_prefixedtitle',
'article_namespace' => 'page_namespace',
'article_articleid' => 'page_id',
'article_restrictions_edit' => 'page_restrictions_edit',
'article_restrictions_move' => 'page_restrictions_move',
'article_restrictions_create' => 'page_restrictions_create',
'article_restrictions_upload' => 'page_restrictions_upload',
'article_recent_contributors' => 'page_recent_contributors',
'article_first_contributor' => 'page_first_contributor',
'moved_from_text' => 'moved_from_title',
'moved_from_prefixedtext' => 'moved_from_prefixedtitle',
'moved_from_articleid' => 'moved_from_id',
'moved_to_text' => 'moved_to_title',
'moved_to_prefixedtext' => 'moved_to_prefixedtitle',
'moved_to_articleid' => 'moved_to_id',
];
/** @var string[][] Final list of builder values */
private $builderValues;
/** @var string[] Final list of deprecated vars */
private $deprecatedVars;
/** @var AbuseFilterHookRunner */
private $hookRunner;
/**
* @param AbuseFilterHookRunner $hookRunner
*/
public function __construct( AbuseFilterHookRunner $hookRunner ) {
$this->hookRunner = $hookRunner;
}
/**
* @return array
*/
public function getDisabledVariables(): array {
return self::DISABLED_VARS;
}
/**
* @return array
*/
public function getDeprecatedVariables(): array {
if ( $this->deprecatedVars === null ) {
$this->deprecatedVars = self::DEPRECATED_VARS;
$this->hookRunner->onAbuseFilter_deprecatedVariables( $this->deprecatedVars );
}
return $this->deprecatedVars;
}
/**
* @return array
*/
public function getBuilderValues(): array {
if ( $this->builderValues === null ) {
$this->builderValues = self::BUILDER_VALUES;
$this->hookRunner->onAbuseFilter_builder( $this->builderValues );
}
return $this->builderValues;
}
/**
* @param string $name
* @return bool
*/
public function isVarDisabled( string $name ): bool {
return array_key_exists( $name, self::DISABLED_VARS );
}
/**
* @param string $name
* @return bool
*/
public function isVarDeprecated( string $name ): bool {
return array_key_exists( $name, $this->getDeprecatedVariables() );
}
/**
* @param string $name
* @return bool
*/
public function isVarInUse( string $name ): bool {
return array_key_exists( $name, $this->getVarsMappings() );
}
/**
* Check whether the given name corresponds to a known variable.
* @param string $name
* @return bool
*/
public function varExists( string $name ): bool {
return $this->isVarInUse( $name ) ||
$this->isVarDisabled( $name ) ||
$this->isVarDeprecated( $name );
}
/**
* Get the message for a builtin variable; takes deprecated variables into account.
* Returns null for non-builtin variables.
*
* @param string $var
* @return string|null
*/
public function getMessageKeyForVar( string $var ): ?string {
if ( !$this->varExists( $var ) ) {
return null;
}
if ( $this->isVarDeprecated( $var ) ) {
$var = $this->getDeprecatedVariables()[$var];
}
$key = self::DISABLED_VARS[$var] ??
$this->getVarsMappings()[$var];
return "abusefilter-edit-builder-vars-$key";
}
/**
* @return array
*/
public function getVarsMappings(): array {
return $this->getBuilderValues()['vars'];
}
/**
* Get a list of core variables, i.e. variables defined in AbuseFilter (ignores hooks).
* You usually want to use getVarsMappings(), not this one.
* @return string[]
*/
public function getCoreVariables(): array {
return array_keys( self::BUILDER_VALUES['vars'] );
}
}