Skip to content

Commit

Permalink
Merge "REST: Respond 429 when hitting the temp user rate limit"
Browse files Browse the repository at this point in the history
  • Loading branch information
jenkins-bot authored and Gerrit Code Review committed Sep 25, 2024
2 parents 645e39c + 96e8ab0 commit 5c8d9e2
Show file tree
Hide file tree
Showing 8 changed files with 69 additions and 1 deletion.
4 changes: 4 additions & 0 deletions repo/config/Wikibase.ci.php
Original file line number Diff line number Diff line change
Expand Up @@ -83,3 +83,7 @@
if ( $request->getHeader( 'X-Wikibase-CI-Anon-Rate-Limit-Zero', WebRequest::GETHEADER_LIST ) ) {
$wgRateLimits = [ 'edit' => [ 'anon' => [ 0, 60 ] ] ];
}

if ( $request->getHeader( 'X-Wikibase-CI-Temp-Account-Limit-One', WebRequest::GETHEADER_LIST ) ) {
$wgTempAccountCreationThrottle = [ [ 'count' => 1, 'seconds' => 86400 ] ];
}
3 changes: 2 additions & 1 deletion repo/rest-api/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ Descriptions of the different kinds of tests can be found in the @ref restApiTes

#### e2e and schema tests

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

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


[1]: https://www.mediawiki.org/wiki/MediaWiki_API_integration_tests
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use Wikibase\Repo\RestApi\Domain\Services\Exceptions\AbuseFilterException;
use Wikibase\Repo\RestApi\Domain\Services\Exceptions\RateLimitReached;
use Wikibase\Repo\RestApi\Domain\Services\Exceptions\ResourceTooLargeException;
use Wikibase\Repo\RestApi\Domain\Services\Exceptions\TempAccountCreationLimitReached;

