Oracle
 sql >> Database >  >> RDS >> Oracle

ORA-6502 con trigger di registrazione di concessione

Ho un nuovo progetto su cui sto lavorando in cui voglio che un lavoro Oracle revochi i privilegi che ho concesso al personale IT che ha più di 30 giorni. Il nostro personale IT ha bisogno di accedere occasionalmente ad alcune tabelle di produzione per risolvere i problemi. Concediamo SELECT privs sui tavoli di cui la persona ha bisogno, ma nessuno mi dice mai quando hanno finito il loro compito e quei privilegi restano lì per sempre. Volevo che un sistema revocasse automaticamente i privilegi più vecchi di 30 giorni in modo da non dovermi ricordare di farlo. Prima di poter revocare i privilegi, avevo bisogno di un modo per tenere traccia di quei privilegi. Quindi ho creato un trigger che viene attivato ogni volta che viene emesso un GRANT e registra i dettagli in una tabella. Successivamente, un processo Oracle eseguirà la scansione di quella tabella e revocherà i privilegi che rileva che sono troppo vecchi. Il mio codice di attivazione è il seguente:

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

Il codice sopra non è originale. Ho trovato un buon esempio su Internet e ho modificato alcune cose. Dopo aver testato il codice per 3 settimane, ho attivato il trigger in produzione. Ho impiegato solo pochi giorni per ricevere un errore.

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... sto creando un utente che non concede nulla. Ma possiamo sicuramente vedere che il mio trigger sta riscontrando un problema nell'esecuzione. Allora perché questo trigger si attiva se tutto ciò che sto facendo è creare un utente? Una semplice traccia SQL mi ha mostrato cosa stava succedendo con quell'SQL ricorsivo. Dietro le quinte, Oracle pubblica per mio conto quanto segue:

CONCEDERE PRIVILEGI DI EREDITÀ SULL'UTENTE "BOB" al PUBBLICO;

Ok... quindi, a questo punto, so che viene emesso un GRANT quando creo un utente, ma perché questo non riesce? Ho testato questo trigger con i privilegi di sistema e ha funzionato perfettamente. Certo, non ho testato INHERIT PRIVILEGES, quindi questo è un caso limite.

Dopo una buona quantità di sforzi di debug, ho determinato che la chiamata alla funzione ora_privilege_list restituisce un set vuoto alla raccolta denominata "priv". Pertanto, npriv viene impostato su un valore NULL. Poiché NPRIV è NULL, la riga in cui si dice "for i in 1..npriv" non ha molto senso, da qui l'errore.

A mio parere, ora_privilege_list dovrebbe restituire un elemento, "INHERIT PRIVILEGES" e credo che non restituire quell'elenco sia un bug. Tuttavia, se ora_privilege_list restituirà una raccolta vuota, l'output della funzione dovrebbe essere zero e quindi npriv otterrebbe un valore più appropriato. Per scopi didattici, ora_privilege_list è sinonimo di DBMS_STANDARD.PRIVILEGE_LIST.

Detto questo, non posso controllare la funzione Oracle. E non voglio aspettare che Oracle modifichi il proprio codice in DBMS_STANDARD in quello che penso dovrebbe essere. Quindi codificherò il mio trigger per gestire il problema. L'aggiunta di due semplici righe ha risolto il mio problema (visto sotto in grassetto).

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

Quindi la soluzione è piuttosto semplice. Esegui i due cicli FOR solo se NPRIV non è nullo.