2010-12-14 16:26:35 +00:00
|
|
|
<?php
|
|
|
|
|
|
2017-04-19 19:37:35 +00:00
|
|
|
use Wikimedia\TestingAccessWrapper;
|
|
|
|
|
|
2010-12-14 16:26:35 +00:00
|
|
|
/**
|
2012-04-02 13:33:43 +00:00
|
|
|
* @group API
|
2010-12-14 16:26:35 +00:00
|
|
|
* @group Database
|
2013-01-18 19:02:28 +00:00
|
|
|
* @group medium
|
2013-10-23 15:36:40 +00:00
|
|
|
*
|
|
|
|
|
* @covers ApiLogin
|
2010-12-14 16:26:35 +00:00
|
|
|
*/
|
2013-10-23 15:36:40 +00:00
|
|
|
class ApiLoginTest extends ApiTestCase {
|
2010-12-14 16:26:35 +00:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Test result of attempted login with an empty username
|
|
|
|
|
*/
|
2013-10-23 22:51:31 +00:00
|
|
|
public function testApiLoginNoName() {
|
2016-02-17 09:09:32 +00:00
|
|
|
$session = [
|
|
|
|
|
'wsTokenSecrets' => [ 'login' => 'foobar' ],
|
|
|
|
|
];
|
|
|
|
|
$data = $this->doApiRequest( [ 'action' => 'login',
|
2016-05-06 15:06:46 +00:00
|
|
|
'lgname' => '', 'lgpassword' => self::$users['sysop']->getPassword(),
|
2016-02-01 20:44:03 +00:00
|
|
|
'lgtoken' => (string)( new MediaWiki\Session\Token( 'foobar', '' ) )
|
2016-02-17 09:09:32 +00:00
|
|
|
], $session );
|
2016-04-01 16:49:26 +00:00
|
|
|
$this->assertEquals( 'Failed', $data[0]['login']['result'] );
|
2010-12-14 16:26:35 +00:00
|
|
|
}
|
|
|
|
|
|
2013-10-23 22:51:31 +00:00
|
|
|
public function testApiLoginBadPass() {
|
2016-04-01 16:49:26 +00:00
|
|
|
global $wgServer;
|
2010-12-14 16:26:35 +00:00
|
|
|
|
2011-07-01 16:34:02 +00:00
|
|
|
$user = self::$users['sysop'];
|
2016-05-06 15:06:46 +00:00
|
|
|
$userName = $user->getUser()->getName();
|
2016-03-18 13:55:54 +00:00
|
|
|
$user->getUser()->logout();
|
2010-12-14 16:26:35 +00:00
|
|
|
|
|
|
|
|
if ( !isset( $wgServer ) ) {
|
|
|
|
|
$this->markTestIncomplete( 'This test needs $wgServer to be set in LocalSettings.php' );
|
|
|
|
|
}
|
2016-02-17 09:09:32 +00:00
|
|
|
$ret = $this->doApiRequest( [
|
2010-12-14 16:26:35 +00:00
|
|
|
"action" => "login",
|
2016-05-06 15:06:46 +00:00
|
|
|
"lgname" => $userName,
|
2010-12-14 16:26:35 +00:00
|
|
|
"lgpassword" => "bad",
|
2016-02-17 09:09:32 +00:00
|
|
|
] );
|
2010-12-14 16:26:35 +00:00
|
|
|
|
|
|
|
|
$result = $ret[0];
|
|
|
|
|
|
2010-12-28 15:13:42 +00:00
|
|
|
$this->assertNotInternalType( "bool", $result );
|
2010-12-14 16:26:35 +00:00
|
|
|
$a = $result["login"]["result"];
|
|
|
|
|
$this->assertEquals( "NeedToken", $a );
|
|
|
|
|
|
|
|
|
|
$token = $result["login"]["token"];
|
|
|
|
|
|
2013-02-14 11:56:23 +00:00
|
|
|
$ret = $this->doApiRequest(
|
2016-02-17 09:09:32 +00:00
|
|
|
[
|
2013-02-14 11:56:23 +00:00
|
|
|
"action" => "login",
|
|
|
|
|
"lgtoken" => $token,
|
2016-05-06 15:06:46 +00:00
|
|
|
"lgname" => $userName,
|
2013-02-14 11:56:23 +00:00
|
|
|
"lgpassword" => "badnowayinhell",
|
2016-02-17 09:09:32 +00:00
|
|
|
],
|
2013-02-14 11:56:23 +00:00
|
|
|
$ret[2]
|
2010-12-14 16:26:35 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
|
|
$result = $ret[0];
|
|
|
|
|
|
2010-12-28 15:13:42 +00:00
|
|
|
$this->assertNotInternalType( "bool", $result );
|
2010-12-14 16:26:35 +00:00
|
|
|
$a = $result["login"]["result"];
|
|
|
|
|
|
2016-04-01 16:49:26 +00:00
|
|
|
$this->assertEquals( 'Failed', $a );
|
2010-12-14 16:26:35 +00:00
|
|
|
}
|
|
|
|
|
|
2013-10-23 22:51:31 +00:00
|
|
|
public function testApiLoginGoodPass() {
|
2010-12-14 16:26:35 +00:00
|
|
|
global $wgServer;
|
|
|
|
|
|
|
|
|
|
if ( !isset( $wgServer ) ) {
|
|
|
|
|
$this->markTestIncomplete( 'This test needs $wgServer to be set in LocalSettings.php' );
|
|
|
|
|
}
|
|
|
|
|
|
2011-07-01 16:34:02 +00:00
|
|
|
$user = self::$users['sysop'];
|
2016-05-06 15:06:46 +00:00
|
|
|
$userName = $user->getUser()->getName();
|
|
|
|
|
$password = $user->getPassword();
|
2016-03-18 13:55:54 +00:00
|
|
|
$user->getUser()->logout();
|
2010-12-14 16:26:35 +00:00
|
|
|
|
2016-02-17 09:09:32 +00:00
|
|
|
$ret = $this->doApiRequest( [
|
2013-02-14 11:56:23 +00:00
|
|
|
"action" => "login",
|
2016-05-06 15:06:46 +00:00
|
|
|
"lgname" => $userName,
|
|
|
|
|
"lgpassword" => $password,
|
2016-02-17 09:09:32 +00:00
|
|
|
]
|
2010-12-14 16:26:35 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
|
|
$result = $ret[0];
|
2010-12-28 15:13:42 +00:00
|
|
|
$this->assertNotInternalType( "bool", $result );
|
|
|
|
|
$this->assertNotInternalType( "null", $result["login"] );
|
2010-12-14 16:26:35 +00:00
|
|
|
|
|
|
|
|
$a = $result["login"]["result"];
|
|
|
|
|
$this->assertEquals( "NeedToken", $a );
|
|
|
|
|
$token = $result["login"]["token"];
|
|
|
|
|
|
2013-02-14 11:56:23 +00:00
|
|
|
$ret = $this->doApiRequest(
|
2016-02-17 09:09:32 +00:00
|
|
|
[
|
2013-02-14 11:56:23 +00:00
|
|
|
"action" => "login",
|
|
|
|
|
"lgtoken" => $token,
|
2016-05-06 15:06:46 +00:00
|
|
|
"lgname" => $userName,
|
|
|
|
|
"lgpassword" => $password,
|
2016-02-17 09:09:32 +00:00
|
|
|
],
|
2013-02-14 11:56:23 +00:00
|
|
|
$ret[2]
|
2010-12-14 16:26:35 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
|
|
$result = $ret[0];
|
|
|
|
|
|
2010-12-28 15:13:42 +00:00
|
|
|
$this->assertNotInternalType( "bool", $result );
|
2010-12-14 16:26:35 +00:00
|
|
|
$a = $result["login"]["result"];
|
|
|
|
|
|
|
|
|
|
$this->assertEquals( "Success", $a );
|
|
|
|
|
}
|
|
|
|
|
|
2011-08-02 04:04:03 +00:00
|
|
|
/**
|
|
|
|
|
* @group Broken
|
|
|
|
|
*/
|
2013-10-23 15:36:40 +00:00
|
|
|
public function testApiLoginGotCookie() {
|
2014-04-24 15:05:10 +00:00
|
|
|
$this->markTestIncomplete( "The server can't do external HTTP requests, "
|
|
|
|
|
. "and the internal one won't give cookies" );
|
2010-12-14 16:26:35 +00:00
|
|
|
|
|
|
|
|
global $wgServer, $wgScriptPath;
|
|
|
|
|
|
|
|
|
|
if ( !isset( $wgServer ) ) {
|
|
|
|
|
$this->markTestIncomplete( 'This test needs $wgServer to be set in LocalSettings.php' );
|
|
|
|
|
}
|
2011-07-01 16:34:02 +00:00
|
|
|
$user = self::$users['sysop'];
|
2016-05-06 15:06:46 +00:00
|
|
|
$userName = $user->getUser()->getName();
|
|
|
|
|
$password = $user->getPassword();
|
2011-07-01 16:34:02 +00:00
|
|
|
|
2010-12-14 16:26:35 +00:00
|
|
|
$req = MWHttpRequest::factory( self::$apiUrl . "?action=login&format=xml",
|
2016-02-17 09:09:32 +00:00
|
|
|
[ "method" => "POST",
|
|
|
|
|
"postData" => [
|
2016-05-06 15:06:46 +00:00
|
|
|
"lgname" => $userName,
|
|
|
|
|
"lgpassword" => $password
|
2016-02-17 09:09:32 +00:00
|
|
|
]
|
|
|
|
|
],
|
2015-02-27 17:08:06 +00:00
|
|
|
__METHOD__
|
2013-02-14 11:56:23 +00:00
|
|
|
);
|
2010-12-14 16:26:35 +00:00
|
|
|
$req->execute();
|
|
|
|
|
|
|
|
|
|
libxml_use_internal_errors( true );
|
|
|
|
|
$sxe = simplexml_load_string( $req->getContent() );
|
2010-12-28 15:13:42 +00:00
|
|
|
$this->assertNotInternalType( "bool", $sxe );
|
2018-01-13 00:02:09 +00:00
|
|
|
$this->assertThat( $sxe, $this->isInstanceOf( SimpleXMLElement::class ) );
|
2010-12-28 15:13:42 +00:00
|
|
|
$this->assertNotInternalType( "null", $sxe->login[0] );
|
2010-12-14 16:26:35 +00:00
|
|
|
|
|
|
|
|
$a = $sxe->login[0]->attributes()->result[0];
|
|
|
|
|
$this->assertEquals( ' result="NeedToken"', $a->asXML() );
|
|
|
|
|
$token = (string)$sxe->login[0]->attributes()->token;
|
|
|
|
|
|
2016-02-17 09:09:32 +00:00
|
|
|
$req->setData( [
|
2010-12-14 16:26:35 +00:00
|
|
|
"lgtoken" => $token,
|
2016-05-06 15:06:46 +00:00
|
|
|
"lgname" => $userName,
|
|
|
|
|
"lgpassword" => $password ] );
|
2010-12-14 16:26:35 +00:00
|
|
|
$req->execute();
|
|
|
|
|
|
|
|
|
|
$cj = $req->getCookieJar();
|
|
|
|
|
$serverName = parse_url( $wgServer, PHP_URL_HOST );
|
|
|
|
|
$this->assertNotEquals( false, $serverName );
|
|
|
|
|
$serializedCookie = $cj->serializeToHttpRequest( $wgScriptPath, $serverName );
|
|
|
|
|
$this->assertNotEquals( '', $serializedCookie );
|
2016-03-19 00:08:06 +00:00
|
|
|
$this->assertRegExp(
|
2014-04-24 15:05:10 +00:00
|
|
|
'/_session=[^;]*; .*UserID=[0-9]*; .*UserName=' . $user->userName . '; .*Token=/',
|
|
|
|
|
$serializedCookie
|
|
|
|
|
);
|
2010-12-14 16:26:35 +00:00
|
|
|
}
|
2012-12-07 21:44:53 +00:00
|
|
|
|
2013-10-23 22:51:31 +00:00
|
|
|
public function testRunLogin() {
|
2016-05-06 15:06:46 +00:00
|
|
|
$user = self::$users['sysop'];
|
|
|
|
|
$userName = $user->getUser()->getName();
|
|
|
|
|
$password = $user->getPassword();
|
|
|
|
|
|
2016-02-17 09:09:32 +00:00
|
|
|
$data = $this->doApiRequest( [
|
2011-01-02 05:52:00 +00:00
|
|
|
'action' => 'login',
|
2016-05-06 15:06:46 +00:00
|
|
|
'lgname' => $userName,
|
|
|
|
|
'lgpassword' => $password ] );
|
2011-01-02 05:52:00 +00:00
|
|
|
|
|
|
|
|
$this->assertArrayHasKey( "login", $data[0] );
|
|
|
|
|
$this->assertArrayHasKey( "result", $data[0]['login'] );
|
|
|
|
|
$this->assertEquals( "NeedToken", $data[0]['login']['result'] );
|
|
|
|
|
$token = $data[0]['login']['token'];
|
|
|
|
|
|
2016-02-17 09:09:32 +00:00
|
|
|
$data = $this->doApiRequest( [
|
2011-01-02 05:52:00 +00:00
|
|
|
'action' => 'login',
|
|
|
|
|
"lgtoken" => $token,
|
2016-05-06 15:06:46 +00:00
|
|
|
"lgname" => $userName,
|
|
|
|
|
"lgpassword" => $password ], $data[2] );
|
2011-01-02 05:52:00 +00:00
|
|
|
|
|
|
|
|
$this->assertArrayHasKey( "login", $data[0] );
|
|
|
|
|
$this->assertArrayHasKey( "result", $data[0]['login'] );
|
|
|
|
|
$this->assertEquals( "Success", $data[0]['login']['result'] );
|
|
|
|
|
}
|
2012-12-07 21:44:53 +00:00
|
|
|
|
2016-02-01 20:44:03 +00:00
|
|
|
public function testBotPassword() {
|
|
|
|
|
global $wgServer, $wgSessionProviders;
|
|
|
|
|
|
|
|
|
|
if ( !isset( $wgServer ) ) {
|
|
|
|
|
$this->markTestIncomplete( 'This test needs $wgServer to be set in LocalSettings.php' );
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-17 09:09:32 +00:00
|
|
|
$this->setMwGlobals( [
|
|
|
|
|
'wgSessionProviders' => array_merge( $wgSessionProviders, [
|
|
|
|
|
[
|
2016-03-28 18:53:04 +00:00
|
|
|
'class' => MediaWiki\Session\BotPasswordSessionProvider::class,
|
2016-02-17 09:09:32 +00:00
|
|
|
'args' => [ [ 'priority' => 40 ] ],
|
|
|
|
|
]
|
|
|
|
|
] ),
|
2016-02-01 20:44:03 +00:00
|
|
|
'wgEnableBotPasswords' => true,
|
|
|
|
|
'wgBotPasswordsDatabase' => false,
|
|
|
|
|
'wgCentralIdLookupProvider' => 'local',
|
2016-02-17 09:09:32 +00:00
|
|
|
'wgGrantPermissions' => [
|
|
|
|
|
'test' => [ 'read' => true ],
|
|
|
|
|
],
|
|
|
|
|
] );
|
2016-02-01 20:44:03 +00:00
|
|
|
|
|
|
|
|
// Make sure our session provider is present
|
|
|
|
|
$manager = TestingAccessWrapper::newFromObject( MediaWiki\Session\SessionManager::singleton() );
|
2016-03-28 18:53:04 +00:00
|
|
|
if ( !isset( $manager->sessionProviders[MediaWiki\Session\BotPasswordSessionProvider::class] ) ) {
|
2016-02-01 20:44:03 +00:00
|
|
|
$tmp = $manager->sessionProviders;
|
|
|
|
|
$manager->sessionProviders = null;
|
|
|
|
|
$manager->sessionProviders = $tmp + $manager->getProviders();
|
|
|
|
|
}
|
|
|
|
|
$this->assertNotNull(
|
|
|
|
|
MediaWiki\Session\SessionManager::singleton()->getProvider(
|
2016-03-28 18:53:04 +00:00
|
|
|
MediaWiki\Session\BotPasswordSessionProvider::class
|
2016-02-01 20:44:03 +00:00
|
|
|
),
|
|
|
|
|
'sanity check'
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
$user = self::$users['sysop'];
|
|
|
|
|
$centralId = CentralIdLookup::factory()->centralIdFromLocalUser( $user->getUser() );
|
|
|
|
|
$this->assertNotEquals( 0, $centralId, 'sanity check' );
|
|
|
|
|
|
2016-09-13 23:25:49 +00:00
|
|
|
$password = 'ngfhmjm64hv0854493hsj5nncjud2clk';
|
2016-02-01 20:44:03 +00:00
|
|
|
$passwordFactory = new PasswordFactory();
|
|
|
|
|
$passwordFactory->init( RequestContext::getMain()->getConfig() );
|
|
|
|
|
// A is unsalted MD5 (thus fast) ... we don't care about security here, this is test only
|
2016-09-13 23:25:49 +00:00
|
|
|
$passwordHash = $passwordFactory->newFromPlaintext( $password );
|
2016-02-01 20:44:03 +00:00
|
|
|
|
|
|
|
|
$dbw = wfGetDB( DB_MASTER );
|
|
|
|
|
$dbw->insert(
|
|
|
|
|
'bot_passwords',
|
2016-02-17 09:09:32 +00:00
|
|
|
[
|
2016-02-01 20:44:03 +00:00
|
|
|
'bp_user' => $centralId,
|
|
|
|
|
'bp_app_id' => 'foo',
|
2016-05-12 10:40:41 +00:00
|
|
|
'bp_password' => $passwordHash->toString(),
|
2016-02-01 20:44:03 +00:00
|
|
|
'bp_token' => '',
|
|
|
|
|
'bp_restrictions' => MWRestrictions::newDefault()->toJson(),
|
|
|
|
|
'bp_grants' => '["test"]',
|
2016-02-17 09:09:32 +00:00
|
|
|
],
|
2016-02-01 20:44:03 +00:00
|
|
|
__METHOD__
|
|
|
|
|
);
|
|
|
|
|
|
2016-05-06 15:06:46 +00:00
|
|
|
$lgName = $user->getUser()->getName() . BotPassword::getSeparator() . 'foo';
|
2016-02-01 20:44:03 +00:00
|
|
|
|
2016-02-17 09:09:32 +00:00
|
|
|
$ret = $this->doApiRequest( [
|
2016-02-01 20:44:03 +00:00
|
|
|
'action' => 'login',
|
|
|
|
|
'lgname' => $lgName,
|
2016-09-13 23:25:49 +00:00
|
|
|
'lgpassword' => $password,
|
2016-02-17 09:09:32 +00:00
|
|
|
] );
|
2016-02-01 20:44:03 +00:00
|
|
|
|
|
|
|
|
$result = $ret[0];
|
|
|
|
|
$this->assertNotInternalType( 'bool', $result );
|
|
|
|
|
$this->assertNotInternalType( 'null', $result['login'] );
|
|
|
|
|
|
|
|
|
|
$a = $result['login']['result'];
|
|
|
|
|
$this->assertEquals( 'NeedToken', $a );
|
|
|
|
|
$token = $result['login']['token'];
|
|
|
|
|
|
2016-02-17 09:09:32 +00:00
|
|
|
$ret = $this->doApiRequest( [
|
2016-02-01 20:44:03 +00:00
|
|
|
'action' => 'login',
|
|
|
|
|
'lgtoken' => $token,
|
|
|
|
|
'lgname' => $lgName,
|
2016-09-13 23:25:49 +00:00
|
|
|
'lgpassword' => $password,
|
2016-02-17 09:09:32 +00:00
|
|
|
], $ret[2] );
|
2016-02-01 20:44:03 +00:00
|
|
|
|
|
|
|
|
$result = $ret[0];
|
|
|
|
|
$this->assertNotInternalType( 'bool', $result );
|
|
|
|
|
$a = $result['login']['result'];
|
|
|
|
|
|
|
|
|
|
$this->assertEquals( 'Success', $a );
|
|
|
|
|
}
|
|
|
|
|
|
2018-03-20 15:43:01 +00:00
|
|
|
public function testLoginWithNoSameOriginSecurity() {
|
|
|
|
|
$this->setTemporaryHook( 'RequestHasSameOriginSecurity',
|
|
|
|
|
function () {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
$result = $this->doApiRequest( [
|
|
|
|
|
'action' => 'login',
|
|
|
|
|
] )[0]['login'];
|
|
|
|
|
|
|
|
|
|
$this->assertSame( [
|
|
|
|
|
'result' => 'Aborted',
|
|
|
|
|
'reason' => 'Cannot log in when the same-origin policy is not applied.',
|
|
|
|
|
], $result );
|
|
|
|
|
}
|
2010-12-14 16:26:35 +00:00
|
|
|
}
|