Политика Laravel никогда не вызывала всегда 403

#php #laravel-5.8 #laravel-middleware #policies

Вопрос:

Laravel используется в качестве конечной точки API, я пытаюсь контролировать способность пользователей видеть определенные части сайта в зависимости от того, к какому типу пользователей они относятся. Используя либо $this->authorizeResource() или $this->authorize() , он всегда возвращает 403, не затрагивая политики. Комментируя строки, приложение разрешает доступ.

AuthServiceProvider

 <?php

namespace ProjectProviders;

use ProjectEntitiesPlumber;
use IlluminateSupportFacadesGate;
use IlluminateFoundationSupportProvidersAuthServiceProvider as ServiceProvider;
use IlluminateSupportFacadesAuth;
use ProjectPoliciesPlumberPolicy;

class AuthServiceProvider extends ServiceProvider
{
    /**
     * The policy mappings for the application.
     *
     * @var array
     */
    protected $policies = [
        Plumber::class => PlumberPolicy::class
    ];

    /**
     * Register any authentication / authorization services.
     *
     * @return void
     */
    public function boot()
    {
        $this->registerPolicies();
    }
}
 

PlumberPolicy, где все возвращает true ради тестирования.

 <?php

namespace ProjectPolicies;

use ProjectEntitiesUser;
use ProjectEntitiesPlumber;
use IlluminateAuthAccessHandlesAuthorization;

class PlumberPolicy
{
    use HandlesAuthorization;

    /**
     * Determine whether the user can view the Plumber.
     *
     * @param  ProjectEntitiesUser  $user
     * @param  ProjectEntitiesPlumber  $plumber
     * @return mixed
     */
    public function view(User $user, Plumber $plumber)
    {
        //
        return true;
    }

    /**
     * Determine whether the user can create Plumbers.
     *
     * @param  ProjectEntitiesUser  $user
     * @return mixed
     */
    public function create(User $user)
    {
        //
        return true;
    }

    /**
     * Determine whether the user can update the Plumber.
     *
     * @param  ProjectEntitiesUser  $user
     * @param  ProjectEntitiesPlumber  $plumber
     * @return mixed
     */
    public function update(User $user, Plumber $plumber)
    {
        //
        return true;
    }

    /**
     * Determine whether the user can delete the Plumber.
     *
     * @param  ProjectEntitiesUser  $user
     * @param  ProjectEntitiesPlumber  $plumber
     * @return mixed
     */
    public function delete(User $user, Plumber $plumber)
    {
        //
        return true;
    }
}
 

Controllers setup.

 namespace ProjectHttpControllers;
class Controller extends BaseController
{
    use AuthorizesRequests, DispatchesJobs, ValidatesRequests;
}

namespace ProjectHttpControllers;
class ApiController extends Controller
{

    public function index(Request $request) { .... }
    public function show(Request $request, $id) { .... }
    public function store(Request $request) { .... }
    public function create(Request $request) { .... }
    public function edit(Request $request) { .... }
    public function update(Request $request, $id) { .... }
    public function destroy(Request $request, $id) { .... }
}

namespace ProjectHttpControllers;
class PlumberController extends ApiController {
    public function __construct() {
        $this->authorizeResource(ProjectEntitiesPlumber::class);
    }

    public function show(Request $request, $id) {
        return parent::show($request, $id);
    }
}
 

Я перепробовал все предложения по загрузке политики и вызову authorizeResource и authorize() со строковыми объявлениями имен классов, чтобы избежать пространств имен и без.

Переходя с помощью Xdebug, кажется, что он терпит неудачу при vendor/laravel/framework/src/Illuminate/Auth/Access/Gate.php

 public function authorize($ability, $arguments = [])
{
    $result = $this->raw($ability, $arguments);

    if ($result instanceof Response) {
        return $resu<
    }

    return $result ? $this->allow() : $this->deny();
}
 

Where $result = $this->raw($ability, $arguments); возвращает значение false . $ability = view и $arguments = пустой массив.

Я прочитал https://github.com/laravel/framework/issues/22847#issuecomment-521308861 где пользователь упоминает о необходимости передать модель в объявлении метода, чего я не могу сделать с огромной переписью системы.

Список маршрутов

 | GET|HEAD                               | api/plumber                                         | plumber.index                 | ProjectHttpControllersPlumberController@index                     | api,auth:api,role:valuer,plumber                                   |
| POST                                   | api/plumber                                         | plumber.store                 | ProjectHttpControllersPlumberController@store                     | api,auth:api,role:valuer,plumber,can:create,ProjectEntitiesplumber |
| GET|HEAD                               | api/plumber/{plumber}                               | plumber.show                  | ProjectHttpControllersPlumberController@show                      | api,auth:api,role:valuer,plumber,can:view,plumber                  |
| PUT|PATCH                              | api/plumber/{plumber}                               | plumber.update                | ProjectHttpControllersPlumberController@update                    | api,auth:api,role:valuer,plumber,can:update,plumber                |
| DELETE                                 | api/plumber/{plumber}                               | plumber.destroy               | ProjectHttpControllersPlumberController@destroy   
 

Ответ №1:

Из того, что я обнаружил, это невозможно использовать authorizeResource() . Вам нужно вызвать $this->authorize() и передать объект, так как он выдает ошибку с массивом.

 class AuthServiceProvider extends ServiceProvider
{
    protected $policies = [
        Plumber::class => PlumberPolicy::class
    ];
}
 

authorize() ожидает объект, он выдаст ошибку, если передан массив.

 class PlumberController extends ApiController
{
    public function index(Request $request) { 
        $this->authorize('viewAny', ProjectEntitiesPlumber::class);
    }
    public function update(Request $request, $id) { 
        $plumber = $this->repository->getByID($id);

        $this->authorize('update', $plumber);
    }
    public function store(Request $request) { 
        $this->authorize('create', ProjectEntitiesPlumber::class);
    }
    public function show(Request $request, $id) { 
        $plumber = $this->repository->getByID($id);

        $this->authorize('update', $plumber);
    }
    public function destroy(Request $request, $id) { 
        $plumber = $this->repository->getByID($id);
        
        $this->authorize('update', $plumber);
    }
}