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
;
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 ) )
49 AuthenticationRequest
::loadRequestsFromSubmission( [ $req1, $req2 ], $data )
53 public function testGetRequestByClass() {
54 $mb = $this->getMockBuilder(
55 AuthenticationRequest
::class, 'AuthenticationRequestTest_AuthenticationRequest2'
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'
73 $this->assertSame( $reqs[0], AuthenticationRequest
::getRequestByClass(
74 $reqs, 'AuthenticationRequestTest_AuthenticationRequest1'
76 $this->assertNull( AuthenticationRequest
::getRequestByClass(
77 $reqs, 'AuthenticationRequestTest_AuthenticationRequest2'
79 $this->assertNull( AuthenticationRequest
::getRequestByClass(
80 $reqs, PasswordAuthenticationRequest
::class
82 $this->assertNull( AuthenticationRequest
::getRequestByClass(
83 $reqs, 'ClassThatDoesNotExist'
86 $this->assertNull( AuthenticationRequest
::getRequestByClass(
87 $reqs, 'AuthenticationRequestTest_AuthenticationRequest0', true
89 $this->assertSame( $reqs[0], AuthenticationRequest
::getRequestByClass(
90 $reqs, 'AuthenticationRequestTest_AuthenticationRequest1', true
92 $this->assertNull( AuthenticationRequest
::getRequestByClass(
93 $reqs, 'AuthenticationRequestTest_AuthenticationRequest2', true
95 $this->assertSame( $reqs[3], AuthenticationRequest
::getRequestByClass(
96 $reqs, PasswordAuthenticationRequest
::class, true
98 $this->assertNull( AuthenticationRequest
::getRequestByClass(
99 $reqs, 'ClassThatDoesNotExist', true
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( [
116 $req = $mb->getMockForAbstractClass();
117 $req->method( 'getFieldInfo' )->willReturn( [] );
118 $req->username
= 'baz';
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';
132 AuthenticationRequest
::getUsernameFromRequests( $reqs );
133 $this->fail( 'Expected exception not thrown' );
134 } catch ( UnexpectedValueException
$ex ) {
136 'Conflicting username fields: "bar" from ' .
137 get_class( $reqs[1] ) . '::$username vs. "foo" from ' .
138 get_class( $reqs[0] ) . '::$username',
144 public function testMergeFieldInfo() {
145 $msg = wfMessage( 'foo' );
147 $req1 = $this->createMock( AuthenticationRequest
::class );
148 $req1->required
= AuthenticationRequest
::REQUIRED
;
149 $req1->method( 'getFieldInfo' )->willReturn( [
168 'options' => [ 'foo' => $msg, 'baz' => $msg ],
174 $req2 = $this->createMock( AuthenticationRequest
::class );
175 $req2->required
= AuthenticationRequest
::REQUIRED
;
176 $req2->method( 'getFieldInfo' )->willReturn( [
190 'options' => [ 'bar' => $msg, 'baz' => $msg ],
196 $req3 = $this->createMock( AuthenticationRequest
::class );
197 $req3->required
= AuthenticationRequest
::REQUIRED
;
198 $req3->method( 'getFieldInfo' )->willReturn( [
200 'type' => 'checkbox',
206 $req4 = $this->createMock( AuthenticationRequest
::class );
207 $req4->required
= AuthenticationRequest
::REQUIRED
;
208 $req4->method( 'getFieldInfo' )->willReturn( [] );
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'] );
221 $this->assertEquals( $expect, $fields );
223 $fields = AuthenticationRequest
::mergeFieldInfo( [ $req1, $req4 ] );
224 $this->assertEquals( $expect, $fields );
227 AuthenticationRequest
::mergeFieldInfo( [ $req1, $req3 ] );
228 $this->fail( 'Expected exception not thrown' );
229 } catch ( UnexpectedValueException
$ex ) {
231 'Field type conflict for "string1", "string" vs "checkbox"',
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
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 );
291 $this->assertFalse( $ret );
295 public static function provideLoadFromSubmission() {
299 $data = [ 'foo' => 'bar' ],
309 $data = [ 'field' => 'string!' ],
312 'Simple field, not supplied' => [
321 'Simple field, empty' => [
330 'Simple field, optional, not supplied' => [
340 'Simple field, optional, empty' => [
347 $data = [ 'field' => '' ],
351 'Checkbox, checked' => [
354 'type' => 'checkbox',
360 'Checkbox, unchecked' => [
363 'type' => 'checkbox',
369 'Checkbox, optional, unchecked' => [
372 'type' => 'checkbox',
389 'Button, unused' => [
398 'Button, optional, unused' => [
408 'Button, image-style' => [
414 [ 'push_x' => 0, 'push_y' => 0 ],
423 'foo' => wfMessage( 'mainpage' ),
424 'bar' => wfMessage( 'mainpage' ),
428 $data = [ 'choose' => 'foo' ],
431 'Select, invalid choice' => [
436 'foo' => wfMessage( 'mainpage' ),
437 'bar' => wfMessage( 'mainpage' ),
441 $data = [ 'choose' => 'baz' ],
444 'Multiselect (2)' => [
447 'type' => 'multiselect',
449 'foo' => wfMessage( 'mainpage' ),
450 'bar' => wfMessage( 'mainpage' ),
454 $data = [ 'choose' => [ 'foo', 'bar' ] ],
457 'Multiselect (1)' => [
460 'type' => 'multiselect',
462 'foo' => wfMessage( 'mainpage' ),
463 'bar' => wfMessage( 'mainpage' ),
467 $data = [ 'choose' => [ 'bar' ] ],
470 'Multiselect, string for some reason' => [
473 'type' => 'multiselect',
475 'foo' => wfMessage( 'mainpage' ),
476 'bar' => wfMessage( 'mainpage' ),
480 [ 'choose' => 'foo' ],
481 [ 'choose' => [ 'foo' ] ]
483 'Multiselect, invalid choice' => [
486 'type' => 'multiselect',
488 'foo' => wfMessage( 'mainpage' ),
489 'bar' => wfMessage( 'mainpage' ),
493 [ 'choose' => [ 'foo', 'baz' ] ],
496 'Multiselect, empty' => [
499 'type' => 'multiselect',
501 'foo' => wfMessage( 'mainpage' ),
502 'bar' => wfMessage( 'mainpage' ),
509 'Multiselect, optional, nothing submitted' => [
512 'type' => 'multiselect',
514 'foo' => wfMessage( 'mainpage' ),
515 'bar' => wfMessage( 'mainpage' ),
527 // Dynamic properties from the testLoadFromSubmission not working in php8.2
528 abstract class AuthenticationRequestForLoadFromSubmission
extends AuthenticationRequest
{