From 1fbf81be57fb2b3ad07a401f0f12e18cd0f2e7a3 Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Thu, 28 Mar 2013 17:43:27 -0400 Subject: [PATCH] User WIP. --- docs/code_style.md | 2 +- framework/base/Component.php | 5 +- framework/base/Event.php | 19 +- framework/base/Model.php | 2 +- framework/db/ActiveRecord.php | 4 +- framework/web/Identity.php | 45 +++ framework/web/Response.php | 3 +- framework/web/Session.php | 1 + framework/web/User.php | 462 ++++++++++++++-------------- framework/web/UserEvent.php | 34 ++ tests/unit/framework/base/ComponentTest.php | 2 +- 11 files changed, 329 insertions(+), 250 deletions(-) create mode 100644 framework/web/Identity.php create mode 100644 framework/web/UserEvent.php diff --git a/docs/code_style.md b/docs/code_style.md index fcf643d..92a934b 100644 --- a/docs/code_style.md +++ b/docs/code_style.md @@ -204,7 +204,7 @@ doIt('a', array( ~~~ if ($event === null) { - return new Event($this); + return new Event(); } elseif ($event instanceof CoolEvent) { return $event->instance(); } else { diff --git a/framework/base/Component.php b/framework/base/Component.php index 2d081d3..f1d549b 100644 --- a/framework/base/Component.php +++ b/framework/base/Component.php @@ -422,7 +422,10 @@ class Component extends \yii\base\Object $this->ensureBehaviors(); if (isset($this->_e[$name]) && $this->_e[$name]->getCount()) { if ($event === null) { - $event = new Event($this); + $event = new Event; + } + if ($event->sender === null) { + $event->sender = $this; } $event->handled = false; $event->name = $name; diff --git a/framework/base/Event.php b/framework/base/Event.php index 4ba57b2..b86ed7c 100644 --- a/framework/base/Event.php +++ b/framework/base/Event.php @@ -28,7 +28,8 @@ class Event extends \yii\base\Object */ public $name; /** - * @var object the sender of this event + * @var object the sender of this event. If not set, this property will be + * set as the object whose "trigger()" method is called. */ public $sender; /** @@ -38,21 +39,7 @@ class Event extends \yii\base\Object */ public $handled = false; /** - * @var mixed extra data associated with the event. + * @var mixed extra custom data associated with the event. */ public $data; - - /** - * Constructor. - * - * @param mixed $sender sender of the event - * @param mixed $data extra data associated with the event - * @param array $config name-value pairs that will be used to initialize the object properties - */ - public function __construct($sender = null, $data = null, $config = array()) - { - $this->sender = $sender; - $this->data = $data; - parent::__construct($config); - } } diff --git a/framework/base/Model.php b/framework/base/Model.php index 402a558..9056a71 100644 --- a/framework/base/Model.php +++ b/framework/base/Model.php @@ -258,7 +258,7 @@ class Model extends Component implements \IteratorAggregate, \ArrayAccess */ public function beforeValidate() { - $event = new ModelEvent($this); + $event = new ModelEvent; $this->trigger(self::EVENT_BEFORE_VALIDATE, $event); return $event->isValid; } diff --git a/framework/db/ActiveRecord.php b/framework/db/ActiveRecord.php index 0c15121..d8f2f65 100644 --- a/framework/db/ActiveRecord.php +++ b/framework/db/ActiveRecord.php @@ -847,7 +847,7 @@ class ActiveRecord extends Model */ public function beforeSave($insert) { - $event = new ModelEvent($this); + $event = new ModelEvent; $this->trigger($insert ? self::EVENT_BEFORE_INSERT : self::EVENT_BEFORE_UPDATE, $event); return $event->isValid; } @@ -887,7 +887,7 @@ class ActiveRecord extends Model */ public function beforeDelete() { - $event = new ModelEvent($this); + $event = new ModelEvent; $this->trigger(self::EVENT_BEFORE_DELETE, $event); return $event->isValid; } diff --git a/framework/web/Identity.php b/framework/web/Identity.php new file mode 100644 index 0000000..4668337 --- /dev/null +++ b/framework/web/Identity.php @@ -0,0 +1,45 @@ + + * @since 2.0 + */ +interface Identity +{ + /** + * Returns an ID that can uniquely identify a user identity. + * The returned ID can be a string, an integer, or any serializable data. + * @return mixed an ID that uniquely identifies a user identity. + */ + public function getId(); + /** + * Returns a key that can be used to check the validity of a given identity ID. + * The space of such keys should be big and random enough to defeat potential identity attacks. + * The returned key can be a string, an integer, or any serializable data. + * @return mixed a key that is used to check the validity of a given identity ID. + * @see validateAuthKey() + */ + public function getAuthKey(); + /** + * Validates the given auth key. + * @param string $authKey the given auth key + * @return boolean whether the given auth key is valid. + * @see getAuthKey() + */ + public function validateAuthKey($authKey); + /** + * Finds an identity by the given ID. + * @param mixed $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. + */ + public static function findIdentity($id); +} \ No newline at end of file diff --git a/framework/web/Response.php b/framework/web/Response.php index d6659cf..d23c5b9 100644 --- a/framework/web/Response.php +++ b/framework/web/Response.php @@ -7,6 +7,7 @@ namespace yii\web; +use Yii; use yii\helpers\FileHelper; /** @@ -178,6 +179,6 @@ class Response extends \yii\base\Response */ public function getCookies() { - return \Yii::$app->getRequest()->getCookies(); + return Yii::$app->getRequest()->getCookies(); } } diff --git a/framework/web/Session.php b/framework/web/Session.php index 840a26d..c289db2 100644 --- a/framework/web/Session.php +++ b/framework/web/Session.php @@ -619,6 +619,7 @@ class Session extends Component implements \IteratorAggregate, \ArrayAccess, \Co } /** + * Returns a value indicating whether there is a flash message associated with the specified key. * @param string $key key identifying the flash message * @return boolean whether the specified flash message exists */ diff --git a/framework/web/User.php b/framework/web/User.php index fdde60b..2326a10 100644 --- a/framework/web/User.php +++ b/framework/web/User.php @@ -9,17 +9,26 @@ namespace yii\web; use Yii; use yii\base\Component; +use yii\base\InvalidConfigException; /** - * * @author Qiang Xue * @since 2.0 */ class User extends Component { - const STATES_VAR = '__states'; - const AUTH_TIMEOUT_VAR = '__timeout'; + 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 string the class name of the [[identity]] object. + */ + public $identityClass; /** * @var boolean whether to enable cookie-based login. Defaults to false. */ @@ -41,7 +50,7 @@ class User extends Component * @var array the configuration of the identity cookie. This property is used only when [[enableAutoLogin]] is true. * @see Cookie */ - public $identityCookie; + public $identityCookie = array('name' => '__identity'); /** * @var integer the number of seconds in which the user will be logged out automatically if he * remains inactive. If this property is not set, the user will be logged out after @@ -70,10 +79,9 @@ class User extends Component * @see loginRequired */ public $loginRequiredAjaxResponse; + - private $_keyPrefix; - private $_access = array(); - + public $stateVar = '__states'; /** * Initializes the application component. @@ -81,13 +89,47 @@ class User extends Component public function init() { parent::init(); + + if ($this->enableAutoLogin && !isset($this->identityCookie['name'])) { + throw new InvalidConfigException('User::identityCookie must contain the "name" element.'); + } + Yii::$app->getSession()->open(); - if ($this->getIsGuest() && $this->enableAutoLogin) { - $this->restoreFromCookie(); - } elseif ($this->autoRenewCookie && $this->enableAutoLogin) { - $this->renewCookie(); + + $this->renewAuthStatus(); + + if ($this->enableAutoLogin) { + if ($this->getIsGuest()) { + $this->loginByCookie(); + } elseif ($this->autoRenewCookie) { + $this->renewIdentityCookie(); + } + } + } + + /** + * @var Identity the identity object associated with the currently logged user. + */ + private $_identity = false; + + public function getIdentity() + { + 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()); + } } - $this->updateAuthStatus(); + return $this->_identity; + } + + public function setIdentity($identity) + { + $this->switchIdentity($identity); } /** @@ -101,7 +143,7 @@ class User extends Component * Note, you have to set {@link enableAutoLogin} to true * if you want to allow user to be authenticated based on the cookie information. * - * @param IUserIdentity $identity the user identity (which should already be authenticated) + * @param Identity $identity the user identity (which should already be authenticated) * @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. * If greater than 0, cookie-based login will be used. In this case, {@link enableAutoLogin} * must be set true, otherwise an exception will be thrown. @@ -109,26 +151,46 @@ class User extends Component */ public function login($identity, $duration = 0) { - $id = $identity->getId(); - $states = $identity->getPersistentStates(); - if ($this->beforeLogin($id, $states, false)) { - $this->changeIdentity($id, $identity->getName(), $states); - - if ($duration > 0) { - if ($this->enableAutoLogin) { - $this->saveToCookie($duration); - } else { - throw new CException(Yii::t('yii', '{class}.enableAutoLogin must be set true in order to use cookie-based authentication.', - array('{class}' => get_class($this)))); - } + if ($this->beforeLogin($identity, false)) { + $this->switchIdentity($identity); + if ($duration > 0 && $this->enableAutoLogin) { + $this->saveIdentityCookie($identity, $duration); } - - $this->afterLogin(false); + $this->afterLogin($identity, false); } return !$this->getIsGuest(); } /** + * Populates the current user object with the information obtained from cookie. + * 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 + */ + protected function loginByCookie() + { + $name = $this->identityCookie['name']; + $value = Yii::$app->getRequest()->getCookies()->getValue($name); + if ($value !== null) { + $data = json_decode($value, true); + if (count($data) === 3 && isset($data[0], $data[1], $data[2])) { + list ($id, $authKey, $duration) = $data; + /** @var $class Identity */ + $class = $this->identityClass; + $identity = $class::findIdentity($id); + if ($identity !== null && $identity->validateAuthKey($authKey) && $this->beforeLogin($identity, true)) { + $this->switchIdentity($identity); + if ($this->autoRenewCookie) { + $this->saveIdentityCookie($identity, $duration); + } + $this->afterLogin($identity, true); + } + } + } + } + + /** * Logs out the current user. * This will remove authentication-related session data. * If the parameter is true, the whole session will be destroyed as well. @@ -137,33 +199,26 @@ class User extends Component */ public function logout($destroySession = true) { - if ($this->beforeLogout()) { + $identity = $this->getIdentity(); + if ($identity !== null && $this->beforeLogout($identity)) { + $this->switchIdentity(null); if ($this->enableAutoLogin) { - Yii::app()->getRequest()->getCookies()->remove($this->getStateKeyPrefix()); - if ($this->identityCookie !== null) { - $cookie = $this->createIdentityCookie($this->getStateKeyPrefix()); - $cookie->value = null; - $cookie->expire = 0; - Yii::app()->getRequest()->getCookies()->add($cookie->name, $cookie); - } + Yii::$app->getResponse()->getCookies()->remove(new Cookie($this->identityCookie)); } if ($destroySession) { - Yii::app()->getSession()->destroy(); - } else { - $this->clearStates(); + Yii::$app->getSession()->destroy(); } - $this->_access = array(); - $this->afterLogout(); + $this->afterLogout($identity); } } /** * Returns a value indicating whether the user is a guest (not authenticated). - * @return boolean whether the current application user is a guest. + * @return boolean whether the current user is a guest. */ public function getIsGuest() { - return $this->getState('__id') === null; + return $this->getIdentity() === null; } /** @@ -172,7 +227,7 @@ class User extends Component */ public function getId() { - return $this->getState('__id'); + return $this->getState(static::ID_VAR); } /** @@ -180,7 +235,7 @@ class User extends Component */ public function setId($value) { - $this->setState('__id', $value); + $this->setState(static::ID_VAR, $value); } /** @@ -253,11 +308,15 @@ class User extends Component * @param array $states a set of name-value pairs that are provided by the user identity. * @param boolean $fromCookie whether the login is based on cookie * @return boolean whether the user should be logged in - * @since 1.1.3 */ - protected function beforeLogin($id, $states, $fromCookie) + protected function beforeLogin($identity, $fromCookie) { - return true; + $event = new UserEvent(array( + 'identity' => $identity, + 'fromCookie' => $fromCookie, + )); + $this->trigger(self::EVENT_BEFORE_LOGIN, $event); + return $event->isValid; } /** @@ -265,10 +324,13 @@ class User extends Component * You may override this method to do some postprocessing (e.g. log the user * login IP and time; load the user permission information). * @param boolean $fromCookie whether the login is based on cookie. - * @since 1.1.3 */ - protected function afterLogin($fromCookie) + protected function afterLogin($identity, $fromCookie) { + $this->trigger(self::EVENT_AFTER_LOGIN, new UserEvent(array( + 'identity' => $identity, + 'fromCookie' => $fromCookie, + ))); } /** @@ -277,66 +339,44 @@ class User extends Component * You may override this method to provide additional check before * logging out a user. * @return boolean whether to log out the user - * @since 1.1.3 */ - protected function beforeLogout() + protected function beforeLogout($identity) { - return true; + $event = new UserEvent(array( + 'identity' => $identity, + )); + $this->trigger(self::EVENT_BEFORE_LOGOUT, $event); + return $event->isValid; } /** * This method is invoked right after a user is logged out. * You may override this method to do some extra cleanup work for the user. - * @since 1.1.3 */ - protected function afterLogout() + protected function afterLogout($identity) { + $this->trigger(self::EVENT_AFTER_LOGOUT, new UserEvent(array( + 'identity' => $identity, + ))); } - /** - * Populates the current user object with the information obtained from cookie. - * 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 saveToCookie - */ - protected function restoreFromCookie() - { - $app = Yii::app(); - $request = $app->getRequest(); - $cookie = $request->getCookies()->itemAt($this->getStateKeyPrefix()); - if ($cookie && !empty($cookie->value) && is_string($cookie->value) && ($data = $app->getSecurityManager()->validateData($cookie->value)) !== false) { - $data = @unserialize($data); - if (is_array($data) && isset($data[0], $data[1], $data[2], $data[3])) { - list($id, $name, $duration, $states) = $data; - if ($this->beforeLogin($id, $states, true)) { - $this->changeIdentity($id, $name, $states); - if ($this->autoRenewCookie) { - $cookie->expire = time() + $duration; - $request->getCookies()->add($cookie->name, $cookie); - } - $this->afterLogin(true); - } - } - } - } /** * Renews the identity cookie. * This method will set the expiration time of the identity cookie to be the current time * plus the originally specified cookie duration. - * @since 1.1.3 */ - protected function renewCookie() + protected function renewIdentityCookie() { - $request = Yii::app()->getRequest(); - $cookies = $request->getCookies(); - $cookie = $cookies->itemAt($this->getStateKeyPrefix()); - if ($cookie && !empty($cookie->value) && ($data = Yii::app()->getSecurityManager()->validateData($cookie->value)) !== false) { - $data = @unserialize($data); - if (is_array($data) && isset($data[0], $data[1], $data[2], $data[3])) { - $cookie->expire = time() + $data[2]; - $cookies->add($cookie->name, $cookie); + $name = $this->identityCookie['name']; + $value = Yii::$app->getRequest()->getCookies()->getValue($name); + if ($value !== null) { + $data = json_decode($value, true); + if (is_array($data) && isset($data[2])) { + $cookie = new Cookie($this->identityCookie); + $cookie->value = $value; + $cookie->expire = time() + (int)$data[2]; + Yii::$app->getResponse()->getCookies()->add($cookie); } } } @@ -346,194 +386,162 @@ class User extends Component * This method is used when automatic login ({@link enableAutoLogin}) is enabled. * This method saves user ID, username, other identity states and a validation key to cookie. * These information are used to do authentication next time when user visits the application. + * @param Identity $identity * @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 restoreFromCookie + * @see loginByCookie */ - protected function saveToCookie($duration) + protected function saveIdentityCookie($identity, $duration) { - $app = Yii::app(); - $cookie = $this->createIdentityCookie($this->getStateKeyPrefix()); - $cookie->expire = time() + $duration; - $data = array( - $this->getId(), - $this->getName(), + $cookie = new Cookie($this->identityCookie); + $cookie->value = json_encode(array( + $identity->getId(), + $identity->getAuthKey(), $duration, - $this->saveIdentityStates(), - ); - $cookie->value = $app->getSecurityManager()->hashData(serialize($data)); - $app->getRequest()->getCookies()->add($cookie->name, $cookie); + )); + $cookie->expire = time() + $duration; + Yii::$app->getResponse()->getCookies()->add($cookie); } /** - * Creates a cookie to store identity information. - * @param string $name the cookie name - * @return CHttpCookie the cookie used to store identity information + * Changes the current user with the specified identity information. + * This method is called by {@link login} and {@link restoreFromCookie} + * when the current user needs to be populated with the corresponding + * identity information. Derived classes may override this method + * by retrieving additional user-related information. Make sure the + * parent implementation is called first. + * @param Identity $identity a unique identifier for the user */ - protected function createIdentityCookie($name) + protected function switchIdentity($identity) { - $cookie = new CHttpCookie($name, ''); - if (is_array($this->identityCookie)) { - foreach ($this->identityCookie as $name => $value) { - $cookie->$name = $value; + Yii::$app->getSession()->regenerateID(true); + $this->setIdentity($identity); + if ($identity instanceof Identity) { + $this->setId($identity->getId()); + if ($this->authTimeout !== null) { + $this->setState(self::AUTH_EXPIRE_VAR, time() + $this->authTimeout); } - } - return $cookie; - } - - /** - * @return string a prefix for the name of the session variables storing user session data. - */ - public function getStateKeyPrefix() - { - if ($this->_keyPrefix !== null) { - return $this->_keyPrefix; } else { - return $this->_keyPrefix = md5('Yii.' . get_class($this) . '.' . Yii::app()->getId()); + $this->removeAllStates(); } } /** - * @param string $value a prefix for the name of the session variables storing user session data. + * Updates the authentication status according to {@link authTimeout}. + * If the user has been inactive for {@link authTimeout} seconds, + * he will be automatically logged out. */ - public function setStateKeyPrefix($value) + protected function renewAuthStatus() { - $this->_keyPrefix = $value; + if ($this->authTimeout !== null && !$this->getIsGuest()) { + $expire = $this->getState(self::AUTH_EXPIRE_VAR); + if ($expire !== null && $expire < time()) { + $this->logout(false); + } else { + $this->setState(self::AUTH_EXPIRE_VAR, time() + $this->authTimeout); + } + } } /** - * Returns the value of a variable that is stored in user session. - * - * This function is designed to be used by CWebUser 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 $key variable name - * @param mixed $defaultValue default value - * @return mixed the value of the variable. If it doesn't exist in the session, - * the provided default value will be returned - * @see setState + * 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) { - $key = $this->getStateKeyPrefix() . $key; - return isset($_SESSION[$key]) ? $_SESSION[$key] : $defaultValue; - } - - /** - * Stores a variable in user session. - * - * This function is designed to be used by CWebUser 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 $key variable name - * @param mixed $value variable value - * @param mixed $defaultValue default value. If $value===$defaultValue, the variable will be - * removed from the session - * @see getState - */ - public function setState($key, $value, $defaultValue = null) - { - $key = $this->getStateKeyPrefix() . $key; - if ($value === $defaultValue) { - unset($_SESSION[$key]); + $manifest = isset($_SESSION[$this->stateVar]) ? $_SESSION[$this->stateVar] : null; + if (is_array($manifest) && isset($manifest[$key], $_SESSION[$key])) { + return $_SESSION[$key]; } else { - $_SESSION[$key] = $value; + return $defaultValue; } } /** - * Returns a value indicating whether there is a state of the specified name. - * @param string $key state name - * @return boolean whether there is a state of the specified name. + * Returns all user states. + * @return array states (key => state). */ - public function hasState($key) + public function getAllStates() { - $key = $this->getStateKeyPrefix() . $key; - return isset($_SESSION[$key]); - } - - /** - * Clears all user identity information from persistent storage. - * This will remove the data stored via {@link setState}. - */ - public function clearStates() - { - $keys = array_keys($_SESSION); - $prefix = $this->getStateKeyPrefix(); - $n = strlen($prefix); - foreach ($keys as $key) { - if (!strncmp($key, $prefix, $n)) { - unset($_SESSION[$key]); + $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; } /** - * Changes the current user with the specified identity information. - * This method is called by {@link login} and {@link restoreFromCookie} - * when the current user needs to be populated with the corresponding - * identity information. Derived classes may override this method - * by retrieving additional user-related information. Make sure the - * parent implementation is called first. - * @param mixed $id a unique identifier for the user - * @param string $name the display name for the user - * @param array $states identity 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 */ - protected function changeIdentity($id, $name, $states) + public function setState($key, $value) { - Yii::app()->getSession()->regenerateID(true); - $this->setId($id); - $this->setName($name); - $this->loadIdentityStates($states); + $manifest = isset($_SESSION[$this->stateVar]) ? $_SESSION[$this->stateVar] : array(); + $manifest[$value] = true; + $_SESSION[$key] = $value; + $_SESSION[$this->stateVar] = $manifest; } /** - * Retrieves identity states from persistent storage and saves them as an array. - * @return array the identity states + * 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. */ - protected function saveIdentityStates() + public function removeState($key) { - $states = array(); - foreach ($this->getState(self::STATES_VAR, array()) as $name => $dummy) { - $states[$name] = $this->getState($name); + $manifest = isset($_SESSION[$this->stateVar]) ? $_SESSION[$this->stateVar] : null; + if (is_array($manifest) && isset($manifest[$key], $_SESSION[$key])) { + $value = $_SESSION[$key]; + } else { + $value = null; } - return $states; + unset($_SESSION[$this->stateVar][$key], $_SESSION[$key]); + return $value; } /** - * Loads identity states from an array and saves them to persistent storage. - * @param array $states the identity states + * 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. */ - protected function loadIdentityStates($states) + public function removeAllStates() { - $names = array(); - if (is_array($states)) { - foreach ($states as $name => $value) { - $this->setState($name, $value); - $names[$name] = true; - } - } - $this->setState(self::STATES_VAR, $names); + $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]); } /** - * Updates the authentication status according to {@link authTimeout}. - * If the user has been inactive for {@link authTimeout} seconds, - * he will be automatically logged out. + * 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 */ - protected function updateAuthStatus() + public function hasState($key) { - if ($this->authTimeout !== null && !$this->getIsGuest()) { - $expires = $this->getState(self::AUTH_TIMEOUT_VAR); - if ($expires !== null && $expires < time()) { - $this->logout(false); - } else { - $this->setState(self::AUTH_TIMEOUT_VAR, time() + $this->authTimeout); - } - } + return $this->getState($key) !== null; } } diff --git a/framework/web/UserEvent.php b/framework/web/UserEvent.php new file mode 100644 index 0000000..3a8723a --- /dev/null +++ b/framework/web/UserEvent.php @@ -0,0 +1,34 @@ + + * @since 2.0 + */ +class UserEvent extends Event +{ + /** + * @var Identity the identity object associated with this event + */ + public $identity; + /** + * @var boolean whether the login is cookie-based. This property is only meaningful + * for [[User::EVENT_BEFORE_LOGIN]] and [[User::EVENT_AFTER_LOGIN]] events. + */ + public $fromCookie; + /** + * @var boolean whether the login or logout should proceed. + * Event handlers may modify this property to determine whether the login or logout should proceed. + * This property is only meaningful for [[User::EVENT_BEFORE_LOGIN]] and [[User::EVENT_BEFORE_LOGOUT]] events. + */ + public $isValid; +} \ No newline at end of file diff --git a/tests/unit/framework/base/ComponentTest.php b/tests/unit/framework/base/ComponentTest.php index 2c456e2..97b0116 100644 --- a/tests/unit/framework/base/ComponentTest.php +++ b/tests/unit/framework/base/ComponentTest.php @@ -352,7 +352,7 @@ class NewComponent extends Component public function raiseEvent() { - $this->trigger('click', new Event($this)); + $this->trigger('click', new Event); } }