4

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

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

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。

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

提前致谢!

4

4 回答 4

1

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

于 2008-10-14T11:50:57.110 回答
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

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

于 2008-10-14T11:56:43.823 回答
1

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

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

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

于 2008-10-14T11:59:56.563 回答
0

托尼:

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

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.

于 2008-10-14T12:36:50.200 回答