2007年5月15日 星期二

CakePHP使用手冊-權限控制表(ACL)

轉貼自 http://www.ezluk.org/

權限控制表(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最明確的設定就是我們想得知的答案。




  1. Fellowship = DENY Ale,所以不允許(因為被設成所有東西都不允許)




  2. Hobbits = ALLOW Ale,所以允許




  3. Pippin = ?; 這裡沒有做任何特別的設定。




  4. 最終結論:允許使用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相關的權限:




  1. Fellowship = DENY 因為設定為全部拒絕




  2. Hobbits = ALLOW: ale, 所以允許




  3. Merry = DENY ale, 所以拒絕




  4. 最終結論:不能使用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包含進來使用:


var $components = array('Acl');

完成之後,讓我們看看建立這些物件的程式看起來是什麼樣子。
以下的程式碼可以放在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裡的函式類似,若實在不懂,直接執行$php acl.php help也會列出使用方法。




第四節


檢查權限: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 = '*');


沒有留言:

網誌存檔

關於我自己

Aspire freedom , Hope to do Soming make self complete ~