Roles/Permissions Access Control [RPAC] Laravel Package
Installation
Service Provider
Add the package to your application service providers in config/app.php
file.
'providers' => [
/*
* Laravel Framework Service Providers...
*/
Illuminate\Foundation\Providers\ArtisanServiceProvider::class,
Illuminate\Auth\AuthServiceProvider::class,
...
/**
* Third Party Service Providers...
*/
Codewiser\Rpac\RpacServiceProvider::class,
],
Config File And Migrations
Publish the package config file and migrations to your application. Run these commands inside your terminal.
php artisan vendor:publish --provider="Codewiser\Rpac\RpacServiceProvider"
And also run migrations.
php artisan migrate
This uses the default users table which is in Laravel. You should already have the migration file for the users table available and migrated.
Roles Trait and $with = [roles]
Include Roles
trait inside your User
model.
class User extends Model implements AuthenticatableContract
{
use Authenticatable, Roles;
And set protected property $with = ['roles'] (for autoloading roles with User's model).
protected $with = ['roles'];
For now User has roles()
relation.
Create Su/Admin User
Run command, example rpac su:1
or rpac admin:email@example.com
or role:user@example.com:pa$$w0r5
.
php artisan rpac su:slava@trunov.me
(:
And go to
your-domain.com/admin-rpac
:)
Usage
Creating Policy
Create policy class extends RpacPolicy
for your model.
namespace App\Policies;
use Codewiser\Rpac\Policies\RpacPolicy;
class PostPolicy extends RpacPolicy
{
}
You may define default rules.
class PostPolicy extends RpacPolicy
{
public function getDefaults($action)
{
if ($action == 'view') {
// Any user may view
return '*';
}
// Other actions allowed only to Admin
return 'admin';
}
}
When you define default rules, you should return roles, allowed to perform given action.
You may return array of roles, one role, *
as any role or nothing.
Role guest
means anonymous user. Role any
means any authorized user.
Other rules you may tune up using administrative interface. Those rules can not override defaults.
Out-of-the-box Policy supports Laravel default actions: viewAny
and create
as non-model
and view
, update
, delete
, restore
and forceDelete
as model actions.
You may define any custom actions.
class PostPolicy extends RpacPolicy
{
public function engage(?User $user, Model $model)
{
return $this->authorize('engage', $user, $model);
}
public function archive(?User $user)
{
return $this->authorize('archive', $user);
}
}
Policy Pseudo-name
As Laravel supposed, you may use one Policy to few Models. RPAC isolate Policies permissions using Policy pseudo-name as a namespace.
For example, your application has few classes, that conforms similar rules and permissions:
App\Models\Settings\Categories
and App\Models\Settings\Tags
.
By default, Policy pseudo-name is a name of Policy class without words Policies
and Policy
.
For App\Models\Policies\SettingsPolicy
pseudo-name will be App\Models\Settings
.
So, we may define one SettingsPolicy
and apply it both to App\Models\Settings\Categories
and App\Models\Settings\Tags
.
From RPAC point of view they both known as App\Models\Settings
.
You may override Policy pseudo-name.
class SettingsPolicy extends RpacPolicy
{
public function getNamespace()
{
return 'Settings';
}
Relationships aka Model Roles
RPAC provides you way to define relationships between User and Model. Relationship is a role, that has sense only in context of current Model.
For every defined relationship, Model must provide User-relation method. Relation may be as single, as plural.
class Post extends Model
{
use RPAC;
public $relationships = ['author', 'manager', 'snake_relationship'];
public function author()
{
return $this->belongsTo(User::class, 'author_id');
}
// relation is plural
public function managers()
{
return $this->belongsToMany(User::class, 'post_managers');
}
// snake_style (or slug-style, or any other) names converted to camelCase
public function snakeRelationships()
{
return $this->hasMany(User::class);
}
}
Only hasOne
, hasMany
, belongsTo
and belongsToMany
relations supported.
Defining default rules, you may return not only roles, but relationships too. They should be namespaced by Policy pseudo-name.
class PostPolicy extends RpacPolicy
{
public function getDefaults($action)
{
if ($action == 'view') {
// Any user may view
return 'any';
}
if ($action == 'update') {
// Author may edit his post
return ['Post\Author', 'admin'];
}
// Other actions allowed only to Admin
return 'admin';
}
}
With relationship you may scope your Model to get only those records, that User may interact.
For example: any user may create post, but user can edit only posts he wrote. In other words, only author can edit posts.
So, the scope will contain only posts with post.author_id=user.id
// Only records User may edit
$posts = Post::query()->allowedTo('update', Auth::user())->get();
Here is an example of the PostController.
class PostController
{
public function index(Request $request)
{
$this->authorize('viewAny', Post::class);
// The current user can see listing of the blog posts
$posts = Post::query()->allowedTo('view', Auth::user());
// return posts to frontend
}
public function update(Request $request, Post $post)
{
$this->authorize('update', $post);
// The current user can update the blog post...
}
}
Getting abilities
To build proper User Interface you need to know whether User allowed to create or edit Model. You may collect full list of authorized actions through Model.
// Model actions
$post = Post::find($id);
$abilities = $post->getAuthorizedActions(Auth::user());
// or use property, that returns actions for authorized user
$abilities = $post->authorizedActions;
// [view, update]
// Non-model actions
$abilities = Post::authorizedActions(Auth::user());
// [viewAny]