2010-02-22 19 views
5

mam schematu tabel, których zawartość w zasadzie sprowadzają się do:Korzystanie z widoków do kontroli dostępu w PostgreSQL

  • Zestaw użytkowników
  • Zestaw grup obiektów
  • listę kontroli dostępu (ACL) wskazujące, co użytkownicy mają dostęp do grup, z których każdy należy do dokładnie jednej grupy.

Chcę utworzyć prostą aplikację, która obsługuje kontrolę dostępu. Myślę, że widoki byłyby tutaj dobrym podejściem.

Załóżmy, że mam następujący inicjalizacji bazy danych:

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

Mój pomysł jest użycie widoki, które odzwierciedlają bazy danych, ale ograniczają treści tylko to, co bieżący użytkownik (ustalona przez mojego skryptu PHP) mogą uzyskać dostęp (tutaj po prostu użyję użytkownika 'bob'). Załóżmy, że uruchomić to na początku każdej sesji PostgreSQL (czyli za każdym razem ktoś uzyskuje dostęp do strony na mojej stronie):

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; 

Moje pytanie brzmi, czy jest to dobre podejście? Czy instrukcje CREATE TEMPORATION VIEW generują znaczny narzut, szczególnie w porównaniu z kilkoma prostymi zapytaniami?

Czy istnieje sposób na trwałe utrwalenie tych widoków w mojej definicji bazy danych, a następnie powiązanie wartości z nazwą użytkownika na sesję? W ten sposób nie trzeba tworzyć wszystkich tych widoków za każdym razem, gdy użytkownik ładuje stronę.

Odpowiedz

6

kilka problemów związanych z takim podejściem:

  1. jeden użytkownik internetowej sesja nie jest to samo, co jeden bazy sesji. Wielu użytkowników z ustawieniami może natychmiast zawieść.

  2. Zarządzanie obciążeniem Tworzenie/niszczenie widoków.

Zamiast tego, polecam coś następującym zdaniem:

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_ 

Następnie wszędzie wybrane obiekty:

SELECT * FROM AllowedObjects 
WHERE alloweduser='Bob' 

Zakłada Bob może mieć tylko łączącego ACL go konkretna grupa, w przeciwnym razie konieczne byłoby WYRÓŻNIENIE.

To może być wydobywane w nieco mniej skomplikowanym widzenia, które mogą być wykorzystane w celu ułatwienia sprawdzić uprawnienia dla UPDATE i 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_ 

Zapewnia to spłaszczony widok której użytkownicy są w których grupy, który można sprawdzić na stole przedmiotów podczas UPDATE/dELETE:

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

W odprawy dla update/delete, jeśli tylko odnoszą się do użytkownika o ID zamiast nazwy, nie mogę zrezygnować Utwórz widok AllowedUserGroup i odnośnik acl bezpośrednio dla tej części? –

+0

Jasne, to zadziała dobrze. Możesz użyć AllowedUserGroup również dla SELECT, ale pomyślałem, że AllowedObjects będzie nieco bardziej użyteczne. Możesz również mieć AllowedObjects po prostu dołączyć 'objects' do AllowedUserGroup, więc cała logika ACL/Group/User jest zawarta w widoku AllowedUserGroup ... ścieżka wykonania powinna być taka sama w każdym kierunku. – richardtallent