thepinecode / policy
Using Laravel's authorization on the front-end.
Installs: 17 207
Dependents: 0
Suggesters: 0
Security: 0
Stars: 89
Watchers: 6
Forks: 6
Open Issues: 1
Type:project
Requires
- php: ^7.2.5 | ^8.0
- laravel/framework: ^6.0 || ^7.0 || ^8.22.1
Requires (Dev)
- fzaninotto/faker: ^1.9.1
- laravel/laravel: ^8.0
- mockery/mockery: ^1.3.1
- phpunit/phpunit: ^9.0
README
Using Laravel's authorization on the front-end.
A nice tool for SPAs and front-end heavy applications.
If you want to see behind the package, we suggest to read this post: Implementing Laravel’s Authorization on the Front-End.
Table of contents
- Getting Started
- Publishing and setting up the JavaScript library
- Using the policies and the Gate.js
- Example
- Contribute
Getting started
You can install the package with composer, running the composer require thepinecode/policy
command.
Since the package supports auto-discovery, Laravel will register the service provider automatically behind the scenes.
In some cases you may disable auto-discovery for this package.
You can add the provider class to the dont-discover
array to disable it.
Then you need to register it manually again.
Publishing and setting up the JavaScript library
By default the package provides a Gate.js
file, that will handle the policies.
Use the php artisan vendor:publish
command and choose the Pine\Policy\PolicyServiceProvider
provider.
After publishing you can find your fresh copy in the resources/js/policies
folder if you are using Laravel 5.7+.
If your application is lower than 5.7, the JS will be published in the resources/assets/js/policies
.
Setting up the Gate.js
Then you can import the Gate
class and assign it to the window
object.
import Gate from './policies/Gate'; window.Gate = Gate;
Initializing a gate instance
From this point you can initialize the translation service anywhere from your application.
let gate = new Gate;
Passing the user to the gate instance
The Gate
object requires a passed user to work properly. This can be a string
or an object
.
By default, it looks for the window['user']
object, however you may customize the key or the object itself.
let gate = new Gate; // window['user'] let gate = new Gate('admin'); // window['admin'] let gate = new Gate({ ... }); // uses the custom object
Note, you can pass any object as a user. If you pass a team or a group object, it works as well. Since you define the logic behind the
Gate
, you can pass anything you wish.
Using it as a Vue service
If you want to use it from Vue templates directly you can extend Vue with this easily.
Vue.prototype.$Gate = new Gate;
<template> <div v-if="$Gate.allow('view', model)">...</div> </template>
computed: { hasPermission: { return this.$Gate.allow('view', this.model); } }
The @currentUser blade directive
To make it quicker, the package comes with a @currentUser
blade directive.
This does nothing more, but to print the currently authenticated user as JSON
and assign it to the window
object.
@currentUser <!-- Result --> <script>window['user'] = { ... };</script>
You may override the default key for the user. You can do that by passing a string to the blade directive.
@currentUser ('admin') <!-- Result --> <script>window['admin'] = { ... };</script>
If there is no authenticated user, the value will be
null
.
Using the policies and the Gate.js
The available methods
allow()
The allow()
accepts two parameters. The first is the action to perform, the second is the model object or the model name, like in Laravel.
Note: model name should be a lower case version of the actual model name in Laravel: for example
Comment
becomescomment
.
gate.allow('view', model); gate.allow('create', 'comment');
deny()
The deny()
has the same signature like allow()
but it will negate its return value.
gate.deny('view', model); gate.deny('create', 'comment');
before()
Like in Laravel, in the before()
method you can provide a custom logic to check for special conditions.
If the condition passes, the rest of the policy rules in the allow()
or deny()
won't run at all.
However if the condition fails, the policy rules will get place.
To use the before()
method, you may extend the gate object and define your custom logic.
Gate.prototype.before = function () { return this.user.is_admin; }
Please note, to use the
this
object correctly, use the traditional function signature instead of the arrow (() => {}) functions.
Adding the UsesModelName
trait to the models
Since, the policies use real JSON shaped eloquent models, the models have to use the Pine\Policy\UsesModelName
trait that generates the proper model name. This model name attribute is used for pairing the proper policy with
the model by the Gate.js
.
use Pine\Policy\UsesModelName; use Illuminate\Database\Eloquent\Model; class Comment extends Model { use UsesModelName; protected $appends = ['model_name']; }
Please note, to be able to use this attribute on the front-end, the attribute has to be appended to the JSON form. You can read more about appending values to JSON in the docs.
Generating policies with artisan
The package comes with an artisan command by default, that helps you to generate your JavaScript policies easily.
To make a policy, run the php artisan make:js-policy Model
command, where the Model
is the model's name.
php artisan make:js-policy Comment
This command will create the CommentPolicy.js
file next to the Gate.js
in the resources/js/policies
directory.
If you are using lower than Laravel 5.7, the policies will be generated in the resources/assets/js/policies
directory.
Note, the command will append the
Policy
automatically in the file name. It means you may pass only the model name when running the command.
After you generated the policy files, use npm
to compile all the JavaScript, including policies.
Important!
The policies are registered automatically. It means, no need for importing them manually.
The gate instance will automatically populate the policies.
Every policy will be used where it matches with the model's model_name
attribute.
Based on
Laravel's default app.js
the Gate instance
registers the policies automatically
when calling npm run dev
, npm run prod
and so on.
Writing the policy rules
Policies – like in Laravel – have the following methods by default:
viewAny
, view
, create
, update
, restore
, delete
and forceDelete
.
Of course, you can use custom methods as well, policies are fully customizables.
... view(user, model) { return user.id == model.user_id; } create(user) { return user.is_admin; } approve(user, model) { return user.is_editor && user.id == model.user_id; } ...
Example
// app.js Vue.prototype.$Gate = new Gate; Vue.component('posts', { mounted() { axios.get('/api/posts') .then(response => this.posts = response.data); }, data() { return { posts: [], }; }, template: ` <ul><li v-for="post in posts" v-if="$Gate.allow('update', post)"></li></ul> <button v-if="$Gate.allow('create', 'post')">Create post</button> ` }); let app = new Vue({ // })
<body> <posts></posts> @currentUser <script src="{{ asset('js/app.js') }}"></script> </body>
Contribute
If you found a bug or you have an idea connecting the package, feel free to open an issue.