spatie / laravel-permission

Associate users with roles and permissions
https://spatie.be/docs/laravel-permission
MIT License
12.18k stars 1.78k forks source link

Method Illuminate\Database\Query\Builder::can does not exist. in PermissionMiddleware #1169

Closed ilyaskhametov closed 5 years ago

ilyaskhametov commented 5 years ago

Stack: laravel/lumen-framework: 5.6.*, spatie/laravel-permission: ^2.37, tymon/jwt-auth: ^1.0.0

I am using multiple authentication guards in my project (users and customers). JWT authentication (registration, login, logout, etc.) works correct. But when I try to access the route given below I get this error Method Illuminate\Database\Query\Builder::can does not exist.

$router->put('/', ['middleware' => ['auth:users', 'permission:update_users'], 'uses' => 'AdminController@update']);

My config/auth.php is:

[
    'defaults' => [
        'guard' => env('AUTH_GUARD', 'customers'),
    ],
    'guards' => [
        'customers' => [
            'driver' => 'jwt',
            'provider' => 'customers',
        ],
        'users' => [
            'driver' => 'jwt',
            'provider' => 'users',
        ],
    ],
    'providers' => [
    'customers' => [
        'driver' => 'eloquent',
        'model' => \Core\Customers\Customer\Entity\Customer::class,
    ],
        'users' => [
            'driver' => 'eloquent',
            'model' => \Core\Users\Entity\User::class,
        ],
    ],
]

I also added this row to User model:

use Authenticatable, SoftDeletes, HasRoles;

Handle method of authentication middleware:

public function handle(Request $request, Closure $next, $guard = null)
{
    app('auth')->setDefaultDriver($guard);
    if (!app('auth')->guest()) return $next($request);
    return response()->json(trans('auth.invalid_token'), 401);
}

In addition, i dumped logged user in PermissionMiddleware and it shows the logged user:

public function handle($request, Closure $next, $permission)
{
    dd(app('auth')->user());

    if (app('auth')->guest()) {
        throw UnauthorizedException::notLoggedIn();
    }

    $permissions = is_array($permission)
        ? $permission
        : explode('|', $permission);

    foreach ($permissions as $permission) {
        if (app('auth')->user()->can($permission)) {
            return $next($request);
        }
    }

    throw UnauthorizedException::forPermissions($permissions);
}

Dump results (I removed standard fields like table, primary, fillable, hidden, etc.):

object(Core\Users\Entity\User)#207 (31) { ["guard_name":protected]=> string(5) "users ["exists"]=> bool(true) ["wasRecentlyCreated"]=> bool(false) ["guarded":protected]=> array(1) { [0]=> string(1) "*" } ["roleClass":"Core\Users\Entity\User":private]=> NULL ["permissionClass":"Core\Users\Entity\User":private]=> NULL }
drbyte commented 5 years ago

Thanks for the detailed report. You're investigating it the same way I would.

when I try to access the route given below I get this error Method Illuminate\Database\Query\Builder::can does not exist.

It seems clear that you understand that can() requires a proper User object, but in this case the error is telling you you've got a Query\Builder object instead. In many cases this is caused by the code only assembling a query but forgetting to call get() on it. But the code snippets you've shared don't show evidence of that.

This makes me wonder 2 things:

  1. Is the error really happening in PermissionMiddleware? Can you provide the rest of the stack trace? It may reveal other things that could be contributing to the problem.

  2. Is the problem isolated to just the route definition's middleware entry? Or is it in the named controller (either its constructor or the method you're calling in it)? What happens if you change or remove the permission middleware from the route -- how do the error messages change? These may give you other clues to the source of the problem.

It may also be something unique to Lumen ... I haven't used Lumen on anything significant in a long time, so I can only offer suggestions for things to research related to your problem.

ilyaskhametov commented 5 years ago
  1. This is really happening in PermissionMiddleware. If I add dd() in the beginning of PermissionMiddleware's handle method the error disappears and dump results are shown.

