3 namespace MediaWiki\Tests\Auth
;
5 use MediaWiki\Auth\AuthenticationRequest
;
6 use MediaWiki\Auth\AuthenticationResponse
;
7 use MediaWiki\Auth\AuthManager
;
8 use MediaWiki\Auth\ButtonAuthenticationRequest
;
9 use MediaWiki\Auth\ConfirmLinkAuthenticationRequest
;
10 use MediaWiki\Auth\ConfirmLinkSecondaryAuthenticationProvider
;
11 use MediaWiki\Config\HashConfig
;
12 use MediaWiki\MainConfigNames
;
13 use MediaWiki\Request\FauxRequest
;
14 use MediaWiki\Tests\Unit\Auth\AuthenticationProviderTestTrait
;
15 use MediaWiki\Tests\Unit\DummyServicesTrait
;
16 use MediaWiki\User\User
;
17 use MediaWiki\User\UserNameUtils
;
18 use MediaWikiIntegrationTestCase
;
21 use Wikimedia\TestingAccessWrapper
;
25 * @covers \MediaWiki\Auth\ConfirmLinkSecondaryAuthenticationProvider
27 class ConfirmLinkSecondaryAuthenticationProviderTest
extends MediaWikiIntegrationTestCase
{
28 use AuthenticationProviderTestTrait
;
29 use DummyServicesTrait
;
32 * @dataProvider provideGetAuthenticationRequests
33 * @param string $action
34 * @param array $response
36 public function testGetAuthenticationRequests( $action, $response ) {
37 $provider = new ConfirmLinkSecondaryAuthenticationProvider();
39 $this->assertEquals( $response, $provider->getAuthenticationRequests( $action, [] ) );
42 public static function provideGetAuthenticationRequests() {
44 [ AuthManager
::ACTION_LOGIN
, [] ],
45 [ AuthManager
::ACTION_CREATE
, [] ],
46 [ AuthManager
::ACTION_LINK
, [] ],
47 [ AuthManager
::ACTION_CHANGE
, [] ],
48 [ AuthManager
::ACTION_REMOVE
, [] ],
52 public function testBeginSecondaryAuthentication() {
53 $user = $this->createMock( User
::class );
56 $mock = $this->getMockBuilder( ConfirmLinkSecondaryAuthenticationProvider
::class )
57 ->onlyMethods( [ 'beginLinkAttempt', 'continueLinkAttempt' ] )
59 $mock->expects( $this->once() )->method( 'beginLinkAttempt' )
60 ->with( $this->identicalTo( $user ), $this->identicalTo( AuthManager
::AUTHN_STATE
) )
62 $mock->expects( $this->never() )->method( 'continueLinkAttempt' );
64 $this->assertSame( $obj, $mock->beginSecondaryAuthentication( $user, [] ) );
67 public function testContinueSecondaryAuthentication() {
68 $user = $this->createMock( User
::class );
70 $reqs = [ new stdClass
];
72 $mock = $this->getMockBuilder( ConfirmLinkSecondaryAuthenticationProvider
::class )
73 ->onlyMethods( [ 'beginLinkAttempt', 'continueLinkAttempt' ] )
75 $mock->expects( $this->never() )->method( 'beginLinkAttempt' );
76 $mock->expects( $this->once() )->method( 'continueLinkAttempt' )
78 $this->identicalTo( $user ),
79 $this->identicalTo( AuthManager
::AUTHN_STATE
),
80 $this->identicalTo( $reqs )
84 $this->assertSame( $obj, $mock->continueSecondaryAuthentication( $user, $reqs ) );
87 public function testBeginSecondaryAccountCreation() {
88 $user = $this->createMock( User
::class );
91 $mock = $this->getMockBuilder( ConfirmLinkSecondaryAuthenticationProvider
::class )
92 ->onlyMethods( [ 'beginLinkAttempt', 'continueLinkAttempt' ] )
94 $mock->expects( $this->once() )->method( 'beginLinkAttempt' )
95 ->with( $this->identicalTo( $user ), $this->identicalTo( AuthManager
::ACCOUNT_CREATION_STATE
) )
97 $mock->expects( $this->never() )->method( 'continueLinkAttempt' );
99 $this->assertSame( $obj, $mock->beginSecondaryAccountCreation( $user, $user, [] ) );
102 public function testContinueSecondaryAccountCreation() {
103 $user = $this->createMock( User
::class );
105 $reqs = [ new stdClass
];
107 $mock = $this->getMockBuilder( ConfirmLinkSecondaryAuthenticationProvider
::class )
108 ->onlyMethods( [ 'beginLinkAttempt', 'continueLinkAttempt' ] )
110 $mock->expects( $this->never() )->method( 'beginLinkAttempt' );
111 $mock->expects( $this->once() )->method( 'continueLinkAttempt' )
113 $this->identicalTo( $user ),
114 $this->identicalTo( AuthManager
::ACCOUNT_CREATION_STATE
),
115 $this->identicalTo( $reqs )
117 ->willReturn( $obj );
119 $this->assertSame( $obj, $mock->continueSecondaryAccountCreation( $user, $user, $reqs ) );
123 * Get requests for testing
124 * @return AuthenticationRequest[]
126 private function getLinkRequests() {
129 $mb = $this->getMockBuilder( AuthenticationRequest
::class )
130 ->onlyMethods( [ 'getUniqueId' ] );
131 for ( $i = 1; $i <= 3; $i++
) {
133 $req = $mb->getMockForAbstractClass();
134 $req->method( 'getUniqueId' )->willReturn( $uid );
141 public function testBeginLinkAttempt() {
142 $badReq = $this->getMockBuilder( AuthenticationRequest
::class )
143 ->onlyMethods( [ 'getUniqueId' ] )
144 ->getMockForAbstractClass();
145 $badReq->method( 'getUniqueId' )
146 ->willReturn( "BadReq" );
148 $user = $this->createMock( User
::class );
149 $provider = new ConfirmLinkSecondaryAuthenticationProvider
;
150 $providerPriv = TestingAccessWrapper
::newFromObject( $provider );
151 $request = new FauxRequest();
152 $mwServices = $this->getServiceContainer();
154 $manager = $this->getMockBuilder( AuthManager
::class )
155 ->onlyMethods( [ 'allowsAuthenticationDataChange' ] )
156 ->setConstructorArgs( [
158 $mwServices->getMainConfig(),
159 $mwServices->getObjectFactory(),
160 $mwServices->getHookContainer(),
161 $mwServices->getReadOnlyMode(),
162 $this->createNoOpMock( UserNameUtils
::class ),
163 $mwServices->getBlockManager(),
164 $mwServices->getWatchlistManager(),
165 $mwServices->getDBLoadBalancer(),
166 $mwServices->getContentLanguage(),
167 $mwServices->getLanguageConverterFactory(),
168 $mwServices->getBotPasswordStore(),
169 $mwServices->getUserFactory(),
170 $mwServices->getUserIdentityLookup(),
171 $mwServices->getUserOptionsManager()
174 $manager->method( 'allowsAuthenticationDataChange' )
175 ->willReturnCallback( static function ( $req ) {
176 return $req->getUniqueId() !== 'BadReq'
177 ? StatusValue
::newGood()
178 : StatusValue
::newFatal( 'no' );
180 $this->initProvider( $provider, null, null, $manager );
183 AuthenticationResponse
::newAbstain(),
184 $providerPriv->beginLinkAttempt( $user, 'state' )
187 $request->getSession()->setSecret( 'state', [
191 AuthenticationResponse
::newAbstain(),
192 $providerPriv->beginLinkAttempt( $user, 'state' )
195 $reqs = $this->getLinkRequests();
196 $request->getSession()->setSecret( 'state', [
197 'maybeLink' => $reqs +
[ 'BadReq' => $badReq ]
199 $res = $providerPriv->beginLinkAttempt( $user, 'state' );
200 $this->assertInstanceOf( AuthenticationResponse
::class, $res );
201 $this->assertSame( AuthenticationResponse
::UI
, $res->status
);
202 $this->assertSame( 'authprovider-confirmlink-message', $res->message
->getKey() );
203 $this->assertCount( 1, $res->neededRequests
);
204 $req = $res->neededRequests
[0];
205 $this->assertInstanceOf( ConfirmLinkAuthenticationRequest
::class, $req );
206 $expectReqs = $this->getLinkRequests();
207 foreach ( $expectReqs as $r ) {
208 $r->action
= AuthManager
::ACTION_CHANGE
;
209 $r->username
= $user->getName();
211 $this->assertEquals( $expectReqs, TestingAccessWrapper
::newFromObject( $req )->linkRequests
);
214 public function testContinueLinkAttempt() {
215 $user = $this->createMock( User
::class );
217 $reqs = $this->getLinkRequests();
221 // First, test the pass-through for not containing the ConfirmLinkAuthenticationRequest
222 $mock = $this->getMockBuilder( ConfirmLinkSecondaryAuthenticationProvider
::class )
223 ->onlyMethods( [ 'beginLinkAttempt' ] )
225 $mock->expects( $this->once() )->method( 'beginLinkAttempt' )
226 ->with( $this->identicalTo( $user ), $this->identicalTo( 'state' ) )
227 ->willReturn( $obj );
230 TestingAccessWrapper
::newFromObject( $mock )->continueLinkAttempt( $user, 'state', $reqs )
233 // Now test the actual functioning
234 $provider = $this->getMockBuilder( ConfirmLinkSecondaryAuthenticationProvider
::class )
236 'beginLinkAttempt', 'providerAllowsAuthenticationDataChange',
237 'providerChangeAuthenticationData'
240 $provider->expects( $this->never() )->method( 'beginLinkAttempt' );
241 $provider->method( 'providerAllowsAuthenticationDataChange' )
242 ->willReturnCallback( static function ( $req ) {
243 return $req->getUniqueId() === 'Request3'
244 ? StatusValue
::newFatal( 'foo' ) : StatusValue
::newGood();
246 $provider->method( 'providerChangeAuthenticationData' )
247 ->willReturnCallback( static function ( $req ) use ( &$done ) {
248 $done[$req->getUniqueId()] = true;
250 $config = new HashConfig( [
251 MainConfigNames
::AuthManagerConfig
=> [
255 [ 'factory' => static function () use ( $provider ) {
261 $request = new FauxRequest();
262 $mwServices = $this->getServiceContainer();
263 $manager = new AuthManager(
266 $this->getDummyObjectFactory(),
267 $mwServices->getHookContainer(),
268 $mwServices->getReadOnlyMode(),
269 $mwServices->getUserNameUtils(),
270 $mwServices->getBlockManager(),
271 $mwServices->getWatchlistManager(),
272 $mwServices->getDBLoadBalancer(),
273 $mwServices->getContentLanguage(),
274 $mwServices->getLanguageConverterFactory(),
275 $mwServices->getBotPasswordStore(),
276 $mwServices->getUserFactory(),
277 $mwServices->getUserIdentityLookup(),
278 $mwServices->getUserOptionsManager()
280 $this->initProvider( $provider, null, null, $manager );
281 $provider = TestingAccessWrapper
::newFromObject( $provider );
283 $req = new ConfirmLinkAuthenticationRequest( $reqs );
286 AuthenticationResponse
::newAbstain(),
287 $provider->continueLinkAttempt( $user, 'state', [ $req ] )
290 $request->getSession()->setSecret( 'state', [
294 AuthenticationResponse
::newAbstain(),
295 $provider->continueLinkAttempt( $user, 'state', [ $req ] )
298 $request->getSession()->setSecret( 'state', [
302 AuthenticationResponse
::newPass(),
303 $provider->continueLinkAttempt( $user, 'state', [ $req ] )
305 $this->assertSame( [], $done );
307 $request->getSession()->setSecret( 'state', [
308 'maybeLink' => [ $reqs['Request2'] ],
310 $req->confirmedLinkIDs
= [ 'Request1', 'Request2' ];
311 $res = $provider->continueLinkAttempt( $user, 'state', [ $req ] );
312 $this->assertEquals( AuthenticationResponse
::newPass(), $res );
313 $this->assertSame( [ 'Request2' => true ], $done );
316 $request->getSession()->setSecret( 'state', [
317 'maybeLink' => $reqs,
319 $req->confirmedLinkIDs
= [ 'Request1', 'Request2' ];
320 $res = $provider->continueLinkAttempt( $user, 'state', [ $req ] );
321 $this->assertEquals( AuthenticationResponse
::newPass(), $res );
322 $this->assertSame( [ 'Request1' => true, 'Request2' => true ], $done );
325 $request->getSession()->setSecret( 'state', [
326 'maybeLink' => $reqs,
328 $req->confirmedLinkIDs
= [ 'Request1', 'Request3' ];
329 $res = $provider->continueLinkAttempt( $user, 'state', [ $req ] );
330 $this->assertEquals( AuthenticationResponse
::UI
, $res->status
);
331 $this->assertCount( 1, $res->neededRequests
);
332 $this->assertInstanceOf( ButtonAuthenticationRequest
::class, $res->neededRequests
[0] );
333 $this->assertSame( [ 'Request1' => true ], $done );
336 $res = $provider->continueLinkAttempt( $user, 'state', [ $res->neededRequests
[0] ] );
337 $this->assertEquals( AuthenticationResponse
::newPass(), $res );
338 $this->assertSame( [], $done );