イントロダクションIntroduction
Laravelは組み込み済みの認証サービスに加え、認可ロジックを取りまとめ、リソースへのアクセスをコントロールする簡単な手段を提供します。認可ロジックを組織立てるのに役立つメソッドやヘルパはたくさん用意されていますので、このドキュメントで紹介します。In addition to providing authentication[/docs/{{version}}/authentication] services out of the box, Laravel also provides a simple way to organize authorization logic and control access to resources. There are a variety of methods and helpers to assist you in organizing your authorization logic, and we'll cover each of them in this document.
アビリティの定義Defining Abilities
あるユーザが指定されたアクションを実行しても良いかをシンプルに判定するには、Illuminate\Auth\Access\Gate
クラスを使い「アビリティ(ability)」を定義してください。Laravelに初めから用意されているAuthServiceProvider
は、アプリケーションの全アビリティを定義するために便利な場所です。例として現在のUser
とPost
モデルを受け取る、update-post
アビリティを定義してみましょう。このアビリティの中で、ユーザのid
がポストのuser_id
と一致するかを判定します。The simplest way to determine if a user may perform a given action is to define an "ability" using the Illuminate\Auth\Access\Gate
class. The AuthServiceProvider
which ships with Laravel serves as a convenient location to define all of the abilities for your application. For example, let's define an update-post
ability which receives the current User
and a Post
model[/docs/{{version}}/eloquent]. Within our ability, we will determine if the user's id
matches the post's user_id
:
<?php
namespace App\Providers;
use Illuminate\Contracts\Auth\Access\Gate as GateContract;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
class AuthServiceProvider extends ServiceProvider
{
/**
* 全認証/認可サービスの登録
*
* @param \Illuminate\Contracts\Auth\Access\Gate $gate
* @return void
*/
public function boot(GateContract $gate)
{
$this->registerPolicies($gate);
$gate->define('update-post', function ($user, $post) {
return $user->id == $post->user_id;
});
}
}
渡ってきた$user
がNULL
ではないかの、チェックを行っていないことに注目です。Gate
はユーザが認証されていないか、forUser
メソッドを使いユーザが指定されていない場合は、全アビリティから自動的にfalse
を返します。Note that we did not check if the given $user
is not NULL
. The Gate
will automatically return false
for all abilities when there is not an authenticated user or a specific user has not been specified using the forUser
method.
クラスベースのアビリティClass Based Abilities
認可のコールバックとして「クロージャ」を登録する方法に加え、クラス名とメソッドの文字列を引数に渡してもクラスメソッドを登録できます。必要であれば、クラスはサービスコンテナを利用し、依存解決されます。In addition to registering Closures
as authorization callbacks, you may register class methods by passing a string containing the class name and the method. When needed, the class will be resolved via the service container[/docs/{{version}}/container]:
$gate->define('update-post', 'Class@method');
認可チェックの停止Intercepting Authorization Checks
場合により、特定のユーザに対しては全アビリティを許可したい場合があります。この場合は他の全認可チェックの前に実行されるコールバックを定義するbefore
メソッドを使ってください。Sometimes, you may wish to grant all abilities to a specific user. For this situation, use the before
method to define a callback that is run before all other authorization checks:
$gate->before(function ($user, $ability) {
if ($user->isSuperAdmin()) {
return true;
}
});
before
コールバックがnullではない結果を返した場合、それをチェック結果として取り扱います。If the before
callback returns a non-null result that result will be considered the result of the check.
after
メソッドを使い、全部の認可のチェックを行った後に実行するコールバックを定義できます。しかしafter
のコールバックの中から認可チェックの結果を変更できません。You may use the after
method to define a callback to be executed after every authorization check. However, you may not modify the result of the authorization check from an after
callback:
$gate->after(function ($user, $ability, $result, $arguments) {
//
});
アビリティの確認Checking Abilities
Gateファサードによる確認Via The Gate Facade
アビリティを定義したら、さまざまな方法で「確認」できます。最初はGate
ファサードのcheck
、allow
、denies
メソッドを使う方法です。これらのメソッドはアビリティ名とコールバックに渡した引数を受け取ります。Gate
が自動的に現在のユーザをコールバック渡す引数の先頭に付け加えるため、こうしたメソッドに現在のユーザを渡す必要はありません。ですから既に定義したupdate-post
アビリティを確認する場合、denies
メソッドにはPost
インスタンスを渡す必要があるだけです。Once an ability has been defined, we may "check" it in a variety of ways. First, we may use the check
, allows
, or denies
methods on the Gate
facade[/docs/{{version}}/facades]. All of these methods receive the name of the ability and the arguments that should be passed to the ability's callback. You do not need to pass the current user to these methods, since the Gate
will automatically prepend the current user to the arguments passed to the callback. So, when checking the update-post
ability we defined earlier, we only need to pass a Post
instance to the denies
method:
<?php
namespace App\Http\Controllers;
use Gate;
use App\User;
use App\Post;
use App\Http\Controllers\Controller;
class PostController extends Controller
{
/**
* 指定したポストの更新
*
* @param int $id
* @return Response
*/
public function update($id)
{
$post = Post::findOrFail($id);
if (Gate::denies('update-post', $post)) {
abort(403);
}
// ポスト更新処理…
}
}
当然のことながら、allows
メソッドはdenies
メソッドをただひっくり返した働きをし、アクションが認可されていればtrue
を返します。check
メソッドはallows
メソッドのエイリアスです。Of course, the allows
method is simply the inverse of the denies
method, and returns true
if the action is authorized. The check
method is an alias of the allows
method.
特定ユーザのアビリティ確認Checking Abilities For Specific Users
現在認証されているユーザではなく、別のユーザが指定したアビリティを持っているかをGate
ファサードで確認したい場合は、forUser
メソッドを使います。If you would like to use the Gate
facade to check if a user other than the currently authenticated user has a given ability, you may use the forUser
method:
if (Gate::forUser($user)->allows('update-post', $post)) {
//
}
複数の引数の指定Passing Multiple Arguments
もちろんアビリティのコールバックには複数の引数が指定できます。Of course, ability callbacks may receive multiple arguments:
Gate::define('delete-comment', function ($user, $post, $comment) {
//
});
アビリティで複数の引数が必要であれば、Gate
ファサードのメソッドに引数を配列として渡してください。If your ability needs multiple arguments, simply pass an array of arguments to the Gate
methods:
if (Gate::allows('delete-comment', [$post, $comment])) {
//
}
Userモデルによる確認Via The User Model
他のやり方として、アビリティをUser
モデルインスタンスでも確認できます。LaravelのApp\User
モデルは、can
とcannot
メソッドを提供しているAuthorizable
トレイトをuseしています。これらのメソッドは、Gate
ファサードのallow
とdenies
メソッドと使い方が似ています。では、前の例と同様に、コードを次のように変更してみましょう。Alternatively, you may check abilities via the User
model instance. By default, Laravel's App\User
model uses an Authorizable
trait which provides two methods: can
and cannot
. These methods may be used similarly to the allows
and denies
methods present on the Gate
facade. So, using our previous example, we may modify our code like so:
<?php
namespace App\Http\Controllers;
use App\Post;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
class PostController extends Controller
{
/**
* 指定したポストの更新
*
* @param \Illuminate\Http\Request $request
* @param int $id
* @return Response
*/
public function update(Request $request, $id)
{
$post = Post::findOrFail($id);
if ($request->user()->cannot('update-post', $post)) {
abort(403);
}
// ポストの更新処理…
}
}
もちろん、can
メソッドは単にcannot
メソッドの真反対の働きです。Of course, the can
method is simply the inverse of the cannot
method:
if ($request->user()->can('update-post', $post)) {
// Update Post...
}
Bladeテンプレートでの確認Within Blade Templates
現在の認証ユーザが指定したアビリティを持っているかを簡単に確認するのに便利なように、Laravelは@can
Bladeディレクティブを用意しています。例を見てください。For convenience, Laravel provides the @can
Blade directive to quickly check if the currently authenticated user has a given ability. For example:
<a href="/post/{{ $post->id }}">ポスト表示</a>
@can('update-post', $post)
<a href="/post/{{ $post->id }}/edit">ポスト編集</a>
@endcan
@can
ディレクティブは、@else
ディレクティブと組み合わせても使えます。You may also combine the @can
directive with @else
directive:
@can('update-post', $post)
<!-- 現在のユーザはポストを更新可能 -->
@else
<!-- 現在のユーザはポストを更新不可 -->
@endcan
フォームリクエストでの確認Within Form Requests
Gate
で定義したアビリティをフォームリクエストのauthorize
メソッドで活用する選択を取ることもできます。You may also choose to utilize your Gate
defined abilities from a form request's[/docs/{{version}}/validation#form-request-validation] authorize
method. For example:
/**
* ユーザがこのリクエストを作成できる認可があるかの判定
*
* @return bool
*/
public function authorize()
{
$postId = $this->route('post');
return Gate::allows('update', Post::findOrFail($postId));
}
ポリシーPolicies
ポリシーの作成Creating Policies
全認可ロジックをAuthServiceProvider
の中で定義するのは、大きなアプリケーションでは厄介ですから、Laravelでは認可ロジックを「ポリシー」クラスに分割できます。ポリシーは普通のPHPクラスで、認可するリソースに基づいてロジックをグループ分けするものです。Since defining all of your authorization logic in the AuthServiceProvider
could become cumbersome in large applications, Laravel allows you to split your authorization logic into "Policy" classes. Policies are plain PHP classes that group authorization logic based on the resource they authorize.
始めにPost
モデルの認可を管理するポリシーを生成しましょう。make:policy
Artisanコマンドでポリシーを生成します。生成したポリシーはapp/Policies
ディレクトリへ設置されます。First, let's generate a policy to manage authorization for our Post
model. You may generate a policy using the make:policy
artisan command[/docs/{{version}}/artisan]. The generated policy will be placed in the app/Policies
directory:
php artisan make:policy PostPolicy
ポリシーの登録Registering Policies
ポリシーができたら、Gate
クラスで登録する必要があります。AuthServiceProvider
には様々なエンティティを管理するポリシーとマップするためのpolicies
プロパティを含んでいます。では、Post
モデルのポリシーであるPostPolicy
クラスを指定しましょう。Once the policy exists, we need to register it with the Gate
class. The AuthServiceProvider
contains a policies
property which maps various entities to the policies that manage them. So, we will specify that the Post
model's policy is the PostPolicy
class:
<?php
namespace App\Providers;
use App\Post;
use App\Policies\PostPolicy;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
class AuthServiceProvider extends ServiceProvider
{
/**
* アプリケーションにマップするポリシー
*
* @var array
*/
protected $policies = [
Post::class => PostPolicy::class,
];
/**
* アプリケーションの全認証/認可サービスを登録
*
* @param \Illuminate\Contracts\Auth\Access\Gate $gate
* @return void
*/
public function boot(GateContract $gate)
{
$this->registerPolicies($gate);
}
}
ポリシーの記述Writing Policies
ポリシーを生成し登録したら、認可する各アビリティのためのメソッドを追加できます。指定したUser
がPost
を「更新(update)」できるかを判定する、update
メソッドをPostPolicy
に例として定義してみます。Once the policy has been generated and registered, we can add methods for each ability it authorizes. For example, let's define an update
method on our PostPolicy
, which will determine if the given User
can "update" a Post
:
<?php
namespace App\Policies;
use App\User;
use App\Post;
class PostPolicy
{
/**
* ユーザにより指定ポストが更新できるかを判定
*
* @param \App\User $user
* @param \App\Post $post
* @return bool
*/
public function update(User $user, Post $post)
{
return $user->id === $post->user_id;
}
}
認可する様々なアビリティを必要なだけポリシーにメソッドとして追加定義してください。たとえばshow
やdestroy
、addComment
メソッドなど、多様なPost
アクションの認可が定義できるでしょう。You may continue to define additional methods on the policy as needed for the various abilities it authorizes. For example, you might define show
, destroy
, or addComment
methods to authorize various Post
actions.
注目: 全ポリシーはLaravelのサービスコンテナにより依存解決されます。つまりポリシーのコンストラクターに必要な依存をタイプヒントで指定すれば、自動的に挿入されます。Note: All policies are resolved via the Laravel service container[/docs/{{version}}/container], meaning you may type-hint any needed dependencies in the policy's constructor and they will be automatically injected.
全チェックの停止Intercepting All Checks
場合により特定のユーザにポリシーの全アビリティを許可したい場合があります。この状況ではポリシーにbefore
メソッドを定義してください。このメソッドはポリシーにある他の全認可チェックより先に実行されます。Sometimes, you may wish to grant all abilities to a specific user on a policy. For this situation, define a before
method on the policy. This method will be run before all other authorization checks on the policy:
public function before($user, $ability)
{
if ($user->isSuperAdmin()) {
return true;
}
}
before
メソッドがnullでない値を返した場合、それをチェックの結果として取り扱います。If the before
method returns a non-null result that result will be considered the result of the check.
ポリシーの確認Checking Policies
ポリシーメソッドはクロージャベースの認可コールバックと全く同じ方法で呼び出します。Gate
ファサードやUser
モデル、@can
Bladeディレクティブ、policy
ヘルパを使用できます。Policy methods are called in exactly the same way as Closure
based authorization callbacks. You may use the Gate
facade, the User
model, the @can
Blade directive, or the policy
helper.
Gateファサードによる確認Via The Gate Facade
Gate
はメソッドに渡された引数のクラスを調べ、どのポリシーを使用するか自動的に決定します。ですからPost
インスタンスをdenies
メソッドに渡せば、Gate
は認可するアクションに合ったPostPolicy
を使用します。The Gate
will automatically determine which policy to use by examining the class of the arguments passed to its methods. So, if we pass a Post
instance to the denies
method, the Gate
will utilize the corresponding PostPolicy
to authorize actions:
<?php
namespace App\Http\Controllers;
use Gate;
use App\User;
use App\Post;
use App\Http\Controllers\Controller;
class PostController extends Controller
{
/**
* 指定ポストの更新
*
* @param int $id
* @return Response
*/
public function update($id)
{
$post = Post::findOrFail($id);
if (Gate::denies('update', $post)) {
abort(403);
}
// ポストの更新処理…
}
}
Userモデルによる確認Via The User Model
User
モデルのcan
とcannot
メソッドも引数に指定された引数に対する使用可能なポリシーがあれば、自動的に使用します。これらのメソッドはアプリケーションが取得したUser
インスタンス全てに対するアクションを認可するために、便利な手法を提供しています。The User
model's can
and cannot
methods will also automatically utilize policies when they are available for the given arguments. These methods provide a convenient way to authorize actions for any User
instance retrieved by your application:
if ($user->can('update', $post)) {
//
}
if ($user->cannot('update', $post)) {
//
}
Bladeテンプレートでの確認Within Blade Templates
同様に、@can
Bladeディレクティブも与えられた引数に対して使用可能なポリシーを活用します。Likewise, the @can
Blade directive will utilize policies when they are available for the given arguments:
@can('update', $post)
<!-- 現在のユーザはポストを更新できる -->
@endcan
policyヘルパによる確認Via The Policy Helper
グローバルなpolicy
ヘルパ関数は、指定されたクラスインスタンスに対する「ポリシー」クラスを取得するために使用します。例えば、Post
インスタンスをpolicy
ヘルパに渡し、対応するPostPolicy
クラスのインスタンスを取得できます。The global policy
helper function may be used to retrieve the Policy
class for a given class instance. For example, we may pass a Post
instance to the policy
helper to get an instance of our corresponding PostPolicy
class:
if (policy($post)->update($user, $post)) {
//
}
コントローラの認可Controller Authorization
App\Http\Controllers\Controller
ベースクラスはデフォルト状態で、Laravelが利用するAuthorizesRequests
トレイトを含んでいます。このトレイトは指定したアクションを簡単に認可するためのauthorize
メソッドを持っており、そのアクションを認可できない場合は、AuthorizationException
例外を投げます。By default, the base App\Http\Controllers\Controller
class included with Laravel uses the AuthorizesRequests
trait. This trait provides the authorize
method, which may be used to quickly authorize a given action and throw a AuthorizationException
if the action is not authorized.
authorize
メソッドはGate::allows
や$user->can()
などの認可メソッドと同じ使用方法です。では、Post
を更新するリクエストを手っ取り早く認可するために、authorize
メソッドを用いてみましょう。The authorize
method shares the same signature as the various other authorization methods such as Gate::allows
and $user->can()
. So, let's use the authorize
method to quickly authorize a request to update a Post
:
<?php
namespace App\Http\Controllers;
use App\Post;
use App\Http\Controllers\Controller;
class PostController extends Controller
{
/**
* 指定したポストの更新
*
* @param int $id
* @return Response
*/
public function update($id)
{
$post = Post::findOrFail($id);
$this->authorize('update', $post);
// ポストの更新処理…
}
}
アクションが認可されていれば、コントローラは通常通り続けて実行されます。しかしauthorize
メソッドがアクションを非認可と判断すると、AuthorizationException
が自動的に投げられ、 403 Not Authorized
ステータスコードのHTTPレスポンスが生成されます。ご覧の通りにauthorize
メソッドはコード1行でアクションを認可し、例外を投げるための便利で手っ取り早い方法です。If the action is authorized, the controller will continue executing normally; however, if the authorize
method determines that the action is not authorized, a AuthorizationException
will automatically be thrown which generates a HTTP response with a 403 Not Authorized
status code. As you can see, the authorize
method is a convenient, fast way to authorize an action or throw an exception with a single line of code.
AuthorizesRequests
トレイトは、現在認証中ではない別のユーザのアクションを認可する、authorizeForUser
メソッドも提供しています。The AuthorizesRequests
trait also provides the authorizeForUser
method to authorize an action on a user that is not the currently authenticated user:
$this->authorizeForUser($user, 'update', $post);
ポリシーメソッドの自動決定Automatically Determining Policy Methods
ポリシーのメソッドはコントローラのメソッドと頻繁に対応します。たとえば前記のupdate
メソッドのように、update
と言う名前がコントローラメソッドとポリシーメソッドで共通です。Frequently, a policy's methods will correspond to the methods on a controller. For example, in the update
method above, the controller method and the policy method share the same name: update
.
そのため、Laravelはauthorize
メソッドの引数にインスタンスだけを渡すことも許しています。認可するアビリティは呼び出しているメソッドの名前を元に自動的に決められます。この例では、コントローラのupdate
メソッドからauthorize
が呼びだされていますから、PostPolicy
上でもupdate
メソッドが呼びだされます。For this reason, Laravel allows you to simply pass the instance arguments to the authorize
method, and the ability being authorized will automatically be determined based on the name of the calling function. In this example, since authorize
is called from the controller's update
method, the update
method will also be called on the PostPolicy
:
/**
* 指定ポストの更新
*
* @param int $id
* @return Response
*/
public function update($id)
{
$post = Post::findOrFail($id);
$this->authorize($post);
// ポストの更新…
}