5 use Wikimedia\TestingAccessWrapper;
14 $this->setMwGlobals( [
15 'wgCaptchaClass' => SimpleCaptcha::class,
16 'wgCaptchaBadLoginAttempts' => 1,
17 'wgCaptchaBadLoginPerUserAttempts' => 1,
18 'wgCaptchaStorageClass' => CaptchaHashStore::class,
19 'wgMainCacheType' => __METHOD__,
23 $services = \MediaWiki\MediaWikiServices::getInstance();
24 if ( method_exists( $services,
'getLocalClusterObjectCache' ) ) {
25 $this->setService(
'LocalClusterObjectCache',
new HashBagOStuff() );
34 TestingAccessWrapper::newFromClass( ConfirmEditHooks::class )->instanceCreated =
false;
41 $action, $username, $triggers, $needsCaptcha, $preTestCallback =
null
44 if ( $preTestCallback ) {
45 $fn = array_shift( $preTestCallback );
46 call_user_func_array( [ $this, $fn ], $preTestCallback );
51 $request->setCookie(
'UserName', $username );
54 $provider->setManager( AuthManager::singleton() );
55 $reqs = $provider->getAuthenticationRequests( $action, [
'username' => $username ] );
56 if ( $needsCaptcha ) {
57 $this->assertCount( 1, $reqs );
58 $this->assertInstanceOf( CaptchaAuthenticationRequest::class, $reqs[0] );
60 $this->assertEmpty( $reqs );
66 [ AuthManager::ACTION_LOGIN,
null, [], false ],
67 [ AuthManager::ACTION_LOGIN,
null, [
'badlogin' ], false ],
68 [ AuthManager::ACTION_LOGIN,
null, [
'badlogin' ],
true, [
'blockLogin',
'Foo' ] ],
69 [ AuthManager::ACTION_LOGIN,
null, [
'badloginperuser' ],
false, [
'blockLogin',
'Foo' ] ],
70 [ AuthManager::ACTION_LOGIN,
'Foo', [
'badloginperuser' ],
false, [
'blockLogin',
'Bar' ] ],
71 [ AuthManager::ACTION_LOGIN,
'Foo', [
'badloginperuser' ],
true, [
'blockLogin',
'Foo' ] ],
72 [ AuthManager::ACTION_LOGIN,
null, [
'badloginperuser' ],
true, [
'flagSession' ] ],
73 [ AuthManager::ACTION_CREATE,
null, [], false ],
74 [ AuthManager::ACTION_CREATE,
null, [
'createaccount' ],
true ],
75 [ AuthManager::ACTION_CREATE,
'UTSysop', [
'createaccount' ], false ],
76 [ AuthManager::ACTION_LINK,
null, [], false ],
77 [ AuthManager::ACTION_CHANGE,
null, [], false ],
78 [ AuthManager::ACTION_REMOVE,
null, [], false ],
86 $provider->setManager( AuthManager::singleton() );
88 $reqs = $provider->getAuthenticationRequests( AuthManager::ACTION_CREATE,
89 [
'username' =>
'Foo' ] );
91 $this->assertCount( 1, $reqs );
92 $this->assertInstanceOf( CaptchaAuthenticationRequest::class, $reqs[0] );
94 $id = $reqs[0]->captchaId;
95 $data = TestingAccessWrapper::newFromObject( $reqs[0] )->captchaData;
96 $this->assertEquals( $captcha->retrieveCaptcha( $id ), $data + [
'index' => $id ] );
103 $isBadLoginPerUserTriggered, $result
105 $this->
setMwHook(
'PingLimiter',
function ( $user, $action, &$result ) {
110 $captcha = $this->getMock( SimpleCaptcha::class,
111 [
'isBadLoginTriggered',
'isBadLoginPerUserTriggered' ] );
112 $captcha->expects( $this->any() )->method(
'isBadLoginTriggered' )
113 ->willReturn( $isBadLoginTriggered );
114 $captcha->expects( $this->any() )->method(
'isBadLoginPerUserTriggered' )
115 ->willReturn( $isBadLoginPerUserTriggered );
116 $this->setMwGlobals(
'wgCaptcha', $captcha );
117 TestingAccessWrapper::newFromClass( ConfirmEditHooks::class )->instanceCreated =
true;
119 $provider->setManager( AuthManager::singleton() );
121 $status = $provider->testForAuthentication( $req ? [ $req ] : [] );
122 $this->assertEquals( $result,
$status->isGood() );
131 'badlogin' => [
$fallback,
true,
false, false ],
132 'badloginperuser, no username' => [
null,
false,
true,
true ],
133 'badloginperuser' => [
$fallback,
false,
true, false ],
134 'non-existent captcha' => [ $this->
getCaptchaRequest(
'123',
'4' ),
true,
true, false ],
135 'wrong captcha' => [ $this->
getCaptchaRequest(
'345',
'6' ),
true,
true, false ],
144 $this->
setMwHook(
'PingLimiter',
function ( &$user, $action, &$result ) {
148 $this->
setTriggers( $disableTrigger ? [] : [
'createaccount' ] );
152 $provider->setManager( AuthManager::singleton() );
154 $status = $provider->testForAccountCreation( $user, $creator, $req ? [ $req ] : [] );
155 $this->assertEquals( $result,
$status->isGood() );
163 'no captcha' => [
null, $user, false ],
164 'non-existent captcha' => [ $this->
getCaptchaRequest(
'123',
'4' ), $user, false ],
167 'user is exempt' => [
null, $sysop,
true ],
168 'disabled' => [
null, $user,
true,
'disable' ],
173 $this->
setTriggers( [
'badlogin',
'badloginperuser' ] );
178 $provider->setManager( AuthManager::singleton() );
180 $this->assertFalse( $captcha->isBadLoginTriggered() );
181 $this->assertFalse( $captcha->isBadLoginPerUserTriggered( $user ) );
183 $provider->postAuthentication( $user, \
MediaWiki\Auth\AuthenticationResponse::newFail(
186 $this->assertTrue( $captcha->isBadLoginTriggered() );
187 $this->assertTrue( $captcha->isBadLoginPerUserTriggered( $user ) );
188 $this->assertFalse( $captcha->isBadLoginPerUserTriggered( $anotherUser ) );
190 $provider->postAuthentication( $user, \
MediaWiki\Auth\AuthenticationResponse::newPass(
'Foo' ) );
192 $this->assertFalse( $captcha->isBadLoginPerUserTriggered( $user ) );
200 $provider->setManager( AuthManager::singleton() );
202 $this->assertFalse( $captcha->isBadLoginTriggered() );
203 $this->assertFalse( $captcha->isBadLoginPerUserTriggered( $user ) );
205 $provider->postAuthentication( $user, \
MediaWiki\Auth\AuthenticationResponse::newFail(
208 $this->assertFalse( $captcha->isBadLoginTriggered() );
209 $this->assertFalse( $captcha->isBadLoginPerUserTriggered( $user ) );
216 $this->mergeMwGlobalArrayValue(
225 $provider->setManager( AuthManager::singleton() );
226 $providerAccess = TestingAccessWrapper::newFromObject( $provider );
228 foreach ( $attempts as $attempt ) {
229 if ( !empty( $attempts[3] ) ) {
230 $this->
setMwHook(
'PingLimiter',
function ( &$user, $action, &$result ) {
235 $this->
setMwHook(
'PingLimiter',
function () {
241 $success = $providerAccess->verifyCaptcha( $captcha, [ $attempts[0] ], $attempts[1] );
242 $this->assertEquals( $attempts[2],
$success );
262 'pinglimiter disabled' => [
271 $req->captchaWord = $word;
272 $req->username = $username;
278 $captcha->increaseBadLoginCounter( $username );
283 ->set(
'ConfirmEdit:loginCaptchaPerUserTriggered',
true );
287 $types = [
'edit',
'create',
'sendemail',
'addurl',
'createaccount',
'badlogin',
289 $captchaTriggers = array_combine( $types, array_map(
function (
$type ) use ( $triggers ) {
290 return in_array(
$type, $triggers,
true );
292 $this->setMwGlobals(
'wgCaptchaTriggers', $captchaTriggers );
301 protected function setMwHook( $hook, callable $callback ) {
302 $this->mergeMwGlobalArrayValue(
'wgHooks', [ $hook => $callback ] );