1
0

Added new (clean) yii boilerplate

This commit is contained in:
2014-05-13 12:40:42 +02:00
parent 1d6d975a16
commit 99d29b432b
1983 changed files with 653465 additions and 17 deletions

View File

@@ -0,0 +1,394 @@
<?php
/**
* CAccessControlFilter class file.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @link http://www.yiiframework.com/
* @copyright 2008-2013 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
/**
* CAccessControlFilter performs authorization checks for the specified actions.
*
* By enabling this filter, controller actions can be checked for access permissions.
* When the user is not denied by one of the security rules or allowed by a rule explicitly,
* he will be able to access the action.
*
* For maximum security consider adding
* <pre>array('deny')</pre>
* as a last rule in a list so all actions will be denied by default.
*
* To specify the access rules, set the {@link setRules rules} property, which should
* be an array of the rules. Each rule is specified as an array of the following structure:
* <pre>
* array(
* 'allow', // or 'deny'
*
* // optional, list of action IDs (case insensitive) that this rule applies to
* // if not specified or empty, rule applies to all actions
* 'actions'=>array('edit', 'delete'),
*
* // optional, list of controller IDs (case insensitive) that this rule applies to
* 'controllers'=>array('post', 'admin/user'),
*
* // optional, list of usernames (case insensitive) that this rule applies to
* // Use * to represent all users, ? guest users, and @ authenticated users
* 'users'=>array('thomas', 'kevin'),
*
* // optional, list of roles (case sensitive!) that this rule applies to.
* 'roles'=>array('admin', 'editor'),
*
* // since version 1.1.11 you can pass parameters for RBAC bizRules
* 'roles'=>array('updateTopic'=>array('topic'=>$topic))
*
* // optional, list of IP address/patterns that this rule applies to
* // e.g. 127.0.0.1, 127.0.0.*
* 'ips'=>array('127.0.0.1'),
*
* // optional, list of request types (case insensitive) that this rule applies to
* 'verbs'=>array('GET', 'POST'),
*
* // optional, a PHP expression whose value indicates whether this rule applies
* // The PHP expression will be evaluated using {@link evaluateExpression}.
* // A PHP expression can be any PHP code that has a value. To learn more about what an expression is,
* // please refer to the {@link http://www.php.net/manual/en/language.expressions.php php manual}.
* 'expression'=>'!$user->isGuest && $user->level==2',
*
* // optional, the customized error message to be displayed
* // This option is available since version 1.1.1.
* 'message'=>'Access Denied.',
*
* // optional, the denied method callback name, that will be called once the
* // access is denied, instead of showing the customized error message. It can also be
* // a valid PHP callback, including class method name (array(ClassName/Object, MethodName)),
* // or anonymous function (PHP 5.3.0+). The function/method signature should be as follows:
* // function foo($user, $rule) { ... }
* // where $user is the current application user object and $rule is this access rule.
* // This option is available since version 1.1.11.
* 'deniedCallback'=>'redirectToDeniedMethod',
* )
* </pre>
*
* @property array $rules List of access rules.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @package system.web.auth
* @since 1.0
*/
class CAccessControlFilter extends CFilter
{
/**
* @var string the error message to be displayed when authorization fails.
* This property can be overridden by individual access rule via {@link CAccessRule::message}.
* If this property is not set, a default error message will be displayed.
* @since 1.1.1
*/
public $message;
private $_rules=array();
/**
* @return array list of access rules.
*/
public function getRules()
{
return $this->_rules;
}
/**
* @param array $rules list of access rules.
*/
public function setRules($rules)
{
foreach($rules as $rule)
{
if(is_array($rule) && isset($rule[0]))
{
$r=new CAccessRule;
$r->allow=$rule[0]==='allow';
foreach(array_slice($rule,1) as $name=>$value)
{
if($name==='expression' || $name==='roles' || $name==='message' || $name==='deniedCallback')
$r->$name=$value;
else
$r->$name=array_map('strtolower',$value);
}
$this->_rules[]=$r;
}
}
}
/**
* Performs the pre-action filtering.
* @param CFilterChain $filterChain the filter chain that the filter is on.
* @return boolean whether the filtering process should continue and the action
* should be executed.
*/
protected function preFilter($filterChain)
{
$app=Yii::app();
$request=$app->getRequest();
$user=$app->getUser();
$verb=$request->getRequestType();
$ip=$request->getUserHostAddress();
foreach($this->getRules() as $rule)
{
if(($allow=$rule->isUserAllowed($user,$filterChain->controller,$filterChain->action,$ip,$verb))>0) // allowed
break;
elseif($allow<0) // denied
{
if(isset($rule->deniedCallback))
call_user_func($rule->deniedCallback, $rule);
else
$this->accessDenied($user,$this->resolveErrorMessage($rule));
return false;
}
}
return true;
}
/**
* Resolves the error message to be displayed.
* This method will check {@link message} and {@link CAccessRule::message} to see
* what error message should be displayed.
* @param CAccessRule $rule the access rule
* @return string the error message
* @since 1.1.1
*/
protected function resolveErrorMessage($rule)
{
if($rule->message!==null)
return $rule->message;
elseif($this->message!==null)
return $this->message;
else
return Yii::t('yii','You are not authorized to perform this action.');
}
/**
* Denies the access of the user.
* This method is invoked when access check fails.
* @param IWebUser $user the current user
* @param string $message the error message to be displayed
*/
protected function accessDenied($user,$message)
{
if($user->getIsGuest())
$user->loginRequired();
else
throw new CHttpException(403,$message);
}
}
/**
* CAccessRule represents an access rule that is managed by {@link CAccessControlFilter}.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @package system.web.auth
* @since 1.0
*/
class CAccessRule extends CComponent
{
/**
* @var boolean whether this is an 'allow' rule or 'deny' rule.
*/
public $allow;
/**
* @var array list of action IDs that this rule applies to. The comparison is case-insensitive.
* If no actions are specified, rule applies to all actions.
*/
public $actions;
/**
* @var array list of controller IDs that this rule applies to. The comparison is case-insensitive.
*/
public $controllers;
/**
* @var array list of user names that this rule applies to. The comparison is case-insensitive.
* If no user names are specified, rule applies to all users.
*/
public $users;
/**
* @var array list of roles this rule applies to. For each role, the current user's
* {@link CWebUser::checkAccess} method will be invoked. If one of the invocations
* returns true, the rule will be applied.
* Note, you should mainly use roles in an "allow" rule because by definition,
* a role represents a permission collection.
* @see CAuthManager
*/
public $roles;
/**
* @var array IP patterns.
*/
public $ips;
/**
* @var array list of request types (e.g. GET, POST) that this rule applies to.
*/
public $verbs;
/**
* @var string a PHP expression whose value indicates whether this rule should be applied.
* In this expression, you can use <code>$user</code> which refers to <code>Yii::app()->user</code>.
* The expression can also be a valid PHP callback,
* including class method name (array(ClassName/Object, MethodName)),
* or anonymous function (PHP 5.3.0+). The function/method signature should be as follows:
* <pre>
* function foo($user, $rule) { ... }
* </pre>
* where $user is the current application user object and $rule is this access rule.
*
* The PHP expression will be evaluated using {@link evaluateExpression}.
*
* A PHP expression can be any PHP code that has a value. To learn more about what an expression is,
* please refer to the {@link http://www.php.net/manual/en/language.expressions.php php manual}.
*/
public $expression;
/**
* @var string the error message to be displayed when authorization is denied by this rule.
* If not set, a default error message will be displayed.
* @since 1.1.1
*/
public $message;
/**
* @var mixed the denied method callback that will be called once the
* access is denied. It replaces the behavior that shows an error message.
* It can be a valid PHP callback including class method name (array(ClassName/Object, MethodName)),
* or anonymous function (PHP 5.3.0+). For more information, on different options, check
* @link http://www.php.net/manual/en/language.pseudo-types.php#language.types.callback
* The function/method signature should be as follows:
* <pre>
* function foo($rule) { ... }
* </pre>
* where $rule is this access rule.
*
* @since 1.1.11
*/
public $deniedCallback;
/**
* Checks whether the Web user is allowed to perform the specified action.
* @param CWebUser $user the user object
* @param CController $controller the controller currently being executed
* @param CAction $action the action to be performed
* @param string $ip the request IP address
* @param string $verb the request verb (GET, POST, etc.)
* @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($user,$controller,$action,$ip,$verb)
{
if($this->isActionMatched($action)
&& $this->isUserMatched($user)
&& $this->isRoleMatched($user)
&& $this->isIpMatched($ip)
&& $this->isVerbMatched($verb)
&& $this->isControllerMatched($controller)
&& $this->isExpressionMatched($user))
return $this->allow ? 1 : -1;
else
return 0;
}
/**
* @param CAction $action the action
* @return boolean whether the rule applies to the action
*/
protected function isActionMatched($action)
{
return empty($this->actions) || in_array(strtolower($action->getId()),$this->actions);
}
/**
* @param CController $controller the controller
* @return boolean whether the rule applies to the controller
*/
protected function isControllerMatched($controller)
{
return empty($this->controllers) || in_array(strtolower($controller->getUniqueId()),$this->controllers);
}
/**
* @param IWebUser $user the user
* @return boolean whether the rule applies to the user
*/
protected function isUserMatched($user)
{
if(empty($this->users))
return true;
foreach($this->users as $u)
{
if($u==='*')
return true;
elseif($u==='?' && $user->getIsGuest())
return true;
elseif($u==='@' && !$user->getIsGuest())
return true;
elseif(!strcasecmp($u,$user->getName()))
return true;
}
return false;
}
/**
* @param IWebUser $user the user object
* @return boolean whether the rule applies to the role
*/
protected function isRoleMatched($user)
{
if(empty($this->roles))
return true;
foreach($this->roles as $key=>$role)
{
if(is_numeric($key))
{
if($user->checkAccess($role))
return true;
}
else
{
if($user->checkAccess($key,$role))
return true;
}
}
return false;
}
/**
* @param string $ip the IP address
* @return boolean whether the rule applies to the IP address
*/
protected function isIpMatched($ip)
{
if(empty($this->ips))
return true;
foreach($this->ips as $rule)
{
if($rule==='*' || $rule===$ip || (($pos=strpos($rule,'*'))!==false && !strncmp($ip,$rule,$pos)))
return true;
}
return false;
}
/**
* @param string $verb the request method
* @return boolean whether the rule applies to the request
*/
protected function isVerbMatched($verb)
{
return empty($this->verbs) || in_array(strtolower($verb),$this->verbs);
}
/**
* @param IWebUser $user the user
* @return boolean the expression value. True if the expression is not specified.
*/
protected function isExpressionMatched($user)
{
if($this->expression===null)
return true;
else
return $this->evaluateExpression($this->expression, array('user'=>$user));
}
}

