3 namespace MediaWiki\Auth
;
8 * @covers MediaWiki\Auth\TemporaryPasswordPrimaryAuthenticationProvider
10 class TemporaryPasswordPrimaryAuthenticationProviderTest
extends \MediaWikiTestCase
{
12 private $manager = null;
13 private $config = null;
14 private $validity = null;
17 * Get an instance of the provider
19 * $provider->checkPasswordValidity is mocked to return $this->validity,
20 * because we don't need to test that here.
22 * @param array $params
23 * @return TemporaryPasswordPrimaryAuthenticationProvider
25 protected function getProvider( $params = [] ) {
26 if ( !$this->config
) {
27 $this->config
= new \
HashConfig( [
28 'EmailEnabled' => true,
31 $config = new \
MultiConfig( [
33 \ConfigFactory
::getDefaultInstance()->makeConfig( 'main' )
36 if ( !$this->manager
) {
37 $this->manager
= new AuthManager( new \
FauxRequest(), $config );
39 $this->validity
= \Status
::newGood();
41 $mockedMethods[] = 'checkPasswordValidity';
42 $provider = $this->getMock(
43 TemporaryPasswordPrimaryAuthenticationProvider
::class,
47 $provider->expects( $this->any() )->method( 'checkPasswordValidity' )
48 ->will( $this->returnCallback( function () {
49 return $this->validity
;
51 $provider->setConfig( $config );
52 $provider->setLogger( new \Psr\Log\
NullLogger() );
53 $provider->setManager( $this->manager
);
58 protected function hookMailer( $func = null ) {
59 \Hooks
::clear( 'AlternateUserMailer' );
61 \Hooks
::register( 'AlternateUserMailer', $func );
63 \Hooks
::register( 'AlternateUserMailer', function () {
67 \Hooks
::register( 'AlternateUserMailer', function () {
68 $this->fail( 'AlternateUserMailer hook called unexpectedly' );
73 return new \
ScopedCallback( function () {
74 \Hooks
::clear( 'AlternateUserMailer' );
75 \Hooks
::register( 'AlternateUserMailer', function () {
81 public function testBasics() {
82 $provider = new TemporaryPasswordPrimaryAuthenticationProvider();
85 PrimaryAuthenticationProvider
::TYPE_CREATE
,
86 $provider->accountCreationType()
89 $this->assertTrue( $provider->testUserExists( 'UTSysop' ) );
90 $this->assertTrue( $provider->testUserExists( 'uTSysop' ) );
91 $this->assertFalse( $provider->testUserExists( 'DoesNotExist' ) );
92 $this->assertFalse( $provider->testUserExists( '<invalid>' ) );
94 $req = new PasswordAuthenticationRequest
;
95 $req->action
= AuthManager
::ACTION_CHANGE
;
96 $req->username
= '<invalid>';
97 $provider->providerChangeAuthenticationData( $req );
100 public function testConfig() {
101 $config = new \
HashConfig( [
102 'EnableEmail' => false,
103 'NewPasswordExpiry' => 100,
104 'PasswordReminderResendTime' => 101,
107 $p = \TestingAccessWrapper
::newFromObject( new TemporaryPasswordPrimaryAuthenticationProvider() );
108 $p->setConfig( $config );
109 $this->assertSame( false, $p->emailEnabled
);
110 $this->assertSame( 100, $p->newPasswordExpiry
);
111 $this->assertSame( 101, $p->passwordReminderResendTime
);
113 $p = \TestingAccessWrapper
::newFromObject( new TemporaryPasswordPrimaryAuthenticationProvider( [
114 'emailEnabled' => true,
115 'newPasswordExpiry' => 42,
116 'passwordReminderResendTime' => 43,
118 $p->setConfig( $config );
119 $this->assertSame( true, $p->emailEnabled
);
120 $this->assertSame( 42, $p->newPasswordExpiry
);
121 $this->assertSame( 43, $p->passwordReminderResendTime
);
124 public function testTestUserCanAuthenticate() {
125 $dbw = wfGetDB( DB_MASTER
);
127 $passwordFactory = new \
PasswordFactory();
128 $passwordFactory->init( \RequestContext
::getMain()->getConfig() );
129 // A is unsalted MD5 (thus fast) ... we don't care about security here, this is test only
130 $passwordFactory->setDefaultType( 'A' );
131 $pwhash = $passwordFactory->newFromPlaintext( 'password' )->toString();
133 $provider = $this->getProvider();
134 $providerPriv = \TestingAccessWrapper
::newFromObject( $provider );
136 $this->assertFalse( $provider->testUserCanAuthenticate( '<invalid>' ) );
137 $this->assertFalse( $provider->testUserCanAuthenticate( 'DoesNotExist' ) );
142 'user_newpassword' => \PasswordFactory
::newInvalidPassword()->toString(),
143 'user_newpass_time' => null,
145 [ 'user_name' => 'UTSysop' ]
147 $this->assertFalse( $provider->testUserCanAuthenticate( 'UTSysop' ) );
152 'user_newpassword' => $pwhash,
153 'user_newpass_time' => null,
155 [ 'user_name' => 'UTSysop' ]
157 $this->assertTrue( $provider->testUserCanAuthenticate( 'UTSysop' ) );
158 $this->assertTrue( $provider->testUserCanAuthenticate( 'uTSysop' ) );
163 'user_newpassword' => $pwhash,
164 'user_newpass_time' => $dbw->timestamp( time() - 10 ),
166 [ 'user_name' => 'UTSysop' ]
168 $providerPriv->newPasswordExpiry
= 100;
169 $this->assertTrue( $provider->testUserCanAuthenticate( 'UTSysop' ) );
170 $providerPriv->newPasswordExpiry
= 1;
171 $this->assertFalse( $provider->testUserCanAuthenticate( 'UTSysop' ) );
176 'user_newpassword' => \PasswordFactory
::newInvalidPassword()->toString(),
177 'user_newpass_time' => null,
179 [ 'user_name' => 'UTSysop' ]
184 * @dataProvider provideGetAuthenticationRequests
185 * @param string $action
186 * @param array $options
187 * @param array $expected
189 public function testGetAuthenticationRequests( $action, $options, $expected ) {
190 $actual = $this->getProvider()->getAuthenticationRequests( $action, $options );
191 foreach ( $actual as $req ) {
192 if ( $req instanceof TemporaryPasswordAuthenticationRequest
&& $req->password
!== null ) {
193 $req->password
= 'random';
196 $this->assertEquals( $expected, $actual );
199 public static function provideGetAuthenticationRequests() {
200 $anon = [ 'username' => null ];
201 $loggedIn = [ 'username' => 'UTSysop' ];
204 [ AuthManager
::ACTION_LOGIN
, $anon, [
205 new PasswordAuthenticationRequest
207 [ AuthManager
::ACTION_LOGIN
, $loggedIn, [
208 new PasswordAuthenticationRequest
210 [ AuthManager
::ACTION_CREATE
, $anon, [] ],
211 [ AuthManager
::ACTION_CREATE
, $loggedIn, [
212 new TemporaryPasswordAuthenticationRequest( 'random' )
214 [ AuthManager
::ACTION_LINK
, $anon, [] ],
215 [ AuthManager
::ACTION_LINK
, $loggedIn, [] ],
216 [ AuthManager
::ACTION_CHANGE
, $anon, [
217 new TemporaryPasswordAuthenticationRequest( 'random' )
219 [ AuthManager
::ACTION_CHANGE
, $loggedIn, [
220 new TemporaryPasswordAuthenticationRequest( 'random' )
222 [ AuthManager
::ACTION_REMOVE
, $anon, [
223 new TemporaryPasswordAuthenticationRequest
225 [ AuthManager
::ACTION_REMOVE
, $loggedIn, [
226 new TemporaryPasswordAuthenticationRequest
231 public function testAuthentication() {
232 $password = 'TemporaryPassword';
233 $hash = ':A:' . md5( $password );
234 $dbw = wfGetDB( DB_MASTER
);
237 [ 'user_newpassword' => $hash, 'user_newpass_time' => $dbw->timestamp( time() - 10 ) ],
238 [ 'user_name' => 'UTSysop' ]
241 $req = new PasswordAuthenticationRequest();
242 $req->action
= AuthManager
::ACTION_LOGIN
;
243 $reqs = [ PasswordAuthenticationRequest
::class => $req ];
245 $provider = $this->getProvider();
246 $providerPriv = \TestingAccessWrapper
::newFromObject( $provider );
248 $providerPriv->newPasswordExpiry
= 100;
252 AuthenticationResponse
::newAbstain(),
253 $provider->beginPrimaryAuthentication( [] )
256 $req->username
= 'foo';
257 $req->password
= null;
259 AuthenticationResponse
::newAbstain(),
260 $provider->beginPrimaryAuthentication( $reqs )
263 $req->username
= null;
264 $req->password
= 'bar';
266 AuthenticationResponse
::newAbstain(),
267 $provider->beginPrimaryAuthentication( $reqs )
270 $req->username
= '<invalid>';
271 $req->password
= 'WhoCares';
272 $ret = $provider->beginPrimaryAuthentication( $reqs );
274 AuthenticationResponse
::newAbstain(),
275 $provider->beginPrimaryAuthentication( $reqs )
278 $req->username
= 'DoesNotExist';
279 $req->password
= 'DoesNotExist';
280 $ret = $provider->beginPrimaryAuthentication( $reqs );
282 AuthenticationResponse
::newAbstain(),
283 $provider->beginPrimaryAuthentication( $reqs )
286 // Validation failure
287 $req->username
= 'UTSysop';
288 $req->password
= $password;
289 $this->validity
= \Status
::newFatal( 'arbitrary-failure' );
290 $ret = $provider->beginPrimaryAuthentication( $reqs );
292 AuthenticationResponse
::FAIL
,
297 $ret->message
->getKey()
301 $this->manager
->removeAuthenticationSessionData( null );
302 $this->validity
= \Status
::newGood();
304 AuthenticationResponse
::newPass( 'UTSysop' ),
305 $provider->beginPrimaryAuthentication( $reqs )
307 $this->assertNotNull( $this->manager
->getAuthenticationSessionData( 'reset-pass' ) );
309 $this->manager
->removeAuthenticationSessionData( null );
310 $this->validity
= \Status
::newGood();
311 $req->username
= 'uTSysop';
313 AuthenticationResponse
::newPass( 'UTSysop' ),
314 $provider->beginPrimaryAuthentication( $reqs )
316 $this->assertNotNull( $this->manager
->getAuthenticationSessionData( 'reset-pass' ) );
317 $req->username
= 'UTSysop';
320 $providerPriv->newPasswordExpiry
= 1;
321 $ret = $provider->beginPrimaryAuthentication( $reqs );
323 AuthenticationResponse
::FAIL
,
328 $ret->message
->getKey()
332 $providerPriv->newPasswordExpiry
= 100;
333 $this->validity
= \Status
::newGood();
334 $req->password
= 'Wrong';
335 $ret = $provider->beginPrimaryAuthentication( $reqs );
337 AuthenticationResponse
::FAIL
,
342 $ret->message
->getKey()
348 * @dataProvider provideProviderAllowsAuthenticationDataChange
349 * @param string $type
350 * @param string $user
351 * @param \Status $validity Result of the password validity check
352 * @param \StatusValue $expect1 Expected result with $checkData = false
353 * @param \StatusValue $expect2 Expected result with $checkData = true
355 public function testProviderAllowsAuthenticationDataChange( $type, $user, \Status
$validity,
356 \StatusValue
$expect1, \StatusValue
$expect2
358 if ( $type === PasswordAuthenticationRequest
::class ||
359 $type === TemporaryPasswordAuthenticationRequest
::class
363 $req = $this->getMock( $type );
365 $req->action
= AuthManager
::ACTION_CHANGE
;
366 $req->username
= $user;
367 $req->password
= 'NewPassword';
369 $provider = $this->getProvider();
370 $this->validity
= $validity;
371 $this->assertEquals( $expect1, $provider->providerAllowsAuthenticationDataChange( $req, false ) );
372 $this->assertEquals( $expect2, $provider->providerAllowsAuthenticationDataChange( $req, true ) );
375 public static function provideProviderAllowsAuthenticationDataChange() {
376 $err = \StatusValue
::newGood();
377 $err->error( 'arbitrary-warning' );
380 [ AuthenticationRequest
::class, 'UTSysop', \Status
::newGood(),
381 \StatusValue
::newGood( 'ignored' ), \StatusValue
::newGood( 'ignored' ) ],
382 [ PasswordAuthenticationRequest
::class, 'UTSysop', \Status
::newGood(),
383 \StatusValue
::newGood( 'ignored' ), \StatusValue
::newGood( 'ignored' ) ],
384 [ TemporaryPasswordAuthenticationRequest
::class, 'UTSysop', \Status
::newGood(),
385 \StatusValue
::newGood(), \StatusValue
::newGood() ],
386 [ TemporaryPasswordAuthenticationRequest
::class, 'uTSysop', \Status
::newGood(),
387 \StatusValue
::newGood(), \StatusValue
::newGood() ],
388 [ TemporaryPasswordAuthenticationRequest
::class, 'UTSysop', \Status
::wrap( $err ),
389 \StatusValue
::newGood(), $err ],
390 [ TemporaryPasswordAuthenticationRequest
::class, 'UTSysop',
391 \Status
::newFatal( 'arbitrary-error' ), \StatusValue
::newGood(),
392 \StatusValue
::newFatal( 'arbitrary-error' ) ],
393 [ TemporaryPasswordAuthenticationRequest
::class, 'DoesNotExist', \Status
::newGood(),
394 \StatusValue
::newGood(), \StatusValue
::newGood( 'ignored' ) ],
395 [ TemporaryPasswordAuthenticationRequest
::class, '<invalid>', \Status
::newGood(),
396 \StatusValue
::newGood(), \StatusValue
::newGood( 'ignored' ) ],
401 * @dataProvider provideProviderChangeAuthenticationData
402 * @param string $user
403 * @param string $type
404 * @param bool $changed
406 public function testProviderChangeAuthenticationData( $user, $type, $changed ) {
407 $cuser = ucfirst( $user );
408 $oldpass = 'OldTempPassword';
409 $newpass = 'NewTempPassword';
411 $hash = ':A:' . md5( $oldpass );
412 $dbw = wfGetDB( DB_MASTER
);
415 [ 'user_newpassword' => $hash, 'user_newpass_time' => $dbw->timestamp( time() +
10 ) ],
416 [ 'user_name' => 'UTSysop' ]
419 $dbw = wfGetDB( DB_MASTER
);
420 $oldHash = $dbw->selectField( 'user', 'user_newpassword', [ 'user_name' => $cuser ] );
421 $cb = new \
ScopedCallback( function () use ( $dbw, $cuser, $oldHash ) {
422 $dbw->update( 'user', [ 'user_newpassword' => $oldHash ], [ 'user_name' => $cuser ] );
425 $provider = $this->getProvider();
428 $loginReq = new PasswordAuthenticationRequest();
429 $loginReq->action
= AuthManager
::ACTION_CHANGE
;
430 $loginReq->username
= $user;
431 $loginReq->password
= $oldpass;
432 $loginReqs = [ PasswordAuthenticationRequest
::class => $loginReq ];
434 AuthenticationResponse
::newPass( $cuser ),
435 $provider->beginPrimaryAuthentication( $loginReqs ),
439 if ( $type === PasswordAuthenticationRequest
::class ||
440 $type === TemporaryPasswordAuthenticationRequest
::class
442 $changeReq = new $type();
444 $changeReq = $this->getMock( $type );
446 $changeReq->action
= AuthManager
::ACTION_CHANGE
;
447 $changeReq->username
= $user;
448 $changeReq->password
= $newpass;
449 $resetMailer = $this->hookMailer();
450 $provider->providerChangeAuthenticationData( $changeReq );
451 \ScopedCallback
::consume( $resetMailer );
453 $loginReq->password
= $oldpass;
454 $ret = $provider->beginPrimaryAuthentication( $loginReqs );
456 AuthenticationResponse
::FAIL
,
458 'old password should fail'
462 $ret->message
->getKey(),
463 'old password should fail'
466 $loginReq->password
= $newpass;
467 $ret = $provider->beginPrimaryAuthentication( $loginReqs );
470 AuthenticationResponse
::newPass( $cuser ),
472 'new password should pass'
474 $this->assertNotNull(
475 $dbw->selectField( 'user', 'user_newpass_time', [ 'user_name' => $cuser ] )
479 AuthenticationResponse
::FAIL
,
481 'new password should fail'
485 $ret->message
->getKey(),
486 'new password should fail'
489 $dbw->selectField( 'user', 'user_newpass_time', [ 'user_name' => $cuser ] )
494 public static function provideProviderChangeAuthenticationData() {
496 [ 'UTSysop', AuthenticationRequest
::class, false ],
497 [ 'UTSysop', PasswordAuthenticationRequest
::class, false ],
498 [ 'UTSysop', TemporaryPasswordAuthenticationRequest
::class, true ],
502 public function testProviderChangeAuthenticationDataEmail() {
503 $dbw = wfGetDB( DB_MASTER
);
506 [ 'user_newpass_time' => $dbw->timestamp( time() - 5 * 3600 ) ],
507 [ 'user_name' => 'UTSysop' ]
510 $user = \User
::newFromName( 'UTSysop' );
511 $reset = new \
ScopedCallback( function ( $email ) use ( $user ) {
512 $user->setEmail( $email );
513 $user->saveSettings();
514 }, [ $user->getEmail() ] );
516 $user->setEmail( 'test@localhost.localdomain' );
517 $user->saveSettings();
519 $req = TemporaryPasswordAuthenticationRequest
::newRandom();
520 $req->username
= $user->getName();
521 $req->mailpassword
= true;
523 $provider = $this->getProvider( [ 'emailEnabled' => false ] );
524 $status = $provider->providerAllowsAuthenticationDataChange( $req, true );
525 $this->assertEquals( \StatusValue
::newFatal( 'passwordreset-emaildisabled' ), $status );
526 $req->hasBackchannel
= true;
527 $status = $provider->providerAllowsAuthenticationDataChange( $req, true );
528 $this->assertFalse( $status->hasMessage( 'passwordreset-emaildisabled' ) );
529 $req->hasBackchannel
= false;
531 $provider = $this->getProvider( [ 'passwordReminderResendTime' => 10 ] );
532 $status = $provider->providerAllowsAuthenticationDataChange( $req, true );
533 $this->assertEquals( \StatusValue
::newFatal( 'throttled-mailpassword', 10 ), $status );
535 $provider = $this->getProvider( [ 'passwordReminderResendTime' => 3 ] );
536 $status = $provider->providerAllowsAuthenticationDataChange( $req, true );
537 $this->assertFalse( $status->hasMessage( 'throttled-mailpassword' ) );
541 [ 'user_newpass_time' => $dbw->timestamp( time() +
5 * 3600 ) ],
542 [ 'user_name' => 'UTSysop' ]
544 $provider = $this->getProvider( [ 'passwordReminderResendTime' => 0 ] );
545 $status = $provider->providerAllowsAuthenticationDataChange( $req, true );
546 $this->assertFalse( $status->hasMessage( 'throttled-mailpassword' ) );
549 $status = $provider->providerAllowsAuthenticationDataChange( $req, true );
550 $this->assertEquals( \StatusValue
::newFatal( 'passwordreset-nocaller' ), $status );
552 $req->caller
= '127.0.0.256';
553 $status = $provider->providerAllowsAuthenticationDataChange( $req, true );
554 $this->assertEquals( \StatusValue
::newFatal( 'passwordreset-nosuchcaller', '127.0.0.256' ),
557 $req->caller
= '<Invalid>';
558 $status = $provider->providerAllowsAuthenticationDataChange( $req, true );
559 $this->assertEquals( \StatusValue
::newFatal( 'passwordreset-nosuchcaller', '<Invalid>' ),
562 $req->caller
= '127.0.0.1';
563 $status = $provider->providerAllowsAuthenticationDataChange( $req, true );
564 $this->assertEquals( \StatusValue
::newGood(), $status );
566 $req->caller
= 'UTSysop';
567 $status = $provider->providerAllowsAuthenticationDataChange( $req, true );
568 $this->assertEquals( \StatusValue
::newGood(), $status );
571 $resetMailer = $this->hookMailer( function ( $headers, $to, $from, $subject, $body )
572 use ( &$mailed, $req )
575 $this->assertSame( 'test@localhost.localdomain', $to[0]->address
);
576 $this->assertContains( $req->password
, $body );
579 $provider->providerChangeAuthenticationData( $req );
580 \ScopedCallback
::consume( $resetMailer );
581 $this->assertTrue( $mailed );
583 $priv = \TestingAccessWrapper
::newFromObject( $provider );
584 $req->username
= '<invalid>';
585 $status = $priv->sendPasswordResetEmail( $req );
586 $this->assertEquals( \Status
::newFatal( 'noname' ), $status );
589 public function testTestForAccountCreation() {
590 $user = \User
::newFromName( 'foo' );
591 $req = new TemporaryPasswordAuthenticationRequest();
592 $req->username
= 'Foo';
593 $req->password
= 'Bar';
594 $reqs = [ TemporaryPasswordAuthenticationRequest
::class => $req ];
596 $provider = $this->getProvider();
598 \StatusValue
::newGood(),
599 $provider->testForAccountCreation( $user, $user, [] ),
600 'No password request'
604 \StatusValue
::newGood(),
605 $provider->testForAccountCreation( $user, $user, $reqs ),
606 'Password request, validated'
609 $this->validity
->error( 'arbitrary warning' );
610 $expect = \StatusValue
::newGood();
611 $expect->error( 'arbitrary warning' );
614 $provider->testForAccountCreation( $user, $user, $reqs ),
615 'Password request, not validated'
619 public function testAccountCreation() {
620 $resetMailer = $this->hookMailer();
622 $user = \User
::newFromName( 'Foo' );
624 $req = new TemporaryPasswordAuthenticationRequest();
625 $reqs = [ TemporaryPasswordAuthenticationRequest
::class => $req ];
627 $authreq = new PasswordAuthenticationRequest();
628 $authreq->action
= AuthManager
::ACTION_CREATE
;
629 $authreqs = [ PasswordAuthenticationRequest
::class => $authreq ];
631 $provider = $this->getProvider();
634 AuthenticationResponse
::newAbstain(),
635 $provider->beginPrimaryAccountCreation( $user, $user, [] )
638 $req->username
= 'foo';
639 $req->password
= null;
641 AuthenticationResponse
::newAbstain(),
642 $provider->beginPrimaryAccountCreation( $user, $user, $reqs )
645 $req->username
= null;
646 $req->password
= 'bar';
648 AuthenticationResponse
::newAbstain(),
649 $provider->beginPrimaryAccountCreation( $user, $user, $reqs )
652 $req->username
= 'foo';
653 $req->password
= 'bar';
655 $expect = AuthenticationResponse
::newPass( 'Foo' );
656 $expect->createRequest
= clone( $req );
657 $expect->createRequest
->username
= 'Foo';
658 $this->assertEquals( $expect, $provider->beginPrimaryAccountCreation( $user, $user, $reqs ) );
659 $this->assertNull( $this->manager
->getAuthenticationSessionData( 'no-email' ) );
661 // We have to cheat a bit to avoid having to add a new user to
662 // the database to test the actual setting of the password works right
663 $user = \User
::newFromName( 'UTSysop' );
664 $req->username
= $authreq->username
= $user->getName();
665 $req->password
= $authreq->password
= 'NewPassword';
666 $expect = AuthenticationResponse
::newPass( 'UTSysop' );
667 $expect->createRequest
= $req;
669 $res2 = $provider->beginPrimaryAccountCreation( $user, $user, $reqs );
670 $this->assertEquals( $expect, $res2, 'Sanity check' );
672 $ret = $provider->beginPrimaryAuthentication( $authreqs );
673 $this->assertEquals( AuthenticationResponse
::FAIL
, $ret->status
, 'sanity check' );
675 $this->assertSame( null, $provider->finishAccountCreation( $user, $user, $res2 ) );
677 $ret = $provider->beginPrimaryAuthentication( $authreqs );
678 $this->assertEquals( AuthenticationResponse
::PASS
, $ret->status
, 'new password is set' );
681 public function testAccountCreationEmail() {
682 $creator = \User
::newFromName( 'Foo' );
683 $user = \User
::newFromName( 'UTSysop' );
684 $reset = new \
ScopedCallback( function ( $email ) use ( $user ) {
685 $user->setEmail( $email );
686 $user->saveSettings();
687 }, [ $user->getEmail() ] );
689 $user->setEmail( null );
691 $req = TemporaryPasswordAuthenticationRequest
::newRandom();
692 $req->username
= $user->getName();
693 $req->mailpassword
= true;
695 $provider = $this->getProvider( [ 'emailEnabled' => false ] );
696 $status = $provider->testForAccountCreation( $user, $creator, [ $req ] );
697 $this->assertEquals( \StatusValue
::newFatal( 'emaildisabled' ), $status );
698 $req->hasBackchannel
= true;
699 $status = $provider->testForAccountCreation( $user, $creator, [ $req ] );
700 $this->assertFalse( $status->hasMessage( 'emaildisabled' ) );
701 $req->hasBackchannel
= false;
703 $provider = $this->getProvider( [ 'emailEnabled' => true ] );
704 $status = $provider->testForAccountCreation( $user, $creator, [ $req ] );
705 $this->assertEquals( \StatusValue
::newFatal( 'noemailcreate' ), $status );
706 $req->hasBackchannel
= true;
707 $status = $provider->testForAccountCreation( $user, $creator, [ $req ] );
708 $this->assertFalse( $status->hasMessage( 'noemailcreate' ) );
709 $req->hasBackchannel
= false;
711 $user->setEmail( 'test@localhost.localdomain' );
712 $status = $provider->testForAccountCreation( $user, $creator, [ $req ] );
713 $this->assertEquals( \StatusValue
::newGood(), $status );
716 $resetMailer = $this->hookMailer( function ( $headers, $to, $from, $subject, $body )
717 use ( &$mailed, $req )
720 $this->assertSame( 'test@localhost.localdomain', $to[0]->address
);
721 $this->assertContains( $req->password
, $body );
725 $expect = AuthenticationResponse
::newPass( 'UTSysop' );
726 $expect->createRequest
= clone( $req );
727 $expect->createRequest
->username
= 'UTSysop';
728 $res = $provider->beginPrimaryAccountCreation( $user, $creator, [ $req ] );
729 $this->assertEquals( $expect, $res );
730 $this->assertTrue( $this->manager
->getAuthenticationSessionData( 'no-email' ) );
731 $this->assertFalse( $mailed );
733 $this->assertSame( 'byemail', $provider->finishAccountCreation( $user, $creator, $res ) );
734 $this->assertTrue( $mailed );
736 \ScopedCallback
::consume( $resetMailer );
737 $this->assertTrue( $mailed );