enableAutoCreateTempUser( [ 'serialProvider' => [ 'type' => 'local', 'useYear' => false ], 'matchPattern' => '~$1', ] ); $tuc = $this->getServiceContainer()->getTempUserCreator(); $this->assertTrue( $tuc->isAutoCreateAction( 'edit' ) ); $this->assertTrue( $tuc->isTempName( '~1' ) ); // Create a temporary account $status = $tuc->create( null, new FauxRequest() ); $this->assertSame( '~1', $status->getUser()->getName() ); $this->assertSame( 1, $this->getDb()->newSelectQueryBuilder() ->from( 'logging' ) ->join( 'actor', null, 'log_actor=actor_id' ) ->where( [ 'actor_name' => '~1', 'log_action' => 'autocreate' ] ) ->fetchRowCount(), 'A logging entry indicating the autocreation of ~1 was expected.' ); // Repeat the test to verify that the serial number increments $status = $tuc->create( null, new FauxRequest() ); $this->assertSame( '~2', $status->getUser()->getName() ); $this->assertSame( 1, $this->getDb()->newSelectQueryBuilder() ->from( 'logging' ) ->join( 'actor', null, 'log_actor=actor_id' ) ->where( [ 'actor_name' => '~2', 'log_action' => 'autocreate' ] ) ->fetchRowCount(), 'A logging entry indicating the autocreation of ~2 was expected.' ); } private function getTempUserCreatorUnit() { $scope1 = ExtensionRegistry::getInstance()->setAttributeForTest( 'TempUserSerialProviders', [ 'test' => [ 'factory' => static function () { return new class implements SerialProvider { public function acquireIndex( int $year = 0 ): int { return 1; } }; } ], ] ); $scope2 = ExtensionRegistry::getInstance()->setAttributeForTest( 'TempUserSerialMappings', [ 'test' => [ 'factory' => static function () { return new class implements SerialMapping { public function getSerialIdForIndex( int $index ): string { $index--; $adjective = (int)( $index / 2 ); $animal = $index % 2; return [ 'active' ][$adjective] . ' ' . [ 'aardvark' ][$animal]; } }; } ] ] ); $config = new RealTempUserConfig( [ 'enabled' => true, 'expireAfterDays' => null, 'actions' => [ 'edit' ], 'genPattern' => '*Unregistered $1', 'matchPattern' => '*$1', 'serialProvider' => [ 'type' => 'test' ], 'serialMapping' => [ 'type' => 'test' ], ] ); $creator = new TempUserCreator( $config, $this->createSimpleObjectFactory(), $this->createMock( UserFactory::class ), $this->createMock( AuthManager::class ), $this->createMock( CentralIdLookup::class ), $this->createMock( Throttler::class ), $this->createMock( Throttler::class ) ); return [ $creator, [ $scope1, $scope2 ] ]; } public function testAcquireName_unit() { [ $creator, $scope ] = $this->getTempUserCreatorUnit(); /** @var TempUserCreator $creator */ $creator = TestingAccessWrapper::newFromObject( $creator ); $this->assertSame( '*Unregistered active aardvark', $creator->acquireName( '127.0.0.1' ) ); } public function testAcquireName_db() { $this->enableAutoCreateTempUser( [ 'serialProvider' => [ 'type' => 'local', 'useYear' => false ], 'matchPattern' => '~$1', ] ); $tuc = TestingAccessWrapper::newFromObject( $this->getServiceContainer()->getTempUserCreator() ); $this->assertSame( '~1', $tuc->acquireName( '127.0.0.1' ) ); $this->assertSame( '~2', $tuc->acquireName( '127.0.0.1' ) ); } public function testAcquireName_dbWithYear() { $this->enableAutoCreateTempUser( [ 'serialProvider' => [ 'type' => 'local', 'useYear' => true ] ] ); ConvertibleTimestamp::setFakeTime( '20000101000000' ); $tuc = TestingAccessWrapper::newFromObject( $this->getServiceContainer()->getTempUserCreator() ); $this->assertSame( '~2000-1', $tuc->acquireName( '127.0.0.1' ) ); $this->assertSame( '~2000-2', $tuc->acquireName( '127.0.0.1' ) ); ConvertibleTimestamp::setFakeTime( '20010101000000' ); $this->assertSame( '~2001-1', $tuc->acquireName( '127.0.0.1' ) ); } public function testAcquireNameOnDuplicate_db() { $this->enableAutoCreateTempUser(); $tuc = TestingAccessWrapper::newFromObject( $this->getServiceContainer()->getTempUserCreator() ); // Create a temporary account $this->assertSame( '~2024-1', $tuc->create( null, new FauxRequest() )->value->getName() ); // Reset the user_autocreate_serial table $this->truncateTable( 'user_autocreate_serial' ); // Because user_autocreate_serial was truncated, the ::acquireName method should // return null as the code attempts to return a temporary account that already exists. $this->assertSame( null, $tuc->acquireName( '127.0.0.1' ) ); } public function testCreateOnDuplicate_db() { $this->enableAutoCreateTempUser(); $tuc = $this->getServiceContainer()->getTempUserCreator(); // Create a temporary account $this->assertSame( '~2024-1', $tuc->create( null, new FauxRequest() )->value->getName() ); // Create a temporary account with an existing temporary account username. $secondCreateStatus = $tuc->create( '~2024-1', new FauxRequest() ); $this->assertStatusError( 'temp-user-unable-to-acquire', $secondCreateStatus ); // Assert that only one log entry for autocreation exists for ~2024-1, as the second call should have not // created a new log entry. $this->assertSame( 1, $this->getDb()->newSelectQueryBuilder() ->from( 'logging' ) ->join( 'actor', null, 'log_actor=actor_id' ) ->where( [ 'actor_name' => '~2024-1', 'log_action' => 'autocreate' ] ) ->fetchRowCount(), 'Only one logging entry indicating the autocreation of ~2024-1 was expected.' ); } public function testCreateOnInvalidUsername() { $this->enableAutoCreateTempUser(); $tuc = $this->getServiceContainer()->getTempUserCreator(); // Attempt to create the temporary account with an invalid username. $secondCreateStatus = $tuc->create( 'Template:InvalidUsername#test', new FauxRequest() ); $this->assertStatusError( 'internalerror_info', $secondCreateStatus ); } public function testAcquireNameThrottled() { $this->enableAutoCreateTempUser(); $this->overrideConfigValue( MainConfigNames::TempAccountNameAcquisitionThrottle, [ 'count' => 1, 'seconds' => 30 * 86400, ] ); $tuc = TestingAccessWrapper::newFromObject( $this->getServiceContainer()->getTempUserCreator() ); // Create a temporary account $this->assertSame( '~2024-1', $tuc->create( null, new FauxRequest() )->value->getName() ); // Attempt again; name acquisition should be limited $this->assertStatusError( 'temp-user-unable-to-acquire', $tuc->create( null, new FauxRequest() ) ); } public function testAcquireAndStashName() { /** @var TempUserCreator $creator */ [ $creator, $scope ] = $this->getTempUserCreatorUnit(); $session = new class extends Session { private $data = []; public function __construct() { } public function __destruct() { } public function get( $key, $default = null ) { return array_key_exists( $key, $this->data ) ? $this->data[$key] : $default; } public function set( $key, $value ) { $this->data[$key] = $value; } public function save() { } public function getRequest() { return new FauxRequest(); } }; $name = $creator->acquireAndStashName( $session ); $this->assertSame( '*Unregistered active aardvark', $name ); $name = $creator->acquireAndStashName( $session ); $this->assertSame( '*Unregistered active aardvark', $name ); } public function testRateLimit() { $this->enableAutoCreateTempUser( [ 'serialProvider' => [ 'type' => 'local', 'useYear' => false ], 'matchPattern' => '~$1', ] ); $this->overrideConfigValues( [ MainConfigNames::AccountCreationThrottle => [ 'count' => 10, 'seconds' => 86400 ], MainConfigNames::TempAccountCreationThrottle => [ 'count' => 1, 'seconds' => 86400 ], ] ); $tuc = $this->getServiceContainer()->getTempUserCreator(); $status = $tuc->create( null, new FauxRequest() ); $this->assertSame( '~1', $status->getUser()->getName() ); // Repeat creating a temporary account, and verify that this fails due to the rate limit. $status = $tuc->create( null, new FauxRequest() ); // TODO: Use new message key (T357777, T357802) $this->assertStatusError( 'acct_creation_throttle_hit', $status ); // If the temporary account creation failed due to the rate limit, then no log entry should have been created. $this->assertSame( 0, $this->getDb()->newSelectQueryBuilder() ->from( 'logging' ) ->join( 'actor', null, 'log_actor=actor_id' ) ->where( [ 'actor_name' => '~2', 'log_action' => 'autocreate' ] ) ->fetchRowCount(), 'A logging entry indicating the autocreation of ~2 was not expected.' ); } }