😄

LaravelでRBACを実装してみる

に公開

LaravelでRBACを実装してみる

あまりしっかりとしたものではないですが、簡単なRBACを実装してみました。

RBACとは

Role Based Access Controlの略
ユーザーのロールに応じてシステムへのアクセス・アクションを制御する比較的新しいアクセス制御方式。
ユーザーには特定のロールが割り当てられ、ロールには操作の実行許可(パーミッション)が割り当てられる。
直接ユーザーにパーミッションが割り当てられるわけではないため、ユーザーが部門移動やまた新しいユーザーが追加された場合でも容易に対応することが可能になる。
パーミッションは細かく操作を許可することが可能で、post:listのような投稿一覧を取得など利用者にとって意味のある形式で設定することが良いのかもしれない

2004年に米国国家規格協会(ANSI)はRBACの原則を業界のコンセンサス標準として採択

構成要素

管理者

  • ロールの管理
  • アクセス許可の付与
  • システム保守

ロール

  • 業務内容に応じたグループ
  • 通常は組織内の職務や責任に関連付けられる

パーミッション

  • 各リソースに対し、実行できる操作のこと

パーミッションとは

ユーザーがアクセスできる対象(リソース)とシステムに対して実行できることを指定する
職務(ロール)の制限を超えてアクセス許可を求めることはしてはいけない。→システムが立ち行かなくなる

主な操作

  • アクセス
  • 読み取り
  • 書き込み
  • 共有
  • 財務

メリット

  1. 管理の効率化
    ユーザーにロールを割り当てるだけで良く、異動や退職でユーザー情報に変更があった時でも再割り当てだけで済むようになる

  2. セキュリティの強化
    AWSを使ったことがある人なら確実に要求される最小権限の原則を適用しやすくなるため、職務分掌等の制約が実現できる。
    権限をまたいだ操作も未然に防ぐことができるので、コンプライアンスを強化できる

デメリット

  1. ロール・パーミッションの肥大化
    ロールやパーミッションなどが増えてくると管理が難しくなる
    ロールなどの設計や組織構造、業務プロセスの理解が必要になる

実装

簡略化したRBACを実装してみる。Laravelには専用のライブラリがあるため簡単に実装することが可能だ。

ロール

今回は簡易版ということでロールはuser, adminの2種類用意

super_admin: 以下2つの操作、ロール、パーミッションの管理が行える
manager: アカウントの削除、投稿の削除が行える
user: 自身のアカウント操作(R,U)、投稿に関する操作(list,C,R,U,D)が行えるようにします。

パーミッション

AWSを参考にし、命名規則はリソース:操作とした。AWSのポリシーは一目見ただけで何をどうするか一目で理解しやすいのでとても参考になる。

  • post:list
  • post:create
  • post:read
  • post:update
  • post:delete
  • user:list
  • user:update
  • user:delete
  • role:list
  • role:create
  • role:update
  • role:delete
  • permission:list
  • permission:create
  • permission:update
  • permission:delete

使用するフレームワーク・パッケージ

  • Laravel v10
  • Laravel Permission

リポジトリ

管理者のみ処理を実行できるようにする

公式ドキュメントではGate::before()を使用する。パーミッションを管理者ロールに割り当てる必要がなくなる。

特定のロールでのみアクセスできるルートの定義

middlewareにロール名を指定する

Route::group(['middleware' => ['role:ロール名']], function () {
    //
});

特定のパーミッションを持っているロールのみアクセスできるルートの定義

middlewareにパーミッション名を指定する。
このパーミッションはユーザーなどの対象に直接割り当てるべきではなく、ロールに割り当てるのが良い。→ロールを通じて権限を継承することができのが最適。
また権限は細かくするとよりアクセス制御しやすい設計になる。

app always checks for permissions (as much as possible), not roles

Route::group(['middleware' => ['can:user:list']], function () {
    Route::get('/users', function () {
        return response()->json([
            'users' => User::all()->makeVisible('password'),
        ]);
    });
});

JSONエラーを返すようにする

registerメソッドに追加する

<?php

namespace App\Exceptions;

use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
use Throwable;

class Handler extends ExceptionHandler
{
    /**
     * The list of the inputs that are never flashed to the session on validation exceptions.
     *
     * @var array<int, string>
     */
    protected $dontFlash = [
        'current_password',
        'password',
        'password_confirmation',
    ];

    /**
     * Register the exception handling callbacks for the application.
     */
    public function register(): void
    {
        $this->reportable(function (Throwable $e) {
            //
        });

        $this->renderable(function (\Spatie\Permission\Exceptions\UnauthorizedException $e, $request) {
            return response()->json([
                'responseMessage' => 'You do not have the required authorization.',
                'responseStatus'  => 403,
            ]);
        });
    }
}

参考

最後に

間違っていること、などあればコメントに書いていただけると幸いです。
よろしくお願いいたします。

GitHubで編集を提案

Discussion