在PostgreSQL中使用视图进行访问控制

Joe*_*ams 5 postgresql access-control sql-view

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

  • 一组用户
  • 一组对象组
  • 访问控制列表(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;
Run Code Online (Sandbox Code Playgroud)

我的想法是使用镜像数据库的视图,但将内容限制为当前用户(由我的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;
Run Code Online (Sandbox Code Playgroud)

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

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

ric*_*ent 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_
Run Code Online (Sandbox Code Playgroud)

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

SELECT * FROM AllowedObjects
WHERE alloweduser='Bob'
Run Code Online (Sandbox Code Playgroud)

这假设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_
Run Code Online (Sandbox Code Playgroud)

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

UPDATE objects SET foo='bar' WHERE id=42 AND EXISTS
(SELECT NULL FROM AllowedUserGroup 
 WHERE alloweduser='Bob' AND allowedgroup = objects.group_)
Run Code Online (Sandbox Code Playgroud)