Подтвердить что ты не робот

Схема базы данных для ACL

Я хочу создать схему для ACL; однако я разрывается между несколькими способами его реализации.

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

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

Сначала я собирался нормализовать данные и иметь три таблицы для представления отношений.

ROLES { id, name }
RESOURCES { id, name }
PERMISSIONS { id, role_id, resource_id }

Запрос, чтобы выяснить, разрешен ли пользователь где-нибудь, будет выглядеть так:

SELECT id FROM resources WHERE name = ?
SELECT * FROM permissions WHERE role_id = ? AND resource_id = ? ($user_role_id, $resource->id)

Тогда я понял, что у меня будет только около 20 ресурсов, каждая из которых имеет до 5 действий (создание, обновление, просмотр и т.д.) и, возможно, еще 8 ролей. Это означает, что я могу проявлять явное пренебрежение нормализацией данных, поскольку у меня никогда не будет более нескольких сотен возможных записей.

Так что, возможно, такая схема имела бы смысл.

ROLES { id, name }
PERMISSIONS { id, role_id, resource_name }

который позволит мне искать записи в одном запросе

SELECT * FROM permissions WHERE role_id = ? AND permission  = ? ($user_role_id, 'post.update')

Итак, какая из них правильнее? Существуют ли другие схемы компоновки для ACL?

4b9b3361

Ответ 1

По моему опыту, реальный вопрос в основном ломается о том, произойдет ли какая-либо часть ограничения доступа для пользователя.

Предположим, например, что вы разрабатываете схему сообщества и разрешаете пользователям изменять видимость своего профиля.

Один из вариантов заключается в том, чтобы придерживаться общедоступного/закрытого профиля и придерживаться широких проверок с предварительным удержанием: "users.view" (просматривает общедоступных пользователей), например, "users.view_all" (просматривает всех пользователей, для модераторов).

Другой способ включает в себя более четкие разрешения, вы можете захотеть, чтобы они могли настраивать вещи, чтобы они могли сделать себя (a) видимыми для всех, (б) просматривать их собеседниками, (c) полностью закрыты и возможно (d) можно просмотреть всеми, кроме своих бозонов, выбранных вручную. В этом случае вам нужно сохранить данные о владельце/доступе для отдельных строк, и вам нужно будет сильно абстрагироваться от некоторых из этих вещей, чтобы избежать материализации транзитивного закрытия плотного ориентированного графика.

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

  • Пользователи могут иметь несколько ролей
  • Роли и разрешения объединены в одну и ту же таблицу с флагом, чтобы различать два (полезно при редактировании ролей/пермов)
  • Роли могут назначать другие роли, а роли и perms могут назначать разрешения (но разрешения не могут назначать роли) из одной таблицы.

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

Оттуда вытаскивание пользовательских разрешений - это проверка того, какие роли у него есть, и обработка их с использованием графа разрешений для получения окончательных разрешений. Проверьте разрешения, указав, что пользователь имеет указанную роль/разрешение или нет. А затем запустите запрос/выпустите ошибку на основе этой проверки прав.

Вы можете продлить проверку для отдельных узлов (т.е. check_perms($user, 'users.edit', $node) for "может редактировать это node" vs check_perms($user, 'users.edit') для "может редактировать node" ), если вам нужно, и у вас будет что-то очень гибкий/простой в использовании для конечных пользователей.

В качестве примера открытия следует проиллюстрировать, будьте осторожны, слишком сильно управляйте разрешениями на уровне строк. Узкое место производительности меньше проверяет отдельные разрешения node, чем при вытягивании списка допустимых узлов (т.е. Только те, которые пользователь может просматривать или редактировать). Я бы советовал ничего, кроме флагов и полей user_id внутри самих строк, если вы не очень хорошо разбираетесь в оптимизации запросов.

Ответ 2

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

Количество строк, которые вы ожидаете, не является критерием выбора для нормальной формы. Нормализация касается целостности данных. Обычно это повышает целостность данных за счет сокращения избыточности.

Реальный вопрос: "Сколько строк у меня есть?", но "Насколько важно, чтобы база данных всегда давала мне правильные ответы?" Для базы данных, которая будет использоваться для реализации ACL, я бы сказал, что "Pretty danged important".

Если что-либо, небольшое количество строк подсказывает, что вам не нужно беспокоиться о производительности, поэтому 5NF должен быть простым выбором. Вам нужно нажать 5NF, прежде чем добавлять номера идентификаторов.

Запрос, чтобы выяснить, был ли пользователь разрешено где-то выглядеть это:

SELECT id FROM resources WHERE name = ?
SELECT * FROM permissions 
WHERE role_id = ? AND resource_id = ? ($user_role_id, $resource->id)

Что вы написали, так как два запроса вместо использования внутреннего соединения подсказывают, что вы можете быть в вашей голове. (Это наблюдение, а не критика.)

SELECT p.* 
FROM permissions p
INNER JOIN resources r ON (r.id = p.resource_id AND 
                           r.name = ?)

Ответ 3

Вы можете использовать SET для назначения ролей.

CREATE TABLE permission (
  id integer primary key autoincrement
  ,name varchar
  ,perm SET('create', 'edit', 'delete', 'view')
  ,resource_id integer );