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;
16 protected function setUp() {
17 global $wgDisableAuthManager;
20 if ( $wgDisableAuthManager ) {
21 $this->markTestSkipped( '$wgDisableAuthManager is set' );
26 * Get an instance of the provider
28 * $provider->checkPasswordValidity is mocked to return $this->validity,
29 * because we don't need to test that here.
31 * @param array $params
32 * @return TemporaryPasswordPrimaryAuthenticationProvider
34 protected function getProvider( $params = [] ) {
35 if ( !$this->config
) {
36 $this->config
= new \
HashConfig( [
37 'EmailEnabled' => true,
40 $config = new \
MultiConfig( [
42 \ConfigFactory
::getDefaultInstance()->makeConfig( 'main' )
45 if ( !$this->manager
) {
46 $this->manager
= new AuthManager( new \
FauxRequest(), $config );
48 $this->validity
= \Status
::newGood();
50 $mockedMethods[] = 'checkPasswordValidity';
51 $provider = $this->getMock(
52 TemporaryPasswordPrimaryAuthenticationProvider
::class,
56 $provider->expects( $this->any() )->method( 'checkPasswordValidity' )
57 ->will( $this->returnCallback( function () {
58 return $this->validity
;
60 $provider->setConfig( $config );
61 $provider->setLogger( new \Psr\Log\
NullLogger() );
62 $provider->setManager( $this->manager
);
67 protected function hookMailer( $func = null ) {
68 \Hooks
::clear( 'AlternateUserMailer' );
70 \Hooks
::register( 'AlternateUserMailer', $func );
72 \Hooks
::register( 'AlternateUserMailer', function () {
76 \Hooks
::register( 'AlternateUserMailer', function () {
77 $this->fail( 'AlternateUserMailer hook called unexpectedly' );
82 return new \
ScopedCallback( function () {
83 \Hooks
::clear( 'AlternateUserMailer' );
84 \Hooks
::register( 'AlternateUserMailer', function () {
90 public function testBasics() {
91 $provider = new TemporaryPasswordPrimaryAuthenticationProvider();
94 PrimaryAuthenticationProvider
::TYPE_CREATE
,
95 $provider->accountCreationType()
98 $this->assertTrue( $provider->testUserExists( 'UTSysop' ) );
99 $this->assertTrue( $provider->testUserExists( 'uTSysop' ) );
100 $this->assertFalse( $provider->testUserExists( 'DoesNotExist' ) );
101 $this->assertFalse( $provider->testUserExists( '<invalid>' ) );
103 $req = new PasswordAuthenticationRequest
;
104 $req->action
= AuthManager
::ACTION_CHANGE
;
105 $req->username
= '<invalid>';
106 $provider->providerChangeAuthenticationData( $req );
109 public function testConfig() {
110 $config = new \
HashConfig( [
111 'EnableEmail' => false,
112 'NewPasswordExpiry' => 100,
113 'PasswordReminderResendTime' => 101,
116 $p = \TestingAccessWrapper
::newFromObject( new TemporaryPasswordPrimaryAuthenticationProvider() );
117 $p->setConfig( $config );
118 $this->assertSame( false, $p->emailEnabled
);
119 $this->assertSame( 100, $p->newPasswordExpiry
);
120 $this->assertSame( 101, $p->passwordReminderResendTime
);
122 $p = \TestingAccessWrapper
::newFromObject( new TemporaryPasswordPrimaryAuthenticationProvider( [
123 'emailEnabled' => true,
124 'newPasswordExpiry' => 42,
125 'passwordReminderResendTime' => 43,
127 $p->setConfig( $config );
128 $this->assertSame( true, $p->emailEnabled
);
129 $this->assertSame( 42, $p->newPasswordExpiry
);
130 $this->assertSame( 43, $p->passwordReminderResendTime
);
133 public function testTestUserCanAuthenticate() {
134 $dbw = wfGetDB( DB_MASTER
);
136 $passwordFactory = new \
PasswordFactory();
137 $passwordFactory->init( \RequestContext
::getMain()->getConfig() );
138 // A is unsalted MD5 (thus fast) ... we don't care about security here, this is test only
139 $passwordFactory->setDefaultType( 'A' );
140 $pwhash = $passwordFactory->newFromPlaintext( 'password' )->toString();
142 $provider = $this->getProvider();
143 $providerPriv = \TestingAccessWrapper
::newFromObject( $provider );
145 $this->assertFalse( $provider->testUserCanAuthenticate( '<invalid>' ) );
146 $this->assertFalse( $provider->testUserCanAuthenticate( 'DoesNotExist' ) );
151 'user_newpassword' => \PasswordFactory
::newInvalidPassword()->toString(),
152 'user_newpass_time' => null,
154 [ 'user_name' => 'UTSysop' ]
156 $this->assertFalse( $provider->testUserCanAuthenticate( 'UTSysop' ) );
161 'user_newpassword' => $pwhash,
162 'user_newpass_time' => null,
164 [ 'user_name' => 'UTSysop' ]
166 $this->assertTrue( $provider->testUserCanAuthenticate( 'UTSysop' ) );
167 $this->assertTrue( $provider->testUserCanAuthenticate( 'uTSysop' ) );
172 'user_newpassword' => $pwhash,
173 'user_newpass_time' => $dbw->timestamp( time() - 10 ),
175 [ 'user_name' => 'UTSysop' ]
177 $providerPriv->newPasswordExpiry
= 100;
178 $this->assertTrue( $provider->testUserCanAuthenticate( 'UTSysop' ) );
179 $providerPriv->newPasswordExpiry
= 1;
180 $this->assertFalse( $provider->testUserCanAuthenticate( 'UTSysop' ) );
185 'user_newpassword' => \PasswordFactory
::newInvalidPassword()->toString(),
186 'user_newpass_time' => null,
188 [ 'user_name' => 'UTSysop' ]
193 * @dataProvider provideGetAuthenticationRequests
194 * @param string $action
195 * @param array $options
196 * @param array $expected
198 public function testGetAuthenticationRequests( $action, $options, $expected ) {
199 $actual = $this->getProvider()->getAuthenticationRequests( $action, $options );
200 foreach ( $actual as $req ) {
201 if ( $req instanceof TemporaryPasswordAuthenticationRequest
&& $req->password
!== null ) {
202 $req->password
= 'random';
205 $this->assertEquals( $expected, $actual );
208 public static function provideGetAuthenticationRequests() {
209 $anon = [ 'username' => null ];
210 $loggedIn = [ 'username' => 'UTSysop' ];
213 [ AuthManager
::ACTION_LOGIN
, $anon, [
214 new PasswordAuthenticationRequest
216 [ AuthManager
::ACTION_LOGIN
, $loggedIn, [
217 new PasswordAuthenticationRequest
219 [ AuthManager
::ACTION_CREATE
, $anon, [] ],
220 [ AuthManager
::ACTION_CREATE
, $loggedIn, [
221 new TemporaryPasswordAuthenticationRequest( 'random' )
223 [ AuthManager
::ACTION_LINK
, $anon, [] ],
224 [ AuthManager
::ACTION_LINK
, $loggedIn, [] ],
225 [ AuthManager
::ACTION_CHANGE
, $anon, [
226 new TemporaryPasswordAuthenticationRequest( 'random' )
228 [ AuthManager
::ACTION_CHANGE
, $loggedIn, [
229 new TemporaryPasswordAuthenticationRequest( 'random' )
231 [ AuthManager
::ACTION_REMOVE
, $anon, [
232 new TemporaryPasswordAuthenticationRequest
234 [ AuthManager
::ACTION_REMOVE
, $loggedIn, [
235 new TemporaryPasswordAuthenticationRequest
240 public function testAuthentication() {
241 $password = 'TemporaryPassword';
242 $hash = ':A:' . md5( $password );
243 $dbw = wfGetDB( DB_MASTER
);
246 [ 'user_newpassword' => $hash, 'user_newpass_time' => $dbw->timestamp( time() - 10 ) ],
247 [ 'user_name' => 'UTSysop' ]
250 $req = new PasswordAuthenticationRequest();
251 $req->action
= AuthManager
::ACTION_LOGIN
;
252 $reqs = [ PasswordAuthenticationRequest
::class => $req ];
254 $provider = $this->getProvider();
255 $providerPriv = \TestingAccessWrapper
::newFromObject( $provider );
257 $providerPriv->newPasswordExpiry
= 100;
261 AuthenticationResponse
::newAbstain(),
262 $provider->beginPrimaryAuthentication( [] )
265 $req->username
= 'foo';
266 $req->password
= null;
268 AuthenticationResponse
::newAbstain(),
269 $provider->beginPrimaryAuthentication( $reqs )
272 $req->username
= null;
273 $req->password
= 'bar';
275 AuthenticationResponse
::newAbstain(),
276 $provider->beginPrimaryAuthentication( $reqs )
279 $req->username
= '<invalid>';
280 $req->password
= 'WhoCares';
281 $ret = $provider->beginPrimaryAuthentication( $reqs );
283 AuthenticationResponse
::newAbstain(),
284 $provider->beginPrimaryAuthentication( $reqs )
287 $req->username
= 'DoesNotExist';
288 $req->password
= 'DoesNotExist';
289 $ret = $provider->beginPrimaryAuthentication( $reqs );
291 AuthenticationResponse
::newAbstain(),
292 $provider->beginPrimaryAuthentication( $reqs )
295 // Validation failure
296 $req->username
= 'UTSysop';
297 $req->password
= $password;
298 $this->validity
= \Status
::newFatal( 'arbitrary-failure' );
299 $ret = $provider->beginPrimaryAuthentication( $reqs );
301 AuthenticationResponse
::FAIL
,
306 $ret->message
->getKey()
310 $this->manager
->removeAuthenticationSessionData( null );
311 $this->validity
= \Status
::newGood();
313 AuthenticationResponse
::newPass( 'UTSysop' ),
314 $provider->beginPrimaryAuthentication( $reqs )
316 $this->assertNotNull( $this->manager
->getAuthenticationSessionData( 'reset-pass' ) );
318 $this->manager
->removeAuthenticationSessionData( null );
319 $this->validity
= \Status
::newGood();
320 $req->username
= 'uTSysop';
322 AuthenticationResponse
::newPass( 'UTSysop' ),
323 $provider->beginPrimaryAuthentication( $reqs )
325 $this->assertNotNull( $this->manager
->getAuthenticationSessionData( 'reset-pass' ) );
326 $req->username
= 'UTSysop';
329 $providerPriv->newPasswordExpiry
= 1;
330 $ret = $provider->beginPrimaryAuthentication( $reqs );
332 AuthenticationResponse
::FAIL
,
337 $ret->message
->getKey()
341 $providerPriv->newPasswordExpiry
= 100;
342 $this->validity
= \Status
::newGood();
343 $req->password
= 'Wrong';
344 $ret = $provider->beginPrimaryAuthentication( $reqs );
346 AuthenticationResponse
::FAIL
,
351 $ret->message
->getKey()
357 * @dataProvider provideProviderAllowsAuthenticationDataChange
358 * @param string $type
359 * @param string $user
360 * @param \Status $validity Result of the password validity check
361 * @param \StatusValue $expect1 Expected result with $checkData = false
362 * @param \StatusValue $expect2 Expected result with $checkData = true
364 public function testProviderAllowsAuthenticationDataChange( $type, $user, \Status
$validity,
365 \StatusValue
$expect1, \StatusValue
$expect2
367 if ( $type === PasswordAuthenticationRequest
::class ||
368 $type === TemporaryPasswordAuthenticationRequest
::class
372 $req = $this->getMock( $type );
374 $req->action
= AuthManager
::ACTION_CHANGE
;
375 $req->username
= $user;
376 $req->password
= 'NewPassword';
378 $provider = $this->getProvider();
379 $this->validity
= $validity;
380 $this->assertEquals( $expect1, $provider->providerAllowsAuthenticationDataChange( $req, false ) );
381 $this->assertEquals( $expect2, $provider->providerAllowsAuthenticationDataChange( $req, true ) );
384 public static function provideProviderAllowsAuthenticationDataChange() {
385 $err = \StatusValue
::newGood();
386 $err->error( 'arbitrary-warning' );
389 [ AuthenticationRequest
::class, 'UTSysop', \Status
::newGood(),
390 \StatusValue
::newGood( 'ignored' ), \StatusValue
::newGood( 'ignored' ) ],
391 [ PasswordAuthenticationRequest
::class, 'UTSysop', \Status
::newGood(),
392 \StatusValue
::newGood( 'ignored' ), \StatusValue
::newGood( 'ignored' ) ],
393 [ TemporaryPasswordAuthenticationRequest
::class, 'UTSysop', \Status
::newGood(),
394 \StatusValue
::newGood(), \StatusValue
::newGood() ],
395 [ TemporaryPasswordAuthenticationRequest
::class, 'uTSysop', \Status
::newGood(),
396 \StatusValue
::newGood(), \StatusValue
::newGood() ],
397 [ TemporaryPasswordAuthenticationRequest
::class, 'UTSysop', \Status
::wrap( $err ),
398 \StatusValue
::newGood(), $err ],
399 [ TemporaryPasswordAuthenticationRequest
::class, 'UTSysop',
400 \Status
::newFatal( 'arbitrary-error' ), \StatusValue
::newGood(),
401 \StatusValue
::newFatal( 'arbitrary-error' ) ],
402 [ TemporaryPasswordAuthenticationRequest
::class, 'DoesNotExist', \Status
::newGood(),
403 \StatusValue
::newGood(), \StatusValue
::newGood( 'ignored' ) ],
404 [ TemporaryPasswordAuthenticationRequest
::class, '<invalid>', \Status
::newGood(),
405 \StatusValue
::newGood(), \StatusValue
::newGood( 'ignored' ) ],
410 * @dataProvider provideProviderChangeAuthenticationData
411 * @param string $user
412 * @param string $type
413 * @param bool $changed
415 public function testProviderChangeAuthenticationData( $user, $type, $changed ) {
416 $cuser = ucfirst( $user );
417 $oldpass = 'OldTempPassword';
418 $newpass = 'NewTempPassword';
420 $hash = ':A:' . md5( $oldpass );
421 $dbw = wfGetDB( DB_MASTER
);
424 [ 'user_newpassword' => $hash, 'user_newpass_time' => $dbw->timestamp( time() +
10 ) ],
425 [ 'user_name' => 'UTSysop' ]
428 $dbw = wfGetDB( DB_MASTER
);
429 $oldHash = $dbw->selectField( 'user', 'user_newpassword', [ 'user_name' => $cuser ] );
430 $cb = new \
ScopedCallback( function () use ( $dbw, $cuser, $oldHash ) {
431 $dbw->update( 'user', [ 'user_newpassword' => $oldHash ], [ 'user_name' => $cuser ] );
434 $provider = $this->getProvider();
437 $loginReq = new PasswordAuthenticationRequest();
438 $loginReq->action
= AuthManager
::ACTION_CHANGE
;
439 $loginReq->username
= $user;
440 $loginReq->password
= $oldpass;
441 $loginReqs = [ PasswordAuthenticationRequest
::class => $loginReq ];
443 AuthenticationResponse
::newPass( $cuser ),
444 $provider->beginPrimaryAuthentication( $loginReqs ),
448 if ( $type === PasswordAuthenticationRequest
::class ||
449 $type === TemporaryPasswordAuthenticationRequest
::class
451 $changeReq = new $type();
453 $changeReq = $this->getMock( $type );
455 $changeReq->action
= AuthManager
::ACTION_CHANGE
;
456 $changeReq->username
= $user;
457 $changeReq->password
= $newpass;
458 $resetMailer = $this->hookMailer();
459 $provider->providerChangeAuthenticationData( $changeReq );
460 \ScopedCallback
::consume( $resetMailer );
462 $loginReq->password
= $oldpass;
463 $ret = $provider->beginPrimaryAuthentication( $loginReqs );
465 AuthenticationResponse
::FAIL
,
467 'old password should fail'
471 $ret->message
->getKey(),
472 'old password should fail'
475 $loginReq->password
= $newpass;
476 $ret = $provider->beginPrimaryAuthentication( $loginReqs );
479 AuthenticationResponse
::newPass( $cuser ),
481 'new password should pass'
483 $this->assertNotNull(
484 $dbw->selectField( 'user', 'user_newpass_time', [ 'user_name' => $cuser ] )
488 AuthenticationResponse
::FAIL
,
490 'new password should fail'
494 $ret->message
->getKey(),
495 'new password should fail'
498 $dbw->selectField( 'user', 'user_newpass_time', [ 'user_name' => $cuser ] )
503 public static function provideProviderChangeAuthenticationData() {
505 [ 'UTSysop', AuthenticationRequest
::class, false ],
506 [ 'UTSysop', PasswordAuthenticationRequest
::class, false ],
507 [ 'UTSysop', TemporaryPasswordAuthenticationRequest
::class, true ],
511 public function testProviderChangeAuthenticationDataEmail() {
512 $dbw = wfGetDB( DB_MASTER
);
515 [ 'user_newpass_time' => $dbw->timestamp( time() - 5 * 3600 ) ],
516 [ 'user_name' => 'UTSysop' ]
519 $user = \User
::newFromName( 'UTSysop' );
520 $reset = new \
ScopedCallback( function ( $email ) use ( $user ) {
521 $user->setEmail( $email );
522 $user->saveSettings();
523 }, [ $user->getEmail() ] );
525 $user->setEmail( 'test@localhost.localdomain' );
526 $user->saveSettings();
528 $req = TemporaryPasswordAuthenticationRequest
::newRandom();
529 $req->username
= $user->getName();
530 $req->mailpassword
= true;
532 $provider = $this->getProvider( [ 'emailEnabled' => false ] );
533 $status = $provider->providerAllowsAuthenticationDataChange( $req, true );
534 $this->assertEquals( \StatusValue
::newFatal( 'passwordreset-emaildisabled' ), $status );
535 $req->hasBackchannel
= true;
536 $status = $provider->providerAllowsAuthenticationDataChange( $req, true );
537 $this->assertFalse( $status->hasMessage( 'passwordreset-emaildisabled' ) );
538 $req->hasBackchannel
= false;
540 $provider = $this->getProvider( [ 'passwordReminderResendTime' => 10 ] );
541 $status = $provider->providerAllowsAuthenticationDataChange( $req, true );
542 $this->assertEquals( \StatusValue
::newFatal( 'throttled-mailpassword', 10 ), $status );
544 $provider = $this->getProvider( [ 'passwordReminderResendTime' => 3 ] );
545 $status = $provider->providerAllowsAuthenticationDataChange( $req, true );
546 $this->assertFalse( $status->hasMessage( 'throttled-mailpassword' ) );
550 [ 'user_newpass_time' => $dbw->timestamp( time() +
5 * 3600 ) ],
551 [ 'user_name' => 'UTSysop' ]
553 $provider = $this->getProvider( [ 'passwordReminderResendTime' => 0 ] );
554 $status = $provider->providerAllowsAuthenticationDataChange( $req, true );
555 $this->assertFalse( $status->hasMessage( 'throttled-mailpassword' ) );
558 $status = $provider->providerAllowsAuthenticationDataChange( $req, true );
559 $this->assertEquals( \StatusValue
::newFatal( 'passwordreset-nocaller' ), $status );
561 $req->caller
= '127.0.0.256';
562 $status = $provider->providerAllowsAuthenticationDataChange( $req, true );
563 $this->assertEquals( \StatusValue
::newFatal( 'passwordreset-nosuchcaller', '127.0.0.256' ),
566 $req->caller
= '<Invalid>';
567 $status = $provider->providerAllowsAuthenticationDataChange( $req, true );
568 $this->assertEquals( \StatusValue
::newFatal( 'passwordreset-nosuchcaller', '<Invalid>' ),
571 $req->caller
= '127.0.0.1';
572 $status = $provider->providerAllowsAuthenticationDataChange( $req, true );
573 $this->assertEquals( \StatusValue
::newGood(), $status );
575 $req->caller
= 'UTSysop';
576 $status = $provider->providerAllowsAuthenticationDataChange( $req, true );
577 $this->assertEquals( \StatusValue
::newGood(), $status );
580 $resetMailer = $this->hookMailer( function ( $headers, $to, $from, $subject, $body )
581 use ( &$mailed, $req )
584 $this->assertSame( 'test@localhost.localdomain', $to[0]->address
);
585 $this->assertContains( $req->password
, $body );
588 $provider->providerChangeAuthenticationData( $req );
589 \ScopedCallback
::consume( $resetMailer );
590 $this->assertTrue( $mailed );
592 $priv = \TestingAccessWrapper
::newFromObject( $provider );
593 $req->username
= '<invalid>';
594 $status = $priv->sendPasswordResetEmail( $req );
595 $this->assertEquals( \Status
::newFatal( 'noname' ), $status );
598 public function testTestForAccountCreation() {
599 $user = \User
::newFromName( 'foo' );
600 $req = new TemporaryPasswordAuthenticationRequest();
601 $req->username
= 'Foo';
602 $req->password
= 'Bar';
603 $reqs = [ TemporaryPasswordAuthenticationRequest
::class => $req ];
605 $provider = $this->getProvider();
607 \StatusValue
::newGood(),
608 $provider->testForAccountCreation( $user, $user, [] ),
609 'No password request'
613 \StatusValue
::newGood(),
614 $provider->testForAccountCreation( $user, $user, $reqs ),
615 'Password request, validated'
618 $this->validity
->error( 'arbitrary warning' );
619 $expect = \StatusValue
::newGood();
620 $expect->error( 'arbitrary warning' );
623 $provider->testForAccountCreation( $user, $user, $reqs ),
624 'Password request, not validated'
628 public function testAccountCreation() {
629 $resetMailer = $this->hookMailer();
631 $user = \User
::newFromName( 'Foo' );
633 $req = new TemporaryPasswordAuthenticationRequest();
634 $reqs = [ TemporaryPasswordAuthenticationRequest
::class => $req ];
636 $authreq = new PasswordAuthenticationRequest();
637 $authreq->action
= AuthManager
::ACTION_CREATE
;
638 $authreqs = [ PasswordAuthenticationRequest
::class => $authreq ];
640 $provider = $this->getProvider();
643 AuthenticationResponse
::newAbstain(),
644 $provider->beginPrimaryAccountCreation( $user, $user, [] )
647 $req->username
= 'foo';
648 $req->password
= null;
650 AuthenticationResponse
::newAbstain(),
651 $provider->beginPrimaryAccountCreation( $user, $user, $reqs )
654 $req->username
= null;
655 $req->password
= 'bar';
657 AuthenticationResponse
::newAbstain(),
658 $provider->beginPrimaryAccountCreation( $user, $user, $reqs )
661 $req->username
= 'foo';
662 $req->password
= 'bar';
664 $expect = AuthenticationResponse
::newPass( 'Foo' );
665 $expect->createRequest
= clone( $req );
666 $expect->createRequest
->username
= 'Foo';
667 $this->assertEquals( $expect, $provider->beginPrimaryAccountCreation( $user, $user, $reqs ) );
668 $this->assertNull( $this->manager
->getAuthenticationSessionData( 'no-email' ) );
670 // We have to cheat a bit to avoid having to add a new user to
671 // the database to test the actual setting of the password works right
672 $user = \User
::newFromName( 'UTSysop' );
673 $req->username
= $authreq->username
= $user->getName();
674 $req->password
= $authreq->password
= 'NewPassword';
675 $expect = AuthenticationResponse
::newPass( 'UTSysop' );
676 $expect->createRequest
= $req;
678 $res2 = $provider->beginPrimaryAccountCreation( $user, $user, $reqs );
679 $this->assertEquals( $expect, $res2, 'Sanity check' );
681 $ret = $provider->beginPrimaryAuthentication( $authreqs );
682 $this->assertEquals( AuthenticationResponse
::FAIL
, $ret->status
, 'sanity check' );
684 $this->assertSame( null, $provider->finishAccountCreation( $user, $user, $res2 ) );
686 $ret = $provider->beginPrimaryAuthentication( $authreqs );
687 $this->assertEquals( AuthenticationResponse
::PASS
, $ret->status
, 'new password is set' );
690 public function testAccountCreationEmail() {
691 $creator = \User
::newFromName( 'Foo' );
692 $user = \User
::newFromName( 'UTSysop' );
693 $reset = new \
ScopedCallback( function ( $email ) use ( $user ) {
694 $user->setEmail( $email );
695 $user->saveSettings();
696 }, [ $user->getEmail() ] );
698 $user->setEmail( null );
700 $req = TemporaryPasswordAuthenticationRequest
::newRandom();
701 $req->username
= $user->getName();
702 $req->mailpassword
= true;
704 $provider = $this->getProvider( [ 'emailEnabled' => false ] );
705 $status = $provider->testForAccountCreation( $user, $creator, [ $req ] );
706 $this->assertEquals( \StatusValue
::newFatal( 'emaildisabled' ), $status );
707 $req->hasBackchannel
= true;
708 $status = $provider->testForAccountCreation( $user, $creator, [ $req ] );
709 $this->assertFalse( $status->hasMessage( 'emaildisabled' ) );
710 $req->hasBackchannel
= false;
712 $provider = $this->getProvider( [ 'emailEnabled' => true ] );
713 $status = $provider->testForAccountCreation( $user, $creator, [ $req ] );
714 $this->assertEquals( \StatusValue
::newFatal( 'noemailcreate' ), $status );
715 $req->hasBackchannel
= true;
716 $status = $provider->testForAccountCreation( $user, $creator, [ $req ] );
717 $this->assertFalse( $status->hasMessage( 'noemailcreate' ) );
718 $req->hasBackchannel
= false;
720 $user->setEmail( 'test@localhost.localdomain' );
721 $status = $provider->testForAccountCreation( $user, $creator, [ $req ] );
722 $this->assertEquals( \StatusValue
::newGood(), $status );
725 $resetMailer = $this->hookMailer( function ( $headers, $to, $from, $subject, $body )
726 use ( &$mailed, $req )
729 $this->assertSame( 'test@localhost.localdomain', $to[0]->address
);
730 $this->assertContains( $req->password
, $body );
734 $expect = AuthenticationResponse
::newPass( 'UTSysop' );
735 $expect->createRequest
= clone( $req );
736 $expect->createRequest
->username
= 'UTSysop';
737 $res = $provider->beginPrimaryAccountCreation( $user, $creator, [ $req ] );
738 $this->assertEquals( $expect, $res );
739 $this->assertTrue( $this->manager
->getAuthenticationSessionData( 'no-email' ) );
740 $this->assertFalse( $mailed );
742 $this->assertSame( 'byemail', $provider->finishAccountCreation( $user, $creator, $res ) );
743 $this->assertTrue( $mailed );
745 \ScopedCallback
::consume( $resetMailer );
746 $this->assertTrue( $mailed );