Merge "Update docs/hooks.txt for ShowSearchHitTitle"
[mediawiki.git] / tests / phpunit / includes / auth / TemporaryPasswordPrimaryAuthenticationProviderTest.php
blobbc7d65e81d8e6103645f44e644cac68a7045b57e
1 <?php
3 namespace MediaWiki\Auth;
5 use MediaWiki\MediaWikiServices;
6 use Wikimedia\ScopedCallback;
8 /**
9 * @group AuthManager
10 * @group Database
11 * @covers MediaWiki\Auth\TemporaryPasswordPrimaryAuthenticationProvider
13 class TemporaryPasswordPrimaryAuthenticationProviderTest extends \MediaWikiTestCase {
15 private $manager = null;
16 private $config = null;
17 private $validity = null;
19 /**
20 * Get an instance of the provider
22 * $provider->checkPasswordValidity is mocked to return $this->validity,
23 * because we don't need to test that here.
25 * @param array $params
26 * @return TemporaryPasswordPrimaryAuthenticationProvider
28 protected function getProvider( $params = [] ) {
29 if ( !$this->config ) {
30 $this->config = new \HashConfig( [
31 'EmailEnabled' => true,
32 ] );
34 $config = new \MultiConfig( [
35 $this->config,
36 MediaWikiServices::getInstance()->getMainConfig()
37 ] );
39 if ( !$this->manager ) {
40 $this->manager = new AuthManager( new \FauxRequest(), $config );
42 $this->validity = \Status::newGood();
44 $mockedMethods[] = 'checkPasswordValidity';
45 $provider = $this->getMock(
46 TemporaryPasswordPrimaryAuthenticationProvider::class,
47 $mockedMethods,
48 [ $params ]
50 $provider->expects( $this->any() )->method( 'checkPasswordValidity' )
51 ->will( $this->returnCallback( function () {
52 return $this->validity;
53 } ) );
54 $provider->setConfig( $config );
55 $provider->setLogger( new \Psr\Log\NullLogger() );
56 $provider->setManager( $this->manager );
58 return $provider;
61 protected function hookMailer( $func = null ) {
62 \Hooks::clear( 'AlternateUserMailer' );
63 if ( $func ) {
64 \Hooks::register( 'AlternateUserMailer', $func );
65 // Safety
66 \Hooks::register( 'AlternateUserMailer', function () {
67 return false;
68 } );
69 } else {
70 \Hooks::register( 'AlternateUserMailer', function () {
71 $this->fail( 'AlternateUserMailer hook called unexpectedly' );
72 return false;
73 } );
76 return new ScopedCallback( function () {
77 \Hooks::clear( 'AlternateUserMailer' );
78 \Hooks::register( 'AlternateUserMailer', function () {
79 return false;
80 } );
81 } );
84 public function testBasics() {
85 $provider = new TemporaryPasswordPrimaryAuthenticationProvider();
87 $this->assertSame(
88 PrimaryAuthenticationProvider::TYPE_CREATE,
89 $provider->accountCreationType()
92 $this->assertTrue( $provider->testUserExists( 'UTSysop' ) );
93 $this->assertTrue( $provider->testUserExists( 'uTSysop' ) );
94 $this->assertFalse( $provider->testUserExists( 'DoesNotExist' ) );
95 $this->assertFalse( $provider->testUserExists( '<invalid>' ) );
97 $req = new PasswordAuthenticationRequest;
98 $req->action = AuthManager::ACTION_CHANGE;
99 $req->username = '<invalid>';
100 $provider->providerChangeAuthenticationData( $req );
103 public function testConfig() {
104 $config = new \HashConfig( [
105 'EnableEmail' => false,
106 'NewPasswordExpiry' => 100,
107 'PasswordReminderResendTime' => 101,
108 ] );
110 $p = \TestingAccessWrapper::newFromObject( new TemporaryPasswordPrimaryAuthenticationProvider() );
111 $p->setConfig( $config );
112 $this->assertSame( false, $p->emailEnabled );
113 $this->assertSame( 100, $p->newPasswordExpiry );
114 $this->assertSame( 101, $p->passwordReminderResendTime );
116 $p = \TestingAccessWrapper::newFromObject( new TemporaryPasswordPrimaryAuthenticationProvider( [
117 'emailEnabled' => true,
118 'newPasswordExpiry' => 42,
119 'passwordReminderResendTime' => 43,
120 ] ) );
121 $p->setConfig( $config );
122 $this->assertSame( true, $p->emailEnabled );
123 $this->assertSame( 42, $p->newPasswordExpiry );
124 $this->assertSame( 43, $p->passwordReminderResendTime );
127 public function testTestUserCanAuthenticate() {
128 $user = self::getMutableTestUser()->getUser();
130 $dbw = wfGetDB( DB_MASTER );
132 $passwordFactory = new \PasswordFactory();
133 $passwordFactory->init( \RequestContext::getMain()->getConfig() );
134 // A is unsalted MD5 (thus fast) ... we don't care about security here, this is test only
135 $passwordFactory->setDefaultType( 'A' );
136 $pwhash = $passwordFactory->newFromPlaintext( 'password' )->toString();
138 $provider = $this->getProvider();
139 $providerPriv = \TestingAccessWrapper::newFromObject( $provider );
141 $this->assertFalse( $provider->testUserCanAuthenticate( '<invalid>' ) );
142 $this->assertFalse( $provider->testUserCanAuthenticate( 'DoesNotExist' ) );
144 $dbw->update(
145 'user',
147 'user_newpassword' => \PasswordFactory::newInvalidPassword()->toString(),
148 'user_newpass_time' => null,
150 [ 'user_id' => $user->getId() ]
152 $this->assertFalse( $provider->testUserCanAuthenticate( $user->getName() ) );
154 $dbw->update(
155 'user',
157 'user_newpassword' => $pwhash,
158 'user_newpass_time' => null,
160 [ 'user_id' => $user->getId() ]
162 $this->assertTrue( $provider->testUserCanAuthenticate( $user->getName() ) );
163 $this->assertTrue( $provider->testUserCanAuthenticate( lcfirst( $user->getName() ) ) );
165 $dbw->update(
166 'user',
168 'user_newpassword' => $pwhash,
169 'user_newpass_time' => $dbw->timestamp( time() - 10 ),
171 [ 'user_id' => $user->getId() ]
173 $providerPriv->newPasswordExpiry = 100;
174 $this->assertTrue( $provider->testUserCanAuthenticate( $user->getName() ) );
175 $providerPriv->newPasswordExpiry = 1;
176 $this->assertFalse( $provider->testUserCanAuthenticate( $user->getName() ) );
178 $dbw->update(
179 'user',
181 'user_newpassword' => \PasswordFactory::newInvalidPassword()->toString(),
182 'user_newpass_time' => null,
184 [ 'user_id' => $user->getId() ]
189 * @dataProvider provideGetAuthenticationRequests
190 * @param string $action
191 * @param array $options
192 * @param array $expected
194 public function testGetAuthenticationRequests( $action, $options, $expected ) {
195 $actual = $this->getProvider()->getAuthenticationRequests( $action, $options );
196 foreach ( $actual as $req ) {
197 if ( $req instanceof TemporaryPasswordAuthenticationRequest && $req->password !== null ) {
198 $req->password = 'random';
201 $this->assertEquals( $expected, $actual );
204 public static function provideGetAuthenticationRequests() {
205 $anon = [ 'username' => null ];
206 $loggedIn = [ 'username' => 'UTSysop' ];
208 return [
209 [ AuthManager::ACTION_LOGIN, $anon, [
210 new PasswordAuthenticationRequest
211 ] ],
212 [ AuthManager::ACTION_LOGIN, $loggedIn, [
213 new PasswordAuthenticationRequest
214 ] ],
215 [ AuthManager::ACTION_CREATE, $anon, [] ],
216 [ AuthManager::ACTION_CREATE, $loggedIn, [
217 new TemporaryPasswordAuthenticationRequest( 'random' )
218 ] ],
219 [ AuthManager::ACTION_LINK, $anon, [] ],
220 [ AuthManager::ACTION_LINK, $loggedIn, [] ],
221 [ AuthManager::ACTION_CHANGE, $anon, [
222 new TemporaryPasswordAuthenticationRequest( 'random' )
223 ] ],
224 [ AuthManager::ACTION_CHANGE, $loggedIn, [
225 new TemporaryPasswordAuthenticationRequest( 'random' )
226 ] ],
227 [ AuthManager::ACTION_REMOVE, $anon, [
228 new TemporaryPasswordAuthenticationRequest
229 ] ],
230 [ AuthManager::ACTION_REMOVE, $loggedIn, [
231 new TemporaryPasswordAuthenticationRequest
232 ] ],
236 public function testAuthentication() {
237 $user = self::getMutableTestUser()->getUser();
239 $password = 'TemporaryPassword';
240 $hash = ':A:' . md5( $password );
241 $dbw = wfGetDB( DB_MASTER );
242 $dbw->update(
243 'user',
244 [ 'user_newpassword' => $hash, 'user_newpass_time' => $dbw->timestamp( time() - 10 ) ],
245 [ 'user_id' => $user->getId() ]
248 $req = new PasswordAuthenticationRequest();
249 $req->action = AuthManager::ACTION_LOGIN;
250 $reqs = [ PasswordAuthenticationRequest::class => $req ];
252 $provider = $this->getProvider();
253 $providerPriv = \TestingAccessWrapper::newFromObject( $provider );
255 $providerPriv->newPasswordExpiry = 100;
257 // General failures
258 $this->assertEquals(
259 AuthenticationResponse::newAbstain(),
260 $provider->beginPrimaryAuthentication( [] )
263 $req->username = 'foo';
264 $req->password = null;
265 $this->assertEquals(
266 AuthenticationResponse::newAbstain(),
267 $provider->beginPrimaryAuthentication( $reqs )
270 $req->username = null;
271 $req->password = 'bar';
272 $this->assertEquals(
273 AuthenticationResponse::newAbstain(),
274 $provider->beginPrimaryAuthentication( $reqs )
277 $req->username = '<invalid>';
278 $req->password = 'WhoCares';
279 $ret = $provider->beginPrimaryAuthentication( $reqs );
280 $this->assertEquals(
281 AuthenticationResponse::newAbstain(),
282 $provider->beginPrimaryAuthentication( $reqs )
285 $req->username = 'DoesNotExist';
286 $req->password = 'DoesNotExist';
287 $ret = $provider->beginPrimaryAuthentication( $reqs );
288 $this->assertEquals(
289 AuthenticationResponse::newAbstain(),
290 $provider->beginPrimaryAuthentication( $reqs )
293 // Validation failure
294 $req->username = $user->getName();
295 $req->password = $password;
296 $this->validity = \Status::newFatal( 'arbitrary-failure' );
297 $ret = $provider->beginPrimaryAuthentication( $reqs );
298 $this->assertEquals(
299 AuthenticationResponse::FAIL,
300 $ret->status
302 $this->assertEquals(
303 'arbitrary-failure',
304 $ret->message->getKey()
307 // Successful auth
308 $this->manager->removeAuthenticationSessionData( null );
309 $this->validity = \Status::newGood();
310 $this->assertEquals(
311 AuthenticationResponse::newPass( $user->getName() ),
312 $provider->beginPrimaryAuthentication( $reqs )
314 $this->assertNotNull( $this->manager->getAuthenticationSessionData( 'reset-pass' ) );
316 $this->manager->removeAuthenticationSessionData( null );
317 $this->validity = \Status::newGood();
318 $req->username = lcfirst( $user->getName() );
319 $this->assertEquals(
320 AuthenticationResponse::newPass( $user->getName() ),
321 $provider->beginPrimaryAuthentication( $reqs )
323 $this->assertNotNull( $this->manager->getAuthenticationSessionData( 'reset-pass' ) );
324 $req->username = $user->getName();
326 // Expired password
327 $providerPriv->newPasswordExpiry = 1;
328 $ret = $provider->beginPrimaryAuthentication( $reqs );
329 $this->assertEquals(
330 AuthenticationResponse::FAIL,
331 $ret->status
333 $this->assertEquals(
334 'wrongpassword',
335 $ret->message->getKey()
338 // Bad password
339 $providerPriv->newPasswordExpiry = 100;
340 $this->validity = \Status::newGood();
341 $req->password = 'Wrong';
342 $ret = $provider->beginPrimaryAuthentication( $reqs );
343 $this->assertEquals(
344 AuthenticationResponse::FAIL,
345 $ret->status
347 $this->assertEquals(
348 'wrongpassword',
349 $ret->message->getKey()
354 * @dataProvider provideProviderAllowsAuthenticationDataChange
355 * @param string $type
356 * @param string $user
357 * @param \Status $validity Result of the password validity check
358 * @param \StatusValue $expect1 Expected result with $checkData = false
359 * @param \StatusValue $expect2 Expected result with $checkData = true
361 public function testProviderAllowsAuthenticationDataChange( $type, $user, \Status $validity,
362 \StatusValue $expect1, \StatusValue $expect2
364 if ( $type === PasswordAuthenticationRequest::class ||
365 $type === TemporaryPasswordAuthenticationRequest::class
367 $req = new $type();
368 } else {
369 $req = $this->getMock( $type );
371 $req->action = AuthManager::ACTION_CHANGE;
372 $req->username = $user;
373 $req->password = 'NewPassword';
375 $provider = $this->getProvider();
376 $this->validity = $validity;
377 $this->assertEquals( $expect1, $provider->providerAllowsAuthenticationDataChange( $req, false ) );
378 $this->assertEquals( $expect2, $provider->providerAllowsAuthenticationDataChange( $req, true ) );
381 public static function provideProviderAllowsAuthenticationDataChange() {
382 $err = \StatusValue::newGood();
383 $err->error( 'arbitrary-warning' );
385 return [
386 [ AuthenticationRequest::class, 'UTSysop', \Status::newGood(),
387 \StatusValue::newGood( 'ignored' ), \StatusValue::newGood( 'ignored' ) ],
388 [ PasswordAuthenticationRequest::class, 'UTSysop', \Status::newGood(),
389 \StatusValue::newGood( 'ignored' ), \StatusValue::newGood( 'ignored' ) ],
390 [ TemporaryPasswordAuthenticationRequest::class, 'UTSysop', \Status::newGood(),
391 \StatusValue::newGood(), \StatusValue::newGood() ],
392 [ TemporaryPasswordAuthenticationRequest::class, 'uTSysop', \Status::newGood(),
393 \StatusValue::newGood(), \StatusValue::newGood() ],
394 [ TemporaryPasswordAuthenticationRequest::class, 'UTSysop', \Status::wrap( $err ),
395 \StatusValue::newGood(), $err ],
396 [ TemporaryPasswordAuthenticationRequest::class, 'UTSysop',
397 \Status::newFatal( 'arbitrary-error' ), \StatusValue::newGood(),
398 \StatusValue::newFatal( 'arbitrary-error' ) ],
399 [ TemporaryPasswordAuthenticationRequest::class, 'DoesNotExist', \Status::newGood(),
400 \StatusValue::newGood(), \StatusValue::newGood( 'ignored' ) ],
401 [ TemporaryPasswordAuthenticationRequest::class, '<invalid>', \Status::newGood(),
402 \StatusValue::newGood(), \StatusValue::newGood( 'ignored' ) ],
407 * @dataProvider provideProviderChangeAuthenticationData
408 * @param string $user
409 * @param string $type
410 * @param bool $changed
412 public function testProviderChangeAuthenticationData( $user, $type, $changed ) {
413 $cuser = ucfirst( $user );
414 $oldpass = 'OldTempPassword';
415 $newpass = 'NewTempPassword';
417 $dbw = wfGetDB( DB_MASTER );
418 $oldHash = $dbw->selectField( 'user', 'user_newpassword', [ 'user_name' => $cuser ] );
419 $cb = new ScopedCallback( function () use ( $dbw, $cuser, $oldHash ) {
420 $dbw->update( 'user', [ 'user_newpassword' => $oldHash ], [ 'user_name' => $cuser ] );
421 } );
423 $hash = ':A:' . md5( $oldpass );
424 $dbw->update(
425 'user',
426 [ 'user_newpassword' => $hash, 'user_newpass_time' => $dbw->timestamp( time() + 10 ) ],
427 [ 'user_name' => $cuser ]
430 $provider = $this->getProvider();
432 // Sanity check
433 $loginReq = new PasswordAuthenticationRequest();
434 $loginReq->action = AuthManager::ACTION_CHANGE;
435 $loginReq->username = $user;
436 $loginReq->password = $oldpass;
437 $loginReqs = [ PasswordAuthenticationRequest::class => $loginReq ];
438 $this->assertEquals(
439 AuthenticationResponse::newPass( $cuser ),
440 $provider->beginPrimaryAuthentication( $loginReqs ),
441 'Sanity check'
444 if ( $type === PasswordAuthenticationRequest::class ||
445 $type === TemporaryPasswordAuthenticationRequest::class
447 $changeReq = new $type();
448 } else {
449 $changeReq = $this->getMock( $type );
451 $changeReq->action = AuthManager::ACTION_CHANGE;
452 $changeReq->username = $user;
453 $changeReq->password = $newpass;
454 $resetMailer = $this->hookMailer();
455 $provider->providerChangeAuthenticationData( $changeReq );
456 ScopedCallback::consume( $resetMailer );
458 $loginReq->password = $oldpass;
459 $ret = $provider->beginPrimaryAuthentication( $loginReqs );
460 $this->assertEquals(
461 AuthenticationResponse::FAIL,
462 $ret->status,
463 'old password should fail'
465 $this->assertEquals(
466 'wrongpassword',
467 $ret->message->getKey(),
468 'old password should fail'
471 $loginReq->password = $newpass;
472 $ret = $provider->beginPrimaryAuthentication( $loginReqs );
473 if ( $changed ) {
474 $this->assertEquals(
475 AuthenticationResponse::newPass( $cuser ),
476 $ret,
477 'new password should pass'
479 $this->assertNotNull(
480 $dbw->selectField( 'user', 'user_newpass_time', [ 'user_name' => $cuser ] )
482 } else {
483 $this->assertEquals(
484 AuthenticationResponse::FAIL,
485 $ret->status,
486 'new password should fail'
488 $this->assertEquals(
489 'wrongpassword',
490 $ret->message->getKey(),
491 'new password should fail'
493 $this->assertNull(
494 $dbw->selectField( 'user', 'user_newpass_time', [ 'user_name' => $cuser ] )
499 public static function provideProviderChangeAuthenticationData() {
500 return [
501 [ 'UTSysop', AuthenticationRequest::class, false ],
502 [ 'UTSysop', PasswordAuthenticationRequest::class, false ],
503 [ 'UTSysop', TemporaryPasswordAuthenticationRequest::class, true ],
507 public function testProviderChangeAuthenticationDataEmail() {
508 $user = self::getMutableTestUser()->getUser();
510 $dbw = wfGetDB( DB_MASTER );
511 $dbw->update(
512 'user',
513 [ 'user_newpass_time' => $dbw->timestamp( time() - 5 * 3600 ) ],
514 [ 'user_id' => $user->getId() ]
517 $req = TemporaryPasswordAuthenticationRequest::newRandom();
518 $req->username = $user->getName();
519 $req->mailpassword = true;
521 $provider = $this->getProvider( [ 'emailEnabled' => false ] );
522 $status = $provider->providerAllowsAuthenticationDataChange( $req, true );
523 $this->assertEquals( \StatusValue::newFatal( 'passwordreset-emaildisabled' ), $status );
525 $provider = $this->getProvider( [ 'passwordReminderResendTime' => 10 ] );
526 $status = $provider->providerAllowsAuthenticationDataChange( $req, true );
527 $this->assertEquals( \StatusValue::newFatal( 'throttled-mailpassword', 10 ), $status );
529 $provider = $this->getProvider( [ 'passwordReminderResendTime' => 3 ] );
530 $status = $provider->providerAllowsAuthenticationDataChange( $req, true );
531 $this->assertFalse( $status->hasMessage( 'throttled-mailpassword' ) );
533 $dbw->update(
534 'user',
535 [ 'user_newpass_time' => $dbw->timestamp( time() + 5 * 3600 ) ],
536 [ 'user_id' => $user->getId() ]
538 $provider = $this->getProvider( [ 'passwordReminderResendTime' => 0 ] );
539 $status = $provider->providerAllowsAuthenticationDataChange( $req, true );
540 $this->assertFalse( $status->hasMessage( 'throttled-mailpassword' ) );
542 $req->caller = null;
543 $status = $provider->providerAllowsAuthenticationDataChange( $req, true );
544 $this->assertEquals( \StatusValue::newFatal( 'passwordreset-nocaller' ), $status );
546 $req->caller = '127.0.0.256';
547 $status = $provider->providerAllowsAuthenticationDataChange( $req, true );
548 $this->assertEquals( \StatusValue::newFatal( 'passwordreset-nosuchcaller', '127.0.0.256' ),
549 $status );
551 $req->caller = '<Invalid>';
552 $status = $provider->providerAllowsAuthenticationDataChange( $req, true );
553 $this->assertEquals( \StatusValue::newFatal( 'passwordreset-nosuchcaller', '<Invalid>' ),
554 $status );
556 $req->caller = '127.0.0.1';
557 $status = $provider->providerAllowsAuthenticationDataChange( $req, true );
558 $this->assertEquals( \StatusValue::newGood(), $status );
560 $req->caller = $user->getName();
561 $status = $provider->providerAllowsAuthenticationDataChange( $req, true );
562 $this->assertEquals( \StatusValue::newGood(), $status );
564 $mailed = false;
565 $resetMailer = $this->hookMailer( function ( $headers, $to, $from, $subject, $body )
566 use ( &$mailed, $req, $user )
568 $mailed = true;
569 $this->assertSame( $user->getEmail(), $to[0]->address );
570 $this->assertContains( $req->password, $body );
571 return false;
572 } );
573 $provider->providerChangeAuthenticationData( $req );
574 ScopedCallback::consume( $resetMailer );
575 $this->assertTrue( $mailed );
577 $priv = \TestingAccessWrapper::newFromObject( $provider );
578 $req->username = '<invalid>';
579 $status = $priv->sendPasswordResetEmail( $req );
580 $this->assertEquals( \Status::newFatal( 'noname' ), $status );
583 public function testTestForAccountCreation() {
584 $user = \User::newFromName( 'foo' );
585 $req = new TemporaryPasswordAuthenticationRequest();
586 $req->username = 'Foo';
587 $req->password = 'Bar';
588 $reqs = [ TemporaryPasswordAuthenticationRequest::class => $req ];
590 $provider = $this->getProvider();
591 $this->assertEquals(
592 \StatusValue::newGood(),
593 $provider->testForAccountCreation( $user, $user, [] ),
594 'No password request'
597 $this->assertEquals(
598 \StatusValue::newGood(),
599 $provider->testForAccountCreation( $user, $user, $reqs ),
600 'Password request, validated'
603 $this->validity->error( 'arbitrary warning' );
604 $expect = \StatusValue::newGood();
605 $expect->error( 'arbitrary warning' );
606 $this->assertEquals(
607 $expect,
608 $provider->testForAccountCreation( $user, $user, $reqs ),
609 'Password request, not validated'
613 public function testAccountCreation() {
614 $resetMailer = $this->hookMailer();
616 $user = \User::newFromName( 'Foo' );
618 $req = new TemporaryPasswordAuthenticationRequest();
619 $reqs = [ TemporaryPasswordAuthenticationRequest::class => $req ];
621 $authreq = new PasswordAuthenticationRequest();
622 $authreq->action = AuthManager::ACTION_CREATE;
623 $authreqs = [ PasswordAuthenticationRequest::class => $authreq ];
625 $provider = $this->getProvider();
627 $this->assertEquals(
628 AuthenticationResponse::newAbstain(),
629 $provider->beginPrimaryAccountCreation( $user, $user, [] )
632 $req->username = 'foo';
633 $req->password = null;
634 $this->assertEquals(
635 AuthenticationResponse::newAbstain(),
636 $provider->beginPrimaryAccountCreation( $user, $user, $reqs )
639 $req->username = null;
640 $req->password = 'bar';
641 $this->assertEquals(
642 AuthenticationResponse::newAbstain(),
643 $provider->beginPrimaryAccountCreation( $user, $user, $reqs )
646 $req->username = 'foo';
647 $req->password = 'bar';
649 $expect = AuthenticationResponse::newPass( 'Foo' );
650 $expect->createRequest = clone( $req );
651 $expect->createRequest->username = 'Foo';
652 $this->assertEquals( $expect, $provider->beginPrimaryAccountCreation( $user, $user, $reqs ) );
653 $this->assertNull( $this->manager->getAuthenticationSessionData( 'no-email' ) );
655 $user = self::getMutableTestUser()->getUser();
656 $req->username = $authreq->username = $user->getName();
657 $req->password = $authreq->password = 'NewPassword';
658 $expect = AuthenticationResponse::newPass( $user->getName() );
659 $expect->createRequest = $req;
661 $res2 = $provider->beginPrimaryAccountCreation( $user, $user, $reqs );
662 $this->assertEquals( $expect, $res2, 'Sanity check' );
664 $ret = $provider->beginPrimaryAuthentication( $authreqs );
665 $this->assertEquals( AuthenticationResponse::FAIL, $ret->status, 'sanity check' );
667 $this->assertSame( null, $provider->finishAccountCreation( $user, $user, $res2 ) );
669 $ret = $provider->beginPrimaryAuthentication( $authreqs );
670 $this->assertEquals( AuthenticationResponse::PASS, $ret->status, 'new password is set' );
673 public function testAccountCreationEmail() {
674 $creator = \User::newFromName( 'Foo' );
676 $user = self::getMutableTestUser()->getUser();
677 $user->setEmail( null );
679 $req = TemporaryPasswordAuthenticationRequest::newRandom();
680 $req->username = $user->getName();
681 $req->mailpassword = true;
683 $provider = $this->getProvider( [ 'emailEnabled' => false ] );
684 $status = $provider->testForAccountCreation( $user, $creator, [ $req ] );
685 $this->assertEquals( \StatusValue::newFatal( 'emaildisabled' ), $status );
687 $provider = $this->getProvider( [ 'emailEnabled' => true ] );
688 $status = $provider->testForAccountCreation( $user, $creator, [ $req ] );
689 $this->assertEquals( \StatusValue::newFatal( 'noemailcreate' ), $status );
691 $user->setEmail( 'test@localhost.localdomain' );
692 $status = $provider->testForAccountCreation( $user, $creator, [ $req ] );
693 $this->assertEquals( \StatusValue::newGood(), $status );
695 $mailed = false;
696 $resetMailer = $this->hookMailer( function ( $headers, $to, $from, $subject, $body )
697 use ( &$mailed, $req )
699 $mailed = true;
700 $this->assertSame( 'test@localhost.localdomain', $to[0]->address );
701 $this->assertContains( $req->password, $body );
702 return false;
703 } );
705 $expect = AuthenticationResponse::newPass( $user->getName() );
706 $expect->createRequest = clone( $req );
707 $expect->createRequest->username = $user->getName();
708 $res = $provider->beginPrimaryAccountCreation( $user, $creator, [ $req ] );
709 $this->assertEquals( $expect, $res );
710 $this->assertTrue( $this->manager->getAuthenticationSessionData( 'no-email' ) );
711 $this->assertFalse( $mailed );
713 $this->assertSame( 'byemail', $provider->finishAccountCreation( $user, $creator, $res ) );
714 $this->assertTrue( $mailed );
716 ScopedCallback::consume( $resetMailer );
717 $this->assertTrue( $mailed );