5

我有一个表的模式,其内容基本上可以归结为:

  • 一组用户
  • 一组对象组
  • 访问控制列表 (acl),指示哪些用户可以访问哪些组
  • 一组对象,每个对象恰好属于一个组。

我想创建一个支持访问控制的简单应用程序。我认为意见将是一个很好的方法。

假设我有以下数据库初始化:

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

我的想法是使用镜像数据库的视图,但将内容限制为仅当前用户(由我的 PHP 脚本确定)可以访问的内容(这里我将只使用用户'bob')。假设我在每个 PostgreSQL 会话开始时运行它(意味着每次有人访问我网站上的页面时):

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;

我的问题是,这是一个好方法吗?这些 CREATE TEMPORARY VIEW 语句是否会产生很大的开销,尤其是与几个简单的查询相比?

另外,有没有办法让这些视图在我的数据库定义中永久存在,然后将一个值绑定到每个会话的用户名?这样,它就不必在每次用户加载页面时创建所有这些视图。

4

1 回答 1

7

这种方法的几个问题:

  1. 一个用户Web会话与一个数据库会话不同。具有某种设置的多个用户将立即失败。

  2. 创建/销毁视图的管理开销。

相反,我会推荐如下视图:

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_

然后,在您选择对象的任何地方:

SELECT * FROM AllowedObjects
WHERE alloweduser='Bob'

这假设 Bob 只能有一个 ACL 将他加入特定组,否则将需要 DISTINCT。

这可以被抽象为一个稍微不那么复杂的视图,可以用来更容易检查 UPDATE 和 DELETE 的权限:

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_

这提供了哪些用户在哪些组中的平面视图,您可以在 UPDATE/DELETE 期间检查对象表:

UPDATE objects SET foo='bar' WHERE id=42 AND EXISTS
(SELECT NULL FROM AllowedUserGroup 
 WHERE alloweduser='Bob' AND allowedgroup = objects.group_)
于 2010-02-22T06:39:19.383 回答