Razvoj teme i za opširniji odgovor na
Primijenjena strategija podrazumijeva koncept „Poslovna logika u bazi podataka“, što je malo više opisano ovdje —
Teorijski dio je iznimno dobro opisan u dokumentaciji — . U nastavku se razmatra praktična implementacija konkretne poslovne zadaće — model pristupa podacima.

U članku nema ništa novo, nema skrivenog smisla ili tajnih saznanja. Samo skica o praktičnoj implementaciji teorijske ideje. Ako vas zanima — čitajte. Ako ne — ne gubite vrijeme.
Postavljanje zadatka
Potrebno je ograničiti pristup za pregled/unos/izmjenu/brisanje dokumenta prema ulozi korisnika aplikacije. Pod ulom podrazumijeva se zapis u tablici roles kojoj je u vezi mnogi-na-mnoge s tablicom users. Detalji implementacije tablica su izostavljeni zbog trivijalnosti. Također su izostavljeni specifični detalji implementacije vezani za predmetno područje.
Implementacija
Kreiramo uloge, sheme, tablicu
Kreiranje objekata baze podataka
CREATE ROLE store;
CREATE SCHEMA store AUTHORIZATION store;
CREATE TABLE store.docs
(
id integer , --id dokumenta
man_id integer , --id menadžera dokumenta
stat_id integer , --id statusa dokumenta
...
is_del BOOLEAN DEFAULT FALSE
);
ALTER TABLE store.docs ADD CONSTRAINT doc_pk PRIMARY KEY (id);
ALTER TABLE store.docs OWNER TO store ;
Kreiramo funkcije za implementaciju RLS
Provjera mogućnosti izvršenja SELECT reda
check_select
CREATE OR REPLACE FUNCTION store.check_select ( current_id store.docs.id%TYPE ) RETURNS boolean AS $$
DECLARE
result boolean ;
curr_pid integer ;
curr_stat_id integer ;
doc_man_id integer ;
BEGIN
-- DBA ima pristup svim dokumentima
IF SESSION_USER = 'curr_dba'
THEN
RETURN TRUE ;
END IF ;
--------------------------------
--Ako dokument ima oznaku 'izbrisan' - ne prikazivati u odabiru
SELECT
is_del
INTO
result
FROM
store.docs
WHERE
id = current_id ;
IF result = TRUE
THEN
RETURN FALSE ;
END IF ;
--------------------------------
--Dobijanje id trenutnog korisnika
SELECT
service_function.get_curr_pid ()
INTO
curr_pid ;
--------------------------------
--Dobijanje id menadžera dokumenta
SELECT
man_id
INTO
doc_man_id
FROM
store.docs
WHERE
id = current_id ;
--------------------------------
--Ako menadžer dokumenta nije trenutni korisnik ili menadžer nije dodeljen
--dodati dokument u odabir
IF doc_man_id != curr_pid OR doc_man_id IS NULL
THEN
RETURN TRUE ;
ELSE
--Dobijanje trenutnog statusa dokumenta
SELECT
stat_id
INTO
curr_statid
FROM
store.docs
WHERE
id = current_id ;
--Ako status omogućava pregled dokumenta - dodati dokument u odabir
IF curr_statid = 4 OR curr_statid = 9
THEN
RETURN TRUE ;
ELSE
--Inače - isključiti dokument iz odabira
RETURN FALSE ;
END IF ;
END IF ;
--------------------------------
RETURN FALSE ;
END
$$ LANGUAGE plpgsql SECURITY DEFINER;
ALTER FUNCTION store.check_select( store.docs.id%TYPE ) OWNER TO store ;
REVOKE EXECUTE ON FUNCTION store.check_select( store.docs.id%TYPE ) FROM public;
GRANT EXECUTE ON FUNCTION store.check_select( store.docs.id%TYPE ) TO service_functions;
Provjera mogućnosti izvršavanja INSERT pristupa
check_insert
CREATE OR REPLACE FUNCTION store.check_insert ( current_id store.docs.id%TYPE ) RETURNS boolean AS $$
DECLARE
curr_role_id integer ;
BEGIN
--DBA može dodavati redak u svakom slučaju
IF SESSION_USER = 'curr_dba'
THEN
RETURN TRUE ;
END IF ;
--------------------------------
--Dobijanje id uloge trenutnog korisnika
SELECT
service_functions.current_rid()
INTO
curr_role_id ;
--------------------------------
--Ako uloga omogućava stvaranje novog dokumenta
--dopustiti
IF curr_role_id = 3 OR curr_role_id = 5
THEN
RETURN TRUE ;
END IF ;
--------------------------------
RETURN FALSE ;
END
$$ LANGUAGE plpgsql SECURITY DEFINER;
ALTER FUNCTION store.check_insert( store.docs.id%TYPE ) OWNER TO store ;
REVOKE EXECUTE ON FUNCTION store.check_insert( store.docs.id%TYPE ) FROM public;
GRANT EXECUTE ON FUNCTION store.check_insert( store.docs.id%TYPE ) TO service_functions;
Provjera mogućnosti izvršavanja DELETE pristupa
check_delete
CREATE OR REPLACE FUNCTION store.check_delete ( current_id store.docs.id%TYPE )
RETURNS boolean AS $$
BEGIN
--Samo DBA može brisati redak
IF SESSION_USER = 'curr_dba'
THEN
RETURN TRUE ;
END IF ;
--------------------------------
RETURN FALSE ;
END
$$ LANGUAGE plpgsql
SECURITY DEFINER;
ALTER FUNCTION store.check_delete( store.docs.id%TYPE ) OWNER TO store ;
REVOKE EXECUTE ON FUNCTION store.check_delete( store.docs.id%TYPE ) FROM public;Provjera mogućnosti izvršavanja UPDATE pristupa.
update_using
CREATE OR REPLACE FUNCTION store.update_using ( current_id store.docs.id%TYPE , is_del boolean )
RETURNS boolean AS $$
BEGIN
--Dokumenti sa statusom 'izbrisan' se ne uređuju
IF is_del
THEN
RETURN FALSE ;
ELSE
RETURN TRUE ;
END IF ;
END
$$ LANGUAGE plpgsql SECURITY DEFINER;
ALTER FUNCTION store.update_using( store.docs.id%TYPE , boolean ) OWNER TO store ;
REVOKE EXECUTE ON FUNCTION store.update_using( store.docs.id%TYPE , boolean ) FROM public;
GRANT EXECUTE ON FUNCTION store.update_using( store.docs.id%TYPE ) TO service_functions;update_check
CREATE OR REPLACE FUNCTION store.update_with_check ( current_id store.docs.id%TYPE , is_del boolean )
RETURNS boolean AS $$
DECLARE
current_rid integer ;
current_statid integer ;
BEGIN
--DBA može pregledati red
IF SESSION_USER = 'curr_dba'
THEN
RETURN TRUE ;
END IF ;
--------------------------------
--Dohvati id uloge trenutnog korisnika
SELECT
service_functions.current_rid()
INTO
curr_role_id ;
--------------------------------
--Brisanje dokumenta - promjena oznake
IF is_deleted
THEN
--Ako je uloga korisnika ***
IF current_role_id = 3
THEN
SELECT
stat_id
INTO
curr_statid
FROM
store.docs
WHERE
id = current_id ;
--Dokument u statusu *** ne može se izbrisati
IF current_status_id = 11
THEN
RETURN FALSE ;
ELSE
--Može se izbrisati dokument u drugim statusima
RETURN TRUE ;
END IF ;
--Inače, ako je uloga korisnika ***
ELSIF current_role_id = 5
THEN
--Svi statusi dokumenta
RETURN TRUE ;
ELSE
--Ostali korisnici ne mogu brisati dokumente
RETURN FALSE ;
END IF ;
ELSE
--Ažuriranje dokumenta je dozvoljeno
RETURN TRUE ;
END IF ;
RETURN FALSE ;
END
$$ LANGUAGE plpgsql SECURITY DEFINER;
ALTER FUNCTION store.update_with_check( storg.docs.id%TYPE , boolean ) OWNER TO store ;
REVOKE EXECUTE ON FUNCTION store.update_with_check( storg.docs.id%TYPE , boolean ) FROM public;
GRANT EXECUTE ON FUNCTION store.update_with_check( store.docs.id%TYPE ) TO service_functions;Aktivacija politike Row Level Security za tablicu.
ENABLE ROW LEVEL SECURITY
ALTER TABLE store.docs ENABLE ROW LEVEL SECURITY ;
CREATE POLICY doc_select ON store.docs FOR SELECT TO service_functions USING ( (SELECT store.check_select(id)) );
CREATE POLICY doc_insert ON store.docs FOR INSERT TO service_functions WITH CHECK ( (SELECT store.check_insert(id)) );
CREATE POLICY docs_delete ON store.docs FOR DELETE TO service_functions USING ( (SELECT store.check_delete(id)) );
CREATE POLICY doc_update_using ON store.docs FOR UPDATE TO service_functions USING ( (SELECT store.update_using(id , is_del )) );
CREATE POLICY doc_update_check ON store.docs FOR UPDATE TO service_functions WITH CHECK ( (SELECT store.update_with_check(id , is_del )) );Zaključak
To radi.
Predložena strategija omogućila je premještanje implementacije modela uloga s razine poslovnih funkcija na razinu pohrane podataka.
Funkcije se mogu koristiti kao predložak za implementaciju složenijih modela skrivenih podataka, ako to zahtijevaju poslovni zahtjevi.
Izvor: habr.com
