もがき系プログラマの日常

もがき系エンジニアの勉強したこと、日常のこと、気になっている技術、備忘録などを紹介するブログです。

Laravel Sanctumのアビリティ試した

はじめに

こんばんは。

前回の続きです。

kojirooooocks.hatenablog.com

短いですがアビリティ試してみました。

本題

前回作ったルートを以下のように修正・追加してみました。

<?php

use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Route;
use Illuminate\Support\Facades\Hash;
use Illuminate\Validation\ValidationException;

// register
route::post('/register', function (request $request) {
    $validatedData = $request->validate([
        'name'     => 'required|string|max:255',
        'email'    => 'required|email|max:255|unique:users',
        'password' => 'required|string|min:16',
    ]);
    $validatedData['password'] = Hash::make($validatedData['password']);
    $user = User::create($validatedData);
    return response()->json(['access_token' => $user->createToken('auth_token', ['user'])->plainTextToken]);
});

// normal user login
route::post('/login/super', function (request $request) {
    $credentials = $request->only('email', 'password');
    if (!Auth::attempt($credentials)) {
        return response()->json(['message' => 'login error...'], 401);
    }
    $user = User::where('email', $request['email'])->firstOrFail();
    return response()->json(['access_token' => $user->createToken('auth_token', ['super', 'user'])->plainTextToken]);
});

// super user login
route::post('/login', function (request $request) {
    $credentials = $request->only('email', 'password');
    if (!Auth::attempt($credentials)) {
        return response()->json(['message' => 'login error...'], 401);
    }
    $user = User::where('email', $request['email'])->firstOrFail();
    return response()->json(['access_token' => $user->createToken('auth_token', ['user'])->plainTextToken]);
});

// logout
Route::middleware(['auth:sanctum', 'ability:user'])->get('/logout', function (Request $request) {
  $request->user()->tokens()->delete();
  return "OK";
});

// get myself
Route::middleware(['auth:sanctum', 'ability:user'])->get('/user/myself', function (Request $request) {
    return $request->user();
});

// get user
Route::middleware(['auth:sanctum', 'ability:super'])->get('/user/{user}', function (User $user) {
    return $user;
});

登録時は user というアビリティが付与されます。

そしてログインが二通りあって、 /login/super の場合は user 以外に super のアビリティが付与されます。

super が付与されている場合のみ 自分以外の他のユーザーの情報が見れる /user/{user} にアクセスできます。

データの事前準備

事前に ユーザーデータを2件登録しておきます。

mysql> select * from users;
+----+----------+--------------------------+-------------------+--------------------------------------------------------------+----------------+---------------------+---------------------+
| id | name     | email                    | email_verified_at | password                                                     | remember_token | created_at          | updated_at          |
+----+----------+--------------------------+-------------------+--------------------------------------------------------------+----------------+---------------------+---------------------+
|  1 | kojirock | example@xxx.com          | NULL              | $2y$10$b620QxzvBSd5Vs.lmQQEMulfwq5joOv5zvT1FCoC.pc44LYaLaRYq | NULL           | 2022-07-10 07:55:27 | 2022-07-10 07:55:27 |
|  2 | kojikoji | example_kojikoji@xxx.com | NULL              | $2y$10$6HfoExYa9.SrNDTIpA0F9uPCRN5oGcZTXG99ORtlWCHu6OhfkWFfi | NULL           | 2022-07-16 16:01:58 | 2022-07-16 16:01:58 |
+----+----------+--------------------------+-------------------+--------------------------------------------------------------+----------------+---------------------+---------------------+
2 rows in set (0.00 sec)

ノーマルユーザーでログイン

mysql> select * from personal_access_tokens;
+----+-----------------+--------------+------------+------------------------------------------------------------------+-----------+--------------+---------------------+---------------------+
| id | tokenable_type  | tokenable_id | name       | token                                                            | abilities | last_used_at | created_at          | updated_at          |
+----+-----------------+--------------+------------+------------------------------------------------------------------+-----------+--------------+---------------------+---------------------+
|  1 | App\Models\User |            1 | auth_token | 235e76ef3da41fbc897f87215cf9a79821071738357dd61cfa9d16ab92fb537d | ["user"]  | NULL         | 2022-07-16 16:34:20 | 2022-07-16 16:34:20 |
+----+-----------------+--------------+------------+------------------------------------------------------------------+-----------+--------------+---------------------+---------------------+
1 row in set (0.01 sec)

abilitiesuser がセットされています!

この状態で、アクセスできるURLにアクセスしてみます。

問題なくアクセスできました。

次に、アクセスできないURLにアクセスしてみます。

403でアクセスが拒否されました。

スーパーユーザーでログイン

mysql> select * from personal_access_tokens;
+----+-----------------+--------------+------------+------------------------------------------------------------------+------------------+--------------+---------------------+---------------------+
| id | tokenable_type  | tokenable_id | name       | token                                                            | abilities        | last_used_at | created_at          | updated_at          |
+----+-----------------+--------------+------------+------------------------------------------------------------------+------------------+--------------+---------------------+---------------------+
|  2 | App\Models\User |            1 | auth_token | 2773ee81be6355be8b228a57b1944b32917f1e32c74d0869eb9fea54b4136c6e | ["super","user"] | NULL         | 2022-07-16 16:37:28 | 2022-07-16 16:37:28 |
+----+-----------------+--------------+------------+------------------------------------------------------------------+------------------+--------------+---------------------+---------------------+
1 row in set (0.00 sec)

abilitiessuperuser がセットされています。

この状態で、先程アクセスできなかったURLにアクセスしてみます。

アクセスできました。

終わりに

こんな感じでとてもかんたんでした。

ちなみに今回試さなかった abilities ミドルウェアの方は指定したアビリティを すべて持っていること が条件のようです。

今回の ability ミドルウェアは 指定したアビリティを どれか一つを持っていること が条件です。

今回は1個しか使ってなかったのですが、もっと複雑な権限制御がある場合は効果ありそうですよね。

次は何試そうかなぁ。

現場からは以上です。