Merge "Overhaul Blockautopromote action"

This commit is contained in:
jenkins-bot 2019-08-07 23:03:08 +00:00 committed by Gerrit Code Review
commit 1fa5eef94c
10 changed files with 151 additions and 32 deletions

View file

@ -74,7 +74,9 @@
"abusefilter/create": "AbuseFilterModifyLogFormatter",
"abusefilterprivatedetails/access": "LogFormatter",
"suppress/hide-afl": "AbuseFilterSuppressLogFormatter",
"suppress/unhide-afl": "AbuseFilterSuppressLogFormatter"
"suppress/unhide-afl": "AbuseFilterSuppressLogFormatter",
"rights/blockautopromote": "AbuseFilterRightsLogFormatter",
"rights/restoreautopromote": "AbuseFilterRightsLogFormatter"
},
"ActionFilteredLogs": {
"abusefilter": {
@ -123,6 +125,7 @@
"AbuseLogHitFormatter": "includes/AbuseLogHitFormatter.php",
"AbuseFilterModifyLogFormatter": "includes/AbuseFilterModifyLogFormatter.php",
"AbuseFilterSuppressLogFormatter": "includes/AbuseFilterSuppressLogFormatter.php",
"AbuseFilterRightsLogFormatter": "includes/AbuseFilterRightsLogFormatter.php",
"AbuseFilterViewList": "includes/Views/AbuseFilterViewList.php",
"AbuseFilterPager": "includes/pagers/AbuseFilterPager.php",
"GlobalAbuseFilterPager": "includes/pagers/GlobalAbuseFilterPager.php",
@ -376,7 +379,7 @@
"value": 10000,
"description": "Number of action that determines when to reset profiling stats."
},
"AbuseFilterRangeBlockSize" : {
"AbuseFilterRangeBlockSize": {
"value": {
"IPv4": 16,
"IPv6": 19

View file

@ -18,6 +18,7 @@
"abusefilter-blocker": "Abuse filter",
"abusefilter-blockreason": "Automatically blocked by abuse filter.\nDescription of matched rule: $1",
"abusefilter-degroupreason": "Rights automatically stripped by abuse filter.\nRule description: $1",
"abusefilter-blockautopromotereason": "Autopromotion automatically delayed by abuse filter.\nRule description: $1",
"abusefilter-accountreserved": "This account name is reserved for use by the abuse filter.",
"right-abusefilter-modify": "Modify abuse filters",
"right-abusefilter-view": "View abuse filters",
@ -110,7 +111,11 @@
"log-action-filter-abusefilter-create": "New filter creation",
"log-action-filter-abusefilter-modify": "Filter modification",
"log-action-filter-suppress-abuselog": "Abuse log suppression",
"log-action-filter-rights-blockautopromote": "Autopromote block",
"log-action-filter-rights-restoreautopromote": "Autopromote restore",
"logentry-abusefilterprivatedetails-access": "$1 {{GENDER:$2|accessed}} private details for $3",
"logentry-rights-blockautopromote": "$1 {{GENDER:$2|blocked}} the autopromotion of {{GENDER:$4|$3}} for 5 days",
"logentry-rights-restoreautopromote": "$1 {{GENDER:$2|restored}} the autopromotion capability of {{GENDER:$4|$3}}",
"abusefilterprivatedetails-log-name": "AbuseFilter private details access log",
"abusefilter-management": "Abuse filter management",
"abusefilter-list": "All filters",
@ -162,6 +167,7 @@
"abusefilter-tools-reautoconfirm": "Restore autoconfirmed status",
"abusefilter-tools-reautoconfirm-user": "User:",
"abusefilter-tools-reautoconfirm-submit": "Re-autoconfirm",
"abusefilter-tools-restoreautopromote": "Autopromotion restored via AbuseFilter tools.",
"abusefilter-reautoconfirm-none": "That user has not had {{GENDER:$1|his|her|their}} autoconfirmed status suspended.",
"abusefilter-reautoconfirm-notallowed": "You are not allowed to restore autoconfirmed status.",
"abusefilter-reautoconfirm-done": "Account's autoconfirmed status has been restored",

View file

@ -52,6 +52,7 @@
"abusefilter-blocker": "Username of reserved user for abuse filter actions.",
"abusefilter-blockreason": "Message given to user because of a triggered filter. Parameters:\n* $1 is a filter description\n* $2 is the filter id",
"abusefilter-degroupreason": "Used as log entry when removal of the user from all privileged groups performed by Abuse filter. Parameters:\n* $1 is the filter description (reason)\n* $2 is the filter id",
"abusefilter-blockautopromotereason": "Used as log entry when delaying the autopromotion of a user. Parameters:\n* $1 is the filter description (reason)\n* $2 is the filter id",
"abusefilter-accountreserved": "Message given when trying to register a reserved account name for AbuseFilter actions.",
"right-abusefilter-modify": "{{doc-right|abusefilter-modify}}",
"right-abusefilter-view": "{{doc-right|abusefilter-view}}",
@ -144,7 +145,11 @@
"log-action-filter-abusefilter-create": "{{doc-log-action-filter-action|abusefilter|create}}",
"log-action-filter-abusefilter-modify": "{{doc-log-action-filter-action|abusefilter|modify}}",
"log-action-filter-suppress-abuselog": "{{doc-log-action-filter-action|suppress|abuselog}}",
"log-action-filter-rights-blockautopromote": "{{doc-log-action-filter-action|rights|blockautopromote}}",
"log-action-filter-rights-restoreautopromote": "{{doc-log-action-filter-action|rights|restoreautopromote}}",
"logentry-abusefilterprivatedetails-access": "This message is for a log entry. Parameters:\n* $1 User who accessed the private logs\n* $2 User who accessed the private logs (used for gender)\n* $3 The log entry of which private details were accessed",
"logentry-rights-blockautopromote": "Message used in rights log entries when AbuseFilter delayed the autopromotion of a user. Parameters:\n* $1 The filter user\n* $2 Same as $1 but for gender support\n* $3 User whose autopromotion was delayed\n* $4 Same user as $3, but used for gender",
"logentry-rights-restoreautopromote": "Message used in rights log entries when autopromotion capability of a user. Parameters:\n* $1 The user who restored the rights\n* $2 Same as $1 but for gender support\n* $3 User whose autopromotion status was restored\n* $4 Same user as $3, but used for gender",
"abusefilterprivatedetails-log-name": "Log name",
"abusefilter-management": "Title of [[Special:AbuseFilter]]",
"abusefilter-list": "Used as HTML <code><nowiki><h2></nowiki></code> heading.\n\nFollowed by the fieldset label {{msg-mw|Abusefilter-list-options}}.",
@ -196,6 +201,7 @@
"abusefilter-tools-reautoconfirm": "Fieldset legend for a form to add a user to the autoconfirmed group again.",
"abusefilter-tools-reautoconfirm-user": "Field label. See {{msg-mw|group-autoconfirmed}} for concept translation.\n{{Identical|User}}",
"abusefilter-tools-reautoconfirm-submit": "Submit button text to add a user to the autoconfirmed user group. See {{msg-mw|group-autoconfirmed}} for concept translation.",
"abusefilter-tools-restoreautopromote": "Message displayed in the logs when a user restores the autopromotion status of another user using the form on Special:AbuseFilter/tools.",
"abusefilter-reautoconfirm-none": "{{doc-singularthey}}\nError text in case a user has not had their autoconfirmed status revoked. See {{msg-mw|group-autoconfirmed}} for concept translation.\n\nParameters:\n* $1 - the target user name used for GENDER",
"abusefilter-reautoconfirm-notallowed": "Error text when trying to perform an action the user cannot perform. See {{msg-mw|group-autoconfirmed}} for concept translation.",
"abusefilter-reautoconfirm-done": "See {{msg-mw|group-autoconfirmed}} for concept translation.\n* $1 is the target user name (optional, used for GENDER).",
@ -476,7 +482,7 @@
"abusefilter-action-tag": "{{doc-abusefilter-action}}\n\nThe edit or change can be 'tagged' with a particular tag, which will be shown on Recent Changes, contributions, logs, new pages, history, and everywhere else. \n\nThis is a verb in the imperative form.\n\n{{Identical|Tag}}",
"abusefilter-action-throttle": "{{doc-abusefilter-action}}",
"abusefilter-action-warn": "{{doc-abusefilter-action}}",
"abusefilter-action-blockautopromote": "{{doc-abusefilter-action}}\n\n'''Revoking auto-promoted groups'''\n\nTo '''block autopromote''' means that actions matching the filter will cause the user in question to be barred from receiving any extra groups from $wgAutoPromote for a period ranging from 3 to 7 days (random). \nAdditional information available: https://www.mediawiki.org/wiki/Extension:AbuseFilter/Actions",
"abusefilter-action-blockautopromote": "{{doc-abusefilter-action}}\n\n'''Revoking auto-promoted groups'''\n\nTo '''block autopromote''' means that actions matching the filter will cause the user in question to be barred from receiving any extra groups from $wgAutoPromote for 5 days. \nAdditional information available: https://www.mediawiki.org/wiki/Extension:AbuseFilter/Actions",
"abusefilter-action-block": "{{doc-abusefilter-action}}\n\nUsers matching the filter will be blocked indefinitely, with a descriptive block summary indicating the rule that was triggered.\n\nThis is a verb.\n{{Identical|Block}}",
"abusefilter-action-degroup": "{{doc-abusefilter-action}}\n\n'''Removing from privileged groups'''\n\nUsers matching the filter will be '''removed from all privileged groups''' (sysop, bureaucrat, etc). A descriptive summary will be used, detailing the rule that was triggered. \nAdditional information: https://www.mediawiki.org/wiki/Extension:AbuseFilter/Actions",
"abusefilter-action-rangeblock": "{{doc-abusefilter-action}}\n\n'''Range-block'''\n\nSomewhat of a 'nuclear option', the entire /16 range from which the rule was triggered will be blocked for 24 hours.\n\nThis is a verb in the imperative form.",

View file

@ -895,6 +895,36 @@ class AbuseFilter {
return $cache->makeKey( 'abusefilter', 'rules', $group );
}
/**
* Unblocks autopromotion for the given user
*
* @param User $target
* @param User $performer
* @param string $msg The message to show in the log
* @return bool True on success, false on failure
*/
public static function unblockAutopromote( User $target, User $performer, $msg ) {
$key = self::autoPromoteBlockKey( $target );
$stash = MediaWikiServices::getInstance()->getMainObjectStash();
if ( !$stash->get( $key ) ) {
// Probably we already removed it
return false;
}
$stash->delete( $key );
$logEntry = new ManualLogEntry( 'rights', 'restoreautopromote' );
$logEntry->setTarget( Title::makeTitle( NS_USER, $target->getName() ) );
$logEntry->setComment( $msg );
// These parameters are unused in our message, but some parts of the code check for them
$logEntry->setParameters( [
'4::oldgroups' => [],
'5::newgroups' => []
] );
$logEntry->setPerformer( $performer );
$logEntry->publish( $logEntry->insert() );
return true;
}
/**
* @param User $user
* @return string

View file

@ -38,6 +38,15 @@ class AbuseFilterHooks {
// Message: log-action-filter-suppress-abuselog
[ 'abuselog' => [ 'hide-afl', 'unhide-afl' ] ]
);
$wgActionFilteredLogs['rights'] = array_merge(
$wgActionFilteredLogs['rights'],
// Messages: log-action-filter-rights-blockautopromote,
// log-action-filter-rights-restoreautopromote
[
'blockautopromote' => [ 'blockautopromote' ],
'restoreautopromote' => [ 'restoreautopromote' ]
]
);
}
/**

View file

@ -0,0 +1,15 @@
<?php
class AbuseFilterRightsLogFormatter extends LogFormatter {
/**
* @return string
*/
protected function getMessageKey() {
$subtype = $this->entry->getSubtype();
// Messages that can be used here:
// * logentry-rights-blockautopromote
// * logentry-rights-restoreautopromote
return "logentry-rights-$subtype";
}
}

View file

@ -958,12 +958,28 @@ class AbuseFilterRunner {
break;
case 'blockautopromote':
if ( !$this->user->isAnon() ) {
// Block for 3-7 days.
$blockPeriod = (int)mt_rand( 3 * 86400, 7 * 86400 );
// Block for 5 days.
MediaWikiServices::getInstance()->getMainObjectStash()->set(
AbuseFilter::autoPromoteBlockKey( $this->user ), true, $blockPeriod
AbuseFilter::autoPromoteBlockKey( $this->user ), true, 5 * 86400
);
$logEntry = new ManualLogEntry( 'rights', 'blockautopromote' );
$logEntry->setPerformer( AbuseFilter::getFilterUser() );
$logEntry->setTarget( $this->user->getUserPage() );
// These parameters are unused in our message, but some parts of the code check for them
$logEntry->setParameters( [
'4::oldgroups' => [],
'5::newgroups' => []
] );
$logEntry->setComment(
wfMessage(
'abusefilter-blockautopromotereason',
$ruleDescription,
$ruleNumber
)->inContentLanguage()->text()
);
$logEntry->publish( $logEntry->insert() );
$message = [
'abusefilter-autopromote-blocked',
$ruleDescription,

View file

@ -1,7 +1,6 @@
<?php
use MediaWiki\Block\DatabaseBlock;
use MediaWiki\MediaWikiServices;
class AbuseFilterViewRevert extends AbuseFilterView {
/**
@ -295,10 +294,12 @@ class AbuseFilterViewRevert extends AbuseFilterView {
$logEntry->publish( $logEntry->insert() );
return true;
case 'blockautopromote':
MediaWikiServices::getInstance()->getMainObjectStash()->delete(
AbuseFilter::autoPromoteBlockKey( User::newFromId( $result['userid'] ) )
);
return true;
$target = User::newFromId( $result['userid'] );
$msg = $this->msg(
'abusefilter-revert-reason', $this->mPage->mFilter, $this->mReason
)->inContentLanguage()->text();
return AbuseFilter::unblockAutopromote( $target, $this->getUser(), $msg );
case 'degroup':
// Pull the user's groups from the vars.
$oldGroups = $result['vars']->getVar( 'user_groups' )->toNative();

View file

@ -1,7 +1,5 @@
<?php
use MediaWiki\MediaWikiServices;
class ApiAbuseFilterUnblockAutopromote extends ApiBase {
/**
* @see ApiBase::execute()
@ -10,9 +8,9 @@ class ApiAbuseFilterUnblockAutopromote extends ApiBase {
$this->checkUserRightsAny( 'abusefilter-modify' );
$params = $this->extractRequestParams();
$user = User::newFromName( $params['user'] );
$target = User::newFromName( $params['user'] );
if ( $user === false ) {
if ( $target === false ) {
$encParamName = $this->encodeParamName( 'user' );
$this->dieWithError(
[ 'apierror-baduser', $encParamName, wfEscapeWikiText( $params['user'] ) ],
@ -20,16 +18,15 @@ class ApiAbuseFilterUnblockAutopromote extends ApiBase {
);
}
$key = AbuseFilter::autoPromoteBlockKey( $user );
$stash = MediaWikiServices::getInstance()->getMainObjectStash();
if ( !$stash->get( $key ) ) {
$this->dieWithError( [ 'abusefilter-reautoconfirm-none', $user->getName() ], 'notsuspended' );
$msg = $this->msg( 'abusefilter-tools-restoreautopromote' )->inContentLanguage()->text();
$res = AbuseFilter::unblockAutopromote( $target, $this->getUser(), $msg );
if ( $res === false ) {
$this->dieWithError( [ 'abusefilter-reautoconfirm-none', $target->getName() ], 'notsuspended' );
}
$stash->delete( $key );
$res = [ 'user' => $params['user'] ];
$this->getResult()->addValue( null, $this->getModuleName(), $res );
$finalResult = [ 'user' => $params['user'] ];
$this->getResult()->addValue( null, $this->getModuleName(), $finalResult );
}
/**

View file

@ -293,6 +293,13 @@ class AbuseFilterConsequencesTest extends MediaWikiTestCase {
'actions' => [
'disallow' => [],
]
],
21 => [
'af_pattern' => '1==1',
'af_public_comments' => 'Dangerous filter',
'actions' => [
'blockautopromote' => []
]
]
];
// phpcs:enable Generic.Files.LineLength
@ -636,6 +643,8 @@ class AbuseFilterConsequencesTest extends MediaWikiTestCase {
* @return array [ expected consequences, actual consequences ]
*/
private function checkConsequences( $result, $actionParams, $consequences ) {
global $wgAbuseFilterRestrictions;
$expectedErrors = [];
$testErrorMessage = false;
foreach ( $consequences as $consequence => $ids ) {
@ -705,6 +714,15 @@ class AbuseFilterConsequencesTest extends MediaWikiTestCase {
break;
case 'throttle':
throw new UnexpectedValueException( 'Use self::testThrottleConsequence to test throttling' );
case 'blockautopromote':
// Aborts the hook with 'abusefilter-autopromote-blocked' error and prevent promotion.
$expectedErrors['blockautopromote'][] = 'abusefilter-autopromote-blocked';
$key = MediaWikiServices::getInstance()->getMainObjectStash()->get(
AbuseFilter::autoPromoteBlockKey( self::$mUser ) );
if ( !$key ) {
$testErrorMessage = "The key for blocking autopromotion wasn't set.";
}
break;
default:
throw new UnexpectedValueException( 'Consequence not recognized.' );
}
@ -715,15 +733,20 @@ class AbuseFilterConsequencesTest extends MediaWikiTestCase {
}
}
// Errors have a priority order
$expected = $expectedErrors['warn'] ?? $expectedErrors['degroup'] ??
$expectedErrors['block'] ?? $expectedErrors['disallow'] ?? null;
if ( isset( $expectedErrors['degroup'] ) && $expected === $expectedErrors['degroup'] &&
isset( $expectedErrors['block'] ) ) {
// Degroup and block warning can be fired together
$expected = array_merge( $expectedErrors['degroup'], $expectedErrors['block'] );
} elseif ( !is_array( $expected ) ) {
$expected = (array)$expected;
if ( array_intersect_key( $expectedErrors, array_filter( $wgAbuseFilterRestrictions ) ) ) {
$filteredExpected = array_intersect_key(
$expectedErrors,
array_filter( $wgAbuseFilterRestrictions )
);
$expected = [];
foreach ( $filteredExpected as $values ) {
$expected = array_merge( $expected, $values );
}
} else {
$expected = $expectedErrors['warn'] ?? $expectedErrors['disallow'] ?? null;
if ( !is_array( $expected ) ) {
$expected = (array)$expected;
}
}
$errors = $result->getErrors();
@ -736,6 +759,8 @@ class AbuseFilterConsequencesTest extends MediaWikiTestCase {
}
}
sort( $expected );
sort( $actual );
return [ $expected, $actual ];
}
@ -909,6 +934,17 @@ class AbuseFilterConsequencesTest extends MediaWikiTestCase {
],
[]
],
'Test for blockautopromote action.' => [
[ 21 ],
[
'action' => 'edit',
'target' => 'Rainbow',
'oldText' => '',
'newText' => '...',
'summary' => ''
],
[ 'blockautopromote' => [ 21 ] ],
],
[
[ 8, 10 ],
[