Authorization
#Using Gate
If you are relying on Laraform's default auto-processing feature using /laraform/process
endpoint the most convenient way to implement authorization is to place it in before
hook using Gate
. We are not going into the details of using Gate
feature as it is explained in Laravel's documentation, but instead let's see how we can use it within our form:
<?php
namespace App\Forms;
use Illuminate\Support\Facades\Gate;
use App\Post;
class PostForm extends \Laraform
{
public $model = Post::class;
public function schema() {
// ...
}
public function before() {
if (Gate::denies('create-post')) {
return $this->fail('You are not allowed to create post');
}
}
}
In case we want to check only either create
or update
the best way to determine the type of action is by examining the value of the entity's primary key field:
<?php
namespace App\Forms;
use Illuminate\Support\Facades\Gate;
use App\Post;
class PostForm extends \Laraform
{
public $model = Post::class;
public function schema() {
return [
'id' => [
'type' => 'key'
],
// ...
];
}
public function before() {
// Checking if the type of action is create
if ($this->isCreate()) {
// If we don't have permission to create then fail
if (Gate::denies('create-post')) {
abort(403);
}
}
// Otherwise we'll try to look for the post
else {
$post = Post::find($this->data['id']);
// If we don't find the post then fail
if (!$post) {
abort(404);
}
// If we don't have permission to update the post then fail also
if (Gate::denies('update-post', $post)) {
abort(403);
}
}
}
protected function isCreate() {
return empty($this->data['id']);
}
}
In this example we're already using HTTP Status Codes to communicate the type of error, which can enable us to create a better, more general solution. Yet this still might seem a bit complicated to be added to each form so we can decide to outsource this to a trait:
<?php
namespace App\Forms\Traits;
use Illuminate\Support\Facades\Gate;
trait AuthorizesBefore
{
public function before() {
if ($this->isCreate()) {
if (Gate::denies($this->gates['create'])) {
abort(403);
}
}
else {
$entity = {$this->model}::find($this->data['id']);
if (!$entity) {
abort(404);
}
if (Gate::denies($this->gates['update'], $entity)) {
abort(403);
}
}
}
protected function isCreate() {
return empty($this->data['id']);
}
}
Now our form becomes more simplified and we only need to create a gates
property where we define the corresponding gates for actions:
<?php
namespace App\Forms;
use App\Post;
use App\Forms\Traits\AuthorizesBefore;
class PostForm extends \Laraform
{
use AuthorizesBefore;
public $model = Post::class;
public $gates = [
'create' => 'create-post',
'update' => 'update-post',
];
public function schema() {
return [
'id' => [
'type' => 'key'
],
// ...
];
}
}
#Custom Solution
This example above is a simplified approach to implement authorization. In some cases a more advanced logic might be required which relies on authorizing endpoints for example. If you want to use middleware
or some other ways to implement autorization check out Custom Endpoint chapter. It describes how you can create custom endpoints that later can be authorized while keeping auto-processing feature.