Commit f8b53197 by Carsten Brandt

Merge branch 'master' into elasticsearch

* master: Fixes #1248: url manager didn't handle array parameters well. travis: speed up composer fixes #1306 fixed travis and composer issue fixed array helper test fixed Sort test some clarification on model docs fixed Sort::params to be retrieved from Request instead of $_GET revokeAll() implementation for PhpManager revokeAll() implementation for DbManager added abstract function revokeAll() indentation fix fixes #1303: Security::decrypt now returns null w/o error when null is passed as $data Added link to new RBAC page added link to forum thread Basic information about RBAC RBAC documentation added revokeAll() test refactored redis AR Query::scalar() Conflicts: .travis.yml
parents e15860c3 7b0ba10d
......@@ -10,8 +10,8 @@ services:
before_script:
- composer self-update && composer --version
- composer require satooshi/php-coveralls 0.6.* --dev
- composer require guzzle/http v3.7.3 --dev
- composer require satooshi/php-coveralls 0.6.* --dev --prefer-dist
- composer require guzzle/http v3.7.3 --dev --prefer-dist
- mysql -e 'CREATE DATABASE yiitest;';
- psql -U postgres -c 'CREATE DATABASE yiitest;';
- tests/unit/data/travis/apc-setup.sh
......
......@@ -63,6 +63,7 @@
"irc": "irc://irc.freenode.net/yii",
"source": "https://github.com/yiisoft/yii2"
},
"minimum-stability": "dev",
"replace": {
"yiisoft/yii2-bootstrap": "self.version",
"yiisoft/yii2-debug": "self.version",
......
......@@ -56,7 +56,7 @@ Security and access control
- [Authorization](authorization.md) - Access control and RBAC
- [Security](security.md) - Hashing and verifying passwords, encryption
- [Views security](view.md#security) - how to prevent XSS
- Role based access control
- [RBAC](rbac.md) - Role-based Access Control
Data providers, lists and grids
===============================
......@@ -84,4 +84,4 @@ References
==========
- [Model validation reference](validation.md)
- [Official Composer documentation](http://getcomposer.org)
\ No newline at end of file
- [Official Composer documentation](http://getcomposer.org)
......@@ -94,7 +94,7 @@ few sections, the concept of scenarios is mainly used for data validation and ma
Associated with each scenario is a list of attributes that are *active* in that particular scenario. For example,
in the `login` scenario, only the `username` and `password` attributes are active; while in the `register` scenario,
additional attributes such as `email` are *active*.
additional attributes such as `email` are *active*. When an attribute is *active* this means that it is subject to validation.
Possible scenarios should be listed in the `scenarios()` method. This method returns an array whose keys are the scenario
names and whose values are lists of attributes that should be active in that scenario:
......@@ -119,6 +119,9 @@ We may do so by prefixing an exclamation character to the attribute name when de
['username', 'password', '!secret']
```
In this example `username`, `password` and `secret` are *active* attributes but only `username` and `password` are
considered safe for massive assignment.
Identifying the active model scenario can be done using one of the following approaches:
```php
......@@ -136,13 +139,17 @@ class EmployeeController extends \yii\web\Controller
// third way
$employee = Employee::find()->where('id = :id', [':id' => $id])->one();
if ($employee !== null) {
$employee->setScenario('managementPanel');
$employee->scenario = 'managementPanel';
}
}
}
```
The example above presumes that the model is based upon [Active Record](active-record.md). For basic form models, scenarios are rarely needed, as the basic form model is normally tied directly to a single form.
The example above presumes that the model is based upon [Active Record](active-record.md). For basic form models,
scenarios are rarely needed, as the basic form model is normally tied directly to a single form.
The default implementation of the `scenarios()`-method will return all scenarios found in the `rules()`
declaration (explained in the next section) so in simple cases you do not need to define scenarios.
Validation
----------
......@@ -170,11 +177,11 @@ instance of a [[\yii\validators\Validator]] child class, or an array with the fo
```php
[
'attribute1, attribute2, ...',
['attribute1', 'attribute2', ...],
'validator class or alias',
// specifies in which scenario(s) this rule is active.
// if not given, it means it is active in all scenarios
'on' => 'scenario1, scenario2, ...',
'on' => ['scenario1', 'scenario2', ...],
// the following name-value pairs will be used
// to initialize the validator properties
'property1' => 'value1',
......
Using RBAC
===========
Lacking proper documentation, this guide is a stub copied from a [topic on the forum](http://www.yiiframework.com/forum/index.php/topic/49104-does-anyone-have-a-working-example-of-rbac/page__view__findpost__p__229098).
First af all, you modify your config (web.php or main.php),
```php
'authManager' => [
'class' => 'app\components\PhpManager', // THIS IS YOUR AUTH MANAGER
'defaultRoles' => ['guest'],
],
```
Next, create the manager itself (app/components/PhpManager.php)
```php
<?php
namespace app\components;
use Yii;
class PhpManager extends \yii\rbac\PhpManager
{
public function init()
{
if ($this->authFile === NULL)
$this->authFile = Yii::getAlias('@app/data/rbac') . '.php'; // HERE GOES YOUR RBAC TREE FILE
parent::init();
if (!Yii::$app->user->isGuest) {
$this->assign(Yii::$app->user->identity->id, Yii::$app->user->identity->role); // we suppose that user's role is stored in identity
}
}
}
```
Now, the rules tree (@app/data/rbac.php):
```php
<?php
use yii\rbac\Item;
return [
// HERE ARE YOUR MANAGEMENT TASKS
'manageThing0' => ['type' => Item::TYPE_OPERATION, 'description' => '...', 'bizRule' => NULL, 'data' => NULL],
'manageThing1' => ['type' => Item::TYPE_OPERATION, 'description' => '...', 'bizRule' => NULL, 'data' => NULL],
'manageThing2' => ['type' => Item::TYPE_OPERATION, 'description' => '...', 'bizRule' => NULL, 'data' => NULL],
'manageThing2' => ['type' => Item::TYPE_OPERATION, 'description' => '...', 'bizRule' => NULL, 'data' => NULL],
// AND THE ROLES
'guest' => [
'type' => Item::TYPE_ROLE,
'description' => 'Guest',
'bizRule' => NULL,
'data' => NULL
],
'user' => [
'type' => Item::TYPE_ROLE,
'description' => 'User',
'children' => [
'guest',
'manageThing0', // User can edit thing0
],
'bizRule' => 'return !Yii::$app->user->isGuest;',
'data' => NULL
],
'moderator' => [
'type' => Item::TYPE_ROLE,
'description' => 'Moderator',
'children' => [
'user', // Can manage all that user can
'manageThing1', // and also thing1
],
'bizRule' => NULL,
'data' => NULL
],
'admin' => [
'type' => Item::TYPE_ROLE,
'description' => 'Admin',
'children' => [
'moderator', // can do all the stuff that moderator can
'manageThing2', // and also manage thing2
],
'bizRule' => NULL,
'data' => NULL
],
'godmode' => [
'type' => Item::TYPE_ROLE,
'description' => 'Super admin',
'children' => [
'admin', // can do all that admin can
'manageThing3', // and also thing3
],
'bizRule' => NULL,
'data' => NULL
],
];
```
As a result, you can now add access control filters to controllers
```php
public function behaviors()
{
return [
'access' => [
'class' => 'yii\web\AccessControl',
'except' => ['something'],
'rules' => [
[
'allow' => true,
'roles' => ['manageThing1'],
],
],
],
];
}
```
......@@ -12,6 +12,7 @@ use yii\base\InvalidConfigException;
use yii\base\Object;
use yii\helpers\Html;
use yii\helpers\Inflector;
use yii\web\Request;
/**
* Sort represents information relevant to sorting.
......@@ -240,7 +241,10 @@ class Sort extends Object
{
if ($this->_attributeOrders === null || $recalculate) {
$this->_attributeOrders = [];
$params = $this->params === null ? $_GET : $this->params;
if (($params = $this->params) === null) {
$request = Yii::$app->getRequest();
$params = $request instanceof Request ? $request->get() : [];
}
if (isset($params[$this->sortVar]) && is_scalar($params[$this->sortVar])) {
$attributes = explode($this->separators[0], $params[$this->sortVar]);
foreach ($attributes as $attribute) {
......@@ -332,7 +336,10 @@ class Sort extends Object
*/
public function createUrl($attribute)
{
$params = $this->params === null ? $_GET : $this->params;
if (($params = $this->params) === null) {
$request = Yii::$app->getRequest();
$params = $request instanceof Request ? $request->get() : [];
}
$params[$this->sortVar] = $this->createSortVar($attribute);
$route = $this->route === null ? Yii::$app->controller->getRoute() : $this->route;
$urlManager = $this->urlManager === null ? Yii::$app->getUrlManager() : $this->urlManager;
......
......@@ -75,6 +75,9 @@ class BaseSecurity
*/
public static function decrypt($data, $password)
{
if ($data === null) {
return null;
}
$module = static::openCryptModule();
$ivSize = mcrypt_enc_get_iv_size($module);
$iv = StringHelper::substr($data, 0, $ivSize);
......
......@@ -277,6 +277,18 @@ class DbManager extends Manager
}
/**
* Revokes all authorization assignments from a user.
* @param mixed $userId the user ID (see [[User::id]])
* @return boolean whether removal is successful
*/
public function revokeAll($userId)
{
return $this->db->createCommand()
->delete($this->assignmentTable, ['user_id' => $userId])
->execute() > 0;
}
/**
* Returns a value indicating whether the item has been assigned to the user.
* @param mixed $userId the user ID (see [[User::id]])
* @param string $itemName the item name
......
......@@ -269,6 +269,12 @@ abstract class Manager extends Component
*/
abstract public function revoke($userId, $itemName);
/**
* Revokes all authorization assignments from a user.
* @param mixed $userId the user ID (see [[User::id]])
* @return boolean whether removal is successful
*/
abstract public function revokeAll($userId);
/**
* Returns a value indicating whether the item has been assigned to the user.
* @param mixed $userId the user ID (see [[User::id]])
* @param string $itemName the item name
......
......@@ -221,6 +221,22 @@ class PhpManager extends Manager
}
/**
* Revokes all authorization assignments from a user.
* @param mixed $userId the user ID (see [[User::id]])
* @return boolean whether removal is successful
*/
public function revokeAll($userId)
{
if (isset($this->_assignments[$userId]) && is_array($this->_assignments[$userId])) {
foreach ($this->_assignments[$userId] as $itemName => $value)
unset($this->_assignments[$userId][$itemName]);
return true;
} else {
return false;
}
}
/**
* Returns a value indicating whether the item has been assigned to the user.
* @param mixed $userId the user ID (see [[User::id]])
* @param string $itemName the item name
......
......@@ -56,7 +56,7 @@ class ActiveQuery extends \yii\base\Component implements ActiveQueryInterface
* Executes the query and returns all results as an array.
* @param Connection $db the database connection used to execute the query.
* If this parameter is not given, the `db` application component will be used.
* @return ActiveRecord[] the query results. If the query results in nothing, an empty array will be returned.
* @return array|ActiveRecord[] the query results. If the query results in nothing, an empty array will be returned.
*/
public function all($db = null)
{
......@@ -215,20 +215,20 @@ class ActiveQuery extends \yii\base\Component implements ActiveQueryInterface
/**
* Returns the query result as a scalar value.
* The value returned will be the first column in the first row of the query results.
* @param string $column name of the column to select
* The value returned will be the specified attribute in the first record of the query results.
* @param string $attribute name of the attribute to select
* @param Connection $db the database connection used to execute the query.
* If this parameter is not given, the `db` application component will be used.
* @return string|boolean the value of the first column in the first row of the query result.
* False is returned if the query result is empty.
* @return string the value of the specified attribute in the first record of the query result.
* Null is returned if the query result is empty.
*/
public function scalar($column, $db = null)
public function scalar($attribute, $db = null)
{
$record = $this->one($db);
if ($record === null) {
return false;
if ($record !== null) {
return $record->$attribute;
} else {
return $record->$column;
return null;
}
}
......
......@@ -288,7 +288,7 @@ class UrlRule extends Object
// match params in the pattern
foreach ($this->_paramRules as $name => $rule) {
if (isset($params[$name]) && ($rule === '' || preg_match($rule, $params[$name]))) {
if (isset($params[$name]) && !is_array($params[$name]) && ($rule === '' || preg_match($rule, $params[$name]))) {
$tr["<$name>"] = urlencode($params[$name]);
unset($params[$name]);
} elseif (!isset($this->defaults[$name]) || isset($params[$name])) {
......
......@@ -19,6 +19,12 @@ use yii\data\Sort;
*/
class SortTest extends TestCase
{
protected function setUp()
{
parent::setUp();
$this->mockApplication();
}
public function testGetOrders()
{
$sort = new Sort([
......
......@@ -40,6 +40,12 @@ class Post3 extends Object
*/
class ArrayHelperTest extends TestCase
{
protected function setUp()
{
parent::setUp();
$this->mockApplication();
}
public function testToArray()
{
$object = new Post1;
......
......@@ -119,6 +119,12 @@ abstract class ManagerTestCase extends TestCase
$this->assertFalse($this->auth->revoke('author B', 'author'));
}
public function testRevokeAll()
{
$this->assertTrue($this->auth->revokeAll('reader E'));
$this->assertFalse($this->auth->isAssigned('reader E', 'reader'));
}
public function testGetAssignments()
{
$this->auth->assign('author B', 'deletePost');
......@@ -201,6 +207,13 @@ abstract class ManagerTestCase extends TestCase
'updateOwnPost' => false,
'deletePost' => true,
],
'reader E' => [
'createPost' => false,
'readPost' => false,
'updatePost' => false,
'updateOwnPost' => false,
'deletePost' => false,
],
];
$params = ['authorID' => 'author B'];
......@@ -245,5 +258,6 @@ abstract class ManagerTestCase extends TestCase
$this->auth->assign('author B', 'author');
$this->auth->assign('editor C', 'editor');
$this->auth->assign('admin D', 'admin');
$this->auth->assign('reader E', 'reader');
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment