|

Check Authorization With Policies Before Delete Function

Use Policy For Authorization

In this tutorial we want to set up the ability for authorized users to delete any reply that they have created. In addition, guests or unauthorized users should not be able to delete any replies. So as usual, we’ll set up a couple of tests to support these new features. In addition we’ll leverage a new policy object to determine whether any particular user is authorized to delete a given reply. Lastly, we’ll update the view side to display the option of deleting a reply by authorized users.


Unauthorized Users Test

First up, let’s open the ParticipateInForumTest.php test class we have and add a new test to it. We can name it test_unauthorized_users_can_not_delete_replies(). The logic is pretty simple. Given there is a Reply, and a guest tries to delete that reply, then the user should be redirected to the login page. Here is a first shot at that test.

Running the test gives us a NotFoundHttpException which is usually a missing route.

vagrant@homestead:~/Code/forumio$ phpunit --filter test_unauthorized_users_can_not_delete_replies
PHPUnit 6.5.5 by Sebastian Bergmann and contributors.

E                                                                   1 / 1 (100%)

Time: 849 ms, Memory: 8.00MB

There was 1 error:

1) Tests\Feature\ParticipateInForumTest::test_unauthorized_users_can_not_delete_replies
Symfony\Component\HttpKernel\Exception\NotFoundHttpException: DELETE http://localhost/replies/1

Add The Route

We register a delete request to /replies/{reply} which will trigger the destroy() method on the RepliesController.


Modifying The Test

Let’s assume a user is actually signed in. In this case, the test might look like this.

If we run the test, now we are getting a BadMethodCallException. This is because we still need to create the destroy() method on the RepliesController.

vagrant@homestead:~/Code/forumio$ phpunit --filter test_unauthorized_users_can_not_delete_replies
PHPUnit 6.5.5 by Sebastian Bergmann and contributors.

E                                                                   1 / 1 (100%)

Time: 821 ms, Memory: 8.00MB

There was 1 error:

1) Tests\Feature\ParticipateInForumTest::test_unauthorized_users_can_not_delete_replies
BadMethodCallException: Method [destroy] does not exist on [App\Http\Controllers\RepliesController].

Adding The destroy() method

Open up RepliesController.php, and we can add the code to complete our goal.

Now we can run the test once more.

vagrant@homestead:~/Code/forumio$ phpunit --filter test_unauthorized_users_can_not_delete_replies
PHPUnit 6.5.5 by Sebastian Bergmann and contributors.

F                                                                   1 / 1 (100%)

Time: 920 ms, Memory: 10.00MB

There was 1 failure:

1) Tests\Feature\ParticipateInForumTest::test_unauthorized_users_can_not_delete_replies
Expected status code 403 but received 302.
Failed asserting that false is true.

/home/vagrant/Code/forumio/vendor/laravel/framework/src/Illuminate/Foundation/Testing/TestResponse.php:78
/home/vagrant/Code/forumio/tests/Feature/ParticipateInForumTest.php:53

FAILURES!
Tests: 1, Assertions: 1, Failures: 1.

What is happening now is during the test, the reply is getting deleted but we have not applied any authorization. As it stands now in the test, the user is signed in then tries to delete a reply that he or she did not create. The response should be a forbidden status of 403. We can enable this using a new policy object. We’ll do this in a moment, but first let’s add another test.


Authorized Users Test

The test for authorized users will look like this.


Setting Up A New Policy

Let’s add that new policy object to determine if a user can delete a reply or not. We’ll start by scaffolding it out like so.

vagrant@homestead:~/Code/forumio$ php artisan make:policy ReplyPolicy
Policy created successfully.

Now we simply add the update method where we specify that in order for a user to be able to delete a reply, their user id must match the user id found on the reply in question. Of course this means that the thread belongs to them, so they can delete it.

Once complete, just make sure to register the new policy within AuthServiceProvider. The highlighted line shows our addition for the registration of the Reply policy we just created. You may recall when we had created a Thread policy in an earlier tutorial as well, the registration for that policy is just above the highlighted line.

Now you are free to authorize an update on the reply in the destroy() method of the RepliesController as we do here.

Our final tests for these functions are as follows:

Running the full suite of this test class does confirm it is all working.

vagrant@homestead:~/Code/forumio$ phpunit --filter ParticipateInForumTest
PHPUnit 6.5.5 by Sebastian Bergmann and contributors.

.....                                                               5 / 5 (100%)

Time: 1.88 seconds, Memory: 14.00MB

OK (5 tests, 10 assertions)

Adding The Delete Button In The Browser

Ok, it looks like all of the functionality is working just fine for both authorized and unauthorized users with regard to deleting replies. We can update the reply.blade.php view to allow that. Highlighted below is the markup to provide the delete button. In addition, since we used policies, we can make use of that useful @can directive to only show the button to a user that is authorized to delete a specific reply. So guests will never see a delete button, but authorized users will see a delete button for their own replies.

The logged in user now sees a delete button for is reply.
laravel can directive example

After clicking on the delete button, we can see the reply is gone. In addition, notice that there are no replies by this user. Therefore, we no longer even see the delete button at all on any remaining replies to the thread.
no delete button seen


Check Authorization With Policies Before Delete Function Summary

So a fairly straight forward example of using a policy object was given here to make sure a user is authorized to delete a reply to a thread. We had already configured a policy object during the tutorial where we set up the ability to delete threads, so this was a bit of review – but still helpful to reinforce our learning.