base on RBAC extension to spatie/laravel-permissions [<img src="https://github-ads.s3.eu-central-1.amazonaws.com/support-ukraine.svg?t=1" />](https://supportukrainenow.org) ![](https://banners.beyondco.de/Laravel%20RBAC.png?theme=light&packageManager=composer+require&packageName=binary-cats%2Flaravel-rbac&pattern=architect&style=style_1&description=Manage+your+spatie%2Flaravel-permission+lists+with+well-defined+roles&md=1&showWatermark=1&fontSize=100px&images=lock-closed) # Laravel RBAC [![Latest Version on Packagist](https://img.shields.io/packagist/v/binary-cats/laravel-rbac.svg?style=flat-square)](https://packagist.org/packages/binary-cats/laravel-rbac) [![run-tests](https://img.shields.io/github/actions/workflow/status/binary-cats/laravel-rbac/run-tests.yml?branch=main&label=tests&style=flat-square)](https://github.com/binary-cats/laravel-rbac/actions/workflows/run-tests.yml) [![GitHub Code Style Action Status](https://github.styleci.io/repos/773171043/shield?branch=main)](https://github.com/binary-cats/laravel-rbac/actions?query=workflow%3A"Fix+PHP+code+style+issues"+branch%3Amain) Enhance your Laravel with opinionated extension for [spatie/laravel-permissions](https://spatie.be/docs/laravel-permission/v6/introduction). Before your permission list grows and maintenance becomes an issue, this package offers simple way of defining roles and their permissions. ## Installation You can install the package via composer: ```bash composer require binary-cats/laravel-rbac ``` You can publish the config file with: ```bash php artisan vendor:publish --tag="rbac-config" ``` This is the contents of the published config file: ```php return [ /* |-------------------------------------------------------------------------- | Role base access reset control |-------------------------------------------------------------------------- | | When running rbac:reset those commands will be executed in sequence | */ 'jobs' => [ \BinaryCats\LaravelRbac\Jobs\FlushPermissionCache::class, \BinaryCats\LaravelRbac\Jobs\ResetPermissions::class, \BinaryCats\LaravelRbac\Jobs\SyncDefinedRoles::class, ], /* |-------------------------------------------------------------------------- | Role base access ability set |-------------------------------------------------------------------------- | | Place your ability files in this folder, and they will be auto discovered | */ 'path' => app()->path('Abilities'), /* |-------------------------------------------------------------------------- | Defined Roles |-------------------------------------------------------------------------- | | Defined roles are immutable by users | */ 'roles' => [ ], ]; ``` You can publish the stub files with: ```bash php artisan vendor:publish --tag="rbac-stubs" ``` ## Usage ```bash php artisan rbac:reset ``` In a simple setup we usually have two basic parts of an RBAC: a permission and a role. Permissions are usually grouped by functional or business logic domain and a Role encapsulates them for a specific guard. 1. [Create Abilities](#abilities) 2. [Define Roles](#defined-roles) 3. [Connect the dots](#connect-the-dots) ### Abilities To avoid collision with `spatie/laravel-permission` we are going to use `BackedEnum` Ability enums to hold out enumerated permissions: You can read more on using `enums` as permissions at the [official docs](https://spatie.be/docs/laravel-permission/v6/basic-usage/enums). To create an Ability: ```bash php artisan make:ability PostAbility ``` This will generate a `PostAbility` in `App\Abilities`: ```php namespace App\Abilities; enum PostAbility: string { case ViewPost = 'view post'; case CreatePost = 'create post'; case UpdatePost = 'update post'; case DeletePost = 'delete post'; } ``` Default stub contains fairly standard CRUD enumeration, generated using the name of the ability. Feel free to publish the stubs and adjsut as needed. ### Defined Roles As the name suggests, a `DefinedRole` offers a mechanism to simplify the definition of all permissions needed for a given role. To create an `EditorRole` run: ```bash php artisan make:role EditorRole ``` This will generate an `EditorRole` within `App\Roles`: ```php use BinaryCats\LaravelRbac\DefinedRole; class EditorRole extends DefinedRole { /** @var array|string[] */ protected array $guards = [ 'web' ]; /** * List of enumerated permissions for the `web` guard * * @return array */ public function web(): array { return []; } } ``` This class contains a (now testable!) configuration definition for the role and its `web` guard. Pretty neat! We can now adjust it like so: ```php namespace App\Roles; use App\Abilities\PostAbility; use BinaryCats\LaravelRbac\DefinedRole; class EditorRole extends DefinedRole { /** @var array|string[] */ protected array $guards = [ 'web' ]; /** * List of enumerated permissions for the `web` guard * * @return array */ public function web(): array { return [ PostAbility::CreatePost, PostAbility::UpdatePost, PostAbility::ViewPost, ]; } } ``` Now you are confident a specific role has specific permissions! ### Connect the dots Now that we have the abilities and roles, simply register role with `rbac.php` config: ```php 'roles' => [ \App\Roles\EditorRole::class, ... ], ``` When you run `rbac:reset` next time, your RBAC will be reset automatically. ## Integration I suggest adding the script to `post-autoload-dump` of your `composer.json` to make sure the RBAC is reset on every composer dump: ```json "post-autoload-dump": [ "Illuminate\\Foundation\\ComposerScripts::postAutoloadDump", "@php artisan rbac:reset" ], ``` ## Testing ```bash composer test ``` ## Changelog Please see [CHANGELOG](CHANGELOG.md) for more information on what has changed recently. ## Contributing Please see [CONTRIBUTING](CONTRIBUTING.md) for details. ## Security If you discover any security related issues, please email [email protected] instead of using issue tracker. ## Postcardware You're free to use this package, but if it makes it to your production environment we highly appreciate you sending us a postcard from your hometown, mentioning which of our package(s) you are using. ## Credits - [Cyrill N Kalita](https://github.com/cyrillkalita) - [All Contributors](../../contributors) ## License The MIT License (MIT). Please see [License File](LICENSE.md) for more information. ", Assign "at most 3 tags" to the expected json: {"id":"8969","tags":[]} "only from the tags list I provide: [{"id":77,"name":"3d"},{"id":89,"name":"agent"},{"id":17,"name":"ai"},{"id":54,"name":"algorithm"},{"id":24,"name":"api"},{"id":44,"name":"authentication"},{"id":3,"name":"aws"},{"id":27,"name":"backend"},{"id":60,"name":"benchmark"},{"id":72,"name":"best-practices"},{"id":39,"name":"bitcoin"},{"id":37,"name":"blockchain"},{"id":1,"name":"blog"},{"id":45,"name":"bundler"},{"id":58,"name":"cache"},{"id":21,"name":"chat"},{"id":49,"name":"cicd"},{"id":4,"name":"cli"},{"id":64,"name":"cloud-native"},{"id":48,"name":"cms"},{"id":61,"name":"compiler"},{"id":68,"name":"containerization"},{"id":92,"name":"crm"},{"id":34,"name":"data"},{"id":47,"name":"database"},{"id":8,"name":"declarative-gui "},{"id":9,"name":"deploy-tool"},{"id":53,"name":"desktop-app"},{"id":6,"name":"dev-exp-lib"},{"id":59,"name":"dev-tool"},{"id":13,"name":"ecommerce"},{"id":26,"name":"editor"},{"id":66,"name":"emulator"},{"id":62,"name":"filesystem"},{"id":80,"name":"finance"},{"id":15,"name":"firmware"},{"id":73,"name":"for-fun"},{"id":2,"name":"framework"},{"id":11,"name":"frontend"},{"id":22,"name":"game"},{"id":81,"name":"game-engine "},{"id":23,"name":"graphql"},{"id":84,"name":"gui"},{"id":91,"name":"http"},{"id":5,"name":"http-client"},{"id":51,"name":"iac"},{"id":30,"name":"ide"},{"id":78,"name":"iot"},{"id":40,"name":"json"},{"id":83,"name":"julian"},{"id":38,"name":"k8s"},{"id":31,"name":"language"},{"id":10,"name":"learning-resource"},{"id":33,"name":"lib"},{"id":41,"name":"linter"},{"id":28,"name":"lms"},{"id":16,"name":"logging"},{"id":76,"name":"low-code"},{"id":90,"name":"message-queue"},{"id":42,"name":"mobile-app"},{"id":18,"name":"monitoring"},{"id":36,"name":"networking"},{"id":7,"name":"node-version"},{"id":55,"name":"nosql"},{"id":57,"name":"observability"},{"id":46,"name":"orm"},{"id":52,"name":"os"},{"id":14,"name":"parser"},{"id":74,"name":"react"},{"id":82,"name":"real-time"},{"id":56,"name":"robot"},{"id":65,"name":"runtime"},{"id":32,"name":"sdk"},{"id":71,"name":"search"},{"id":63,"name":"secrets"},{"id":25,"name":"security"},{"id":85,"name":"server"},{"id":86,"name":"serverless"},{"id":70,"name":"storage"},{"id":75,"name":"system-design"},{"id":79,"name":"terminal"},{"id":29,"name":"testing"},{"id":12,"name":"ui"},{"id":50,"name":"ux"},{"id":88,"name":"video"},{"id":20,"name":"web-app"},{"id":35,"name":"web-server"},{"id":43,"name":"webassembly"},{"id":69,"name":"workflow"},{"id":87,"name":"yaml"}]" returns me the "expected json"