yiisoft/rbac-php

Yii RBAC PHP File Storage

2.0.0 2024-03-07 10:52 UTC

This package is auto-updated.

Last update: 2024-04-07 11:03:13 UTC


README

68747470733a2f2f796969736f66742e6769746875622e696f2f646f63732f696d616765732f7969695f6c6f676f2e737667

Yii RBAC PHP File Storage


Latest Stable Version Total Downloads Build status codecov Mutation testing badge static analysis type-coverage

This package provides PHP file-based storage for RBAC (Role-Based Access Control) package.

Requirements

  • PHP 8.1 or higher.

Installation

The package could be installed with composer:

composer require yiisoft/rbac-php --prefer-dist

See yiisoft/rbac for RBAC package installation instructions.

General usage

The storage is suitable for authorization data that is not too big (for example, the authorization data for a personal blog system) or for fairly static RBAC hierarchy.

Authorization data is stored in PHP files. PHP should be able to read and write these files. Non-existing files will be created automatically on any write operation.

Using storages

The storages are not intended to be used directly. Instead, use them with Manager from Yii RBAC package:

use Yiisoft\Rbac\Manager;
use Yiisoft\Rbac\Permission;
use Yiisoft\Rbac\Php\AssignmentsStorage;
use Yiisoft\Rbac\Php\ItemsStorage;
use Yiisoft\Rbac\RuleFactoryInterface;

$directory = __DIR__ . '/rbac';
$itemsStorage = new ItemsStorage($directory . '/items.php');
$assignmentsStorage = new AssignmentsStorage($directory . '/assignments.php');
/** @var RuleFactoryInterface $rulesContainer */
$manager = new Manager(
    itemsStorage: $itemsStorage, 
    assignmentsStorage: $assignmentsStorage,
    // Requires https://github.com/yiisoft/rbac-rules-container or other compatible factory.
    ruleFactory: $rulesContainer,
),
$manager->addPermission(new Permission('posts.create'));

Note that it's not necessary to use both PHP storages. Combining different implementations is possible. A quite popular case is to manage items via PHP file while store assignments in database (see Cycle and Yiisoft DB implementations).

More examples can be found in Yii RBAC documentation.

File structure

In case you decide to manually edit the files, make sure to keep the following structure.

Items

Required and optional fields:

<?php

return [
    [
        'name' => 'posts.update',
        'description' => 'Update a post', // Optional
        'rule_name' => 'is_author', // Optional
        'type' => 'permission', // or 'role'        
        'created_at' => 1683707079, // UNIX timestamp, optional
        'updated_at' => 1683707079, // UNIX timestamp, optional
    ],
];

While it's recommended to maintain created and updated timestamps, if any is missing, the file modification time will be used instead as a fallback.

The structure for an item with children:

<?php

return [
    [
        'name' => 'posts.redactor',
        'type' => 'role',        
        'created_at' => 1683707079,
        'updated_at' => 1683707079,
        'children' => [
            'posts.viewer',
            'posts.create',
            'posts.update',
        ],
    ],
];

The complete example for managing posts:

<?php

return [
    [
        'name' => 'posts.admin',        
        'type' => 'role',        
        'created_at' => 1683707079,
        'updated_at' => 1683707079,
        'children' => [
            'posts.redactor',
            'posts.delete',
            'posts.update.all',
        ],
    ],
    [
        'name' => 'posts.redactor',
        'type' => 'role',        
        'created_at' => 1683707079,
        'updated_at' => 1683707079,
        'children' => [
            'posts.viewer',
            'posts.create',
            'posts.update',
        ],
    ],
    [
        'name' => 'posts.viewer',
        'type' => 'role',        
        'created_at' => 1683707079,
        'updated_at' => 1683707079,
        'children' => [
            'posts.view',
        ],
    ],
    [
        'name' => 'posts.view',
        'type' => 'permission',        
        'created_at' => 1683707079,
        'updated_at' => 1683707079,
    ],
    [
        'name' => 'posts.create',
        'type' => 'permission',        
        'created_at' => 1683707079,
        'updated_at' => 1683707079,
    ],
    [
        'name' => 'posts.update',
        'rule_name' => 'is_author',
        'type' => 'permission',        
        'created_at' => 1683707079,
        'updated_at' => 1683707079,
    ],
    [
        'name' => 'posts.delete',        
        'type' => 'permission',        
        'created_at' => 1683707079,
        'updated_at' => 1683707079,
    ],
    [
        'name' => 'posts.update.all',
        'type' => 'permission',        
        'created_at' => 1683707079,
        'updated_at' => 1683707079,
    ],
];

Assignments

return [
    [
        'item_name' => 'posts.redactor',
        'user_id' => 'john',
        'created_at' => 1683707079, // Optional
    ],
    // ...
    [
        'item_name' => 'posts.admin',
        'user_id' => 'jack',
        'created_at' => 1683707079,
    ],
];

While it's recommended to maintain created timestamps, if it is missing, the file modification time will be used instead as a fallback.

Concurrency

By default, working with PHP storage does not support concurrency. This might be OK if you store its files under VCS for example. If your scenario is different and, let's say, some kind of web interface is used - then, to enable concurrency, do not use the storage directly - wrap it with decorator instead:

use Yiisoft\Rbac\Manager;
use Yiisoft\Rbac\Permission;
use Yiisoft\Rbac\Php\AssignmentsStorage;
use Yiisoft\Rbac\Php\ConcurrentAssignmentsStorageDecorator;
use Yiisoft\Rbac\Php\ConcurrentItemsStorageDecorator;
use Yiisoft\Rbac\Php\ItemsStorage;
use Yiisoft\Rbac\RuleFactoryInterface;

$directory = __DIR__ . DIRECTORY_SEPARATOR . 'rbac';
$itemsSstorage = new ConcurrentItemsStorageDecorator(ItemsStorage($directory));
$assignmentsStorage = new ConcurrentAssignmentsStorageDecorator(AssignmentsStorage($directory));
/** @var RuleFactoryInterface $rulesContainer */
$manager = new Manager(
    itemsStorage: $itemsStorage, 
    assignmentsStorage: $assignmentsStorage,
    // Requires https://github.com/yiisoft/rbac-rules-container or other compatible factory.
    ruleFactory: $rulesContainer,
),

Note that it will have an impact on performance so don't use it unless you really have to.

Configuring file updated time

A closure can be used to customize getting file modification time:

use Yiisoft\Rbac\Php\AssignmentsStorage;
use Yiisoft\Rbac\Php\ItemsStorage;

$directory = __DIR__ . '/rbac',
$getFileUpdatedAt = static fn (string $filePath): int|false => @filemtime($filePath)
$itemsStorage = new ItemsStorage(
    $directory . '/items.php',
    getFileUpdatedAt: static fn (string $filePath): int|false => @filemtime($filePath),
);
$itemsStorage = new AssignmentsStorage(
    $directory . '/assignments.php',
    getFileUpdatedAt: static fn (string $filePath): int|false => @filemtime($filePath),
);

This is useful for 2 things:

  • Using for empty timestamps when files are edited manually.
  • Detection of file changes when concurrency is enabled. This helps to optimize perfomance by preventing of unnecessary loads (when file contents has not been changed).

Syncing storages manually

The storages stay synced thanks to manager, but there can be situations where you need to sync them manually. One of them is editing storage manually.

Let's say PHP files are used for both items and assignments and some items were deleted.

return [
    [
        'name' => 'posts.admin',        
        'type' => 'role',        
        'created_at' => 1683707079,
        'updated_at' => 1683707079,
        'children' => [
            'posts.redactor',
            'posts.delete',
            'posts.update.all',
        ],
    ],
-   [
-       'name' => 'posts.redactor',
-       'type' => 'role',        
-       'created_at' => 1683707079,
-       'updated_at' => 1683707079,
-       'children' => [
-           'posts.viewer',
-           'posts.create',
-           'posts.update',
-       ],
-   ],
    [
        'name' => 'posts.viewer',
        'type' => 'role',        
        'created_at' => 1683707079,
        'updated_at' => 1683707079,
        'children' => [
            'posts.view',
        ],
    ],
    [
        'name' => 'posts.view',
        'type' => 'permission',        
        'created_at' => 1683707079,
        'updated_at' => 1683707079,
    ],
    [
        'name' => 'posts.create',
        'type' => 'permission',        
        'created_at' => 1683707079,
        'updated_at' => 1683707079,
    ],
-   [
-       'name' => 'posts.update',
-       'rule_name' => 'is_author',
-       'type' => 'permission',
-       'created_at' => 1683707079,
-       'updated_at' => 1683707079,
-   ],
    [
        'name' => 'posts.delete',        
        'type' => 'permission',        
        'created_at' => 1683707079,
        'updated_at' => 1683707079,
    ],
    [
        'name' => 'posts.update.all',
        'type' => 'permission',        
        'created_at' => 1683707079,
        'updated_at' => 1683707079,
    ],
];

Then related entries in assignments storage needs to be deleted as well:

return [
-   [
-       'item_name' => 'posts.redactor',
-       'user_id' => 'john',
-       'created_at' => 1683707079,
-   ],
    [
        'item_name' => 'posts.admin',
        'user_id' => 'jack',
        'created_at' => 1683707079,
    ],
];

When using database as a second storage, this can be done within a migration. Depending on chosen implementation, refer to either RBAC Cycle example or RBAC DB example.

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.

Support the project

Open Collective

Follow updates

Official website Twitter Telegram Facebook Slack