/**
* @group AuthManager
* @group Database
- * @covers MediaWiki\Auth\AuthManager
+ * @covers \MediaWiki\Auth\AuthManager
*/
class AuthManagerTest extends \MediaWikiTestCase {
/** @var WebRequest */
/** @var TestingAccessWrapper */
protected $managerPriv;
- protected function setUp() {
- parent::setUp();
-
- $this->setMwGlobals( [ 'wgAuth' => null ] );
- $this->stashMwGlobals( [ 'wgHooks' ] );
- }
-
/**
* Sets a mock on a hook
* @param string $hook
* @return object $mock->expects( $expect )->method( ... ).
*/
protected function hook( $hook, $expect ) {
- global $wgHooks;
$mock = $this->getMockBuilder( __CLASS__ )
->setMethods( [ "on$hook" ] )
->getMock();
- $wgHooks[$hook] = [ $mock ];
+ $this->setTemporaryHook( $hook, $mock );
return $mock->expects( $expect )->method( "on$hook" );
}
return new \Message( $key, $params, \Language::factory( 'en' ) );
}
+ /**
+ * Test two AuthenticationResponses for equality. We don't want to use regular assertEquals
+ * because that recursively compares members, which leads to false negatives if e.g. Language
+ * caches are reset.
+ *
+ * @param AuthenticationResponse $response1
+ * @param AuthenticationResponse $response2
+ * @param string $msg
+ * @return bool
+ */
+ private function assertResponseEquals(
+ AuthenticationResponse $expected, AuthenticationResponse $actual, $msg = ''
+ ) {
+ foreach ( ( new \ReflectionClass( $expected ) )->getProperties() as $prop ) {
+ $name = $prop->getName();
+ $usedMsg = ltrim( "$msg ($name)" );
+ if ( $name === 'message' && $expected->message ) {
+ $this->assertSame( $expected->message->serialize(), $actual->message->serialize(),
+ $usedMsg );
+ } else {
+ $this->assertEquals( $expected->$name, $actual->$name, $usedMsg );
+ }
+ }
+ }
+
/**
* Initialize the AuthManagerConfig variable in $this->config
*
);
}
- public function testSetDefaultUserOptions() {
+ /**
+ * @dataProvider provideSetDefaultUserOptions
+ */
+ public function testSetDefaultUserOptions(
+ $contLang, $useContextLang, $expectedLang, $expectedVariant
+ ) {
$this->initializeManager();
+ $this->setContentLang( $contLang );
$context = \RequestContext::getMain();
$reset = new ScopedCallback( [ $context, 'setLanguage' ], [ $context->getLanguage() ] );
$context->setLanguage( 'de' );
- $this->setMwGlobals( 'wgContLang', \Language::factory( 'zh' ) );
-
- $user = \User::newFromName( self::usernameForCreation() );
- $user->addToDatabase();
- $oldToken = $user->getToken();
- $this->managerPriv->setDefaultUserOptions( $user, false );
- $user->saveSettings();
- $this->assertNotEquals( $oldToken, $user->getToken() );
- $this->assertSame( 'zh', $user->getOption( 'language' ) );
- $this->assertSame( 'zh', $user->getOption( 'variant' ) );
$user = \User::newFromName( self::usernameForCreation() );
$user->addToDatabase();
$oldToken = $user->getToken();
- $this->managerPriv->setDefaultUserOptions( $user, true );
+ $this->managerPriv->setDefaultUserOptions( $user, $useContextLang );
$user->saveSettings();
$this->assertNotEquals( $oldToken, $user->getToken() );
- $this->assertSame( 'de', $user->getOption( 'language' ) );
- $this->assertSame( 'zh', $user->getOption( 'variant' ) );
-
- $this->setMwGlobals( 'wgContLang', \Language::factory( 'fr' ) );
+ $this->assertSame( $expectedLang, $user->getOption( 'language' ) );
+ $this->assertSame( $expectedVariant, $user->getOption( 'variant' ) );
+ }
- $user = \User::newFromName( self::usernameForCreation() );
- $user->addToDatabase();
- $oldToken = $user->getToken();
- $this->managerPriv->setDefaultUserOptions( $user, true );
- $user->saveSettings();
- $this->assertNotEquals( $oldToken, $user->getToken() );
- $this->assertSame( 'de', $user->getOption( 'language' ) );
- $this->assertSame( null, $user->getOption( 'variant' ) );
+ public function provideSetDefaultUserOptions() {
+ return [
+ [ 'zh', false, 'zh', 'zh' ],
+ [ 'zh', true, 'de', 'zh' ],
+ [ 'fr', true, 'de', null ],
+ ];
}
public function testForcePrimaryAuthenticationProviders() {
$this->assertSame( 'http://localhost/', $req->returnToUrl );
$ret->message = $this->message( $ret->message );
- $this->assertEquals( $response, $ret, "Response $i, response" );
+ $this->assertResponseEquals( $response, $ret, "Response $i, response" );
if ( $success ) {
$this->assertSame( $id, $session->getUser()->getId(),
"Response $i, authn" );
}
public function testCheckAccountCreatePermissions() {
- global $wgGroupPermissions;
-
- $this->stashMwGlobals( [ 'wgGroupPermissions' ] );
-
$this->initializeManager( true );
- $wgGroupPermissions['*']['createaccount'] = true;
+ $this->setGroupPermissions( '*', 'createaccount', true );
$this->assertEquals(
\Status::newGood(),
$this->manager->checkAccountCreatePermissions( new \User )
);
$readOnlyMode->setReason( false );
- $wgGroupPermissions['*']['createaccount'] = false;
+ $this->setGroupPermissions( '*', 'createaccount', false );
$status = $this->manager->checkAccountCreatePermissions( new \User );
$this->assertFalse( $status->isOK() );
$this->assertTrue( $status->hasMessage( 'badaccess-groups' ) );
- $wgGroupPermissions['*']['createaccount'] = true;
+ $this->setGroupPermissions( '*', 'createaccount', true );
$user = \User::newFromName( 'UTBlockee' );
if ( $user->getID() == 0 ) {
$this->fail( 'Expected exception not thrown' );
} catch ( \UnexpectedValueException $ex ) {
$this->assertEquals(
- "User \"{$name}\" exists, but ID $id != " . ( $id + 1 ) . '!', $ex->getMessage()
+ "User \"{$name}\" exists, but ID $id !== " . ( $id + 1 ) . '!', $ex->getMessage()
);
}
$this->unhook( 'LocalUserCreated' );
"Response $i, login marker" );
}
$ret->message = $this->message( $ret->message );
- $this->assertEquals( $response, $ret, "Response $i, response" );
+ $this->assertResponseEquals( $response, $ret, "Response $i, response" );
if ( $success || $response->status === AuthenticationResponse::FAIL ) {
$this->assertNull(
$this->request->getSession()->getSecret( 'AuthManager::accountCreationState' ),
}
public function testAutoAccountCreation() {
- global $wgGroupPermissions, $wgHooks;
-
// PHPUnit seems to have a bug where it will call the ->with()
// callbacks for our hooks again after the test is run (WTF?), which
// breaks here because $username no longer matches $user by the end of
$workaroundPHPUnitBug = false;
$username = self::usernameForCreation();
+ $expectedSource = AuthManager::AUTOCREATE_SOURCE_SESSION;
$this->initializeManager();
- $this->stashMwGlobals( [ 'wgGroupPermissions' ] );
- $wgGroupPermissions['*']['createaccount'] = true;
- $wgGroupPermissions['*']['autocreateaccount'] = false;
+ $this->setGroupPermissions( '*', 'createaccount', true );
+ $this->setGroupPermissions( '*', 'autocreateaccount', false );
- \ObjectCache::$instances[__METHOD__] = new \HashBagOStuff();
+ $this->mergeMwGlobalArrayValue( 'wgObjectCaches',
+ [ __METHOD__ => [ 'class' => 'HashBagOStuff' ] ] );
$this->setMwGlobals( [ 'wgMainCacheType' => __METHOD__ ] );
// Set up lots of mocks...
}
$good = StatusValue::newGood();
+ $ok = StatusValue::newFatal( 'ok' );
$callback = $this->callback( function ( $user ) use ( &$username, &$workaroundPHPUnitBug ) {
return $workaroundPHPUnitBug || $user->getName() === $username;
} );
+ $callback2 = $this->callback(
+ function ( $source ) use ( &$expectedSource, &$workaroundPHPUnitBug ) {
+ return $workaroundPHPUnitBug || $source === $expectedSource;
+ }
+ );
- $mocks['pre']->expects( $this->exactly( 12 ) )->method( 'testUserForCreation' )
- ->with( $callback, $this->identicalTo( AuthManager::AUTOCREATE_SOURCE_SESSION ) )
+ $mocks['pre']->expects( $this->exactly( 13 ) )->method( 'testUserForCreation' )
+ ->with( $callback, $callback2 )
->will( $this->onConsecutiveCalls(
- StatusValue::newFatal( 'ok' ), StatusValue::newFatal( 'ok' ), // For testing permissions
+ $ok, $ok, $ok, // For testing permissions
StatusValue::newFatal( 'fail-in-pre' ), $good, $good,
$good, // backoff test
$good, // addToDatabase fails test
$mocks['primary']->expects( $this->any() )->method( 'testUserExists' )
->will( $this->returnValue( true ) );
$mocks['primary']->expects( $this->exactly( 9 ) )->method( 'testUserForCreation' )
- ->with( $callback, $this->identicalTo( AuthManager::AUTOCREATE_SOURCE_SESSION ) )
+ ->with( $callback, $callback2 )
->will( $this->onConsecutiveCalls(
StatusValue::newFatal( 'fail-in-primary' ), $good,
$good, // backoff test
$good, $good, $good
) );
$mocks['primary']->expects( $this->exactly( 3 ) )->method( 'autoCreatedAccount' )
- ->with( $callback, $this->identicalTo( AuthManager::AUTOCREATE_SOURCE_SESSION ) );
+ ->with( $callback, $callback2 );
$mocks['secondary']->expects( $this->exactly( 8 ) )->method( 'testUserForCreation' )
- ->with( $callback, $this->identicalTo( AuthManager::AUTOCREATE_SOURCE_SESSION ) )
+ ->with( $callback, $callback2 )
->will( $this->onConsecutiveCalls(
StatusValue::newFatal( 'fail-in-secondary' ),
$good, // backoff test
$good, $good, $good
) );
$mocks['secondary']->expects( $this->exactly( 3 ) )->method( 'autoCreatedAccount' )
- ->with( $callback, $this->identicalTo( AuthManager::AUTOCREATE_SOURCE_SESSION ) );
+ ->with( $callback, $callback2 );
$this->preauthMocks = [ $mocks['pre'] ];
$this->primaryauthMocks = [ $mocks['primary'] ];
$this->assertSame( 'noname', $session->get( 'AuthManager::AutoCreateBlacklist' ) );
// IP unable to create accounts
- $wgGroupPermissions['*']['createaccount'] = false;
- $wgGroupPermissions['*']['autocreateaccount'] = false;
+ $this->setGroupPermissions( '*', 'createaccount', false );
+ $this->setGroupPermissions( '*', 'autocreateaccount', false );
$session->clear();
$user = \User::newFromName( $username );
$this->hook( 'LocalUserCreated', $this->never() );
'authmanager-autocreate-noperm', $session->get( 'AuthManager::AutoCreateBlacklist' )
);
+ // maintenance scripts always work
+ $expectedSource = AuthManager::AUTOCREATE_SOURCE_MAINT;
+ $this->setGroupPermissions( '*', 'createaccount', false );
+ $this->setGroupPermissions( '*', 'autocreateaccount', false );
+ $session->clear();
+ $user = \User::newFromName( $username );
+ $this->hook( 'LocalUserCreated', $this->never() );
+ $ret = $this->manager->autoCreateUser( $user, AuthManager::AUTOCREATE_SOURCE_MAINT, true );
+ $this->unhook( 'LocalUserCreated' );
+ $this->assertEquals( \Status::newFatal( 'ok' ), $ret );
+
// Test that both permutations of permissions are allowed
// (this hits the two "ok" entries in $mocks['pre'])
- $wgGroupPermissions['*']['createaccount'] = false;
- $wgGroupPermissions['*']['autocreateaccount'] = true;
+ $expectedSource = AuthManager::AUTOCREATE_SOURCE_SESSION;
+ $this->setGroupPermissions( '*', 'createaccount', false );
+ $this->setGroupPermissions( '*', 'autocreateaccount', true );
$session->clear();
$user = \User::newFromName( $username );
$this->hook( 'LocalUserCreated', $this->never() );
$this->unhook( 'LocalUserCreated' );
$this->assertEquals( \Status::newFatal( 'ok' ), $ret );
- $wgGroupPermissions['*']['createaccount'] = true;
- $wgGroupPermissions['*']['autocreateaccount'] = false;
+ $this->setGroupPermissions( '*', 'createaccount', true );
+ $this->setGroupPermissions( '*', 'autocreateaccount', false );
$session->clear();
$user = \User::newFromName( $username );
$this->hook( 'LocalUserCreated', $this->never() );
$session->clear();
$username = self::usernameForCreation();
$user = \User::newFromName( $username );
- $this->hook( 'AuthPluginAutoCreate', $this->once() )
- ->with( $callback );
- $this->hideDeprecated( 'AuthPluginAutoCreate hook (used in ' .
- get_class( $wgHooks['AuthPluginAutoCreate'][0] ) . '::onAuthPluginAutoCreate)' );
$this->hook( 'LocalUserCreated', $this->once() )
->with( $callback, $this->equalTo( true ) );
$ret = $this->manager->autoCreateUser( $user, AuthManager::AUTOCREATE_SOURCE_SESSION, true );
$this->unhook( 'LocalUserCreated' );
- $this->unhook( 'AuthPluginAutoCreate' );
$this->assertEquals( \Status::newGood(), $ret );
$this->assertNotEquals( 0, $user->getId() );
$this->assertEquals( $username, $user->getName() );
$this->fail( 'Expected exception not thrown' );
} catch ( \UnexpectedValueException $ex ) {
$this->assertEquals(
- "User \"{$user->getName()}\" is valid, but ID $id != " . ( $id + 1 ) . '!',
+ "User \"{$user->getName()}\" is valid, but ID $id !== " . ( $id + 1 ) . '!',
$ex->getMessage()
);
}
$this->assertSame( 'http://localhost/', $req->returnToUrl );
$ret->message = $this->message( $ret->message );
- $this->assertEquals( $response, $ret, "Response $i, response" );
+ $this->assertResponseEquals( $response, $ret, "Response $i, response" );
if ( $response->status === AuthenticationResponse::PASS ||
$response->status === AuthenticationResponse::FAIL
) {