Oracle
 sql >> Base de Dados >  >> RDS >> Oracle

ORA-6502 com gatilho de registro de concessão


Estou trabalhando em um novo projeto no qual quero que um trabalho da Oracle revogue privilégios que concedi à equipe de TI com mais de 30 dias. Nossa equipe de TI precisa de acesso ocasional a algumas tabelas de produção para solucionar problemas. Concedemos privs SELECT nas tabelas que a pessoa precisa, mas ninguém nunca me diz quando eles terminam sua tarefa e esses privilégios ficam lá para sempre. Eu queria que um sistema revogasse automaticamente privilégios com mais de 30 dias para que eu não precisasse me lembrar de fazer isso. Antes que eu pudesse revogar privilégios, eu precisava de uma maneira de rastrear esses privilégios. Então eu criei um gatilho que é acionado sempre que um GRANT é emitido e registra os detalhes em uma tabela. Mais tarde, um trabalho do Oracle examinará essa tabela e revogará os privilégios que achar que são muito antigos. Meu código de gatilho é o seguinte:
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;
 / 

O código acima não é original. Encontrei um bom exemplo na Internet e modifiquei algumas coisas. Depois de testar o código por 3 semanas, coloquei o gatilho em produção. Levou apenas alguns dias para eu receber um erro.
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… estou criando um usuário que não concede nada. Mas podemos ver que meu gatilho está tendo um problema de execução. Então, por que esse gatilho está disparando se tudo o que estou fazendo é criar um usuário? Um simples rastreamento de SQL me mostrou o que estava acontecendo com esse SQL recursivo. Nos bastidores, a Oracle está emitindo o seguinte em meu nome:

CONCEDER PRIVILÉGIOS DE HERDADE DO USUÁRIO “BOB” AO PÚBLICO;

Ok… então, neste ponto, eu sei que há um GRANT sendo emitido quando eu crio um usuário, mas por que isso está falhando? Eu testei esse gatilho com privilégios de sistema e funcionou muito bem. Concedido, eu não testei INHERIT PRIVILEGES, então este é um caso meio extremo.

Após um bom esforço de depuração, determinei que a chamada da função ora_privilege_list está retornando um conjunto vazio para a coleção chamada “priv”. Como tal, npriv está sendo definido como um valor NULL. Como NPRIV é NULL, a linha onde está diz “for i in 1..npriv” não faz muito sentido, daí o erro.

Na minha opinião, ora_privilege_list deve retornar um item, “HERIT PRIVILEGES” e acredito que não retornar essa lista seja um bug. No entanto, se ora_privilege_list for retornar uma coleção vazia, a saída da função deve ser zero e, em seguida, npriv obterá um valor mais adequado. Para fins educacionais, ora_privilege_list é sinônimo de DBMS_STANDARD.PRIVILEGE_LIST.

Tudo isso dito, não posso controlar a função Oracle. E eu não quero esperar que o Oracle mude seu código em DBMS_STANDARD para o que eu acho que deveria ser. Então, vou apenas codificar meu gatilho para lidar com o problema. Adicionar duas linhas simples resolveu meu problema (visto abaixo em negrito).
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;
 / 

Então a correção é bem simples. Somente execute os dois loops FOR se NPRIV não for nulo.