From 6aa86712e2d5fa37bbd632dbab553bb9b4623a50 Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Thu, 28 Mar 2013 21:51:31 -0400 Subject: [PATCH] User WIP --- framework/web/Application.php | 30 ++++- framework/web/Controller.php | 15 +++ framework/web/Identity.php | 3 +- framework/web/Response.php | 65 ++++++----- framework/web/User.php | 252 +++++++++++------------------------------- 5 files changed, 147 insertions(+), 218 deletions(-) diff --git a/framework/web/Application.php b/framework/web/Application.php index 2533f04..b839d92 100644 --- a/framework/web/Application.php +++ b/framework/web/Application.php @@ -7,7 +7,7 @@ namespace yii\web; -use yii\base\InvalidParamException; +use Yii; /** * Application is the base class for all application classes. @@ -28,7 +28,7 @@ class Application extends \yii\base\Application public function registerDefaultAliases() { parent::registerDefaultAliases(); - \Yii::$aliases['@webroot'] = dirname($_SERVER['SCRIPT_FILENAME']); + Yii::$aliases['@webroot'] = dirname($_SERVER['SCRIPT_FILENAME']); } /** @@ -41,6 +41,32 @@ class Application extends \yii\base\Application return $this->runAction($route, $params); } + private $_homeUrl; + + /** + * @return string the homepage URL + */ + public function getHomeUrl() + { + if ($this->_homeUrl === null) { + if ($this->getUrlManager()->showScriptName) { + return $this->getRequest()->getScriptUrl(); + } else { + return $this->getRequest()->getBaseUrl() . '/'; + } + } else { + return $this->_homeUrl; + } + } + + /** + * @param string $value the homepage URL + */ + public function setHomeUrl($value) + { + $this->_homeUrl = $value; + } + /** * Returns the request component. * @return Request the request component diff --git a/framework/web/Controller.php b/framework/web/Controller.php index 93b74aa..126fbbc 100644 --- a/framework/web/Controller.php +++ b/framework/web/Controller.php @@ -8,6 +8,7 @@ namespace yii\web; use Yii; +use yii\helpers\Html; /** * Controller is the base class of Web controllers. @@ -41,4 +42,18 @@ class Controller extends \yii\base\Controller return Yii::$app->getUrlManager()->createUrl($route, $params); } + /** + * Redirects the browser to the specified URL or route (controller/action). + * @param mixed $url the URL to be redirected to. If the parameter is an array, + * the first element must be a route to a controller action and the rest + * are GET parameters in name-value pairs. + * @param boolean $terminate whether to terminate the current application after calling this method. Defaults to true. + * @param integer $statusCode the HTTP status code. Defaults to 302. See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} + * for details about HTTP status code. + */ + public function redirect($url, $terminate = true, $statusCode = 302) + { + $url = Html::url($url); + Yii::$app->getResponse()->redirect($url, $terminate, $statusCode); + } } \ No newline at end of file diff --git a/framework/web/Identity.php b/framework/web/Identity.php index 805d3d4..5f07d58 100644 --- a/framework/web/Identity.php +++ b/framework/web/Identity.php @@ -38,7 +38,8 @@ interface Identity * Finds an identity by the given ID. * @param string|integer $id the ID to be looked for * @return Identity the identity object that matches the given ID. - * Null should be returned if such an identity cannot be found. + * Null should be returned if such an identity cannot be found + * or the identity is not in an active state (disabled, deleted, etc.) */ public static function findIdentity($id); } \ No newline at end of file diff --git a/framework/web/Response.php b/framework/web/Response.php index d23c5b9..8133132 100644 --- a/framework/web/Response.php +++ b/framework/web/Response.php @@ -107,37 +107,42 @@ class Response extends \yii\base\Response *
  • addHeaders: an array of additional http headers in header-value pairs (available since version 1.1.10)
  • * */ - public function xSendFile($filePath, $options=array()) + public function xSendFile($filePath, $options = array()) { - if(!isset($options['forceDownload']) || $options['forceDownload']) - $disposition='attachment'; - else - $disposition='inline'; + if (!isset($options['forceDownload']) || $options['forceDownload']) { + $disposition = 'attachment'; + } else { + $disposition = 'inline'; + } - if(!isset($options['saveName'])) - $options['saveName']=basename($filePath); + if (!isset($options['saveName'])) { + $options['saveName'] = basename($filePath); + } - if(!isset($options['mimeType'])) - { - if(($options['mimeType']=CFileHelper::getMimeTypeByExtension($filePath))===null) - $options['mimeType']='text/plain'; + if (!isset($options['mimeType'])) { + if (($options['mimeType'] = CFileHelper::getMimeTypeByExtension($filePath)) === null) { + $options['mimeType'] = 'text/plain'; + } } - if(!isset($options['xHeader'])) - $options['xHeader']='X-Sendfile'; + if (!isset($options['xHeader'])) { + $options['xHeader'] = 'X-Sendfile'; + } - if($options['mimeType'] !== null) - header('Content-type: '.$options['mimeType']); - header('Content-Disposition: '.$disposition.'; filename="'.$options['saveName'].'"'); - if(isset($options['addHeaders'])) - { - foreach($options['addHeaders'] as $header=>$value) - header($header.': '.$value); + if ($options['mimeType'] !== null) { + header('Content-type: ' . $options['mimeType']); + } + header('Content-Disposition: ' . $disposition . '; filename="' . $options['saveName'] . '"'); + if (isset($options['addHeaders'])) { + foreach ($options['addHeaders'] as $header => $value) { + header($header . ': ' . $value); + } } - header(trim($options['xHeader']).': '.$filePath); + header(trim($options['xHeader']) . ': ' . $filePath); - if(!isset($options['terminate']) || $options['terminate']) - Yii::app()->end(); + if (!isset($options['terminate']) || $options['terminate']) { + Yii::$app->end(); + } } /** @@ -148,13 +153,15 @@ class Response extends \yii\base\Response * @param integer $statusCode the HTTP status code. Defaults to 302. See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} * for details about HTTP status code. */ - public function redirect($url,$terminate=true,$statusCode=302) + public function redirect($url, $terminate = true, $statusCode = 302) { - if(strpos($url,'/')===0 && strpos($url,'//')!==0) - $url=$this->getHostInfo().$url; - header('Location: '.$url, true, $statusCode); - if($terminate) - Yii::app()->end(); + if (strpos($url, '/') === 0 && strpos($url, '//') !== 0) { + $url = Yii::$app->getRequest()->getHostInfo() . $url; + } + header('Location: ' . $url, true, $statusCode); + if ($terminate) { + Yii::$app->end(); + } } diff --git a/framework/web/User.php b/framework/web/User.php index 74b5f18..aa9e421 100644 --- a/framework/web/User.php +++ b/framework/web/User.php @@ -9,7 +9,9 @@ namespace yii\web; use Yii; use yii\base\Component; +use yii\base\HttpException; use yii\base\InvalidConfigException; +use yii\helpers\Html; /** * @author Qiang Xue @@ -17,15 +19,21 @@ use yii\base\InvalidConfigException; */ class User extends Component { - const ID_VAR = '__id'; - const AUTH_EXPIRE_VAR = '__expire'; - const EVENT_BEFORE_LOGIN = 'beforeLogin'; const EVENT_AFTER_LOGIN = 'afterLogin'; const EVENT_BEFORE_LOGOUT = 'beforeLogout'; const EVENT_AFTER_LOGOUT = 'afterLogout'; /** + * @var Identity the identity object associated with the currently logged user. + * This property is set automatically be the User component. Do not modify it directly + * unless you understand the consequence. You should normally use [[login()]], [[logout()]], + * or [[switchIdentity()]] to update the identity associated with the current user. + * + * If this property is null, it means the current user is a guest (not authenticated). + */ + public $identity; + /** * @var string the class name of the [[identity]] object. */ public $identityClass; @@ -64,24 +72,12 @@ class User extends Component * is initially logged in. When this is true, the identity cookie will expire after the specified duration * since the user visits the site the last time. * @see enableAutoLogin - * @since 1.1.0 - */ - public $autoRenewCookie = false; - /** - * @var string value that will be echoed in case that user session has expired during an ajax call. - * When a request is made and user session has expired, {@link loginRequired} redirects to {@link loginUrl} for login. - * If that happens during an ajax call, the complete HTML login page is returned as the result of that ajax call. That could be - * a problem if the ajax call expects the result to be a json array or a predefined string, as the login page is ignored in that case. - * To solve this, set this property to the desired return value. - * - * If this property is set, this value will be returned as the result of the ajax call in case that the user session has expired. - * @since 1.1.9 - * @see loginRequired */ - public $loginRequiredAjaxResponse; - + public $autoRenewCookie = true; - public $stateVar = '__states'; + public $idSessionVar = '__id'; + public $authTimeoutSessionVar = '__expire'; + public $returnUrlSessionVar = '__returnUrl'; /** * Initializes the application component. @@ -99,6 +95,8 @@ class User extends Component Yii::$app->getSession()->open(); + $this->loadIdentity(); + $this->renewAuthStatus(); if ($this->enableAutoLogin) { @@ -110,29 +108,16 @@ class User extends Component } } - /** - * @var Identity the identity object associated with the currently logged user. - */ - private $_identity = false; - - public function getIdentity() + public function loadIdentity() { - if ($this->_identity === false) { - $id = $this->getId(); - if ($id === null) { - $this->_identity = null; - } else { - /** @var $class Identity */ - $class = $this->identityClass; - $this->_identity = $class::findIdentity($this->getId()); - } + $id = $this->getId(); + if ($id === null) { + $this->identity = null; + } else { + /** @var $class Identity */ + $class = $this->identityClass; + $this->identity = $class::findIdentity($this->getId()); } - return $this->_identity; - } - - public function setIdentity($identity) - { - $this->switchIdentity($identity); } /** @@ -157,7 +142,7 @@ class User extends Component if ($this->beforeLogin($identity, false)) { $this->switchIdentity($identity); if ($duration > 0 && $this->enableAutoLogin) { - $this->saveIdentityCookie($identity, $duration); + $this->sendIdentityCookie($identity, $duration); } $this->afterLogin($identity, false); } @@ -169,7 +154,7 @@ class User extends Component * This method is used when automatic login ({@link enableAutoLogin}) is enabled. * The user identity information is recovered from cookie. * Sufficient security measures are used to prevent cookie data from being tampered. - * @see saveIdentityCookie + * @see sendIdentityCookie */ protected function loginByCookie() { @@ -182,18 +167,16 @@ class User extends Component /** @var $class Identity */ $class = $this->identityClass; $identity = $class::findIdentity($id); - if ($identity === null || !$identity->validateAuthKey($authKey)) { - if ($identity !== null) { - Yii::warning("Invalid auth key attempted for user '$id': $authKey", __METHOD__); + if ($identity !== null && $identity->validateAuthKey($authKey)) { + if ($this->beforeLogin($identity, true)) { + $this->switchIdentity($identity); + if ($this->autoRenewCookie) { + $this->sendIdentityCookie($identity, $duration); + } + $this->afterLogin($identity, true); } - return; - } - if ($this->beforeLogin($identity, true)) { - $this->switchIdentity($identity); - if ($this->autoRenewCookie) { - $this->saveIdentityCookie($identity, $duration); - } - $this->afterLogin($identity, true); + } elseif ($identity !== null) { + Yii::warning("Invalid auth key attempted for user '$id': $authKey", __METHOD__); } } } @@ -208,7 +191,7 @@ class User extends Component */ public function logout($destroySession = true) { - $identity = $this->getIdentity(); + $identity = $this->identity; if ($identity !== null && $this->beforeLogout($identity)) { $this->switchIdentity(null); if ($this->enableAutoLogin) { @@ -227,7 +210,7 @@ class User extends Component */ public function getIsGuest() { - return $this->getIdentity() === null; + return $this->identity === null; } /** @@ -236,7 +219,7 @@ class User extends Component */ public function getId() { - return $this->getState(static::ID_VAR); + return Yii::$app->getSession()->get($this->idSessionVar); } /** @@ -244,7 +227,7 @@ class User extends Component */ public function setId($value) { - $this->setState(static::ID_VAR, $value); + Yii::$app->getSession()->set($this->idSessionVar, $value); } /** @@ -258,12 +241,12 @@ class User extends Component */ public function getReturnUrl($defaultUrl = null) { - if ($defaultUrl === null) { - $defaultReturnUrl = Yii::app()->getUrlManager()->showScriptName ? Yii::app()->getRequest()->getScriptUrl() : Yii::app()->getRequest()->getBaseUrl() . '/'; + $url = Yii::$app->getSession()->get($this->returnUrlSessionVar, $defaultUrl); + if ($url === null) { + return Yii::$app->getHomeUrl(); } else { - $defaultReturnUrl = CHtml::normalizeUrl($defaultUrl); + return Html::url($url); } - return $this->getState('__returnUrl', $defaultReturnUrl); } /** @@ -271,7 +254,7 @@ class User extends Component */ public function setReturnUrl($value) { - $this->setState('__returnUrl', $value); + Yii::$app->getSession()->set($this->returnUrlSessionVar, $value); } /** @@ -285,24 +268,22 @@ class User extends Component */ public function loginRequired() { - $app = Yii::app(); - $request = $app->getRequest(); - - if (!$request->getIsAjaxRequest()) { - $this->setReturnUrl($request->getUrl()); - } elseif (isset($this->loginRequiredAjaxResponse)) { - echo $this->loginRequiredAjaxResponse; - Yii::app()->end(); - } - if (($url = $this->loginUrl) !== null) { - if (is_array($url)) { - $route = isset($url[0]) ? $url[0] : $app->defaultController; - $url = $app->createUrl($route, array_splice($url, 1)); + $url = Html::url($url); + $request = Yii::$app->getRequest(); + if (strpos($url, '/') === 0 && strpos($url, '//') !== 0) { + $url = $request->getHostInfo() . $url; + } + if ($request->getIsAjaxRequest()) { + echo json_encode(array( + 'redirect' => $url, + )); + Yii::$app->end(); + } else { + Yii::$app->getResponse()->redirect($url); } - $request->redirect($url); } else { - throw new CHttpException(403, Yii::t('yii', 'Login Required')); + throw new HttpException(403, Yii::t('yii|Login Required')); } } @@ -399,7 +380,7 @@ class User extends Component * @param integer $duration number of seconds that the user can remain in logged-in status. Defaults to 0, meaning login till the user closes the browser. * @see loginByCookie */ - protected function saveIdentityCookie($identity, $duration) + protected function sendIdentityCookie($identity, $duration) { $cookie = new Cookie($this->identityCookie); $cookie->value = json_encode(array( @@ -423,14 +404,16 @@ class User extends Component protected function switchIdentity($identity) { Yii::$app->getSession()->regenerateID(true); - $this->setIdentity($identity); + $this->identity = $identity; if ($identity instanceof Identity) { $this->setId($identity->getId()); if ($this->authTimeout !== null) { - $this->setState(self::AUTH_EXPIRE_VAR, time() + $this->authTimeout); + Yii::$app->getSession()->set($this->authTimeoutSessionVar, time() + $this->authTimeout); } } else { - $this->removeAllStates(); + $session = Yii::$app->getSession(); + $session->remove($this->idSessionVar); + $session->remove($this->authTimeoutSessionVar); } } @@ -442,115 +425,12 @@ class User extends Component protected function renewAuthStatus() { if ($this->authTimeout !== null && !$this->getIsGuest()) { - $expire = $this->getState(self::AUTH_EXPIRE_VAR); + $expire = Yii::$app->getSession()->get($this->authTimeoutSessionVar); if ($expire !== null && $expire < time()) { $this->logout(false); } else { - $this->setState(self::AUTH_EXPIRE_VAR, time() + $this->authTimeout); + Yii::$app->getSession()->set($this->authTimeoutSessionVar, time() + $this->authTimeout); } } } - - /** - * Returns a user state. - * A user state is a session data item associated with the current user. - * If the user logs out, all his/her user states will be removed. - * @param string $key the key identifying the state - * @param mixed $defaultValue value to be returned if the state does not exist. - * @return mixed the state - */ - public function getState($key, $defaultValue = null) - { - $manifest = isset($_SESSION[$this->stateVar]) ? $_SESSION[$this->stateVar] : null; - if (is_array($manifest) && isset($manifest[$key], $_SESSION[$key])) { - return $_SESSION[$key]; - } else { - return $defaultValue; - } - } - - /** - * Returns all user states. - * @return array states (key => state). - */ - public function getAllStates() - { - $manifest = isset($_SESSION[$this->stateVar]) ? $_SESSION[$this->stateVar] : null; - $states = array(); - if (is_array($manifest)) { - foreach (array_keys($manifest) as $key) { - if (isset($_SESSION[$key])) { - $states[$key] = $_SESSION[$key]; - } - } - } - return $states; - } - - /** - * Stores a user state. - * A user state is a session data item associated with the current user. - * If the user logs out, all his/her user states will be removed. - * @param string $key the key identifying the state. Note that states - * and normal session variables share the same name space. If you have a normal - * session variable using the same name, its value will be overwritten by this method. - * @param mixed $value state - */ - public function setState($key, $value) - { - $manifest = isset($_SESSION[$this->stateVar]) ? $_SESSION[$this->stateVar] : array(); - $manifest[$value] = true; - $_SESSION[$key] = $value; - $_SESSION[$this->stateVar] = $manifest; - } - - /** - * Removes a user state. - * If the user logs out, all his/her user states will be removed automatically. - * @param string $key the key identifying the state. Note that states - * and normal session variables share the same name space. If you have a normal - * session variable using the same name, it will be removed by this method. - * @return mixed the removed state. Null if the state does not exist. - */ - public function removeState($key) - { - $manifest = isset($_SESSION[$this->stateVar]) ? $_SESSION[$this->stateVar] : null; - if (is_array($manifest) && isset($manifest[$key], $_SESSION[$key])) { - $value = $_SESSION[$key]; - } else { - $value = null; - } - unset($_SESSION[$this->stateVar][$key], $_SESSION[$key]); - return $value; - } - - /** - * Removes all states. - * If the user logs out, all his/her user states will be removed automatically - * without the need to call this method manually. - * - * Note that states and normal session variables share the same name space. - * If you have a normal session variable using the same name, it will be removed - * by this method. - */ - public function removeAllStates() - { - $manifest = isset($_SESSION[$this->stateVar]) ? $_SESSION[$this->stateVar] : null; - if (is_array($manifest)) { - foreach (array_keys($manifest) as $key) { - unset($_SESSION[$key]); - } - } - unset($_SESSION[$this->stateVar]); - } - - /** - * Returns a value indicating whether there is a state associated with the specified key. - * @param string $key key identifying the state - * @return boolean whether the specified state exists - */ - public function hasState($key) - { - return $this->getState($key) !== null; - } }