ControllerAccess

This commit is contained in:
buddh4 2017-08-01 22:10:35 +02:00
parent 7da8715f57
commit 03fdb7a5df
66 changed files with 3402 additions and 373 deletions

View File

@ -22,6 +22,7 @@
"yiisoft/yii2-imagine": "~2.0.0",
"raoul2000/yii2-jcrop-widget": "*",
"kartik-v/yii2-widgets": "*",
"phpoffice/phpexcel": "*",
"cebe/markdown": "1.0.2",
"yiisoft/yii2-jui": "~2.0.0",
"zendframework/zend-http": "*",
@ -52,7 +53,7 @@
"bower-asset/imagesloaded": "*"
},
"require-dev": {
"yiisoft/yii2-codeception": "~2.0.0",
"codeception/codeception": "*",
"yiisoft/yii2-debug": "~2.0.0",
"yiisoft/yii2-gii": "~2.0.0",
"yiisoft/yii2-faker": "~2.0.0",

View File

@ -8,6 +8,8 @@
namespace humhub\compat;
use humhub\widgets\MarkdownField;
use humhub\widgets\MultiSelectField;
use Yii;
/**
@ -234,7 +236,7 @@ class HForm extends \yii\base\Component
}
return $field;
case 'multiselectdropdown':
return \humhub\widgets\MultiSelectField::widget([
return MultiSelectField::widget([
'form' => $this->form,
'model' => $model,
'attribute' => $name,
@ -289,8 +291,7 @@ class HForm extends \yii\base\Component
]);
case 'markdown':
$options['id'] = $name;
$returnField = $this->form->field($model, $name)->textarea($options);
$returnField .= \humhub\widgets\MarkdownEditor::widget(array('fieldId' => $name));
$returnField = $this->form->field($model, $name)->widget(MarkdownField::class, $options);
return $returnField;
default:
return "Field Type " . $definition['type'] . " not supported by Compat HForm";

View File

@ -8,6 +8,7 @@
namespace humhub\components;
use humhub\components\access\ControllerAccess;
use humhub\components\behaviors\AccessControl;
use Yii;
use yii\helpers\Url;
@ -49,36 +50,10 @@ class Controller extends \yii\web\Controller
public $prependActionTitles = true;
/**
* @var string[] defines the allowed actions for guests if $strictGuestMode is set to true.
* @var string defines the ControllerAccess class for this controller responsible for managing access rules
* @see self::getAccess()
*/
public $guestActions = [];
/**
* @var bool if set to true will only allow actions defined in $guestActions, otherwise the controller has to handle its own guest handling
*/
public $strictGuestMode = false;
/**
* @var bool if set to true only (space/profile/system) admins are allowed to access any controller action
*/
protected $adminOnly = false;
/**
* @inheritdoc
*/
public function behaviors()
{
return [
'acl' => [
'class' => AccessControl::className(),
'adminOnly' => $this->adminOnly,
'guestAllowedActions' => $this->guestActions,
'loggedInOnly' => $this->strictGuestMode,
'rules' => $this->getAccessRules()
]
];
}
protected $access = ControllerAccess::class;
/**
* Returns access rules for the standard access control behavior.
@ -91,6 +66,18 @@ class Controller extends \yii\web\Controller
return [];
}
/**
* @return null|ControllerAccess returns an ControllerAccess instance
*/
public function getAccess()
{
if(!$this->access) {
return null;
}
return Yii::createObject($this->access);
}
/**
* @inheritdoc
*/
@ -100,6 +87,16 @@ class Controller extends \yii\web\Controller
$this->trigger(self::EVENT_INIT);
}
public function behaviors()
{
return [
'acl' => [
'class' => AccessControl::class,
'rules' => $this->getAccessRules()
]
];
}
/**
* @inheritdoc
*/

View File

@ -0,0 +1,217 @@
<?php
/**
* @link https://www.humhub.org/
* @copyright Copyright (c) 2017 HumHub GmbH & Co. KG
* @license https://www.humhub.com/licences
*
*/
/**
* Created by PhpStorm.
* User: buddha
* Date: 30.07.2017
* Time: 03:07
*/
namespace humhub\components\access;
use Yii;
use yii\base\InvalidParamException;
use yii\base\Object;
/**
* AccessValidators are responsible for validating a given set of rules.
*
* Rules consist of an array with at leas an rule name and optional further rule settings.
* If only a rule name is given, the rule is considered global, otherwise it may be restricted to specific actions by
* providing an action setting e.g.:
*
* ```
* // Global myRule
* ['myRule']
*
* // MyRule restricted to action1 and action2
* ['myRule' => ['action1', 'action2']]
*
* // Alternative action configuration
* ['myRule', 'actions' => ['action1', 'action2']]
* ```
*
* A Validator has an unique name which is used to detect related rules and can filter out non related rules by
* means of the `filterRelatedRules()` function.
*
* Subclasses have to overwrite the `run()` function, which holds the actual validation logic.
*
* AccessValidators have access to a ControllerAccess instance, which holds the ruleset and validation state.
*
* This abstract validator class furthermore provides some helper functions as:
*
* - `isActionRelated()`: Checks if a given rule is related to the current action
* - `extractActions()`: Extracts the action settings from a given rule array
* - `getRuleName()`: Extracts the rule name from a given rule array
*
* @package humhub\components\access
*/
abstract class AccessValidator extends Object
{
/**
* @var string the name of the valdiator
*/
public $name;
/**
* @var int http error code used in case the validation failes
*/
public $code = 403;
/**
* @var string validator error message
*/
public $reason;
/**
* @var ControllerAccess access instance
*/
public $access;
/**
* @var bool determines if this validator is only interested in action related rules or all validator related rules
*/
public $actionFilter = true;
public function init()
{
if(!$this->name) {
$this->name = static::class;
}
if(empty($this->reason)) {
$this->reason = Yii::t('error', 'You are not permitted to access this section.');
}
}
/**
* Responsible for validating the given ruleset.
* Related rules may be filtered by means of the `filterRelatedRules()` function.
* The whole rule set can be retrieved by calling `$this->access->rules`.
*
*
* @return boolean true if validation passed otherwise true
*/
abstract function run();
/**
* Filters out all rules which are not related to this validator.
*
* @param $rules
* @return array
*/
protected function filterRelatedRules($rules = null)
{
if($rules === null) {
$rules = $this->access->getRules();
}
$result = [];
foreach ($rules as $rule) {
$ruleName = $this->getRuleName($rule);
if($this->name === $ruleName) {
$result[] = $rule;
}
}
return $result;
}
/**
* Checks if the current action is contained in the given $rule.
* This is the case either if the current action is contained in the rules action settings or
* the rule is global (no action restriction).
*
* @param array|string $actionArray single action id or array of action ids
* @return bool
*/
protected function isActionRelated($rule)
{
$actions = $this->extractActions($rule);
// If no action array is given we consider the rule to be controller global
if (empty($actions)) {
return true;
}
if (!is_array($actions) && !is_string($actions)) {
throw new InvalidParamException('Invalid rule provided!');
}
$actions = is_string($actions) ? [$actions] : $actions;
return in_array($this->access->action, $actions);
}
/**
* Extracts actions settings form a given rule.
*
* Action rules can be either set like:
*
* ['ruleName', 'actions' => ['action1', 'action2']]
*
* or in some cases:
*
* ['ruleName' => ['action1', 'action2']]
*
* @param $rule
* @return array
*/
protected function extractActions($rule)
{
$name = $this->getRuleName($rule);
$actions = [];
if (isset($rule['actions'])) {
$actions = $rule['actions'];
} else {
$actions = isset($rule[$name]) ? $rule[$name] : $actions;
}
return $actions;
}
/**
* Extracts the ruleName from the given array.
*
* @param $arr
* @return mixed|null
*/
protected function getRuleName($rule)
{
if(empty($rule)) {
return null;
}
$firstKey = current(array_keys($rule));
if(is_string($firstKey)) {
return $firstKey;
} else {
return $rule[$firstKey];
}
}
/**
* @return string the error message in case the validation fails
*/
public function getReason()
{
return $this->reason;
}
/**
* @return int http error code used in case the validation fails
*/
public function getCode()
{
return $this->code;
}
}

View File

@ -0,0 +1,84 @@
<?php
/**
* @link https://www.humhub.org/
* @copyright Copyright (c) 2017 HumHub GmbH & Co. KG
* @license https://www.humhub.com/licences
*
*/
/**
* Created by PhpStorm.
* User: buddha
* Date: 28.07.2017
* Time: 17:43
*/
namespace humhub\components\access;
/**
* This Validator filters out non action related rules and supports a $strict mode, which will require all validator
* related rules to pass.
*
* If $strict mode is set to false only one of the validator related rules have pass.
*
* Subclasses of ActionAccessValidator only have to extend the `validate()` function for validating a single rule.
*
* @package humhub\components\access
*/
abstract class ActionAccessValidator extends AccessValidator
{
/**
* @var bool if set to true (default) all validator related rules have to pass otherwise only one
*/
public $strict = true;
/**
* Runs the validation against all validator and action related rules.
*
* This function will return true, if there is no action related rule for this validator.
*
* @param $rule array
* @param $access ControllerAccess
* @return boolean
*/
public function run()
{
$rules = $this->filterRelatedRules();
if(empty($rules)) {
return true;
}
foreach ($rules as $rule) {
$can = $this->validate($rule);
if ($can && !$this->strict) {
return true;
} else if (!$can && $this->strict) {
return false;
}
}
return $this->strict;
}
/**
* Filters our rules not related to the current validator and action.
*
* @param array|null $rules
* @return array
*/
protected function filterRelatedRules($rules = null)
{
$result = [];
foreach (parent::filterRelatedRules($rules) as $rule) {
if($this->isActionRelated($rule)) {
$result[] = $rule;
}
}
return $result;
}
protected abstract function validate($rule);
}

View File

@ -0,0 +1,363 @@
<?php
/**
* @link https://www.humhub.org/
* @copyright Copyright (c) 2017 HumHub GmbH & Co. KG
* @license https://www.humhub.com/licences
*
*/
/**
* Created by PhpStorm.
* User: buddha
* Date: 26.07.2017
* Time: 13:33
*/
namespace humhub\components\access;
use humhub\libs\BasePermission;
use humhub\modules\user\models\User;
use Yii;
use yii\base\InvalidParamException;
use yii\base\Object;
use yii\web\Controller;
/**
* ControllerAccess contains the actual logic to verify if a user can access a given $action.
*
* By default the AccessCheck will set the current logged in user permission object, if $user is null, we assume a guest
* user.
*
* The guest user access can be verified by calling the `reguiresLogin()` check.
*
* Inactive users are can be catched by calling `isInActiveUser()`.
*
* The actual permission rule verification is handled by the `verify()` check, subclasses may overwrite and extend this
* function with additional checks.
*
* Subclasses can extend available validators by calling `registerValidator` and providing a validator setting array as:
*
* ```
* public function init()
* {
* parent::init();
* $this->registerValidator([
* self::RULE_MY_CUSTOM_RULE => 'validateCustomRule',
* 'reason' => Yii::t('error', 'Guest mode not active, please login first.'),
* 'code' => 401]);
* }
* ```
*
* The previous example registered an new validator repsonsible for validating $rules with name self::RULE_MY_CUSTOM_RULE and validation
* handler function 'validateCustomRule' which defines an handler method within the subclass.
*
* The validator can be set the following additional settings:
*
* - **reason**: Reason set if the validaiton fails
* - **code**: Http Code e.g. 404, 403, 401
* - **actionType**: Defines how to determine if a rule is action related by default an action rule allows the following action settings:
* ['myCustomRule' => ['action1', 'action2']] and ['myCustomRule', 'actions' => ['action1', 'action2']] but can be restricted to the second definition only by setting ACTION_SETTING_TYPE_OPTION_ONLY
* - **actionFilter**: if set to false the validations handler is always executed even if the action settings do not match with the current action (default true)
* - **strict**: if set to false only one rule of a given validator has to pass otherwise all rules have to pass (default true)
*
* @since 1.2.2
*/
class ControllerAccess extends Object
{
/**
* 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;
const RULE_ADMIN_ONLY = 'admin';
const RULE_PERMISSION = 'permission';
const RULE_LOGGED_IN_ONLY = 'login';
const RULE_GUEST_CUSTOM = 'custom';
const RULE_STRICT = 'strict';
const RULE_DISABLED_USER = 'disabledUser';
const RULE_UNAPPROVED_USER = 'unapprovedUser';
const RULE_POST = 'post';
const RULE_JSON = 'json';
/**
* @var array fixed rules will always be added to the current rule set
*/
protected $fixedRules = [
[self::RULE_DISABLED_USER],
[self::RULE_UNAPPROVED_USER],
];
/**
* @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 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]);
// 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', 'code' => 405, 'reason' => Yii::t('base', 'Invalid request method!')]);
$this->registerValidator([self::RULE_JSON => 'validateJsonResponse']);
}
public function getRules()
{
return $this->rules;
}
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
*/
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);
} else if(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;
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 InvalidParamException('Invalid validator settings given for rule '.$ruleName);
}
/**
* Extracts the ruleName from the given $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() || Yii::$app->user->isGuestAccessEnabled();
}
/**
* @return mixed checks if the current request is a post request
*/
public function validatePostRequest()
{
return Yii::$app->request->method == 'POST';
}
/**
* @return bool makes sure the response type is json
*/
public function validateJson()
{
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;
}
/**
* @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();
}
}

View File

@ -0,0 +1,30 @@
<?php
/**
* @link https://www.humhub.org/
* @copyright Copyright (c) 2017 HumHub GmbH & Co. KG
* @license https://www.humhub.com/licences
*
*/
/**
* Created by PhpStorm.
* User: buddha
* Date: 30.07.2017
* Time: 02:21
*/
namespace humhub\components\access;
class DelegateAccessValidator extends ActionAccessValidator
{
public $owner;
public $handler;
protected function validate($rule)
{
$handler = $this->handler;
return $this->owner->$handler($rule, $this);
}
}

View File

@ -0,0 +1,25 @@
<?php
/**
* @link https://www.humhub.org/
* @copyright Copyright (c) 2017 HumHub GmbH & Co. KG
* @license https://www.humhub.com/licences
*
*/
/**
* Created by PhpStorm.
* User: buddha
* Date: 30.07.2017
* Time: 04:04
*/
namespace humhub\components\access;
use Yii;
use yii\base\InvalidParamException;
class DeprecatedPermissionAccessValidator extends PermissionAccessValidator
{
public $name = 'permissions';
}

