システムの権限管理を設計することになったとき、どのようにすればいいか分からないかもしれません。この記事では、権限管理とは何か、その際に押さえておくべきRBACとABACについて、また権限管理を設計するときの考え方を解説しています。
私はエンジニアとして、主に立ち上げのフェーズでサービスを開発してきました。権限管理は、どのサービスにも必ず入ってくるプロセスでした。ここでの経験や、調べたことを元にこの記事を書いています。
この記事が想定している読者の方としては、エンジニアとしてシステムを設計または実装する立場にある、実務経験が1〜3年目の方を想定して記事を書いています。
テクニカルライター。元エンジニア。共著で「現場で使えるRuby on Rails 5」を書きました。プログラミング教室を作るのが目標です。
権限管理とは
この記事でいう権限管理とは、ユーザー認証を行った相手に対して、「誰がどの機能に対してなにをしていいのか、あるいはしてはいけないのかを管理すること」をいいます。たとえばTwitterにおける権限管理について考えてみると、次のようになります。
- すべてのユーザーは投稿できる
- すべてのユーザーは自分の投稿を削除できる
- ツイートはすべてのユーザーが閲覧できる
- 非公開ツイートはフォロワーのみが閲覧できる
- ブロックされたユーザーはツイートを閲覧できない
こういった、サービス内の行動に対する権限を設計するのが権限管理です。この権限管理について、この記事で書いていきます。
権限管理は、システムの種類によって大きく変わってきます。このため、この記事の内容はすべてのシステムに適用できるやり方というわけではありません。参考資料のひとつとしてお読みいただければと思っています。
どう権限管理を設計するか
Googleで「権限管理 設計」で検索してもらってもわかることですが、権限管理にはいろんなやり方があって、ベストプラクティスはないと思っています。この前提で、私はシートで管理するやり方をとっています。
たとえば次のような感じです。これは架空のドキュメント共有サービスを例に作ってみています。

権限管理のシートに書く内容
このシートには、次のようなことを書いていきます。
項目 | 内容 | 例 |
---|---|---|
機能 | どの機能に対する権限か | 投稿閲覧 |
役割 | どの役割に対する権限か | 一般ユーザー / 管理者 |
属性 | どんな属性をもったユーザーに対する権限か | 同じグループに属していること |
対象となるデータ | 処理するデータはどれか | グループのメンバーの投稿 |
処理の可否 | 処理をできるのかどうか | できる / できない |
説明 | その権限の概要 | 同じグループのユーザーの投稿を閲覧できる |
開発が進むにつれて、機能が追加・削除されて、権限もどんどん変わっていくことになります。その度に、このシートを更新していきます。すべての権限管理が一枚のシートに集約されている状態がベストです。
権限管理におけるRBACとABACという考え方
上の表で、権限管理には「役割」と「属性」があると書きました。これについて、権限管理を設計する上でRBACとABACという考え方が大切なので、それぞれについて書いていきます。
RBAC: Role-Based Access Control
RBACは「ユーザーの役割によって権限のチェックを行う」やり方です。役割とは、たとえば一般ユーザーや管理者などがあります。権限を変更するだけで処理の可否を変更できるという利点がありますが、権限がふえると管理や実装が大変になってきます。
「管理者」のような役割以外にも、WritableやReadableのような複数のパーミッションを持たせるようなやり方もあります。
ABAC: Attribute-Based Access Control
ABACは「ユーザーの属性によって権限のチェックを行う」やり方です。たとえばツイートの投稿者かどうか、あるいは同じグループに所属しているかどうか、です。
ABACは柔軟な権限管理ができる一方で実装が大変だったり、ものによってはデータベースへのアクセスがふえてパフォーマンスが悪くなったりします。RBACとABACは、システムの種類によって使い分けるとよいです。ひとつのシステム内で両方を使うことももちろんあります。
権限管理を設計するフロー
権限管理の設計はシートでやるといい、と書きました。その手順としては、次の3つでやるとスムーズに設計できます。
- 機能を洗い出す
- 機能に対する権限を洗い出す
- 重複した権限の優先度を考える
まずシステムの機能を洗い出して、それぞれの機能に対する権限をシートにまとめていきます。このとき、重複した権限が出てきます。たとえば、次のように2つの権限があったとします。
- 管理者はすべての投稿を表示できる
- ブロックされたユーザーは、相手の投稿を表示できない
このとき、「ブロックされている管理者」は投稿を閲覧できるのかどうかを考える必要が出てきます。こういった重複について、ひとつひとつどうするかを決めていきます。
権限はどこでチェックするか
次はコード面での設計についてです。権限は、データベースなどのデータとのやりとりが発生するところでチェックするといいです。たとえばフロントエンドにJavaScriptを用いていて、データとのやりとりにAPIサーバーがある、という構成について考えてみます。
フロントエンド側で権限管理をする/しないについては選択の余地がありますが、API側での権限管理は必須です。なぜなら不正なリクエストなどで、意図しないやりとりが行われる可能性があるからです。
もちろんトークンの照合などでリクエスト自体の正当性は検証しつつも、API側で権限をチェックする必要があるといえます。
権限はどこで実装するか
次は、チェックではなく権限自体をどこで実装するかについて考えてみます。権限管理とは、「誰がどの機能に対してなにをしていいのか、あるいはしてはいけないのかを管理すること」であると書きました。
つまり権限はビジネスロジックであるといえます。権限はそれ単体でひとつの責務をもつので、権限管理をする責務として切り出して実装していくとよさそうです。
RubyにはPunditというGemがあります。これはモデル層に属する純粋なRubyオブジェクトで、リソースに対する権限を実装するときに有用なライブラリです。
権限管理を設計するときの指針
権限管理を設計するときは、次の二つのことを実践していきます。
- 役割をできるだけ減らす
- テストコードを書く
それぞれについて簡単に書きます。
役割をできるだけ減らす
権限管理の役割がふえると、その分だけ権限管理シートやソースコードが複雑になってしまいます。ビジネスを表現するために必要であれば役割をふやすことも仕方がないですが、「とりあえず問題を解決するために役割をふやそう」という発想は避けるべきだといえます。
テストコードを書く
前述のとおり、権限管理はビジネスロジックに属します。権限の実装にミスがあると、意図しないデータの流出などにつながるおそれがあります。
権限に対するユニットテストはもちろん、データとのやりとりが発生するコントローラ層へのユニットテスト、またE2Eテストなどを書くことで、信頼性の担保につながります。
まとめ
この記事では権限管理を設計するときの考え方、やり方について書いてきました。権限管理はシステムの種類によって変わってきます。この記事の内容を参考にしつつ、システムに適したやり方を見つけられればと思います。