1<?php 2 3class MWRestrictionsTest extends MediaWikiUnitTestCase { 4 5 protected static $restrictionsForChecks; 6 7 public static function setUpBeforeClass(): void { 8 parent::setUpBeforeClass(); 9 self::$restrictionsForChecks = MWRestrictions::newFromArray( [ 10 'IPAddresses' => [ 11 '10.0.0.0/8', 12 '172.16.0.0/12', 13 '2001:db8::/33', 14 ] 15 ] ); 16 } 17 18 /** 19 * @covers MWRestrictions::newDefault 20 * @covers MWRestrictions::__construct 21 */ 22 public function testNewDefault() { 23 $ret = MWRestrictions::newDefault(); 24 $this->assertInstanceOf( MWRestrictions::class, $ret ); 25 $this->assertSame( 26 '{"IPAddresses":["0.0.0.0/0","::/0"]}', 27 $ret->toJson() 28 ); 29 } 30 31 /** 32 * @covers MWRestrictions::newFromArray 33 * @covers MWRestrictions::__construct 34 * @covers MWRestrictions::loadFromArray 35 * @covers MWRestrictions::toArray 36 * @dataProvider provideArray 37 * @param array $data 38 * @param bool|InvalidArgumentException $expect True if the call succeeds, 39 * otherwise the exception that should be thrown. 40 */ 41 public function testArray( $data, $expect ) { 42 if ( $expect === true ) { 43 $ret = MWRestrictions::newFromArray( $data ); 44 $this->assertInstanceOf( MWRestrictions::class, $ret ); 45 $this->assertSame( $data, $ret->toArray() ); 46 } else { 47 try { 48 MWRestrictions::newFromArray( $data ); 49 $this->fail( 'Expected exception not thrown' ); 50 } catch ( InvalidArgumentException $ex ) { 51 $this->assertEquals( $expect, $ex ); 52 } 53 } 54 } 55 56 public static function provideArray() { 57 return [ 58 [ [ 'IPAddresses' => [] ], true ], 59 [ [ 'IPAddresses' => [ '127.0.0.1/32' ] ], true ], 60 [ 61 [ 'IPAddresses' => [ '256.0.0.1/32' ] ], 62 new InvalidArgumentException( 'Invalid IP address: 256.0.0.1/32' ) 63 ], 64 [ 65 [ 'IPAddresses' => '127.0.0.1/32' ], 66 new InvalidArgumentException( 'IPAddresses is not an array' ) 67 ], 68 [ 69 [], 70 new InvalidArgumentException( 'Array is missing required keys: IPAddresses' ) 71 ], 72 [ 73 [ 'foo' => 'bar', 'bar' => 42 ], 74 new InvalidArgumentException( 'Array contains invalid keys: foo, bar' ) 75 ], 76 ]; 77 } 78 79 /** 80 * @covers MWRestrictions::newFromJson 81 * @covers MWRestrictions::__construct 82 * @covers MWRestrictions::loadFromArray 83 * @covers MWRestrictions::toJson 84 * @covers MWRestrictions::__toString 85 * @dataProvider provideJson 86 * @param string $json 87 * @param array|InvalidArgumentException $expect 88 */ 89 public function testJson( $json, $expect ) { 90 if ( is_array( $expect ) ) { 91 $ret = MWRestrictions::newFromJson( $json ); 92 $this->assertInstanceOf( MWRestrictions::class, $ret ); 93 $this->assertSame( $expect, $ret->toArray() ); 94 95 $this->assertSame( $json, $ret->toJson( false ) ); 96 $this->assertSame( $json, (string)$ret ); 97 98 $this->assertSame( 99 FormatJson::encode( $expect, true, FormatJson::ALL_OK ), 100 $ret->toJson( true ) 101 ); 102 } else { 103 try { 104 MWRestrictions::newFromJson( $json ); 105 $this->fail( 'Expected exception not thrown' ); 106 } catch ( InvalidArgumentException $ex ) { 107 $this->assertTrue( true ); 108 } 109 } 110 } 111 112 public static function provideJson() { 113 return [ 114 [ 115 '{"IPAddresses":[]}', 116 [ 'IPAddresses' => [] ] 117 ], 118 [ 119 '{"IPAddresses":["127.0.0.1/32"]}', 120 [ 'IPAddresses' => [ '127.0.0.1/32' ] ] 121 ], 122 [ 123 '{"IPAddresses":["256.0.0.1/32"]}', 124 new InvalidArgumentException( 'Invalid IP address: 256.0.0.1/32' ) 125 ], 126 [ 127 '{"IPAddresses":"127.0.0.1/32"}', 128 new InvalidArgumentException( 'IPAddresses is not an array' ) 129 ], 130 [ 131 '{}', 132 new InvalidArgumentException( 'Array is missing required keys: IPAddresses' ) 133 ], 134 [ 135 '{"foo":"bar","bar":42}', 136 new InvalidArgumentException( 'Array contains invalid keys: foo, bar' ) 137 ], 138 [ 139 '{"IPAddresses":[]', 140 new InvalidArgumentException( 'Invalid restrictions JSON' ) 141 ], 142 [ 143 '"IPAddresses"', 144 new InvalidArgumentException( 'Invalid restrictions JSON' ) 145 ], 146 ]; 147 } 148 149 /** 150 * @covers MWRestrictions::checkIP 151 * @dataProvider provideCheckIP 152 * @param string $ip 153 * @param bool $pass 154 */ 155 public function testCheckIP( $ip, $pass ) { 156 $this->assertSame( $pass, self::$restrictionsForChecks->checkIP( $ip ) ); 157 } 158 159 public static function provideCheckIP() { 160 return [ 161 [ '10.0.0.1', true ], 162 [ '172.16.0.0', true ], 163 [ '192.0.2.1', false ], 164 [ '2001:db8:1::', true ], 165 [ '2001:0db8:0000:0000:0000:0000:0000:0000', true ], 166 [ '2001:0DB8:8000::', false ], 167 ]; 168 } 169 170 /** 171 * @covers MWRestrictions::check 172 * @dataProvider provideCheck 173 * @param WebRequest $request 174 * @param Status $expect 175 */ 176 public function testCheck( $request, $expect ) { 177 $this->assertEquals( $expect, self::$restrictionsForChecks->check( $request ) ); 178 } 179 180 public function provideCheck() { 181 $ret = []; 182 183 $mockBuilder = $this->getMockBuilder( FauxRequest::class ) 184 ->onlyMethods( [ 'getIP' ] ); 185 186 foreach ( self::provideCheckIP() as $checkIP ) { 187 $ok = []; 188 $request = $mockBuilder->getMock(); 189 190 $request->method( 'getIP' ) 191 ->willReturn( $checkIP[0] ); 192 $ok['ip'] = $checkIP[1]; 193 194 /* If we ever add more restrictions, add nested for loops here: 195 * foreach ( self::provideCheckFoo() as $checkFoo ) { 196 * $request->method( 'getFoo' )->willReturn( $checkFoo[0] ); 197 * $ok['foo'] = $checkFoo[1]; 198 * 199 * foreach ( self::provideCheckBar() as $checkBar ) { 200 * $request->method( 'getBar' )->willReturn( $checkBar[0] ); 201 * $ok['bar'] = $checkBar[1]; 202 * 203 * // etc. 204 * } 205 * } 206 */ 207 208 $status = Status::newGood(); 209 $status->setResult( $ok === array_filter( $ok ), $ok ); 210 $ret[] = [ $request, $status ]; 211 } 212 213 return $ret; 214 } 215} 216