From cb62a8b25b67f4c23148efe5d9e93278651d1901 Mon Sep 17 00:00:00 2001
From: xue <>
Date: Wed, 26 Sep 2007 13:15:56 +0000
Subject: added support to remember login.
---
HISTORY | 1 +
UPGRADE | 7 +-
.../quickstart/protected/pages/Advanced/Auth.page | 42 ++++++++++++
.../pages/GettingStarted/NewFeatures.page | 1 +
framework/Security/IUserManager.php | 13 ++++
framework/Security/TAuthManager.php | 79 ++++++++++++++++++++--
framework/Security/TDbUserManager.php | 66 ++++++++++++++++++
framework/Security/TUserManager.php | 37 ++++++++++
framework/TApplication.php | 4 +-
framework/Web/THttpRequest.php | 2 +-
10 files changed, 241 insertions(+), 11 deletions(-)
diff --git a/HISTORY b/HISTORY
index c2dd9b6a..28eace7c 100644
--- a/HISTORY
+++ b/HISTORY
@@ -21,6 +21,7 @@ ENH: Ticket#681 - TUrlMapping can construct custom URLs now (Qiang)
ENH: Ticket#692,700 - Added support to wildcard in pages configuration; added IP filters in auth rules. (Qiang)
ENH: Added THead requirement check (Qiang)
ENH: Added TOutputCache.CacheTime (Qiang)
+ENH: Added "remember login" support to TAuthManager and the related modules/classes (Qiang)
CHG: Ticket#685 - Slashes and backslashes mixup in PradoBase (Qiang)
CHG: Ticket#705 - TImage will not set border style by default (Qiang)
CHG: GeSHi is replaced with Text_Highlighter (Christophe)
diff --git a/UPGRADE b/UPGRADE
index 77b136e8..6b609eea 100644
--- a/UPGRADE
+++ b/UPGRADE
@@ -11,10 +11,15 @@ for both A and B.
Upgrading from v3.1.0
---------------------
-- IFeedContentProvider adds a new method getContentType(). This affects any
+- IFeedContentProvider adds a new method: getContentType(). This affects any
class implementing this interface.
- TUrlMapping now only uses the PATH_INFO part of URL for matching, and the matching
is for the whole PATH_INFO.
+- IUserManager adds two new methods: getUserFromCookie() and saveUserToCookie().
+ This affects classes that implements this interface and does not extend from
+ TUserManager.
+- The order of application lifecycles is changed. The loadState and loadStateComplete
+ are moved to right after onBeginRequest.
Upgrading from v3.1b
--------------------
diff --git a/demos/quickstart/protected/pages/Advanced/Auth.page b/demos/quickstart/protected/pages/Advanced/Auth.page
index 531b8bdf..e0cbcaef 100644
--- a/demos/quickstart/protected/pages/Advanced/Auth.page
+++ b/demos/quickstart/protected/pages/Advanced/Auth.page
@@ -124,5 +124,47 @@ In the above, UserClass specifies what class will be used to create use
The user class has to implement the two abstract methods in TDbUser: validateUser() and createUser(). Since user account information is stored in a database, the user class may make use of its DbConnection property to reach the database.
+
+
+Since 3.1.1, TAuthManager provides support to allow remembering login. Accordingly, TDbUser adds two methods to facilitate the implementation of this feature. In particular, two new methods are introduced: createUserFromCookie() and saveUserToCookie(). Developers should implement these two methods if remembering login is needed. Below is a sample implementation:
+
+
+public function createUserFromCookie($cookie)
+{
+ if(($data=$cookie->Value)!=='')
+ {
+ $application=Prado::getApplication();
+ if(($data=$application->SecurityManager->validateData($data))!==false)
+ {
+ $data=unserialize($data);
+ if(is_array($data) && count($data)===3)
+ {
+ list($username,$address,$token)=$data;
+ $sql='SELECT passcode FROM user WHERE LOWER(username)=:username';
+ $command=$this->DbConnection->createCommand($sql);
+ $command->bindValue(':username',strtolower($username));
+ if($token===$command->queryScalar() && $token!==false && $address=$application->Request->UserHostAddress)
+ return $this->createUser($username);
+ }
+ }
+ }
+ return null;
+}
+
+public function saveUserToCookie($cookie)
+{
+ $application=Prado::getApplication();
+ $username=strtolower($this->Name);
+ $address=$application->Request->UserHostAddress;
+ $sql='SELECT passcode FROM user WHERE LOWER(username)=:username';
+ $command=$this->DbConnection->createCommand($sql);
+ $command->bindValue(':username',strtolower($username));
+ $token=$command->queryScalar();
+ $data=array($username,$address,$token);
+ $data=serialize($data);
+ $data=$application->SecurityManager->hashData($data);
+ $cookie->setValue($data);
+}
+
$Id$
\ No newline at end of file
diff --git a/demos/quickstart/protected/pages/GettingStarted/NewFeatures.page b/demos/quickstart/protected/pages/GettingStarted/NewFeatures.page
index adc19fc7..faf3b9b1 100644
--- a/demos/quickstart/protected/pages/GettingStarted/NewFeatures.page
+++ b/demos/quickstart/protected/pages/GettingStarted/NewFeatures.page
@@ -17,6 +17,7 @@ This page summarizes the main new features that are introduced in each PRADO rel
Added support to TDataGrid to allow grouping consecutive cells with the same content.
Added support to allow configuring page properties and authorization rules using relative page paths in application and page configurations. Added support to allow authorization based on remote host address.
Added a new page state persister TCachePageStatePersister. It allows page state to be stored using a cache module (e.g. TMemCache, TDbCache, etc.)
+Added support to the auth framework to allow remembering login.
Version 3.1.0
diff --git a/framework/Security/IUserManager.php b/framework/Security/IUserManager.php
index 649c41b6..d90450a9 100644
--- a/framework/Security/IUserManager.php
+++ b/framework/Security/IUserManager.php
@@ -34,6 +34,19 @@ interface IUserManager
* @return TUser the user instance, null if the specified username is not in the user database.
*/
public function getUser($username=null);
+ /**
+ * Returns a user instance according to auth data stored in a cookie.
+ * @param THttpCookie the cookie storing user authentication information
+ * @return TUser the user instance generated based on the cookie auth data, null if the cookie does not have valid auth data.
+ * @since 3.1.1
+ */
+ public function getUserFromCookie($cookie);
+ /**
+ * Saves user auth data into a cookie.
+ * @param THttpCookie the cookie to receive the user auth data.
+ * @since 3.1.1
+ */
+ public function saveUserToCookie($cookie);
/**
* Validates if the username and password are correct.
* @param string user name
diff --git a/framework/Security/TAuthManager.php b/framework/Security/TAuthManager.php
index fcbb64d2..7a6b96a1 100644
--- a/framework/Security/TAuthManager.php
+++ b/framework/Security/TAuthManager.php
@@ -60,6 +60,14 @@ class TAuthManager extends TModule
* @var string the session var name for storing return URL
*/
private $_returnUrlVarName;
+ /**
+ * @var boolean whether to allow auto login (using cookie)
+ */
+ private $_allowAutoLogin=false;
+ /**
+ * @var string variable name used to store user session or cookie
+ */
+ private $_userKey;
/**
* Initializes this module.
@@ -215,6 +223,24 @@ class TAuthManager extends TModule
$this->getSession()->add($this->getReturnUrlVarName(),$value);
}
+ /**
+ * @return boolean whether to allow remembering login so that the user logs on automatically next time. Defaults to false.
+ * @since 3.1.1
+ */
+ public function getAllowAutoLogin()
+ {
+ return $this->_allowAutoLogin;
+ }
+
+ /**
+ * @param boolean whether to allow remembering login so that the user logs on automatically next time. Users have to enable cookie to make use of this feature.
+ * @since 3.1.1
+ */
+ public function setAllowAutoLogin($value)
+ {
+ $this->_allowAutoLogin=TPropertyValue::ensureBoolean($value);
+ }
+
/**
* Performs the real authentication work.
* An OnAuthenticate event will be raised if there is any handler attached to it.
@@ -227,11 +253,27 @@ class TAuthManager extends TModule
{
$application=$this->getApplication();
+ // restoring user info from session
if(($session=$application->getSession())===null)
throw new TConfigurationException('authmanager_session_required');
$session->open();
- $sessionInfo=$session->itemAt($this->generateUserSessionKey());
+ $sessionInfo=$session->itemAt($this->getUserKey());
$user=$this->_userManager->getUser(null)->loadFromString($sessionInfo);
+
+ // try authenticating through cookie if possible
+ if($this->getAllowAutoLogin() && $user->getIsGuest())
+ {
+ $cookie=$this->getRequest()->getCookies()->itemAt($this->getUserKey());
+ if($cookie instanceof THttpCookie)
+ {
+ if(($user2=$this->_userManager->getUserFromCookie($cookie))!==null)
+ {
+ $user=$user2;
+ $this->updateSessionUser($user);
+ }
+ }
+ }
+
$application->setUser($user);
// event handler gets a chance to do further auth work
@@ -258,10 +300,22 @@ class TAuthManager extends TModule
}
}
+ /**
+ * @return string a unique variable name for storing user session/cookie data
+ * @since 3.1.1
+ */
+ public function getUserKey()
+ {
+ if($this->_userKey===null)
+ $this->_userKey=$this->generateUserKey();
+ return $this->_userKey;
+ }
+
/**
* @return string a key used to store user information in session
+ * @since 3.1.1
*/
- protected function generateUserSessionKey()
+ protected function generateUserKey()
{
return md5($this->getApplication()->getUniqueID().'prado:user');
}
@@ -278,7 +332,7 @@ class TAuthManager extends TModule
if(($session=$this->getSession())===null)
throw new TConfigurationException('authmanager_session_required');
else
- $session->add($this->generateUserSessionKey(),$user->saveToString());
+ $session->add($this->getUserKey(),$user->saveToString());
}
}
@@ -288,15 +342,24 @@ class TAuthManager extends TModule
* If yes, a user object will be created for the application.
* @param string username
* @param string password
+ * @param integer number of seconds that automatic login will remain effective. If 0, it means user logs out when session ends. This parameter is added since 3.1.1.
* @return boolean if login is successful
*/
- public function login($username,$password)
+ public function login($username,$password,$expire=0)
{
if($this->_userManager->validateUser($username,$password))
{
$user=$this->_userManager->getUser($username);
$this->updateSessionUser($user);
$this->getApplication()->setUser($user);
+
+ if($expire>0)
+ {
+ $cookie=new THttpCookie($this->getUserKey(),'');
+ $cookie->setExpire(time()+$expire);
+ $this->_userManager->saveUserToCookie($cookie);
+ $this->getResponse()->getCookies()->add($cookie);
+ }
return true;
}
else
@@ -312,10 +375,12 @@ class TAuthManager extends TModule
{
if(($session=$this->getSession())===null)
throw new TConfigurationException('authmanager_session_required');
- else
+ $this->getApplication()->getUser()->setIsGuest(true);
+ $session->destroy();
+ if($this->getAllowAutoLogin())
{
- $this->getApplication()->getUser()->setIsGuest(true);
- $session->destroy();
+ $cookie=new THttpCookie($this->getUserKey(),'');
+ $this->getResponse()->getCookies()->add($cookie);
}
}
}
diff --git a/framework/Security/TDbUserManager.php b/framework/Security/TDbUserManager.php
index a2f30642..35cf4fd7 100644
--- a/framework/Security/TDbUserManager.php
+++ b/framework/Security/TDbUserManager.php
@@ -155,7 +155,10 @@ class TDbUserManager extends TModule implements IUserManager
public function getDbConnection()
{
if($this->_conn===null)
+ {
$this->_conn=$this->createDbConnection($this->_connID);
+ $this->_conn->setActive(true);
+ }
return $this->_conn;
}
@@ -178,6 +181,29 @@ class TDbUserManager extends TModule implements IUserManager
else
throw new TConfigurationException('dbusermanager_connectionid_required');
}
+
+ /**
+ * Returns a user instance according to auth data stored in a cookie.
+ * @param THttpCookie the cookie storing user authentication information
+ * @return TDbUser the user instance generated based on the cookie auth data, null if the cookie does not have valid auth data.
+ * @since 3.1.1
+ */
+ public function getUserFromCookie($cookie)
+ {
+ return $this->_userFactory->createUserFromCookie($cookie);
+ }
+
+ /**
+ * Saves user auth data into a cookie.
+ * @param THttpCookie the cookie to receive the user auth data.
+ * @since 3.1.1
+ */
+ public function saveUserToCookie($cookie)
+ {
+ $user=$this->getApplication()->getUser();
+ if($user instanceof TDbUser)
+ $user->saveUserToCookie($cookie);
+ }
}
@@ -250,6 +276,46 @@ abstract class TDbUser extends TUser
* @return TDbUser the newly created and initialized user instance
*/
abstract public function createUser($username);
+
+ /**
+ * Creates a new user instance given the cookie containing auth data.
+ *
+ * This method is invoked when {@link TAuthManager::setAllowAutoLogin AllowAutoLogin} is set true.
+ * The default implementation simply returns null, meaning no user instance can be created
+ * from the given cookie.
+ *
+ * If you want to support automatic login (remember login), you should override this method.
+ * Typically, you obtain the username and a unique token from the cookie's value.
+ * You then verify the token is valid and use the username to create a user instance.
+ *
+ * @param THttpCookie the cookie storing user authentication information
+ * @return TDbUser the user instance generated based on the cookie auth data, null if the cookie does not have valid auth data.
+ * @see saveUserToCookie
+ * @since 3.1.1
+ */
+ public function createUserFromCookie($cookie)
+ {
+ return null;
+ }
+
+ /**
+ * Saves necessary auth data into a cookie.
+ * This method is invoked when {@link TAuthManager::rememberLogin} is invoked.
+ * The default implementation does nothing, meaning auth data is not stored in the cookie
+ * (and thus automatic login is not supported.)
+ *
+ * If you want to support automatic login (remember login), you should override this method.
+ * Typically, you generate a unique token according to the current login information
+ * and save it together with the username in the cookie's value.
+ * You should avoid revealing the password in the generated token.
+ *
+ * @param THttpCookie the cookie to store the user auth information
+ * @see createUserFromCookie
+ * @since 3.1.1
+ */
+ public function saveUserToCookie($cookie)
+ {
+ }
}
?>
\ No newline at end of file
diff --git a/framework/Security/TUserManager.php b/framework/Security/TUserManager.php
index 28651de8..f6aee767 100644
--- a/framework/Security/TUserManager.php
+++ b/framework/Security/TUserManager.php
@@ -250,6 +250,43 @@ class TUserManager extends TModule implements IUserManager
}
}
+ /**
+ * Returns a user instance according to auth data stored in a cookie.
+ * @param THttpCookie the cookie storing user authentication information
+ * @return TUser the user instance generated based on the cookie auth data, null if the cookie does not have valid auth data.
+ * @since 3.1.1
+ */
+ public function getUserFromCookie($cookie)
+ {
+ if(($data=$cookie->getValue())!=='')
+ {
+ $data=unserialize($data);
+ if(is_array($data) && count($data)===2)
+ {
+ list($username,$token)=$data;
+ if(isset($this->_users[$username]) && $token===md5($username.$this->_users[$username]))
+ return $this->getUser($username);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Saves user auth data into a cookie.
+ * @param THttpCookie the cookie to receive the user auth data.
+ * @since 3.1.1
+ */
+ public function saveUserToCookie($cookie)
+ {
+ $user=$this->getApplication()->getUser();
+ $username=strtolower($user->getName());
+ if(isset($this->_users[$username]))
+ {
+ $data=array($username,md5($username.$this->_users[$username]));
+ $cookie->setValue(serialize($data));
+ }
+ }
+
/**
* Sets a user as a guest.
* User name is changed as guest name, and roles are emptied.
diff --git a/framework/TApplication.php b/framework/TApplication.php
index 3aa2cec1..a0239072 100644
--- a/framework/TApplication.php
+++ b/framework/TApplication.php
@@ -150,12 +150,12 @@ class TApplication extends TComponent
*/
private static $_steps=array(
'onBeginRequest',
+ 'onLoadState',
+ 'onLoadStateComplete',
'onAuthentication',
'onAuthenticationComplete',
'onAuthorization',
'onAuthorizationComplete',
- 'onLoadState',
- 'onLoadStateComplete',
'onPreRunService',
'runService',
'onSaveState',
diff --git a/framework/Web/THttpRequest.php b/framework/Web/THttpRequest.php
index a9e5fe68..ca7780cd 100644
--- a/framework/Web/THttpRequest.php
+++ b/framework/Web/THttpRequest.php
@@ -911,7 +911,7 @@ class THttpCookie extends TComponent
/**
* @var string value of the cookie
*/
- private $_value=0;
+ private $_value='';
/**
* @var integer expire of the cookie
*/
--
cgit v1.2.3