23 'wgEnableBotPasswords' =>
true,
24 'wgBotPasswordsDatabase' =>
false,
25 'wgCentralIdLookupProvider' =>
'BotPasswordTest OkMock',
26 'wgGrantPermissions' => [
27 'test' => [
'read' =>
true ],
29 'wgUserrightsInterwikiDelimiter' =>
'@',
33 $this->testUserName = $this->testUser->getUser()->getName();
35 $mock1 = $this->getMockForAbstractClass( CentralIdLookup::class );
36 $mock1->expects( $this->
any() )->method(
'isAttached' )
37 ->will( $this->returnValue(
true ) );
38 $mock1->expects( $this->
any() )->method(
'lookupUserNames' )
39 ->will( $this->returnValue( [ $this->testUserName => 42,
'UTDummy' => 43,
'UTInvalid' => 0 ] ) );
40 $mock1->expects( $this->never() )->method(
'lookupCentralIds' );
42 $mock2 = $this->getMockForAbstractClass( CentralIdLookup::class );
43 $mock2->expects( $this->
any() )->method(
'isAttached' )
44 ->will( $this->returnValue(
false ) );
45 $mock2->expects( $this->
any() )->method(
'lookupUserNames' )
46 ->will( $this->returnArgument( 0 ) );
47 $mock2->expects( $this->never() )->method(
'lookupCentralIds' );
50 'BotPasswordTest OkMock' => [
'factory' =>
function () use ( $mock1 ) {
53 'BotPasswordTest FailMock' => [
'factory' =>
function () use ( $mock2 ) {
58 CentralIdLookup::resetCache();
97 $user = $this->testUser->getUser();
98 $bp = BotPassword::newFromUser( $user,
'BotPassword' );
99 $this->assertInstanceOf( BotPassword::class, $bp );
100 $this->assertTrue( $bp->isSaved() );
101 $this->assertSame( 42, $bp->getUserCentralId() );
102 $this->assertSame(
'BotPassword', $bp->getAppId() );
103 $this->assertSame(
'token!', trim( $bp->getToken(),
" \0" ) );
104 $this->assertEquals(
'{"IPAddresses":["127.0.0.0/8"]}', $bp->getRestrictions()->toJson() );
105 $this->assertSame( [
'test' ], $bp->getGrants() );
107 $this->assertNull( BotPassword::newFromUser( $user,
'DoesNotExist' ) );
110 'wgCentralIdLookupProvider' =>
'BotPasswordTest FailMock'
112 $this->assertNull( BotPassword::newFromUser( $user,
'BotPassword' ) );
114 $this->assertSame(
'@', BotPassword::getSeparator() );
116 'wgUserrightsInterwikiDelimiter' =>
'#',
118 $this->assertSame(
'#', BotPassword::getSeparator() );
122 $user = $this->testUser->getUser();
123 $bp = BotPassword::newUnsaved( [
125 'appId' =>
'DoesNotExist'
127 $this->assertInstanceOf( BotPassword::class, $bp );
128 $this->assertFalse( $bp->isSaved() );
129 $this->assertSame( 42, $bp->getUserCentralId() );
130 $this->assertSame(
'DoesNotExist', $bp->getAppId() );
132 $this->assertSame( [], $bp->getGrants() );
134 $bp = BotPassword::newUnsaved( [
135 'username' =>
'UTDummy',
136 'appId' =>
'DoesNotExist2',
138 'grants' => [
'test' ],
140 $this->assertInstanceOf( BotPassword::class, $bp );
141 $this->assertFalse( $bp->isSaved() );
142 $this->assertSame( 43, $bp->getUserCentralId() );
143 $this->assertSame(
'DoesNotExist2', $bp->getAppId() );
144 $this->assertEquals(
'{"IPAddresses":["127.0.0.0/8"]}', $bp->getRestrictions()->toJson() );
145 $this->assertSame( [
'test' ], $bp->getGrants() );
147 $user = $this->testUser->getUser();
148 $bp = BotPassword::newUnsaved( [
150 'appId' =>
'DoesNotExist'
152 $this->assertInstanceOf( BotPassword::class, $bp );
153 $this->assertFalse( $bp->isSaved() );
154 $this->assertSame( 45, $bp->getUserCentralId() );
155 $this->assertSame(
'DoesNotExist', $bp->getAppId() );
157 $user = $this->testUser->getUser();
158 $bp = BotPassword::newUnsaved( [
160 'appId' =>
'BotPassword'
162 $this->assertInstanceOf( BotPassword::class, $bp );
163 $this->assertFalse( $bp->isSaved() );
165 $this->assertNull( BotPassword::newUnsaved( [
169 $this->assertNull( BotPassword::newUnsaved( [
171 'appId' => str_repeat(
'X', BotPassword::APPID_MAXLENGTH + 1 ),
173 $this->assertNull( BotPassword::newUnsaved( [
174 'user' => $this->testUserName,
177 $this->assertNull( BotPassword::newUnsaved( [
178 'username' =>
'UTInvalid',
181 $this->assertNull( BotPassword::newUnsaved( [
187 $bp = TestingAccessWrapper::newFromObject( BotPassword::newFromCentralId( 42,
'BotPassword' ) );
189 $password = $bp->getPassword();
190 $this->assertInstanceOf( Password::class, $password );
191 $this->assertTrue( $password->equals(
'foobaz' ) );
194 $password = $bp->getPassword();
195 $this->assertInstanceOf( InvalidPassword::class, $password );
197 $bp = TestingAccessWrapper::newFromObject( BotPassword::newFromCentralId( 42,
'BotPassword' ) );
201 [
'bp_password' =>
'garbage' ],
202 [
'bp_user' => 42,
'bp_app_id' =>
'BotPassword' ],
205 $password = $bp->getPassword();
206 $this->assertInstanceOf( InvalidPassword::class, $password );
210 $bp1 = TestingAccessWrapper::newFromObject( BotPassword::newFromCentralId( 42,
'BotPassword' ) );
211 $bp2 = TestingAccessWrapper::newFromObject( BotPassword::newFromCentralId( 43,
'BotPassword' ) );
213 $this->assertNotInstanceOf( InvalidPassword::class, $bp1->getPassword(),
'sanity check' );
214 $this->assertNotInstanceOf( InvalidPassword::class, $bp2->getPassword(),
'sanity check' );
215 BotPassword::invalidateAllPasswordsForUser( $this->testUserName );
216 $this->assertInstanceOf( InvalidPassword::class, $bp1->getPassword() );
217 $this->assertNotInstanceOf( InvalidPassword::class, $bp2->getPassword() );
219 $bp = TestingAccessWrapper::newFromObject( BotPassword::newFromCentralId( 42,
'BotPassword' ) );
220 $this->assertInstanceOf( InvalidPassword::class, $bp->getPassword() );
264 $status = BotPassword::login(
"{$this->testUserName}@BotPassword",
'foobaz',
new FauxRequest );
265 $this->assertEquals( Status::newFatal(
'botpasswords-disabled' ),
$status );
270 'logger' =>
new Psr\Log\NullLogger,
273 $reset = MediaWiki\Session\TestUtils::setSessionManagerSingleton( $manager );
275 $manager->getProvider(
MediaWiki\Session\BotPasswordSessionProvider::class ),
278 $status = BotPassword::login(
"{$this->testUserName}@BotPassword",
'foobaz',
new FauxRequest );
279 $this->assertEquals( Status::newFatal(
'botpasswords-no-provider' ),
$status );
280 ScopedCallback::consume( $reset );
285 'SessionProviders' => $mainConfig->get(
'SessionProviders' ) + [
286 MediaWiki\Session\BotPasswordSessionProvider::class => [
287 'class' =>
MediaWiki\Session\BotPasswordSessionProvider::class,
288 'args' => [ [
'priority' => 40 ] ],
294 'logger' =>
new Psr\Log\NullLogger,
297 $reset = MediaWiki\Session\TestUtils::setSessionManagerSingleton( $manager );
301 $this->assertEquals( Status::newFatal(
'botpasswords-invalid-name',
'@' ),
$status );
305 $this->assertEquals( Status::newFatal(
'nosuchuser',
'UTDummy' ),
$status );
308 $status = BotPassword::login(
"{$this->testUserName}@DoesNotExist",
'foobaz',
new FauxRequest );
310 Status::newFatal(
'botpasswords-not-exist', $this->testUserName,
'DoesNotExist' ),
315 $request = $this->getMockBuilder( FauxRequest::class )
316 ->setMethods( [
'getIP' ] )
318 $request->expects( $this->
any() )->method(
'getIP' )
319 ->will( $this->returnValue(
'10.0.0.1' ) );
320 $status = BotPassword::login(
"{$this->testUserName}@BotPassword",
'foobaz',
$request );
321 $this->assertEquals( Status::newFatal(
'botpasswords-restriction-failed' ),
$status );
325 "{$this->testUserName}@BotPassword", $this->testUser->getPassword(),
new FauxRequest );
326 $this->assertEquals( Status::newFatal(
'wrongpassword' ),
$status );
330 $this->assertNotInstanceOf(
331 MediaWiki\Session\BotPasswordSessionProvider::class,
332 $request->getSession()->getProvider(),
335 $status = BotPassword::login(
"{$this->testUserName}@BotPassword",
'foobaz',
$request );
336 $this->assertInstanceOf( Status::class,
$status );
337 $this->assertTrue(
$status->isGood() );
338 $session =
$status->getValue();
339 $this->assertInstanceOf(
MediaWiki\Session\Session::class, $session );
340 $this->assertInstanceOf(
341 MediaWiki\Session\BotPasswordSessionProvider::class, $session->getProvider()
343 $this->assertSame( $session->getId(),
$request->getSession()->getId() );
345 ScopedCallback::consume( $reset );
353 $passwordFactory = new \PasswordFactory();
356 $bp = BotPassword::newUnsaved( [
358 'appId' =>
'TestSave',
360 'grants' => [
'test' ],
362 $this->assertFalse( $bp->isSaved(),
'sanity check' );
364 BotPassword::newFromCentralId( 42,
'TestSave', BotPassword::READ_LATEST ),
'sanity check'
367 $passwordHash = $password ? $passwordFactory->newFromPlaintext( $password ) :
null;
368 $this->assertFalse( $bp->save(
'update', $passwordHash )->isGood() );
369 $this->assertTrue( $bp->save(
'insert', $passwordHash )->isGood() );
371 $bp2 = BotPassword::newFromCentralId( 42,
'TestSave', BotPassword::READ_LATEST );
372 $this->assertInstanceOf( BotPassword::class, $bp2 );
373 $this->assertEquals( $bp->getUserCentralId(), $bp2->getUserCentralId() );
374 $this->assertEquals( $bp->getAppId(), $bp2->getAppId() );
375 $this->assertEquals( $bp->getToken(), $bp2->getToken() );
376 $this->assertEquals( $bp->getRestrictions(), $bp2->getRestrictions() );
377 $this->assertEquals( $bp->getGrants(), $bp2->getGrants() );
380 $pw = TestingAccessWrapper::newFromObject( $bp )->getPassword();
381 if ( $password ===
null ) {
382 $this->assertInstanceOf( InvalidPassword::class, $pw );
384 $this->assertTrue( $pw->equals( $password ) );
387 $token = $bp->getToken();
388 $this->assertEquals( 42, $bp->getUserCentralId() );
389 $this->assertEquals(
'TestSave', $bp->getAppId() );
390 $this->assertFalse( $bp->save(
'insert' )->isGood() );
391 $this->assertTrue( $bp->save(
'update' )->isGood() );
392 $this->assertNotEquals( $token, $bp->getToken() );
394 $bp2 = BotPassword::newFromCentralId( 42,
'TestSave', BotPassword::READ_LATEST );
395 $this->assertInstanceOf( BotPassword::class, $bp2 );
396 $this->assertEquals( $bp->getToken(), $bp2->getToken() );
397 $pw = TestingAccessWrapper::newFromObject( $bp )->getPassword();
398 if ( $password ===
null ) {
399 $this->assertInstanceOf( InvalidPassword::class, $pw );
401 $this->assertTrue( $pw->equals( $password ) );
404 $passwordHash = $passwordFactory->newFromPlaintext(
'XXX' );
405 $token = $bp->getToken();
406 $this->assertTrue( $bp->save(
'update', $passwordHash )->isGood() );
407 $this->assertNotEquals( $token, $bp->getToken() );
410 $pw = TestingAccessWrapper::newFromObject( $bp )->getPassword();
411 $this->assertTrue( $pw->equals(
'XXX' ) );
413 $this->assertTrue( $bp->delete() );
414 $this->assertFalse( $bp->isSaved() );
415 $this->assertNull( BotPassword::newFromCentralId( 42,
'TestSave', BotPassword::READ_LATEST ) );
417 $this->expectException( UnexpectedValueException::class );
418 $bp->save(
'foobar' )->isGood();