Supongamos que tengo una tabla de elementos:
CREATE TABLE items
(
item serial PRIMARY KEY,
...
);
Ahora, quiero presentar el concepto de "permisos" para cada elemento (tenga en cuenta que no estoy hablando de permisos de acceso a la base de datos aquí, sino de permisos de lógica de negocios para ese elemento). Cada elemento tiene permisos predeterminados y también permisos por usuario que pueden anular los permisos predeterminados.
Traté de pensar en varias formas de implementar esto y se me ocurrieron las siguientes soluciones, pero no estoy seguro de cuál es la mejor y por qué:
1) La solución booleana
Use una columna booleana para cada permiso:
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,
...
);
Ventajas : se nombra cada permiso.
Desventajas : hay docenas de permisos que aumentan significativamente el número de columnas y debe definirlas dos veces (una en cada tabla).
2) La solución entera
Use un número entero y trátelo como un campo de bits (es decir, el bit 0 es para can_change_description
, el bit 1 es para can_change_price
, y así sucesivamente, y use operaciones bit a bit para establecer o leer permisos).
CREATE DOMAIN permissions AS integer;
Ventajas : muy rápido.
Desventajas : debe realizar un seguimiento de qué bit representa qué permiso tanto en la base de datos como en la interfaz front-end.
3) La solución de Bitfield
Igual que 2), pero uso bit(n)
. Lo más probable es que tengan las mismas ventajas y desventajas, quizás un poco más lento.
4) La solución Enum
Use un tipo de enumeración para los permisos:
CREATE TYPE permission AS ENUM ('can_change_description', 'can_change_price', .....);
y luego cree una tabla adicional para los permisos predeterminados:
CREATE TABLE item_default_permissions
(
item int NOT NULL REFERENCES items(item),
perm permission NOT NULL,
PRIMARY KEY(item, perm)
);
y cambie la tabla de definición por usuario a:
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)
);
Ventajas : Fácil de nombrar permisos individuales (no tiene que manejar posiciones de bits).
Desventajas : incluso cuando solo se recuperan los permisos predeterminados, se requiere acceder a dos tablas adicionales: primero, la tabla de permisos predeterminada y, en segundo lugar, el catálogo del sistema que almacena los valores de enumeración.
Especialmente porque los permisos predeterminados deben recuperarse para cada vista de página de ese elemento , el impacto en el rendimiento de la última alternativa podría ser significativo.
5) La solución de matriz de enumeración
Igual que 4), pero use una matriz para mantener todos los permisos (predeterminados):
CREATE TYPE permission AS ENUM ('can_change_description', 'can_change_price', .....);
CREATE TABLE items
(
item serial PRIMARY KEY,
granted_permissions permission ARRAY,
...
);
Ventajas : Fácil de nombrar permisos individuales (no tiene que manejar posiciones de bits).
Desventajas : rompe la primera forma normal y es un poco feo. Ocupa una cantidad considerable de bytes seguidos si la cantidad de permisos es grande (aproximadamente 50).
¿Se te ocurren otras alternativas?
¿Qué enfoque se debe tomar y por qué?
Tenga en cuenta: esta es una versión modificada de una pregunta publicada anteriormente en Stackoverflow .
fuente
bigint
campos (cada uno válido para 64 bits) o una cadena de bits. Escribí un par de respuestas relacionadas sobre SO que podrían ser de ayuda.Respuestas:
Sé que no está preguntando sobre la seguridad de la base de datos per se , pero puede hacer lo que quiera con la seguridad de la base de datos. Incluso puedes usar esto en una aplicación web. Si no desea utilizar la seguridad de la base de datos, los esquemas aún se aplican.
Desea seguridad a nivel de columna, seguridad a nivel de fila y probablemente administración de roles jerárquicos. La seguridad basada en roles es mucho más fácil de administrar que la seguridad basada en usuarios.
Este código de ejemplo es para PostgreSQL 9.4, que saldrá pronto. Puede hacerlo con 9.3, pero se requiere más trabajo manual.
Desea que todo sea indexable si le preocupa el rendimiento †, que debería ser. Esto significa que los campos de máscara de bits y matriz probablemente no serán una buena idea.
En este ejemplo, mantenemos las tablas de datos principales en el
data
esquema y las vistas correspondientes enpublic
.Ponga un disparador en data.thing para inserciones y actualizaciones que obliguen a que la columna del propietario sea current_user. Quizás permita que solo el propietario elimine sus propios registros (otro desencadenante).
Cree una
WITH CHECK OPTION
vista, que es lo que los usuarios realmente usarán. Intenta realmente hacer que sea actualizable, de lo contrario necesitarás disparadores / reglas, que es más trabajo.A continuación, cree una tabla de lista de control de acceso:
Cambie su vista para tener en cuenta las ACL:
Cree una tabla de privilegios de fila predeterminada:
Ponga un disparador en insertar en data.thing para que copie privilegios de fila predeterminados a security.thing_acl.
grantor
yadmin_option
columnas a ACL tablas para hacer un seguimiento que ha otorgado el privilegio, y si el concesionario puede gestionar los privilegios de esa fila.† En este caso, pg_has_role probablemente no sea indexable. Debería obtener una lista de todos los roles superiores de current_user y compararlos con el valor del propietario / concesionario.
fuente
¿Ha considerado usar la extensión PostgreSQL de Access Control List ?
Contiene el tipo de datos PostgreSQL nativo ACE y un conjunto de funciones que le permiten verificar si un usuario tiene permiso para acceder a los datos. Funciona con el sistema de roles PostgreSQL o con números abstractos (o UUID) que representan los ID de usuario / rol de su aplicación.
En su caso, simplemente agregue una columna ACL a sus tablas de datos y use una de las
acl_check_access
funciones para comparar a un usuario con una ACL.El uso de ACL es una forma extremadamente flexible de tratar con los permisos de lógica de negocios. Además, es increíblemente rápido: la sobrecarga promedio es solo el 25% del tiempo necesario para leer un registro. La única limitación es que admite un máximo de 16 permisos personalizados por tipo de objeto.
fuente
Puedo pensar en otra posibilidad para codificar esto, la relacional
Si no necesita la
permission_per_item
mesa, puede omitirla y conectarsePermissions
yItems
directamente a laitem_per_user_permissions
mesa.diagrama de leyenda
fuente