فهرست منبع

Refactoring of IpValidator to use native php filter extension, also adding additional flag support and test cover.

stloyd 14 سال پیش
والد
کامیت
5997b930a5

+ 29 - 1
src/Symfony/Component/Validator/Constraints/Ip.php

@@ -17,6 +17,7 @@ use Symfony\Component\Validator\Exception\ConstraintDefinitionException;
  * Validates that a value is a valid IP address
  *
  * @author Bernhard Schussek <bernhard.schussek@symfony.com>
+ * @author Joseph Bielawski <stloyd@gmail.com>
  */
 class Ip extends \Symfony\Component\Validator\Constraint
 {
@@ -24,10 +25,37 @@ class Ip extends \Symfony\Component\Validator\Constraint
     const V6 = '6';
     const ALL = 'all';
 
+    // adds FILTER_FLAG_NO_PRIV_RANGE flag (skip private ranges)
+    const V4_NO_PRIV = '4_no_priv';
+    const V6_NO_PRIV = '6_no_priv';
+    const ALL_NO_PRIV = 'all_no_priv';
+
+    // adds FILTER_FLAG_NO_RES_RANGE flag (skip reserved ranges)
+    const V4_NO_RES = '4_no_res';
+    const V6_NO_RES = '6_no_res';
+    const ALL_NO_RES = 'all_no_res';
+
+    // adds FILTER_FLAG_NO_PRIV_RANGE and FILTER_FLAG_NO_RES_RANGE flags (skip both)
+    const V4_ONLY_PUBLIC = '4_public';
+    const V6_ONLY_PUBLIC = '6_public';
+    const ALL_ONLY_PUBLIC = 'all_public';
+
     static protected $versions = array(
         self::V4,
         self::V6,
         self::ALL,
+
+        self::V4_NO_PRIV,
+        self::V6_NO_PRIV,
+        self::ALL_NO_PRIV,
+
+        self::V4_NO_RES,
+        self::V6_NO_RES,
+        self::ALL_NO_RES,
+
+        self::V4_ONLY_PUBLIC,
+        self::V6_ONLY_PUBLIC,
+        self::ALL_ONLY_PUBLIC,
     );
 
     public $version = self::V4;
@@ -53,4 +81,4 @@ class Ip extends \Symfony\Component\Validator\Constraint
     {
         return self::PROPERTY_CONSTRAINT;
     }
-}
+}

+ 44 - 48
src/Symfony/Component/Validator/Constraints/IpValidator.php

@@ -19,6 +19,7 @@ use Symfony\Component\Validator\Exception\UnexpectedTypeException;
  * Validates whether a value is a valid IP address
  *
  * @author Bernhard Schussek <bernhard.schussek@symfony.com>
+ * @author Joseph Bielawski <stloyd@gmail.com>
  */
 class IpValidator extends ConstraintValidator
 {
@@ -36,66 +37,61 @@ class IpValidator extends ConstraintValidator
         }
 
         $value = (string) $value;
-        $valid = false;
 
-        if ($constraint->version == Ip::V4 || $constraint->version == Ip::ALL) {
-            $valid = $this->isValidV4($value);
-        }
+        switch ($constraint->version) {
+            case Ip::V4:
+               $flag = FILTER_FLAG_IPV4;
+               break;
 
-        if ($constraint->version == Ip::V6 || $constraint->version == Ip::ALL) {
-            $valid = $valid || $this->isValidV6($value);
-        }
+            case Ip::V6:
+               $flag = FILTER_FLAG_IPV6;
+               break;
 
-        if (!$valid) {
-            $this->setMessage($constraint->message, array('{{ value }}' => $value));
+            case Ip::V4_NO_PRIV:
+               $flag = FILTER_FLAG_IPV4 | FILTER_FLAG_NO_PRIV_RANGE;
+               break;
 
-            return false;
-        }
+            case Ip::V6_NO_PRIV:
+               $flag = FILTER_FLAG_IPV6 | FILTER_FLAG_NO_PRIV_RANGE;
+               break;
 
-        return true;
-    }
+            case Ip::ALL_NO_PRIV:
+               $flag = FILTER_FLAG_NO_PRIV_RANGE;
+               break;
 
-    /**
-     * Validates that a value is a valid IPv4 address
-     *
-     * @param string $value
-     */
-    protected function isValidV4($value)
-    {
-        if (!preg_match('/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/', $value, $matches)) {
-            return false;
-        }
+            case Ip::V4_NO_RES:
+               $flag = FILTER_FLAG_IPV4 | FILTER_FLAG_NO_RES_RANGE;
+               break;
 
-        for ($i = 1; $i <= 4; ++$i) {
-            if ($matches[$i] > 255) {
-                return false;
-            }
-        }
+            case Ip::V6_NO_RES:
+               $flag = FILTER_FLAG_IPV6 | FILTER_FLAG_NO_RES_RANGE;
+               break;
 
-        return true;
-    }
+            case Ip::ALL_NO_RES:
+               $flag = FILTER_FLAG_NO_RES_RANGE;
+               break;
 
