Skip to content

Instantly share code, notes, and snippets.

@albofish
Forked from uzegonemad/entrust-user-permission.md
Last active March 20, 2017 11:47
Show Gist options
  • Save albofish/f81cd9d745ac99f36bd67a8c8a32076f to your computer and use it in GitHub Desktop.
Save albofish/f81cd9d745ac99f36bd67a8c8a32076f to your computer and use it in GitHub Desktop.
Entrust Laravel 5 User Permissions

What is this?

Entrust is a fantastic role-based permission library for Laravel. However, by design, it only supports attaching permissions to roles, not to users.

This gist adds support for user-specific permissions on top of existing roles.

There's a chance that this hasn't been thought out fully, so use it at your own risk... I'm offering zero support for this. It either works, or it doesn't.

This has only been tested on Entrust's Laravel 5 branch.

How to use it

1. Migration

The migration adds support for a many-to-many relationship between users and permissions.

1a. Create new migration

php artisan make:migration create_permission_user_table

1b. Migration contents

<?php

use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreatePermissionUserTable extends Migration {

    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('permission_user', function (Blueprint $table) {
            $table->integer('user_id')->unsigned();
            $table->integer('permission_id')->unsigned();

            $table->foreign('user_id')->references('id')->on('users')
                  ->onUpdate('cascade')->onDelete('cascade');
            $table->foreign('permission_id')->references('id')->on('permissions')
                  ->onUpdate('cascade')->onDelete('cascade');

            $table->primary(['user_id', 'permission_id']);
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::drop('permission_user');
    }

}

1c. Run Migration

php artisan migrate

2. Config

2a. Publish Entrust Config

Publish the default Entrust configuration file if you haven't done so before php artisan vendor:publish

2b. Add permission_user table

Add the following to config/entrust.php

/*
|--------------------------------------------------------------------------
| Entrust permission_user Table
|--------------------------------------------------------------------------
|
| This is the permission_user table used by Entrust to save relationship
| between permissions and users to the database.
|
*/
'permission_user_table' => 'permission_user',

3. Trait

The trait "extends" Entrust's existing EntrustUserTrait trait.

Overriding can() method The can() method is overridden. It looks in our new permission_user table and if it doesn't find the permission, calls the original Entrust can() method. This override is forward compatible.

There are also methods for attaching permissions to a specific user, in the form of:

  • attachPermission()
  • detachPermission()
  • attachPermissions()
  • detachPermissions()

The singular methods take an ID and the plural methods take an array of IDs, just like the existing Entrust syntax.

3a. Create new trait

I created a new directory app/Traits. Put the trait wherever you want, just be sure to update the namespace accordingly.

3b. Trait contents

<?php namespace App\Traits;

use Illuminate\Support\Facades\Config;
use InvalidArgumentException;
use Zizaco\Entrust\Traits\EntrustUserTrait;

trait EntrustUserWithPermissionsTrait
{
    use EntrustUserTrait {
        can as canEntrust;
    }

    /**
     * Many-to-Many relations with Permission.
     *
     * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
     */
    public function permissions()
    {
        return $this->belongsToMany(Config::get('entrust.permission'), Config::get('entrust.permission_user_table'), 'user_id', 'permission_id');
    }

    /**
     * Check if user has a permission by its name.
     *
     * @param string|array $permission Permission string or array of permissions.
     * @param bool         $requireAll All permissions in the array are required.
     *
     * @return bool
     */
    public function can($permission, $requireAll = false)
    {
        // Check specific permissions first because permissions override roles
        $permFound = false;

        $permissionArray = is_array($permission) ? $permission : [$permission];
        $getUserPermissions = $this->permissions->lists('name');
        foreach($getUserPermissions as $userPerm)
        {
            // if permission IS found
            if(in_array($userPerm, $permissionArray))
            {
                $permFound = true;

                // if we DON'T require all, bail
                if(!$requireAll)
                {
                    break;
                }
            }
            // if permission is NOT found
            else
            {
                $permFound = false;

                // if we DO require all, bail
                if($requireAll)
                {
                    break;
                }
            }
        }

        // User permission override found
        if($permFound)
        {
            return $permFound;
        }

        // User permission not granted, check roles via entrust
        return $this->canEntrust($permission, $requireAll);
    }

    /**
     * Alias to eloquent many-to-many relation's attach() method.
     *
     * @param mixed $permission
     */
    public function attachPermission($permission)
    {
        if(is_object($permission)) {
            $permission = $permission->getKey();
        }

        if(is_array($permission)) {
            $permission = $permission['id'];
        }

        $this->permissions()->attach($permission);
    }

    /**
     * Alias to eloquent many-to-many relation's detach() method.
     *
     * @param mixed $permission
     */
    public function detachPermission($permission)
    {
        if (is_object($permission)) {
            $permission = $permission->getKey();
        }

        if (is_array($permission)) {
            $permission = $permission['id'];
        }

        $this->permissions()->detach($permission);
    }

    /**
     * Attach multiple permissions to a user
     *
     * @param mixed $permissions
     */
    public function attachPermissions($permissions)
    {
        foreach ($permissions as $permission) {
            $this->attachPermission($permission);
        }
    }

    /**
     * Detach multiple permissions from a user
     *
     * @param mixed $permissions
     */
    public function detachPermissions($permissions)
    {
        foreach ($permissions as $permission) {
            $this->detachPermission($permission);
        }
    }

}

3c. How do I use it?

In your User model, replace Zizaco\Entrust\Traits\EntrustUserTrait with App\Traits\EntrustUserWithPermissionsTrait

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment