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