diff --git a/src/Appwrite/Network/Validator/Email.php b/src/Appwrite/Network/Validator/Email.php new file mode 100644 index 000000000..451e8e3b0 --- /dev/null +++ b/src/Appwrite/Network/Validator/Email.php @@ -0,0 +1,66 @@ + + * @license The MIT License (MIT) + */ + +namespace Utopia\Validator; + +use Utopia\Validator; + +/** + * Email + * + * Validate that an variable is a valid email address + * + * @package Utopia\Validator + */ +class Email extends Validator +{ + /** + * Get Description + * + * Returns validator description + * + * @return string + */ + public function getDescription() + { + return 'Value must be a valid email address'; + } + + /** + * Get Type + * + * Returns validator type. + * + * @return string + */ + public function getType() + { + return self::TYPE_STRING; + } + + /** + * Is valid + * + * Validation will pass when $value is valid email address. + * + * @param mixed $value + * @return bool + */ + public function isValid($value) + { + if (!\filter_var($value, FILTER_VALIDATE_EMAIL)) { + return false; + } + + return true; + } +} diff --git a/src/Appwrite/Network/Validator/Host.php b/src/Appwrite/Network/Validator/Host.php new file mode 100644 index 000000000..54c9ebbd2 --- /dev/null +++ b/src/Appwrite/Network/Validator/Host.php @@ -0,0 +1,82 @@ + + * @license The MIT License (MIT) + */ + +namespace Utopia\Validator; + +use Utopia\Validator; + +/** + * Host + * + * Validate that a host is allowed from given whitelisted hosts list + * + * @package Utopia\Validator + */ +class Host extends Validator +{ + protected $whitelist = []; + + /** + * @param array $whitelist + */ + public function __construct(array $whitelist) + { + $this->whitelist = $whitelist; + } + + /** + * Get Description + * + * Returns validator description + * + * @return string + */ + public function getDescription() + { + return 'URL host must be one of: ' . \implode(', ', $this->whitelist); + } + + /** + * Get Type + * + * Returns validator type. + * + * @return string + */ + public function getType() + { + return self::TYPE_STRING; + } + + /** + * Is valid + * + * Validation will pass when $value starts with one of the given hosts + * + * @param mixed $value + * @return bool + */ + public function isValid($value) + { + $urlValidator = new URL(); + + if (!$urlValidator->isValid($value)) { + return false; + } + + if (\in_array(\parse_url($value, PHP_URL_HOST), $this->whitelist)) { + return true; + } + + return false; + } +} diff --git a/src/Appwrite/Network/Validator/IP.php b/src/Appwrite/Network/Validator/IP.php new file mode 100644 index 000000000..b1ea72779 --- /dev/null +++ b/src/Appwrite/Network/Validator/IP.php @@ -0,0 +1,112 @@ + + * @license The MIT License (MIT) + */ + +namespace Utopia\Validator; + +use Exception; +use Utopia\Validator; + +/** + * IP + * + * Validate that an variable is a valid IP address + * + * @package Utopia\Validator + */ +class IP extends Validator +{ + const ALL = 'all'; + const V4 = 'ipv4'; + const V6 = 'ipv6'; + + /** + * @var string + */ + protected $type = self::ALL; + + /** + * Constructor + * + * Set a the type of IP check. + * + * @param string $type + */ + public function __construct(string $type = self::ALL) + { + if (!in_array($type, [self::ALL, self::V4, self::V6])) { + throw new Exception('Unsupported IP type'); + } + + $this->type = $type; + } + + /** + * Get Description + * + * Returns validator description + * + * @return string + */ + public function getDescription() + { + return 'Value must be a valid IP address'; + } + + /** + * Get Type + * + * Returns validator type. + * + * @return string + */ + public function getType() + { + return self::TYPE_STRING; + } + + /** + * Is valid + * + * Validation will pass when $value is valid IP address. + * + * @param mixed $value + * @return bool + */ + public function isValid($value) + { + switch ($this->type) { + case self::ALL: + if (\filter_var($value, FILTER_VALIDATE_IP)) { + return true; + } + break; + + case self::V4: + if (\filter_var($value, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) { + return true; + } + break; + + case self::V6: + if (\filter_var($value, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) { + return true; + } + break; + + default: + return false; + break; + } + + return false; + } +} diff --git a/src/Appwrite/Network/Validator/URL.php b/src/Appwrite/Network/Validator/URL.php new file mode 100644 index 000000000..a610928d7 --- /dev/null +++ b/src/Appwrite/Network/Validator/URL.php @@ -0,0 +1,66 @@ + + * @license The MIT License (MIT) + */ + +namespace Utopia\Validator; + +use Utopia\Validator; + +/** + * URL + * + * Validate that an variable is a valid URL + * + * @package Utopia\Validator + */ +class URL extends Validator +{ + /** + * Get Description + * + * Returns validator description + * + * @return string + */ + public function getDescription() + { + return 'Value must be a valid URL'; + } + + /** + * Get Type + * + * Returns validator type. + * + * @return string + */ + public function getType() + { + return self::TYPE_STRING; + } + + /** + * Is valid + * + * Validation will pass when $value is valid URL. + * + * @param mixed $value + * @return bool + */ + public function isValid($value) + { + if (\filter_var($value, FILTER_VALIDATE_URL) === false) { + return false; + } + + return true; + } +} diff --git a/tests/unit/Network/Validators/EmailTest.php b/tests/unit/Network/Validators/EmailTest.php new file mode 100755 index 000000000..03e4d043c --- /dev/null +++ b/tests/unit/Network/Validators/EmailTest.php @@ -0,0 +1,71 @@ + + * @version 1.0 RC4 + * @license The MIT License (MIT) + */ + +namespace Utopia\Validator; + +use PHPUnit\Framework\TestCase; + +class EmailTest extends TestCase +{ + /** + * @var Email + */ + protected $email = null; + + public function setUp():void + { + $this->email = new Email(); + } + + public function tearDown():void + { + $this->email = null; + } + + public function testIsValid() + { + // Assertions + $this->assertEquals(true, $this->email->isValid('email@domain.com')); + $this->assertEquals(true, $this->email->isValid('firstname.lastname@domain.com')); + $this->assertEquals(true, $this->email->isValid('email@subdomain.domain.com')); + $this->assertEquals(true, $this->email->isValid('firstname+lastname@domain.com')); + $this->assertEquals(true, $this->email->isValid('email@[123.123.123.123]')); + $this->assertEquals(true, $this->email->isValid('"email"@domain.com')); + $this->assertEquals(true, $this->email->isValid('1234567890@domain.com')); + $this->assertEquals(true, $this->email->isValid('email@domain-one.com')); + $this->assertEquals(true, $this->email->isValid('_______@domain.com')); + $this->assertEquals(true, $this->email->isValid('email@domain.name')); + $this->assertEquals(true, $this->email->isValid('email@domain.co.jp')); + $this->assertEquals(true, $this->email->isValid('firstname-lastname@domain.com')); + $this->assertEquals(false, $this->email->isValid(false)); + $this->assertEquals(false, $this->email->isValid(['string', 'string'])); + $this->assertEquals(false, $this->email->isValid(1)); + $this->assertEquals(false, $this->email->isValid(1.2)); + $this->assertEquals(false, $this->email->isValid('plainaddress')); // Missing @ sign and domain + $this->assertEquals(false, $this->email->isValid('@domain.com')); // Missing username + $this->assertEquals(false, $this->email->isValid('#@%^%#$@#$@#.com')); // Garbage + $this->assertEquals(false, $this->email->isValid('Joe Smith ')); // Encoded html within email is invalid + $this->assertEquals(false, $this->email->isValid('email.domain.com')); // Missing @ + $this->assertEquals(false, $this->email->isValid('email@domain@domain.com')); // Two @ sign + $this->assertEquals(false, $this->email->isValid('.email@domain.com')); // Leading dot in address is not allowed + $this->assertEquals(false, $this->email->isValid('email.@domain.com')); // Trailing dot in address is not allowed + $this->assertEquals(false, $this->email->isValid('email..email@domain.com')); // Multiple dots + $this->assertEquals(false, $this->email->isValid('あいうえお@domain.com')); // Unicode char as address + $this->assertEquals(false, $this->email->isValid('email@domain.com (Joe Smith)')); // Text followed email is not allowed + $this->assertEquals(false, $this->email->isValid('email@domain')); // Missing top level domain (.com/.net/.org/etc) + $this->assertEquals(false, $this->email->isValid('email@-domain.com')); // Leading dash in front of domain is invalid + $this->assertEquals(false, $this->email->isValid('email@111.222.333.44444')); // Invalid IP format + $this->assertEquals(false, $this->email->isValid('email@domain..com')); // Multiple dot in the domain portion is invalid + $this->assertEquals($this->email->getType(), 'string'); + } +} diff --git a/tests/unit/Network/Validators/HostTest.php b/tests/unit/Network/Validators/HostTest.php new file mode 100755 index 000000000..dbaf83b7b --- /dev/null +++ b/tests/unit/Network/Validators/HostTest.php @@ -0,0 +1,45 @@ + + * @version 1.0 RC4 + * @license The MIT License (MIT) + */ + +namespace Utopia\Validator; + +use PHPUnit\Framework\TestCase; + +class HostTest extends TestCase +{ + /** + * @var Host + */ + protected $host = null; + + public function setUp():void + { + $this->host = new Host(['appwrite.io', 'subdomain.appwrite.test', 'localhost']); + } + + public function tearDown():void + { + $this->host = null; + } + + public function testIsValid() + { + // Assertions + $this->assertEquals($this->host->isValid('https://appwrite.io/link'), true); + $this->assertEquals($this->host->isValid('https://localhost'), true); + $this->assertEquals($this->host->isValid('localhost'), false); + $this->assertEquals($this->host->isValid('http://subdomain.appwrite.test/path'), true); + $this->assertEquals($this->host->isValid('http://test.subdomain.appwrite.test/path'), false); + $this->assertEquals($this->host->getType(), 'string'); + } +} diff --git a/tests/unit/Network/Validators/IPTest.php b/tests/unit/Network/Validators/IPTest.php new file mode 100755 index 000000000..1be8f78ab --- /dev/null +++ b/tests/unit/Network/Validators/IPTest.php @@ -0,0 +1,81 @@ + + * @version 1.0 RC4 + * @license The MIT License (MIT) + */ + +namespace Utopia\Validator; + +use PHPUnit\Framework\TestCase; + +class IPTest extends TestCase +{ + public function tearDown():void + { + $this->validator = null; + } + + public function testIsValidIP() + { + $validator = new IP(); + + // Assertions + $this->assertEquals($validator->isValid('2001:0db8:85a3:08d3:1319:8a2e:0370:7334'), true); + $this->assertEquals($validator->isValid('109.67.204.101'), true); + $this->assertEquals($validator->isValid(23.5), false); + $this->assertEquals($validator->isValid('23.5'), false); + $this->assertEquals($validator->isValid(null), false); + $this->assertEquals($validator->isValid(true), false); + $this->assertEquals($validator->isValid(false), false); + $this->assertEquals($validator->getType(), 'string'); + } + + public function testIsValidIPALL() + { + $validator = new IP(IP::ALL); + + // Assertions + $this->assertEquals($validator->isValid('2001:0db8:85a3:08d3:1319:8a2e:0370:7334'), true); + $this->assertEquals($validator->isValid('109.67.204.101'), true); + $this->assertEquals($validator->isValid(23.5), false); + $this->assertEquals($validator->isValid('23.5'), false); + $this->assertEquals($validator->isValid(null), false); + $this->assertEquals($validator->isValid(true), false); + $this->assertEquals($validator->isValid(false), false); + } + + public function testIsValidIPV4() + { + $validator = new IP(IP::V4); + + // Assertions + $this->assertEquals($validator->isValid('2001:0db8:85a3:08d3:1319:8a2e:0370:7334'), false); + $this->assertEquals($validator->isValid('109.67.204.101'), true); + $this->assertEquals($validator->isValid(23.5), false); + $this->assertEquals($validator->isValid('23.5'), false); + $this->assertEquals($validator->isValid(null), false); + $this->assertEquals($validator->isValid(true), false); + $this->assertEquals($validator->isValid(false), false); + } + + public function testIsValidIPV6() + { + $validator = new IP(IP::V6); + + // Assertions + $this->assertEquals($validator->isValid('2001:0db8:85a3:08d3:1319:8a2e:0370:7334'), true); + $this->assertEquals($validator->isValid('109.67.204.101'), false); + $this->assertEquals($validator->isValid(23.5), false); + $this->assertEquals($validator->isValid('23.5'), false); + $this->assertEquals($validator->isValid(null), false); + $this->assertEquals($validator->isValid(true), false); + $this->assertEquals($validator->isValid(false), false); + } +} diff --git a/tests/unit/Network/Validators/URLTest.php b/tests/unit/Network/Validators/URLTest.php new file mode 100755 index 000000000..cd4844f7c --- /dev/null +++ b/tests/unit/Network/Validators/URLTest.php @@ -0,0 +1,48 @@ + + * @version 1.0 RC4 + * @license The MIT License (MIT) + */ + +namespace Utopia\Validator; + +use PHPUnit\Framework\TestCase; + +class URLTest extends TestCase +{ + /** + * @var Domain + */ + protected $url = null; + + public function setUp():void + { + $this->url = new URL(); + } + + public function tearDown():void + { + $this->url = null; + } + + public function testIsValid() + { + // Assertions + $this->assertEquals(true, $this->url->isValid('http://example.com')); + $this->assertEquals(true, $this->url->isValid('https://example.com')); + $this->assertEquals(true, $this->url->isValid('htts://example.com')); // does not validate protocol + $this->assertEquals(false, $this->url->isValid('example.com')); // though, requires some kind of protocol + $this->assertEquals(false, $this->url->isValid('http:/example.com')); + $this->assertEquals(true, $this->url->isValid('http://exa-mple.com')); + $this->assertEquals(false, $this->url->isValid('htt@s://example.com')); + $this->assertEquals(true, $this->url->isValid('http://www.example.com/foo%2\u00c2\u00a9zbar')); + $this->assertEquals(true, $this->url->isValid('http://www.example.com/?q=%3Casdf%3E')); + } +}