2018/04/20

SampleCMS 帳號與權限系統元件說明:元件提供的功能與使用方式

上一篇 帳號與權限規則說明 提到了授權範圍的定義以及後台網頁程式如何達成權限判斷,而系統邏輯層中的帳號與權限元件就是依照帳號與權限規則設計。

接下來繼續說明帳號與權限元件所提供的功能,以及呼叫者端(展示層的網頁程式)如何使用這個元件。

下圖是帳號與權限元件關聯圖,

帳號與權限元件 EmployeeAuthorityLogic 主要提供三大類的功能,
  1. 初始化
  2. 權限判斷結果
  3. 資料存取

1. 初始化

1-1. 建構式 EmployeeAuthorityLogic(IAuthenticationConditionProvider)
EmployeeAuthorityLogic 只負責取得帳號在指定作業選項的授權設定值與提供權限判斷結果。帳號名稱與作業選項代碼需要由外部提供,而介面 IAuthenticationConditionProvider 負責提供這類相關的條件值。通常 IAuthenticationConditionProvider 交由各個管理頁共用元件實作。若呼叫者端不需要使用後續「權限判斷結果」的功能,則不需要輸入參數 IAuthenticationConditionProvider。

1-2. 開放外部元件自訂帳號授權結果 SetCustomEmployeeAuthorizationResult(ICustomEmployeeAuthorizationResult)
EmployeeAuthorityLogic 透過介面 ICustomEmployeeAuthorizationResult 取得作業選項子項目的擁有者資訊做為權限判斷條件,或者是取回外部元件自訂的權限判斷結果取代原本的結果值。
此外,系統預設會在建構式時檢查 IAuthenticationConditionProvider 的物件是否也有實作介面 ICustomEmployeeAuthorizationResult,若有就自動設定。通常各個管理頁共用元件會實作此介面。

1-3. 初始化授權結果
依照前述初始化功能得到的條件(帳號、作業選項代碼、是否有自訂帳號授權結果)取得並且暫存相關的授權設定值在 EmployeeAuthorizations 物件裡。
InitialAuthorizationResultOfTopPage() 提供給「作業選項」的最上層使用(例如:帳號清單頁)。
InitialAuthorizationResultOfSubPages() 提供給「作業選項的子項目」使用(例如:帳號設定頁)。

2. 權限判斷結果
使用「1-3. 初始化授權結果」暫存的授權設定值,依據權限規則回傳各項權限判斷結果。
  • CanOpenThisPage()
    檢查是否可開啟此頁面,清單頁載入時使用。
     
  • CanEditThisPage(bool useTopRule, string ownerAccount, int ownerDeptId)
    檢查是否可編輯此頁面,設定頁載入時使用,或者是清單頁用來判斷(子項目)清單中各項資料的編輯鈕是否該出現。
     
  • CanDelThisPage(string ownerAccount, int ownerDeptId)
    檢查是否可刪除此頁面,清單頁用來判斷(子項目)清單中各項資料的刪除鈕是否該出現。
     
  • CanAddSubItemInThisPage()
    檢查是否可在此頁面新增子項目,(新增資料模式的)設定頁載入時使用。

3. 資料存取

3-1. 讀取暫存的授權設定值
  • CanReadSubItemOfOthers(), CanReadSubItemOfCrew(), CanReadSubItemOfSelf()
    可否閱讀(任何人的、同部門的、自己的)子項目。
     
  • CanEditSubItemOfOthers(), CanEditSubItemOfCrew(), CanEditSubItemOfSelf()
    可否修改(任何人的、同部門的、自己的)子項目。
相同的權限規則判斷在某些功能中需要寫在 Stored Procedure 裡面,因此需要這幾個方法來讀取暫存的授權設定值,傳入給需要的 Stored Procedure。

3-2. 存取資料庫
  • GetYYYList()
  • GetYYYData()
  • InsertYYYData()
  • UpdateYYYData()
  • DeleteYYYData()
YYY 代表各種不同的資料類型名稱。例如:部門管理相關的方法名稱就是 GetDepartmentList(), GetDepartmentData(), InsertDepartmentData(), UpdateDepartmentData(), DeleteDepartmentData()。

取得資料用的方法名稱皆以 Get 開頭命名,而不管是取得清單資料或是單一資料,皆使用 DataSet 存放回傳值,留給 Stored Procedure 可回傳多個資料表的彈性。

新增資料、更新資料、刪除資料用的方法名稱以順序以 Insert, Update, Delete 開頭命名。

若執行上述存取資料庫用的方法失敗了,可使用 GetDbErrMsg() 取得資料層回傳的錯誤訊息文字。


接下來以「帳號管理」的帳號清單頁帳號設定頁為例說明網頁程式使用帳號與權限元件的方式。

帳號清單頁 Account-List.aspx.cs

初始化元件
public partial class Account_List : BasePage
{
    protected AccountCommonOfBackend c;
    protected EmployeeAuthorityLogic empAuth;
    // ...略...

