From eb7fb6d7e363d69f3e58ec21b81bc8a3e79a6701 Mon Sep 17 00:00:00 2001 From: nate Date: Wed, 24 Sep 2008 23:02:14 +0000 Subject: [PATCH] Re-implementing form hashing security to using string-based keying. Fixes #5262, fixes #5300 git-svn-id: https://svn.cakephp.org/repo/branches/1.2.x.x@7658 3807eeeb-6ff5-0310-8944-8be069107fe0 --- cake/libs/controller/components/security.php | 256 ++++------- .../model/datasources/dbo/dbo_postgres.php | 5 +- cake/libs/view/helpers/form.php | 309 +++++++------ .../controller/components/security.test.php | 427 +++++++----------- .../cases/libs/view/helpers/form.test.php | 282 +++++++----- 5 files changed, 589 insertions(+), 690 deletions(-) diff --git a/cake/libs/controller/components/security.php b/cake/libs/controller/components/security.php index f53e2dc51..d34a7a481 100644 --- a/cake/libs/controller/components/security.php +++ b/cake/libs/controller/components/security.php @@ -181,7 +181,11 @@ class SecurityComponent extends Object { ); if ($isPost && $isRequestAction && $this->validatePost) { - $this->_validatePost($controller); + if ($this->_validatePost($controller) === false) { + if (!$this->blackHole($controller, 'auth')) { + return null; + } + } } $this->_generateToken($controller); } @@ -451,7 +455,7 @@ class SecurityComponent extends Object { $requireAuth = array_map('strtolower', $this->requireAuth); if (in_array($this->_action, $requireAuth) || $this->requireAuth == array('*')) { - if (!isset($controller->data['__Token'] )) { + if (!isset($controller->data['_Token'] )) { if (!$this->blackHole($controller, 'auth')) { return null; } @@ -510,7 +514,10 @@ class SecurityComponent extends Object { } $this->blackHole($controller, 'login'); } else { - if (!(in_array($login['username'], array_keys($this->loginUsers)) && $this->loginUsers[$login['username']] == $login['password'])) { + if ( + !(in_array($login['username'], array_keys($this->loginUsers)) && + $this->loginUsers[$login['username']] == $login['password']) + ) { $this->blackHole($controller, 'login'); } } @@ -528,164 +535,66 @@ class SecurityComponent extends Object { * @access protected */ function _validatePost(&$controller) { - if (!empty($controller->data)) { - if (!isset($controller->data['__Token'])) { - if (!$this->blackHole($controller, 'auth')) { - return null; - } - } - $token = $controller->data['__Token']['key']; + if (empty($controller->data)) { + return true; + } + $data = $controller->data; - if ($this->Session->check('_Token')) { - $tData = unserialize($this->Session->read('_Token')); + if (!isset($data['_Token']) || !isset($data['_Token']['fields'])) { + return false; + } + $token = $data['_Token']['key']; - if ($tData['expires'] < time() || $tData['key'] !== $token) { - if (!$this->blackHole($controller, 'auth')) { - return null; - } - } - } + if ($this->Session->check('_Token')) { + $tokenData = unserialize($this->Session->read('_Token')); - if (!isset($controller->data['__Token']['fields'])) { - if (!$this->blackHole($controller, 'auth')) { - return null; - } + if ($tokenData['expires'] < time() || $tokenData['key'] !== $token) { + return false; } - $form = $controller->data['__Token']['fields']; - $check = $controller->data; - unset($check['__Token']['fields']); + } + + $locked = null; + $check = $controller->data; + $token = urldecode($check['_Token']['fields']); + + if (strpos($token, ':')) { + list($token, $locked) = explode(':', $token, 2); + } + unset($check['_Token']); + + $lockedFields = array(); + $fields = Set::flatten($check); + $fieldList = array_keys($fields); + $locked = unserialize(str_rot13($locked)); + + foreach ($fieldList as $i => $key) { + $isDisabled = false; + $isLocked = (is_array($locked) && in_array($key, $locked)); if (!empty($this->disabledFields)) { - foreach ($check as $model => $fields) { - foreach ($fields as $field => $value) { - $key[] = $model . '.' . $field; - } - unset($field); - } - - foreach ($this->disabledFields as $value) { - $parts = explode('.', $value); - - if (count($parts) == 1) { - $key1[] = $controller->modelClass . '.' . $parts['0']; - } elseif (count($parts) == 2) { - $key1[] = $parts['0'] . '.' . $parts['1']; - } - } - - foreach ($key1 as $value) { - if (in_array($value, $key)) { - $remove = explode('.', $value); - unset($check[$remove['0']][$remove['1']]); - if (empty($check[$remove['0']])) { - unset($check[$remove['0']]); - } - } elseif (in_array('_' . $value, $key)) { - $remove = explode('.', $value); - $controller->data[$remove['0']][$remove['1']] = $controller->data['_' . $remove['0']][$remove['1']]; - unset($check['_' . $remove['0']][$remove['1']]); - if (empty($check['_' . $remove['0']])) { - unset($check['_' . $remove['0']]); - } + foreach ((array)$this->disabledFields as $disabled) { + $disabled = explode('.', $disabled); + $field = array_values(array_intersect(explode('.', $key), $disabled)); + $isDisabled = ($field === $disabled); + if ($isDisabled) { + break; } } } - ksort($check); - foreach ($check as $key => $value) { - $merge = array(); - if ($key === '__Token') { - $field[$key] = $value; - continue; - } - $string = substr($key, 0, 1); - - if ($string === '_') { - $newKey = substr($key, 1); - - if (!isset($controller->data[$newKey])) { - $controller->data[$newKey] = array(); - - if (array_keys($controller->data[$key]) === array($newKey)) { - $field[$newKey] = array($newKey); - } - } - - if (is_array($value)) { - $values = array_values($value); - $k = array_keys($value); - $count = count($k); - - if ($count > 0 && is_numeric($k[0])) { - for ($i = 0; $count > $i; $i++) { - foreach ($values[$i] as $key2 => $value1) { - if ($value1 === '0' && !in_array($key2, $field[$newKey][$i])) { - $field[$newKey][$i] = array_merge($field[$newKey][$i], array($key2)); - } - } - } - $controller->data[$newKey] = Set::pushDiff($controller->data[$newKey], $controller->data[$key]); - } - - for ($i = 0; $count > $i; $i++) { - $field[$key][$k[$i]] = $values[$i]; - } - - foreach ($k as $lookup) { - if (isset($controller->data[$newKey][$lookup])) { - unset($controller->data[$key][$lookup]); - } elseif ($controller->data[$key][$lookup] === '0') { - $merge[] = $lookup; - } - } - - if ($count == 0 || !is_numeric($k[0])) { - if (isset($field[$newKey])) { - $field[$newKey] = array_merge($merge, $field[$newKey]); - } else { - $field[$newKey] = $merge; - } - $controller->data[$newKey] = Set::pushDiff($controller->data[$key], $controller->data[$newKey]); - } - unset($controller->data[$key]); - } - continue; - } - if (is_array($value)) { - $keys = array_keys($value); - } else { - $keys = $value; - } - - if (isset($field[$key])) { - $field[$key] = array_merge($field[$key], $keys); - } elseif (is_array($keys) && !empty($keys) && is_numeric($keys[0])) { - foreach ($value as $fields) { - $merge[] = array_keys($fields); - } - $field[$key] = $merge; - } else if (is_array($keys)) { - $field[$key] = $keys; - } else { - $field[] = $key; - } - } - - foreach ($field as $key => $value) { - if ($key[0] != '_' && is_array($field[$key])) { - sort($field[$key]); - } - } - ksort($field, SORT_STRING); - - $check = urlencode(Security::hash(serialize($field) . Configure::read('Security.salt'))); - if ($form !== $check) { - if (!$this->blackHole($controller, 'auth')) { - return null; + if ($isDisabled || $isLocked) { + unset($fieldList[$i]); + if ($isLocked) { + $lockedFields[$key] = $fields[$key]; } } } - return true; + sort($fieldList, SORT_STRING); + ksort($lockedFields, SORT_STRING); + + $fieldList += $lockedFields; + $check = Security::hash(serialize($fieldList) . Configure::read('Security.salt')); + return ($token === $check); } /** * Add authentication key for new form posts @@ -695,30 +604,38 @@ class SecurityComponent extends Object { * @access protected */ function _generateToken(&$controller) { - if (!isset($controller->params['requested']) || $controller->params['requested'] != 1) { - $authKey = Security::generateAuthKey(); - $expires = strtotime('+' . Security::inactiveMins() . ' minutes'); - $token = array( - 'key' => $authKey, - 'expires' => $expires, - 'allowedControllers' => $this->allowedControllers, - 'allowedActions' => $this->allowedActions, - 'disabledFields' => $this->disabledFields + if (isset($controller->params['requested']) && $controller->params['requested'] === 1) { + return false; + } + $authKey = Security::generateAuthKey(); + $expires = strtotime('+' . Security::inactiveMins() . ' minutes'); + $token = array( + 'key' => $authKey, + 'expires' => $expires, + 'allowedControllers' => $this->allowedControllers, + 'allowedActions' => $this->allowedActions, + 'disabledFields' => $this->disabledFields + ); + + if (!isset($controller->data)) { + $controller->data = array(); + } + + if ($this->Session->check('_Token')) { + $tokenData = unserialize($this->Session->read('_Token')); + $valid = ( + isset($tokenData['expires']) && + $tokenData['expires'] > time() && + isset($tokenData['key']) ); - if (!isset($controller->data)) { - $controller->data = array(); + if ($valid) { + $token['key'] = $tokenData['key']; } - - if ($this->Session->check('_Token')) { - $tData = unserialize($this->Session->read('_Token')); - if (isset($tData['expires']) && $tData['expires'] > time() && isset($tData['key'])) { - $token['key'] = $tData['key']; - } - } - $controller->params['_Token'] = $token; - $this->Session->write('_Token', serialize($token)); } + $controller->params['_Token'] = $token; + $this->Session->write('_Token', serialize($token)); + return true; } /** @@ -754,4 +671,5 @@ class SecurityComponent extends Object { } } } -?> + +?> \ No newline at end of file diff --git a/cake/libs/model/datasources/dbo/dbo_postgres.php b/cake/libs/model/datasources/dbo/dbo_postgres.php index f8874adaa..ff2769244 100644 --- a/cake/libs/model/datasources/dbo/dbo_postgres.php +++ b/cake/libs/model/datasources/dbo/dbo_postgres.php @@ -560,7 +560,7 @@ class DboPostgres extends DboSource { switch ($type) { case 'bool': - $resultRow[$table][$column] = $this->boolean($row[$index], false); + $resultRow[$table][$column] = $this->boolean($row[$index], false); break; case 'binary': case 'bytea': @@ -618,7 +618,8 @@ class DboPostgres extends DboSource { /** * Generate a Postgres-native column schema string * - * @param array $column An array structured like the following: array('name'=>'value', 'type'=>'value'[, options]), + * @param array $column An array structured like the following: + * array('name'=>'value', 'type'=>'value'[, options]), * where options can be 'default', 'length', or 'key'. * @return string */ diff --git a/cake/libs/view/helpers/form.php b/cake/libs/view/helpers/form.php index 83fa4dff9..3f7157ab8 100644 --- a/cake/libs/view/helpers/form.php +++ b/cake/libs/view/helpers/form.php @@ -297,118 +297,63 @@ class FormHelper extends AppHelper { * @access public */ function secure($fields) { - if (!empty($fields)) { - $append = '
'; - - foreach ($fields as $key => $value) { - if ($key[0] != '_' && is_array($fields[$key])) { - sort($fields[$key]); - } else { - $model = substr($key, 1); - if ($key !== '__Token' && !isset($fields[$model])) { - $fields[$model] = array(); - } - } - } - ksort($fields, SORT_STRING); - $fields = Security::hash(serialize($fields) . Configure::read('Security.salt')); - - $append .= $this->hidden('_Token.fields', array( - 'value' => urlencode($fields), - 'id' => 'TokenFields' . mt_rand() - )); - $append .= '
'; - return $append; + if (!isset($this->params['_Token']) || empty($this->params['_Token'])) { + return; } - return null; + $out = '
'; + $locked = array(); + + foreach ($fields as $key => $value) { + if (!is_int($key)) { + $locked[$key] = $value; + unset($fields[$key]); + } + } + sort($fields, SORT_STRING); + ksort($locked, SORT_STRING); + $fields += $locked; + + $fields = Security::hash(serialize($fields) . Configure::read('Security.salt')); + $locked = str_rot13(serialize(array_keys($locked))); + + $out .= $this->hidden('_Token.fields', array( + 'value' => urlencode($fields . ':' . $locked), + 'id' => 'TokenFields' . mt_rand() + )); + return $out .= '
'; } /** * Determine which fields of a form should be used for hash * - * @param string $model Model name - * @param mixed $options Options + * @param mixed $field Reference to field to be secured + * @param mixed $value Field value, if value should not be tampered with * @access private */ - function __secure($model = null, $value = null) { - if (!isset($this->params['_Token']) || empty($this->params['_Token'])) { - return; + function __secure($field = null, $value = null) { + if (!$field) { + $view =& ClassRegistry::getObject('view'); + $field = $view->entity(); + } elseif (is_string($field)) { + $field = Set::filter(explode('.', $field), true); } - if (!$model) { - $model = $this->model(); - } - $view =& ClassRegistry::getObject('view'); - $field = $view->field; - $fieldSuffix = $view->fieldSuffix; if (!empty($this->params['_Token']['disabledFields'])) { - foreach ($this->params['_Token']['disabledFields'] as $f) { - if (!strpos($f, '.') && ($f === $field || $f === $fieldSuffix)) { - return; - } elseif ($value === join('.', $view->entity())) { + foreach ((array)$this->params['_Token']['disabledFields'] as $disabled) { + $disabled = explode('.', $disabled); + if (array_values(array_intersect($field, $disabled)) === $disabled) { return; } - $parts = explode('.', $f); - - if (count($parts) == 2) { - $fieldMatch = ( - $parts[0] === $model && - ($parts[1] === $field || $parts[1] === $fieldSuffix) - ); - if ($fieldMatch) { - return; - } - } } } - $this->__fields($model, $field, $fieldSuffix, $value); - } -/** - * Generates a field list used to secure forms - * - * @param string $model - * @param string $field - * @param string $fieldSuffix - * @param mixed $options - * @access private - */ - function __fields($model, $field, $fieldSuffix, $value) { - if (!is_null($value)) { - if (is_numeric($field)) { - $this->fields[$model][$field][$fieldSuffix] = $value; - } else { - $this->fields[$model][$field] = $value; - } - return; - } - $invalid = ( - (!isset($this->fields[$model]) || in_array($field, $this->fields[$model], true)) && - isset($this->fields[$model]) - ); - - if ($invalid) { - return; - } - - if (is_numeric($field)) { - if (!isset($this->fields[$model][$field])) { - $this->fields[$model][$field][] = $fieldSuffix; - } else if (!in_array($fieldSuffix, $this->fields[$model][$field])) { - if (!isset($this->fields[$model][$field])) { - $this->fields[$model][$field][] = $fieldSuffix; - } else if (!in_array($fieldSuffix, $this->fields[$model][$field])) { - $this->fields[$model][$field][] = $fieldSuffix; - } - } - } else if (is_null($field)) { - $this->fields[] = $model; - } else { - $this->fields[$model][] = $field; + if ($value !== null) { + return $this->fields[join('.', $field)] = $value; } + $this->fields[] = join('.', $field); } /** * Returns true if there is an error for the given field, otherwise false * - * @param string $field This should be "Modelname.fieldname", "Modelname/fieldname" is deprecated + * @param string $field This should be "Modelname.fieldname" * @return boolean If there are errors this method returns true, else false. * @access public */ @@ -419,11 +364,12 @@ class FormHelper extends AppHelper { /** * Returns a formatted error message for given FORM field, NULL if no errors. * - * @param string $field A field name, like "Modelname.fieldname", "Modelname/fieldname" is deprecated + * @param string $field A field name, like "Modelname.fieldname" * @param mixed $text Error message or array of $options * @param array $options Rendering options for
wrapper tag * 'escape' bool Whether or not to html escape the contents of the error. - * 'wrap' mixed Whether or not the error message should be wrapped in a div. If a string, will be used as the HTML tag to use. + * 'wrap' mixed Whether or not the error message should be wrapped in a div. If a + * string, will be used as the HTML tag to use. * 'class' string The classname for the error message * @return string If there are errors this method returns an error message, otherwise null. * @access public @@ -475,7 +421,7 @@ class FormHelper extends AppHelper { /** * Returns a formatted LABEL element for HTML FORMs. * - * @param string $fieldName This should be "Modelname.fieldname", "Modelname/fieldname" is deprecated + * @param string $fieldName This should be "Modelname.fieldname" * @param string $text Text that will appear in the label field. * @param array $attributes Array of HTML attributes. * @return string The formatted LABEL element @@ -505,7 +451,11 @@ class FormHelper extends AppHelper { $labelFor = $this->domId($fieldName); } - return $this->output(sprintf($this->Html->tags['label'], $labelFor, $this->_parseAttributes($attributes), $text)); + return $this->output(sprintf( + $this->Html->tags['label'], + $labelFor, + $this->_parseAttributes($attributes), $text + )); } /** * Will display all the fields passed in an array expects fieldName as an array key @@ -569,7 +519,11 @@ class FormHelper extends AppHelper { } if ($fieldset && $legend) { - return sprintf($this->Html->tags['fieldset'], $fieldsetClass, sprintf($this->Html->tags['legend'], $legend) . $out); + return sprintf( + $this->Html->tags['fieldset'], + $fieldsetClass, + sprintf($this->Html->tags['legend'], $legend) . $out + ); } elseif ($fieldset) { return sprintf($this->Html->tags['fieldset'], $fieldsetClass, $out); } else { @@ -579,8 +533,9 @@ class FormHelper extends AppHelper { /** * Generates a form input element complete with label and wrapper div * - * @param string $fieldName This should be "Modelname.fieldname", "Modelname/fieldname" is deprecated - * @param array $options - Each type of input takes different options. See each field type method for more information. + * @param string $fieldName This should be "Modelname.fieldname" + * @param array $options - Each type of input takes different options. + * See each field type method for more information. * 'type' - Force the type of widget you want. e.g. type => 'select' * 'label' - control the label * 'div' - control the wrapping div element @@ -646,7 +601,9 @@ class FormHelper extends AppHelper { if (!isset($options['options']) && in_array($options['type'], $types)) { $view =& ClassRegistry::getObject('view'); - $varName = Inflector::variable(Inflector::pluralize(preg_replace('/_id$/', '', $this->field()))); + $varName = Inflector::variable( + Inflector::pluralize(preg_replace('/_id$/', '', $this->field())) + ); $varOptions = $view->getVar($varName); if (is_array($varOptions)) { if ($options['type'] !== 'radio') { @@ -840,7 +797,7 @@ class FormHelper extends AppHelper { /** * Creates a checkbox input widget. * - * @param string $fieldNamem Name of a field, like this "Modelname.fieldname", "Modelname/fieldname" is deprecated + * @param string $fieldNamem Name of a field, like this "Modelname.fieldname" * @param array $options Array of HTML attributes. * 'value' - the value of the checkbox * 'checked' - boolean indicate that this checkbox is checked. @@ -859,15 +816,15 @@ class FormHelper extends AppHelper { } $output = $this->hidden($fieldName, array( - 'name' => $options['name'], 'value' => '0', 'id' => $options['id'] . '_' + 'id' => $options['id'] . '_', 'name' => $options['name'], + 'value' => '0', 'secure' => false )); - $output .= sprintf( + return $this->output($output . sprintf( $this->Html->tags['checkbox'], $options['name'], $this->_parseAttributes($options, array('name'), null, ' ') - ); - return $this->output($output); + )); } /** * Creates a set of radio widgets. @@ -954,7 +911,7 @@ class FormHelper extends AppHelper { /** * Creates a text input widget. * - * @param string $fieldNamem Name of a field, like this "Modelname.fieldname", "Modelname/fieldname" is deprecated + * @param string $fieldNamem Name of a field, in the form "Modelname.fieldname" * @param array $options Array of HTML attributes. * @return string An HTML text input element */ @@ -971,7 +928,7 @@ class FormHelper extends AppHelper { /** * Creates a password input widget. * - * @param string $fieldName Name of a field, like this "Modelname.fieldname", "Modelname/fieldname" is deprecated + * @param string $fieldName Name of a field, like in the form "Modelname.fieldname" * @param array $options Array of HTML attributes. * @return string */ @@ -986,7 +943,7 @@ class FormHelper extends AppHelper { /** * Creates a textarea widget. * - * @param string $fieldNamem Name of a field, like this "Modelname.fieldname", "Modelname/fieldname" is deprecated + * @param string $fieldNamem Name of a field, in the form "Modelname.fieldname" * @param array $options Array of HTML attributes. * @return string An HTML text input element */ @@ -1011,34 +968,27 @@ class FormHelper extends AppHelper { /** * Creates a hidden input field. * - * @param string $fieldName Name of a field, like this "Modelname.fieldname", "Modelname/fieldname" is deprecated + * @param string $fieldName Name of a field, in the form"Modelname.fieldname" * @param array $options Array of HTML attributes. * @return string * @access public */ function hidden($fieldName, $options = array()) { + $secure = true; + + if (isset($options['secure'])) { + $secure = $options['secure']; + unset($options['secure']); + } $options = $this->_initInputField($fieldName, array_merge( $options, array('secure' => false) )); $model = $this->model(); - $value = ''; - $key = '_' . $model; - if (isset($this->params['_Token']) && !empty($this->params['_Token'])) { - $options['name'] = preg_replace("/$model/", $key, $options['name'], 1); + if ($fieldName !== '_method' && $model !== '_Token' && $secure) { + $this->__secure(null, '' . $options['value']); } - if (!empty($options['value']) || $options['value'] === '0') { - $value = $options['value']; - } - - if (!in_array($fieldName, array('_method'))) { - $this->__secure($key, $value); - - if (!in_array($model, array('_Token', '__Token')) && $value === '0') { - $this->__secure($model); - } - } return $this->output(sprintf( $this->Html->tags['hidden'], $options['name'], @@ -1048,18 +998,15 @@ class FormHelper extends AppHelper { /** * Creates file input widget. * - * @param string $fieldName Name of a field, like this "Modelname.fieldname", "Modelname/fieldname" is deprecated + * @param string $fieldName Name of a field, in the form "Modelname.fieldname" * @param array $options Array of HTML attributes. * @return string * @access public */ function file($fieldName, $options = array()) { $options = $this->_initInputField($fieldName, $options); - return $this->output(sprintf( - $this->Html->tags['file'], - $options['name'], - $this->_parseAttributes($options, array('name'), '', ' ') - )); + $attributes = $this->_parseAttributes($options, array('name'), '', ' '); + return $this->output(sprintf($this->Html->tags['file'], $options['name'], $attributes)); } /** * Creates a button tag. @@ -1072,7 +1019,7 @@ class FormHelper extends AppHelper { function button($title, $options = array()) { $options = array_merge(array('type' => 'button', 'value' => $title), $options); - if (isset($options['name']) && (strpos($options['name'], "/") !== false || strpos($options['name'], ".") !== false)) { + if (isset($options['name']) && strpos($options['name'], '.') !== false) { if ($this->value($options['name'])) { $options['checked'] = 'checked'; } @@ -1089,10 +1036,10 @@ class FormHelper extends AppHelper { /** * Creates a submit button element. * - * @param string $caption The label appearing on the button OR - * if string contains :// or the extension .jpg, .jpe, .jpeg, .gif, .png use an image - * if the extension exists, AND the first character is /, image is relative to webroot, - * OR if the first character is not /, image is relative to webroot/img, + * @param string $caption The label appearing on the button OR if string contains :// or the + * extension .jpg, .jpe, .jpeg, .gif, .png use an image if the extension + * exists, AND the first character is /, image is relative to webroot, + * OR if the first character is not /, image is relative to webroot/img. * @param array $options * @return string A HTML submit button */ @@ -1162,14 +1109,15 @@ class FormHelper extends AppHelper { * Returns a formatted SELECT element. * * @param string $fieldName Name attribute of the SELECT - * @param array $options Array of the OPTION elements (as 'value'=>'Text' pairs) to be used in the SELECT element + * @param array $options Array of the OPTION elements (as 'value'=>'Text' pairs) to be used in the + * SELECT element * @param mixed $selected The option selected by default. If null, the default value * from POST data will be used when available. * @param array $attributes The HTML attributes of the select element. * 'showParents' - If included in the array and set to true, an additional option element * will be added for the parent of each option group. - * 'multiple' - show a multiple select box. If set to 'checkbox' multiple checkboxes will be created instead. - * + * 'multiple' - show a multiple select box. If set to 'checkbox' multiple checkboxes will be + * created instead. * @param mixed $showEmpty If true, the empty select option is shown. If a string, * that string is displayed as the empty element. * @return string Formatted SELECT element @@ -1220,10 +1168,16 @@ class FormHelper extends AppHelper { if (!empty($tag)) { $this->__secure(); - $select[] = sprintf($tag, $attributes['name'], $this->_parseAttributes($attributes, array('name', 'value'))); + $select[] = sprintf($tag, $attributes['name'], $this->_parseAttributes( + $attributes, array('name', 'value')) + ); } + $emptyMulti = ( + $showEmpty !== null && $showEmpty !== false && + !(empty($showEmpty) && (isset($attributes) && array_key_exists('multiple', $attributes))) + ); - if ($showEmpty !== null && $showEmpty !== false && !(empty($showEmpty) && (isset($attributes) && array_key_exists('multiple', $attributes)))) { + if ($emptyMulti) { if ($showEmpty === true) { $showEmpty = ''; } @@ -1231,7 +1185,13 @@ class FormHelper extends AppHelper { $options[''] = $showEmpty; $options = array_reverse($options, true); } - $select = array_merge($select, $this->__selectOptions(array_reverse($options, true), $selected, array(), $showParents, array('escape' => $escapeOptions, 'style' => $style))); + $select = array_merge($select, $this->__selectOptions( + array_reverse($options, true), + $selected, + array(), + $showParents, + array('escape' => $escapeOptions, 'style' => $style) + )); if ($style == 'checkbox') { $select[] = $this->Html->tags['checkboxmultipleend']; @@ -1270,7 +1230,9 @@ class FormHelper extends AppHelper { } elseif ($selected === false) { $selected = null; } - return $this->select($fieldName . ".day", $this->__generateOptions('day'), $selected, $attributes, $showEmpty); + return $this->select( + $fieldName . ".day", $this->__generateOptions('day'), $selected, $attributes, $showEmpty + ); } /** * Returns a SELECT element for years @@ -1308,7 +1270,10 @@ class FormHelper extends AppHelper { $selected = null; } $yearOptions = array('min' => $minYear, 'max' => $maxYear); - return $this->select($fieldName . ".year", $this->__generateOptions('year', $yearOptions), $selected, $attributes, $showEmpty); + return $this->select( + $fieldName . ".year", $this->__generateOptions('year', $yearOptions), + $selected, $attributes, $showEmpty + ); } /** * Returns a SELECT element for months. @@ -1347,7 +1312,11 @@ class FormHelper extends AppHelper { $monthNames = $attributes['monthNames']; unset($attributes['monthNames']); - return $this->select($fieldName . ".month", $this->__generateOptions('month', array('monthNames' => $monthNames)), $selected, $attributes, $showEmpty); + return $this->select( + $fieldName . ".month", + $this->__generateOptions('month', array('monthNames' => $monthNames)), + $selected, $attributes, $showEmpty + ); } /** * Returns a SELECT element for hours. @@ -1384,7 +1353,11 @@ class FormHelper extends AppHelper { } elseif ($selected === false) { $selected = null; } - return $this->select($fieldName . ".hour", $this->__generateOptions($format24Hours ? 'hour24' : 'hour'), $selected, $attributes, $showEmpty); + return $this->select( + $fieldName . ".hour", + $this->__generateOptions($format24Hours ? 'hour24' : 'hour'), + $selected, $attributes, $showEmpty + ); } /** * Returns a SELECT element for minutes. @@ -1422,7 +1395,10 @@ class FormHelper extends AppHelper { $minuteOptions['interval'] = $attributes['interval']; unset($attributes['interval']); } - return $this->select($fieldName . ".min", $this->__generateOptions('minute', $minuteOptions), $selected, $attributes, $showEmpty); + return $this->select( + $fieldName . ".min", $this->__generateOptions('minute', $minuteOptions), + $selected, $attributes, $showEmpty + ); } /** * Returns a SELECT element for AM or PM. @@ -1452,7 +1428,10 @@ class FormHelper extends AppHelper { if ($selected === false) { $selected = null; } - return $this->select($fieldName . ".meridian", $this->__generateOptions('meridian'), $selected, $attributes, $showEmpty); + return $this->select( + $fieldName . ".meridian", $this->__generateOptions('meridian'), + $selected, $attributes, $showEmpty + ); } /** * Returns a set of SELECT elements for a full datetime setup: day, month and year, and then time. @@ -1522,7 +1501,10 @@ class FormHelper extends AppHelper { } $elements = array('Day','Month','Year','Hour','Minute','Meridian'); - $defaults = array('minYear' => null, 'maxYear' => null, 'separator' => '-', 'interval' => 1, 'monthNames' => true); + $defaults = array( + 'minYear' => null, 'maxYear' => null, 'separator' => '-', + 'interval' => 1, 'monthNames' => true + ); $attributes = array_merge($defaults, (array) $attributes); if (isset($attributes['minuteInterval'])) { $attributes['interval'] = $attributes['minuteInterval']; @@ -1566,7 +1548,9 @@ class FormHelper extends AppHelper { foreach (preg_split('//', $dateFormat, -1, PREG_SPLIT_NO_EMPTY) as $char) { switch ($char) { case 'Y': - $selects[] = $this->year($fieldName, $minYear, $maxYear, $year, $selectYearAttr, $showEmpty); + $selects[] = $this->year( + $fieldName, $minYear, $maxYear, $year, $selectYearAttr, $showEmpty + ); break; case 'M': $selectMonthAttr['monthNames'] = $monthNames; @@ -1654,7 +1638,9 @@ class FormHelper extends AppHelper { } $parents[] = $name; } - $select = array_merge($select, $this->__selectOptions($title, $selected, $parents, $showParents, $attributes)); + $select = array_merge($select, $this->__selectOptions( + $title, $selected, $parents, $showParents, $attributes + )); if (!empty($name)) { if ($attributes['style'] === 'checkbox') { @@ -1686,7 +1672,9 @@ class FormHelper extends AppHelper { if ($attributes['style'] === 'checkbox') { $htmlOptions['value'] = $name; - $tagName = Inflector::camelize($this->model() . '_' . $this->field() . '_'.Inflector::underscore($name)); + $tagName = Inflector::camelize( + $this->model() . '_' . $this->field().'_'.Inflector::underscore($name) + ); $htmlOptions['id'] = $tagName; $label = array('for' => $tagName); @@ -1699,10 +1687,17 @@ class FormHelper extends AppHelper { if (empty($attributes['class'])) { $attributes['class'] = 'checkbox'; } - - $select[] = $this->Html->div($attributes['class'], sprintf($this->Html->tags['checkboxmultiple'], $name, $this->Html->_parseAttributes($htmlOptions)) . $this->label(null, $title, $label)); + $label = $this->label(null, $title, $label); + $item = sprintf( + $this->Html->tags['checkboxmultiple'], $name, + $this->Html->_parseAttributes($htmlOptions) + ); + $select[] = $this->Html->div($attributes['class'], $item . $label); } else { - $select[] = sprintf($this->Html->tags['selectoption'], $name, $this->Html->_parseAttributes($htmlOptions), $title); + $select[] = sprintf( + $this->Html->tags['selectoption'], + $name, $this->Html->_parseAttributes($htmlOptions), $title + ); } } } @@ -1816,11 +1811,11 @@ class FormHelper extends AppHelper { * @access protected */ function _initInputField($field, $options = array()) { - $secure = true; - if (isset($options['secure'])) { $secure = $options['secure']; unset($options['secure']); + } else { + $secure = (isset($this->params['_Token']) && !empty($this->params['_Token'])); } $result = parent::_initInputField($field, $options); diff --git a/cake/tests/cases/libs/controller/components/security.test.php b/cake/tests/cases/libs/controller/components/security.test.php index 9c5e48936..def68bd8b 100644 --- a/cake/tests/cases/libs/controller/components/security.test.php +++ b/cake/tests/cases/libs/controller/components/security.test.php @@ -39,7 +39,6 @@ class TestSecurityComponent extends SecurityComponent { function validatePost(&$controller) { return $this->_validatePost($controller); } - } /** * Short description for class. @@ -110,7 +109,23 @@ class SecurityComponentTest extends CakeTestCase { $this->Controller->Component->init($this->Controller); $this->Controller->Security =& $this->Controller->TestSecurity; $this->Controller->Security->blackHoleCallback = 'fail'; + + $this->oldSalt = Configure::read('Security.salt'); + Configure::write('Security.salt', 'foo!'); } +/** + * Tear-down method. Resets environment state. + * + * @access public + * @return void + */ + function tearDown() { + unset($this->Controller->Security); + unset($this->Controller->Component); + unset($this->Controller); + Configure::write('Security.salt', $this->oldSalt); + } + /** * testStartup method * @@ -197,7 +212,9 @@ class SecurityComponentTest extends CakeTestCase { $this->Controller->Security->startup($this->Controller); $this->assertTrue($this->Controller->failed); - $this->Controller->Session->write('_Token', array('allowedControllers' => array('SecurityTest'), 'allowedActions' => array('posted2'))); + $this->Controller->Session->write('_Token', array( + 'allowedControllers' => array('SecurityTest'), 'allowedActions' => array('posted2') + )); $this->Controller->data = array('username' => 'willy', 'password' => 'somePass'); $this->Controller->action = 'posted'; $this->Controller->Security->requireAuth('posted'); @@ -217,11 +234,15 @@ class SecurityComponentTest extends CakeTestCase { $this->Controller->Security->startup($this->Controller); $this->assertFalse($this->Controller->failed); - $this->Controller->Security->Session->write('_Token', serialize(array('allowedControllers' => array('SecurityTest'), 'allowedActions' => array('posted')))); + $this->Controller->Security->Session->write('_Token', serialize(array( + 'allowedControllers' => array('SecurityTest'), 'allowedActions' => array('posted') + ))); $this->Controller->params['controller'] = 'SecurityTest'; $this->Controller->params['action'] = 'posted'; - $this->Controller->data = array('username' => 'willy', 'password' => 'somePass', '__Token' => ''); + $this->Controller->data = array( + 'username' => 'willy', 'password' => 'somePass', '_Token' => '' + ); $this->Controller->action = 'posted'; $this->Controller->Security->requireAuth('posted'); $this->Controller->Security->startup($this->Controller); @@ -323,10 +344,10 @@ class SecurityComponentTest extends CakeTestCase { response="460d0d3c6867c2f1ab85b1ada1aece48", opaque="5ccc069c403ebaf9f0171e9517f40e41" DIGEST; - $this->Controller->Security->requireLogin( - 'posted', - array('type' => 'digest', 'users' => array('Mufasa' => 'password'), 'realm' => 'testrealm@host.com') - ); + $this->Controller->Security->requireLogin('posted', array( + 'type' => 'digest', 'users' => array('Mufasa' => 'password'), + 'realm' => 'testrealm@host.com' + )); $this->Controller->Security->startup($this->Controller); $this->assertFalse($this->Controller->failed); } @@ -448,6 +469,24 @@ DIGEST; $this->assertEqual($this->Controller->Security->requireLogin, array('*')); $this->assertEqual($this->Controller->Security->loginUsers, array('admin' => 'password')); } +/** + * Simple hash validation test + * + * @access public + * @return void + */ + function testValidatePost() { + $this->Controller->Security->startup($this->Controller); + $key = $this->Controller->params['_Token']['key']; + $fields = 'a5475372b40f6e3ccbf9f8af191f20e1642fd877%3An%3A1%3A%7Bv%3A0%3B'; + $fields .= 'f%3A11%3A%22Zbqry.inyvq%22%3B%7D'; + + $this->Controller->data = array( + 'Model' => array('username' => 'nate', 'password' => 'foo', 'valid' => '0'), + '_Token' => compact('key', 'fields') + ); + $this->assertTrue($this->Controller->Security->validatePost($this->Controller)); + } /** * testValidatePostNoModel method * @@ -457,17 +496,15 @@ DIGEST; function testValidatePostNoModel() { $this->Controller->Security->startup($this->Controller); $key = $this->Controller->params['_Token']['key']; + $fields = '540ac9c60d323c22bafe997b72c0790f39a8bdef%3An%3A0%3A%7B%7D'; - $data['anything'] = 'some_data'; - $data['__Token']['key'] = $key; - $fields = $this->__sortFields(array('anything', '__Token' => array('key' => $key))); + $this->Controller->data = array( + 'anything' => 'some_data', + '_Token' => compact('key', 'fields') + ); - $fields = urlencode(Security::hash(serialize($fields) . Configure::read('Security.salt'))); - $data['__Token']['fields'] = $fields; - $this->Controller->data = $data; $result = $this->Controller->Security->validatePost($this->Controller); $this->assertTrue($result); - $this->assertEqual($this->Controller->data, $data); } /** * testValidatePostSimple method @@ -478,20 +515,43 @@ DIGEST; function testValidatePostSimple() { $this->Controller->Security->startup($this->Controller); $key = $this->Controller->params['_Token']['key']; + $fields = '69f493434187b867ea14b901fdf58b55d27c935d%3An%3A0%3A%7B%7D'; - $data['Model']['username'] = ''; - $data['Model']['password'] = ''; - $data['__Token']['key'] = $key; + $this->Controller->data = $data = array( + 'Model' => array('username' => '', 'password' => ''), + '_Token' => compact('key', 'fields') + ); - $fields = array('Model' => array('username','password'), '__Token' => array('key' => $key)); - $fields = $this->__sortFields($fields); - - $fields = urlencode(Security::hash(serialize($fields) . Configure::read('Security.salt'))); - $data['__Token']['fields'] = $fields; - $this->Controller->data = $data; $result = $this->Controller->Security->validatePost($this->Controller); $this->assertTrue($result); - $this->assertEqual($this->Controller->data, $data); + } +/** + * Tests hash validation for multiple records, including locked fields + * + * @access public + * @return void + */ + function testValidatePostComplex() { + $this->Controller->Security->startup($this->Controller); + $key = $this->Controller->params['_Token']['key']; + $fields = 'c9118120e680a7201b543f562e5301006ccfcbe2%3An%3A2%3A%7Bv%3A0%3Bf%3A14%3A%'; + $fields .= '22Nqqerffrf.0.vq%22%3Bv%3A1%3Bf%3A14%3A%22Nqqerffrf.1.vq%22%3B%7D'; + + $this->Controller->data = array( + 'Addresses' => array( + '0' => array( + 'id' => '123456', 'title' => '', 'first_name' => '', 'last_name' => '', + 'address' => '', 'city' => '', 'phone' => '', 'primary' => '' + ), + '1' => array( + 'id' => '654321', 'title' => '', 'first_name' => '', 'last_name' => '', + 'address' => '', 'city' => '', 'phone' => '', 'primary' => '' + ) + ), + '_Token' => compact('key', 'fields') + ); + $result = $this->Controller->Security->validatePost($this->Controller); + $this->assertTrue($result); } /** * testValidatePostCheckbox method @@ -505,57 +565,39 @@ DIGEST; function testValidatePostCheckbox() { $this->Controller->Security->startup($this->Controller); $key = $this->Controller->params['_Token']['key']; + $fields = 'a5475372b40f6e3ccbf9f8af191f20e1642fd877%3An%3A1%3A%7Bv%3A0%'; + $fields .= '3Bf%3A11%3A%22Zbqry.inyvq%22%3B%7D'; - $data['Model']['username'] = ''; - $data['Model']['password'] = ''; - $data['_Model']['valid'] = '0'; - $data['__Token']['key'] = $key; - - $fields = array( - 'Model' => array('username', 'password', 'valid'), - '_Model' => array('valid' => '0'), - '__Token' => array('key' => $key) + $this->Controller->data = array( + 'Model' => array('username' => '', 'password' => '', 'valid' => '0'), + '_Token' => compact('key', 'fields') ); - $fields = $this->__sortFields($fields); - $fields = urlencode(Security::hash(serialize($fields) . Configure::read('Security.salt'))); - $data['__Token']['fields'] = $fields; - - $this->Controller->data = $data; $result = $this->Controller->Security->validatePost($this->Controller); $this->assertTrue($result); - unset($data['_Model']); - $data['Model']['valid'] = '0'; - $this->assertEqual($this->Controller->data, $data); + $fields = '874439ca69f89b4c4a5f50fb9c36ff56a28f5d42%3An%3A0%3A%7B%7D'; + + $this->Controller->data = array( + 'Model' => array('username' => '', 'password' => '', 'valid' => '0'), + '_Token' => compact('key', 'fields') + ); + + $result = $this->Controller->Security->validatePost($this->Controller); + $this->assertTrue($result); $this->Controller->data = array(); $this->Controller->Security->startup($this->Controller); $key = $this->Controller->params['_Token']['key']; - $data['Model']['username'] = ''; - $data['Model']['password'] = ''; - $data['Model']['valid'] = '1'; - $data['_Model']['valid'] = '0'; - $data['__Token']['key'] = $key; - - $fields = array( - 'Model' => array('username', 'password', 'valid'), - '_Model' => array('valid' => '0'), - '__Token' => array('key' => $key) + $this->Controller->data = $data = array( + 'Model' => array('username' => '', 'password' => '', 'valid' => '0'), + '_Token' => compact('key', 'fields') ); - $fields = $this->__sortFields($fields); - $fields = urlencode(Security::hash(serialize($fields) . Configure::read('Security.salt'))); - $data['__Token']['fields'] = $fields; - - $this->Controller->data = $data; $result = $this->Controller->Security->validatePost($this->Controller); $this->assertTrue($result); - - unset($data['_Model']); - $this->assertEqual($this->Controller->data, $data); } /** * testValidatePostHidden method @@ -566,31 +608,18 @@ DIGEST; function testValidatePostHidden() { $this->Controller->Security->startup($this->Controller); $key = $this->Controller->params['_Token']['key']; + $fields = '51ccd8cb0997c7b3d4523ecde5a109318405ef8c%3An%3A2%3A%7Bv%3A0%3Bf%3A12%3A'; + $fields .= '%22Zbqry.uvqqra%22%3Bv%3A1%3Bf%3A18%3A%22Zbqry.bgure_uvqqra%22%3B%7D'; - $data['Model']['username'] = ''; - $data['Model']['password'] = ''; - $data['_Model']['hidden'] = '0'; - $data['_Model']['other_hidden'] = 'some hidden value'; - $data['__Token']['key'] = $key; - - $fields = array( - 'Model' => array('username', 'password', 'hidden'), - '_Model' => array('hidden' => '0', 'other_hidden' => 'some hidden value'), - '__Token' => array('key' => $key) + $this->Controller->data = array( + 'Model' => array( + 'username' => '', 'password' => '', 'hidden' => '0', + 'other_hidden' => 'some hidden value' + ), + '_Token' => compact('key', 'fields') ); - $fields = $this->__sortFields($fields); - - $fields = urlencode(Security::hash(serialize($fields) . Configure::read('Security.salt'))); - $data['__Token']['fields'] = $fields; - - $this->Controller->data = $data; $result = $this->Controller->Security->validatePost($this->Controller); $this->assertTrue($result); - - unset($data['_Model']); - $data['Model']['hidden'] = '0'; - $data['Model']['other_hidden'] = 'some hidden value'; - $this->assertTrue($this->Controller->data == $data); } /** * testValidatePostWithDisabledFields method @@ -599,32 +628,21 @@ DIGEST; * @return void */ function testValidatePostWithDisabledFields() { - $this->Controller->Security->startup($this->Controller); $this->Controller->Security->disabledFields = array('Model.username', 'Model.password'); + $this->Controller->Security->startup($this->Controller); $key = $this->Controller->params['_Token']['key']; + $fields = 'ef1082968c449397bcd849f963636864383278b1%3An%3A1%3A%7Bv%'; + $fields .= '3A0%3Bf%3A12%3A%22Zbqry.uvqqra%22%3B%7D'; - $data['Model']['username'] = ''; - $data['Model']['password'] = ''; - $data['_Model']['hidden'] = '0'; - $data['__Token']['key'] = $key; - - $fields = array( - 'Model' => array('hidden'), - '_Model' => array('hidden' => '0'), - '__Token' => array('key' => $key) + $this->Controller->data = array( + 'Model' => array( + 'username' => '', 'password' => '', 'hidden' => '0' + ), + '_Token' => compact('fields', 'key') ); - $fields = $this->__sortFields($fields); - $fields = urlencode(Security::hash(serialize($fields) . Configure::read('Security.salt'))); - $data['__Token']['fields'] = $fields; - - $this->Controller->data = $data; $result = $this->Controller->Security->validatePost($this->Controller); $this->assertTrue($result); - - unset($data['_Model']); - $data['Model']['hidden'] = '0'; - $this->assertTrue($this->Controller->data == $data); } /** * testValidateHiddenMultipleModel method @@ -635,36 +653,18 @@ DIGEST; function testValidateHiddenMultipleModel() { $this->Controller->Security->startup($this->Controller); $key = $this->Controller->params['_Token']['key']; + $fields = 'a2d01072dc4660eea9d15007025f35a7a5b58e18%3An%3A3%3A%7Bv%3A0%3Bf%3A11'; + $fields .= '%3A%22Zbqry.inyvq%22%3Bv%3A1%3Bf%3A12%3A%22Zbqry2.inyvq%22%3Bv%3A2%'; + $fields .= '3Bf%3A12%3A%22Zbqry3.inyvq%22%3B%7D'; - $data['Model']['username'] = ''; - $data['Model']['password'] = ''; - $data['_Model']['valid'] = '0'; - $data['_Model2']['valid'] = '0'; - $data['_Model3']['valid'] = '0'; - $data['__Token']['key'] = $key; - - $fields = array( - 'Model' => array('username', 'password', 'valid'), - 'Model2'=> array('valid'), - 'Model3'=> array('valid'), - '_Model2'=> array('valid' => '0'), - '_Model3'=> array('valid' => '0'), - '_Model' => array('valid' => '0'), - '__Token' => array('key' => $key) + $this->Controller->data = array( + 'Model' => array('username' => '', 'password' => '', 'valid' => '0'), + 'Model2' => array('valid' => '0'), + 'Model3' => array('valid' => '0'), + '_Token' => compact('key', 'fields') ); - - $fields = urlencode(Security::hash(serialize($this->__sortFields($fields)) . Configure::read('Security.salt'))); - $data['__Token']['fields'] = $fields; - - $this->Controller->data = $data; $result = $this->Controller->Security->validatePost($this->Controller); $this->assertTrue($result); - - unset($data['_Model'], $data['_Model2'], $data['_Model3']); - $data['Model']['valid'] = '0'; - $data['Model2']['valid'] = '0'; - $data['Model3']['valid'] = '0'; - $this->assertTrue($this->Controller->data == $data); } /** * testLoginValidation method @@ -684,42 +684,26 @@ DIGEST; function testValidateHasManyModel() { $this->Controller->Security->startup($this->Controller); $key = $this->Controller->params['_Token']['key']; + $fields = '51e3b55a6edd82020b3f29c9ae200e14bbeb7ee5%3An%3A4%3A%7Bv%3A0%3Bf%3A14%3A%2'; + $fields .= '2Zbqry.0.uvqqra%22%3Bv%3A1%3Bf%3A13%3A%22Zbqry.0.inyvq%22%3Bv%3A2%3Bf%3'; + $fields .= 'A14%3A%22Zbqry.1.uvqqra%22%3Bv%3A3%3Bf%3A13%3A%22Zbqry.1.inyvq%22%3B%7D'; - $data['Model'][0]['username'] = 'username'; - $data['Model'][0]['password'] = 'password'; - $data['Model'][1]['username'] = 'username'; - $data['Model'][1]['password'] = 'password'; - $data['_Model'][0]['hidden'] = 'value'; - $data['_Model'][1]['hidden'] = 'value'; - $data['_Model'][0]['valid'] = '0'; - $data['_Model'][1]['valid'] = '0'; - $data['__Token']['key'] = $key; - - $fields = array( + $this->Controller->data = array( 'Model' => array( - 0 => array('username', 'password', 'valid'), - 1 => array('username', 'password', 'valid')), - '_Model' => array( - 0 => array('hidden' => 'value', 'valid' => '0'), - 1 => array('hidden' => 'value', 'valid' => '0')), - '__Token' => array('key' => $key)); + array( + 'username' => 'username', 'password' => 'password', + 'hidden' => 'value', 'valid' => '0' + ), + array( + 'username' => 'username', 'password' => 'password', + 'hidden' => 'value', 'valid' => '0' + ) + ), + '_Token' => compact('key', 'fields') + ); - $fields = $this->__sortFields($fields); - - $fields = urlencode(Security::hash(serialize($fields) . Configure::read('Security.salt'))); - $data['__Token']['fields'] = $fields; - - $this->Controller->data = $data; $result = $this->Controller->Security->validatePost($this->Controller); $this->assertTrue($result); - - unset($data['_Model']); - $data['Model'][0]['hidden'] = 'value'; - $data['Model'][1]['hidden'] = 'value'; - $data['Model'][0]['valid'] = '0'; - $data['Model'][1]['valid'] = '0'; - - $this->assertTrue($this->Controller->data == $data); } /** @@ -731,10 +715,14 @@ DIGEST; function testValidateHasManyRecordsPass() { $this->Controller->Security->startup($this->Controller); $key = $this->Controller->params['_Token']['key']; + $fields = '7a203edb3d345bbf38fe0dccae960da8842e11d7%3An%3A4%3A%7Bv%3A0%3Bf%3A12%3A%2'; + $fields .= '2Nqqerff.0.vq%22%3Bv%3A1%3Bf%3A17%3A%22Nqqerff.0.cevznel%22%3Bv%3A2%3Bf%'; + $fields .= '3A12%3A%22Nqqerff.1.vq%22%3Bv%3A3%3Bf%3A17%3A%22Nqqerff.1.cevznel%22%3B%7D'; - $data = array( + $this->Controller->data = array( 'Address' => array( 0 => array( + 'id' => '123', 'title' => 'home', 'first_name' => 'Bilbo', 'last_name' => 'Baggins', @@ -744,52 +732,21 @@ DIGEST; 'primary' => '1', ), 1 => array( + 'id' => '124', 'title' => 'home', 'first_name' => 'Frodo', 'last_name' => 'Baggins', 'address' => '50 Bag end way', 'city' => 'the shire', 'phone' => 'N/A', - 'primary' => '1', - ), - ), - '_Address' => array( - 0 => array( - 'id' => '123', - 'primary' => '0', - ), - 1 => array( - 'id' => '124', - 'primary' => '0', + 'primary' => '1' ) ), - '__Token' => array( - 'key' => $key, - ), + '_Token' => compact('key', 'fields') ); - $fields = array( - 'Address' => array( - 0 => array('title', 'first_name', 'last_name', 'address', 'city', 'phone', 'primary'), - 1 => array('title', 'first_name', 'last_name', 'address', 'city', 'phone', 'primary')), - '_Address' => array( - 0 => array('id' => '123', 'primary' => '0'), - 1 => array('id' => '124', 'primary' => '0')), - '__Token' => array('key' => $key) - ); - $fields = $this->__sortFields($fields); - $fields = urlencode(Security::hash(serialize($fields) . Configure::read('Security.salt'))); - $data['__Token']['fields'] = $fields; - - $this->Controller->data = $data; $result = $this->Controller->Security->validatePost($this->Controller); $this->assertTrue($result); - - unset($data['_Address']); - $data['Address'][0]['id'] = '123'; - $data['Address'][1]['id'] = '124'; - - $this->assertEqual($this->Controller->data, $data); } /** * testValidateHasManyRecords method @@ -802,57 +759,36 @@ DIGEST; function testValidateHasManyRecordsFail() { $this->Controller->Security->startup($this->Controller); $key = $this->Controller->params['_Token']['key']; + $fields = '7a203edb3d345bbf38fe0dccae960da8842e11d7%3An%3A4%3A%7Bv%3A0%3Bf%3A12%3A%2'; + $fields .= '2Nqqerff.0.vq%22%3Bv%3A1%3Bf%3A17%3A%22Nqqerff.0.cevznel%22%3Bv%3A2%3Bf%'; + $fields .= '3A12%3A%22Nqqerff.1.vq%22%3Bv%3A3%3Bf%3A17%3A%22Nqqerff.1.cevznel%22%3B%7D'; - $data = array( + $this->Controller->data = array( 'Address' => array( 0 => array( + 'id' => '123', 'title' => 'home', 'first_name' => 'Bilbo', 'last_name' => 'Baggins', 'address' => '23 Bag end way', 'city' => 'the shire', 'phone' => 'N/A', - 'primary' => '1', + 'primary' => '5', ), 1 => array( + 'id' => '124', 'title' => 'home', 'first_name' => 'Frodo', 'last_name' => 'Baggins', 'address' => '50 Bag end way', 'city' => 'the shire', 'phone' => 'N/A', - 'primary' => '1', - ), - ), - '_Address' => array( - 0 => array( - 'id' => '123', - 'primary' => '23', - ), - 1 => array( - 'id' => '124', - 'primary' => '0', + 'primary' => '1' ) ), - '__Token' => array( - 'key' => $key, - ), + '_Token' => compact('key', 'fields') ); - $fields = array( - 'Address' => array( - 0 => array('title', 'first_name', 'last_name', 'address', 'city', 'phone', 'primary'), - 1 => array('title', 'first_name', 'last_name', 'address', 'city', 'phone', 'primary')), - '_Address' => array( - 0 => array('id' => '123', 'primary' => '0'), - 1 => array('id' => '124', 'primary' => '0')), - '__Token' => array('key' => $key) - ); - $fields = $this->__sortFields($fields); - $fields = urlencode(Security::hash(serialize($fields) . Configure::read('Security.salt'))); - $data['__Token']['fields'] = $fields; - - $this->Controller->data = $data; $result = $this->Controller->Security->validatePost($this->Controller); $this->assertFalse($result); } @@ -902,7 +838,8 @@ DIGEST; $result = $this->Controller->Security->generateDigestResponseHash($data); $expected = md5( - md5($data['username'] . ':' . $loginData['realm'].':'.$data['password']) . ':' . $data['nonce'] . ':' . $data['nc'] . ':' . $data['cnonce'] . ':' . $data['qop'] . ':' . + md5($data['username'] . ':' . $loginData['realm'] . ':' . $data['password']) . ':' . + $data['nonce'] . ':' . $data['nc'] . ':' . $data['cnonce'] . ':' . $data['qop'] . ':' . md5(env('REQUEST_METHOD') . ':' . $data['uri']) ); $this->assertIdentical($result, $expected); @@ -992,16 +929,12 @@ DIGEST; function testFormDisabledFields() { $this->Controller->Security->startup($this->Controller); $key = $this->Controller->params['_Token']['key']; + $fields = '11842060341b9d0fc3808b90ba29fdea7054d6ad%3An%3A0%3A%7B%7D'; - $data = array(); - $data['MyModel']['name'] = 'some data'; - $data['__Token']['key'] = $key; - - $fields = $this->__sortFields(array('__Token' => array('key' => $key))); - $fields = urlencode(Security::hash(serialize($fields) . Configure::read('Security.salt'))); - $data['__Token']['fields'] = $fields; - $this->Controller->data = $data; - + $this->Controller->data = array( + 'MyModel' => array('name' => 'some data'), + '_Token' => compact('key', 'fields') + ); $result = $this->Controller->Security->validatePost($this->Controller); $this->assertFalse($result); @@ -1009,34 +942,14 @@ DIGEST; $this->Controller->Security->disabledFields = array('MyModel.name'); $key = $this->Controller->params['_Token']['key']; - $data = array(); - $data['MyModel']['name'] = 'some data'; - $data['__Token']['key'] = $key; - - $fields = $this->__sortFields(array('__Token' => array('key' => $key))); - $fields = urlencode(Security::hash(serialize($fields) . Configure::read('Security.salt'))); - $data['__Token']['fields'] = $fields; - $this->Controller->data = $data; + $this->Controller->data = array( + 'MyModel' => array('name' => 'some data'), + '_Token' => compact('key', 'fields') + ); $result = $this->Controller->Security->validatePost($this->Controller); $this->assertTrue($result); - $this->assertEqual($this->Controller->data, $data); - } -/** - * sortFields method - * - * @param mixed $fields - * @access private - * @return void - */ - function __sortFields($fields) { - foreach ($fields as $key => $value) { - if ($key[0] != '_' && is_array($fields[$key])) { - sort($fields[$key]); - } - } - ksort($fields, SORT_STRING); - return $fields; } } + ?> \ No newline at end of file diff --git a/cake/tests/cases/libs/view/helpers/form.test.php b/cake/tests/cases/libs/view/helpers/form.test.php index 27adca75b..69d4b0496 100644 --- a/cake/tests/cases/libs/view/helpers/form.test.php +++ b/cake/tests/cases/libs/view/helpers/form.test.php @@ -573,6 +573,9 @@ class FormHelperTest extends CakeTestCase { ClassRegistry::addObject('ValidateItem', new ValidateItem()); ClassRegistry::addObject('ValidateUser', new ValidateUser()); ClassRegistry::addObject('ValidateProfile', new ValidateProfile()); + + $this->oldSalt = Configure::read('Security.salt'); + Configure::write('Security.salt', 'foo!'); } /** * testFormCreateWithSecurity method @@ -588,7 +591,7 @@ class FormHelperTest extends CakeTestCase { 'form' => array('method' => 'post', 'action' => '/contacts/add'), 'fieldset' => array('style' => 'display:none;'), array('input' => array('type' => 'hidden', 'name' => '_method', 'value' => 'POST')), - array('input' => array('type' => 'hidden', 'name' => 'data[__Token][key]', 'value' => 'testKey', 'id')), + array('input' => array('type' => 'hidden', 'name' => 'data[_Token][key]', 'value' => 'testKey', 'id')), '/fieldset' ); $this->assertTags($result, $expected); @@ -597,6 +600,30 @@ class FormHelperTest extends CakeTestCase { $expected['form']['id'] = 'MyForm'; $this->assertTags($result, $expected); } +/** + * Tests form hash generation with model-less data + * + * @access public + * @return void + */ + function testValidateHashNoModel() { + $this->Form->params['_Token'] = array('key' => 'foo'); + $result = $this->Form->secure(array('anything')); + $this->assertPattern('/540ac9c60d323c22bafe997b72c0790f39a8bdef/', $result); + } +/** + * Tests that hidden fields generated for checkboxes don't get locked + * + * @access public + * @return void + */ + function testNoCheckboxLocking() { + $this->Form->params['_Token'] = array('key' => 'foo'); + $this->assertIdentical($this->Form->fields, array()); + + $this->Form->checkbox('check', array('value' => '1')); + $this->assertIdentical($this->Form->fields, array('check')); + } /** * testFormSecurityFields method * @@ -605,19 +632,19 @@ class FormHelperTest extends CakeTestCase { */ function testFormSecurityFields() { $key = 'testKey'; - $fields = array( - 'Model' => array('password', 'username', 'valid'), - '_Model' => array('valid' => '0'), - '__Token' => array('key' => $key) - ); + $fields = array('Model.password', 'Model.username', 'Model.valid' => '0'); $this->Form->params['_Token']['key'] = $key; $result = $this->Form->secure($fields); - $fields = $this->__sortFields($fields); - $expected = urlencode(Security::hash(serialize($fields) . Configure::read('Security.salt'))); + $expected = Security::hash(serialize($fields) . Configure::read('Security.salt')); + $expected .= ':' . str_rot13(serialize(array('Model.valid'))); + $expected = array( 'fieldset' => array('style' => 'display:none;'), - 'input' => array('type' => 'hidden', 'name' => 'data[__Token][fields]', 'value' => $expected, 'id' => 'preg:/TokenFields\d+/'), + 'input' => array( + 'type' => 'hidden', 'name' => 'data[_Token][fields]', + 'value' => urlencode($expected), 'id' => 'preg:/TokenFields\d+/' + ), '/fieldset' ); $this->assertTags($result, $expected); @@ -644,7 +671,10 @@ class FormHelperTest extends CakeTestCase { 'label' => array('for' => 'ContactFoo'), 'Foo', '/label', - array('input' => array('type' => 'text', 'name' => 'data[Contact][foo]', 'value' => '', 'id' => 'ContactFoo')), + array('input' => array( + 'type' => 'text', 'name' => 'data[Contact][foo]', + 'value' => '', 'id' => 'ContactFoo' + )), '/div' ); } @@ -656,23 +686,25 @@ class FormHelperTest extends CakeTestCase { */ function testFormSecurityMultipleFields() { $key = 'testKey'; + $fields = array( - 'Model' => array( - 0 => array('username', 'password', 'valid'), - 1 => array('username', 'password', 'valid')), - '_Model' => array( - 0 => array('hidden' => 'value', 'valid' => '0'), - 1 => array('hidden' => 'value', 'valid' => '0')), - '__Token' => array('key' => $key) + 'Model.0.password', 'Model.0.username', 'Model.0.hidden' => 'value', + 'Model.0.valid' => '0', 'Model.1.password', 'Model.1.username', + 'Model.1.hidden' => 'value', 'Model.1.valid' => '0' ); $this->Form->params['_Token']['key'] = $key; $result = $this->Form->secure($fields); - $fields = $this->__sortFields($fields); - $expected = urlencode(Security::hash(serialize($fields) . Configure::read('Security.salt'))); + $hash = '51e3b55a6edd82020b3f29c9ae200e14bbeb7ee5%3An%3A4%3A%7Bv%3A0%3Bf%3A14%3A%22Zbqry.'; + $hash .= '0.uvqqra%22%3Bv%3A1%3Bf%3A13%3A%22Zbqry.0.inyvq%22%3Bv%3A2%3Bf%3A14%3A%22Zbqry.1'; + $hash .= '.uvqqra%22%3Bv%3A3%3Bf%3A13%3A%22Zbqry.1.inyvq%22%3B%7D'; + $expected = array( 'fieldset' => array('style' => 'display:none;'), - 'input' => array('type' => 'hidden', 'name' => 'data[__Token][fields]', 'value' => $expected, 'id' => 'preg:/TokenFields\d+/'), + 'input' => array( + 'type' => 'hidden', 'name' => 'data[_Token][fields]', + 'value' => $hash, 'id' => 'preg:/TokenFields\d+/' + ), '/fieldset' ); $this->assertTags($result, $expected); @@ -685,6 +717,7 @@ class FormHelperTest extends CakeTestCase { */ function testFormSecurityMultipleInputFields() { $key = 'testKey'; + $this->Form->params['_Token']['key'] = $key; $this->Form->create('Addresses'); @@ -706,29 +739,21 @@ class FormHelperTest extends CakeTestCase { $this->Form->input('Addresses.1.phone'); $this->Form->input('Addresses.1.primary', array('type' => 'checkbox')); - $fields = array( - 'Addresses' => array( - 0 => array('title', 'first_name', 'last_name', 'address', 'city', 'phone', 'primary'), - 1 => array('title', 'first_name', 'last_name', 'address', 'city', 'phone', 'primary') - ), - '_Addresses' => array( - 0 => array('id' => '123456', 'primary' => '0'), - 1 => array('id' => '654321', 'primary' => '0') - ), - '__Token' => array('key' => $key) - ); - - $fields = $this->__sortFields($fields); $result = $this->Form->secure($this->Form->fields); - $expected = urlencode(Security::hash(serialize($fields) . Configure::read('Security.salt'))); + + $hash = 'c9118120e680a7201b543f562e5301006ccfcbe2%3An%3A2%3A%7Bv%3A0%3Bf%3A14%'; + $hash .= '3A%22Nqqerffrf.0.vq%22%3Bv%3A1%3Bf%3A14%3A%22Nqqerffrf.1.vq%22%3B%7D'; + $expected = array( 'fieldset' => array('style' => 'display:none;'), - 'input' => array('type' => 'hidden', 'name' => 'data[__Token][fields]', 'value' => $expected, 'id' => 'preg:/TokenFields\d+/'), + 'input' => array( + 'type' => 'hidden', 'name' => 'data[_Token][fields]', + 'value' => $hash, 'id' => 'preg:/TokenFields\d+/' + ), '/fieldset' ); $this->assertTags($result, $expected); } - /** * testFormSecurityMultipleInputDisabledFields method * @@ -756,24 +781,15 @@ class FormHelperTest extends CakeTestCase { $this->Form->input('Addresses.1.city'); $this->Form->input('Addresses.1.phone'); - $fields = array( - 'Addresses' => array( - 0 => array('title', 'last_name', 'city', 'phone'), - 1 => array('title', 'last_name', 'city', 'phone')), - '_Addresses' => array( - 0 => array('id' => '123456'), - 1 => array('id' => '654321')), - '__Token' => array('key' => $key) - ); - - $fields = $this->__sortFields($fields); $result = $this->Form->secure($this->Form->fields); - $expected = urlencode(Security::hash(serialize($fields) . Configure::read('Security.salt'))); + $hash = '774df31936dc850b7d8a5277dc0b890123788b09%3An%3A2%3A%7Bv%3A0%3Bf%3A14%3A%22Nqqerf'; + $hash .= 'frf.0.vq%22%3Bv%3A1%3Bf%3A14%3A%22Nqqerffrf.1.vq%22%3B%7D'; + $expected = array( 'fieldset' => array('style' => 'display:none;'), 'input' => array( - 'type' => 'hidden', 'name' => 'data[__Token][fields]', - 'value' => $expected, 'id' => 'preg:/TokenFields\d+/' + 'type' => 'hidden', 'name' => 'data[_Token][fields]', + 'value' => $hash, 'id' => 'preg:/TokenFields\d+/' ), '/fieldset' ); @@ -799,18 +815,23 @@ class FormHelperTest extends CakeTestCase { $this->Form->input('Addresses.city'); $this->Form->input('Addresses.phone'); - $fields = array( - 'Addresses' => array('title', 'last_name', 'city', 'phone'), - '_Addresses' => array('id' => '123456'), - '__Token' => array('key' => $key) + $result = $this->Form->fields; + $expected = array( + 'Addresses.id' => '123456', 'Addresses.title', 'Addresses.last_name', + 'Addresses.city', 'Addresses.phone' ); + $this->assertEqual($result, $expected); - $fields = $this->__sortFields($fields); - $result = $this->Form->secure($this->Form->fields); - $expected = urlencode(Security::hash(serialize($fields) . Configure::read('Security.salt'))); + $result = $this->Form->secure($expected); + + $hash = '449b7e889128e8e52c5e81d19df68f5346571492%3An%3A1%3A%'; + $hash .= '7Bv%3A0%3Bf%3A12%3A%22Nqqerffrf.vq%22%3B%7D'; $expected = array( 'fieldset' => array('style' => 'display:none;'), - 'input' => array('type' => 'hidden', 'name' => 'data[__Token][fields]', 'value' => $expected, 'id' => 'preg:/TokenFields\d+/'), + 'input' => array( + 'type' => 'hidden', 'name' => 'data[_Token][fields]', + 'value' => $hash, 'id' => 'preg:/TokenFields\d+/' + ), '/fieldset' ); $this->assertTags($result, $expected); @@ -825,13 +846,9 @@ class FormHelperTest extends CakeTestCase { $fields = array( 'UserForm' => array('0' => 'published', '1' => 'other', '2' => 'hidden', '3' => 'something'), '_UserForm' => array('stuff' => '', 'hidden' => '0', 'something' => '0'), - '__Token' => array('key' => 'testKey') + '_Token' => array('key' => 'testKey') ); - $fields = $this->__sortFields($fields); - $fieldsKey = urlencode(Security::hash(serialize($fields) . Configure::read('Security.salt'))); - $fields['__Token']['fields'] = $fieldsKey; - $this->Form->params['_Token']['key'] = 'testKey'; $result = $this->Form->create('Contact', array('url' => '/contacts/add')); @@ -839,7 +856,7 @@ class FormHelperTest extends CakeTestCase { 'form' => array('method' => 'post', 'action' => '/contacts/add'), 'fieldset' => array('style' => 'display:none;'), array('input' => array('type' => 'hidden', 'name' => '_method', 'value' => 'POST')), - array('input' => array('type' => 'hidden', 'name' => 'data[__Token][key]', 'value' => 'testKey', 'id' => 'preg:/Token\d+/')), + array('input' => array('type' => 'hidden', 'name' => 'data[_Token][key]', 'value' => 'testKey', 'id' => 'preg:/Token\d+/')), '/fieldset' ); $this->assertTags($result, $expected); @@ -850,7 +867,10 @@ class FormHelperTest extends CakeTestCase { 'label' => array('for' => 'UserFormPublished'), 'Published', '/label', - array('input' => array('type' => 'text', 'name' => 'data[UserForm][published]', 'value' => '', 'id' => 'UserFormPublished')), + array('input' => array( + 'type' => 'text', 'name' => 'data[UserForm][published]', + 'value' => '', 'id' => 'UserFormPublished' + )), '/div' ); $this->assertTags($result, $expected); @@ -861,27 +881,32 @@ class FormHelperTest extends CakeTestCase { 'label' => array('for' => 'UserFormOther'), 'Other', '/label', - array('input' => array('type' => 'text', 'name' => 'data[UserForm][other]', 'value' => '', 'id' => 'UserFormOther')), + array('input' => array( + 'type' => 'text', 'name' => 'data[UserForm][other]', + 'value' => '', 'id' => 'UserFormOther' + )), '/div' ); $this->assertTags($result, $expected); $result = $this->Form->hidden('UserForm.stuff'); - $expected = array( - 'input' => array('type' => 'hidden', 'name' => 'data[_UserForm][stuff]', 'value' => '', 'id' => 'UserFormStuff') - ); + $expected = array('input' => array( + 'type' => 'hidden', 'name' => 'data[UserForm][stuff]', + 'value' => '', 'id' => 'UserFormStuff' + )); $this->assertTags($result, $expected); $result = $this->Form->hidden('UserForm.hidden', array('value' => '0')); - $expected = array( - 'input' => array('type' => 'hidden', 'name' => 'data[_UserForm][hidden]', 'value' => '0', 'id' => 'UserFormHidden') - ); + $expected = array('input' => array( + 'type' => 'hidden', 'name' => 'data[UserForm][hidden]', + 'value' => '0', 'id' => 'UserFormHidden' + )); $this->assertTags($result, $expected); $result = $this->Form->input('UserForm.something', array('type' => 'checkbox')); $expected = array( 'div' => array('class' => 'input checkbox'), - array('input' => array('type' => 'hidden', 'name' => 'data[_UserForm][something]', 'value' => '0', 'id' => 'UserFormSomething_')), + array('input' => array('type' => 'hidden', 'name' => 'data[UserForm][something]', 'value' => '0', 'id' => 'UserFormSomething_')), array('input' => array('type' => 'checkbox', 'name' => 'data[UserForm][something]', 'value' => '1', 'id' => 'UserFormSomething')), 'label' => array('for' => 'UserFormSomething'), 'Something', @@ -890,17 +915,26 @@ class FormHelperTest extends CakeTestCase { ); $this->assertTags($result, $expected); + $result = $this->Form->fields; + $expected = array( + 'UserForm.published', 'UserForm.other', 'UserForm.stuff' => '', + 'UserForm.hidden' => '0', 'UserForm.something' + ); + $this->assertEqual($result, $expected); + + $hash = 'bd7c4a654e5361f9a433a43f488ff9a1065d0aaf%3An%3A2%3A%7Bv%3A0%3Bf%3A15%3'; + $hash .= 'A%22HfreSbez.uvqqra%22%3Bv%3A1%3Bf%3A14%3A%22HfreSbez.fghss%22%3B%7D'; + $result = $this->Form->secure($this->Form->fields); $expected = array( 'fieldset' => array('style' => 'display:none;'), - array('input' => array('type' => 'hidden', 'name' => 'data[__Token][fields]', 'value' => $fieldsKey, 'id' => 'preg:/TokenFields\d+/')), + array('input' => array( + 'type' => 'hidden', 'name' => 'data[_Token][fields]', + 'value' => $hash, 'id' => 'preg:/TokenFields\d+/' + )), '/fieldset' ); $this->assertTags($result, $expected); - - $result = $this->Form->fields; - $result = $this->__sortFields($result); - $this->assertEqual($result, $fields); } /** * testPasswordValidation method @@ -2131,7 +2165,12 @@ class FormHelperTest extends CakeTestCase { * @return void */ function testNestedSelect() { - $result = $this->Form->select('Model.field', array(1 => 'One', 2 => 'Two', 'Three' => array(3 => 'Three', 4 => 'Four', 5 => 'Five')), null, array(), false); + $result = $this->Form->select( + 'Model.field', + array(1 => 'One', 2 => 'Two', 'Three' => array( + 3 => 'Three', 4 => 'Four', 5 => 'Five' + )), null, array(), false + ); $expected = array( 'select' => array('name' => 'data[Model][field]', 'id' => 'ModelField'), @@ -2153,27 +2192,30 @@ class FormHelperTest extends CakeTestCase { ); $this->assertTags($result, $expected); - $result = $this->Form->select('Model.field', array(1 => 'One', 2 => 'Two', 'Three' => array(3 => 'Three', 4 => 'Four')), null, array('showParents' => true), false); + $result = $this->Form->select( + 'Model.field', + array(1 => 'One', 2 => 'Two', 'Three' => array(3 => 'Three', 4 => 'Four')), null, + array('showParents' => true), false + ); $expected = array( - 'select' => array('name' => 'data[Model][field]', - 'id' => 'ModelField'), - array('option' => array('value' => 1)), - 'One', + 'select' => array('name' => 'data[Model][field]', 'id' => 'ModelField'), + array('option' => array('value' => 1)), + 'One', + '/option', + array('option' => array('value' => 2)), + 'Two', + '/option', + array('optgroup' => array('label' => 'Three')), + array('option' => array('value' => 3)), + 'Three', '/option', - array('option' => array('value' => 2)), - 'Two', + array('option' => array('value' => 4)), + 'Four', '/option', - array('optgroup' => array('label' => 'Three')), - array('option' => array('value' => 3)), - 'Three', - '/option', - array('option' => array('value' => 4)), - 'Four', - '/option', - '/optgroup', - '/select' - ); + '/optgroup', + '/select' + ); $this->assertTags($result, $expected); } @@ -2242,23 +2284,39 @@ class FormHelperTest extends CakeTestCase { * @return void */ function testSelectMultipleCheckboxes() { - $result = $this->Form->select('Model.multi_field', array('first', 'second', 'third'), null, array('multiple' => 'checkbox')); + $result = $this->Form->select( + 'Model.multi_field', + array('first', 'second', 'third'), null, + array('multiple' => 'checkbox') + ); + $expected = array( - 'input' => array('type' => 'hidden', 'name' => 'data[Model][multi_field]', 'value' => ''), + 'input' => array( + 'type' => 'hidden', 'name' => 'data[Model][multi_field]', 'value' => '' + ), array('div' => array('class' => 'checkbox')), - array('input' => array('type' => 'checkbox', 'name' => 'data[Model][multi_field][]', 'value' => '0', 'id' => 'ModelMultiField0')), + array('input' => array( + 'type' => 'checkbox', 'name' => 'data[Model][multi_field][]', + 'value' => '0', 'id' => 'ModelMultiField0' + )), array('label' => array('for' => 'ModelMultiField0')), 'first', '/label', '/div', array('div' => array('class' => 'checkbox')), - array('input' => array('type' => 'checkbox', 'name' => 'data[Model][multi_field][]', 'value' => '1', 'id' => 'ModelMultiField1')), + array('input' => array( + 'type' => 'checkbox', 'name' => 'data[Model][multi_field][]', + 'value' => '1', 'id' => 'ModelMultiField1' + )), array('label' => array('for' => 'ModelMultiField1')), 'second', '/label', '/div', array('div' => array('class' => 'checkbox')), - array('input' => array('type' => 'checkbox', 'name' => 'data[Model][multi_field][]', 'value' => '2', 'id' => 'ModelMultiField2')), + array('input' => array( + 'type' => 'checkbox', 'name' => 'data[Model][multi_field][]', + 'value' => '2', 'id' => 'ModelMultiField2' + )), array('label' => array('for' => 'ModelMultiField2')), 'third', '/label', @@ -2266,23 +2324,36 @@ class FormHelperTest extends CakeTestCase { ); $this->assertTags($result, $expected); - $result = $this->Form->select('Model.multi_field', array('a' => 'first', 'b' => 'second', 'c' => 'third'), null, array('multiple' => 'checkbox')); + $result = $this->Form->select( + 'Model.multi_field', + array('a' => 'first', 'b' => 'second', 'c' => 'third'), null, + array('multiple' => 'checkbox') + ); $expected = array( 'input' => array('type' => 'hidden', 'name' => 'data[Model][multi_field]', 'value' => ''), array('div' => array('class' => 'checkbox')), - array('input' => array('type' => 'checkbox', 'name' => 'data[Model][multi_field][]', 'value' => 'a', 'id' => 'ModelMultiFieldA')), + array('input' => array( + 'type' => 'checkbox', 'name' => 'data[Model][multi_field][]', + 'value' => 'a', 'id' => 'ModelMultiFieldA' + )), array('label' => array('for' => 'ModelMultiFieldA')), 'first', '/label', '/div', array('div' => array('class' => 'checkbox')), - array('input' => array('type' => 'checkbox', 'name' => 'data[Model][multi_field][]', 'value' => 'b', 'id' => 'ModelMultiFieldB')), + array('input' => array( + 'type' => 'checkbox', 'name' => 'data[Model][multi_field][]', + 'value' => 'b', 'id' => 'ModelMultiFieldB' + )), array('label' => array('for' => 'ModelMultiFieldB')), 'second', '/label', '/div', array('div' => array('class' => 'checkbox')), - array('input' => array('type' => 'checkbox', 'name' => 'data[Model][multi_field][]', 'value' => 'c', 'id' => 'ModelMultiFieldC')), + array('input' => array( + 'type' => 'checkbox', 'name' => 'data[Model][multi_field][]', + 'value' => 'c', 'id' => 'ModelMultiFieldC' + )), array('label' => array('for' => 'ModelMultiFieldC')), 'third', '/label', @@ -4411,6 +4482,7 @@ class FormHelperTest extends CakeTestCase { ClassRegistry::removeObject('ValidateUser'); ClassRegistry::removeObject('ValidateProfile'); unset($this->Form->Html, $this->Form, $this->Controller, $this->View); + Configure::write('Security.salt', $this->oldSalt); } /** * sortFields method