View File

@@ -0,0 +1,106 @@
<?php
/**
* CAuthAssignment class file.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @link http://www.yiiframework.com/
* @copyright 2008-2013 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
/**
* CAuthAssignment represents an assignment of a role to a user.
* It includes additional assignment information such as {@link bizRule} and {@link data}.
* Do not create a CAuthAssignment instance using the 'new' operator.
* Instead, call {@link IAuthManager::assign}.
*
* @property mixed $userId User ID (see {@link IWebUser::getId}).
* @property string $itemName The authorization item name.
* @property string $bizRule The business rule associated with this assignment.
* @property mixed $data Additional data for this assignment.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @package system.web.auth
* @since 1.0
*/
class CAuthAssignment extends CComponent
{
private $_auth;
private $_itemName;
private $_userId;
private $_bizRule;
private $_data;
/**
* Constructor.
* @param IAuthManager $auth the authorization manager
* @param string $itemName authorization item name
* @param mixed $userId user ID (see {@link IWebUser::getId})
* @param string $bizRule the business rule associated with this assignment
* @param mixed $data additional data for this assignment
*/
public function __construct($auth,$itemName,$userId,$bizRule=null,$data=null)
{
$this->_auth=$auth;
$this->_itemName=$itemName;
$this->_userId=$userId;
$this->_bizRule=$bizRule;
$this->_data=$data;
}
/**
* @return mixed user ID (see {@link IWebUser::getId})
*/
public function getUserId()
{
return $this->_userId;
}
/**
* @return string the authorization item name
*/
public function getItemName()
{
return $this->_itemName;
}
/**
* @return string the business rule associated with this assignment
*/
public function getBizRule()
{
return $this->_bizRule;
}
/**
* @param string $value the business rule associated with this assignment
*/
public function setBizRule($value)
{
if($this->_bizRule!==$value)
{
$this->_bizRule=$value;
$this->_auth->saveAuthAssignment($this);
}
}
/**
* @return mixed additional data for this assignment
*/
public function getData()
{
return $this->_data;
}
/**
* @param mixed $value additional data for this assignment
*/
public function setData($value)
{
if($this->_data!==$value)
{
$this->_data=$value;
$this->_auth->saveAuthAssignment($this);
}
}
}

View File

@@ -0,0 +1,277 @@
<?php
/**
* CAuthItem class file.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @link http://www.yiiframework.com/
* @copyright 2008-2013 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
/**
* CAuthItem represents an authorization item.
* An authorization item can be an operation, a task or a role.
* They form an authorization hierarchy. Items on higher levels of the hierarchy
* inherit the permissions represented by items on lower levels.
* A user may be assigned one or several authorization items (called {@link CAuthAssignment assignments}.
* He can perform an operation only when it is among his assigned items.
*
* @property IAuthManager $authManager The authorization manager.
* @property integer $type The authorization item type. This could be 0 (operation), 1 (task) or 2 (role).
* @property string $name The item name.
* @property string $description The item description.
* @property string $bizRule The business rule associated with this item.
* @property mixed $data The additional data associated with this item.
* @property array $children All child items of this item.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @package system.web.auth
* @since 1.0
*/
class CAuthItem extends CComponent
{
const TYPE_OPERATION=0;
const TYPE_TASK=1;
const TYPE_ROLE=2;
private $_auth;
private $_type;
private $_name;
private $_description;
private $_bizRule;
private $_data;
/**
* Constructor.
* @param IAuthManager $auth authorization manager
* @param string $name authorization item name
* @param integer $type authorization item type. This can be 0 (operation), 1 (task) or 2 (role).
* @param string $description the description
* @param string $bizRule the business rule associated with this item
* @param mixed $data additional data for this item
*/
public function __construct($auth,$name,$type,$description='',$bizRule=null,$data=null)
{
$this->_type=(int)$type;
$this->_auth=$auth;
$this->_name=$name;
$this->_description=$description;
$this->_bizRule=$bizRule;
$this->_data=$data;
}
/**
* Checks to see if the specified item is within the hierarchy starting from this item.
* This method is expected to be internally used by the actual implementations
* of the {@link IAuthManager::checkAccess}.
* @param string $itemName the name of the item to be checked
* @param array $params the parameters to be passed to business rule evaluation
* @return boolean whether the specified item is within the hierarchy starting from this item.
*/
public function checkAccess($itemName,$params=array())
{
Yii::trace('Checking permission "'.$this->_name.'"','system.web.auth.CAuthItem');
if($this->_auth->executeBizRule($this->_bizRule,$params,$this->_data))
{
if($this->_name==$itemName)
return true;
foreach($this->_auth->getItemChildren($this->_name) as $item)
{
if($item->checkAccess($itemName,$params))
return true;
}
}
return false;
}
/**
* @return IAuthManager the authorization manager
*/
public function getAuthManager()
{
return $this->_auth;
}
/**
* @return integer the authorization item type. This could be 0 (operation), 1 (task) or 2 (role).
*/
public function getType()
{
return $this->_type;
}
/**
* @return string the item name
*/
public function getName()
{
return $this->_name;
}
/**
* @param string $value the item name
*/
public function setName($value)
{
if($this->_name!==$value)
{
$oldName=$this->_name;
$this->_name=$value;
$this->_auth->saveAuthItem($this,$oldName);
}
}
/**
* @return string the item description
*/
public function getDescription()
{
return $this->_description;
}
/**
* @param string $value the item description
*/
public function setDescription($value)
{
if($this->_description!==$value)
{
$this->_description=$value;
$this->_auth->saveAuthItem($this);
}
}
/**
* @return string the business rule associated with this item
*/
public function getBizRule()
{
return $this->_bizRule;
}
/**
* @param string $value the business rule associated with this item
*/
public function setBizRule($value)
{
if($this->_bizRule!==$value)
{
$this->_bizRule=$value;
$this->_auth->saveAuthItem($this);
}
}
/**
* @return mixed the additional data associated with this item
*/
public function getData()
{
return $this->_data;
}
/**
* @param mixed $value the additional data associated with this item
*/
public function setData($value)
{
if($this->_data!==$value)
{
$this->_data=$value;
$this->_auth->saveAuthItem($this);
}
}
/**
* Adds a child item.
* @param string $name the name of the child item
* @return boolean whether the item is added successfully
* @throws CException if either parent or child doesn't exist or if a loop has been detected.
* @see IAuthManager::addItemChild
*/
public function addChild($name)
{
return $this->_auth->addItemChild($this->_name,$name);
}
/**
* Removes a child item.
* Note, the child item is not deleted. Only the parent-child relationship is removed.
* @param string $name the child item name
* @return boolean whether the removal is successful
* @see IAuthManager::removeItemChild
*/
public function removeChild($name)
{
return $this->_auth->removeItemChild($this->_name,$name);
}
/**
* Returns a value indicating whether a child exists
* @param string $name the child item name
* @return boolean whether the child exists
* @see IAuthManager::hasItemChild
*/
public function hasChild($name)
{
return $this->_auth->hasItemChild($this->_name,$name);
}
/**
* Returns the children of this item.
* @return array all child items of this item.
* @see IAuthManager::getItemChildren
*/
public function getChildren()
{
return $this->_auth->getItemChildren($this->_name);
}
/**
* Assigns this item to a user.
* @param mixed $userId the user ID (see {@link IWebUser::getId})
* @param string $bizRule the business rule to be executed when {@link checkAccess} is called
* for this particular authorization item.
* @param mixed $data additional data associated with this assignment
* @return CAuthAssignment the authorization assignment information.
* @throws CException if the item has already been assigned to the user
* @see IAuthManager::assign
*/
public function assign($userId,$bizRule=null,$data=null)
{
return $this->_auth->assign($this->_name,$userId,$bizRule,$data);
}
/**
* Revokes an authorization assignment from a user.
* @param mixed $userId the user ID (see {@link IWebUser::getId})
* @return boolean whether removal is successful
* @see IAuthManager::revoke
*/
public function revoke($userId)
{
return $this->_auth->revoke($this->_name,$userId);
}
/**
* Returns a value indicating whether this item has been assigned to the user.
* @param mixed $userId the user ID (see {@link IWebUser::getId})
* @return boolean whether the item has been assigned to the user.
* @see IAuthManager::isAssigned
*/
public function isAssigned($userId)
{
return $this->_auth->isAssigned($this->_name,$userId);
}
/**
* Returns the item assignment information.
* @param mixed $userId the user ID (see {@link IWebUser::getId})
* @return CAuthAssignment the item assignment information. Null is returned if
* this item is not assigned to the user.
* @see IAuthManager::getAuthAssignment
*/
public function getAssignment($userId)
{
return $this->_auth->getAuthAssignment($this->_name,$userId);
}
}

View File

