user9611583
user9611583

Reputation:

Laravel: How to throw 403 if the user enter the ID manually in the route?

Building an app (Blog/posts). Where only auth users can edit their post(which ofcourse belongs to them only). For example, Post with an id of 15 belongs to particular user, so if he edits it, the route will be like this

http://localhost:8000/post/15/edit

this is correct.

But when the user enters any other post ID(which doesn't belongs to him) in the route, it shows

http://localhost:8000/post/16/edit

ErrorException (E_NOTICE)
Trying to get property 'user_id' of non-object

How to show unauthorised page in this case?

This is the postController

public function edit($id)

{

$post = Post::find($id);

        if(Auth::user()->id == $post->user_id){

        return view('post-edit',compact('post'));
    }else {
        return redirect()->route('home');      

}
}

Upvotes: 2

Views: 9128

Answers (2)

Priyash
Priyash

Reputation: 181

use laravel authorization policy to authorize users.

php artisan make:policy PostPolicy --model=Post

This command will create PostPolicy.php in app\policies dir. now you'll have to register the policy in AuthServiceProvider. So first add use statements of your policy and model for example.

use App\Post;
use App\Policies\PostPolicy;

then find protected $policies and in that array register your policy. Model followed by policy.

protected $policies = [
    Post::class => PostPolicy::class,
];

Now in your Policy that we generated using artisan command. will hold all CRUD related methods. each of them accepts two parameters one is User and second is the model you want to authorize except create method. note that you can modify create or other methods to accept more parameters. it's upto you.

Now for example in your policy let's build logic for update method.

/**
 * Determine if the given post can be updated by the user.
 *
 * @param  \App\User  $user
 * @param  \App\Post  $post
 * @return bool
 */
public function update(User $user, Post $post)
{
    return $user->id === $post->user_id;
}

As you can see return Boolean here. you can customize methods as you want. Next in your controller method. where you want to authorize user simply add

public function update(Post $post)
{
    $this->authorize('update', $post);
    // then your logic here.
}

For create authorization you just pass pass empty class

$this->authorize('create', Post::class);

It accepts two parameters one is authorization method name and second is model.It automatically get's authenticated user and authorize user. if not authorized then throws Illuminate\Auth\Access\AuthorizationException which is 403.

Also if you need to modify the 403 error view you'll need to create 403 blade in

resources/views/errors/403.blade.php

Everything is well documented in laravel doc.

Extra tip if you are going to use some Boolean datatype value for returned from database as tinyint which are 1 or 0. for example

public function view(User $user, Post $post)
{
    if(! $post->isPrivate) {
       return true;
    }
    return $user->id === $post->user_id;
}

then make sure to cast that value to Boolean in model to return as true or false. because it was not working for when i deployed my application on shared hosting. Later i found that it was returning as a string. also the version of the database was old.

Upvotes: 1

AntoineB
AntoineB

Reputation: 4694

The following code checks if the post exist (which is why you are getting the error Trying to get property 'user_id' of non-object, because it doesn't exist), and then checks if it belongs to the user in the same condition. If it's not valid it aborts with a 403 UNAUTHORIZED error code.

public function edit($id)
{
    $post = Post::find($id);
    if (empty($post) || Auth::id() != $post->user_id) {
        abort(403);
    }
    else {
        return view('post-edit',compact('post'));      
    }
}

Here is a better version that checks if a post exist, with the specified ID, but also with the right user and throws an exception otherwise:

public function edit($id)
{
    $post = Post::whereHas('user', function ($q) {
        $q->where('users.id', Auth::id());
    })->findOrFail($id);

    return view('post-edit',compact('post'));      
}

A third version, on the same idea as the 2nd one, but simpler:

public function edit($id)
{
    $post = Post::where('user_id', Auth::id())->findOrFail($id);

    return view('post-edit',compact('post'));      
}

Upvotes: 2

Related Questions