View File

@ -0,0 +1,51 @@
<?php
/**
* @link https://www.humhub.org/
* @copyright Copyright (c) 2017 HumHub GmbH & Co. KG
* @license https://www.humhub.com/licences
*
*/
/**
* Created by PhpStorm.
* User: buddha
* Date: 30.07.2017
* Time: 03:00
*/
namespace humhub\components\access;
use Yii;
class GuestAccessValidator extends AccessValidator
{
public $name = 'guestAccess';
public $code = 403;
/**
* @inheritdoc
*/
public function run()
{
if($this->access->isGuest() && !Yii::$app->user->isGuestAccessEnabled()) {
$this->code = 401;
return false;
}
if(!$this->access->isGuest()) {
return true;
}
// If there is a guest restriction rule only return true if there is an action related rule
foreach ($this->filterRelatedRules() as $rule) {
if($this->isActionRelated($rule)) {
return true;
}
}
return false;
}
}

View File

@ -0,0 +1,60 @@
<?php
/**
* @link https://www.humhub.org/
* @copyright Copyright (c) 2017 HumHub GmbH & Co. KG
* @license https://www.humhub.com/licences
*
*/
/**
* Created by PhpStorm.
* User: buddha
* Date: 30.07.2017
* Time: 04:04
*/
namespace humhub\components\access;
use Yii;
use yii\base\InvalidParamException;
class PermissionAccessValidator extends ActionAccessValidator
{
public $name = 'permission';
public $strict = false;
protected function validate($rule)
{
if (isset($rule[$this->name]) && !empty($rule[$this->name])) {
return $this->verifyPermission($rule[$this->name], $rule);
}
throw new InvalidParamException('Invalid permission rule provided for action ' . $this->action);
}
/**
* Checks if the user has the given $permission.
*
* @param string|string[]|BasePermission $permission
* @param array $params
* @param array $rule
* @return bool true if the given $permission is granted
*/
protected function verifyPermission($permission, $rule)
{
return Yii::$app->user->can($permission, $rule);
}
protected function extractActions($rule)
{
$actions = null;
if (isset($rule['actions'])) {
$actions = $rule['actions'];
}
return $actions;
}
}

View File

@ -0,0 +1,33 @@
<?php
/**
* @link https://www.humhub.org/
* @copyright Copyright (c) 2017 HumHub GmbH & Co. KG
* @license https://www.humhub.com/licences
*
*/
/**
* Created by PhpStorm.
* User: buddha
* Date: 27.07.2017
* Time: 23:25
*/
namespace humhub\components\access;
/**
* StrictAccess should be used by all controllers which don't allow guest access if guest mode is inactive.
* There are only some controllers which require guest access even if guest mode is active as Login, Registration etc.
*
* @package humhub\components\access
*/
class StrictAccess extends ControllerAccess
{
public function getFixedRules()
{
$fixed = parent::getFixedRules();
$fixed[] = [self::RULE_STRICT];
return $fixed;
}
}

View File

