Oracle
 sql >> Datenbank >  >> RDS >> Oracle

ORA-6502 mit Grant Logging Trigger

Ich arbeite an einem neuen Projekt, bei dem ich möchte, dass ein Oracle-Job Berechtigungen entzieht, die ich IT-Mitarbeitern gewährt habe, die älter als 30 Tage sind. Unsere IT-Mitarbeiter benötigen gelegentlich Zugriff auf einige Produktionstabellen, um Probleme zu beheben. Wir gewähren SELECT-Privilegien für die Tabellen, die diese Person benötigt, aber niemand sagt mir, wann sie mit ihrer Aufgabe fertig sind, und diese Privilegien bleiben für immer da draußen. Ich wollte, dass ein System Berechtigungen, die älter als 30 Tage sind, automatisch entzieht, damit ich nicht daran denken muss. Bevor ich Privilegien widerrufen konnte, brauchte ich eine Möglichkeit, diese Privilegien nachzuverfolgen. Also habe ich einen Trigger erstellt, der ausgelöst wird, wenn ein GRANT ausgegeben wird, und die Details in einer Tabelle protokolliert. Später scannt ein Oracle-Job diese Tabelle und widerruft Berechtigungen, die zu alt sind. Mein Auslösecode lautet wie folgt:

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

Der obige Code ist nicht original. Ich habe ein gutes Beispiel im Internet gefunden und ein paar Dinge modifiziert. Nachdem ich den Code 3 Wochen lang getestet hatte, rollte ich den Trigger in die Produktion. Es dauerte nur ein paar Tage, bis ich eine Fehlermeldung erhielt.

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

Hmmm … Ich erstelle einen Benutzer, der nichts gewährt. Aber wir können sicher sehen, dass mein Trigger ein Problem bei der Ausführung hat. Warum wird dieser Trigger ausgelöst, wenn ich nur einen Benutzer erstelle? Ein einfacher SQL-Trace zeigte mir, was mit diesem rekursiven SQL los war. Hinter den Kulissen gibt Oracle in meinem Namen Folgendes heraus:

VERERBE-PRIVILEGIEN FÜR BENUTZER „BOB“ GEWÄHREN an PUBLIC;

Ok ... an diesem Punkt weiß ich, dass beim Erstellen eines Benutzers ein GRANT ausgegeben wird, aber warum schlägt dies fehl? Ich habe diesen Trigger mit Systemprivilegien getestet und er hat einwandfrei funktioniert. Zugegeben, ich habe INHERIT PRIVILEGES nicht getestet, also ist dies eine Art Grenzfall.

Nach einer Menge Debugging-Aufwand habe ich festgestellt, dass der Funktionsaufruf ora_privilege_list einen leeren Satz an die Sammlung mit dem Namen „priv“ zurückgibt. Daher wird npriv auf einen NULL-Wert gesetzt. Da NPRIV NULL ist, macht die Zeile, in der „for i in 1..npriv“ steht, nicht viel Sinn, daher der Fehler.

Meiner Meinung nach sollte ora_privilege_list ein Element zurückgeben, „INHERIT PRIVILEGES“, und ich glaube, dass es ein Fehler ist, diese Liste nicht zurückzugeben. Wenn ora_privilege_list jedoch eine leere Sammlung zurückgeben wird, sollte die Ausgabe der Funktion null sein, und dann würde npriv einen angemesseneren Wert erhalten. Für Bildungszwecke ist ora_privilege_list ein Synonym für DBMS_STANDARD.PRIVILEGE_LIST.

Abgesehen davon kann ich die Oracle-Funktion nicht kontrollieren. Und ich möchte nicht darauf warten, dass Oracle seinen Code in DBMS_STANDARD so ändert, wie ich es für richtig halte. Also werde ich einfach meinen Trigger codieren, um das Problem zu lösen. Das Hinzufügen von zwei einfachen Zeilen löste mein Problem (siehe unten in Fettdruck).

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

Die Lösung ist also ziemlich einfach. Führen Sie die beiden FOR-Schleifen nur aus, wenn NPRIV nicht null ist.