Merge "Add ConsequencesLookup"

This commit is contained in:
jenkins-bot 2020-12-02 15:49:33 +00:00 committed by Gerrit Code Review
commit 753a0dc482
7 changed files with 171 additions and 80 deletions

View file

@ -184,6 +184,7 @@
"MediaWiki\\Extension\\AbuseFilter\\InvalidImportDataException": "includes/InvalidImportDataException.php",
"MediaWiki\\Extension\\AbuseFilter\\FilterStore": "includes/FilterStore.php",
"MediaWiki\\Extension\\AbuseFilter\\ConsequencesFactory": "includes/ConsequencesFactory.php",
"MediaWiki\\Extension\\AbuseFilter\\ConsequencesLookup": "includes/ConsequencesLookup.php",
"AFComputedVariable": "includes/AFComputedVariable.php",
"AFPData": "includes/parser/AFPData.php",
"AFPException": "includes/parser/AFPException.php",

View file

@ -5,7 +5,6 @@ use MediaWiki\Extension\AbuseFilter\Hooks\AbuseFilterHookRunner;
use MediaWiki\Extension\AbuseFilter\VariableGenerator\VariableGenerator;
use MediaWiki\Logger\LoggerFactory;
use MediaWiki\Revision\RevisionRecord;
use Wikimedia\Rdbms\IDatabase;
/**
* This class contains most of the business logic of AbuseFilter. It consists of
@ -170,86 +169,10 @@ class AbuseFilter {
/**
* @param string[] $filters
* @return array[][]
* @deprecated since 1.36 Use ConsequencesLookup
*/
public static function getConsequencesForFilters( $filters ) {
$globalFilters = [];
$localFilters = [];
foreach ( $filters as $filter ) {
list( $filterID, $global ) = self::splitGlobalName( $filter );
if ( $global ) {
$globalFilters[] = $filterID;
} else {
$localFilters[] = $filter;
}
}
// Load local filter info
$dbr = wfGetDB( DB_REPLICA );
// Retrieve the consequences.
$consequences = [];
if ( count( $localFilters ) ) {
$consequences = self::loadConsequencesFromDB( $dbr, $localFilters, '' );
}
if ( count( $globalFilters ) ) {
$consequences += self::loadConsequencesFromDB(
AbuseFilterServices::getCentralDBManager()->getConnection( DB_REPLICA ),
$globalFilters,
self::GLOBAL_FILTER_PREFIX
);
}
return $consequences;
}
/**
* @param IDatabase $dbr
* @param string[] $filters
* @param string $prefix
* @return array[][]
*/
private static function loadConsequencesFromDB( IDatabase $dbr, $filters, $prefix = '' ) {
$actionsByFilter = [];
foreach ( $filters as $filter ) {
$actionsByFilter[$prefix . $filter] = [];
}
$res = $dbr->select(
[ 'abuse_filter_action', 'abuse_filter' ],
'*',
[ 'af_id' => $filters ],
__METHOD__,
[],
[ 'abuse_filter_action' => [ 'LEFT JOIN', 'afa_filter=af_id' ] ]
);
// Categorise consequences by filter.
foreach ( $res as $row ) {
if ( $row->af_throttled
&& in_array( $row->afa_consequence, self::getDangerousActions() )
) {
// Don't do the action, just log
$logger = LoggerFactory::getInstance( 'AbuseFilter' );
$logger->info(
'Filter {filter_id} is throttled, skipping action: {action}',
[
'filter_id' => $row->af_id,
'action' => $row->afa_consequence
]
);
} elseif ( $row->afa_filter !== $row->af_id ) {
// We probably got a NULL, as it's a LEFT JOIN. Don't add it.
continue;
} else {
$actionsByFilter[$prefix . $row->afa_filter][$row->afa_consequence] =
array_filter( explode( "\n", $row->afa_parameters ) );
}
}
return $actionsByFilter;
return AbuseFilterServices::getConsequencesLookup()->getConsequencesForFilters( $filters );
}
/**

View file

@ -448,7 +448,8 @@ class AbuseFilterRunner {
* the errors and warnings to be shown to the user to explain the actions.
*/
protected function executeFilterActions( array $filters ) : Status {
$actionsByFilter = AbuseFilter::getConsequencesForFilters( $filters );
$consLookup = AbuseFilterServices::getConsequencesLookup();
$actionsByFilter = $consLookup->getConsequencesForFilters( $filters );
$consequences = $this->replaceArraysWithConsequences( $actionsByFilter );
$actionsToTake = $this->getFilteredConsequences( $consequences );
$actionsTaken = array_fill_keys( $filters, [] );

View file

@ -136,4 +136,11 @@ class AbuseFilterServices {
public static function getEditBoxBuilderFactory() : EditBoxBuilderFactory {
return MediaWikiServices::getInstance()->getService( EditBoxBuilderFactory::SERVICE_NAME );
}
/**
* @return ConsequencesLookup
*/
public static function getConsequencesLookup() : ConsequencesLookup {
return MediaWikiServices::getInstance()->getService( ConsequencesLookup::SERVICE_NAME );
}
}

View file

@ -0,0 +1,123 @@
<?php
namespace MediaWiki\Extension\AbuseFilter;
use AbuseFilter;
use Psr\Log\LoggerInterface;
use Wikimedia\Rdbms\IDatabase;
use Wikimedia\Rdbms\ILoadBalancer;
/**
* Class for retrieving actions and parameters from the database
*/
class ConsequencesLookup {
public const SERVICE_NAME = 'AbuseFilterConsequencesLookup';
/** @var ILoadBalancer */
private $loadBalancer;
/** @var CentralDBManager */
private $centralDBManager;
/** @var LoggerInterface */
private $logger;
/**
* @param ILoadBalancer $loadBalancer
* @param CentralDBManager $centralDBManager
* @param LoggerInterface $logger
*/
public function __construct(
ILoadBalancer $loadBalancer,
CentralDBManager $centralDBManager,
LoggerInterface $logger
) {
$this->loadBalancer = $loadBalancer;
$this->centralDBManager = $centralDBManager;
$this->logger = $logger;
}
/**
* @param string[] $filters
* @return array[][]
*/
public function getConsequencesForFilters( array $filters ) : array {
$globalFilters = [];
$localFilters = [];
foreach ( $filters as $filter ) {
list( $filterID, $global ) = AbuseFilter::splitGlobalName( $filter );
if ( $global ) {
$globalFilters[] = $filterID;
} else {
$localFilters[] = $filter;
}
}
// Load local filter info
$dbr = $this->loadBalancer->getConnectionRef( DB_REPLICA );
// Retrieve the consequences.
$consequences = [];
if ( count( $localFilters ) ) {
$consequences = $this->loadConsequencesFromDB( $dbr, $localFilters );
}
if ( count( $globalFilters ) ) {
$consequences += $this->loadConsequencesFromDB(
$this->centralDBManager->getConnection( DB_REPLICA ),
$globalFilters,
AbuseFilter::GLOBAL_FILTER_PREFIX
);
}
return $consequences;
}
/**
* @param IDatabase $dbr
* @param string[] $filters
* @param string $prefix
* @return array[][]
*/
private function loadConsequencesFromDB( IDatabase $dbr, array $filters, string $prefix = '' ) : array {
$actionsByFilter = [];
foreach ( $filters as $filter ) {
$actionsByFilter[$prefix . $filter] = [];
}
$res = $dbr->select(
[ 'abuse_filter_action', 'abuse_filter' ],
'*',
[ 'af_id' => $filters ],
__METHOD__,
[],
[ 'abuse_filter_action' => [ 'LEFT JOIN', 'afa_filter=af_id' ] ]
);
$dangerousActions = AbuseFilter::getDangerousActions();
// Categorise consequences by filter.
foreach ( $res as $row ) {
if ( $row->af_throttled
&& in_array( $row->afa_consequence, $dangerousActions )
) {
// Don't do the action, just log
$this->logger->info(
'Filter {filter_id} is throttled, skipping action: {action}',
[
'filter_id' => $row->af_id,
'action' => $row->afa_consequence
]
);
} elseif ( $row->afa_filter !== $row->af_id ) {
// We probably got a NULL, as it's a LEFT JOIN. Don't add it.
continue;
} else {
$actionsByFilter[$prefix . $row->afa_filter][$row->afa_consequence] =
array_filter( explode( "\n", $row->afa_parameters ) );
}
}
return $actionsByFilter;
}
}

View file

@ -8,6 +8,7 @@ use MediaWiki\Extension\AbuseFilter\ChangeTags\ChangeTagger;
use MediaWiki\Extension\AbuseFilter\ChangeTags\ChangeTagsManager;
use MediaWiki\Extension\AbuseFilter\ChangeTags\ChangeTagValidator;
use MediaWiki\Extension\AbuseFilter\ConsequencesFactory;
use MediaWiki\Extension\AbuseFilter\ConsequencesLookup;
use MediaWiki\Extension\AbuseFilter\EditBoxBuilderFactory;
use MediaWiki\Extension\AbuseFilter\FilterCompare;
use MediaWiki\Extension\AbuseFilter\FilterImporter;
@ -173,6 +174,13 @@ return [
ExtensionRegistry::getInstance()->isLoaded( 'CodeEditor' )
);
},
ConsequencesLookup::SERVICE_NAME => function ( MediaWikiServices $services ) : ConsequencesLookup {
return new ConsequencesLookup(
$services->getDBLoadBalancer(),
$services->get( CentralDBManager::SERVICE_NAME ),
LoggerFactory::getInstance( 'AbuseFilter' )
);
},
];
// @codeCoverageIgnoreEnd

View file

@ -0,0 +1,28 @@
<?php
use MediaWiki\Extension\AbuseFilter\CentralDBManager;
use MediaWiki\Extension\AbuseFilter\ConsequencesLookup;
use Psr\Log\NullLogger;
use Wikimedia\Rdbms\ILoadBalancer;
/**
* @group Test
* @group AbuseFilter
* @coversDefaultClass \MediaWiki\Extension\AbuseFilter\ConsequencesLookup
* @todo Write unit tests (non-trivial because the class is tied to a DB)
*/
class AbuseFilterConsequencesLookupTest extends MediaWikiUnitTestCase {
/**
* @covers ::__construct
*/
public function testConstructor() {
$this->assertInstanceOf(
ConsequencesLookup::class,
new ConsequencesLookup(
$this->createMock( ILoadBalancer::class ),
$this->createMock( CentralDBManager::class ),
new NullLogger()
)
);
}
}