Как я могу осуществлять контроль доступа через таблицу 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

Доступ может быть предоставлен либо путем непосредственного указания идентификатора «вещи», либо предоставлен для всей группы вещей путем указания идентификатора группы.В приведенном выше примере пользователю 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.

Пожалуйста, не стесняйтесь оставлять комментарии, если есть какая-то информация, которую я пропустил.

Заранее спасибо!

Это было полезно?

Решение

Я не знаю диалекта 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 на Ограничение раскрытия информации в базах данных Гиппократа.

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

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 |    

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top