Use new hooks in API action=createaccount for Captcha

Hooks used:
* AddNewAccountApiForm
* AddNewAccountApiResult

This adds a 'captcha' section to the results with the same format
as we provide for editing; you'll get this for instance at the
same time as you do a first-request that prompts for a token.

No modification to the result status is included; presence of
the 'captcha' section is assumed to be enough to prompt the client
to fetch and render the captcha prompt.

Failure to pass a captcha will return an API error message; a
subsequent commit fixes problems with that error message being
hard to machine-read.

Note that logic from inside Captcha::confirmUserCreate has been
pulled out to Captcha::needCreateAccountCaptcha so we don't
send captcha information to users who don't need it.

Requires core changes: If5b7dab8

Sample API client: https://github.com/brion/api-createaccount

Bug: 46072

Change-Id: Id628defaeab2bf5979ca8f4284d14fc42d9c3e46
This commit is contained in:
Brion Vibber 2014-01-10 15:16:34 -08:00
parent 2355fc6170
commit 78e6f5ec79
3 changed files with 79 additions and 10 deletions

View file

@ -540,15 +540,7 @@ class SimpleCaptcha {
* @return bool true to continue, false to abort user creation * @return bool true to continue, false to abort user creation
*/ */
function confirmUserCreate( $u, &$message ) { function confirmUserCreate( $u, &$message ) {
global $wgCaptchaTriggers, $wgUser; if ( $this->needCreateAccountCaptcha() ) {
if ( $wgCaptchaTriggers['createaccount'] ) {
if ( $wgUser->isAllowed( 'skipcaptcha' ) ) {
wfDebug( "ConfirmEdit: user group allows skipping captcha on account creation\n" );
return true;
}
if ( $this->isIPWhitelisted() )
return true;
$this->trigger = "new account '" . $u->getName() . "'"; $this->trigger = "new account '" . $u->getName() . "'";
if ( !$this->passCaptcha() ) { if ( !$this->passCaptcha() ) {
$message = wfMessage( 'captcha-createaccount-fail' )->text(); $message = wfMessage( 'captcha-createaccount-fail' )->text();
@ -557,6 +549,27 @@ class SimpleCaptcha {
} }
return true; return true;
} }
/**
* Logic to check if we need to pass a captcha for the current user
* to create a new account, or not
*
* @return bool true to show captcha, false to skip captcha
*/
function needCreateAccountCaptcha() {
global $wgCaptchaTriggers, $wgUser;
if ( $wgCaptchaTriggers['createaccount'] ) {
if ( $wgUser->isAllowed( 'skipcaptcha' ) ) {
wfDebug( "ConfirmEdit: user group allows skipping captcha on account creation\n" );
return false;
}
if ( $this->isIPWhitelisted() ) {
return false;
}
return true;
}
return false;
}
/** /**
* Hook for user login form submissions. * Hook for user login form submissions.
@ -619,7 +632,7 @@ class SimpleCaptcha {
* @return bool * @return bool
*/ */
protected function isAPICaptchaModule( $module ) { protected function isAPICaptchaModule( $module ) {
return $module instanceof ApiEditPage; return $module instanceof ApiEditPage || $module instanceof ApiCreateAccount;
} }
/** /**
@ -770,4 +783,50 @@ class SimpleCaptcha {
$wgOut->addWikiMsg( 'captchahelp-cookies-needed' ); $wgOut->addWikiMsg( 'captchahelp-cookies-needed' );
} }
} }
/**
* Pass API captcha parameters on to the login form when using
* API account creation.
*
* @param ApiCreateAccount $apiModule
* @param LoginForm $loginForm
* @return hook return value
*/
function addNewAccountApiForm( $apiModule, $loginForm ) {
global $wgRequest;
$main = $apiModule->getMain();
$id = $main->getVal( 'captchaid' );
if ( $id ) {
$wgRequest->setVal( 'wpCaptchaId', $id );
// Suppress "unrecognized parameter" warning:
$main->getVal( 'wpCaptchaId' );
}
$word = $main->getVal( 'captchaword' );
if ( $word ) {
$wgRequest->setVal( 'wpCaptchaWord', $word );
// Suppress "unrecognized parameter" warning:
$main->getVal( 'wpCaptchaWord' );
}
return true;
}
/**
* Pass extra data back in API results for account creation.
*
* @param ApiCreateAccount $apiModule
* @param LoginForm &loginForm
* @param array &$params
* @return hook return value
*/
function addNewAccountApiResult( $apiModule, $loginPage, &$result ) {
if ( $result['result'] !== 'success' && $this->needCreateAccountCaptcha() ) {
$this->addCaptchaAPI( $result );
}
return true;
}
} }

View file

@ -189,6 +189,8 @@ $wgHooks['EmailUser'][] = 'ConfirmEditHooks::confirmEmailUser';
$wgHooks['APIEditBeforeSave'][] = 'ConfirmEditHooks::confirmEditAPI'; $wgHooks['APIEditBeforeSave'][] = 'ConfirmEditHooks::confirmEditAPI';
$wgHooks['APIGetAllowedParams'][] = 'ConfirmEditHooks::APIGetAllowedParams'; $wgHooks['APIGetAllowedParams'][] = 'ConfirmEditHooks::APIGetAllowedParams';
$wgHooks['APIGetParamDescription'][] = 'ConfirmEditHooks::APIGetParamDescription'; $wgHooks['APIGetParamDescription'][] = 'ConfirmEditHooks::APIGetParamDescription';
$wgHooks['AddNewAccountApiForm'][] = 'ConfirmEditHooks::addNewAccountApiForm';
$wgHooks['AddNewAccountApiResult'][] = 'ConfirmEditHooks::addNewAccountApiResult';
$wgAutoloadClasses['ConfirmEditHooks'] = "$wgConfirmEditIP/ConfirmEditHooks.php"; $wgAutoloadClasses['ConfirmEditHooks'] = "$wgConfirmEditIP/ConfirmEditHooks.php";
$wgAutoloadClasses['SimpleCaptcha'] = "$wgConfirmEditIP/Captcha.php"; $wgAutoloadClasses['SimpleCaptcha'] = "$wgConfirmEditIP/Captcha.php";

View file

@ -26,6 +26,14 @@ class ConfirmEditHooks {
static function confirmEditAPI( $editPage, $newtext, &$resultArr ) { static function confirmEditAPI( $editPage, $newtext, &$resultArr ) {
return self::getInstance()->confirmEditAPI( $editPage, $newtext, $resultArr ); return self::getInstance()->confirmEditAPI( $editPage, $newtext, $resultArr );
} }
static function addNewAccountApiForm( $apiModule, $loginForm ) {
return self::getInstance()->addNewAccountApiForm( $apiModule, $loginForm );
}
static function addNewAccountApiResult( $apiModule, $loginPage, &$result ) {
return self::getInstance()->addNewAccountApiResult( $apiModule, $loginPage, $result );
}
static function injectUserCreate( &$template ) { static function injectUserCreate( &$template ) {
return self::getInstance()->injectUserCreate( $template ); return self::getInstance()->injectUserCreate( $template );