diff --git a/README_toolbox.md b/README_toolbox.md index ac18ac0..523bd52 100644 --- a/README_toolbox.md +++ b/README_toolbox.md @@ -62,4 +62,18 @@ Save some actions made in backoffice into a log file Clean name of the file uploaded in backoffice when moving into storage to prevent SEO penalties. Change special characters to their latin correspondence when it's possible (else they will be removed), replace spaces and force lower case. - \ No newline at end of file + +### IP CIDR Validator + +You can check if an IP is in CIDR range with this : + +```php +$ipConstraint = new \SQLI\EzToolboxBundle\Validator\Constraints\IpCidr(['cidr' => "192.168.0.0/24"]); +$errors = $this->validator->validate( '192.168.1.10', $ipConstraint ); + +if(count($errors)) { + $message = $errors[0]->getMessage(); +} +``` + +In this case, $message contains `192.168.1.10 not validated with CIDR mask 192.168.0.0/24` \ No newline at end of file diff --git a/Validator/Constraints/IpCidr.php b/Validator/Constraints/IpCidr.php new file mode 100644 index 0000000..fa2f98b --- /dev/null +++ b/Validator/Constraints/IpCidr.php @@ -0,0 +1,51 @@ + 'INVALID_MASK', + self::INVALID_IP => 'INVALID_IP', + self::NOT_IN_MASK => 'NOT_IN_MASK', + ]; + public $message = "{{ value }} not validated with CIDR mask {{ cidr }}"; + public $cidr; + + /** + * {@inheritdoc} + */ + public function __construct($options = null) + { + parent::__construct($options); + } + + /** + * Returns the name of the default option. + * Override this method to define a default option. + * + * @return string|null + * @see __construct() + */ + public function getDefaultOption() + { + return 'cidr'; + } + + /** + * Returns the name of the required options. + * Override this method if you want to define required options. + * + * @return array + * @see __construct() + */ + public function getRequiredOptions() + { + return ['cidr']; + } +} \ No newline at end of file diff --git a/Validator/Constraints/IpCidrValidator.php b/Validator/Constraints/IpCidrValidator.php new file mode 100644 index 0000000..2a4fc25 --- /dev/null +++ b/Validator/Constraints/IpCidrValidator.php @@ -0,0 +1,73 @@ +checkCidrMask($value, $constraint->cidr)) { + $this->context->buildViolation($constraint->message) + ->setParameter('{{ value }}', $value) + ->setParameter('{{ cidr }}', $constraint->cidr) + ->setCode(IpCidr::NOT_IN_MASK) + ->setInvalidValue($value) + ->addViolation(); + } + } + + /** + * Check if $iptocheck is in range defined by $CIDR + * Ex: 192.168.0.1 in 192.168.0.0/24 => true + * Ex: 192.168.1.1 in 192.168.0.0/24 => false + * + * @param string $iptocheck + * @param string $CIDR + * @return bool + */ + private function checkCidrMask($iptocheck, $CIDR): bool + { + /* get the base and the bits from the ban in the database */ + list($base, $bits) = explode('/', $CIDR); + + /* now split it up into it's classes */ + list($a, $b, $c, $d) = explode('.', $base); + + /* now do some bit shfiting/switching to convert to ints */ + $i = ($a << 24) + ($b << 16) + ($c << 8) + $d; + $mask = $bits == 0 ? 0 : (~0 << (32 - $bits)); + + /* here's our lowest int */ + $low = $i & $mask; + + /* here's our highest int */ + $high = $i | (~$mask & 0xFFFFFFFF); + + /* now split the ip were checking against up into classes */ + list($a, $b, $c, $d) = explode('.', $iptocheck); + + /* now convert the ip we're checking against to an int */ + $check = ($a << 24) + ($b << 16) + ($c << 8) + $d; + + /* if the ip is within the range, including + highest/lowest values, then it's witin the CIDR range */ + + return ($check >= $low && $check <= $high); + } +} \ No newline at end of file