3 namespace MediaWiki\Tests\Auth
;
5 use DynamicPropertyTestHelper
;
6 use MediaWiki\Auth\AuthenticationRequest
;
7 use MediaWiki\Auth\AuthenticationResponse
;
8 use MediaWiki\Auth\AuthManager
;
9 use MediaWiki\Auth\ButtonAuthenticationRequest
;
10 use MediaWiki\Auth\PasswordAuthenticationRequest
;
11 use MediaWiki\Auth\ResetPasswordSecondaryAuthenticationProvider
;
12 use MediaWiki\Config\HashConfig
;
13 use MediaWiki\MainConfigNames
;
14 use MediaWiki\Request\FauxRequest
;
15 use MediaWiki\Tests\Unit\Auth\AuthenticationProviderTestTrait
;
16 use MediaWiki\Tests\Unit\DummyServicesTrait
;
17 use MediaWiki\User\BotPasswordStore
;
18 use MediaWiki\User\User
;
19 use MediaWiki\User\UserNameUtils
;
20 use MediaWikiIntegrationTestCase
;
23 use UnexpectedValueException
;
24 use Wikimedia\TestingAccessWrapper
;
28 * @covers \MediaWiki\Auth\ResetPasswordSecondaryAuthenticationProvider
30 class ResetPasswordSecondaryAuthenticationProviderTest
extends MediaWikiIntegrationTestCase
{
31 use AuthenticationProviderTestTrait
;
32 use DummyServicesTrait
;
35 * @dataProvider provideGetAuthenticationRequests
36 * @param string $action
37 * @param array $response
39 public function testGetAuthenticationRequests( $action, $response ) {
40 $provider = new ResetPasswordSecondaryAuthenticationProvider();
42 $this->assertEquals( $response, $provider->getAuthenticationRequests( $action, [] ) );
45 public static function provideGetAuthenticationRequests() {
47 [ AuthManager
::ACTION_LOGIN
, [] ],
48 [ AuthManager
::ACTION_CREATE
, [] ],
49 [ AuthManager
::ACTION_LINK
, [] ],
50 [ AuthManager
::ACTION_CHANGE
, [] ],
51 [ AuthManager
::ACTION_REMOVE
, [] ],
55 public function testBasics() {
56 $user = $this->createMock( User
::class );
59 $reqs = [ new stdClass
];
61 $mb = $this->getMockBuilder( ResetPasswordSecondaryAuthenticationProvider
::class )
62 ->onlyMethods( [ 'tryReset' ] );
65 'beginSecondaryAuthentication' => [ $user, $reqs ],
66 'continueSecondaryAuthentication' => [ $user, $reqs ],
67 'beginSecondaryAccountCreation' => [ $user, $user2, $reqs ],
68 'continueSecondaryAccountCreation' => [ $user, $user2, $reqs ],
70 foreach ( $methods as $method => $args ) {
71 $mock = $mb->getMock();
72 $mock->expects( $this->once() )->method( 'tryReset' )
73 ->with( $this->identicalTo( $user ), $this->identicalTo( $reqs ) )
75 $this->assertSame( $obj, $mock->$method( ...$args ) );
79 public function testTryReset() {
80 $username = 'TestTryReset';
81 $user = User
::newFromName( $username );
83 $provider = $this->getMockBuilder(
84 ResetPasswordSecondaryAuthenticationProvider
::class
87 'providerAllowsAuthenticationDataChange', 'providerChangeAuthenticationData'
90 $provider->method( 'providerAllowsAuthenticationDataChange' )
91 ->willReturnCallback( function ( $req ) use ( $username ) {
92 $this->assertSame( $username, $req->username
);
93 return DynamicPropertyTestHelper
::getDynamicProperty( $req, 'allow' );
95 $provider->method( 'providerChangeAuthenticationData' )
96 ->willReturnCallback( function ( $req ) use ( $username ) {
97 $this->assertSame( $username, $req->username
);
98 DynamicPropertyTestHelper
::setDynamicProperty( $req, 'done', true );
100 $config = new HashConfig( [
101 MainConfigNames
::AuthManagerConfig
=> [
105 [ 'factory' => static function () use ( $provider ) {
111 $mwServices = $this->getServiceContainer();
112 $manager = new AuthManager(
115 $this->getDummyObjectFactory(),
116 $this->createHookContainer(),
117 $mwServices->getReadOnlyMode(),
118 $this->createNoOpMock( UserNameUtils
::class ),
119 $mwServices->getBlockManager(),
120 $mwServices->getWatchlistManager(),
121 $mwServices->getDBLoadBalancer(),
122 $mwServices->getContentLanguage(),
123 $mwServices->getLanguageConverterFactory(),
124 $this->createMock( BotPasswordStore
::class ),
125 $mwServices->getUserFactory(),
126 $mwServices->getUserIdentityLookup(),
127 $mwServices->getUserOptionsManager()
129 $this->initProvider( $provider, null, null, $manager );
130 $provider = TestingAccessWrapper
::newFromObject( $provider );
132 $msg = wfMessage( 'foo' );
133 $skipReq = new ButtonAuthenticationRequest(
135 wfMessage( 'authprovider-resetpass-skip-label' ),
136 wfMessage( 'authprovider-resetpass-skip-help' )
138 $passReq = new PasswordAuthenticationRequest();
139 $passReq->action
= AuthManager
::ACTION_CHANGE
;
140 $passReq->password
= 'Foo';
141 $passReq->retype
= 'Bar';
142 DynamicPropertyTestHelper
::setDynamicProperty( $passReq, 'allow', StatusValue
::newGood() );
143 DynamicPropertyTestHelper
::setDynamicProperty( $passReq, 'done', false );
145 $passReq2 = $this->getMockBuilder( PasswordAuthenticationRequest
::class )
146 ->enableProxyingToOriginalMethods()
148 $passReq2->action
= AuthManager
::ACTION_CHANGE
;
149 $passReq2->password
= 'Foo';
150 $passReq2->retype
= 'Foo';
151 DynamicPropertyTestHelper
::setDynamicProperty( $passReq2, 'allow', StatusValue
::newGood() );
152 DynamicPropertyTestHelper
::setDynamicProperty( $passReq2, 'done', false );
154 $passReq3 = new PasswordAuthenticationRequest();
155 $passReq3->action
= AuthManager
::ACTION_LOGIN
;
156 $passReq3->password
= 'Foo';
157 $passReq3->retype
= 'Foo';
158 DynamicPropertyTestHelper
::setDynamicProperty( $passReq3, 'allow', StatusValue
::newGood() );
159 DynamicPropertyTestHelper
::setDynamicProperty( $passReq3, 'done', false );
162 AuthenticationResponse
::newAbstain(),
163 $provider->tryReset( $user, [] )
166 $manager->setAuthenticationSessionData( 'reset-pass', 'foo' );
168 $provider->tryReset( $user, [] );
169 $this->fail( 'Expected exception not thrown' );
170 } catch ( UnexpectedValueException
$ex ) {
171 $this->assertSame( 'reset-pass is not valid', $ex->getMessage() );
174 $manager->setAuthenticationSessionData( 'reset-pass', (object)[] );
176 $provider->tryReset( $user, [] );
177 $this->fail( 'Expected exception not thrown' );
178 } catch ( UnexpectedValueException
$ex ) {
179 $this->assertSame( 'reset-pass msg is missing', $ex->getMessage() );
182 $manager->setAuthenticationSessionData( 'reset-pass', [
186 $provider->tryReset( $user, [] );
187 $this->fail( 'Expected exception not thrown' );
188 } catch ( UnexpectedValueException
$ex ) {
189 $this->assertSame( 'reset-pass msg is not valid', $ex->getMessage() );
192 $manager->setAuthenticationSessionData( 'reset-pass', [
196 $provider->tryReset( $user, [] );
197 $this->fail( 'Expected exception not thrown' );
198 } catch ( UnexpectedValueException
$ex ) {
199 $this->assertSame( 'reset-pass hard is missing', $ex->getMessage() );
202 $manager->setAuthenticationSessionData( 'reset-pass', [
208 $provider->tryReset( $user, [] );
209 $this->fail( 'Expected exception not thrown' );
210 } catch ( UnexpectedValueException
$ex ) {
211 $this->assertSame( 'reset-pass req is not valid', $ex->getMessage() );
214 $manager->setAuthenticationSessionData( 'reset-pass', [
220 $provider->tryReset( $user, [ $passReq ] );
221 $this->fail( 'Expected exception not thrown' );
222 } catch ( UnexpectedValueException
$ex ) {
223 $this->assertSame( 'reset-pass req is not valid', $ex->getMessage() );
226 $manager->setAuthenticationSessionData( 'reset-pass', [
230 $res = $provider->tryReset( $user, [] );
231 $this->assertInstanceOf( AuthenticationResponse
::class, $res );
232 $this->assertSame( AuthenticationResponse
::UI
, $res->status
);
233 $this->assertEquals( $msg, $res->message
);
234 $this->assertCount( 1, $res->neededRequests
);
235 $this->assertInstanceOf(
236 PasswordAuthenticationRequest
::class,
237 $res->neededRequests
[0]
239 $this->assertNotNull( $manager->getAuthenticationSessionData( 'reset-pass' ) );
240 $this->assertFalse( DynamicPropertyTestHelper
::getDynamicProperty( $passReq, 'done' ) );
242 $manager->setAuthenticationSessionData( 'reset-pass', [
247 $res = $provider->tryReset( $user, [] );
248 $this->assertInstanceOf( AuthenticationResponse
::class, $res );
249 $this->assertSame( AuthenticationResponse
::UI
, $res->status
);
250 $this->assertEquals( $msg, $res->message
);
251 $this->assertCount( 2, $res->neededRequests
);
252 $expectedPassReq = clone $passReq;
253 $expectedPassReq->required
= AuthenticationRequest
::OPTIONAL
;
254 $this->assertEquals( $expectedPassReq, $res->neededRequests
[0] );
255 $this->assertEquals( $skipReq, $res->neededRequests
[1] );
256 $this->assertNotNull( $manager->getAuthenticationSessionData( 'reset-pass' ) );
257 $this->assertFalse( DynamicPropertyTestHelper
::getDynamicProperty( $passReq, 'done' ) );
259 $passReq->retype
= 'Bad';
260 $manager->setAuthenticationSessionData( 'reset-pass', [
265 $res = $provider->tryReset( $user, [ $skipReq, $passReq ] );
266 $this->assertEquals( AuthenticationResponse
::newPass(), $res );
267 $this->assertNull( $manager->getAuthenticationSessionData( 'reset-pass' ) );
268 $this->assertFalse( DynamicPropertyTestHelper
::getDynamicProperty( $passReq, 'done' ) );
270 $passReq->retype
= 'Bad';
271 $manager->setAuthenticationSessionData( 'reset-pass', [
275 $res = $provider->tryReset( $user, [ $skipReq, $passReq ] );
276 $this->assertSame( AuthenticationResponse
::UI
, $res->status
);
277 $this->assertSame( 'badretype', $res->message
->getKey() );
278 $this->assertCount( 1, $res->neededRequests
);
279 $this->assertInstanceOf(
280 PasswordAuthenticationRequest
::class,
281 $res->neededRequests
[0]
283 $this->assertNotNull( $manager->getAuthenticationSessionData( 'reset-pass' ) );
284 $this->assertFalse( DynamicPropertyTestHelper
::getDynamicProperty( $passReq, 'done' ) );
286 $manager->setAuthenticationSessionData( 'reset-pass', [
290 $res = $provider->tryReset( $user, [ $skipReq, $passReq3 ] );
291 $this->assertSame( AuthenticationResponse
::UI
, $res->status
);
292 $this->assertEquals( $msg, $res->message
);
293 $this->assertCount( 1, $res->neededRequests
);
294 $this->assertInstanceOf(
295 PasswordAuthenticationRequest
::class,
296 $res->neededRequests
[0]
298 $this->assertNotNull( $manager->getAuthenticationSessionData( 'reset-pass' ) );
299 $this->assertFalse( DynamicPropertyTestHelper
::getDynamicProperty( $passReq, 'done' ) );
301 $passReq->retype
= $passReq->password
;
302 DynamicPropertyTestHelper
::setDynamicProperty( $passReq, 'allow', StatusValue
::newFatal( 'arbitrary-fail' ) );
303 $res = $provider->tryReset( $user, [ $skipReq, $passReq ] );
304 $this->assertSame( AuthenticationResponse
::UI
, $res->status
);
305 $this->assertSame( 'arbitrary-fail', $res->message
->getKey() );
306 $this->assertCount( 1, $res->neededRequests
);
307 $this->assertInstanceOf(
308 PasswordAuthenticationRequest
::class,
309 $res->neededRequests
[0]
311 $this->assertNotNull( $manager->getAuthenticationSessionData( 'reset-pass' ) );
312 $this->assertFalse( DynamicPropertyTestHelper
::getDynamicProperty( $passReq, 'done' ) );
314 DynamicPropertyTestHelper
::setDynamicProperty( $passReq, 'allow', StatusValue
::newGood() );
315 $res = $provider->tryReset( $user, [ $skipReq, $passReq ] );
316 $this->assertEquals( AuthenticationResponse
::newPass(), $res );
317 $this->assertNull( $manager->getAuthenticationSessionData( 'reset-pass' ) );
318 $this->assertTrue( DynamicPropertyTestHelper
::getDynamicProperty( $passReq, 'done' ) );
320 $manager->setAuthenticationSessionData( 'reset-pass', [
325 $res = $provider->tryReset( $user, [ $passReq2 ] );
326 $this->assertEquals( AuthenticationResponse
::newPass(), $res );
327 $this->assertNull( $manager->getAuthenticationSessionData( 'reset-pass' ) );
328 $this->assertTrue( DynamicPropertyTestHelper
::getDynamicProperty( $passReq2, 'done' ) );
330 DynamicPropertyTestHelper
::setDynamicProperty( $passReq, 'done', false );
331 DynamicPropertyTestHelper
::setDynamicProperty( $passReq2, 'done', false );
332 $manager->setAuthenticationSessionData( 'reset-pass', [
337 $res = $provider->tryReset( $user, [ $passReq ] );
338 $this->assertInstanceOf( AuthenticationResponse
::class, $res );
339 $this->assertSame( AuthenticationResponse
::UI
, $res->status
);
340 $this->assertEquals( $msg, $res->message
);
341 $this->assertCount( 2, $res->neededRequests
);
342 $expectedPassReq = clone $passReq2;
343 $expectedPassReq->required
= AuthenticationRequest
::OPTIONAL
;
344 $this->assertEquals( $expectedPassReq, $res->neededRequests
[0] );
345 $this->assertEquals( $skipReq, $res->neededRequests
[1] );
346 $this->assertNotNull( $manager->getAuthenticationSessionData( 'reset-pass' ) );
347 $this->assertFalse( DynamicPropertyTestHelper
::getDynamicProperty( $passReq, 'done' ) );
348 $this->assertFalse( DynamicPropertyTestHelper
::getDynamicProperty( $passReq2, 'done' ) );