Merge "Add redirect=no only to redirects on redirect pages"
[lhc/web/wiklou.git] / tests / phpunit / includes / session / BotPasswordSessionProviderTest.php
1 <?php
2
3 namespace MediaWiki\Session;
4
5 use Psr\Log\LogLevel;
6 use MediaWikiTestCase;
7 use User;
8
9 /**
10 * @group Session
11 * @group Database
12 * @covers MediaWiki\Session\BotPasswordSessionProvider
13 */
14 class BotPasswordSessionProviderTest extends MediaWikiTestCase {
15
16 private $config;
17
18 private function getProvider( $name = null, $prefix = null ) {
19 global $wgSessionProviders;
20
21 $params = [
22 'priority' => 40,
23 'sessionCookieName' => $name,
24 'sessionCookieOptions' => [],
25 ];
26 if ( $prefix !== null ) {
27 $params['sessionCookieOptions']['prefix'] = $prefix;
28 }
29
30 if ( !$this->config ) {
31 $this->config = new \HashConfig( [
32 'CookiePrefix' => 'wgCookiePrefix',
33 'EnableBotPasswords' => true,
34 'BotPasswordsDatabase' => false,
35 'SessionProviders' => $wgSessionProviders + [
36 'MediaWiki\\Session\\BotPasswordSessionProvider' => [
37 'class' => 'MediaWiki\\Session\\BotPasswordSessionProvider',
38 'args' => [ $params ],
39 ]
40 ],
41 ] );
42 }
43 $manager = new SessionManager( [
44 'config' => new \MultiConfig( [ $this->config, \RequestContext::getMain()->getConfig() ] ),
45 'logger' => new \Psr\Log\NullLogger,
46 'store' => new TestBagOStuff,
47 ] );
48
49 return $manager->getProvider( 'MediaWiki\\Session\\BotPasswordSessionProvider' );
50 }
51
52 protected function setUp() {
53 parent::setUp();
54
55 $this->setMwGlobals( [
56 'wgEnableBotPasswords' => true,
57 'wgBotPasswordsDatabase' => false,
58 'wgCentralIdLookupProvider' => 'local',
59 'wgGrantPermissions' => [
60 'test' => [ 'read' => true ],
61 ],
62 ] );
63 }
64
65 public function addDBDataOnce() {
66 $passwordFactory = new \PasswordFactory();
67 $passwordFactory->init( \RequestContext::getMain()->getConfig() );
68 // A is unsalted MD5 (thus fast) ... we don't care about security here, this is test only
69 $passwordFactory->setDefaultType( 'A' );
70 $pwhash = $passwordFactory->newFromPlaintext( 'foobaz' );
71
72 $userId = \CentralIdLookup::factory( 'local' )->centralIdFromName( 'UTSysop' );
73
74 $dbw = wfGetDB( DB_MASTER );
75 $dbw->delete(
76 'bot_passwords',
77 [ 'bp_user' => $userId, 'bp_app_id' => 'BotPasswordSessionProvider' ],
78 __METHOD__
79 );
80 $dbw->insert(
81 'bot_passwords',
82 [
83 'bp_user' => $userId,
84 'bp_app_id' => 'BotPasswordSessionProvider',
85 'bp_password' => $pwhash->toString(),
86 'bp_token' => 'token!',
87 'bp_restrictions' => '{"IPAddresses":["127.0.0.0/8"]}',
88 'bp_grants' => '["test"]',
89 ],
90 __METHOD__
91 );
92 }
93
94 public function testConstructor() {
95 try {
96 $provider = new BotPasswordSessionProvider();
97 $this->fail( 'Expected exception not thrown' );
98 } catch ( \InvalidArgumentException $ex ) {
99 $this->assertSame(
100 'MediaWiki\\Session\\BotPasswordSessionProvider::__construct: priority must be specified',
101 $ex->getMessage()
102 );
103 }
104
105 try {
106 $provider = new BotPasswordSessionProvider( [
107 'priority' => SessionInfo::MIN_PRIORITY - 1
108 ] );
109 $this->fail( 'Expected exception not thrown' );
110 } catch ( \InvalidArgumentException $ex ) {
111 $this->assertSame(
112 'MediaWiki\\Session\\BotPasswordSessionProvider::__construct: Invalid priority',
113 $ex->getMessage()
114 );
115 }
116
117 try {
118 $provider = new BotPasswordSessionProvider( [
119 'priority' => SessionInfo::MAX_PRIORITY + 1
120 ] );
121 $this->fail( 'Expected exception not thrown' );
122 } catch ( \InvalidArgumentException $ex ) {
123 $this->assertSame(
124 'MediaWiki\\Session\\BotPasswordSessionProvider::__construct: Invalid priority',
125 $ex->getMessage()
126 );
127 }
128
129 $provider = new BotPasswordSessionProvider( [
130 'priority' => 40
131 ] );
132 $priv = \TestingAccessWrapper::newFromObject( $provider );
133 $this->assertSame( 40, $priv->priority );
134 $this->assertSame( '_BPsession', $priv->sessionCookieName );
135 $this->assertSame( [], $priv->sessionCookieOptions );
136
137 $provider = new BotPasswordSessionProvider( [
138 'priority' => 40,
139 'sessionCookieName' => null,
140 ] );
141 $priv = \TestingAccessWrapper::newFromObject( $provider );
142 $this->assertSame( '_BPsession', $priv->sessionCookieName );
143
144 $provider = new BotPasswordSessionProvider( [
145 'priority' => 40,
146 'sessionCookieName' => 'Foo',
147 'sessionCookieOptions' => [ 'Bar' ],
148 ] );
149 $priv = \TestingAccessWrapper::newFromObject( $provider );
150 $this->assertSame( 'Foo', $priv->sessionCookieName );
151 $this->assertSame( [ 'Bar' ], $priv->sessionCookieOptions );
152 }
153
154 public function testBasics() {
155 $provider = $this->getProvider();
156
157 $this->assertTrue( $provider->persistsSessionId() );
158 $this->assertFalse( $provider->canChangeUser() );
159
160 $this->assertNull( $provider->newSessionInfo() );
161 $this->assertNull( $provider->newSessionInfo( 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' ) );
162 }
163
164 public function testProvideSessionInfo() {
165 $provider = $this->getProvider();
166 $request = new \FauxRequest;
167 $request->setCookie( '_BPsession', 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', 'wgCookiePrefix' );
168
169 if ( !defined( 'MW_API' ) ) {
170 $this->assertNull( $provider->provideSessionInfo( $request ) );
171 define( 'MW_API', 1 );
172 }
173
174 $info = $provider->provideSessionInfo( $request );
175 $this->assertInstanceOf( 'MediaWiki\\Session\\SessionInfo', $info );
176 $this->assertSame( 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', $info->getId() );
177
178 $this->config->set( 'EnableBotPasswords', false );
179 $this->assertNull( $provider->provideSessionInfo( $request ) );
180 $this->config->set( 'EnableBotPasswords', true );
181
182 $this->assertNull( $provider->provideSessionInfo( new \FauxRequest ) );
183 }
184
185 public function testNewSessionInfoForRequest() {
186 $provider = $this->getProvider();
187 $user = \User::newFromName( 'UTSysop' );
188 $request = $this->getMock( 'FauxRequest', [ 'getIP' ] );
189 $request->expects( $this->any() )->method( 'getIP' )
190 ->will( $this->returnValue( '127.0.0.1' ) );
191 $bp = \BotPassword::newFromUser( $user, 'BotPasswordSessionProvider' );
192
193 $session = $provider->newSessionForRequest( $user, $bp, $request );
194 $this->assertInstanceOf( 'MediaWiki\\Session\\Session', $session );
195
196 $this->assertEquals( $session->getId(), $request->getSession()->getId() );
197 $this->assertEquals( $user->getName(), $session->getUser()->getName() );
198
199 $this->assertEquals( [
200 'centralId' => $bp->getUserCentralId(),
201 'appId' => $bp->getAppId(),
202 'token' => $bp->getToken(),
203 'rights' => [ 'read' ],
204 ], $session->getProviderMetadata() );
205
206 $this->assertEquals( [ 'read' ], $session->getAllowedUserRights() );
207 }
208
209 public function testCheckSessionInfo() {
210 $logger = new \TestLogger( true );
211 $provider = $this->getProvider();
212 $provider->setLogger( $logger );
213
214 $user = \User::newFromName( 'UTSysop' );
215 $request = $this->getMock( 'FauxRequest', [ 'getIP' ] );
216 $request->expects( $this->any() )->method( 'getIP' )
217 ->will( $this->returnValue( '127.0.0.1' ) );
218 $bp = \BotPassword::newFromUser( $user, 'BotPasswordSessionProvider' );
219
220 $data = [
221 'provider' => $provider,
222 'id' => 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
223 'userInfo' => UserInfo::newFromUser( $user, true ),
224 'persisted' => false,
225 'metadata' => [
226 'centralId' => $bp->getUserCentralId(),
227 'appId' => $bp->getAppId(),
228 'token' => $bp->getToken(),
229 ],
230 ];
231 $dataMD = $data['metadata'];
232
233 foreach ( array_keys( $data['metadata'] ) as $key ) {
234 $data['metadata'] = $dataMD;
235 unset( $data['metadata'][$key] );
236 $info = new SessionInfo( SessionInfo::MIN_PRIORITY, $data );
237 $metadata = $info->getProviderMetadata();
238
239 $this->assertFalse( $provider->refreshSessionInfo( $info, $request, $metadata ) );
240 $this->assertSame( [
241 [ LogLevel::INFO, 'Session "{session}": Missing metadata: {missing}' ]
242 ], $logger->getBuffer() );
243 $logger->clearBuffer();
244 }
245
246 $data['metadata'] = $dataMD;
247 $data['metadata']['appId'] = 'Foobar';
248 $info = new SessionInfo( SessionInfo::MIN_PRIORITY, $data );
249 $metadata = $info->getProviderMetadata();
250 $this->assertFalse( $provider->refreshSessionInfo( $info, $request, $metadata ) );
251 $this->assertSame( [
252 [ LogLevel::INFO, 'Session "{session}": No BotPassword for {centralId} {appId}' ],
253 ], $logger->getBuffer() );
254 $logger->clearBuffer();
255
256 $data['metadata'] = $dataMD;
257 $data['metadata']['token'] = 'Foobar';
258 $info = new SessionInfo( SessionInfo::MIN_PRIORITY, $data );
259 $metadata = $info->getProviderMetadata();
260 $this->assertFalse( $provider->refreshSessionInfo( $info, $request, $metadata ) );
261 $this->assertSame( [
262 [ LogLevel::INFO, 'Session "{session}": BotPassword token check failed' ],
263 ], $logger->getBuffer() );
264 $logger->clearBuffer();
265
266 $request2 = $this->getMock( 'FauxRequest', [ 'getIP' ] );
267 $request2->expects( $this->any() )->method( 'getIP' )
268 ->will( $this->returnValue( '10.0.0.1' ) );
269 $data['metadata'] = $dataMD;
270 $info = new SessionInfo( SessionInfo::MIN_PRIORITY, $data );
271 $metadata = $info->getProviderMetadata();
272 $this->assertFalse( $provider->refreshSessionInfo( $info, $request2, $metadata ) );
273 $this->assertSame( [
274 [ LogLevel::INFO, 'Session "{session}": Restrictions check failed' ],
275 ], $logger->getBuffer() );
276 $logger->clearBuffer();
277
278 $info = new SessionInfo( SessionInfo::MIN_PRIORITY, $data );
279 $metadata = $info->getProviderMetadata();
280 $this->assertTrue( $provider->refreshSessionInfo( $info, $request, $metadata ) );
281 $this->assertSame( [], $logger->getBuffer() );
282 $this->assertEquals( $dataMD + [ 'rights' => [ 'read' ] ], $metadata );
283 }
284
285 public function testGetAllowedUserRights() {
286 $logger = new \TestLogger( true );
287 $provider = $this->getProvider();
288 $provider->setLogger( $logger );
289
290 $backend = TestUtils::getDummySessionBackend();
291 $backendPriv = \TestingAccessWrapper::newFromObject( $backend );
292
293 try {
294 $provider->getAllowedUserRights( $backend );
295 $this->fail( 'Expected exception not thrown' );
296 } catch ( \InvalidArgumentException $ex ) {
297 $this->assertSame( 'Backend\'s provider isn\'t $this', $ex->getMessage() );
298 }
299
300 $backendPriv->provider = $provider;
301 $backendPriv->providerMetadata = [ 'rights' => [ 'foo', 'bar', 'baz' ] ];
302 $this->assertSame( [ 'foo', 'bar', 'baz' ], $provider->getAllowedUserRights( $backend ) );
303 $this->assertSame( [], $logger->getBuffer() );
304
305 $backendPriv->providerMetadata = [ 'foo' => 'bar' ];
306 $this->assertSame( [], $provider->getAllowedUserRights( $backend ) );
307 $this->assertSame( [
308 [
309 LogLevel::DEBUG,
310 'MediaWiki\\Session\\BotPasswordSessionProvider::getAllowedUserRights: ' .
311 'No provider metadata, returning no rights allowed'
312 ]
313 ], $logger->getBuffer() );
314 $logger->clearBuffer();
315
316 $backendPriv->providerMetadata = [ 'rights' => 'bar' ];
317 $this->assertSame( [], $provider->getAllowedUserRights( $backend ) );
318 $this->assertSame( [
319 [
320 LogLevel::DEBUG,
321 'MediaWiki\\Session\\BotPasswordSessionProvider::getAllowedUserRights: ' .
322 'No provider metadata, returning no rights allowed'
323 ]
324 ], $logger->getBuffer() );
325 $logger->clearBuffer();
326
327 $backendPriv->providerMetadata = null;
328 $this->assertSame( [], $provider->getAllowedUserRights( $backend ) );
329 $this->assertSame( [
330 [
331 LogLevel::DEBUG,
332 'MediaWiki\\Session\\BotPasswordSessionProvider::getAllowedUserRights: ' .
333 'No provider metadata, returning no rights allowed'
334 ]
335 ], $logger->getBuffer() );
336 $logger->clearBuffer();
337 }
338 }