Why does the combination of Laravel Gate and Policy return NULL when logging in to a message?

205 Views Asked by At

In the Laravel 10 application, I use authorization of actions using Gate, Policy and Spatie. The authorization of the event itself works fine, that is, where it is necessary and who it is necessary to let and does not let, respectively.

However, there was a small problem with the following case: the user is not an admin, has no privileges in any way, respectively, the before method returns false to check the administrator role. Next, a check should be run in the canEditStatus method, from where a response with a code and an error message should be returned, since, again, the user does not have privileges. Instead of the message and the code, NULL is returned, although the check really failed.

Below are the sources with the policy, controller, and response in Postman.

app\Policies\UserPolicy.php

<?php

namespace App\Policies;

use App\Models\User;
use Illuminate\Auth\Access\HandlesAuthorization;
use Illuminate\Auth\Access\Response;

class UserPolicy
{
    use HandlesAuthorization;

    private const ACCESS_ALLOW_MESSAGE = 'Ok';

    private const ACCESS_DENIED_MESSAGE = 'Access denied';

    public function before(User $user): bool
    {
        return $user->hasRole('admin'); // <-- it doesn't pass, that's right
    }

    public function canEditStatus(User $user): Response
    {
        if ($user->hasPermissionTo('edit status')) {
            return $this->allow(self::ACCESS_ALLOW_MESSAGE, 200);
        } else {
            return $this->deny(self::ACCESS_DENIED_MESSAGE, 403); // <-- expect this answer
        }
    }
}

app\Http\Controllers\Api\UserController.php

<?php

namespace App\Http\Controllers\Api;

use App\Http\Requests\Api\UserUpdateRequest;
use App\Http\Resources\UserResource;
use App\Models\User;
use Illuminate\Database\RecordsNotFoundException;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Gate;

class UserController extends ApiController
{
    public function update(UserUpdateRequest $updateRequest, string $id): JsonResponse
    {
        if ($updateRequest->has('is_active')) {
            $authorize = Gate::inspect('can-edit-status', User::class);
            if ($authorize->denied()) {
                dd($authorize); // <-- authorization result dump
                abort($authorize->code(), $authorize->message());
            }
        }

        // next is the update logic
    }
}

And, in fact, the response to the request from Postman:

Response to the request from Postman

1

There are 1 best solutions below

3
On

As mentioned by the comment, it is possible this is not hitting the actual code. enter code here In my experience, laravel policies should be referenced exactly like the method definition and accessed through the user:

<?php

namespace App\Http\Controllers\Api;

use App\Http\Requests\Api\UserUpdateRequest;
use App\Http\Resources\UserResource;
use App\Models\User;
use Illuminate\Database\RecordsNotFoundException;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Gate;

class UserController extends ApiController
{
    public function update(UserUpdateRequest $updateRequest, string $id): JsonResponse
    {
        if ($updateRequest->has('is_active')) {
            if ($updateRequest->user()->cannot('canEditStatus')) {
                dd($authorize); // <-- authorization result dump
                abort($authorize->code(), $authorize->message());
            }
        }

        // next is the update logic
    }
}