@ -8,11 +8,10 @@
namespace humhub\components\behaviors;
use humhub\modules\content\components\ContentContainerController;
use humhub\modules\space\models\Space;
use Yii;
use yii\helpers\ArrayHelper;
use yii\web\ForbiddenHttpException;
use humhub\components\access\ControllerAccess;
use yii\web\HttpException;
/**
* AccessControl provides basic controller access protection
@ -52,13 +51,6 @@ use yii\web\ForbiddenHttpException;
class AccessControl extends \yii\base\ActionFilter
{
/**
* Action ids which are allowed when Guest Mode is enabled
*
* @var array
*/
public $guestAllowedActions = [];
/**
* Rules for access to controller
*
@ -66,22 +58,32 @@ class AccessControl extends \yii\base\ActionFilter
*/
public $rules = [];
/**
* Action ids which are allowed when Guest Mode is enabled
*
* @var array
* @deprecated since 1.2.2 use ['guestAccess' => ['action1', 'action2']] rule instead
*/
public $guestAllowedActions = [];
/**
* Only allow admins access to this controller
*
* @var boolean
* @deprecated since 1.2.2 use ['adminOnly'] rule instead
*/
public $adminOnly = false;
/**
* Only allow logged in users access to this controller
* @deprecated since 1.2.2 use ['loggedInOnly'] rule instead
*/
public $loggedInOnly = true;
public $loggedInOnly = false;
/**
* User groups cache;
* @var ControllerAccess instance
*/
private $_usergroupNames = null;
protected $_controllerAccess;
/**
* @inheritdoc
@ -93,200 +95,77 @@ class AccessControl extends \yii\base\ActionFilter
return true;
}
$identity = Yii::$app->user->getIdentity();
$this->handleDeprecatedSettings();
$this->_controllerAccess = $this->getControllerAccess($this->rules);
if ($identity != null && !$identity->isActive()) {
return $this->handleInactiveUser();
}
if (Yii::$app->user->isGuest && !$this->adminOnly) {
return $this->handleGuestAccess($action);
}
if ($this->adminOnly && !$this->isAdmin()) {
$this->forbidden();
}
if ($this->checkRules()) {
return true;
}
return $this->loggedInOnly;
}
/**
* Denys access for non active users by performing a logout.
* @return boolean always false
*/
protected function handleInactiveUser()
{
Yii::$app->user->logout();
Yii::$app->response->redirect(['/user/auth/login']);
return false;
}
/**
* Checks access for guest users.
*
* Guests users are allowed to access an action if either the $loggedInOnly and $adminOnly flags are
* set to false or the given controller action is contained in $guestAllowedActions.
*
* @return boolean
*/
protected function handleGuestAccess($action)
{
if (!$this->loggedInOnly && !$this->adminOnly) {
return true;
}
if (in_array($action->id, $this->guestAllowedActions) && Yii::$app->getModule('user')->settings->get('auth.allowGuestAccess') == 1) {
return true;
}
Yii::$app->user->loginRequired();
return false;
}
/**
* Checks group and permission rules.
* @return boolean
*/
protected function checkRules()
{
$result = true;
if (!empty($this->rules)) {
foreach ($this->rules as $rule) {
// If the rule contains an action restriction, which does not match the current controller action we ignore the rule.
if(!$this->checkRuleAction($rule)) {
continue;
}
if ($this->checkGroupRule($rule) || $this->checkPermissionRule($rule)) {
return true;
}
}
$this->forbidden();
}
return true;
}
/**
* Checks a given permission rules.
*
* @param type $rule
* @return boolean
*/
protected function checkPermissionRule($rule)
{
if (isset($rule['permissions']) && !empty($rule['permissions'])) {
$permissionArr = (!is_array($rule['permissions'])) ? [$rule['permissions']] : $rule['permissions'];
$params = isset($rule['params']) ? $rule['params'] : [];
if ($this->isContentContainerController()) {
return $this->owner->contentContainer->can($permissionArr, $params) || Yii::$app->user->can($permissionArr, $params);
}
return Yii::$app->user->can($permissionArr, $params);
}
return false;
}
/**
* Checks the current controller action against the allowed rule action.
* If the rule does not contain any action settings, the rule is allowed for all controller actions.
*
* @param array $rule
* @return boolean true if current action is allowed
*/
private function checkRuleAction($rule)
{
if (!empty($rule['actions'])) {
$action = Yii::$app->controller->action->id;
return in_array($action, $rule['actions']);
}
return true;
}
protected function isContentContainerController()
{
return $this->owner instanceof ContentContainerController;
}
protected function isAdmin()
{
if(Yii::$app->user->isGuest) {
return false;
}
if($this->isContentContainerController()) {
if($this->owner->contentContainer instanceof Space) {
return $this->owner->contentContainer->isAdmin();
if(!$this->_controllerAccess->run()) {
if($this->_controllerAccess->code == 401) {
return $this->loginRequired();
} else {
return $this->owner->contentContainer->isCurrentUser();
$this->forbidden();
}
}
return Yii::$app->user->isAdmin();
}
private function getControllerSpace()
{
if ($this->isContentContainerController()) {
return $this->owner->getSpace();
}
return null;
return parent::beforeAction($action);
}
/**
* Checks specific group access by group names.
*
* @param type $rule
* @return boolean
* Compatibility with pre 1.2.2 usage of AccessControl
*/
protected function checkGroupRule($rule)
protected function handleDeprecatedSettings()
{
if (!empty($rule['groups'])) {
$userGroups = $this->getUserGroupNames();
$isAllowedAction = $this->checkRuleAction($rule);
$allowedGroups = array_map('strtolower', $rule['groups']);
foreach ($allowedGroups as $allowedGroup) {
if (in_array($allowedGroup, $userGroups) && $isAllowedAction) {
return true;
}
}
if($this->adminOnly) {
$this->rules[] = [ControllerAccess::RULE_ADMIN_ONLY];
}
return false;
if($this->loggedInOnly) {
$this->rules[] = [ControllerAccess::RULE_LOGGED_IN_ONLY];
}
if(!empty($this->guestAllowedActions)) {
$this->rules[] = ['guestAccess' => $this->guestAllowedActions];
}
}
/**
* Returns an array of strings with all user groups of the current user.
* Returns a ControllerAccess instance, controllers are able to overwrite this by implementing an own `getAccess()`
* function.
*
* @return type
* @return ControllerAccess
*/
private function getUserGroupNames()
protected function getControllerAccess($rules = [])
{
if ($this->_userGroupNames == null) {
$identity = Yii::$app->user->getIdentity();
$this->_userGroupNames = ArrayHelper::getColumn(ArrayHelper::toArray($identity->groups), 'name');
$this->_userGroupNames = array_map('strtolower', $this->_userGroupNames);
$instance = null;
if(method_exists($this->owner, 'getAccess')) {
$instance = $this->owner->getAccess();
}
return $this->_userGroupNames;
if(!$instance) {
$instance = new ControllerAccess();
}
$instance->setRules($rules);
$instance->owner = $this->owner;
return $instance;
}
/**
* @throws ForbiddenHttpException
*/
protected function forbidden()
{
throw new ForbiddenHttpException(Yii::t('error', 'You are not allowed to perform this action.'));
throw new HttpException($this->_controllerAccess->code, $this->_controllerAccess->reason);
}
/**
* @return bool forces user login
*/
protected function loginRequired()
{
Yii::$app->user->logout();
Yii::$app->user->loginRequired();
return false;
}
}

View File

@ -50,8 +50,10 @@ HumHub Change Log
- Enh: Darkened comment links for better readability
- Fix #2582 Userfollow activity click action not working
- Enh: Make space membership activities clickable
- Enh: Removed `yii2-codeception` dependency
- Chg: Removed `yii2-codeception` dependency
- Chg: Added `phpoffice/phpexcel` dependency
- Enh: Added `JsWidget::fadeIn` for smooth widget initialization
- Enh: Enhanced `AccessControl` filter with `ControllerAccess` layer for better testability and flexibility
1.2.1 (June 17, 2017)
- Fix: Invite error in french language

View File

@ -1,4 +1,4 @@
<?php //[STAMP] 1cb90cb7de157697c2044b397df5dd61
<?php //[STAMP] 142934168f3943147d5338fad03349ce
namespace activity\_generated;
// This class was automatically generated by build task
@ -3062,6 +3062,28 @@ trait FunctionalTesterActions
}
/**
* [!] Method is generated. Documentation taken from corresponding module.
*
*
* @see \tests\codeception\_support\HumHubHelper::assertMailSent()
*/
public function assertMailSent($count = null, $msg = null) {
return $this->getScenario()->runStep(new \Codeception\Step\Action('assertMailSent', func_get_args()));
}
/**
* [!] Method is generated. Documentation taken from corresponding module.
*
*
* @see \tests\codeception\_support\HumHubHelper::assertEqualsLastEmailSubject()
*/
public function assertEqualsLastEmailSubject($subject) {
return $this->getScenario()->runStep(new \Codeception\Step\Action('assertEqualsLastEmailSubject', func_get_args()));
}
/**
* [!] Method is generated. Documentation taken from corresponding module.
*

View File

@ -29,6 +29,8 @@ class Controller extends \humhub\components\Controller
*/
public $adminOnly = true;
public $loggedInOnly = true;
/**
* @inheritdoc
*/

View File

@ -0,0 +1,158 @@
<?php
/**
* @link https://www.humhub.org/
* @copyright Copyright (c) 2017 HumHub GmbH & Co. KG
* @license https://www.humhub.com/licences
*
*/
/**
* Created by PhpStorm.
* User: buddha
* Date: 31.07.2017
* Time: 13:25
*/
namespace humhub\modules\admin\controllers;
use DateTime;
use humhub\components\ActiveRecord;
use humhub\modules\admin\components\Controller;
use humhub\modules\admin\models\PendingRegistrationSearch;
use humhub\modules\admin\permissions\ManageGroups;
use humhub\modules\admin\permissions\ManageUsers;
use PHPExcel;
use PHPExcel_Cell;
use PHPExcel_IOFactory;
use PHPExcel_Shared_Date;
use PHPExcel_Style_NumberFormat;
use PHPExcel_Writer_Excel2007;
use function PHPSTORM_META\type;
use Yii;
use yii\helpers\ArrayHelper;
class PendingRegistrationsController extends Controller
{
public function init()
{
$this->subLayout = '@admin/views/layouts/user';
$this->appendPageTitle(Yii::t('AdminModule.base', 'Pending user registrations'));
return parent::init();
}
/**
* @inheritdoc
*/
public function getAccessRules()
{
return [
[
'permission' => [
ManageUsers::class,
ManageGroups::class,
]
]
];
}
public function actionIndex($export = false, $format = null)
{
$searchModel = new PendingRegistrationSearch();
$dataProvider = $searchModel->search(Yii::$app->request->queryParams);
if($export) {
return $this->createCVS($dataProvider, $searchModel, $format);
}
return $this->render('index', [
'dataProvider' => $dataProvider,
'searchModel' => $searchModel,
'types' => $this->getTypeMapping()
]);
}
public function getTypeMapping()
{
return [
PendingRegistrationSearch::SOURCE_INVITE => Yii::t('AdminModule.base', 'Invite'),
PendingRegistrationSearch::SOURCE_SELF => Yii::t('AdminModule.base', 'Sign up'),
];
}
public function createCVS($dataProvider, ActiveRecord $model, $format = null)
{
$columns = [
['email'],
['originator.username'],
['language'],
['source'],
['created_at', 'type' => 'datetime'],
];
$file = new PHPExcel();
$file->getProperties()->setCreator('HumHub');
$file->getProperties()->setTitle(Yii::t('AdminModule.base', 'Pending user registrations'));
$file->getProperties()->setSubject(Yii::t('AdminModule.base', 'Pending user registrations'));
$file->getProperties()->setDescription(Yii::t('AdminModule.base', 'Pending user registrations'));
$file->setActiveSheetIndex(0);
$worksheet = $file->getActiveSheet();
$worksheet->setTitle(Yii::t('AdminModule.base', 'Pending user registrations'));
// Creat header
$row = 1;
$lastColumn = count($columns);
for ($column = 0; $column != $lastColumn; $column++) {
$columnKey = PHPExcel_Cell::stringFromColumnIndex($column);
$worksheet->getColumnDimension($columnKey)->setWidth(30);
$worksheet->setCellValueByColumnAndRow($column, $row, $model->getAttributeLabel($columns[$column][0]));
}
$row++;
// Fill content header
foreach($dataProvider->query->all() as $record) {
for ($column = 0; $column != $lastColumn; $column++) {
$attribute = $columns[$column][0];
$value = ArrayHelper::getValue($record,$attribute);
if(isset($columns[$column]['type']) && $columns[$column]['type'] === 'datetime') {
$value = PHPExcel_Shared_Date::PHPToExcel(new DateTime($value));
if($format === 'CSV') {
$worksheet->getStyleByColumnAndRow($column, $row)->getNumberFormat()->setFormatCode(Yii::$app->formatter->getDateTimePattern());
} else {
$worksheet->getStyleByColumnAndRow($column, $row)->getNumberFormat()->setFormatCode(PHPExcel_Style_NumberFormat::FORMAT_DATE_DATETIME);
}
}
if($attribute === 'source') {
$types = $this->getTypeMapping();
$value = isset($types[$value]) ? $types[$value] : $value;
}
$worksheet->setCellValueByColumnAndRow($column, $row, $value);
}
$row++;
}
$filePrefix = 'pur_export_'.time();
if($format === 'CSV') {
$writer = PHPExcel_IOFactory::createWriter($file, 'CSV');
$writer->setDelimiter(';');
header('Content-Type: application/csv');
header('Content-Disposition: attachment;filename="'.$filePrefix.'.csv"');
header('Cache-Control: max-age=0');
} else {
$writer = PHPExcel_IOFactory::createWriter($file, 'Excel2007');
header('Content-type: application/vnd.ms-excel');
header('Content-Disposition: attachment; filename="'.$filePrefix.'.xlsx"');
header('Cache-Control: max-age=0');
}
$writer->save('php://output');
}
}

View File

@ -47,13 +47,14 @@ class UserController extends Controller
public function getAccessRules()
{
return [
['permissions' => [
ManageUsers::className(),
ManageGroups::className()
[
'permissions' => [
ManageUsers::class,
ManageGroups::class,
]
],
[
'permissions' => ManageSettings::className(),
'permissions' => [ManageSettings::class],
'actions' => ['index']
]
];
@ -71,7 +72,7 @@ class UserController extends Controller
'dataProvider' => $dataProvider,
'searchModel' => $searchModel
]);
} else if (Yii::$app->user->can(new ManageSettings())) {
} else if (Yii::$app->user->can(ManageSettings::class)) {
$this->redirect(['/admin/authentication']);
} else {
$this->forbidden();
@ -85,9 +86,7 @@ class UserController extends Controller
*/
public function actionEdit()
{
$user = UserEditForm::findOne([
'id' => Yii::$app->request->get('id')
]);
$user = UserEditForm::findOne(['id' => Yii::$app->request->get('id')]);
$user->initGroupSelection();
if ($user == null) {

View File

@ -0,0 +1,83 @@
<?php
/**
* @link https://www.humhub.org/
* @copyright Copyright (c) 2017 HumHub GmbH & Co. KG
* @license https://www.humhub.com/licences
*
*/
namespace humhub\modules\admin\models;
use humhub\modules\user\models\Invite;
use Yii;
use yii\data\ActiveDataProvider;
/**
* PendingRegistrationSearch
*
* @author buddha
*/
class PendingRegistrationSearch extends Invite
{
public function attributes()
{
// add related fields to searchable attributes
return array_merge(parent::attributes(), ['originator.username']);
}
public function rules()
{
return [
[['id'], 'integer'],
[['email', 'created_at', 'originator.username', 'source', 'language'], 'safe'],
];
}
public function attributeLabels()
{
$result = parent::attributeLabels(); // TODO: Change the autogenerated stub
$result['originator.username'] = Yii::t('AdminModule.base', 'Invited by');
return $result;
}
/**
* Creates data provider instance with search query applied
*
* @param array $params
*
* @return ActiveDataProvider
*/
public function search($params = [])
{
$query = self::find()->joinWith(['originator']);
$dataProvider = new ActiveDataProvider([
'query' => $query,
'pagination' => ['pageSize' => 50],
]);
$dataProvider->setSort([
'attributes' => [
'email',
'created_at',
]
]);
$this->load($params);
if (!$this->validate()) {
$query->where('0=1');
return $dataProvider;
}
$query->andFilterWhere(['id' => $this->id]);
$query->andFilterWhere(['like', 'id', $this->id]);
$query->andFilterWhere(['like', 'username', $this->getAttribute('originator.username')]);
$query->andFilterWhere(['like', 'user_invite.email', $this->email]);
$query->andFilterWhere(['like', 'user_invite.language', $this->language]);
$query->andFilterWhere(['like', 'source', $this->source]);
return $dataProvider;
}
}

View File

@ -1,4 +1,4 @@
<?php //[STAMP] 1cb90cb7de157697c2044b397df5dd61
<?php //[STAMP] 142934168f3943147d5338fad03349ce
namespace admin\_generated;
// This class was automatically generated by build task
@ -3062,6 +3062,28 @@ trait FunctionalTesterActions
}
/**
* [!] Method is generated. Documentation taken from corresponding module.
*
*
* @see \tests\codeception\_support\HumHubHelper::assertMailSent()
*/
public function assertMailSent($count = null, $msg = null) {
return $this->getScenario()->runStep(new \Codeception\Step\Action('assertMailSent', func_get_args()));
}
/**
* [!] Method is generated. Documentation taken from corresponding module.
*
*
* @see \tests\codeception\_support\HumHubHelper::assertEqualsLastEmailSubject()
*/
public function assertEqualsLastEmailSubject($subject) {
return $this->getScenario()->runStep(new \Codeception\Step\Action('assertEqualsLastEmailSubject', func_get_args()));
}
/**
* [!] Method is generated. Documentation taken from corresponding module.
*

View File

@ -0,0 +1,47 @@
<?php
use humhub\widgets\Button;
use humhub\widgets\GridView;
use yii\helpers\Html;
use yii\helpers\Url;
?>
<div class="panel-body">
<h4><?= Yii::t('AdminModule.base', 'Pending user registrations'); ?></h4>
<div class="help-block">
<?= Yii::t('AdminModule.views_approval_index', 'The following list contains all pending sign-ups and invites.'); ?>
</div>
<div class="dropdown pull-right">
<button class="btn btn-primary btn-sm " type="button" data-toggle="dropdown"><i class="fa fa-download"></i> <?= Yii::t('base', 'Export')?> <span class="caret"></span></button>
<ul class="dropdown-menu">
<li><?= Button::asLink('csv', Url::current(['export' => '1', 'format' => 'CSV']))->pjax(false)->icon('fa-file-code-o')->sm() ?></li>
<li><?= Button::asLink('xlsx', Url::current(['export' => '1', 'format' => 'XLSX']))->pjax(false)->icon('fa-file-excel-o')->sm() ?></li>
</ul>
</div>
<?=
GridView::widget([
'dataProvider' => $dataProvider,
'filterModel' => $searchModel,
'columns' => ['email',
'originator.username',
'language',
'created_at',
[
'attribute' => 'source',
'filter' => \yii\helpers\Html::activeDropDownList($searchModel, 'source', array_merge(['' => ''], $types)),
'options' => ['width' => '40px'],
'format' => 'raw',
'value' => function($data) use ($types) {
if (isset($types[$data->source])) {
return $types[$data->source];
}
return Html::encode($data->source);
},
],]
]);
?>
</div>

View File

@ -1,4 +1,4 @@
<?php //[STAMP] b0a7d07ce759b891f3ddfe460849f7a6
<?php //[STAMP] 2aea435445801f2b571f53bf6c1759b0
namespace comment\_generated;
// This class was automatically generated by build task

View File

@ -1,4 +1,4 @@
<?php //[STAMP] 3f9180251a575519e16ad271d87f69d5
<?php //[STAMP] 142934168f3943147d5338fad03349ce
namespace comment\_generated;
// This class was automatically generated by build task
@ -3062,6 +3062,28 @@ trait FunctionalTesterActions
}
/**
* [!] Method is generated. Documentation taken from corresponding module.
*
*
* @see \tests\codeception\_support\HumHubHelper::assertMailSent()
*/
public function assertMailSent($count = null, $msg = null) {
return $this->getScenario()->runStep(new \Codeception\Step\Action('assertMailSent', func_get_args()));
}
/**
* [!] Method is generated. Documentation taken from corresponding module.
*
*
* @see \tests\codeception\_support\HumHubHelper::assertEqualsLastEmailSubject()
*/
public function assertEqualsLastEmailSubject($subject) {
return $this->getScenario()->runStep(new \Codeception\Step\Action('assertEqualsLastEmailSubject', func_get_args()));
}
/**
* [!] Method is generated. Documentation taken from corresponding module.
*

View File

@ -1,4 +1,4 @@
<?php //[STAMP] 4620099acaa171d89740a45ce8351624
<?php //[STAMP] 7f1a977c3860981721f99092d50a0edb
namespace comment\_generated;
// This class was automatically generated by build task

View File

@ -8,6 +8,7 @@
namespace humhub\modules\content\components;
use humhub\libs\BasePermission;
use humhub\modules\content\models\Content;
use Yii;
use humhub\libs\ProfileBannerImage;
@ -24,6 +25,8 @@ use humhub\modules\content\models\ContentContainer;
* - getUrl()
*
* @property integer $id
* @property integer $visibility
*
* @since 1.0
* @author Luke
*/
@ -166,37 +169,20 @@ abstract class ContentContainerActiveRecord extends ActiveRecord
return $this->hasOne(ContentContainer::className(), ['pk' => 'id'])
->andOnCondition(['class' => self::className()]);
}
/**
* Returns the permissionManager of this container by default for the current logged in user unsless another $user instance was provided.
*
* @param User $user
* @return ContentContainerPermissionManager
*/
public function getPermissionManager(User $user = null)
{
if($user && !$user->is(Yii::$app->user->getIdentity())) {
$permissionManager = new ContentContainerPermissionManager;
$permissionManager->contentContainer = $this;
$permissionManager->subject = $user;
return $permissionManager;
}
if ($this->permissionManager !== null) {
return $this->permissionManager;
}
$this->permissionManager = new ContentContainerPermissionManager;
$this->permissionManager->contentContainer = $this;
return $this->permissionManager;
}
/**
* Shortcut for getPermisisonManager()->can().
* Checks if the current user has the given Permission on this ContentContainerActiveRecord.
* This is a shortcut for `$this->getPermisisonManager()->can()`.
*
* The following example will check if the current user has MyPermission on the given $contentContainer
*
* ```php
* $contentContainer->can(MyPermisison::class);
* ```
*
* Note: This method is used to verify ContentContainerPermissions and not GroupPermissions.
*
* @param mixed $permission
* @param string|string[]|BasePermission $permission
* @see PermissionManager::can()
* @return boolean
* @since 1.2
@ -206,6 +192,31 @@ abstract class ContentContainerActiveRecord extends ActiveRecord
return $this->getPermissionManager()->can($permission, $params, $allowCaching);
}
/**
* Returns a ContentContainerPermissionManager instance for this ContentContainerActiveRecord as permission object
* and the given user (or current user if not given) as permission subject.
*
* @param User $user
* @return ContentContainerPermissionManager
*/
public function getPermissionManager(User $user = null)
{
if($user && !$user->is(Yii::$app->user->getIdentity())) {
return new ContentContainerPermissionManager([
'contentContainer' => $this,
'subject' => $user
]);
}
if ($this->permissionManager !== null) {
return $this->permissionManager;
}
return $this->permissionManager = new ContentContainerPermissionManager([
'contentContainer' => $this
]);
}
/**
* Returns user group for the given $user or current logged in user if no $user instance was provided.
*
@ -245,4 +256,14 @@ abstract class ContentContainerActiveRecord extends ActiveRecord
return Content::VISIBILITY_PRIVATE;
}
/**
* Checks the current visibility setting of this ContentContainerActiveRecord
* @param $visibility
* @return bool
*/
public function isVisibleFor($visibility)
{
return $this->visibility == $visibility;
}
}

View File

@ -103,7 +103,7 @@ class ContentContainerController extends Controller
throw new HttpException(405, Yii::t('base', 'Module is not enabled on this content container!'));
}
return parent::init();
parent::init();
}
/**
@ -153,6 +153,14 @@ class ContentContainerController extends Controller
$this->checkAccess();
}
/**
* @inheritdoc
*/
public function getAccess()
{
return new ContentContainerControllerAccess(['contentContainer' => $this->contentContainer]);
}
/**
* Checks if current module is enabled on this content container.
*

View File

@ -0,0 +1,208 @@
<?php
/**
* @link https://www.humhub.org/
* @copyright Copyright (c) 2017 HumHub GmbH & Co. KG
* @license https://www.humhub.com/licences
*
*/
/**
* Created by PhpStorm.
* User: buddha
* Date: 26.07.2017
* Time: 18:11
*/
namespace humhub\modules\content\components;
use Yii;
use humhub\components\access\StrictAccess;
use humhub\modules\space\models\Membership;
use humhub\modules\space\models\Space;
use humhub\modules\user\models\User;
/**
* Class ContentContainerControllerAccess
*
* Adds a container permission check to
*
* @package components
*/
class ContentContainerControllerAccess extends StrictAccess
{
const RULE_SPACE_ONLY = 'space';
const RULE_PROFILE_ONLY = 'profile';
const RULE_USER_GROUP_ONLY = 'userGroup';
const RULE_CONTAINER_ACCESS = 'containerAccess';
/**
* @var ContentContainerActiveRecord
*/
public $contentContainer;
private $_membership = false;
/**
* @inheritdoc
*/
public function init()
{
parent::init();
if (!$this->contentContainer && Yii::$app->controller instanceof ContentContainerController) {
$this->contentContainer = Yii::$app->controller->contentContainer;
}
// overwrite default permission validator
$this->registerValidator([ContentContainerPermissionAccess::class, 'contentContainer' => $this->contentContainer]);
$this->registerValidator([self::RULE_SPACE_ONLY => 'validateSpaceOnlyRule']);
$this->registerValidator([self::RULE_PROFILE_ONLY => 'validateProfileOnlyRule']);
$this->registerValidator([UserGroupAccessValidator::class, 'contentContainer' => $this->contentContainer]);
$this->registerValidator([self::RULE_CONTAINER_ACCESS => 'validateContainerAccess']);
}
/**
* Verifies the 'userGroup' rule which requires the given $user to be in the given userGroup setting array.
* @return bool
*/
public function validateUserGroupRule($rule)
{
$userGroup = $this->contentContainer->getUserGroup($this->user);
if(!in_array($userGroup, $rule[self::RULE_USER_GROUP_ONLY])) {
return false;
}
return false;
}
/**
* @return bool verifies 'spaceOnly' rules
*/
public function validateSpaceOnlyRule()
{
return $this->isSpaceController();
}
/**
* @return bool verifies 'userOnly' rules
*/
public function validateProfileOnlyRule()
{
return $this->isProfileController();
}
/**
* @return bool Additional ContentContainerActiveRecord specific checks
*/
public function validateContainerAccess()
{
if($this->isSpaceController()) {
return $this->canAccessSpace();
} else {
return $this->canAccessUser();
}
}
/**
* @return bool Space related access checks
*/
private function canAccessSpace()
{
if($this->contentContainer->isVisibleFor(Space::VISIBILITY_ALL)) {
return true;
}
// don't allow guests since visibility != VISIBILITY_ALL
if($this->isGuest()) {
$this->code = 401;
return false;
}
if($this->user->isSystemAdmin()) {
return true;
}
// @see SpaceModelMembership
$membership = $this->getSpaceMembership();
if ($membership) {
return true;
}
if($this->isVisibleFor(Space::VISIBILITY_NONE)) {
$this->code = 404;
$this->reason = Yii::t('ContentModule.base', 'This space is not visible!');
return false;
}
return true;
}
/**
* @return Membership
*/
private function getSpaceMembership()
{
if(!$this->isSpaceController() || $this->isGuest()) {
return null;
}
if($this->_membership === false) {
$this->_membership = $this->contentContainer->getMembership($this->user->id);
}
return $this->_membership;
}
/**
* @return bool User related access checks
*/
private function canAccessUser()
{
if($this->contentContainer->status == User::STATUS_NEED_APPROVAL) {
$this->reason = Yii::t('UserModule.behaviors_ProfileControllerBehavior', 'This user account is not approved yet!');
$this->code = 404;
return false;
}
if($this->isGuest() && $this->contentContainer->isVisibleFor(User::VISIBILITY_ALL)) {
$this->code = 401;
$this->reason = Yii::t('UserModule.behaviors_ProfileControllerBehavior', 'You need to login to view this user profile!');
return false;
}
//TODO: visibility + friendship check
}
/**
* @inheritdoc
*/
public function isAdmin()
{
if(parent::isAdmin()) {
return true;
}
if ($this->contentContainer instanceof Space) {
return $this->contentContainer->isAdmin($this->user);
} else if($this->contentContainer instanceof Space) {
return $this->user && $this->user->is($this->contentContainer);
}
return false;
}
protected function isSpaceController()
{
return $this->contentContainer instanceof Space;
}
protected function isProfileController()
{
return $this->contentContainer instanceof User;
}
}

