Проверка правил проверки FormRequest перед авторизацией в Laravel

Я реализую API в Laravel, используя спецификацию JSON:API.

В нем у меня есть ресурс, назовем его Пруды, с отношениями «многие ко многим» с другим ресурсом, назовем его Утки.

Согласно спецификациям JSON: API, чтобы удалить такую ​​связь, я должен использовать конечную точку DELETE /пруды/{id}/отношения/утки с запросом следующего тела:

{
    "data": [
        { "type": "ducks", "id": "123" },
        { "type": "ducks", "id": "987" }
    ]
}

Это обрабатывается PondRemoveDucksRequest, который выглядит следующим образом:

<?php
...
class PondRemoveDucksRequest extends FormRequest
{
    public function authorize() 
    {
        return $this->allDucksAreRemovableByUser();
    }

    public function rules()
    {
        return [
            "data.*.type" => "required|in:ducks",
            "data.*.id" => "required|string|min:1"
        ];
    }

    protected function allDucksAreRemovableByUser(): bool
    {
        // Here goes the somewhat complex logic determining if the user is authorized 
        // to remove each and every relationship passed in the data array.
    }
}

Проблема в том, что если я отправлю тело, например:

{
    "data": [
        { "type": "ducks", "id": "123" },
        { "type": "ducks" }
    ]
}

, я получаю 500, потому что проверка авторизации запускается первой и зависит от идентификаторов, присутствующих в каждом элементе массива. В идеале я хотел бы получить ошибку 422 со стандартным сообщением о проверке правил.

Быстрое исправление, которое я вижу, заключается в добавлении проверки наличия идентификатора в метод allDucksAreRemovableByUser(), но это кажется несколько хакерским.

Есть ли лучший способ сначала проверить правила проверки, а затем перейти к части авторизации?

Заранее спасибо!

Не могли бы вы опубликовать, где вы называете правила проверки, пожалуйста?

party-ring 11.03.2019 16:02

@party-ring Я не уверен, что понял твой вопрос. Насколько я понимаю, правила проверки проверяются автоматически при использовании запроса в параметрах метода контроллера.

EmilBogumil 11.03.2019 16:07

В вашем контроллере, когда вы получаете запрос, не могли бы вы dd $request->validated(); просто посмотреть, проходит ли запрос проверку? Потому что если он проходит, то есть проблема с проверкой, а если нет, то вам нужно отклонить этот запрос :)

party-ring 11.03.2019 16:09

@party-ring Сама проверка правил, похоже, работает, поскольку она возвращает 422, если в одном из удаленных элементов указан неверный «тип». Я проверю, добавил ли $request->validated(); хотя помогает с порядком правил и проверок авторизации, спасибо!

EmilBogumil 11.03.2019 16:24

удачи в решении?

party-ring 11.03.2019 17:02

@party-ring Добавление вызова $request->validated() в контроллер ничего не изменило. Я предполагаю, что логика авторизации FormRequest выполняется перед любым кодом в методах контроллера. Что помогло, так это добавление $this->getValidatorInstance()->validated(); в начале метода authorize(). getValidatorInstance() требуется, потому что при выполнении логики авторизации экземпляр валидатора еще не создан. Таким образом, я получил стандартные сообщения об ошибках и проверку на основе rules(). Тем не менее, все еще выглядит немного грязно, я думаю о переносе его на промежуточное программное обеспечение.

EmilBogumil 11.03.2019 21:09

Проверка IMO перед авторизацией звучит неправильно. Возврат ошибок проверки пользователю, которому вообще не разрешено просматривать этот ресурс, может привести к раскрытию информации. Я бы порекомендовал защитить вашу логику авторизации от искаженных запросов.

jelhan 13.03.2019 19:45
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Symfony Station Communiqué - 7 июля 2023 г
Symfony Station Communiqué - 7 июля 2023 г
Это коммюнике первоначально появилось на Symfony Station .
Оживление вашего приложения Laravel: Понимание режима обслуживания
Оживление вашего приложения Laravel: Понимание режима обслуживания
Здравствуйте, разработчики! В сегодняшней статье мы рассмотрим важный аспект управления приложениями, который часто упускается из виду в суете...
Установка и настройка Nginx и PHP на Ubuntu-сервере
Установка и настройка Nginx и PHP на Ubuntu-сервере
В этот раз я сделаю руководство по установке и настройке nginx и php на Ubuntu OS.
Коллекции в Laravel более простым способом
Коллекции в Laravel более простым способом
Привет, читатели, сегодня мы узнаем о коллекциях. В Laravel коллекции - это способ манипулировать массивами и играть с массивами данных. Благодаря...
Как установить PHP на Mac
Как установить PHP на Mac
PHP - это популярный язык программирования, который используется для разработки веб-приложений. Если вы используете Mac и хотите разрабатывать...
1
7
1 942
4

