Oracle
 sql >> Baza danych >  >> RDS >> Oracle

ORA-6502 z wyzwalaczem Grant Logging

Mam nowy projekt, nad którym pracuję, w którym chcę, aby praca w Oracle odebrała uprawnienia, które nadałem personelowi IT starszemu niż 30 dni. Nasz personel IT potrzebuje okazjonalnego dostępu do kilku stołów produkcyjnych w celu rozwiązywania problemów. Przyznajemy przywileje SELECT na stołach, których potrzebuje osoba, ale nikt nigdy nie mówi mi, kiedy skończą z zadaniem, a te przywileje pozostają tam na zawsze. Chciałem, aby system automatycznie odbierał uprawnienia starsze niż 30 dni, abym nie musiał o tym pamiętać. Zanim mogłem cofnąć uprawnienia, potrzebowałem sposobu na śledzenie tych uprawnień. Stworzyłem więc wyzwalacz, który jest uruchamiany przy każdym wydaniu GRANT i rejestruje szczegóły w tabeli. Później zadanie Oracle przeskanuje tę tabelę i odbierze uprawnienia, które uzna za zbyt stare. Mój kod wyzwalacza wygląda następująco:

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

Powyższy kod nie jest oryginalny. Znalazłem dobry przykład w Internecie i zmodyfikowałem kilka rzeczy. Po testowaniu kodu przez 3 tygodnie uruchomiłem wyzwalacz w środowisku produkcyjnym. Otrzymanie błędu zajęło mi tylko kilka dni.

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… tworzę użytkownika, który niczego nie udziela. Ale możemy być pewni, że mój wyzwalacz ma problem z wykonaniem. Dlaczego więc ten wyzwalacz się uruchamia, jeśli jedyne, co robię, to tworzenie użytkownika? Prosty ślad SQL pokazał mi, co się dzieje z tym rekurencyjnym SQL. Za kulisami Oracle wydaje w moim imieniu następujące dokumenty:

PRZYZNAJ PUBLICZNE UPRAWNIENIA DZIEDZICZĄCE UŻYTKOWNIKOWI „BOB”;

Ok… więc w tym momencie wiem, że podczas tworzenia użytkownika jest wydawany GRANT, ale dlaczego to się nie udaje? Przetestowałem ten wyzwalacz z uprawnieniami systemowymi i działał dobrze. To prawda, że ​​nie testowałem INHERIT PRIVILEGES, więc jest to trochę skrajny przypadek.

Po sporym wysiłku związanym z debugowaniem ustaliłem, że wywołanie funkcji ora_privilege_list zwraca pusty zestaw do kolekcji o nazwie „priv”. W związku z tym npriv jest ustawiany na wartość NULL. Ponieważ NPRIV ma wartość NULL, wiersz, w którym jest napisane „for i in 1..npriv”, nie ma większego sensu, stąd błąd.

Moim zdaniem ora_privilege_list powinna zwrócić jedną pozycję „Odziedzicz przywileje” i uważam, że nie zwraca tej listy jako błędu. Jeśli jednak ora_privilege_list ma zamiar zwrócić pustą kolekcję, to wyjście z funkcji powinno wynosić zero, a wtedy npriv otrzyma bardziej odpowiednią wartość. Dla celów edukacyjnych ora_privilege_list jest synonimem DBMS_STANDARD.PRIVILEGE_LIST.

Biorąc to wszystko pod uwagę, nie mogę kontrolować funkcji Oracle. I nie chcę czekać, aż Oracle zmieni swój kod w DBMS_STANDARD na taki, jaki moim zdaniem powinien być. Więc po prostu zakoduję mój wyzwalacz, aby poradzić sobie z problemem. Dodanie dwóch prostych linii rozwiązało mój problem (poniżej pogrubioną czcionką).

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

Więc poprawka jest dość prosta. Wykonaj dwie pętle FOR tylko wtedy, gdy NPRIV nie ma wartości NULL.


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Dlaczego CONNECT BY LEVEL w tabeli zwraca dodatkowe wiersze?

  2. Programista SQL 4

  3. Powiązanie parametrów z Oracle Dynamic SQL

  4. Uzyskaj dostęp do usługi sieciowej z procedury składowanej Oracle

  5. Zainstaluj klienta Oracle Instant w kontenerze Docker dla Pythona cx_Oracle