mr-luke/privileges

Laravel 5 package that adds roles & priviledges to your application.

v1.0.0-rc.1 2020-12-08 10:38 UTC

README

Latest Stable Version License Scrutinizer Code Quality Build Status

This package provides privileges manager that supports multi-roles, permissions and restrictions.

Getting Started

Privileges Manager has been developed using Laravel 5.5 It's recommended to test it out before using with previous versions. PHP >= 7.1.3 is required.

Note! This package is still in Beta release.

Installation

To install through composer, simply put the following in your composer.json file and run composer update

{
    "require": {
        "mr-luke/privileges": "~1.0.0"
    }
}

Or use the following command

composer require "mr-luke/privileges"

Next, add the service provider to app/config/app.php

Mrluke\Privileges\PrivilegesServiceProvider::class,

Note: Package is auto-discoverable!

Configuration

To use Privileges you need to setup your Authorizable model & allowed scopes in config file:

/*
|--------------------------------------------------------------------------
| Authorizable model class
|--------------------------------------------------------------------------
|
| This config specify which model class is authorizable.
|
*/

'authorizable' => \App\User::class,

/*
|--------------------------------------------------------------------------
| Available scopes
|--------------------------------------------------------------------------
|
| This config is a list of all available in application scopes.
|
*/

'scopes'   => [
    'users', 'settings',
],

You can also set a mapping rule that transform a given Eloquent model reference to a scope:

/*
|--------------------------------------------------------------------------
| Models mapping
|--------------------------------------------------------------------------
|
| This config allows you to map all application models to specific scopes.
|
| Example:      \App\Model::class => 'scope'
|
*/

'mapping'   => [
    \App\Users::class => 'users'
],

By default Detector returns bool value in case of allowed or denied access but you can set a custom on:

'allowed_value' => true,
'denied_value'  => false,

You can also publish config file via command:

php artisan vendor:publish

Usage

Facade

You can access to Manager and Detector using Mrluke\Privileges\Facades namespace.

Contracts

Privileges is a packages built with Contracts. You need to implement Mrluke\Privileges\Contracts\Authorizable to your User model first. It is recommended to use default Mrluke\Privilges\Extentions\Authorizable trait combined with Contract:

<?php

namespace App;

use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Mrluke\Privileges\Contracts\Authorizable as Contract;
use Mrluke\Privileges\Extentions\Authorizable;

class User extends Authenticatable implements Contract
{
    use Authorizable, Notifiable;
}

Manager - Mrluke\Privileges\Manager

Manager is a main tool that provides a simple interface to assign Role & Privileges to Authorizable.

  • assignRole(Authorizable $auth, $role): void

Allows you to assign new Mrluke\Privileges\Models\Role to Authorizable. $role can be one of three values: int in case you know Role id, Role instance or array<int> in case you want to assign more roles at once.

  • considerPermission(Authorizable $auth, string $scope): int

Returns permission level for given Authorizable based on roles & personal permissions in a $scope. Note! Personal permissions are always on top.

  • considerRestriction(Authorizable $auth, string $scope): array

Returns array of restrictions in given $scope like: IP or Time restrictions for a given Authorizable.

  • detectScope(string $model)

Using this method you can get a scope that is connected with Model by mapping, eg: \App\User::class => 'users'.

  • getPermission(Permitable $subject, string $scope): mixed

Returns Mrluke\Privileges\Models\Permission instance for a given Authorizable & scope.

  • grantPermission(Permitable $subject, string $scope, int $level): void

Creates new Permission in given scope & level and assigns it to a given Mrluke\Privileges\Contracts\Permitable. Note! Authorizable and Role implements the Permitable contract.

  • hasPermission(Permitable $subject, string $scope): bool

Determine if given Permitable has assigned scope permission.

  • regainPermission(Permitable $subject, string $scope): void

Regains a Permission from Permitable.

  • removeRole(Authorizable $auth, $role): void

Removes a Role from Authorizable.

Levels

There are 5 different Permission level that can by apply to Permitable. All of them with combination of multi-roles, personal permissions, restrictions and role's levels gives you a wide range of many possibilities but let's have a look on those 5:

  • 0 - No access.
  • 1 - Authorizable can only view.
  • 2 - Authorizable can manager & view but only existing ones.
  • 3 - Authorizable can create & manage owns.
  • 4 - Authorizable can manage all.

Detector - Mrluke\Privileges\Detector

Detector is a main tool that provides an interface for detecting Authorizable privileges. There are 6 methods that can perform various check for you:

  • has(Model $model, string $relation = null): bool

This method is responsible for belongsToMany scenario when two models are connected Many-to-many relation. By defult the name of function (relation) is detected from $model base class name (plural). In case you have your own convention of naming use parameter $relation to provide a function name.

  • hasOrLevel(Model $model, int $min, string $relation = null): bool

This method is combination of has & level in row.

  • level(int $min): bool

This method detects if Authorizable has assigned Permission by role or prsonal that satisfy the condition.

  • owner(Model $model, string $foreign = null): bool

This method is responsible for hasOne and hasMany scenarion when Authorizable is an owner of model by flat relation. By default the foreign key is detecting from Authorizable base class name (with _id). In case you have a different key name use parameter $foreign to provide a foreign key's column name.

  • ownerOrLevel(Model $model, int $min, string $foreign = null): bool

This method is combination of owner & level in row.

  • share(Model $model, string $modelRelation, string $relation): bool

This method is responsible for a deeper relation scenario when Authorizable shares some model with a parent of $model. Let's imagine that you have a User that has many Thread which can have many Reply. Now you need to consider if it can rate Reply & only only an owner can do this. This is method for you.

Detector::subject($auth)->share($reply, 'thread', 'threads');

Subject & Scope

Detector has two methods that are required as a predefinition. Befor any check you need to set a subject:

$detector->subject($authorizable);

In case you want to detect level you need also to specife the scope by:

$detector->scope($scope);

Examples

  1. Let's check if user can update all posts (level 4):
/**
 * Determine whether the user can update the post.
 *
 * @param  \App\Models\User  $user
 * @param  \App\Models\Post  $post
 * @return mixed
 */
public function update(User $user, Post $post)
{
    $scope = Manager::detectScope(Post::class);
    
    return Detector::subject($user)->scope($scope)->level(4);
}
  1. Let's check if user is an owner or can update all posts (custom key):
/**
 * Determine whether the user can update the post.
 *
 * @param  \App\Models\User  $user
 * @param  \App\Models\Post  $post
 * @return mixed
 */
public function update(User $user, Post $post)
{
    $scope = Manager::detectScope(Post::class);
    
    return Detector::subject($user)->scope($scope)->ownerOrLevel($post, 4, 'author_id');
}

Note! Manager & Detector are singleton instances.

Plans

  • Tests
  • Auto-detection extention for Policy
  • Two-factor confirmation (Manager/Director approval)