diff --git a/lib/Cake/Configure/IniReader.php b/lib/Cake/Configure/IniReader.php index f8fe2077f..900d65489 100644 --- a/lib/Cake/Configure/IniReader.php +++ b/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.')); } - $contents = parse_ini_file($filename, true); + 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'; + } + + 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 { diff --git a/lib/Cake/Test/Case/Configure/IniReaderTest.php b/lib/Cake/Test/Case/Configure/IniReaderTest.php index dd1f087b4..5bbbe57c1 100644 --- a/lib/Cake/Test/Case/Configure/IniReaderTest.php +++ b/lib/Cake/Test/Case/Configure/IniReaderTest.php @@ -56,6 +56,47 @@ class IniReaderTest extends CakeTestCase { * @return void */ public function testConstruct() { + $reader = new IniReader($this->path); + $config = $reader->read('acl.ini'); + + $this->assertTrue(isset($config['admin'])); + $this->assertTrue(isset($config['paul']['groups'])); + $this->assertEquals('ads', $config['admin']['deny']); + } + +/** + * Test reading files. + * + * @return void + */ + 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'); + + $this->assertTrue(isset($config['groups'])); + $this->assertEquals('administrators', $config['groups']); + } + +/** + * Test reading acl.ini.php. + * + * @return void + */ + public function testReadSpecialAclIniPhp() { $reader = new IniReader($this->path); $config = $reader->read('acl.ini.php'); @@ -65,24 +106,11 @@ class IniReaderTest extends CakeTestCase { } /** - * no other sections should exist. + * Test without section. * * @return void */ - public function testReadingOnlyOneSection() { - $reader = new IniReader($this->path, 'admin'); - $config = $reader->read('acl.ini.php'); - - $this->assertTrue(isset($config['groups'])); - $this->assertEquals('administrators', $config['groups']); - } - -/** - * test without section - * - * @return void - */ - public function testReadingWithoutSection() { + 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 dump method. + * 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. * * @return void */ diff --git a/lib/Cake/Test/test_app/Config/acl.ini b/lib/Cake/Test/test_app/Config/acl.ini new file mode 100644 index 000000000..b9215b4ca --- /dev/null +++ b/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 diff --git a/lib/Cake/Test/test_app/Config/empty.ini b/lib/Cake/Test/test_app/Config/empty.ini new file mode 100644 index 000000000..15623bc79 --- /dev/null +++ b/lib/Cake/Test/test_app/Config/empty.ini @@ -0,0 +1 @@ +; do nothing this is an empty file. diff --git a/lib/Cake/Test/test_app/Config/no_ini_extension b/lib/Cake/Test/test_app/Config/no_ini_extension new file mode 100644 index 000000000..12f09f056 --- /dev/null +++ b/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 diff --git a/lib/Cake/Test/test_app/Plugin/TestPlugin/Config/acl.ini.php b/lib/Cake/Test/test_app/Plugin/TestPlugin/Config/acl.ini.php new file mode 100644 index 000000000..b9215b4ca --- /dev/null +++ b/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 diff --git a/lib/Cake/Test/test_app/Plugin/TestPlugin/Config/nested.ini b/lib/Cake/Test/test_app/Plugin/TestPlugin/Config/nested.ini new file mode 100644 index 000000000..1ae279986 --- /dev/null +++ b/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