yiisoft / rbac
Yii Role-Based Access Control
Fund package maintenance!
Opencollective
yiisoft
Installs: 39 269
Dependents: 10
Suggesters: 0
Security: 0
Stars: 52
Watchers: 19
Forks: 24
Open Issues: 5
Requires
- php: ^7.4|^8.0
- yiisoft/access: ^1.0
Requires (Dev)
- phpunit/phpunit: ^9.5
- roave/infection-static-analysis-plugin: ^1.18
- spatie/phpunit-watcher: ^1.23
- vimeo/psalm: ^4.22
Suggests
- yiisoft/rbac-php: To store rules in PHP files
- yiisoft/rbac-rules-container: To create rules via Yii Factory
This package is auto-updated.
Last update: 2023-01-19 08:53:55 UTC
README
Yii Role-Based Access Control
This package provides RBAC (Role-Based Access Control) library. It is used in Yii Framework but is usable separately as well.
Features
- Flexible RBAC hierarchy with roles, permissions and rules.
- Role inheritance.
- Data could be passed to rules when checking access.
- Multiple storage adapters.
- Separate storages could be used for user-role assignments and role hierarchy.
- API to manage RBAC hierarchy.
Requirements
- PHP 7.4 or higher.
Installation
The package could be installed with composer:
composer require yiisoft/rbac --prefer-dist
One of the following storages should be installed as well:
- PHP storage - PHP file storage;
- DB storage - database storage based on Yii DB (in development);
- Cycle DB storage - database storage based on Cycle DBAL (in development).
Also a rule factory implementation should be installed such as
Rules Container (based on
Yii Factory).
General usage
Setting up manager
First step when using RBAC is to configure an instance of Manager
:
/** * @var \Yiisoft\Rbac\ItemsStorageInterface $itemsStorage * @var \Yiisoft\Rbac\AssignmentsStorageInterface $assignmentsStorage */ $manager = new Manager($itemsStorage, $assignmentsStorage, new ClassNameRuleFactory());
It requires specifying items storage (hierarchy itself) and assignment storage where user IDs are mapped to roles. Also,
rule factory is requires. Given a rule name stored in items storage it can create an instance of Rule
.
- 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.
- Assignments, on the other hand, could be considered "dynamic". They change more often: when creating a new user, or when updating user role from within your application. It may make sense to use database storage for assignments.
Managing RBAC hierarchy
Before being able to check for permissions, a RBAC hierarchy should 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".
- A role is what is assigned to the user. Role is granted one or more permissions. Typical roles are "manager" or "admin".
- 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:
$manager->addPermission(new Permission('createPost')); $manager->addPermission(new Permission('readPost')); $manager->addPermission(new Permission('deletePost'));
To add some roles:
$manager->addRole(new Role('author')); $manager->addRole(new Role('reader'));
Next, we need to attach permissions to roles:
$manager->addChild('reader', 'readPost'); $manager->addChild('author', 'createPost'); $manager->addChild('author', 'deletePost'); $manager->addChild('author', 'reader');
Hierarchy for the example above:
flowchart LR createPost:::permission ---> author:::role readPost:::permission --> reader:::role --> author:::role deletePost:::permission ---> author:::role classDef permission fill:#fc0,stroke:#000,color:#000 classDef role fill:#9c0,stroke:#000,color:#000
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
.
/** @var \Yiisoft\Rbac\Manager $manager */ $manager->addRule(new ActionRule()); $manager->addPermission( (new Permission('viewList'))->withRuleName('action_rule') ); // or $manager->addRule(new NewYearOnlyRule()); $manager->addRole( (new Role('NewYearMaintainer'))->withRuleName('new_year_only_rule') );
The rule itself implementation is usually quite simple:
use Yiisoft\Rbac\Rule; class ActionRule extends Rule { public function __construct() { parent::__construct('action_rule'); } public function execute(string $userId, Item $item, array $parameters = []): bool { return isset($parameters['action']) && $parameters['action'] === 'home'; } }
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.
If you need to consider multiple rules at once, use composite rule:
// Fresh and owned $compositeRule = new CompositeRule('fresh_and_owned', CompositeRule::AND, [new FreshRule(), new OwnedRule()]); // Fresh or owned $compositeRule = new CompositeRule('fresh_and_owned', 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:
$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 itself.
Check for permission
In order to check for permission, obtain an instance of \Yiisoft\Access\AccessCheckerInterface
and use it:
public function actionCreate(\Yiisoft\Access\AccessCheckerInterface $accessChecker): ResponseInterface { $userId = getUserId(); if ($accessChecker->userHasPermission($userId, 'createPost')) { // author has permission to create post } }
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:
$manager->setGuestRole('guest'); $manager->addPermission(new Permission('signup')); $manager->addRole(new Role('guest')); $manager->addChild('guest', 'signup'); $guestId = null; if ($accessChecker->userHasPermission($guestId, 'signup')) { // Guest has "signup" permission. }
If there is a rule involved, you may pass extra parameters:
$anotherUserId = 103; if (!$manager->userHasPermission($anotherUserId, 'viewList', ['action' => 'home'])) { echo 'reader not has permission index'; }
Testing
Unit testing
The package is tested with PHPUnit. To run tests:
./vendor/bin/phpunit
Mutation testing
The package tests are checked with Infection mutation framework with Infection Static Analysis Plugin. To run it:
./vendor/bin/roave-infection-static-analysis-plugin
Static analysis
The code is statically analyzed with Psalm. To run static analysis:
./vendor/bin/psalm
License
The Yii Dependency Injection is free software. It is released under the terms of the BSD License.
Please see LICENSE
for more information.
Maintained by Yii Software.