◎ 本文原載 XDite 網站,原文章連結
權限存取設計是在開發 Application 中相當讓人棘手的一個題目。
在一個網站開始建設的初期,通常這樣的問題並不會浮現,畢竟一般人的需求大半只會有 user 和 admin 兩種角色。但是隨著網站長大,更多的生意需求浮現,第三種角色的出現,通常就會把原本乾淨的 code 弄得骯髒不堪。
當只有 user 和 admin 的情況下,你可以在 view 裡面單純的做出這樣的設計
<% if user.is_admin ? %>
  <%= link_to("Admin Pannel", admin_panel_path ) %>
<% end %>
並且在 controller 裡面加上權限判斷
class Admin::ArticleController < ApplicationController before_filter :require_is_admin end
但一段時間之後,User Story 被加進了這樣的需求:
身為開發者的你,要如何在現有後台內加入這樣的設計?
不用實際動手寫也知道,若如以往使用 if / else 的設計,Helper / Controller / View 鐵定變成一團血肉模糊。
抱怨不能解決問題,但世界上是否存在乾淨的解答?
答案就是:「Rule Engine」。
「針對多種條件執行多種動作」,此類的使用者需求,無論是使用 if / else,甚至是 case when,架構還是不免會一團混亂。與其承襲舊思路,不如啟用新想法「Rule Engine」實作:預先設計撰寫一套邏輯規則引擎,而後程式針對預設的規則進行邏輯判斷後執行。
而「角色權限」的設計需求上,正特別適合用 Rule Engine 這樣的觀念去建構。Rails 界知名的 authorization library cancan 正是以此作為基礎。
cancan 希望做到的是,把權限判定的處理部分從 Helper / Controller / View 裡面,全部移到 app/models/ability.rb 進行判定。也因此可以做到
<% if can? :update, @article %> <%= link_to "Edit", edit_article_path(@article) %> <% end %>
class ArticlesController < ApplicationController
  authorize_resource
  def show
    # @article is already authorized
  end
end
但驚人的是 view 的權限會是與 controller 的權限判定規則 卻是一致的。(以往「自刻」權限判定,往往加了 view 卻會忘記 controller, 加了 controller 卻會忘記 view )
而是否有權限存取,則全交給 app/models/ability.rb 去判斷處理。
class Ability
  include CanCan::Ability
  def initialize(user)
    if user.blank?
      # not logged in
      cannot :manage, :all
      basic_read_only
    elsif user.has_role?(:admin)
      # admin
      can :manage, :all
    elsif user.has_role?(:member)
      can :create, Topic
      can :update, Topic do |topic|
        (topic.user_id == user.id)
      end
      can :destroy, Topic do |topic|
         (topic.user_id == user.id)
      end
      basic_read_only
    else
      # banned or unknown situation
      cannot :manage, :all
      basic_read_only
    end
  end
  protected
  def basic_read_only
    can :read,    Topic
    can :list,    Topic
    can :search,  Topic
  end
end
cancan 是一套相當 powerful 的權限管理系統,但是它的文件卻相當不好讀,第一次想使用 cacan的 developer 很難從文件上找到自己想要的範例以及 api,或者了解其原理構造。如果沒有先給一些基礎範例,往往會是寸步難行。
下一篇我會深入頗析 Cancan 更深的設計原理,讓大家更看得懂 cancan 的 API 到底想幹什麼….。