Browse Source

Add plugin support to IniReader, and harden tests.
The actual config file must always have .ini extension.
For backwards compatibility, acl.ini.php will be treated specially until 3.0.

Ber Clausen 13 years ago
parent
commit
bdac857293

+ 31 - 10
lib/Cake/Configure/IniReader.php

@@ -84,20 +84,41 @@ class IniReader implements ConfigReaderInterface {
 /**
  * Read an ini file and return the results as an array.
  *
- * @param string $file Name of the file to read. The chosen file
- *    must be on the reader's path.
- * @return array
+ * For backwards compatibility, acl.ini.php will be treated specially until 3.0.
+ *
+ * @param string $key The identifier to read from. If the key has a . it will be treated
+ *  as a plugin prefix. The chosen file must be on the reader's path.
+ * @return array Parsed configuration values.
+ * @throws ConfigureException when files don't exist.
+ *  Or when files contain '..' as this could lead to abusive reads.
  * @throws ConfigureException
  */
-	public function read($file) {
-		$filename = $this->_path . $file;
-		if (!file_exists($filename)) {
-			$filename .= '.ini';
-			if (!file_exists($filename)) {
-				throw new ConfigureException(__d('cake_dev', 'Could not load configuration files: %s or %s', substr($filename, 0, -4), $filename));
+	public function read($key) {
+		if (strpos($key, '..') !== false) {
+			throw new ConfigureException(__d('cake_dev', 'Cannot load configuration files with ../ in them.'));
+		}
+		if (substr($key, -8) === '.ini.php') {
+			$key = substr($key, 0, -8);
+			list($plugin, $key) = pluginSplit($key);
+			$key .= '.ini.php';
+		} else {
+			if (substr($key, -4) === '.ini') {
+				$key = substr($key, 0, -4);
 			}
+			list($plugin, $key) = pluginSplit($key);
+			$key .= '.ini';
 		}
-		$contents = parse_ini_file($filename, true);
+
+		if ($plugin) {
+			$file = App::pluginPath($plugin) . 'Config' . DS . $key;
+		} else {
+			$file = $this->_path . $key;
+		}
+		if (!is_file($file)) {
+			throw new ConfigureException(__d('cake_dev', 'Could not load configuration file: %s', $file));
+		}
+
+		$contents = parse_ini_file($file, true);
 		if (!empty($this->_section) && isset($contents[$this->_section])) {
 			$values = $this->_parseNestedValues($contents[$this->_section]);
 		} else {

+ 117 - 14
lib/Cake/Test/Case/Configure/IniReaderTest.php

@@ -57,7 +57,7 @@ class IniReaderTest extends CakeTestCase {
  */
 	public function testConstruct() {
 		$reader = new IniReader($this->path);
-		$config = $reader->read('acl.ini.php');
+		$config = $reader->read('acl.ini');
 
 		$this->assertTrue(isset($config['admin']));
 		$this->assertTrue(isset($config['paul']['groups']));
@@ -65,24 +65,52 @@ class IniReaderTest extends CakeTestCase {
 	}
 
 /**
- * no other sections should exist.
+ * Test reading files.
  *
  * @return void
  */
-	public function testReadingOnlyOneSection() {
+	public function testRead() {
+		$reader = new IniReader($this->path);
+		$config = $reader->read('nested');
+		$this->assertTrue($config['bools']['test_on']);
+
+		$config = $reader->read('nested.ini');
+		$this->assertTrue($config['bools']['test_on']);
+	}
+
+/**
+ * No other sections should exist.
+ *
+ * @return void
+ */
+	public function testReadOnlyOneSection() {
 		$reader = new IniReader($this->path, 'admin');
-		$config = $reader->read('acl.ini.php');
+		$config = $reader->read('acl.ini');
 
 		$this->assertTrue(isset($config['groups']));
 		$this->assertEquals('administrators', $config['groups']);
 	}
 
 /**
- * test without section
+ * Test reading acl.ini.php.
  *
  * @return void
  */
-	public function testReadingWithoutSection() {
+	public function testReadSpecialAclIniPhp() {
+		$reader = new IniReader($this->path);
+		$config = $reader->read('acl.ini.php');
+
+		$this->assertTrue(isset($config['admin']));
+		$this->assertTrue(isset($config['paul']['groups']));
+		$this->assertEquals('ads', $config['admin']['deny']);
+	}
+
+/**
+ * Test without section.
+ *
+ * @return void
+ */
+	public function testReadWithoutSection() {
 		$reader = new IniReader($this->path);
 		$config = $reader->read('no_section.ini');
 
@@ -94,11 +122,11 @@ class IniReaderTest extends CakeTestCase {
 	}
 
 /**
- * test that names with .'s get exploded into arrays.
+ * Test that names with .'s get exploded into arrays.
  *
  * @return void
  */
-	public function testReadingValuesWithDots() {
+	public function testReadValuesWithDots() {
 		$reader = new IniReader($this->path);
 		$config = $reader->read('nested.ini');
 
@@ -110,7 +138,7 @@ class IniReaderTest extends CakeTestCase {
 	}
 
 /**
- * test boolean reading
+ * Test boolean reading.
  *
  * @return void
  */
@@ -131,18 +159,93 @@ class IniReaderTest extends CakeTestCase {
 	}
 
 /**
- * test read file without extension
+ * Test an exception is thrown by reading files that exist without .ini extension.
  *
+ * @expectedException ConfigureException
  * @return void
  */
-	public function testReadingWithoutExtension() {
+	public function testReadWithExistentFileWithoutExtension() {
 		$reader = new IniReader($this->path);
-		$config = $reader->read('nested');
-		$this->assertTrue($config['bools']['test_on']);
+		$reader->read('no_ini_extension');
+	}
+
+/**
+ * Test an exception is thrown by reading files that don't exist.
+ *
+ * @expectedException ConfigureException
+ * @return void
+ */
+	public function testReadWithNonExistentFile() {
+		$reader = new IniReader($this->path);
+		$reader->read('fake_values');
+	}
+
+/**
+ * Test reading an empty file.
+ *
+ * @return void
+ */
+	public function testReadEmptyFile() {
+		$reader = new IniReader($this->path);
+		$config = $reader->read('empty');
+		$this->assertEquals(array(), $config);
+	}
+
+/**
+ * Test reading keys with ../ doesn't work.
+ *
+ * @expectedException ConfigureException
+ * @return void
+ */
+	public function testReadWithDots() {
+		$reader = new IniReader($this->path);
+		$reader->read('../empty');
+	}
+
+/**
+ * Test reading from plugins.
+ *
+ * @return void
+ */
+	public function testReadPluginValue() {
+		App::build(array(
+			'Plugin' => array(CAKE . 'Test' . DS . 'test_app' . DS . 'Plugin' . DS)
+		), App::RESET);
+		CakePlugin::load('TestPlugin');
+		$reader = new IniReader($this->path);
+		$result = $reader->read('TestPlugin.nested');
+
+		$this->assertTrue(isset($result['database']['db']['username']));
+		$this->assertEquals('bar', $result['database']['db']['username']);
+		$this->assertFalse(isset($result['database.db.username']));
+		$this->assertFalse(isset($result['database']['db.username']));
+
+		$result = $reader->read('TestPlugin.nested.ini');
+		$this->assertEquals('foo', $result['database']['db']['password']);
+		CakePlugin::unload();
+	}
+
+/**
+ * Test reading acl.ini.php from plugins.
+ *
+ * @return void
+ */
+	public function testReadPluginSpecialAclIniPhpValue() {
+		App::build(array(
+			'Plugin' => array(CAKE . 'Test' . DS . 'test_app' . DS . 'Plugin' . DS)
+		), App::RESET);
+		CakePlugin::load('TestPlugin');
+		$reader = new IniReader($this->path);
+		$result = $reader->read('TestPlugin.acl.ini.php');
+
+		$this->assertTrue(isset($result['admin']));
+		$this->assertTrue(isset($result['paul']['groups']));
+		$this->assertEquals('ads', $result['admin']['deny']);
+		CakePlugin::unload();
 	}
 
 /**
- * test dump method.
+ * Test dump method.
  *
  * @return void
  */

+ 60 - 0
lib/Cake/Test/test_app/Config/acl.ini

@@ -0,0 +1,60 @@
+;<?php exit() ?>
+; SVN FILE: $Id$
+;/**
+; * Test App Ini Based Acl Config File
+; *
+; *
+; * PHP 5
+; *
+; * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+; * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+; *
+; *  Licensed under The MIT License
+; *  Redistributions of files must retain the above copyright notice.
+; *
+; * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+; * @link          http://cakephp.org CakePHP(tm) Project
+;; * @package       Cake.Test.test_app.Config
+; * @since         CakePHP(tm) v 0.10.0.1076
+; * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+; */
+
+;-------------------------------------
+;Users
+;-------------------------------------
+
+[admin]
+groups = administrators
+allow =
+deny = ads
+
+[paul]
+groups = users
+allow =
+deny =
+
+[jenny]
+groups = users
+allow = ads
+deny = images, files
+
+[nobody]
+groups = anonymous
+allow =
+deny =
+
+;-------------------------------------
+;Groups
+;-------------------------------------
+
+[administrators]
+deny =
+allow = posts, comments, images, files, stats, ads
+
+[users]
+allow = posts, comments, images, files
+deny = stats, ads
+
+[anonymous]
+allow =
+deny = posts, comments, images, files, stats, ads

+ 1 - 0
lib/Cake/Test/test_app/Config/empty.ini

@@ -0,0 +1 @@
+; do nothing this is an empty file.

+ 3 - 0
lib/Cake/Test/test_app/Config/no_ini_extension

@@ -0,0 +1,3 @@
+; Test file for testing config file without .ini extension.
+some_key = some_value
+bool_key = 1

+ 60 - 0
lib/Cake/Test/test_app/Plugin/TestPlugin/Config/acl.ini.php

@@ -0,0 +1,60 @@
+;<?php exit() ?>
+; SVN FILE: $Id$
+;/**
+; * Test App Ini Based Acl Config File
+; *
+; *
+; * PHP 5
+; *
+; * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+; * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+; *
+; *  Licensed under The MIT License
+; *  Redistributions of files must retain the above copyright notice.
+; *
+; * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+; * @link          http://cakephp.org CakePHP(tm) Project
+;; * @package       Cake.Test.test_app.Config
+; * @since         CakePHP(tm) v 0.10.0.1076
+; * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+; */
+
+;-------------------------------------
+;Users
+;-------------------------------------
+
+[admin]
+groups = administrators
+allow =
+deny = ads
+
+[paul]
+groups = users
+allow =
+deny =
+
+[jenny]
+groups = users
+allow = ads
+deny = images, files
+
+[nobody]
+groups = anonymous
+allow =
+deny =
+
+;-------------------------------------
+;Groups
+;-------------------------------------
+
+[administrators]
+deny =
+allow = posts, comments, images, files, stats, ads
+
+[users]
+allow = posts, comments, images, files
+deny = stats, ads
+
+[anonymous]
+allow =
+deny = posts, comments, images, files, stats, ads

+ 4 - 0
lib/Cake/Test/test_app/Plugin/TestPlugin/Config/nested.ini

@@ -0,0 +1,4 @@
+; Test file for testing ini files with . syntax
+[database]
+db.username = bar
+db.password = foo