簡體   English   中英

Oracle SQL:在創建視圖后觸發添加授權

[英]Oracle SQL: Trigger to add grants after creating view

我正在使用使用 Oracle 數據庫的供應商應用程序。 為了保存內容,它使用數據庫表,這些表通過使用視圖進行查詢。 我對該代碼沒有任何控制權。 出於安全考慮,我將這些視圖的訪問權限授予了只能從中選擇條目的特殊報告用戶。

每當在應用程序中進行一些重大更改時,它都會刪除相應的視圖並重新創建它。 當然,所有授權都會丟失,並且由於很少進行更改,因此很容易忘記備份並在之后恢復它們。

我咨詢了 DBA,他建議編寫一個觸發器將授權保存在臨時表中,之后可以使用這些條目來恢復授權。 保存部分按預期工作正常:

create or replace TRIGGER RECORD_GRANTS_ONDROP 
BEFORE DROP ON MYUSER.SCHEMA 
BEGIN
    IF ora_dict_obj_owner = 'MYUSER' and ora_dict_obj_name not like 'TEMP_PRIV%' and ora_dict_obj_type='VIEW' then
        EXECUTE IMMEDIATE 'CREATE TABLE TEMP_PRIV AS SELECT ''GRANT '' || PRIVILEGE || '' ON MYUSER.'' || TABLE_NAME || '' TO '' || GRANTEE PRIVILEGE_x FROM USER_TAB_PRIVS WHERE GRANTEE not in (''MYUSER'',''PUBLIC'') AND TABLE_NAME=''' || ora_dict_obj_name || '''';
    ELSE null;
    END IF;
END;

結果,我得到了一個表,其中包含分配給所述視圖的所有授權。 為了恢復我想運行類似的觸發器:

create or replace TRIGGER RESTORE_GRANTS_AFTERCREATE
AFTER CREATE ON MYUSER.SCHEMA 
BEGIN
    IF ora_dict_obj_owner = 'MYUSER' and ora_dict_obj_type='VIEW' then
        FOR loop_counter IN (select '''' || privilege_x || '''' AS privilege_x from temp_priv)
        LOOP
            EXECUTE IMMEDIATE loop_counter.privilege_x;
            DBMS_OUTPUT.PUT_LINE(loop_counter.privilege_x);
        END LOOP;
    ELSE null;
    END IF;
    NULL;
END;

我會在這里注意到這只是概念的基本測試,沒有任何適當的檢查,所以只關注這里的大問題。

當我現在嘗試創建視圖時,出現錯誤:

Error report -
ORA-00604: error occurred at recursive SQL level 1
ORA-00900: invalid SQL statement
ORA-06512: at line 5
00604. 00000 -  "error occurred at recursive SQL level %s"
*Cause:    An error occurred while processing a recursive SQL statement
           (a statement applying to internal dictionary tables).
*Action:   If the situation described in the next error on the stack
           can be corrected, do so; otherwise contact Oracle Support.

這只能意味着在觸發器嘗試添加授權時未創建視圖。 語法明智我成功運行了命令:

BEGIN
    EXECUTE IMMEDIATE 'GRANT SELECT ON MYUSER.CR_STATUS_TABLE TO SOMEUSER';
END

但是,當我嘗試在“創建后”觸發器中使用 for 或單獨運行它時,我得到了同樣的錯誤。 有誰知道如何解決這個問題,如果可能的話,我想不惜一切代價避免工作。

忽略這是否是一個好主意……您收到該錯誤是因為您對字符串操作的考慮過多。 你放在桌子上的東西看起來不錯。 問題是當你把它拿出來的時候。 表中的值已經是一個字符串,因此您無需再將其括在另一組引號中。

您實際運行的內容相當於:

EXECUTE IMMEDIATE '''GRANT SELECT ON MYUSER.CR_STATUS_TABLE TO SOMEUSER''';

這也將拋出“ORA-00900:無效的 SQL 語句”,而不是您的獨立、工作版本:

EXECUTE IMMEDIATE 'GRANT SELECT ON MYUSER.CR_STATUS_TABLE TO SOMEUSER';

如果您交換EXECUTE IMMEDIATEDBMS_OUTPUT調用的順序,您會運行之前看到問題陳述,這會更有幫助 - 您會看到這些引號作為字符串的一部分。

所以在你的第二個觸發器中,而不是做:

FOR loop_counter IN (select '''' || privilege_x || '''' AS privilege_x from temp_priv)
LOOP
    EXECUTE IMMEDIATE loop_counter.privilege_x;
    DBMS_OUTPUT.PUT_LINE(loop_counter.privilege_x);
END LOOP;

做就是了:

FOR loop_counter IN (select privilege_x from temp_priv)
LOOP
    DBMS_OUTPUT.PUT_LINE(loop_counter.privilege_x);
    EXECUTE IMMEDIATE loop_counter.privilege_x;
END LOOP;

但是,這仍然行不通; 現在會得到 ORA-30511: invalid DDL operation in system triggers。 這大概是因為文檔中顯示的限制:

觸發器不能對導致事件生成的對象進行 DDL 操作。

其他對象上的 DDL 僅限於編譯對象、創建觸發器以及創建、更改和刪除表。

您說過“很容易忘記備份並在之后恢復它們”,但是您將不得不圍繞升級制定一個強大的流程以確保這確實發生。

您可以將您的流程更改為在每次運行的升級結束時有一個單獨的步驟,該步驟為所有對象執行所有這些存儲的語句 - 可能跳過或忽略任何未重新創建的錯誤 - 然后刪除temp_priv桌子。

但是無論如何您都不想(嘗試)在觸發器中創建該臨時表 - 如果刪除了兩個視圖,第一個創建它,第二個失敗,因為它已經存在。 一個或許更現實的方法可能是曾經,現在創建表:

create table TEMP_PRIV (PRIVILEGE_X VARCHAR2(4000));

然后將它用於所有后續升級,或者在升級開始之前將所有視圖的所有授權作為一個步驟填充它:

INSERT INTO TEMP_PRIVS (PRIVILEGE_X)
SELECT 'GRANT ' || PRIVILEGE || ' ON MYUSER.' || TABLE_NAME || ' TO ' || GRANTEE
FROM USER_VIEWS UV
JOIN USER_TAB_PRIVS UTP ON UTP.TABLE_NAME = UV.VIEW_NAME
WHERE UTP.GRANTEE not in ('MYUSER','PUBLIC');

或者,如果您仍然擔心可能會忘記該步驟,那么在它們被丟棄時使用觸發器一次執行一個視圖:

create or replace TRIGGER RECORD_GRANTS_ONDROP 
BEFORE DROP ON MYUSER.SCHEMA 
BEGIN
    IF ora_dict_obj_owner = 'MYUSER' and ora_dict_obj_name not like 'TEMP_PRIV%' and ora_dict_obj_type='VIEW' then
      INSERT INTO TEMP_PRIV
      SELECT 'GRANT ' || PRIVILEGE || ' ON MYUSER.' || TABLE_NAME || ' TO ' || GRANTEE
      FROM USER_TAB_PRIVS
      WHERE GRANTEE not in ('MYUSER','PUBLIC')
      AND TABLE_NAME = ora_dict_obj_name;
    END IF;
END;
/

然后在升級過程結束時重新發出表中的所有語句並清除它以備下次使用:

DECLARE
    missing_view EXCEPTION;
    PRAGMA EXCEPTION_INIT(missing_view, -942);
BEGIN
    FOR loop_counter IN (select privilege_x from temp_priv)
    LOOP
        BEGIN
            DBMS_OUTPUT.PUT_LINE(loop_counter.privilege_x);
            EXECUTE IMMEDIATE loop_counter.privilege_x;
        EXCEPTION
          WHEN missing_view THEN
              -- report but otherwise ignore
              DBMS_OUTPUT.PUT_LINE(SQLERRM);
        END;
    END LOOP;
END;
/

TRUNCATE TABLE temp_priv;

如果您采用更簡單的非觸發方法,那么它將重新授予現有權限,但沒關系。 並且異常處理程序意味着它會報告但跳過任何已刪除且未重新創建的視圖,如果發生這種情況。 (當然,您仍然必須處理任何視圖;無論如何,您的創建后觸發器都無濟於事。)請注意,我已經截斷了表格,而不是刪除它 - 所以它仍然存在,空,當下一次升級到來並想要填充它時。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM