panoscape/privileges

Permission control system for Laravel

0.2.4 2016-11-20 16:36 UTC

This package is not auto-updated.

Last update: 2024-04-13 17:27:29 UTC


README

Privilege and Group control for Laravel

Installation

You can install this package via composer using this command:

composer require panoscape/privileges

Register service provider:

config/app.php

'providers' => [
    ...
    Panoscape\Privileges\PrivilegesServiceProvider::class,
];

If you need blade directives, also add this:

config/app.php

'providers' => [
    ...
    Panoscape\Privileges\PrivilegesBladeServiceProvider::class,
];

A middleware can also be registered:

app/Http/Kernel.php

protected $routeMiddleware = [
  ...
  'privileges' => \Panoscape\Privileges\Middleware\PrivilegesMiddleware::class,
];

Publish profile config:

php artisan vendor:publish --provider="Panoscape\Privileges\PrivilegesServiceProvider" --tag="profile"

Modify the published profile template to suit your application.

config/privileges_profile.php

<?php

return [

    /*
    |--------------------------------------------------------------
    | User entity
    |--------------------------------------------------------------
    |
    */
    'user' => [

        /*
        |--------------------------------------------------------------
        | Model class
        |--------------------------------------------------------------
        |
        */
        'model' => '\App\User',

        /*
        |--------------------------------------------------------------
        | Table name
        |--------------------------------------------------------------
        |
        */
        'table' => 'users',

        /*
        |--------------------------------------------------------------
        | Primary key name in table
        |--------------------------------------------------------------
        |
        */
        'id' => 'id',
    ],

    /*
    |--------------------------------------------------------------
    | Group entity
    |--------------------------------------------------------------
    |
    */
    'group' => [

        /*
        |--------------------------------------------------------------
        | Model class
        |--------------------------------------------------------------
        |
        */
        'model' => '\App\Group',

        /*
        |--------------------------------------------------------------
        | Table name
        |--------------------------------------------------------------
        |
        */
        'table' => 'groups',

        /*
        |--------------------------------------------------------------
        | Primary key name in table
        |--------------------------------------------------------------
        |
        */
        'id' => 'id',
    ],

    /*
    |--------------------------------------------------------------
    | Privilege entity
    |--------------------------------------------------------------
    |
    */
    'privilege' => [

        /*
        |--------------------------------------------------------------
        | Model class
        |--------------------------------------------------------------
        |
        */
        'model' => '\App\Privilege',

        /*
        |--------------------------------------------------------------
        | Table name
        |--------------------------------------------------------------
        |
        */
        'table' => 'privileges',

        /*
        |--------------------------------------------------------------
        | Primary key name in table
        |--------------------------------------------------------------
        |
        */
        'id' => 'id',
    ],

    /*
    |--------------------------------------------------------------
    | User-Group pivot table
    |--------------------------------------------------------------
    |
    */
    'user_group' => [

        /*
        |--------------------------------------------------------------
        | Table name
        |--------------------------------------------------------------
        |
        */
        'table' => 'group_user',

        /*
        |--------------------------------------------------------------
        | User foreign key in table
        |--------------------------------------------------------------
        |
        */
        'user_id' => 'user_id',

        /*
        |--------------------------------------------------------------
        | Group foreign key in table
        |--------------------------------------------------------------
        |
        */
        'group_id' => 'group_id',
    ],

    /*
    |--------------------------------------------------------------
    | Group-Privilege pivot table
    |--------------------------------------------------------------
    |
    */
    'group_privilege' => [

        /*
        |--------------------------------------------------------------
        | Table name
        |--------------------------------------------------------------
        |
        */
        'table' => 'privilege_group',

        /*
        |--------------------------------------------------------------
        | Group foreign key in table
        |--------------------------------------------------------------
        |
        */
        'group_id' => 'group_id',

        /*
        |--------------------------------------------------------------
        | Privilege foreign key in table
        |--------------------------------------------------------------
        |
        */
        'privilege_id' => 'privilege_id',
    ]

];