@@ -0,0 +1,165 @@
<?php
/**
* CAuthManager class file.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @link http://www.yiiframework.com/
* @copyright 2008-2013 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
/**
* CAuthManager is the base class for authorization manager classes.
*
* CAuthManager extends {@link CApplicationComponent} and implements some methods
* that are common among authorization manager classes.
*
* CAuthManager together with its concrete child classes implement the Role-Based
* Access Control (RBAC).
*
* The main idea is that permissions are organized as a hierarchy of
* {@link CAuthItem authorization items}. Items on higer level inherit the permissions
* represented by items on lower level. And roles are simply top-level authorization items
* that may be assigned to individual users. A user is said to have a permission
* to do something if the corresponding authorization item is inherited by one of his roles.
*
* Using authorization manager consists of two aspects. First, the authorization hierarchy
* and assignments have to be established. CAuthManager and its child classes
* provides APIs to accomplish this task. Developers may need to develop some GUI
* so that it is more intuitive to end-users. Second, developers call {@link IAuthManager::checkAccess}
* at appropriate places in the application code to check if the current user
* has the needed permission for an operation.
*
* @property array $roles Roles (name=>CAuthItem).
* @property array $tasks Tasks (name=>CAuthItem).
* @property array $operations Operations (name=>CAuthItem).
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @package system.web.auth
* @since 1.0
*/
abstract class CAuthManager extends CApplicationComponent implements IAuthManager
{
/**
* @var boolean Enable error reporting for bizRules.
* @since 1.1.3
*/
public $showErrors = false;
/**
* @var array list of role names that are assigned to all users implicitly.
* These roles do not need to be explicitly assigned to any user.
* When calling {@link checkAccess}, these roles will be checked first.
* For performance reason, you should minimize the number of such roles.
* A typical usage of such roles is to define an 'authenticated' role and associate
* it with a biz rule which checks if the current user is authenticated.
* And then declare 'authenticated' in this property so that it can be applied to
* every authenticated user.
*/
public $defaultRoles=array();
/**
* Creates a role.
* This is a shortcut method to {@link IAuthManager::createAuthItem}.
* @param string $name the item name
* @param string $description the item description.
* @param string $bizRule the business rule associated with this item
* @param mixed $data additional data to be passed when evaluating the business rule
* @return CAuthItem the authorization item
*/
public function createRole($name,$description='',$bizRule=null,$data=null)
{
return $this->createAuthItem($name,CAuthItem::TYPE_ROLE,$description,$bizRule,$data);
}
/**
* Creates a task.
* This is a shortcut method to {@link IAuthManager::createAuthItem}.
* @param string $name the item name
* @param string $description the item description.
* @param string $bizRule the business rule associated with this item
* @param mixed $data additional data to be passed when evaluating the business rule
* @return CAuthItem the authorization item
*/
public function createTask($name,$description='',$bizRule=null,$data=null)
{
return $this->createAuthItem($name,CAuthItem::TYPE_TASK,$description,$bizRule,$data);
}
/**
* Creates an operation.
* This is a shortcut method to {@link IAuthManager::createAuthItem}.
* @param string $name the item name
* @param string $description the item description.
* @param string $bizRule the business rule associated with this item
* @param mixed $data additional data to be passed when evaluating the business rule
* @return CAuthItem the authorization item
*/
public function createOperation($name,$description='',$bizRule=null,$data=null)
{
return $this->createAuthItem($name,CAuthItem::TYPE_OPERATION,$description,$bizRule,$data);
}
/**
* Returns roles.
* This is a shortcut method to {@link IAuthManager::getAuthItems}.
* @param mixed $userId the user ID. If not null, only the roles directly assigned to the user
* will be returned. Otherwise, all roles will be returned.
* @return array roles (name=>CAuthItem)
*/
public function getRoles($userId=null)
{
return $this->getAuthItems(CAuthItem::TYPE_ROLE,$userId);
}
/**
* Returns tasks.
* This is a shortcut method to {@link IAuthManager::getAuthItems}.
* @param mixed $userId the user ID. If not null, only the tasks directly assigned to the user
* will be returned. Otherwise, all tasks will be returned.
* @return array tasks (name=>CAuthItem)
*/
public function getTasks($userId=null)
{
return $this->getAuthItems(CAuthItem::TYPE_TASK,$userId);
}
/**
* Returns operations.
* This is a shortcut method to {@link IAuthManager::getAuthItems}.
* @param mixed $userId the user ID. If not null, only the operations directly assigned to the user
* will be returned. Otherwise, all operations will be returned.
* @return array operations (name=>CAuthItem)
*/
public function getOperations($userId=null)
{
return $this->getAuthItems(CAuthItem::TYPE_OPERATION,$userId);
}
/**
* Executes the specified business rule.
* @param string $bizRule the business rule to be executed.
* @param array $params parameters passed to {@link IAuthManager::checkAccess}.
* @param mixed $data additional data associated with the authorization item or assignment.
* @return boolean whether the business rule returns true.
* If the business rule is empty, it will still return true.
*/
public function executeBizRule($bizRule,$params,$data)
{
return $bizRule==='' || $bizRule===null || ($this->showErrors ? eval($bizRule)!=0 : @eval($bizRule)!=0);
}
/**
* Checks the item types to make sure a child can be added to a parent.
* @param integer $parentType parent item type
* @param integer $childType child item type
* @throws CException if the item cannot be added as a child due to its incompatible type.
*/
protected function checkItemChildType($parentType,$childType)
{
static $types=array('operation','task','role');
if($parentType < $childType)
throw new CException(Yii::t('yii','Cannot add an item of type "{child}" to an item of type "{parent}".',
array('{child}'=>$types[$childType], '{parent}'=>$types[$parentType])));
}
}

View File

@@ -0,0 +1,131 @@
<?php
/**
* CBaseUserIdentity class file
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @link http://www.yiiframework.com/
* @copyright 2008-2013 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
/**
* CBaseUserIdentity is a base class implementing {@link IUserIdentity}.
*
* CBaseUserIdentity implements the scheme for representing identity
* information that needs to be persisted. It also provides the way
* to represent the authentication errors.
*
* Derived classes should implement {@link IUserIdentity::authenticate}
* and {@link IUserIdentity::getId} that are required by the {@link IUserIdentity}
* interface.
*
* @property mixed $id A value that uniquely represents the identity (e.g. primary key value).
* The default implementation simply returns {@link name}.
* @property string $name The display name for the identity.
* The default implementation simply returns empty string.
* @property array $persistentStates The identity states that should be persisted.
* @property boolean $isAuthenticated Whether the authentication is successful.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @package system.web.auth
* @since 1.0
*/
abstract class CBaseUserIdentity extends CComponent implements IUserIdentity
{
const ERROR_NONE=0;
const ERROR_USERNAME_INVALID=1;
const ERROR_PASSWORD_INVALID=2;
const ERROR_UNKNOWN_IDENTITY=100;
/**
* @var integer the authentication error code. If there is an error, the error code will be non-zero.
* Defaults to 100, meaning unknown identity. Calling {@link authenticate} will change this value.
*/
public $errorCode=self::ERROR_UNKNOWN_IDENTITY;
/**
* @var string the authentication error message. Defaults to empty.
*/
public $errorMessage='';
private $_state=array();
/**
* Returns a value that uniquely represents the identity.
* @return mixed a value that uniquely represents the identity (e.g. primary key value).
* The default implementation simply returns {@link name}.
*/
public function getId()
{
return $this->getName();
}
/**
* Returns the display name for the identity (e.g. username).
* @return string the display name for the identity.
* The default implementation simply returns empty string.
*/
public function getName()
{
return '';
}
/**
* Returns the identity states that should be persisted.
* This method is required by {@link IUserIdentity}.
* @return array the identity states that should be persisted.
*/
public function getPersistentStates()
{
return $this->_state;
}
/**
* Sets an array of persistent states.
*
* @param array $states the identity states that should be persisted.
*/
public function setPersistentStates($states)
{
$this->_state = $states;
}
/**
* Returns a value indicating whether the identity is authenticated.
* This method is required by {@link IUserIdentity}.
* @return boolean whether the authentication is successful.
*/
public function getIsAuthenticated()
{
return $this->errorCode==self::ERROR_NONE;
}
/**
* Gets the persisted state by the specified name.
* @param string $name the name of the state
* @param mixed $defaultValue the default value to be returned if the named state does not exist
* @return mixed the value of the named state
*/
public function getState($name,$defaultValue=null)
{
return isset($this->_state[$name])?$this->_state[$name]:$defaultValue;
}
/**
* Sets the named state with a given value.
* @param string $name the name of the state
* @param mixed $value the value of the named state
*/
public function setState($name,$value)
{
$this->_state[$name]=$value;
}
/**
* Removes the specified state.
* @param string $name the name of the state
*/
public function clearState($name)
{
unset($this->_state[$name]);
}
}

View File

