【问题标题】:Using views for access control in PostgreSQL在 PostgreSQL 中使用视图进行访问控制
【发布时间】:2011-01-19 12:44:18
【问题描述】:

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

  • 一组用户
  • 一组对象组
  • 访问控制列表 (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 语句是否会产生很大的开销,尤其是与几个简单的查询相比?

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

【问题讨论】:

    标签: postgresql access-control sql-view


    【解决方案1】:

    这种方法存在几个问题:

    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_)
    

    【讨论】:

    • 在检查更新/删除时,如果我只是通过 id 而不是名称来引用用户,我不能放弃 CREATE VIEW AllowedUserGroup 并直接为该部分引用 acl 吗?
    • 当然,这样就可以了。您也可以将 AllowedUserGroup 用于 SELECT,但我认为 AllowedObjects 会稍微有用一些。您也可以让 AllowedObjects 将 objects 加入 AllowedUserGroup,这样整个 ACL/Group/User 逻辑都包含在 AllowedUserGroup 视图中......无论哪种方式,执行路径都应该相同。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-07-14
    • 1970-01-01
    • 2014-12-25
    • 2014-03-17
    相关资源
    最近更新 更多