View File

@ -0,0 +1,32 @@
<?php
/**
* @link https://www.humhub.org/
* @copyright Copyright (c) 2017 HumHub GmbH & Co. KG
* @license https://www.humhub.com/licences
*
*/
/**
* Created by PhpStorm.
* User: buddha
* Date: 30.07.2017
* Time: 04:15
*/
namespace humhub\modules\content\components;
use humhub\components\access\PermissionAccessValidator;
class ContentContainerPermissionAccess extends PermissionAccessValidator
{
/**
* @var ContentContainerActiveRecord
*/
public $contentContainer;
protected function verifyPermission($permission, $rule)
{
return parent::verifyPermission($permission, $rule) || $this->contentContainer->can($permission, $rule);
}
}

View File

@ -0,0 +1,101 @@
<?php
/**
* @link https://www.humhub.org/
* @copyright Copyright (c) 2017 HumHub GmbH & Co. KG
* @license https://www.humhub.com/licences
*
*/
/**
* Created by PhpStorm.
* User: buddha
* Date: 30.07.2017
* Time: 04:04
*/
namespace humhub\modules\content\components;
use humhub\components\access\ActionAccessValidator;
use humhub\libs\BasePermission;
use humhub\modules\space\models\Space;
use humhub\modules\user\models\User;
use Yii;
use yii\base\InvalidParamException;
class UserGroupAccessValidator extends ActionAccessValidator
{
public $name = 'userGroup';
/**
* @var ContentContainerActiveRecord
*/
public $contentContainer;
public $strict = false;
private $spaceGroupLevel = [
Space::USERGROUP_GUEST,
Space::USERGROUP_USER,
Space::USERGROUP_MEMBER,
Space::USERGROUP_MODERATOR,
Space::USERGROUP_ADMIN,
Space::USERGROUP_OWNER,
];
private $profileGroupLevel = [
User::USERGROUP_GUEST,
User::USERGROUP_USER,
User::USERGROUP_FRIEND,
User::USERGROUP_SELF,
];
protected function validate($rule)
{
if (isset($rule[$this->name]) && !empty($rule[$this->name])) {
$allowedGroups = is_string($rule[$this->name]) ? [$rule[$this->name]] : $rule[$this->name];
$userGroup = $this->contentContainer->getUserGroup($this->access->user);
if(isset($rule['strict']) && $rule['strict'] == true) {
return in_array($userGroup, $allowedGroups);
}
foreach ($allowedGroups as $allowedUserGroup) {
if($this->getUserGroupLevel($userGroup) >= $this->getUserGroupLevel($allowedUserGroup)) {
return true;
}
}
return false;
}
throw new InvalidParamException('Invalid userGroup rule provided for action ' . $this->action);
}
public function getUserGroupLevel($userGroup)
{
$userGroupLevelArr = ($this->contentContainer instanceof Space) ? $this->spaceGroupLevel : $this->profileGroupLevel;
if(!in_array($userGroup, $userGroupLevelArr)) {
return PHP_INT_MAX;
}
return array_search($userGroup, $userGroupLevelArr);
}
protected function extractActions($rule)
{
$actions = null;
if (isset($rule['actions'])) {
$actions = $rule['actions'];
}
return $actions;
}
public function getReason()
{
return Yii::t('error', 'You are not permitted to access this section.');
}
}

View File

