我們在階段 1 中已經完成了最基本的 DACL 資料結構,能針對簡單的 CRUD 權限做控制,但一般來說權限設定沒那麼簡單 (尤其是複雜度高又講求安全的系統),很多程式或系統有時無法只單純用 CRUD 就可以解決...
我們在階段 1 中已經完成了最基本的 DACL 資料結構,能針對簡單的 CRUD 權限做控制,但一般來說權限設定沒那麼簡單 (尤其是複雜度高又講求安全的系統),很多程式或系統有時無法只單純用 CRUD 就可以解決,像是某些系統有這樣的需求:
- 系統必須允許設定編輯的資料範圍。
 - 系統必須允許設定使用者編輯詮釋資料 (metadata) 的權利。
 - 系統必須可以限制使用者可查詢或瀏覽到的欄位資料。
 
這些需要是比較細部粒度的權限設定,若只以 CRUD 來看的話似乎相當不足,所以我們的 DACL 也必須要能夠支援這樣的需求,然而,也不是每個系統模組都需要這樣的權限,因此我們又多了一個需求:系統或模組可以自己決定要向使用者要求哪些權限,簡單的說,模組的權限由模組自己決定,並且不受 (或亦受) CRUD 基本權限的設定,是否要這麼做則是在設計時就先做好,故系統分析的結果在這裡就變得很重要,因為要實作與否是由使用者的需求來決定。
在階段 1 時,我們定義了一組基本權限,佔用了 1 個 byte,其中有 4 個 bit 是保留的,在階段 2 我們還是暫時保留,不過因為要湊齊 4 個 byte,我們在階段 1 時有定義一個完全未使用的 reserved byte,而在這裡我們就使用它來實作物件自己的權限吧。
首先,在階段 1 中的 byte _reserved = (byte)0x00;,把它改名為 _objectPermission,然後在建構式中增加一段程式碼:
	private ObjectACE(byte[] BinaryData)
	{
	    this._objectID = BitConverter.ToUInt16(new byte[] { BinaryData[0], BinaryData[1] }, 0);
	    this._permission = BinaryData[2];
	    this._objectPermission = BinaryData[3]; 
	}
接著,加入下列兩個新函式,這兩個新函式會檢查指定的位元旗標,而也允許設定特定位元的旗標值:
	public bool GetObjectBasedPermissionFlag(int PermissionIndex)
	{
	    return (new BitArray(new byte[] { this._objectPermission }).Get(PermissionIndex));
	} 
	public void SetObjectPermissionFlag(ushort PermissionIndex, bool Value)
	{
	    BitArray bitArray = new BitArray(new byte[] { this._objectPermission });
	    bitArray.Set(PermissionIndex, Value); 
	    byte[] buffer = new byte[1];
	    bitArray.CopyTo(buffer, 0);
	    bitArray = null; 
	    this._objectPermission = buffer[0];
	}
然後,修改 operator & 的覆寫程式:
	public static ObjectACE operator &(ObjectACE ACE1, ObjectACE ACE2)
	{
	    ObjectACE resultACE = ObjectACE.CreateEmpty(); 
	    resultACE.UpdatePermission(
	        (ACE1.CanCreate && ACE2.CanCreate), (ACE1.CanRead && ACE2.CanRead),
	        (ACE1.CanUpdate && ACE2.CanUpdate), (ACE1.CanDelete && ACE2.CanDelete));
	    
	    for (ushort i = 0; i < 8; i++)
	    {
	        bool v = ACE1.GetObjectBasedPermissionFlag(i) & ACE2.GetObjectBasedPermissionFlag(i);
	        resultACE.SetObjectPermissionFlag(i, v);
	    } 
	    return resultACE;
	}
