Skip to content

Commit

Permalink
Merge pull request #7880 from waterada/ticket-7862-based-on-master
Browse files Browse the repository at this point in the history
[3.1] Add IntegrationTestCase::cookieEncrypted().
  • Loading branch information
markstory committed Dec 23, 2015
2 parents 2464c71 + 7fcc471 commit 5be4156
Show file tree
Hide file tree
Showing 6 changed files with 390 additions and 127 deletions.
133 changes: 6 additions & 127 deletions src/Controller/Component/CookieComponent.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@
use Cake\I18n\Time;
use Cake\Network\Request;
use Cake\Network\Response;
use Cake\Utility\CookieCryptTrait;
use Cake\Utility\Hash;
use Cake\Utility\Security;
use RuntimeException;

/**
* Cookie Component.
Expand All @@ -36,6 +36,7 @@
*/
class CookieComponent extends Component
{
use CookieCryptTrait;

/**
* Default config
Expand Down Expand Up @@ -106,13 +107,6 @@ class CookieComponent extends Component
*/
protected $_response = null;

/**
* Valid cipher names for encrypted cookies.
*
* @var array
*/
protected $_validCiphers = ['aes', 'rijndael'];

/**
* Initialize config data and properties.
*
Expand Down Expand Up @@ -351,127 +345,12 @@ protected function _delete($name)
}

/**
* Encrypts $value using public $type method in Security class
* Returns the encryption key to be used.
*
* @param string $value Value to encrypt
* @param string|bool $encrypt Encryption mode to use. False
* disabled encryption.
* @return string Encoded values
* @return string
*/
protected function _encrypt($value, $encrypt)
protected function _getCookieEncryptionKey()
{
if (is_array($value)) {
$value = $this->_implode($value);
}
if ($encrypt === false) {
return $value;
}
$this->_checkCipher($encrypt);
$prefix = "Q2FrZQ==.";
if ($encrypt === 'rijndael') {
$cipher = Security::rijndael($value, $this->_config['key'], 'encrypt');
}
if ($encrypt === 'aes') {
$cipher = Security::encrypt($value, $this->_config['key']);
}
return $prefix . base64_encode($cipher);
}

/**
* Helper method for validating encryption cipher names.
*
* @param string $encrypt The cipher name.
* @return void
* @throws \RuntimeException When an invalid cipher is provided.
*/
protected function _checkCipher($encrypt)
{
if (!in_array($encrypt, $this->_validCiphers)) {
$msg = sprintf(
'Invalid encryption cipher. Must be one of %s.',
implode(', ', $this->_validCiphers)
);
throw new RuntimeException($msg);
}
}

/**
* Decrypts $value using public $type method in Security class
*
* @param array $values Values to decrypt
* @param string|bool $mode Encryption mode
* @return string decrypted string
*/
protected function _decrypt($values, $mode)
{
if (is_string($values)) {
return $this->_decode($values, $mode);
}

$decrypted = [];
foreach ($values as $name => $value) {
$decrypted[$name] = $this->_decode($value, $mode);
}
return $decrypted;
}

/**
* Decodes and decrypts a single value.
*
* @param string $value The value to decode & decrypt.
* @param string|false $encrypt The encryption cipher to use.
* @return string Decoded value.
*/
protected function _decode($value, $encrypt)
{
if (!$encrypt) {
return $this->_explode($value);
}
$this->_checkCipher($encrypt);
$prefix = 'Q2FrZQ==.';
$value = base64_decode(substr($value, strlen($prefix)));
if ($encrypt === 'rijndael') {
$value = Security::rijndael($value, $this->_config['key'], 'decrypt');
}
if ($encrypt === 'aes') {
$value = Security::decrypt($value, $this->_config['key']);
}
return $this->_explode($value);
}

/**
* Implode method to keep keys are multidimensional arrays
*
* @param array $array Map of key and values
* @return string A json encoded string.
*/
protected function _implode(array $array)
{
return json_encode($array);
}

/**
* Explode method to return array from string set in CookieComponent::_implode()
* Maintains reading backwards compatibility with 1.x CookieComponent::_implode().
*
* @param string $string A string containing JSON encoded data, or a bare string.
* @return array Map of key and values
*/
protected function _explode($string)
{
$first = substr($string, 0, 1);
if ($first === '{' || $first === '[') {
$ret = json_decode($string, true);
return ($ret !== null) ? $ret : $string;
}
$array = [];
foreach (explode(',', $string) as $pair) {
$key = explode('|', $pair);
if (!isset($key[1])) {
return $key[0];
}
$array[$key[0]] = $key[1];
}
return $array;
return $this->_config['key'];
}
}
43 changes: 43 additions & 0 deletions src/TestSuite/IntegrationTestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@
use Cake\Routing\DispatcherFactory;
use Cake\Routing\Router;
use Cake\TestSuite\Stub\Response;
use Cake\Utility\CookieCryptTrait;
use Cake\Utility\Hash;
use Cake\Utility\Security;
use Cake\Utility\Text;
use Cake\View\Helper\SecureFieldTokenTrait;
use Exception;
Expand All @@ -38,6 +40,7 @@
*/
abstract class IntegrationTestCase extends TestCase
{
use CookieCryptTrait;
use SecureFieldTokenTrait;

/**
Expand Down Expand Up @@ -119,6 +122,13 @@ abstract class IntegrationTestCase extends TestCase
*/
protected $_csrfToken = false;

/**
*
*
* @var null|string
*/
protected $_cookieEncriptionKey = null;

/**
* Clears the state used for requests.
*
Expand Down Expand Up @@ -218,6 +228,39 @@ public function cookie($name, $value)
$this->_cookie[$name] = $value;
}

/**
* Returns the encryption key to be used.
*
* @return string
*/
protected function _getCookieEncryptionKey()
{
if (isset($this->_cookieEncriptionKey)) {
return $this->_cookieEncriptionKey;
}
return Security::salt();
}

/**
* Sets a encrypted request cookie for future requests.
*
* The difference from cookie() is this encrypts the cookie
* value like the CookieComponent.
*
* @param string $name The cookie name to use.
* @param mixed $value The value of the cookie.
* @param string|bool $encrypt Encryption mode to use.
* @param string|null $key Encryption key used. Defaults
* to Security.salt.
* @return void
* @see CookieCryptTrait::_encrypt
*/
public function cookieEncrypted($name, $value, $encrypt = 'aes', $key = null)
{
$this->_cookieEncriptionKey = $key;
$this->_cookie[$name] = $this->_encrypt($value, $encrypt);
}

/**
* Performs a GET request using the current request data.
*
Expand Down
Loading

0 comments on commit 5be4156

Please sign in to comment.