2010-02-22 3 views
5

que tienen un esquema de tablas cuyos contenidos básicamente se reducen a:el uso de vistas para el control de acceso en PostgreSQL

  • Un conjunto de usuarios
  • Un conjunto de grupos de objetos
  • una lista de control de acceso (ACL) indicando qué usuarios tienen acceso a qué grupos
  • Un conjunto de objetos, cada uno de los cuales pertenece exactamente a un grupo.

Quiero crear una aplicación simple que admita el control de acceso. Estoy pensando que las vistas serían un buen enfoque aquí.

Supongamos que tengo la siguiente inicialización de la base de datos:

/* Database definition */ 

BEGIN; 

CREATE SCHEMA foo; 

CREATE TABLE foo.users (
    id SERIAL PRIMARY KEY, 
    name TEXT 
); 

CREATE TABLE foo.groups (
    id SERIAL PRIMARY KEY, 
    name TEXT 
); 

CREATE TABLE foo.acl (
    user_ INT REFERENCES foo.users, 
    group_ INT REFERENCES foo.groups 
); 

CREATE TABLE foo.objects (
    id SERIAL PRIMARY KEY, 
    group_ INT REFERENCES foo.groups, 
    name TEXT, 
    data TEXT 
); 

/* Sample data */ 

-- Create groups A and B 
INSERT INTO foo.groups VALUES (1, 'A'); 
INSERT INTO foo.groups VALUES (2, 'B'); 

-- Create objects belonging to group A 
INSERT INTO foo.objects VALUES (1, 1, 'object in A', 'apples'); 
INSERT INTO foo.objects VALUES (2, 1, 'another object in A', 'asparagus'); 

-- Create objects belonging to group B 
INSERT INTO foo.objects VALUES (3, 2, 'object in B', 'bananas'); 
INSERT INTO foo.objects VALUES (4, 2, 'object in B', 'blueberries'); 

-- Create users 
INSERT INTO foo.users VALUES (1, 'alice'); 
INSERT INTO foo.users VALUES (2, 'amy'); 
INSERT INTO foo.users VALUES (3, 'billy'); 
INSERT INTO foo.users VALUES (4, 'bob'); 
INSERT INTO foo.users VALUES (5, 'caitlin'); 
INSERT INTO foo.users VALUES (6, 'charlie'); 

-- alice and amy can access group A 
INSERT INTO foo.acl VALUES (1, 1); 
INSERT INTO foo.acl VALUES (2, 1); 

-- billy and bob can access group B 
INSERT INTO foo.acl VALUES (3, 2); 
INSERT INTO foo.acl VALUES (4, 2); 

-- caitlin and charlie can access groups A and B 
INSERT INTO foo.acl VALUES (5, 1); 
INSERT INTO foo.acl VALUES (5, 2); 
INSERT INTO foo.acl VALUES (6, 1); 
INSERT INTO foo.acl VALUES (6, 2); 

COMMIT; 

Mi idea es utilizar puntos de vista que reflejan la base de datos, pero restringir el contenido a sólo lo que el usuario actual (comprobado por mi script PHP) pueden acceder (aquí solo usaré el usuario 'bob'). Supongamos que yo corro esto al comienzo de cada sesión de PostgreSQL (es decir, cada vez que alguien accede a una página en mi sitio):

BEGIN; 

CREATE TEMPORARY VIEW users AS 
SELECT * FROM foo.users 
WHERE name='bob'; 

CREATE TEMPORARY VIEW acl AS 
SELECT acl.* FROM foo.acl, users 
WHERE acl.user_=users.id; 

CREATE TEMPORARY VIEW groups AS 
SELECT groups.* FROM foo.groups, acl 
WHERE groups.id=acl.group_; 

CREATE TEMPORARY VIEW objects AS 
SELECT objects.* FROM foo.objects, groups 
WHERE objects.group_=groups.id; 

COMMIT; 

Mi pregunta es, ¿es un buen enfoque? ¿Estas declaraciones CREATE TEMPORARY VIEW producen una sobrecarga significativa, especialmente en comparación con un par de consultas simples?

Además, ¿hay alguna manera de hacer que estas vistas sean permanentes en la definición de mi base de datos, y luego vincular un valor al nombre de usuario por sesión? De esta forma, no tiene que crear todas estas vistas cada vez que un usuario carga una página.

Respuesta

6

varios problemas con este enfoque:

  1. un usuario web sesión no es lo mismo que una sesión de base de datos . Múltiples usuarios con un tipo de configuración fallarían instantáneamente.

  2. Sobrecarga de gestión creando/destruyendo las vistas.

En su lugar, yo recomendaría algo así como los próximos:

CREATE VIEW AllowedObjects 
SELECT objects.*, users.name AS alloweduser 
FROM objects 
    INNER JOIN groups ON groups.id = objects.group_ 
    INNER JOIN acl ON acl.group_ = groups.id 
    INNER JOIN users ON users.id = acl.user_ 

Entonces, en todas partes se seleccionan objetos:

SELECT * FROM AllowedObjects 
WHERE alloweduser='Bob' 

Esto supone Bob sólo puede tener un ACL unirse a él a un grupo particular; de lo contrario, sería necesario DISTINCT.

Esto podría ser abstraído a una vista ligeramente menos compleja que podría ser utilizado para hacer más fácil para comprobar permisos para actualizar y borrar:

CREATE VIEW AllowedUserGroup 
SELECT groups.id AS allowedgroup, users.name AS alloweduser 
FROM groups 
    INNER JOIN acl ON acl.group_ = groups.id 
    INNER JOIN users ON users.id = acl.user_ 

Esto proporciona una vista aplanada de las cuales los usuarios están en la que los grupos, que se puede comprobar en la mesa objetos durante un uPDATE/DELETE:

UPDATE objects SET foo='bar' WHERE id=42 AND EXISTS 
(SELECT NULL FROM AllowedUserGroup 
WHERE alloweduser='Bob' AND allowedgroup = objects.group_) 
+0

En la comprobación de una actualización/eliminar, si me refiero al usuario por ID en lugar del nombre, no puedo renunciar CREATE VIEW AllowedUserGroup y acl de referencia directamente para esa parte? –

+0

Claro, eso funcionará bien. También puedes usar AllowedUserGroup para SELECT, pero pensé que AllowedObjects sería un poco más útil. También podría hacer que AllowedObjects simplemente uniera 'objects' al AllowedUserGroup para que toda la lógica de ACL/Grupo/Usuario esté contenida en la vista AllowedUserGroup ... la ruta de ejecución debería ser la misma en ambos sentidos. – richardtallent

Cuestiones relacionadas