權限控制表(ACL)
第一節
了解ACL運作方式
大部分強大又重要的東西都需要一些權限控制。
權限控制表是一種分級明確又好管理的權限控制方式。
權限控制表(或簡稱ACL)主要管理二種事物:需要使用服務的事物,和被控制的事物。
以ACL的角度看,需要使用服務的事物(通常是使用者)叫access request object,簡稱ARO。
而系統內被控制的事物(通常是action或資料)叫access control object,簡單ACO。
每一個對象被稱為一個物件(object),因為有時候這些對象並不是人-有時候可能是程式中另一部分的邏輯。
ACO可以是任何你想要控制的東西,從controller的action 到一項web service,甚至是你祖母的線上日記裡的一行文字。
用一句話來說明這三個簡稱:ACL是用來決定ARO是否可以使用ACO的規範。
為了讓你更加了解這個概念,讓我們舉個實用的例子。
一群魔戒中的勇者們共同使用一組電腦系統。隊長必須在努力衝次任務的同時,在成員間守住一定程度的秘密與安全性。
ARO包含下面幾位:
Gandalf
Aragorn
Bilbo
Frodo
Gollum
Legolas
Gimli
Pippin
Merry
這些是系統中會要求使用服務的使用者(ARO),他們要使用的東西就是ACO。
這裡可以注意到,ACL並不是一種使用者認証系統。
你應該早有另一套存放使用者資料,並在他們進入系統時確認身份的方法。
在確認道使用者身份之後,才是ACL 站出發揮功用的時機。好,話題回到我們的勇者們身上。
Gandalf下一步得做的事是列出系統要管理的物品列表(ACO)。看起來會像這樣:
Weapons
The One Ring
Salted Pork
Diplomacy
Ale
傳統的方法裡,系統會用矩陣的方式管理,矩陣中顯示使用者與各物品間的使用權限。
若將這些資料成列成表,看起來就像下面所示,'X'表示拒絕存取,'O'表示允許存取:
Weapons The One Ring Salted Pork Diplomacy Ale
Gandalf X X O O O
Aragorn O X O O O
Bilbo X X X X O
Frodo X O X X O
Gollum X X O X X
Legolas O X O O O
Gimli O X O X X
Pippin X X X O O
Merry X X X X O
這樣的設計,看起來很完善。既可以保守機密(只有Frodo可以拿到魔戒),又可以防止意外發生(哈比人碰不到肉乾)。
權限似忽設計的很好,也很容易看得懂,對吧?
對這麼一個小系統而言,矩陣的的設定方式也許已經足夠。
但面對會慢慢變大的系統或系統裡有一大堆資源(ACO)和使用者(ARO)時,就無法這麼輕易的建立這個表了,比如說要為每個單位分配使用上百個營地。
矩陣的另一個缺點是無法真的為使用者分組,或依不同的組別給與不同的權限。
例如,我們想在戰爭結束後解除哈比人對酒和肉乾的禁令:
若只能分別更改每個使用者的權限,不但要改的項目很多,出錯的機率也非常高;若有辦法直接更改'哈比人'這一組的權限,一切就簡單多了。
ACL 以樹狀結構進行設計,通常會有一棵ARO的樹和一棵ACO的樹。
把物件整理成樹狀圖,不但可以做細部的設定,還可以以巨觀的方式管理。
身為一個叡智的領導者,Gandalf選擇在新系統中使用ACL,把他的物件整理如下:
Fellowship of the Ring
Warriors
Aragorn
Legolas
Gimli
Wizards
Gandalf
Hobbits
Frodo
Bilbo
Merry
Pippin
Vistors
Gollum
將我們的團隊以這種方式整理之後,便可以對這棵樹的每個結點定義使用權限了。
預設的權限是所有東西都不能使用,接者向下層一項一項權限開放。
最後開放的一定是最在意的事物。所以,依據ARO樹,Gandalf把權限如此設計:
Fellowship of the Ring: [Deny: ALL]
Warriors [Allow: Weapons, Ale, Elven Rations, Salted Pork]
Aragorn
Legolas
Gimli
Wizards [Allow: Salted Pork, Diplomacy, Ale]
Gandalf
Hobbits [Allow: Ale]
Frodo
Bilbo
Merry
Pippin
Vistors [Allow: Salted Pork]
Gollum
若想透過ACL 看看Pippin是否可以拿到Ale,首先查出他在樹中的路徑是Fellowship->Hobbits->Pippin。
然後看看每個節結的權限,其中對Pippin最明確的設定就是我們想得知的答案。
Fellowship = DENY Ale,所以不允許(因為被設成所有東西都不允許)
Hobbits = ALLOW Ale,所以允許
Pippin = ?; 這裡沒有做任何特別的設定。
最終結論:允許使用ale。
我們還可以透過這棵樹對更底層做更細節的設定,變更上層的設定:
Fellowship of the Ring: [Deny: ALL]
Warriors [Allow: Weapons, Ale, Elven Rations, Salted Pork]
Aragorn [Allow: Diplomacy]
Legolas
Gimli
Wizards [Allow: Salted Pork, Diplomacy, Ale]
Gandalf
Hobbits [Allow: Ale]
Frodo [Allow: Ring]
Bilbo
Merry [Deny: Ale]
Pippin [Allow: Diplomacy]
Vistors [Allow: Salted Pork]
Gollum
你可以發現,Aragorn的權限雖然和所屬的Warrior組一樣,但仍可以單獨給他特別的權限。
再說明一次,我們把預設權限設成DENY,然後一層層向下,將想開放的設回ALLOW。
想看看Merry是否可以拿到Ale,先查一下Merry在這棵樹的路徑為:Fellowship->Hobbits->Merry,
然後查出每個節點所設與ale相關的權限:
Fellowship = DENY 因為設定為全部拒絕
Hobbits = ALLOW: ale, 所以允許
Merry = DENY ale, 所以拒絕
最終結論:不能使用ale。
第二節
定義權限:Cake的INI型ACL
Cake的第一套ACL系統是透過存於Cake安裝設定裡的INI檔進行設定。
雖然它既實用又穩定,我們還是建議您使用以資料庫為基礎的ACL,最大原因是它可以動態建立ACO和ARO。
舊的ACL(ini版)在簡單的應用中還是非常好用,特別是對那些不想使用資料庫的人而言。
ARO/ACO的權限被定義在
permissions are specified in/app/config/acl.ini.php中。
使用的設定指定可以在acl.ini.php檔的開頭找到說明:
; acl.ini.php - Cake ACL 設定
; ----------------------------------------
; 使用這個檔案指定使用者權限
; aco = access control object (系統裡的某樣事物)
; aro = access request object (需要使用服務的某樣事物)
;
; 使用者資料加入方法如下:
;
; [使用者名稱]
; groups = group1, group2, group3
; allow = aco1, aco2, aco3
; deny = aco4, aco5, aco6
;
; 群組資料加入方法也差不多:
;
; [群組名稱]
; allow = aco1, aco2, aco3
; deny = aco4, aco5, aco6
;
; allow, deny, 和 groups 都可以省略。
; 注意:群組名稱千萬*不可以*和使用者名稱相同!!
; ACL 權限檢查順序如下:
; 1. 檢查使用者不允許的(如果找到穩合的,就決定不允許)
; 2. 檢查使用者允許的(的(如果找到穩合的,就決定允許)
; 3. 看看使用者屬於那些群組
; 4. 檢查群組不允許的(如果找到穩合的,就決定不允許)
; 5. 檢查群組允許的(如果找到穩合的,就決定允許)
; 6. 如果全部都沒找到,則決定不允許
這INI檔可用來定義使用者(ARO)所屬的群組與權限,也可用於定義群組的權限。
譯註:原文要大家去11.4節找更詳細的資料,不過譯者沒找到。乾脆直接把在acl.ini.php檔發現的說明文字放進來讓大家參考。
第三節
定義權限:Cake的資料庫型ACL
開始
ACL權限資料預設存放於資料庫中。資料庫型ACL(或叫dbACL)含有一組核心model,和可由命令列執行的script。
這model用來與資料互動,存放與讀取ACL的樹狀資料。
命令列的script則幫你開個頭,讓你在一開始學習就能和這棵樹互動。
一開始,你必需確定/app/config/database.php有存在,且設定正確。
對於一個剛剛安裝好的Cake系統來說,分辨這件事最容易的方法就是用web流覽器連到安裝目錄。
在這一頁最上方應該會看到"Your database configuration file is present."和"Cake is able to connect to the database."。
如果有錯誤,請參考4.1節,會教您如何設定資料庫。
接著,使用ACL命令列script將資料庫初始化,以便儲存ACL資料。
/cake/scripts/acl.php這支script可以幫助完成這件事。
在/cake/scripts/目錄下執行以下的命令:
使用acl.php將資料庫初始化
$ php acl.php initdb
Initializing Database...
Creating access control objects table (acos)...
Creating access request objects table (acos)...
Creating relationships table (aros_acos)...
Done.
此時,在這個專案的資料庫中應該可以看到新的資料表。
如果你對Cake建立的樹狀資料感到好奇,可以打開資料庫看個清楚。
基本上,它存放的是各節點與節點在樹中的位置。
而acos和aros資料表則存放與他們相關的節點,aros_acos資料表則將aros和acos連結起來。
現在,你可以開始建立自己的ARO和ACO樹了。
建立ARO和ACO
有二種方法取用某個ARO或ACO。
其一是指定一個數字的id,而這個數字通常就是資料表的主鍵。
另一個方法是指定一個字串別稱。
這二種方法並不是互斥的。
建立ARO的方法是使用Aro model裡的方法。
Aro類別的create()方法有三個參數:$link_id,$parent_id,和$alias。
這個方法會在$parent_id下建立新的ACL物件,如果$parent_id是null,則表示這是個根物件。
$link_id則可以將使用者物件連結到ACL結構內。
$alias則可以讓你透過別名取用ARO物件,而不是沒意義的數字。
在建立ACO和ARO之前,必需先載入這些類別。
最簡單的方法是在controller裡使用$components陣列把ACL component包含進來使用:
完成之後,讓我們看看建立這些物件的程式看起來是什麼樣子。
以下的程式碼可以放在controller action的任何地方:
$aro = new Aro();
// 首先,建立幾個ARO
// 這些物件的父層一開始並沒有東西
$aro->create( 1, null, 'Bob Marley' );
$aro->create( 2, null, 'Jimi Hendrix');
$aro->create( 3, null, 'George Washington');
$aro->create( 4, null, 'Abraham Lincoln');
// 現在,建立幾個群組將這些使用者組織起來:
// 注音,這些物件的ID是0,因為他們永遠不會代表系統內任何一個使用者。
$aro->create(0, null, 'Presidents');
$aro->create(0, null, 'Artists');
//再來,把所有的ARO與相對的群組連起來:
$aro->setParent('Presidents', 'George Washington');
$aro->setParent('Presidents', 'Abraham Lincoln');
$aro->setParent('Artists', 'Jimi Hendrix');
$aro->setParent('Artists', 'Bob Marley');
//簡而言之,建立ARO的方法如下:
$aro = new Aro();
$aro->create($user_id, $parent_id, $alias);
你也可以在命令列模式下用$acl.php create aro <link_id> <parent_id>
<alias> 這個script建立ARO。
ACO的建立方法也類似:
$aco = new Aco();
//建立一些ACO物件
$aco->create(1, null, 'Electric Guitar');
$aco->create(2, null, 'United States Army');
$aco->create(3, null, 'Fans');
// 要為這些物件建立群組,可以使用setParent(),在這個範例如就不示範了。
//所以,建立ACO的方法為:
$aco = new Aco();
$aco->create($id, $parent, $alias);命令列模式下的script:
$acl.php create aco <link_id> <parent_id>
<alias>指定權限
建立ACO和ARO後,我們終於可以開始指定二個群組間的權限了。
負責做這件事的,是Cake的核心Acl component。
繼續我們剛剛的範例:
// 首先,我們必需在controller裡使用Cake的ACL component
class SomethingsController extends AppController
{
// 宣告對Acl component的使用權
var $components = array('Acl');
// 記得:ACL在沒有任何資訊的情況下,預設是拒絕所有的事。
// 所有的存取動作都會被禁止。讓我們對ARO開啟一些對ACO的
// 存取權限。
function someAction()
{
// 允許
// 這兒是給予ARO對ACO完整控制權的地方
$this->Acl->allow('Jimi Hendrix', 'Electric Guitar');
$this->Acl->allow('Bob Marley', 'Electric Guitar');
// 我們也可以對群組指配權限,記得嗎?
$this->Acl->Allow('Presidents', 'United States Army');
// allow()方法其實有第三個參數,$action。
// 可以使用這個參數只指定部分存取功能。
// $action可以設成create, read, update 或 delete。
// 如果沒有指定,則假設全部一起指定。
$this->Acl->allow('George Washington', 'Electric Guitar', 'read');
$this->Acl->allow('Abraham Lincoln', 'Electric Guitar', 'read');
// 拒絕
// 設定拒絕的方法和上面類似:
$this->Acl->deny('Abraham Lincoln', 'United States Army');
}
}
這麼一個特殊的範例也許沒什麼特別的功用,但它可以向您展示整個流程。
把Acl component和使用者管理的controller合在一起使用最有用。
當系統建立了一個使用者,他的ARO可以同時被建立,並放在正確的節點上。
接著依他的狀況將權限設到某個ACO或ACO群組上。
權限也可以在命令列下透過script指派。
語法和model裡的函式類似,若實在不懂,直接執行
第四節
檢查權限:ACL Component
檢查權限是使用Cake ACL裡最簡單的一部分:只有一個函式,check()。
在AppController的某個地方進行ACL檢查是個不錯的主意,只要放在那兒,
就可以在應用程式的任何地方進行權限檢查。下面的個範例:
class AppController extends Controller
{
// 取得component
var $components = array('Acl');
function checkAccess($aco)
{
// 使用component檢查權限
$access = $this->Acl->check($this->Session->read('user_alias'), $aco, $action = "*");
//拒絕存取
if ($access === false)
{
echo "access denied";
exit;
}
//允許存取
else
{
echo "access allowed";
exit;
}
}
}基本上,只要讓Acl component在AppController裡可以使用,應用程式的其他地方就都可以使用。
// 這是基本的呼叫法:
$this->Acl->Check($aro, $aco, $action = '*');
沒有留言:
張貼留言