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