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

Setting the permissions_as_gates with teams enabled breaks the can method in routes. #635

Open
Pen-y-Fan opened this issue May 14, 2023 · 1 comment

Comments

@Pen-y-Fan
Copy link

  • Laravel Version: 10.10.1
  • Laratrust Version: 8.2.1

Describe the bug

Setting the permissions_as_gates with teams enabled breaks the can method in routes. The view is returned 404 not found.

I bumped Laravel 10.7.1 to 10.10.1 and Laratrust from 8.0.1 to 8.2.1, and the app stopped working on all routes with the can method.

The app was working prior to the upgrade with all tests passing.

To Reproduce

Steps to reproduce the behavior:

New Laravel app, with Laratrust

Update the laratrust configuration file enable teams and permissions_as_gates' => true,

Add a user, policy, model(s) and team.

In web.php add a route with a can method e.g.:

Route::get('/plan', \App\Http\Controllers\Plan\Dashboard::class)
    ->can('viewAny', \App\Models\Plan::class);

This route will now fail with the error message:

404 NOT FOUND

No exceptions are logged. The Laravel debug bar does catch the exception:

No query results for model [App\Models\LaratrustTeam]. at \vendor\laravel\framework\src\Illuminate\Database\Eloquent\Builder.php:599)
[stacktrace]
"}},"userId":3,"exception":"[object] (Spatie\LaravelIgnition\Exceptions\ViewException(code: 0): No query results for model [App\Models\LaratrustTeam]. at vendor\laravel\framework\src\Illuminate\Database\Eloquent\Builder.php:599)
[stacktrace]
#0 vendor\santigarcor\laratrust\src\Helper.php(50): Illuminate\Database\Eloquent\Builder->firstOrFail()
#1 \vendor\santigarcor\laratrust\src\Checkers\User\UserDefaultChecker.php(121): Laratrust\Helper::getIdFor('App\\Models\\Even...', 'team')
#2 \vendor\santigarcor\laratrust\src\Traits\HasRolesAndPermissions.php(192): Laratrust\Checkers\User\UserDefaultChecker->currentUserHasPermission('viewAny', 'App\\Models\\Even...', false)
#3 \vendor\santigarcor\laratrust\src\LaratrustServiceProvider.php(175): App\Models\User->hasPermission('viewAny', 'App\\Models\\Even...', false)
#4 \vendor\laravel\framework\src\Illuminate\Auth\Access\Gate.php(553): Laratrust\LaratrustServiceProvider->Laratrust\{closure}(Object(App\Models\User), 'viewAny', Array)
#5 \vendor\laravel\framework\src\Illuminate\Auth\Access\Gate.php(426): Illuminate\Auth\Access\Gate->callBeforeCallbacks(Object(App\Models\User), 'viewAny', Array)
#6 \vendor\laravel\framework\src\Illuminate\Auth\Access\Gate.php(395): Illuminate\Auth\Access\Gate->raw('viewAny', Array)
#7 \vendor\laravel\framework\src\Illuminate\Auth\Access\Gate.php(382): Illuminate\Auth\Access\Gate->inspect('viewAny', 'App\\Models\\Even...')
#8 \app\View\Components\Plan\PlanList\Index.php(54): Illuminate\Auth\Access\Gate->authorize('viewAny', 'App\\Models\\Even...')
#9 [internal function]: App\View\Components\Plan\PlanList\Index->__construct(Object(Illuminate\Http\Request), Object(App\Models\Plan))
... etc...

When I run my test most failed too, the stack trace above is from one of the tests, which has the gate in the controller, the stack trace is the same for both.

The Gate in a controller can be fixed:

- Gate::forUser(auth()->user())->authorize('viewAny', Plan::class);
+ Gate::forUser(auth()->user())->authorize('plans-read', auth()->user()->currentTeam);

This used to work with model policies, something changed recently to change the behaviour.

The model policy:

public function viewAny(User $user): \Illuminate\Auth\Access\Response|bool
{
    return $user->isAbleTo('plans-read', $user->currentTeam);
}

The can method on routes can not be fixed this way.

e.g.

    Route::get('/list', static function () {
        return view('list.index');
    })
        ->can('events-read', auth()->user()?->currentTeam)

This will return:

This action is unauthorized.

Reading the stack trace the team is dropped and an empty array is passed through as arguments:

  1 => array:6 [▼
    "file" => "... \vendor\laravel\framework\src\Illuminate\Auth\Middleware\Authorize.php"
    "line" => 55
    "function" => "authorize"
    "class" => "Illuminate\Auth\Access\Gate"
    "type" => "->"
    "args" => array:2 [▼
      0 => "events-read"
      1 => []
    ]
  ]

The can method on Route:

    /**
     * Specify that the "Authorize" / "can" middleware should be applied to the route with the given options.
     *
     * @param  string  $ability
     * @param  array|string  $models
     * @return $this
     */
    public function can($ability, $models = [])
    {
        return empty($models)
                    ? $this->middleware(['can:'.$ability])
                    : $this->middleware(['can:'.$ability.','.implode(',', Arr::wrap($models))]);
    }

It doesn't accept a team.

The workaround for me was to update the config permissions_as_gates=false, the previous behaviour using policies returned and everything now works.

Looking at recent changes the Gate policy has been updated to add attributes for a team. This has unexpected consequences regarding the Route can method.

a745962

@santigarcor
Copy link
Owner

Do you know what values we are getting in the $team and $requireAll variables in the service provider in a745962?

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

2 participants