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