Skip to content

Commit 96e8ab0

Browse files
REST: Respond 429 when hitting the temp user rate limit
Bug: T374970 Change-Id: I27590357a81b385241d7b0bc87c33f88e8f2a681
1 parent fffbcbc commit 96e8ab0

File tree

8 files changed

+69
-1
lines changed

8 files changed

+69
-1
lines changed

repo/config/Wikibase.ci.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,3 +83,7 @@
8383
if ( $request->getHeader( 'X-Wikibase-CI-Anon-Rate-Limit-Zero', WebRequest::GETHEADER_LIST ) ) {
8484
$wgRateLimits = [ 'edit' => [ 'anon' => [ 0, 60 ] ] ];
8585
}
86+
87+
if ( $request->getHeader( 'X-Wikibase-CI-Temp-Account-Limit-One', WebRequest::GETHEADER_LIST ) ) {
88+
$wgTempAccountCreationThrottle = [ [ 'count' => 1, 'seconds' => 86400 ] ];
89+
}

repo/rest-api/README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ Descriptions of the different kinds of tests can be found in the @ref restApiTes
106106

107107
#### e2e and schema tests
108108

109-
These tests can be run with the command `npm run api-testing`.
109+
These tests can be run with the command `npm run api-testing`.
110110

111111
The following needs to be correctly set up in order for all the tests to pass:
112112
* the targeted wiki to act as both [client and repo], so that Items can have sitelinks to pages on the same wiki
@@ -117,6 +117,7 @@ The following needs to be correctly set up in order for all the tests to pass:
117117
- `X-Wikibase-CI-Redirect-Badges`
118118
- `X-Wikibase-Ci-Tempuser-Config`
119119
- `X-Wikibase-CI-Anon-Rate-Limit-Zero`
120+
- `X-Wikibase-CI-Temp-Account-Limit-One`
120121

121122

122123
[1]: https://www.mediawiki.org/wiki/MediaWiki_API_integration_tests

repo/rest-api/src/Application/UseCases/UpdateExceptionHandler.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
use Wikibase\Repo\RestApi\Domain\Services\Exceptions\AbuseFilterException;
66
use Wikibase\Repo\RestApi\Domain\Services\Exceptions\RateLimitReached;
77
use Wikibase\Repo\RestApi\Domain\Services\Exceptions\ResourceTooLargeException;
8+
use Wikibase\Repo\RestApi\Domain\Services\Exceptions\TempAccountCreationLimitReached;
89

910
/**
1011
* @license GPL-2.0-or-later
@@ -33,6 +34,8 @@ public function executeWithExceptionHandling( callable $callback ) {
3334
] );
3435
} catch ( RateLimitReached $e ) {
3536
throw UseCaseError::newRateLimitReached( UseCaseError::REQUEST_LIMIT_REASON_RATE_LIMIT );
37+
} catch ( TempAccountCreationLimitReached $e ) {
38+
throw UseCaseError::newRateLimitReached( UseCaseError::REQUEST_LIMIT_REASON_TEMP_ACCOUNT_CREATION_LIMIT );
3639
}
3740
}
3841

repo/rest-api/src/Application/UseCases/UseCaseError.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ class UseCaseError extends UseCaseException {
4343
public const REFERENCED_RESOURCE_NOT_FOUND = 'referenced-resource-not-found';
4444
public const REQUEST_LIMIT_REACHED = 'request-limit-reached';
4545
public const REQUEST_LIMIT_REASON_RATE_LIMIT = 'rate-limit-reached';
46+
public const REQUEST_LIMIT_REASON_TEMP_ACCOUNT_CREATION_LIMIT = 'temp-account-creation-limit-reached';
4647
public const RESOURCE_NOT_FOUND = 'resource-not-found';
4748
public const RESOURCE_TOO_LARGE = 'resource-too-large';
4849
public const STATEMENT_GROUP_PROPERTY_ID_MISMATCH = 'statement-group-property-id-mismatch';
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?php declare( strict_types=1 );
2+
3+
namespace Wikibase\Repo\RestApi\Domain\Services\Exceptions;
4+
5+
use Exception;
6+
7+
/**
8+
* @license GPL-2.0-or-later
9+
*/
10+
class TempAccountCreationLimitReached extends Exception {
11+
}