@@ -0,0 +1,603 @@
<?php
/**
* CDbAuthManager class file.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @link http://www.yiiframework.com/
* @copyright 2008-2013 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
/**
* CDbAuthManager represents an authorization manager that stores authorization information in database.
*
* The database connection is specified by {@link connectionID}. And the database schema
* should be as described in "framework/web/auth/*.sql". You may change the names of
* the three tables used to store the authorization data by setting {@link itemTable},
* {@link itemChildTable} and {@link assignmentTable}.
*
* @property array $authItems The authorization items of the specific type.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @package system.web.auth
* @since 1.0
*/
class CDbAuthManager extends CAuthManager
{
/**
* @var string the ID of the {@link CDbConnection} application component. Defaults to 'db'.
* The database must have the tables as declared in "framework/web/auth/*.sql".
*/
public $connectionID='db';
/**
* @var string the name of the table storing authorization items. Defaults to 'AuthItem'.
*/
public $itemTable='AuthItem';
/**
* @var string the name of the table storing authorization item hierarchy. Defaults to 'AuthItemChild'.
*/
public $itemChildTable='AuthItemChild';
/**
* @var string the name of the table storing authorization item assignments. Defaults to 'AuthAssignment'.
*/
public $assignmentTable='AuthAssignment';
/**
* @var CDbConnection the database connection. By default, this is initialized
* automatically as the application component whose ID is indicated as {@link connectionID}.
*/
public $db;
private $_usingSqlite;
/**
* Initializes the application component.
* This method overrides the parent implementation by establishing the database connection.
*/
public function init()
{
parent::init();
$this->_usingSqlite=!strncmp($this->getDbConnection()->getDriverName(),'sqlite',6);
}
/**
* Performs access check for the specified user.
* @param string $itemName the name of the operation that need access check
* @param mixed $userId the user ID. This should can be either an integer and a string representing
* the unique identifier of a user. See {@link IWebUser::getId}.
* @param array $params name-value pairs that would be passed to biz rules associated
* with the tasks and roles assigned to the user.
* Since version 1.1.11 a param with name 'userId' is added to this array, which holds the value of <code>$userId</code>.
* @return boolean whether the operations can be performed by the user.
*/
public function checkAccess($itemName,$userId,$params=array())
{
$assignments=$this->getAuthAssignments($userId);
return $this->checkAccessRecursive($itemName,$userId,$params,$assignments);
}
/**
* Performs access check for the specified user.
* This method is internally called by {@link checkAccess}.
* @param string $itemName the name of the operation that need access check
* @param mixed $userId the user ID. This should can be either an integer and a string representing
* the unique identifier of a user. See {@link IWebUser::getId}.
* @param array $params name-value pairs that would be passed to biz rules associated
* with the tasks and roles assigned to the user.
* Since version 1.1.11 a param with name 'userId' is added to this array, which holds the value of <code>$userId</code>.
* @param array $assignments the assignments to the specified user
* @return boolean whether the operations can be performed by the user.
* @since 1.1.3
*/
protected function checkAccessRecursive($itemName,$userId,$params,$assignments)
{
if(($item=$this->getAuthItem($itemName))===null)
return false;
Yii::trace('Checking permission "'.$item->getName().'"','system.web.auth.CDbAuthManager');
if(!isset($params['userId']))
$params['userId'] = $userId;
if($this->executeBizRule($item->getBizRule(),$params,$item->getData()))
{
if(in_array($itemName,$this->defaultRoles))
return true;
if(isset($assignments[$itemName]))
{
$assignment=$assignments[$itemName];
if($this->executeBizRule($assignment->getBizRule(),$params,$assignment->getData()))
return true;
}
$parents=$this->db->createCommand()
->select('parent')
->from($this->itemChildTable)
->where('child=:name', array(':name'=>$itemName))
->queryColumn();
foreach($parents as $parent)
{
if($this->checkAccessRecursive($parent,$userId,$params,$assignments))
return true;
}
}
return false;
}
/**
* Adds an item as a child of another item.
* @param string $itemName the parent item name
* @param string $childName the child item name
* @return boolean whether the item is added successfully
* @throws CException if either parent or child doesn't exist or if a loop has been detected.
*/
public function addItemChild($itemName,$childName)
{
if($itemName===$childName)
throw new CException(Yii::t('yii','Cannot add "{name}" as a child of itself.',
array('{name}'=>$itemName)));
$rows=$this->db->createCommand()
->select()
->from($this->itemTable)
->where('name=:name1 OR name=:name2', array(
':name1'=>$itemName,
':name2'=>$childName
))
->queryAll();
if(count($rows)==2)
{
if($rows[0]['name']===$itemName)
{
$parentType=$rows[0]['type'];
$childType=$rows[1]['type'];
}
else
{
$childType=$rows[0]['type'];
$parentType=$rows[1]['type'];
}
$this->checkItemChildType($parentType,$childType);
if($this->detectLoop($itemName,$childName))
throw new CException(Yii::t('yii','Cannot add "{child}" as a child of "{name}". A loop has been detected.',
array('{child}'=>$childName,'{name}'=>$itemName)));
$this->db->createCommand()
->insert($this->itemChildTable, array(
'parent'=>$itemName,
'child'=>$childName,
));
return true;
}
else
throw new CException(Yii::t('yii','Either "{parent}" or "{child}" does not exist.',array('{child}'=>$childName,'{parent}'=>$itemName)));
}
/**
* Removes a child from its parent.
* Note, the child item is not deleted. Only the parent-child relationship is removed.
* @param string $itemName the parent item name
* @param string $childName the child item name
* @return boolean whether the removal is successful
*/
public function removeItemChild($itemName,$childName)
{
return $this->db->createCommand()
->delete($this->itemChildTable, 'parent=:parent AND child=:child', array(
':parent'=>$itemName,
':child'=>$childName
)) > 0;
}
/**
* Returns a value indicating whether a child exists within a parent.
* @param string $itemName the parent item name
* @param string $childName the child item name
* @return boolean whether the child exists
*/
public function hasItemChild($itemName,$childName)
{
return $this->db->createCommand()
->select('parent')
->from($this->itemChildTable)
->where('parent=:parent AND child=:child', array(
':parent'=>$itemName,
':child'=>$childName))
->queryScalar() !== false;
}
/**
* Returns the children of the specified item.
* @param mixed $names the parent item name. This can be either a string or an array.
* The latter represents a list of item names.
* @return array all child items of the parent
*/
public function getItemChildren($names)
{
if(is_string($names))
$condition='parent='.$this->db->quoteValue($names);
elseif(is_array($names) && $names!==array())
{
foreach($names as &$name)
$name=$this->db->quoteValue($name);
$condition='parent IN ('.implode(', ',$names).')';
}
$rows=$this->db->createCommand()
->select('name, type, description, bizrule, data')
->from(array(
$this->itemTable,
$this->itemChildTable
))
->where($condition.' AND name=child')
->queryAll();
$children=array();
foreach($rows as $row)
{
if(($data=@unserialize($row['data']))===false)
$data=null;
$children[$row['name']]=new CAuthItem($this,$row['name'],$row['type'],$row['description'],$row['bizrule'],$data);
}
return $children;
}
/**
* Assigns an authorization item to a user.
* @param string $itemName the item name
* @param mixed $userId the user ID (see {@link IWebUser::getId})
* @param string $bizRule the business rule to be executed when {@link checkAccess} is called
* for this particular authorization item.
* @param mixed $data additional data associated with this assignment
* @return CAuthAssignment the authorization assignment information.
* @throws CException if the item does not exist or if the item has already been assigned to the user
*/
public function assign($itemName,$userId,$bizRule=null,$data=null)
{
if($this->usingSqlite() && $this->getAuthItem($itemName)===null)
throw new CException(Yii::t('yii','The item "{name}" does not exist.',array('{name}'=>$itemName)));
$this->db->createCommand()
->insert($this->assignmentTable, array(
'itemname'=>$itemName,
'userid'=>$userId,
'bizrule'=>$bizRule,
'data'=>serialize($data)
));
return new CAuthAssignment($this,$itemName,$userId,$bizRule,$data);
}
/**
* Revokes an authorization assignment from a user.
* @param string $itemName the item name
* @param mixed $userId the user ID (see {@link IWebUser::getId})
* @return boolean whether removal is successful
*/
public function revoke($itemName,$userId)
{
return $this->db->createCommand()
->delete($this->assignmentTable, 'itemname=:itemname AND userid=:userid', array(
':itemname'=>$itemName,
':userid'=>$userId
)) > 0;
}
/**
* Returns a value indicating whether the item has been assigned to the user.
* @param string $itemName the item name
* @param mixed $userId the user ID (see {@link IWebUser::getId})
* @return boolean whether the item has been assigned to the user.
*/
public function isAssigned($itemName,$userId)
{
return $this->db->createCommand()
->select('itemname')
->from($this->assignmentTable)
->where('itemname=:itemname AND userid=:userid', array(
':itemname'=>$itemName,
':userid'=>$userId))
->queryScalar() !== false;
}
/**
* Returns the item assignment information.
* @param string $itemName the item name
* @param mixed $userId the user ID (see {@link IWebUser::getId})
* @return CAuthAssignment the item assignment information. Null is returned if
* the item is not assigned to the user.
*/
public function getAuthAssignment($itemName,$userId)
{
$row=$this->db->createCommand()
->select()
->from($this->assignmentTable)
->where('itemname=:itemname AND userid=:userid', array(
':itemname'=>$itemName,
':userid'=>$userId))
->queryRow();
if($row!==false)
{
if(($data=@unserialize($row['data']))===false)
$data=null;
return new CAuthAssignment($this,$row['itemname'],$row['userid'],$row['bizrule'],$data);
}
else
return null;
}
/**
* Returns the item assignments for the specified user.
* @param mixed $userId the user ID (see {@link IWebUser::getId})
* @return array the item assignment information for the user. An empty array will be
* returned if there is no item assigned to the user.
*/
public function getAuthAssignments($userId)
{
$rows=$this->db->createCommand()
->select()
->from($this->assignmentTable)
->where('userid=:userid', array(':userid'=>$userId))
->queryAll();
$assignments=array();
foreach($rows as $row)
{
if(($data=@unserialize($row['data']))===false)
$data=null;
$assignments[$row['itemname']]=new CAuthAssignment($this,$row['itemname'],$row['userid'],$row['bizrule'],$data);
}
return $assignments;
}
/**
* Saves the changes to an authorization assignment.
* @param CAuthAssignment $assignment the assignment that has been changed.
*/
public function saveAuthAssignment($assignment)
{
$this->db->createCommand()
->update($this->assignmentTable, array(
'bizrule'=>$assignment->getBizRule(),
'data'=>serialize($assignment->getData()),
), 'itemname=:itemname AND userid=:userid', array(
'itemname'=>$assignment->getItemName(),
'userid'=>$assignment->getUserId()
));
}
/**
* Returns the authorization items of the specific type and user.
* @param integer $type the item type (0: operation, 1: task, 2: role). Defaults to null,
* meaning returning all items regardless of their type.
* @param mixed $userId the user ID. Defaults to null, meaning returning all items even if
* they are not assigned to a user.
* @return array the authorization items of the specific type.
*/
public function getAuthItems($type=null,$userId=null)
{
if($type===null && $userId===null)
{
$command=$this->db->createCommand()
->select()
->from($this->itemTable);
}
elseif($userId===null)
{
$command=$this->db->createCommand()
->select()
->from($this->itemTable)
->where('type=:type', array(':type'=>$type));
}
elseif($type===null)
{
$command=$this->db->createCommand()
->select('name,type,description,t1.bizrule,t1.data')
->from(array(
$this->itemTable.' t1',
$this->assignmentTable.' t2'
))
->where('name=itemname AND userid=:userid', array(':userid'=>$userId));
}
else
{
$command=$this->db->createCommand()
->select('name,type,description,t1.bizrule,t1.data')
->from(array(
$this->itemTable.' t1',
$this->assignmentTable.' t2'
))
->where('name=itemname AND type=:type AND userid=:userid', array(
':type'=>$type,
':userid'=>$userId
));
}
$items=array();
foreach($command->queryAll() as $row)
{
if(($data=@unserialize($row['data']))===false)
$data=null;
$items[$row['name']]=new CAuthItem($this,$row['name'],$row['type'],$row['description'],$row['bizrule'],$data);
}
return $items;
}
/**
* Creates an authorization item.
* An authorization item represents an action permission (e.g. creating a post).
* It has three types: operation, task and role.
* Authorization items form a hierarchy. Higher level items inheirt permissions representing
* by lower level items.
* @param string $name the item name. This must be a unique identifier.
* @param integer $type the item type (0: operation, 1: task, 2: role).
* @param string $description description of the item
* @param string $bizRule business rule associated with the item. This is a piece of
* PHP code that will be executed when {@link checkAccess} is called for the item.
* @param mixed $data additional data associated with the item.
* @return CAuthItem the authorization item
* @throws CException if an item with the same name already exists
*/
public function createAuthItem($name,$type,$description='',$bizRule=null,$data=null)
{
$this->db->createCommand()
->insert($this->itemTable, array(
'name'=>$name,
'type'=>$type,
'description'=>$description,
'bizrule'=>$bizRule,
'data'=>serialize($data)
));
return new CAuthItem($this,$name,$type,$description,$bizRule,$data);
}
/**
* Removes the specified authorization item.
* @param string $name the name of the item to be removed
* @return boolean whether the item exists in the storage and has been removed
*/
public function removeAuthItem($name)
{
if($this->usingSqlite())
{
$this->db->createCommand()
->delete($this->itemChildTable, 'parent=:name1 OR child=:name2', array(
':name1'=>$name,
':name2'=>$name
));
$this->db->createCommand()
->delete($this->assignmentTable, 'itemname=:name', array(
':name'=>$name,
));
}
return $this->db->createCommand()
->delete($this->itemTable, 'name=:name', array(
':name'=>$name
)) > 0;
}
/**
* Returns the authorization item with the specified name.
* @param string $name the name of the item
* @return CAuthItem the authorization item. Null if the item cannot be found.
*/
public function getAuthItem($name)
{
$row=$this->db->createCommand()
->select()
->from($this->itemTable)
->where('name=:name', array(':name'=>$name))
->queryRow();
if($row!==false)
{
if(($data=@unserialize($row['data']))===false)
$data=null;
return new CAuthItem($this,$row['name'],$row['type'],$row['description'],$row['bizrule'],$data);
}
else
return null;
}
/**
* Saves an authorization item to persistent storage.
* @param CAuthItem $item the item to be saved.
* @param string $oldName the old item name. If null, it means the item name is not changed.
*/
public function saveAuthItem($item,$oldName=null)
{
if($this->usingSqlite() && $oldName!==null && $item->getName()!==$oldName)
{
$this->db->createCommand()
->update($this->itemChildTable, array(
'parent'=>$item->getName(),
), 'parent=:whereName', array(
':whereName'=>$oldName,
));
$this->db->createCommand()
->update($this->itemChildTable, array(
'child'=>$item->getName(),
), 'child=:whereName', array(
':whereName'=>$oldName,
));
$this->db->createCommand()
->update($this->assignmentTable, array(
'itemname'=>$item->getName(),
), 'itemname=:whereName', array(
':whereName'=>$oldName,
));
}
$this->db->createCommand()
->update($this->itemTable, array(
'name'=>$item->getName(),
'type'=>$item->getType(),
'description'=>$item->getDescription(),
'bizrule'=>$item->getBizRule(),
'data'=>serialize($item->getData()),
), 'name=:whereName', array(
':whereName'=>$oldName===null?$item->getName():$oldName,
));
}
/**
* Saves the authorization data to persistent storage.
*/
public function save()
{
}
/**
* Removes all authorization data.
*/
public function clearAll()
{
$this->clearAuthAssignments();
$this->db->createCommand()->delete($this->itemChildTable);
$this->db->createCommand()->delete($this->itemTable);
}
/**
* Removes all authorization assignments.
*/
public function clearAuthAssignments()
{
$this->db->createCommand()->delete($this->assignmentTable);
}
/**
* Checks whether there is a loop in the authorization item hierarchy.
* @param string $itemName parent item name
* @param string $childName the name of the child item that is to be added to the hierarchy
* @return boolean whether a loop exists
*/
protected function detectLoop($itemName,$childName)
{
if($childName===$itemName)
return true;
foreach($this->getItemChildren($childName) as $child)
{
if($this->detectLoop($itemName,$child->getName()))
return true;
}
return false;
}
/**
* @return CDbConnection the DB connection instance
* @throws CException if {@link connectionID} does not point to a valid application component.
*/
protected function getDbConnection()
{
if($this->db!==null)
return $this->db;
elseif(($this->db=Yii::app()->getComponent($this->connectionID)) instanceof CDbConnection)
return $this->db;
else
throw new CException(Yii::t('yii','CDbAuthManager.connectionID "{id}" is invalid. Please make sure it refers to the ID of a CDbConnection application component.',
array('{id}'=>$this->connectionID)));
}
/**
* @return boolean whether the database is a SQLite database
*/
protected function usingSqlite()
{
return $this->_usingSqlite;
}
}

