Commit 2659d305 authored by Emma's avatar Emma 😻

add validator for IP with CIDR notation

parent cf35b19c
......@@ -4,11 +4,13 @@ namespace Raddit\AppBundle\Form\Model;
use Raddit\AppBundle\Entity\IpBan;
use Raddit\AppBundle\Entity\User;
use Raddit\AppBundle\Validator\Constraints\IpWithCidr;
use Symfony\Component\Validator\Constraints as Assert;
class IpBanData {
/**
* @Assert\NotBlank()
* @IpWithCidr()
*
* @var string|null
*/
......
......@@ -5,12 +5,13 @@ namespace Raddit\AppBundle\Form\Model;
use Raddit\AppBundle\Entity\IpBan;
use Raddit\AppBundle\Entity\User;
use Raddit\AppBundle\Entity\UserBan;
use Raddit\AppBundle\Validator\Constraints\IpWithCidr;
use Symfony\Component\Validator\Constraints as Assert;
class UserBanData {
/**
* @Assert\Ip(version="all", groups={"ban_ip"})
* @Assert\NotBlank(groups={"ban_ip"})
* @IpWithCidr(groups={"ban_ip"})
*
* @var string|null
*/
......
<?php
namespace Raddit\AppBundle\Validator\Constraints;
use Doctrine\Common\Annotations\Annotation\Target;
use Symfony\Component\Validator\Constraint;
/**
* @Annotation
* @Target({"CLASS", "PROPERTY"})
*/
class IpWithCidr extends Constraint {
const INVALID_IP = '24672f6c-5a23-4067-8566-e44c35db9556';
const INVALID_CIDR = 'adf9db03-ccd6-43d2-8fd6-8dcc9ce9c3a1';
const MISSING_CIDR = '07301ef6-c958-430d-952e-2969a7d9cfb9';
protected static $errorNames = [
self::INVALID_IP => 'INVALID_IP',
self::INVALID_CIDR => 'INVALID_CIDR',
self::MISSING_CIDR => 'MISSING_CIDR',
];
public $cidrOptional = true;
public $invalidIpMessage = 'The IP address is not valid.';
public $invalidCidrMessage = 'The CIDR mask is not valid.';
public $missingCidrMessage = 'Missing CIDR mask.';
/**
* {@inheritdoc}
*/
public function getTargets() {
return [self::CLASS_CONSTRAINT, self::PROPERTY_CONSTRAINT];
}
}
<?php
namespace Raddit\AppBundle\Validator\Constraints;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
class IpWithCidrValidator extends ConstraintValidator {
/**
* {@inheritdoc}
*/
public function validate($value, Constraint $constraint) {
if (!$constraint instanceof IpWithCidr) {
throw new UnexpectedTypeException($constraint, IpWithCidr::class);
}
if (!is_scalar($value) && method_exists($value, '__toString')) {
throw new UnexpectedTypeException($value, 'string');
}
$value = (string) $value;
if ($value === '') {
return;
}
list($ip, $cidr) = array_pad(explode('/', $value, 2), 2, null);
if (!filter_var($ip, FILTER_VALIDATE_IP)) {
$this->context->buildViolation($constraint->invalidIpMessage)
->setCode(IpWithCidr::INVALID_IP)
->addViolation();
return;
}
if ($cidr === null) {
if (!$constraint->cidrOptional) {
$this->context->buildViolation($constraint->missingCidrMessage)
->setCode(IpWithCidr::MISSING_CIDR)
->addViolation();
}
} else {
$maxCidr = strpos($ip, ':') !== false ? 128 : 32;
if (!ctype_digit($cidr) || $cidr < 0 || $cidr > $maxCidr) {
$this->context->buildViolation($constraint->invalidCidrMessage)
->setCode(IpWithCidr::INVALID_CIDR)
->addViolation();
}
}
}
}
<?php
namespace Raddit\Tests\AppBundle\Validator\Constraints;
use Raddit\AppBundle\Validator\Constraints\IpWithCidr;
use Raddit\AppBundle\Validator\Constraints\IpWithCidrValidator;
use Symfony\Component\Validator\Test\ConstraintValidatorTestCase;
class IpWithCidrValidatorTest extends ConstraintValidatorTestCase {
protected function createValidator() {
return new IpWithCidrValidator();
}
/**
* @dataProvider validValuesProvider
*/
public function testValidValues($value) {
$this->validator->validate($value, new IpWithCidr());
$this->assertNoViolation();
}
/**
* @dataProvider cidrLessProvider
*/
public function testRaisesErrorWithoutCidr($value) {
$constraint = new IpWithCidr([
'cidrOptional' => false,
'missingCidrMessage' => 'missingCidr',
]);
$this->validator->validate($value, $constraint);
$this->buildViolation('missingCidr')
->setCode(IpWithCidr::MISSING_CIDR)
->assertRaised();
}
/**
* @dataProvider invalidIpProvider
*/
public function testRaisesErrorOnInvalidIp($value) {
$constraint = new IpWithCidr([
'invalidIpMessage' => 'invalidIp',
]);
$this->validator->validate($value, $constraint);
$this->buildViolation('invalidIp')
->setCode(IpWithCidr::INVALID_IP)
->assertRaised();
}
/**
* @dataProvider invalidCidrProvider
*/
public function testRaisesErrorOnInvalidCidr($value) {
$constraint = new IpWithCidr([
'invalidCidrMessage' => 'invalidCidr',
]);
$this->validator->validate($value, $constraint);
$this->buildViolation('invalidCidr')
->setCode(IpWithCidr::INVALID_CIDR)
->assertRaised();
}
public function validValuesProvider() {
yield ['127.0.0.1/32'];
yield ['254.253.252.251/31'];
yield ['192.168.4.20'];
yield ['1312::1917/24'];
yield ['420::69/15'];
yield ['4:3:2::1'];
}
public function cidrLessProvider() {
yield ['::1'];
yield ['192.168.4.20'];
}
public function invalidIpProvider() {
yield ['256.256.256.256/32'];
yield ['goop::crap'];
}
public function invalidCidrProvider() {
yield ['::1/129'];
yield ['::/-128'];
yield ['127.6.5.4/33'];
yield ['127.0.0.1/'.PHP_INT_MAX.PHP_INT_MAX];
yield ['127.0.0.1/crap'];
}
}
......@@ -17,3 +17,6 @@
'No such theme.': 'No such theme.'
'That theme cannot be extended.': 'That theme cannot be extended.'
'That user is already a moderator.': 'That user is already a moderator.'
'The IP address is not valid.': 'The IP address is not valid.'
'The CIDR mask is not valid.': 'The CIDR mask is not valid.'
'Missing CIDR mask.': 'Missing CIDR mask.'
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment