我正在尝试创建一个访问控制系统。
以下是我试图控制访问权限的表格的简要示例:
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
可以通过直接指定“thing”的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)
)
但是当我只想在'things'表中的每一行时,它会返回多行。我不确定如何针对每个“事物”找到一行,或者如何将“事物”规则优先于“组”规则。
如果有帮助,我使用的数据库是PostgreSQL。
如果有任何我错过的信息,请随时发表评论。
提前致谢!
答案 0 :(得分:1)
我昨晚刚读了一篇论文。它有一些关于如何做到这一点的想法。如果您无法使用标题上的链接,请尝试使用Limiting Disclosure in Hippocratic Databases.
上的Google学术搜索答案 1 :(得分: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 :(得分: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
其中只使用添加到您查询中的摘要来获取您要查找的内容。
答案 3 :(得分: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
内解决这个问题,但如果有人有任何建议,我很高兴在这一点上被证明是错误的。