Authorization
After setting up the Restify configuration, and the authentication. The next logical step is to protect your API Repositories against unauthorized users.
Request lifecycle
Before diving in details about authorization, it's important for you to understand what is the actual lifecycle of the request. So you can know what to expect, and how to debug your app at any point.
Booting
When you run a request (ie via Postman), it hits the Laravel application. Laravel will load every single Service Provider it has defined into config/app.php
and auto discovered providers as well.
Restify injects the RestifyApplicationServiceProvider
, it is injected in your config/app.php
and it also has an auto discovered provider called LaravelRestify\LaravelRestifyServiceProvider
.
-
The
LaravelRestifyServiceProvider
is booted firstly, this will push theRestifyInjector
middleware at the end of the middleware stack. -
Then
RestifyApplicationServiceProvider
is booted, this will define the gate, will load repositories and make the auth routes macro. You have the full control over this provider. -
The
RestifyInjector
will be handled. It will register all routes. -
On each request, if the request route is a Restify route, Laravel will handle other middlewares defined in the
restify.php
->middleware
.
View Restify
Since we are now aware of how Restify boot itself, let's see how to guard it.
Let's take a closer look to the package global gate:
// app/Providers/RestifyServiceProvider.php
protected function gate()
{
Gate::define('viewRestify', function ($user) {
return in_array($user->email, [
//
]);
});
}
This is the first gate to access the Restify repositories. In a real life project, you may allow every authenticated user to have access to repositories, and just after that, using policies you can restrict specific actions. To do so:
Gate::define('viewRestify', function ($user) {
return true;
});
If you want to allow unauthenticated users to be authorized to see restify routes, you can nullify the $user
:
Gate::define('viewRestify', function ($user = null) {
return true;
});
From this point, it's highly recommended having a policy for each model have exposed via Restify. Otherwise, users may access unauthorized resources, which is not what we want.
Policies
If you are not aware of what a policy is, we highly recommend reading the documentation before you move forward.
You can use the Laravel command for generating a policy, it's recommended to generate a policy using Restify command, because it will scaffold Restify CRUD authorization methods for you:
php artisan restify:policy UserPolicy
It will automatically detect the User
model (the word before Policy
). However, you can specify the model:
php artisan restify:policy PostPolicy --model=User
If you already have a policy, here is the Restify default scaffolded one, so you can take methods on your own:
namespace App\Policies;
use App\Models\Post;
use App\Models\User;
use Illuminate\Auth\Access\HandlesAuthorization;
class PostPolicy
{
use HandlesAuthorization;
/**
* Determine whether the user can use restify feature for each CRUD operation.
* So if this is not allowed, all operations will be disabled
* @param User $user
* @return mixed
*/
public function allowRestify(User $user = null)
{
//
}
/**
* Determine whether the user can get the model.
*
* @param User $user
* @param Post $model
* @return mixed
*/
public function show(User $user, Post $model)
{
//
}
/**
* Determine whether the user can create models.
*
* @param User $user
* @return mixed
*/
public function store(User $user)
{
//
}
/**
* Determine whether the user can create multiple models at once.
*
* @param User $user
* @return mixed
*/
public function storeBulk(User $user)
{
//
}
/**
* Determine whether the user can update the model.
*
* @param User $user
* @param Post $model
* @return mixed
*/
public function update(User $user, Post $model)
{
//
}
/**
* Determine whether the user can update bulk the model.
*
* @param User $user
* @param Post $model
* @return mixed
*/
public function updateBulk(User $user, Post $model)
{
//
}
/**
* Determine whether the user can delete the model.
*
* @param User $user
* @param Post $model
* @return mixed
*/
public function delete(User $user, Post $model)
{
//
}
/**
* Determine whether the user can restore the model.
*
* @param User $user
* @param Post $model
* @return mixed
*/
public function restore(User $user, Post $model)
{
//
}
/**
* Determine whether the user can permanently delete the model.
*
* @param User $user
* @param Post $model
* @return mixed
*/
public function forceDelete(User $user, Post $model)
{
//
}
}
Allow restify
Just after Restify detects the repository class, it will invoke this method, to check if the given user can load this repository at all. You can check if the user is admin for some specific repositories, for example:
// PostPolicy
/**
* Determine whether the user can use restify feature for each CRUD operation.
* So if this is not allowed, all operations will be disabled
* @param User $user
* @return mixed
*/
public function allowRestify(User $user)
{
return $user->isAdmin();
}
Allow show
From here, bellow, each policy corresponds to an exposed Restify route.
The show
method, correspond to the routes:
POST: /api/restify/posts // it will filter out from the pagination the entities you don't have access to
and:
POST: /api/restify/posts/{id} // it will give 403 Forbidden status if you don't have access to the resource
Definition:
/**
* Determine whether the user can get the model.
*
* @param User $user
* @param Post $model
* @return mixed
*/
public function show(User $user, Post $model)
{
//
}
Allow store
Determine if a specific user has access to the POST route for creation an entity.
The store
method, correspond to the route:
POST: /api/restify/posts
Definition:
/**
* Determine whether the user can create models.
*
* @param User $user
* @return mixed
*/
public function store(User $user)
{
//
}
Allow storeBulk
Determine if the user can store multiple entities at once.
The storeBulk
method, correspond to the route:
POST: api/posts/bulk
Definition:
/**
* Determine whether the user can create multiple models at once.
*
* @param User $user
* @return mixed
*/
public function storeBulk(User $user)
{
//
}
Allow update
Determine if the user can update a specific model.
The update
method, correspond to the routes:
PUT: api/restify/posts/{id}
PATCH: api/restify/posts/{id}
POST: api/restify/posts/{id}
Definition:
/**
* Determine whether the user can update the model.
*
* @param User $user
* @param Post $model
* @return mixed
*/
public function update(User $user, Post $model)
{
//
}
Allow updateBulk
Determine if the user can update multiple entities at once. When you bulk update, this method will be invoked for each entity you're trying to update, and if at least one will return false, no one will be updated, this is because the bulk update is a DB transaction.
The updateBulk
method, correspond to the route:
POST: api/restify/posts/bulk/update
Definition:
/**
* Determine whether the user can update bulk the model.
*
* @param User $user
* @param Post $model
* @return mixed
*/
public function updateBulk(User $user = null, Post $model)
{
return true;
}
Allow delete
The delete endpoint policy.
The delete
method, correspond to the route:
DELETE: api/restify/posts/{id}
Definition:
/**
* Determine whether the user can delete the model.
*
* @param User $user
* @param Post $model
* @return mixed
*/
public function delete(User $user, Post $model)
{
//
}
Allow Attach
Here we're talking about pivot tables. Many to many relationships.
When attaching a model to another, we should check if the user is able to do that. For example attaching posts to a user:
POST: api/restify/users/{id}/attach/posts
{ "posts": [1, 2, 3] }
In this case, Restify will guess the policy name, by the related entity, in this case it will be attachPost
:
// UserPolicy.php
/**
* Determine if the post could be attached to the user.
*
* @param User $user
* @param Post $model
* @return mixed
*/
public function attachPost(User $user, Post $model)
{
return $user->is($model->creator()->first());
}
The attachPost
method, will be called for each post in part.
Allow Detach
Here we're talking about pivot tables. Many to many relationships.
When detaching a model from another, we should check if the user is able to do that. For example detaching posts from a user:
POST: api/restify/users/{id}/detach/posts
{ "posts": [1, 2, 3] }
In this case, Restify will guess the policy name, by the related entity, in this case it will be detachPost
:
/**
* Determine if the post could be attached to the user.
*
* @param User $user
* @param Post $model
* @return mixed
*/
public function attachPost(User $user, Post $model)
{
return $user->is($model->creator()->first());
}
The detachPost
method, will be called for each post in part.
Register Policy
A common mistake, is that sometimes you may define a policy, but you don't attach it to a model in your app/Providers/AuthServiceProvider.php
. Make sure you have it defined there.
See documentation.