Si trabajas con Oracle desde hace tiempo, es muy probable que hayas visto este escenario:
- Desde SQL, una operación funciona.
- Desde un bloque PL/SQL anónimo, también.
- Desde un procedimiento almacenado… ORA-01031: insufficient privileges.
La forma de resolverlo habitual suele ser (además de la queja al DBA) revisar grants, añadir permisos directamente, probar con otro rol, volver a probar. Incluso solicitar el rol DBA. Y, aun así, el error persiste.
Este artículo no pretende ofrecer una solución rápida, sino explicar por qué este problema sigue existiendo hoy, incluso en versiones actuales como Oracle AI Database 23ai / 26ai, y por qué no es un fallo puntual, sino una consecuencia directa del modelo de herencia de privilegios de Oracle.
El modelo básico de herencia de privilegios desde Definer’s Rights
En Oracle, el modelo básico es conocido:
- Un privilegio define algo que un usuario puede hacer.
- Un rol es un conjunto de privilegios.
- Los privilegios se conceden con
GRANTy se revocan conREVOKE.
En ejecución interactiva (SQL), este modelo funciona de forma bastante intuitiva pero el problema aparece cuando entramos en PL/SQL almacenado.
Para empezar, hay que entender que las unidades de PL/SQL almacenadas (procedimientos, funciones, paquetes… incluso vistas) se ejecutan desde el contexto de derechos del usuario creador. Es decir, «Definer’s Rights».
Para ello, en el momento de la ejecución (importante) esto implica que:
- Oracle cambia el contexto de ejecución al propietario del objeto.
- Los roles quedan deshabilitados (excepto
PUBLIC). - Solo se consideran privilegios concedidos directamente al propietario.
Este comportamiento está perfectamente documentado y es consistente a lo largo de todas las versiones desde hace décadas.
El resultado práctico es que un privilegio que se resuelve correctamente desde desde SQL a través de un rol puede no existir a ojos de un procedimiento almacenado.
Y aquí es dónde aparece la primera incoherencia aparente.
Un bloque PL/SQL anónimo se ejecuta en el contexto de la sesión y utiliza los roles activos del usuario. Es decir, no cambia el contexto de identidad aunque se trate de PL/SQL.
Los desarrolladores en este punto ejecutan en un bloque anónimo, no encuentran errores, crean el procedure, y al ejecutarlo éste falla, solicitan el rol DBA, y aun con ese rol concedido les sigue fallando mientras que el bloque anónimo les funciona siempre.
Y lo más importante, sin que haya cambiado ni el usuario ni la sesión. Es decir, ejecutan el procedure con el mismo usuario que lo han creado, y sobre el mismo código que ejecutan desde SQL y desde un bloque anónimo sin problemas.
Para muestra el siguiente ejemplo, ejecutado en una Oracle26AI Database.
Nota: El usuario DESARROLLO tiene únicamente CONNECT, RESOURCE, UNLIMITED TABLESPACE y CREATE MATERIALIZED VIEW
SQL> create materialized view test as select * from dual;
Materialized view created.
SQL> drop materialized view test;
Materialized view dropped.
SQL> begin
2 execute immediate 'create materialized view test as select * from dual';
3 end;
4 /
PL/SQL procedure successfully completed.
SQL> drop materialized view test;
Materialized view dropped.
SQL> create procedure crea_mv_test as
2 begin
3 execute immediate 'create materialized view test as select * from dual';
4 end;
5 /
Procedure created.
SQL> exec crea_mv_test
BEGIN crea_mv_test; END;
*
ERROR at line 1:
ORA-01031: insufficient privileges
ORA-06512: at "DESARROLLO.CREA_MV_TEST", line 3
ORA-06512: at line 1
Help: https://docs.oracle.com/error-help/db/ora-01031/
El motivo no está en la vista materializada en sí, sino en los privilegios implícitos que Oracle necesita internamente (como CREATE TABLE) y en cómo evalúa su procedencia.
Este es uno de los casos más habituales… y más frustrantes. De hecho, lo he visto tantas veces que fue motivo de escribir un artículo que publiqué en el año 2012 en Experts Exchange.
Aquí lo tienes, por si tienes curiosidad:
A lo largo de los años, Oracle ha ido introduciendo mecanismos para sortear estas limitaciones.
AUTHID CURRENT USERpara ejecutar código con privilegios del invocador (es decir, del usuario que ejecuta el procedure).BEQUEATH CURRENT_USERpara evitar que las vistas actúen como frontera de seguridad ya que siempre se ejecutan desde el modelo de «Definer’s Rights».- Privilegios como
INHERIT PRIVILEGESpara controlar la herencia entre esquemas, sobre todo al acceder a bases de datos remotas a través de DBLinks.
Siempre teniendo en cuenta que estos mecanismos no sustituyen el modelo original, existente desde los orígenes de PL/SQL.
Son un workaround al comportamiento esperado.
Conclusión
El error de insufficient privileges en PL/SQL no es un accidente ni un descuido.
Es el síntoma visible de un modelo de herencia de privilegios que, desde su concepción inicial, se diseñó bajo una aproximación que parecía segura y consistente, pero que en la práctica ha requerido múltiples correcciones, extensiones y mecanismos auxiliares para cubrir sus limitaciones.
Prueba de ello son la constante insistencia en la recomendación de aplicar el principio de mínimo privilegio —difícil de materializar de forma limpia en este modelo— y la necesidad de proporcionar herramientas adicionales para auditoría y captura efectiva de privilegios.
En definitiva, estamos ante un modelo que ha evolucionado por acumulación y no por rediseño, lo que explica muchas de las incoherencias y comportamientos inesperados que siguen apareciendo hoy en día, incluso en versiones recientes de Oracle.
En resumen, un modelo evolucionado por acumulación y no por rediseño.
En los próximos días realizaré una Café Database Session abierta, en formato laboratorio, donde recorreremos estos escenarios paso a paso, ejecutando el código y analizando el comportamiento real del motor en tiempo real.
Si quieres asistir y ver todo esto con calma, podrás apuntarte en el enlace que compartiré próximamente en la lista de suscripción.