Pergunta

Eu tenho um esquema de mesas cujo conteúdo basicamente se resume a:

  • Um conjunto de usuários
  • Um conjunto de grupos de objetos
  • Uma lista de controle de acesso (ACL) indicando o que os usuários têm acesso a quais grupos
  • Um conjunto de objetos, cada um dos quais pertence a exatamente um grupo.

Quero criar um aplicativo simples que suporta controle de acesso. Estou pensando que as opiniões seriam uma boa abordagem aqui.

Suponha que eu tenha a seguinte inicialização do banco de dados:

/* 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;

Minha idéia é usar as visualizações que refletem o banco de dados, mas restringem o conteúdo apenas daquilo que o usuário atual (verificado pelo meu script PHP) pode acessar (aqui vou usar o usuário 'bob'). Suponha que eu execute isso no início de todas as sessões do PostgreSQL (o que significa toda vez que alguém acessa uma página no meu site):

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;

Minha pergunta é: essa é uma boa abordagem? Eles criam declarações de visão temporária produzem sobrecarga significativa, especialmente em comparação com algumas consultas simples?

Além disso, existe uma maneira de tornar essas visualizações permanentes na minha definição de banco de dados e vincular um valor ao nome do usuário por sessão? Dessa forma, ele não precisa criar todas essas visualizações toda vez que um usuário carrega uma página.

Foi útil?

Solução

Vários problemas com esta abordagem:

  1. Um usuário rede sessão não é a mesma coisa que uma base de dados sessão. Vários usuários com tipo de configuração falhariam instantaneamente.

  2. Gerenciamento de sobrecarga de gerenciamento criando/destruindo as vistas.

Em vez disso, eu recomendaria algo como a seguinte visão:

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_

Então, em todos os lugares em que você seleciona objetos:

SELECT * FROM AllowedObjects
WHERE alloweduser='Bob'

Isso pressupõe que Bob só pode ter um LCA se juntando a ele em um grupo específico, caso contrário, seria necessário um distinto.

Isso pode ser abstraído para uma visão um pouco menos complexa que poderia ser usada para facilitar a verificação das permissões para atualizar e excluir:

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_

Isso fornece uma visão achatada de quais usuários estão nos quais grupos, que você pode verificar na tabela de objetos durante uma atualização/exclusão:

UPDATE objects SET foo='bar' WHERE id=42 AND EXISTS
(SELECT NULL FROM AllowedUserGroup 
 WHERE alloweduser='Bob' AND allowedgroup = objects.group_)
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top