Qiang Xue
12 years ago
5 changed files with 454 additions and 1 deletions
@ -0,0 +1,104 @@ |
|||||||
|
<?php |
||||||
|
/** |
||||||
|
* @link http://www.yiiframework.com/ |
||||||
|
* @copyright Copyright (c) 2008 Yii Software LLC |
||||||
|
* @license http://www.yiiframework.com/license/ |
||||||
|
*/ |
||||||
|
|
||||||
|
namespace yii\web; |
||||||
|
|
||||||
|
use Yii; |
||||||
|
use yii\base\Action; |
||||||
|
use yii\base\ActionFilter; |
||||||
|
use yii\base\HttpException; |
||||||
|
|
||||||
|
/** |
||||||
|
* |
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com> |
||||||
|
* @since 2.0 |
||||||
|
*/ |
||||||
|
class AccessControl extends ActionFilter |
||||||
|
{ |
||||||
|
/** |
||||||
|
* @var callback a callback that will be called if the access should be denied |
||||||
|
* to the current user. If not set, [[denyAccess()]] will be called. |
||||||
|
* |
||||||
|
* The signature of the callback should be as follows: |
||||||
|
* |
||||||
|
* ~~~ |
||||||
|
* function ($rule, $action) |
||||||
|
* ~~~ |
||||||
|
* |
||||||
|
* where `$rule` is this rule, and `$action` is the current [[Action|action]] object. |
||||||
|
*/ |
||||||
|
public $denyCallback; |
||||||
|
/** |
||||||
|
* @var string the default class of the access rules. This is used when |
||||||
|
* a rule is configured without specifying a class in [[rules]]. |
||||||
|
*/ |
||||||
|
public $defaultRuleClass = 'yii\web\AccessRule'; |
||||||
|
/** |
||||||
|
* @var array a list of access rule objects or configurations for creating the rule objects. |
||||||
|
*/ |
||||||
|
public $rules = array(); |
||||||
|
|
||||||
|
/** |
||||||
|
* Initializes the [[rules]] array by instantiating rule objects from configurations. |
||||||
|
*/ |
||||||
|
public function init() |
||||||
|
{ |
||||||
|
parent::init(); |
||||||
|
foreach ($this->rules as $i => $rule) { |
||||||
|
if (is_array($rule)) { |
||||||
|
if (!isset($rule['class'])) { |
||||||
|
$rule['class'] = $this->defaultRuleClass; |
||||||
|
} |
||||||
|
$this->rules[$i] = Yii::createObject($rule); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* This method is invoked right before an action is to be executed (after all possible filters.) |
||||||
|
* You may override this method to do last-minute preparation for the action. |
||||||
|
* @param Action $action the action to be executed. |
||||||
|
* @return boolean whether the action should continue to be executed. |
||||||
|
*/ |
||||||
|
public function beforeAction($action) |
||||||
|
{ |
||||||
|
$user = Yii::$app->getUser(); |
||||||
|
$request = Yii::$app->getRequest(); |
||||||
|
/** @var $rule AccessRule */ |
||||||
|
foreach ($this->rules as $rule) { |
||||||
|
if ($allow = $rule->allows($action, $user, $request)) { |
||||||
|
break; |
||||||
|
} elseif ($allow === false) { |
||||||
|
if (isset($rule->denyCallback)) { |
||||||
|
call_user_func($rule->denyCallback, $rule); |
||||||
|
} elseif (isset($this->denyCallback)) { |
||||||
|
call_user_func($this->denyCallback, $rule); |
||||||
|
} else { |
||||||
|
$this->denyAccess($user); |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Denies the access of the user. |
||||||
|
* The default implementation will redirect the user to the login page if he is a guest; |
||||||
|
* if the user is already logged, a 403 HTTP exception will be thrown. |
||||||
|
* @param User $user the current user |
||||||
|
* @throws HttpException if the user is already logged in. |
||||||
|
*/ |
||||||
|
protected function denyAccess($user) |
||||||
|
{ |
||||||
|
if ($user->getIsGuest()) { |
||||||
|
$user->loginRequired(); |
||||||
|
} else { |
||||||
|
throw new HttpException(403, Yii::t('yii|You are not allowed to perform this action.')); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,212 @@ |
|||||||
|
<?php |
||||||
|
/** |
||||||
|
* @link http://www.yiiframework.com/ |
||||||
|
* @copyright Copyright (c) 2008 Yii Software LLC |
||||||
|
* @license http://www.yiiframework.com/license/ |
||||||
|
*/ |
||||||
|
|
||||||
|
namespace yii\web; |
||||||
|
|
||||||
|
use yii\base\Component; |
||||||
|
use yii\base\Action; |
||||||
|
use yii\base\Controller; |
||||||
|
use yii\web\User; |
||||||
|
use yii\web\Request; |
||||||
|
|
||||||
|
/** |
||||||
|
* |
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com> |
||||||
|
* @since 2.0 |
||||||
|
*/ |
||||||
|
class AccessRule extends Component |
||||||
|
{ |
||||||
|
/** |
||||||
|
* @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-sensitive. |
||||||
|
* If not set or empty, it means this rule applies to all actions. |
||||||
|
*/ |
||||||
|
public $actions; |
||||||
|
/** |
||||||
|
* @var array list of controller IDs that this rule applies to. The comparison is case-sensitive. |
||||||
|
* If not set or empty, it means this rule applies to all controllers. |
||||||
|
*/ |
||||||
|
public $controllers; |
||||||
|
/** |
||||||
|
* @var array list of user names that this rule applies to. The comparison is case-insensitive. |
||||||
|
* If not set or empty, it means this rule applies to all users. Two special tokens are recognized: |
||||||
|
* |
||||||
|
* - `?`: matches a guest user (not authenticated yet) |
||||||
|
* - `@`: matches an authenticated user |
||||||
|
* |
||||||
|
* @see \yii\web\Application::user |
||||||
|
*/ |
||||||
|
public $users; |
||||||
|
/** |
||||||
|
* @var array list of roles that 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. |
||||||
|
* If not set or empty, it means this rule applies to all roles. |
||||||
|
*/ |
||||||
|
public $roles; |
||||||
|
/** |
||||||
|
* @var array list of user IP addresses that this rule applies to. An IP address |
||||||
|
* can contain the wildcard `*` at the end so that it matches IP addresses with the same prefix. |
||||||
|
* For example, '192.168.*' matches all IP addresses in the segment '192.168.'. |
||||||
|
* If not set or empty, it means this rule applies to all IP addresses. |
||||||
|
* @see Request::userIP |
||||||
|
*/ |
||||||
|
public $ips; |
||||||
|
/** |
||||||
|
* @var array list of request methods (e.g. `GET`, `POST`) that this rule applies to. |
||||||
|
* The request methods must be specified in uppercase. |
||||||
|
* If not set or empty, it means this rule applies to all request methods. |
||||||
|
* @see Request::requestMethod |
||||||
|
*/ |
||||||
|
public $verbs; |
||||||
|
/** |
||||||
|
* @var callback a callback that will be called to determine if the rule should be applied. |
||||||
|
* The signature of the callback should be as follows: |
||||||
|
* |
||||||
|
* ~~~ |
||||||
|
* function ($rule, $action) |
||||||
|
* ~~~ |
||||||
|
* |
||||||
|
* where `$rule` is this rule, and `$action` is the current [[Action|action]] object. |
||||||
|
* The callback should return a boolean value indicating whether this rule should be applied. |
||||||
|
*/ |
||||||
|
public $matchCallback; |
||||||
|
/** |
||||||
|
* @var callback a callback that will be called if this rule determines the access to |
||||||
|
* the current action should be denied. If not set, the behavior will be determined by |
||||||
|
* [[AccessControl]]. |
||||||
|
* |
||||||
|
* The signature of the callback should be as follows: |
||||||
|
* |
||||||
|
* ~~~ |
||||||
|
* function ($rule, $action) |
||||||
|
* ~~~ |
||||||
|
* |
||||||
|
* where `$rule` is this rule, and `$action` is the current [[Action|action]] object. |
||||||
|
*/ |
||||||
|
public $denyCallback; |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Checks whether the Web user is allowed to perform the specified action. |
||||||
|
* @param Action $action the action to be performed |
||||||
|
* @param User $user the user object |
||||||
|
* @param Request $request |
||||||
|
* @return boolean|null true if the user is allowed, false if the user is denied, null if the rule does not apply to the user |
||||||
|
*/ |
||||||
|
public function allows($action, $user, $request) |
||||||
|
{ |
||||||
|
if ($this->matchAction($action) |
||||||
|
&& $this->matchUser($user) |
||||||
|
&& $this->matchRole($user) |
||||||
|
&& $this->matchIP($request->getUserIP()) |
||||||
|
&& $this->matchVerb($request->getRequestMethod()) |
||||||
|
&& $this->matchController($action->controller) |
||||||
|
&& $this->matchCustom($action) |
||||||
|
) { |
||||||
|
return $this->allow ? true : false; |
||||||
|
} else { |
||||||
|
return null; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @param Action $action the action |
||||||
|
* @return boolean whether the rule applies to the action |
||||||
|
*/ |
||||||
|
protected function matchAction($action) |
||||||
|
{ |
||||||
|
return empty($this->actions) || in_array($action->id, $this->actions, true); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @param Controller $controller the controller |
||||||
|
* @return boolean whether the rule applies to the controller |
||||||
|
*/ |
||||||
|
protected function matchController($controller) |
||||||
|
{ |
||||||
|
return empty($this->controllers) || in_array($controller->id, $this->controllers, true); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @param User $user the user |
||||||
|
* @return boolean whether the rule applies to the user |
||||||
|
*/ |
||||||
|
protected function matchUser($user) |
||||||
|
{ |
||||||
|
if (empty($this->users)) { |
||||||
|
return true; |
||||||
|
} |
||||||
|
foreach ($this->users as $u) { |
||||||
|
if ($u === '?' && $user->getIsGuest()) { |
||||||
|
return true; |
||||||
|
} elseif ($u === '@' && !$user->getIsGuest()) { |
||||||
|
return true; |
||||||
|
} elseif (!strcasecmp($u, $user->getName())) { |
||||||
|
return true; |
||||||
|
} |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @param User $user the user object |
||||||
|
* @return boolean whether the rule applies to the role |
||||||
|
*/ |
||||||
|
protected function matchRole($user) |
||||||
|
{ |
||||||
|
if (empty($this->roles)) { |
||||||
|
return true; |
||||||
|
} |
||||||
|
foreach ($this->roles as $role) { |
||||||
|
if ($user->checkAccess($role)) { |
||||||
|
return true; |
||||||
|
} |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @param string $ip the IP address |
||||||
|
* @return boolean whether the rule applies to the IP address |
||||||
|
*/ |
||||||
|
protected function matchIP($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 matchVerb($verb) |
||||||
|
{ |
||||||
|
return empty($this->verbs) || in_array($verb, $this->verbs, true); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @param Action $action the action to be performed |
||||||
|
* @return boolean whether the rule should be applied |
||||||
|
*/ |
||||||
|
protected function matchCustom($action) |
||||||
|
{ |
||||||
|
return empty($this->matchCallback) || call_user_func($this->matchCallback, $this, $action); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,125 @@ |
|||||||
|
<?php |
||||||
|
/** |
||||||
|
* @link http://www.yiiframework.com/ |
||||||
|
* @copyright Copyright (c) 2008 Yii Software LLC |
||||||
|
* @license http://www.yiiframework.com/license/ |
||||||
|
*/ |
||||||
|
|
||||||
|
namespace yii\web; |
||||||
|
|
||||||
|
use Yii; |
||||||
|
use yii\base\ActionFilter; |
||||||
|
use yii\base\Action; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author Da:Sourcerer <webmaster@dasourcerer.net> |
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com> |
||||||
|
* @since 2.0 |
||||||
|
*/ |
||||||
|
class HttpCache extends ActionFilter |
||||||
|
{ |
||||||
|
/** |
||||||
|
* @var callback a PHP callback that returns the UNIX timestamp of the last modification time. |
||||||
|
* The callback's signature should be: |
||||||
|
* |
||||||
|
* ~~~ |
||||||
|
* function ($action, $params) |
||||||
|
* ~~~ |
||||||
|
* |
||||||
|
* where `$action` is the [[Action]] object that this filter is currently handling; |
||||||
|
* `$params` takes the value of [[params]]. |
||||||
|
*/ |
||||||
|
public $lastModified; |
||||||
|
/** |
||||||
|
* @var callback a PHP callback that generates the Etag seed string. |
||||||
|
* The callback's signature should be: |
||||||
|
* |
||||||
|
* ~~~ |
||||||
|
* function ($action, $params) |
||||||
|
* ~~~ |
||||||
|
* |
||||||
|
* where `$action` is the [[Action]] object that this filter is currently handling; |
||||||
|
* `$params` takes the value of [[params]]. The callback should return a string. |
||||||
|
*/ |
||||||
|
public $etagSeed; |
||||||
|
/** |
||||||
|
* @var mixed additional parameters that should be passed to the [[lastModified]] and [[etagSeed]] callbacks. |
||||||
|
*/ |
||||||
|
public $params; |
||||||
|
/** |
||||||
|
* Http cache control headers. Set this to an empty string in order to keep this |
||||||
|
* header from being sent entirely. |
||||||
|
* @var string |
||||||
|
*/ |
||||||
|
public $cacheControl = 'max-age=3600, public'; |
||||||
|
|
||||||
|
/** |
||||||
|
* This method is invoked right before an action is to be executed (after all possible filters.) |
||||||
|
* You may override this method to do last-minute preparation for the action. |
||||||
|
* @param Action $action the action to be executed. |
||||||
|
* @return boolean whether the action should continue to be executed. |
||||||
|
*/ |
||||||
|
public function beforeAction($action) |
||||||
|
{ |
||||||
|
$requestMethod = Yii::$app->request->getRequestMethod(); |
||||||
|
if ($requestMethod !== 'GET' && $requestMethod !== 'HEAD') { |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
$lastModified = $etag = null; |
||||||
|
if ($this->lastModified !== null) { |
||||||
|
$lastModified = call_user_func($this->lastModified, $action, $this->params); |
||||||
|
} |
||||||
|
if ($this->etagSeed !== null) { |
||||||
|
$seed = call_user_func($this->etagSeed, $action, $this->params); |
||||||
|
$etag = $this->generateEtag($seed); |
||||||
|
} |
||||||
|
|
||||||
|
if ($etag !== null) { |
||||||
|
header("ETag: $etag"); |
||||||
|
} |
||||||
|
$this->sendCacheControlHeader(); |
||||||
|
|
||||||
|
if ($this->hasChanged($lastModified, $etag)) { |
||||||
|
if ($lastModified !== null) { |
||||||
|
header('Last-Modified: ' . gmdate('D, d M Y H:i:s', $lastModified) . ' GMT'); |
||||||
|
} |
||||||
|
return true; |
||||||
|
} else { |
||||||
|
header('HTTP/1.1 304 Not Modified'); |
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
protected function hasChanged($lastModified, $etag) |
||||||
|
{ |
||||||
|
$changed = !isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) || @strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) < $lastModified; |
||||||
|
if (!$changed && $etag !== null) { |
||||||
|
$changed = !isset($_SERVER['HTTP_IF_NONE_MATCH']) || $_SERVER['HTTP_IF_NONE_MATCH'] !== $etag; |
||||||
|
} |
||||||
|
return $changed; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Sends the cache control header to the client |
||||||
|
* @see cacheControl |
||||||
|
*/ |
||||||
|
protected function sendCacheControlHeader() |
||||||
|
{ |
||||||
|
if (Yii::$app->session->isActive) { |
||||||
|
session_cache_limiter('public'); |
||||||
|
header('Pragma:', true); |
||||||
|
} |
||||||
|
header('Cache-Control: ' . $this->cacheControl, true); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Generates an Etag from a given seed string. |
||||||
|
* @param string $seed Seed for the ETag |
||||||
|
* @return string the generated Etag |
||||||
|
*/ |
||||||
|
protected function generateEtag($seed) |
||||||
|
{ |
||||||
|
return '"' . base64_encode(sha1($seed, true)) . '"'; |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue