Skip to content

Instantly share code, notes, and snippets.

@uzegonemad
Created March 12, 2015 22:15
Show Gist options
  • Save uzegonemad/38608e137f7e8f95d933 to your computer and use it in GitHub Desktop.
Save uzegonemad/38608e137f7e8f95d933 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. 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.

2a. Create new trait

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

2b. 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);
        }
    }

}

2c. How do I use it?

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

@mjgritli
Copy link

mjgritli commented Apr 8, 2018

Dear Sirs,
i Loved your work, very helpful, however, during my work, i came to a situation where i thought of connecting users & permissions directly, without the use of foles.
for example, a role of user, can do many stuff, but other users, can't, why should i make another role, why not make permissions to users with the same role.
i'll try to implement it my self,
let me know what you think

@dineshsuthar92
Copy link

For Laravel 5.6 +,
Replace: $getUserPermissions = $this->permissions->lists('name');
With: $getUserPermissions = $this->permissions->pluck('name');

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