Commit 6aa86712 by Qiang Xue

User WIP

parent b7be92ce
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
namespace yii\web; namespace yii\web;
use yii\base\InvalidParamException; use Yii;
/** /**
* Application is the base class for all application classes. * Application is the base class for all application classes.
...@@ -28,7 +28,7 @@ class Application extends \yii\base\Application ...@@ -28,7 +28,7 @@ class Application extends \yii\base\Application
public function registerDefaultAliases() public function registerDefaultAliases()
{ {
parent::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 ...@@ -41,6 +41,32 @@ class Application extends \yii\base\Application
return $this->runAction($route, $params); 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. * Returns the request component.
* @return Request the request component * @return Request the request component
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
namespace yii\web; namespace yii\web;
use Yii; use Yii;
use yii\helpers\Html;
/** /**
* Controller is the base class of Web controllers. * Controller is the base class of Web controllers.
...@@ -41,4 +42,18 @@ class Controller extends \yii\base\Controller ...@@ -41,4 +42,18 @@ class Controller extends \yii\base\Controller
return Yii::$app->getUrlManager()->createUrl($route, $params); 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
...@@ -38,7 +38,8 @@ interface Identity ...@@ -38,7 +38,8 @@ interface Identity
* Finds an identity by the given ID. * Finds an identity by the given ID.
* @param string|integer $id the ID to be looked for * @param string|integer $id the ID to be looked for
* @return Identity the identity object that matches the given ID. * @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); public static function findIdentity($id);
} }
\ No newline at end of file
...@@ -107,37 +107,42 @@ class Response extends \yii\base\Response ...@@ -107,37 +107,42 @@ class Response extends \yii\base\Response
* <li>addHeaders: an array of additional http headers in header-value pairs (available since version 1.1.10)</li> * <li>addHeaders: an array of additional http headers in header-value pairs (available since version 1.1.10)</li>
* </ul> * </ul>
*/ */
public function xSendFile($filePath, $options=array()) public function xSendFile($filePath, $options = array())
{ {
if(!isset($options['forceDownload']) || $options['forceDownload']) if (!isset($options['forceDownload']) || $options['forceDownload']) {
$disposition='attachment'; $disposition = 'attachment';
else } else {
$disposition='inline'; $disposition = 'inline';
}
if(!isset($options['saveName'])) if (!isset($options['saveName'])) {
$options['saveName']=basename($filePath); $options['saveName'] = basename($filePath);
}
if(!isset($options['mimeType'])) if (!isset($options['mimeType'])) {
{ if (($options['mimeType'] = CFileHelper::getMimeTypeByExtension($filePath)) === null) {
if(($options['mimeType']=CFileHelper::getMimeTypeByExtension($filePath))===null) $options['mimeType'] = 'text/plain';
$options['mimeType']='text/plain'; }
} }
if(!isset($options['xHeader'])) if (!isset($options['xHeader'])) {
$options['xHeader']='X-Sendfile'; $options['xHeader'] = 'X-Sendfile';
}
if($options['mimeType'] !== null) if ($options['mimeType'] !== null) {
header('Content-type: '.$options['mimeType']); 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('Content-Disposition: ' . $disposition . '; filename="' . $options['saveName'] . '"');
if (isset($options['addHeaders'])) {
foreach ($options['addHeaders'] as $header => $value) {
header($header . ': ' . $value);
}
}
header(trim($options['xHeader']) . ': ' . $filePath);
if(!isset($options['terminate']) || $options['terminate']) if (!isset($options['terminate']) || $options['terminate']) {
Yii::app()->end(); Yii::$app->end();
}
} }
/** /**
...@@ -148,13 +153,15 @@ class Response extends \yii\base\Response ...@@ -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} * @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. * 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) if (strpos($url, '/') === 0 && strpos($url, '//') !== 0) {
$url=$this->getHostInfo().$url; $url = Yii::$app->getRequest()->getHostInfo() . $url;
header('Location: '.$url, true, $statusCode); }
if($terminate) header('Location: ' . $url, true, $statusCode);
Yii::app()->end(); if ($terminate) {
Yii::$app->end();
}
} }
......
...@@ -9,7 +9,9 @@ namespace yii\web; ...@@ -9,7 +9,9 @@ namespace yii\web;
use Yii; use Yii;
use yii\base\Component; use yii\base\Component;
use yii\base\HttpException;
use yii\base\InvalidConfigException; use yii\base\InvalidConfigException;
use yii\helpers\Html;
/** /**
* @author Qiang Xue <qiang.xue@gmail.com> * @author Qiang Xue <qiang.xue@gmail.com>
...@@ -17,15 +19,21 @@ use yii\base\InvalidConfigException; ...@@ -17,15 +19,21 @@ use yii\base\InvalidConfigException;
*/ */
class User extends Component class User extends Component
{ {
const ID_VAR = '__id';
const AUTH_EXPIRE_VAR = '__expire';
const EVENT_BEFORE_LOGIN = 'beforeLogin'; const EVENT_BEFORE_LOGIN = 'beforeLogin';
const EVENT_AFTER_LOGIN = 'afterLogin'; const EVENT_AFTER_LOGIN = 'afterLogin';
const EVENT_BEFORE_LOGOUT = 'beforeLogout'; const EVENT_BEFORE_LOGOUT = 'beforeLogout';
const EVENT_AFTER_LOGOUT = 'afterLogout'; 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. * @var string the class name of the [[identity]] object.
*/ */
public $identityClass; public $identityClass;
...@@ -64,24 +72,12 @@ class User extends Component ...@@ -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 * 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. * since the user visits the site the last time.
* @see enableAutoLogin * @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 $idSessionVar = '__id';
public $stateVar = '__states'; public $authTimeoutSessionVar = '__expire';
public $returnUrlSessionVar = '__returnUrl';
/** /**
* Initializes the application component. * Initializes the application component.
...@@ -99,6 +95,8 @@ class User extends Component ...@@ -99,6 +95,8 @@ class User extends Component
Yii::$app->getSession()->open(); Yii::$app->getSession()->open();
$this->loadIdentity();
$this->renewAuthStatus(); $this->renewAuthStatus();
if ($this->enableAutoLogin) { if ($this->enableAutoLogin) {
...@@ -110,29 +108,16 @@ class User extends Component ...@@ -110,29 +108,16 @@ class User extends Component
} }
} }
/** public function loadIdentity()
* @var Identity the identity object associated with the currently logged user.
*/
private $_identity = false;
public function getIdentity()
{ {
if ($this->_identity === false) {
$id = $this->getId(); $id = $this->getId();
if ($id === null) { if ($id === null) {
$this->_identity = null; $this->identity = null;
} else { } else {
/** @var $class Identity */ /** @var $class Identity */
$class = $this->identityClass; $class = $this->identityClass;
$this->_identity = $class::findIdentity($this->getId()); $this->identity = $class::findIdentity($this->getId());
}
} }
return $this->_identity;
}
public function setIdentity($identity)
{
$this->switchIdentity($identity);
} }
/** /**
...@@ -157,7 +142,7 @@ class User extends Component ...@@ -157,7 +142,7 @@ class User extends Component
if ($this->beforeLogin($identity, false)) { if ($this->beforeLogin($identity, false)) {
$this->switchIdentity($identity); $this->switchIdentity($identity);
if ($duration > 0 && $this->enableAutoLogin) { if ($duration > 0 && $this->enableAutoLogin) {
$this->saveIdentityCookie($identity, $duration); $this->sendIdentityCookie($identity, $duration);
} }
$this->afterLogin($identity, false); $this->afterLogin($identity, false);
} }
...@@ -169,7 +154,7 @@ class User extends Component ...@@ -169,7 +154,7 @@ class User extends Component
* This method is used when automatic login ({@link enableAutoLogin}) is enabled. * This method is used when automatic login ({@link enableAutoLogin}) is enabled.
* The user identity information is recovered from cookie. * The user identity information is recovered from cookie.
* Sufficient security measures are used to prevent cookie data from being tampered. * Sufficient security measures are used to prevent cookie data from being tampered.
* @see saveIdentityCookie * @see sendIdentityCookie
*/ */
protected function loginByCookie() protected function loginByCookie()
{ {
...@@ -182,19 +167,17 @@ class User extends Component ...@@ -182,19 +167,17 @@ class User extends Component
/** @var $class Identity */ /** @var $class Identity */
$class = $this->identityClass; $class = $this->identityClass;
$identity = $class::findIdentity($id); $identity = $class::findIdentity($id);
if ($identity === null || !$identity->validateAuthKey($authKey)) { if ($identity !== null && $identity->validateAuthKey($authKey)) {
if ($identity !== null) {
Yii::warning("Invalid auth key attempted for user '$id': $authKey", __METHOD__);
}
return;
}
if ($this->beforeLogin($identity, true)) { if ($this->beforeLogin($identity, true)) {
$this->switchIdentity($identity); $this->switchIdentity($identity);
if ($this->autoRenewCookie) { if ($this->autoRenewCookie) {
$this->saveIdentityCookie($identity, $duration); $this->sendIdentityCookie($identity, $duration);
} }
$this->afterLogin($identity, true); $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 ...@@ -208,7 +191,7 @@ class User extends Component
*/ */
public function logout($destroySession = true) public function logout($destroySession = true)
{ {
$identity = $this->getIdentity(); $identity = $this->identity;
if ($identity !== null && $this->beforeLogout($identity)) { if ($identity !== null && $this->beforeLogout($identity)) {
$this->switchIdentity(null); $this->switchIdentity(null);
if ($this->enableAutoLogin) { if ($this->enableAutoLogin) {
...@@ -227,7 +210,7 @@ class User extends Component ...@@ -227,7 +210,7 @@ class User extends Component
*/ */
public function getIsGuest() public function getIsGuest()
{ {
return $this->getIdentity() === null; return $this->identity === null;
} }
/** /**
...@@ -236,7 +219,7 @@ class User extends Component ...@@ -236,7 +219,7 @@ class User extends Component
*/ */
public function getId() public function getId()
{ {
return $this->getState(static::ID_VAR); return Yii::$app->getSession()->get($this->idSessionVar);
} }
/** /**
...@@ -244,7 +227,7 @@ class User extends Component ...@@ -244,7 +227,7 @@ class User extends Component
*/ */
public function setId($value) 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 ...@@ -258,12 +241,12 @@ class User extends Component
*/ */
public function getReturnUrl($defaultUrl = null) public function getReturnUrl($defaultUrl = null)
{ {
if ($defaultUrl === null) { $url = Yii::$app->getSession()->get($this->returnUrlSessionVar, $defaultUrl);
$defaultReturnUrl = Yii::app()->getUrlManager()->showScriptName ? Yii::app()->getRequest()->getScriptUrl() : Yii::app()->getRequest()->getBaseUrl() . '/'; if ($url === null) {
return Yii::$app->getHomeUrl();
} else { } else {
$defaultReturnUrl = CHtml::normalizeUrl($defaultUrl); return Html::url($url);
} }
return $this->getState('__returnUrl', $defaultReturnUrl);
} }
/** /**
...@@ -271,7 +254,7 @@ class User extends Component ...@@ -271,7 +254,7 @@ class User extends Component
*/ */
public function setReturnUrl($value) public function setReturnUrl($value)
{ {
$this->setState('__returnUrl', $value); Yii::$app->getSession()->set($this->returnUrlSessionVar, $value);
} }
/** /**
...@@ -285,24 +268,22 @@ class User extends Component ...@@ -285,24 +268,22 @@ class User extends Component
*/ */
public function loginRequired() 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 (($url = $this->loginUrl) !== null) {
if (is_array($url)) { $url = Html::url($url);
$route = isset($url[0]) ? $url[0] : $app->defaultController; $request = Yii::$app->getRequest();
$url = $app->createUrl($route, array_splice($url, 1)); 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 { } 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 ...@@ -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. * @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 * @see loginByCookie
*/ */
protected function saveIdentityCookie($identity, $duration) protected function sendIdentityCookie($identity, $duration)
{ {
$cookie = new Cookie($this->identityCookie); $cookie = new Cookie($this->identityCookie);
$cookie->value = json_encode(array( $cookie->value = json_encode(array(
...@@ -423,14 +404,16 @@ class User extends Component ...@@ -423,14 +404,16 @@ class User extends Component
protected function switchIdentity($identity) protected function switchIdentity($identity)
{ {
Yii::$app->getSession()->regenerateID(true); Yii::$app->getSession()->regenerateID(true);
$this->setIdentity($identity); $this->identity = $identity;
if ($identity instanceof Identity) { if ($identity instanceof Identity) {
$this->setId($identity->getId()); $this->setId($identity->getId());
if ($this->authTimeout !== null) { if ($this->authTimeout !== null) {
$this->setState(self::AUTH_EXPIRE_VAR, time() + $this->authTimeout); Yii::$app->getSession()->set($this->authTimeoutSessionVar, time() + $this->authTimeout);
} }
} else { } else {
$this->removeAllStates(); $session = Yii::$app->getSession();
$session->remove($this->idSessionVar);
$session->remove($this->authTimeoutSessionVar);
} }
} }
...@@ -442,115 +425,12 @@ class User extends Component ...@@ -442,115 +425,12 @@ class User extends Component
protected function renewAuthStatus() protected function renewAuthStatus()
{ {
if ($this->authTimeout !== null && !$this->getIsGuest()) { 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()) { if ($expire !== null && $expire < time()) {
$this->logout(false); $this->logout(false);
} else { } 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;
}
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment