Предположим, у меня есть таблица предметов:
CREATE TABLE items
(
item serial PRIMARY KEY,
...
);
Теперь я хочу представить концепцию «разрешений» для каждого элемента (обратите внимание, я здесь говорю не о разрешениях доступа к базе данных, а о разрешениях бизнес-логики для этого элемента). Каждый элемент имеет разрешения по умолчанию, а также разрешения для каждого пользователя, которые могут переопределять разрешения по умолчанию.
Я попытался придумать несколько способов реализовать это и придумал следующие решения, но я не уверен, какой из них лучший и почему:
1) Булево решение
Используйте логический столбец для каждого разрешения:
CREATE TABLE items
(
item serial PRIMARY KEY,
can_change_description boolean NOT NULL,
can_change_price boolean NOT NULL,
can_delete_item_from_store boolean NOT NULL,
...
);
CREATE TABLE item_per_user_permissions
(
item int NOT NULL REFERENCES items(item),
user int NOT NULL REFERENCES users(user),
PRIMARY KEY(item, user),
can_change_description boolean NOT NULL,
can_change_price boolean NOT NULL,
can_delete_item_from_store boolean NOT NULL,
...
);
Преимущества : Каждое разрешение названо.
Недостатки : существуют десятки разрешений, которые значительно увеличивают количество столбцов, и вам нужно определить их дважды (один раз в каждой таблице).
2) Целочисленное решение
Используйте целое число и обрабатывайте его как битовое поле (т. Е. Бит 0 для can_change_description
, бит 1 для can_change_price
и т. Д. И используйте побитовые операции для установки или чтения разрешений).
CREATE DOMAIN permissions AS integer;
Преимущества : очень быстро.
Недостатки : необходимо отслеживать, какой бит обозначает какое разрешение, как в базе данных, так и во внешнем интерфейсе.
3) Решение битового поля
То же, что 2), но использовать bit(n)
. Скорее всего, те же преимущества и недостатки, возможно, немного медленнее.
4) Решение Enum
Используйте тип enum для разрешений:
CREATE TYPE permission AS ENUM ('can_change_description', 'can_change_price', .....);
а затем создайте дополнительную таблицу для разрешений по умолчанию:
CREATE TABLE item_default_permissions
(
item int NOT NULL REFERENCES items(item),
perm permission NOT NULL,
PRIMARY KEY(item, perm)
);
и измените таблицу определения для пользователя на:
CREATE TABLE item_per_user_permissions
(
item int NOT NULL REFERENCES items(item),
user int NOT NULL REFERENCES users(user),
perm permission NOT NULL,
PRIMARY KEY(item, user, perm)
);
Преимущества : легко назвать индивидуальные разрешения (вам не нужно обрабатывать битовые позиции).
Недостатки : даже при простом получении разрешений по умолчанию требуется доступ к двум дополнительным таблицам: во-первых, к таблице разрешений по умолчанию и, во-вторых, к системному каталогу, в котором хранятся значения перечисления.
Тем более, что разрешения по умолчанию должны быть получены для каждого отдельного просмотра страницы этого элемента , влияние последней альтернативы на производительность может быть значительным.
5) Решение Enum Array
То же, что 4), но использовать массив для хранения всех (по умолчанию) разрешений:
CREATE TYPE permission AS ENUM ('can_change_description', 'can_change_price', .....);
CREATE TABLE items
(
item serial PRIMARY KEY,
granted_permissions permission ARRAY,
...
);
Преимущества : легко назвать индивидуальные разрешения (вам не нужно обрабатывать битовые позиции).
Недостатки : ломает 1-ую нормальную форму и немного уродлив. Занимает значительное количество байтов подряд, если количество разрешений велико (около 50).
Можете ли вы придумать другие альтернативы?
Какой подход следует использовать и почему?
Обратите внимание: это измененная версия вопроса, опубликованного ранее в Stackoverflow .
источник
bigint
полей (каждое подходит для 64 бит) или битовую строку. Я написал пару связанных ответов на SO, которые могут помочь.Ответы:
Я знаю, что вы не спрашиваете о безопасности базы данных как таковой , но вы можете делать то, что вы хотите, используя безопасность базы данных. Вы даже можете использовать это в веб-приложении. Если вы не хотите использовать защиту базы данных, схемы все еще применяются.
Требуется защита на уровне столбцов, защита на уровне строк и, возможно, иерархическое управление ролями. Ролевая безопасность намного проще в управлении, чем пользовательская безопасность.
Этот пример кода для PostgreSQL 9.4, который скоро выйдет. Вы можете сделать это с 9.3, но требуется больше ручного труда.
Вы хотите, чтобы все было индексируемым, если вы обеспокоены производительностью †, которой вы должны быть. Это означает, что битовая маска и поля массива, вероятно, не будут хорошей идеей.
В этом примере мы сохраняем основные таблицы данных в
data
схеме и соответствующие представления вpublic
.Поместите триггер data.thing для вставок и обновлений, следя за тем, чтобы столбец владельца был current_user. Возможно, разрешить только владельцу удалять свои записи (еще один триггер).
Создайте
WITH CHECK OPTION
представление, которое пользователи будут использовать. Постарайтесь сделать его обновляемым, иначе вам понадобятся триггеры / правила, что требует больше усилий.Затем создайте таблицу со списком контроля доступа:
Измените свой вид на учетную запись для ACL:
Создайте таблицу привилегий строки по умолчанию:
Установите триггер для вставки в data.thing так, чтобы он копировал привилегии строки по умолчанию в security.thing_acl.
grantor
иadmin_option
столбцов в таблицы acl, чтобы отслеживать, кто предоставил привилегию, и может ли получатель гранта управлять привилегиями в этой строке.† В этом случае pg_has_role, вероятно, не индексируется. Вы должны получить список всех вышестоящих ролей для current_user и сравнить со значением владельца / получателя.
источник
Рассматривали ли вы возможность использования расширения Access Control List PostgreSQL?
Он содержит собственный тип данных PostgreSQL ACE и набор функций, которые позволяют вам проверить, есть ли у пользователя разрешение на доступ к данным. Он работает либо с системой ролей PostgreSQL, либо с абстрактными числами (или UUID), представляющими идентификаторы пользователя / роли вашего приложения.
В вашем случае вы просто добавляете столбец ACL в свои таблицы данных и используете одну из
acl_check_access
функций, чтобы проверить пользователя по ACL.Использование ACL - это чрезвычайно гибкий способ работы с разрешениями бизнес-логики. Кроме того, это невероятно быстро - средняя нагрузка составляет всего 25% времени, необходимого для чтения записи. Единственным ограничением является то, что он поддерживает максимум 16 пользовательских разрешений для каждого типа объекта.
источник
Я могу думать о другой возможности закодировать это, реляционная
Если вам не нужен
permission_per_item
tableyou может пропустить его и подключитьPermissions
иItems
непосредственно кitem_per_user_permissions
столу.схема легенды
источник