From 903ae8a581fac1e6917fc3e31d2ad8fb91df80c3 Mon Sep 17 00:00:00 2001 From: ctrlaltca <> Date: Thu, 12 Jul 2012 11:21:01 +0000 Subject: standardize the use of unix eol; use svn properties to enforce native eol --- framework/Security/IUserManager.php | 114 ++-- framework/Security/TAuthManager.php | 914 +++++++++++++++--------------- framework/Security/TAuthorizationRule.php | 590 +++++++++---------- framework/Security/TDbUserManager.php | 638 ++++++++++----------- framework/Security/TUser.php | 442 +++++++-------- framework/Security/TUserManager.php | 802 +++++++++++++------------- 6 files changed, 1750 insertions(+), 1750 deletions(-) (limited to 'framework/Security') diff --git a/framework/Security/IUserManager.php b/framework/Security/IUserManager.php index 2769db8f..19431bd9 100644 --- a/framework/Security/IUserManager.php +++ b/framework/Security/IUserManager.php @@ -1,58 +1,58 @@ - - * @link http://www.pradosoft.com/ + + * @link http://www.pradosoft.com/ * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id$ - * @package System.Security - */ - -/** - * IUserManager interface - * - * IUserManager specifies the interface that must be implemented by - * a user manager class if it is to be used together with {@link TAuthManager} - * and {@link TUser}. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Security - * @since 3.0 - */ -interface IUserManager -{ - /** - * @return string name for a guest user. - */ - public function getGuestName(); - /** - * Returns a user instance given the user name. - * @param string user name, null if it is a guest. - * @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 - * @param string password - * @return boolean true if validation is successful, false otherwise. - */ - public function validateUser($username,$password); -} - + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Security + */ + +/** + * IUserManager interface + * + * IUserManager specifies the interface that must be implemented by + * a user manager class if it is to be used together with {@link TAuthManager} + * and {@link TUser}. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Security + * @since 3.0 + */ +interface IUserManager +{ + /** + * @return string name for a guest user. + */ + public function getGuestName(); + /** + * Returns a user instance given the user name. + * @param string user name, null if it is a guest. + * @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 + * @param string password + * @return boolean true if validation is successful, false otherwise. + */ + public function validateUser($username,$password); +} + diff --git a/framework/Security/TAuthManager.php b/framework/Security/TAuthManager.php index 92836195..6a774a8e 100644 --- a/framework/Security/TAuthManager.php +++ b/framework/Security/TAuthManager.php @@ -1,457 +1,457 @@ - - * @link http://www.pradosoft.com/ - * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id$ - * @package System.Security - */ - -/** - * Using IUserManager interface - */ -Prado::using('System.Security.IUserManager'); - -/** - * TAuthManager class - * - * TAuthManager performs user authentication and authorization for a Prado application. - * TAuthManager works together with a {@link IUserManager} module that can be - * specified via the {@link setUserManager UserManager} property. - * If an authorization fails, TAuthManager will try to redirect the client - * browser to a login page that is specified via the {@link setLoginPage LoginPage}. - * To login or logout a user, call {@link login} or {@link logout}, respectively. - * - * The {@link setAuthExpire AuthExpire} property can be used to define the time - * in seconds after which the authentication should expire. - * {@link setAllowAutoLogin AllowAutoLogin} specifies if the login information - * should be stored in a cookie to perform automatic login. Enabling this - * feature will cause that {@link setAuthExpire AuthExpire} has no effect - * since the user will be logged in again on authentication expiration. - * - * To load TAuthManager, configure it in application configuration as follows, - * - * - * - * @author Qiang Xue - * @version $Id$ - * @package System.Security - * @since 3.0 - */ -class TAuthManager extends TModule -{ - /** - * GET variable name for return url - */ - const RETURN_URL_VAR='ReturnUrl'; - /** - * @var boolean if the module has been initialized - */ - private $_initialized=false; - /** - * @var IUserManager user manager instance - */ - private $_userManager; - /** - * @var string login page - */ - private $_loginPage; - /** - * @var boolean whether authorization should be skipped - */ - private $_skipAuthorization=false; - /** - * @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; - /** - * @var integer authentication expiration time in seconds. Defaults to zero (no expiration) - */ - private $_authExpire=0; - - /** - * Initializes this module. - * This method is required by the IModule interface. - * @param TXmlElement configuration for this module, can be null - * @throws TConfigurationException if user manager does not exist or is not IUserManager - */ - public function init($config) - { - if($this->_userManager===null) - throw new TConfigurationException('authmanager_usermanager_required'); - if($this->_returnUrlVarName===null) - $this->_returnUrlVarName=$this->getApplication()->getID().':'.self::RETURN_URL_VAR; - $application=$this->getApplication(); - if(is_string($this->_userManager)) - { - if(($users=$application->getModule($this->_userManager))===null) - throw new TConfigurationException('authmanager_usermanager_inexistent',$this->_userManager); - if(!($users instanceof IUserManager)) - throw new TConfigurationException('authmanager_usermanager_invalid',$this->_userManager); - $this->_userManager=$users; - } - $application->attachEventHandler('OnAuthentication',array($this,'doAuthentication')); - $application->attachEventHandler('OnEndRequest',array($this,'leave')); - $application->attachEventHandler('OnAuthorization',array($this,'doAuthorization')); - $this->_initialized=true; - } - - /** - * @return IUserManager user manager instance - */ - public function getUserManager() - { - return $this->_userManager; - } - - /** - * @param string|IUserManager the user manager module ID or the user manager object - * @throws TInvalidOperationException if the module has been initialized or the user manager object is not IUserManager - */ - public function setUserManager($provider) - { - if($this->_initialized) - throw new TInvalidOperationException('authmanager_usermanager_unchangeable'); - if(!is_string($provider) && !($provider instanceof IUserManager)) - throw new TConfigurationException('authmanager_usermanager_invalid',$this->_userManager); - $this->_userManager=$provider; - } - - /** - * @return string path of login page should login is required - */ - public function getLoginPage() - { - return $this->_loginPage; - } - - /** - * Sets the login page that the client browser will be redirected to if login is needed. - * Login page should be specified in the format of page path. - * @param string path of login page should login is required - * @see TPageService - */ - public function setLoginPage($pagePath) - { - $this->_loginPage=$pagePath; - } - - /** - * Performs authentication. - * This is the event handler attached to application's Authentication event. - * Do not call this method directly. - * @param mixed sender of the Authentication event - * @param mixed event parameter - */ - public function doAuthentication($sender,$param) - { - $this->onAuthenticate($param); - - $service=$this->getService(); - if(($service instanceof TPageService) && $service->getRequestedPagePath()===$this->getLoginPage()) - $this->_skipAuthorization=true; - } - - /** - * Performs authorization. - * This is the event handler attached to application's Authorization event. - * Do not call this method directly. - * @param mixed sender of the Authorization event - * @param mixed event parameter - */ - public function doAuthorization($sender,$param) - { - if(!$this->_skipAuthorization) - { - $this->onAuthorize($param); - } - } - - /** - * Performs login redirect if authorization fails. - * This is the event handler attached to application's EndRequest event. - * Do not call this method directly. - * @param mixed sender of the event - * @param mixed event parameter - */ - public function leave($sender,$param) - { - $application=$this->getApplication(); - if($application->getResponse()->getStatusCode()===401) - { - $service=$application->getService(); - if($service instanceof TPageService) - { - $returnUrl=$application->getRequest()->getRequestUri(); - $this->setReturnUrl($returnUrl); - $url=$service->constructUrl($this->getLoginPage()); - $application->getResponse()->redirect($url); - } - } - } - - /** - * @return string the name of the session variable storing return URL. It defaults to 'AppID:ReturnUrl' - */ - public function getReturnUrlVarName() - { - return $this->_returnUrlVarName; - } - - /** - * @param string the name of the session variable storing return URL. - */ - public function setReturnUrlVarName($value) - { - $this->_returnUrlVarName=$value; - } - - /** - * @return string URL that the browser should be redirected to when login succeeds. - */ - public function getReturnUrl() - { - return $this->getSession()->itemAt($this->getReturnUrlVarName()); - } - - /** - * Sets the URL that the browser should be redirected to when login succeeds. - * @param string the URL to be redirected to. - */ - public function setReturnUrl($value) - { - $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); - } - - /** - * @return integer authentication expiration time in seconds. Defaults to zero (no expiration). - * @since 3.1.3 - */ - public function getAuthExpire() - { - return $this->_authExpire; - } - - /** - * @param integer authentication expiration time in seconds. Defaults to zero (no expiration). - * @since 3.1.3 - */ - public function setAuthExpire($value) - { - $this->_authExpire=TPropertyValue::ensureInteger($value); - } - - /** - * Performs the real authentication work. - * An OnAuthenticate event will be raised if there is any handler attached to it. - * If the application already has a non-null user, it will return without further authentication. - * Otherwise, user information will be restored from session data. - * @param mixed parameter to be passed to OnAuthenticate event - * @throws TConfigurationException if session module does not exist. - */ - public function onAuthenticate($param) - { - $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->getUserKey()); - $user=$this->_userManager->getUser(null)->loadFromString($sessionInfo); - - // check for authentication expiration - $isAuthExpired = $this->_authExpire>0 && !$user->getIsGuest() && - ($expiretime=$session->itemAt('AuthExpireTime')) && $expiretimegetAllowAutoLogin() && ($user->getIsGuest() || $isAuthExpired)) - { - $cookie=$this->getRequest()->getCookies()->itemAt($this->getUserKey()); - if($cookie instanceof THttpCookie) - { - if(($user2=$this->_userManager->getUserFromCookie($cookie))!==null) - { - $user=$user2; - $this->updateSessionUser($user); - // user is restored from cookie, auth may not expire - $isAuthExpired = false; - } - } - } - - $application->setUser($user); - - // handle authentication expiration or update expiration time - if($isAuthExpired) - $this->onAuthExpire($param); - else - $session->add('AuthExpireTime', time() + $this->_authExpire); - - // event handler gets a chance to do further auth work - if($this->hasEventHandler('OnAuthenticate')) - $this->raiseEvent('OnAuthenticate',$this,$application); - } - - /** - * Performs user logout on authentication expiration. - * An 'OnAuthExpire' event will be raised if there is any handler attached to it. - * @param mixed parameter to be passed to OnAuthExpire event. - */ - public function onAuthExpire($param) - { - $this->logout(); - if($this->hasEventHandler('OnAuthExpire')) - $this->raiseEvent('OnAuthExpire',$this,$param); - } - - /** - * Performs the real authorization work. - * Authorization rules obtained from the application will be used to check - * if a user is allowed. If authorization fails, the response status code - * will be set as 401 and the application terminates. - * @param mixed parameter to be passed to OnAuthorize event - */ - public function onAuthorize($param) - { - $application=$this->getApplication(); - if($this->hasEventHandler('OnAuthorize')) - $this->raiseEvent('OnAuthorize',$this,$application); - if(!$application->getAuthorizationRules()->isUserAllowed($application->getUser(),$application->getRequest()->getRequestType(),$application->getRequest()->getUserHostAddress())) - { - $application->getResponse()->setStatusCode(401); - $application->completeRequest(); - } - } - - /** - * @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 generateUserKey() - { - return md5($this->getApplication()->getUniqueID().'prado:user'); - } - - /** - * Updates the user data stored in session. - * @param IUser user object - * @throws new TConfigurationException if session module is not loaded. - */ - public function updateSessionUser($user) - { - if(!$user->getIsGuest()) - { - if(($session=$this->getSession())===null) - throw new TConfigurationException('authmanager_session_required'); - else - $session->add($this->getUserKey(),$user->saveToString()); - } - } - - /** - * Switches to a new user. - * This method will logout the current user first and login with a new one (without password.) - * @param string the new username - * @return boolean if the switch is successful - */ - public function switchUser($username) - { - if(($user=$this->_userManager->getUser($username))===null) - return false; - $this->updateSessionUser($user); - $this->getApplication()->setUser($user); - return true; - } - - /** - * Logs in a user with username and password. - * The username and password will be used to validate if login is successful. - * 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,$expire=0) - { - if($this->_userManager->validateUser($username,$password)) - { - if(($user=$this->_userManager->getUser($username))===null) - return false; - $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 - return false; - } - - /** - * Logs out a user. - * User session will be destroyed after this method is called. - * @throws TConfigurationException if session module is not loaded. - */ - public function logout() - { - if(($session=$this->getSession())===null) - throw new TConfigurationException('authmanager_session_required'); - $this->getApplication()->getUser()->setIsGuest(true); - $session->destroy(); - if($this->getAllowAutoLogin()) - { - $cookie=new THttpCookie($this->getUserKey(),''); - $this->getResponse()->getCookies()->add($cookie); - } - } -} - -?> + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2012 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Security + */ + +/** + * Using IUserManager interface + */ +Prado::using('System.Security.IUserManager'); + +/** + * TAuthManager class + * + * TAuthManager performs user authentication and authorization for a Prado application. + * TAuthManager works together with a {@link IUserManager} module that can be + * specified via the {@link setUserManager UserManager} property. + * If an authorization fails, TAuthManager will try to redirect the client + * browser to a login page that is specified via the {@link setLoginPage LoginPage}. + * To login or logout a user, call {@link login} or {@link logout}, respectively. + * + * The {@link setAuthExpire AuthExpire} property can be used to define the time + * in seconds after which the authentication should expire. + * {@link setAllowAutoLogin AllowAutoLogin} specifies if the login information + * should be stored in a cookie to perform automatic login. Enabling this + * feature will cause that {@link setAuthExpire AuthExpire} has no effect + * since the user will be logged in again on authentication expiration. + * + * To load TAuthManager, configure it in application configuration as follows, + * + * + * + * @author Qiang Xue + * @version $Id$ + * @package System.Security + * @since 3.0 + */ +class TAuthManager extends TModule +{ + /** + * GET variable name for return url + */ + const RETURN_URL_VAR='ReturnUrl'; + /** + * @var boolean if the module has been initialized + */ + private $_initialized=false; + /** + * @var IUserManager user manager instance + */ + private $_userManager; + /** + * @var string login page + */ + private $_loginPage; + /** + * @var boolean whether authorization should be skipped + */ + private $_skipAuthorization=false; + /** + * @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; + /** + * @var integer authentication expiration time in seconds. Defaults to zero (no expiration) + */ + private $_authExpire=0; + + /** + * Initializes this module. + * This method is required by the IModule interface. + * @param TXmlElement configuration for this module, can be null + * @throws TConfigurationException if user manager does not exist or is not IUserManager + */ + public function init($config) + { + if($this->_userManager===null) + throw new TConfigurationException('authmanager_usermanager_required'); + if($this->_returnUrlVarName===null) + $this->_returnUrlVarName=$this->getApplication()->getID().':'.self::RETURN_URL_VAR; + $application=$this->getApplication(); + if(is_string($this->_userManager)) + { + if(($users=$application->getModule($this->_userManager))===null) + throw new TConfigurationException('authmanager_usermanager_inexistent',$this->_userManager); + if(!($users instanceof IUserManager)) + throw new TConfigurationException('authmanager_usermanager_invalid',$this->_userManager); + $this->_userManager=$users; + } + $application->attachEventHandler('OnAuthentication',array($this,'doAuthentication')); + $application->attachEventHandler('OnEndRequest',array($this,'leave')); + $application->attachEventHandler('OnAuthorization',array($this,'doAuthorization')); + $this->_initialized=true; + } + + /** + * @return IUserManager user manager instance + */ + public function getUserManager() + { + return $this->_userManager; + } + + /** + * @param string|IUserManager the user manager module ID or the user manager object + * @throws TInvalidOperationException if the module has been initialized or the user manager object is not IUserManager + */ + public function setUserManager($provider) + { + if($this->_initialized) + throw new TInvalidOperationException('authmanager_usermanager_unchangeable'); + if(!is_string($provider) && !($provider instanceof IUserManager)) + throw new TConfigurationException('authmanager_usermanager_invalid',$this->_userManager); + $this->_userManager=$provider; + } + + /** + * @return string path of login page should login is required + */ + public function getLoginPage() + { + return $this->_loginPage; + } + + /** + * Sets the login page that the client browser will be redirected to if login is needed. + * Login page should be specified in the format of page path. + * @param string path of login page should login is required + * @see TPageService + */ + public function setLoginPage($pagePath) + { + $this->_loginPage=$pagePath; + } + + /** + * Performs authentication. + * This is the event handler attached to application's Authentication event. + * Do not call this method directly. + * @param mixed sender of the Authentication event + * @param mixed event parameter + */ + public function doAuthentication($sender,$param) + { + $this->onAuthenticate($param); + + $service=$this->getService(); + if(($service instanceof TPageService) && $service->getRequestedPagePath()===$this->getLoginPage()) + $this->_skipAuthorization=true; + } + + /** + * Performs authorization. + * This is the event handler attached to application's Authorization event. + * Do not call this method directly. + * @param mixed sender of the Authorization event + * @param mixed event parameter + */ + public function doAuthorization($sender,$param) + { + if(!$this->_skipAuthorization) + { + $this->onAuthorize($param); + } + } + + /** + * Performs login redirect if authorization fails. + * This is the event handler attached to application's EndRequest event. + * Do not call this method directly. + * @param mixed sender of the event + * @param mixed event parameter + */ + public function leave($sender,$param) + { + $application=$this->getApplication(); + if($application->getResponse()->getStatusCode()===401) + { + $service=$application->getService(); + if($service instanceof TPageService) + { + $returnUrl=$application->getRequest()->getRequestUri(); + $this->setReturnUrl($returnUrl); + $url=$service->constructUrl($this->getLoginPage()); + $application->getResponse()->redirect($url); + } + } + } + + /** + * @return string the name of the session variable storing return URL. It defaults to 'AppID:ReturnUrl' + */ + public function getReturnUrlVarName() + { + return $this->_returnUrlVarName; + } + + /** + * @param string the name of the session variable storing return URL. + */ + public function setReturnUrlVarName($value) + { + $this->_returnUrlVarName=$value; + } + + /** + * @return string URL that the browser should be redirected to when login succeeds. + */ + public function getReturnUrl() + { + return $this->getSession()->itemAt($this->getReturnUrlVarName()); + } + + /** + * Sets the URL that the browser should be redirected to when login succeeds. + * @param string the URL to be redirected to. + */ + public function setReturnUrl($value) + { + $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); + } + + /** + * @return integer authentication expiration time in seconds. Defaults to zero (no expiration). + * @since 3.1.3 + */ + public function getAuthExpire() + { + return $this->_authExpire; + } + + /** + * @param integer authentication expiration time in seconds. Defaults to zero (no expiration). + * @since 3.1.3 + */ + public function setAuthExpire($value) + { + $this->_authExpire=TPropertyValue::ensureInteger($value); + } + + /** + * Performs the real authentication work. + * An OnAuthenticate event will be raised if there is any handler attached to it. + * If the application already has a non-null user, it will return without further authentication. + * Otherwise, user information will be restored from session data. + * @param mixed parameter to be passed to OnAuthenticate event + * @throws TConfigurationException if session module does not exist. + */ + public function onAuthenticate($param) + { + $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->getUserKey()); + $user=$this->_userManager->getUser(null)->loadFromString($sessionInfo); + + // check for authentication expiration + $isAuthExpired = $this->_authExpire>0 && !$user->getIsGuest() && + ($expiretime=$session->itemAt('AuthExpireTime')) && $expiretimegetAllowAutoLogin() && ($user->getIsGuest() || $isAuthExpired)) + { + $cookie=$this->getRequest()->getCookies()->itemAt($this->getUserKey()); + if($cookie instanceof THttpCookie) + { + if(($user2=$this->_userManager->getUserFromCookie($cookie))!==null) + { + $user=$user2; + $this->updateSessionUser($user); + // user is restored from cookie, auth may not expire + $isAuthExpired = false; + } + } + } + + $application->setUser($user); + + // handle authentication expiration or update expiration time + if($isAuthExpired) + $this->onAuthExpire($param); + else + $session->add('AuthExpireTime', time() + $this->_authExpire); + + // event handler gets a chance to do further auth work + if($this->hasEventHandler('OnAuthenticate')) + $this->raiseEvent('OnAuthenticate',$this,$application); + } + + /** + * Performs user logout on authentication expiration. + * An 'OnAuthExpire' event will be raised if there is any handler attached to it. + * @param mixed parameter to be passed to OnAuthExpire event. + */ + public function onAuthExpire($param) + { + $this->logout(); + if($this->hasEventHandler('OnAuthExpire')) + $this->raiseEvent('OnAuthExpire',$this,$param); + } + + /** + * Performs the real authorization work. + * Authorization rules obtained from the application will be used to check + * if a user is allowed. If authorization fails, the response status code + * will be set as 401 and the application terminates. + * @param mixed parameter to be passed to OnAuthorize event + */ + public function onAuthorize($param) + { + $application=$this->getApplication(); + if($this->hasEventHandler('OnAuthorize')) + $this->raiseEvent('OnAuthorize',$this,$application); + if(!$application->getAuthorizationRules()->isUserAllowed($application->getUser(),$application->getRequest()->getRequestType(),$application->getRequest()->getUserHostAddress())) + { + $application->getResponse()->setStatusCode(401); + $application->completeRequest(); + } + } + + /** + * @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 generateUserKey() + { + return md5($this->getApplication()->getUniqueID().'prado:user'); + } + + /** + * Updates the user data stored in session. + * @param IUser user object + * @throws new TConfigurationException if session module is not loaded. + */ + public function updateSessionUser($user) + { + if(!$user->getIsGuest()) + { + if(($session=$this->getSession())===null) + throw new TConfigurationException('authmanager_session_required'); + else + $session->add($this->getUserKey(),$user->saveToString()); + } + } + + /** + * Switches to a new user. + * This method will logout the current user first and login with a new one (without password.) + * @param string the new username + * @return boolean if the switch is successful + */ + public function switchUser($username) + { + if(($user=$this->_userManager->getUser($username))===null) + return false; + $this->updateSessionUser($user); + $this->getApplication()->setUser($user); + return true; + } + + /** + * Logs in a user with username and password. + * The username and password will be used to validate if login is successful. + * 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,$expire=0) + { + if($this->_userManager->validateUser($username,$password)) + { + if(($user=$this->_userManager->getUser($username))===null) + return false; + $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 + return false; + } + + /** + * Logs out a user. + * User session will be destroyed after this method is called. + * @throws TConfigurationException if session module is not loaded. + */ + public function logout() + { + if(($session=$this->getSession())===null) + throw new TConfigurationException('authmanager_session_required'); + $this->getApplication()->getUser()->setIsGuest(true); + $session->destroy(); + if($this->getAllowAutoLogin()) + { + $cookie=new THttpCookie($this->getUserKey(),''); + $this->getResponse()->getCookies()->add($cookie); + } + } +} + +?> diff --git a/framework/Security/TAuthorizationRule.php b/framework/Security/TAuthorizationRule.php index 6c12d301..4eb32b10 100644 --- a/framework/Security/TAuthorizationRule.php +++ b/framework/Security/TAuthorizationRule.php @@ -1,296 +1,296 @@ - - * @link http://www.pradosoft.com/ + + * @link http://www.pradosoft.com/ * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id$ - * @package System.Security - */ -/** - * TAuthorizationRule class - * - * TAuthorizationRule represents a single authorization rule. - * A rule is specified by an action (required), a list of users (optional), - * a list of roles (optional), a verb (optional), and a list of IP rules (optional). - * Action can be either 'allow' or 'deny'. - * Guest (anonymous, unauthenticated) users are represented by question mark '?'. - * All users (including guest users) are represented by asterisk '*'. - * Authenticated users are represented by '@'. - * Users/roles are case-insensitive. - * Different users/roles are separated by comma ','. - * Verb can be either 'get' or 'post'. If it is absent, it means both. - * IP rules are separated by comma ',' and can contain wild card in the rules (e.g. '192.132.23.33, 192.122.*.*') - * - * @author Qiang Xue - * @version $Id$ - * @package System.Security - * @since 3.0 - */ -class TAuthorizationRule extends TComponent -{ - /** - * @var string action, either 'allow' or 'deny' - */ - private $_action; - /** - * @var array list of user IDs - */ - private $_users; - /** - * @var array list of roles - */ - private $_roles; - /** - * @var string verb, may be empty, 'get', or 'post'. - */ - private $_verb; - /** - * @var string IP patterns - */ - private $_ipRules; - /** - * @var boolean if this rule applies to everyone - */ - private $_everyone; - /** - * @var boolean if this rule applies to guest user - */ - private $_guest; - /** - * @var boolean if this rule applies to authenticated users - */ - private $_authenticated; - - /** - * Constructor. - * @param string action, either 'deny' or 'allow' - * @param string a comma separated user list - * @param string a comma separated role list - * @param string verb, can be empty, 'get', or 'post' - * @param string IP rules (separated by comma, can contain wild card *) - */ - public function __construct($action,$users,$roles,$verb='',$ipRules='') - { - $action=strtolower(trim($action)); - if($action==='allow' || $action==='deny') - $this->_action=$action; - else - throw new TInvalidDataValueException('authorizationrule_action_invalid',$action); - $this->_users=array(); - $this->_roles=array(); - $this->_ipRules=array(); - $this->_everyone=false; - $this->_guest=false; - $this->_authenticated=false; - - if(trim($users)==='') - $users='*'; - foreach(explode(',',$users) as $user) - { - if(($user=trim(strtolower($user)))!=='') - { - if($user==='*') - { - $this->_everyone=true; - break; - } - else if($user==='?') - $this->_guest=true; - else if($user==='@') - $this->_authenticated=true; - else - $this->_users[]=$user; - } - } - - if(trim($roles)==='') - $roles='*'; - foreach(explode(',',$roles) as $role) - { - if(($role=trim(strtolower($role)))!=='') - $this->_roles[]=$role; - } - - if(($verb=trim(strtolower($verb)))==='') - $verb='*'; - if($verb==='*' || $verb==='get' || $verb==='post') - $this->_verb=$verb; - else - throw new TInvalidDataValueException('authorizationrule_verb_invalid',$verb); - - if(trim($ipRules)==='') - $ipRules='*'; - foreach(explode(',',$ipRules) as $ipRule) - { - if(($ipRule=trim($ipRule))!=='') - $this->_ipRules[]=$ipRule; - } - } - - /** - * @return string action, either 'allow' or 'deny' - */ - public function getAction() - { - return $this->_action; - } - - /** - * @return array list of user IDs - */ - public function getUsers() - { - return $this->_users; - } - - /** - * @return array list of roles - */ - public function getRoles() - { - return $this->_roles; - } - - /** - * @return string verb, may be empty, 'get', or 'post'. - */ - public function getVerb() - { - return $this->_verb; - } - - /** - * @return array list of IP rules. - * @since 3.1.1 - */ - public function getIPRules() - { - return $this->_ipRules; - } - - /** - * @return boolean if this rule applies to everyone - */ - public function getGuestApplied() - { - return $this->_guest || $this->_everyone; - } - - /** - * @return boolean if this rule applies to everyone - */ - public function getEveryoneApplied() - { - return $this->_everyone; - } - - /** - * @return boolean if this rule applies to authenticated users - */ - public function getAuthenticatedApplied() - { - return $this->_authenticated || $this->_everyone; - } - - /** - * @param IUser the user object - * @param string the request verb (GET, PUT) - * @param string the request IP address - * @return integer 1 if the user is allowed, -1 if the user is denied, 0 if the rule does not apply to the user - */ - public function isUserAllowed(IUser $user,$verb,$ip) - { - if($this->isVerbMatched($verb) && $this->isIpMatched($ip) && $this->isUserMatched($user) && $this->isRoleMatched($user)) - return ($this->_action==='allow')?1:-1; - else - return 0; - } - - private function isIpMatched($ip) - { - if(empty($this->_ipRules)) - return 1; - foreach($this->_ipRules as $rule) - { - if($rule==='*' || $rule===$ip || (($pos=strpos($rule,'*'))!==false && strncmp($ip,$rule,$pos)===0)) - return 1; - } - return 0; - } - - private function isUserMatched($user) - { - return ($this->_everyone || ($this->_guest && $user->getIsGuest()) || ($this->_authenticated && !$user->getIsGuest()) || in_array(strtolower($user->getName()),$this->_users)); - } - - private function isRoleMatched($user) - { - foreach($this->_roles as $role) - { - if($role==='*' || $user->isInRole($role)) - return true; - } - return false; - } - - private function isVerbMatched($verb) - { - return ($this->_verb==='*' || strcasecmp($verb,$this->_verb)===0); - } -} - - -/** - * TAuthorizationRuleCollection class. - * TAuthorizationRuleCollection represents a collection of authorization rules {@link TAuthorizationRule}. - * To check if a user is allowed, call {@link isUserAllowed}. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Security - * @since 3.0 - */ -class TAuthorizationRuleCollection extends TList -{ - /** - * @param IUser the user to be authorized - * @param string verb, can be empty, 'post' or 'get'. - * @param string the request IP address - * @return boolean whether the user is allowed - */ - public function isUserAllowed($user,$verb,$ip) - { - if($user instanceof IUser) - { - $verb=strtolower(trim($verb)); - foreach($this as $rule) - { - if(($decision=$rule->isUserAllowed($user,$verb,$ip))!==0) - return ($decision>0); - } - return true; - } - else - return false; - } - - /** - * Inserts an item at the specified position. - * This overrides the parent implementation by performing additional - * operations for each newly added TAuthorizationRule object. - * @param integer the specified position. - * @param mixed new item - * @throws TInvalidDataTypeException if the item to be inserted is not a TAuthorizationRule object. - */ - public function insertAt($index,$item) - { - if($item instanceof TAuthorizationRule) - parent::insertAt($index,$item); - else - throw new TInvalidDataTypeException('authorizationrulecollection_authorizationrule_required'); - } -} - + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Security + */ +/** + * TAuthorizationRule class + * + * TAuthorizationRule represents a single authorization rule. + * A rule is specified by an action (required), a list of users (optional), + * a list of roles (optional), a verb (optional), and a list of IP rules (optional). + * Action can be either 'allow' or 'deny'. + * Guest (anonymous, unauthenticated) users are represented by question mark '?'. + * All users (including guest users) are represented by asterisk '*'. + * Authenticated users are represented by '@'. + * Users/roles are case-insensitive. + * Different users/roles are separated by comma ','. + * Verb can be either 'get' or 'post'. If it is absent, it means both. + * IP rules are separated by comma ',' and can contain wild card in the rules (e.g. '192.132.23.33, 192.122.*.*') + * + * @author Qiang Xue + * @version $Id$ + * @package System.Security + * @since 3.0 + */ +class TAuthorizationRule extends TComponent +{ + /** + * @var string action, either 'allow' or 'deny' + */ + private $_action; + /** + * @var array list of user IDs + */ + private $_users; + /** + * @var array list of roles + */ + private $_roles; + /** + * @var string verb, may be empty, 'get', or 'post'. + */ + private $_verb; + /** + * @var string IP patterns + */ + private $_ipRules; + /** + * @var boolean if this rule applies to everyone + */ + private $_everyone; + /** + * @var boolean if this rule applies to guest user + */ + private $_guest; + /** + * @var boolean if this rule applies to authenticated users + */ + private $_authenticated; + + /** + * Constructor. + * @param string action, either 'deny' or 'allow' + * @param string a comma separated user list + * @param string a comma separated role list + * @param string verb, can be empty, 'get', or 'post' + * @param string IP rules (separated by comma, can contain wild card *) + */ + public function __construct($action,$users,$roles,$verb='',$ipRules='') + { + $action=strtolower(trim($action)); + if($action==='allow' || $action==='deny') + $this->_action=$action; + else + throw new TInvalidDataValueException('authorizationrule_action_invalid',$action); + $this->_users=array(); + $this->_roles=array(); + $this->_ipRules=array(); + $this->_everyone=false; + $this->_guest=false; + $this->_authenticated=false; + + if(trim($users)==='') + $users='*'; + foreach(explode(',',$users) as $user) + { + if(($user=trim(strtolower($user)))!=='') + { + if($user==='*') + { + $this->_everyone=true; + break; + } + else if($user==='?') + $this->_guest=true; + else if($user==='@') + $this->_authenticated=true; + else + $this->_users[]=$user; + } + } + + if(trim($roles)==='') + $roles='*'; + foreach(explode(',',$roles) as $role) + { + if(($role=trim(strtolower($role)))!=='') + $this->_roles[]=$role; + } + + if(($verb=trim(strtolower($verb)))==='') + $verb='*'; + if($verb==='*' || $verb==='get' || $verb==='post') + $this->_verb=$verb; + else + throw new TInvalidDataValueException('authorizationrule_verb_invalid',$verb); + + if(trim($ipRules)==='') + $ipRules='*'; + foreach(explode(',',$ipRules) as $ipRule) + { + if(($ipRule=trim($ipRule))!=='') + $this->_ipRules[]=$ipRule; + } + } + + /** + * @return string action, either 'allow' or 'deny' + */ + public function getAction() + { + return $this->_action; + } + + /** + * @return array list of user IDs + */ + public function getUsers() + { + return $this->_users; + } + + /** + * @return array list of roles + */ + public function getRoles() + { + return $this->_roles; + } + + /** + * @return string verb, may be empty, 'get', or 'post'. + */ + public function getVerb() + { + return $this->_verb; + } + + /** + * @return array list of IP rules. + * @since 3.1.1 + */ + public function getIPRules() + { + return $this->_ipRules; + } + + /** + * @return boolean if this rule applies to everyone + */ + public function getGuestApplied() + { + return $this->_guest || $this->_everyone; + } + + /** + * @return boolean if this rule applies to everyone + */ + public function getEveryoneApplied() + { + return $this->_everyone; + } + + /** + * @return boolean if this rule applies to authenticated users + */ + public function getAuthenticatedApplied() + { + return $this->_authenticated || $this->_everyone; + } + + /** + * @param IUser the user object + * @param string the request verb (GET, PUT) + * @param string the request IP address + * @return integer 1 if the user is allowed, -1 if the user is denied, 0 if the rule does not apply to the user + */ + public function isUserAllowed(IUser $user,$verb,$ip) + { + if($this->isVerbMatched($verb) && $this->isIpMatched($ip) && $this->isUserMatched($user) && $this->isRoleMatched($user)) + return ($this->_action==='allow')?1:-1; + else + return 0; + } + + private function isIpMatched($ip) + { + if(empty($this->_ipRules)) + return 1; + foreach($this->_ipRules as $rule) + { + if($rule==='*' || $rule===$ip || (($pos=strpos($rule,'*'))!==false && strncmp($ip,$rule,$pos)===0)) + return 1; + } + return 0; + } + + private function isUserMatched($user) + { + return ($this->_everyone || ($this->_guest && $user->getIsGuest()) || ($this->_authenticated && !$user->getIsGuest()) || in_array(strtolower($user->getName()),$this->_users)); + } + + private function isRoleMatched($user) + { + foreach($this->_roles as $role) + { + if($role==='*' || $user->isInRole($role)) + return true; + } + return false; + } + + private function isVerbMatched($verb) + { + return ($this->_verb==='*' || strcasecmp($verb,$this->_verb)===0); + } +} + + +/** + * TAuthorizationRuleCollection class. + * TAuthorizationRuleCollection represents a collection of authorization rules {@link TAuthorizationRule}. + * To check if a user is allowed, call {@link isUserAllowed}. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Security + * @since 3.0 + */ +class TAuthorizationRuleCollection extends TList +{ + /** + * @param IUser the user to be authorized + * @param string verb, can be empty, 'post' or 'get'. + * @param string the request IP address + * @return boolean whether the user is allowed + */ + public function isUserAllowed($user,$verb,$ip) + { + if($user instanceof IUser) + { + $verb=strtolower(trim($verb)); + foreach($this as $rule) + { + if(($decision=$rule->isUserAllowed($user,$verb,$ip))!==0) + return ($decision>0); + } + return true; + } + else + return false; + } + + /** + * Inserts an item at the specified position. + * This overrides the parent implementation by performing additional + * operations for each newly added TAuthorizationRule object. + * @param integer the specified position. + * @param mixed new item + * @throws TInvalidDataTypeException if the item to be inserted is not a TAuthorizationRule object. + */ + public function insertAt($index,$item) + { + if($item instanceof TAuthorizationRule) + parent::insertAt($index,$item); + else + throw new TInvalidDataTypeException('authorizationrulecollection_authorizationrule_required'); + } +} + diff --git a/framework/Security/TDbUserManager.php b/framework/Security/TDbUserManager.php index 11dcdadd..8c875148 100644 --- a/framework/Security/TDbUserManager.php +++ b/framework/Security/TDbUserManager.php @@ -1,320 +1,320 @@ - - * @link http://www.pradosoft.com/ + + * @link http://www.pradosoft.com/ * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id$ - * @package System.Security - */ - -/** - * Using IUserManager interface - */ -Prado::using('System.Security.IUserManager'); -Prado::using('System.Data.TDataSourceConfig'); -Prado::using('System.Security.TUser'); - -/** - * TDbUserManager class - * - * TDbUserManager manages user accounts that are stored in a database. - * TDbUserManager is mainly designed to be used together with {@link TAuthManager} - * which manages how users are authenticated and authorized in a Prado application. - * - * To use TDbUserManager together with TAuthManager, configure them in - * the application configuration like following: - * - * - * - * - * - * - * In the above, {@link setUserClass UserClass} specifies what class will be used - * to create user instance. The class must extend from {@link TDbUser}. - * {@link setConnectionID ConnectionID} refers to the ID of a {@link TDataSourceConfig} module - * which specifies how to establish database connection to retrieve user information. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Security - * @since 3.1.0 - */ -class TDbUserManager extends TModule implements IUserManager -{ - private $_connID=''; - private $_conn; - private $_guestName='Guest'; - private $_userClass=''; - private $_userFactory; - - /** - * Initializes the module. - * This method is required by IModule and is invoked by application. - * @param TXmlElement module configuration - */ - public function init($config) - { - if($this->_userClass==='') - throw new TConfigurationException('dbusermanager_userclass_required'); - $this->_userFactory=Prado::createComponent($this->_userClass,$this); - if(!($this->_userFactory instanceof TDbUser)) - throw new TInvalidDataTypeException('dbusermanager_userclass_invalid',$this->_userClass); - } - - /** - * @return string the user class name in namespace format. Defaults to empty string, meaning not set. - */ - public function getUserClass() - { - return $this->_userClass; - } - - /** - * @param string the user class name in namespace format. The user class must extend from {@link TDbUser}. - */ - public function setUserClass($value) - { - $this->_userClass=$value; - } - - /** - * @return string guest name, defaults to 'Guest' - */ - public function getGuestName() - { - return $this->_guestName; - } - - /** - * @param string name to be used for guest users. - */ - public function setGuestName($value) - { - $this->_guestName=$value; - } - - /** - * Validates if the username and password are correct. - * @param string user name - * @param string password - * @return boolean true if validation is successful, false otherwise. - */ - public function validateUser($username,$password) - { - return $this->_userFactory->validateUser($username,$password); - } - - /** - * Returns a user instance given the user name. - * @param string user name, null if it is a guest. - * @return TUser the user instance, null if the specified username is not in the user database. - */ - public function getUser($username=null) - { - if($username===null) - { - $user=Prado::createComponent($this->_userClass,$this); - $user->setIsGuest(true); - return $user; - } - else - return $this->_userFactory->createUser($username); - } - - /** - * @return string the ID of a TDataSourceConfig module. Defaults to empty string, meaning not set. - */ - public function getConnectionID() - { - return $this->_connID; - } - - /** - * Sets the ID of a TDataSourceConfig module. - * The datasource module will be used to establish the DB connection - * that will be used by the user manager. - * @param string module ID. - */ - public function setConnectionID($value) - { - $this->_connID=$value; - } - - /** - * @return TDbConnection the database connection that may be used to retrieve user data. - */ - public function getDbConnection() - { - if($this->_conn===null) - { - $this->_conn=$this->createDbConnection($this->_connID); - $this->_conn->setActive(true); - } - return $this->_conn; - } - - /** - * Creates the DB connection. - * @param string the module ID for TDataSourceConfig - * @return TDbConnection the created DB connection - * @throws TConfigurationException if module ID is invalid or empty - */ - protected function createDbConnection($connectionID) - { - if($connectionID!=='') - { - $conn=$this->getApplication()->getModule($connectionID); - if($conn instanceof TDataSourceConfig) - return $conn->getDbConnection(); - else - throw new TConfigurationException('dbusermanager_connectionid_invalid',$connectionID); - } - 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); - } -} - - -/** - * TDbUser class - * - * TDbUser is the base user class for using together with {@link TDbUserManager}. - * Two methods are declared and must be implemented in the descendant classes: - * - {@link validateUser()}: validates if username and password are correct entries. - * - {@link createUser()}: creates a new user instance given the username - * - * @author Qiang Xue - * @version $Id$ - * @package System.Security - * @since 3.1.0 - */ -abstract class TDbUser extends TUser -{ - private $_connection; - - /** - * Returns a database connection that may be used to retrieve data from database. - * - * @return TDbConnection database connection that may be used to retrieve data from database - */ - public function getDbConnection() - { - if($this->_connection===null) - { - $userManager=$this->getManager(); - if($userManager instanceof TDbUserManager) - { - $connection=$userManager->getDbConnection(); - if($connection instanceof TDbConnection) - { - $connection->setActive(true); - $this->_connection=$connection; - } - } - if($this->_connection===null) - throw new TConfigurationException('dbuser_dbconnection_invalid'); - } - return $this->_connection; - } - - /** - * Validates if username and password are correct entries. - * Usually, this is accomplished by checking if the user database - * contains this (username, password) pair. - * You may use {@link getDbConnection DbConnection} to deal with database. - * @param string username (case-sensitive) - * @param string password - * @return boolean whether the validation succeeds - */ - abstract public function validateUser($username,$password); - - /** - * Creates a new user instance given the username. - * This method usually needs to retrieve necessary user information - * (e.g. role, name, rank, etc.) from the user database according to - * the specified username. The newly created user instance should be - * initialized with these information. - * - * If the username is invalid (not found in the user database), null - * should be returned. - * - * You may use {@link getDbConnection DbConnection} to deal with database. - * - * @param string username (case-sensitive) - * @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) - { - } -} - + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Security + */ + +/** + * Using IUserManager interface + */ +Prado::using('System.Security.IUserManager'); +Prado::using('System.Data.TDataSourceConfig'); +Prado::using('System.Security.TUser'); + +/** + * TDbUserManager class + * + * TDbUserManager manages user accounts that are stored in a database. + * TDbUserManager is mainly designed to be used together with {@link TAuthManager} + * which manages how users are authenticated and authorized in a Prado application. + * + * To use TDbUserManager together with TAuthManager, configure them in + * the application configuration like following: + * + * + * + * + * + * + * In the above, {@link setUserClass UserClass} specifies what class will be used + * to create user instance. The class must extend from {@link TDbUser}. + * {@link setConnectionID ConnectionID} refers to the ID of a {@link TDataSourceConfig} module + * which specifies how to establish database connection to retrieve user information. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Security + * @since 3.1.0 + */ +class TDbUserManager extends TModule implements IUserManager +{ + private $_connID=''; + private $_conn; + private $_guestName='Guest'; + private $_userClass=''; + private $_userFactory; + + /** + * Initializes the module. + * This method is required by IModule and is invoked by application. + * @param TXmlElement module configuration + */ + public function init($config) + { + if($this->_userClass==='') + throw new TConfigurationException('dbusermanager_userclass_required'); + $this->_userFactory=Prado::createComponent($this->_userClass,$this); + if(!($this->_userFactory instanceof TDbUser)) + throw new TInvalidDataTypeException('dbusermanager_userclass_invalid',$this->_userClass); + } + + /** + * @return string the user class name in namespace format. Defaults to empty string, meaning not set. + */ + public function getUserClass() + { + return $this->_userClass; + } + + /** + * @param string the user class name in namespace format. The user class must extend from {@link TDbUser}. + */ + public function setUserClass($value) + { + $this->_userClass=$value; + } + + /** + * @return string guest name, defaults to 'Guest' + */ + public function getGuestName() + { + return $this->_guestName; + } + + /** + * @param string name to be used for guest users. + */ + public function setGuestName($value) + { + $this->_guestName=$value; + } + + /** + * Validates if the username and password are correct. + * @param string user name + * @param string password + * @return boolean true if validation is successful, false otherwise. + */ + public function validateUser($username,$password) + { + return $this->_userFactory->validateUser($username,$password); + } + + /** + * Returns a user instance given the user name. + * @param string user name, null if it is a guest. + * @return TUser the user instance, null if the specified username is not in the user database. + */ + public function getUser($username=null) + { + if($username===null) + { + $user=Prado::createComponent($this->_userClass,$this); + $user->setIsGuest(true); + return $user; + } + else + return $this->_userFactory->createUser($username); + } + + /** + * @return string the ID of a TDataSourceConfig module. Defaults to empty string, meaning not set. + */ + public function getConnectionID() + { + return $this->_connID; + } + + /** + * Sets the ID of a TDataSourceConfig module. + * The datasource module will be used to establish the DB connection + * that will be used by the user manager. + * @param string module ID. + */ + public function setConnectionID($value) + { + $this->_connID=$value; + } + + /** + * @return TDbConnection the database connection that may be used to retrieve user data. + */ + public function getDbConnection() + { + if($this->_conn===null) + { + $this->_conn=$this->createDbConnection($this->_connID); + $this->_conn->setActive(true); + } + return $this->_conn; + } + + /** + * Creates the DB connection. + * @param string the module ID for TDataSourceConfig + * @return TDbConnection the created DB connection + * @throws TConfigurationException if module ID is invalid or empty + */ + protected function createDbConnection($connectionID) + { + if($connectionID!=='') + { + $conn=$this->getApplication()->getModule($connectionID); + if($conn instanceof TDataSourceConfig) + return $conn->getDbConnection(); + else + throw new TConfigurationException('dbusermanager_connectionid_invalid',$connectionID); + } + 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); + } +} + + +/** + * TDbUser class + * + * TDbUser is the base user class for using together with {@link TDbUserManager}. + * Two methods are declared and must be implemented in the descendant classes: + * - {@link validateUser()}: validates if username and password are correct entries. + * - {@link createUser()}: creates a new user instance given the username + * + * @author Qiang Xue + * @version $Id$ + * @package System.Security + * @since 3.1.0 + */ +abstract class TDbUser extends TUser +{ + private $_connection; + + /** + * Returns a database connection that may be used to retrieve data from database. + * + * @return TDbConnection database connection that may be used to retrieve data from database + */ + public function getDbConnection() + { + if($this->_connection===null) + { + $userManager=$this->getManager(); + if($userManager instanceof TDbUserManager) + { + $connection=$userManager->getDbConnection(); + if($connection instanceof TDbConnection) + { + $connection->setActive(true); + $this->_connection=$connection; + } + } + if($this->_connection===null) + throw new TConfigurationException('dbuser_dbconnection_invalid'); + } + return $this->_connection; + } + + /** + * Validates if username and password are correct entries. + * Usually, this is accomplished by checking if the user database + * contains this (username, password) pair. + * You may use {@link getDbConnection DbConnection} to deal with database. + * @param string username (case-sensitive) + * @param string password + * @return boolean whether the validation succeeds + */ + abstract public function validateUser($username,$password); + + /** + * Creates a new user instance given the username. + * This method usually needs to retrieve necessary user information + * (e.g. role, name, rank, etc.) from the user database according to + * the specified username. The newly created user instance should be + * initialized with these information. + * + * If the username is invalid (not found in the user database), null + * should be returned. + * + * You may use {@link getDbConnection DbConnection} to deal with database. + * + * @param string username (case-sensitive) + * @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) + { + } +} + diff --git a/framework/Security/TUser.php b/framework/Security/TUser.php index b282234b..2b367255 100644 --- a/framework/Security/TUser.php +++ b/framework/Security/TUser.php @@ -1,222 +1,222 @@ - - * @link http://www.pradosoft.com/ + + * @link http://www.pradosoft.com/ * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id$ - * @package System.Security - */ - -/** - * Using IUserManager interface - */ -Prado::using('System.Security.IUserManager'); - -/** - * TUser class - * - * TUser implements basic user functionality for a Prado application. - * To get the name of the user, use {@link getName Name} property. - * The property {@link getIsGuest IsGuest} tells if the user a guest/anonymous user. - * To obtain or test the roles that the user is in, use property - * {@link getRoles Roles} and call {@link isInRole()}, respectively. - * - * TUser is meant to be used together with {@link IUserManager}. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Security - * @since 3.0 - */ -class TUser extends TComponent implements IUser -{ - /** - * @var array persistent state - */ - private $_state; - /** - * @var boolean whether user state is changed - */ - private $_stateChanged=false; - /** - * @var IUserManager user manager - */ - private $_manager; - - /** - * Constructor. - * @param IUserManager user manager - */ - public function __construct(IUserManager $manager) - { - $this->_state=array(); - $this->_manager=$manager; - $this->setName($manager->getGuestName()); - } - - /** - * @return IUserManager user manager - */ - public function getManager() - { - return $this->_manager; - } - - /** - * @return string username, defaults to empty string. - */ - public function getName() - { - return $this->getState('Name',''); - } - - /** - * @param string username - */ - public function setName($value) - { - $this->setState('Name',$value,''); - } - - /** - * @return boolean if the user is a guest, defaults to true. - */ - public function getIsGuest() - { - return $this->getState('IsGuest',true); - } - - /** - * @param boolean if the user is a guest - */ - public function setIsGuest($value) - { - if($isGuest=TPropertyValue::ensureBoolean($value)) - { - $this->setName($this->_manager->getGuestName()); - $this->setRoles(array()); - } - $this->setState('IsGuest',$isGuest); - } - - /** - * @return array list of roles that the user is of - */ - public function getRoles() - { - return $this->getState('Roles',array()); - } - - /** - * @return array|string list of roles that the user is of. If it is a string, roles are assumed by separated by comma - */ - public function setRoles($value) - { - if(is_array($value)) - $this->setState('Roles',$value,array()); - else - { - $roles=array(); - foreach(explode(',',$value) as $role) - { - if(($role=trim($role))!=='') - $roles[]=$role; - } - $this->setState('Roles',$roles,array()); - } - } - - /** - * @param string role to be tested. Note, role is case-insensitive. - * @return boolean whether the user is of this role - */ - public function isInRole($role) - { - foreach($this->getRoles() as $r) - if(strcasecmp($role,$r)===0) - return true; - return false; - } - - /** - * @return string user data that is serialized and will be stored in session - */ - public function saveToString() - { - return serialize($this->_state); - } - - /** - * @param string user data that is serialized and restored from session - * @return IUser the user object - */ - public function loadFromString($data) - { - if(!empty($data)) - $this->_state=unserialize($data); - if(!is_array($this->_state)) - $this->_state=array(); - return $this; - } - - /** - * Returns the value of a variable that is stored in user session. - * - * This function is designed to be used by TUser descendant classes - * who want to store additional user information in user session. - * A variable, if stored in user session using {@link setState} can be - * retrieved back using this function. - * - * @param string variable name - * @param mixed default value - * @return mixed the value of the variable. If it doesn't exist, the provided default value will be returned - * @see setState - */ - protected function getState($key,$defaultValue=null) - { - return isset($this->_state[$key])?$this->_state[$key]:$defaultValue; - } - - /** - * Stores a variable in user session. - * - * This function is designed to be used by TUser descendant classes - * who want to store additional user information in user session. - * By storing a variable using this function, the variable may be retrieved - * back later using {@link getState}. The variable will be persistent - * across page requests during a user session. - * - * @param string variable name - * @param mixed variable value - * @param mixed default value. If $value===$defaultValue, the variable will be removed from persistent storage. - * @see getState - */ - protected function setState($key,$value,$defaultValue=null) - { - if($value===$defaultValue) - unset($this->_state[$key]); - else - $this->_state[$key]=$value; - $this->_stateChanged=true; - } - - /** - * @return boolean whether user session state is changed (i.e., setState() is called) - */ - public function getStateChanged() - { - return $this->_stateChanged; - } - - /** - * @param boolean whether user session state is changed - */ - public function setStateChanged($value) - { - $this->_stateChanged=TPropertyValue::ensureBoolean($value); - } -} - + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Security + */ + +/** + * Using IUserManager interface + */ +Prado::using('System.Security.IUserManager'); + +/** + * TUser class + * + * TUser implements basic user functionality for a Prado application. + * To get the name of the user, use {@link getName Name} property. + * The property {@link getIsGuest IsGuest} tells if the user a guest/anonymous user. + * To obtain or test the roles that the user is in, use property + * {@link getRoles Roles} and call {@link isInRole()}, respectively. + * + * TUser is meant to be used together with {@link IUserManager}. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Security + * @since 3.0 + */ +class TUser extends TComponent implements IUser +{ + /** + * @var array persistent state + */ + private $_state; + /** + * @var boolean whether user state is changed + */ + private $_stateChanged=false; + /** + * @var IUserManager user manager + */ + private $_manager; + + /** + * Constructor. + * @param IUserManager user manager + */ + public function __construct(IUserManager $manager) + { + $this->_state=array(); + $this->_manager=$manager; + $this->setName($manager->getGuestName()); + } + + /** + * @return IUserManager user manager + */ + public function getManager() + { + return $this->_manager; + } + + /** + * @return string username, defaults to empty string. + */ + public function getName() + { + return $this->getState('Name',''); + } + + /** + * @param string username + */ + public function setName($value) + { + $this->setState('Name',$value,''); + } + + /** + * @return boolean if the user is a guest, defaults to true. + */ + public function getIsGuest() + { + return $this->getState('IsGuest',true); + } + + /** + * @param boolean if the user is a guest + */ + public function setIsGuest($value) + { + if($isGuest=TPropertyValue::ensureBoolean($value)) + { + $this->setName($this->_manager->getGuestName()); + $this->setRoles(array()); + } + $this->setState('IsGuest',$isGuest); + } + + /** + * @return array list of roles that the user is of + */ + public function getRoles() + { + return $this->getState('Roles',array()); + } + + /** + * @return array|string list of roles that the user is of. If it is a string, roles are assumed by separated by comma + */ + public function setRoles($value) + { + if(is_array($value)) + $this->setState('Roles',$value,array()); + else + { + $roles=array(); + foreach(explode(',',$value) as $role) + { + if(($role=trim($role))!=='') + $roles[]=$role; + } + $this->setState('Roles',$roles,array()); + } + } + + /** + * @param string role to be tested. Note, role is case-insensitive. + * @return boolean whether the user is of this role + */ + public function isInRole($role) + { + foreach($this->getRoles() as $r) + if(strcasecmp($role,$r)===0) + return true; + return false; + } + + /** + * @return string user data that is serialized and will be stored in session + */ + public function saveToString() + { + return serialize($this->_state); + } + + /** + * @param string user data that is serialized and restored from session + * @return IUser the user object + */ + public function loadFromString($data) + { + if(!empty($data)) + $this->_state=unserialize($data); + if(!is_array($this->_state)) + $this->_state=array(); + return $this; + } + + /** + * Returns the value of a variable that is stored in user session. + * + * This function is designed to be used by TUser descendant classes + * who want to store additional user information in user session. + * A variable, if stored in user session using {@link setState} can be + * retrieved back using this function. + * + * @param string variable name + * @param mixed default value + * @return mixed the value of the variable. If it doesn't exist, the provided default value will be returned + * @see setState + */ + protected function getState($key,$defaultValue=null) + { + return isset($this->_state[$key])?$this->_state[$key]:$defaultValue; + } + + /** + * Stores a variable in user session. + * + * This function is designed to be used by TUser descendant classes + * who want to store additional user information in user session. + * By storing a variable using this function, the variable may be retrieved + * back later using {@link getState}. The variable will be persistent + * across page requests during a user session. + * + * @param string variable name + * @param mixed variable value + * @param mixed default value. If $value===$defaultValue, the variable will be removed from persistent storage. + * @see getState + */ + protected function setState($key,$value,$defaultValue=null) + { + if($value===$defaultValue) + unset($this->_state[$key]); + else + $this->_state[$key]=$value; + $this->_stateChanged=true; + } + + /** + * @return boolean whether user session state is changed (i.e., setState() is called) + */ + public function getStateChanged() + { + return $this->_stateChanged; + } + + /** + * @param boolean whether user session state is changed + */ + public function setStateChanged($value) + { + $this->_stateChanged=TPropertyValue::ensureBoolean($value); + } +} + diff --git a/framework/Security/TUserManager.php b/framework/Security/TUserManager.php index 418da79c..c66ce8ad 100644 --- a/framework/Security/TUserManager.php +++ b/framework/Security/TUserManager.php @@ -1,402 +1,402 @@ - - * @link http://www.pradosoft.com/ + + * @link http://www.pradosoft.com/ * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id$ - * @package System.Security - */ - -/** - * Using TUser class - */ -Prado::using('System.Security.TUser'); - -/** - * TUserManager class - * - * TUserManager manages a static list of users {@link TUser}. - * The user information is specified via module configuration using the following XML syntax, - * - * - * - * - * - * - * - * - * - * PHP configuration style: - * - * array( - * 'users' => array( - * 'class' => 'System.Security.TUserManager', - * 'properties' => array( - * 'PasswordMode' => 'Clear', - * ), - * 'users' => array( - * array('name'=>'Joe','password'=>'demo'), - * array('name'=>'John','password'=>'demo'), - * ), - * 'roles' => array( - * array('name'=>'Administrator','users'=>'John'), - * array('name'=>'Writer','users'=>'Joe,John'), - * ), - * ), - * ) - * - * - * In addition, user information can also be loaded from an external file - * specified by {@link setUserFile UserFile} property. Note, the property - * only accepts a file path in namespace format. The user file format is - * similar to the above sample. - * - * The user passwords may be specified as clear text, SH1 or MD5 hashed by setting - * {@link setPasswordMode PasswordMode} as Clear, SHA1 or MD5. - * The default name for a guest user is Guest. It may be changed - * by setting {@link setGuestName GuestName} property. - * - * TUserManager may be used together with {@link TAuthManager} which manages - * how users are authenticated and authorized in a Prado application. - * - * @author Qiang Xue - * @author Carl Mathisen - * @version $Id$ - * @package System.Security - * @since 3.0 - */ -class TUserManager extends TModule implements IUserManager -{ - /** - * extension name to the user file - */ - const USER_FILE_EXT='.xml'; - - /** - * @var array list of users managed by this module - */ - private $_users=array(); - /** - * @var array list of roles managed by this module - */ - private $_roles=array(); - /** - * @var string guest name - */ - private $_guestName='Guest'; - /** - * @var TUserManagerPasswordMode password mode - */ - private $_passwordMode=TUserManagerPasswordMode::MD5; - /** - * @var boolean whether the module has been initialized - */ - private $_initialized=false; - /** - * @var string user/role information file - */ - private $_userFile=null; - - /** - * Initializes the module. - * This method is required by IModule and is invoked by application. - * It loads user/role information from the module configuration. - * @param mixed module configuration - */ - public function init($config) - { - $this->loadUserData($config); - if($this->_userFile!==null) - { - if($this->getApplication()->getConfigurationType()==TApplication::CONFIG_TYPE_PHP) - { - $userFile = include $this->_userFile; - $this->loadUserDataFromPhp($userFile); - } - else - { - $dom=new TXmlDocument; - $dom->loadFromFile($this->_userFile); - $this->loadUserDataFromXml($dom); - } - } - $this->_initialized=true; - } - - /* - * Loads user/role information - * @param mixed the variable containing the user information - */ - private function loadUserData($config) - { - if($this->getApplication()->getConfigurationType()==TApplication::CONFIG_TYPE_PHP) - $this->loadUserDataFromPhp($config); - else - $this->loadUserDataFromXml($config); - } - - /** - * Loads user/role information from an php array. - * @param array the array containing the user information - */ - private function loadUserDataFromPhp($config) - { - if(isset($config['users']) && is_array($config['users'])) - { - foreach($config['users'] as $user) - { - $name = trim(strtolower(isset($user['name'])?$user['name']:'')); - $password = isset($user['password'])?$user['password']:''; - $this->_users[$name] = $password; - $roles = isset($user['roles'])?$user['roles']:''; - if($roles!=='') - { - foreach(explode(',',$roles) as $role) - { - if(($role=trim($role))!=='') - $this->_roles[$name][]=$role; - } - } - } - } - if(isset($config['roles']) && is_array($config['roles'])) - { - foreach($config['roles'] as $role) - { - $name = isset($role['name'])?$role['name']:''; - $users = isset($role['users'])?$role['users']:''; - foreach(explode(',',$users) as $user) - { - if(($user=trim($user))!=='') - $this->_roles[strtolower($user)][]=$name; - } - } - } - } - - /** - * Loads user/role information from an XML node. - * @param TXmlElement the XML node containing the user information - */ - private function loadUserDataFromXml($xmlNode) - { - foreach($xmlNode->getElementsByTagName('user') as $node) - { - $name=trim(strtolower($node->getAttribute('name'))); - $this->_users[$name]=$node->getAttribute('password'); - if(($roles=trim($node->getAttribute('roles')))!=='') - { - foreach(explode(',',$roles) as $role) - { - if(($role=trim($role))!=='') - $this->_roles[$name][]=$role; - } - } - } - foreach($xmlNode->getElementsByTagName('role') as $node) - { - foreach(explode(',',$node->getAttribute('users')) as $user) - { - if(($user=trim($user))!=='') - $this->_roles[strtolower($user)][]=$node->getAttribute('name'); - } - } - } - - /** - * Returns an array of all users. - * Each array element represents a single user. - * The array key is the username in lower case, and the array value is the - * corresponding user password. - * @return array list of users - */ - public function getUsers() - { - return $this->_users; - } - - /** - * Returns an array of user role information. - * Each array element represents the roles for a single user. - * The array key is the username in lower case, and the array value is - * the roles (represented as an array) that the user is in. - * @return array list of user role information - */ - public function getRoles() - { - return $this->_roles; - } - - /** - * @return string the full path to the file storing user/role information - */ - public function getUserFile() - { - return $this->_userFile; - } - - /** - * @param string user/role data file path (in namespace form). The file format is XML - * whose content is similar to that user/role block in application configuration. - * @throws TInvalidOperationException if the module is already initialized - * @throws TConfigurationException if the file is not in proper namespace format - */ - public function setUserFile($value) - { - if($this->_initialized) - throw new TInvalidOperationException('usermanager_userfile_unchangeable'); - else if(($this->_userFile=Prado::getPathOfNamespace($value,self::USER_FILE_EXT))===null || !is_file($this->_userFile)) - throw new TConfigurationException('usermanager_userfile_invalid',$value); - } - - /** - * @return string guest name, defaults to 'Guest' - */ - public function getGuestName() - { - return $this->_guestName; - } - - /** - * @param string name to be used for guest users. - */ - public function setGuestName($value) - { - $this->_guestName=$value; - } - - /** - * @return TUserManagerPasswordMode how password is stored, clear text, or MD5 or SHA1 hashed. Default to TUserManagerPasswordMode::MD5. - */ - public function getPasswordMode() - { - return $this->_passwordMode; - } - - /** - * @param TUserManagerPasswordMode how password is stored, clear text, or MD5 or SHA1 hashed. - */ - public function setPasswordMode($value) - { - $this->_passwordMode=TPropertyValue::ensureEnum($value,'TUserManagerPasswordMode'); - } - - /** - * Validates if the username and password are correct. - * @param string user name - * @param string password - * @return boolean true if validation is successful, false otherwise. - */ - public function validateUser($username,$password) - { - if($this->_passwordMode===TUserManagerPasswordMode::MD5) - $password=md5($password); - else if($this->_passwordMode===TUserManagerPasswordMode::SHA1) - $password=sha1($password); - $username=strtolower($username); - return (isset($this->_users[$username]) && $this->_users[$username]===$password); - } - - /** - * Returns a user instance given the user name. - * @param string user name, null if it is a guest. - * @return TUser the user instance, null if the specified username is not in the user database. - */ - public function getUser($username=null) - { - if($username===null) - { - $user=new TUser($this); - $user->setIsGuest(true); - return $user; - } - else - { - $username=strtolower($username); - if(isset($this->_users[$username])) - { - $user=new TUser($this); - $user->setName($username); - $user->setIsGuest(false); - if(isset($this->_roles[$username])) - $user->setRoles($this->_roles[$username]); - return $user; - } - else - return 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) - { - 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. - * @param TUser the user to be changed to a guest. - */ - public function switchToGuest($user) - { - $user->setIsGuest(true); - } -} - -/** - * TUserManagerPasswordMode class. - * TUserManagerPasswordMode defines the enumerable type for the possible modes - * that user passwords can be specified for a {@link TUserManager}. - * - * The following enumerable values are defined: - * - Clear: the password is in plain text - * - MD5: the password is recorded as the MD5 hash value of the original password - * - SHA1: the password is recorded as the SHA1 hash value of the original password - * - * @author Qiang Xue - * @version $Id$ - * @package System.Security - * @since 3.0.4 - */ -class TUserManagerPasswordMode extends TEnumerable -{ - const Clear='Clear'; - const MD5='MD5'; - const SHA1='SHA1'; -} - + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Security + */ + +/** + * Using TUser class + */ +Prado::using('System.Security.TUser'); + +/** + * TUserManager class + * + * TUserManager manages a static list of users {@link TUser}. + * The user information is specified via module configuration using the following XML syntax, + * + * + * + * + * + * + * + * + * + * PHP configuration style: + * + * array( + * 'users' => array( + * 'class' => 'System.Security.TUserManager', + * 'properties' => array( + * 'PasswordMode' => 'Clear', + * ), + * 'users' => array( + * array('name'=>'Joe','password'=>'demo'), + * array('name'=>'John','password'=>'demo'), + * ), + * 'roles' => array( + * array('name'=>'Administrator','users'=>'John'), + * array('name'=>'Writer','users'=>'Joe,John'), + * ), + * ), + * ) + * + * + * In addition, user information can also be loaded from an external file + * specified by {@link setUserFile UserFile} property. Note, the property + * only accepts a file path in namespace format. The user file format is + * similar to the above sample. + * + * The user passwords may be specified as clear text, SH1 or MD5 hashed by setting + * {@link setPasswordMode PasswordMode} as Clear, SHA1 or MD5. + * The default name for a guest user is Guest. It may be changed + * by setting {@link setGuestName GuestName} property. + * + * TUserManager may be used together with {@link TAuthManager} which manages + * how users are authenticated and authorized in a Prado application. + * + * @author Qiang Xue + * @author Carl Mathisen + * @version $Id$ + * @package System.Security + * @since 3.0 + */ +class TUserManager extends TModule implements IUserManager +{ + /** + * extension name to the user file + */ + const USER_FILE_EXT='.xml'; + + /** + * @var array list of users managed by this module + */ + private $_users=array(); + /** + * @var array list of roles managed by this module + */ + private $_roles=array(); + /** + * @var string guest name + */ + private $_guestName='Guest'; + /** + * @var TUserManagerPasswordMode password mode + */ + private $_passwordMode=TUserManagerPasswordMode::MD5; + /** + * @var boolean whether the module has been initialized + */ + private $_initialized=false; + /** + * @var string user/role information file + */ + private $_userFile=null; + + /** + * Initializes the module. + * This method is required by IModule and is invoked by application. + * It loads user/role information from the module configuration. + * @param mixed module configuration + */ + public function init($config) + { + $this->loadUserData($config); + if($this->_userFile!==null) + { + if($this->getApplication()->getConfigurationType()==TApplication::CONFIG_TYPE_PHP) + { + $userFile = include $this->_userFile; + $this->loadUserDataFromPhp($userFile); + } + else + { + $dom=new TXmlDocument; + $dom->loadFromFile($this->_userFile); + $this->loadUserDataFromXml($dom); + } + } + $this->_initialized=true; + } + + /* + * Loads user/role information + * @param mixed the variable containing the user information + */ + private function loadUserData($config) + { + if($this->getApplication()->getConfigurationType()==TApplication::CONFIG_TYPE_PHP) + $this->loadUserDataFromPhp($config); + else + $this->loadUserDataFromXml($config); + } + + /** + * Loads user/role information from an php array. + * @param array the array containing the user information + */ + private function loadUserDataFromPhp($config) + { + if(isset($config['users']) && is_array($config['users'])) + { + foreach($config['users'] as $user) + { + $name = trim(strtolower(isset($user['name'])?$user['name']:'')); + $password = isset($user['password'])?$user['password']:''; + $this->_users[$name] = $password; + $roles = isset($user['roles'])?$user['roles']:''; + if($roles!=='') + { + foreach(explode(',',$roles) as $role) + { + if(($role=trim($role))!=='') + $this->_roles[$name][]=$role; + } + } + } + } + if(isset($config['roles']) && is_array($config['roles'])) + { + foreach($config['roles'] as $role) + { + $name = isset($role['name'])?$role['name']:''; + $users = isset($role['users'])?$role['users']:''; + foreach(explode(',',$users) as $user) + { + if(($user=trim($user))!=='') + $this->_roles[strtolower($user)][]=$name; + } + } + } + } + + /** + * Loads user/role information from an XML node. + * @param TXmlElement the XML node containing the user information + */ + private function loadUserDataFromXml($xmlNode) + { + foreach($xmlNode->getElementsByTagName('user') as $node) + { + $name=trim(strtolower($node->getAttribute('name'))); + $this->_users[$name]=$node->getAttribute('password'); + if(($roles=trim($node->getAttribute('roles')))!=='') + { + foreach(explode(',',$roles) as $role) + { + if(($role=trim($role))!=='') + $this->_roles[$name][]=$role; + } + } + } + foreach($xmlNode->getElementsByTagName('role') as $node) + { + foreach(explode(',',$node->getAttribute('users')) as $user) + { + if(($user=trim($user))!=='') + $this->_roles[strtolower($user)][]=$node->getAttribute('name'); + } + } + } + + /** + * Returns an array of all users. + * Each array element represents a single user. + * The array key is the username in lower case, and the array value is the + * corresponding user password. + * @return array list of users + */ + public function getUsers() + { + return $this->_users; + } + + /** + * Returns an array of user role information. + * Each array element represents the roles for a single user. + * The array key is the username in lower case, and the array value is + * the roles (represented as an array) that the user is in. + * @return array list of user role information + */ + public function getRoles() + { + return $this->_roles; + } + + /** + * @return string the full path to the file storing user/role information + */ + public function getUserFile() + { + return $this->_userFile; + } + + /** + * @param string user/role data file path (in namespace form). The file format is XML + * whose content is similar to that user/role block in application configuration. + * @throws TInvalidOperationException if the module is already initialized + * @throws TConfigurationException if the file is not in proper namespace format + */ + public function setUserFile($value) + { + if($this->_initialized) + throw new TInvalidOperationException('usermanager_userfile_unchangeable'); + else if(($this->_userFile=Prado::getPathOfNamespace($value,self::USER_FILE_EXT))===null || !is_file($this->_userFile)) + throw new TConfigurationException('usermanager_userfile_invalid',$value); + } + + /** + * @return string guest name, defaults to 'Guest' + */ + public function getGuestName() + { + return $this->_guestName; + } + + /** + * @param string name to be used for guest users. + */ + public function setGuestName($value) + { + $this->_guestName=$value; + } + + /** + * @return TUserManagerPasswordMode how password is stored, clear text, or MD5 or SHA1 hashed. Default to TUserManagerPasswordMode::MD5. + */ + public function getPasswordMode() + { + return $this->_passwordMode; + } + + /** + * @param TUserManagerPasswordMode how password is stored, clear text, or MD5 or SHA1 hashed. + */ + public function setPasswordMode($value) + { + $this->_passwordMode=TPropertyValue::ensureEnum($value,'TUserManagerPasswordMode'); + } + + /** + * Validates if the username and password are correct. + * @param string user name + * @param string password + * @return boolean true if validation is successful, false otherwise. + */ + public function validateUser($username,$password) + { + if($this->_passwordMode===TUserManagerPasswordMode::MD5) + $password=md5($password); + else if($this->_passwordMode===TUserManagerPasswordMode::SHA1) + $password=sha1($password); + $username=strtolower($username); + return (isset($this->_users[$username]) && $this->_users[$username]===$password); + } + + /** + * Returns a user instance given the user name. + * @param string user name, null if it is a guest. + * @return TUser the user instance, null if the specified username is not in the user database. + */ + public function getUser($username=null) + { + if($username===null) + { + $user=new TUser($this); + $user->setIsGuest(true); + return $user; + } + else + { + $username=strtolower($username); + if(isset($this->_users[$username])) + { + $user=new TUser($this); + $user->setName($username); + $user->setIsGuest(false); + if(isset($this->_roles[$username])) + $user->setRoles($this->_roles[$username]); + return $user; + } + else + return 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) + { + 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. + * @param TUser the user to be changed to a guest. + */ + public function switchToGuest($user) + { + $user->setIsGuest(true); + } +} + +/** + * TUserManagerPasswordMode class. + * TUserManagerPasswordMode defines the enumerable type for the possible modes + * that user passwords can be specified for a {@link TUserManager}. + * + * The following enumerable values are defined: + * - Clear: the password is in plain text + * - MD5: the password is recorded as the MD5 hash value of the original password + * - SHA1: the password is recorded as the SHA1 hash value of the original password + * + * @author Qiang Xue + * @version $Id$ + * @package System.Security + * @since 3.0.4 + */ +class TUserManagerPasswordMode extends TEnumerable +{ + const Clear='Clear'; + const MD5='MD5'; + const SHA1='SHA1'; +} + -- cgit v1.2.3