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.
The migration adds support for a many-to-many relationship between users and permissions.
php artisan make:migration create_permission_user_table
<?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');
}
}
php artisan migrate
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.
I created a new directory app/Traits
. Put the trait wherever you want, just be sure to update the namespace accordingly.
<?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);
}
}
}
In your User model, replace Zizaco\Entrust\Traits\EntrustUserTrait
with App\Traits\EntrustUserWithPermissionsTrait
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