-    /**
-     * Validates that a value is a valid IPv6 address
-     *
-     * @param string $value
-     */
-    protected function isValidV6($value)
-    {
-        if (!preg_match('/^[0-9a-fA-F]{0,4}(:[0-9a-fA-F]{0,4}){1,5}((:[0-9a-fA-F]{0,4}){1,2}|:([\d\.]+))$/', $value, $matches)) {
-            return false;
-        }
+            case Ip::V4_ONLY_PUBLIC:
+               $flag = FILTER_FLAG_IPV4 | FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE;
+               break;
 
-        // allow V4 addresses mapped to V6
-        if (isset($matches[4]) && !$this->isValidV4($matches[4])) {
-            return false;
+            case Ip::V6_ONLY_PUBLIC:
+               $flag = FILTER_FLAG_IPV6 | FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE;
+               break;
+
+            case Ip::ALL_ONLY_PUBLIC:
+               $flag = FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE;
+               break;
+
+            default:
+                $flag = null;
+                break;
         }
 
-        // "::" is only allowed once per address
-        if (($offset = strpos($value, '::')) !== false) {
-            if (strpos($value, '::', $offset + 1) !== false) {
-                return false;
-            }
+        if (!filter_var($value, FILTER_VALIDATE_IP, $flag)) {
+            $this->setMessage($constraint->message, array('{{ value }}' => $value));
+
+            return false;
         }
 
         return true;

+ 159 - 13
tests/Symfony/Tests/Component/Validator/Constraints/IpValidatorTest.php

@@ -43,9 +43,9 @@ class IpValidatorTest extends \PHPUnit_Framework_TestCase
     /**
      * @dataProvider getValidIpsV4
      */
-    public function testValidIpsV4($date)
+    public function testValidIpsV4($ip)
     {
-        $this->assertTrue($this->validator->isValid($date, new Ip(array(
+        $this->assertTrue($this->validator->isValid($ip, new Ip(array(
             'version' => Ip::V4,
         ))));
     }
@@ -54,17 +54,22 @@ class IpValidatorTest extends \PHPUnit_Framework_TestCase
     {
         return array(
             array('0.0.0.0'),
-            array('255.255.255.255'),
+            array('10.0.0.0'),
             array('123.45.67.178'),
+            array('172.16.0.0'),
+            array('192.168.1.0'),
+            array('224.0.0.1'),
+            array('255.255.255.255'),
+            array('127.0.0.0'),
         );
     }
 
     /**
      * @dataProvider getValidIpsV6
      */
-    public function testValidIpsV6($date)
+    public function testValidIpsV6($ip)
     {
-        $this->assertTrue($this->validator->isValid($date, new Ip(array(
+        $this->assertTrue($this->validator->isValid($ip, new Ip(array(
             'version' => Ip::V6,
         ))));
     }
@@ -75,6 +80,9 @@ class IpValidatorTest extends \PHPUnit_Framework_TestCase
             array('2001:0db8:85a3:0000:0000:8a2e:0370:7334'),
             array('2001:0DB8:85A3:0000:0000:8A2E:0370:7334'),
             array('2001:0Db8:85a3:0000:0000:8A2e:0370:7334'),
+            array('fdfe:dcba:9876:ffff:fdc6:c46b:bb8f:7d4c'),
+            array('fdc6:c46b:bb8f:7d4c:fdc6:c46b:bb8f:7d4c'),
+            array('fdc6:c46b:bb8f:7d4c:0000:8a2e:0370:7334'),
             array('fe80:0000:0000:0000:0202:b3ff:fe1e:8329'),
             array('fe80:0:0:0:202:b3ff:fe1e:8329'),
             array('fe80::202:b3ff:fe1e:8329'),
@@ -94,9 +102,9 @@ class IpValidatorTest extends \PHPUnit_Framework_TestCase
     /**
      * @dataProvider getValidIpsAll
      */
-    public function testValidIpsAll($date)
+    public function testValidIpsAll($ip)
     {
-        $this->assertTrue($this->validator->isValid($date, new Ip(array(
+        $this->assertTrue($this->validator->isValid($ip, new Ip(array(
             'version' => Ip::ALL,
         ))));
     }
@@ -109,9 +117,9 @@ class IpValidatorTest extends \PHPUnit_Framework_TestCase
     /**
      * @dataProvider getInvalidIpsV4
      */
-    public function testInvalidIpsV4($date)
+    public function testInvalidIpsV4($ip)
     {
-        $this->assertFalse($this->validator->isValid($date, new Ip(array(
+        $this->assertFalse($this->validator->isValid($ip, new Ip(array(
             'version' => Ip::V4,
         ))));
     }
@@ -131,12 +139,65 @@ class IpValidatorTest extends \PHPUnit_Framework_TestCase
         );
     }
 
+    /**
+     * @dataProvider getInvalidPrivateIpsV4
+     */
+    public function testInvalidPrivateIpsV4($ip)
+    {
+        $this->assertFalse($this->validator->isValid($ip, new Ip(array(
+            'version' => Ip::V4_NO_PRIV,
+        ))));
+    }
+
+    public function getInvalidPrivateIpsV4()
+    {
+        return array(
+            array('10.0.0.0'),
+            array('172.16.0.0'),
+            array('192.168.1.0'),
+        );
+    }
+
+    /**
+     * @dataProvider getInvalidReservedIpsV4
+     */
+    public function testInvalidReservedIpsV4($ip)
+    {
+        $this->assertFalse($this->validator->isValid($ip, new Ip(array(
+            'version' => Ip::V4_NO_RES,
+        ))));
+    }
+
+    public function getInvalidReservedIpsV4()
+    {
+        return array(
+            array('0.0.0.0'),
+            array('224.0.0.1'),
+            array('255.255.255.255'),
+        );
+    }
+
+    /**
+     * @dataProvider getInvalidPublicIpsV4
+     */
+    public function testInvalidPublicIpsV4($ip)
+    {
+        $this->assertFalse($this->validator->isValid($ip, new Ip(array(
+            'version' => Ip::V4_ONLY_PUBLIC,
+        ))));
+    }
+
+    public function getInvalidPublicIpsV4()
+    {
+        return array_merge($this->getInvalidPrivateIpsV4(), $this->getInvalidReservedIpsV4());
+    }
+
     /**
      * @dataProvider getInvalidIpsV6
      */
-    public function testInvalidIpsV6($date)
+    public function testInvalidIpsV6($ip)
     {
-        $this->assertFalse($this->validator->isValid($date, new Ip(array(
+        $this->assertFalse($this->validator->isValid($ip, new Ip(array(
             'version' => Ip::V6,
         ))));
     }
@@ -160,12 +221,52 @@ class IpValidatorTest extends \PHPUnit_Framework_TestCase
         );
     }
 
+    /**
+     * @dataProvider getInvalidPrivateIpsV6
+     */
+    public function testInvalidPrivateIpsV6($ip)
+    {
+        $this->assertFalse($this->validator->isValid($ip, new Ip(array(
+            'version' => Ip::V6_NO_PRIV,
+        ))));
+    }
+
+    public function getInvalidPrivateIpsV6()
+    {
+        return array(
+            array('fdfe:dcba:9876:ffff:fdc6:c46b:bb8f:7d4c'),
+            array('fdc6:c46b:bb8f:7d4c:fdc6:c46b:bb8f:7d4c'),
+            array('fdc6:c46b:bb8f:7d4c:0000:8a2e:0370:7334'),
+        );
+    }
+
+    public function getInvalidReservedIpsV6()
+    {
+        // Empty array as I cannot find any reserved IP6 address
+        return array();
+    }
+
+    /**
+     * @dataProvider getInvalidPublicIpsV6
+     */
+    public function testInvalidPublicIpsV6($ip)
+    {
+        $this->assertFalse($this->validator->isValid($ip, new Ip(array(
+            'version' => Ip::V6_ONLY_PUBLIC,
+        ))));
+    }
+
+    public function getInvalidPublicIpsV6()
+    {
+        return array_merge($this->getInvalidPrivateIpsV6(), $this->getInvalidReservedIpsV6());
+    }
+
     /**
      * @dataProvider getInvalidIpsAll
      */
-    public function testInvalidIpsAll($date)
+    public function testInvalidIpsAll($ip)
     {
-        $this->assertFalse($this->validator->isValid($date, new Ip(array(
+        $this->assertFalse($this->validator->isValid($ip, new Ip(array(
             'version' => Ip::ALL,
         ))));
     }
@@ -175,6 +276,51 @@ class IpValidatorTest extends \PHPUnit_Framework_TestCase
         return array_merge($this->getInvalidIpsV4(), $this->getInvalidIpsV6());
     }
 
+    /**
+     * @dataProvider getInvalidPrivateIpsAll
+     */
+    public function testInvalidPrivateIpsAll($ip)
+    {
+        $this->assertFalse($this->validator->isValid($ip, new Ip(array(
+            'version' => Ip::ALL_NO_PRIV,
+        ))));
+    }
+
+    public function getInvalidPrivateIpsAll()
+    {
+        return array_merge($this->getInvalidPrivateIpsV4(), $this->getInvalidPrivateIpsV6());
+    }
+
+    /**
+     * @dataProvider getInvalidReservedIpsAll
+     */
+    public function testInvalidReservedIpsAll($ip)
+    {
+        $this->assertFalse($this->validator->isValid($ip, new Ip(array(
+            'version' => Ip::ALL_NO_RES,
+        ))));
+    }
+
+    public function getInvalidReservedIpsAll()
+    {
+        return array_merge($this->getInvalidReservedIpsV4(), $this->getInvalidReservedIpsV6());
+    }
+
+    /**
+     * @dataProvider getInvalidPublicIpsAll
+     */
+    public function testInvalidPublicIpsAll($ip)
+    {
+        $this->assertFalse($this->validator->isValid($ip, new Ip(array(
+            'version' => Ip::ALL_ONLY_PUBLIC,
+        ))));
+    }
+
+    public function getInvalidPublicIpsAll()
+    {
+        return array_merge($this->getInvalidPublicIpsV4(), $this->getInvalidPublicIpsV6());
+    }
+
     public function testMessageIsSet()
     {
         $constraint = new Ip(array(