30日以上経過したITスタッフに付与した特権をOracleのジョブで取り消すという新しいプロジェクトに取り組んでいます。 ITスタッフは、問題のトラブルシューティングを行うために、いくつかの本番テーブルに時々アクセスする必要があります。私たちは、その人が必要とするテーブルにSELECT特権を付与しますが、それらがタスクを完了したときに誰も私に教えてくれず、それらの特権は永遠にそこにあります。 30日以上経過した特権をシステムが自動的に取り消すようにしたかったので、忘れずに取り消すことができます。特権を取り消す前に、それらの特権を追跡する方法が必要でした。そこで、GRANTが発行されるたびに起動され、詳細をテーブルに記録するトリガーを作成しました。後で、Oracleジョブはそのテーブルをスキャンし、古すぎると判断した特権を取り消します。私のトリガーコードは次のとおりです:
create or replace trigger sys.grant_logging_trig after grant on database
declare
priv dbms_standard.ora_name_list_t;
who dbms_standard.ora_name_list_t;
npriv pls_integer;
nwho pls_integer;
begin
npriv := ora_privilege_list(priv);
if (ora_sysevent = 'GRANT') then
nwho := ora_grantee(who);
else
nwho := ora_revokee(who);
end if;
for i in 1..npriv
loop
for j in 1..nwho
loop
insert into system.grant_logging values
( systimestamp,
ora_login_user,
ora_sysevent,
who(j),
priv(i),
ora_dict_obj_owner,
ora_dict_obj_name
);
end loop;
end loop;
end;
/ 上記のコードはオリジナルではありません。私はインターネットで良い例を見つけ、いくつかのことを修正しました。コードを3週間テストした後、トリガーを本番環境に移行しました。エラーが表示されるまでに数日しかかかりませんでした。
SQL> CREATE USER bob IDENTIFIED BY password; ERROR at line 1: ORA-00604: error occurred at recursive SQL level 1 ORA-04088: error during execution of trigger 'SYS.GRANT_LOGGING_TRIG' ORA-00604: error occurred at recursive SQL level 2 ORA-06502: PL/SQL: numeric or value error ORA-06512: at line 28
うーん…何も許可しないユーザーを作成しています。しかし、トリガーの実行に問題があることは確かです。では、ユーザーを作成するだけの場合、なぜこのトリガーが起動するのでしょうか。単純なSQLトレースは、その再帰SQLで何が起こっているかを示しました。舞台裏では、オラクルは私に代わって以下を発行しています:
ユーザー「BOB」の特権を公開に付与します。
わかりました…この時点で、ユーザーを作成するときにGRANTが発行されていることはわかっていますが、なぜこれが失敗するのですか?このトリガーをシステム権限でテストしたところ、問題なく機能しました。確かに、私はINHERIT PRIVILEGESをテストしなかったので、これは一種のエッジケースです。
かなりのデバッグ作業を行った後、ora_privilege_list関数呼び出しが「priv」という名前のコレクションに空のセットを返していることを確認しました。そのため、nprivはNULL値に設定されています。 NPRIVがNULLであるため、「for i in 1..npriv」という行はあまり意味がなく、エラーになります。
私の意見では、ora_privilege_listは「INHERITPRIVILEGES」という1つの項目を返す必要があり、そのリストをバグとして返さないと思います。ただし、ora_privilege_listが空のコレクションを返す場合、関数からの出力はゼロである必要があり、nprivはより適切な値を取得します。教育目的では、ora_privilege_listはDBMS_STANDARD.PRIVILEGE_LISTの同義語です。
そうは言っても、私はOracleの機能を制御することはできません。そして、OracleがDBMS_STANDARDのコードを私が思うはずのコードに変更するのを待ちたくありません。そのため、問題を処理するためにトリガーをコーディングします。 2つの単純な行を追加すると、私の問題が解決しました(以下の太字で表示)。
create or replace trigger sys.grant_logging_trig after grant on database
declare
priv dbms_standard.ora_name_list_t;
who dbms_standard.ora_name_list_t;
npriv pls_integer;
nwho pls_integer;
begin
npriv := ora_privilege_list(priv);
if (ora_sysevent = 'GRANT') then
nwho := ora_grantee(who);
else
nwho := ora_revokee(who);
end if;
if to_char(npriv) is not null then
for i in 1..npriv
loop
for j in 1..nwho
loop
insert into system.grant_logging values
( systimestamp,
ora_login_user,
ora_sysevent,
who(j),
priv(i),
ora_dict_obj_owner,
ora_dict_obj_name
);
end loop;
end loop;
end if;
end;
/ したがって、修正は非常に簡単です。 NPRIVがnullでない場合にのみ、2つのFORループを実行します。