mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/AbuseFilter.git
synced 2024-12-02 17:46:30 +00:00
e8471a717c
This replaces the previous pattern of callers having to use RevisionLookup if the result was 'implicit'. Also, in some cases where we were just hiding things if the visibility was !== true, properly handle the implicit case by using the new method. Make the new method return string constants rather than bool|string. The new method also fixes some potential info leaks which happened when the row was hidden, the user could view suppressed AbuseLog entries, but the associated revision was also deleted and the user couldn't see it (this shouldn't be relevant for WMF wikis since AF deletion is oversight-level). Also add a bunch of tests for the various cases to ensure we don't regress again. Bug: T261532 Change-Id: I929f865acf5d207b739cb3af043f70cb59243ee0
268 lines
7.9 KiB
PHP
268 lines
7.9 KiB
PHP
<?php
|
|
|
|
namespace MediaWiki\Extension\AbuseFilter\Special;
|
|
|
|
use Html;
|
|
use MediaWiki\Extension\AbuseFilter\AbuseFilterPermissionManager;
|
|
use MediaWiki\Extension\AbuseFilter\CentralDBManager;
|
|
use MediaWiki\Extension\AbuseFilter\Consequences\ConsequencesFactory;
|
|
use MediaWiki\Extension\AbuseFilter\Consequences\ConsequencesRegistry;
|
|
use MediaWiki\Extension\AbuseFilter\EditBox\EditBoxBuilderFactory;
|
|
use MediaWiki\Extension\AbuseFilter\FilterImporter;
|
|
use MediaWiki\Extension\AbuseFilter\FilterLookup;
|
|
use MediaWiki\Extension\AbuseFilter\FilterProfiler;
|
|
use MediaWiki\Extension\AbuseFilter\FilterStore;
|
|
use MediaWiki\Extension\AbuseFilter\Parser\RuleCheckerFactory;
|
|
use MediaWiki\Extension\AbuseFilter\SpecsFormatter;
|
|
use MediaWiki\Extension\AbuseFilter\VariableGenerator\VariableGeneratorFactory;
|
|
use MediaWiki\Extension\AbuseFilter\Variables\VariablesBlobStore;
|
|
use MediaWiki\Extension\AbuseFilter\Variables\VariablesFormatter;
|
|
use MediaWiki\Extension\AbuseFilter\Variables\VariablesManager;
|
|
use MediaWiki\Extension\AbuseFilter\View\AbuseFilterView;
|
|
use MediaWiki\Extension\AbuseFilter\View\AbuseFilterViewDiff;
|
|
use MediaWiki\Extension\AbuseFilter\View\AbuseFilterViewEdit;
|
|
use MediaWiki\Extension\AbuseFilter\View\AbuseFilterViewExamine;
|
|
use MediaWiki\Extension\AbuseFilter\View\AbuseFilterViewHistory;
|
|
use MediaWiki\Extension\AbuseFilter\View\AbuseFilterViewImport;
|
|
use MediaWiki\Extension\AbuseFilter\View\AbuseFilterViewList;
|
|
use MediaWiki\Extension\AbuseFilter\View\AbuseFilterViewRevert;
|
|
use MediaWiki\Extension\AbuseFilter\View\AbuseFilterViewTestBatch;
|
|
use MediaWiki\Extension\AbuseFilter\View\AbuseFilterViewTools;
|
|
use Title;
|
|
use Wikimedia\ObjectFactory;
|
|
|
|
class SpecialAbuseFilter extends AbuseFilterSpecialPage {
|
|
|
|
private const PAGE_NAME = 'AbuseFilter';
|
|
|
|
/**
|
|
* @var ObjectFactory
|
|
*/
|
|
private $objectFactory;
|
|
|
|
private const SERVICES_PER_VIEW = [
|
|
AbuseFilterViewDiff::class => [
|
|
AbuseFilterPermissionManager::SERVICE_NAME,
|
|
SpecsFormatter::SERVICE_NAME,
|
|
FilterLookup::SERVICE_NAME,
|
|
],
|
|
AbuseFilterViewEdit::class => [
|
|
'PermissionManager',
|
|
AbuseFilterPermissionManager::SERVICE_NAME,
|
|
FilterProfiler::SERVICE_NAME,
|
|
FilterLookup::SERVICE_NAME,
|
|
FilterImporter::SERVICE_NAME,
|
|
FilterStore::SERVICE_NAME,
|
|
EditBoxBuilderFactory::SERVICE_NAME,
|
|
ConsequencesRegistry::SERVICE_NAME,
|
|
SpecsFormatter::SERVICE_NAME,
|
|
],
|
|
AbuseFilterViewExamine::class => [
|
|
AbuseFilterPermissionManager::SERVICE_NAME,
|
|
FilterLookup::SERVICE_NAME,
|
|
EditBoxBuilderFactory::SERVICE_NAME,
|
|
VariablesBlobStore::SERVICE_NAME,
|
|
VariablesFormatter::SERVICE_NAME,
|
|
VariablesManager::SERVICE_NAME,
|
|
VariableGeneratorFactory::SERVICE_NAME,
|
|
],
|
|
AbuseFilterViewHistory::class => [
|
|
AbuseFilterPermissionManager::SERVICE_NAME,
|
|
FilterLookup::SERVICE_NAME,
|
|
SpecsFormatter::SERVICE_NAME,
|
|
'UserNameUtils',
|
|
],
|
|
AbuseFilterViewImport::class => [
|
|
AbuseFilterPermissionManager::SERVICE_NAME,
|
|
],
|
|
AbuseFilterViewList::class => [
|
|
AbuseFilterPermissionManager::SERVICE_NAME,
|
|
FilterProfiler::SERVICE_NAME,
|
|
SpecsFormatter::SERVICE_NAME,
|
|
CentralDBManager::SERVICE_NAME,
|
|
],
|
|
AbuseFilterViewRevert::class => [
|
|
'UserFactory',
|
|
AbuseFilterPermissionManager::SERVICE_NAME,
|
|
FilterLookup::SERVICE_NAME,
|
|
ConsequencesFactory::SERVICE_NAME,
|
|
VariablesBlobStore::SERVICE_NAME,
|
|
SpecsFormatter::SERVICE_NAME,
|
|
],
|
|
AbuseFilterViewTestBatch::class => [
|
|
AbuseFilterPermissionManager::SERVICE_NAME,
|
|
EditBoxBuilderFactory::SERVICE_NAME,
|
|
RuleCheckerFactory::SERVICE_NAME,
|
|
VariableGeneratorFactory::SERVICE_NAME,
|
|
],
|
|
AbuseFilterViewTools::class => [
|
|
AbuseFilterPermissionManager::SERVICE_NAME,
|
|
EditBoxBuilderFactory::SERVICE_NAME,
|
|
],
|
|
];
|
|
|
|
/**
|
|
* @param AbuseFilterPermissionManager $afPermissionManager
|
|
* @param ObjectFactory $objectFactory
|
|
*/
|
|
public function __construct(
|
|
AbuseFilterPermissionManager $afPermissionManager,
|
|
ObjectFactory $objectFactory
|
|
) {
|
|
parent::__construct( self::PAGE_NAME, 'abusefilter-view', $afPermissionManager );
|
|
$this->objectFactory = $objectFactory;
|
|
}
|
|
|
|
/**
|
|
* @codeCoverageIgnore Merely declarative
|
|
* @inheritDoc
|
|
*/
|
|
public function doesWrites() {
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* @codeCoverageIgnore Merely declarative
|
|
* @inheritDoc
|
|
*/
|
|
protected function getGroupName() {
|
|
return 'wiki';
|
|
}
|
|
|
|
/**
|
|
* @param string|null $subpage
|
|
*/
|
|
public function execute( $subpage ) {
|
|
$out = $this->getOutput();
|
|
$request = $this->getRequest();
|
|
|
|
$out->addModuleStyles( 'ext.abuseFilter' );
|
|
|
|
$this->setHeaders();
|
|
$this->addHelpLink( 'Extension:AbuseFilter' );
|
|
|
|
$this->checkPermissions();
|
|
|
|
if ( $request->getVal( 'result' ) === 'success' ) {
|
|
$out->setSubtitle( $this->msg( 'abusefilter-edit-done-subtitle' ) );
|
|
$changedFilter = intval( $request->getVal( 'changedfilter' ) );
|
|
$changeId = intval( $request->getVal( 'changeid' ) );
|
|
$out->addHTML( Html::successBox(
|
|
$this->msg(
|
|
'abusefilter-edit-done',
|
|
$changedFilter,
|
|
$changeId,
|
|
$this->getLanguage()->formatNum( $changedFilter )
|
|
)->parse()
|
|
) );
|
|
}
|
|
|
|
[ $view, $pageType, $params ] = $this->getViewClassAndPageType( $subpage );
|
|
|
|
// Links at the top
|
|
$this->addNavigationLinks( $pageType );
|
|
|
|
$view = $this->instantiateView( $view, $params );
|
|
$view->show();
|
|
}
|
|
|
|
/**
|
|
* Instantiate the view class
|
|
*
|
|
* @phan-param class-string $viewClass
|
|
* @suppress PhanTypeInvalidCallableArraySize
|
|
*
|
|
* @param string $viewClass
|
|
* @param array $params
|
|
* @return AbuseFilterView
|
|
*/
|
|
public function instantiateView( string $viewClass, array $params ): AbuseFilterView {
|
|
return $this->objectFactory->createObject( [
|
|
'class' => $viewClass,
|
|
'services' => self::SERVICES_PER_VIEW[$viewClass],
|
|
'args' => [ $this->getContext(), $this->getLinkRenderer(), self::PAGE_NAME, $params ]
|
|
] );
|
|
}
|
|
|
|
/**
|
|
* Determine the view class to instantiate
|
|
*
|
|
* @param string|null $subpage
|
|
* @return array A tuple of three elements:
|
|
* - a subclass of AbuseFilterView
|
|
* - type of page for addNavigationLinks
|
|
* - array of parameters for the class
|
|
* @phan-return array{0:class-string,1:string,2:array}
|
|
*/
|
|
public function getViewClassAndPageType( $subpage ): array {
|
|
// Filter by removing blanks.
|
|
$params = array_values( array_filter(
|
|
explode( '/', $subpage ?: '' ),
|
|
static function ( $value ) {
|
|
return $value !== '';
|
|
}
|
|
) );
|
|
|
|
if ( $subpage === 'tools' ) {
|
|
return [ AbuseFilterViewTools::class, 'tools', [] ];
|
|
}
|
|
|
|
if ( $subpage === 'import' ) {
|
|
return [ AbuseFilterViewImport::class, 'import', [] ];
|
|
}
|
|
|
|
if ( is_numeric( $subpage ) || $subpage === 'new' ) {
|
|
return [
|
|
AbuseFilterViewEdit::class,
|
|
'edit',
|
|
[ 'filter' => is_numeric( $subpage ) ? (int)$subpage : null ]
|
|
];
|
|
}
|
|
|
|
if ( $params ) {
|
|
if ( count( $params ) === 2 && $params[0] === 'revert' && is_numeric( $params[1] ) ) {
|
|
$params[1] = (int)$params[1];
|
|
return [ AbuseFilterViewRevert::class, 'revert', $params ];
|
|
}
|
|
|
|
if ( $params[0] === 'test' ) {
|
|
return [ AbuseFilterViewTestBatch::class, 'test', $params ];
|
|
}
|
|
|
|
if ( $params[0] === 'examine' ) {
|
|
return [ AbuseFilterViewExamine::class, 'examine', $params ];
|
|
}
|
|
|
|
if ( $params[0] === 'history' || $params[0] === 'log' ) {
|
|
if ( count( $params ) <= 2 ) {
|
|
$params = isset( $params[1] ) ? [ 'filter' => (int)$params[1] ] : [];
|
|
return [ AbuseFilterViewHistory::class, 'recentchanges', $params ];
|
|
}
|
|
if ( count( $params ) === 4 && $params[2] === 'item' ) {
|
|
return [
|
|
AbuseFilterViewEdit::class,
|
|
'',
|
|
[ 'filter' => (int)$params[1], 'history' => (int)$params[3] ]
|
|
];
|
|
}
|
|
if ( count( $params ) === 5 && $params[2] === 'diff' ) {
|
|
// Special:AbuseFilter/history/<filter>/diff/<oldid>/<newid>
|
|
return [ AbuseFilterViewDiff::class, '', $params ];
|
|
}
|
|
}
|
|
}
|
|
|
|
return [ AbuseFilterViewList::class, 'home', [] ];
|
|
}
|
|
|
|
/**
|
|
* Static variant to get the associated Title.
|
|
*
|
|
* @param string|int $subpage
|
|
* @return Title
|
|
*/
|
|
public static function getTitleForSubpage( $subpage ): Title {
|
|
return self::getTitleFor( self::PAGE_NAME, $subpage );
|
|
}
|
|
}
|