Implemented IP range validation (#95)

This commit is contained in:
Luís Otávio Cobucci Oblonczyk 2013-01-09 12:48:11 -02:00
parent 8355f4bc9d
commit 86f91ad978
2 changed files with 187 additions and 18 deletions

View file

@ -2,22 +2,119 @@
namespace Respect\Validation\Rules;
use Respect\Validation\Exceptions\ComponentException;
class Ip extends AbstractRule
{
public $ipOptions;
public $networkRange;
public function __construct($ipOptions=null)
{
$this->ipOptions = $ipOptions;
if (is_int($ipOptions)) {
$this->ipOptions = $ipOptions;
return ;
}
$this->networkRange = $this->parseRange($ipOptions);
}
protected function parseRange($input)
{
if ($input === null || $input == '*' || $input == '*.*.*.*'
|| $input == '0.0.0.0-255.255.255.255')
return null;
$range = array('min' => null, 'max' => null);
if (strpos($input, '-') !== false)
list($range['min'], $range['max']) = explode('-', $input);
elseif (strpos($input, '*') !== false) {
$this->parseRangeUsingWildcards($input, $range);
} elseif (strpos($input, '/') !== false) {
$this->parseRangeUsingCidr($input, $range);
} else
throw new ComponentException('Invalid network range');
if (!$this->verifyAddress($range['min'])) {
throw new ComponentException('Invalid network range');
}
if (isset($range['max']) && !$this->verifyAddress($range['max'])) {
throw new ComponentException('Invalid network range');
}
return $range;
}
protected function fillAddress(&$input, $char = '*')
{
while (substr_count($input, '.') < 3) {
$input .= '.' . $char;
}
}
protected function parseRangeUsingWildcards($input, &$range)
{
$this->fillAddress($input);
$range['min'] = strtr($input, '*', '0');
$range['max'] = str_replace('*', '255', $input);
}
protected function parseRangeUsingCidr($input, &$range)
{
$input = explode('/', $input);
$this->fillAddress($input[0], '0');
$range['min'] = $input[0];
$isAddressMask = strpos($input[1], '.') !== false;
if ($isAddressMask && $this->verifyAddress($input[1])) {
$range['mask'] = ip2long($input[1]);
return ;
}
if ($isAddressMask || $input[1] < 8 || $input[1] > 30) {
throw new ComponentException('Invalid network mask');
}
$range['mask'] = ~ (pow(2, (32 - $input[1])) - 1);
}
public function validate($input)
{
return $this->verifyAddress($input) && $this->verifyNetwork($input);
}
protected function verifyAddress($address)
{
return (boolean) filter_var(
$input, FILTER_VALIDATE_IP, array('flags' => $this->ipOptions)
$address,
FILTER_VALIDATE_IP,
array(
'flags' => $this->ipOptions
)
);
}
}
protected function verifyNetwork($input)
{
if ($this->networkRange === null)
return true;
$input = ip2long($input);
$range = $this->networkRange;
if (isset($range['mask'])) {
return ($input & $range['mask']) == (ip2long($range['min']) & $range['mask']);
}
return $input >= ip2long($range['min'])
&& $input <= ip2long($range['max']);
}
}

View file

@ -4,24 +4,27 @@ namespace Respect\Validation\Rules;
class IpTest extends \PHPUnit_Framework_TestCase
{
protected $ipValidator;
protected function setUp()
{
$this->ipValidator = new Ip;
}
/**
* @dataProvider providerForIp
*
*/
public function test_valid_ips_should_return_True($input, $options=null)
{
$this->ipValidator->ipOptions = $options;
$this->assertTrue($this->ipValidator->validate($input));
$this->assertTrue($this->ipValidator->assert($input));
$this->assertTrue($this->ipValidator->check($input));
$ipValidator = new Ip($options);
$this->assertTrue($ipValidator->validate($input));
$this->assertTrue($ipValidator->assert($input));
$this->assertTrue($ipValidator->check($input));
}
/**
* @dataProvider providerForIpBetweenRange
*/
public function test_ips_between_range_should_return_True($input, $networkRange)
{
$ipValidator = new Ip($networkRange);
$this->assertTrue($ipValidator->validate($input));
$this->assertTrue($ipValidator->assert($input));
$this->assertTrue($ipValidator->check($input));
}
/**
@ -30,9 +33,20 @@ class IpTest extends \PHPUnit_Framework_TestCase
*/
public function test_invalid_ips_should_throw_IpException($input, $options=null)
{
$this->ipValidator->ipOptions = $options;
$this->assertFalse($this->ipValidator->validate($input));
$this->assertFalse($this->ipValidator->assert($input));
$ipValidator = new Ip($options);
$this->assertFalse($ipValidator->validate($input));
$this->assertFalse($ipValidator->assert($input));
}
/**
* @dataProvider providerForIpOutsideRange
* @expectedException Respect\Validation\Exceptions\IpException
*/
public function test_ips_outside_range_should_return_False($input, $networkRange)
{
$ipValidator = new Ip($networkRange);
$this->assertFalse($ipValidator->validate($input));
$this->assertFalse($ipValidator->assert($input));
}
public function providerForIp()
@ -42,6 +56,28 @@ class IpTest extends \PHPUnit_Framework_TestCase
);
}
public function providerForIpBetweenRange()
{
return array(
array('127.0.0.1', '127.*'),
array('127.0.0.1', '127.0.*'),
array('127.0.0.1', '127.0.0.*'),
array('192.168.2.6', '192.168.*.6'),
array('192.168.2.6', '192.*.2.6'),
array('10.168.2.6', '*.168.2.6'),
array('192.168.2.6', '192.168.*.*'),
array('192.10.2.6', '192.*.*.*'),
array('192.168.255.156', '*'),
array('192.168.255.156', '*.*.*.*'),
array('127.0.0.1', '127.0.0.0-127.0.0.255'),
array('192.168.2.6', '192.168.0.0-192.168.255.255'),
array('192.10.2.6', '192.0.0.0-192.255.255.255'),
array('192.168.255.156', '0.0.0.0-255.255.255.255'),
array('220.78.173.2', '220.78.168/21'),
array('220.78.173.2', '220.78.168.0/21'),
);
}
public function providerForNotIp()
{
return array(
@ -54,4 +90,40 @@ class IpTest extends \PHPUnit_Framework_TestCase
);
}
public function providerForIpOutsideRange()
{
return array(
array('127.0.0.1', '127.0.1.*'),
array('192.168.2.6', '192.163.*.*'),
array('192.10.2.6', '193.*.*.*'),
array('127.0.0.1', '127.0.1.0-127.0.1.255'),
array('192.168.2.6', '192.163.0.0-192.163.255.255'),
array('192.10.2.6', '193.168.0.0-193.255.255.255'),
array('220.78.176.1', '220.78.168/21'),
array('220.78.176.2', '220.78.168.0/21'),
);
}
/**
* @dataProvider providerForInvalidRanges
* @expectedException Respect\Validation\Exceptions\ComponentException
*/
public function test_invalid_range_should_raise_exception($range)
{
$o = new Ip($range);
}
public function providerForInvalidRanges()
{
return array(
array('192.168'),
array('asd'),
array('192.168.0.0-192.168.0.256'),
array('192.168.0.0-192.168.0.1/4'),
array('192.168.0/1'),
array('192.168.2.0/256.256.256.256'),
array('192.168.2.0/8.256.256.256'),
);
}
}