View File

@@ -0,0 +1,506 @@
<?php
/**
* CPhpAuthManager class file.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @link http://www.yiiframework.com/
* @copyright 2008-2013 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
/**
* CPhpAuthManager represents an authorization manager that stores authorization information in terms of a PHP script file.
*
* The authorization data will be saved to and loaded from a file
* specified by {@link authFile}, which defaults to 'protected/data/auth.php'.
*
* CPhpAuthManager is mainly suitable for authorization data that is not too big
* (for example, the authorization data for a personal blog system).
* Use {@link CDbAuthManager} for more complex authorization data.
*
* @property array $authItems The authorization items of the specific type.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @package system.web.auth
* @since 1.0
*/
class CPhpAuthManager extends CAuthManager
{
/**
* @var string the path of the PHP script that contains the authorization data.
* If not set, it will be using 'protected/data/auth.php' as the data file.
* Make sure this file is writable by the Web server process if the authorization
* needs to be changed.
* @see loadFromFile
* @see saveToFile
*/
public $authFile;
private $_items=array(); // itemName => item
private $_children=array(); // itemName, childName => child
private $_assignments=array(); // userId, itemName => assignment
/**
* Initializes the application component.
* This method overrides parent implementation by loading the authorization data
* from PHP script.
*/
public function init()
{
parent::init();
if($this->authFile===null)
$this->authFile=Yii::getPathOfAlias('application.data.auth').'.php';
$this->load();
}
/**
* Performs access check for the specified user.
* @param string $itemName the name of the operation that need access check
* @param mixed $userId the user ID. This can be either an integer or a string representing
* the unique identifier of a user. See {@link IWebUser::getId}.
* @param array $params name-value pairs that would be passed to biz rules associated
* with the tasks and roles assigned to the user.
* Since version 1.1.11 a param with name 'userId' is added to this array, which holds the value of <code>$userId</code>.
* @return boolean whether the operations can be performed by the user.
*/
public function checkAccess($itemName,$userId,$params=array())
{
if(!isset($this->_items[$itemName]))
return false;
$item=$this->_items[$itemName];
Yii::trace('Checking permission "'.$item->getName().'"','system.web.auth.CPhpAuthManager');
if(!isset($params['userId']))
$params['userId'] = $userId;
if($this->executeBizRule($item->getBizRule(),$params,$item->getData()))
{
if(in_array($itemName,$this->defaultRoles))
return true;
if(isset($this->_assignments[$userId][$itemName]))
{
$assignment=$this->_assignments[$userId][$itemName];
if($this->executeBizRule($assignment->getBizRule(),$params,$assignment->getData()))
return true;
}
foreach($this->_children as $parentName=>$children)
{
if(isset($children[$itemName]) && $this->checkAccess($parentName,$userId,$params))
return true;
}
}
return false;
}
/**
* Adds an item as a child of another item.
* @param string $itemName the parent item name
* @param string $childName the child item name
* @return boolean whether the item is added successfully
* @throws CException if either parent or child doesn't exist or if a loop has been detected.
*/
public function addItemChild($itemName,$childName)
{
if(!isset($this->_items[$childName],$this->_items[$itemName]))
throw new CException(Yii::t('yii','Either "{parent}" or "{child}" does not exist.',array('{child}'=>$childName,'{name}'=>$itemName)));
$child=$this->_items[$childName];
$item=$this->_items[$itemName];
$this->checkItemChildType($item->getType(),$child->getType());
if($this->detectLoop($itemName,$childName))
throw new CException(Yii::t('yii','Cannot add "{child}" as a child of "{parent}". A loop has been detected.',
array('{child}'=>$childName,'{parent}'=>$itemName)));
if(isset($this->_children[$itemName][$childName]))
throw new CException(Yii::t('yii','The item "{parent}" already has a child "{child}".',
array('{child}'=>$childName,'{parent}'=>$itemName)));
$this->_children[$itemName][$childName]=$this->_items[$childName];
return true;
}
/**
* Removes a child from its parent.
* Note, the child item is not deleted. Only the parent-child relationship is removed.
* @param string $itemName the parent item name
* @param string $childName the child item name
* @return boolean whether the removal is successful
*/
public function removeItemChild($itemName,$childName)
{
if(isset($this->_children[$itemName][$childName]))
{
unset($this->_children[$itemName][$childName]);
return true;
}
else
return false;
}
/**
* Returns a value indicating whether a child exists within a parent.
* @param string $itemName the parent item name
* @param string $childName the child item name
* @return boolean whether the child exists
*/
public function hasItemChild($itemName,$childName)
{
return isset($this->_children[$itemName][$childName]);
}
/**
* Returns the children of the specified item.
* @param mixed $names the parent item name. This can be either a string or an array.
* The latter represents a list of item names.
* @return array all child items of the parent
*/
public function getItemChildren($names)
{
if(is_string($names))
return isset($this->_children[$names]) ? $this->_children[$names] : array();
$children=array();
foreach($names as $name)
{
if(isset($this->_children[$name]))
$children=array_merge($children,$this->_children[$name]);
}
return $children;
}
/**
* Assigns an authorization item to a user.
* @param string $itemName the item name
* @param mixed $userId the user ID (see {@link IWebUser::getId})
* @param string $bizRule the business rule to be executed when {@link checkAccess} is called
* for this particular authorization item.
* @param mixed $data additional data associated with this assignment
* @return CAuthAssignment the authorization assignment information.
* @throws CException if the item does not exist or if the item has already been assigned to the user
*/
public function assign($itemName,$userId,$bizRule=null,$data=null)
{
if(!isset($this->_items[$itemName]))
throw new CException(Yii::t('yii','Unknown authorization item "{name}".',array('{name}'=>$itemName)));
elseif(isset($this->_assignments[$userId][$itemName]))
throw new CException(Yii::t('yii','Authorization item "{item}" has already been assigned to user "{user}".',
array('{item}'=>$itemName,'{user}'=>$userId)));
else
return $this->_assignments[$userId][$itemName]=new CAuthAssignment($this,$itemName,$userId,$bizRule,$data);
}
/**
* Revokes an authorization assignment from a user.
* @param string $itemName the item name
* @param mixed $userId the user ID (see {@link IWebUser::getId})
* @return boolean whether removal is successful
*/
public function revoke($itemName,$userId)
{
if(isset($this->_assignments[$userId][$itemName]))
{
unset($this->_assignments[$userId][$itemName]);
return true;
}
else
return false;
}
/**
* Returns a value indicating whether the item has been assigned to the user.
* @param string $itemName the item name
* @param mixed $userId the user ID (see {@link IWebUser::getId})
* @return boolean whether the item has been assigned to the user.
*/
public function isAssigned($itemName,$userId)
{
return isset($this->_assignments[$userId][$itemName]);
}
/**
* Returns the item assignment information.
* @param string $itemName the item name
* @param mixed $userId the user ID (see {@link IWebUser::getId})
* @return CAuthAssignment the item assignment information. Null is returned if
* the item is not assigned to the user.
*/
public function getAuthAssignment($itemName,$userId)
{
return isset($this->_assignments[$userId][$itemName])?$this->_assignments[$userId][$itemName]:null;
}
/**
* Returns the item assignments for the specified user.
* @param mixed $userId the user ID (see {@link IWebUser::getId})
* @return array the item assignment information for the user. An empty array will be
* returned if there is no item assigned to the user.
*/
public function getAuthAssignments($userId)
{
return isset($this->_assignments[$userId])?$this->_assignments[$userId]:array();
}
/**
* Returns the authorization items of the specific type and user.
* @param integer $type the item type (0: operation, 1: task, 2: role). Defaults to null,
* meaning returning all items regardless of their type.
* @param mixed $userId the user ID. Defaults to null, meaning returning all items even if
* they are not assigned to a user.
* @return array the authorization items of the specific type.
*/
public function getAuthItems($type=null,$userId=null)
{
if($type===null && $userId===null)
return $this->_items;
$items=array();
if($userId===null)
{
foreach($this->_items as $name=>$item)
{
if($item->getType()==$type)
$items[$name]=$item;
}
}
elseif(isset($this->_assignments[$userId]))
{
foreach($this->_assignments[$userId] as $assignment)
{
$name=$assignment->getItemName();
if(isset($this->_items[$name]) && ($type===null || $this->_items[$name]->getType()==$type))
$items[$name]=$this->_items[$name];
}
}
return $items;
}
/**
* Creates an authorization item.
* An authorization item represents an action permission (e.g. creating a post).
* It has three types: operation, task and role.
* Authorization items form a hierarchy. Higher level items inheirt permissions representing
* by lower level items.
* @param string $name the item name. This must be a unique identifier.
* @param integer $type the item type (0: operation, 1: task, 2: role).
* @param string $description description of the item
* @param string $bizRule business rule associated with the item. This is a piece of
* PHP code that will be executed when {@link checkAccess} is called for the item.
* @param mixed $data additional data associated with the item.
* @return CAuthItem the authorization item
* @throws CException if an item with the same name already exists
*/
public function createAuthItem($name,$type,$description='',$bizRule=null,$data=null)
{
if(isset($this->_items[$name]))
throw new CException(Yii::t('yii','Unable to add an item whose name is the same as an existing item.'));
return $this->_items[$name]=new CAuthItem($this,$name,$type,$description,$bizRule,$data);
}
/**
* Removes the specified authorization item.
* @param string $name the name of the item to be removed
* @return boolean whether the item exists in the storage and has been removed
*/
public function removeAuthItem($name)
{
if(isset($this->_items[$name]))
{
foreach($this->_children as &$children)
unset($children[$name]);
foreach($this->_assignments as &$assignments)
unset($assignments[$name]);
unset($this->_items[$name]);
return true;
}
else
return false;
}
/**
* Returns the authorization item with the specified name.
* @param string $name the name of the item
* @return CAuthItem the authorization item. Null if the item cannot be found.
*/
public function getAuthItem($name)
{
return isset($this->_items[$name])?$this->_items[$name]:null;
}
/**
* Saves an authorization item to persistent storage.
* @param CAuthItem $item the item to be saved.
* @param string $oldName the old item name. If null, it means the item name is not changed.
*/
public function saveAuthItem($item,$oldName=null)
{
if($oldName!==null && ($newName=$item->getName())!==$oldName) // name changed
{
if(isset($this->_items[$newName]))
throw new CException(Yii::t('yii','Unable to change the item name. The name "{name}" is already used by another item.',array('{name}'=>$newName)));
if(isset($this->_items[$oldName]) && $this->_items[$oldName]===$item)
{
unset($this->_items[$oldName]);
$this->_items[$newName]=$item;
if(isset($this->_children[$oldName]))
{
$this->_children[$newName]=$this->_children[$oldName];
unset($this->_children[$oldName]);
}
foreach($this->_children as &$children)
{
if(isset($children[$oldName]))
{
$children[$newName]=$children[$oldName];
unset($children[$oldName]);
}
}
foreach($this->_assignments as &$assignments)
{
if(isset($assignments[$oldName]))
{
$assignments[$newName]=$assignments[$oldName];
unset($assignments[$oldName]);
}
}
}
}
}
/**
* Saves the changes to an authorization assignment.
* @param CAuthAssignment $assignment the assignment that has been changed.
*/
public function saveAuthAssignment($assignment)
{
}
/**
* Saves authorization data into persistent storage.
* If any change is made to the authorization data, please make
* sure you call this method to save the changed data into persistent storage.
*/
public function save()
{
$items=array();
foreach($this->_items as $name=>$item)
{
$items[$name]=array(
'type'=>$item->getType(),
'description'=>$item->getDescription(),
'bizRule'=>$item->getBizRule(),
'data'=>$item->getData(),
);
if(isset($this->_children[$name]))
{
foreach($this->_children[$name] as $child)
$items[$name]['children'][]=$child->getName();
}
}
foreach($this->_assignments as $userId=>$assignments)
{
foreach($assignments as $name=>$assignment)
{
if(isset($items[$name]))
{
$items[$name]['assignments'][$userId]=array(
'bizRule'=>$assignment->getBizRule(),
'data'=>$assignment->getData(),
);
}
}
}
$this->saveToFile($items,$this->authFile);
}
/**
* Loads authorization data.
*/
public function load()
{
$this->clearAll();
$items=$this->loadFromFile($this->authFile);
foreach($items as $name=>$item)
$this->_items[$name]=new CAuthItem($this,$name,$item['type'],$item['description'],$item['bizRule'],$item['data']);
foreach($items as $name=>$item)
{
if(isset($item['children']))
{
foreach($item['children'] as $childName)
{
if(isset($this->_items[$childName]))
$this->_children[$name][$childName]=$this->_items[$childName];
}
}
if(isset($item['assignments']))
{
foreach($item['assignments'] as $userId=>$assignment)
{
$this->_assignments[$userId][$name]=new CAuthAssignment($this,$name,$userId,$assignment['bizRule'],$assignment['data']);
}
}
}
}
/**
* Removes all authorization data.
*/
public function clearAll()
{
$this->clearAuthAssignments();
$this->_children=array();
$this->_items=array();
}
/**
* Removes all authorization assignments.
*/
public function clearAuthAssignments()
{
$this->_assignments=array();
}
/**
* Checks whether there is a loop in the authorization item hierarchy.
* @param string $itemName parent item name
* @param string $childName the name of the child item that is to be added to the hierarchy
* @return boolean whether a loop exists
*/
protected function detectLoop($itemName,$childName)
{
if($childName===$itemName)
return true;
if(!isset($this->_children[$childName], $this->_items[$itemName]))
return false;
foreach($this->_children[$childName] as $child)
{
if($this->detectLoop($itemName,$child->getName()))
return true;
}
return false;
}
/**
* Loads the authorization data from a PHP script file.
* @param string $file the file path.
* @return array the authorization data
* @see saveToFile
*/
protected function loadFromFile($file)
{
if(is_file($file))
return require($file);
else
return array();
}
/**
* Saves the authorization data to a PHP script file.
* @param array $data the authorization data
* @param string $file the file path.
* @see loadFromFile
*/
protected function saveToFile($data,$file)
{
file_put_contents($file,"<?php\nreturn ".var_export($data,true).";\n");
}
}

