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