Как я могу сделать контроль доступа через таблицу SQL? - PullRequest
4 голосов
/ 14 октября 2008

Я пытаюсь создать систему контроля доступа.

Вот урезанный пример того, как выглядит таблица, к которой я пытаюсь контролировать доступ:

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

Доступ может быть предоставлен либо указанием идентификатора «вещи» напрямую, либо предоставлением всей группы вещей путем указания идентификатора группы. В приведенном выше примере пользователю 1 был предоставлен уровень доступа 50 к группе 1, который должен применяться, если не существует каких-либо других правил, предоставляющих более конкретный доступ к отдельной вещи.

Мне нужен запрос, который возвращает список вещей (только идентификаторы в порядке) вместе с уровнем доступа для конкретного пользователя. Поэтому, используя приведенный выше пример, я бы хотел что-то подобное для идентификатора пользователя 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 ]

1 голос
/ 14 октября 2008

Я не знаю диалект 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

Я предпочитаю это, потому что внешние ключи теперь могут использоваться в таблицах доступа.

1 голос
/ 14 октября 2008

Хотя есть несколько хороших ответов, наиболее эффективный, вероятно, будет что-то вроде этого:

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

Который просто использует суммирование, добавленное к вашему запросу, чтобы получить то, что вы ищете.

1 голос
/ 14 октября 2008

Прошлой ночью я только что прочитал об этом. У него есть некоторые идеи о том, как это сделать. Если вы не можете использовать ссылку на заголовок, попробуйте Google Scholar на Ограничение раскрытия в базах данных Гиппократа.

0 голосов
/ 14 октября 2008

Tony:

Неплохое решение, мне нравится, кажется, работает. Вот ваш запрос после незначительной настройки:

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 |    

Уровень доступа для «вещи 1» принял более высокое значение доступа «группы», а не более конкретное значение доступа «вещи», равное 10, и это то, чего я добиваюсь. Я не думаю, что есть способ исправить это в GROUP BY, но если у кого-то есть какие-либо предложения, я более чем счастлив оказаться неверным в этом вопросе.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...