mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/ConfirmEdit
synced 2024-11-23 15:56:50 +00:00
Various minor code cleanup
Change-Id: I75f34c66f1c1968cfb9a3e1932068ec2420e0fa6
This commit is contained in:
parent
a801949300
commit
48a60aa762
|
@ -7,7 +7,9 @@ use MediaWiki\Auth\AuthManager;
|
||||||
use MediaWiki\Extension\ConfirmEdit\Hooks;
|
use MediaWiki\Extension\ConfirmEdit\Hooks;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generic captcha authentication request class. A captcha consist some data stored in the session
|
* Generic captcha authentication request class.
|
||||||
|
*
|
||||||
|
* A captcha consists of some data stored in the session
|
||||||
* (e.g. a question and its answer), an ID that references the data, and a solution.
|
* (e.g. a question and its answer), an ID that references the data, and a solution.
|
||||||
*/
|
*/
|
||||||
class CaptchaAuthenticationRequest extends AuthenticationRequest {
|
class CaptchaAuthenticationRequest extends AuthenticationRequest {
|
||||||
|
@ -36,13 +38,11 @@ class CaptchaAuthenticationRequest extends AuthenticationRequest {
|
||||||
return 'CaptchaAuthenticationRequest';
|
return 'CaptchaAuthenticationRequest';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** @inheritDoc */
|
||||||
* @inheritDoc
|
|
||||||
*/
|
|
||||||
public function loadFromSubmission( array $data ) {
|
public function loadFromSubmission( array $data ) {
|
||||||
$success = parent::loadFromSubmission( $data );
|
$success = parent::loadFromSubmission( $data );
|
||||||
if ( $success ) {
|
if ( $success ) {
|
||||||
// captchaId and captchaWord was set from the submission but captchaData was not.
|
// The captchaId and captchaWord was set from the submission, but captchaData was not.
|
||||||
$captcha = Hooks::getInstance();
|
$captcha = Hooks::getInstance();
|
||||||
$this->captchaData = $captcha->retrieveCaptcha( $this->captchaId );
|
$this->captchaData = $captcha->retrieveCaptcha( $this->captchaId );
|
||||||
if ( !$this->captchaData ) {
|
if ( !$this->captchaData ) {
|
||||||
|
@ -52,13 +52,11 @@ class CaptchaAuthenticationRequest extends AuthenticationRequest {
|
||||||
return $success;
|
return $success;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** @inheritDoc */
|
||||||
* @inheritDoc
|
|
||||||
*/
|
|
||||||
public function getFieldInfo() {
|
public function getFieldInfo() {
|
||||||
$captcha = Hooks::getInstance();
|
$captcha = Hooks::getInstance();
|
||||||
|
|
||||||
// doesn't actually exist but *Captcha::getMessage will handle that
|
// generic action doesn't exist, but *Captcha::getMessage will handle that
|
||||||
$action = 'generic';
|
$action = 'generic';
|
||||||
switch ( $this->action ) {
|
switch ( $this->action ) {
|
||||||
case AuthManager::ACTION_LOGIN:
|
case AuthManager::ACTION_LOGIN:
|
||||||
|
@ -69,7 +67,7 @@ class CaptchaAuthenticationRequest extends AuthenticationRequest {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
$fields = [
|
return [
|
||||||
'captchaId' => [
|
'captchaId' => [
|
||||||
'type' => 'hidden',
|
'type' => 'hidden',
|
||||||
'value' => $this->captchaId,
|
'value' => $this->captchaId,
|
||||||
|
@ -88,13 +86,9 @@ class CaptchaAuthenticationRequest extends AuthenticationRequest {
|
||||||
'help' => wfMessage( 'captcha-help' ),
|
'help' => wfMessage( 'captcha-help' ),
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
return $fields;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** @inheritDoc */
|
||||||
* @inheritDoc
|
|
||||||
*/
|
|
||||||
public function getMetadata() {
|
public function getMetadata() {
|
||||||
return ( Hooks::getInstance() )->describeCaptchaType();
|
return ( Hooks::getInstance() )->describeCaptchaType();
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,9 +13,7 @@ use MediaWiki\Status\Status;
|
||||||
use MediaWiki\User\User;
|
use MediaWiki\User\User;
|
||||||
|
|
||||||
class CaptchaPreAuthenticationProvider extends AbstractPreAuthenticationProvider {
|
class CaptchaPreAuthenticationProvider extends AbstractPreAuthenticationProvider {
|
||||||
/**
|
/** @inheritDoc */
|
||||||
* @inheritDoc
|
|
||||||
*/
|
|
||||||
public function getAuthenticationRequests( $action, array $options ) {
|
public function getAuthenticationRequests( $action, array $options ) {
|
||||||
$captcha = Hooks::getInstance();
|
$captcha = Hooks::getInstance();
|
||||||
$user = User::newFromName( $options['username'] );
|
$user = User::newFromName( $options['username'] );
|
||||||
|
@ -47,7 +45,7 @@ class CaptchaPreAuthenticationProvider extends AbstractPreAuthenticationProvider
|
||||||
// failed login attempt using a username that needs a captcha, set a session flag
|
// failed login attempt using a username that needs a captcha, set a session flag
|
||||||
// to display a captcha on login from that point on. This will result in confusing
|
// to display a captcha on login from that point on. This will result in confusing
|
||||||
// error messages if the browser cannot persist the session (because we'll never show
|
// error messages if the browser cannot persist the session (because we'll never show
|
||||||
// a required captcha field), but then login would be impossible anyway so no big deal.
|
// a required captcha field), but then login would be impossible anyway, so no big deal.
|
||||||
|
|
||||||
// If the username ends to be one that does not trigger the captcha, that will
|
// If the username ends to be one that does not trigger the captcha, that will
|
||||||
// result in weird behavior (if the user leaves the captcha field empty, they get
|
// result in weird behavior (if the user leaves the captcha field empty, they get
|
||||||
|
@ -57,8 +55,8 @@ class CaptchaPreAuthenticationProvider extends AbstractPreAuthenticationProvider
|
||||||
$userProbablyNeedsCaptcha = $session->get( 'ConfirmEdit:loginCaptchaPerUserTriggered' );
|
$userProbablyNeedsCaptcha = $session->get( 'ConfirmEdit:loginCaptchaPerUserTriggered' );
|
||||||
$suggestedUsername = $session->suggestLoginUsername();
|
$suggestedUsername = $session->suggestLoginUsername();
|
||||||
if (
|
if (
|
||||||
$loginCounter->isBadLoginTriggered()
|
$userProbablyNeedsCaptcha
|
||||||
|| $userProbablyNeedsCaptcha
|
|| $loginCounter->isBadLoginTriggered()
|
||||||
|| ( $suggestedUsername && $loginCounter->isBadLoginPerUserTriggered( $suggestedUsername ) )
|
|| ( $suggestedUsername && $loginCounter->isBadLoginPerUserTriggered( $suggestedUsername ) )
|
||||||
) {
|
) {
|
||||||
$needed = true;
|
$needed = true;
|
||||||
|
@ -77,21 +75,18 @@ class CaptchaPreAuthenticationProvider extends AbstractPreAuthenticationProvider
|
||||||
|
|
||||||
if ( $needed ) {
|
if ( $needed ) {
|
||||||
return [ $captcha->createAuthenticationRequest() ];
|
return [ $captcha->createAuthenticationRequest() ];
|
||||||
} else {
|
|
||||||
return [];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** @inheritDoc */
|
||||||
* @inheritDoc
|
|
||||||
*/
|
|
||||||
public function testForAuthentication( array $reqs ) {
|
public function testForAuthentication( array $reqs ) {
|
||||||
$captcha = Hooks::getInstance();
|
$captcha = Hooks::getInstance();
|
||||||
$username = AuthenticationRequest::getUsernameFromRequests( $reqs );
|
$username = AuthenticationRequest::getUsernameFromRequests( $reqs );
|
||||||
$loginCounter = $this->getLoginAttemptCounter( $captcha );
|
$loginCounter = $this->getLoginAttemptCounter( $captcha );
|
||||||
$success = true;
|
$success = true;
|
||||||
$isBadLoginPerUserTriggered = $username ?
|
$isBadLoginPerUserTriggered = $username && $loginCounter->isBadLoginPerUserTriggered( $username );
|
||||||
$loginCounter->isBadLoginPerUserTriggered( $username ) : false;
|
|
||||||
|
|
||||||
if ( $loginCounter->isBadLoginTriggered() || $isBadLoginPerUserTriggered ) {
|
if ( $loginCounter->isBadLoginTriggered() || $isBadLoginPerUserTriggered ) {
|
||||||
$captcha->setAction( 'badlogin' );
|
$captcha->setAction( 'badlogin' );
|
||||||
|
@ -118,9 +113,7 @@ class CaptchaPreAuthenticationProvider extends AbstractPreAuthenticationProvider
|
||||||
return $success ? Status::newGood() : $this->makeError( 'wrongpassword', $captcha );
|
return $success ? Status::newGood() : $this->makeError( 'wrongpassword', $captcha );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** @inheritDoc */
|
||||||
* @inheritDoc
|
|
||||||
*/
|
|
||||||
public function testForAccountCreation( $user, $creator, array $reqs ) {
|
public function testForAccountCreation( $user, $creator, array $reqs ) {
|
||||||
$captcha = Hooks::getInstance();
|
$captcha = Hooks::getInstance();
|
||||||
|
|
||||||
|
@ -146,9 +139,7 @@ class CaptchaPreAuthenticationProvider extends AbstractPreAuthenticationProvider
|
||||||
return Status::newGood();
|
return Status::newGood();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** @inheritDoc */
|
||||||
* @inheritDoc
|
|
||||||
*/
|
|
||||||
public function postAuthentication( $user, AuthenticationResponse $response ) {
|
public function postAuthentication( $user, AuthenticationResponse $response ) {
|
||||||
$captcha = Hooks::getInstance();
|
$captcha = Hooks::getInstance();
|
||||||
$loginCounter = $this->getLoginAttemptCounter( $captcha );
|
$loginCounter = $this->getLoginAttemptCounter( $captcha );
|
||||||
|
@ -167,7 +158,7 @@ class CaptchaPreAuthenticationProvider extends AbstractPreAuthenticationProvider
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Verify submitted captcha.
|
* Verify submitted captcha.
|
||||||
* Assumes that the user has to pass the capctha (permission checks are caller's responsibility).
|
* Assumes that the user has to pass the captcha (permission checks are caller's responsibility).
|
||||||
* @param SimpleCaptcha $captcha
|
* @param SimpleCaptcha $captcha
|
||||||
* @param AuthenticationRequest[] $reqs
|
* @param AuthenticationRequest[] $reqs
|
||||||
* @param User $user
|
* @param User $user
|
||||||
|
|
|
@ -14,6 +14,5 @@ abstract class CaptchaTriggers {
|
||||||
public const CREATE_ACCOUNT = 'createaccount';
|
public const CREATE_ACCOUNT = 'createaccount';
|
||||||
public const BAD_LOGIN = 'badlogin';
|
public const BAD_LOGIN = 'badlogin';
|
||||||
public const BAD_LOGIN_PER_USER = 'badloginperuser';
|
public const BAD_LOGIN_PER_USER = 'badloginperuser';
|
||||||
|
|
||||||
public const EXT_REG_ATTRIBUTE_NAME = 'CaptchaTriggers';
|
public const EXT_REG_ATTRIBUTE_NAME = 'CaptchaTriggers';
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,15 +8,13 @@ namespace MediaWiki\Extension\ConfirmEdit;
|
||||||
class CaptchaValue {
|
class CaptchaValue {
|
||||||
/**
|
/**
|
||||||
* ID that is used to store the captcha in cache.
|
* ID that is used to store the captcha in cache.
|
||||||
* @var string
|
|
||||||
*/
|
*/
|
||||||
protected $id;
|
protected string $id;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Answer to the captcha.
|
* Answer to the captcha.
|
||||||
* @var string
|
|
||||||
*/
|
*/
|
||||||
protected $solution;
|
protected string $solution;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var mixed
|
* @var mixed
|
||||||
|
|
|
@ -12,7 +12,6 @@ use MediaWiki\Api\ApiBase;
|
||||||
*/
|
*/
|
||||||
class ApiFancyCaptchaReload extends ApiBase {
|
class ApiFancyCaptchaReload extends ApiBase {
|
||||||
public function execute() {
|
public function execute() {
|
||||||
# Get a new FancyCaptcha form data
|
|
||||||
$captcha = new FancyCaptcha();
|
$captcha = new FancyCaptcha();
|
||||||
$info = $captcha->getCaptcha();
|
$info = $captcha->getCaptcha();
|
||||||
$captchaIndex = $captcha->storeCaptcha( $info );
|
$captchaIndex = $captcha->storeCaptcha( $info );
|
||||||
|
|
|
@ -19,7 +19,7 @@ use Wikimedia\FileBackend\FileBackend;
|
||||||
use Wikimedia\FileBackend\FSFileBackend;
|
use Wikimedia\FileBackend\FSFileBackend;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* FancyCaptcha for displaying captchas precomputed by captcha.py
|
* FancyCaptcha for displaying captcha images precomputed by captcha.py
|
||||||
*/
|
*/
|
||||||
class FancyCaptcha extends SimpleCaptcha {
|
class FancyCaptcha extends SimpleCaptcha {
|
||||||
/**
|
/**
|
||||||
|
@ -114,9 +114,7 @@ class FancyCaptcha extends SimpleCaptcha {
|
||||||
$resultArr['captcha']['url'] = $title->getLocalURL( 'wpCaptchaId=' . urlencode( $index ) );
|
$resultArr['captcha']['url'] = $title->getLocalURL( 'wpCaptchaId=' . urlencode( $index ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** @inheritDoc */
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public function describeCaptchaType() {
|
public function describeCaptchaType() {
|
||||||
return [
|
return [
|
||||||
'type' => 'image',
|
'type' => 'image',
|
||||||
|
@ -124,10 +122,7 @@ class FancyCaptcha extends SimpleCaptcha {
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** @inheritDoc */
|
||||||
* @param int $tabIndex
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public function getFormInformation( $tabIndex = 1 ) {
|
public function getFormInformation( $tabIndex = 1 ) {
|
||||||
$title = SpecialPage::getTitleFor( 'Captcha', 'image' );
|
$title = SpecialPage::getTitleFor( 'Captcha', 'image' );
|
||||||
$info = $this->getCaptcha();
|
$info = $this->getCaptcha();
|
||||||
|
@ -163,7 +158,7 @@ class FancyCaptcha extends SimpleCaptcha {
|
||||||
'class' => 'cdx-text-input__input',
|
'class' => 'cdx-text-input__input',
|
||||||
'id' => 'wpCaptchaWord',
|
'id' => 'wpCaptchaWord',
|
||||||
'type' => 'text',
|
'type' => 'text',
|
||||||
// max_length in captcha.py plus fudge factor
|
// max_length in captcha.py plus some fudge factor
|
||||||
'size' => '12',
|
'size' => '12',
|
||||||
'autocomplete' => 'off',
|
'autocomplete' => 'off',
|
||||||
'autocorrect' => 'off',
|
'autocorrect' => 'off',
|
||||||
|
@ -175,7 +170,7 @@ class FancyCaptcha extends SimpleCaptcha {
|
||||||
]
|
]
|
||||||
) . Html::closeElement( 'div' );
|
) . Html::closeElement( 'div' );
|
||||||
if ( $this->action == 'createaccount' ) {
|
if ( $this->action == 'createaccount' ) {
|
||||||
// use raw element, because the message can contain links or some other html
|
// use a raw element, because the message can contain links or some other html
|
||||||
$form .= Html::rawElement( 'small', [
|
$form .= Html::rawElement( 'small', [
|
||||||
'class' => 'mw-createacct-captcha-assisted'
|
'class' => 'mw-createacct-captcha-assisted'
|
||||||
], wfMessage( 'createacct-imgcaptcha-help' )->parse()
|
], wfMessage( 'createacct-imgcaptcha-help' )->parse()
|
||||||
|
@ -236,7 +231,7 @@ class FancyCaptcha extends SimpleCaptcha {
|
||||||
if ( !is_array( $dirs ) || !count( $dirs ) ) {
|
if ( !is_array( $dirs ) || !count( $dirs ) ) {
|
||||||
// cache miss
|
// cache miss
|
||||||
$dirs = [];
|
$dirs = [];
|
||||||
// subdirs actually present...
|
// subdirs are actually present...
|
||||||
foreach ( $backend->getTopDirectoryList( [ 'dir' => $directory ] ) as $entry ) {
|
foreach ( $backend->getTopDirectoryList( [ 'dir' => $directory ] ) as $entry ) {
|
||||||
if ( ctype_xdigit( $entry ) && strlen( $entry ) == 1 ) {
|
if ( ctype_xdigit( $entry ) && strlen( $entry ) == 1 ) {
|
||||||
$dirs[] = $entry;
|
$dirs[] = $entry;
|
||||||
|
@ -249,7 +244,7 @@ class FancyCaptcha extends SimpleCaptcha {
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( !count( $dirs ) ) {
|
if ( !count( $dirs ) ) {
|
||||||
// Remove this directory if empty so callers don't keep looking here
|
// Remove this directory if empty, so callers don't keep looking here
|
||||||
$backend->clean( [ 'dir' => $directory ] );
|
$backend->clean( [ 'dir' => $directory ] );
|
||||||
// none found
|
// none found
|
||||||
return false;
|
return false;
|
||||||
|
@ -257,7 +252,7 @@ class FancyCaptcha extends SimpleCaptcha {
|
||||||
|
|
||||||
// pick a random subdir
|
// pick a random subdir
|
||||||
$place = mt_rand( 0, count( $dirs ) - 1 );
|
$place = mt_rand( 0, count( $dirs ) - 1 );
|
||||||
// In case all dirs are not filled, cycle through next digits...
|
// In case all dirs are not filled, cycle through the next digits...
|
||||||
$fancyCount = count( $dirs );
|
$fancyCount = count( $dirs );
|
||||||
for ( $j = 0; $j < $fancyCount; $j++ ) {
|
for ( $j = 0; $j < $fancyCount; $j++ ) {
|
||||||
$char = $dirs[( $place + $j ) % count( $dirs )];
|
$char = $dirs[( $place + $j ) % count( $dirs )];
|
||||||
|
@ -311,7 +306,7 @@ class FancyCaptcha extends SimpleCaptcha {
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( !count( $files ) ) {
|
if ( !count( $files ) ) {
|
||||||
// Remove this directory if empty so callers don't keep looking here
|
// Remove this directory if empty, so callers don't keep looking here
|
||||||
$backend->clean( [ 'dir' => $directory ] );
|
$backend->clean( [ 'dir' => $directory ] );
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -370,7 +365,7 @@ class FancyCaptcha extends SimpleCaptcha {
|
||||||
// listing cache too stale? break out so it will be cleared
|
// listing cache too stale? break out so it will be cleared
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// try next file
|
// try the next file
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
return [
|
return [
|
||||||
|
@ -434,11 +429,14 @@ class FancyCaptcha extends SimpleCaptcha {
|
||||||
* @return array (salt, hash)
|
* @return array (salt, hash)
|
||||||
*/
|
*/
|
||||||
public function hashFromImageName( $basename ) {
|
public function hashFromImageName( $basename ) {
|
||||||
if ( preg_match( '/^image_([0-9a-f]+)_([0-9a-f]+)\\.png$/', $basename, $matches ) ) {
|
if ( !preg_match( '/^image_([0-9a-f]+)_([0-9a-f]+)\\.png$/', $basename, $matches ) ) {
|
||||||
return [ $matches[1], $matches[2] ];
|
|
||||||
} else {
|
|
||||||
throw new InvalidArgumentException( "Invalid filename '$basename'.\n" );
|
throw new InvalidArgumentException( "Invalid filename '$basename'.\n" );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
$matches[1],
|
||||||
|
$matches[2]
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -481,8 +479,8 @@ class FancyCaptcha extends SimpleCaptcha {
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public function getCaptchaInfo( $captchaData, $id ) {
|
public function getCaptchaInfo( $captchaData, $id ) {
|
||||||
$title = SpecialPage::getTitleFor( 'Captcha', 'image' );
|
return SpecialPage::getTitleFor( 'Captcha', 'image' )
|
||||||
return $title->getLocalURL( 'wpCaptchaId=' . urlencode( $id ) );
|
->getLocalURL( 'wpCaptchaId=' . urlencode( $id ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -31,9 +31,7 @@ class HTMLFancyCaptchaField extends HTMLFormField {
|
||||||
$this->showCreateHelp = !empty( $params['showCreateHelp'] );
|
$this->showCreateHelp = !empty( $params['showCreateHelp'] );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** @inheritDoc */
|
||||||
* @inheritDoc
|
|
||||||
*/
|
|
||||||
public function getInputHTML( $value ) {
|
public function getInputHTML( $value ) {
|
||||||
$out = $this->mParent->getOutput();
|
$out = $this->mParent->getOutput();
|
||||||
|
|
||||||
|
@ -54,7 +52,7 @@ class HTMLFancyCaptchaField extends HTMLFormField {
|
||||||
'id' => $this->mID,
|
'id' => $this->mID,
|
||||||
'name' => $this->mName,
|
'name' => $this->mName,
|
||||||
'class' => 'cdx-text-input__input',
|
'class' => 'cdx-text-input__input',
|
||||||
// max_length in captcha.py plus fudge factor
|
// max_length in captcha.py plus a fudge factor
|
||||||
'size' => '12',
|
'size' => '12',
|
||||||
'dir' => $this->mDir,
|
'dir' => $this->mDir,
|
||||||
'autocomplete' => 'off',
|
'autocomplete' => 'off',
|
||||||
|
@ -76,7 +74,7 @@ class HTMLFancyCaptchaField extends HTMLFormField {
|
||||||
. Html::element( 'input', $attribs ) . Html::closeElement( 'div' );
|
. Html::element( 'input', $attribs ) . Html::closeElement( 'div' );
|
||||||
|
|
||||||
if ( $this->showCreateHelp ) {
|
if ( $this->showCreateHelp ) {
|
||||||
// use raw element, the message will contain a link
|
// use a raw element, the message will contain a link
|
||||||
$html .= Html::rawElement( 'small', [
|
$html .= Html::rawElement( 'small', [
|
||||||
'class' => 'mw-createacct-captcha-assisted'
|
'class' => 'mw-createacct-captcha-assisted'
|
||||||
], $this->mParent->msg( 'createacct-imgcaptcha-help' )->parse() );
|
], $this->mParent->msg( 'createacct-imgcaptcha-help' )->parse() );
|
||||||
|
@ -87,9 +85,7 @@ class HTMLFancyCaptchaField extends HTMLFormField {
|
||||||
return $html;
|
return $html;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** @inheritDoc */
|
||||||
* @inheritDoc
|
|
||||||
*/
|
|
||||||
public function getLabel() {
|
public function getLabel() {
|
||||||
// slight abuse of what getLabel() should mean; $mLabel is used for the pre-label text
|
// slight abuse of what getLabel() should mean; $mLabel is used for the pre-label text
|
||||||
// as the actual label is always the same
|
// as the actual label is always the same
|
||||||
|
@ -97,13 +93,11 @@ class HTMLFancyCaptchaField extends HTMLFormField {
|
||||||
. $this->mParent->msg( 'fancycaptcha-captcha' )->text();
|
. $this->mParent->msg( 'fancycaptcha-captcha' )->text();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** @inheritDoc */
|
||||||
* @inheritDoc
|
|
||||||
*/
|
|
||||||
public function getLabelHtml( $cellAttributes = [] ) {
|
public function getLabelHtml( $cellAttributes = [] ) {
|
||||||
$labelHtml = parent::getLabelHtml( $cellAttributes );
|
$labelHtml = parent::getLabelHtml( $cellAttributes );
|
||||||
if ( $this->mLabel ) {
|
if ( $this->mLabel ) {
|
||||||
// use raw element, the message will contain a link
|
// use a raw element, the message will contain a link
|
||||||
$labelHtml = Html::rawElement( 'p', [], $this->mLabel ) . $labelHtml;
|
$labelHtml = Html::rawElement( 'p', [], $this->mLabel ) . $labelHtml;
|
||||||
}
|
}
|
||||||
return $labelHtml;
|
return $labelHtml;
|
||||||
|
|
|
@ -4,12 +4,9 @@
|
||||||
|
|
||||||
namespace MediaWiki\Extension\ConfirmEdit;
|
namespace MediaWiki\Extension\ConfirmEdit;
|
||||||
|
|
||||||
use MailAddress;
|
|
||||||
use MediaWiki\Api\ApiBase;
|
|
||||||
use MediaWiki\Api\Hook\APIGetAllowedParamsHook;
|
use MediaWiki\Api\Hook\APIGetAllowedParamsHook;
|
||||||
use MediaWiki\Content\Content;
|
use MediaWiki\Content\Content;
|
||||||
use MediaWiki\Context\IContextSource;
|
use MediaWiki\Context\IContextSource;
|
||||||
use MediaWiki\EditPage\EditPage;
|
|
||||||
use MediaWiki\Extension\ConfirmEdit\SimpleCaptcha\SimpleCaptcha;
|
use MediaWiki\Extension\ConfirmEdit\SimpleCaptcha\SimpleCaptcha;
|
||||||
use MediaWiki\Hook\AlternateEditPreviewHook;
|
use MediaWiki\Hook\AlternateEditPreviewHook;
|
||||||
use MediaWiki\Hook\EditFilterMergedContentHook;
|
use MediaWiki\Hook\EditFilterMergedContentHook;
|
||||||
|
@ -18,26 +15,18 @@ use MediaWiki\Hook\EditPageBeforeEditButtonsHook;
|
||||||
use MediaWiki\Hook\EmailUserFormHook;
|
use MediaWiki\Hook\EmailUserFormHook;
|
||||||
use MediaWiki\Hook\EmailUserHook;
|
use MediaWiki\Hook\EmailUserHook;
|
||||||
use MediaWiki\Html\Html;
|
use MediaWiki\Html\Html;
|
||||||
use MediaWiki\HTMLForm\HTMLForm;
|
|
||||||
use MediaWiki\Output\OutputPage;
|
|
||||||
use MediaWiki\Parser\ParserOutput;
|
|
||||||
use MediaWiki\Permissions\Hook\TitleReadWhitelistHook;
|
use MediaWiki\Permissions\Hook\TitleReadWhitelistHook;
|
||||||
use MediaWiki\Registration\ExtensionRegistry;
|
use MediaWiki\Registration\ExtensionRegistry;
|
||||||
use MediaWiki\ResourceLoader\Hook\ResourceLoaderRegisterModulesHook;
|
use MediaWiki\ResourceLoader\Hook\ResourceLoaderRegisterModulesHook;
|
||||||
use MediaWiki\ResourceLoader\ResourceLoader;
|
use MediaWiki\ResourceLoader\ResourceLoader;
|
||||||
use MediaWiki\Revision\RevisionRecord;
|
|
||||||
use MediaWiki\SpecialPage\Hook\AuthChangeFormFieldsHook;
|
use MediaWiki\SpecialPage\Hook\AuthChangeFormFieldsHook;
|
||||||
use MediaWiki\SpecialPage\SpecialPage;
|
use MediaWiki\SpecialPage\SpecialPage;
|
||||||
use MediaWiki\Status\Status;
|
use MediaWiki\Status\Status;
|
||||||
use MediaWiki\Storage\EditResult;
|
|
||||||
use MediaWiki\Storage\Hook\PageSaveCompleteHook;
|
use MediaWiki\Storage\Hook\PageSaveCompleteHook;
|
||||||
use MediaWiki\Title\Title;
|
use MediaWiki\Title\Title;
|
||||||
use MediaWiki\User\User;
|
use MediaWiki\User\User;
|
||||||
use MediaWiki\User\UserIdentity;
|
|
||||||
use Wikimedia\IPUtils;
|
use Wikimedia\IPUtils;
|
||||||
use Wikimedia\Message\MessageSpecifier;
|
|
||||||
use Wikimedia\ObjectCache\WANObjectCache;
|
use Wikimedia\ObjectCache\WANObjectCache;
|
||||||
use WikiPage;
|
|
||||||
|
|
||||||
class Hooks implements
|
class Hooks implements
|
||||||
AlternateEditPreviewHook,
|
AlternateEditPreviewHook,
|
||||||
|
@ -81,38 +70,20 @@ class Hooks implements
|
||||||
return $wgCaptcha;
|
return $wgCaptcha;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** @inheritDoc */
|
||||||
* @param IContextSource $context
|
|
||||||
* @param Content $content
|
|
||||||
* @param Status $status
|
|
||||||
* @param string $summary
|
|
||||||
* @param User $user
|
|
||||||
* @param bool $minorEdit
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public function onEditFilterMergedContent( IContextSource $context, Content $content, Status $status,
|
public function onEditFilterMergedContent( IContextSource $context, Content $content, Status $status,
|
||||||
$summary, User $user, $minorEdit
|
$summary, User $user, $minorEdit
|
||||||
) {
|
) {
|
||||||
$simpleCaptcha = self::getInstance();
|
$simpleCaptcha = self::getInstance();
|
||||||
// Set a flag indicating that ConfirmEdit's implementation of
|
// Set a flag indicating that ConfirmEdit's implementation of
|
||||||
// EditFilterMergedContent ran. This can be checked by other extensions
|
// EditFilterMergedContent ran.
|
||||||
// e.g. AbuseFilter.
|
// This can be checked by other MediaWiki extensions, e.g. AbuseFilter.
|
||||||
$simpleCaptcha->setEditFilterMergedContentHandlerInvoked();
|
$simpleCaptcha->setEditFilterMergedContentHandlerInvoked();
|
||||||
return $simpleCaptcha->confirmEditMerged( $context, $content, $status, $summary,
|
return $simpleCaptcha->confirmEditMerged( $context, $content, $status, $summary,
|
||||||
$user, $minorEdit );
|
$user, $minorEdit );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** @inheritDoc */
|
||||||
* @see https://www.mediawiki.org/wiki/Manual:Hooks/PageSaveComplete
|
|
||||||
*
|
|
||||||
* @param WikiPage $wikiPage
|
|
||||||
* @param UserIdentity $user
|
|
||||||
* @param string $summary
|
|
||||||
* @param int $flags
|
|
||||||
* @param RevisionRecord $revisionRecord
|
|
||||||
* @param EditResult $editResult
|
|
||||||
* @return bool|void
|
|
||||||
*/
|
|
||||||
public function onPageSaveComplete(
|
public function onPageSaveComplete(
|
||||||
$wikiPage,
|
$wikiPage,
|
||||||
$user,
|
$user,
|
||||||
|
@ -129,68 +100,32 @@ class Hooks implements
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** @inheritDoc */
|
||||||
* @see https://www.mediawiki.org/wiki/Manual:Hooks/EditPageBeforeEditButtons
|
|
||||||
*
|
|
||||||
* @param EditPage $editpage Current EditPage object
|
|
||||||
* @param array &$buttons Array of edit buttons, "Save", "Preview", "Live", and "Diff"
|
|
||||||
* @param int &$tabindex HTML tabindex of the last edit check/button
|
|
||||||
*/
|
|
||||||
public function onEditPageBeforeEditButtons( $editpage, &$buttons, &$tabindex ) {
|
public function onEditPageBeforeEditButtons( $editpage, &$buttons, &$tabindex ) {
|
||||||
self::getInstance()->editShowCaptcha( $editpage );
|
self::getInstance()->editShowCaptcha( $editpage );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** @inheritDoc */
|
||||||
* @param EditPage $editPage
|
|
||||||
* @param OutputPage $out
|
|
||||||
*/
|
|
||||||
public function onEditPage__showEditForm_fields( $editPage, $out ) {
|
public function onEditPage__showEditForm_fields( $editPage, $out ) {
|
||||||
self::getInstance()->showEditFormFields( $editPage, $out );
|
self::getInstance()->showEditFormFields( $editPage, $out );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** @inheritDoc */
|
||||||
* @see https://www.mediawiki.org/wiki/Manual:Hooks/EmailUserForm
|
|
||||||
*
|
|
||||||
* @param HTMLForm &$form HTMLForm object
|
|
||||||
* @return bool|void True or no return value to continue or false to abort
|
|
||||||
*/
|
|
||||||
public function onEmailUserForm( &$form ) {
|
public function onEmailUserForm( &$form ) {
|
||||||
return self::getInstance()->injectEmailUser( $form );
|
return self::getInstance()->injectEmailUser( $form );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** @inheritDoc */
|
||||||
* @see https://www.mediawiki.org/wiki/Manual:Hooks/EmailUser
|
|
||||||
*
|
|
||||||
* @param MailAddress &$to MailAddress object of receiving user
|
|
||||||
* @param MailAddress &$from MailAddress object of sending user
|
|
||||||
* @param string &$subject subject of the mail
|
|
||||||
* @param string &$text text of the mail
|
|
||||||
* @param bool|Status|MessageSpecifier|array &$error Out-param for an error.
|
|
||||||
* Should be set to a Status object or boolean false.
|
|
||||||
* @return bool|void True or no return value to continue or false to abort
|
|
||||||
*/
|
|
||||||
public function onEmailUser( &$to, &$from, &$subject, &$text, &$error ) {
|
public function onEmailUser( &$to, &$from, &$subject, &$text, &$error ) {
|
||||||
return self::getInstance()->confirmEmailUser( $from, $to, $subject, $text, $error );
|
return self::getInstance()->confirmEmailUser( $from, $to, $subject, $text, $error );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** @inheritDoc */
|
||||||
* APIGetAllowedParams hook handler
|
|
||||||
* Default $flags to 1 for backwards-compatible behavior
|
|
||||||
* @param ApiBase $module
|
|
||||||
* @param array &$params
|
|
||||||
* @param int $flags
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public function onAPIGetAllowedParams( $module, &$params, $flags ) {
|
public function onAPIGetAllowedParams( $module, &$params, $flags ) {
|
||||||
return self::getInstance()->apiGetAllowedParams( $module, $params, $flags );
|
return self::getInstance()->apiGetAllowedParams( $module, $params, $flags );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** @inheritDoc */
|
||||||
* @param array $requests
|
|
||||||
* @param array $fieldInfo
|
|
||||||
* @param array &$formDescriptor
|
|
||||||
* @param string $action
|
|
||||||
*/
|
|
||||||
public function onAuthChangeFormFields(
|
public function onAuthChangeFormFields(
|
||||||
$requests, $fieldInfo, &$formDescriptor, $action
|
$requests, $fieldInfo, &$formDescriptor, $action
|
||||||
) {
|
) {
|
||||||
|
@ -206,14 +141,7 @@ class Hooks implements
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** @inheritDoc */
|
||||||
* @see https://www.mediawiki.org/wiki/Manual:Hooks/TitleReadWhitelist
|
|
||||||
*
|
|
||||||
* @param Title $title
|
|
||||||
* @param User $user
|
|
||||||
* @param bool &$whitelisted
|
|
||||||
* @return bool|void
|
|
||||||
*/
|
|
||||||
public function onTitleReadWhitelist( $title, $user, &$whitelisted ) {
|
public function onTitleReadWhitelist( $title, $user, &$whitelisted ) {
|
||||||
$image = SpecialPage::getTitleFor( 'Captcha', 'image' );
|
$image = SpecialPage::getTitleFor( 'Captcha', 'image' );
|
||||||
$help = SpecialPage::getTitleFor( 'Captcha', 'help' );
|
$help = SpecialPage::getTitleFor( 'Captcha', 'help' );
|
||||||
|
@ -223,7 +151,6 @@ class Hooks implements
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
|
||||||
* Callback for extension.json of FancyCaptcha to set a default captcha directory,
|
* Callback for extension.json of FancyCaptcha to set a default captcha directory,
|
||||||
* which depends on wgUploadDirectory
|
* which depends on wgUploadDirectory
|
||||||
*/
|
*/
|
||||||
|
@ -234,15 +161,7 @@ class Hooks implements
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** @inheritDoc */
|
||||||
* @see https://www.mediawiki.org/wiki/Manual:Hooks/AlternateEditPreview
|
|
||||||
*
|
|
||||||
* @param EditPage $editPage
|
|
||||||
* @param Content &$content
|
|
||||||
* @param string &$previewHTML
|
|
||||||
* @param ParserOutput &$parserOutput
|
|
||||||
* @return bool|void
|
|
||||||
*/
|
|
||||||
public function onAlternateEditPreview( $editPage, &$content, &$previewHTML,
|
public function onAlternateEditPreview( $editPage, &$content, &$previewHTML,
|
||||||
&$parserOutput
|
&$parserOutput
|
||||||
) {
|
) {
|
||||||
|
@ -312,12 +231,7 @@ class Hooks implements
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** @inheritDoc */
|
||||||
* @see https://www.mediawiki.org/wiki/Manual:Hooks/ResourceLoaderRegisterModules
|
|
||||||
*
|
|
||||||
* @param ResourceLoader $resourceLoader
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function onResourceLoaderRegisterModules( ResourceLoader $resourceLoader ): void {
|
public function onResourceLoaderRegisterModules( ResourceLoader $resourceLoader ): void {
|
||||||
$extensionRegistry = ExtensionRegistry::getInstance();
|
$extensionRegistry = ExtensionRegistry::getInstance();
|
||||||
$messages = [];
|
$messages = [];
|
||||||
|
|
|
@ -31,8 +31,7 @@ use MediaWiki\Page\PageIdentity;
|
||||||
class HookRunner implements
|
class HookRunner implements
|
||||||
ConfirmEditTriggersCaptchaHook
|
ConfirmEditTriggersCaptchaHook
|
||||||
{
|
{
|
||||||
/** @var HookContainer */
|
private HookContainer $hookContainer;
|
||||||
private $hookContainer;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param HookContainer $hookContainer
|
* @param HookContainer $hookContainer
|
||||||
|
@ -41,9 +40,7 @@ class HookRunner implements
|
||||||
$this->hookContainer = $hookContainer;
|
$this->hookContainer = $hookContainer;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** @inheritDoc */
|
||||||
* @inheritDoc
|
|
||||||
*/
|
|
||||||
public function onConfirmEditTriggersCaptcha(
|
public function onConfirmEditTriggersCaptcha(
|
||||||
string $action,
|
string $action,
|
||||||
?PageIdentity $page,
|
?PageIdentity $page,
|
||||||
|
|
|
@ -42,20 +42,7 @@ class QuestyCaptcha extends SimpleCaptcha {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** @inheritDoc */
|
||||||
* @param array &$resultArr
|
|
||||||
*/
|
|
||||||
protected function addCaptchaAPI( &$resultArr ) {
|
|
||||||
$captcha = $this->getCaptcha();
|
|
||||||
$index = $this->storeCaptcha( $captcha );
|
|
||||||
$resultArr['captcha'] = $this->describeCaptchaType();
|
|
||||||
$resultArr['captcha']['id'] = $index;
|
|
||||||
$resultArr['captcha']['question'] = $captcha['question'];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public function describeCaptchaType() {
|
public function describeCaptchaType() {
|
||||||
return [
|
return [
|
||||||
'type' => 'question',
|
'type' => 'question',
|
||||||
|
@ -63,9 +50,7 @@ class QuestyCaptcha extends SimpleCaptcha {
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** @inheritDoc */
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public function getCaptcha() {
|
public function getCaptcha() {
|
||||||
global $wgCaptchaQuestions;
|
global $wgCaptchaQuestions;
|
||||||
|
|
||||||
|
|
|
@ -32,9 +32,7 @@ class HTMLReCaptchaNoCaptchaField extends HTMLFormField {
|
||||||
$this->mName = 'g-recaptcha-response';
|
$this->mName = 'g-recaptcha-response';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** @inheritDoc */
|
||||||
* @inheritDoc
|
|
||||||
*/
|
|
||||||
public function getInputHTML( $value ) {
|
public function getInputHTML( $value ) {
|
||||||
$out = $this->mParent->getOutput();
|
$out = $this->mParent->getOutput();
|
||||||
$lang = htmlspecialchars( urlencode( $this->mParent->getLanguage()->getCode() ) );
|
$lang = htmlspecialchars( urlencode( $this->mParent->getLanguage()->getCode() ) );
|
||||||
|
|
|
@ -77,9 +77,7 @@ HTML;
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** @inheritDoc */
|
||||||
* @return string[]
|
|
||||||
*/
|
|
||||||
public static function getCSPUrls() {
|
public static function getCSPUrls() {
|
||||||
return [ 'https://www.recaptcha.net/recaptcha/api.js' ];
|
return [ 'https://www.recaptcha.net/recaptcha/api.js' ];
|
||||||
}
|
}
|
||||||
|
@ -213,33 +211,25 @@ HTML;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** @inheritDoc */
|
||||||
* @inheritDoc
|
|
||||||
*/
|
|
||||||
public function getError() {
|
public function getError() {
|
||||||
return $this->error;
|
return $this->error;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** @inheritDoc */
|
||||||
* @inheritDoc
|
|
||||||
*/
|
|
||||||
public function storeCaptcha( $info ) {
|
public function storeCaptcha( $info ) {
|
||||||
// ReCaptcha is stored by Google; the ID will be generated at that time as well, and
|
// ReCaptcha is stored by Google; the ID will be generated at that time as well, and
|
||||||
// the one returned here won't be used. Just pretend this worked.
|
// the one returned here won't be used. Just pretend this worked.
|
||||||
return 'not used';
|
return 'not used';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** @inheritDoc */
|
||||||
* @inheritDoc
|
|
||||||
*/
|
|
||||||
public function retrieveCaptcha( $index ) {
|
public function retrieveCaptcha( $index ) {
|
||||||
// just pretend it worked
|
// just pretend it worked
|
||||||
return [ 'index' => $index ];
|
return [ 'index' => $index ];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** @inheritDoc */
|
||||||
* @inheritDoc
|
|
||||||
*/
|
|
||||||
public function getCaptcha() {
|
public function getCaptcha() {
|
||||||
// ReCaptcha is handled by frontend code + an external provider; nothing to do here.
|
// ReCaptcha is handled by frontend code + an external provider; nothing to do here.
|
||||||
return [];
|
return [];
|
||||||
|
|
|
@ -6,7 +6,7 @@ use MediaWiki\Auth\AuthenticationRequest;
|
||||||
use MediaWiki\Extension\ConfirmEdit\Auth\CaptchaAuthenticationRequest;
|
use MediaWiki\Extension\ConfirmEdit\Auth\CaptchaAuthenticationRequest;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Authentication request for ReCaptcha v2. Unlike the parent class, no session storage is used
|
* Authentication request for ReCaptcha v2. Unlike the parent class, no session storage is used,
|
||||||
* and there is no ID; Google provides a single proof string after successfully solving a captcha.
|
* and there is no ID; Google provides a single proof string after successfully solving a captcha.
|
||||||
*/
|
*/
|
||||||
class ReCaptchaNoCaptchaAuthenticationRequest extends CaptchaAuthenticationRequest {
|
class ReCaptchaNoCaptchaAuthenticationRequest extends CaptchaAuthenticationRequest {
|
||||||
|
@ -14,17 +14,13 @@ class ReCaptchaNoCaptchaAuthenticationRequest extends CaptchaAuthenticationReque
|
||||||
parent::__construct( '', [] );
|
parent::__construct( '', [] );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** @inheritDoc */
|
||||||
* @inheritDoc
|
|
||||||
*/
|
|
||||||
public function loadFromSubmission( array $data ) {
|
public function loadFromSubmission( array $data ) {
|
||||||
// unhack the hack in parent
|
// unhack the hack in parent
|
||||||
return AuthenticationRequest::loadFromSubmission( $data );
|
return AuthenticationRequest::loadFromSubmission( $data );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** @inheritDoc */
|
||||||
* @inheritDoc
|
|
||||||
*/
|
|
||||||
public function getFieldInfo() {
|
public function getFieldInfo() {
|
||||||
$fieldInfo = parent::getFieldInfo();
|
$fieldInfo = parent::getFieldInfo();
|
||||||
|
|
||||||
|
|
|
@ -115,7 +115,7 @@ class SimpleCaptcha {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a list of activate captchas for a page by key.
|
* Returns a list of activated captchas for a page by key.
|
||||||
* @return bool[]
|
* @return bool[]
|
||||||
*/
|
*/
|
||||||
public function getActivatedCaptchas() {
|
public function getActivatedCaptchas() {
|
||||||
|
@ -153,7 +153,7 @@ class SimpleCaptcha {
|
||||||
* Override this!
|
* Override this!
|
||||||
*
|
*
|
||||||
* It is not guaranteed that the CAPTCHA will load synchronously with the main page
|
* It is not guaranteed that the CAPTCHA will load synchronously with the main page
|
||||||
* content. So you can not rely on registering handlers before page load. E.g.:
|
* content. So you cannot rely on registering handlers before page load. E.g.:
|
||||||
*
|
*
|
||||||
* NOT SAFE: $( window ).on( 'load', handler )
|
* NOT SAFE: $( window ).on( 'load', handler )
|
||||||
* SAFE: $( handler )
|
* SAFE: $( handler )
|
||||||
|
@ -214,7 +214,7 @@ class SimpleCaptcha {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds the CSP policies necessary for the captcha module to work in a CSP enforced
|
* Adds the necessary CSP policies for the captcha module to work in a CSP enforced
|
||||||
* setup.
|
* setup.
|
||||||
*
|
*
|
||||||
* @param ContentSecurityPolicy $csp The CSP instance to add the policies to, usually
|
* @param ContentSecurityPolicy $csp The CSP instance to add the policies to, usually
|
||||||
|
@ -275,7 +275,7 @@ class SimpleCaptcha {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Show error message for missing or incorrect captcha on EditPage.
|
* Show the error message for missing or incorrect captcha on EditPage.
|
||||||
* @param EditPage $editPage
|
* @param EditPage $editPage
|
||||||
* @param OutputPage $out
|
* @param OutputPage $out
|
||||||
*/
|
*/
|
||||||
|
@ -420,7 +420,7 @@ class SimpleCaptcha {
|
||||||
* 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 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.
|
||||||
*
|
*
|
||||||
* @param string[] $input
|
* @param string[] $input
|
||||||
|
@ -645,6 +645,7 @@ class SimpleCaptcha {
|
||||||
|
|
||||||
$numHits = count( $addedMatches );
|
$numHits = count( $addedMatches );
|
||||||
if ( $numHits > 0 ) {
|
if ( $numHits > 0 ) {
|
||||||
|
// TODO: last parameter to sprintf() isn't used
|
||||||
$this->trigger = sprintf( "%dx %s at [[%s]]: %s",
|
$this->trigger = sprintf( "%dx %s at [[%s]]: %s",
|
||||||
$numHits,
|
$numHits,
|
||||||
$regex,
|
$regex,
|
||||||
|
@ -751,61 +752,62 @@ class SimpleCaptcha {
|
||||||
if ( count( $lines ) == 0 ) {
|
if ( count( $lines ) == 0 ) {
|
||||||
wfDebug( "No lines\n" );
|
wfDebug( "No lines\n" );
|
||||||
return [];
|
return [];
|
||||||
} else {
|
|
||||||
# Make regex
|
|
||||||
# It's faster using the S modifier even though it will usually only be run once
|
|
||||||
// $regex = 'http://+[a-z0-9_\-.]*(' . implode( '|', $lines ) . ')';
|
|
||||||
// return '/' . str_replace( '/', '\/', preg_replace('|\\\*/|', '/', $regex) ) . '/Si';
|
|
||||||
$regexes = [];
|
|
||||||
$regexStart = [
|
|
||||||
'normal' => '/^(?:https?:)?\/\/+[a-z0-9_\-.]*(?:',
|
|
||||||
'noprotocol' => '/^(?:',
|
|
||||||
];
|
|
||||||
$regexEnd = [
|
|
||||||
'normal' => ')/Si',
|
|
||||||
'noprotocol' => ')/Si',
|
|
||||||
];
|
|
||||||
$regexMax = 4096;
|
|
||||||
$build = [];
|
|
||||||
foreach ( $lines as $line ) {
|
|
||||||
# Extract flags from the line
|
|
||||||
$options = [];
|
|
||||||
if ( preg_match( '/^(.*?)\s*<([^<>]*)>$/', $line, $matches ) ) {
|
|
||||||
if ( $matches[1] === '' ) {
|
|
||||||
wfDebug( "Line with empty regex\n" );
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$line = $matches[1];
|
|
||||||
$opts = preg_split( '/\s*\|\s*/', trim( $matches[2] ) );
|
|
||||||
foreach ( $opts as $opt ) {
|
|
||||||
$opt = strtolower( $opt );
|
|
||||||
if ( $opt == 'noprotocol' ) {
|
|
||||||
$options['noprotocol'] = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$key = isset( $options['noprotocol'] ) ? 'noprotocol' : 'normal';
|
|
||||||
|
|
||||||
// FIXME: not very robust size check, but should work. :)
|
|
||||||
if ( !isset( $build[$key] ) ) {
|
|
||||||
$build[$key] = $line;
|
|
||||||
} elseif ( strlen( $build[$key] ) + strlen( $line ) > $regexMax ) {
|
|
||||||
$regexes[] = $regexStart[$key] .
|
|
||||||
str_replace( '/', '\/', preg_replace( '|\\\*/|', '/', $build[$key] ) ) .
|
|
||||||
$regexEnd[$key];
|
|
||||||
$build[$key] = $line;
|
|
||||||
} else {
|
|
||||||
$build[$key] .= '|' . $line;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
foreach ( $build as $key => $value ) {
|
|
||||||
$regexes[] = $regexStart[$key] .
|
|
||||||
str_replace( '/', '\/', preg_replace( '|\\\*/|', '/', $value ) ) .
|
|
||||||
$regexEnd[$key];
|
|
||||||
}
|
|
||||||
return $regexes;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Make regex
|
||||||
|
# It's faster using the S modifier even though it will usually only be run once
|
||||||
|
// $regex = 'http://+[a-z0-9_\-.]*(' . implode( '|', $lines ) . ')';
|
||||||
|
// return '/' . str_replace( '/', '\/', preg_replace('|\\\*/|', '/', $regex) ) . '/Si';
|
||||||
|
$regexes = [];
|
||||||
|
$regexStart = [
|
||||||
|
'normal' => '/^(?:https?:)?\/\/+[a-z0-9_\-.]*(?:',
|
||||||
|
'noprotocol' => '/^(?:',
|
||||||
|
];
|
||||||
|
$regexEnd = [
|
||||||
|
'normal' => ')/Si',
|
||||||
|
'noprotocol' => ')/Si',
|
||||||
|
];
|
||||||
|
$regexMax = 4096;
|
||||||
|
$build = [];
|
||||||
|
foreach ( $lines as $line ) {
|
||||||
|
# Extract flags from the line
|
||||||
|
$options = [];
|
||||||
|
if ( preg_match( '/^(.*?)\s*<([^<>]*)>$/', $line, $matches ) ) {
|
||||||
|
if ( $matches[1] === '' ) {
|
||||||
|
wfDebug( "Line with empty regex\n" );
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$line = $matches[1];
|
||||||
|
$opts = preg_split( '/\s*\|\s*/', trim( $matches[2] ) );
|
||||||
|
foreach ( $opts as $opt ) {
|
||||||
|
$opt = strtolower( $opt );
|
||||||
|
if ( $opt == 'noprotocol' ) {
|
||||||
|
$options['noprotocol'] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$key = isset( $options['noprotocol'] ) ? 'noprotocol' : 'normal';
|
||||||
|
|
||||||
|
// FIXME: not very robust size check, but should work. :)
|
||||||
|
if ( !isset( $build[$key] ) ) {
|
||||||
|
$build[$key] = $line;
|
||||||
|
} elseif ( strlen( $build[$key] ) + strlen( $line ) > $regexMax ) {
|
||||||
|
$regexes[] = $regexStart[$key] .
|
||||||
|
str_replace( '/', '\/', preg_replace( '|\\\*/|', '/', $build[$key] ) ) .
|
||||||
|
$regexEnd[$key];
|
||||||
|
$build[$key] = $line;
|
||||||
|
} else {
|
||||||
|
$build[$key] .= '|' . $line;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
foreach ( $build as $key => $value ) {
|
||||||
|
$regexes[] = $regexStart[$key] .
|
||||||
|
str_replace( '/', '\/', preg_replace( '|\\\*/|', '/', $value ) ) .
|
||||||
|
$regexEnd[$key];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $regexes;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -839,10 +841,10 @@ class SimpleCaptcha {
|
||||||
}
|
}
|
||||||
if ( $this->shouldCheck( $page, $newtext, $section, $context ) ) {
|
if ( $this->shouldCheck( $page, $newtext, $section, $context ) ) {
|
||||||
return $this->passCaptchaLimitedFromRequest( $wgRequest, $user );
|
return $this->passCaptchaLimitedFromRequest( $wgRequest, $user );
|
||||||
} else {
|
|
||||||
wfDebug( "ConfirmEdit: no need to show captcha.\n" );
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
wfDebug( "ConfirmEdit: no need to show captcha.\n" );
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -857,7 +859,7 @@ class SimpleCaptcha {
|
||||||
*/
|
*/
|
||||||
public function confirmEditMerged( $context, $content, $status, $summary, $user, $minorEdit ) {
|
public function confirmEditMerged( $context, $content, $status, $summary, $user, $minorEdit ) {
|
||||||
$title = $context->getTitle();
|
$title = $context->getTitle();
|
||||||
if ( !( $title->canExist() ) ) {
|
if ( !$title->canExist() ) {
|
||||||
// we check WikiPage only
|
// we check WikiPage only
|
||||||
// try to get an appropriate title for this page
|
// try to get an appropriate title for this page
|
||||||
$title = $context->getTitle();
|
$title = $context->getTitle();
|
||||||
|
@ -888,7 +890,7 @@ class SimpleCaptcha {
|
||||||
// that the user already submitted an edit, and so the 'captcha-edit'
|
// that the user already submitted an edit, and so the 'captcha-edit'
|
||||||
// message is more appropriate.
|
// message is more appropriate.
|
||||||
$message = 'captcha-edit';
|
$message = 'captcha-edit';
|
||||||
[ $_index, $word ] = $this->getCaptchaParamsFromRequest(
|
[ , $word ] = $this->getCaptchaParamsFromRequest(
|
||||||
RequestContext::getMain()->getRequest()
|
RequestContext::getMain()->getRequest()
|
||||||
);
|
);
|
||||||
// But if there's a word supplied in the request, then we should
|
// But if there's a word supplied in the request, then we should
|
||||||
|
@ -917,11 +919,7 @@ class SimpleCaptcha {
|
||||||
*/
|
*/
|
||||||
public function needCreateAccountCaptcha( User $creatingUser ) {
|
public function needCreateAccountCaptcha( User $creatingUser ) {
|
||||||
if ( $this->triggersCaptcha( CaptchaTriggers::CREATE_ACCOUNT ) ) {
|
if ( $this->triggersCaptcha( CaptchaTriggers::CREATE_ACCOUNT ) ) {
|
||||||
if ( $this->canSkipCaptcha( $creatingUser,
|
return !$this->canSkipCaptcha( $creatingUser, MediaWikiServices::getInstance()->getMainConfig() );
|
||||||
\MediaWiki\MediaWikiServices::getInstance()->getMainConfig() ) ) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -941,7 +939,7 @@ class SimpleCaptcha {
|
||||||
$user = RequestContext::getMain()->getUser();
|
$user = RequestContext::getMain()->getUser();
|
||||||
if ( $this->triggersCaptcha( CaptchaTriggers::SENDEMAIL ) ) {
|
if ( $this->triggersCaptcha( CaptchaTriggers::SENDEMAIL ) ) {
|
||||||
if ( $this->canSkipCaptcha( $user,
|
if ( $this->canSkipCaptcha( $user,
|
||||||
\MediaWiki\MediaWikiServices::getInstance()->getMainConfig() ) ) {
|
MediaWikiServices::getInstance()->getMainConfig() ) ) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -988,7 +986,7 @@ class SimpleCaptcha {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks, if the user reached the amount of false CAPTCHAs and give him some vacation
|
* Checks, if the user reached the number of false CAPTCHAs and give him some vacation
|
||||||
* or run self::passCaptcha() and clear counter if correct.
|
* or run self::passCaptcha() and clear counter if correct.
|
||||||
*
|
*
|
||||||
* @param WebRequest $request
|
* @param WebRequest $request
|
||||||
|
@ -1011,7 +1009,7 @@ class SimpleCaptcha {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks, if the user reached the amount of false CAPTCHAs and give him some vacation
|
* Checks, if the user reached the number of false CAPTCHAs and give him some vacation
|
||||||
* or run self::passCaptcha() and clear counter if correct.
|
* or run self::passCaptcha() and clear counter if correct.
|
||||||
*
|
*
|
||||||
* @param string $index Captcha idenitifier
|
* @param string $index Captcha idenitifier
|
||||||
|
@ -1023,7 +1021,7 @@ class SimpleCaptcha {
|
||||||
public function passCaptchaLimited( $index, $word, User $user ) {
|
public function passCaptchaLimited( $index, $word, User $user ) {
|
||||||
// don't increase pingLimiter here, just check, if CAPTCHA limit exceeded
|
// don't increase pingLimiter here, just check, if CAPTCHA limit exceeded
|
||||||
if ( $user->pingLimiter( 'badcaptcha', 0 ) ) {
|
if ( $user->pingLimiter( 'badcaptcha', 0 ) ) {
|
||||||
// for debugging add an proper error message, the user just see an false captcha error message
|
// for debugging add a proper error message, the user will just see a false captcha error message
|
||||||
$this->log( 'User reached RateLimit, preventing action' );
|
$this->log( 'User reached RateLimit, preventing action' );
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -1032,7 +1030,7 @@ class SimpleCaptcha {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// captcha was not solved: increase limit and return false
|
// captcha was not solved: increase the limit and return false
|
||||||
$user->pingLimiter( 'badcaptcha' );
|
$user->pingLimiter( 'badcaptcha' );
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,9 +10,7 @@ class SpecialCaptcha extends UnlistedSpecialPage {
|
||||||
parent::__construct( 'Captcha' );
|
parent::__construct( 'Captcha' );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** @inheritDoc */
|
||||||
* @inheritDoc
|
|
||||||
*/
|
|
||||||
public function execute( $par ) {
|
public function execute( $par ) {
|
||||||
$this->setHeaders();
|
$this->setHeaders();
|
||||||
|
|
||||||
|
|
|
@ -14,9 +14,7 @@ class CaptchaCacheStore extends CaptchaStore {
|
||||||
$this->store = MediaWikiServices::getInstance()->getMicroStash();
|
$this->store = MediaWikiServices::getInstance()->getMicroStash();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** @inheritDoc */
|
||||||
* @inheritDoc
|
|
||||||
*/
|
|
||||||
public function store( $index, $info ) {
|
public function store( $index, $info ) {
|
||||||
global $wgCaptchaSessionExpiration;
|
global $wgCaptchaSessionExpiration;
|
||||||
|
|
||||||
|
@ -24,26 +22,23 @@ class CaptchaCacheStore extends CaptchaStore {
|
||||||
$this->store->makeKey( 'captcha', $index ),
|
$this->store->makeKey( 'captcha', $index ),
|
||||||
$info,
|
$info,
|
||||||
$wgCaptchaSessionExpiration,
|
$wgCaptchaSessionExpiration,
|
||||||
// Assume the write will reach the master DC before the user sends the
|
// Assume the write action will reach the primary DC before the user sends the
|
||||||
// HTTP POST request attempted to solve the captcha and perform an action
|
// HTTP POST request attempted to solve the captcha and perform an action
|
||||||
$this->store::WRITE_BACKGROUND
|
$this->store::WRITE_BACKGROUND
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** @inheritDoc */
|
||||||
* @inheritDoc
|
|
||||||
*/
|
|
||||||
public function retrieve( $index ) {
|
public function retrieve( $index ) {
|
||||||
return $this->store->get( $this->store->makeKey( 'captcha', $index ) );
|
return $this->store->get( $this->store->makeKey( 'captcha', $index ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** @inheritDoc */
|
||||||
* @inheritDoc
|
|
||||||
*/
|
|
||||||
public function clear( $index ) {
|
public function clear( $index ) {
|
||||||
$this->store->delete( $this->store->makeKey( 'captcha', $index ) );
|
$this->store->delete( $this->store->makeKey( 'captcha', $index ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @inheritDoc */
|
||||||
public function cookiesNeeded() {
|
public function cookiesNeeded() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,16 +6,12 @@ class CaptchaHashStore extends CaptchaStore {
|
||||||
/** @var array */
|
/** @var array */
|
||||||
protected $data = [];
|
protected $data = [];
|
||||||
|
|
||||||
/**
|
/** @inheritDoc */
|
||||||
* @inheritDoc
|
|
||||||
*/
|
|
||||||
public function store( $index, $info ) {
|
public function store( $index, $info ) {
|
||||||
$this->data[$index] = $info;
|
$this->data[$index] = $info;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** @inheritDoc */
|
||||||
* @inheritDoc
|
|
||||||
*/
|
|
||||||
public function retrieve( $index ) {
|
public function retrieve( $index ) {
|
||||||
if ( array_key_exists( $index, $this->data ) ) {
|
if ( array_key_exists( $index, $this->data ) ) {
|
||||||
return $this->data[$index];
|
return $this->data[$index];
|
||||||
|
@ -23,17 +19,17 @@ class CaptchaHashStore extends CaptchaStore {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** @inheritDoc */
|
||||||
* @inheritDoc
|
|
||||||
*/
|
|
||||||
public function clear( $index ) {
|
public function clear( $index ) {
|
||||||
unset( $this->data[$index] );
|
unset( $this->data[$index] );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @inheritDoc */
|
||||||
public function cookiesNeeded() {
|
public function cookiesNeeded() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @inheritDoc */
|
||||||
public function clearAll() {
|
public function clearAll() {
|
||||||
$this->data = [];
|
$this->data = [];
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,27 +10,22 @@ class CaptchaSessionStore extends CaptchaStore {
|
||||||
SessionManager::getGlobalSession()->persist();
|
SessionManager::getGlobalSession()->persist();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** @inheritDoc */
|
||||||
* @inheritDoc
|
|
||||||
*/
|
|
||||||
public function store( $index, $info ) {
|
public function store( $index, $info ) {
|
||||||
SessionManager::getGlobalSession()->set( 'captcha' . $index, $info );
|
SessionManager::getGlobalSession()->set( 'captcha' . $index, $info );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** @inheritDoc */
|
||||||
* @inheritDoc
|
|
||||||
*/
|
|
||||||
public function retrieve( $index ) {
|
public function retrieve( $index ) {
|
||||||
return SessionManager::getGlobalSession()->get( 'captcha' . $index, false );
|
return SessionManager::getGlobalSession()->get( 'captcha' . $index, false );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** @inheritDoc */
|
||||||
* @inheritDoc
|
|
||||||
*/
|
|
||||||
public function clear( $index ) {
|
public function clear( $index ) {
|
||||||
SessionManager::getGlobalSession()->remove( 'captcha' . $index );
|
SessionManager::getGlobalSession()->remove( 'captcha' . $index );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @inheritDoc */
|
||||||
public function cookiesNeeded() {
|
public function cookiesNeeded() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,7 @@ class HTMLTurnstileField extends HTMLFormField {
|
||||||
/**
|
/**
|
||||||
* Parameters:
|
* Parameters:
|
||||||
* - key: (string, required) Turnstile public key
|
* - key: (string, required) Turnstile public key
|
||||||
* - error: (string) Turnstile error from previous round
|
* - error: (string) Turnstile error from the previous round
|
||||||
* @param array $params
|
* @param array $params
|
||||||
*/
|
*/
|
||||||
public function __construct( array $params ) {
|
public function __construct( array $params ) {
|
||||||
|
@ -32,14 +32,12 @@ class HTMLTurnstileField extends HTMLFormField {
|
||||||
$this->mName = 'cf-turnstile-response';
|
$this->mName = 'cf-turnstile-response';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** @inheritDoc */
|
||||||
* @inheritDoc
|
|
||||||
*/
|
|
||||||
public function getInputHTML( $value ) {
|
public function getInputHTML( $value ) {
|
||||||
$out = $this->mParent->getOutput();
|
$out = $this->mParent->getOutput();
|
||||||
$lang = htmlspecialchars( urlencode( $this->mParent->getLanguage()->getCode() ) );
|
$lang = htmlspecialchars( urlencode( $this->mParent->getLanguage()->getCode() ) );
|
||||||
|
|
||||||
// Insert Turnstile script, in display language, if available.
|
// Insert the Turnstile script, in display language, if available.
|
||||||
// Language falls back to the browser's display language.
|
// Language falls back to the browser's display language.
|
||||||
// See https://developers.cloudflare.com/turnstile/reference/supported-languages/
|
// See https://developers.cloudflare.com/turnstile/reference/supported-languages/
|
||||||
$out->addHeadItem(
|
$out->addHeadItem(
|
||||||
|
|
|
@ -44,7 +44,7 @@ class Turnstile extends SimpleCaptcha {
|
||||||
return [
|
return [
|
||||||
'html' => $output,
|
'html' => $output,
|
||||||
'headitems' => [
|
'headitems' => [
|
||||||
// Insert Turnstile script, in display language, if available.
|
// Insert the Turnstile script, in display language, if available.
|
||||||
// Language falls back to the browser's display language.
|
// Language falls back to the browser's display language.
|
||||||
// See https://developers.cloudflare.com/turnstile/reference/supported-languages/
|
// See https://developers.cloudflare.com/turnstile/reference/supported-languages/
|
||||||
"<script src=\"https://challenges.cloudflare.com/turnstile/v0/api.js?language={$lang}\" async defer>
|
"<script src=\"https://challenges.cloudflare.com/turnstile/v0/api.js?language={$lang}\" async defer>
|
||||||
|
@ -53,9 +53,7 @@ class Turnstile extends SimpleCaptcha {
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** @inheritDoc */
|
||||||
* @return string[]
|
|
||||||
*/
|
|
||||||
public static function getCSPUrls() {
|
public static function getCSPUrls() {
|
||||||
return [ 'https://challenges.cloudflare.com/turnstile/v0/api.js' ];
|
return [ 'https://challenges.cloudflare.com/turnstile/v0/api.js' ];
|
||||||
}
|
}
|
||||||
|
@ -140,17 +138,13 @@ class Turnstile extends SimpleCaptcha {
|
||||||
return $response['success'];
|
return $response['success'];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** @inheritDoc */
|
||||||
* @param array &$resultArr
|
|
||||||
*/
|
|
||||||
protected function addCaptchaAPI( &$resultArr ) {
|
protected function addCaptchaAPI( &$resultArr ) {
|
||||||
$resultArr['captcha'] = $this->describeCaptchaType();
|
$resultArr['captcha'] = $this->describeCaptchaType();
|
||||||
$resultArr['captcha']['error'] = $this->error;
|
$resultArr['captcha']['error'] = $this->error;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** @inheritDoc */
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public function describeCaptchaType() {
|
public function describeCaptchaType() {
|
||||||
global $wgTurnstileSiteKey;
|
global $wgTurnstileSiteKey;
|
||||||
return [
|
return [
|
||||||
|
@ -191,31 +185,23 @@ class Turnstile extends SimpleCaptcha {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** @inheritDoc */
|
||||||
* @inheritDoc
|
|
||||||
*/
|
|
||||||
public function getError() {
|
public function getError() {
|
||||||
return $this->error;
|
return $this->error;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** @inheritDoc */
|
||||||
* @inheritDoc
|
|
||||||
*/
|
|
||||||
public function storeCaptcha( $info ) {
|
public function storeCaptcha( $info ) {
|
||||||
return 'not used';
|
return 'not used';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** @inheritDoc */
|
||||||
* @inheritDoc
|
|
||||||
*/
|
|
||||||
public function retrieveCaptcha( $index ) {
|
public function retrieveCaptcha( $index ) {
|
||||||
// just pretend it worked
|
// Pretend it worked
|
||||||
return [ 'index' => $index ];
|
return [ 'index' => $index ];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** @inheritDoc */
|
||||||
* @inheritDoc
|
|
||||||
*/
|
|
||||||
public function getCaptcha() {
|
public function getCaptcha() {
|
||||||
// Turnstile is handled by frontend code + an external provider; nothing to do here.
|
// Turnstile is handled by frontend code + an external provider; nothing to do here.
|
||||||
return [];
|
return [];
|
||||||
|
|
|
@ -13,17 +13,13 @@ class TurnstileAuthenticationRequest extends CaptchaAuthenticationRequest {
|
||||||
parent::__construct( '', [] );
|
parent::__construct( '', [] );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** @inheritDoc */
|
||||||
* @inheritDoc
|
|
||||||
*/
|
|
||||||
public function loadFromSubmission( array $data ) {
|
public function loadFromSubmission( array $data ) {
|
||||||
// unhack the hack in parent
|
// unhack the hack in parent
|
||||||
return AuthenticationRequest::loadFromSubmission( $data );
|
return AuthenticationRequest::loadFromSubmission( $data );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** @inheritDoc */
|
||||||
* @inheritDoc
|
|
||||||
*/
|
|
||||||
public function getFieldInfo() {
|
public function getFieldInfo() {
|
||||||
$fieldInfo = parent::getFieldInfo();
|
$fieldInfo = parent::getFieldInfo();
|
||||||
|
|
||||||
|
|
|
@ -62,18 +62,16 @@ class HCaptcha extends SimpleCaptcha {
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** @inheritDoc */
|
||||||
* @return string[]
|
|
||||||
*/
|
|
||||||
public static function getCSPUrls() {
|
public static function getCSPUrls() {
|
||||||
return [ 'https://hcaptcha.com', 'https://*.hcaptcha.com' ];
|
return [ 'https://hcaptcha.com', 'https://*.hcaptcha.com' ];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds the CSP policies necessary for the captcha module to work in a CSP enforced
|
* Adds the CSP policies that are necessary for the captcha module to work in a CSP enforced
|
||||||
* setup.
|
* setup.
|
||||||
*
|
*
|
||||||
* @param ContentSecurityPolicy $csp The CSP instance to add the policies to, usually
|
* @param ContentSecurityPolicy $csp The CSP instance to add the policies to, this is usually to be
|
||||||
* obtained from {@link OutputPage::getCSP()}
|
* obtained from {@link OutputPage::getCSP()}
|
||||||
*/
|
*/
|
||||||
public static function addCSPSources( ContentSecurityPolicy $csp ) {
|
public static function addCSPSources( ContentSecurityPolicy $csp ) {
|
||||||
|
@ -216,35 +214,27 @@ class HCaptcha extends SimpleCaptcha {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** @inheritDoc */
|
||||||
* @inheritDoc
|
|
||||||
*/
|
|
||||||
public function getError() {
|
public function getError() {
|
||||||
return $this->error;
|
return $this->error;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** @inheritDoc */
|
||||||
* @inheritDoc
|
|
||||||
*/
|
|
||||||
public function storeCaptcha( $info ) {
|
public function storeCaptcha( $info ) {
|
||||||
// hCaptcha is stored externally, the ID will be generated at that time as well, and
|
// hCaptcha is stored externally, the ID will be generated at that time as well, and
|
||||||
// the one returned here won't be used. Just pretend this worked.
|
// the one returned here won't be used. Just pretend this worked.
|
||||||
return 'not used';
|
return 'not used';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** @inheritDoc */
|
||||||
* @inheritDoc
|
|
||||||
*/
|
|
||||||
public function retrieveCaptcha( $index ) {
|
public function retrieveCaptcha( $index ) {
|
||||||
// just pretend it worked
|
// Just pretend it worked
|
||||||
return [ 'index' => $index ];
|
return [ 'index' => $index ];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** @inheritDoc */
|
||||||
* @inheritDoc
|
|
||||||
*/
|
|
||||||
public function getCaptcha() {
|
public function getCaptcha() {
|
||||||
// hCaptcha is handled by frontend code + an external provider; nothing to do here.
|
// hCaptcha is handled by frontend code, and an external provider; nothing to do here.
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,17 +10,13 @@ class HCaptchaAuthenticationRequest extends CaptchaAuthenticationRequest {
|
||||||
parent::__construct( '', [] );
|
parent::__construct( '', [] );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** @inheritDoc */
|
||||||
* @inheritDoc
|
|
||||||
*/
|
|
||||||
public function loadFromSubmission( array $data ) {
|
public function loadFromSubmission( array $data ) {
|
||||||
// unhack the hack in parent
|
// unhack the hack in parent
|
||||||
return AuthenticationRequest::loadFromSubmission( $data );
|
return AuthenticationRequest::loadFromSubmission( $data );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** @inheritDoc */
|
||||||
* @inheritDoc
|
|
||||||
*/
|
|
||||||
public function getFieldInfo() {
|
public function getFieldInfo() {
|
||||||
$fieldInfo = parent::getFieldInfo();
|
$fieldInfo = parent::getFieldInfo();
|
||||||
|
|
||||||
|
|
|
@ -28,9 +28,7 @@ class HTMLHCaptchaField extends HTMLFormField {
|
||||||
$this->mName = 'h-captcha-response';
|
$this->mName = 'h-captcha-response';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** @inheritDoc */
|
||||||
* @inheritDoc
|
|
||||||
*/
|
|
||||||
public function getInputHTML( $value ) {
|
public function getInputHTML( $value ) {
|
||||||
$out = $this->mParent->getOutput();
|
$out = $this->mParent->getOutput();
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,6 @@ use MediaWiki\Extension\ConfirmEdit\QuestyCaptcha\QuestyCaptcha;
|
||||||
class QuestyCaptchaTest extends MediaWikiIntegrationTestCase {
|
class QuestyCaptchaTest extends MediaWikiIntegrationTestCase {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @covers \MediaWiki\Extension\ConfirmEdit\QuestyCaptcha\QuestyCaptcha::getCaptcha
|
|
||||||
* @dataProvider provideGetCaptcha
|
* @dataProvider provideGetCaptcha
|
||||||
*/
|
*/
|
||||||
public function testGetCaptcha( $config, $expected ) {
|
public function testGetCaptcha( $config, $expected ) {
|
||||||
|
|
Loading…
Reference in a new issue