Ответы 4

Здесь немного другой подход, чем тот, который вы пытаетесь использовать, но он может привести к желаемому результату для вас.

Если вы пытаетесь проверить, принадлежит ли данный идентификатор утки пользователю, это можно сделать в самом правиле следующим образом:

"data.*.id" => "exists:ducks,id,user_id,".Auth::user()->id

Это правило спрашивает, существует ли запись в таблице уток, которая соответствует идентификатору и где user_id — это текущий вошедший в систему user_id.

Если вы привяжете его к своим существующим правилам (required|string|min:1), используя 'bail', то он не будет запускать запрос, если сначала не будет передано три других правила:

"data.*.id" => "bail|required|string|min:1|exists:ducks,id,user_id,".Auth::user()->id

Это похоже на правильное решение в случае, если логика авторизации опирается только на отношения между пользователем и прудом. К сожалению, в моем случае это еще не все - необходимо определить вещи, когда речь идет о привилегиях пользователя, отношении к родительскому объекту пруда (на данный момент не могу найти хорошую аналогию;)) и т. д. В любом случае спасибо, я думаю, что это может быть полезным во многих других случаях.

EmilBogumil 26.04.2019 19:15

добавить $this->getValidatorInstance()->validate(); в начало метода authorize()

1 - Создайте абстрактный класс с именем «FormRequest» внутри каталога App\Requests и переопределите метод проверки разрешенных():


<?php

namespace App\Http\Requests;

use Illuminate\Validation\ValidationException;
use Illuminate\Auth\Access\AuthorizationException;
use Illuminate\Foundation\Http\FormRequest as BaseFormRequest;

abstract class FormRequest extends BaseFormRequest
{

    /**
     * Validate the class instance.
     *
     * @return void
     * @throws AuthorizationException
     * @throws ValidationException
     */
    public function validateResolved()
    {

        $validator = $this->getValidatorInstance();

        if ($validator->fails())
        {
            $this->failedValidation($validator);
        }

        if (!$this->passesAuthorization())
        {
            $this->failedAuthorization();
        }
    }

}

2 - Расширьте свои FormRequests с помощью пользовательского FormRequest

<?php

namespace App\Http\Requests\Orders;

use App\Http\Requests\FormRequest;

class StoreOrderRequest extends FormRequest
{



}


Самым простым решением, которое я нашел для решения этой проблемы, было создание небольшой черты для FormRequest и использование ее в любое время, когда вы хотите запустить проверку перед авторизацией. Посмотрите пример ниже:

<?php

namespace App\Http\Requests\Traits;

/**
 *  This trait to run the authorize after a valid validation
 */
trait AuthorizesAfterValidation
{
    /**
     * Determine if the user is authorized to make this request.
     *
     * @return bool
     */
    public function authorize()
    {
        return true;
    }

    /**
     *  Set the logic after the validation
     * 
     * @param $validator
     * @return void
     */
    public function withValidator($validator)
    {
        $validator->after(function ($validator) {
            if (! $validator->failed() && ! $this->authorizeValidated()) {
                $this->failedAuthorization();
            }
        });
    }

    /**
     *  Define the abstract method to run the logic.
     * 
     * @return void
     */
    abstract public function authorizeValidated();
}

Затем в вашем классе запроса:

<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;
use App\Http\Requests\Traits\AuthorizesAfterValidation;

class SomeKindOfRequest extends FormRequest
{
    use AuthorizesAfterValidation;

    /**
     * Determine if the user is authorized to make this request.
     *
     * @return bool
     */
    public function authorizeValidated()
    {
        return true; // <---- Set your authorization logic here
    }

    /**
     * Get the validation rules that apply to the request.
     *
     * @return array
     */
    public function rules()
    {
        return [
            //
        ];
    }
}

Источник https://github.com/laravel/framework/issues/27808#issuecomment-470394076

Другие вопросы по теме