wiki.techinc.nl/tests/phpunit/integration/includes/user/TempUser/TempUserCreatorTest.php

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

288 lines
9.6 KiB
PHP
Raw Normal View History

<?php
Support multiple matchPatterns in temp user autocreate config Why: * There is a need to update the generation and match pattern on WMF wikis to a new format that includes the year and starts with `~`. As such, the 'matchPattern' key needs to be updated. * Removing the old 'matchPattern' from the wgAutoCreateTempUser config currently leaves existing temporary accounts as no longer recongnised as temporary accounts. * Instead, the 'matchPattern' needs to be able to take an array of string patterns so that old patterns can still be used for matching. What: * Update the MainConfigSchama to indicate that 'matchPattern' in the wgAutoCreateTempUser config can be an array of strings. * Add TempUserConfig::getMatchPatterns and deprecate TempUserConfig:: getMatchPattern. This is needed because ::getMatchPattern was typed to only ever return one Pattern, which is no longer the case with this config change. * Update the RealTempUserConfig to support multiple patterns defined in the 'matchPattern' key. The RealTempUserConfig::getMatchPattern method returns the pattern or first pattern if multiple are defined to allow time for existing usages of this deprecated method to be updated. * Update the RealTempUserConfig to rely on other methods instead of checking object property values where possible (e.g. use ::isEnabled instead of checking $this->enabled) to allow easier unit testing. * Update UserSelectQueryBuilder and ChangesListSpecialPage to use TempUserConfig ::getMatchPatterns instead of ::getMatchPattern. * Update mediawiki.util/util.js to be able to parse the 'matchPattern' value when it is an array of strings. * Update maintenance/userOptions.php to use ::getMatchPatterns instead of ::getMatchPattern. * Add and update unit and integration tests for the new code, as well as expanding coverage over existing code that was indirectly affected. Bug: T354619 Change-Id: I3763daefe4dc7c76370bd934fb20452591c9c762
2024-01-10 23:40:27 +00:00
namespace MediaWiki\Tests\Integration\User\TempUser;
use ExtensionRegistry;
use MediaWiki\Auth\AuthManager;
use MediaWiki\Auth\Throttler;
use MediaWiki\MainConfigNames;
use MediaWiki\Request\FauxRequest;
use MediaWiki\Session\Session;
Support multiple matchPatterns in temp user autocreate config Why: * There is a need to update the generation and match pattern on WMF wikis to a new format that includes the year and starts with `~`. As such, the 'matchPattern' key needs to be updated. * Removing the old 'matchPattern' from the wgAutoCreateTempUser config currently leaves existing temporary accounts as no longer recongnised as temporary accounts. * Instead, the 'matchPattern' needs to be able to take an array of string patterns so that old patterns can still be used for matching. What: * Update the MainConfigSchama to indicate that 'matchPattern' in the wgAutoCreateTempUser config can be an array of strings. * Add TempUserConfig::getMatchPatterns and deprecate TempUserConfig:: getMatchPattern. This is needed because ::getMatchPattern was typed to only ever return one Pattern, which is no longer the case with this config change. * Update the RealTempUserConfig to support multiple patterns defined in the 'matchPattern' key. The RealTempUserConfig::getMatchPattern method returns the pattern or first pattern if multiple are defined to allow time for existing usages of this deprecated method to be updated. * Update the RealTempUserConfig to rely on other methods instead of checking object property values where possible (e.g. use ::isEnabled instead of checking $this->enabled) to allow easier unit testing. * Update UserSelectQueryBuilder and ChangesListSpecialPage to use TempUserConfig ::getMatchPatterns instead of ::getMatchPattern. * Update mediawiki.util/util.js to be able to parse the 'matchPattern' value when it is an array of strings. * Update maintenance/userOptions.php to use ::getMatchPatterns instead of ::getMatchPattern. * Add and update unit and integration tests for the new code, as well as expanding coverage over existing code that was indirectly affected. Bug: T354619 Change-Id: I3763daefe4dc7c76370bd934fb20452591c9c762
2024-01-10 23:40:27 +00:00
use MediaWiki\Tests\User\TempUser\TempUserTestTrait;
Handle collisions from SerialProvider::acquireIndex Why: * When using the TempUserCreator::create or ::acquireAndStashName to get temporary account when the chosen username already existed, that temporary account is treated as if it doesn't exist. This causes confusing "userexists" errors and also causes the user to be logged into an already existing temporary account. * Furthermore, because the user existence check in AuthManager::auto CreateUser only checks the local wiki, if an existing temporary account exists globally but not on the local wiki then the code sign a new user into an existing temporary account. * This is very bad behaviour, though shouldn't happen unless the serialMapping configuration uses a SerialMapping class that could provide duplicates and/or the configuration has been changed to use a different SerialMapping class. * There is a need to change the SerialMapping class in use to a different class, which means that the code will attempt to use temporary account usernames which already exist. * As such, the code that is generating the temporary account usernames based on the SerialMapping and SerialProvider should be aware that it may produce an already existing temporary account username, even if the SerialMapping class being used is asserted to never provide duplicates. * Therefore, the code that generates temporary account usernames should always attempt to verify that a automatically generated temporary account name does not already exist on the wiki. What: * Update TempUserCreator::acquireName to check to see if the username it generates already exists centrally using the CentralIdLookup. If it does, then the method returns null. Otherwise, the username that hasn't been used yet is returned. * Create the private method TempUserCreator::attemptAutoCreate that attempts an autocreate for a temporary account name, and optionally logs the account in. * Update TempUserCreator::create to use ::attemptAutoCreate to first to check if the account can be created and then again once the account is created to actually login to that temporary account. This is done to prevent logins to existing temporary accounts on the local wiki. The second call to actually perform the login is necessary as there is no other way to login to a temporary account. * Update TempUserCreator::acquireAndStashName to respond to the changes to ::acquireName, such that it returns null if ::acquireName returns null and also does not modify the session. * Update EditPage::maybeActivateTempUserCreate to return a Status and return a good status in all cases except when a temporary account name could not be acquired. * Add IEditObject::AS_UNABLE_TO_ACQUIRE_TEMP_ACCOUNT, and use it as the value of the fatal status returned by EditPage ::internalAttemptSave if a temporary account name could not be acquired. This will cause the display of a useful error to the user on edit. * Update ApiEditPage and ApiAcquireTempUserName to die with an error if a temporary account username was unable to be acquired. * Provide tests for the untested ApiAcquireTempUserName.php file including testing the new behaviour. * Add and update tests for TempUserCreator.php Bug: T353390 Change-Id: Id3a316ea0eba544d51d4ffcdfb03e35f4b3c54cc
2023-12-13 22:56:29 +00:00
use MediaWiki\User\CentralId\CentralIdLookup;
use MediaWiki\User\TempUser\RealTempUserConfig;
use MediaWiki\User\TempUser\SerialMapping;
use MediaWiki\User\TempUser\SerialProvider;
use MediaWiki\User\TempUser\TempUserCreator;
use MediaWiki\User\UserFactory;
use Wikimedia\TestingAccessWrapper;
use Wikimedia\Timestamp\ConvertibleTimestamp;
/**
* @group Database
* @covers \MediaWiki\User\TempUser\DBSerialProvider
* @covers \MediaWiki\User\TempUser\LocalSerialProvider
* @covers \MediaWiki\User\TempUser\TempUserCreator
* @covers \MediaWiki\User\TempUser\CreateStatus
*/
class TempUserCreatorTest extends \MediaWikiIntegrationTestCase {
Support multiple matchPatterns in temp user autocreate config Why: * There is a need to update the generation and match pattern on WMF wikis to a new format that includes the year and starts with `~`. As such, the 'matchPattern' key needs to be updated. * Removing the old 'matchPattern' from the wgAutoCreateTempUser config currently leaves existing temporary accounts as no longer recongnised as temporary accounts. * Instead, the 'matchPattern' needs to be able to take an array of string patterns so that old patterns can still be used for matching. What: * Update the MainConfigSchama to indicate that 'matchPattern' in the wgAutoCreateTempUser config can be an array of strings. * Add TempUserConfig::getMatchPatterns and deprecate TempUserConfig:: getMatchPattern. This is needed because ::getMatchPattern was typed to only ever return one Pattern, which is no longer the case with this config change. * Update the RealTempUserConfig to support multiple patterns defined in the 'matchPattern' key. The RealTempUserConfig::getMatchPattern method returns the pattern or first pattern if multiple are defined to allow time for existing usages of this deprecated method to be updated. * Update the RealTempUserConfig to rely on other methods instead of checking object property values where possible (e.g. use ::isEnabled instead of checking $this->enabled) to allow easier unit testing. * Update UserSelectQueryBuilder and ChangesListSpecialPage to use TempUserConfig ::getMatchPatterns instead of ::getMatchPattern. * Update mediawiki.util/util.js to be able to parse the 'matchPattern' value when it is an array of strings. * Update maintenance/userOptions.php to use ::getMatchPatterns instead of ::getMatchPattern. * Add and update unit and integration tests for the new code, as well as expanding coverage over existing code that was indirectly affected. Bug: T354619 Change-Id: I3763daefe4dc7c76370bd934fb20452591c9c762
2024-01-10 23:40:27 +00:00
use TempUserTestTrait;
public function testCreate() {
$this->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];
}
};
}
]
]
);
Support multiple matchPatterns in temp user autocreate config Why: * There is a need to update the generation and match pattern on WMF wikis to a new format that includes the year and starts with `~`. As such, the 'matchPattern' key needs to be updated. * Removing the old 'matchPattern' from the wgAutoCreateTempUser config currently leaves existing temporary accounts as no longer recongnised as temporary accounts. * Instead, the 'matchPattern' needs to be able to take an array of string patterns so that old patterns can still be used for matching. What: * Update the MainConfigSchama to indicate that 'matchPattern' in the wgAutoCreateTempUser config can be an array of strings. * Add TempUserConfig::getMatchPatterns and deprecate TempUserConfig:: getMatchPattern. This is needed because ::getMatchPattern was typed to only ever return one Pattern, which is no longer the case with this config change. * Update the RealTempUserConfig to support multiple patterns defined in the 'matchPattern' key. The RealTempUserConfig::getMatchPattern method returns the pattern or first pattern if multiple are defined to allow time for existing usages of this deprecated method to be updated. * Update the RealTempUserConfig to rely on other methods instead of checking object property values where possible (e.g. use ::isEnabled instead of checking $this->enabled) to allow easier unit testing. * Update UserSelectQueryBuilder and ChangesListSpecialPage to use TempUserConfig ::getMatchPatterns instead of ::getMatchPattern. * Update mediawiki.util/util.js to be able to parse the 'matchPattern' value when it is an array of strings. * Update maintenance/userOptions.php to use ::getMatchPatterns instead of ::getMatchPattern. * Add and update unit and integration tests for the new code, as well as expanding coverage over existing code that was indirectly affected. Bug: T354619 Change-Id: I3763daefe4dc7c76370bd934fb20452591c9c762
2024-01-10 23:40:27 +00:00
$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 ),
Handle collisions from SerialProvider::acquireIndex Why: * When using the TempUserCreator::create or ::acquireAndStashName to get temporary account when the chosen username already existed, that temporary account is treated as if it doesn't exist. This causes confusing "userexists" errors and also causes the user to be logged into an already existing temporary account. * Furthermore, because the user existence check in AuthManager::auto CreateUser only checks the local wiki, if an existing temporary account exists globally but not on the local wiki then the code sign a new user into an existing temporary account. * This is very bad behaviour, though shouldn't happen unless the serialMapping configuration uses a SerialMapping class that could provide duplicates and/or the configuration has been changed to use a different SerialMapping class. * There is a need to change the SerialMapping class in use to a different class, which means that the code will attempt to use temporary account usernames which already exist. * As such, the code that is generating the temporary account usernames based on the SerialMapping and SerialProvider should be aware that it may produce an already existing temporary account username, even if the SerialMapping class being used is asserted to never provide duplicates. * Therefore, the code that generates temporary account usernames should always attempt to verify that a automatically generated temporary account name does not already exist on the wiki. What: * Update TempUserCreator::acquireName to check to see if the username it generates already exists centrally using the CentralIdLookup. If it does, then the method returns null. Otherwise, the username that hasn't been used yet is returned. * Create the private method TempUserCreator::attemptAutoCreate that attempts an autocreate for a temporary account name, and optionally logs the account in. * Update TempUserCreator::create to use ::attemptAutoCreate to first to check if the account can be created and then again once the account is created to actually login to that temporary account. This is done to prevent logins to existing temporary accounts on the local wiki. The second call to actually perform the login is necessary as there is no other way to login to a temporary account. * Update TempUserCreator::acquireAndStashName to respond to the changes to ::acquireName, such that it returns null if ::acquireName returns null and also does not modify the session. * Update EditPage::maybeActivateTempUserCreate to return a Status and return a good status in all cases except when a temporary account name could not be acquired. * Add IEditObject::AS_UNABLE_TO_ACQUIRE_TEMP_ACCOUNT, and use it as the value of the fatal status returned by EditPage ::internalAttemptSave if a temporary account name could not be acquired. This will cause the display of a useful error to the user on edit. * Update ApiEditPage and ApiAcquireTempUserName to die with an error if a temporary account username was unable to be acquired. * Provide tests for the untested ApiAcquireTempUserName.php file including testing the new behaviour. * Add and update tests for TempUserCreator.php Bug: T353390 Change-Id: Id3a316ea0eba544d51d4ffcdfb03e35f4b3c54cc
2023-12-13 22:56:29 +00:00
$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() {
Support multiple matchPatterns in temp user autocreate config Why: * There is a need to update the generation and match pattern on WMF wikis to a new format that includes the year and starts with `~`. As such, the 'matchPattern' key needs to be updated. * Removing the old 'matchPattern' from the wgAutoCreateTempUser config currently leaves existing temporary accounts as no longer recongnised as temporary accounts. * Instead, the 'matchPattern' needs to be able to take an array of string patterns so that old patterns can still be used for matching. What: * Update the MainConfigSchama to indicate that 'matchPattern' in the wgAutoCreateTempUser config can be an array of strings. * Add TempUserConfig::getMatchPatterns and deprecate TempUserConfig:: getMatchPattern. This is needed because ::getMatchPattern was typed to only ever return one Pattern, which is no longer the case with this config change. * Update the RealTempUserConfig to support multiple patterns defined in the 'matchPattern' key. The RealTempUserConfig::getMatchPattern method returns the pattern or first pattern if multiple are defined to allow time for existing usages of this deprecated method to be updated. * Update the RealTempUserConfig to rely on other methods instead of checking object property values where possible (e.g. use ::isEnabled instead of checking $this->enabled) to allow easier unit testing. * Update UserSelectQueryBuilder and ChangesListSpecialPage to use TempUserConfig ::getMatchPatterns instead of ::getMatchPattern. * Update mediawiki.util/util.js to be able to parse the 'matchPattern' value when it is an array of strings. * Update maintenance/userOptions.php to use ::getMatchPatterns instead of ::getMatchPattern. * Add and update unit and integration tests for the new code, as well as expanding coverage over existing code that was indirectly affected. Bug: T354619 Change-Id: I3763daefe4dc7c76370bd934fb20452591c9c762
2024-01-10 23:40:27 +00:00
$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' ) );
}
Handle collisions from SerialProvider::acquireIndex Why: * When using the TempUserCreator::create or ::acquireAndStashName to get temporary account when the chosen username already existed, that temporary account is treated as if it doesn't exist. This causes confusing "userexists" errors and also causes the user to be logged into an already existing temporary account. * Furthermore, because the user existence check in AuthManager::auto CreateUser only checks the local wiki, if an existing temporary account exists globally but not on the local wiki then the code sign a new user into an existing temporary account. * This is very bad behaviour, though shouldn't happen unless the serialMapping configuration uses a SerialMapping class that could provide duplicates and/or the configuration has been changed to use a different SerialMapping class. * There is a need to change the SerialMapping class in use to a different class, which means that the code will attempt to use temporary account usernames which already exist. * As such, the code that is generating the temporary account usernames based on the SerialMapping and SerialProvider should be aware that it may produce an already existing temporary account username, even if the SerialMapping class being used is asserted to never provide duplicates. * Therefore, the code that generates temporary account usernames should always attempt to verify that a automatically generated temporary account name does not already exist on the wiki. What: * Update TempUserCreator::acquireName to check to see if the username it generates already exists centrally using the CentralIdLookup. If it does, then the method returns null. Otherwise, the username that hasn't been used yet is returned. * Create the private method TempUserCreator::attemptAutoCreate that attempts an autocreate for a temporary account name, and optionally logs the account in. * Update TempUserCreator::create to use ::attemptAutoCreate to first to check if the account can be created and then again once the account is created to actually login to that temporary account. This is done to prevent logins to existing temporary accounts on the local wiki. The second call to actually perform the login is necessary as there is no other way to login to a temporary account. * Update TempUserCreator::acquireAndStashName to respond to the changes to ::acquireName, such that it returns null if ::acquireName returns null and also does not modify the session. * Update EditPage::maybeActivateTempUserCreate to return a Status and return a good status in all cases except when a temporary account name could not be acquired. * Add IEditObject::AS_UNABLE_TO_ACQUIRE_TEMP_ACCOUNT, and use it as the value of the fatal status returned by EditPage ::internalAttemptSave if a temporary account name could not be acquired. This will cause the display of a useful error to the user on edit. * Update ApiEditPage and ApiAcquireTempUserName to die with an error if a temporary account username was unable to be acquired. * Provide tests for the untested ApiAcquireTempUserName.php file including testing the new behaviour. * Add and update tests for TempUserCreator.php Bug: T353390 Change-Id: Id3a316ea0eba544d51d4ffcdfb03e35f4b3c54cc
2023-12-13 22:56:29 +00:00
public function testAcquireNameOnDuplicate_db() {
$this->enableAutoCreateTempUser();
Handle collisions from SerialProvider::acquireIndex Why: * When using the TempUserCreator::create or ::acquireAndStashName to get temporary account when the chosen username already existed, that temporary account is treated as if it doesn't exist. This causes confusing "userexists" errors and also causes the user to be logged into an already existing temporary account. * Furthermore, because the user existence check in AuthManager::auto CreateUser only checks the local wiki, if an existing temporary account exists globally but not on the local wiki then the code sign a new user into an existing temporary account. * This is very bad behaviour, though shouldn't happen unless the serialMapping configuration uses a SerialMapping class that could provide duplicates and/or the configuration has been changed to use a different SerialMapping class. * There is a need to change the SerialMapping class in use to a different class, which means that the code will attempt to use temporary account usernames which already exist. * As such, the code that is generating the temporary account usernames based on the SerialMapping and SerialProvider should be aware that it may produce an already existing temporary account username, even if the SerialMapping class being used is asserted to never provide duplicates. * Therefore, the code that generates temporary account usernames should always attempt to verify that a automatically generated temporary account name does not already exist on the wiki. What: * Update TempUserCreator::acquireName to check to see if the username it generates already exists centrally using the CentralIdLookup. If it does, then the method returns null. Otherwise, the username that hasn't been used yet is returned. * Create the private method TempUserCreator::attemptAutoCreate that attempts an autocreate for a temporary account name, and optionally logs the account in. * Update TempUserCreator::create to use ::attemptAutoCreate to first to check if the account can be created and then again once the account is created to actually login to that temporary account. This is done to prevent logins to existing temporary accounts on the local wiki. The second call to actually perform the login is necessary as there is no other way to login to a temporary account. * Update TempUserCreator::acquireAndStashName to respond to the changes to ::acquireName, such that it returns null if ::acquireName returns null and also does not modify the session. * Update EditPage::maybeActivateTempUserCreate to return a Status and return a good status in all cases except when a temporary account name could not be acquired. * Add IEditObject::AS_UNABLE_TO_ACQUIRE_TEMP_ACCOUNT, and use it as the value of the fatal status returned by EditPage ::internalAttemptSave if a temporary account name could not be acquired. This will cause the display of a useful error to the user on edit. * Update ApiEditPage and ApiAcquireTempUserName to die with an error if a temporary account username was unable to be acquired. * Provide tests for the untested ApiAcquireTempUserName.php file including testing the new behaviour. * Add and update tests for TempUserCreator.php Bug: T353390 Change-Id: Id3a316ea0eba544d51d4ffcdfb03e35f4b3c54cc
2023-12-13 22:56:29 +00:00
$tuc = TestingAccessWrapper::newFromObject(
$this->getServiceContainer()->getTempUserCreator()
);
// Create a temporary account
$this->assertSame( '~2024-1', $tuc->create( null, new FauxRequest() )->value->getName() );
Handle collisions from SerialProvider::acquireIndex Why: * When using the TempUserCreator::create or ::acquireAndStashName to get temporary account when the chosen username already existed, that temporary account is treated as if it doesn't exist. This causes confusing "userexists" errors and also causes the user to be logged into an already existing temporary account. * Furthermore, because the user existence check in AuthManager::auto CreateUser only checks the local wiki, if an existing temporary account exists globally but not on the local wiki then the code sign a new user into an existing temporary account. * This is very bad behaviour, though shouldn't happen unless the serialMapping configuration uses a SerialMapping class that could provide duplicates and/or the configuration has been changed to use a different SerialMapping class. * There is a need to change the SerialMapping class in use to a different class, which means that the code will attempt to use temporary account usernames which already exist. * As such, the code that is generating the temporary account usernames based on the SerialMapping and SerialProvider should be aware that it may produce an already existing temporary account username, even if the SerialMapping class being used is asserted to never provide duplicates. * Therefore, the code that generates temporary account usernames should always attempt to verify that a automatically generated temporary account name does not already exist on the wiki. What: * Update TempUserCreator::acquireName to check to see if the username it generates already exists centrally using the CentralIdLookup. If it does, then the method returns null. Otherwise, the username that hasn't been used yet is returned. * Create the private method TempUserCreator::attemptAutoCreate that attempts an autocreate for a temporary account name, and optionally logs the account in. * Update TempUserCreator::create to use ::attemptAutoCreate to first to check if the account can be created and then again once the account is created to actually login to that temporary account. This is done to prevent logins to existing temporary accounts on the local wiki. The second call to actually perform the login is necessary as there is no other way to login to a temporary account. * Update TempUserCreator::acquireAndStashName to respond to the changes to ::acquireName, such that it returns null if ::acquireName returns null and also does not modify the session. * Update EditPage::maybeActivateTempUserCreate to return a Status and return a good status in all cases except when a temporary account name could not be acquired. * Add IEditObject::AS_UNABLE_TO_ACQUIRE_TEMP_ACCOUNT, and use it as the value of the fatal status returned by EditPage ::internalAttemptSave if a temporary account name could not be acquired. This will cause the display of a useful error to the user on edit. * Update ApiEditPage and ApiAcquireTempUserName to die with an error if a temporary account username was unable to be acquired. * Provide tests for the untested ApiAcquireTempUserName.php file including testing the new behaviour. * Add and update tests for TempUserCreator.php Bug: T353390 Change-Id: Id3a316ea0eba544d51d4ffcdfb03e35f4b3c54cc
2023-12-13 22:56:29 +00:00
// 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() ) );
Handle collisions from SerialProvider::acquireIndex Why: * When using the TempUserCreator::create or ::acquireAndStashName to get temporary account when the chosen username already existed, that temporary account is treated as if it doesn't exist. This causes confusing "userexists" errors and also causes the user to be logged into an already existing temporary account. * Furthermore, because the user existence check in AuthManager::auto CreateUser only checks the local wiki, if an existing temporary account exists globally but not on the local wiki then the code sign a new user into an existing temporary account. * This is very bad behaviour, though shouldn't happen unless the serialMapping configuration uses a SerialMapping class that could provide duplicates and/or the configuration has been changed to use a different SerialMapping class. * There is a need to change the SerialMapping class in use to a different class, which means that the code will attempt to use temporary account usernames which already exist. * As such, the code that is generating the temporary account usernames based on the SerialMapping and SerialProvider should be aware that it may produce an already existing temporary account username, even if the SerialMapping class being used is asserted to never provide duplicates. * Therefore, the code that generates temporary account usernames should always attempt to verify that a automatically generated temporary account name does not already exist on the wiki. What: * Update TempUserCreator::acquireName to check to see if the username it generates already exists centrally using the CentralIdLookup. If it does, then the method returns null. Otherwise, the username that hasn't been used yet is returned. * Create the private method TempUserCreator::attemptAutoCreate that attempts an autocreate for a temporary account name, and optionally logs the account in. * Update TempUserCreator::create to use ::attemptAutoCreate to first to check if the account can be created and then again once the account is created to actually login to that temporary account. This is done to prevent logins to existing temporary accounts on the local wiki. The second call to actually perform the login is necessary as there is no other way to login to a temporary account. * Update TempUserCreator::acquireAndStashName to respond to the changes to ::acquireName, such that it returns null if ::acquireName returns null and also does not modify the session. * Update EditPage::maybeActivateTempUserCreate to return a Status and return a good status in all cases except when a temporary account name could not be acquired. * Add IEditObject::AS_UNABLE_TO_ACQUIRE_TEMP_ACCOUNT, and use it as the value of the fatal status returned by EditPage ::internalAttemptSave if a temporary account name could not be acquired. This will cause the display of a useful error to the user on edit. * Update ApiEditPage and ApiAcquireTempUserName to die with an error if a temporary account username was unable to be acquired. * Provide tests for the untested ApiAcquireTempUserName.php file including testing the new behaviour. * Add and update tests for TempUserCreator.php Bug: T353390 Change-Id: Id3a316ea0eba544d51d4ffcdfb03e35f4b3c54cc
2023-12-13 22:56:29 +00:00
}
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.'
);
}
}