580ef6cd4229a8b7ddcb9bf1adfc10faa9a04896
[lhc/web/wiklou.git] / tests / phpunit / includes / auth / ConfirmLinkSecondaryAuthenticationProviderTest.php
1 <?php
2
3 namespace MediaWiki\Auth;
4
5 /**
6 * @group AuthManager
7 * @covers MediaWiki\Auth\ConfirmLinkSecondaryAuthenticationProvider
8 */
9 class ConfirmLinkSecondaryAuthenticationProviderTest extends \MediaWikiTestCase {
10 protected function setUp() {
11 global $wgDisableAuthManager;
12
13 parent::setUp();
14 if ( $wgDisableAuthManager ) {
15 $this->markTestSkipped( '$wgDisableAuthManager is set' );
16 }
17 }
18
19 /**
20 * @dataProvider provideGetAuthenticationRequests
21 * @param string $action
22 * @param array $response
23 */
24 public function testGetAuthenticationRequests( $action, $response ) {
25 $provider = new ConfirmLinkSecondaryAuthenticationProvider();
26
27 $this->assertEquals( $response, $provider->getAuthenticationRequests( $action, [] ) );
28 }
29
30 public static function provideGetAuthenticationRequests() {
31 return [
32 [ AuthManager::ACTION_LOGIN, [] ],
33 [ AuthManager::ACTION_CREATE, [] ],
34 [ AuthManager::ACTION_LINK, [] ],
35 [ AuthManager::ACTION_CHANGE, [] ],
36 [ AuthManager::ACTION_REMOVE, [] ],
37 ];
38 }
39
40 public function testBeginSecondaryAuthentication() {
41 $user = \User::newFromName( 'UTSysop' );
42 $obj = new \stdClass;
43
44 $mock = $this->getMockBuilder( ConfirmLinkSecondaryAuthenticationProvider::class )
45 ->setMethods( [ 'beginLinkAttempt', 'continueLinkAttempt' ] )
46 ->getMock();
47 $mock->expects( $this->once() )->method( 'beginLinkAttempt' )
48 ->with( $this->identicalTo( $user ), $this->identicalTo( 'AuthManager::authnState' ) )
49 ->will( $this->returnValue( $obj ) );
50 $mock->expects( $this->never() )->method( 'continueLinkAttempt' );
51
52 $this->assertSame( $obj, $mock->beginSecondaryAuthentication( $user, [] ) );
53 }
54
55 public function testContinueSecondaryAuthentication() {
56 $user = \User::newFromName( 'UTSysop' );
57 $obj = new \stdClass;
58 $reqs = [ new \stdClass ];
59
60 $mock = $this->getMockBuilder( ConfirmLinkSecondaryAuthenticationProvider::class )
61 ->setMethods( [ 'beginLinkAttempt', 'continueLinkAttempt' ] )
62 ->getMock();
63 $mock->expects( $this->never() )->method( 'beginLinkAttempt' );
64 $mock->expects( $this->once() )->method( 'continueLinkAttempt' )
65 ->with(
66 $this->identicalTo( $user ),
67 $this->identicalTo( 'AuthManager::authnState' ),
68 $this->identicalTo( $reqs )
69 )
70 ->will( $this->returnValue( $obj ) );
71
72 $this->assertSame( $obj, $mock->continueSecondaryAuthentication( $user, $reqs ) );
73 }
74
75 public function testBeginSecondaryAccountCreation() {
76 $user = \User::newFromName( 'UTSysop' );
77 $obj = new \stdClass;
78
79 $mock = $this->getMockBuilder( ConfirmLinkSecondaryAuthenticationProvider::class )
80 ->setMethods( [ 'beginLinkAttempt', 'continueLinkAttempt' ] )
81 ->getMock();
82 $mock->expects( $this->once() )->method( 'beginLinkAttempt' )
83 ->with( $this->identicalTo( $user ), $this->identicalTo( 'AuthManager::accountCreationState' ) )
84 ->will( $this->returnValue( $obj ) );
85 $mock->expects( $this->never() )->method( 'continueLinkAttempt' );
86
87 $this->assertSame( $obj, $mock->beginSecondaryAccountCreation( $user, $user, [] ) );
88 }
89
90 public function testContinueSecondaryAccountCreation() {
91 $user = \User::newFromName( 'UTSysop' );
92 $obj = new \stdClass;
93 $reqs = [ new \stdClass ];
94
95 $mock = $this->getMockBuilder( ConfirmLinkSecondaryAuthenticationProvider::class )
96 ->setMethods( [ 'beginLinkAttempt', 'continueLinkAttempt' ] )
97 ->getMock();
98 $mock->expects( $this->never() )->method( 'beginLinkAttempt' );
99 $mock->expects( $this->once() )->method( 'continueLinkAttempt' )
100 ->with(
101 $this->identicalTo( $user ),
102 $this->identicalTo( 'AuthManager::accountCreationState' ),
103 $this->identicalTo( $reqs )
104 )
105 ->will( $this->returnValue( $obj ) );
106
107 $this->assertSame( $obj, $mock->continueSecondaryAccountCreation( $user, $user, $reqs ) );
108 }
109
110 /**
111 * Get requests for testing
112 * @return AuthenticationRequest[]
113 */
114 private function getLinkRequests() {
115 $reqs = [];
116
117 $mb = $this->getMockBuilder( AuthenticationRequest::class )
118 ->setMethods( [ 'getUniqueId' ] );
119 for ( $i = 1; $i <= 3; $i++ ) {
120 $req = $mb->getMockForAbstractClass();
121 $req->expects( $this->any() )->method( 'getUniqueId' )
122 ->will( $this->returnValue( "Request$i" ) );
123 $req->id = $i - 1;
124 $reqs[$req->getUniqueId()] = $req;
125 }
126
127 return $reqs;
128 }
129
130 public function testBeginLinkAttempt() {
131 $badReq = $this->getMockBuilder( AuthenticationRequest::class )
132 ->setMethods( [ 'getUniqueId' ] )
133 ->getMockForAbstractClass();
134 $badReq->expects( $this->any() )->method( 'getUniqueId' )
135 ->will( $this->returnValue( "BadReq" ) );
136
137 $user = \User::newFromName( 'UTSysop' );
138 $provider = \TestingAccessWrapper::newFromObject(
139 new ConfirmLinkSecondaryAuthenticationProvider
140 );
141 $request = new \FauxRequest();
142 $manager = $this->getMockBuilder( AuthManager::class )
143 ->setMethods( [ 'allowsAuthenticationDataChange' ] )
144 ->setConstructorArgs( [ $request, \RequestContext::getMain()->getConfig() ] )
145 ->getMock();
146 $manager->expects( $this->any() )->method( 'allowsAuthenticationDataChange' )
147 ->will( $this->returnCallback( function ( $req ) {
148 return $req->getUniqueId() !== 'BadReq'
149 ? \StatusValue::newGood()
150 : \StatusValue::newFatal( 'no' );
151 } ) );
152 $provider->setManager( $manager );
153
154 $this->assertEquals(
155 AuthenticationResponse::newAbstain(),
156 $provider->beginLinkAttempt( $user, 'state' )
157 );
158
159 $request->getSession()->setSecret( 'state', [
160 'maybeLink' => [],
161 ] );
162 $this->assertEquals(
163 AuthenticationResponse::newAbstain(),
164 $provider->beginLinkAttempt( $user, 'state' )
165 );
166
167 $reqs = $this->getLinkRequests();
168 $request->getSession()->setSecret( 'state', [
169 'maybeLink' => $reqs + [ 'BadReq' => $badReq ]
170 ] );
171 $res = $provider->beginLinkAttempt( $user, 'state' );
172 $this->assertInstanceOf( AuthenticationResponse::class, $res );
173 $this->assertSame( AuthenticationResponse::UI, $res->status );
174 $this->assertSame( 'authprovider-confirmlink-message', $res->message->getKey() );
175 $this->assertCount( 1, $res->neededRequests );
176 $req = $res->neededRequests[0];
177 $this->assertInstanceOf( ConfirmLinkAuthenticationRequest::class, $req );
178 $expectReqs = $this->getLinkRequests();
179 foreach ( $expectReqs as $r ) {
180 $r->action = AuthManager::ACTION_CHANGE;
181 $r->username = $user->getName();
182 }
183 $this->assertEquals( $expectReqs, \TestingAccessWrapper::newFromObject( $req )->linkRequests );
184 }
185
186 public function testContinueLinkAttempt() {
187 $user = \User::newFromName( 'UTSysop' );
188 $obj = new \stdClass;
189 $reqs = $this->getLinkRequests();
190
191 $done = [ false, false, false ];
192
193 // First, test the pass-through for not containing the ConfirmLinkAuthenticationRequest
194 $mock = $this->getMockBuilder( ConfirmLinkSecondaryAuthenticationProvider::class )
195 ->setMethods( [ 'beginLinkAttempt' ] )
196 ->getMock();
197 $mock->expects( $this->once() )->method( 'beginLinkAttempt' )
198 ->with( $this->identicalTo( $user ), $this->identicalTo( 'state' ) )
199 ->will( $this->returnValue( $obj ) );
200 $this->assertSame(
201 $obj,
202 \TestingAccessWrapper::newFromObject( $mock )->continueLinkAttempt( $user, 'state', $reqs )
203 );
204
205 // Now test the actual functioning
206 $provider = $this->getMockBuilder( ConfirmLinkSecondaryAuthenticationProvider::class )
207 ->setMethods( [
208 'beginLinkAttempt', 'providerAllowsAuthenticationDataChange',
209 'providerChangeAuthenticationData'
210 ] )
211 ->getMock();
212 $provider->expects( $this->never() )->method( 'beginLinkAttempt' );
213 $provider->expects( $this->any() )->method( 'providerAllowsAuthenticationDataChange' )
214 ->will( $this->returnCallback( function ( $req ) use ( $reqs ) {
215 return $req->getUniqueId() === 'Request3'
216 ? \StatusValue::newFatal( 'foo' ) : \StatusValue::newGood();
217 } ) );
218 $provider->expects( $this->any() )->method( 'providerChangeAuthenticationData' )
219 ->will( $this->returnCallback( function ( $req ) use ( &$done ) {
220 $done[$req->id] = true;
221 } ) );
222 $config = new \HashConfig( [
223 'AuthManagerConfig' => [
224 'preauth' => [],
225 'primaryauth' => [],
226 'secondaryauth' => [
227 [ 'factory' => function () use ( $provider ) {
228 return $provider;
229 } ],
230 ],
231 ],
232 ] );
233 $request = new \FauxRequest();
234 $manager = new AuthManager( $request, $config );
235 $provider->setManager( $manager );
236 $provider = \TestingAccessWrapper::newFromObject( $provider );
237
238 $req = new ConfirmLinkAuthenticationRequest( $reqs );
239
240 $this->assertEquals(
241 AuthenticationResponse::newAbstain(),
242 $provider->continueLinkAttempt( $user, 'state', [ $req ] )
243 );
244
245 $request->getSession()->setSecret( 'state', [
246 'maybeLink' => [],
247 ] );
248 $this->assertEquals(
249 AuthenticationResponse::newAbstain(),
250 $provider->continueLinkAttempt( $user, 'state', [ $req ] )
251 );
252
253 $request->getSession()->setSecret( 'state', [
254 'maybeLink' => $reqs
255 ] );
256 $this->assertEquals(
257 AuthenticationResponse::newPass(),
258 $res = $provider->continueLinkAttempt( $user, 'state', [ $req ] )
259 );
260 $this->assertSame( [ false, false, false ], $done );
261
262 $request->getSession()->setSecret( 'state', [
263 'maybeLink' => [ $reqs['Request2'] ],
264 ] );
265 $req->confirmedLinkIDs = [ 'Request1', 'Request2' ];
266 $res = $provider->continueLinkAttempt( $user, 'state', [ $req ] );
267 $this->assertEquals( AuthenticationResponse::newPass(), $res );
268 $this->assertSame( [ false, true, false ], $done );
269 $done = [ false, false, false ];
270
271 $request->getSession()->setSecret( 'state', [
272 'maybeLink' => $reqs,
273 ] );
274 $req->confirmedLinkIDs = [ 'Request1', 'Request2' ];
275 $res = $provider->continueLinkAttempt( $user, 'state', [ $req ] );
276 $this->assertEquals( AuthenticationResponse::newPass(), $res );
277 $this->assertSame( [ true, true, false ], $done );
278 $done = [ false, false, false ];
279
280 $request->getSession()->setSecret( 'state', [
281 'maybeLink' => $reqs,
282 ] );
283 $req->confirmedLinkIDs = [ 'Request1', 'Request3' ];
284 $res = $provider->continueLinkAttempt( $user, 'state', [ $req ] );
285 $this->assertEquals( AuthenticationResponse::UI, $res->status );
286 $this->assertCount( 1, $res->neededRequests );
287 $this->assertInstanceOf( ButtonAuthenticationRequest::class, $res->neededRequests[0] );
288 $this->assertSame( [ true, false, false ], $done );
289 $done = [ false, false, false ];
290
291 $res = $provider->continueLinkAttempt( $user, 'state', [ $res->neededRequests[0] ] );
292 $this->assertEquals( AuthenticationResponse::newPass(), $res );
293 $this->assertSame( [ false, false, false ], $done );
294 }
295
296 }