View File

@@ -0,0 +1,81 @@
<?php
/**
* CUserIdentity class file
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @link http://www.yiiframework.com/
* @copyright 2008-2013 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
/**
* CUserIdentity is a base class for representing identities that are authenticated based on a username and a password.
*
* Derived classes should implement {@link authenticate} with the actual
* authentication scheme (e.g. checking username and password against a DB table).
*
* By default, CUserIdentity assumes the {@link username} is a unique identifier
* and thus use it as the {@link id ID} of the identity.
*
* @property string $id The unique identifier for the identity.
* @property string $name The display name for the identity.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @package system.web.auth
* @since 1.0
*/
class CUserIdentity extends CBaseUserIdentity
{
/**
* @var string username
*/
public $username;
/**
* @var string password
*/
public $password;
/**
* Constructor.
* @param string $username username
* @param string $password password
*/
public function __construct($username,$password)
{
$this->username=$username;
$this->password=$password;
}
/**
* Authenticates a user based on {@link username} and {@link password}.
* Derived classes should override this method, or an exception will be thrown.
* This method is required by {@link IUserIdentity}.
* @return boolean whether authentication succeeds.
*/
public function authenticate()
{
throw new CException(Yii::t('yii','{class}::authenticate() must be implemented.',array('{class}'=>get_class($this))));
}
/**
* Returns the unique identifier for the identity.
* The default implementation simply returns {@link username}.
* This method is required by {@link IUserIdentity}.
* @return string the unique identifier for the identity.
*/
public function getId()
{
return $this->username;
}
/**
* Returns the display name for the identity.
* The default implementation simply returns {@link username}.
* This method is required by {@link IUserIdentity}.
* @return string the display name for the identity.
*/
public function getName()
{
return $this->username;
}
}

View File