@ -1,4 +1,4 @@
<?php //[STAMP] 1cb90cb7de157697c2044b397df5dd61
<?php //[STAMP] 142934168f3943147d5338fad03349ce
namespace content\_generated;
// This class was automatically generated by build task
@ -3062,6 +3062,28 @@ trait FunctionalTesterActions
}
/**
* [!] Method is generated. Documentation taken from corresponding module.
*
*
* @see \tests\codeception\_support\HumHubHelper::assertMailSent()
*/
public function assertMailSent($count = null, $msg = null) {
return $this->getScenario()->runStep(new \Codeception\Step\Action('assertMailSent', func_get_args()));
}
/**
* [!] Method is generated. Documentation taken from corresponding module.
*
*
* @see \tests\codeception\_support\HumHubHelper::assertEqualsLastEmailSubject()
*/
public function assertEqualsLastEmailSubject($subject) {
return $this->getScenario()->runStep(new \Codeception\Step\Action('assertEqualsLastEmailSubject', func_get_args()));
}
/**
* [!] Method is generated. Documentation taken from corresponding module.
*

View File

@ -0,0 +1,430 @@
<?php
/**
* @link https://www.humhub.org/
* @copyright Copyright (c) 2017 HumHub GmbH & Co. KG
* @license https://www.humhub.com/licences
*
*/
/**
* Created by PhpStorm.
* User: buddha
* Date: 26.07.2017
* Time: 16:13
*/
namespace humhub\modules\content\tests\codeception\unit;
use humhub\components\access\ControllerAccess;
use humhub\modules\content\components\ContentContainerControllerAccess;
use humhub\modules\space\models\Space;
use humhub\modules\user\models\User;
use tests\codeception\_support\HumHubDbTestCase;
use Yii;
class ContentContainerAccessControllerTest extends HumHubDbTestCase
{
public function testSimpleGlobalGuestAccess()
{
$space = Space::findOne(1);
$this->allowGuestAccess();
Yii::$app->getModule('user')->settings->set('auth.allowGuestAccess', 1);
// Controller global guestAccess
$accessCheck = new ContentContainerControllerAccess([
'contentContainer' => $space,
'rules' => [
['guestAccess']
],
'action' => 'testAction']);
$this->assertTrue($accessCheck->isGuest());
// GuestAccess given with not matching action setting
$accessCheck->rules = [
['guestAccess' => ['otherTestAction']]
];
$this->assertFalse($accessCheck->run());
// GuestAccess given with matching actoin setting
$accessCheck->rules = [
['guestAccess' => ['otherTestAction', 'testAction']]
];
$this->assertTrue($accessCheck->run());
// AdminOnly setting should overwrite the guestAccess
$accessCheck->rules = [
['guestAccess' => ['otherTestAction', 'testAction']],
[ControllerAccess::RULE_ADMIN_ONLY]
];
$this->assertFalse($accessCheck->run());
// AdminOnly setting should overwrite the guestAccess
$accessCheck->rules = [
['guestAccess' => ['otherTestAction', 'testAction']],
[ControllerAccess::RULE_ADMIN_ONLY => ['testAction']]
];
$this->assertFalse($accessCheck->run());
// AdminOnly setting should overwrite the guestAccess
$accessCheck->rules = [
['guestAccess' => ['otherTestAction', 'testAction']],
[ControllerAccess::RULE_ADMIN_ONLY => ['otherTestAction']]
];
$this->assertTrue($accessCheck->run());
// LoggedInOnly setting should overwrite the guestAccess
$accessCheck->rules = [
[ControllerAccess::RULE_ADMIN_ONLY => ['otherTestAction']],
[ControllerAccess::RULE_LOGGED_IN_ONLY]
];
$this->assertFalse($accessCheck->run());
// LoggedInOnly setting should overwrite the guestAccess
$accessCheck->rules = [
[ControllerAccess::RULE_ADMIN_ONLY => ['otherTestAction']],
[ControllerAccess::RULE_LOGGED_IN_ONLY => 'otherTestAction']
];
$this->assertTrue($accessCheck->run());
// LoggedInOnly setting should overwrite the guestAccess
$accessCheck->rules = [
[ControllerAccess::RULE_ADMIN_ONLY => ['otherTestAction']],
[ControllerAccess::RULE_LOGGED_IN_ONLY => 'testAction'],
['guestAccess' => 'testAction']
];
$this->assertFalse($accessCheck->run());
// By default guests are allowed without further rules
$accessCheck->rules = [];
$this->assertTrue($accessCheck->run());
// Global Permission setting
$accessCheck->rules = [
['permission' => ContentTestPermission1::class]
];
$this->assertFalse($accessCheck->run());
// Action restricted permission setting
$accessCheck->rules = [
['permission' => ContentTestPermission1::class, 'actions' => ['otherTestAction']]
];
$this->assertTrue($accessCheck->run());
// Matching action related permission setting
$accessCheck->rules = [
['permission' => ContentTestPermission1::class, 'actions' => ['otherTestAction', 'testAction']]
];
$this->assertFalse($accessCheck->run());
}
public function testStrictAccess()
{
$space = Space::findOne(1);
// Controller global guestAccess
$accessCheck = new ContentContainerControllerAccess([
'contentContainer' => $space,
'rules' => [],
'action' => 'testAction']);
// Test strict behaviour
$this->allowGuestAccess(false);
$this->assertFalse($accessCheck->run());
}
public function testSpaceOnlyAccess()
{
$this->becomeUser('User1');
$space = Space::findOne(1);
// Controller global guestAccess
$accessCheck = new ContentContainerControllerAccess([
'contentContainer' => $space,
'rules' => [[ContentContainerControllerAccess::RULE_SPACE_ONLY]],
'action' => 'testAction']);
$this->assertTrue($accessCheck->run());
$accessCheck->contentContainer = Yii::$app->user->getIdentity();
$this->assertFalse($accessCheck->run());
// Not related
$accessCheck->rules = [
[ContentContainerControllerAccess::RULE_SPACE_ONLY => ['otherAction']]
];
$this->assertTrue($accessCheck->run());
$accessCheck->rules = [
[ContentContainerControllerAccess::RULE_SPACE_ONLY => ['otherAction', 'testAction']]
];
$this->assertFalse($accessCheck->run());
}
public function testProfileOnlyAccess()
{
$this->becomeUser('User1');
$space = Space::findOne(1);
// Controller global guestAccess
$accessCheck = new ContentContainerControllerAccess([
'contentContainer' => Yii::$app->user->getIdentity(),
'rules' => [[ContentContainerControllerAccess::RULE_PROFILE_ONLY]],
'action' => 'testAction']);
$this->assertTrue($accessCheck->run());
$accessCheck->contentContainer = $space;
$this->assertFalse($accessCheck->run());
// Not related
$accessCheck->rules = [
[ContentContainerControllerAccess::RULE_PROFILE_ONLY => ['otherAction']]
];
$this->assertTrue($accessCheck->run());
$accessCheck->rules = [
[ContentContainerControllerAccess::RULE_PROFILE_ONLY => ['otherAction', 'testAction']]
];
$this->assertFalse($accessCheck->run());
}
public function testUserGroupAccess()
{
$this->allowGuestAccess();
// Guest is not user
$space = Space::findOne(1);
$accessCheck = new ContentContainerControllerAccess([
'contentContainer' => $space,
'action' => 'testAction',
'rules' => [
['userGroup' => [Space::USERGROUP_USER]]
]
]);
$this->assertFalse($accessCheck->run());
// Guest is allowed
$accessCheck = new ContentContainerControllerAccess([
'contentContainer' => $space,
'rules' => [['userGroup' => [Space::USERGROUP_GUEST]]],
'action' => 'testAction']);
$this->assertTrue($accessCheck->run());
// User 1 is not member of Space1
$this->becomeUser('User1');
$accessCheck = new ContentContainerControllerAccess([
'contentContainer' => $space,
'rules' => [['userGroup' => [Space::USERGROUP_MEMBER]]],
'action' => 'testAction']);
$this->assertFalse($accessCheck->run());
// User 1 is member of Space1
$space3 = Space::findOne(3);
$accessCheck = new ContentContainerControllerAccess([
'contentContainer' => $space3,
'rules' => [['userGroup' => [Space::USERGROUP_MEMBER]]],
'action' => 'testAction']);
$this->assertTrue($accessCheck->run());
// Since its leveled member should also be allowed to access user restricted
$accessCheck = new ContentContainerControllerAccess([
'contentContainer' => $space3,
'rules' => [['userGroup' => Space::USERGROUP_USER]],
'action' => 'testAction']);
$this->assertTrue($accessCheck->run());
$accessCheck = new ContentContainerControllerAccess([
'contentContainer' => $space3,
'rules' => [['userGroup' => Space::USERGROUP_MODERATOR]],
'action' => 'testAction']);
$this->assertFalse($accessCheck->run());
// Only non user related group provided
$accessCheck = new ContentContainerControllerAccess([
'contentContainer' => Yii::$app->user->getIdentity(),
'rules' => [['userGroup' => Space::USERGROUP_MODERATOR]],
'action' => 'testAction']);
$this->assertFalse($accessCheck->run());
// Also User related group provided
$accessCheck = new ContentContainerControllerAccess([
'contentContainer' => Yii::$app->user->getIdentity(),
'rules' => [['userGroup' => [Space::USERGROUP_MODERATOR, User::USERGROUP_SELF]]],
'action' => 'testAction']);
$this->assertTrue($accessCheck->run());
$user1 = Yii::$app->user->getIdentity();
$this->becomeUser('User2');
$accessCheck = new ContentContainerControllerAccess([
'contentContainer' => $user1,
'rules' => [['userGroup' => [User::USERGROUP_SELF]]],
'action' => 'testAction']);
$this->assertFalse($accessCheck->run());
$accessCheck = new ContentContainerControllerAccess([
'contentContainer' => $user1,
'rules' => [['userGroup' => [User::USERGROUP_GUEST]]],
'action' => 'testAction']);
$this->assertTrue($accessCheck->run());
}
public function testLoggedInOnlyAccess()
{
$space = Space::findOne(1);
$this->allowGuestAccess();
// Controller global guestAccess
$accessCheck = new ContentContainerControllerAccess([
'contentContainer' => $space,
'rules' => [
[ControllerAccess::RULE_LOGGED_IN_ONLY]
],
'action' => 'testAction']);
$this->assertFalse($accessCheck->run());
$this->becomeUser('User1');
$accessCheck->user = Yii::$app->user->getIdentity();
$this->assertFalse($accessCheck->isGuest());
$this->assertTrue($accessCheck->run());
}
public function testAdminOnly()
{
// Non space member not allowed
$space4 = Space::findOne(4);
$this->becomeUser('User3');
$accessCheck = new ContentContainerControllerAccess([
'contentContainer' => $space4,
'rules' => [[ControllerAccess::RULE_ADMIN_ONLY]],
'action' => 'testAction'
]);
$this->assertFalse($accessCheck->run());
// Member not allowed
$this->becomeUser('User2');
$accessCheck = new ContentContainerControllerAccess([
'contentContainer' => $space4,
'rules' => [[ControllerAccess::RULE_ADMIN_ONLY]],
'action' => 'testAction'
]);
$this->assertFalse($accessCheck->run());
// Space Admin allowed
$this->becomeUser('User1');
$accessCheck = new ContentContainerControllerAccess([
'contentContainer' => $space4,
'rules' => [[ControllerAccess::RULE_ADMIN_ONLY]],
'action' => 'testAction'
]);
$this->assertTrue($accessCheck->run());
// System/Space Admin allowed
$this->becomeUser('Admin');
$accessCheck = new ContentContainerControllerAccess([
'contentContainer' => $space4,
'rules' => [[ControllerAccess::RULE_ADMIN_ONLY]],
'action' => 'testAction'
]);
$this->assertTrue($accessCheck->run());
// System Admin allowed
$space2 = Space::findOne(2);
$accessCheck = new ContentContainerControllerAccess([
'contentContainer' => $space4,
'rules' => [[ControllerAccess::RULE_ADMIN_ONLY]],
'action' => 'testAction'
]);
$this->assertTrue($accessCheck->run());
}
public function testInactiveUser()
{
$space = Space::findOne(1);
$this->becomeUser('DisabledUser');
$accessCheck = new ContentContainerControllerAccess(['contentContainer' => $space,'rules' => [], 'action' => 'testAction']);
$this->assertFalse($accessCheck->run());
$this->becomeUser('UnapprovedUser');
$accessCheck = new ContentContainerControllerAccess(['contentContainer' => $space, 'rules' => [], 'action' => 'testAction']);
$this->assertFalse($accessCheck->run());
}
public function testPermissionRule()
{
$space = Space::findOne(3);
$accessCheck = new ContentContainerControllerAccess([
'contentContainer' => $space,
'rules' => [
['permission' => [ContentTestPermission1::class]]
],
'action' => 'testAction'
]);
# $this->assertFalse($accessCheck->run());
$this->setGroupPermission(2, ContentTestPermission1::class);
$this->becomeUser('User1');
$accessCheck = new ContentContainerControllerAccess([
'contentContainer' => $space,
'rules' => [
['permission' => [ContentTestPermission2::class]]
],
'action' => 'testAction'
]);
# $this->assertFalse($accessCheck->run());
$accessCheck->rules = [
['permission' => [ContentTestPermission1::class, ContentTestPermission2::class]]
];
# $this->assertTrue($accessCheck->run());
$accessCheck->rules = [
['permission' => [ContentTestPermission2::class], 'actions' => 'otherPermission'],
['permission' => [ContentTestPermission1::class, ContentTestPermission2::class]]
];
# $this->assertTrue($accessCheck->run());
$accessCheck->rules = [
['permission' => [ContentTestPermission2::class], 'actions' => 'otherPermission'],
['permission' => ContentTestPermission1::class]
];
#$this->assertTrue($accessCheck->run());
$accessCheck->rules = [
[ControllerAccess::RULE_ADMIN_ONLY],
['permission' => [ContentTestPermission2::class], 'actions' => 'otherPermission'],
['permission' => [ContentTestPermission1::class, ContentTestPermission2::class]]
];
#$this->assertFalse($accessCheck->run());
// Set contentcotnainer permission
$this->setContentContainerPermission($space, Space::USERGROUP_MEMBER, ContentTestPermission2::class);
$accessCheck = new ContentContainerControllerAccess([
'contentContainer' => $space,
'rules' => [
['permission' => [ContentTestPermission2::class]]
],
'action' => 'testAction'
]);
$this->assertTrue($accessCheck->run());
}
}

View File

@ -0,0 +1,27 @@
<?php
/**
* @link https://www.humhub.org/
* @copyright Copyright (c) 2017 HumHub GmbH & Co. KG
* @license https://www.humhub.com/licences
*
*/
/**
* Created by PhpStorm.
* User: buddha
* Date: 27.07.2017
* Time: 00:06
*/
namespace humhub\modules\content\tests\codeception\unit;
use humhub\libs\BasePermission;
class ContentTestPermission1 extends BasePermission
{
public $moduleId = 'content';
public $id = 'content-test-permission2';
}

View File

@ -0,0 +1,27 @@
<?php
/**
* @link https://www.humhub.org/
* @copyright Copyright (c) 2017 HumHub GmbH & Co. KG
* @license https://www.humhub.com/licences
*
*/
/**
* Created by PhpStorm.
* User: buddha
* Date: 27.07.2017
* Time: 00:06
*/
namespace humhub\modules\content\tests\codeception\unit;
use humhub\libs\BasePermission;
class ContentTestPermission2 extends BasePermission
{
public $moduleId = 'content';
public $id = 'content-test-permission';
}

View File

@ -1,4 +1,4 @@
<?php //[STAMP] 1cb90cb7de157697c2044b397df5dd61
<?php //[STAMP] 142934168f3943147d5338fad03349ce
namespace dashboard\_generated;
// This class was automatically generated by build task
@ -3062,6 +3062,28 @@ trait FunctionalTesterActions
}
/**
* [!] Method is generated. Documentation taken from corresponding module.
*
*
* @see \tests\codeception\_support\HumHubHelper::assertMailSent()
*/
public function assertMailSent($count = null, $msg = null) {
return $this->getScenario()->runStep(new \Codeception\Step\Action('assertMailSent', func_get_args()));
}
/**
* [!] Method is generated. Documentation taken from corresponding module.
*
*
* @see \tests\codeception\_support\HumHubHelper::assertEqualsLastEmailSubject()
*/
public function assertEqualsLastEmailSubject($subject) {
return $this->getScenario()->runStep(new \Codeception\Step\Action('assertEqualsLastEmailSubject', func_get_args()));
}
/**
* [!] Method is generated. Documentation taken from corresponding module.
*

View File

@ -1,4 +1,4 @@
<?php //[STAMP] 1cb90cb7de157697c2044b397df5dd61
<?php //[STAMP] 142934168f3943147d5338fad03349ce
namespace friendship\_generated;
// This class was automatically generated by build task
@ -3062,6 +3062,28 @@ trait FunctionalTesterActions
}
/**
* [!] Method is generated. Documentation taken from corresponding module.
*
*
* @see \tests\codeception\_support\HumHubHelper::assertMailSent()
*/
public function assertMailSent($count = null, $msg = null) {
return $this->getScenario()->runStep(new \Codeception\Step\Action('assertMailSent', func_get_args()));
}
/**
* [!] Method is generated. Documentation taken from corresponding module.
*
*
* @see \tests\codeception\_support\HumHubHelper::assertEqualsLastEmailSubject()
*/
public function assertEqualsLastEmailSubject($subject) {
return $this->getScenario()->runStep(new \Codeception\Step\Action('assertEqualsLastEmailSubject', func_get_args()));
}
/**
* [!] Method is generated. Documentation taken from corresponding module.
*

View File

@ -1,4 +1,4 @@
<?php //[STAMP] 1cb90cb7de157697c2044b397df5dd61
<?php //[STAMP] 142934168f3943147d5338fad03349ce
namespace like\_generated;
// This class was automatically generated by build task
@ -3062,6 +3062,28 @@ trait FunctionalTesterActions
}
/**
* [!] Method is generated. Documentation taken from corresponding module.
*
*
* @see \tests\codeception\_support\HumHubHelper::assertMailSent()
*/
public function assertMailSent($count = null, $msg = null) {
return $this->getScenario()->runStep(new \Codeception\Step\Action('assertMailSent', func_get_args()));
}
/**
* [!] Method is generated. Documentation taken from corresponding module.
*
*
* @see \tests\codeception\_support\HumHubHelper::assertEqualsLastEmailSubject()
*/
public function assertEqualsLastEmailSubject($subject) {
return $this->getScenario()->runStep(new \Codeception\Step\Action('assertEqualsLastEmailSubject', func_get_args()));
}
/**
* [!] Method is generated. Documentation taken from corresponding module.
*

View File

@ -1,4 +1,4 @@
<?php //[STAMP] 1cb90cb7de157697c2044b397df5dd61
<?php //[STAMP] 142934168f3943147d5338fad03349ce
namespace notification\_generated;
// This class was automatically generated by build task
@ -3062,6 +3062,28 @@ trait FunctionalTesterActions
}
/**
* [!] Method is generated. Documentation taken from corresponding module.
*
*
* @see \tests\codeception\_support\HumHubHelper::assertMailSent()
*/
public function assertMailSent($count = null, $msg = null) {
return $this->getScenario()->runStep(new \Codeception\Step\Action('assertMailSent', func_get_args()));
}
/**
* [!] Method is generated. Documentation taken from corresponding module.
*
*
* @see \tests\codeception\_support\HumHubHelper::assertEqualsLastEmailSubject()
*/
public function assertEqualsLastEmailSubject($subject) {
return $this->getScenario()->runStep(new \Codeception\Step\Action('assertEqualsLastEmailSubject', func_get_args()));
}
/**
* [!] Method is generated. Documentation taken from corresponding module.
*

View File

@ -1,4 +1,4 @@
<?php //[STAMP] 1cb90cb7de157697c2044b397df5dd61
<?php //[STAMP] 142934168f3943147d5338fad03349ce
namespace post\_generated;
// This class was automatically generated by build task
@ -3062,6 +3062,28 @@ trait FunctionalTesterActions
}
/**
* [!] Method is generated. Documentation taken from corresponding module.
*
*
* @see \tests\codeception\_support\HumHubHelper::assertMailSent()
*/
public function assertMailSent($count = null, $msg = null) {
return $this->getScenario()->runStep(new \Codeception\Step\Action('assertMailSent', func_get_args()));
}
/**
* [!] Method is generated. Documentation taken from corresponding module.
*
*
* @see \tests\codeception\_support\HumHubHelper::assertEqualsLastEmailSubject()
*/
public function assertEqualsLastEmailSubject($subject) {
return $this->getScenario()->runStep(new \Codeception\Step\Action('assertEqualsLastEmailSubject', func_get_args()));
}
/**
* [!] Method is generated. Documentation taken from corresponding module.
*

View File

@ -8,6 +8,7 @@
namespace humhub\modules\space\modules\manage\components;
use humhub\modules\admin\permissions\ManageSpaces;
use Yii;
use yii\web\HttpException;
@ -23,48 +24,13 @@ class Controller extends \humhub\modules\content\components\ContentContainerCont
*/
public $hideSidebar = true;
/**
* Can be overwritten by subclasses to allow non space admins
* @var type
*/
protected $adminOnly = true;
/**
* @inheritdoc
*/
public function behaviors()
{
return [
'acl' => [
'class' => \humhub\components\behaviors\AccessControl::className(),
'adminOnly' => $this->adminOnly,
'rules' => $this->getAccessRules()
]
];
}
protected function getAccessRules() {
return [];
return [
['login'],
['permission' => [
ManageSpaces::class
]]
];
}
/**
* Request only allowed for space admins
*/
public function adminOnly()
{
if (!$this->getSpace()->isAdmin())
throw new HttpException(403, 'Access denied - Space Administrator only!');
}
/**
* Request only allowed for workspace owner
*/
public function ownerOnly()
{
$workspace = $this->getSpace();
if (!$workspace->isSpaceOwner() && !Yii::$app->user->isAdmin())
throw new HttpException(403, 'Access denied - Space Owner only!');
}
}

