SQL 테이블을 통해 어떻게 액세스 제어를 할 수 있습니까?
-
03-07-2019 - |
문제
액세스 제어 시스템을 만들려고합니다.
다음은 내가 룩에 대한 액세스를 제어하려는 테이블의 예를 살펴 봅니다.
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은 50에서 그룹 1에서 액세스 레벨을 부여 받았으며, 이는 개인에 대한보다 구체적인 액세스 권한을 부여하는 다른 규칙이없는 한 적용되어야합니다.
특정 사용자의 액세스 레벨과 함께 사물 목록 (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입니다.
내가 놓친 정보가 있으면 의견을 남겨 주시기 바랍니다.
미리 감사드립니다!
해결책
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
외국 키는 이제 액세스 테이블에서 사용할 수 있기 때문에 이것을 선호합니다.
다른 팁
나는 어제 밤에 종이를 읽었습니다. 이 작업을 수행하는 방법에 대한 아이디어가 있습니다. 제목에서 링크를 사용할 수없는 경우 Google Scholar를 사용해보십시오. 해마 데이터베이스의 공개 제한.
몇 가지 좋은 답변이 있지만 가장 효율적인 것은 아마도 다음과 같은 것일 것입니다.
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
이것은 단순히 당신이 찾고있는 것을 얻기 위해 쿼리에 추가 된 요약을 사용합니다.
토니 :
나쁜 해결책이 아니라, 나는 그것을 좋아하는 것 같습니다. 사소한 조정 후 쿼리는 다음과 같습니다.
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 |
그래요 완전히 이상적인 스키마가 아닌 것에 대해 지적하십시오. 그러나 나는 어느 정도 그것에 붙어 있습니다.
Josef :
당신의 해결책은 내가 가지고있는 것들과 매우 유사하며, 내 본능 (예 : 그것들과 같은)은 그렇게 할 수 있다고 말해줍니다. 불행히도 완전히 올바른 결과를 얻지 못합니다.
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 |
'Thing 1'의 액세스 레벨은 10의보다 구체적인 'Thing'액세스 값보다는 더 높은 '그룹'액세스 값을 가져 왔습니다. 나는 그것을 고치는 방법이 있다고 생각하지 않습니다. GROUP BY
, 그러나 누군가 제안이 있다면 그 시점에서 부정확 한 것으로 입증되어 기쁩니다.