@@ -0,0 +1,821 @@
<?php
/**
* CWebUser class file
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @link http://www.yiiframework.com/
* @copyright 2008-2013 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
/**
* CWebUser represents the persistent state for a Web application user.
*
* CWebUser is used as an application component whose ID is 'user'.
* Therefore, at any place one can access the user state via
* <code>Yii::app()->user</code>.
*
* CWebUser should be used together with an {@link IUserIdentity identity}
* which implements the actual authentication algorithm.
*
* A typical authentication process using CWebUser is as follows:
* <ol>
* <li>The user provides information needed for authentication.</li>
* <li>An {@link IUserIdentity identity instance} is created with the user-provided information.</li>
* <li>Call {@link IUserIdentity::authenticate} to check if the identity is valid.</li>
* <li>If valid, call {@link CWebUser::login} to login the user, and
* Redirect the user browser to {@link returnUrl}.</li>
* <li>If not valid, retrieve the error code or message from the identity
* instance and display it.</li>
* </ol>
*
* The property {@link id} and {@link name} are both identifiers
* for the user. The former is mainly used internally (e.g. primary key), while
* the latter is for display purpose (e.g. username). The {@link id} property
* is a unique identifier for a user that is persistent
* during the whole user session. It can be a username, or something else,
* depending on the implementation of the {@link IUserIdentity identity class}.
*
* Both {@link id} and {@link name} are persistent during the user session.
* Besides, an identity may have additional persistent data which can
* be accessed by calling {@link getState}.
* Note, when {@link allowAutoLogin cookie-based authentication} is enabled,
* all these persistent data will be stored in cookie. Therefore, do not
* store password or other sensitive data in the persistent storage. Instead,
* you should store them directly in session on the server side if needed.
*
* @property boolean $isGuest Whether the current application user is a guest.
* @property mixed $id The unique identifier for the user. If null, it means the user is a guest.
* @property string $name The user name. If the user is not logged in, this will be {@link guestName}.
* @property string $returnUrl The URL that the user should be redirected to after login.
* @property string $stateKeyPrefix A prefix for the name of the session variables storing user session data.
* @property array $flashes Flash messages (key => message).
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @package system.web.auth
* @since 1.0
*/
class CWebUser extends CApplicationComponent implements IWebUser
{
const FLASH_KEY_PREFIX='Yii.CWebUser.flash.';
const FLASH_COUNTERS='Yii.CWebUser.flashcounters';
const STATES_VAR='__states';
const AUTH_TIMEOUT_VAR='__timeout';
const AUTH_ABSOLUTE_TIMEOUT_VAR='__absolute_timeout';
/**
* @var boolean whether to enable cookie-based login. Defaults to false.
*/
public $allowAutoLogin=false;
/**
* @var string the name for a guest user. Defaults to 'Guest'.
* This is used by {@link getName} when the current user is a guest (not authenticated).
*/
public $guestName='Guest';
/**
* @var string|array the URL for login. If using array, the first element should be
* the route to the login action, and the rest name-value pairs are GET parameters
* to construct the login URL (e.g. array('/site/login')). If this property is null,
* a 403 HTTP exception will be raised instead.
* @see CController::createUrl
*/
public $loginUrl=array('/site/login');
/**
* @var array the property values (in name-value pairs) used to initialize the identity cookie.
* Any property of {@link CHttpCookie} may be initialized.
* This property is effective only when {@link allowAutoLogin} is true.
*/
public $identityCookie;
/**
* @var integer timeout in seconds after which user is logged out if inactive.
* If this property is not set, the user will be logged out after the current session expires
* (c.f. {@link CHttpSession::timeout}).
* @since 1.1.7
*/
public $authTimeout;
/**
* @var integer timeout in seconds after which user is logged out regardless of activity.
* @since 1.1.14
*/
public $absoluteAuthTimeout;
/**
* @var boolean whether to automatically renew the identity cookie each time a page is requested.
* Defaults to false. This property is effective only when {@link allowAutoLogin} is true.
* When this is false, the identity cookie will expire after the specified duration since the user
* 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 allowAutoLogin
* @since 1.1.0
*/
public $autoRenewCookie=false;
/**
* @var boolean whether to automatically update the validity of flash messages.
* Defaults to true, meaning flash messages will be valid only in the current and the next requests.
* If this is set false, you will be responsible for ensuring a flash message is deleted after usage.
* (This can be achieved by calling {@link getFlash} with the 3rd parameter being true).
* @since 1.1.7
*/
public $autoUpdateFlash=true;
/**
* @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;
private $_keyPrefix;
private $_access=array();
/**
* PHP magic method.
* This method is overriden so that persistent states can be accessed like properties.
* @param string $name property name
* @return mixed property value
*/
public function __get($name)
{
if($this->hasState($name))
return $this->getState($name);
else
return parent::__get($name);
}
/**
* PHP magic method.
* This method is overriden so that persistent states can be set like properties.
* @param string $name property name
* @param mixed $value property value
*/
public function __set($name,$value)
{
if($this->hasState($name))
$this->setState($name,$value);
else
parent::__set($name,$value);
}
/**
* PHP magic method.
* This method is overriden so that persistent states can also be checked for null value.
* @param string $name property name
* @return boolean
*/
public function __isset($name)
{
if($this->hasState($name))
return $this->getState($name)!==null;
else
return parent::__isset($name);
}
/**
* PHP magic method.
* This method is overriden so that persistent states can also be unset.
* @param string $name property name
* @throws CException if the property is read only.
*/
public function __unset($name)
{
if($this->hasState($name))
$this->setState($name,null);
else
parent::__unset($name);
}
/**
* Initializes the application component.
* This method overrides the parent implementation by starting session,
* performing cookie-based authentication if enabled, and updating the flash variables.
*/
public function init()
{
parent::init();
Yii::app()->getSession()->open();
if($this->getIsGuest() && $this->allowAutoLogin)
$this->restoreFromCookie();
elseif($this->autoRenewCookie && $this->allowAutoLogin)
$this->renewCookie();
if($this->autoUpdateFlash)
$this->updateFlash();
$this->updateAuthStatus();
}
/**
* Logs in a user.
*
* The user identity information will be saved in storage that is
* persistent during the user session. By default, the storage is simply
* the session storage. If the duration parameter is greater than 0,
* a cookie will be sent to prepare for cookie-based login in future.
*
* Note, you have to set {@link allowAutoLogin} 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 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 allowAutoLogin}
* must be set true, otherwise an exception will be thrown.
* @return boolean whether the user is logged in
*/
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->allowAutoLogin)
$this->saveToCookie($duration);
else
throw new CException(Yii::t('yii','{class}.allowAutoLogin must be set true in order to use cookie-based authentication.',
array('{class}'=>get_class($this))));
}
if ($this->absoluteAuthTimeout)
$this->setState(self::AUTH_ABSOLUTE_TIMEOUT_VAR, time()+$this->absoluteAuthTimeout);
$this->afterLogin(false);
}
return !$this->getIsGuest();
}
/**
* 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.
* @param boolean $destroySession whether to destroy the whole session. Defaults to true. If false,
* then {@link clearStates} will be called, which removes only the data stored via {@link setState}.
*/
public function logout($destroySession=true)
{
if($this->beforeLogout())
{
if($this->allowAutoLogin)
{
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);
}
}
if($destroySession)
Yii::app()->getSession()->destroy();
else
$this->clearStates();
$this->_access=array();
$this->afterLogout();
}
}
/**
* Returns a value indicating whether the user is a guest (not authenticated).
* @return boolean whether the current application user is a guest.
*/
public function getIsGuest()
{
return $this->getState('__id')===null;
}
/**
* Returns a value that uniquely represents the user.
* @return mixed the unique identifier for the user. If null, it means the user is a guest.
*/
public function getId()
{
return $this->getState('__id');
}
/**
* @param mixed $value the unique identifier for the user. If null, it means the user is a guest.
*/
public function setId($value)
{
$this->setState('__id',$value);
}
/**
* Returns the unique identifier for the user (e.g. username).
* This is the unique identifier that is mainly used for display purpose.
* @return string the user name. If the user is not logged in, this will be {@link guestName}.
*/
public function getName()
{
if(($name=$this->getState('__name'))!==null)
return $name;
else
return $this->guestName;
}
/**
* Sets the unique identifier for the user (e.g. username).
* @param string $value the user name.
* @see getName
*/
public function setName($value)
{
$this->setState('__name',$value);
}
/**
* Returns the URL that the user should be redirected to after successful login.
* This property is usually used by the login action. If the login is successful,
* the action should read this property and use it to redirect the user browser.
* @param string $defaultUrl the default return URL in case it was not set previously. If this is null,
* the application entry URL will be considered as the default return URL.
* @return string the URL that the user should be redirected to after login.
* @see loginRequired
*/
public function getReturnUrl($defaultUrl=null)
{
if($defaultUrl===null)
{
$defaultReturnUrl=Yii::app()->getUrlManager()->showScriptName ? Yii::app()->getRequest()->getScriptUrl() : Yii::app()->getRequest()->getBaseUrl().'/';
}
else
{
$defaultReturnUrl=CHtml::normalizeUrl($defaultUrl);
}
return $this->getState('__returnUrl',$defaultReturnUrl);
}
/**
* @param string $value the URL that the user should be redirected to after login.
*/
public function setReturnUrl($value)
{
$this->setState('__returnUrl',$value);
}
/**
* Redirects the user browser to the login page.
* Before the redirection, the current URL (if it's not an AJAX url) will be
* kept in {@link returnUrl} so that the user browser may be redirected back
* to the current page after successful login. Make sure you set {@link loginUrl}
* so that the user browser can be redirected to the specified login URL after
* calling this method.
* After calling this method, the current request processing will be terminated.
*/
public function loginRequired()
{
$app=Yii::app();
$request=$app->getRequest();
if(!$request->getIsAjaxRequest())
{
$this->setReturnUrl($request->getUrl());
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));
}
$request->redirect($url);
}
}
elseif(isset($this->loginRequiredAjaxResponse))
{
echo $this->loginRequiredAjaxResponse;
Yii::app()->end();
}
throw new CHttpException(403,Yii::t('yii','Login Required'));
}
/**
* This method is called before logging in a user.
* You may override this method to provide additional security check.
* For example, when the login is cookie-based, you may want to verify
* that the user ID together with a random token in the states can be found
* in the database. This will prevent hackers from faking arbitrary
* identity cookies even if they crack down the server private key.
* @param mixed $id the user ID. This is the same as returned by {@link getId()}.
* @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)
{
return true;
}
/**
* This method is called after the user is successfully logged in.
* 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)
{
}
/**
* This method is invoked when calling {@link logout} to log out a user.
* If this method return false, the logout action will be cancelled.
* 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()
{
return true;
}
/**
* 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()
{
}
/**
* Populates the current user object with the information obtained from cookie.
* This method is used when automatic login ({@link allowAutoLogin}) 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)
{
$this->saveToCookie($duration);
}
$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()
{
$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]))
{
$this->saveToCookie($data[2]);
}
}
}
/**
* Saves necessary user data into a cookie.
* This method is used when automatic login ({@link allowAutoLogin}) 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 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
*/
protected function saveToCookie($duration)
{
$app=Yii::app();
$cookie=$this->createIdentityCookie($this->getStateKeyPrefix());
$cookie->expire=time()+$duration;
$data=array(
$this->getId(),
$this->getName(),
$duration,
$this->saveIdentityStates(),
);
$cookie->value=$app->getSecurityManager()->hashData(serialize($data));
$app->getRequest()->getCookies()->add($cookie->name,$cookie);
}
/**
* Creates a cookie to store identity information.
* @param string $name the cookie name
* @return CHttpCookie the cookie used to store identity information
*/
protected function createIdentityCookie($name)
{
$cookie=new CHttpCookie($name,'');
if(is_array($this->identityCookie))
{
foreach($this->identityCookie as $name=>$value)
$cookie->$name=$value;
}
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());
}
/**
* @param string $value a prefix for the name of the session variables storing user session data.
*/
public function setStateKeyPrefix($value)
{
$this->_keyPrefix=$value;
}
/**
* 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
*/
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]);
else
$_SESSION[$key]=$value;
}
/**
* 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.
*/
public function hasState($key)
{
$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]);
}
}
/**
* Returns all flash messages.
* This method is similar to {@link getFlash} except that it returns all
* currently available flash messages.
* @param boolean $delete whether to delete the flash messages after calling this method.
* @return array flash messages (key => message).
* @since 1.1.3
*/
public function getFlashes($delete=true)
{
$flashes=array();
$prefix=$this->getStateKeyPrefix().self::FLASH_KEY_PREFIX;
$keys=array_keys($_SESSION);
$n=strlen($prefix);
foreach($keys as $key)
{
if(!strncmp($key,$prefix,$n))
{
$flashes[substr($key,$n)]=$_SESSION[$key];
if($delete)
unset($_SESSION[$key]);
}
}
if($delete)
$this->setState(self::FLASH_COUNTERS,array());
return $flashes;
}
/**
* Returns a flash message.
* A flash message is available only in the current and the next requests.
* @param string $key key identifying the flash message
* @param mixed $defaultValue value to be returned if the flash message is not available.
* @param boolean $delete whether to delete this flash message after accessing it.
* Defaults to true.
* @return mixed the message message
*/
public function getFlash($key,$defaultValue=null,$delete=true)
{
$value=$this->getState(self::FLASH_KEY_PREFIX.$key,$defaultValue);
if($delete)
$this->setFlash($key,null);
return $value;
}
/**
* Stores a flash message.
* A flash message is available only in the current and the next requests.
* @param string $key key identifying the flash message
* @param mixed $value flash message
* @param mixed $defaultValue if this value is the same as the flash message, the flash message
* will be removed. (Therefore, you can use setFlash('key',null) to remove a flash message.)
*/
public function setFlash($key,$value,$defaultValue=null)
{
$this->setState(self::FLASH_KEY_PREFIX.$key,$value,$defaultValue);
$counters=$this->getState(self::FLASH_COUNTERS,array());
if($value===$defaultValue)
unset($counters[$key]);
else
$counters[$key]=0;
$this->setState(self::FLASH_COUNTERS,$counters,array());
}
/**
* @param string $key key identifying the flash message
* @return boolean whether the specified flash message exists
*/
public function hasFlash($key)
{
return $this->getFlash($key, null, false)!==null;
}
/**
* 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
*/
protected function changeIdentity($id,$name,$states)
{
Yii::app()->getSession()->regenerateID(true);
$this->setId($id);
$this->setName($name);
$this->loadIdentityStates($states);
}
/**
* Retrieves identity states from persistent storage and saves them as an array.
* @return array the identity states
*/
protected function saveIdentityStates()
{
$states=array();
foreach($this->getState(self::STATES_VAR,array()) as $name=>$dummy)
$states[$name]=$this->getState($name);
return $states;
}
/**
* Loads identity states from an array and saves them to persistent storage.
* @param array $states the identity states
*/
protected function loadIdentityStates($states)
{
$names=array();
if(is_array($states))
{
foreach($states as $name=>$value)
{
$this->setState($name,$value);
$names[$name]=true;
}
}
$this->setState(self::STATES_VAR,$names);
}
/**
* Updates the internal counters for flash messages.
* This method is internally used by {@link CWebApplication}
* to maintain the availability of flash messages.
*/
protected function updateFlash()
{
$counters=$this->getState(self::FLASH_COUNTERS);
if(!is_array($counters))
return;
foreach($counters as $key=>$count)
{
if($count)
{
unset($counters[$key]);
$this->setState(self::FLASH_KEY_PREFIX.$key,null);
}
else
$counters[$key]++;
}
$this->setState(self::FLASH_COUNTERS,$counters,array());
}
/**
* Updates the authentication status according to {@link authTimeout}.
* If the user has been inactive for {@link authTimeout} seconds, or {link absoluteAuthTimeout} has passed,
* he will be automatically logged out.
* @since 1.1.7
*/
protected function updateAuthStatus()
{
if(($this->authTimeout!==null || $this->absoluteAuthTimeout!==null) && !$this->getIsGuest())
{
$expires=$this->getState(self::AUTH_TIMEOUT_VAR);
$expiresAbsolute=$this->getState(self::AUTH_ABSOLUTE_TIMEOUT_VAR);
if ($expires!==null && $expires < time() || $expiresAbsolute!==null && $expiresAbsolute < time())
$this->logout(false);
else
$this->setState(self::AUTH_TIMEOUT_VAR,time()+$this->authTimeout);
}
}
/**
* Performs access check for this user.
* @param string $operation the name of the operation that need access check.
* @param array $params name-value pairs that would be passed to business rules associated
* with the tasks and roles assigned to the user.
* Since version 1.1.11 a param with name 'userId' is added to this array, which holds the value of
* {@link getId()} when {@link CDbAuthManager} or {@link CPhpAuthManager} is used.
* @param boolean $allowCaching whether to allow caching the result of access check.
* When this parameter
* is true (default), if the access check of an operation was performed before,
* its result will be directly returned when calling this method to check the same operation.
* If this parameter is false, this method will always call {@link CAuthManager::checkAccess}
* to obtain the up-to-date access result. Note that this caching is effective
* only within the same request and only works when <code>$params=array()</code>.
* @return boolean whether the operations can be performed by this user.
*/
public function checkAccess($operation,$params=array(),$allowCaching=true)
{
if($allowCaching && $params===array() && isset($this->_access[$operation]))
return $this->_access[$operation];
$access=Yii::app()->getAuthManager()->checkAccess($operation,$this->getId(),$params);
if($allowCaching && $params===array())
$this->_access[$operation]=$access;
return $access;
}
}