repo/rest-api/src/Infrastructure/DataAccess/EntityUpdater.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
use Wikibase\Repo\RestApi\Domain\Services\Exceptions\AbuseFilterException;
2323
use Wikibase\Repo\RestApi\Domain\Services\Exceptions\RateLimitReached;
2424
use Wikibase\Repo\RestApi\Domain\Services\Exceptions\ResourceTooLargeException;
25+
use Wikibase\Repo\RestApi\Domain\Services\Exceptions\TempAccountCreationLimitReached;
2526
use Wikibase\Repo\RestApi\Infrastructure\DataAccess\Exceptions\EntityUpdateFailed;
2627
use Wikibase\Repo\RestApi\Infrastructure\DataAccess\Exceptions\EntityUpdatePrevented;
2728
use Wikibase\Repo\RestApi\Infrastructure\EditSummaryFormatter;
@@ -65,6 +66,7 @@ public function __construct(
6566
* @throws ResourceTooLargeException
6667
* @throws AbuseFilterException
6768
* @throws RateLimitReached
69+
* @throws TempAccountCreationLimitReached
6870
*/
6971
public function create( EntityDocument $entity, EditMetadata $editMetadata ): EntityRevision {
7072
return $this->createOrUpdate( $entity, $editMetadata, EDIT_NEW );
@@ -75,6 +77,7 @@ public function create( EntityDocument $entity, EditMetadata $editMetadata ): En
7577
* @throws ResourceTooLargeException
7678
* @throws AbuseFilterException
7779
* @throws RateLimitReached
80+
* @throws TempAccountCreationLimitReached
7881
*/
7982
public function update( EntityDocument $entity, EditMetadata $editMetadata ): EntityRevision {
8083
return $this->createOrUpdate( $entity, $editMetadata, EDIT_UPDATE );
@@ -85,6 +88,7 @@ public function update( EntityDocument $entity, EditMetadata $editMetadata ): En
8588
* @throws ResourceTooLargeException
8689
* @throws AbuseFilterException
8790
* @throws RateLimitReached
91+
* @throws TempAccountCreationLimitReached
8892
*/
8993
private function createOrUpdate(
9094
EntityDocument $entity,
@@ -131,6 +135,10 @@ private function createOrUpdate(
131135
throw new RateLimitReached();
132136
}
133137

138+
if ( $this->findErrorInStatus( $status, 'acct_creation_throttle_hit' ) ) {
139+
throw new TempAccountCreationLimitReached();
140+
}
141+
134142
if ( $this->isPreventedEdit( $status ) ) {
135143
throw new EntityUpdatePrevented( (string)$status );
136144
}

repo/rest-api/tests/mocha/api-testing/TempUserTest.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ const {
99
getItemCreateRequest
1010
} = require( '../helpers/happyPathRequestBuilders' );
1111
const entityHelper = require( '../helpers/entityHelper' );
12+
const { assertValidError } = require( '../helpers/responseValidator' );
1213

1314
describeWithTestData( 'IP masking', ( itemRequestInputs, propertyRequestInputs, describeEachRouteWithReset ) => {
1415
function withTempUserConfig( newRequestBuilder, config ) {
@@ -41,6 +42,27 @@ describeWithTestData( 'IP masking', ( itemRequestInputs, propertyRequestInputs,
4142
const { user } = await entityHelper.getLatestEditMetadata( requestInputs.mainTestSubject );
4243
assert.include( user, tempUserPrefix );
4344
} );
45+
46+
// Note: If this test fails, it might be due to the throttler relying on caching.
47+
// Ensure caching is enabled for the wiki under test, as the throttler won't work without it.
48+
it( 'responds 429 when the temp user creation limit is reached', async () => {
49+
await newRequestBuilder()
50+
.withHeader( 'X-Wikibase-Ci-Tempuser-Config', JSON.stringify( { enabled: true } ) )
51+
.withHeader( 'X-Wikibase-CI-Temp-Account-Limit-One', true )
52+
.makeRequest();
53+
54+
const response = await newRequestBuilder()
55+
.withHeader( 'X-Wikibase-Ci-Tempuser-Config', JSON.stringify( { enabled: true } ) )
56+
.withHeader( 'X-Wikibase-CI-Temp-Account-Limit-One', true )
57+
.makeRequest();
58+
59+
assertValidError(
60+
response,
61+
429,
62+
'request-limit-reached',
63+
{ reason: 'temp-account-creation-limit-reached' }
64+
);
65+
} );
4466
} );
4567

4668
// checking the latest metadata for the newly created item

repo/rest-api/tests/phpunit/Infrastructure/DataAccess/EntityUpdaterTest.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
use Wikibase\Repo\RestApi\Domain\Services\Exceptions\AbuseFilterException;
3434
use Wikibase\Repo\RestApi\Domain\Services\Exceptions\RateLimitReached;
3535
use Wikibase\Repo\RestApi\Domain\Services\Exceptions\ResourceTooLargeException;
36+
use Wikibase\Repo\RestApi\Domain\Services\Exceptions\TempAccountCreationLimitReached;
3637
use Wikibase\Repo\RestApi\Infrastructure\DataAccess\EntityUpdater;
3738
use Wikibase\Repo\RestApi\Infrastructure\DataAccess\Exceptions\EntityUpdateFailed;
3839
use Wikibase\Repo\RestApi\Infrastructure\DataAccess\Exceptions\EntityUpdatePrevented;
@@ -260,6 +261,23 @@ public function testGivenAbuseFilterMatch_throwsCorrespondingException( EntityDo
260261
$this->newEntityUpdater()->update( $entity, $this->createStub( EditMetadata::class ) );
261262
}
262263

264+
/**
265+
* @dataProvider provideEntity
266+
*/
267+
public function testGivenAcctCreationThrottleHit_throwsTempAccountCreationLimitReached( EntityDocument $entityToUpdate ): void {
268+
$errorStatus = EditEntityStatus::newFatal( 'acct_creation_throttle_hit' );
269+
270+
$editEntity = $this->createStub( EditEntity::class );
271+
$editEntity->method( 'attemptSave' )->willReturn( $errorStatus );
272+
273+
$this->editEntityFactory = $this->createStub( MediaWikiEditEntityFactory::class );
274+
$this->editEntityFactory->method( 'newEditEntity' )->willReturn( $editEntity );
275+
276+
$this->expectException( TempAccountCreationLimitReached::class );
277+
278+
$this->newEntityUpdater()->update( $entityToUpdate, $this->createStub( EditMetadata::class ) );
279+
}
280+
263281
/**
264282
* @dataProvider provideEntity
265283
*/

0 commit comments

Comments
 (0)