システムの権限処理を改善する

「プロジェクト内の権限管理が扱いづらいので改善したい」という要望が上がり"簡単な権限処理"に関して改善しました。

その時のことを備忘録として記載します。

簡単な権限処理と難しい権限処理

冒頭で"簡単な権限処理"について対応したと記載しました。 本記事内における"簡単な権限処理"と"難しい権限処理"の違いについて説明します。

簡単な権限処理

組織や役職で管理しているパターンが簡単な権限処理に該当します。   例えば以下のような感じ。

def has_register_authority? staff
  # リーダーなら登録可能
  return true if staff.is_leader?

  return false
end

リーダだったらxxxできる。事務だったらxxxできる。   が簡単な権限処理です。

難しい権限処理

簡単な権限処理に対して難しい権限処理は、役職などの情報 + 権限付与対象のレコード状態を見る必要があります。

例えば以下のような感じ。

MASKING_TEXT = "*******"

def has_reffer_authority? user, staff
  # リーダーは全て閲覧可能
  return true if staff.is_leader?

  # 退会しておらず、年齢が20歳以上のメンバーのみ閲覧可能
  user["status"] == true && user["age"] > 20
end

# マスキングされたユーザを取得
masking_users = 
  User.all.reduce([]) do |acc, r|
    acc.psuh({
      name: MASKING_TEXT,
      age: MASKING_TEXT
    })

    if has_reffer_authority?(member, staff)
      acc["name"] = r["name"]
      acc["age"] = r["age"]
    end

    return acc
  end

参照権限の付与部分がビジネスロジックを持ってしまい 簡単に切り剥がすことが出来ません。

なので、一旦保留にしてメインサービスの方で引き続き対応することにしました。

改善前の状態

簡単な権限処理に関して改善する前の状態について記載します。

イメージとしては以下のようなメソッド群があらゆるファイルに散っていました。

TANAKA = 191

def has_xxx_authority? staff
  return true if staff.is_xxx?
  return true if staff.is_xxx?
  return true if staff.is_xxx?
  return true if staff.is_xxx?
  return true if staff.is_xxx?

  return false
end

def has_xxx2_authority? staff
  return true if staff.is_xxx?
  return true if staff.is_xxx?
  return true if staff.is_xxx?
  return true if staff.is_xxx?
  return true if staff.is_xxx?

  return false
end

def has_xxx3_authority? staff
  return true if staff.id == 123?
  return true if staff.id == 433?
  return false
end

def has_xxx4_authority? staff
  return true if staff.id == TANAKA

  return false
end

この状態だと以下のようなデメリットが発生します。

  • 権限更新時にソースコードを修正する必要がある
  • 誰が何の権限を今持っているのかを理解するのに一々コードを追う必要性がある
  • 酷いものになると権限の判定部分にIDが貼り付けられておりコードのみならずテーブルに格納されているstaffの情報まで見る必要性がある

放置しておくと組織の移動イベントが発生した時に 確認作業の時間だらけになり地獄と化します。

この状態を脱却すべく改善を始めたのでした。

権限表を使った改善

あらゆるファイルに散った権限情報を一元管理したいという思いがありました。

そこでシステム内の権限を全てかき集めてテーブルにして管理することにしました。

イメージとしては以下のような感じです。

 権限DB

 # 役職などのスタッフに関する要素で権限情報テーブルを複数作成
 # reffer_xやregister_xの中身はboolean

 セクション権限テーブル
 |ID|セクションID|reffer_x|register_x|delete_x|

 役職権限テーブル
 |ID|役職ID|reffer_x|register_x|delete_x|

 .
 .
 .

 スタッフ権限テーブル
 # staff.id == TANAKAなどの個人に対する権限を表現するテーブルを作成
 |ID|スタッフID|reffer_x|register_x|delete_x|

 ブロックテーブル
 #「xxの部署はAの機能を使えるのですが、xxの部署のbさんだけその機能を使えないようにしてください」という要望がきた場合の権限テーブル
 # スタッフ個人に機能制限をかける
 |ID|スタッフID|reffer_x|register_x|delete_x|

権限表からスタッフの権限を取得するためには以下のクエリを実行します

( スタッフが所属するセクションの権限レコード OR スタッフが担当している役職の権限レコード ... OR スタッフの権限レコード) AND NOT スタッフのブロックレコード => その社員が持つ全権限

改善対応としてこの権限表にシステムの権限を全て移植しました。

権限表に変えた結果

権限表に変えたメリットを以下に記載します。

  • 今までシステムのコードを彷徨って集めていた権限情報がクエリ1本で取得できる。

  • xxx(部署とか人)が何の権限を持っているのかが テーブルを参照しただけで理解できる。

  • 権限を変更する場合のコードの修正作業が無くなる。(テーブルのboolean変更するだけ)

  • コードからテーブルに移したのでUI側で権限を制御できるようになる(現在の権限情報を表示したり管理者向けに権限編集機能を入れたり等)

同じような悩みを抱えていたら権限表による改善いかがでしょうか。