Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Middleware for checking if resource is owned by user

I'm having some trouble making middleware that checks if the user owns the resource being requested.

For example, if the user goes to /playlists/1/edit, and they do not own playlist 1, it should display a 401 error.

Here's what I have so far:

class CheckOwnership {

    public function handle(Request $request, Closure $next)
    {
        if (Playlist::find($request->route()->parameters()['playlists'])->user_id !== $request->user()->id)
        {
            return response('Unauthorized.', 401);
        }

        return $next($request);
    }

}

This is terrible and only works for the Playlist resource, but I can't find any better way of doing this.

Thanks

like image 508
ntzm Avatar asked Mar 19 '15 23:03

ntzm


3 Answers

This can easily be achieved with the newly added Form Request Validation.

You can see it in detail here (Authorizing Form Requests): http://laravel.com/docs/5.0/validation#form-request-validation

The example given is actually about a user attempting to edit a comment they own.

Extract:

The form request class also contains an authorize method. Within this method, you may check if the authenticated user actually has the authority to update a given resource. For example, if a user is attempting to update a blog post comment, do they actually own that comment?

In your case, simply return false from the authorize method if they do no own the Playlist.

like image 64
Chris Avatar answered Oct 26 '22 18:10

Chris


Currently, Laravel 5 does not support passing parameters to middlewares. I use sessions instead.

On your playlist controller, fetch the owner of the playlist and store it on a session. Let's say you have a playlists table with columns userID and playlistID.

public function __construct($playlistID){
    $owner = Playlist::where('playlistID',$playlistID)->pluck('userID');
    Session::put('OWNER',$owner);
    $this->middleware('CheckOwnership',['only'=>'edit']); // apply it to edit function only, assuming you are using a route resource
}

Then, simply retrieve it on your middleware handle function.

public function handle(Request $request, Closure $next)
{
    if (Session::get('OWNER') != $request->user()->id)
    {
        return response('Unauthorized.', 401);
    }

    return $next($request);
}

So far, this is a simple workaround. We have to wait till Otwell considers filter-like middlewares. Hope this helps!

like image 41
eiNNod Avatar answered Oct 26 '22 17:10

eiNNod


For those using Laravel 8, I recommend using using Policies. This would let you organize authorization logic for specific models (e.x. the Playlist model for @ntzm).

So for example, a PlaylistPolicy class can be generated,

php artisan make:policy PlaylistPolicy --model=Playlist

and then the update function could look like this.

public function update(User $user, Playlist $playlist)
{
    return $user->id === $playlist->user_id;
}

There are multiple way of enforcing this policy. If you would like to use middleware, Laravel has the can middleware that can enforce policies, so new middleware won't need to be written. In your route file this would look something like this,

Route::put('playlists/{playlist}/edit', ...)
    ->middleware(['can:update,playlist']);

Note: If the --model option isn't used, the policy will have to be registered manually, and example policy methods won't be automatically generated.

like image 25
Oliver Lewis Avatar answered Oct 26 '22 18:10

Oliver Lewis