/**
* @license GPL-2.0-or-later
Expand Down Expand Up @@ -33,6 +34,8 @@ public function executeWithExceptionHandling( callable $callback ) {
] );
} catch ( RateLimitReached $e ) {
throw UseCaseError::newRateLimitReached( UseCaseError::REQUEST_LIMIT_REASON_RATE_LIMIT );
} catch ( TempAccountCreationLimitReached $e ) {
throw UseCaseError::newRateLimitReached( UseCaseError::REQUEST_LIMIT_REASON_TEMP_ACCOUNT_CREATION_LIMIT );
}
}

Expand Down
1 change: 1 addition & 0 deletions repo/rest-api/src/Application/UseCases/UseCaseError.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ class UseCaseError extends UseCaseException {
public const REFERENCED_RESOURCE_NOT_FOUND = 'referenced-resource-not-found';
public const REQUEST_LIMIT_REACHED = 'request-limit-reached';
public const REQUEST_LIMIT_REASON_RATE_LIMIT = 'rate-limit-reached';
public const REQUEST_LIMIT_REASON_TEMP_ACCOUNT_CREATION_LIMIT = 'temp-account-creation-limit-reached';
public const RESOURCE_NOT_FOUND = 'resource-not-found';
public const RESOURCE_TOO_LARGE = 'resource-too-large';
public const STATEMENT_GROUP_PROPERTY_ID_MISMATCH = 'statement-group-property-id-mismatch';
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php declare( strict_types=1 );

namespace Wikibase\Repo\RestApi\Domain\Services\Exceptions;

use Exception;

/**
* @license GPL-2.0-or-later
*/
class TempAccountCreationLimitReached extends Exception {
}
8 changes: 8 additions & 0 deletions repo/rest-api/src/Infrastructure/DataAccess/EntityUpdater.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
use Wikibase\Repo\RestApi\Domain\Services\Exceptions\AbuseFilterException;
use Wikibase\Repo\RestApi\Domain\Services\Exceptions\RateLimitReached;
use Wikibase\Repo\RestApi\Domain\Services\Exceptions\ResourceTooLargeException;
use Wikibase\Repo\RestApi\Domain\Services\Exceptions\TempAccountCreationLimitReached;
use Wikibase\Repo\RestApi\Infrastructure\DataAccess\Exceptions\EntityUpdateFailed;
use Wikibase\Repo\RestApi\Infrastructure\DataAccess\Exceptions\EntityUpdatePrevented;
use Wikibase\Repo\RestApi\Infrastructure\EditSummaryFormatter;
Expand Down Expand Up @@ -65,6 +66,7 @@ public function __construct(
* @throws ResourceTooLargeException
* @throws AbuseFilterException
* @throws RateLimitReached
* @throws TempAccountCreationLimitReached
*/
public function create( EntityDocument $entity, EditMetadata $editMetadata ): EntityRevision {
return $this->createOrUpdate( $entity, $editMetadata, EDIT_NEW );
Expand All @@ -75,6 +77,7 @@ public function create( EntityDocument $entity, EditMetadata $editMetadata ): En
* @throws ResourceTooLargeException
* @throws AbuseFilterException
* @throws RateLimitReached
* @throws TempAccountCreationLimitReached
*/
public function update( EntityDocument $entity, EditMetadata $editMetadata ): EntityRevision {
return $this->createOrUpdate( $entity, $editMetadata, EDIT_UPDATE );
Expand All @@ -85,6 +88,7 @@ public function update( EntityDocument $entity, EditMetadata $editMetadata ): En
* @throws ResourceTooLargeException
* @throws AbuseFilterException
* @throws RateLimitReached
* @throws TempAccountCreationLimitReached
*/
private function createOrUpdate(
EntityDocument $entity,
Expand Down Expand Up @@ -131,6 +135,10 @@ private function createOrUpdate(
throw new RateLimitReached();
}

if ( $this->findErrorInStatus( $status, 'acct_creation_throttle_hit' ) ) {
throw new TempAccountCreationLimitReached();
}

if ( $this->isPreventedEdit( $status ) ) {
throw new EntityUpdatePrevented( (string)$status );
}
Expand Down
22 changes: 22 additions & 0 deletions repo/rest-api/tests/mocha/api-testing/TempUserTest.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ const {
getItemCreateRequest
} = require( '../helpers/happyPathRequestBuilders' );
const entityHelper = require( '../helpers/entityHelper' );
const { assertValidError } = require( '../helpers/responseValidator' );

describeWithTestData( 'IP masking', ( itemRequestInputs, propertyRequestInputs, describeEachRouteWithReset ) => {
function withTempUserConfig( newRequestBuilder, config ) {
Expand Down Expand Up @@ -41,6 +42,27 @@ describeWithTestData( 'IP masking', ( itemRequestInputs, propertyRequestInputs,
const { user } = await entityHelper.getLatestEditMetadata( requestInputs.mainTestSubject );
assert.include( user, tempUserPrefix );
} );

// Note: If this test fails, it might be due to the throttler relying on caching.
// Ensure caching is enabled for the wiki under test, as the throttler won't work without it.
it( 'responds 429 when the temp user creation limit is reached', async () => {
await newRequestBuilder()
.withHeader( 'X-Wikibase-Ci-Tempuser-Config', JSON.stringify( { enabled: true } ) )
.withHeader( 'X-Wikibase-CI-Temp-Account-Limit-One', true )
.makeRequest();

const response = await newRequestBuilder()
.withHeader( 'X-Wikibase-Ci-Tempuser-Config', JSON.stringify( { enabled: true } ) )
.withHeader( 'X-Wikibase-CI-Temp-Account-Limit-One', true )
.makeRequest();

assertValidError(
response,
429,
'request-limit-reached',
{ reason: 'temp-account-creation-limit-reached' }
);
} );
} );

// checking the latest metadata for the newly created item
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
use Wikibase\Repo\RestApi\Domain\Services\Exceptions\AbuseFilterException;
use Wikibase\Repo\RestApi\Domain\Services\Exceptions\RateLimitReached;
use Wikibase\Repo\RestApi\Domain\Services\Exceptions\ResourceTooLargeException;
use Wikibase\Repo\RestApi\Domain\Services\Exceptions\TempAccountCreationLimitReached;
use Wikibase\Repo\RestApi\Infrastructure\DataAccess\EntityUpdater;
use Wikibase\Repo\RestApi\Infrastructure\DataAccess\Exceptions\EntityUpdateFailed;
use Wikibase\Repo\RestApi\Infrastructure\DataAccess\Exceptions\EntityUpdatePrevented;
Expand Down Expand Up @@ -260,6 +261,23 @@ public function testGivenAbuseFilterMatch_throwsCorrespondingException( EntityDo
$this->newEntityUpdater()->update( $entity, $this->createStub( EditMetadata::class ) );
}

/**
* @dataProvider provideEntity
*/
public function testGivenAcctCreationThrottleHit_throwsTempAccountCreationLimitReached( EntityDocument $entityToUpdate ): void {
$errorStatus = EditEntityStatus::newFatal( 'acct_creation_throttle_hit' );

$editEntity = $this->createStub( EditEntity::class );
$editEntity->method( 'attemptSave' )->willReturn( $errorStatus );

$this->editEntityFactory = $this->createStub( MediaWikiEditEntityFactory::class );
$this->editEntityFactory->method( 'newEditEntity' )->willReturn( $editEntity );

$this->expectException( TempAccountCreationLimitReached::class );

$this->newEntityUpdater()->update( $entityToUpdate, $this->createStub( EditMetadata::class ) );
}

/**
* @dataProvider provideEntity
*/
Expand Down

0 comments on commit 5c8d9e2

Please sign in to comment.