帝國 - ACL, RBAC, DAC with resources-condition based on PHP.
Switch branches/tags
Nothing to show
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Failed to load latest commit information.
src
test
LICENSE
README.md

README.md

Do what you want, if you can.

 

Imperium

帝國是一個基於 PHP 的權限管理系統,建立一個帝國需要細心的規劃,

與其他權限管理系統不一樣的地方在於:帝國是十分重視資源條件的系統。

如果你不清楚,我們稍候會提到什麼是資源條件。

 

特色

  1. 動作請求紀錄(可選)

  2. 明瞭的使用方式

  3. 動作捷徑

  4. 角色系統

  5. 組織系統

  6. 單一個使用者可擁有多重角色

  7. 資源條件設置

 

工程清單

  1. 動作紀錄系統

  2. 取得所有允許、拒絕權限表

  3. 若為訪客,則禁止所有 allow()deny() 的設置

 

索引

  1. 範例

  2. 設定使用者

  3. 組織

  4. 角色

  5. 使用者

  6. 權限

  7. 動作紀錄

  8. 架構

  9. 和資料庫搭配

  10. 可參考文件

 

範例

這個範例是帝國大約 80% 的用法,通常能夠滿足一般人對權限管理系統的要求,

假設你希望可以特別針對特定使用者、角色、組織持有的資源做判斷,

請在稍候參考任何有關資源的章節

/** 設定現在使用者的編號,假設為 1 */
$imperium->caller(1)
         
         /** 建立一個組織,然後在該組織下新增三個角色 */
         ->addOrg('網站群組')                   // 新增一個組織,名為:網站群組
         ->addRole(['管理員', '版主', '使用者']) // 一次新增三種角色

         /** 新增權限給「網站群組」的「管理員」 */
         ->org('網站群組')        // 選擇「網站群組」這個組織
         ->role('管理員')         // 選擇「管理員」這個角色
         ->allow('編輯', '文章')  // 允許我們選擇的角色「編輯」所有「文章」

         /** 選擇剛才建立的組織,然後讓這個使用者成為管理員 */
         ->org('網站群組')        // 選擇「網站群組」這個組織
         ->assign('管理員');      // 指派使用者成為「網站群組」裡的「管理員」

/** 現在可以像這樣詢問使用者有無權限「編輯」所有「文章」 */
if($imperium->can('編輯', '文章'))
{
    ... 程式 ...
}
         

 

設定使用者

透過 caller() 來設定目前使用者的編號,

如果傳入的參數為 falsenull 則會返回到訪客狀態(也就是沒有設置 caller())。

/** 將目前使用者指定為編號 1 */
->caller(1)

 

組織

如果你要指配給使用者一個角色,那麼就必須要先有組織,

意思是你必須先建立組織,才能夠建立角色

 

建立

透過 addOrg() 來建立一個新的組織。

/** 新增一個名為「單身俱樂部」的組織 */
->addOrg('單身俱樂部')

 

選擇

當你建立完一個組織後,你未來若要在這個組織下進行任何動作,你就必須先選擇這個組織,

透過 org() 來選擇一個組織。

/** 選擇「單身俱樂部」這個組織 */
->org('單身俱樂部')

 

角色

在建立角色之前,必須要先有至少一個組織

 

建立

在建立之前,你要先選擇一個組織,如此一來,才不會讓這個角色沒有歸屬,

透過 addRole() 新增一個,或是多個角色。

/** 先選擇名為「亞凡芽愛好會」的組織 */
->org('亞凡芽愛好會')

/** 接著在這個組織下新增「管理員」這個角色 */
->addRole('管理員')

/** 或者,你也可以一次新增多個角色 */
->addRole(['版主', '使用者'])

 

選擇

當你建立完角色後,你可以透過 role() 來選擇角色,但是別忘記,你必須先選擇一個組織

/** 先選擇名為「亞凡芽愛好會」的組織 */
->org('亞凡芽愛好會')

/** 然後選擇你要的角色 */
->role('管理員')

 

使用者

使用者在帝國中,其實是不必要的,

但是,有時候你可能不希望以組織,或是角色來授予或拒絕他們權限,

而是希望這個權限只有這個使用者擁有,這個時候,你就可以只選擇使用者自己一個人

 

選擇

透過 self() 來只選擇使用者自己,要注意的是,當你使用這個方法,

你先前透過 org() 或是 role() 所選擇的都會被取消(畢竟你現在只希望選擇使用者自己)。

