%PDF- %PDF-
| Direktori : /home/vacivi36/intranet.vacivitta.com.br/protected/humhub/components/access/ |
| Current File : /home/vacivi36/intranet.vacivitta.com.br/protected/humhub/components/access/ControllerAccess.php |
<?php
/**
* @link https://www.humhub.org/
* @copyright Copyright (c) 2017 HumHub GmbH & Co. KG
* @license https://www.humhub.com/licences
*
*/
namespace humhub\components\access;
use humhub\modules\user\helpers\AuthHelper;
use Yii;
use humhub\modules\user\models\User;
use yii\base\InvalidArgumentException;
use yii\base\BaseObject;
use yii\web\Controller;
/**
* ControllerAccess contains the actual logic to verify whether or not a user can access a controller action by means of
* a given set of access rules.
*
* By default the AccessCheck will use the current logged in user as permission subject.
*
* The actual permission rule verification is handled by the [[run()]] function.
*
* Subclasses can extend the set of available validators by calling [[registerValidator()]]
* and providing a validator setting array as:
*
* ```php
* public function init()
* {
* parent::init();
* $this->registerValidator([
* self::RULE_MY_RULE => 'validateMyRule',
* 'reason' => Yii::t('error', 'My validation rule could not be verified.'),
* 'code' => 401
* ]);
* }
* ```
*
* The previous example registered a new validator responsible for validating rules with the name `validateMyRule`
* and validation handler function `validateMyRule` which defines an handler method within the subclass.
*
* Custom Validators can also be added by means of a Validator class as in the following example:
*
* ```php
* $this->registerValidator(MyValidator::class);
* ```
*
* where `MyValidator` is a subclass of [[\humhub\components\access\AccessValidator]]
*
* A single rule is provided as a array. If not specified otherwise, a rule supports the following base format:
*
* ```php
* ['ruleName', 'actions' => ['action1', 'action2']]
* ```
* or
*
* ```php
* ['ruleName' => ['action1', action2]]
* ```
*
* > Note: the second format is not supported by all rules e.g. permission rule
*
* If no action array is provided, the rule is considered to be controller global and will be verified for all actions.
*
* If a rule for a given name could not be found, the ControllerAccess tries to determine a custom rule validator
* set by the controller itself:
*
* ```php
* ['validateMyCustomRule', 'someParameter' => $value]
* ```
*
* will search for controller validator function `validateMyCustomRule`:
*
* ```php
* public function validateTestRule($rule, $access)
* {
* if($rule['someParameter'] == 'valid') {
* $access->code = 401;
* $access->reason = 'Not authorized!';
* return false;
* }
*
* return true;
* }
* ```
*
* By defining the [[fixedRules]] array property a ControllerAccess can define rules which are always applied,
* this property (or [[getFixedRules()]] function may be overwritten by subclasses.
*
* The following rules are available by default:
*
* - **admin**: The user has to be system admin to access a action
* - **permission** Group Permission check
* - **login**: The user has to be logged in to access a action
* - **strict**: Will check for guest users against the guest users allowed setting
* - **post**: Will only accept post requests for the given actions
* - **json**: Will handle json result requests by setting `Yii::$app->response->format = 'json'`
* - **ajax**: Allows only AJAX requests. See: `Yii::$app->request->isAjax`
* - **disabledUser**: Checks if the given user is a disabled user **(fixed)**
* - **unapprovedUser**: Checks if the given user is a unapproved user **(fixed)**
*
* @see AccessValidator
* @since 1.2.2
*/
class ControllerAccess extends BaseObject
{
/**
* Allows the action rule setting only by extra option ['myRule', 'actions' => ['action1', 'action2']]
*/
const ACTION_SETTING_TYPE_OPTION_ONLY = 0;
/**
* Allows the action rule setting by extra option ['myRule', 'actions' => ['action1', 'action2']]
* or immediate ['myRule' => ['action1', 'action2']]
*/
const ACTION_SETTING_TYPE_BOTH = 1;
/**
* Only admins have access to the given set of actions e.g.: ['admin' => ['action1']]
*/
const RULE_ADMIN_ONLY = 'admin';
/**
* Validate against a given set of permissions e.g.:
* ['permission' => [MyPermission::class], 'actions' => ['action1']]
*/
const RULE_PERMISSION = 'permission';
/**
* Only logged in user have access e.g.: ['login' => ['action1', 'action2']]
*/
const RULE_LOGGED_IN_ONLY = 'login';
/**
* Check guest mode e.g.: ['strict'] (mainly used as global)
*/
const RULE_STRICT = 'strict';
/**
* Check guest if user is disabled
*/
const RULE_DISABLED_USER = 'disabledUser';
/**
* Check guest if user is unnapproved
*/
const RULE_UNAPPROVED_USER = 'unapprovedUser';
/**
* Check guest if user must change password
* @since 1.8
*/
const RULE_MUST_CHANGE_PASSWORD = 'mustChangePassword';
/**
* Maintenance mode is active
*/
const RULE_MAINTENANCE_MODE = 'maintenance';
/**
* Check guest if request method is post
*/
const RULE_POST = 'post';
/**
* Make sure response type is json
*/
const RULE_JSON = 'json';
/**
* Only AJAX request is allowed for the actions
*/
const RULE_AJAX_ONLY = 'ajax';
/**
* @var array fixed rules will always be added to the current rule set
*/
protected $fixedRules = [
[self::RULE_DISABLED_USER],
[self::RULE_UNAPPROVED_USER],
[self::RULE_MUST_CHANGE_PASSWORD],
[self::RULE_MAINTENANCE_MODE],
];
/**
* @var array defines all available validators, this list can be extended by calling `registerValidator()`
*/
protected $validators = [];
/**
* @var User identity to test against
*/
public $user;
/**
* @var string the controller action id to test
*/
public $action;
/**
* @var array access rule array
*/
protected $rules = [];
/**
* @var string actual decline message, can be changed in verify checks for specific error messages
*/
public $reason;
/**
* @var int http code, can be changed in verify checks for specific error codes
*/
public $code;
/**
* @var string Name of callback method to run after failed validation
* @since 1.8
*/
public $codeCallback;
/**
* @var Controller owner object of this ControllerAccess the owner is mainly used to find custom validation handler
*/
public $owner;
/**
* @inheritdoc
*/
public function init()
{
$this->user = Yii::$app->user->getIdentity();
if (empty($this->action)) {
$this->action = Yii::$app->controller->action->id;
}
$this->registerValidator([
self::RULE_STRICT => 'validateStrictMode',
'reason' => Yii::t('error', 'Guest mode not active, please login first.'),
'code' => 401
]);
$this->registerValidator([
self::RULE_UNAPPROVED_USER => 'validateUnapprovedUser',
'reason' => Yii::t('error', 'Your user account has not been approved yet, please try again later or contact a network administrator.'),
'code' => 401
]);
$this->registerValidator([
self::RULE_DISABLED_USER => 'validateDisabledUser',
'reason' => Yii::t('error', 'Your user account is inactive, please login with an active account or contact a network administrator.'),
'code' => 401
]);
$this->registerValidator([
self::RULE_LOGGED_IN_ONLY => 'validateLoggedInOnly',
'reason' => Yii::t('error', 'Login required for this section.'),
'code' => 401
]);
$this->registerValidator([
self::RULE_MAINTENANCE_MODE => 'validateMaintenanceMode',
'reason' => ControllerAccess::getMaintenanceModeWarningText(),
'code' => 403,
'codeCallback' => 'checkMaintenanceMode',
]);
$this->registerValidator([
self::RULE_MUST_CHANGE_PASSWORD => 'validateMustChangePassword',
'reason' => Yii::t('error', 'You must change password.'),
'code' => 403,
'codeCallback' => 'forceChangePassword',
]);
// We don't set code 401 since we want to show an error instead of redirecting to login
$this->registerValidator(GuestAccessValidator::class);
$this->registerValidator([
self::RULE_ADMIN_ONLY => 'validateAdminOnly',
'reason' => Yii::t('error', 'You need admin permissions to access this section.')
]);
$this->registerValidator(PermissionAccessValidator::class);
$this->registerValidator(DeprecatedPermissionAccessValidator::class);
$this->registerValidator([
self::RULE_POST => 'validatePostRequest',
'reason' => Yii::t('base', 'Invalid request method!'),
'code' => 405
]);
$this->registerValidator([self::RULE_JSON => 'validateJsonResponse']);
$this->registerValidator([
self::RULE_AJAX_ONLY => 'validateAjaxOnlyRequest',
'reason' => Yii::t('error', 'The specified URL cannot be called directly.'),
'code' => 405
]);
}
/**
* @return array set of rules
*/
public function getRules()
{
return $this->rules;
}
/**
* Sets the current set of rules.
* > Note: This will merge the given set of rules with the fixed rules.
*
* @param array $rules sets th
*/
public function setRules($rules = [])
{
$this->rules = array_merge($this->getFixedRules(), $rules);
}
/**
* Adds a new validator to the available validators and sets some default values.
*
* A validator shoud have the following form
*
* `['ruleName' => 'handler', 'code' => 401, 'reason' => 'Some message in case the validation failed']`
*
* to allow other direct settings required by the action validator e.g. direct permission settings.
*
* @param $setting array validator setting array
* @throws \yii\base\InvalidConfigException
*/
protected function registerValidator($options)
{
if (is_string($options)) {
$options = ['class' => $options];
}
$options['access'] = $this;
$name = $this->getName($options);
if ($name == 'class') {
$validator = Yii::createObject($options);
} elseif (class_exists($name)) {
unset($options[0]);
$options['class'] = $name;
$validator = Yii::createObject($options);
} else {
$handler = $options[$name];
unset($options[$name]);
$options['name'] = $name;
$options['owner'] = $this;
$options['handler'] = $handler;
$validator = new DelegateAccessValidator($options);
}
$this->validators[$validator->name] = $validator;
}
/**
* Runs the current $rule setting against all available validators
* @return bool
*/
public function run()
{
$finished = [];
foreach ($this->rules as $rule) {
$ruleName = $this->getName($rule);
// A validator validates all rules of the given $name,
// so we don't have to rerun the validation here if already handled
if (in_array($ruleName, $finished)) {
continue;
}
$finished[] = $ruleName;
$validator = $this->findValidator($ruleName);
if (!$validator->run()) {
$this->reason = (!$this->reason) ? $validator->getReason() : $this->reason;
$this->code = (!$this->code) ? $validator->getCode() : $this->code;
if (isset($validator->codeCallback)) {
$this->codeCallback = $validator->codeCallback;
}
return false;
}
}
return true;
}
protected function findValidator($ruleName)
{
if (isset($this->validators[$ruleName])) {
return $this->validators[$ruleName];
}
return $this->getCustomValidator($ruleName);
}
protected function getCustomValidator($ruleName)
{
if ($this->owner && method_exists($this->owner, $ruleName)) {
return new DelegateAccessValidator([
'access' => $this,
'owner' => $this->owner,
'handler' => $ruleName,
'name' => $ruleName
]);
}
if (class_exists($ruleName)) {
return Yii::createObject([
'class' => $ruleName,
'access' => $this
]);
}
throw new InvalidArgumentException('Invalid validator settings given for rule ' . $ruleName);
}
/**
* Extracts the ruleName from a given rule option array.
*
* @param $arr
* @return mixed|null
*/
protected function getName($arr)
{
if (empty($arr)) {
return null;
}
$firstKey = current(array_keys($arr));
if (is_string($firstKey)) {
return $firstKey;
} else {
return $arr[$firstKey];
}
}
/**
* @return array returns array of rules which will always be added to the rule set
*/
protected function getFixedRules()
{
return $this->fixedRules;
}
/**
* @return bool makes sure if the current user is loggedIn
*/
public function validateLoggedInOnly()
{
return !$this->isGuest();
}
/**
* @return bool makes sure the current user has administration rights
*/
public function validateAdminOnly()
{
return $this->isAdmin();
}
/**
* @return bool checks if guest mode is activated for guestaccess
*/
public function validateStrictMode()
{
return !$this->isGuest() || AuthHelper::isGuestAccessEnabled();
}
/**
* @return mixed checks if the current request is a post request
*/
public function validatePostRequest()
{
return Yii::$app->request->isPost;
}
/**
* @return mixed checks if the current request is an ajax request
*/
public function validateAjaxOnlyRequest()
{
return Yii::$app->request->isAjax;
}
/**
* @return bool makes sure the response type is json
*/
public function validateJsonResponse()
{
Yii::$app->response->format = 'json';
return true;
}
/**
* @return bool checks if the current user is a disabled user
*/
public function validateDisabledUser()
{
return $this->isGuest() ||
($this->user->status !== User::STATUS_DISABLED &&
$this->user->status !== User::STATUS_SOFT_DELETED);
}
/**
* @return bool checks if the current user is an unapproved user
*/
public function validateUnapprovedUser()
{
return $this->isGuest() || $this->user->status !== User::STATUS_NEED_APPROVAL;
}
/**
* @return bool Checks if the given $user is set.
*/
public function isGuest()
{
return $this->user == null;
}
public function isAdmin()
{
return !$this->isGuest() && $this->user->isSystemAdmin();
}
/**
* @return bool checks if the current user must change password
* @since 1.8
*/
public function validateMustChangePassword()
{
return $this->isGuest() || Yii::$app->user->isMustChangePasswordUrl() || !$this->user->mustChangePassword() ||
($this->owner->module->id == 'user' && $this->owner->id == 'auth' && $this->owner->action->id == 'logout');
}
/**
* @return bool makes sure the current user has an access on maintenance mode
* @since 1.8
*/
public function validateMaintenanceMode()
{
return !Yii::$app->settings->get('maintenanceMode') ||
$this->isAdmin() ||
($this->owner->module->id == 'user' && $this->owner->id == 'auth' && $this->owner->action->id == 'login');
}
/**
* @param string $beforeCustomInfo
* @return string returns the maintenance mode warning text
* @since 1.8
*/
public static function getMaintenanceModeWarningText($beforeCustomInfo = ' ')
{
$customInfo = Yii::$app->settings->get('maintenanceModeInfo', '');
return Yii::t('error', 'Maintenance mode is active. Only Administrators can access the platform.') .
($customInfo === '' ? '' : $beforeCustomInfo . $customInfo);
}
}