Don't show action links for IP ranges outside block limit

These action links don't work but they appear like they would. So better not show them.

Bug: T211910
Change-Id: Ibec312870b3ba225dc26e533e478cd50df37fcaa
This commit is contained in:
Ammar Abdulhamid 2020-04-17 15:46:31 +01:00 committed by Ammarpad
parent da8ace4020
commit 032dc91f47
2 changed files with 170 additions and 67 deletions

View file

@ -68,6 +68,9 @@ class SpecialContributions extends IncludableSpecialPage {
/** @var UserOptionsLookup */
private $userOptionsLookup;
/** @var ContribsPager|null */
private $pager = null;
/**
* @param LinkBatchFactory|null $linkBatchFactory
* @param PermissionManager|null $permissionManager
@ -145,6 +148,45 @@ class SpecialContributions extends IncludableSpecialPage {
$this->opts['newOnly'] = $request->getBool( 'newOnly' );
$this->opts['hideMinor'] = $request->getBool( 'hideMinor' );
$ns = $request->getVal( 'namespace', null );
if ( $ns !== null && $ns !== '' && $ns !== 'all' ) {
$this->opts['namespace'] = intval( $ns );
} else {
$this->opts['namespace'] = '';
}
// Backwards compatibility: Before using OOUI form the old HTML form had
// fields for nsInvert and associated. These have now been replaced with the
// wpFilters query string parameters. These are retained to keep old URIs working.
$this->opts['associated'] = $request->getBool( 'associated' );
$this->opts['nsInvert'] = (bool)$request->getVal( 'nsInvert' );
$nsFilters = $request->getArray( 'wpfilters', null );
if ( $nsFilters !== null ) {
$this->opts['associated'] = in_array( 'associated', $nsFilters );
$this->opts['nsInvert'] = in_array( 'nsInvert', $nsFilters );
}
$this->opts['tagfilter'] = (string)$request->getVal( 'tagfilter' );
// Allows reverts to have the bot flag in recent changes. It is just here to
// be passed in the form at the top of the page
if ( MediaWikiServices::getInstance()
->getPermissionManager()
->userHasRight( $user, 'markbotedits' ) && $request->getBool( 'bot' )
) {
$this->opts['bot'] = '1';
}
$skip = $request->getText( 'offset' ) || $request->getText( 'dir' ) == 'prev';
# Offset overrides year/month selection
if ( !$skip ) {
$this->opts['year'] = $request->getVal( 'year' );
$this->opts['month'] = $request->getVal( 'month' );
$this->opts['start'] = $request->getVal( 'start' );
$this->opts['end'] = $request->getVal( 'end' );
}
$id = 0;
if ( ExternalUserNames::isExternal( $target ) ) {
$userObj = User::newFromName( $target, false );
@ -185,41 +227,6 @@ class SpecialContributions extends IncludableSpecialPage {
}
}
$ns = $request->getVal( 'namespace', null );
if ( $ns !== null && $ns !== '' && $ns !== 'all' ) {
$this->opts['namespace'] = intval( $ns );
} else {
$this->opts['namespace'] = '';
}
// Backwards compatibility: Before using OOUI form the old HTML form had
// fields for nsInvert and associated. These have now been replaced with the
// wpFilters query string parameters. These are retained to keep old URIs working.
$this->opts['associated'] = $request->getBool( 'associated' );
$this->opts['nsInvert'] = (bool)$request->getVal( 'nsInvert' );
$nsFilters = $request->getArray( 'wpfilters', null );
if ( $nsFilters !== null ) {
$this->opts['associated'] = in_array( 'associated', $nsFilters );
$this->opts['nsInvert'] = in_array( 'nsInvert', $nsFilters );
}
$this->opts['tagfilter'] = (string)$request->getVal( 'tagfilter' );
// Allows reverts to have the bot flag in recent changes. It is just here to
// be passed in the form at the top of the page
if ( $this->permissionManager->userHasRight( $user, 'markbotedits' ) && $request->getBool( 'bot' ) ) {
$this->opts['bot'] = '1';
}
$skip = $request->getText( 'offset' ) || $request->getText( 'dir' ) == 'prev';
# Offset overrides year/month selection
if ( !$skip ) {
$this->opts['year'] = $request->getVal( 'year' );
$this->opts['month'] = $request->getVal( 'month' );
$this->opts['start'] = $request->getVal( 'start' );
$this->opts['end'] = $request->getVal( 'end' );
}
$this->opts = ContribsPager::processDateFilter( $this->opts );
if ( $this->opts['namespace'] < NS_MAIN ) {
@ -281,33 +288,10 @@ class SpecialContributions extends IncludableSpecialPage {
if ( $this->getHookRunner()->onSpecialContributionsBeforeMainOutput(
$id, $userObj, $this )
) {
$pager = new ContribsPager(
$this->getContext(), [
'target' => $target,
'namespace' => $this->opts['namespace'],
'tagfilter' => $this->opts['tagfilter'],
'start' => $this->opts['start'],
'end' => $this->opts['end'],
'deletedOnly' => $this->opts['deletedOnly'],
'topOnly' => $this->opts['topOnly'],
'newOnly' => $this->opts['newOnly'],
'hideMinor' => $this->opts['hideMinor'],
'nsInvert' => $this->opts['nsInvert'],
'associated' => $this->opts['associated'],
],
$this->getLinkRenderer(),
$this->linkBatchFactory,
$this->getHookContainer(),
$this->permissionManager,
$this->loadBalancer,
$this->actorMigration,
$this->revisionStore,
$this->namespaceInfo
);
if ( !$this->including() ) {
$out->addHTML( $this->getForm( $this->opts ) );
}
$pager = $this->getPager();
if ( IPUtils::isValidRange( $target ) && !$pager->isQueryableRange( $target ) ) {
// Valid range, but outside CIDR limit.
$limits = $this->getConfig()->get( 'RangeContributionsCIDRLimit' );
@ -353,7 +337,7 @@ class SpecialContributions extends IncludableSpecialPage {
$out->preventClickjacking( $pager->getPreventClickjacking() );
# Show the appropriate "footer" message - WHOIS tools, etc.
if ( IPUtils::isValidRange( $target ) ) {
if ( IPUtils::isValidRange( $target ) && $pager->isQueryableRange( $target ) ) {
$message = 'sp-contributions-footer-anon-range';
} elseif ( IPUtils::isIPAddress( $target ) ) {
$message = 'sp-contributions-footer-anon';
@ -404,13 +388,13 @@ class SpecialContributions extends IncludableSpecialPage {
$nt = $userObj->getUserPage();
$talk = $userObj->getTalkPage();
$links = '';
if ( $talk ) {
$tools = self::getUserLinks(
$this,
$userObj,
$this->permissionManager,
$this->getHookRunner()
);
// T211910. Don't show action links if a range is outside block limit
$showForIp = IPUtils::isValid( $userObj ) ||
( IPUtils::isValidRange( $userObj ) && $this->getPager()->isQueryableRange( $userObj ) );
if ( $talk && ( $userObj->isRegistered() || $showForIp ) ) {
$tools = self::getUserLinks( $this, $userObj );
$links = Html::openElement( 'span', [ 'class' => 'mw-changeslist-links' ] );
foreach ( $tools as $tool ) {
$links .= Html::rawElement( 'span', [], $tool ) . ' ';
@ -812,6 +796,32 @@ class SpecialContributions extends IncludableSpecialPage {
->search( UserNamePrefixSearch::AUDIENCE_PUBLIC, $search, $limit, $offset );
}
private function getPager() {
if ( $this->pager === null ) {
$options = [
'target' => $this->opts['target'],
'namespace' => $this->opts['namespace'],
'tagfilter' => $this->opts['tagfilter'],
'start' => $this->opts['start'],
'end' => $this->opts['end'],
'deletedOnly' => $this->opts['deletedOnly'],
'topOnly' => $this->opts['topOnly'],
'newOnly' => $this->opts['newOnly'],
'hideMinor' => $this->opts['hideMinor'],
'nsInvert' => $this->opts['nsInvert'],
'associated' => $this->opts['associated'],
];
$this->pager = new ContribsPager(
$this->getContext(),
$options,
$this->getLinkRenderer()
);
}
return $this->pager;
}
protected function getGroupName() {
return 'users';
}

View file

@ -0,0 +1,93 @@
<?php
/**
* @author Ammarpad
* @group Database
* @covers SpecialContributions
*/
class SpecialContributionsTest extends SpecialPageTestBase {
private $admin;
protected function setUp(): void {
parent::setUp();
$this->setMwGlobals( [
'wgRangeContributionsCIDRLimit' => [
'IPv4' => 16,
'IPv6' => 32,
]
] );
$this->setTemporaryHook(
'SpecialContributionsBeforeMainOutput',
function () {
return;
}
);
$this->admin = $this->getTestSysop()->getUser();
}
/**
* @covers SpecialContributions::execute
* @dataProvider provideTestExecuteRange
*/
public function testExecuteRange( $username, $shouldShowLinks ) {
list( $html ) = $this->executeSpecialPage( $username, null, 'qqx', $this->admin, true );
if ( $shouldShowLinks ) {
$this->assertStringContainsString( 'blocklink', $html );
} else {
$this->assertStringNotContainsString( 'blocklink', $html );
$this->assertStringContainsString( 'sp-contributions-outofrange', $html );
}
}
/**
* @covers SpecialContributions::execute
* @dataProvider provideTestExecuteNonRange
*/
public function testExecuteNonRange( $username, $shouldShowLinks ) {
list( $html ) = $this->executeSpecialPage( $username, null, 'qqx', $this->admin, true );
if ( $shouldShowLinks ) {
$this->assertStringContainsString( 'blocklink', $html );
} else {
$this->assertStringNotContainsString( 'blocklink', $html );
}
}
public function provideTestExecuteRange() {
yield 'Queryable IPv4 range should have blocklink for admin'
=> [ '24.237.208.166/30', true ];
yield 'Queryable IPv6 range should have blocklink for admin'
=> [ '2001:DB8:0:0:0:0:0:01/43', true ];
yield 'Unqueryable IPv4 range should not have blocklink for admin'
=> [ '212.35.31.121/14', false ];
yield 'Unqueryable IPv6 range should not have blocklink for admin'
=> [ '2000::/24', false ];
}
public function provideTestExecuteNonRange() {
yield 'Valid IPv4 should have blocklink for admin' => [ '124.24.52.13', true ];
yield 'Valid IPv6 should have blocklink for admin' => [ '2001:db8::', true ];
yield 'Local user should have blocklink for admin' => [ 'UTSysop', true ];
yield 'Invalid IP should not have blocklink for admin' => [ '24.237.222208.166', false ];
yield 'External user should not have blocklink for admin' => [ 'imported>UTSysop', false ];
yield 'Nonexistent user should not have blocklink for admin' => [ __CLASS__, false ];
}
protected function newSpecialPage(): SpecialContributions {
$services = MediaWiki\MediaWikiServices::getInstance();
return new SpecialContributions(
$services->getLinkBatchFactory(),
$services->getPermissionManager(),
$services->getDBLoadBalancer(),
$services->getActorMigration(),
$services->getRevisionStore(),
$services->getNamespaceInfo(),
$services->getUserNameUtils(),
$services->getUserNamePrefixSearch(),
$services->getUserOptionsLookup()
);
}
}