mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/AbuseFilter.git
synced 2024-11-23 13:46:48 +00:00
Add RemoveProtectedFlagFromFilter maintenance script
Why: * Protected variables were introduced to support temporary accounts so that temporary users could be filtered based on their IP address. * Filters that use protected variables are protected in order to preserve privacy. This can't be undone. * It is mistakenly possible to protect a filter that does not use protected variables (T378553). We need a mechanism to fix these mistakes. What: * Introduce a maintenance script that takes a filter ID and, if the filter is protected, sets it to unprotected while maintaining any other existing privacy levels. Bug: T378551 Change-Id: I4dfe3970221397d5be5ea0697490d8c8e3726adf
This commit is contained in:
parent
1fcf9adb92
commit
91456d79b2
75
maintenance/RemoveProtectedFlagFromFilter.php
Normal file
75
maintenance/RemoveProtectedFlagFromFilter.php
Normal file
|
@ -0,0 +1,75 @@
|
|||
<?php
|
||||
|
||||
namespace MediaWiki\Extension\AbuseFilter\Maintenance;
|
||||
|
||||
use MediaWiki\Extension\AbuseFilter\Filter\Flags;
|
||||
use MediaWiki\Maintenance\Maintenance;
|
||||
|
||||
// @codeCoverageIgnoreStart
|
||||
$IP = getenv( 'MW_INSTALL_PATH' );
|
||||
if ( $IP === false ) {
|
||||
$IP = __DIR__ . '/../../..';
|
||||
}
|
||||
require_once "$IP/maintenance/Maintenance.php";
|
||||
// @codeCoverageIgnoreEnd
|
||||
|
||||
/**
|
||||
* Maintenance script that allows an individual filter's privacy level to remove the
|
||||
* "protected" flag from a filter, while keeping other privacy flags. This is for
|
||||
* correcting filters that were mistakenly allowed to be protected (T378551).
|
||||
*
|
||||
* @ingroup Maintenance
|
||||
* @since 1.44
|
||||
*/
|
||||
class RemoveProtectedFlagFromFilter extends Maintenance {
|
||||
public function __construct() {
|
||||
parent::__construct();
|
||||
|
||||
$this->addDescription(
|
||||
'Remove the "protected" flag from a filter, while keeping other privacy flags'
|
||||
);
|
||||
$this->addArg( 'filter', 'ID of the protected filter to update' );
|
||||
$this->requireExtension( 'Abuse Filter' );
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function execute() {
|
||||
$filter = $this->getArg( 0 );
|
||||
|
||||
$privacyLevel = $this->getReplicaDB()->newSelectQueryBuilder()
|
||||
->select( 'af_hidden' )
|
||||
->from( 'abuse_filter' )
|
||||
->where( [
|
||||
'af_id' => $filter
|
||||
] )
|
||||
->caller( __METHOD__ )
|
||||
->fetchField();
|
||||
|
||||
if ( $privacyLevel === false ) {
|
||||
$this->fatalError( "Filter $filter not found.\n" );
|
||||
}
|
||||
|
||||
if ( ( $privacyLevel & Flags::FILTER_USES_PROTECTED_VARS ) === 0 ) {
|
||||
$this->output( "Filter $filter is not protected. Nothing to update.\n" );
|
||||
return false;
|
||||
}
|
||||
|
||||
// The new privacy level is the old level with the bit representing "protected" unset.
|
||||
$newPrivacyLevel = (string)( $privacyLevel & ( ~Flags::FILTER_USES_PROTECTED_VARS ) );
|
||||
|
||||
$this->getPrimaryDB()->newUpdateQueryBuilder()
|
||||
->update( 'abuse_filter' )
|
||||
->set( [ 'af_hidden' => $newPrivacyLevel ] )
|
||||
->where( [ 'af_id' => $filter ] )
|
||||
->caller( __METHOD__ )
|
||||
->execute();
|
||||
|
||||
$this->output( "Successfully removed \"protected\" flag from filter $filter.\n" );
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
$maintClass = RemoveProtectedFlagFromFilter::class;
|
||||
require_once RUN_MAINTENANCE_IF_MAIN;
|
|
@ -0,0 +1,113 @@
|
|||
<?php
|
||||
|
||||
namespace MediaWiki\Extension\AbuseFilter\Tests\Integration;
|
||||
|
||||
use MediaWiki\Extension\AbuseFilter\Filter\Flags;
|
||||
use MediaWiki\Extension\AbuseFilter\Maintenance\RemoveProtectedFlagFromFilter;
|
||||
use MediaWiki\Tests\Maintenance\MaintenanceBaseTestCase;
|
||||
|
||||
/**
|
||||
* @group Test
|
||||
* @group AbuseFilter
|
||||
* @group Database
|
||||
* @covers \MediaWiki\Extension\AbuseFilter\Maintenance\RemoveProtectedFlagFromFilter
|
||||
*/
|
||||
class RemoveProtectedFlagFromFilterTest extends MaintenanceBaseTestCase {
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
protected function getMaintenanceClass() {
|
||||
return RemoveProtectedFlagFromFilter::class;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function addDBDataOnce() {
|
||||
$defaultRow = [
|
||||
'af_actor' => 1,
|
||||
'af_timestamp' => $this->getDb()->timestamp( '20000101000000' ),
|
||||
'af_enabled' => 1,
|
||||
'af_comments' => '',
|
||||
'af_public_comments' => 'Test filter',
|
||||
'af_hit_count' => 0,
|
||||
'af_throttled' => 0,
|
||||
'af_deleted' => 0,
|
||||
'af_global' => 0,
|
||||
'af_group' => 'default',
|
||||
'af_pattern' => '',
|
||||
'af_actions' => '',
|
||||
];
|
||||
$rows = [
|
||||
[
|
||||
'af_id' => 1,
|
||||
'af_hidden' => Flags::FILTER_PUBLIC
|
||||
] + $defaultRow,
|
||||
[
|
||||
'af_id' => 2,
|
||||
'af_hidden' => Flags::FILTER_HIDDEN
|
||||
] + $defaultRow,
|
||||
[
|
||||
'af_id' => 3,
|
||||
'af_hidden' => Flags::FILTER_USES_PROTECTED_VARS
|
||||
] + $defaultRow,
|
||||
[
|
||||
'af_id' => 4,
|
||||
'af_hidden' => Flags::FILTER_USES_PROTECTED_VARS | Flags::FILTER_HIDDEN
|
||||
] + $defaultRow,
|
||||
];
|
||||
$this->getDb()->newInsertQueryBuilder()
|
||||
->insertInto( 'abuse_filter' )
|
||||
->rows( $rows )
|
||||
->caller( __METHOD__ )
|
||||
->execute();
|
||||
}
|
||||
|
||||
public function testExecuteNonexistentFilter() {
|
||||
$filter = 100;
|
||||
$this->expectCallToFatalError();
|
||||
$this->maintenance->loadParamsAndArgs( null, null, [ $filter ] );
|
||||
$this->maintenance->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideUnprotectedFilter
|
||||
*/
|
||||
public function testExecuteUnprotectedFilter( $filter ) {
|
||||
$this->expectOutputString( "Filter $filter is not protected. Nothing to update.\n" );
|
||||
$this->maintenance->loadParamsAndArgs( null, null, [ $filter ] );
|
||||
$this->assertFalse( $this->maintenance->execute() );
|
||||
}
|
||||
|
||||
public function provideUnprotectedFilter() {
|
||||
return [
|
||||
'Fail on public filter' => [
|
||||
'filterId' => 1,
|
||||
],
|
||||
'Fail on unprotected, private filter' => [
|
||||
'filterId' => 2,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideProtectedFilter
|
||||
*/
|
||||
public function testExecuteProtectedFilter( $filter ) {
|
||||
$this->maintenance->loadParamsAndArgs( null, null, [ $filter ] );
|
||||
$this->assertTrue( $this->maintenance->execute() );
|
||||
}
|
||||
|
||||
public function provideProtectedFilter() {
|
||||
return [
|
||||
'Remove protected flag from protected filter' => [
|
||||
'filterId' => 3,
|
||||
'expectedNewPrivacyLevel' => Flags::FILTER_PUBLIC,
|
||||
],
|
||||
'Remove protected flag from private, protected filter' => [
|
||||
'filterId' => 4,
|
||||
'expectedNewPrivacyLevel' => Flags::FILTER_HIDDEN,
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue