diff --git a/.travis/run-tests.sh b/.travis/run-tests.sh index 7e1baa3b7..a84e0ba2a 100755 --- a/.travis/run-tests.sh +++ b/.travis/run-tests.sh @@ -1,5 +1,5 @@ #!/bin/bash -travisdir=$(dirname $(readlink /proc/$$/fd/255)) +travisdir=$(dirname "$0") testdir="$travisdir/../tests" testedcomponents=(`cat "$travisdir/tested-components"`) result=0 diff --git a/.travis/skipped-components b/.travis/skipped-components index 31bcaa87f..171dfe9d7 100644 --- a/.travis/skipped-components +++ b/.travis/skipped-components @@ -1,5 +1,6 @@ Zend/Amf Zend/Date +Zend/Dojo Zend/Queue Zend/Service Zend/Test diff --git a/.travis/tested-components b/.travis/tested-components index b1f4a794d..b0b943800 100644 --- a/.travis/tested-components +++ b/.travis/tested-components @@ -22,6 +22,7 @@ Zend/Form Zend/GData Zend/Http Zend/InfoCard +Zend/InputFilter Zend/Json Zend/Ldap Zend/Loader @@ -29,10 +30,11 @@ Zend/Locale Zend/Log Zend/Mail Zend/Markup +Zend/Math Zend/Measure Zend/Memory Zend/Mime -Zend/Module +Zend/ModuleManager Zend/Mvc Zend/Navigation Zend/OAuth diff --git a/src/Barcode.php b/src/Barcode.php index 7a3012589..5b2f5115c 100644 --- a/src/Barcode.php +++ b/src/Barcode.php @@ -103,9 +103,6 @@ public function setAdapter($adapter, $options = null) if (is_string($adapter)) { $adapter = ucfirst(strtolower($adapter)); $adapter = 'Zend\Validator\Barcode\\' . $adapter; - if (\Zend\Loader::isReadable('Zend/Validator/Barcode/' . $adapter . '.php')) { - $adapter = 'Zend\Validator\Barcode\\' . $adapter; - } if (!class_exists($adapter)) { throw new Exception\InvalidArgumentException('Barcode adapter matching "' . $adapter . '" not found'); diff --git a/src/Callback.php b/src/Callback.php index d1fc88e64..75aace488 100644 --- a/src/Callback.php +++ b/src/Callback.php @@ -125,9 +125,10 @@ public function setCallbackOptions($options) * for the provided $value * * @param mixed $value + * @param mixed $context Additional context to provide to the callback * @return boolean */ - public function isValid($value) + public function isValid($value, $context = null) { $this->setValue($value); @@ -137,11 +138,20 @@ public function isValid($value) throw new Exception\InvalidArgumentException('No callback given'); } - $args = func_get_args(); - $options = array_merge($args, $options); + $args = array($value); + if (empty($options) && !empty($context)) { + $args[] = $context; + } + if (!empty($options) && empty($context)) { + $args = array_merge($args, $options); + } + if (!empty($options) && !empty($context)) { + $args[] = $context; + $args = array_merge($args, $options); + } try { - if (!call_user_func_array($callback, $options)) { + if (!call_user_func_array($callback, $args)) { $this->error(self::INVALID_VALUE); return false; } diff --git a/src/Csrf.php b/src/Csrf.php new file mode 100644 index 000000000..138731975 --- /dev/null +++ b/src/Csrf.php @@ -0,0 +1,316 @@ + "The form submitted did not originate from the expected site", + ); + + /** + * Actual hash used. + * + * @var mixed + */ + protected $hash; + + /** + * Static cache of the session names to generated hashes + * + * @var array + */ + protected static $hashCache; + + /** + * Name of CSRF element (used to create non-colliding hashes) + * + * @var string + */ + protected $name = 'csrf'; + + /** + * Salt for CSRF token + * @var string + */ + protected $salt = 'salt'; + + /** + * @var SessionContainer + */ + protected $session; + + /** + * TTL for CSRF token + * @var int + */ + protected $timeout = 300; + + /** + * Constructor + * + * @param array $options + * @return void + */ + public function __construct($options = array()) + { + parent::__construct(); + + if ($options instanceof Traversable) { + $options = ArrayUtils::iteratorToArray($options); + } + + if (!is_array($options)) { + $options = (array) $options; + } + + foreach ($options as $key => $value) { + switch (strtolower($key)) { + case 'name': + $this->setName($value); + break; + case 'salt': + $this->setSalt($value); + break; + case 'session': + $this->setSession($value); + break; + case 'timeout': + $this->setTimeout($value); + break; + default: + // ignore uknown options + break; + } + } + } + + /** + * Does the provided token match the one generated? + * + * @param string $value + * @param mixed $context + * @return bool + */ + public function isValid($value, $context = null) + { + $this->setValue((string) $value); + + $hash = $this->getValidationToken(); + + if ($value !== $hash) { + $this->error(self::NOT_SAME); + return false; + } + + return true; + } + + /** + * Set CSRF name + * + * @param string $name + * @return Csrf + */ + public function setName($name) + { + $this->name = (string) $name; + return $this; + } + + /** + * Get CSRF name + * + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * Set session container + * + * @param SessionContainer $session + * @return Csrf + */ + public function setSession(SessionContainer $session) + { + $this->session = $session; + if ($this->hash) { + $this->initCsrfToken(); + } + return $this; + } + + /** + * Get session container + * + * Instantiate session container if none currently exists + * + * @return SessionContainer + */ + public function getSession() + { + if (null === $this->session) { + $this->session = new SessionContainer($this->getSessionName()); + } + return $this->session; + } + + /** + * Salt for CSRF token + * + * @param string $salt + * @return Csrf + */ + public function setSalt($salt) + { + $this->salt = (string) $salt; + return $this; + } + + /** + * Retrieve salt for CSRF token + * + * @return string + */ + public function getSalt() + { + return $this->salt; + } + + /** + * Retrieve CSRF token + * + * If no CSRF token currently exists, generates one. + * + * @return string + */ + public function getHash() + { + if (null === $this->hash) { + $this->generateHash(); + } + return $this->hash; + } + + /** + * Get session namespace for CSRF token + * + * Generates a session namespace based on salt, element name, and class. + * + * @return string + */ + public function getSessionName() + { + return str_replace('\\', '_', __CLASS__) . '_' . $this->getSalt() . '_' . $this->getName(); + } + + /** + * Set timeout for CSRF session token + * + * @param int $ttl + * @return Csrf + */ + public function setTimeout($ttl) + { + $this->timeout = (int) $ttl; + return $this; + } + + /** + * Get CSRF session token timeout + * + * @return int + */ + public function getTimeout() + { + return $this->timeout; + } + + /** + * Initialize CSRF token in session + * + * @return void + */ + public function initCsrfToken() + { + $session = $this->getSession(); + $session->setExpirationHops(1, null, true); + $session->setExpirationSeconds($this->getTimeout()); + $session->hash = $this->getHash(); + } + + /** + * Generate CSRF token + * + * Generates CSRF token and stores both in {@link $hash} and element + * value. + * + * @return void + */ + protected function generateHash() + { + if (isset(static::$hashCache[$this->getSessionName()])) { + $this->hash = static::$hashCache[$this->getSessionName()]; + } else { + $this->hash = md5( + mt_rand(1,1000000) + . $this->getSalt() + . $this->getName() + . mt_rand(1,1000000) + ); + static::$hashCache[$this->getSessionName()] = $this->hash; + } + $this->setValue($this->hash); + $this->initCsrfToken(); + } + + /** + * Get validation token + * + * Retrieve token from session, if it exists. + * + * @return null|string + */ + protected function getValidationToken() + { + $session = $this->getSession(); + if (isset($session->hash)) { + return $session->hash; + } + return null; + } +} diff --git a/src/Exception/BadMethodCallException.php b/src/Exception/BadMethodCallException.php index 2faa196b0..3b4ba3caa 100644 --- a/src/Exception/BadMethodCallException.php +++ b/src/Exception/BadMethodCallException.php @@ -4,6 +4,5 @@ class BadMethodCallException extends \BadMethodCallException - implements \Zend\Validator\Exception\ExceptionInterface -{ -} + implements ExceptionInterface +{} \ No newline at end of file diff --git a/src/Exception/ExceptionInterface.php b/src/Exception/ExceptionInterface.php index 45f83d7f0..aaf554af1 100644 --- a/src/Exception/ExceptionInterface.php +++ b/src/Exception/ExceptionInterface.php @@ -27,4 +27,4 @@ * @license http://framework.zend.com/license/new-bsd New BSD License */ interface ExceptionInterface -{} +{} \ No newline at end of file diff --git a/src/Exception/ExtensionNotLoadedException.php b/src/Exception/ExtensionNotLoadedException.php index fbbf2b151..96eda8984 100644 --- a/src/Exception/ExtensionNotLoadedException.php +++ b/src/Exception/ExtensionNotLoadedException.php @@ -3,8 +3,5 @@ namespace Zend\Validator\Exception; class ExtensionNotLoadedException - extends \RuntimeException - implements \Zend\Validator\Exception\ExceptionInterface -{ - -} + extends RuntimeException +{} \ No newline at end of file diff --git a/src/Exception/InvalidArgumentException.php b/src/Exception/InvalidArgumentException.php index ec4a9917d..8877f8843 100644 --- a/src/Exception/InvalidArgumentException.php +++ b/src/Exception/InvalidArgumentException.php @@ -4,6 +4,5 @@ class InvalidArgumentException extends \InvalidArgumentException - implements \Zend\Validator\Exception\ExceptionInterface -{ -} + implements ExceptionInterface +{} \ No newline at end of file diff --git a/src/Exception/InvalidMagicMimeFileException.php b/src/Exception/InvalidMagicMimeFileException.php index dc88b5463..dbe7f58da 100644 --- a/src/Exception/InvalidMagicMimeFileException.php +++ b/src/Exception/InvalidMagicMimeFileException.php @@ -3,7 +3,5 @@ namespace Zend\Validator\Exception; class InvalidMagicMimeFileException - extends \InvalidArgumentException - implements \Zend\Validator\Exception\ExceptionInterface -{ -} + extends InvalidArgumentException +{} \ No newline at end of file diff --git a/src/Exception/RuntimeException.php b/src/Exception/RuntimeException.php index 01e98249d..b03e34a9a 100644 --- a/src/Exception/RuntimeException.php +++ b/src/Exception/RuntimeException.php @@ -4,6 +4,5 @@ class RuntimeException extends \RuntimeException - implements \Zend\Validator\Exception\ExceptionInterface -{ -} + implements ExceptionInterface +{} \ No newline at end of file diff --git a/src/File/Crc32.php b/src/File/Crc32.php index 30c766000..2958d106c 100644 --- a/src/File/Crc32.php +++ b/src/File/Crc32.php @@ -20,8 +20,6 @@ namespace Zend\Validator\File; -use Zend\Loader; - /** * Validator for the crc32 hash of given files * @@ -106,7 +104,7 @@ public function isValid($value, $file = null) } // Is file readable ? - if (!Loader::isReadable($value)) { + if (false === stream_resolve_include_path($value)) { return $this->_throw($file, self::NOT_FOUND); } diff --git a/src/File/ExcludeExtension.php b/src/File/ExcludeExtension.php index e42b4f31c..2b9817a2f 100644 --- a/src/File/ExcludeExtension.php +++ b/src/File/ExcludeExtension.php @@ -20,8 +20,6 @@ namespace Zend\Validator\File; -use Zend\Loader; - /** * Validator for the excluding file extensions * @@ -61,7 +59,7 @@ public function isValid($value, $file = null) } // Is file readable ? - if (!Loader::isReadable($value)) { + if (false === stream_resolve_include_path($value)) { return $this->_throw($file, self::NOT_FOUND); } diff --git a/src/File/ExcludeMimeType.php b/src/File/ExcludeMimeType.php index 1bdd1e8e9..9d91ce540 100644 --- a/src/File/ExcludeMimeType.php +++ b/src/File/ExcludeMimeType.php @@ -20,8 +20,7 @@ namespace Zend\Validator\File; -use finfo, - Zend\Loader; +use finfo; /** * Validator for the mime type of a file @@ -56,7 +55,7 @@ public function isValid($value, $file = null) } // Is file readable ? - if (!Loader::isReadable($value)) { + if (false === stream_resolve_include_path($value)) { return $this->createError($file, self::NOT_READABLE); } diff --git a/src/File/Extension.php b/src/File/Extension.php index 4eda5b1c5..af56ab983 100644 --- a/src/File/Extension.php +++ b/src/File/Extension.php @@ -22,7 +22,6 @@ use Traversable; use Zend\Stdlib\ArrayUtils; -use Zend\Loader; /** * Validator for the file extension of a file @@ -197,7 +196,7 @@ public function isValid($value, $file = null) } // Is file readable ? - if (!Loader::isReadable($value)) { + if (false === stream_resolve_include_path($value)) { return $this->_throw($file, self::NOT_FOUND); } diff --git a/src/File/FilesSize.php b/src/File/FilesSize.php index e0aac7a5f..5391dfd3b 100644 --- a/src/File/FilesSize.php +++ b/src/File/FilesSize.php @@ -22,7 +22,6 @@ use Traversable; use Zend\Stdlib\ArrayUtils; -use Zend\Loader; /** * Validator for the size of all files which will be validated in sum @@ -109,7 +108,7 @@ public function isValid($value, $file = null) $size = $this->_getSize(); foreach ($value as $files) { // Is file readable ? - if (!Loader::isReadable($files)) { + if (false === stream_resolve_include_path($files)) { $this->_throw($file, self::NOT_READABLE); continue; } diff --git a/src/File/Hash.php b/src/File/Hash.php index 7f209b615..b682911dd 100644 --- a/src/File/Hash.php +++ b/src/File/Hash.php @@ -20,8 +20,7 @@ namespace Zend\Validator\File; -use Zend\Loader, - Zend\Validator, +use Zend\Validator, Zend\Validator\Exception; /** @@ -151,7 +150,7 @@ public function isValid($value, $file = null) } // Is file readable ? - if (!Loader::isReadable($value)) { + if (false === stream_resolve_include_path($value)) { return $this->_throw($file, self::NOT_FOUND); } diff --git a/src/File/ImageSize.php b/src/File/ImageSize.php index 0ca010b1c..93dbb3f67 100644 --- a/src/File/ImageSize.php +++ b/src/File/ImageSize.php @@ -20,8 +20,7 @@ namespace Zend\Validator\File; -use Zend\Loader, - Zend\Validator, +use Zend\Validator, Zend\Validator\Exception; /** @@ -393,7 +392,7 @@ public function isValid($value, $file = null) } // Is file readable ? - if (!Loader::isReadable($value)) { + if (false === stream_resolve_include_path($value)) { return $this->_throw($file, self::NOT_READABLE); } diff --git a/src/File/Md5.php b/src/File/Md5.php index 6c4b660c5..3bce10766 100644 --- a/src/File/Md5.php +++ b/src/File/Md5.php @@ -20,8 +20,6 @@ namespace Zend\Validator\File; -use Zend\Loader; - /** * Validator for the md5 hash of given files * @@ -106,7 +104,7 @@ public function isValid($value, $file = null) } // Is file readable ? - if (!Loader::isReadable($value)) { + if (false === stream_resolve_include_path($value)) { return $this->_throw($file, self::NOT_FOUND); } diff --git a/src/File/MimeType.php b/src/File/MimeType.php index fa72a3834..72a8a86fe 100644 --- a/src/File/MimeType.php +++ b/src/File/MimeType.php @@ -21,7 +21,6 @@ namespace Zend\Validator\File; use Traversable, - Zend\Loader, Zend\Stdlib\ArrayUtils, Zend\Validator\AbstractValidator, Zend\Validator\Exception; @@ -358,7 +357,7 @@ public function isValid($value, $file = null) } // Is file readable ? - if (!Loader::isReadable($value)) { + if (false === stream_resolve_include_path($value)) { return $this->createError($file, self::NOT_READABLE); } diff --git a/src/File/Sha1.php b/src/File/Sha1.php index e63ece700..480b1b53b 100644 --- a/src/File/Sha1.php +++ b/src/File/Sha1.php @@ -20,8 +20,7 @@ namespace Zend\Validator\File; -use Zend\Loader, - Zend\Validator\Exception; +use Zend\Validator\Exception; /** * Validator for the sha1 hash of given files @@ -107,7 +106,7 @@ public function isValid($value, $file = null) } // Is file readable ? - if (!Loader::isReadable($value)) { + if (false === stream_resolve_include_path($value)) { return $this->_throw($file, self::NOT_FOUND); } diff --git a/src/File/Size.php b/src/File/Size.php index fc52d9684..775060919 100644 --- a/src/File/Size.php +++ b/src/File/Size.php @@ -20,8 +20,7 @@ namespace Zend\Validator\File; -use Zend\Loader, - Zend\Validator, +use Zend\Validator, Zend\Validator\Exception; /** @@ -253,7 +252,7 @@ public function isValid($value, $file = null) } // Is file readable ? - if (!Loader::isReadable($value)) { + if (false === stream_resolve_include_path($value)) { return $this->_throw($file, self::NOT_FOUND); } diff --git a/src/File/WordCount.php b/src/File/WordCount.php index 7a8125a38..d987816fb 100644 --- a/src/File/WordCount.php +++ b/src/File/WordCount.php @@ -20,8 +20,6 @@ namespace Zend\Validator\File; -use Zend\Loader; - /** * Validator for counting all words in a file * @@ -63,7 +61,7 @@ public function isValid($value, $file = null) } // Is file readable ? - if (!Loader::isReadable($value)) { + if (false === stream_resolve_include_path($value)) { return $this->_throw($file, self::NOT_FOUND); } diff --git a/src/Float.php b/src/Float.php index 9490bb983..3d8a5551b 100644 --- a/src/Float.php +++ b/src/Float.php @@ -68,7 +68,7 @@ public function __construct($options = null) /** * Returns the set locale * - * @return \Zend\Locale + * @return \Zend\Locale\Locale */ public function getLocale() { @@ -109,7 +109,7 @@ public function isValid($value) $this->error(self::NOT_FLOAT); return false; } - } catch (Locale\Exception $e) { + } catch (Locale\Exception\ExceptionInterface $e) { $this->error(self::NOT_FLOAT); return false; } diff --git a/src/Int.php b/src/Int.php index dd0415e2a..f88f8acdb 100644 --- a/src/Int.php +++ b/src/Int.php @@ -129,7 +129,7 @@ public function isValid($value) $this->error(self::NOT_INT); return false; } - } catch (\Zend\Locale\Exception $e) { + } catch (\Zend\Locale\Exception\ExceptionInterface $e) { $this->error(self::NOT_INT); return false; } diff --git a/src/PostCode.php b/src/PostCode.php index b9b345d43..96905a5ea 100644 --- a/src/PostCode.php +++ b/src/PostCode.php @@ -65,7 +65,7 @@ class PostCode extends AbstractValidator * Accepts either a string locale, a Zend_Locale object, or an array or * Zend_Config object containing the keys "locale" and/or "format". * - * @param string|Zend_Locale|array|\Traversable $options + * @param string|\Zend\Locale\Locale|array|\Traversable $options * @throws \Zend\Validator\Exception On empty format */ public function __construct($options = null) @@ -161,7 +161,7 @@ public function setFormat($format) $this->options['format'] = $format; return $this; } - + /** * Returns the actual set service * @@ -218,7 +218,7 @@ public function isValid($value) return false; } } - + $format = $this->getFormat(); if (!preg_match($format, $value)) { $this->error(self::NO_MATCH); diff --git a/src/ValidatorChain.php b/src/ValidatorChain.php index 42ba60ac1..bb313e6e4 100644 --- a/src/ValidatorChain.php +++ b/src/ValidatorChain.php @@ -20,27 +20,39 @@ namespace Zend\Validator; +use Countable; +use Zend\Loader\Broker; +use Zend\Loader\Pluggable; + /** * @category Zend * @package Zend_Validate * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com) * @license http://framework.zend.com/license/new-bsd New BSD License */ -class ValidatorChain implements ValidatorInterface +class ValidatorChain implements + Countable, + Pluggable, + ValidatorInterface { + /** + * @var Broker + */ + protected $broker; + /** * Validator chain * * @var array */ - protected $_validators = array(); + protected $validators = array(); /** * Array of validation failure messages * * @var array */ - protected $_messages = array(); + protected $messages = array(); /** * Array of validation failure message codes @@ -48,7 +60,62 @@ class ValidatorChain implements ValidatorInterface * @var array * @deprecated Since 1.5.0 */ - protected $_errors = array(); + protected $errors = array(); + + /** + * Return the count of attached valicators + * + * @return int + */ + public function count() + { + return count($this->validators); + } + + /** + * Get plugin broker instance + * + * @return Zend\Loader\Broker + */ + public function getBroker() + { + if (!$this->broker) { + $this->setBroker(new ValidatorBroker()); + } + return $this->broker; + } + + /** + * Set plugin broker instance + * + * @param string|Broker $broker Plugin broker to load plugins + * @return ValidatorChain + */ + public function setBroker($broker) + { + if (!$broker instanceof Broker) { + throw new Exception\RuntimeException(sprintf( + '%s expects an argument of type Zend\Loader\Broker; received "%s"', + __METHOD__, + (is_object($broker) ? get_class($broker) : gettype($broker)) + )); + } + $this->broker = $broker; + return $this; + } + + /** + * Retrieve a validator by name + * + * @param string $plugin Name of validator to return + * @param null|array $options Options to pass to validator constructor (if not already instantiated) + * @return ValidatorInterface + */ + public function plugin($name, array $options = null) + { + $broker = $this->getBroker(); + return $broker->load($name, $options); + } /** * Adds a validator to the end of the chain @@ -56,16 +123,65 @@ class ValidatorChain implements ValidatorInterface * If $breakChainOnFailure is true, then if the validator fails, the next validator in the chain, * if one exists, will not be executed. * - * @param \Zend\Validator\ValidatorInterface $validator + * @param ValidatorInterface $validator * @param boolean $breakChainOnFailure - * @return \Zend\Validator\ValidatorChain Provides a fluent interface + * @return ValidatorChain Provides a fluent interface */ public function addValidator(ValidatorInterface $validator, $breakChainOnFailure = false) { - $this->_validators[] = array( - 'instance' => $validator, + $this->validators[] = array( + 'instance' => $validator, + 'breakChainOnFailure' => (boolean) $breakChainOnFailure + ); + return $this; + } + + /** + * Adds a validator to the beginning of the chain + * + * If $breakChainOnFailure is true, then if the validator fails, the next validator in the chain, + * if one exists, will not be executed. + * + * @param ValidatorInterface $validator + * @param boolean $breakChainOnFailure + * @return ValidatorChain Provides a fluent interface + */ + public function prependValidator(ValidatorInterface $validator, $breakChainOnFailure = false) + { + array_unshift($this->validators, array( + 'instance' => $validator, 'breakChainOnFailure' => (boolean) $breakChainOnFailure - ); + )); + return $this; + } + + /** + * Use the plugin broker to add a validator by name + * + * @param string $name + * @param array $options + * @param bool $breakChainOnFailure + * @return ValidatorChain + */ + public function addByName($name, $options = array(), $breakChainOnFailure = false) + { + $validator = $this->plugin($name, $options); + $this->addValidator($validator, $breakChainOnFailure); + return $this; + } + + /** + * Use the plugin broker to prepend a validator by name + * + * @param string $name + * @param array $options + * @param bool $breakChainOnFailure + * @return ValidatorChain + */ + public function prependByName($name, $options = array(), $breakChainOnFailure = false) + { + $validator = $this->plugin($name, $options); + $this->prependValidator($validator, $breakChainOnFailure); return $this; } @@ -75,22 +191,23 @@ public function addValidator(ValidatorInterface $validator, $breakChainOnFailure * Validators are run in the order in which they were added to the chain (FIFO). * * @param mixed $value + * @param mixed $context Extra "context" to provide the validator * @return boolean */ - public function isValid($value) + public function isValid($value, $context = null) { - $this->_messages = array(); - $this->_errors = array(); + $this->messages = array(); + $this->errors = array(); $result = true; - foreach ($this->_validators as $element) { + foreach ($this->validators as $element) { $validator = $element['instance']; - if ($validator->isValid($value)) { + if ($validator->isValid($value, $context)) { continue; } $result = false; $messages = $validator->getMessages(); - $this->_messages = array_merge($this->_messages, $messages); - $this->_errors = array_merge($this->_errors, array_keys($messages)); + $this->messages = array_merge($this->messages, $messages); + $this->errors = array_merge($this->errors, array_keys($messages)); if ($element['breakChainOnFailure']) { break; } @@ -105,7 +222,7 @@ public function isValid($value) */ public function getMessages() { - return $this->_messages; + return $this->messages; } /** @@ -116,7 +233,7 @@ public function getMessages() */ public function getErrors() { - return $this->_errors; + return $this->errors; } /** diff --git a/src/ValidatorLoader.php b/src/ValidatorLoader.php index 88d89c9b4..f9e4adced 100644 --- a/src/ValidatorLoader.php +++ b/src/ValidatorLoader.php @@ -141,6 +141,7 @@ class ValidatorLoader extends PluginClassLoader 'callback' => 'Zend\Validator\Callback', 'credit_card' => 'Zend\Validator\CreditCard', 'creditcard' => 'Zend\Validator\CreditCard', + 'csrf' => 'Zend\Validator\Csrf', 'date' => 'Zend\Validator\Date', 'db_\\no_record_exists' => 'Zend\Validator\Db\NoRecordExists', 'db_\\norecordexists' => 'Zend\Validator\Db\NoRecordExists', diff --git a/test/CallbackTest.php b/test/CallbackTest.php index 97d84c659..b991466a6 100644 --- a/test/CallbackTest.php +++ b/test/CallbackTest.php @@ -124,6 +124,28 @@ public function testEqualsMessageVariables() ); } + public function testCanAcceptContextWithoutOptions() + { + $value = 'bar'; + $context = array('foo' => 'bar', 'bar' => 'baz'); + $validator = new Validator\Callback(function($v, $c) use ($value, $context) { + return (($value == $v) && ($context == $c)); + }); + $this->assertTrue($validator->isValid($value, $context)); + } + + public function testCanAcceptContextWithOptions() + { + $value = 'bar'; + $context = array('foo' => 'bar', 'bar' => 'baz'); + $options = array('baz' => 'bat'); + $validator = new Validator\Callback(function($v, $c, $baz) use ($value, $context, $options) { + return (($value == $v) && ($context == $c) && ($options['baz'] == $baz)); + }); + $validator->setCallbackOptions($options); + $this->assertTrue($validator->isValid($value, $context)); + } + public function objectCallback($value) { return true; diff --git a/test/CsrfTest.php b/test/CsrfTest.php new file mode 100644 index 000000000..a3fb3af39 --- /dev/null +++ b/test/CsrfTest.php @@ -0,0 +1,177 @@ + 'Zend\Session\Storage\ArrayStorage', + )); + $sessionManager = new TestAsset\SessionManager($sessionConfig); + $this->sessionManager = $sessionManager; + Container::setDefaultManager($sessionManager); + + $this->validator = new Validator\Csrf; + } + + public function tearDown() + { + $_SESSION = array(); + Container::setDefaultManager(null); + } + + public function testSaltHasDefaultValueIfNotSet() + { + $this->assertEquals('salt', $this->validator->getSalt()); + } + + public function testSaltIsMutable() + { + $this->validator->setSalt('pepper'); + $this->assertEquals('pepper', $this->validator->getSalt()); + } + + public function testSessionContainerIsLazyLoadedIfNotSet() + { + $container = $this->validator->getSession(); + $this->assertInstanceOf('Zend\Session\Container', $container); + } + + public function testSessionContainerIsMutable() + { + $container = new Container('foo', $this->sessionManager); + $this->validator->setSession($container); + $this->assertSame($container, $this->validator->getSession()); + } + + public function testNameHasDefaultValue() + { + $this->assertEquals('csrf', $this->validator->getName()); + } + + public function testNameIsMutable() + { + $this->validator->setName('foo'); + $this->assertEquals('foo', $this->validator->getName()); + } + + public function testTimeoutHasDefaultValue() + { + $this->assertEquals(300, $this->validator->getTimeout()); + } + + public function testTimeoutIsMutable() + { + $this->validator->setTimeout(600); + $this->assertEquals(600, $this->validator->getTimeout()); + } + + public function testAllOptionsMayBeSetViaConstructor() + { + $container = new Container('foo', $this->sessionManager); + $options = array( + 'name' => 'hash', + 'salt' => 'hashful', + 'session' => $container, + 'timeout' => 600, + ); + $validator = new Validator\Csrf($options); + foreach ($options as $key => $value) { + if ($key == 'session') { + $this->assertSame($container, $value); + continue; + } + $method = 'get' . $key; + $this->assertEquals($value, $validator->$method()); + } + } + + public function testHashIsGeneratedOnFirstRetrieval() + { + $hash = $this->validator->getHash(); + $this->assertFalse(empty($hash)); + $test = $this->validator->getHash(); + $this->assertEquals($hash, $test); + } + + public function testSessionNameIsDerivedFromClassSaltAndName() + { + $class = get_class($this->validator); + $class = str_replace('\\', '_', $class); + $expected = sprintf('%s_%s_%s', $class, $this->validator->getSalt(), $this->validator->getName()); + $this->assertEquals($expected, $this->validator->getSessionName()); + } + + public function testIsValidReturnsFalseWhenValueDoesNotMatchHash() + { + $this->assertFalse($this->validator->isValid('foo')); + } + + public function testValidationErrorMatchesNotSameConstantAndRelatedMessage() + { + $this->validator->isValid('foo'); + $messages = $this->validator->getMessages(); + $this->assertArrayHasKey(Validator\Csrf::NOT_SAME, $messages); + $this->assertEquals("The form submitted did not originate from the expected site", $messages[Validator\Csrf::NOT_SAME]); + } + + public function testIsValidReturnsTrueeWhenValueMatchesHash() + { + $hash = $this->validator->getHash(); + $this->assertTrue($this->validator->isValid($hash)); + } + + public function testSessionContainerContainsHashAfterHashHasBeenGenerated() + { + $hash = $this->validator->getHash(); + $container = $this->validator->getSession(); + $test = $container->hash; // Doing this, as expiration hops are 1; have to grab on first access + $this->assertEquals($hash, $test); + } + + public function testSettingNewSessionContainerSetsHashInNewContainer() + { + $hash = $this->validator->getHash(); + $container = new Container('foo', $this->sessionManager); + $this->validator->setSession($container); + $test = $container->hash; // Doing this, as expiration hops are 1; have to grab on first access + $this->assertEquals($hash, $test); + } +} diff --git a/test/TestAsset/SessionManager.php b/test/TestAsset/SessionManager.php new file mode 100644 index 000000000..d0a1e6001 --- /dev/null +++ b/test/TestAsset/SessionManager.php @@ -0,0 +1,92 @@ +started = true; + } + + public function destroy() + { + $this->started = false; + } + + public function stop() + {} + + public function writeClose() + { + $this->started = false; + } + + public function getName() + {} + + public function setName($name) + {} + + public function getId() + {} + + public function setId($id) + {} + + public function regenerateId() + {} + + public function rememberMe($ttl = null) + {} + + public function forgetMe() + {} + + + public function setValidatorChain(EventManagerInterface $chain) + {} + + public function getValidatorChain() + {} + + public function isValid() + {} + + + public function sessionExists() + {} + + public function expireSessionCookie() + {} +} diff --git a/test/ValidateTest.php b/test/ValidateTest.php index 4c6970a6a..1a3da866f 100644 --- a/test/ValidateTest.php +++ b/test/ValidateTest.php @@ -142,6 +142,24 @@ public function testSetGetDefaultTranslator() $this->assertSame($translator->getAdapter(), Validator\AbstractValidator::getDefaultTranslator()); } + public function testAllowsPrependingValidators() + { + $this->_validator->addValidator(new ValidatorTrue()) + ->prependValidator(new ValidatorFalse(), true); + $this->assertFalse($this->_validator->isValid(true)); + $messages = $this->_validator->getMessages(); + $this->assertArrayHasKey('error', $messages); + } + + public function testAllowsPrependingValidatorsByName() + { + $this->_validator->addValidator(new ValidatorTrue()) + ->prependByName('NotEmpty', array(), true); + $this->assertFalse($this->_validator->isValid('')); + $messages = $this->_validator->getMessages(); + $this->assertArrayHasKey('isEmpty', $messages); + } + /** * Handle file not found errors *