/** 不需要額外的參數,因為你已經透過先前的 caller() 設置過了 */
->self()

 

權限

當你有了角色、組織,接下來就是時候授權或是不允許他們做什麼了。

假設你希望拒絕「編輯」某個特定組織、角色所持有的「東西」,

參考資源條件的章節

 

授權和拒絕

在一般情況下,你沒有授權的,使用者就無法執行,

而**「拒絕」比「授權」還要來的有優先權**,

假設你設置了兩個權限:

允許:「編輯」所有「文章」。

拒絕:「編輯」「編號 3」的「文章」。

那麼你可以編輯所有文章,但是編號 3 的那篇文章則不行。

 

授權

透過 allow() 來允許你所選擇的對象(使用者、角色或組織)做某一件事情。

請注意:你指定的資源、組織、角色會在使用這個函式後自動清除,因此你之後需要在重新指定一次。

->allow('編輯', '文章')

/** 或者一次新增多個種類 */
->allow('編輯', ['文章', '相簿', '留言'])

 

拒絕

透過 deny() 來拒絕你所選擇的對象(使用者、角色或組織)做某一件事情。

請注意:你指定的資源、組織、角色會在使用這個函式後自動清除,因此你之後需要在重新指定一次。

->deny('編輯', '文章')

/** 或者一次拒絕多個種類 */
->deny('編輯', ['文章', '相簿', '留言'])

 

萬用符號

透過 % 這個符號,來代表任何事情。

/** 允許對「文章」做出「任何事」 */
->allow('%', '文章')

/** 或是可以「編輯」「任何東西」 */
->allow('編輯', '%')

/** 當然你也可以用在「拒絕」上 */
->deny('移除', '%')

 

檢查

當你設置了權限,你就可以開始透過下列方式,

來確認你的使用者是否有權限執行某一件事情了。

 

是否為訪客?

透過 isGuest不是 isGuest()) 來確認是否為訪客,

訪客的定義為:沒有透過 caller() 設置成為一個使用者,

倘若你當初用 caller() 設置了一個 false 或是 null 的使用者,也會被認定為訪客

if($imperium->isGuest)
    echo "大哥,你是路人啊!";

 

可以嗎?

透過 can() 來詢問是不是可以執行某個動作,當然,你也可以跟萬用符號搭配

/** 假設你允許「編輯」「相簿」 */
->allow('編輯', '相簿')

->can('編輯', '相簿')  // True
->can('編輯', '%')    // False


/** 假設你拒絕「編輯」「相簿」 */
->deny('編輯', '相簿')

->can('編輯', '相簿')  // False
->can('編輯', '%')    // False


/** 或是設置一個萬用字元的權限 */
->allow('%', '相簿')

->can('移除', '相簿')  // True
->can('編輯', '相簿')  // True
->can('%', '相簿')    // True

 

不可以嗎?

can() 相反,透過 cannot() 來詢問是不是不可以做某一件事情。

/** 假設你允許「新增」「文章」 */
->allow('新增', '文章')

->cannot('新增', '文章')  // False
->cannot('移除', '文章')  // True
->cannot('%',   '文章')  // False


/** 假設你拒絕「新增」「文章」 */
->deny('新增', '文章')

->cannot('新增', '文章')  // True

 

同時檢查多個種類

can()cannot() 之中,

你也可以透過傳入陣列一次檢查多個種類,但必須全部符合。

->deny('移除', '相簿')
->deny('移除', '留言')

->cannot('移除', ['文章', '相簿'])  // False
->cannot('移除', ['留言', '相簿'])  // True

或者透過 cannotAny()canAny() 來採用寬鬆模式

意思是只要其中有一個可以(或不可以),則符合條件。

->allow('移除', '相簿')

->canAny('移除', ['文章', '相簿'])  // True

 

基於資源條件

其實在上方我們提到了很多,設想看看,你擁有三個角色:管理員、版主、使用者:

管理員:可以刪除任何人的文章。

版主:可以刪除任何人的文章,但是不可以刪除管理員的文章

使用者:只可以刪除自己的文章。

現在,透過一般的權限管理系統,根本沒辦法做到這樣

因為他們通常只管理你「是否有權利執行這個動作」而不是「是否有權利對誰的資源執行這個動作」

 

針對組織

假設你希望這個權限對某個組織才生效,透過 resOrg() 來針對一個組織。

->resOrg('小安俱樂部')   // 針對「小安俱樂部」這個組織
->allow('刪除', '文章')  // 授權「刪除」「文章」
                        // 請注意:並不是授予給「小安俱樂部」的所有人。


