Merge "RCFilters: Override frameless button opacity to show real highlight color"
[lhc/web/wiklou.git] / tests / phpunit / includes / api / ApiLoginTest.php
1 <?php
2
3 use MediaWiki\MediaWikiServices;
4 use MediaWiki\Session\BotPasswordSessionProvider;
5 use MediaWiki\Session\SessionManager;
6 use Wikimedia\TestingAccessWrapper;
7
8 /**
9 * @group API
10 * @group Database
11 * @group medium
12 *
13 * @covers ApiLogin
14 */
15 class ApiLoginTest extends ApiTestCase {
16
17 /**
18 * Test result of attempted login with an empty username
19 */
20 public function testNoName() {
21 $session = [
22 'wsTokenSecrets' => [ 'login' => 'foobar' ],
23 ];
24 $ret = $this->doApiRequest( [
25 'action' => 'login',
26 'lgname' => '',
27 'lgpassword' => self::$users['sysop']->getPassword(),
28 'lgtoken' => (string)( new MediaWiki\Session\Token( 'foobar', '' ) ),
29 ], $session );
30 $this->assertSame( 'Failed', $ret[0]['login']['result'] );
31 }
32
33 private function doUserLogin( $name, $password ) {
34 $ret = $this->doApiRequest( [
35 'action' => 'login',
36 'lgname' => $name,
37 ] );
38
39 $this->assertSame( 'NeedToken', $ret[0]['login']['result'] );
40
41 return $this->doApiRequest( [
42 'action' => 'login',
43 'lgtoken' => $ret[0]['login']['token'],
44 'lgname' => $name,
45 'lgpassword' => $password,
46 ], $ret[2] );
47 }
48
49 public function testBadPass() {
50 $user = self::$users['sysop'];
51 $userName = $user->getUser()->getName();
52 $user->getUser()->logout();
53
54 $ret = $this->doUserLogin( $userName, 'bad' );
55
56 $this->assertSame( 'Failed', $ret[0]['login']['result'] );
57 }
58
59 public function testGoodPass() {
60 $user = self::$users['sysop'];
61 $userName = $user->getUser()->getName();
62 $password = $user->getPassword();
63 $user->getUser()->logout();
64
65 $ret = $this->doUserLogin( $userName, $password );
66
67 $this->assertSame( 'Success', $ret[0]['login']['result'] );
68 }
69
70 /**
71 * @group Broken
72 */
73 public function testGotCookie() {
74 $this->markTestIncomplete( "The server can't do external HTTP requests, "
75 . "and the internal one won't give cookies" );
76
77 global $wgServer, $wgScriptPath;
78
79 $user = self::$users['sysop'];
80 $userName = $user->getUser()->getName();
81 $password = $user->getPassword();
82
83 $req = MWHttpRequest::factory(
84 self::$apiUrl . '?action=login&format=json',
85 [
86 'method' => 'POST',
87 'postData' => [
88 'lgname' => $userName,
89 'lgpassword' => $password,
90 ],
91 ],
92 __METHOD__
93 );
94 $req->execute();
95
96 $content = json_decode( $req->getContent() );
97
98 $this->assertSame( 'NeedToken', $content->login->result );
99
100 $req->setData( [
101 'lgtoken' => $content->login->token,
102 'lgname' => $userName,
103 'lgpassword' => $password,
104 ] );
105 $req->execute();
106
107 $cj = $req->getCookieJar();
108 $serverName = parse_url( $wgServer, PHP_URL_HOST );
109 $this->assertNotEquals( false, $serverName );
110 $serializedCookie = $cj->serializeToHttpRequest( $wgScriptPath, $serverName );
111 $this->assertRegExp(
112 '/_session=[^;]*; .*UserID=[0-9]*; .*UserName=' . $userName . '; .*Token=/',
113 $serializedCookie
114 );
115 }
116
117 public function testBotPassword() {
118 global $wgSessionProviders;
119
120 $this->setMwGlobals( [
121 // We can't use mergeMwGlobalArrayValue because it will overwrite the existing entry
122 // with index 0
123 'wgSessionProviders' => array_merge( $wgSessionProviders, [
124 [
125 'class' => BotPasswordSessionProvider::class,
126 'args' => [ [ 'priority' => 40 ] ],
127 ],
128 ] ),
129 'wgEnableBotPasswords' => true,
130 'wgBotPasswordsDatabase' => false,
131 'wgCentralIdLookupProvider' => 'local',
132 'wgGrantPermissions' => [
133 'test' => [ 'read' => true ],
134 ],
135 ] );
136
137 // Make sure our session provider is present
138 $manager = TestingAccessWrapper::newFromObject( SessionManager::singleton() );
139 if ( !isset( $manager->sessionProviders[BotPasswordSessionProvider::class] ) ) {
140 $tmp = $manager->sessionProviders;
141 $manager->sessionProviders = null;
142 $manager->sessionProviders = $tmp + $manager->getProviders();
143 }
144 $this->assertNotNull(
145 SessionManager::singleton()->getProvider( BotPasswordSessionProvider::class ),
146 'sanity check'
147 );
148
149 $user = self::$users['sysop'];
150 $centralId = CentralIdLookup::factory()->centralIdFromLocalUser( $user->getUser() );
151 $this->assertNotSame( 0, $centralId, 'sanity check' );
152
153 $password = 'ngfhmjm64hv0854493hsj5nncjud2clk';
154 $passwordFactory = MediaWikiServices::getInstance()->getPasswordFactory();
155 // A is unsalted MD5 (thus fast) ... we don't care about security here, this is test only
156 $passwordHash = $passwordFactory->newFromPlaintext( $password );
157
158 $dbw = wfGetDB( DB_MASTER );
159 $dbw->insert(
160 'bot_passwords',
161 [
162 'bp_user' => $centralId,
163 'bp_app_id' => 'foo',
164 'bp_password' => $passwordHash->toString(),
165 'bp_token' => '',
166 'bp_restrictions' => MWRestrictions::newDefault()->toJson(),
167 'bp_grants' => '["test"]',
168 ],
169 __METHOD__
170 );
171
172 $lgName = $user->getUser()->getName() . BotPassword::getSeparator() . 'foo';
173
174 $ret = $this->doUserLogin( $lgName, $password );
175
176 $this->assertSame( 'Success', $ret[0]['login']['result'] );
177 }
178
179 public function testLoginWithNoSameOriginSecurity() {
180 $this->setTemporaryHook( 'RequestHasSameOriginSecurity',
181 function () {
182 return false;
183 }
184 );
185
186 $ret = $this->doApiRequest( [
187 'action' => 'login',
188 ] )[0]['login'];
189
190 $this->assertSame( [
191 'result' => 'Aborted',
192 'reason' => 'Cannot log in when the same-origin policy is not applied.',
193 ], $ret );
194 }
195 }