Feedback Stay at home and get up to 40% off with STAYATHOME coupon code | 0d 0h 0m 0s left

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.