Localisation updates from https://translatewiki.net.
[mediawiki.git] / tests / phpunit / includes / auth / ConfirmLinkSecondaryAuthenticationProviderTest.php
blobeb149f611e7fe29f8af4430f0eb5a1826e2371fb
1 <?php
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;
19 use StatusValue;
20 use stdClass;
21 use Wikimedia\TestingAccessWrapper;
23 /**
24 * @group AuthManager
25 * @covers \MediaWiki\Auth\ConfirmLinkSecondaryAuthenticationProvider
27 class ConfirmLinkSecondaryAuthenticationProviderTest extends MediaWikiIntegrationTestCase {
28 use AuthenticationProviderTestTrait;
29 use DummyServicesTrait;
31 /**
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() {
43 return [
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 );
54 $obj = new stdClass;
56 $mock = $this->getMockBuilder( ConfirmLinkSecondaryAuthenticationProvider::class )
57 ->onlyMethods( [ 'beginLinkAttempt', 'continueLinkAttempt' ] )
58 ->getMock();
59 $mock->expects( $this->once() )->method( 'beginLinkAttempt' )
60 ->with( $this->identicalTo( $user ), $this->identicalTo( AuthManager::AUTHN_STATE ) )
61 ->willReturn( $obj );
62 $mock->expects( $this->never() )->method( 'continueLinkAttempt' );
64 $this->assertSame( $obj, $mock->beginSecondaryAuthentication( $user, [] ) );
67 public function testContinueSecondaryAuthentication() {
68 $user = $this->createMock( User::class );
69 $obj = new stdClass;
70 $reqs = [ new stdClass ];
72 $mock = $this->getMockBuilder( ConfirmLinkSecondaryAuthenticationProvider::class )
73 ->onlyMethods( [ 'beginLinkAttempt', 'continueLinkAttempt' ] )
74 ->getMock();
75 $mock->expects( $this->never() )->method( 'beginLinkAttempt' );
76 $mock->expects( $this->once() )->method( 'continueLinkAttempt' )
77 ->with(
78 $this->identicalTo( $user ),
79 $this->identicalTo( AuthManager::AUTHN_STATE ),
80 $this->identicalTo( $reqs )
82 ->willReturn( $obj );
84 $this->assertSame( $obj, $mock->continueSecondaryAuthentication( $user, $reqs ) );
87 public function testBeginSecondaryAccountCreation() {
88 $user = $this->createMock( User::class );
89 $obj = new stdClass;
91 $mock = $this->getMockBuilder( ConfirmLinkSecondaryAuthenticationProvider::class )
92 ->onlyMethods( [ 'beginLinkAttempt', 'continueLinkAttempt' ] )
93 ->getMock();
94 $mock->expects( $this->once() )->method( 'beginLinkAttempt' )
95 ->with( $this->identicalTo( $user ), $this->identicalTo( AuthManager::ACCOUNT_CREATION_STATE ) )
96 ->willReturn( $obj );
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 );
104 $obj = new stdClass;
105 $reqs = [ new stdClass ];
107 $mock = $this->getMockBuilder( ConfirmLinkSecondaryAuthenticationProvider::class )
108 ->onlyMethods( [ 'beginLinkAttempt', 'continueLinkAttempt' ] )
109 ->getMock();
110 $mock->expects( $this->never() )->method( 'beginLinkAttempt' );
111 $mock->expects( $this->once() )->method( 'continueLinkAttempt' )
112 ->with(
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() {
127 $reqs = [];
129 $mb = $this->getMockBuilder( AuthenticationRequest::class )
130 ->onlyMethods( [ 'getUniqueId' ] );
131 for ( $i = 1; $i <= 3; $i++ ) {
132 $uid = "Request$i";
133 $req = $mb->getMockForAbstractClass();
134 $req->method( 'getUniqueId' )->willReturn( $uid );
135 $reqs[$uid] = $req;
138 return $reqs;
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( [
157 $request,
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()
173 ->getMock();
174 $manager->method( 'allowsAuthenticationDataChange' )
175 ->willReturnCallback( static function ( $req ) {
176 return $req->getUniqueId() !== 'BadReq'
177 ? StatusValue::newGood()
178 : StatusValue::newFatal( 'no' );
179 } );
180 $this->initProvider( $provider, null, null, $manager );
182 $this->assertEquals(
183 AuthenticationResponse::newAbstain(),
184 $providerPriv->beginLinkAttempt( $user, 'state' )
187 $request->getSession()->setSecret( 'state', [
188 'maybeLink' => [],
189 ] );
190 $this->assertEquals(
191 AuthenticationResponse::newAbstain(),
192 $providerPriv->beginLinkAttempt( $user, 'state' )
195 $reqs = $this->getLinkRequests();
196 $request->getSession()->setSecret( 'state', [
197 'maybeLink' => $reqs + [ 'BadReq' => $badReq ]
198 ] );
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 );
216 $obj = new stdClass;
217 $reqs = $this->getLinkRequests();
219 $done = [];
221 // First, test the pass-through for not containing the ConfirmLinkAuthenticationRequest
222 $mock = $this->getMockBuilder( ConfirmLinkSecondaryAuthenticationProvider::class )
223 ->onlyMethods( [ 'beginLinkAttempt' ] )
224 ->getMock();
225 $mock->expects( $this->once() )->method( 'beginLinkAttempt' )
226 ->with( $this->identicalTo( $user ), $this->identicalTo( 'state' ) )
227 ->willReturn( $obj );
228 $this->assertSame(
229 $obj,
230 TestingAccessWrapper::newFromObject( $mock )->continueLinkAttempt( $user, 'state', $reqs )
233 // Now test the actual functioning
234 $provider = $this->getMockBuilder( ConfirmLinkSecondaryAuthenticationProvider::class )
235 ->onlyMethods( [
236 'beginLinkAttempt', 'providerAllowsAuthenticationDataChange',
237 'providerChangeAuthenticationData'
239 ->getMock();
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();
245 } );
246 $provider->method( 'providerChangeAuthenticationData' )
247 ->willReturnCallback( static function ( $req ) use ( &$done ) {
248 $done[$req->getUniqueId()] = true;
249 } );
250 $config = new HashConfig( [
251 MainConfigNames::AuthManagerConfig => [
252 'preauth' => [],
253 'primaryauth' => [],
254 'secondaryauth' => [
255 [ 'factory' => static function () use ( $provider ) {
256 return $provider;
257 } ],
260 ] );
261 $request = new FauxRequest();
262 $mwServices = $this->getServiceContainer();
263 $manager = new AuthManager(
264 $request,
265 $config,
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 );
285 $this->assertEquals(
286 AuthenticationResponse::newAbstain(),
287 $provider->continueLinkAttempt( $user, 'state', [ $req ] )
290 $request->getSession()->setSecret( 'state', [
291 'maybeLink' => [],
292 ] );
293 $this->assertEquals(
294 AuthenticationResponse::newAbstain(),
295 $provider->continueLinkAttempt( $user, 'state', [ $req ] )
298 $request->getSession()->setSecret( 'state', [
299 'maybeLink' => $reqs
300 ] );
301 $this->assertEquals(
302 AuthenticationResponse::newPass(),
303 $provider->continueLinkAttempt( $user, 'state', [ $req ] )
305 $this->assertSame( [], $done );
307 $request->getSession()->setSecret( 'state', [
308 'maybeLink' => [ $reqs['Request2'] ],
309 ] );
310 $req->confirmedLinkIDs = [ 'Request1', 'Request2' ];
311 $res = $provider->continueLinkAttempt( $user, 'state', [ $req ] );
312 $this->assertEquals( AuthenticationResponse::newPass(), $res );
313 $this->assertSame( [ 'Request2' => true ], $done );
314 $done = [];
316 $request->getSession()->setSecret( 'state', [
317 'maybeLink' => $reqs,
318 ] );
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 );
323 $done = [];
325 $request->getSession()->setSecret( 'state', [
326 'maybeLink' => $reqs,
327 ] );
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 );
334 $done = [];
336 $res = $provider->continueLinkAttempt( $user, 'state', [ $res->neededRequests[0] ] );
337 $this->assertEquals( AuthenticationResponse::newPass(), $res );
338 $this->assertSame( [], $done );