Permalink
Browse files

WARING BC Break! Auth: adding 'persist' option to scope database fiel…

…ds. This breaks BC if you previously stored a field with the name 'password' in the session. Implements #458
  • Loading branch information...
1 parent da99ce2 commit 9eec276bb4d9b8a0c19b8b4aa84d3533daa95a83 @daschl daschl committed Jun 3, 2012
Showing with 91 additions and 3 deletions.
  1. +40 −2 security/Auth.php
  2. +51 −1 tests/cases/security/AuthTest.php
View
@@ -24,6 +24,22 @@
* the data returned from the credential check is written to the session, which is automatically
* accessed on subsequent checks (though manual re-checking can be forced on a per-instance basis).
*
+ * To be secure by default (and if you don't override it), a `password` field is never stored in
+ * the session adapter. This prevents a possible password hash to be leaked in a cookie (for
+ * example). You can also be very specific on what you want to store in the session:
+ *
+ * {{{
+ * Auth::config(array(
+ * 'default' => array(
+ * 'session' => array(
+ * 'persist' => array('username', 'email')
+ * )
+ * )
+ * ));
+ * }}}
+ *
+ * You can also pass an optional `persist` param to the `check` method to override this default.
+ *
* For additional information on configuring and working with `Auth`, see the `Form` adapter.
*
* @see lithium\security\auth\adapter\Form
@@ -69,7 +85,8 @@ protected static function _initConfig($name, $config) {
$defaults = array('session' => array(
'key' => $name,
'class' => static::$_classes['session'],
- 'options' => array()
+ 'options' => array(),
+ 'persist' => array()
));
$config = parent::_initConfig($name, $config) + $defaults;
$config['session'] += $defaults['session'];
@@ -99,19 +116,31 @@ protected static function _initConfig($name, $config) {
* - `'writeSession'` _boolean_: Upon a successful credentials check, the returned
* user information is, by default, written to the session. Set this to `false`
* to disable session writing for this authentication check.
+ * - `'persist'` _array_: A list of fields that should be stored in the session.
* @return array After a successful credential check against the adapter (or a successful
* lookup against the current session), returns an array of user information from the
* storage backend used by the configured adapter.
* @filter
*/
public static function check($name, $credentials = null, array $options = array()) {
- $defaults = array('checkSession' => true, 'writeSession' => true);
+ $config = static::config($name);
+ $defaults = array(
+ 'checkSession' => true,
+ 'writeSession' => true,
+ 'persist' => static::_config('persist')
+ );
+
+ if(!empty($config['session']['persist'])) {
+ $defaults['persist'] = $config['session']['persist'];
+ }
+
$options += $defaults;
$params = compact('name', 'credentials', 'options');
return static::_filter(__FUNCTION__, $params, function($self, $params) {
extract($params);
$config = $self::invokeMethod('_config', array($name));
+ $persist = $options['persist'];
if ($config === null) {
throw new ConfigException("Configuration `{$name}` has not been defined.");
@@ -125,6 +154,15 @@ public static function check($name, $credentials = null, array $options = array(
}
if (($credentials) && $data = $self::adapter($name)->check($credentials, $options)) {
+ if(empty($options['persist'])) {
+ unset($data['password']);
+ } else {
+ foreach($data as $key => $value) {
+ if(!in_array($key, $options['persist'])) {
+ unset($data[$key]);
+ }
+ }
+ }
return ($options['writeSession']) ? $self::set($name, $data) : $data;
}
return false;
@@ -13,14 +13,18 @@
class AuthTest extends \lithium\test\Unit {
+ protected $_classes = array(
+ 'mockAuthAdapter' => 'lithium\tests\mocks\security\auth\adapter\MockAuthAdapter'
+ );
+
public function setUp() {
Session::config(array(
'test' => array('adapter' => 'Memory')
));
Auth::config(array(
'test' => array(
- 'adapter' => 'lithium\tests\mocks\security\auth\adapter\MockAuthAdapter'
+ 'adapter' => $this->_classes['mockAuthAdapter']
)
));
}
@@ -76,6 +80,52 @@ public function testNoConfigurations() {
$this->expectException("Configuration `user` has not been defined.");
Auth::check('user');
}
+
+ public function testAuthPersist() {
+ Auth::reset();
+
+ Auth::config(array(
+ 'test' => array(
+ 'adapter' => $this->_classes['mockAuthAdapter'],
+ )
+ ));
+
+ $config = Auth::config();
+ $this->assertTrue(isset($config['test']['session']['persist']));
+ $this->assertTrue(empty($config['test']['session']['persist']));
+
+ $user = array('username' => 'foo', 'password' => 'bar');
+ $result = Auth::check('test', $user, array('success' => true));
+ $this->assertTrue(isset($result['username']));
+ $this->assertFalse(isset($result['password']));
+
+ Auth::reset();
+
+ Auth::config(array(
+ 'test' => array(
+ 'adapter' => $this->_classes['mockAuthAdapter'],
+ 'session' => array(
+ 'persist' => array('username', 'email')
+ )
+ )
+ ));
+
+ $user = array(
+ 'username' => 'foobar',
+ 'password' => 'not!important',
+ 'email' => 'foo@bar.com',
+ 'insuranceNumer' => 1234567
+ );
+
+ $expected = array(
+ 'username' => 'foobar',
+ 'email' => 'foo@bar.com'
+ );
+
+ $result = Auth::check('test', $user, array('success' => true, 'checkSession' => false));
+ $this->assertEqual($expected, $result);
+ $this->assertEqual($expected, Session::read('test'));
+ }
}
?>

0 comments on commit 9eec276

Please sign in to comment.