View File

@ -9,6 +9,9 @@
namespace humhub\modules\space\modules\manage\controllers;
use Yii;
use humhub\modules\space\models\Space;
use humhub\modules\space\modules\manage\models\AdvancedSettingsSpace;
use humhub\modules\space\widgets\Chooser;
use humhub\modules\space\modules\manage\components\Controller;
use humhub\modules\space\modules\manage\models\DeleteForm;
@ -20,6 +23,18 @@ use humhub\modules\space\modules\manage\models\DeleteForm;
class DefaultController extends Controller
{
/**
* @inheritdoc
*/
public function getAccessRules()
{
$result = parent::getAccessRules();
$result[] = [
'userGroup' => [Space::USERGROUP_OWNER], 'actions' => ['archive', 'unarchive', 'delete']
];
return $result;
}
/**
* General space settings
*/
@ -37,7 +52,7 @@ class DefaultController extends Controller
public function actionAdvanced()
{
$space = \humhub\modules\space\modules\manage\models\AdvancedSettingsSpace::findOne(['id' => $this->contentContainer->id]);
$space = AdvancedSettingsSpace::findOne(['id' => $this->contentContainer->id]);
$space->scenario = 'edit';
$space->indexUrl = Yii::$app->getModule('space')->settings->space()->get('indexUrl');
$space->indexGuestUrl = Yii::$app->getModule('space')->settings->space()->get('indexGuestUrl');
@ -61,7 +76,6 @@ class DefaultController extends Controller
*/
public function actionArchive()
{
$this->ownerOnly();
$space = $this->getSpace();
$space->archive();
@ -69,7 +83,7 @@ class DefaultController extends Controller
Yii::$app->response->format = 'json';
return [
'success' => true,
'space' => \humhub\modules\space\widgets\Chooser::getSpaceResult($space, true, ['isMember' => true])
'space' => Chooser::getSpaceResult($space, true, ['isMember' => true])
];
}
@ -81,7 +95,6 @@ class DefaultController extends Controller
*/
public function actionUnarchive()
{
$this->ownerOnly();
$space = $this->getSpace();
$space->unarchive();
@ -89,7 +102,7 @@ class DefaultController extends Controller
Yii::$app->response->format = 'json';
return [
'success' => true,
'space' => \humhub\modules\space\widgets\Chooser::getSpaceResult($space, true, ['isMember' => true])
'space' => Chooser::getSpaceResult($space, true, ['isMember' => true])
];
}
@ -101,14 +114,13 @@ class DefaultController extends Controller
*/
public function actionDelete()
{
$this->ownerOnly();
$model = new DeleteForm();
if ($model->load(Yii::$app->request->post()) && $model->validate()) {
$this->getSpace()->delete();
return $this->goHome();
}
return $this->render('delete', array('model' => $model, 'space' => $this->getSpace()));
return $this->render('delete', ['model' => $model, 'space' => $this->getSpace()]);
}
}

View File

@ -8,6 +8,7 @@
namespace humhub\modules\space\modules\manage\controllers;
use humhub\modules\space\models\Space;
use Yii;
use yii\web\HttpException;
use humhub\modules\space\modules\manage\components\Controller;
@ -23,6 +24,17 @@ use humhub\modules\space\modules\manage\models\ChangeOwnerForm;
*/
class MemberController extends Controller
{
/**
* @inheritdoc
*/
public function getAccessRules()
{
$result = parent::getAccessRules();
$result[] = [
'userGroup' => [Space::USERGROUP_OWNER], 'actions' => ['change-owner']
];
return $result;
}
/**
* Members Administration Action
@ -157,7 +169,6 @@ class MemberController extends Controller
*/
public function actionChangeOwner()
{
$this->ownerOnly();
$space = $this->getSpace();
$model = new ChangeOwnerForm([

View File

@ -1,4 +1,4 @@
<?php //[STAMP] 1cb90cb7de157697c2044b397df5dd61
<?php //[STAMP] 142934168f3943147d5338fad03349ce
namespace space\_generated;
// This class was automatically generated by build task
@ -3062,6 +3062,28 @@ trait FunctionalTesterActions
}
/**
* [!] Method is generated. Documentation taken from corresponding module.
*
*
* @see \tests\codeception\_support\HumHubHelper::assertMailSent()
*/
public function assertMailSent($count = null, $msg = null) {
return $this->getScenario()->runStep(new \Codeception\Step\Action('assertMailSent', func_get_args()));
}
/**
* [!] Method is generated. Documentation taken from corresponding module.
*
*
* @see \tests\codeception\_support\HumHubHelper::assertEqualsLastEmailSubject()
*/
public function assertEqualsLastEmailSubject($subject) {
return $this->getScenario()->runStep(new \Codeception\Step\Action('assertEqualsLastEmailSubject', func_get_args()));
}
/**
* [!] Method is generated. Documentation taken from corresponding module.
*

View File

@ -1,4 +1,4 @@
<?php //[STAMP] 1cb90cb7de157697c2044b397df5dd61
<?php //[STAMP] 142934168f3943147d5338fad03349ce
namespace stream\_generated;
// This class was automatically generated by build task
@ -3062,6 +3062,28 @@ trait FunctionalTesterActions
}
/**
* [!] Method is generated. Documentation taken from corresponding module.
*
*
* @see \tests\codeception\_support\HumHubHelper::assertMailSent()
*/
public function assertMailSent($count = null, $msg = null) {
return $this->getScenario()->runStep(new \Codeception\Step\Action('assertMailSent', func_get_args()));
}
/**
* [!] Method is generated. Documentation taken from corresponding module.
*
*
* @see \tests\codeception\_support\HumHubHelper::assertEqualsLastEmailSubject()
*/
public function assertEqualsLastEmailSubject($subject) {
return $this->getScenario()->runStep(new \Codeception\Step\Action('assertEqualsLastEmailSubject', func_get_args()));
}
/**
* [!] Method is generated. Documentation taken from corresponding module.
*

View File

@ -41,10 +41,10 @@ class PermissionManager extends \yii\base\Component
/**
* Verifies a given $permission or $permission array for a permission subject.
*
* If $params['all'] is set to true and a $permission array is given all given permissions
* have to be verified successfully otherwise (default) only one permission test has to pass.
* If $params['strict'] is set to true and a $permission array is given all given permissions
* have to be granted otherwise (default) only one permission test has to pass.
*
* @param array|BasePermission|mixed $permission
* @param string|string[]|BasePermission $permission
* @param array $params
* @param boolean $allowCaching
* @return boolean
@ -53,7 +53,8 @@ class PermissionManager extends \yii\base\Component
{
if (is_array($permission)) {
$verifyAll = isset($params['all']) ? $params['all'] : false;
// compatibility for old 'all' param
$verifyAll = $this->isVerifyAll($params);
foreach ($permission as $current) {
$can = $this->can($current, $params, $allowCaching);
if ($can && !$verifyAll) {
@ -78,6 +79,20 @@ class PermissionManager extends \yii\base\Component
}
}
private function isVerifyAll($params = [])
{
if(isset($params['strict'])) {
return $params['strict'];
}
//deprecated
if(isset($params['all'])) {
return $params['all'];
}
return false;
}
/**
* Verifies a single permission for a given permission subject.
*
@ -118,11 +133,12 @@ class PermissionManager extends \yii\base\Component
* Sets the state for a given groupId.
*
* @param string $groupId
* @param BasePermission $permission
* @param string|BasePermission $permission either permission class or instance
* @param string $state
*/
public function setGroupState($groupId, BasePermission $permission, $state)
public function setGroupState($groupId, $permission, $state)
{
$permission = (is_string($permission)) ? Yii::createObject($permission) : $permission;
$record = $this->getGroupStateRecord($groupId, $permission);
// No need to store default state

View File

@ -63,22 +63,23 @@ class User extends \yii\web\User
}
/**
* Shortcut for getPermisisonManager()->can().
*
* Note: This method is used to verify global GroupPermissions and not ContentContainerPermissions.
*
* @param mixed $permission
* Verifies global GroupPermissions of this User component.
*
* The following example checks if this User is granted the GroupPermission
*
* ```php
* if(Yii::$app->user->can(MyGroupPermission::class) {
* // ...
* }
* ```
*
* @param string|string[]|BasePermission $permission
* @see PermissionManager::can()
* @return boolean
* @since 1.2
*/
public function can($permission, $params = [], $allowCaching = true)
{
// Compatibility with Yii2 base permission system.
if (is_string($permission)) {
return parent::can($permission, $params, $allowCaching);
}
return $this->getPermissionManager()->can($permission, $params, $allowCaching);
}

View File

@ -63,13 +63,13 @@ class AccountController extends BaseAccountController
// Get Form Definition
$definition = $user->profile->getFormDefinition();
$definition['buttons'] = array(
'save' => array(
$definition['buttons'] = [
'save' => [
'type' => 'submit',
'label' => Yii::t('UserModule.controllers_AccountController', 'Save profile'),
'class' => 'btn btn-primary'
),
);
],
];
$form = new \humhub\compat\HForm($definition, $user->profile);
$form->showErrorSummary = true;
@ -82,7 +82,7 @@ class AccountController extends BaseAccountController
return $this->redirect(['edit']);
}
return $this->render('edit', array('hForm' => $form));
return $this->render('edit', ['hForm' => $form]);
}
/**

View File

@ -1,7 +1,7 @@
<?php
namespace humhub\modules\user\models;
use humhub\libs\BasePermission;
/**
@ -22,6 +22,21 @@ class GroupPermission extends \yii\db\ActiveRecord
{
return 'group_permission';
}
public static function instance(BasePermission $basePermission, $groupId = null, $state = null) {
$instance = new static([
'permission_id' => $basePermission->getId(),
'module_id' => $basePermission->getModuleId(),
'class' => $basePermission->className()
]);
if(!empty($groupId)) {
$instance->group_id = ($groupId instanceof Group) ? $groupId->id : $groupId;
}
$instance->state = $state;
return $instance;
}
public function init()
{

View File

@ -424,15 +424,6 @@ class User extends ContentContainerActiveRecord implements \yii\web\IdentityInte
$userInvite->delete();
}
// Auto Assign User to the Group Space
/* $group = Group::findOne(['id' => $this->group_id]);
if ($group != null && $group->space_id != "") {
$space = \humhub\modules\space\models\Space::findOne(['id' => $group->space_id]);
if ($space !== null) {
$space->addMember($this->id);
}
} */
// Auto Add User to the default spaces
foreach (\humhub\modules\space\models\Space::findAll(['auto_add_new_members' => 1]) as $space) {
$space->addMember($this->id);
@ -486,6 +477,9 @@ class User extends ContentContainerActiveRecord implements \yii\web\IdentityInte
*/
public function is(User $user)
{
if(!$user) {
return false;
}
return $user->id === $this->id;
}
@ -662,6 +656,4 @@ class User extends ContentContainerActiveRecord implements \yii\web\IdentityInte
// TODO: Implement same logic as for Spaces
return Content::VISIBILITY_PUBLIC;
}
}

View File

@ -26,22 +26,22 @@ class CountrySelect extends Select
*
* @return array Form Definition
*/
public function getFormDefinition($definition = array())
public function getFormDefinition($definition = [])
{
return parent::getFormDefinition(array(
get_class($this) => array(
return parent::getFormDefinition([
get_class($this) => [
'type' => 'form',
'title' => Yii::t('UserModule.models_ProfileFieldTypeSelect', 'Supported ISO3166 country codes'),
'elements' => array(
'options' => array(
'elements' => [
'options' => [
'type' => 'textarea',
'label' => Yii::t('UserModule.models_ProfileFieldTypeSelect', 'Possible values'),
'class' => 'form-control',
'hint' => Yii::t('UserModule.models_ProfileFieldTypeSelect', 'Comma separated country codes, e.g. DE,EN,AU')
)
)
)
));
]
]
]
]);
}
/**
@ -103,7 +103,7 @@ class CountrySelect extends Select
public function getFieldFormDefinition()
{
$definition = parent::getFieldFormDefinition();
$definition[$this->profileField->internal_name]['htmlOptions'] = ['data-ui-select2' => true];
$definition[$this->profileField->internal_name]['htmlOptions'] = ['data-ui-select2' => true, 'style' => 'width:100%'];
return $definition;
}

View File

@ -1,4 +1,4 @@
<?php //[STAMP] 05d523175a26a8580a6b1e51bc071c3c
<?php //[STAMP] 142934168f3943147d5338fad03349ce
namespace user\_generated;
// This class was automatically generated by build task

View File

@ -14,12 +14,10 @@ modules:
- WebDriver
- tests\codeception\_support\WebHelper
- tests\codeception\_support\DynamicFixtureHelper
- Yii2
config:
WebDriver:
url: http://localhost:8080/
browser: firefox
window_size: 1024x768
restart: true
lang: en
Yii
lang: en

View File

@ -0,0 +1,28 @@
<?php
/**
* @link https://www.humhub.org/
* @copyright Copyright (c) 2017 HumHub GmbH & Co. KG
* @license https://www.humhub.com/licences
*
*/
namespace humhub\modules\user\tests\codeception\fixtures;
use yii\test\ActiveFixture;
class UserFullFixture extends ActiveFixture
{
public $tableName = 'user_mentioning';
public $depends = [
UserFixture::class,
'humhub\modules\user\tests\codeception\fixtures\UserProfileFixture',
'humhub\modules\content\tests\codeception\fixtures\ContentContainerFixture',
'humhub\modules\user\tests\codeception\fixtures\UserPasswordFixture',
'humhub\modules\user\tests\codeception\fixtures\UserFollowFixture',
'humhub\modules\user\tests\codeception\fixtures\UserModuleFixture',
'humhub\modules\user\tests\codeception\fixtures\GroupFixture'
];
}

View File

@ -0,0 +1,42 @@
<?php
/**
* @link https://www.humhub.org/
* @copyright Copyright (c) 2017 HumHub GmbH & Co. KG
* @license https://www.humhub.com/licences
*
*/
/**
* Created by PhpStorm.
* User: buddha
* Date: 01.08.2017
* Time: 20:22
*/
namespace humhub\modules\user\tests\codeception\functional;
use tests\codeception\_pages\LoginPage;
use Yii;
use user\FunctionalTester;
class RegistrationCest
{
public function testRegister(FunctionalTester $I)
{
$auth = Yii::$app->getModule('user')->settings->set('auth.anonymousRegistration', 1);
LoginPage::openBy($I);
$I->see('Sign up');
$I->fillField('#register-email', 'wronEmail');
$I->click('.btn-primary', '#invite-form');
$I->see('Email is not a valid email address.');
$I->fillField('#register-email', 'mytestmail@test.de');
$I->click('.btn-primary', '#invite-form');
$I->see('Registration successful!');
$I->assertMailSent(1);
$I->assertEqualsLastEmailSubject('Welcome to HumHub Test');
}
}

View File

@ -0,0 +1,46 @@
<?php
/**
* @link https://www.humhub.org/
* @copyright Copyright (c) 2017 HumHub GmbH & Co. KG
* @license https://www.humhub.com/licences
*
*/
namespace user\functional;
use humhub\modules\user\models\User;
use user\FunctionalTester;
class UserAccessCest
{
public function testDisabledUserAccess(FunctionalTester $I)
{
$I->wantTo('ensure that disabled users have no access');
$I->amUser2();
$I->amGoingTo('to deactivate the current user');
$user = User::findOne(3);
$user->status = User::STATUS_DISABLED;
$user->save();
$I->amOnPage(['/dashboard/dashboard']);
$I->see('Please sign in');
}
public function testNeedApprovalUserAccess(FunctionalTester $I)
{
$I->wantTo('ensure that see admin information permission works');
$I->amUser2();
$I->amGoingTo('to deactivate the current user');
$user = User::findOne(3);
$user->status = User::STATUS_NEED_APPROVAL;
$user->save();
$I->amOnPage(['/dashboard/dashboard']);
$I->see('Please sign in');
}
}

View File

@ -1,9 +1,13 @@
<?php
use yii\bootstrap\ActiveForm;
?>
<?php $this->beginContent('@user/views/account/_userProfileLayout.php') ?>
<div class="help-block">
<?php echo Yii::t('UserModule.views_account_edit', 'Here you can edit your general profile data, which is visible in the about page of your profile.'); ?>
<?= Yii::t('UserModule.views_account_edit', 'Here you can edit your general profile data, which is visible in the about page of your profile.'); ?>
</div>
<?php $form = \yii\bootstrap\ActiveForm::begin(['enableClientValidation' => false, 'options' => ['data-ui-widget' => 'ui.form.TabbedForm', 'data-ui-init' => '']]); ?>
<?php echo $hForm->render($form); ?>
<?php \yii\bootstrap\ActiveForm::end(); ?>
<?php $form = ActiveForm::begin(['enableClientValidation' => false, 'options' => ['data-ui-widget' => 'ui.form.TabbedForm', 'data-ui-init' => '', 'style' => 'display:none']]); ?>
<?= $hForm->render($form); ?>
<?php ActiveForm::end(); ?>
<?php $this->endContent(); ?>

View File

@ -0,0 +1,81 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace tests\codeception\_support;
use Yii;
use yii\base\Component;
use yii\base\InvalidConfigException;
/**
* BasePage is the base class for page classes that represent Web pages to be tested.
*
* @property string $url The URL to this page. This property is read-only.
*
* @author Mark Jebri <mark.github@yandex.ru>
* @since 2.0
*/
abstract class BasePage extends Component
{
/**
* @var string|array the route (controller ID and action ID, e.g. `site/about`) to this page.
* Use array to represent a route with GET parameters. The first element of the array represents
* the route and the rest of the name-value pairs are treated as GET parameters, e.g. `array('site/page', 'name' => 'about')`.
*/
public $route;
/**
* @var \Codeception\Actor the testing guy object
*/
protected $actor;
/**
* Constructor.
*
* @param \Codeception\Actor $I the testing guy object
*/
public function __construct($I)
{
$this->actor = $I;
}
/**
* Returns the URL to this page.
* The URL will be returned by calling the URL manager of the application
* with [[route]] and the provided parameters.
* @param array $params the GET parameters for creating the URL
* @return string the URL to this page
* @throws InvalidConfigException if [[route]] is not set or invalid
*/
public function getUrl($params = [])
{
if (is_string($this->route)) {
$params[0] = $this->route;
return Yii::$app->getUrlManager()->createUrl($params);
} elseif (is_array($this->route) && isset($this->route[0])) {
return Yii::$app->getUrlManager()->createUrl(array_merge($this->route, $params));
} else {
throw new InvalidConfigException('The "route" property must be set.');
}
}
/**
* Creates a page instance and sets the test guy to use [[url]].
* @param \Codeception\Actor $I the test guy instance
* @param array $params the GET parameters to be used to generate [[url]]
* @return static the page instance
*/
public static function openBy($I, $params = [])
{
$page = new static($I);
$I->amOnPage($page->getUrl($params));
return $page;
}
}

View File

@ -47,6 +47,7 @@ class HumHubDbTestCase extends Test
Yii::setAlias('@webroot', $webRoot);
$this->initModules();
$this->reloadSettings();
$this->deleteMails();
}
protected function tearDown()
@ -66,6 +67,17 @@ class HumHubDbTestCase extends Test
}
}
protected function deleteMails()
{
$path = Yii::getAlias('@runtime/mail');
$files = glob($path . '/*'); // get all file names
foreach ($files as $file) { // iterate files
if (is_file($file)) {
unlink($file); // delete file
}
}
}
/**
* Initializes modules defined in @tests/codeception/config/test.config.php
* Note the config key in test.config.php is modules and not humhubModules!

View File

@ -0,0 +1,32 @@
<?php
/**
* @link https://www.humhub.org/
* @copyright Copyright (c) 2017 HumHub GmbH & Co. KG
* @license https://www.humhub.com/licences
*
*/
namespace tests\codeception\_support;
use Codeception\Events;
/**
* Created by PhpStorm.
* User: buddha
* Date: 31.07.2017
* Time: 23:22
*/
class HumHubExtension extends \Codeception\Extension
{
public static $events = [
Events::MODULE_INIT => 'moduleInit',
#Events::STEP_BEFORE => 'beforeStep',
#Events::TEST_FAIL => 'testFailed',
#Events::RESULT_PRINT_AFTER => 'print',
];
public function moduleInit($test) {
$GLOBALS['env'] = $this->options['env'];
}
}