    protected void Page_PreInit(object sender, EventArgs e)
    {
        c = new AccountCommonOfBackend(this.Context, this.ViewState);
        // ...略...

        empAuth = new EmployeeAuthorityLogic(c);
        empAuth.InitialAuthorizationResultOfTopPage();

        // ...略...
    }

Line 9: 產生「帳號管理」專屬的管理頁共用元件。

Line 12: 產生帳號與權限元件,同時將管理頁共用元件傳入提供驗證時需要的條件值。

Line 13: 初始化授權結果,取得並且暫存目前登入帳號被授權使用「帳號管理」的授權設定值。

開啟頁面時,判斷是否允許開啟此頁面
    protected void Page_Load(object sender, EventArgs e)
    {
        // ...略...

        if (!IsPostBack)
        {
            if (!empAuth.CanOpenThisPage())
            {
                Response.Redirect(c.BACK_END_HOME);
            }

            // ...略...
        }
        // ...略...
    }

Line 7, 9: 詢問帳號與權限元件目前登入帳號是否可開啟此頁面,如果不允許則跳轉至後台首頁,不讓登入帳號的使用者看到任何相關內容。

使用者介面元素控制
    private void LoadUIData()
    {
        //...略...

        //HUD
        if (empAuth.CanAddSubItemInThisPage())
        {
            hud.SetButtonVisible(HudButtonNameEnum.AddNew, true);
            hud.SetButtonAttribute(HudButtonNameEnum.AddNew, HudButtonAttributeEnum.JsInNavigateUrl,
                string.Format("popWin('Account-Config.aspx?act={0}', 700, 600);", ConfigFormAction.add));
        }

        //...略...
    }
 
 //...略...

    protected void rptAccounts_ItemDataBound(object sender, RepeaterItemEventArgs e)
    {
        //...略...

        HtmlAnchor btnEdit = (HtmlAnchor)e.Item.FindControl("btnEdit");
        //...略...

        LinkButton btnDelete = (LinkButton)e.Item.FindControl("btnDelete");
        //...略...

        string ownerAccount = drvTemp.ToSafeStr("OwnerAccount");
        int ownerDeptId = Convert.ToInt32(drvTemp["OwnerDeptId"]);

        btnEdit.Visible = (empAuth.CanEditThisPage(false, ownerAccount, ownerDeptId) || c.IsMyAccount(empAccount));

        if (!empAuth.CanDelThisPage(ownerAccount, ownerDeptId)
            || empAccount == "admin"
            || empAccount == c.GetEmpAccount())
        {
            btnDelete.Visible = false;
        }
    }

Line 6~11: 詢問帳號與權限元件目前登入帳號是否可在此頁面新增子項目,如果可以則在畫面上顯示新增帳號的按鈕(系統預設隱藏此按鈕)。

從 Line 18 開始,是繪製清單項目的程式碼。

Line 28, 29: 取得輪詢到的帳號資料的擁有者資訊(擁有者帳號 OwnerAccount、擁有者所屬部門代碼 OwnerDeptId)。

Line 31: 詢問帳號與權限元件目前登入帳號是否可編輯這項帳號資料,若不行則隱藏修改鈕(如上圖所示,右側綠底的按鈕)。
使用帳號與權限元件的方法
CanEditThisPage(bool useTopRule, string ownerAccount, int ownerDeptId)
第一個參數 useTopRule 使用 false,代表使用權限規則中「作業選項的子項目」的設定值。
另外需傳入 Line 28, 29 取得的擁有者資訊,權限判斷才會正確。

Line 33~38: 詢問帳號與權限元件目前登入帳號是否可刪除這項帳號資料,若不行則隱藏刪除鈕(如上圖所示,右側紅底的按鈕)。
使用帳號與權限元件的方法
CanDelThisPage(string ownerAccount, int ownerDeptId)
這個方法只會使用權限規則中「作業選項的子項目」的設定值,因此不像 CanEditThisPage() 需要 useTopRule 參數。

* 完整程式碼請參考 Account-List.aspx.cs

帳號設定頁 Account-Config.aspx.cs

初始化元件
public partial class Account_Config : System.Web.UI.Page
{
    protected AccountCommonOfBackend c;
    protected EmployeeAuthorityLogic empAuth;

    // ...略...

    protected void Page_PreInit(object sender, EventArgs e)
    {
        c = new AccountCommonOfBackend(this.Context, this.ViewState);
        // ...略...

        empAuth = new EmployeeAuthorityLogic(c);
        empAuth.InitialAuthorizationResultOfSubPages();
    }

Line 10: 產生「帳號管理」專屬的管理頁共用元件。

Line 13: 產生帳號與權限元件,同時將管理頁共用元件傳入提供驗證時需要的條件值。

Line 14: 初始化授權結果,取得並且暫存目前登入帳號被授權使用「帳號管理」的授權設定值,與清單頁不同的是這裡使用 InitialAuthorizationResultOfSubPages() 方法名稱結尾為 SubPages,不是清單頁用的 TopPage。

開啟頁面時,判斷是否允許開啟此頁面
    protected void Page_Load(object sender, EventArgs e)
    {
        if (!IsPostBack)
        {
            // Authenticate
            if (c.qsAct == ConfigFormAction.edit && !(empAuth.CanEditThisPage() || c.IsMyAccount())
                || c.qsAct == ConfigFormAction.add && !empAuth.CanAddSubItemInThisPage())
            {
                string jsClose = "closeThisForm();";
                ClientScript.RegisterStartupScript(this.GetType(), "invalid", jsClose, true);
                return;
            }

            // ...略...
        }
        // ...略...
    }

Line 6~12: 詢問帳號與權限元件目前登入帳號是否可開啟此頁面(依 c.qsAct 區分是新增或是編輯模式),如果不允許則關閉目前網頁(因為設定頁皆開啟於獨立的網頁視窗),並且停止載入本設定頁的所有資料,不讓登入帳號的使用者看到任何相關內容。

* Line 6 使用的方法 CanEditThisPage() 之所以不用傳入擁有者資訊是因為管理頁共用元件實作了介面 ICustomEmployeeAuthorizationResult,透過實作的介面方法 InitialAuthorizationResult(bool isTopPageOfOperation, EmployeeAuthorizations authorizations) 將擁有者資訊交給了帳號與權限元件。這部分屬於客製化權限判斷,將另文說明。

* 完整程式碼請參考 Account-Config.aspx.cs




沒有留言:

張貼留言