Задать вопрос

Как хранить в БД права доступа?

Положим есть единицы данных «content» и есть пользователи «user».


Пользователи могут создавать content и настраивать права доступа для других пользователей по нескольким правилам:

— доступно всем

— доступно друзьям

— доступно конкретному другу/списку друзей


Правила складываются через «или». Т.е. доступно, если доступно хотя бы по одному из правил.

content [id, user_id, name]
user [id, name]
user_friend [user_id, friend_user_id]


Как организовать хранение таких правил в БД с тем, чтобы можно было составить не фулскановый запрос на выборку всех (via pagination) «content», доступных для конкретного пользователя?


Простейшая нормализованная схема хранения:

content_share_wide [content_id, type] // всем или друзьям, где type = 1 - всем, type = 2 - друзьям

content_share_user [content_id, user_id] // контент, расшаренный на user_id



При такой схеме получается невозможным составить запрос кроме как с помощью UNION, что очень негативно сказывается на производительности.


Как сделать лучше? (как вообще сделать, потому что такой union и за работающий вариант считать не хочется)
  • Вопрос задан
  • 5361 просмотр
Подписаться 9 Оценить Комментировать
Ответ пользователя rPman К ответам на вопрос (5)
@rPman
'Доступно всем' без вариантов нужно хранить в виде bolean у content, даже хотя бы в виде копии, заполняемой тригером у таблицы content_share.

'Доступно друзьям' и 'Доступно конкретному пользователю'… так ли важно разделять эти понятия. это бы имело смысл, если бы количество действий по созданию нового пользователя и добавлению прав было бы сравнимо с количеством запросов на права доступа, а это маловероятно, наверняка в вашей задаче количество запросов на чтение на порядок (или обычно это логарифм) больше изменений.
Может быть достаточно правила 'Доступно конкретному пользователю', а значит обойдетесь таблицей content_share_user {user_id,content_id}

Дальше, никогда не нужно надеяться на чистую реляционную модель. Делайте дополнительную копию на все, что читается чаще чем пишется в удобном для этого месте. Сериализованный список идентификаторов user_id в content.authorised_list (если это числа, то к примеру через ',' с обязательным ',' в конце), если их количество меньше определенного, удобен для запросов вида like '%12345,%', и ведь его можно заполнять не сразу, а периодически отдельным процессом и очищать по триггеру на изменении. Тогда основная нагрузка ляжет не на выполнение тригера, а на запросы только последних измененных данных, а их обычно не так много.
content
.authorised_list varchar = '123,234,345,' или null — для данных, которые нужно запросить из content_share_user
.authorised_all boolean
content_share_user {user_id,content_id}
Ответ написан