什么数据类型用于存储用户权限?

8
我们已经开发了一段代码,基本上返回用户在实体上的权限数据。例如,实体可以是以下之一:
-Company
-Contact
-Project
-Issue etc...

我们可以分配策略(一个人可以有多个策略),允许用户执行某些操作:
-Create
-Edit
-Delete
-Export

基本上,一个策略可能会说用户A有权创建公司,但是另一个相同用户拥有的策略却说他没有创建公司的权利。在这种情况下,我们会考虑允许的权利而不是禁止的权利。在此示例中,他/她将被允许创建公司。

基本上,你最终得到的数据可能是这样的:

Policy1 Company Create Yes
Policy1 Company Edit Yes
Policy1 Company Delete No
Policy2 Company Create No
Policy2 Company Edit   Yes
Policy2 Company Delete No

我有一个查询,用来根据我们讨论的规则返回用户的权限。

在这种情况下运行查询,结果如下:

Company create yes
Company edit yes
Company delete no

我们的应用程序不仅仅是一个简单的“是”或“否”,以确定用户是否可以执行某个操作。我们有“是”、“否”和“仅所有者”(对于只能由所有者编辑/删除的记录)。我们的查询很棒,返回了正确的数据。
我的问题是,在C#中应该使用哪种数据类型来表达以下意思:
给定一个实体(公司),给定一个操作(创建),其价值是什么。基本上,最终我想要构建一个类似于这样的矩阵:
           Create     Edit        Delete
Company      Yes     Owner Only    Yes
Contact      No      No            No
Project      Yes     Yes          Owner Only

第一列的行表示实体,此后的列表示动作(创建、编辑、删除)。例如索引的组合:[公司][创建]=是会根据实体赋予您该操作的权限。

那么什么类型的数据适用于这个模型,使我可以执行像[联系人][编辑]=否这样的索引呢?我们还需要对此对象进行会话/设计一种方法(可能是动态的),以便根据实体和操作获取结果。

我认为会话是不错的选择,因为我们只需要在用户注销之前检查权限一次。

2个回答

4

这不是最终答案,但我考虑了一下,也许可以得到一些反馈。以下是我的想法:

我在考虑如何存储权限,我相信我已经想出了一些可行的方法。

我一直在想的是 - 我只需要一个二维矩阵...然后显而易见的是,为什么我们不使用一个简单的二维整数矩阵呢?它至少在纸上是可行的。
正如您所知,我们有一些枚举:

Modules          Answer                 Action 
Contact = 1      Yes = 1                Create = 1
Corp = 2         No = 2                 Edit = 2
Company = 3      Originator Only = 3    Delete = 3
Program = 4                             View = 4
Project = 5                             List =5
SubProject  = 6                         Export = 6
Issue = 7       
LL = 8      
User = 9        

在这种情况下,我们有9个模块和6种操作类型,因此我们需要一个大小为[9x6]的矩阵,基于matrix[i,j]生成54个可能的条目。即使我们添加了更多的模块和操作,这也可以工作。
类型很简单: int[,] permissions = new int[Enum.GetNames(typeof(Modules)).Length, Enum.GetNames(typeof(Action)).Length]; 简单地转化为: int[,] permissions = new int[9,6] 到目前为止,这给了我们一个空矩阵。我们必须记住,在C#中,数组/矩阵从零开始计数...这很好。我们已经有一个存储过程返回数据如下:
Module       Action          Answer
1 (Contact)   1 (Create)     1 (Yes)
1 (Contact)   2 (Edit)       1 (Yes)
1 (Contact)   3 (Delete)     2 (No)
…..     
2 (Corp)      1 (Create)     1 (Yes)
2 (Corp)      2 (Edit)       1 (Yes)
Etc…etc…

现在我们要填充数组,以下是C#伪代码:

for(int i=0; i<Enum.GetNames(typeof(Modules)).Length; i++) {
  Reader.read();  //read the first row from the database
   For(int j=0; j<Enum.GetNames(typeof(Action)).Length; j++) {
         Permissions[i,j] = reader[“Answer”];  //assign the answer from the returned row to the array
        If( j<Enum.GetNames(typeof(Action)).Length-1) {
            Reader.read(); //read next row in database
        } //end if condition
  } //end for j
} //end for i

这个循环将简单地产生以下结果:
             1 Create    2 Edit 3 Delete    4 View  5 List  6 Export
1(Contact)     1           1      2            1    1         1
2(Corp)        1           3      2            1    1         1
3(Company)     1           1      1            1    1         1
4(Program)     1           1      1            1    1         1
5(Project)     1           1      2            1    1         1
6(SubProject)  1           1      2            1    1         1
7(Issues)      1           1      1            2    1         1
8(LL)          1           1      1            1    1         1
9(User)        2           2      2            1    2         2

这将是每个用户的…而这正是我想要的,因为我想编写类似于以下伪代码的代码(请记住,这只是伪代码):
If(MySession.Current.Permission[(int)Module.Contact1, (int)Action.Create] == (int)Answer.Yes)
 {
  //this allows contact create
}

这种方法非常灵活,我们可以随时添加任何新的模块而不影响其他内容。我可以通过简单地引用Permission[Module.ModuleIWant – 1, Action.ActionIWant – 1] 来实现所需功能。

由于数组以零为基础,我们只需确保从中减去1即可...我们不需要其他东西。虽然这可能不是最佳解决方案,但我认为它能够正常工作...


3
我们最终得到了:
 public class EntityPermission
    {
        private readonly List<GetUserPermissionsResult> _userPermissionDataSet;
        private readonly Dictionary<int, Dictionary<int, int>> _permissions;
        /// <summary>
        /// Constructor to generate permissions for a user
        /// </summary>
        /// <param name="ds">
        /// Dataset of type List GetUserPermissionsResult
        /// based on a stored procedure which brings back the 
        /// valid permissions of a user.
        /// The result is a matrix of size [Enitities] * [Actions]
        /// Where each entity action [index] is the value (right).
        /// In general terms, the entity e with action a has right r.
        /// </param>
        public EntityPermission(List<GetUserPermissionsResult> ds)
        {
            _userPermissionDataSet = ds;
            _permissions = new Dictionary<int, Dictionary<int, int>>();
            SetPermissions();
        }

        /// <summary>
        /// Called from the constructor of EntityPermission.
        /// This method fills our matrix of size entity * action with 
        /// the valid rights.
        /// </summary>
        public void SetPermissions()
        {
            var dt = _userPermissionDataSet;
            for (int i = 1; i<=Enum.GetNames(typeof(Module)).Length; i++)
            {
                var actionDictionary = new Dictionary<int, int>();
                for (int j = 1; j<=Enum.GetNames(typeof(ActionEnum)).Length; j++)
                {
                    var value = (from a in dt where a.EntityID == i && a.ActionID == j select a.Answer).FirstOrDefault();
                    if (value != null)
                        actionDictionary.Add(j , (int) value);
                    else actionDictionary.Add(j, (int)Answer.No);
                }
                _permissions.Add(i, actionDictionary);
            }
        }

        /// <summary>
        /// Method to get the rights provided an entity (a module)
        /// and an action on that module.
        /// </summary>
        /// <param name="entityIdKey"></param>
        /// <param name="actionIdKey"></param>
        /// <returns></returns>
        public int GetPermission(int entityIdKey, int actionIdKey)
        {
            return _permissions[entityIdKey][actionIdKey]; 
        }   
    }

只读 List<GetUserPermissionsResult> 是从一个基于我的问题返回矩阵的存储过程中返回的类型 - 没有太多细节:

SELECT 
    e.EntityID AS EntityID,
    a.ActionID AS ActionID, 
    CASE MAX(ar.[Rank]) 
        WHEN 3 THEN 1   --yes
        WHEN 2 THEN 3   --originator only
        WHEN 1 THEN 2   --no
    END AS [Answer]
FROM
 ....

这个存储过程有很多连接,但基本上是按以下方式分组的:
GROUP BY
    e.EntityID,
    a.ActionID

这样做可以保证我们每个模块(实体)都有一个动作。我们将此对象存储为用户会话的一部分:public EntityPermission Permission { get; set; },然后只需调用 GetPermission 即可获取结果。
 if (((int)Answer.Yes ==
                 MySession.Current.Permission.GetPermission((int)Module.SubProject, (int)ActionEnum.Edit))

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接