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 $user = self
::getMutableTestUser()->getUser();
127 $dbw = wfGetDB( DB_MASTER
);
129 $passwordFactory = new \
PasswordFactory();
130 $passwordFactory->init( \RequestContext
::getMain()->getConfig() );
131 // A is unsalted MD5 (thus fast) ... we don't care about security here, this is test only
132 $passwordFactory->setDefaultType( 'A' );
133 $pwhash = $passwordFactory->newFromPlaintext( 'password' )->toString();
135 $provider = $this->getProvider();
136 $providerPriv = \TestingAccessWrapper
::newFromObject( $provider );
138 $this->assertFalse( $provider->testUserCanAuthenticate( '<invalid>' ) );
139 $this->assertFalse( $provider->testUserCanAuthenticate( 'DoesNotExist' ) );
144 'user_newpassword' => \PasswordFactory
::newInvalidPassword()->toString(),
145 'user_newpass_time' => null,
147 [ 'user_id' => $user->getId() ]
149 $this->assertFalse( $provider->testUserCanAuthenticate( $user->getName() ) );
154 'user_newpassword' => $pwhash,
155 'user_newpass_time' => null,
157 [ 'user_id' => $user->getId() ]
159 $this->assertTrue( $provider->testUserCanAuthenticate( $user->getName() ) );
160 $this->assertTrue( $provider->testUserCanAuthenticate( lcfirst( $user->getName() ) ) );
165 'user_newpassword' => $pwhash,
166 'user_newpass_time' => $dbw->timestamp( time() - 10 ),
168 [ 'user_id' => $user->getId() ]
170 $providerPriv->newPasswordExpiry
= 100;
171 $this->assertTrue( $provider->testUserCanAuthenticate( $user->getName() ) );
172 $providerPriv->newPasswordExpiry
= 1;
173 $this->assertFalse( $provider->testUserCanAuthenticate( $user->getName() ) );
178 'user_newpassword' => \PasswordFactory
::newInvalidPassword()->toString(),
179 'user_newpass_time' => null,
181 [ 'user_id' => $user->getId() ]
186 * @dataProvider provideGetAuthenticationRequests
187 * @param string $action
188 * @param array $options
189 * @param array $expected
191 public function testGetAuthenticationRequests( $action, $options, $expected ) {
192 $actual = $this->getProvider()->getAuthenticationRequests( $action, $options );
193 foreach ( $actual as $req ) {
194 if ( $req instanceof TemporaryPasswordAuthenticationRequest
&& $req->password
!== null ) {
195 $req->password
= 'random';
198 $this->assertEquals( $expected, $actual );
201 public static function provideGetAuthenticationRequests() {
202 $anon = [ 'username' => null ];
203 $loggedIn = [ 'username' => 'UTSysop' ];
206 [ AuthManager
::ACTION_LOGIN
, $anon, [
207 new PasswordAuthenticationRequest
209 [ AuthManager
::ACTION_LOGIN
, $loggedIn, [
210 new PasswordAuthenticationRequest
212 [ AuthManager
::ACTION_CREATE
, $anon, [] ],
213 [ AuthManager
::ACTION_CREATE
, $loggedIn, [
214 new TemporaryPasswordAuthenticationRequest( 'random' )
216 [ AuthManager
::ACTION_LINK
, $anon, [] ],
217 [ AuthManager
::ACTION_LINK
, $loggedIn, [] ],
218 [ AuthManager
::ACTION_CHANGE
, $anon, [
219 new TemporaryPasswordAuthenticationRequest( 'random' )
221 [ AuthManager
::ACTION_CHANGE
, $loggedIn, [
222 new TemporaryPasswordAuthenticationRequest( 'random' )
224 [ AuthManager
::ACTION_REMOVE
, $anon, [
225 new TemporaryPasswordAuthenticationRequest
227 [ AuthManager
::ACTION_REMOVE
, $loggedIn, [
228 new TemporaryPasswordAuthenticationRequest
233 public function testAuthentication() {
234 $user = self
::getMutableTestUser()->getUser();
236 $password = 'TemporaryPassword';
237 $hash = ':A:' . md5( $password );
238 $dbw = wfGetDB( DB_MASTER
);
241 [ 'user_newpassword' => $hash, 'user_newpass_time' => $dbw->timestamp( time() - 10 ) ],
242 [ 'user_id' => $user->getId() ]
245 $req = new PasswordAuthenticationRequest();
246 $req->action
= AuthManager
::ACTION_LOGIN
;
247 $reqs = [ PasswordAuthenticationRequest
::class => $req ];
249 $provider = $this->getProvider();
250 $providerPriv = \TestingAccessWrapper
::newFromObject( $provider );
252 $providerPriv->newPasswordExpiry
= 100;
256 AuthenticationResponse
::newAbstain(),
257 $provider->beginPrimaryAuthentication( [] )
260 $req->username
= 'foo';
261 $req->password
= null;
263 AuthenticationResponse
::newAbstain(),
264 $provider->beginPrimaryAuthentication( $reqs )
267 $req->username
= null;
268 $req->password
= 'bar';
270 AuthenticationResponse
::newAbstain(),
271 $provider->beginPrimaryAuthentication( $reqs )
274 $req->username
= '<invalid>';
275 $req->password
= 'WhoCares';
276 $ret = $provider->beginPrimaryAuthentication( $reqs );
278 AuthenticationResponse
::newAbstain(),
279 $provider->beginPrimaryAuthentication( $reqs )
282 $req->username
= 'DoesNotExist';
283 $req->password
= 'DoesNotExist';
284 $ret = $provider->beginPrimaryAuthentication( $reqs );
286 AuthenticationResponse
::newAbstain(),
287 $provider->beginPrimaryAuthentication( $reqs )
290 // Validation failure
291 $req->username
= $user->getName();
292 $req->password
= $password;
293 $this->validity
= \Status
::newFatal( 'arbitrary-failure' );
294 $ret = $provider->beginPrimaryAuthentication( $reqs );
296 AuthenticationResponse
::FAIL
,
301 $ret->message
->getKey()
305 $this->manager
->removeAuthenticationSessionData( null );
306 $this->validity
= \Status
::newGood();
308 AuthenticationResponse
::newPass( $user->getName() ),
309 $provider->beginPrimaryAuthentication( $reqs )
311 $this->assertNotNull( $this->manager
->getAuthenticationSessionData( 'reset-pass' ) );
313 $this->manager
->removeAuthenticationSessionData( null );
314 $this->validity
= \Status
::newGood();
315 $req->username
= lcfirst( $user->getName() );
317 AuthenticationResponse
::newPass( $user->getName() ),
318 $provider->beginPrimaryAuthentication( $reqs )
320 $this->assertNotNull( $this->manager
->getAuthenticationSessionData( 'reset-pass' ) );
321 $req->username
= $user->getName();
324 $providerPriv->newPasswordExpiry
= 1;
325 $ret = $provider->beginPrimaryAuthentication( $reqs );
327 AuthenticationResponse
::FAIL
,
332 $ret->message
->getKey()
336 $providerPriv->newPasswordExpiry
= 100;
337 $this->validity
= \Status
::newGood();
338 $req->password
= 'Wrong';
339 $ret = $provider->beginPrimaryAuthentication( $reqs );
341 AuthenticationResponse
::FAIL
,
346 $ret->message
->getKey()
352 * @dataProvider provideProviderAllowsAuthenticationDataChange
353 * @param string $type
354 * @param string $user
355 * @param \Status $validity Result of the password validity check
356 * @param \StatusValue $expect1 Expected result with $checkData = false
357 * @param \StatusValue $expect2 Expected result with $checkData = true
359 public function testProviderAllowsAuthenticationDataChange( $type, $user, \Status
$validity,
360 \StatusValue
$expect1, \StatusValue
$expect2
362 if ( $type === PasswordAuthenticationRequest
::class ||
363 $type === TemporaryPasswordAuthenticationRequest
::class
367 $req = $this->getMock( $type );
369 $req->action
= AuthManager
::ACTION_CHANGE
;
370 $req->username
= $user;
371 $req->password
= 'NewPassword';
373 $provider = $this->getProvider();
374 $this->validity
= $validity;
375 $this->assertEquals( $expect1, $provider->providerAllowsAuthenticationDataChange( $req, false ) );
376 $this->assertEquals( $expect2, $provider->providerAllowsAuthenticationDataChange( $req, true ) );
379 public static function provideProviderAllowsAuthenticationDataChange() {
380 $err = \StatusValue
::newGood();
381 $err->error( 'arbitrary-warning' );
384 [ AuthenticationRequest
::class, 'UTSysop', \Status
::newGood(),
385 \StatusValue
::newGood( 'ignored' ), \StatusValue
::newGood( 'ignored' ) ],
386 [ PasswordAuthenticationRequest
::class, 'UTSysop', \Status
::newGood(),
387 \StatusValue
::newGood( 'ignored' ), \StatusValue
::newGood( 'ignored' ) ],
388 [ TemporaryPasswordAuthenticationRequest
::class, 'UTSysop', \Status
::newGood(),
389 \StatusValue
::newGood(), \StatusValue
::newGood() ],
390 [ TemporaryPasswordAuthenticationRequest
::class, 'uTSysop', \Status
::newGood(),
391 \StatusValue
::newGood(), \StatusValue
::newGood() ],
392 [ TemporaryPasswordAuthenticationRequest
::class, 'UTSysop', \Status
::wrap( $err ),
393 \StatusValue
::newGood(), $err ],
394 [ TemporaryPasswordAuthenticationRequest
::class, 'UTSysop',
395 \Status
::newFatal( 'arbitrary-error' ), \StatusValue
::newGood(),
396 \StatusValue
::newFatal( 'arbitrary-error' ) ],
397 [ TemporaryPasswordAuthenticationRequest
::class, 'DoesNotExist', \Status
::newGood(),
398 \StatusValue
::newGood(), \StatusValue
::newGood( 'ignored' ) ],
399 [ TemporaryPasswordAuthenticationRequest
::class, '<invalid>', \Status
::newGood(),
400 \StatusValue
::newGood(), \StatusValue
::newGood( 'ignored' ) ],
405 * @dataProvider provideProviderChangeAuthenticationData
406 * @param string $user
407 * @param string $type
408 * @param bool $changed
410 public function testProviderChangeAuthenticationData( $user, $type, $changed ) {
411 $cuser = ucfirst( $user );
412 $oldpass = 'OldTempPassword';
413 $newpass = 'NewTempPassword';
415 $dbw = wfGetDB( DB_MASTER
);
416 $oldHash = $dbw->selectField( 'user', 'user_newpassword', [ 'user_name' => $cuser ] );
417 $cb = new \
ScopedCallback( function () use ( $dbw, $cuser, $oldHash ) {
418 $dbw->update( 'user', [ 'user_newpassword' => $oldHash ], [ 'user_name' => $cuser ] );
421 $hash = ':A:' . md5( $oldpass );
424 [ 'user_newpassword' => $hash, 'user_newpass_time' => $dbw->timestamp( time() +
10 ) ],
425 [ 'user_name' => $cuser ]
428 $provider = $this->getProvider();
431 $loginReq = new PasswordAuthenticationRequest();
432 $loginReq->action
= AuthManager
::ACTION_CHANGE
;
433 $loginReq->username
= $user;
434 $loginReq->password
= $oldpass;
435 $loginReqs = [ PasswordAuthenticationRequest
::class => $loginReq ];
437 AuthenticationResponse
::newPass( $cuser ),
438 $provider->beginPrimaryAuthentication( $loginReqs ),
442 if ( $type === PasswordAuthenticationRequest
::class ||
443 $type === TemporaryPasswordAuthenticationRequest
::class
445 $changeReq = new $type();
447 $changeReq = $this->getMock( $type );
449 $changeReq->action
= AuthManager
::ACTION_CHANGE
;
450 $changeReq->username
= $user;
451 $changeReq->password
= $newpass;
452 $resetMailer = $this->hookMailer();
453 $provider->providerChangeAuthenticationData( $changeReq );
454 \ScopedCallback
::consume( $resetMailer );
456 $loginReq->password
= $oldpass;
457 $ret = $provider->beginPrimaryAuthentication( $loginReqs );
459 AuthenticationResponse
::FAIL
,
461 'old password should fail'
465 $ret->message
->getKey(),
466 'old password should fail'
469 $loginReq->password
= $newpass;
470 $ret = $provider->beginPrimaryAuthentication( $loginReqs );
473 AuthenticationResponse
::newPass( $cuser ),
475 'new password should pass'
477 $this->assertNotNull(
478 $dbw->selectField( 'user', 'user_newpass_time', [ 'user_name' => $cuser ] )
482 AuthenticationResponse
::FAIL
,
484 'new password should fail'
488 $ret->message
->getKey(),
489 'new password should fail'
492 $dbw->selectField( 'user', 'user_newpass_time', [ 'user_name' => $cuser ] )
497 public static function provideProviderChangeAuthenticationData() {
499 [ 'UTSysop', AuthenticationRequest
::class, false ],
500 [ 'UTSysop', PasswordAuthenticationRequest
::class, false ],
501 [ 'UTSysop', TemporaryPasswordAuthenticationRequest
::class, true ],
505 public function testProviderChangeAuthenticationDataEmail() {
506 $user = self
::getMutableTestUser()->getUser();
508 $dbw = wfGetDB( DB_MASTER
);
511 [ 'user_newpass_time' => $dbw->timestamp( time() - 5 * 3600 ) ],
512 [ 'user_id' => $user->getId() ]
515 $req = TemporaryPasswordAuthenticationRequest
::newRandom();
516 $req->username
= $user->getName();
517 $req->mailpassword
= true;
519 $provider = $this->getProvider( [ 'emailEnabled' => false ] );
520 $status = $provider->providerAllowsAuthenticationDataChange( $req, true );
521 $this->assertEquals( \StatusValue
::newFatal( 'passwordreset-emaildisabled' ), $status );
522 $req->hasBackchannel
= true;
523 $status = $provider->providerAllowsAuthenticationDataChange( $req, true );
524 $this->assertFalse( $status->hasMessage( 'passwordreset-emaildisabled' ) );
525 $req->hasBackchannel
= false;
527 $provider = $this->getProvider( [ 'passwordReminderResendTime' => 10 ] );
528 $status = $provider->providerAllowsAuthenticationDataChange( $req, true );
529 $this->assertEquals( \StatusValue
::newFatal( 'throttled-mailpassword', 10 ), $status );
531 $provider = $this->getProvider( [ 'passwordReminderResendTime' => 3 ] );
532 $status = $provider->providerAllowsAuthenticationDataChange( $req, true );
533 $this->assertFalse( $status->hasMessage( 'throttled-mailpassword' ) );
537 [ 'user_newpass_time' => $dbw->timestamp( time() +
5 * 3600 ) ],
538 [ 'user_id' => $user->getId() ]
540 $provider = $this->getProvider( [ 'passwordReminderResendTime' => 0 ] );
541 $status = $provider->providerAllowsAuthenticationDataChange( $req, true );
542 $this->assertFalse( $status->hasMessage( 'throttled-mailpassword' ) );
545 $status = $provider->providerAllowsAuthenticationDataChange( $req, true );
546 $this->assertEquals( \StatusValue
::newFatal( 'passwordreset-nocaller' ), $status );
548 $req->caller
= '127.0.0.256';
549 $status = $provider->providerAllowsAuthenticationDataChange( $req, true );
550 $this->assertEquals( \StatusValue
::newFatal( 'passwordreset-nosuchcaller', '127.0.0.256' ),
553 $req->caller
= '<Invalid>';
554 $status = $provider->providerAllowsAuthenticationDataChange( $req, true );
555 $this->assertEquals( \StatusValue
::newFatal( 'passwordreset-nosuchcaller', '<Invalid>' ),
558 $req->caller
= '127.0.0.1';
559 $status = $provider->providerAllowsAuthenticationDataChange( $req, true );
560 $this->assertEquals( \StatusValue
::newGood(), $status );
562 $req->caller
= $user->getName();
563 $status = $provider->providerAllowsAuthenticationDataChange( $req, true );
564 $this->assertEquals( \StatusValue
::newGood(), $status );
567 $resetMailer = $this->hookMailer( function ( $headers, $to, $from, $subject, $body )
568 use ( &$mailed, $req, $user )
571 $this->assertSame( $user->getEmail(), $to[0]->address
);
572 $this->assertContains( $req->password
, $body );
575 $provider->providerChangeAuthenticationData( $req );
576 \ScopedCallback
::consume( $resetMailer );
577 $this->assertTrue( $mailed );
579 $priv = \TestingAccessWrapper
::newFromObject( $provider );
580 $req->username
= '<invalid>';
581 $status = $priv->sendPasswordResetEmail( $req );
582 $this->assertEquals( \Status
::newFatal( 'noname' ), $status );
585 public function testTestForAccountCreation() {
586 $user = \User
::newFromName( 'foo' );
587 $req = new TemporaryPasswordAuthenticationRequest();
588 $req->username
= 'Foo';
589 $req->password
= 'Bar';
590 $reqs = [ TemporaryPasswordAuthenticationRequest
::class => $req ];
592 $provider = $this->getProvider();
594 \StatusValue
::newGood(),
595 $provider->testForAccountCreation( $user, $user, [] ),
596 'No password request'
600 \StatusValue
::newGood(),
601 $provider->testForAccountCreation( $user, $user, $reqs ),
602 'Password request, validated'
605 $this->validity
->error( 'arbitrary warning' );
606 $expect = \StatusValue
::newGood();
607 $expect->error( 'arbitrary warning' );
610 $provider->testForAccountCreation( $user, $user, $reqs ),
611 'Password request, not validated'
615 public function testAccountCreation() {
616 $resetMailer = $this->hookMailer();
618 $user = \User
::newFromName( 'Foo' );
620 $req = new TemporaryPasswordAuthenticationRequest();
621 $reqs = [ TemporaryPasswordAuthenticationRequest
::class => $req ];
623 $authreq = new PasswordAuthenticationRequest();
624 $authreq->action
= AuthManager
::ACTION_CREATE
;
625 $authreqs = [ PasswordAuthenticationRequest
::class => $authreq ];
627 $provider = $this->getProvider();
630 AuthenticationResponse
::newAbstain(),
631 $provider->beginPrimaryAccountCreation( $user, $user, [] )
634 $req->username
= 'foo';
635 $req->password
= null;
637 AuthenticationResponse
::newAbstain(),
638 $provider->beginPrimaryAccountCreation( $user, $user, $reqs )
641 $req->username
= null;
642 $req->password
= 'bar';
644 AuthenticationResponse
::newAbstain(),
645 $provider->beginPrimaryAccountCreation( $user, $user, $reqs )
648 $req->username
= 'foo';
649 $req->password
= 'bar';
651 $expect = AuthenticationResponse
::newPass( 'Foo' );
652 $expect->createRequest
= clone( $req );
653 $expect->createRequest
->username
= 'Foo';
654 $this->assertEquals( $expect, $provider->beginPrimaryAccountCreation( $user, $user, $reqs ) );
655 $this->assertNull( $this->manager
->getAuthenticationSessionData( 'no-email' ) );
657 $user = self
::getMutableTestUser()->getUser();
658 $req->username
= $authreq->username
= $user->getName();
659 $req->password
= $authreq->password
= 'NewPassword';
660 $expect = AuthenticationResponse
::newPass( $user->getName() );
661 $expect->createRequest
= $req;
663 $res2 = $provider->beginPrimaryAccountCreation( $user, $user, $reqs );
664 $this->assertEquals( $expect, $res2, 'Sanity check' );
666 $ret = $provider->beginPrimaryAuthentication( $authreqs );
667 $this->assertEquals( AuthenticationResponse
::FAIL
, $ret->status
, 'sanity check' );
669 $this->assertSame( null, $provider->finishAccountCreation( $user, $user, $res2 ) );
671 $ret = $provider->beginPrimaryAuthentication( $authreqs );
672 $this->assertEquals( AuthenticationResponse
::PASS
, $ret->status
, 'new password is set' );
675 public function testAccountCreationEmail() {
676 $creator = \User
::newFromName( 'Foo' );
678 $user = self
::getMutableTestUser()->getUser();
679 $user->setEmail( null );
681 $req = TemporaryPasswordAuthenticationRequest
::newRandom();
682 $req->username
= $user->getName();
683 $req->mailpassword
= true;
685 $provider = $this->getProvider( [ 'emailEnabled' => false ] );
686 $status = $provider->testForAccountCreation( $user, $creator, [ $req ] );
687 $this->assertEquals( \StatusValue
::newFatal( 'emaildisabled' ), $status );
688 $req->hasBackchannel
= true;
689 $status = $provider->testForAccountCreation( $user, $creator, [ $req ] );
690 $this->assertFalse( $status->hasMessage( 'emaildisabled' ) );
691 $req->hasBackchannel
= false;
693 $provider = $this->getProvider( [ 'emailEnabled' => true ] );
694 $status = $provider->testForAccountCreation( $user, $creator, [ $req ] );
695 $this->assertEquals( \StatusValue
::newFatal( 'noemailcreate' ), $status );
696 $req->hasBackchannel
= true;
697 $status = $provider->testForAccountCreation( $user, $creator, [ $req ] );
698 $this->assertFalse( $status->hasMessage( 'noemailcreate' ) );
699 $req->hasBackchannel
= false;
701 $user->setEmail( 'test@localhost.localdomain' );
702 $status = $provider->testForAccountCreation( $user, $creator, [ $req ] );
703 $this->assertEquals( \StatusValue
::newGood(), $status );
706 $resetMailer = $this->hookMailer( function ( $headers, $to, $from, $subject, $body )
707 use ( &$mailed, $req )
710 $this->assertSame( 'test@localhost.localdomain', $to[0]->address
);
711 $this->assertContains( $req->password
, $body );
715 $expect = AuthenticationResponse
::newPass( $user->getName() );
716 $expect->createRequest
= clone( $req );
717 $expect->createRequest
->username
= $user->getName();
718 $res = $provider->beginPrimaryAccountCreation( $user, $creator, [ $req ] );
719 $this->assertEquals( $expect, $res );
720 $this->assertTrue( $this->manager
->getAuthenticationSessionData( 'no-email' ) );
721 $this->assertFalse( $mailed );
723 $this->assertSame( 'byemail', $provider->finishAccountCreation( $user, $creator, $res ) );
724 $this->assertTrue( $mailed );
726 \ScopedCallback
::consume( $resetMailer );
727 $this->assertTrue( $mailed );