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にはグループ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'テーブルの各行に1つだけが必要な場合、複数の行が返されます。各「もの」ごとに単一の行にたどり着く方法や、「グループ」規則よりも「もの」規則を優先する方法がわかりません。
それが役立つ場合、使用しているデータベースは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
ちなみに、私はデザインが好きではありません。アクセステーブルを2つに分割することをお勧めします。
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 |
私は完全に、それが理想的なスキーマではないという点を理解しています。ただし、ある程度は行き詰まっています。
ジョセフ:
あなたの解決策は私が遊んでいたものと非常に似ており、私の本能(など)は、そのようにすることが可能であるべきだと言っています。残念ながら、完全に正しい結果は得られません:
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'のアクセスレベルは、より具体的な 'thing'アクセス値10ではなく、より高い 'group'アクセス値を取得しました。 GROUP BY
内でそれを修正する方法はないと思いますが、何か提案があれば、その点で間違っていることが証明されてうれしいです。