2011-10-11 32 views
4

假設我有一個只包含名稱(varchar)和用戶客戶端的表人員。postgresql特權確保插入僅通過函數完成

我想,對於客戶端的唯一方式插入到人是通過函數:

CREATE OR REPLACE FUNCTION add_a_person(a_name varying character) 
    RETURNS void AS 
$BODY$ 
BEGIN 
    INSERT INTO persons VALUES(a_name); 
END; 
$BODY$ 
    LANGUAGE plpgsql VOLATILE COST 100; 

所以,我不想授予者客戶端插入權限,只給了執行權限add_a_person。 但是沒有這樣做,我會得到一個權限被拒絕,因爲在函數內部使用插入。

我還沒有在postgres文檔中找到關於授予權限的方法。 有沒有辦法做到這一點?

謝謝先進。

回答

4

您可以使用SECURITY DEFINER定義該功能。這將允許函數爲受限用戶運行,就好像它們具有函數創建者的更高特權(需要能夠插入表中)。

定義的最後一行是這樣的:

LANGUAGE plpgsql VOLATILE COST 100 SECURITY DEFINER; 
0

馬修的答案是在正確的安全DEFINER將使函數與不同的用戶的權限運行。此文件是在http://www.postgresql.org/docs/9.1/static/sql-createfunction.html

你爲什麼想實現安全這種方式?如果你想強制插入一些邏輯,那麼我強烈建議在約束條件下進行。 http://www.postgresql.org/docs/9.1/static/ddl-constraints.html

如果你想要的邏輯要比在約束條件下合理實現的邏輯高得多,我建議你考慮在你的表示層和數據存儲層之間建立一個業務邏輯層。你會發現可擴展性要求這非常瞬間。

如果你的目標是抵禦SQL注入,那麼你已經找到可能的工作方式,但會創造很多關於你的工作的赫克。更糟的是,它會導致大量真正無意識的代碼,所有代碼都必須在架構更改中保持同步。如果你想做任何敏捷的事情,這是相當粗糙的。請考慮使用一個編程框架,它利用PREPARE/EXECUTE,這一點幾乎全部都是。 http://www.postgresql.org/docs/9.0/static/sql-prepare.html

+1

以下是您的選擇最少的常見(會計)情況:「允許插入到訂單項文件夾中,以便給定的一組插入的總金額列爲0」。你可以用約束觸發器來做到這一點,或者通過破壞1NF來實現這一點,但是第一種快速和骯髒的方法是強制所有插入函數通過函數。這並不意味着也不會做約束,但這意味着單行約束本身是不夠的。 –

0

這是一個有點簡單化,但假設正在運行9.2或更高版本,這是一個如何檢查單功能許可執行INSERT的例子:

CREATE TABLE my_table (col1 text, col2 integer, col3 timestamp); 

CREATE FUNCTION my_table_insert_function(col1 text, col2 integer) RETURNS integer AS $$ 
BEGIN 
    INSERT INTO my_table VALUES (col1, col2, current_timestamp); 
    RETURN 1; 
END $$ LANGUAGE plpgsql; 

CREATE FUNCTION my_table_insert_trigger_function() RETURNS trigger AS $$ 
DECLARE 
    stack text; 
    fn integer; 
BEGIN 
    RAISE EXCEPTION 'secured'; 
EXCEPTION WHEN OTHERS THEN 
    BEGIN 
    GET STACKED DIAGNOSTICS stack = PG_EXCEPTION_CONTEXT; 
    fn := position('my_table_insert_function' in stack); 
    IF (fn <= 0) THEN 
     RAISE EXCEPTION 'Expecting insert from my_table_insert_function' 
     USING HINT = 'Use function to insert data'; 
    END IF; 
    RETURN new; 
    END; 
END $$ LANGUAGE plpgsql; 

CREATE TRIGGER my_table_insert_trigger BEFORE INSERT ON my_table 
FOR EACH ROW EXECUTE PROCEDURE my_table_insert_trigger_function(); 

和使用的一個簡單的例子:

INSERT INTO my_table VALUES ('test one', 1, current_timestamp); -- FAILS 
SELECT my_table_insert_function('test one', 1); -- SUCCEEDS 

你要偷看進棧更詳細,如果你想你的代碼更加可靠,安全等多項功能的檢查是可能的,當然,但涉及更多的工作。將堆棧分成多行並解析它可能會相當複雜,所以如果事情變得更復雜,您可能需要一些幫助函數。

這只是一個概念的證明,但它做它聲稱的東西。如果使用異常處理和堆棧檢查,我希望這段代碼相當慢,所以不要在應用程序的性能關鍵部分使用它。對於DML陳述頻繁的情況,它不太可能適用,但如果安全性比性能更重要,那麼就去做吧。