mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/ConfirmEdit
synced 2024-12-04 12:38:40 +00:00
Update code for voice and tone reasons
* Reduces use of whitelist * Deprecates and provides new $wg to replace $wgCaptchaWhitelistIP and $wgCaptchaWhitelist Bug: T277936 Change-Id: I9c4a572321bb06e5a1d4208a295e21b73e75b787
This commit is contained in:
parent
890370b888
commit
da22e2c767
12
README.md
12
README.md
|
@ -45,9 +45,9 @@ Additional maintenance work was done by Yaron Koren.
|
||||||
*
|
*
|
||||||
* Specific IP addresses or CIDR-style ranges may be used,
|
* Specific IP addresses or CIDR-style ranges may be used,
|
||||||
* for instance:
|
* for instance:
|
||||||
* $wgCaptchaWhitelistIP = array('192.168.1.0/24', '10.1.0.0/16');
|
* $wgCaptchaBypassIPs = [ '192.168.1.0/24', '10.1.0.0/16' ];
|
||||||
*/
|
*/
|
||||||
$wgCaptchaWhitelistIP = false;
|
$wgCaptchaBypassIPs = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Actions which can trigger a captcha
|
* Actions which can trigger a captcha
|
||||||
|
@ -132,12 +132,12 @@ $wgAllowConfirmedEmail = false;
|
||||||
$wgCaptchaBadLoginAttempts = 3;
|
$wgCaptchaBadLoginAttempts = 3;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Regex to whitelist URLs to known-good sites...
|
* Regex to ignore URLs to known-good sites...
|
||||||
* For instance:
|
* For instance:
|
||||||
* $wgCaptchaWhitelist = '#^https?://([a-z0-9-]+\\.)?(wikimedia|wikipedia)\.org/#i';
|
* $wgCaptchaIgnoredUrls = '#^https?://([a-z0-9-]+\\.)?(wikimedia|wikipedia)\.org/#i';
|
||||||
* Local admins can define a whitelist under [[MediaWiki:captcha-addurl-whitelist]]
|
* Local admins can define a local allow list under [[MediaWiki:captcha-addurl-whitelist]]
|
||||||
*/
|
*/
|
||||||
$wgCaptchaWhitelist = false;
|
$wgCaptchaIgnoredUrls = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Additional regexes to check for. Use full regexes; can match things
|
* Additional regexes to check for. Use full regexes; can match things
|
||||||
|
|
|
@ -119,6 +119,11 @@
|
||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
"CaptchaWhitelistIP": {
|
"CaptchaWhitelistIP": {
|
||||||
|
"description": "DEPRECATED! Use CaptchaBypassIPs",
|
||||||
|
"value": false
|
||||||
|
},
|
||||||
|
"CaptchaBypassIPs": {
|
||||||
|
"description": "A list of IP addresses that can skip the captcha",
|
||||||
"value": false
|
"value": false
|
||||||
},
|
},
|
||||||
"Captcha": {
|
"Captcha": {
|
||||||
|
@ -162,6 +167,11 @@
|
||||||
"value": 20
|
"value": 20
|
||||||
},
|
},
|
||||||
"CaptchaWhitelist": {
|
"CaptchaWhitelist": {
|
||||||
|
"description": "DEPRECATED: Use CaptchaIgnoredUrls",
|
||||||
|
"value": false
|
||||||
|
},
|
||||||
|
"CaptchaIgnoredUrls": {
|
||||||
|
"description": "Urls that won't trigger a captcha",
|
||||||
"value": false
|
"value": false
|
||||||
},
|
},
|
||||||
"CaptchaRegexes": {
|
"CaptchaRegexes": {
|
||||||
|
|
|
@ -94,7 +94,7 @@ class Hooks implements
|
||||||
) {
|
) {
|
||||||
$title = $wikiPage->getTitle();
|
$title = $wikiPage->getTitle();
|
||||||
if ( $title->getText() === 'Captcha-ip-whitelist' && $title->getNamespace() === NS_MEDIAWIKI ) {
|
if ( $title->getText() === 'Captcha-ip-whitelist' && $title->getNamespace() === NS_MEDIAWIKI ) {
|
||||||
$this->cache->delete( $this->cache->makeKey( 'confirmedit', 'ipwhitelist' ) );
|
$this->cache->delete( $this->cache->makeKey( 'confirmedit', 'ipbypasslist' ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -357,27 +357,30 @@ class SimpleCaptcha {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if the current IP is allowed to skip captchas. This checks
|
* Check if the current IP is allowed to skip solving a captcha.
|
||||||
* the whitelist from two sources.
|
* This checks the bypass list from two sources.
|
||||||
* 1) From the server-side config array $wgCaptchaWhitelistIP
|
* 1) From the server-side config array $wgCaptchaWhitelistIP (deprecated) or $wgCaptchaBypassIPs
|
||||||
* 2) From the local [[MediaWiki:Captcha-ip-whitelist]] message
|
* 2) From the local [[MediaWiki:Captcha-ip-whitelist]] message
|
||||||
*
|
*
|
||||||
* @return bool true if whitelisted, false if not
|
* @return bool true if the IP can bypass a captcha, false if not
|
||||||
*/
|
*/
|
||||||
private function isIPWhitelisted() {
|
private function canIPBypassCaptcha() {
|
||||||
global $wgCaptchaWhitelistIP, $wgRequest;
|
global $wgCaptchaWhitelistIP, $wgCaptchaBypassIPs, $wgRequest;
|
||||||
$ip = $wgRequest->getIP();
|
$ip = $wgRequest->getIP();
|
||||||
|
|
||||||
if ( $wgCaptchaWhitelistIP ) {
|
// Deprecated; to be removed later
|
||||||
if ( IPUtils::isInRanges( $ip, $wgCaptchaWhitelistIP ) ) {
|
if ( $wgCaptchaWhitelistIP && IPUtils::isInRanges( $ip, $wgCaptchaWhitelistIP ) ) {
|
||||||
return true;
|
return true;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$whitelistMsg = wfMessage( 'captcha-ip-whitelist' )->inContentLanguage();
|
if ( $wgCaptchaBypassIPs && IPUtils::isInRanges( $ip, $wgCaptchaBypassIPs ) ) {
|
||||||
if ( !$whitelistMsg->isDisabled() ) {
|
return true;
|
||||||
$whitelistedIPs = $this->getWikiIPWhitelist( $whitelistMsg );
|
}
|
||||||
if ( IPUtils::isInRanges( $ip, $whitelistedIPs ) ) {
|
|
||||||
|
$msg = wfMessage( 'captcha-ip-whitelist' )->inContentLanguage();
|
||||||
|
if ( !$msg->isDisabled() ) {
|
||||||
|
$allowedIPs = $this->getWikiIPBypassList( $msg );
|
||||||
|
if ( IPUtils::isInRanges( $ip, $allowedIPs ) ) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -386,40 +389,38 @@ class SimpleCaptcha {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the on-wiki IP whitelist stored in [[MediaWiki:Captcha-ip-whitelist]]
|
* Get the on-wiki IP bypass list stored on a MediaWiki page from cache if possible.
|
||||||
* page from cache if possible.
|
|
||||||
*
|
*
|
||||||
* @param Message $msg whitelist Message on wiki
|
* @param Message $msg Message on wiki with IP lists
|
||||||
* @return array whitelisted IP addresses or IP ranges, empty array if no whitelist
|
* @return array Allowed IP addresses or IP ranges, empty array if none
|
||||||
*/
|
*/
|
||||||
private function getWikiIPWhitelist( Message $msg ) {
|
private function getWikiIPBypassList( Message $msg ) {
|
||||||
$cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
|
$cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
|
||||||
$cacheKey = $cache->makeKey( 'confirmedit', 'ipwhitelist' );
|
$cacheKey = $cache->makeKey( 'confirmedit', 'ipbypasslist' );
|
||||||
|
|
||||||
$cachedWhitelist = $cache->get( $cacheKey );
|
$cached = $cache->get( $cacheKey );
|
||||||
if ( $cachedWhitelist === false ) {
|
if ( $cached !== false ) {
|
||||||
// Could not retrieve from cache so build the whitelist directly
|
return $cached;
|
||||||
// from the wikipage
|
|
||||||
$whitelist = $this->buildValidIPs(
|
|
||||||
explode( "\n", $msg->plain() )
|
|
||||||
);
|
|
||||||
// And then store it in cache for one day. This cache is cleared on
|
|
||||||
// modifications to the whitelist page.
|
|
||||||
// @see MediaWiki\Extension\ConfirmEdit\Hooks::onPageSaveComplete()
|
|
||||||
$cache->set( $cacheKey, $whitelist, 86400 );
|
|
||||||
} else {
|
|
||||||
// Whitelist from the cache
|
|
||||||
$whitelist = $cachedWhitelist;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return $whitelist;
|
// Could not retrieve from cache, so build the list directly
|
||||||
|
// from the MediaWiki page
|
||||||
|
$list = $this->buildValidIPs(
|
||||||
|
explode( "\n", $msg->plain() )
|
||||||
|
);
|
||||||
|
// And then store it in cache for one day.
|
||||||
|
// This cache is cleared on modifications to the wiki page.
|
||||||
|
// @see MediaWiki\Extension\ConfirmEdit\Hooks::onPageSaveComplete()
|
||||||
|
$cache->set( $cacheKey, $list, 86400 );
|
||||||
|
|
||||||
|
return $list;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* From a list of unvalidated input, get all the valid
|
* From a list of unvalidated input, get all the valid
|
||||||
* IP addresses and IP ranges from it.
|
* IP addresses and IP ranges from it.
|
||||||
*
|
*
|
||||||
* Note that only lines with just the IP address or IP range is considered
|
* Note that only lines with just the IP address or the IP range is considered
|
||||||
* as valid. Whitespace is allowed, but if there is any other character on
|
* as valid. Whitespace is allowed, but if there is any other character on
|
||||||
* the line, it's not considered as a valid entry.
|
* the line, it's not considered as a valid entry.
|
||||||
*
|
*
|
||||||
|
@ -706,12 +707,12 @@ class SimpleCaptcha {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Filter callback function for URL whitelisting
|
* Filter callback function for URL allow-listing
|
||||||
* @param string $url string to check
|
* @param string $url string to check
|
||||||
* @return bool true if unknown, false if whitelisted
|
* @return bool true if unknown, false if allowed
|
||||||
*/
|
*/
|
||||||
private function filterLink( $url ) {
|
private function filterLink( $url ) {
|
||||||
global $wgCaptchaWhitelist;
|
global $wgCaptchaWhitelist, $wgCaptchaIgnoredUrls;
|
||||||
static $regexes = null;
|
static $regexes = null;
|
||||||
|
|
||||||
if ( $regexes === null ) {
|
if ( $regexes === null ) {
|
||||||
|
@ -721,9 +722,13 @@ class SimpleCaptcha {
|
||||||
? []
|
? []
|
||||||
: $this->buildRegexes( explode( "\n", $source->plain() ) );
|
: $this->buildRegexes( explode( "\n", $source->plain() ) );
|
||||||
|
|
||||||
|
// DEPRECATED
|
||||||
if ( $wgCaptchaWhitelist !== false ) {
|
if ( $wgCaptchaWhitelist !== false ) {
|
||||||
array_unshift( $regexes, $wgCaptchaWhitelist );
|
array_unshift( $regexes, $wgCaptchaWhitelist );
|
||||||
}
|
}
|
||||||
|
if ( $wgCaptchaIgnoredUrls !== false ) {
|
||||||
|
array_unshift( $regexes, $wgCaptchaIgnoredUrls );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ( $regexes as $regex ) {
|
foreach ( $regexes as $regex ) {
|
||||||
|
@ -736,8 +741,8 @@ class SimpleCaptcha {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Build regex from whitelist
|
* Build regex from list of URLs
|
||||||
* @param string[] $lines string from [[MediaWiki:Captcha-addurl-whitelist]]
|
* @param string[] $lines string from MediaWiki page
|
||||||
* @return string[] Regexes
|
* @return string[] Regexes
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
|
@ -749,12 +754,12 @@ class SimpleCaptcha {
|
||||||
$lines = array_filter( array_map( 'trim', preg_replace( '/#.*$/', '', $lines ) ) );
|
$lines = array_filter( array_map( 'trim', preg_replace( '/#.*$/', '', $lines ) ) );
|
||||||
|
|
||||||
# No lines, don't make a regex which will match everything
|
# No lines, don't make a regex which will match everything
|
||||||
if ( count( $lines ) == 0 ) {
|
if ( count( $lines ) === 0 ) {
|
||||||
wfDebug( "No lines\n" );
|
wfDebug( "No lines\n" );
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
# Make regex
|
# Make regex
|
||||||
# It's faster using the S modifier even though it will usually only be run once
|
# It's faster using the S modifier even though it will usually only be run once
|
||||||
// $regex = 'http://+[a-z0-9_\-.]*(' . implode( '|', $lines ) . ')';
|
// $regex = 'http://+[a-z0-9_\-.]*(' . implode( '|', $lines ) . ')';
|
||||||
// return '/' . str_replace( '/', '\/', preg_replace('|\\\*/|', '/', $regex) ) . '/Si';
|
// return '/' . str_replace( '/', '\/', preg_replace('|\\\*/|', '/', $regex) ) . '/Si';
|
||||||
|
@ -1241,8 +1246,8 @@ class SimpleCaptcha {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( $this->isIPWhitelisted() ) {
|
if ( $this->canIPBypassCaptcha() ) {
|
||||||
wfDebug( "ConfirmEdit: user IP is whitelisted" );
|
wfDebug( "ConfirmEdit: user IP can bypass captcha" );
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -140,9 +140,9 @@ class CaptchaTest extends MediaWikiIntegrationTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @dataProvider provideCanSkipCaptchaIPWhitelisted
|
* @dataProvider provideCanSkipCaptchaBypassIPList
|
||||||
*/
|
*/
|
||||||
public function testCanSkipCaptchaIPWhitelisted( $requestIP, $IPWhitelist, $expected ) {
|
public function testCanSkipCaptchaBypassIP( $requestIP, $list, $expected ) {
|
||||||
$testObject = new SimpleCaptcha();
|
$testObject = new SimpleCaptcha();
|
||||||
$config = new HashConfig( [ 'AllowConfirmedEmail' => false ] );
|
$config = new HashConfig( [ 'AllowConfirmedEmail' => false ] );
|
||||||
$request = $this->createMock( WebRequest::class );
|
$request = $this->createMock( WebRequest::class );
|
||||||
|
@ -151,14 +151,14 @@ class CaptchaTest extends MediaWikiIntegrationTestCase {
|
||||||
$this->setMwGlobals( [
|
$this->setMwGlobals( [
|
||||||
'wgRequest' => $request,
|
'wgRequest' => $request,
|
||||||
] );
|
] );
|
||||||
$this->overrideConfigValue( 'CaptchaWhitelistIP', $IPWhitelist );
|
$this->overrideConfigValue( 'CaptchaBypassIPs', $list );
|
||||||
|
|
||||||
$actual = $testObject->canSkipCaptcha( RequestContext::getMain()->getUser(), $config );
|
$actual = $testObject->canSkipCaptcha( RequestContext::getMain()->getUser(), $config );
|
||||||
|
|
||||||
$this->assertEquals( $expected, $actual );
|
$this->assertEquals( $expected, $actual );
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function provideCanSkipCaptchaIPWhitelisted() {
|
public static function provideCanSkipCaptchaBypassIPList() {
|
||||||
return ( [
|
return ( [
|
||||||
[ '127.0.0.1', [ '127.0.0.1', '127.0.0.2' ], true ],
|
[ '127.0.0.1', [ '127.0.0.1', '127.0.0.2' ], true ],
|
||||||
[ '127.0.0.1', [], false ]
|
[ '127.0.0.1', [], false ]
|
||||||
|
|
Loading…
Reference in a new issue