简体   繁体   中英

laravel formrequest before middleware

I know, this is a complex case but maybe one of you might have an idea on how to do this.

Concept

I have the following process in my API:

  1. Process query string parameters ( FormRequest )
    • Replace key aliases by preferred keys
    • Map string parameters to arrays if an array ist expected
    • Set defaults (including Auth::user() for id -based parameters)
    • etc.
  2. Check if the user is allowed to do the request ( Middleware )
    • Using processed (validated and sanitized) query params → otherwise I had to do exceptions for every possible alias and mapping as well as checking if the paramter is checked and that doesn't seem reasonable to me.

Problem

Nevertheless, if you just assign the middleware via ->middleware('middlewareName') to the route and the FormRequest via dependency injection to the controller method, first the middleware is called and after that the FormRequest. As described above, that's not what I need.

Solution approach

I first tried dependency injection at the middleware but it didn't work.

My solution was to assign the middleware in the controller constructor. Dependency injection works here, but suddenly Auth::user() returns null .

Then, I came across the FormRequest::createFrom($request) method in \Illuminate\Foundation\Providers\FormRequestServiceProvider.php:34 and the possibility to pass the $request object to the middleware's handle() method. The result looks like this:

public function __construct(Request $request)
{
    $middleware = new MyMiddleware();

    $request = MyRequest::createFrom($request);

    $middleware->handle($request, function() {})
}

But now the request is not validated yet. Just calling $request->validated() returns nothing. So I digged a little deeper and found that $resolved->validateResolved(); is done in \Illuminate\Foundation\Providers\FormRequestServiceProvider.php:30 but that doesn't seem to trigger the validation since it throws an exception saying that this method cannot be called on null but $request isn't null :

Call to a member function validated() on null

Now, I'm completely stumped. Does anyone know how to solve this or am I just doing it wrong?

Thanks in advance!

I guess, I figured out a better way to do this.

My misconception

While middleware is doing authentication , I was doing authorization there and therefore I have to use a Gate

Resulting code

Controller

...
public function getData(MyRequest $request)
{
    $filters = $request->query();
    // execute queries
}
...

FormRequest

class MyRequest extends FormRequest
{
    /**
     * Determine if the user is authorized to make this request.
     *
     * @return bool
     */
    public function authorize()
    {
        return Gate::allows('get-data', $this);
    }

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

    /**
     * Prepare the data for validation.
     *
     * @return void
     */
    protected function prepareForValidation()
    {
        $this->replace($this->cleanQueryParameters($this->query()));
    }

    private function cleanQueryParameters($queryParams): array
    {
        $queryParams = array_filter($queryParams, function($param) {
            return is_array($param) ? count($param) : strlen($param);
        });
        $defaultStartDate = (new \DateTime())->modify('monday next week');
        $defaultEndDate = (new \DateTime())->modify('friday next week');
        $defaults = [
            'article.created_by_id' => self::getDefaultEmployeeIds(),
            'date_from' => $defaultStartDate->format('Y-m-d'),
            'date_to' => $defaultEndDate->format('Y-m-d')
        ];
        $aliases = [
            // ...
        ];
        $mapper = [
            // ...
        ];
        foreach($aliases as $alias => $key) {
            if (array_key_exists($alias, $queryParams)) {
                $queryParams[$key] = $queryParams[$alias];
                unset($queryParams[$alias]);
            }
        }
        foreach($mapper as $key => $fn) {
            if (array_key_exists($key, $queryParams)) {
                $fn($queryParams, $key);
            }
        }
        $allowedFilters = array_merge(
            Ticket::$allowedApiParameters,
            array_map(function(string $param) {
                return 'article.'.$param;
            }, TicketArticle::$allowedApiParameters)
        );
        $arrayProps = [
            // ..
        ];
        foreach($queryParams as $param => $value) {
            if (!in_array($param, $allowedFilters) && !in_array($param, ['date_from', 'date_to'])) {
                abort(400, 'Filter "'.$param.'" not found');
            }
            if (in_array($param, $arrayProps)) {                
                $queryParams[$param] = guarantee('array', $value);
            }
        }
        return array_merge($defaults, $queryParams);
    }
}

Gate

class MyGate
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Auth\Access\Response|Void
     * @throws \Symfony\Component\HttpKernel\Exception\HttpException
     */
    public function authorizeGetDataCall(User $user, MyRequest $request): Response
    {
        Log::info('[MyGate] Checking permissions …');

        if (in_array(LDAPGroups::Admin, session('PermissionGroups', []))) {
            // no further checks needed
            Log::info('[MyGate] User is administrator. No further checks needed');
            return Response::allow();
        }

        if (
            ($request->has('group') && !in_array(Group::toLDAPGroup($request->get('group')), session('PermissionGroups', []))) ||
            $request->has('owner.department') && !in_array(Department::toLDAPGroup($request->query('owner.department')), session('PermissionGroups', [])) ||
            $request->has('creator.department') && !in_array(Department::toLDAPGroup($request->query('creator.department')), session('PermissionGroups', []))
        ) {
            Log::warning('[MyGate] Access denied due to insufficient group/deparment membership', [ 'group/department' =>
                $request->has('group') ?
                    Group::toLDAPGroup($request->get('group')) :
                ($request->has('owner.department') ?
                    Department::toLDAPGroup($request->query('owner.department')) :
                ($request->has('creator.department') ?
                    Department::toLDAPGroup($request->query('creator.department')) :
                null))
            ]);
            return Response::deny('Access denied');
        }
        if ($request->has('customer_id') || $request->has('article.created_by_id')) {
            $ids = [];
            if ($request->has('customer_id')) {
                $ids = array_merge($ids, $request->query('customer_id'));
            }
            if ($request->has('article.created_by_id')) {
                $ids = array_merge($ids, $request->query('article.created_by_id'));
            }
            $users = User::find($ids);
            $hasOtherLDAPGroup = !$users->every(function($user) {
                return in_array(Department::toLDAPGroup($user->department), session('PermissionGroups', []));
            });
            if ($hasOtherLDAPGroup) {
                Log::warning('[MyGate] Access denied due to insufficient permissions to see specific other user\'s data', [ 'ids' => $ids ]);
                return Response::deny('Access denied');;
            }
        }
        if ($request->has('owner.login') || $request->has('creator.login')) {
            $logins = [];
            if ($request->has('owner.login')) {
                $logins = array_merge(
                    $logins,
                    guarantee('array', $request->query('owner.login'))
                );
            }
            if ($request->has('creator.login')) {
                $logins = array_merge(
                    $logins,
                    guarantee('array', $request->query('creator.login'))
                );
            }
            $users = User::where([ 'samaccountname' => $logins ])->get();
            $hasOtherLDAPGroup = !$users->every(function($user) {
                return in_array(Department::toLDAPGroup($user->department), session('PermissionGroups', []));
            });
            if ($hasOtherLDAPGroup) {
                Log::warning('[MyGate] Access denied due to insufficient permissions to see specific other user\'s data', [ 'logins' => $logins ]);
                return Response::deny('Access denied');
            }
        }
        Log::info('[MyGate] Permission checks passed');
        return Response::allow();
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM