Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[5.2] Fix missing middleware parameters when using authorizeResource() #14592

Merged
merged 2 commits into from
Aug 4, 2016

Conversation

misenhower
Copy link
Contributor

@misenhower misenhower commented Aug 2, 2016

This fixes an issue with the authorizeResource method in the AuthorizesResources trait. Previously, the middleware would not be properly applied to some of the controller's methods.

As an example, say you have a PostsController class that uses authorizeResource as follows:

class PostsController extends Controller
{
   function __construct()
   {
        $this->authorizeResource(\App\Post::class);
   }
}

Let's say this controller is registered as a resource:

Route::singularResourceParameters();
Route::resource('posts', 'PostsController');

Running php artisan route:list will reveal that the controller's create and edit methods do not have the proper middleware applied:

+--------+-----------+-------------------+---------------+----------------------------------------------+-------------------------+
| Domain | Method    | URI               | Name          | Action                                       | Middleware              |
+--------+-----------+-------------------+---------------+----------------------------------------------+-------------------------+
|        | GET|HEAD  | /                 |               | Closure                                      | web                     |
|        | POST      | posts             | posts.store   | App\Http\Controllers\PostsController@store   | web,can:create,App\Post |
|        | GET|HEAD  | posts             | posts.index   | App\Http\Controllers\PostsController@index   | web,can:view,App\Post   |
|        | GET|HEAD  | posts/create      | posts.create  | App\Http\Controllers\PostsController@create  | web                     |
|        | DELETE    | posts/{post}      | posts.destroy | App\Http\Controllers\PostsController@destroy | web,can:delete,post     |
|        | PUT|PATCH | posts/{post}      | posts.update  | App\Http\Controllers\PostsController@update  | web,can:update,post     |
|        | GET|HEAD  | posts/{post}      | posts.show    | App\Http\Controllers\PostsController@show    | web,can:view,post       |
|        | GET|HEAD  | posts/{post}/edit | posts.edit    | App\Http\Controllers\PostsController@edit    | web                     |
+--------+-----------+-------------------+---------------+----------------------------------------------+-------------------------+

This happens because authorizeResource repeatedly calls $this->middleware() (here), which replaces the entry in the middleware each time it is called (here).

This was missed by the unit tests for this method because the tests only verified the presence of middleware by name -- the tests didn't make sure the middleware was actually being applied to the correct methods.

After applying this fix, the routing table will appear as follows:

+--------+-----------+-------------------+---------------+----------------------------------------------+-------------------------+
| Domain | Method    | URI               | Name          | Action                                       | Middleware              |
+--------+-----------+-------------------+---------------+----------------------------------------------+-------------------------+
|        | GET|HEAD  | /                 |               | Closure                                      | web                     |
|        | POST      | posts             | posts.store   | App\Http\Controllers\PostsController@store   | web,can:create,App\Post |
|        | GET|HEAD  | posts             | posts.index   | App\Http\Controllers\PostsController@index   | web,can:view,App\Post   |
|        | GET|HEAD  | posts/create      | posts.create  | App\Http\Controllers\PostsController@create  | web,can:create,App\Post |
|        | DELETE    | posts/{post}      | posts.destroy | App\Http\Controllers\PostsController@destroy | web,can:delete,post     |
|        | PUT|PATCH | posts/{post}      | posts.update  | App\Http\Controllers\PostsController@update  | web,can:update,post     |
|        | GET|HEAD  | posts/{post}      | posts.show    | App\Http\Controllers\PostsController@show    | web,can:view,post       |
|        | GET|HEAD  | posts/{post}/edit | posts.edit    | App\Http\Controllers\PostsController@edit    | web,can:update,post     |
+--------+-----------+-------------------+---------------+----------------------------------------------+-------------------------+

The same issue appears in Laravel 5.3, but I am guessing the middleware names applied in this method might be changing since the default Policy stub for resources includes methods like viewAny in addition to view.

Previously, the middleware would not be applied to the create and edit methods since calling middleware() overwrites any previous definitions with a matching name.
@misenhower misenhower force-pushed the fix-authorize-resource branch from 946c66d to 8b55fbd Compare August 2, 2016 06:03
@JosephSilber
Copy link
Member

Overall this looks good to me 👍

Should we maybe add a comment there in the code explaining what's going on? Without knowing about the middleware overriding one another, that code looks little weird.

{
$this->assertTrue(
in_array($middleware, array_keys($controller->getMiddleware())),
in_array($middleware, array_keys($controller->getMiddleware()))
&& in_array($method, $controller->getMiddleware()[$middleware]['only']),
"The [{$middleware}] middleware was not registered"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You may also want to add the method to the error message.

@misenhower misenhower force-pushed the fix-authorize-resource branch from e506a53 to 2b92896 Compare August 3, 2016 18:54
@misenhower
Copy link
Contributor Author

Great feedback, thanks. After thinking about it I realized it might be better to simulate each request in the test to avoid relying on implementation details of the controller class.

Let me know if you notice any other issues. Thanks!

@taylorotwell taylorotwell merged commit bf1b486 into laravel:5.2 Aug 4, 2016
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants