Commit 6aa86712 by Qiang Xue

User WIP

parent b7be92ce
......@@ -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
......
......@@ -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
......@@ -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
......@@ -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>
* </ul>
*/
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(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'])
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();
}
}
......
......@@ -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 <qiang.xue@gmail.com>
......@@ -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;
$this->identity = null;
} else {
/** @var $class Identity */
$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
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,19 +167,17 @@ 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__);
}
return;
}
if ($identity !== null && $identity->validateAuthKey($authKey)) {
if ($this->beforeLogin($identity, true)) {
$this->switchIdentity($identity);
if ($this->autoRenewCookie) {
$this->saveIdentityCookie($identity, $duration);
$this->sendIdentityCookie($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);
}
}
}
/**
* 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];
Yii::$app->getSession()->set($this->authTimeoutSessionVar, time() + $this->authTimeout);
}
}
}
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