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

authorizeResource does not work with CamelCase models #22493

Closed
rehmatworks opened this issue Dec 20, 2017 · 11 comments
Closed

authorizeResource does not work with CamelCase models #22493

rehmatworks opened this issue Dec 20, 2017 · 11 comments

Comments

@rehmatworks
Copy link

rehmatworks commented Dec 20, 2017

  • Laravel Version: 5.5.25
  • PHP Version: 7.0.24
  • Database Driver & Version: MySQL (5.7.20-0ubuntu0.16.04.1)

Description:

Laravel resource policies work perfectly for generally named models but with CamelCased names, they don't.

Steps To Reproduce:

In my controller's constructor:

This doesn't work and always return unauthorized access:

public function __construct()
{
     $this->authorizeResource(MyModel::class); 
}

For a generally named model, it works:

public function __construct()
{
     $this->authorizeResource(Model::class); 
}

p.s.: The fixes suggested for the earlier versions of Laravel don't work and I already have tested.

@sisve
Copy link
Contributor

sisve commented Dec 20, 2017

Provide complete code examples and error messages for reproduction. It could just be a missing using-statement in this case.

@devcircus
Copy link
Contributor

I just set up a quick app to reproduce and everything worked as I expected.

// web.php
Route::resource('user_posts', 'UserPostsController');

If you want the parameter name to differ from the route segment, you can name the parameter. See naming resource route parameters

Example with named parameter:

// web.php
Route::resource('posts', 'UserPostsController', ['parameters' => [
    'posts' => 'user_post'
]]);
//UserPostPolicy.php
    public function view(User $user, UserPost $user_post)
    {
        return $user->id === $user_post->user_id;
    }
// AuthServiceProvider.php
    public function boot()
    {
        $this->registerPolicies();

        Gate::resource('user_post', 'UserPostPolicy');
    }
// UserPostsController.php
    public function __construct()
    {
        $this->authorizeResource(UserPost::class);
    }

    public function show(UserPost $user_post)
    {
        return $user_post;
    }

Now visit www.myapp.com/user_posts/1 and authorization will work correctly.
OR if you chose a different resource name and customized the parameter name as shown above, visit www.myapp.com/posts/1 and you'll get the same result.

I'll qualify this by saying that on the rare occasion I've needed a multi-word model, this is how I've always named everything. Probably because when I first tried, this is how I was able to get authorizeResource to work. I can't comment on any other naming conventions and whether or not they break this.

@rehmatworks
Copy link
Author

rehmatworks commented Dec 21, 2017

Thanks for your input @devcircus but still I'm not able to fix the issue. I've used the policy mapping in AuthServiceProvider as well as defined the resource gate but still, I'm reaching nowhere. Regarding your naming convention for the routes, I get a route not defined error. If I need to name resource routes, here is what I do:

Route::resource('customer-requests', 'CustomerRequestController', [
	'names' => [
		'index' => 'customer_requests'
	]
]);

I'll try your suggestions on a fresh Laravel app and will see how it behaves. Thanks again for your input.

@Miguel-Serejo
Copy link

Regarding your naming convention for the routes, I get a route not defined error.

He didn't change route names, he changed the route parameter names.

@devcircus
Copy link
Contributor

I'll post a link to the repo shortly.

@devcircus
Copy link
Contributor

Demo repo

@rehmatworks
Copy link
Author

@devcircus Thank you!

@JeffreyDavidson
Copy link

I am experiencing this same issue. Did someone find a solution to this problem?

@danszewczyk
Copy link

This is the temporary solution, that solved it for me:

$this->authorizeResource(InventoryItem::class, 'App\Models\InventoryItem');

Use the second parameter of authorizeResource to explicitly define the correct reference to the class.

@Dannyson
Copy link

Dannyson commented Nov 17, 2020

So I have come to this issue too. Mainly it is issue with naming conventions and some automatic transformations. It works well if you do it as intended, but it gets ugly when you diverge a bit. It is important to keep in mind that:

  1. Resource route sends parameter named after resource name and tries to singularize it following:
    resource name -> parameter name
  • newsPosts -> newsPost
  • news-Posts -> news_Post
  • news_Posts -> news_Post
    but it can be overriden by
Route::resource('newsPosts', [NewsPostsController::class, 'index'])->parameters(['newsPosts' => 'something_completely-different']);
  1. Model bound Controller functions
public function edit(NewsPost $newsPost) ...

expect parameter from the route named the same as in the function. But camel case works for snake case meaning route parameter something_long works for controller parameter $somethingLong

  1. The same is applied to Policy functions

  2. Authorize resource function

public function authorizeResource($model, $parameter = null, array $options = [], $request = null)
    {
        $parameter = $parameter ?: Str::snake(class_basename($model));
        $modelName = in_array($method, $this->resourceMethodsWithoutModels()) ? $model : $parameter;
        $middleware["can:{$ability},{$modelName}"][] = $method;

Gets class name, converts it to snake case and expect route parameter of this name. So if your model class is named newsPost it expects route parameter named news_post. This can be overriden by supplying parameter name like this

$this->authorizeResource(NewsPost::class, 'something_completely-different');

So if you use route resource name news-posts, it will all work. If you use newsPosts or something else, perhaps localized, you have to change the parameter name to the expected snake case model name.

Route::resource('newsPosts', [NewsPostsController::class, 'index'])->parameters(['newsPosts' => 'news_post']);

@islamazouz
Copy link

islamazouz commented Feb 10, 2021

you must put route parameter in snake case

$this->authorizeResource(CountryZone::class, 'countries_zone');

and parameter in camel case

public function edit(CountryZone $countriesZone)
{
}

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

No branches or pull requests

9 participants