View File

@ -1,4 +1,4 @@
<?php //[STAMP] ffd625c70933a7c0926fa73ac6513fde
<?php //[STAMP] b8162d517b4257b92313df71eac4c547
namespace _generated;
// This class was automatically generated by build task
@ -3061,6 +3061,28 @@ trait FunctionalTesterActions
}
/**
* [!] Method is generated. Documentation taken from corresponding module.
*
*
* @see \tests\codeception\_support\HumHubHelper::assertMailSent()
*/
public function assertMailSent($count = null, $msg = null) {
return $this->getScenario()->runStep(new \Codeception\Step\Action('assertMailSent', func_get_args()));
}
/**
* [!] Method is generated. Documentation taken from corresponding module.
*
*
* @see \tests\codeception\_support\HumHubHelper::assertEqualsLastEmailSubject()
*/
public function assertEqualsLastEmailSubject($subject) {
return $this->getScenario()->runStep(new \Codeception\Step\Action('assertEqualsLastEmailSubject', func_get_args()));
}
/**
* [!] Method is generated. Documentation taken from corresponding module.
*

View File

@ -0,0 +1,27 @@
<?php
/**
* @link https://www.humhub.org/
* @copyright Copyright (c) 2017 HumHub GmbH & Co. KG
* @license https://www.humhub.com/licences
*
*/
/**
* Created by PhpStorm.
* User: buddha
* Date: 27.07.2017
* Time: 00:06
*/
namespace humhub\tests\codeception\unit\components\access;
use humhub\libs\BasePermission;
class AccessTestPermission1 extends BasePermission
{
public $moduleId = 'content';
public $id = 'content-test-permission2';
}

View File

@ -0,0 +1,27 @@
<?php
/**
* @link https://www.humhub.org/
* @copyright Copyright (c) 2017 HumHub GmbH & Co. KG
* @license https://www.humhub.com/licences
*
*/
/**
* Created by PhpStorm.
* User: buddha
* Date: 27.07.2017
* Time: 00:06
*/
namespace humhub\tests\codeception\unit\components\access;
use humhub\libs\BasePermission;
class AccessTestPermission2 extends BasePermission
{
public $moduleId = 'test';
public $id = 'content-test-permission';
}

View File

