SQLテーブル経由でアクセス制御を行うにはどうすればよいですか?

StackOverflow https://stackoverflow.com/questions/200810

  •  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 内でそれを修正する方法はないと思いますが、何か提案があれば、その点で間違っていることが証明されてうれしいです。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top