back2lobby/access-control

Roles and Permissions manager for laravel

v1.0.4 2023-06-03 11:39 UTC

README

Total Downloads License

AccessControl is a Laravel package for easy role & permission management with model-based role assignment and role-based permissions.

Table of Contents

Click to expand

Introduction

AccessControl simplifies role & permission management by enabling the assignment of roles based on models and defining role-based permissions for fine-grained control over user access.

Once installed, you can simply tell the access-control what you want to allow at the gate:

// Give a role some permission
AccessControl::allow("manager")->to('edit-company');

// Assign role to any user
AccessControl::assign('manager')->to($user);

// You can also assign role for a specific roleable model
AccessControl::assign('manager',$company)->to($user);

// Checking the permission on user for a roleable model
AccessControl::canUser($user)->do("edit-company",$company);

// Checking if the user has a role for that model
AccessControl::is($user)->a("manager",$company);

Installation

Note: AccessControl requires PHP 8.1+ and Laravel 9.0+

  1. Install AccessControl with composer:

    composer require back2lobby/access-control
    
  2. Use the HasRoles in the User Model:

    use Illuminate\Database\Eloquent\Factories\HasFactory;
    use Illuminate\Foundation\Auth\User as Authenticatable;
    use Back2Lobby\AccessControl\Traits\HasRoles;
     
    class User extends Authenticatable
    {
     use HasFactory, HasRoles
    
     // code here
    }

    If you want to use custom user model instead of App\Models\User, head over to Custom User Model section.

  3. If you have a roleable model, then add AccessControl's trait to your roleable model:

    use Back2Lobby\AccessControl\Traits\Roleable;
    class Post extends Model
    {
        use Roleable;
    }
  4. Now, to run AccessControl's migrations. First publish the migrations into your app's migrations directory, by running the following command:

    php artisan vendor:publish --tag="access-control.migrations"
    
  5. Finally, run the migrations:

    php artisan migrate
    

Once it's installed, you can use a seeder to create base roles and permissions for your Laravel application. For example:

use Illuminate\Database\Seeder;
use Back2Lobby\AccessControl\Facades\AccessControlFacade as AccessControl;

class AccessControlSeeder extends Seeder
{
    public function run()
    {
        // Create all roles
        AccessControl::createManyRoles([
            [
                'name' => 'admin',
                'title' => 'Administrator',
            ],
            [
                'name' => 'editor',
                'title' => 'Editor',
            ]
        ]);

        // Create all permissions
        AccessControl::createManyPermissions([
            [
                'name' => 'create-post',
                'title' => 'Create Post',
                'description' => 'Allows user to create a new post',
            ],
            [
                'name' => 'edit-post',
                'title' => 'Edit Post',
                'description' => 'Allows user to edit an existing post',
            ],
        ]);
    }
}

Facade

Whenever you use the AccessControl facade in your code, remember to add this line to your namespace imports at the top of the file:

use AccessControl;

If your IDE is facing any issues with this facade, please use barryvdh/laravel-ide-helper

Terminologies

Role:

A set of permissions that can be assigned to a user.

Permission:

A right to perform a specific action or access a specific resource.

Direct permission:

A permission that is allowed or forbidden directly to a role.

Indirect permission:

A permission that a user has because of a super permission it has, rather than being directly allowed or forbidden.

Super permission:

A permission that is used to grant all permissions, except for those that are forbidden directly.

Direct Role

A role is a direct role for a permission if it is allowed for the permission directly.

Indirect Role

A role is an indirect role for a permission, if it is not allowed for the permission directly rather the role have that permission because it has super permission.

Usage

Role

Creating Role

Role can be created using createRole method. Example:

$superAdmin = AccessControl::createRole([
    'name' => 'super-admin',
    'title' => 'Super Admin'
]);

You can specify roleables also, which will restrict the role to be assigned for the given roleable. Example:

AccessControl::createRole([
    'name' => 'manager',
    'title' => 'Manager',
    'roleables' => [Company::class]
]);

// ✓ assigning role with allowed roleable will work fine
$user->assign('manager',$company);

// ✗ assigning role with roleable that's not allowed will throw error
$user->assign('manager',$post);

Multiple roles can be created at once like this:

AccessControl::createManyRole([
    ['name','company-manager','tittle'=>'Company Manager'],
    ['name','company-user','title' => 'Company User']
]);

Updating Role

Role can be updated using updateRole method. Example:

// using role name
AccessControl::updateRole('author',[
'name' => 'post-author',
'title' => 'Post Author'
]);

Deleting Role

Role can be deleted using deleteRole method. Example:

AccessControl::deleteRole('author');

Getting Role

To retrieve a role, you can use the method getRole. Example:

AccessControl::getRole('admin');

To retrieve all the roles available we can do something like to get a Collection of available roles.

AccessControl::getAllRoles();

Allowing Permissions

To allow a role for a specific permission, you can use the method allow and then chain it with method to. Example:

AccessControl::allow('author')->to('edit'); // with permission name

AccessControl::allow('author')->to($permission); // with permission object

AccessControl::allow('author')->to(3); // with permission id

Alternatively, we can use allow method from Role Model itself like:

$role->allow('create-post');

To allow all the permissions available indirectly (except forbidden specifically), use method superPermission like this:

AccessControl::allow('super-admin')->superPermission();

The method superPermission needs the permission named *. It will create it in case it's not available.

Disallowing Permissions

Take back a permission from a user with method disallow and chain it with method to. Example:

AccessControl::disallow('admin')->to('create admin');

Alternatively, we can use disallow from Role Model itself like:

$role->disallow('create-post');

To take back the super permission given to the role, use method superPermission() like this:

AccessControl::disallow('manager')->superPermission();

Forbidding Permissions

Forbid a permission for the role using method forbid and chain it with method to. Example:

AccessControl::forbid('manager')->to('delete company');

Alternatively, we can use forbid from Role Model itself like:

$role->forbid('create-post');

You can forbid the role from all the permissions indirectly (except allowed specifically) using the method superPermission() like this:

AccessControl::forbid('manager')->superPermission();

Getting Permissions For Role

To get all the permissions a role have including allowed and forbidden, we can use method getAllPermissionsOf like:

$permissions = AccessControl::getAllPermissionsOf('manager');

To get only specific type of permissions for the role, we can use methods getAllowedPermissionsOf, getDirectlyAllowedPermissionsOf, getIndirectlyAllowedPermissionsOf, getForbiddenPermissionsOf, getDirectlyForbiddenPermissionsOf, getIndirectlyForbiddenPermissionsOf. Examples:

 // getting allowed permissions
 $allowedPermissions = AccessControl::getAllowedPermissionsOf('manager');

 $directlyAllowedPermissions = AccessControl::getDirectlyAllowedPermissionsOf('manager');

 $indirectlyAllowedPermissions = AccessControl::getIndirectlyAllowedPermissionsOf('manager');

 // getting forbidden permissions
 $forbiddenPermissions = AccessControl::getForbiddenPermissionsOf('manager');

 $directlyForbiddenPermissions = AccessControl::getDirectlyForbiddenPermissionsOf('manager');

 $indirectlyForbiddenPermissions = AccessControl::getIndirectlyForbiddenPermissionsOf('manager');

Read Terminologies if you don't know about direct/indirect permissions.

Assigning Role

Role can be assigned to any user using method assign and chain it with to method. Alternatively, you can use the assign method that's available method on the User model itself. Example:

AccessControl::assign('admin')->to($user); // using facade

$user->assign('editor'); // using user model

You can also specify the roleable if the role requires specific roleables like this. For example, you want a user to be manager for a specific project only. It can be achieved like this:

AccessControl::assign('manager',$project)->to($user); // using facade

$user->assign('manager',$project); // using user model

Retracting Role

Assigned role can be retracted from any user using method retract and chain it with from method. Example:

AccessControl::retract('admin')->from($user);

You can also specify the roleable to retract role only for the specific roleable. For example, if a user is the manager of multiple companies, the manager role for only given company will be retracted from the user:

AccessControl::retract('manager',$company)->from($user);

Checking Role

To check if a user have a specific role use method is and chain it with a or an methods. Example:

AccessControl::is($user)->a('manager',$company);

AccessControl::is($user)->an('admin');

To check if a user have a specific role chain it with notA or notAn methods. Example:

AccessControl::is($user)->notA('manager',$company);

AccessControl::is($user)->notAn('admin');

To check if user have all the given roles, we can do something like:

AccessControl::is($user)->all([
    'admin',
    'manager'
]);

It doesn't check for any roleables even if the role is restricted to some roleables. For Example, It will return true if the user have manager role for any roleable:

AccessControl::is($user)->all([
    'manager'
]);

To check if user have any of the given roles, we can do something like:

AccessControl::is($user)->any([
    'admin',
    'manager'
]);

Resetting Role

To remove all the permissions from a role, we can reset it using the method resetRole method. Example:

AccessControl::resetRole('admin');

Permission

Creating Permission

Permission can be created using the method createPermission method. Example:

AccessControl::createPermission([
    'name' => 'edit-post',
    'title' => 'Edit Post'
]);

Multiple permissions can be created at once using the method createManyPermissions like this:

AccessControl::createManyPermissions([
    ['name' => 'create-post','title' => 'Create Post'],
    ['name' => 'edit-post','title' => 'Edit Post'],
    ['name' => 'delete-post','title' => 'Delete Post']
]);

Updating Permission

Permission can be updated using the method updatePermission. For example, to change a permission's name from remove-post to delete-post we can do something like:

AccessControl::updatePermission('remove-post',[
   'name' => 'delete-post',
   'title' => 'Delete Post'
]);

Deleting Permission

To delete a permission we can use the method deletePermission like:

AccessControl::deletePermission('edit-post');

Getting Permission

To retrieve a permission we can use the method getPermission like:

AccessControl::getPermission('delete-company');

To retrieve all the permissions available, use the method getPermissions. Example:

AccessControl::getPermissions();

Getting Roles Having Permission

To get all the roles that have a permission, we can use method getAllRolesOf like:

$rolesWhoCanEdit = AccessControl::getAllRolesOf('edit-post');

To get only specific type of roles for the permission, we can use methods getAllowedRolesOf, getDirectlyAllowedRolesOf, getIndirectlyAllowedRolesOf, getForbiddenRolesOf, getDirectlyForbiddenRolesOf, getIndirectlyForbiddenRolesOf. Examples:

// getting allowed roles
$allowedRoles = AccessControl::getAllowedRolesOf('edit-post');

$directlyAllowedRoles = AccessControl::getDirectlyAllowedRolesOf('edit-post');

$indirectlyAllowedRoles = AccessControl::getIndirectlyAllowedRolesOf('edit-post');

// getting forbidden roles
$forbiddenRoles = AccessControl::getForbiddenRolesOf('edit-post');

$directlyForbiddenRoles = AccessControl::getDirectlyForbiddenRolesOf('edit-post');

$indirectlyForbiddenRoles = AccessControl::getIndirectlyForbiddenRolesOf('edit-post');

Read Terminologies if you don't know about direct/indirect roles.

User

Getting User Roles

To get all the roles assigned to a user, we can use the method roles provided by Back2Lobby\AccessControl\Models\User. Example:

$roles = $user->roles()->get();

Getting User Permissions

To get all the permissions allowed for user through various roles, we can use the method permissions provided by Back2Lobby\AccessControl\Models\User. Example:

$roles = $user->permissions();

Getting Users With Specific Role

To get all the users that have a specific role, we can use the static method whereIs provided by Back2Lobby\AccessControl\Models\User. Example:

$admins = User::whereIs('admin')->get();

If the target role is restricted to some roleables, we can do something like:

$players = User::whereIs('player',$team)->get();

You can also reverse the logic by using users method from the role model instead:

$admins = $adminRole->users()->get();

Getting Users With Specific Permission

To get all the users that have a specific permission, we can use the static method whereHas provided by Back2Lobby\AccessControl\Models\User. Example:

$users = User::whereHas('edit-post',$post)->get();

Checking User Permission

To check if a user have specific permission from any role, we can use the method canUser and chain it with method do like this:

$canCreatePost = AccessControl::canUser($user)->do('create-post');

You can also specify roleables like this:

$canEditPost = AccessControl::canUser($user)->do('edit-post',$post);

Resetting User

To remove all the roles from a user, we can use the method resetUser. Example:

AccessControl::resetUser($user);

Features

Cache

All roles and permissions are cached and refreshed automatically every 24 hours. This optimization improves performance and reduces unnecessary database queries. Note that user data is not cached as it can frequently change.

You can manually sync all the roles and permissions with database with sync method. For example:

AccessControl::sync();

To clear the cache you can use the method clearCache like:

AccessControl::clearCache();

Even after clearing cache the local store will still have the roles and permissions, you can remove them also using the method reset: Example:

AccessControl::reset();

Manually caching the store can be achieved using cache like:

AccessControl::cache();

Note: By default, file is used as the cache driver, but it can be changed in access.php config file.

Authorization

To check roles and permissions in blade files, we can use Laravel built in can method on the user model. For Example:

if($user->can('view-dashboard')){
    // your code here
}

If you want to check permission for a specific model, then we can do something like:

$user->can('edit-company',$company);

Blade Directive

Similarly, to check roles and permissions in blade files, we can use Laravel built in @can directive to check. For Example:

@can('ban-users')
	<button class="btn btn-danger">Ban User</button>
@endcan

@can('edit-post',$post)
	<a href="{{ route('post.edit') }}">Edit Post</a>
@endcan

Config File

Access Control provides a configuration file that can be used to configure the behaviour of the package including specifying cache driver and custom user model.

You can publish the config file access.php using the command:

php artisan vendor:publish --tag="access-control.config"

Middleware

Similarly, built in can middleware from Laravel as:

Route::get('/dashboard', function () {
    return view('dashboard');
})->middleware('can:access-dashboard');

This will check if the authenticated user has the access-dashboard permission before processing the request.

You can also use the can middleware to check permissions for a specific model instance. For example, the route below will only be processed for users who have the edit-post permission on the Post model instance that is passed to route model binding in /posts/{post}/edit.

Route::get('/posts/{post}/edit', function () {
    return view('dashboard');
})->middleware('can:edit-post,' . Post::class);

In this case, Post::class is passed to specify the model class for which the permission check should be performed. Note that this will only work if the route has route model binding for the Post model.

Custom User Model

By default, App\Models\User model is used for authorization and authentication for this package. To use custom model instead, following steps are needed:

  • Specify the model in access.php. For Example,
'auth_user_model' => CustomUserModel::class
  • Make sure your custom user model extends Illuminate\Foundation\Auth\User.
  • Migrate the database tables related to the package or just use this command to get a fresh database.
php artisan migrate:fresh
  • Note: Make sure you have specified the new auth model for the guard in auth.php config file. For Example,
	'guards' => [
		'web' => [
			'driver' => 'session',
			'provider' => 'customUsers',
		],
	],

	'providers' => [
		'customUsers' => [
			'driver' => 'eloquent',
			'model' => App\Models\CustomUserModel::class,
		],
	],