Browse files

Merge pull request #16 from dereuromark/master

some fixes
  • Loading branch information...
2 parents a9a4c11 + 4a67122 commit 63c470728a9008f73aeb36bd28297ccf664cd8cf @milesj committed Feb 7, 2012
Showing with 194 additions and 48 deletions.
  1. +38 −45 Controller/Component/AutoLoginComponent.php
  2. +142 −0 Test/Case/Controller/Component/AutoLoginComponentTest.php
  3. +14 −3 readme.md
View
83 Controller/Component/AutoLoginComponent.php
@@ -1,4 +1,6 @@
<?php
+App::uses('Component', 'Controller');
+
/**
* AutoLoginComponent
*
@@ -30,22 +32,6 @@ class AutoLoginComponent extends Component {
public $components = array('Auth', 'Cookie');
/**
- * Cookie name.
- *
- * @access public
- * @var string
- */
- public $cookieName = 'autoLogin';
-
- /**
- * Cookie length (strtotime() format).
- *
- * @access public
- * @var string
- */
- public $expires = '+2 weeks';
-
- /**
* Settings.
*
* @access public
@@ -54,27 +40,25 @@ class AutoLoginComponent extends Component {
public $settings = array();
/**
- * Should we debug?
- *
- * @access protected
- * @var boolean
- */
- protected $_debug = false;
-
- /**
* Default settings.
*
* @access protected
* @var array
*/
protected $_defaults = array(
+ 'active' => true,
'model' => 'User',
'username' => 'username',
'password' => 'password',
'plugin' => '',
'controller' => 'users',
'loginAction' => 'login',
- 'logoutAction' => 'logout'
+ 'logoutAction' => 'logout',
+ 'cookieName' => 'autoLogin',
+ 'expires' => '+2 weeks', # Cookie length (strtotime() format)
+ 'redirect' => true,
+ 'requirePrompt' => true, # Displayed checkbox determines if cookie is created
+ 'debug' => null # Auto-Select based on debug mode or ip range
);
/**
@@ -93,16 +77,18 @@ class AutoLoginComponent extends Component {
* @return void
*/
public function initialize(Controller $controller) {
- $autoLogin = (array) Configure::read('AutoLogin');
-
- $defaults = $autoLogin + $this->_defaults;
- $this->settings = $this->settings + $defaults;
+ $this->settings = array_merge($this->_defaults, (array) Configure::read('AutoLogin'));
// Validate the cookie
- $cookie = $this->Cookie->read($this->cookieName);
+ $cookie = $this->Cookie->read($this->settings['cookieName']);
$user = $this->Auth->user();
+
+ // Is debug enabled
+ if ($this->settings['debug'] === null) {
+ $this->settings['debug'] = Configure::read('debug') > 0 || !empty($autoLogin['email']) && !empty($autoLogin['ips']) && in_array(env('REMOTE_ADDR'), (array) $autoLogin['ips']);
+ }
- if (!empty($user) || $controller->request->is('post')) {
+ if (!$this->settings['active'] || !empty($user) || !$cookie || !$controller->request->is('get')) {
return;
} elseif (!is_array($cookie)) {
@@ -120,9 +106,6 @@ public function initialize(Controller $controller) {
$controller->request->data[$this->settings['model']][$this->settings['username']] = $cookie['username'];
$controller->request->data[$this->settings['model']][$this->settings['password']] = base64_decode($cookie['password']);
- // Is debug enabled
- $this->_debug = (isset($autoLogin['email']) && isset($autoLogin['ips']) && in_array(env('REMOTE_ADDR'), (array) $autoLogin['ips']));
-
// Request is valid, stop startup()
$this->_isValidRequest = true;
}
@@ -147,13 +130,16 @@ public function startup(Controller $controller) {
$this->Auth->user()
));
}
-
+ if ($this->settings['redirect']) {
+ $controller->redirect(array(), 301);
+ }
+
} else {
$this->debug('loginFail', $this->Cookie, $this->Auth->user());
if (in_array('_autoLoginError', get_class_methods($controller))) {
call_user_func_array(array($controller, '_autoLoginError'), array(
- $this->Cookie->read($this->cookieName)
+ $this->Cookie->read($this->settings['cookieName'])
));
}
}
@@ -167,7 +153,10 @@ public function startup(Controller $controller) {
* @param Controller $controller
* @return void
*/
- public function beforeRedirect(Controller $controller) {
+ public function beforeRedirect(Controller $controller, $url, $status = null, $exit = true) {
+ if (empty($this->settings['active'])) {
+ return;
+ }
$model = $this->settings['model'];
if (is_array($this->Auth->loginAction)) {
@@ -198,21 +187,21 @@ public function beforeRedirect(Controller $controller) {
if (isset($data[$model])) {
$username = $data[$model][$this->settings['username']];
$password = $data[$model][$this->settings['password']];
- $autoLogin = isset($data[$model]['auto_login']) ? $data[$model]['auto_login'] : 0;
+ $autoLogin = isset($data[$model]['auto_login']) ? $data[$model]['auto_login'] : !$this->settings['requirePrompt'];
if (!empty($username) && !empty($password) && $autoLogin) {
$this->save($username, $password);
- } else if (!$autoLogin) {
+ } elseif (!$autoLogin) {
$this->delete();
}
}
- break;
+ break;
case $this->settings['logoutAction']:
$this->debug('logout', $this->Cookie, $this->Auth->user());
$this->delete();
- break;
+ break;
}
}
}
@@ -238,7 +227,7 @@ public function save($username, $password) {
$this->Cookie->domain = false;
}
- $this->Cookie->write($this->cookieName, $cookie, true, $this->expires);
+ $this->Cookie->write($this->settings['cookieName'], $cookie, true, $this->settings['expires']);
$this->debug('cookieSet', $cookie, $this->Auth->user());
}
@@ -249,7 +238,7 @@ public function save($username, $password) {
* @return void
*/
public function delete() {
- $this->Cookie->delete($this->cookieName);
+ $this->Cookie->delete($this->settings['cookieName']);
}
/**
@@ -274,7 +263,7 @@ public function debug($key, $cookie = array(), $user = array()) {
'custom' => 'Custom Callback'
);
- if ($this->_debug && isset($scopes[$key])) {
+ if ($this->settings['debug'] && isset($scopes[$key])) {
$debug = (array) Configure::read('AutoLogin');
$content = "";
@@ -291,7 +280,11 @@ public function debug($key, $cookie = array(), $user = array()) {
}
if (empty($debug['scope']) || in_array($key, (array) $debug['scope'])) {
- mail($debug['email'], '[AutoLogin] ' . $scopes[$key], $content, 'From: ' . $debug['email']);
+ if (!empty($debug['email'])) {
+ mail($debug['email'], '[AutoLogin] ' . $scopes[$key], $content, 'From: ' . $debug['email']);
+ } else {
+ $this->log($scopes[$key] . ': ' . $content, 'autologin');
+ }
}
}
}
View
142 Test/Case/Controller/Component/AutoLoginComponentTest.php
@@ -0,0 +1,142 @@
+<?php
+
+App::import('Component', 'AutoLogin');
+App::uses('Controller', 'Controller');
+
+/**
+ * Short description for class.
+ *
+ * @package cake.tests
+ * @subpackage cake.tests.cases.libs.controller.components
+ */
+class AutoLoginComponentTest extends CakeTestCase {
+
+ /**
+ * setUp method
+ *
+ * @access public
+ * @return void
+ */
+ public function setUp() {
+ $this->Controller = new AutoLoginTestController(new CakeRequest, new CakeResponse);
+ $this->Controller->AutoLogin = new AutoLoginComponent(new ComponentCollection());
+ }
+
+ /**
+ * Tear-down method. Resets environment state.
+ *
+ * @access public
+ * @return void
+ */
+ public function tearDown() {
+ unset($this->Controller->AutoLogin);
+ unset($this->Controller);
+ }
+
+ /**
+ * test if suhosin isn't messing up srand() and mt_srand()
+ * run this on every the environment you want AutoLogin to work!
+ * It this test fails add `suhosin.srand.ignore = Off`
+ * in your `/etc/php5/apache2/php.ini`
+ * And don't forget to restart apache or at least `/etc/init.d/apache2 force-reload`
+ */
+ public function testIfRandWillWork() {
+ srand('1234567890');
+ $rand1 = rand(0, 255);
+
+ srand('1234567890');
+ $rand2 = rand(0, 255);
+
+ $this->assertSame($rand1, $rand2, 'You have the Suhosin BUG! Add `suhosin.srand.ignore = Off` to your php.ini!');
+ }
+
+ /**
+ * test merge of configs
+ */
+ public function testConfigs() {
+ $this->Controller->AutoLogin->initialize($this->Controller);
+ $settings = $this->Controller->AutoLogin->settings;
+ $this->assertSame('autoLogin', $settings['cookieName']);
+
+ Configure::write('AutoLogin.cookieName', 'myAutoLogin');
+ $this->Controller->AutoLogin->initialize($this->Controller);
+ $settings = $this->Controller->AutoLogin->settings;
+ $this->assertSame('myAutoLogin', $settings['cookieName']);
+
+ Configure::write('AutoLogin.cookieName', 'myOtherAutoLogin');
+ $this->Controller->AutoLogin->initialize($this->Controller);
+ $settings = $this->Controller->AutoLogin->settings;
+ //debug($settings); die();
+ $this->assertSame('myOtherAutoLogin', $settings['cookieName']);
+ }
+
+}
+
+
+/**
+ * Short description for class.
+ *
+ * @package cake.tests
+ * @subpackage cake.tests.cases.libs.controller.components
+ */
+class AutoLoginTestController extends Controller {
+ /**
+ * name property
+ *
+ * @var string 'SecurityTest'
+ * @access public
+ */
+
+ /**
+ * components property
+ *
+ * @var array
+ * @access public
+ */
+ public $components = array('AutoLogin');
+ /**
+ * failed property
+ *
+ * @var bool false
+ * @access public
+ */
+ public $failed = false;
+ /**
+ * Used for keeping track of headers in test
+ *
+ * @var array
+ * @access public
+ */
+ public $testHeaders = array();
+ /**
+ * fail method
+ *
+ * @access public
+ * @return void
+ */
+ public function fail() {
+ $this->failed = true;
+ }
+ /**
+ * redirect method
+ *
+ * @param mixed $option
+ * @param mixed $code
+ * @param mixed $exit
+ * @access public
+ * @return void
+ */
+ public function redirect($option, $code, $exit) {
+ return $code;
+ }
+ /**
+ * Conveinence method for header()
+ *
+ * @param string $status
+ * @return void
+ * @access public
+ */
+ public function header($status) {
+ $this->testHeaders[] = $status;
+ }
+}
View
17 readme.md
@@ -2,12 +2,12 @@
A CakePHP Component that will automatically login the Auth session for a duration if the user requested to (saves data to cookies).
-This version is only compatible with CakePHP 2.0.
+This version is only compatible with CakePHP 2.x.
## Compatibility ##
* v2.x - CakePHP 1.3
-* v3.x - CakePHP 2.0
+* v3.x - CakePHP 2.x
## Requirements ##
@@ -16,18 +16,24 @@ This version is only compatible with CakePHP 2.0.
## Features ##
* Requires no installation except for adding the checkbox into your user login forms
+* Using `requirePrompt` as `false` does not even require that (but use with caution!)
* Automatically saves the cookie and info when a user logs in
* Automatically kills the cookie and session when a user logs out
* Inserts a hash within the cookie so that it cannot be hijacked
* Encrypts the cookie so the information cannot be harvested
* Configuration options for cookie name and length
* Functionality for additional user updating or error logging
* Minor support for localhost cookies
+* Can be enabled/disabled dynamically using Configure
## Troubleshooting ##
-If you have Suhosin installed alongside PHP, you will run into problems with cookie encryption. It's best to disable Suhosin or implement this workaround. If you find a better solution, please notify me!
+If you have Suhosin installed alongside PHP, you will run into problems with cookie encryption.
+TIP: Use the test case attached to find out if your environment is affected.
+If so, add `suhosin.srand.ignore = Off` in your `/etc/php5/apache2/php.ini`.
+And don't forget to restart apache or at least `/etc/init.d/apache2 force-reload`.
+Details on this bug:
http://milesj.me/blog/read/security-cipher-suhosin
Furthermore, you should define all the redirects, validation and login logic within your login action.
@@ -41,6 +47,11 @@ Furthermore, you should define all the redirects, validation and login logic wit
}
}
+Also note:
+This component should be included before the AuthComponent in order to avoid race conditions and "not logged in" messages triggered.
+
+ public $components = array('Session', 'RequestHandler', 'AutoLogin', 'Auth', ...);
+
## Documentation ##
Thorough documentation can be found here: http://milesj.me/code/cakephp/auto-login

0 comments on commit 63c4707

Please sign in to comment.