2010-02-22 23 views
5

訪問控制使用視圖我有表,其內容的模式基本上可歸結爲:在PostgreSQL中

  • 一組用戶的
  • 一組對象組
  • 的訪問控制列表(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; 

我的問題是,這是一個好的方法嗎?做這些創建臨時VIEW語句產生顯著的開銷,特別是相對於一對夫婦簡單的查詢?

此外,有沒有一種方法,使這些觀點在我的數據庫定義永久性的,然後綁定一個值到每個會話的用戶名?這樣,就不必創建所有這些觀點在用戶每次加載頁面的時間。

回答

6

幾個問題這種方法:

  1. 一個用戶網絡會議是不一樣的東西作爲一個數據庫會議。具有各種設置的多個用戶會立即失敗。

  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' 

這是假設鮑勃只能有一個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_ 

這提供了一個平面視圖,其中用戶是其中基團中,您可以在更新過程中核對對象表/ DELETE:

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

在檢查更新/刪除,如果我只是指用戶通過ID而不是名字,我不能放棄CREATE VIEW AllowedUserGroup並直接引用該部分的acl? – 2010-02-22 06:59:19

+0

當然,這將工作得很好。你也可以爲SELECT使用AllowedUserGroup,但我認爲AllowedObjects稍微有用。您也可以讓AllowedObjects將'objects'連接到AllowedUserGroup,這樣整個ACL /組/用戶邏輯就包含在AllowedUserGroup視圖中了......執行路徑應該是相同的。 – richardtallent 2010-02-22 20:34:50