@ -0,0 +1,452 @@
<?php
/**
* @link https://www.humhub.org/
* @copyright Copyright (c) 2017 HumHub GmbH & Co. KG
* @license https://www.humhub.com/licences
*
*/
/**
* Created by PhpStorm.
* User: buddha
* Date: 27.07.2017
* Time: 13:27
*/
namespace humhub\tests\codeception\unit\components\access;
use humhub\commands\TestController;
use humhub\components\access\AccessValidator;
use humhub\components\access\ControllerAccess;
use humhub\components\access\StrictAccess;
use tests\codeception\_support\HumHubDbTestCase;
use Yii;
class ControllerAccessTest extends HumHubDbTestCase
{
public $fixtureConfig = ['default'];
public function testLoggedInOnlyValidator()
{
// Guest not allowed for global loggedInOnly rule
$controllerAccess = new ControllerAccess(['action' => 'testAction', 'rules' => [
[ControllerAccess::RULE_LOGGED_IN_ONLY]
]]);
$this->assertFalse($controllerAccess->run());
$this->assertEquals('Login required for this section.', $controllerAccess->reason);
// Guest not allowed for not action related loggedInOnly rule
$controllerAccess = new ControllerAccess(['action' => 'testAction', 'rules' => [
[ControllerAccess::RULE_LOGGED_IN_ONLY => ['testAction2']]
]]);
$this->assertTrue($controllerAccess->run());
// Guest not allowed for action related loggedInOnly rule
$controllerAccess = new ControllerAccess(['action' => 'testAction', 'rules' => [
[ControllerAccess::RULE_LOGGED_IN_ONLY => ['testAction', 'testAction2']]
]]);
$this->assertFalse($controllerAccess->run());
$this->assertEquals('Login required for this section.', $controllerAccess->reason);
// User allowed for global loggedInOnly rule
$this->becomeUser('User1');
$controllerAccess = new ControllerAccess(['action' => 'testAction', 'rules' => [
[ControllerAccess::RULE_LOGGED_IN_ONLY]
]]);
$this->assertTrue($controllerAccess->run());
}
public function testAdminOnlyValidator()
{
// Guest
$controllerAccess = new ControllerAccess(['action' => 'testAction', 'rules' => [
[ControllerAccess::RULE_ADMIN_ONLY]
]]);
$this->assertFalse($controllerAccess->run());
$this->assertEquals('You need admin permissions to access this section.', $controllerAccess->reason);
// User
$this->becomeUser('User1');
$controllerAccess = new ControllerAccess(['action' => 'testAction', 'rules' => [
[ControllerAccess::RULE_ADMIN_ONLY]
]]);
$this->assertFalse($controllerAccess->run());
$this->assertEquals('You need admin permissions to access this section.', $controllerAccess->reason);
// Admin
$this->becomeUser('Admin');
$controllerAccess = new ControllerAccess(['action' => 'testAction', 'rules' => [
[ControllerAccess::RULE_ADMIN_ONLY]
]]);
$this->assertTrue($controllerAccess->run());
}
public function testStrictModeValidator()
{
// Guest not allowed for global strict rule
$this->allowGuestAccess(false);
$controllerAccess = new ControllerAccess(['action' => 'testAction', 'rules' => [
[ControllerAccess::RULE_STRICT]
]]);
$this->assertFalse($controllerAccess->run());
$this->assertEquals('Guest mode not active, please login first.', $controllerAccess->reason);
// Guest allowed for global strict rule if guest mode active
$this->allowGuestAccess(true);
$controllerAccess = new ControllerAccess(['action' => 'testAction', 'rules' => [
[ControllerAccess::RULE_STRICT]
]]);
$this->assertTrue($controllerAccess->run());
// User allowed for global strict rule if guest mode not active
$this->allowGuestAccess(false);
$this->becomeUser('User1');
$controllerAccess = new ControllerAccess(['action' => 'testAction', 'rules' => [
[ControllerAccess::RULE_STRICT]
]]);
$this->assertTrue($controllerAccess->run());
}
public function testInactiveUserValidator()
{
// Guests should not be affected
$controllerAccess = new ControllerAccess(['action' => 'testAction', 'rules' => [
[ControllerAccess::RULE_DISABLED_USER],
[ControllerAccess::RULE_UNAPPROVED_USER]
]]);
$this->assertTrue($controllerAccess->run());
// Active user should not be affected
$this->becomeUser('User1');
$controllerAccess = new ControllerAccess(['action' => 'testAction', 'rules' => [
[ControllerAccess::RULE_DISABLED_USER],
[ControllerAccess::RULE_UNAPPROVED_USER]
]]);
$this->assertTrue($controllerAccess->run());
// Disabled user should not be allowed
$this->becomeUser('DisabledUser');
$controllerAccess = new ControllerAccess(['action' => 'testAction', 'rules' => [
[ControllerAccess::RULE_DISABLED_USER],
[ControllerAccess::RULE_UNAPPROVED_USER]
]]);
$this->assertFalse($controllerAccess->run());
$this->assertEquals(401, $controllerAccess->code );
$this->assertEquals('Your user account is inactive, please login with an active account or contact a network administrator.', $controllerAccess->reason);
// UnnapprovedUser
$this->becomeUser('UnapprovedUser');
$controllerAccess = new ControllerAccess(['action' => 'testAction', 'rules' => [
[ControllerAccess::RULE_DISABLED_USER],
[ControllerAccess::RULE_UNAPPROVED_USER]
]]);
$this->assertFalse($controllerAccess->run());
$this->assertEquals(401, $controllerAccess->code );
$this->assertEquals('Your user account has not been approved yet, please try again later or contact a network administrator.', $controllerAccess->reason);
}
public function testGuestUserValidator()
{
$this->allowGuestAccess();
// If no guest restriction is given the validation should pass
$controllerAccess = new ControllerAccess(['action' => 'testAction', 'rules' => []]);
$this->assertTrue($controllerAccess->run());
// Set two guestaccess rules
$controllerAccess = new ControllerAccess(['action' => 'testAction', 'rules' => [
['guestAccess' => ['otherAction']],
['guestAccess' => ['testAction']],
]]);
#$this->assertTrue($controllerAccess->run());
// If global guest restriction is given allow for all actions
$controllerAccess = new ControllerAccess(['action' => 'testAction', 'rules' => [
['guestAccess']
]]);
#$this->assertTrue($controllerAccess->run());
// Non action related guestAccess rule should fail
$controllerAccess = new ControllerAccess(['action' => 'testAction', 'rules' => [
['guestAccess' => ['testAction2']]
]]);
$this->assertFalse($controllerAccess->run());
// Action related guestAccess rule should succeed
$controllerAccess = new ControllerAccess(['action' => 'testAction', 'rules' => [
['guestAccess' => ['testAction']]
]]);
$this->assertTrue($controllerAccess->run());
// LoggedIn users should not be affected
$this->becomeUser('User1');
$controllerAccess = new ControllerAccess(['action' => 'testAction', 'rules' => [
['guestAccess' => ['testAction2']]
]]);
$this->assertTrue($controllerAccess->run());
}
public function testPermissionRuleValidator()
{
// Guest has no permission
$accessCheck = new ControllerAccess([
'rules' => [
['permissions' => [AccessTestPermission1::class]]
],
'action' => 'testAction'
]);
$this->assertFalse($accessCheck->run());
// Add Permission1 to User Group but validate against Permission2
$this->setGroupPermission(2, AccessTestPermission1::class);
$this->becomeUser('User1');
$accessCheck = new ControllerAccess([
'rules' => [
['permissions' => [AccessTestPermission2::class]]
],
'action' => 'testAction'
]);
$this->assertFalse($accessCheck->run());
// Permission2 included
$accessCheck->rules = [
['permissions' => [AccessTestPermission1::class, AccessTestPermission2::class]]
];
$this->assertTrue($accessCheck->run());
// In strict mode both permission have to be granted
$accessCheck->rules = [
['permissions' => [AccessTestPermission1::class, AccessTestPermission2::class], 'strict' => true]
];
$this->assertFalse($accessCheck->run());
// Check two permission rules one non action related and one global valid one. The non action related should be ignored
$accessCheck->rules = [
['permissions' => [AccessTestPermission2::class], 'actions' => 'otherPermission'],
['permissions' => [AccessTestPermission1::class, AccessTestPermission2::class]]
];
$this->assertTrue($accessCheck->run());
// Check non strict behaviour of permissoin rule with one not allowed global rule which is overwritten by action related
// This check passes, since only one of them has to pass.
$accessCheck->rules = [
['permissions' => [AccessTestPermission2::class]],
['permissions' => [AccessTestPermission1::class], 'actions' => 'testAction']
];
$this->assertTrue($accessCheck->run());
// Check string permission definition
$accessCheck->rules = [
['permissions' => AccessTestPermission1::class]
];
$this->assertTrue($accessCheck->run());
// Check permission rule in combination with adminOnly
$accessCheck->rules = [
[ControllerAccess::RULE_ADMIN_ONLY],
['permissions' => [AccessTestPermission2::class], 'actions' => 'otherPermission'],
['permissions' => [AccessTestPermission1::class, AccessTestPermission2::class]]
];
$this->assertFalse($accessCheck->run());
}
public function testGuestRunValidation()
{
$this->allowGuestAccess();
// AdminOnly overwrites guestAccess
$accessCheck = new ControllerAccess([
'rules' => [
[ControllerAccess::RULE_ADMIN_ONLY],
['guestAccess' => ['testAction']]
],
'action' => 'testAction'
]);
$this->assertFalse($accessCheck->run());
// AdminOnly setting should overwrite the guestAccess
$accessCheck->rules = [
['guestAccess' => ['otherTestAction', 'testAction']],
[ControllerAccess::RULE_ADMIN_ONLY => ['testAction']]
];
$this->assertFalse($accessCheck->run());
// AdminOnly setting should overwrite the guestAccess
$accessCheck->rules = [
['guestAccess' => ['testAction']],
[ControllerAccess::RULE_ADMIN_ONLY => ['otherTestAction']]
];
$this->assertTrue($accessCheck->run());
// LoggedInOnly setting should overwrite the guestAccess
$accessCheck->rules = [
['guestAccess'],
[ControllerAccess::RULE_LOGGED_IN_ONLY]
];
$this->assertFalse($accessCheck->run());
// Non action related adminOnly and loggedInOnly
$accessCheck->rules = [
[ControllerAccess::RULE_ADMIN_ONLY => ['otherTestAction']],
[ControllerAccess::RULE_LOGGED_IN_ONLY => 'otherTestAction']
];
$this->assertTrue($accessCheck->run());
// LoggedInOnly overwrites guest access
$accessCheck->rules = [
[ControllerAccess::RULE_ADMIN_ONLY => ['otherTestAction']],
[ControllerAccess::RULE_LOGGED_IN_ONLY => 'testAction'],
['guestAccess' => 'testAction']
];
$this->assertFalse($accessCheck->run());
// Global permission rule
$accessCheck->rules = [
['permissions' => [AccessTestPermission1::class]]
];
$this->assertFalse($accessCheck->run());
// Non action related permission rule
$accessCheck->rules = [
['permissions' => AccessTestPermission1::class, 'actions' => ['otherTestAction']]
];
$this->assertTrue($accessCheck->run());
// Matching action related permission setting
$accessCheck->rules = [
['permissions' => [AccessTestPermission1::class], 'actions' => ['otherTestAction', 'testAction']]
];
$this->assertFalse($accessCheck->run());
$this->allowGuestAccess(false);
$accessCheck->rules = [['strict']];
$this->assertFalse($accessCheck->run());
}
public function testFixedRuleValidation()
{
// DsiabledUser and Unapproved are fixed rules and should always be validated
$this->becomeUser('DisabledUser');
$controllerAccess = new ControllerAccess(['action' => 'testAction', 'rules' => []]);
$this->assertFalse($controllerAccess->run());
$this->assertEquals(401, $controllerAccess->code );
$this->assertEquals('Your user account is inactive, please login with an active account or contact a network administrator.', $controllerAccess->reason);
// UnnapprovedUser
$this->becomeUser('UnapprovedUser');
$controllerAccess = new ControllerAccess(['action' => 'testAction', 'rules' => []]);
$this->assertFalse($controllerAccess->run());
$this->assertEquals(401, $controllerAccess->code );
$this->assertEquals('Your user account has not been approved yet, please try again later or contact a network administrator.', $controllerAccess->reason);
}
public function testStrictAccess()
{
// Guest not allowed for global strict rule
$this->allowGuestAccess(false);
$controllerAccess = new StrictAccess(['action' => 'testAction', 'rules' => []]);
$this->assertFalse($controllerAccess->run());
$this->assertEquals('Guest mode not active, please login first.', $controllerAccess->reason);
// Guest allowed for global strict rule if guest mode active
$this->allowGuestAccess(true);
$controllerAccess = new StrictAccess(['action' => 'testAction', 'rules' => []]);
$this->assertTrue($controllerAccess->run());
// User allowed for global strict rule if guest mode not active
$this->allowGuestAccess(false);
$this->becomeUser('User1');
$controllerAccess = new StrictAccess(['action' => 'testAction', 'rules' => []]);
$this->assertTrue($controllerAccess->run());
}
public function testCustomOwnerRule()
{
$controllerAccess = new ControllerAccess(['action' => 'testAction', 'owner' => $this, 'rules' => [
['validateTestRule', 'return' => false]
]]);
$this->assertFalse($controllerAccess->run());
$this->assertEquals(404, $controllerAccess->code);
$this->assertEquals('Not you again!', $controllerAccess->reason);
$controllerAccess = new ControllerAccess(['action' => 'testAction', 'owner' => $this, 'rules' => [
['validateTestRule', 'return' => true]
]]);
$this->assertTrue($controllerAccess->run());
$this->assertEquals(null, $controllerAccess->code);
$this->assertEquals(null, $controllerAccess->reason);
}
public function validateTestRule($rule, $access)
{
$this->assertEquals($access->owner, $this);
if(!$rule['return']) {
$access->code = 404;
$access->reason = 'Not you again!';
return false;
}
return true;
}
public function testCustomClassRule()
{
$controllerAccess = new ControllerAccess(['action' => 'testAction', 'owner' => $this, 'rules' => [
[TestActionValidator::class, 'return' => false]
]]);
$this->assertFalse($controllerAccess->run());
$this->assertEquals(404, $controllerAccess->code);
$this->assertEquals('Not you again!', $controllerAccess->reason);
$controllerAccess = new ControllerAccess(['action' => 'testAction', 'owner' => $this, 'rules' => [
[TestActionValidator::class, 'return' => true]
]]);
$this->assertTrue($controllerAccess->run());
$this->assertEquals(null, $controllerAccess->code);
$this->assertEquals(null, $controllerAccess->reason);
}
}

View File

@ -0,0 +1,33 @@
<?php
/**
* @link https://www.humhub.org/
* @copyright Copyright (c) 2017 HumHub GmbH & Co. KG
* @license https://www.humhub.com/licences
*
*/
/**
* Created by PhpStorm.
* User: buddha
* Date: 28.07.2017
* Time: 17:47
*/
namespace humhub\tests\codeception\unit\components\access;
use humhub\components\access\ActionAccessValidator;
class TestActionValidator extends ActionAccessValidator
{
protected function validate($rule)
{
if(!$rule['return']) {
$this->access->code = 404;
$this->access->reason = 'Not you again!';
return false;
}
return true;
}
}

View File

@ -34,6 +34,7 @@ abstract class BootstrapComponent extends Widget
public $htmlOptions = [];
public $text;
public $_icon;
public $_iconRight;
public $_visible = true;
@ -119,6 +120,12 @@ abstract class BootstrapComponent extends Widget
return static::info($text)->color($color);
}
public function setType($type)
{
$this->type = $type;
return $this;
}
/**
* @param $text
* @return $this
@ -238,12 +245,13 @@ abstract class BootstrapComponent extends Widget
return $this;
}
public function icon($content, $raw = false)
public function icon($content, $right = false, $raw = false)
{
if(!$raw) {
$this->icon(Html::tag('i', '', ['class' => 'fa '.$content]), true);
$this->icon(Html::tag('i', '', ['class' => 'fa '.$content]), $right, true);
} else {
$this->_icon = $content;
$this->_iconRight = $right;
}
return $this;
@ -299,7 +307,7 @@ abstract class BootstrapComponent extends Widget
protected function getText()
{
if($this->_icon) {
return $this->_icon.' '.$this->text;
return ($this->_iconRight) ? $this->text.' '.$this->_icon : $this->_icon.' '.$this->text;
}
return $this->text;
@ -327,6 +335,7 @@ abstract class BootstrapComponent extends Widget
'text' => $this->text,
'htmlOptions' => $this->htmlOptions,
'_icon' => $this->_icon,
'_iconRight' => $this->_iconRight,
'render' => $this->_visible
];
}

View File

@ -0,0 +1,57 @@
<?php
/**
* @link https://www.humhub.org/
* @copyright Copyright (c) 2017 HumHub GmbH & Co. KG
* @license https://www.humhub.com/licences
*
*/
/**
* Created by PhpStorm.
* User: buddha
* Date: 25.07.2017
* Time: 14:31
*/
namespace humhub\widgets;
/**
* Simple FadeIn JsWidget
* @since 1.2.2
*/
class FadeIn extends JsWidget
{
/**
* @inheritdoc
*/
public $fadeIn = true;
/**
* @inheritdoc
*/
public $jsWidget = 'ui.widget.Widget';
/**
* @inheritdoc
*/
public $init = true;
/**
* @inheritdoc
*/
public function init()
{
parent::init();
ob_start();
ob_implicit_flush(false);
}
public function run()
{
$this->content = ob_get_clean();
return parent::run();
}
}

View File

@ -3,6 +3,7 @@
namespace humhub\widgets;
use humhub\components\Widget;
use humhub\libs\Html;
/**
* Description of JsWidget
@ -57,6 +58,15 @@ class JsWidget extends Widget
*/
public $container = 'div';
/**
* If set to true or 'fast', 'slow' or a integer duration in milliseconds the jsWidget will fade in the root element after initialization.
* This can be handy for widgets which need some time to initialize.
*
* @var bool|string|integer
* @since 1.2.2
*/
public $fadeIn = false;
/**
* @var string html content.
*/
@ -92,11 +102,7 @@ class JsWidget extends Widget
$result = \yii\helpers\ArrayHelper::merge($attributes, $this->options);
if (!$this->visible) {
if (isset($result['style'])) {
$result['style'] .= ';display:none;';
} else {
$result['style'] = 'display:none;';
}
Html::addCssStyle($result, 'display:none');
}
return $result;
@ -117,6 +123,12 @@ class JsWidget extends Widget
$this->options['data']['ui-widget'] = $this->jsWidget;
}
if($this->fadeIn) {
$fadeIn = $this->fadeIn === true ? 'fast' : $this->fadeIn;
$this->options['data']['widget-fade-in'] = $fadeIn;
$this->visible = false;
}
if (!empty($this->init)) {
$this->options['data']['ui-init'] = $this->init;
}

View File

@ -89,6 +89,11 @@ class MarkdownField extends InputWidget
*/
public $placeholder;
/**
* @inheritdoc
*/
public $fadeIn = 'fast';
/**
* @inheritdoc
*/
@ -127,7 +132,8 @@ class MarkdownField extends InputWidget
'rows' => $this->rows,
'disabled' => $this->disabled,
'readonly' => $this->readonly,
'placeholder' => $this->placeholder
'placeholder' => $this->placeholder,
'class' => 'form-control'
];
}