Merge "Improve Doxygen template used by mwdocgen.php"
[lhc/web/wiklou.git] / tests / phpunit / includes / auth / TemporaryPasswordPrimaryAuthenticationProviderTest.php
1 <?php
2
3 namespace MediaWiki\Auth;
4
5 /**
6 * @group AuthManager
7 * @group Database
8 * @covers MediaWiki\Auth\TemporaryPasswordPrimaryAuthenticationProvider
9 */
10 class TemporaryPasswordPrimaryAuthenticationProviderTest extends \MediaWikiTestCase {
11
12 private $manager = null;
13 private $config = null;
14 private $validity = null;
15
16 /**
17 * Get an instance of the provider
18 *
19 * $provider->checkPasswordValidity is mocked to return $this->validity,
20 * because we don't need to test that here.
21 *
22 * @param array $params
23 * @return TemporaryPasswordPrimaryAuthenticationProvider
24 */
25 protected function getProvider( $params = [] ) {
26 if ( !$this->config ) {
27 $this->config = new \HashConfig( [
28 'EmailEnabled' => true,
29 ] );
30 }
31 $config = new \MultiConfig( [
32 $this->config,
33 \ConfigFactory::getDefaultInstance()->makeConfig( 'main' )
34 ] );
35
36 if ( !$this->manager ) {
37 $this->manager = new AuthManager( new \FauxRequest(), $config );
38 }
39 $this->validity = \Status::newGood();
40
41 $mockedMethods[] = 'checkPasswordValidity';
42 $provider = $this->getMock(
43 TemporaryPasswordPrimaryAuthenticationProvider::class,
44 $mockedMethods,
45 [ $params ]
46 );
47 $provider->expects( $this->any() )->method( 'checkPasswordValidity' )
48 ->will( $this->returnCallback( function () {
49 return $this->validity;
50 } ) );
51 $provider->setConfig( $config );
52 $provider->setLogger( new \Psr\Log\NullLogger() );
53 $provider->setManager( $this->manager );
54
55 return $provider;
56 }
57
58 protected function hookMailer( $func = null ) {
59 \Hooks::clear( 'AlternateUserMailer' );
60 if ( $func ) {
61 \Hooks::register( 'AlternateUserMailer', $func );
62 // Safety
63 \Hooks::register( 'AlternateUserMailer', function () {
64 return false;
65 } );
66 } else {
67 \Hooks::register( 'AlternateUserMailer', function () {
68 $this->fail( 'AlternateUserMailer hook called unexpectedly' );
69 return false;
70 } );
71 }
72
73 return new \ScopedCallback( function () {
74 \Hooks::clear( 'AlternateUserMailer' );
75 \Hooks::register( 'AlternateUserMailer', function () {
76 return false;
77 } );
78 } );
79 }
80
81 public function testBasics() {
82 $provider = new TemporaryPasswordPrimaryAuthenticationProvider();
83
84 $this->assertSame(
85 PrimaryAuthenticationProvider::TYPE_CREATE,
86 $provider->accountCreationType()
87 );
88
89 $this->assertTrue( $provider->testUserExists( 'UTSysop' ) );
90 $this->assertTrue( $provider->testUserExists( 'uTSysop' ) );
91 $this->assertFalse( $provider->testUserExists( 'DoesNotExist' ) );
92 $this->assertFalse( $provider->testUserExists( '<invalid>' ) );
93
94 $req = new PasswordAuthenticationRequest;
95 $req->action = AuthManager::ACTION_CHANGE;
96 $req->username = '<invalid>';
97 $provider->providerChangeAuthenticationData( $req );
98 }
99
100 public function testConfig() {
101 $config = new \HashConfig( [
102 'EnableEmail' => false,
103 'NewPasswordExpiry' => 100,
104 'PasswordReminderResendTime' => 101,
105 ] );
106
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 );
112
113 $p = \TestingAccessWrapper::newFromObject( new TemporaryPasswordPrimaryAuthenticationProvider( [
114 'emailEnabled' => true,
115 'newPasswordExpiry' => 42,
116 'passwordReminderResendTime' => 43,
117 ] ) );
118 $p->setConfig( $config );
119 $this->assertSame( true, $p->emailEnabled );
120 $this->assertSame( 42, $p->newPasswordExpiry );
121 $this->assertSame( 43, $p->passwordReminderResendTime );
122 }
123
124 public function testTestUserCanAuthenticate() {
125 $user = self::getMutableTestUser()->getUser();
126
127 $dbw = wfGetDB( DB_MASTER );
128
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();
134
135 $provider = $this->getProvider();
136 $providerPriv = \TestingAccessWrapper::newFromObject( $provider );
137
138 $this->assertFalse( $provider->testUserCanAuthenticate( '<invalid>' ) );
139 $this->assertFalse( $provider->testUserCanAuthenticate( 'DoesNotExist' ) );
140
141 $dbw->update(
142 'user',
143 [
144 'user_newpassword' => \PasswordFactory::newInvalidPassword()->toString(),
145 'user_newpass_time' => null,
146 ],
147 [ 'user_id' => $user->getId() ]
148 );
149 $this->assertFalse( $provider->testUserCanAuthenticate( $user->getName() ) );
150
151 $dbw->update(
152 'user',
153 [
154 'user_newpassword' => $pwhash,
155 'user_newpass_time' => null,
156 ],
157 [ 'user_id' => $user->getId() ]
158 );
159 $this->assertTrue( $provider->testUserCanAuthenticate( $user->getName() ) );
160 $this->assertTrue( $provider->testUserCanAuthenticate( lcfirst( $user->getName() ) ) );
161
162 $dbw->update(
163 'user',
164 [
165 'user_newpassword' => $pwhash,
166 'user_newpass_time' => $dbw->timestamp( time() - 10 ),
167 ],
168 [ 'user_id' => $user->getId() ]
169 );
170 $providerPriv->newPasswordExpiry = 100;
171 $this->assertTrue( $provider->testUserCanAuthenticate( $user->getName() ) );
172 $providerPriv->newPasswordExpiry = 1;
173 $this->assertFalse( $provider->testUserCanAuthenticate( $user->getName() ) );
174
175 $dbw->update(
176 'user',
177 [
178 'user_newpassword' => \PasswordFactory::newInvalidPassword()->toString(),
179 'user_newpass_time' => null,
180 ],
181 [ 'user_id' => $user->getId() ]
182 );
183 }
184
185 /**
186 * @dataProvider provideGetAuthenticationRequests
187 * @param string $action
188 * @param array $options
189 * @param array $expected
190 */
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';
196 }
197 }
198 $this->assertEquals( $expected, $actual );
199 }
200
201 public static function provideGetAuthenticationRequests() {
202 $anon = [ 'username' => null ];
203 $loggedIn = [ 'username' => 'UTSysop' ];
204
205 return [
206 [ AuthManager::ACTION_LOGIN, $anon, [
207 new PasswordAuthenticationRequest
208 ] ],
209 [ AuthManager::ACTION_LOGIN, $loggedIn, [
210 new PasswordAuthenticationRequest
211 ] ],
212 [ AuthManager::ACTION_CREATE, $anon, [] ],
213 [ AuthManager::ACTION_CREATE, $loggedIn, [
214 new TemporaryPasswordAuthenticationRequest( 'random' )
215 ] ],
216 [ AuthManager::ACTION_LINK, $anon, [] ],
217 [ AuthManager::ACTION_LINK, $loggedIn, [] ],
218 [ AuthManager::ACTION_CHANGE, $anon, [
219 new TemporaryPasswordAuthenticationRequest( 'random' )
220 ] ],
221 [ AuthManager::ACTION_CHANGE, $loggedIn, [
222 new TemporaryPasswordAuthenticationRequest( 'random' )
223 ] ],
224 [ AuthManager::ACTION_REMOVE, $anon, [
225 new TemporaryPasswordAuthenticationRequest
226 ] ],
227 [ AuthManager::ACTION_REMOVE, $loggedIn, [
228 new TemporaryPasswordAuthenticationRequest
229 ] ],
230 ];
231 }
232
233 public function testAuthentication() {
234 $user = self::getMutableTestUser()->getUser();
235
236 $password = 'TemporaryPassword';
237 $hash = ':A:' . md5( $password );
238 $dbw = wfGetDB( DB_MASTER );
239 $dbw->update(
240 'user',
241 [ 'user_newpassword' => $hash, 'user_newpass_time' => $dbw->timestamp( time() - 10 ) ],
242 [ 'user_id' => $user->getId() ]
243 );
244
245 $req = new PasswordAuthenticationRequest();
246 $req->action = AuthManager::ACTION_LOGIN;
247 $reqs = [ PasswordAuthenticationRequest::class => $req ];
248
249 $provider = $this->getProvider();
250 $providerPriv = \TestingAccessWrapper::newFromObject( $provider );
251
252 $providerPriv->newPasswordExpiry = 100;
253
254 // General failures
255 $this->assertEquals(
256 AuthenticationResponse::newAbstain(),
257 $provider->beginPrimaryAuthentication( [] )
258 );
259
260 $req->username = 'foo';
261 $req->password = null;
262 $this->assertEquals(
263 AuthenticationResponse::newAbstain(),
264 $provider->beginPrimaryAuthentication( $reqs )
265 );
266
267 $req->username = null;
268 $req->password = 'bar';
269 $this->assertEquals(
270 AuthenticationResponse::newAbstain(),
271 $provider->beginPrimaryAuthentication( $reqs )
272 );
273
274 $req->username = '<invalid>';
275 $req->password = 'WhoCares';
276 $ret = $provider->beginPrimaryAuthentication( $reqs );
277 $this->assertEquals(
278 AuthenticationResponse::newAbstain(),
279 $provider->beginPrimaryAuthentication( $reqs )
280 );
281
282 $req->username = 'DoesNotExist';
283 $req->password = 'DoesNotExist';
284 $ret = $provider->beginPrimaryAuthentication( $reqs );
285 $this->assertEquals(
286 AuthenticationResponse::newAbstain(),
287 $provider->beginPrimaryAuthentication( $reqs )
288 );
289
290 // Validation failure
291 $req->username = $user->getName();
292 $req->password = $password;
293 $this->validity = \Status::newFatal( 'arbitrary-failure' );
294 $ret = $provider->beginPrimaryAuthentication( $reqs );
295 $this->assertEquals(
296 AuthenticationResponse::FAIL,
297 $ret->status
298 );
299 $this->assertEquals(
300 'arbitrary-failure',
301 $ret->message->getKey()
302 );
303
304 // Successful auth
305 $this->manager->removeAuthenticationSessionData( null );
306 $this->validity = \Status::newGood();
307 $this->assertEquals(
308 AuthenticationResponse::newPass( $user->getName() ),
309 $provider->beginPrimaryAuthentication( $reqs )
310 );
311 $this->assertNotNull( $this->manager->getAuthenticationSessionData( 'reset-pass' ) );
312
313 $this->manager->removeAuthenticationSessionData( null );
314 $this->validity = \Status::newGood();
315 $req->username = lcfirst( $user->getName() );
316 $this->assertEquals(
317 AuthenticationResponse::newPass( $user->getName() ),
318 $provider->beginPrimaryAuthentication( $reqs )
319 );
320 $this->assertNotNull( $this->manager->getAuthenticationSessionData( 'reset-pass' ) );
321 $req->username = $user->getName();
322
323 // Expired password
324 $providerPriv->newPasswordExpiry = 1;
325 $ret = $provider->beginPrimaryAuthentication( $reqs );
326 $this->assertEquals(
327 AuthenticationResponse::FAIL,
328 $ret->status
329 );
330 $this->assertEquals(
331 'wrongpassword',
332 $ret->message->getKey()
333 );
334
335 // Bad password
336 $providerPriv->newPasswordExpiry = 100;
337 $this->validity = \Status::newGood();
338 $req->password = 'Wrong';
339 $ret = $provider->beginPrimaryAuthentication( $reqs );
340 $this->assertEquals(
341 AuthenticationResponse::FAIL,
342 $ret->status
343 );
344 $this->assertEquals(
345 'wrongpassword',
346 $ret->message->getKey()
347 );
348
349 }
350
351 /**
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
358 */
359 public function testProviderAllowsAuthenticationDataChange( $type, $user, \Status $validity,
360 \StatusValue $expect1, \StatusValue $expect2
361 ) {
362 if ( $type === PasswordAuthenticationRequest::class ||
363 $type === TemporaryPasswordAuthenticationRequest::class
364 ) {
365 $req = new $type();
366 } else {
367 $req = $this->getMock( $type );
368 }
369 $req->action = AuthManager::ACTION_CHANGE;
370 $req->username = $user;
371 $req->password = 'NewPassword';
372
373 $provider = $this->getProvider();
374 $this->validity = $validity;
375 $this->assertEquals( $expect1, $provider->providerAllowsAuthenticationDataChange( $req, false ) );
376 $this->assertEquals( $expect2, $provider->providerAllowsAuthenticationDataChange( $req, true ) );
377 }
378
379 public static function provideProviderAllowsAuthenticationDataChange() {
380 $err = \StatusValue::newGood();
381 $err->error( 'arbitrary-warning' );
382
383 return [
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' ) ],
401 ];
402 }
403
404 /**
405 * @dataProvider provideProviderChangeAuthenticationData
406 * @param string $user
407 * @param string $type
408 * @param bool $changed
409 */
410 public function testProviderChangeAuthenticationData( $user, $type, $changed ) {
411 $cuser = ucfirst( $user );
412 $oldpass = 'OldTempPassword';
413 $newpass = 'NewTempPassword';
414
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 ] );
419 } );
420
421 $hash = ':A:' . md5( $oldpass );
422 $dbw->update(
423 'user',
424 [ 'user_newpassword' => $hash, 'user_newpass_time' => $dbw->timestamp( time() + 10 ) ],
425 [ 'user_name' => $cuser ]
426 );
427
428 $provider = $this->getProvider();
429
430 // Sanity check
431 $loginReq = new PasswordAuthenticationRequest();
432 $loginReq->action = AuthManager::ACTION_CHANGE;
433 $loginReq->username = $user;
434 $loginReq->password = $oldpass;
435 $loginReqs = [ PasswordAuthenticationRequest::class => $loginReq ];
436 $this->assertEquals(
437 AuthenticationResponse::newPass( $cuser ),
438 $provider->beginPrimaryAuthentication( $loginReqs ),
439 'Sanity check'
440 );
441
442 if ( $type === PasswordAuthenticationRequest::class ||
443 $type === TemporaryPasswordAuthenticationRequest::class
444 ) {
445 $changeReq = new $type();
446 } else {
447 $changeReq = $this->getMock( $type );
448 }
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 );
455
456 $loginReq->password = $oldpass;
457 $ret = $provider->beginPrimaryAuthentication( $loginReqs );
458 $this->assertEquals(
459 AuthenticationResponse::FAIL,
460 $ret->status,
461 'old password should fail'
462 );
463 $this->assertEquals(
464 'wrongpassword',
465 $ret->message->getKey(),
466 'old password should fail'
467 );
468
469 $loginReq->password = $newpass;
470 $ret = $provider->beginPrimaryAuthentication( $loginReqs );
471 if ( $changed ) {
472 $this->assertEquals(
473 AuthenticationResponse::newPass( $cuser ),
474 $ret,
475 'new password should pass'
476 );
477 $this->assertNotNull(
478 $dbw->selectField( 'user', 'user_newpass_time', [ 'user_name' => $cuser ] )
479 );
480 } else {
481 $this->assertEquals(
482 AuthenticationResponse::FAIL,
483 $ret->status,
484 'new password should fail'
485 );
486 $this->assertEquals(
487 'wrongpassword',
488 $ret->message->getKey(),
489 'new password should fail'
490 );
491 $this->assertNull(
492 $dbw->selectField( 'user', 'user_newpass_time', [ 'user_name' => $cuser ] )
493 );
494 }
495 }
496
497 public static function provideProviderChangeAuthenticationData() {
498 return [
499 [ 'UTSysop', AuthenticationRequest::class, false ],
500 [ 'UTSysop', PasswordAuthenticationRequest::class, false ],
501 [ 'UTSysop', TemporaryPasswordAuthenticationRequest::class, true ],
502 ];
503 }
504
505 public function testProviderChangeAuthenticationDataEmail() {
506 $user = self::getMutableTestUser()->getUser();
507
508 $dbw = wfGetDB( DB_MASTER );
509 $dbw->update(
510 'user',
511 [ 'user_newpass_time' => $dbw->timestamp( time() - 5 * 3600 ) ],
512 [ 'user_id' => $user->getId() ]
513 );
514
515 $req = TemporaryPasswordAuthenticationRequest::newRandom();
516 $req->username = $user->getName();
517 $req->mailpassword = true;
518
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;
526
527 $provider = $this->getProvider( [ 'passwordReminderResendTime' => 10 ] );
528 $status = $provider->providerAllowsAuthenticationDataChange( $req, true );
529 $this->assertEquals( \StatusValue::newFatal( 'throttled-mailpassword', 10 ), $status );
530
531 $provider = $this->getProvider( [ 'passwordReminderResendTime' => 3 ] );
532 $status = $provider->providerAllowsAuthenticationDataChange( $req, true );
533 $this->assertFalse( $status->hasMessage( 'throttled-mailpassword' ) );
534
535 $dbw->update(
536 'user',
537 [ 'user_newpass_time' => $dbw->timestamp( time() + 5 * 3600 ) ],
538 [ 'user_id' => $user->getId() ]
539 );
540 $provider = $this->getProvider( [ 'passwordReminderResendTime' => 0 ] );
541 $status = $provider->providerAllowsAuthenticationDataChange( $req, true );
542 $this->assertFalse( $status->hasMessage( 'throttled-mailpassword' ) );
543
544 $req->caller = null;
545 $status = $provider->providerAllowsAuthenticationDataChange( $req, true );
546 $this->assertEquals( \StatusValue::newFatal( 'passwordreset-nocaller' ), $status );
547
548 $req->caller = '127.0.0.256';
549 $status = $provider->providerAllowsAuthenticationDataChange( $req, true );
550 $this->assertEquals( \StatusValue::newFatal( 'passwordreset-nosuchcaller', '127.0.0.256' ),
551 $status );
552
553 $req->caller = '<Invalid>';
554 $status = $provider->providerAllowsAuthenticationDataChange( $req, true );
555 $this->assertEquals( \StatusValue::newFatal( 'passwordreset-nosuchcaller', '<Invalid>' ),
556 $status );
557
558 $req->caller = '127.0.0.1';
559 $status = $provider->providerAllowsAuthenticationDataChange( $req, true );
560 $this->assertEquals( \StatusValue::newGood(), $status );
561
562 $req->caller = $user->getName();
563 $status = $provider->providerAllowsAuthenticationDataChange( $req, true );
564 $this->assertEquals( \StatusValue::newGood(), $status );
565
566 $mailed = false;
567 $resetMailer = $this->hookMailer( function ( $headers, $to, $from, $subject, $body )
568 use ( &$mailed, $req, $user )
569 {
570 $mailed = true;
571 $this->assertSame( $user->getEmail(), $to[0]->address );
572 $this->assertContains( $req->password, $body );
573 return false;
574 } );
575 $provider->providerChangeAuthenticationData( $req );
576 \ScopedCallback::consume( $resetMailer );
577 $this->assertTrue( $mailed );
578
579 $priv = \TestingAccessWrapper::newFromObject( $provider );
580 $req->username = '<invalid>';
581 $status = $priv->sendPasswordResetEmail( $req );
582 $this->assertEquals( \Status::newFatal( 'noname' ), $status );
583 }
584
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 ];
591
592 $provider = $this->getProvider();
593 $this->assertEquals(
594 \StatusValue::newGood(),
595 $provider->testForAccountCreation( $user, $user, [] ),
596 'No password request'
597 );
598
599 $this->assertEquals(
600 \StatusValue::newGood(),
601 $provider->testForAccountCreation( $user, $user, $reqs ),
602 'Password request, validated'
603 );
604
605 $this->validity->error( 'arbitrary warning' );
606 $expect = \StatusValue::newGood();
607 $expect->error( 'arbitrary warning' );
608 $this->assertEquals(
609 $expect,
610 $provider->testForAccountCreation( $user, $user, $reqs ),
611 'Password request, not validated'
612 );
613 }
614
615 public function testAccountCreation() {
616 $resetMailer = $this->hookMailer();
617
618 $user = \User::newFromName( 'Foo' );
619
620 $req = new TemporaryPasswordAuthenticationRequest();
621 $reqs = [ TemporaryPasswordAuthenticationRequest::class => $req ];
622
623 $authreq = new PasswordAuthenticationRequest();
624 $authreq->action = AuthManager::ACTION_CREATE;
625 $authreqs = [ PasswordAuthenticationRequest::class => $authreq ];
626
627 $provider = $this->getProvider();
628
629 $this->assertEquals(
630 AuthenticationResponse::newAbstain(),
631 $provider->beginPrimaryAccountCreation( $user, $user, [] )
632 );
633
634 $req->username = 'foo';
635 $req->password = null;
636 $this->assertEquals(
637 AuthenticationResponse::newAbstain(),
638 $provider->beginPrimaryAccountCreation( $user, $user, $reqs )
639 );
640
641 $req->username = null;
642 $req->password = 'bar';
643 $this->assertEquals(
644 AuthenticationResponse::newAbstain(),
645 $provider->beginPrimaryAccountCreation( $user, $user, $reqs )
646 );
647
648 $req->username = 'foo';
649 $req->password = 'bar';
650
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' ) );
656
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;
662
663 $res2 = $provider->beginPrimaryAccountCreation( $user, $user, $reqs );
664 $this->assertEquals( $expect, $res2, 'Sanity check' );
665
666 $ret = $provider->beginPrimaryAuthentication( $authreqs );
667 $this->assertEquals( AuthenticationResponse::FAIL, $ret->status, 'sanity check' );
668
669 $this->assertSame( null, $provider->finishAccountCreation( $user, $user, $res2 ) );
670
671 $ret = $provider->beginPrimaryAuthentication( $authreqs );
672 $this->assertEquals( AuthenticationResponse::PASS, $ret->status, 'new password is set' );
673 }
674
675 public function testAccountCreationEmail() {
676 $creator = \User::newFromName( 'Foo' );
677
678 $user = self::getMutableTestUser()->getUser();
679 $user->setEmail( null );
680
681 $req = TemporaryPasswordAuthenticationRequest::newRandom();
682 $req->username = $user->getName();
683 $req->mailpassword = true;
684
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;
692
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;
700
701 $user->setEmail( 'test@localhost.localdomain' );
702 $status = $provider->testForAccountCreation( $user, $creator, [ $req ] );
703 $this->assertEquals( \StatusValue::newGood(), $status );
704
705 $mailed = false;
706 $resetMailer = $this->hookMailer( function ( $headers, $to, $from, $subject, $body )
707 use ( &$mailed, $req )
708 {
709 $mailed = true;
710 $this->assertSame( 'test@localhost.localdomain', $to[0]->address );
711 $this->assertContains( $req->password, $body );
712 return false;
713 } );
714
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 );
722
723 $this->assertSame( 'byemail', $provider->finishAccountCreation( $user, $creator, $res ) );
724 $this->assertTrue( $mailed );
725
726 \ScopedCallback::consume( $resetMailer );
727 $this->assertTrue( $mailed );
728 }
729
730 }