Browse Source

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
tags/2.0.0-beta
Carsten Brandt 11 years ago
parent
commit
f8b53197ac
  1. 4
      .travis.yml
  2. 1
      composer.json
  3. 2
      docs/guide/index.md
  4. 17
      docs/guide/model.md
  5. 122
      docs/guide/rbac.md
  6. 11
      framework/yii/data/Sort.php
  7. 3
      framework/yii/helpers/BaseSecurity.php
  8. 12
      framework/yii/rbac/DbManager.php
  9. 6
      framework/yii/rbac/Manager.php
  10. 16
      framework/yii/rbac/PhpManager.php
  11. 18
      framework/yii/redis/ActiveQuery.php
  12. 2
      framework/yii/web/UrlRule.php
  13. 6
      tests/unit/framework/data/SortTest.php
  14. 6
      tests/unit/framework/helpers/ArrayHelperTest.php
  15. 14
      tests/unit/framework/rbac/ManagerTestCase.php

4
.travis.yml

@ -10,8 +10,8 @@ services:
before_script: before_script:
- composer self-update && composer --version - composer self-update && composer --version
- composer require satooshi/php-coveralls 0.6.* --dev - composer require satooshi/php-coveralls 0.6.* --dev --prefer-dist
- composer require guzzle/http v3.7.3 --dev - composer require guzzle/http v3.7.3 --dev --prefer-dist
- mysql -e 'CREATE DATABASE yiitest;'; - mysql -e 'CREATE DATABASE yiitest;';
- psql -U postgres -c 'CREATE DATABASE yiitest;'; - psql -U postgres -c 'CREATE DATABASE yiitest;';
- tests/unit/data/travis/apc-setup.sh - tests/unit/data/travis/apc-setup.sh

1
composer.json

@ -63,6 +63,7 @@
"irc": "irc://irc.freenode.net/yii", "irc": "irc://irc.freenode.net/yii",
"source": "https://github.com/yiisoft/yii2" "source": "https://github.com/yiisoft/yii2"
}, },
"minimum-stability": "dev",
"replace": { "replace": {
"yiisoft/yii2-bootstrap": "self.version", "yiisoft/yii2-bootstrap": "self.version",
"yiisoft/yii2-debug": "self.version", "yiisoft/yii2-debug": "self.version",

2
docs/guide/index.md

@ -56,7 +56,7 @@ Security and access control
- [Authorization](authorization.md) - Access control and RBAC - [Authorization](authorization.md) - Access control and RBAC
- [Security](security.md) - Hashing and verifying passwords, encryption - [Security](security.md) - Hashing and verifying passwords, encryption
- [Views security](view.md#security) - how to prevent XSS - [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 Data providers, lists and grids
=============================== ===============================

17
docs/guide/model.md

@ -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, 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, 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 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: 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'] ['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: Identifying the active model scenario can be done using one of the following approaches:
```php ```php
@ -136,13 +139,17 @@ class EmployeeController extends \yii\web\Controller
// third way // third way
$employee = Employee::find()->where('id = :id', [':id' => $id])->one(); $employee = Employee::find()->where('id = :id', [':id' => $id])->one();
if ($employee !== null) { 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 Validation
---------- ----------
@ -170,11 +177,11 @@ instance of a [[\yii\validators\Validator]] child class, or an array with the fo
```php ```php
[ [
'attribute1, attribute2, ...', ['attribute1', 'attribute2', ...],
'validator class or alias', 'validator class or alias',
// specifies in which scenario(s) this rule is active. // specifies in which scenario(s) this rule is active.
// if not given, it means it is active in all scenarios // 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 // the following name-value pairs will be used
// to initialize the validator properties // to initialize the validator properties
'property1' => 'value1', 'property1' => 'value1',

122
docs/guide/rbac.md

@ -0,0 +1,122 @@
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'],
],
],
],
];
}
```

11
framework/yii/data/Sort.php

@ -12,6 +12,7 @@ use yii\base\InvalidConfigException;
use yii\base\Object; use yii\base\Object;
use yii\helpers\Html; use yii\helpers\Html;
use yii\helpers\Inflector; use yii\helpers\Inflector;
use yii\web\Request;
/** /**
* Sort represents information relevant to sorting. * Sort represents information relevant to sorting.
@ -240,7 +241,10 @@ class Sort extends Object
{ {
if ($this->_attributeOrders === null || $recalculate) { if ($this->_attributeOrders === null || $recalculate) {
$this->_attributeOrders = []; $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])) { if (isset($params[$this->sortVar]) && is_scalar($params[$this->sortVar])) {
$attributes = explode($this->separators[0], $params[$this->sortVar]); $attributes = explode($this->separators[0], $params[$this->sortVar]);
foreach ($attributes as $attribute) { foreach ($attributes as $attribute) {
@ -332,7 +336,10 @@ class Sort extends Object
*/ */
public function createUrl($attribute) 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); $params[$this->sortVar] = $this->createSortVar($attribute);
$route = $this->route === null ? Yii::$app->controller->getRoute() : $this->route; $route = $this->route === null ? Yii::$app->controller->getRoute() : $this->route;
$urlManager = $this->urlManager === null ? Yii::$app->getUrlManager() : $this->urlManager; $urlManager = $this->urlManager === null ? Yii::$app->getUrlManager() : $this->urlManager;

3
framework/yii/helpers/BaseSecurity.php

@ -75,6 +75,9 @@ class BaseSecurity
*/ */
public static function decrypt($data, $password) public static function decrypt($data, $password)
{ {
if ($data === null) {
return null;
}
$module = static::openCryptModule(); $module = static::openCryptModule();
$ivSize = mcrypt_enc_get_iv_size($module); $ivSize = mcrypt_enc_get_iv_size($module);
$iv = StringHelper::substr($data, 0, $ivSize); $iv = StringHelper::substr($data, 0, $ivSize);

12
framework/yii/rbac/DbManager.php

@ -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. * Returns a value indicating whether the item has been assigned to the user.
* @param mixed $userId the user ID (see [[User::id]]) * @param mixed $userId the user ID (see [[User::id]])
* @param string $itemName the item name * @param string $itemName the item name

6
framework/yii/rbac/Manager.php

@ -269,6 +269,12 @@ abstract class Manager extends Component
*/ */
abstract public function revoke($userId, $itemName); 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. * Returns a value indicating whether the item has been assigned to the user.
* @param mixed $userId the user ID (see [[User::id]]) * @param mixed $userId the user ID (see [[User::id]])
* @param string $itemName the item name * @param string $itemName the item name

16
framework/yii/rbac/PhpManager.php

@ -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. * Returns a value indicating whether the item has been assigned to the user.
* @param mixed $userId the user ID (see [[User::id]]) * @param mixed $userId the user ID (see [[User::id]])
* @param string $itemName the item name * @param string $itemName the item name

18
framework/yii/redis/ActiveQuery.php

@ -56,7 +56,7 @@ class ActiveQuery extends \yii\base\Component implements ActiveQueryInterface
* Executes the query and returns all results as an array. * Executes the query and returns all results as an array.
* @param Connection $db the database connection used to execute the query. * @param Connection $db the database connection used to execute the query.
* If this parameter is not given, the `db` application component will be used. * 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) 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. * Returns the query result as a scalar value.
* The value returned will be the first column in the first row of the query results. * The value returned will be the specified attribute in the first record of the query results.
* @param string $column name of the column to select * @param string $attribute name of the attribute to select
* @param Connection $db the database connection used to execute the query. * @param Connection $db the database connection used to execute the query.
* If this parameter is not given, the `db` application component will be used. * 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. * @return string the value of the specified attribute in the first record of the query result.
* False is returned if the query result is empty. * 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); $record = $this->one($db);
if ($record === null) { if ($record !== null) {
return false; return $record->$attribute;
} else { } else {
return $record->$column; return null;
} }
} }

2
framework/yii/web/UrlRule.php

@ -288,7 +288,7 @@ class UrlRule extends Object
// match params in the pattern // match params in the pattern
foreach ($this->_paramRules as $name => $rule) { 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]); $tr["<$name>"] = urlencode($params[$name]);
unset($params[$name]); unset($params[$name]);
} elseif (!isset($this->defaults[$name]) || isset($params[$name])) { } elseif (!isset($this->defaults[$name]) || isset($params[$name])) {

6
tests/unit/framework/data/SortTest.php

@ -19,6 +19,12 @@ use yii\data\Sort;
*/ */
class SortTest extends TestCase class SortTest extends TestCase
{ {
protected function setUp()
{
parent::setUp();
$this->mockApplication();
}
public function testGetOrders() public function testGetOrders()
{ {
$sort = new Sort([ $sort = new Sort([

6
tests/unit/framework/helpers/ArrayHelperTest.php

@ -40,6 +40,12 @@ class Post3 extends Object
*/ */
class ArrayHelperTest extends TestCase class ArrayHelperTest extends TestCase
{ {
protected function setUp()
{
parent::setUp();
$this->mockApplication();
}
public function testToArray() public function testToArray()
{ {
$object = new Post1; $object = new Post1;

14
tests/unit/framework/rbac/ManagerTestCase.php

@ -119,6 +119,12 @@ abstract class ManagerTestCase extends TestCase
$this->assertFalse($this->auth->revoke('author B', 'author')); $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() public function testGetAssignments()
{ {
$this->auth->assign('author B', 'deletePost'); $this->auth->assign('author B', 'deletePost');
@ -201,6 +207,13 @@ abstract class ManagerTestCase extends TestCase
'updateOwnPost' => false, 'updateOwnPost' => false,
'deletePost' => true, 'deletePost' => true,
], ],
'reader E' => [
'createPost' => false,
'readPost' => false,
'updatePost' => false,
'updateOwnPost' => false,
'deletePost' => false,
],
]; ];
$params = ['authorID' => 'author B']; $params = ['authorID' => 'author B'];
@ -245,5 +258,6 @@ abstract class ManagerTestCase extends TestCase
$this->auth->assign('author B', 'author'); $this->auth->assign('author B', 'author');
$this->auth->assign('editor C', 'editor'); $this->auth->assign('editor C', 'editor');
$this->auth->assign('admin D', 'admin'); $this->auth->assign('admin D', 'admin');
$this->auth->assign('reader E', 'reader');
} }
} }

Loading…
Cancel
Save