->resOrg('小安俱樂部')                      
->can('刪除', '文章')    // 可以「刪除」「小安俱樂部」內的所有「文章」嗎?

發現到了嗎?當你採用 resOrg() 的時候,表示這個權限對什麼組織生效

 

針對角色

透過 resRole() 來針對一個角色。

->resRole('管理員')      // 針對「管理員」所持有的資源
->allow('刪除', '文章')  // 授權「刪除」「文章」


->resRole('管理員')                  
->can('刪除', '文章')    // 可以「刪除」「管理員」所發表的「文章」嗎?

 

針對種類

種類就是「文章」、「相簿」這種東西(就是你已經正在用的那個),

事實上你並不需要用到這個功能,倘若你需要的話,透過 resType() 來針對一個種類。

/** 這個作法 */
->resType('相簿')
->allow('刪除')

/** 跟這個是一樣的 */
->allow('刪除', '相簿')

 

針對編號

透過 resId() 來針對一個編號。

->resId(3)              // 針對任何編號為 3 的資源
->allow('刪除', '文章')  // 授權「刪除」「文章」


->resId(3)                  
->can('刪除', '文章')    // 可以「刪除」任何「編號為 3」 的「文章」嗎?

 

縮寫

如果你覺得一直 resId() 過於麻煩,其實你可以在一些函式最後面直接放上資源編號

/** 例如 allow() 或是 deny() */
->allow('刪除', '相簿', 3)
->deny('刪除', '相簿', 3)

/** 或者 can() 跟 cannot() */
->can('刪除', '相簿', 3)
->cannot('刪除', '相簿', 3)

 

儲存和讀取針設置

如果你發現縮寫滿足不了你,你可以直接把你所設置的資源條件透過 resSave() 儲存

然後透過 resLoad() 讀取

$Data = $imperium->resOrg('伊繁星組織')
                 ->resRole('管理員')
                 ->resType('文章')
                 ->resId(3)
                 ->resSave();  // 儲存!

$imperium->resLoad($Data)      // 讀取!
         ->allow('新增');

$imperium->resLoad($Data)
         ->can('新增')          // True

 

動作紀錄

 

架構

帝國透過數個陣列儲存這些資料,如果你想要觀看範例架構,

可以開啟 test/structure.php

 

和資料庫搭配

你可能會開始好奇,如果帝國沒有任何跟資料庫連接的橋樑,要如何和資料庫搭配使用?

在這裡,我們會提出一些理念,但基於時間,可能沒有辦法實作給你看。

 

架構

實際上,帝國是非常龐大的,

我們依照大型商業邏輯所規劃出的資料表大約如下,

不過當然,這只是理想,你可以將下列規模縮減。

資料表

 

組織表

這個資料表用來儲存有哪些組織,通常欄位是:

  1. 編號-組織的編號。
  2. 名稱-組織的名稱。

 

角色表

這個資料表用來儲存有哪些角色,並且隸屬於哪些組織,通常有這些欄位:

  1. 編號-角色的編號。
  2. 名稱-角色的名稱。
  3. 隸屬組織-這個角色隸屬的組織編號。

 

使用者角色表

這個資料表用來儲存使用者具有什麼身份,通常會有兩個欄位:

  1. 使用者編號-使用者的編號。
  2. 角色編號-角色的編號。

 

角色權限表

這個資料表用來儲存角色擁有和被禁止哪些授權,通常是這些欄位:

  1. 權限編號-這個權限的編號,排序用。
  2. 角色編號-這個權限是為哪個角色設計的。
  3. 資源種類-這個權限針對哪個種類?例如:文章。
  4. 資源角色
  5. 資源組織
  6. 資源編號
  7. 已授權-儲存 1 和 0;1 為允許,0 為拒絕。

 

使用者權限表

與角色權限表十分相同,但這個資料表是基於「使用者」而非「角色」。

  1. 權限編號-這個權限的編號,排序用。
  2. 使用者編號-這個權限是為哪個使用者設計的。
  3. 資源種類-這個權限針對哪個種類?例如:文章。
  4. 資源角色
  5. 資源組織
  6. 資源編號
  7. 已授權-儲存 1 和 0;1 為允許,0 為拒絕。

 

可參考文件

這裡是幾個可能會啟發你創意,或是更能讓你了解這類東西的連結。

Role Based Access Control in PHP

efficiently/authority-controller

BeatSwitch/lock

OWASP/rbac

machuga/authority