【问题标题】:How can I do access control via an SQL table?如何通过 SQL 表进行访问控制?
【发布时间】:2010-09-17 01:55:39
【问题描述】:

我正在尝试创建一个访问控制系统。

这是我试图控制访问权限的表的简化示例:

things table:
id   group_id   name
1    1          thing 1
2    1          thing 2
3    1          thing 3
4    1          thing 4
5    2          thing 5

访问控制表是这样的:

access table:
user_id  type   object_id  access
1        group  1          50
1        thing  1          10
1        thing  2          100

可以通过直接指定“事物”的 ID 来授予访问权限,也可以通过指定组 ID 来授予整个事物组的访问权限。在上面的示例中,用户 1 已被授予对组 1 的访问级别 50,除非有任何其他规则授予对单个事物的更具体的访问权限,否则该级别应适用。

我需要一个返回列表的查询(只有 id 可以)以及特定用户的访问级别。因此,使用上面的示例,我希望用户 id 1 是这样的:

desired result:
thing_id   access
1          10
2          100
3          50   (things 3 and 4 have no specific access rule,
4          50   so this '50' is from the group rule)
5               (thing 5 has no rules at all, so although I 
                still want it in the output, there's no access
        level for it)

我能想到的最接近的是:

SELECT * 
FROM things 
LEFT JOIN access ON 
    user_id = 1
    AND (
        (access.type = 'group' AND access.object_id = things.group_id)
        OR (access.type = 'thing' AND access.object_id = things.id)
    )

但这会返回多行,而我只希望“事物”表中的每一行都有一个。我不确定如何将每个“事物”归为一行,或者如何将“事物”规则优先于“组”规则。

如果有帮助,我使用的数据库是 PostgreSQL。

如果我遗漏了任何信息,请随时发表评论。

提前致谢!

【问题讨论】:

    标签: sql postgresql access-control


    【解决方案1】:

    我不知道 Postgres SQL 方言,但可能类似于:

    select thing.*, coalesce ( ( select access
                                 from   access
                                 where  userid = 1
                                 and    type = 'thing'
                                 and    object_id = thing.id
                               ),
                               ( select access
                                 from   access
                                 where  userid = 1
                                 and    type = 'group'
                                 and    object_id = thing.group_id
                               )
                             )
    from things
    

    顺便说一句,我不喜欢这个设计。我希望将访问表分成两个:

    thing_access (user_id, thing_id, access)
    group_access (user_id, group_id, access)
    

    然后我的查询变成:

    select thing.*, coalesce ( ( select access
                                 from   thing_access
                                 where  userid = 1
                                 and    thing_id = thing.id
                               ),
                               ( select access
                                 from   group_access
                                 where  userid = 1
                                 and    group_id = thing.group_id
                               )
                             )
    from things
    

    我更喜欢这个,因为现在可以在访问表中使用外键。

    【讨论】:

    • 哦,这是一个有趣的解决方案。似乎适用于我有限的示例数据,我将尝试一些真实数据,看看它的表现如何。作为记录,我同意桌子设计有点讨厌。不幸的是,我坚持下去了。
    【解决方案2】:

    我昨晚刚读了一篇关于这个的论文。它对如何做到这一点有一些想法。如果您无法使用标题上的链接,请尝试使用 Google Scholar Limiting Disclosure in Hippocratic Databases.

    【讨论】:

      【解决方案3】:

      虽然有几个很好的答案,但最有效的可能是这样的:

      SELECT things.id, things.group_id, things.name, max(access) 
      FROM things 
      LEFT JOIN access ON 
              user_id = 1 
              AND (
                      (access.type = 'group' AND access.object_id = things.group_id)
                      OR (access.type = 'thing' AND access.object_id = things.id)
              )
      group by things.id, things.group_id, things.name
      

      这只是使用添加到您的查询中的摘要来获取您要查找的内容。

      【讨论】:

        【解决方案4】:

        托尼:

        不错的解决方案,我喜欢它,似乎可行。这是您稍作调整后的查询:

        SELECT 
            things.*, 
            coalesce ( 
                (   SELECT access 
                    FROM access 
                    WHERE user_id = 1 
                        AND type = 'thing' 
                        AND object_id = things.id
                ), 
                (   SELECT access 
                    FROM access 
                    WHERE user_id = 1 
                        AND type = 'group' 
                        AND object_id = things.group_id 
                ) 
            ) AS access
            FROM things;
        

        而且结果看起来是正确的:

         id | group_id |  name   | access 
        ----+----------+---------+--------
          1 |        1 | thing 1 |     10
          2 |        1 | thing 2 |    100
          3 |        1 | thing 3 |     50
          4 |        1 | thing 4 |     50
          5 |        2 | thing 5 |       
        

        我确实完全认为它不是一个理想的模式。但是,我在某种程度上坚持了下来。


        约瑟夫:

        您的解决方案与我正在玩的东西非常相似,我的直觉(例如它们)告诉我应该可以这样做。不幸的是,它不会产生完全正确的结果:

         id | group_id |  name   | max 
        ----+----------+---------+-----
          1 |        1 | thing 1 |  50
          2 |        1 | thing 2 | 100
          3 |        1 | thing 3 |  50
          4 |        1 | thing 4 |  50
          5 |        2 | thing 5 |    
        

        “事物 1”的访问级别采用了更高的“组”访问值,而不是更具体的“事物”访问值 10,这正是我所追求的。我认为没有办法在 GROUP BY 中解决这个问题,但如果有人有任何建议,我很高兴在这一点上被证明是不正确的。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2016-11-18
          • 1970-01-01
          • 2011-08-15
          • 1970-01-01
          • 1970-01-01
          • 2016-09-27
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多