[Laravel]モデルの認可ポリシーを作る
はじめに
ポリシーとは、特定のモデルに対してアクセス権(create
,show
,edit
,update
,destroy
など)を設定するためのLaravelの機能です。認可とも呼ばれます。
認可はGate(ゲート)とPolicy(ポリシー)という2つのアクションのを提供しています。
今回はポリシーについて見ていきます。
環境
PHP 8.x
Laravel 10.x
MySQL 8.x
前提
UserモデルとListingモデルを例に進めていきます。
ログインしたユーザーが複数のリスティングにCRUDアクションを行うことができます。
リスティングの投稿者のみリスティングに対して編集、更新、削除することができます。
1. UserテーブルとListingテーブルが作成されている
2. Listingテーブルにforeign_key
制約で参照するUserテーブルのカラムを追加されている
$table->foreignId('user_id')->constrained()->onDelete('cascade');
3. ユーザーと投稿の関連付けを設定されている
モデルの関連付け
<?php
namespace App\Models;
use App\Models\Listing;
class User extends Authenticatable
{
...
public function listings()
{
return $this->hasMany(Listing::class);
}
...
}
<?php
namespace App\Models;
use App\Models\User;
class Listing extends Model
{
...
public function user()
{
return $this->belongsTo(User::class);
}
}
tl;dr
- ポリシーを作成する
- ポリシーを設定する
- ポリシーを登録する
- Listingコントローラーでポリシーを使用する
-
authorize
メソッド -
can
メソッド - middleware(ミドルウェア)
ポリシーを作成する
➜ php artisan make:policy ListingPolicy --model=Listing
INFO Policy [app/Policies/ListingPolicy.php] created successfully.
初期のListingPolicy.php
<?php
namespace App\Policies;
use App\Models\Listing;
use App\Models\User;
use Illuminate\Auth\Access\Response;
class ListingPolicy
{
/**
* Determine whether the user can view any models.
*/
public function viewAny(User $user): bool
{
//
}
/**
* Determine whether the user can view the model.
*/
public function view(User $user, Listing $listing): bool
{
//
}
/**
* Determine whether the user can create models.
*/
public function create(User $user): bool
{
//
}
/**
* Determine whether the user can update the model.
*/
public function update(User $user, Listing $listing): bool
{
//
}
/**
* Determine whether the user can delete the model.
*/
public function delete(User $user, Listing $listing): bool
{
//
}
/**
* Determine whether the user can restore the model.
*/
public function restore(User $user, Listing $listing): bool
{
//
}
/**
* Determine whether the user can permanently delete the model.
*/
public function forceDelete(User $user, Listing $listing): bool
{
//
}
}
ポリシーを設定する
コントローラーのアクションに応じてメソッドを追加していきます。
Controller | Policy |
---|---|
index | viewAny |
show | view |
create | create |
store | create |
edit | update |
update | update |
destroy | delete |
<?php
namespace App\Policies;
use App\Models\Listing;
use App\Models\User;
use Illuminate\Auth\Access\Response;
class ListingPolicy
{
public function update(User $user, Listing $listing): bool
{
// ユーザーがリスティングを編集・更新できるかどうかを確認
return $user->id === $listing->user_id;
}
public function delete(User $user, Listing $listing): bool
{
// ユーザーがリスティングを削除できるかどうかを確認
return $user->id === $listing->user_id;
}
}
ポリシーを登録する
App\Providers\AuthServiceProvider
に作成したポリシーを登録する必要があります。
<?php
namespace App\Providers;
// use Illuminate\Support\Facades\Gate;
use App\Models\Listing;
use App\Policies\ListingPolicy;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
class AuthServiceProvider extends ServiceProvider
{
protected $policies = [
Listing::class => ListingPolicy::class,
];
...
}
ポリシーを使用する
コントローラーまたはビューでポリシーを使用することで、ユーザーが編集、更新、削除のアクションを実行する際アクセス制御を行います。
authorize
メソッド
public function update(Request $request, Listing $listing)
{
$this->authorize('update', $listing);
// ユーザーはリスティングを編集できることが確認されました
// ここに更新のロジックを追加
}
authorize
の第一引数のupdate
がListingPolicy.php
ファイル内に記述したupdate
メソッドを対応します。
第二引数には、Listingモデルの変数$listing
を指定しています。
can
メソッド
can
メソッドを使う場合、auth()
ヘルパーを使って現在のユーザーを取得する必要があります。
public function update(Listing $listing){
$user = auth()->user(); //ユーザ情報を取得
if($user->can('update',$listing)){
...
}else{
...
}
}
アクセス権があるかどうかでビューの表示を切り替えたい場合@can
ディレクティブを使うことができます。
@can('update', $listing)
// 編集ボタンーを表示するなど
@else
//
@endcan
ミドルウェアによる制限
ファイルのルーティングでmiddleware
の設定を追加してアクセスを行うこともできます。
use App\Models\Listing;
Route::put('/listings/{listing}', function (Listing $listing) {
// The current user may update the post...
})->middleware('can:update,listing');
レスポンスをカスタマイズする
ユーザーが権限を持っている場合のみ操作することができるようになりました。
そうでない場合403
エラーのページが表示されます。
レスポンスの表示をカスタマイズしたい場合、ポリシーの設定を追加できます。
public function update(User $user, Post $post): Response
{
return $user->id === $post->user_id
? Response::allow()
: Response::deny('権限がありません。');
}
終わりに
モデルの認可ポリシーを作ってアクセス制限することができました!
Discussion