Browse Source

Fix writing mixed encryption cookies in one write() call.

With the various parameters removed, having the correct configuration
for each top level cookie key is important.
mark_story 11 years ago
parent
commit
c79533cd3f

+ 31 - 20
src/Controller/Component/CookieComponent.php

@@ -179,10 +179,16 @@ class CookieComponent extends Component {
 			$key = array($key => $value);
 		}
 
+		$keys = [];
 		foreach ($key as $name => $value) {
 			$this->_values = Hash::insert($this->_values, $name, $value);
+			$parts = explode('.', $name);
+			$keys[] = $parts[0];
+		}
+
+		foreach ($keys as $name) {
+			$this->_write($name, $this->_values[$name]);
 		}
-		// TODO write cookie data out.
 	}
 
 /**
@@ -212,7 +218,8 @@ class CookieComponent extends Component {
  */
 	protected function _load() {
 		foreach ($this->_request->cookies as $name => $value) {
-			$this->_values[$name] = $this->_decrypt($value);
+			$config = $this->configKey($name);
+			$this->_values[$name] = $this->_decrypt($value, $config['encryption']);
 		}
 	}
 
@@ -325,12 +332,12 @@ class CookieComponent extends Component {
  * @return void
  */
 	protected function _write($name, $value) {
-		$config = $this->config();
+		$config = $this->configKey($name);
 		$expires = new \DateTime($config['expires']);
 
 		$this->_response->cookie(array(
 			'name' => $name,
-			'value' => $this->_encrypt($value),
+			'value' => $this->_encrypt($value, $config['encryption']),
 			'expire' => $expires->format('U'),
 			'path' => $config['path'],
 			'domain' => $config['domain'],
@@ -346,7 +353,7 @@ class CookieComponent extends Component {
  * @return void
  */
 	protected function _delete($name) {
-		$config = $this->config();
+		$config = $this->configKey($name);
 		$expires = new \DateTime($config['expires']);
 
 		$this->_response->cookie(array(
@@ -364,20 +371,22 @@ class CookieComponent extends Component {
  * Encrypts $value using public $type method in Security class
  *
  * @param string $value Value to encrypt
+ * @param string|bool $encrypt Encryption mode to use. False
+ *   disabled encryption.
  * @return string Encoded values
  */
-	protected function _encrypt($value) {
+	protected function _encrypt($value, $encrypt) {
 		if (is_array($value)) {
 			$value = $this->_implode($value);
 		}
-		if (!$this->_encrypted) {
+		if (!$encrypt) {
 			return $value;
 		}
 		$prefix = "Q2FrZQ==.";
-		if ($this->_config['encryption'] === 'rijndael') {
+		if ($encrypt === 'rijndael') {
 			$cipher = Security::rijndael($value, $this->_config['key'], 'encrypt');
 		}
-		if ($this->_config['encryption'] === 'aes') {
+		if ($encrypt === 'aes') {
 			$cipher = Security::encrypt($value, $this->_config['key']);
 		}
 		return $prefix . base64_encode($cipher);
@@ -387,21 +396,22 @@ class CookieComponent extends Component {
  * 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) {
+	protected function _decrypt($values, $mode) {
 		if (is_string($values)) {
-			return $this->_decode($values);
+			return $this->_decode($values, $mode);
 		}
 
 		$decrypted = array();
 		foreach ($values as $name => $value) {
 			if (is_array($value)) {
 				foreach ($value as $key => $val) {
-					$decrypted[$name][$key] = $this->_decode($val);
+					$decrypted[$name][$key] = $this->_decode($val, $mode);
 				}
 			} else {
-				$decrypted[$name] = $this->_decode($value);
+				$decrypted[$name] = $this->_decode($value, $mode);
 			}
 		}
 		return $decrypted;
@@ -411,22 +421,23 @@ class CookieComponent extends Component {
  * Decodes and decrypts a single value.
  *
  * @param string $value The value to decode & decrypt.
+ * @param string|false $encryption The encryption cipher to use.
  * @return string Decoded value.
  */
-	protected function _decode($value) {
+	protected function _decode($value, $encryption) {
 		$prefix = 'Q2FrZQ==.';
 		$pos = strpos($value, $prefix);
-		if ($pos === false) {
+		if (!$encryption) {
 			return $this->_explode($value);
 		}
 		$value = base64_decode(substr($value, strlen($prefix)));
-		if ($this->_config['encryption'] === 'rijndael') {
-			$plain = Security::rijndael($value, $this->_config['key'], 'decrypt');
+		if ($encryption === 'rijndael') {
+			$value = Security::rijndael($value, $this->_config['key'], 'decrypt');
 		}
-		if ($this->_config['encryption'] === 'aes') {
-			$plain = Security::decrypt($value, $this->_config['key']);
+		if ($encryption === 'aes') {
+			$value = Security::decrypt($value, $this->_config['key']);
 		}
-		return $this->_explode($plain);
+		return $this->_explode($value);
 	}
 
 /**

+ 47 - 39
tests/TestCase/Controller/Component/CookieComponentTest.php

@@ -51,7 +51,8 @@ class CookieComponentTest extends TestCase {
 			'path' => '/',
 			'domain' => '',
 			'secure' => false,
-			'key' => 'somerandomhaskeysomerandomhaskey'
+			'key' => 'somerandomhaskeysomerandomhaskey',
+			'encryption' => false,
 		]);
 	}
 
@@ -70,7 +71,7 @@ class CookieComponentTest extends TestCase {
 			'key' => 'somerandomhaskeysomerandomhaskey',
 			'secure' => false,
 			'httpOnly' => false,
-			'encryption' => 'aes',
+			'encryption' => false,
 		];
 		$this->assertEquals($expected, $result);
 	}
@@ -93,7 +94,7 @@ class CookieComponentTest extends TestCase {
 			'key' => 'somerandomhaskeysomerandomhaskey',
 			'secure' => false,
 			'httpOnly' => false,
-			'encryption' => 'aes',
+			'encryption' => false,
 		];
 		$this->assertEquals($expected, $result);
 	}
@@ -136,7 +137,6 @@ class CookieComponentTest extends TestCase {
  * @return void
  */
 	public function testReadEncryptedCookieData() {
-		$this->markTestIncomplete();
 		$this->_setCookieData();
 		$data = $this->Cookie->read('Encrytped_array');
 		$expected = array('name' => 'CakePHP', 'version' => '1.2.0.x', 'tag' => 'CakePHP Rocks!');
@@ -153,7 +153,6 @@ class CookieComponentTest extends TestCase {
  * @return void
  */
 	public function testReadPlainCookieData() {
-		$this->markTestIncomplete();
 		$this->_setCookieData();
 		$data = $this->Cookie->read('Plain_array');
 		$expected = array('name' => 'CakePHP', 'version' => '1.2.0.x', 'tag' => 'CakePHP Rocks!');
@@ -189,7 +188,6 @@ class CookieComponentTest extends TestCase {
  * @return void
  */
 	public function testWriteSimple() {
-		$this->markTestIncomplete();
 		$this->Cookie->write('Testing', 'value');
 		$result = $this->Cookie->read('Testing');
 
@@ -202,7 +200,6 @@ class CookieComponentTest extends TestCase {
  * @return void
  */
 	public function testWriteWithFalseyValue() {
-		$this->markTestIncomplete();
 		$this->Cookie->encryption('aes');
 		$this->Cookie->key = 'qSI232qs*&sXOw!adre@34SAv!@*(XSL#$%)asGb$@11~_+!@#HKis~#^';
 
@@ -232,43 +229,24 @@ class CookieComponentTest extends TestCase {
 	}
 
 /**
- * test that two write() calls use the expiry.
- *
- * @return void
- */
-	public function testWriteMultipleShareExpiry() {
-		$this->markTestIncomplete();
-		$this->Cookie->write('key1', 'value1', false);
-		$this->Cookie->write('key2', 'value2', false);
-
-		$name = $this->Cookie->config('name') . '[key1]';
-		$result = $this->Controller->response->cookie($name);
-		$this->assertWithinMargin(time() + 10, $result['expire'], 2, 'Expiry time is wrong');
-
-		$name = $this->Cookie->config('name') . '[key2]';
-		$result = $this->Controller->response->cookie($name);
-		$this->assertWithinMargin(time() + 10, $result['expire'], 2, 'Expiry time is wrong');
-	}
-
-/**
  * test write with distant future cookies
  *
  * @return void
  */
 	public function testWriteFarFuture() {
-		$this->markTestIncomplete();
-		$this->Cookie->write('Testing', 'value', false, '+90 years');
+		$this->Cookie->configKey('Testing', 'expires', '+90 years');
+		$this->Cookie->write('Testing', 'value');
 		$future = new \DateTime('now');
 		$future->modify('+90 years');
 
 		$expected = array(
-			'name' => $this->Cookie->config('name') . '[Testing]',
+			'name' => 'Testing',
 			'value' => 'value',
 			'path' => '/',
 			'domain' => '',
 			'secure' => false,
 			'httpOnly' => false);
-		$result = $this->Controller->response->cookie($this->Cookie->config('name') . '[Testing]');
+		$result = $this->Controller->response->cookie('Testing');
 
 		$this->assertEquals($future->format('U'), $result['expire'], '', 3);
 		unset($result['expire']);
@@ -282,25 +260,52 @@ class CookieComponentTest extends TestCase {
  * @return void
  */
 	public function testWriteHttpOnly() {
-		$this->markTestIncomplete();
 		$this->Cookie->config([
 			'httpOnly' => true,
 			'secure' => false
 		]);
 		$this->Cookie->write('Testing', 'value', false);
 		$expected = array(
-			'name' => $this->Cookie->config('name') . '[Testing]',
+			'name' => 'Testing',
 			'value' => 'value',
 			'expire' => time() + 10,
 			'path' => '/',
 			'domain' => '',
 			'secure' => false,
 			'httpOnly' => true);
-		$result = $this->Controller->response->cookie($this->Cookie->config('name') . '[Testing]');
+		$result = $this->Controller->response->cookie('Testing');
 		$this->assertEquals($expected, $result);
 	}
 
 /**
+ * Test writing multiple nested keys when some are encrypted.
+ *
+ * @return void
+ */
+	public function testWriteMulitMixedEncryption() {
+		$this->Cookie->configKey('Open', 'encryption', false);
+		$this->Cookie->configKey('Closed', 'encryption', 'aes');
+		$this->Cookie->write([
+			'Closed.key' => 'secret',
+			'Open.key' => 'not secret',
+		]);
+		$expected = array(
+			'name' => 'Open',
+			'value' => '{"key":"not secret"}',
+			'path' => '/',
+			'domain' => '',
+			'secure' => false,
+			'httpOnly' => false
+		);
+		$result = $this->Controller->response->cookie('Open');
+		unset($result['expire']);
+		$this->assertEquals($expected, $result);
+
+		$result = $this->Controller->response->cookie('Closed');
+		$this->assertContains('Q2FrZQ==.', $result['value']);
+	}
+
+/**
  * test delete with httpOnly
  *
  * @return void
@@ -522,10 +527,13 @@ class CookieComponentTest extends TestCase {
  * @return void
  */
 	public function testReadingDataFromRequest() {
-		$data = $this->Cookie->read('Encrytped_array');
+		$this->Cookie->configKey('Encrypted_array', 'encryption', 'aes');
+		$this->Cookie->configKey('Encrypted_multi_cookies', 'encryption', 'aes');
+
+		$data = $this->Cookie->read('Encrypted_array');
 		$this->assertNull($data);
 
-		$data = $this->Cookie->read('Encrytped_multi_cookies');
+		$data = $this->Cookie->read('Encrypted_multi_cookies');
 		$this->assertNull($data);
 
 		$data = $this->Cookie->read('Plain_array');
@@ -535,8 +543,8 @@ class CookieComponentTest extends TestCase {
 		$this->assertNull($data);
 
 		$this->request->cookies = array(
-			'Encrytped_array' => $this->_encrypt(array('name' => 'CakePHP', 'version' => '1.2.0.x', 'tag' => 'CakePHP Rocks!')),
-			'Encrytped_multi_cookies' => array(
+			'Encrypted_array' => $this->_encrypt(array('name' => 'CakePHP', 'version' => '1.2.0.x', 'tag' => 'CakePHP Rocks!')),
+			'Encrypted_multi_cookies' => array(
 				'name' => $this->_encrypt('CakePHP'),
 				'version' => $this->_encrypt('1.2.0.x'),
 				'tag' => $this->_encrypt('CakePHP Rocks!')
@@ -549,11 +557,11 @@ class CookieComponentTest extends TestCase {
 			)
 		);
 
-		$data = $this->Cookie->read('Encrytped_array');
+		$data = $this->Cookie->read('Encrypted_array');
 		$expected = array('name' => 'CakePHP', 'version' => '1.2.0.x', 'tag' => 'CakePHP Rocks!');
 		$this->assertEquals($expected, $data);
 
-		$data = $this->Cookie->read('Encrytped_multi_cookies');
+		$data = $this->Cookie->read('Encrypted_multi_cookies');
 		$expected = array('name' => 'CakePHP', 'version' => '1.2.0.x', 'tag' => 'CakePHP Rocks!');
 		$this->assertEquals($expected, $data);