tohokuaikiのチラシの裏

技術的ネタとか。

LaravelのPolicyで丸二日ほど悩む…効かないってか効きすぎるというか…

やりたいこと

apiのrouteに対して適宜認可を掛ける。対象になるのは、usersテーブル。これはLaravelのデフォルトのユーザーテーブル。

show/editできるのは、管理者とその当該ユーザーのみとか。

やったこと

routes/api.php

<?php
Route::middleware('auth')->group(function () {
    Route::resources([
        'user' => 'UserController',

として、(このauthはログイン状態を見ているだけ)

app/Http/Controllers/UserController.php にリソースコントローラの認可を設定

<?php
class UserController extends Controller
{
    /**
     * UserController constructor.
     */
    public function __construct()
    {
        $this->authorizeResource(User::class, 'user');
    }

で、ポリシーファイル app/Policies/UserPolicy.php

<?php
class UserPolicy
{
    use HandlesAuthorization;

    /**
     * Determine whether the user can view the model.
     *
     * @param  \App\User  $user
     * @param  \App\User  $model
     * @return mixed
     */
    public function view(User $user, User $model)
    {
         return $this->viewAny($user) || $user->id === $model->id;
    }

を作ってサービスプロバイダーに登録 app/Providers/AuthServiceProvider.php

<?php
class AuthServiceProvider extends ServiceProvider
{
    /**
     * The policy mappings for the application.
     *
     * @var array
     */
    protected $policies = [
        App\User::class => App\Policies\UserPolicy::class,

症状

403エラーが返ってくる

{ exception: "Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException",
  file: "/home/vagrant/myapp/vendor/laravel/framework/src/Illuminate/Foundation/Exceptions/Handler.php"
  line: 223
  message: "This action is unauthorized." }
  trace : {...}
}

みたいな。

対処方法

ControllerのメソッドDIを変えると全然違う

<?php
public function show($id)

の場合だと、403になる。というか、そもそもPolicyのview()まで来ていない。

<?php
public function show($id)

の場合だと、403になる。というか、そもそもUserPolicy::view()まで来ていない。

<?php
public function show(Request $request, User $user)

とすると、Policy::view()まで来るようになる。

ということで

Policyを使って認可する場合は、Controllerのメソッド引数も考慮しなければならないようだ。
LaravelってDIの魔法みたいなことするけど、こういうわけわからない挙動もあるのどうなんだ…