The rest of the stack trace:

Method Illuminate\Database\Query\Builder::can does not exist.
--
in Builder.php line 2816
at Builder->__call('can', array('create_users')) in Builder.php line 1286
at Builder->__call('can', array('create_users')) in Model.php line 1570
at Model->__call('can', array('create_users')) in PermissionMiddleware.php line 21
at PermissionMiddleware->handle(object(Request), object(Closure), 'create_users') in Pipeline.php line 151
at Pipeline->Illuminate\Pipeline\{closure}(object(Request))
at call_user_func(object(Closure), object(Request)) in Pipeline.php line 32
at Pipeline->Laravel\Lumen\Routing\{closure}(object(Request)) in Authenticate.php line 42
at Authenticate->handle(object(Request), object(Closure), 'users') in Pipeline.php line 151
at Pipeline->Illuminate\Pipeline\{closure}(object(Request))
at call_user_func(object(Closure), object(Request)) in Pipeline.php line 32
at Pipeline->Laravel\Lumen\Routing\{closure}(object(Request))in Pipeline.php line 104
at Pipeline->then(object(Closure)) in RoutesRequests.php line 410
at Application->sendThroughPipeline(array('App\\Http\\Middleware\\Authenticate:users', 'Spatie\\Permission\\Middlewares\\PermissionMiddleware:create_users'), object(Closure)) in RoutesRequests.php line 256
at Application->handleFoundRoute(array(true, array('middleware' => array('auth:users', 'permission:create_users'), 'uses' => 'App\\Http\\Controllers\\Users\\AdminController@create'), array())) in RoutesRequests.php line 160
at Application->Laravel\Lumen\Concerns\{closure}(object(Request))
at call_user_func(object(Closure), object(Request)) in Pipeline.php line 52
at Pipeline->Laravel\Lumen\Routing\{closure}(object(Request)) in HandlePreflight.php line 29
at HandlePreflight->handle(object(Request), object(Closure)) in Pipeline.php line 151
at Pipeline->Illuminate\Pipeline\{closure}(object(Request))
at call_user_func(object(Closure), object(Request)) in Pipeline.php line 32
at Pipeline->Laravel\Lumen\Routing\{closure}(object(Request)) in PreprocessRequests.php line 30
at PreprocessRequests->handle(object(Request), object(Closure)) in Pipeline.php line 151
at Pipeline->Illuminate\Pipeline\{closure}(object(Request))
at call_user_func(object(Closure), object(Request)) in Pipeline.php line 32
at Pipeline->Laravel\Lumen\Routing\{closure}(object(Request)) in HandleCors.php line 36
at HandleCors->handle(object(Request), object(Closure)) in Pipeline.php line 151
at Pipeline->Illuminate\Pipeline\{closure}(object(Request))
at call_user_func(object(Closure), object(Request)) in Pipeline.php line 32
at Pipeline->Laravel\Lumen\Routing\{closure}(object(Request)) in Pipeline.php line 104
at Pipeline->then(object(Closure)) in RoutesRequests.php line 410
at Application->sendThroughPipeline(array('Barryvdh\\Cors\\HandleCors', 'App\\Http\\Middleware\\PreprocessRequests', 'Barryvdh\\Cors\\HandlePreflight'),object(Closure)) in RoutesRequests.php line 166
at Application->dispatch(null) in RoutesRequests.php line 107
at Application->run() in index.php line 28
  1. If I remove permission middleware from the route, the error disappears and everything works as expected.
drbyte commented 5 years ago

Your issue appears to be very app-specific, and not something related to this package.

Two examples of this include:

If you're going to use Gate methods like can(), doesn't your User model need to use the Authorizable trait? Like the default Lumen User model does. You posted that you're only using use Authenticatable, SoftDeletes, HasRoles;

Your App\\Http\\Middleware\\Authenticate middleware seems very different from the default Lumen Authenticate middleware.