このエントリーの続き。
このエントリーを書いた後早々に「Laravel のドキュメントを全部読め」という旨のツッコミを頂いた。
そして、Laravel の機能でやりたいことが実現できることを学んだ。
日本語訳はこちら→ 認可 5.4 Laravel
ということで、当初は続きのエントリーとして「それぞれの方法のメリット・デメリットの比較」を書こうと思っていたのだが、公式の方法がどちらの方法にも勝るので単に Laravel の認可の機能について説明をする。
実現したいこと
前回のエントリーの内容では説明のためには不充分なので、より具体的な例を挙げる。
前回のエントリーで示した条件(再掲)
前提:Laravel 5.4 で開発している web システム
ユーザごとに「このデータは編集できる」「このデータは閲覧のみ可」といった権限管理をしたい。
登場するテーブルは以下の2つ。
(テーブル名は実際のものとは異なる。あくまでイメージ)
users
- ユーザのテーブル。後述する
groups
テーブルのid
を外部キーとして持つ。
- ユーザのテーブル。後述する
groups
- 権限グループのマスタテーブル。
id
と「閲覧のみ」「管理者」といった表示用の名前name
のみを格納。
- 権限グループのマスタテーブル。
より具体的な実現したいこと
前提
以下に説明するような posts
というテーブルがあるとする。
- 「所有者」として
user
テーブルのid
を外部キーとして持つ。 content
,category
というカラムを持つ。
目的
posts
に対応する Post という Model に対して次のような権限管理したいを考える。
○:可 ×:不可
操作 | 閲覧のみ | 所有者 | 管理者 |
---|---|---|---|
閲覧 | ○ | ○ | ○ |
新規作成 | × | - | ○ |
content の更新 |
× | ○ | ○ |
category の更新 |
× | × | ○ |
削除 | × | × | ○ |
また、「閲覧のみ」ユーザのgroups.id
は 1 、「管理者」の groups.id
は 9 とする。
実装方法
Policy の作成
app/Policies/PostPolicy.php
<?php namespace App\Policies; use App\Models\User; use App\Models\Post; class PostPolicy { /** * 全てのメソッドの前に呼ばれる * null を返した場合、各メソッドに委ねられる * * @params User $user * @return bool|null */ public function before(User $user) { // 「管理者」は全ての操作が可能 if ($user->group_id === 9) { return true; } return null; } /** * 閲覧が可能かどうか * * @params User $user * @params Post $post * @return bool */ public function view(User $user, Post $post): bool { // 全てのユーザが可能 return true; } /** * 新規作成が可能かどうか * * @params User $user * @return bool */ public function create(User $user): bool { // 「閲覧のみ」ユーザでないこと return $user->group_id !== 1; } /** * content の更新が可能か * * @params User $user * @params Post $post * @return bool */ public function updateContent(User $user, Post $post): bool { // 所有者であること return $user->id === $post->user_id; } /** * category の更新が可能か * * @params User $user * @params Post $post * @return bool */ public function updateCategory(User $user, Post $post): bool { // (「管理者」ユーザ以外)全てのユーザが不可 return false; } /** * 削除が可能か * * @params User $user * @params Post $post * @return bool */ public function delete(User $user, Post $post): bool { // (「管理者」ユーザ以外)全てのユーザが不可 return false; } }
※実際は「管理者かどうか」といった判定を Model のメソッドにしておくのがよい
Policy の登録
app/Providers/AuthServiceProvider.php
<?php namespace App\Providers; use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider; class AuthServiceProvider extends ServiceProvider { /** * アプリケーションにマップ付されたポリシー * * @var array */ protected $policies = [ App\Models\Post::class => App\Policies\PostPolicy::class, ]; /** * アプリケーションの全認証/認可サービスの登録 * * @return void */ public function boot() { $this->registerPolicies(); // ... } }
ロジック内で利用
<?php use App\Models\Post; $user = \Auth::user(); // $user が $post を閲覧できるか $user->can('view', $post); // $user が Post を新規作成できるか $user->can('create', Post::class);
Middleware として利用
許可されていない場合は 403 を返す。
routes/web.php
<?php Route::post('/posts', 'PostController@create')->middleware('can:create, App\Models\Post');
ControllerHelper
authorize()
メソッドが利用可能。こちらも許可されていない場合 403 を返す。
app/Http/Controllers/PostController.php
<?php namespace App\Http\Controllers; use App\Models\Post; use Illuminate\Http\Request; use App\Http\Controllers\Controller; class PostController extends Controller { /** * ポストの削除 * * @param Request $request * @param Post $post * @return Response */ public function delete(Request $request, Post $post) { $this->authorize('delete', $post); //... } }
Blade テンプレート内で利用
@can('updateCategory', $post) {{-- カテゴリ編集可の場合 --}} @else {{-- 不可の場合 --}} @endcan
反省
勉強不足で本来不必要な考察・議論をしていた。
まずはドキュメントを調べる、というのは基本であるはずだが、つい気が緩んでいたようだ。