Add Panoscape\Privileges\Privilege\UserEntity trait to your user model, Panoscape\Privileges\Privilege\GroupEntity trait to your group model, and Panoscape\Privileges\Privilege\PrivilegeEntity trait to your privilege model.

If you have multiple privileges control flow or you prefer a different profile name, you may copy and modify the default profile template and rename it to something else, admin_profile for example. Then defile a method named profile in your related models and set them to the config name of your choice.

Here is an example of Admin, Role, Permission(instead of User,Group,Privilege):

config/admin_profile.php

<?php

return [
    'user' => [
        'model' => '\App\Admin',
        'table' => 'admins',
        'id' => 'id',
    ],
    'group' => [
        'model' => '\App\Role',
        'table' => 'roles',
        'id' => 'id',
    ],
    'privilege' => [
        'model' => '\App\Permission',
        'table' => 'permissions',
        'id' => 'id',
    ],
    'user_group' => [
        'table' => 'admin_role',
        'user_id' => 'admin_id',
        'group_id' => 'role_id',
    ],
    'group_privilege' => [
        'table' => 'permission_role',
        'group_id' => 'role_id',
        'privilege_id' => 'permission_id',
    ]
];

app/Admin.php

class Admin extends Authenticatable
{
  	...
    use \Panoscape\Privileges\UserEntity;
    
  	public function profile()
    {
        return 'admin_profile';
    }
}

app\Role.php

class Role extends Model
{
  	...
    use \Panoscape\Privileges\GroupEntity;
    
  	public function profile()
    {
        return 'admin_profile';
    }
}

app\Permission.php

class Permission extends Model
{
  	...
    use \Panoscape\Privileges\PrivilegeEntity;
    
  	public function profile()
    {
        return 'admin_profile';
    }
}

Migration

This package does not provide any migrations or commands. You should create three required models/migrations and two pivot tables by yourself. The minimal requirements of table structures are listed in the profile template.

Basic Usage

Access groups/privileges relationship of a user:

$user->groups()->get();
$user->privileges()->get();

or via dynamic properties:

$user->groups->get();
$user->privileges->get();

If you have different entity names other than the default User, Group, Privilege, You should access the relationships by the table values defined in your profile.

Example of Admin, Role, Permission:

$admin->roles()->get();
$admin->roles->get();
$admin->permissions()->get();
$role->admins->get();
$role->permissions->get();
$permission->roles->get();

Group and Privilege validation

has:

//returns true if target group is found on this user
$user->groups()->has('root');

all:

//returns false unless all groups are found on this user
$user->groups()->all(['editor', 'author', 'subscriber']);

any:

//returns true as long as any of these groups are found on this user
$user->groups()->any(['editor', 'author', 'subscriber']);

Instead of the default name column checking, you may specify which column to check:

//check name column by default
$user->groups()->has('root');
//check fullname column instead
$user->groups()->has('Root Administrator', 'fullname');
//check id column instead
$user->groups()->any([1, 3, 5], 'id');

validate:

With this method you can do complex checking

all:

$user->groups()->validate('root|author|subscriber')

equivalent to

$user->groups()->all(['editor', 'author', 'subscriber'])

any:

$user->groups()->validate('(root|author|subscriber)')

equivalent to

$user->groups()->any(['editor', 'author', 'subscriber'])

all + any:

$user->privileges()->validate('query|(delete|insert)|update')

equivalent to

$user->privileges()->all(['query', 'update']) && $user->privileges()->any(['delete', 'insert'])

group:

$user->validate('g=root|(author|subscriber)')

equivalent to

$user->groups()->all(['root']) && $user->groups()->any(['author', 'subscriber'])

privilege:

$user->validate('p=query|(delete|insert)|update')

equivalent to

$user->privileges()->all(['query', 'update']) && $user->privileges()->any(['delete', 'insert'])

group + privilege:

$user->validate('g=root|(author|subscriber);p=query|(delete|insert)|update')

equivalent to

$user->groups()->all(['root']) && $user->groups()->any(['author', 'subscriber'])
  && $user->privileges()->all(['query', 'update']) && $user->privileges()->any(['delete', 'insert'])

Column specification is also available:

$user->validate('g=1|(3|5);p=1|(2|10)|3', 'id')

Middleware

If you have registered the middleware, you can add it to any routes you'd like to guard with it.

Route::get('/pages', 'PageController@index')->middleware('privileges:g=editor|(author|subscriber);p=query|(delete|insert)|update');

Balde

If you have registered the blade service provider, you may guard your blade codes with @validate , @group and @privilege.

Also your user entity need to implement Panoscape\Privileges\Privileged interface in order to use these blade directives.

class Admin extends Authenticatable implements \Panoscape\Privileges\Privileged
{
  	...
    use \Panoscape\Privileges\UserEntity;
}

Blade directives:

@group('root')
  <button>
  	...
  </button>
 @endgroup
  
 @privilege('edit_users')
  <button>
  	...
  </button>
 @endprivilege
  
 @validate('g=(root|editor);p=edit_users')
  <button>
  	...
  </button>
 @endvalidate

Performance

$user->privileges()->validate('editor_users|edit_admins')

joined 5 tables(2 of them are pivot tables) within one query:

select count(*) as aggregate from "permissions" inner join "permission_role" on "permissions"."id" = "permission_role"."permission_id" inner join "roles" on "roles"."id" = "permission_role"."role_id" inner join "admin_role" on "roles"."id" = "admin_role"."role_id" inner join "admins" on "admins"."id" = "admin_role"."admin_id" where "admins"."id" = '1' and "permissions"."name" in ('edit_users', 'edit_admins')
$user->privileges()->validate('edit_users|(create_admins|edit_admins)')

joined 5 tables(2 of them are pivot tables) within two query:

select count(*) as aggregate from "permissions" inner join "permission_role" on "permissions"."id" = "permission_role"."permission_id" inner join "roles" on "roles"."id" = "permission_role"."role_id" inner join "admin_role" on "roles"."id" = "admin_role"."role_id" inner join "admins" on "admins"."id" = "admin_role"."admin_id" where "admins"."id" = '1' and "permissions"."name" in ('create_admins', 'edit_admins')
select count(*) as aggregate from "permissions" inner join "permission_role" on "permissions"."id" = "permission_role"."permission_id" inner join "roles" on "roles"."id" = "permission_role"."role_id" inner join "admin_role" on "roles"."id" = "admin_role"."role_id" inner join "admins" on "admins"."id" = "admin_role"."admin_id" where "admins"."id" = '1' and "permissions"."name" in ('edit_users')
$user->privileges()->validate('(edit_users|delete_users)|(create_admins|edit_admins)')

joined 5 tables(2 of them are pivot tables) within two query:

select count(*) as aggregate from "permissions" inner join "permission_role" on "permissions"."id" = "permission_role"."permission_id" inner join "roles" on "roles"."id" = "permission_role"."role_id" inner join "admin_role" on "roles"."id" = "admin_role"."role_id" inner join "admins" on "admins"."id" = "admin_role"."admin_id" where "admins"."id" = '1' and "permissions"."name" in ('edit_users', 'delete_users')
select count(*) as aggregate from "permissions" inner join "permission_role" on "permissions"."id" = "permission_role"."permission_id" inner join "roles" on "roles"."id" = "permission_role"."role_id" inner join "admin_role" on "roles"."id" = "admin_role"."role_id" inner join "admins" on "admins"."id" = "admin_role"."admin_id" where "admins"."id" = '1' and "permissions"."name" in ('create_admins', 'edit_admins')

Conclusion

Each any group costs one query;

All all group costs one query.