Skip to content

Commit

Permalink
Actualize README and PHPDoc (#174)
Browse files Browse the repository at this point in the history
  • Loading branch information
arogachev committed Sep 3, 2023
1 parent f9ca4b4 commit 4e46f99
Show file tree
Hide file tree
Showing 2 changed files with 90 additions and 57 deletions.
143 changes: 88 additions & 55 deletions README.md
Expand Up @@ -15,7 +15,7 @@
[![static analysis](https://github.com/yiisoft/rbac/workflows/static%20analysis/badge.svg)](https://github.com/yiisoft/rbac/actions?query=workflow%3A%22static+analysis%22)
[![type-coverage](https://shepherd.dev/github/yiisoft/rbac/coverage.svg)](https://shepherd.dev/github/yiisoft/rbac)

This package provides [RBAC](https://en.wikipedia.org/wiki/Role-based_access_control) (Role-Based Access Control)
This package provides [RBAC](https://en.wikipedia.org/wiki/Role-based_access_control) (Role-Based Access Control)
library. It is used in [Yii Framework](https://yiiframework.com) but is usable separately as well.

## Features
Expand All @@ -29,27 +29,27 @@ library. It is used in [Yii Framework](https://yiiframework.com) but is usable s

## Requirements

- PHP 7.4 or higher.
- PHP 8.0 or higher.

## Installation

The package could be installed with composer:
The package is installed with composer:

```shell
composer require yiisoft/rbac
```

One of the following storages should be installed as well:
One of the following storages could be installed as well:

- [PHP storage](https://github.com/yiisoft/rbac-php) - PHP file storage;
- [DB storage](https://github.com/yiisoft/rbac-db) - database storage based on
[Yii DB](https://github.com/yiisoft/db);
- [Cycle DB storage](https://github.com/yiisoft/rbac-cycle-db) - database storage based on
- [DB storage](https://github.com/yiisoft/rbac-db) - database storage based on [Yii DB](https://github.com/yiisoft/db);
- [Cycle DB storage](https://github.com/yiisoft/rbac-cycle-db) - database storage based on
[Cycle DBAL](https://github.com/cycle/database).

Also, a rule factory implementation should be installed such as
[Rules Container](https://github.com/yiisoft/rbac-rules-container) (based on
[Yii Factory](https://github.com/yiisoft/factory)).
Also, there is a rule factory implementation - [Rules Container](https://github.com/yiisoft/rbac-rules-container) (based
on [Yii Factory](https://github.com/yiisoft/factory)).

All these can be replaced with custom implementations.

## General usage

Expand All @@ -58,15 +58,25 @@ Also, a rule factory implementation should be installed such as
First step when using RBAC is to configure an instance of `Manager`:

```php
use Yiisoft\Rbac\AssignmentsStorageInterface;
use Yiisoft\Rbac\ItemsStorageInterface;
use Yiisoft\Rbac\RuleFactoryInterface;

/**
* @var \Yiisoft\Rbac\ItemsStorageInterface $itemsStorage
* @var \Yiisoft\Rbac\AssignmentsStorageInterface $assignmentsStorage
* @var ItemsStorageInterface $itemsStorage
* @var AssignmentsStorageInterface $assignmentsStorage
* @var RuleFactoryInterface $ruleFactory
*/
$manager = new Manager($itemsStorage, $assignmentsStorage, new ClassNameRuleFactory());
$manager = new Manager($itemsStorage, $assignmentsStorage, $ruleFactory);
```

It requires specifying items storage (hierarchy itself) and assignment storage where user IDs are mapped to roles. Also,
rule factory is required. Given a rule name stored in items storage it can create an instance of `Rule`.
It requires specifying the following dependencies:

- Items storage (hierarchy itself).
- Assignments storage where user IDs are mapped to roles.
- Rule factory. Given a rule name stored in items storage it can create an instance of `Rule`.

Here are a few tips for choosing storage backend:

- Roles and permissions could usually be considered "semi-static", as they only change when you update your application
code, so it may make sense to use PHP storage for it.
Expand All @@ -75,7 +85,7 @@ rule factory is required. Given a rule name stored in items storage it can creat

### Managing RBAC hierarchy

Before being able to check for permissions, a RBAC hierarchy should be defined. Usually it is done via either console
Before being able to check for permissions, a RBAC hierarchy must be defined. Usually it is done via either console
commands or migrations. Hierarchy consists of permissions, roles and rules:

- Permissions are granules of access such as "create a post" or "read a post".
Expand All @@ -84,9 +94,13 @@ commands or migrations. Hierarchy consists of permissions, roles and rules:
- Rule is a PHP class that given some data answers a single question "given the data, has the user the permission asked
for".

In order to create permission, use the following code:
In order to create a permission, use the following code:

```php
use Yiisoft\Rbac\ManagerInterface;
use Yiisoft\Rbac\Permission;

/** @var ManagerInterface $manager */
$manager->addPermission(new Permission('createPost'));
$manager->addPermission(new Permission('readPost'));
$manager->addPermission(new Permission('deletePost'));
Expand All @@ -95,13 +109,20 @@ $manager->addPermission(new Permission('deletePost'));
To add some roles:

```php
use Yiisoft\Rbac\ManagerInterface;
use Yiisoft\Rbac\Role;

/** @var ManagerInterface $manager */
$manager->addRole(new Role('author'));
$manager->addRole(new Role('reader'));
```

Next, we need to attach permissions to roles:

```php
use Yiisoft\Rbac\ManagerInterface;

/** @var ManagerInterface $manager */
$manager->addChild('reader', 'readPost');
$manager->addChild('author', 'createPost');
$manager->addChild('author', 'deletePost');
Expand All @@ -120,75 +141,85 @@ flowchart LR
```

Sometimes, basic permissions are not enough. In this case, rules are helpful. Rules are PHP classes that could be
added to permissions and roles. In this case, the role or permission is considered only when rule's `execute()` method
returns `true`.
added to permissions and roles:

```php
/** @var \Yiisoft\Rbac\Manager $manager */
use Yiisoft\Rbac\RuleInterface;

$manager->addRule(new ActionRule());
$manager->addPermission(
(new Permission('viewList'))->withRuleName('action_rule')
);
class ActionRule implements RuleInterface
{
public function execute(string $userId, Item $item, array $parameters = []): bool
{
return isset($parameters['action']) && $parameters['action'] === 'home';
}
}
```

// or
With rule added, the role or permission is considered only when rule's `execute()` method returns `true`.

$manager->addRule(new NewYearOnlyRule());
$manager->addRole(
(new Role('NewYearMaintainer'))->withRuleName('new_year_only_rule')
);
```
The parameters are:

The rule itself implementation is usually quite simple:
- `$userId` is user id to check permission against;
- `$item` is RBAC hierarchy item that rule is attached to;
- `$parameters` is extra data supplied when checking for permission.

To use rules with `Manager`, specify their names with added permissions or roles:

```php
use Yiisoft\Rbac\Rule;
use Yiisoft\Rbac\ManagerInterface;
use Yiisoft\Rbac\Permission;

class ActionRule extends Rule
{
public function __construct()
{
parent::__construct('action_rule');
}
/** @var ManagerInterface $manager */
$manager->addPermission(
(new Permission('viewList'))->withRuleName('action_rule'),
);

public function execute(string $userId, Item $item, array $parameters = []): bool
{
return isset($parameters['action']) && $parameters['action'] === 'home';
}
}
// or

$manager->addRole(
(new Role('NewYearMaintainer'))->withRuleName('new_year_only_rule')
);
```

In the above `$userId` that permission is checked by, `$item` is RBAC hierarchy item rule is attached to, and
`$parameters` is extra data supplied when checking for permission.
The rule names `action_rule` and `new_year_only_rule` are resolved to `ActionRule` and `NewYearOnlyRule` class instances
accordingly via rule factory.

If you need to consider multiple rules at once, use composite rule:
If you need to aggregate multiple rules at once, use composite rule:

```php
use Yiisoft\Rbac\CompositeRule;

// Fresh and owned
$compositeRule = new CompositeRule('fresh_and_owned', CompositeRule::AND, [new FreshRule(), new OwnedRule()]);
$compositeRule = new CompositeRule(CompositeRule::AND, [new FreshRule(), new OwnedRule()]);

// Fresh or owned
$compositeRule = new CompositeRule('fresh_and_owned', CompositeRule::OR, [new FreshRule(), new OwnedRule()]);
$compositeRule = new CompositeRule(CompositeRule::OR, [new FreshRule(), new OwnedRule()]);
```

### Assigning roles to users

In order to assign a certain role to a user with a given ID, use the following code:

```php
use Yiisoft\Rbac\ManagerInterface;

/** @var ManagerInterface $manager */
$userId = 100;
$manager->assign('author', $userId);
```

It could be done in an admin panel, via console command, or it could be built into the application business logic
It could be done in an admin panel, via console command, or it could be built into the application business logic
itself.

### Check for permission

In order to check for permission, obtain an instance of `\Yiisoft\Access\AccessCheckerInterface` and use it:
In order to check for permission, obtain an instance of `Yiisoft\Access\AccessCheckerInterface` and use it:

```php
public function actionCreate(\Yiisoft\Access\AccessCheckerInterface $accessChecker): ResponseInterface
use Psr\Http\Message\ResponseInterface;
use Yiisoft\Access\AccessCheckerInterface;

public function actionCreate(AccessCheckerInterface $accessChecker): ResponseInterface
{
$userId = getUserId();

Expand All @@ -198,8 +229,8 @@ public function actionCreate(\Yiisoft\Access\AccessCheckerInterface $accessCheck
}
```

Sometimes you need to add guest-only permission, which is not assigned to any user ID. In this case, you can specify
a role which is assigned to guest user:
Sometimes you need to add guest-only permission, which is not assigned to any user ID. In this case, you can specify a
role which is assigned to guest user:

```php
use Yiisoft\Access\AccessCheckerInterface;
Expand All @@ -210,7 +241,6 @@ use Yiisoft\Rbac\Role;
* @var ManagerInterface $manager
* @var AccessCheckerInterface $accessChecker
*/

$manager->setGuestRoleName('guest');
$manager->addPermission(new Permission('signup'));
$manager->addRole(new Role('guest'));
Expand All @@ -225,9 +255,12 @@ if ($accessChecker->userHasPermission($guestId, 'signup')) {
If there is a rule involved, you may pass extra parameters:

```php
use Yiisoft\Rbac\ManagerInterface;

/** @var ManagerInterface $manager */
$anotherUserId = 103;
if (!$manager->userHasPermission($anotherUserId, 'viewList', ['action' => 'home'])) {
echo 'reader not has permission index';
echo 'reader hasn\'t "index" permission';
}
```

Expand Down
4 changes: 2 additions & 2 deletions src/CompositeRule.php
Expand Up @@ -13,10 +13,10 @@
*
* ```php
* // Fresh and owned
* $compositeRule = new CompositeRule('fresh_and_owned', CompositeRule::AND, [new FreshRule(), new OwnedRule()]);
* $compositeRule = new CompositeRule(CompositeRule::AND, [new FreshRule(), new OwnedRule()]);
*
* // Fresh or owned
* $compositeRule = new CompositeRule('fresh_and_owned', CompositeRule::OR, [new FreshRule(), new OwnedRule()]);
* $compositeRule = new CompositeRule(CompositeRule::OR, [new FreshRule(), new OwnedRule()]);
* ```
*/
final class CompositeRule implements RuleInterface
Expand Down

0 comments on commit 4e46f99

Please sign in to comment.