至此,ObjectACE 就修改完成,而 DACL 主類別的部份不需更動。
接著,我們回到 demo 的程式,修改 User 和 Group 的定義,加入測試物件權限的指令:
	public class User
	{
	    public string UserID { get; set; }
	    public string Name { get; set; }
	    public DiscretionaryACL UserACL { get; set; } 
	    public User()
	    {
	        DiscretionaryACL acl = DiscretionaryACL.CreateEmpty();
	        ObjectACE defaultACE = ObjectACE.CreateEmpty(); 
	        defaultACE.UpdatePermission(true, true, false, false);
	        acl.SetDefaultACE(defaultACE); 
ObjectACE objectACE = ObjectACE.CreateEmpty(0x01);
	        objectACE.SetObjectPermissionFlag(0, false);
	        objectACE.SetObjectPermissionFlag(1, false);
	        objectACE.SetObjectPermissionFlag(2, false);
	        objectACE.SetObjectPermissionFlag(3, true);
	        objectACE.SetObjectPermissionFlag(4, false);
	        objectACE.SetObjectPermissionFlag(5, false);
	        objectACE.SetObjectPermissionFlag(6, true);
	        objectACE.SetObjectPermissionFlag(7, false); 
acl.AddObjectACE(objectACE);
	        this.UserACL = acl;
	    }
	}
	public class Group
	{
	    public string GroupID { get; set; }
	    public string Name { get; set; }
	    public DiscretionaryACL GroupACL { get; set; }
	    public List<User> Users { get; set; } 
	    public Group()
	    {
	        this.Users = new List<User>(); 
	        DiscretionaryACL acl = DiscretionaryACL.CreateEmpty();
	        ObjectACE defaultACE = ObjectACE.CreateEmpty(); 
	        defaultACE.UpdatePermission(false, true, true, false);
	        acl.SetDefaultACE(defaultACE); 
ObjectACE objectACE = ObjectACE.CreateEmpty(0x01);
	        objectACE.SetObjectPermissionFlag(0, false);
	        objectACE.SetObjectPermissionFlag(1, true);
	        objectACE.SetObjectPermissionFlag(2, true);
	        objectACE.SetObjectPermissionFlag(3, true);
	        objectACE.SetObjectPermissionFlag(4, false);
	        objectACE.SetObjectPermissionFlag(5, false);
	        objectACE.SetObjectPermissionFlag(6, true);
	        objectACE.SetObjectPermissionFlag(7, false); 
acl.AddObjectACE(objectACE);
	        this.GroupACL = acl;
	    }
	}
至此,ObjectACE 與 DACL 都已準備妥當,接下來我們還需要定義一個方法,讓物件可以告訴主程式它擁有哪些權限,而也需要有一個地方進行檢驗,所以我們定義了一個介面 IModuleACE,內含兩個方法:
	public interface IModuleACE
	{
	    Dictionary<ushort, string> GetObjectBasedPermissions();
	    void Verify();
	}
其中,GetObjectBasedPermission() 會由模組實作,傳回模組內有哪些權限,而 Verify() 則是供模組進行權限檢查與授權之用。我們在 demo 專案中新增一個新的 user control,稱為 ucForm2,裡面的設計也很簡單:
這三個按鈕分別代表了權限 P1, P3 和 P6,而我們會在程式中實作 IModuleACE,以取得物件權限的設定與檢驗能力:
	public partial class ucForm2 : UserControl, IModuleACE
	{
	    public ucForm2()
	    {
	        InitializeComponent();
	    } 
	    public Dictionary<ushort, string> GetObjectBasedPermissions()
	    {
	        Dictionary<ushort, string> items = new Dictionary<ushort, string>(); 
	        items.Add(0, "P1");
	        items.Add(1, "P2");
	        items.Add(2, "P3");
	        items.Add(3, "P4");
	        items.Add(4, "P5");
	        items.Add(5, "P6");
	        items.Add(6, "P7");
	        items.Add(7, "P8"); 
	        return items;
	    } 
	    public void Verify()
	    {
	        if (this.DesignMode)
	            return; 
ObjectACE ace = Program.User.UserACL.GetObjectACE(0x01) & Program.Group.GroupACL.GetObjectACE(0x01);
	        this.button1.Enabled = ace.GetObjectBasedPermissionFlag(3);
	        this.button2.Enabled = ace.GetObjectBasedPermissionFlag(1);
	        this.button3.Enabled = ace.GetObjectBasedPermissionFlag(6);
	    } 
	    private void ucForm2_Load(object sender, EventArgs e)
	    {
	        this.Verify();
	    }
	}
完成後,我們就執行編譯並執行 demo 程式,你可以得到這個畫面:
你會發現 P1 是無法使用的,因為在 ucForm2 中,我們定義了 P0-P7 八種權限 (每個權限 1 個 bit),而 user 允許 P3 和 P6,group 允許 P1, P2, P3 和 P6,在負向授權之下,P3 和 P6 是被允許的,所以 P3 和 P6 是可使用的,而 P1 雖然 group 允許,但 user 不允許,所以仍然是不可用狀態。
你可以修改一下 user 和 group 的權限定義,來觀察功能的可用狀態變化。
Sample Code: https://dotblogsfile.blob.core.windows.net/user/regionbbs/1108/201185143611230.rar