View File

@@ -0,0 +1,42 @@
/**
* Database schema required by CDbAuthManager.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @link http://www.yiiframework.com/
* @copyright 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
* @since 1.0
*/
drop table if exists [AuthAssignment];
drop table if exists [AuthItemChild];
drop table if exists [AuthItem];
create table [AuthItem]
(
[name] varchar(64) not null,
[type] integer not null,
[description] text,
[bizrule] text,
[data] text,
primary key ([name])
);
create table [AuthItemChild]
(
[parent] varchar(64) not null,
[child] varchar(64) not null,
primary key ([parent],[child]),
foreign key ([parent]) references [AuthItem] ([name]) on delete cascade on update cascade,
foreign key ([child]) references [AuthItem] ([name]) on delete cascade on update cascade
);
create table [AuthAssignment]
(
[itemname] varchar(64) not null,
[userid] varchar(64) not null,
[bizrule] text,
[data] text,
primary key ([itemname],[userid]),
foreign key ([itemname]) references [AuthItem] ([name]) on delete cascade on update cascade
);

View File

@@ -0,0 +1,42 @@
/**
* Database schema required by CDbAuthManager.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @link http://www.yiiframework.com/
* @copyright 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
* @since 1.0
*/
drop table if exists `AuthAssignment`;
drop table if exists `AuthItemChild`;
drop table if exists `AuthItem`;
create table `AuthItem`
(
`name` varchar(64) not null,
`type` integer not null,
`description` text,
`bizrule` text,
`data` text,
primary key (`name`)
) engine InnoDB;
create table `AuthItemChild`
(
`parent` varchar(64) not null,
`child` varchar(64) not null,
primary key (`parent`,`child`),
foreign key (`parent`) references `AuthItem` (`name`) on delete cascade on update cascade,
foreign key (`child`) references `AuthItem` (`name`) on delete cascade on update cascade
) engine InnoDB;
create table `AuthAssignment`
(
`itemname` varchar(64) not null,
`userid` varchar(64) not null,
`bizrule` text,
`data` text,
primary key (`itemname`,`userid`),
foreign key (`itemname`) references `AuthItem` (`name`) on delete cascade on update cascade
) engine InnoDB;

View File

@@ -0,0 +1,42 @@
/**
* Database schema required by CDbAuthManager.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @link http://www.yiiframework.com/
* @copyright 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
* @since 1.0
*/
drop table if exists "AuthAssignment";
drop table if exists "AuthItemChild";
drop table if exists "AuthItem";
create table "AuthItem"
(
"name" varchar(64) not null,
"type" integer not null,
"description" text,
"bizrule" text,
"data" text,
primary key ("name")
);
create table "AuthItemChild"
(
"parent" varchar(64) not null,
"child" varchar(64) not null,
primary key ("parent","child"),
foreign key ("parent") references "AuthItem" ("name") on delete cascade on update cascade,
foreign key ("child") references "AuthItem" ("name") on delete cascade on update cascade
);
create table "AuthAssignment"
(
"itemname" varchar(64) not null,
"userid" varchar(64) not null,
"bizrule" text,
"data" text,
primary key ("itemname","userid"),
foreign key ("itemname") references "AuthItem" ("name") on delete cascade on update cascade
);

View File

@@ -0,0 +1,42 @@
/**
* Database schema required by CDbAuthManager.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @link http://www.yiiframework.com/
* @copyright 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
* @since 1.0
*/
drop table if exists "AuthAssignment";
drop table if exists "AuthItemChild";
drop table if exists "AuthItem";
create table "AuthItem"
(
"name" varchar(64) not null,
"type" integer not null,
"description" text,
"bizrule" text,
"data" text,
primary key ("name")
);
create table "AuthItemChild"
(
"parent" varchar(64) not null,
"child" varchar(64) not null,
primary key ("parent","child"),
foreign key ("parent") references "AuthItem" ("name") on delete cascade on update cascade,
foreign key ("child") references "AuthItem" ("name") on delete cascade on update cascade
);
create table "AuthAssignment"
(
"itemname" varchar(64) not null,
"userid" varchar(64) not null,
"bizrule" text,
"data" text,
primary key ("itemname","userid"),
foreign key ("itemname") references "AuthItem" ("name") on delete cascade on update cascade
);

View File

@@ -0,0 +1,42 @@
/**
* Database schema required by CDbAuthManager.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @link http://www.yiiframework.com/
* @copyright 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
* @since 1.0
*/
drop table if exists 'AuthAssignment';
drop table if exists 'AuthItemChild';
drop table if exists 'AuthItem';
create table 'AuthItem'
(
"name" varchar(64) not null,
"type" integer not null,
"description" text,
"bizrule" text,
"data" text,
primary key ("name")
);
create table 'AuthItemChild'
(
"parent" varchar(64) not null,
"child" varchar(64) not null,
primary key ("parent","child"),
foreign key ("parent") references 'AuthItem' ("name") on delete cascade on update cascade,
foreign key ("child") references 'AuthItem' ("name") on delete cascade on update cascade
);
create table 'AuthAssignment'
(
"itemname" varchar(64) not null,
"userid" varchar(64) not null,
"bizrule" text,
"data" text,
primary key ("itemname","userid"),
foreign key ("itemname") references 'AuthItem' ("name") on delete cascade on update cascade
);