Localisation updates from https://translatewiki.net.
[mediawiki.git] / tests / phpunit / includes / auth / AuthenticationRequestTest.php
bloba518b915437ca78cb9bc06c820dc8b5ef0a9e96f
1 <?php
3 namespace MediaWiki\Tests\Auth;
5 use MediaWiki\Auth\AuthenticationRequest;
6 use MediaWiki\Auth\PasswordAuthenticationRequest;
7 use MediaWiki\Message\Message;
8 use MediaWikiIntegrationTestCase;
9 use UnexpectedValueException;
11 /**
12 * @group AuthManager
13 * @covers \MediaWiki\Auth\AuthenticationRequest
15 class AuthenticationRequestTest extends MediaWikiIntegrationTestCase {
16 public function testBasics() {
17 $mock = $this->getMockForAbstractClass( AuthenticationRequest::class );
19 $this->assertSame( get_class( $mock ), $mock->getUniqueId() );
21 $this->assertIsArray( $mock->getMetadata() );
23 $ret = $mock->describeCredentials();
24 $this->assertIsArray( $ret );
25 $this->assertArrayHasKey( 'provider', $ret );
26 $this->assertInstanceOf( Message::class, $ret['provider'] );
27 $this->assertArrayHasKey( 'account', $ret );
28 $this->assertInstanceOf( Message::class, $ret['account'] );
31 public function testLoadRequestsFromSubmission() {
32 $mb = $this->getMockBuilder( AuthenticationRequest::class )
33 ->onlyMethods( [ 'loadFromSubmission' ] );
35 $data = [ 'foo', 'bar' ];
37 $req1 = $mb->getMockForAbstractClass();
38 $req1->expects( $this->once() )->method( 'loadFromSubmission' )
39 ->with( $this->identicalTo( $data ) )
40 ->willReturn( false );
42 $req2 = $mb->getMockForAbstractClass();
43 $req2->expects( $this->once() )->method( 'loadFromSubmission' )
44 ->with( $this->identicalTo( $data ) )
45 ->willReturn( true );
47 $this->assertSame(
48 [ $req2 ],
49 AuthenticationRequest::loadRequestsFromSubmission( [ $req1, $req2 ], $data )
53 public function testGetRequestByClass() {
54 $mb = $this->getMockBuilder(
55 AuthenticationRequest::class, 'AuthenticationRequestTest_AuthenticationRequest2'
58 $reqs = [
59 $this->getMockForAbstractClass(
60 AuthenticationRequest::class, [], 'AuthenticationRequestTest_AuthenticationRequest1'
62 $mb->getMockForAbstractClass(),
63 $mb->getMockForAbstractClass(),
64 $this->getMockForAbstractClass(
65 PasswordAuthenticationRequest::class, [],
66 'AuthenticationRequestTest_PasswordAuthenticationRequest'
70 $this->assertNull( AuthenticationRequest::getRequestByClass(
71 $reqs, 'AuthenticationRequestTest_AuthenticationRequest0'
72 ) );
73 $this->assertSame( $reqs[0], AuthenticationRequest::getRequestByClass(
74 $reqs, 'AuthenticationRequestTest_AuthenticationRequest1'
75 ) );
76 $this->assertNull( AuthenticationRequest::getRequestByClass(
77 $reqs, 'AuthenticationRequestTest_AuthenticationRequest2'
78 ) );
79 $this->assertNull( AuthenticationRequest::getRequestByClass(
80 $reqs, PasswordAuthenticationRequest::class
81 ) );
82 $this->assertNull( AuthenticationRequest::getRequestByClass(
83 $reqs, 'ClassThatDoesNotExist'
84 ) );
86 $this->assertNull( AuthenticationRequest::getRequestByClass(
87 $reqs, 'AuthenticationRequestTest_AuthenticationRequest0', true
88 ) );
89 $this->assertSame( $reqs[0], AuthenticationRequest::getRequestByClass(
90 $reqs, 'AuthenticationRequestTest_AuthenticationRequest1', true
91 ) );
92 $this->assertNull( AuthenticationRequest::getRequestByClass(
93 $reqs, 'AuthenticationRequestTest_AuthenticationRequest2', true
94 ) );
95 $this->assertSame( $reqs[3], AuthenticationRequest::getRequestByClass(
96 $reqs, PasswordAuthenticationRequest::class, true
97 ) );
98 $this->assertNull( AuthenticationRequest::getRequestByClass(
99 $reqs, 'ClassThatDoesNotExist', true
100 ) );
103 public function testGetUsernameFromRequests() {
104 $mb = $this->getMockBuilder( AuthenticationRequest::class );
106 for ( $i = 0; $i < 3; $i++ ) {
107 $req = $mb->getMockForAbstractClass();
108 $req->method( 'getFieldInfo' )->willReturn( [
109 'username' => [
110 'type' => 'string',
112 ] );
113 $reqs[] = $req;
116 $req = $mb->getMockForAbstractClass();
117 $req->method( 'getFieldInfo' )->willReturn( [] );
118 $req->username = 'baz';
119 $reqs[] = $req;
121 $this->assertNull( AuthenticationRequest::getUsernameFromRequests( $reqs ) );
123 $reqs[1]->username = 'foo';
124 $this->assertSame( 'foo', AuthenticationRequest::getUsernameFromRequests( $reqs ) );
126 $reqs[0]->username = 'foo';
127 $reqs[2]->username = 'foo';
128 $this->assertSame( 'foo', AuthenticationRequest::getUsernameFromRequests( $reqs ) );
130 $reqs[1]->username = 'bar';
131 try {
132 AuthenticationRequest::getUsernameFromRequests( $reqs );
133 $this->fail( 'Expected exception not thrown' );
134 } catch ( UnexpectedValueException $ex ) {
135 $this->assertSame(
136 'Conflicting username fields: "bar" from ' .
137 get_class( $reqs[1] ) . '::$username vs. "foo" from ' .
138 get_class( $reqs[0] ) . '::$username',
139 $ex->getMessage()
144 public function testMergeFieldInfo() {
145 $msg = wfMessage( 'foo' );
147 $req1 = $this->createMock( AuthenticationRequest::class );
148 $req1->required = AuthenticationRequest::REQUIRED;
149 $req1->method( 'getFieldInfo' )->willReturn( [
150 'string1' => [
151 'type' => 'string',
152 'label' => $msg,
153 'help' => $msg,
155 'string2' => [
156 'type' => 'string',
157 'label' => $msg,
158 'help' => $msg,
160 'optional' => [
161 'type' => 'string',
162 'label' => $msg,
163 'help' => $msg,
164 'optional' => true,
166 'select' => [
167 'type' => 'select',
168 'options' => [ 'foo' => $msg, 'baz' => $msg ],
169 'label' => $msg,
170 'help' => $msg,
172 ] );
174 $req2 = $this->createMock( AuthenticationRequest::class );
175 $req2->required = AuthenticationRequest::REQUIRED;
176 $req2->method( 'getFieldInfo' )->willReturn( [
177 'string1' => [
178 'type' => 'string',
179 'label' => $msg,
180 'help' => $msg,
181 'sensitive' => true,
183 'string3' => [
184 'type' => 'string',
185 'label' => $msg,
186 'help' => $msg,
188 'select' => [
189 'type' => 'select',
190 'options' => [ 'bar' => $msg, 'baz' => $msg ],
191 'label' => $msg,
192 'help' => $msg,
194 ] );
196 $req3 = $this->createMock( AuthenticationRequest::class );
197 $req3->required = AuthenticationRequest::REQUIRED;
198 $req3->method( 'getFieldInfo' )->willReturn( [
199 'string1' => [
200 'type' => 'checkbox',
201 'label' => $msg,
202 'help' => $msg,
204 ] );
206 $req4 = $this->createMock( AuthenticationRequest::class );
207 $req4->required = AuthenticationRequest::REQUIRED;
208 $req4->method( 'getFieldInfo' )->willReturn( [] );
210 // Basic combining
212 $this->assertEquals( [], AuthenticationRequest::mergeFieldInfo( [] ) );
214 $fields = AuthenticationRequest::mergeFieldInfo( [ $req1 ] );
215 $expect = $req1->getFieldInfo();
216 foreach ( $expect as &$options ) {
217 $options['optional'] = !empty( $options['optional'] );
218 $options['sensitive'] = !empty( $options['sensitive'] );
220 unset( $options );
221 $this->assertEquals( $expect, $fields );
223 $fields = AuthenticationRequest::mergeFieldInfo( [ $req1, $req4 ] );
224 $this->assertEquals( $expect, $fields );
226 try {
227 AuthenticationRequest::mergeFieldInfo( [ $req1, $req3 ] );
228 $this->fail( 'Expected exception not thrown' );
229 } catch ( UnexpectedValueException $ex ) {
230 $this->assertSame(
231 'Field type conflict for "string1", "string" vs "checkbox"',
232 $ex->getMessage()
236 $fields = AuthenticationRequest::mergeFieldInfo( [ $req1, $req2 ] );
237 $expect += $req2->getFieldInfo();
238 $expect['string1']['sensitive'] = true;
239 $expect['string2']['optional'] = false;
240 $expect['string3']['optional'] = false;
241 $expect['string3']['sensitive'] = false;
242 $expect['select']['options']['bar'] = $msg;
243 $this->assertEquals( $expect, $fields );
245 // Combining with something not required
247 $req1->required = AuthenticationRequest::PRIMARY_REQUIRED;
249 $fields = AuthenticationRequest::mergeFieldInfo( [ $req1, $req2 ] );
250 $expect += $req2->getFieldInfo();
251 $expect['string1']['optional'] = false;
252 $expect['string1']['sensitive'] = true;
253 $expect['string3']['optional'] = false;
254 $expect['select']['optional'] = false;
255 $expect['select']['options']['bar'] = $msg;
256 $this->assertEquals( $expect, $fields );
258 $req2->required = AuthenticationRequest::PRIMARY_REQUIRED;
260 $fields = AuthenticationRequest::mergeFieldInfo( [ $req1, $req2 ] );
261 $expect = $req1->getFieldInfo() + $req2->getFieldInfo();
262 foreach ( $expect as &$options ) {
263 $options['sensitive'] = !empty( $options['sensitive'] );
265 $expect['string1']['optional'] = false;
266 $expect['string1']['sensitive'] = true;
267 $expect['string2']['optional'] = true;
268 $expect['string3']['optional'] = true;
269 $expect['select']['optional'] = false;
270 $expect['select']['options']['bar'] = $msg;
271 $this->assertEquals( $expect, $fields );
275 * @dataProvider provideLoadFromSubmission
276 * @param array $fieldInfo
277 * @param array $data
278 * @param array|bool $expectState
280 public function testLoadFromSubmission( $fieldInfo, $data, $expectState ) {
281 $mock = $this->getMockForAbstractClass( AuthenticationRequestForLoadFromSubmission::class );
282 $mock->method( 'getFieldInfo' )
283 ->willReturn( $fieldInfo );
285 $ret = $mock->loadFromSubmission( $data );
286 if ( is_array( $expectState ) ) {
287 $this->assertTrue( $ret );
288 $expect = $mock::__set_state( $expectState );
289 $this->assertEquals( $expect, $mock );
290 } else {
291 $this->assertFalse( $ret );
295 public static function provideLoadFromSubmission() {
296 return [
297 'No fields' => [
299 $data = [ 'foo' => 'bar' ],
300 false
303 'Simple field' => [
305 'field' => [
306 'type' => 'string',
309 $data = [ 'field' => 'string!' ],
310 $data
312 'Simple field, not supplied' => [
314 'field' => [
315 'type' => 'string',
319 false
321 'Simple field, empty' => [
323 'field' => [
324 'type' => 'string',
327 [ 'field' => '' ],
328 false
330 'Simple field, optional, not supplied' => [
332 'field' => [
333 'type' => 'string',
334 'optional' => true,
338 false
340 'Simple field, optional, empty' => [
342 'field' => [
343 'type' => 'string',
344 'optional' => true,
347 $data = [ 'field' => '' ],
348 $data
351 'Checkbox, checked' => [
353 'check' => [
354 'type' => 'checkbox',
357 [ 'check' => '' ],
358 [ 'check' => true ]
360 'Checkbox, unchecked' => [
362 'check' => [
363 'type' => 'checkbox',
367 false
369 'Checkbox, optional, unchecked' => [
371 'check' => [
372 'type' => 'checkbox',
373 'optional' => true,
377 [ 'check' => false ]
380 'Button, used' => [
382 'push' => [
383 'type' => 'button',
386 [ 'push' => '' ],
387 [ 'push' => true ]
389 'Button, unused' => [
391 'push' => [
392 'type' => 'button',
396 false
398 'Button, optional, unused' => [
400 'push' => [
401 'type' => 'button',
402 'optional' => true,
406 [ 'push' => false ]
408 'Button, image-style' => [
410 'push' => [
411 'type' => 'button',
414 [ 'push_x' => 0, 'push_y' => 0 ],
415 [ 'push' => true ]
418 'Select' => [
420 'choose' => [
421 'type' => 'select',
422 'options' => [
423 'foo' => wfMessage( 'mainpage' ),
424 'bar' => wfMessage( 'mainpage' ),
428 $data = [ 'choose' => 'foo' ],
429 $data
431 'Select, invalid choice' => [
433 'choose' => [
434 'type' => 'select',
435 'options' => [
436 'foo' => wfMessage( 'mainpage' ),
437 'bar' => wfMessage( 'mainpage' ),
441 $data = [ 'choose' => 'baz' ],
442 false
444 'Multiselect (2)' => [
446 'choose' => [
447 'type' => 'multiselect',
448 'options' => [
449 'foo' => wfMessage( 'mainpage' ),
450 'bar' => wfMessage( 'mainpage' ),
454 $data = [ 'choose' => [ 'foo', 'bar' ] ],
455 $data
457 'Multiselect (1)' => [
459 'choose' => [
460 'type' => 'multiselect',
461 'options' => [
462 'foo' => wfMessage( 'mainpage' ),
463 'bar' => wfMessage( 'mainpage' ),
467 $data = [ 'choose' => [ 'bar' ] ],
468 $data
470 'Multiselect, string for some reason' => [
472 'choose' => [
473 'type' => 'multiselect',
474 'options' => [
475 'foo' => wfMessage( 'mainpage' ),
476 'bar' => wfMessage( 'mainpage' ),
480 [ 'choose' => 'foo' ],
481 [ 'choose' => [ 'foo' ] ]
483 'Multiselect, invalid choice' => [
485 'choose' => [
486 'type' => 'multiselect',
487 'options' => [
488 'foo' => wfMessage( 'mainpage' ),
489 'bar' => wfMessage( 'mainpage' ),
493 [ 'choose' => [ 'foo', 'baz' ] ],
494 false
496 'Multiselect, empty' => [
498 'choose' => [
499 'type' => 'multiselect',
500 'options' => [
501 'foo' => wfMessage( 'mainpage' ),
502 'bar' => wfMessage( 'mainpage' ),
506 [ 'choose' => [] ],
507 false
509 'Multiselect, optional, nothing submitted' => [
511 'choose' => [
512 'type' => 'multiselect',
513 'options' => [
514 'foo' => wfMessage( 'mainpage' ),
515 'bar' => wfMessage( 'mainpage' ),
517 'optional' => true,
521 [ 'choose' => [] ]
527 // Dynamic properties from the testLoadFromSubmission not working in php8.2
528 abstract class AuthenticationRequestForLoadFromSubmission extends AuthenticationRequest {
529 /** @var array */
530 public $choose;
531 /** @var bool */
532 public $push;
533 /** @var bool */
534 public $check;
535 /** @var string */
536 public $field;