Authid Current_User 通过role获取到的权限 package不生效

发布于:2024-03-11 ⋅ 阅读:(57) ⋅ 点赞:(0)

 

我们知道,用户拥有的role权限在存储过程是不可用的。如:
SQL> 

select  *  from  dba_role_privs  where  grantee='SUK';
GRANTEE  GRANTED_ROLE  ADMIN_OPTION  DEFAULT_ROLE
------------  ------------  ------------  ------------
SUK  DBA  NO  YES
SUK  CONNECT  NO  YES
SUK  RESOURCE  NO  YES
--用户SUK拥有DBA这个role
--再创建一个测试存储过程:
create  or  replace  procedure  p_create_table
is
begin
Execute  Immediate  'create  table  create_table(id  int)';
end  p_create_table;
--然后测试
SQL>  exec  p_create_table;
begin  p_create_table;  end;
ORA-01031:  权限不足
ORA-06512:  在"SUK.P_CREATE_TABLE",  line  3
ORA-06512:  在line  1
--可以看到,即使拥有DBA  role,也不能创建表。role在存储过程中不可用。
--遇到这种情况,我们一般需要显式进行系统权限,如grant  create  table  to  suk;
--但这种方法太麻烦,有时候可能需要进行非常多的授权才能执行存储过程
--实际上,oracle给我们提供了在存储过程中使用role权限的方法:
--修改存储过程,加入Authid  Current_User时存储过程可以使用role权限。
create  or  replace  procedure  p_create_table
Authid  Current_User  is
begin
Execute  Immediate  'create  table  create_table(id  int)';
end  p_create_table;
--再尝试执行:
SQL>  exec  p_create_table;
PL/SQL  procedure  successfully  completed
--已经可以执行了。

-----

如果存储过程中涉及多个库的表,有时会提示权限不足,oracle存储过程分为两种:DR(Definer's Rights) Procedure 和IR(Invoker's Rights) Procedure.以下两段话为转载:
1:定义者权限:定义者权限PL/SQL程序单元是以这个程序单元拥有者的特权来执行它的,也就是说,任何具有这个PL/SQL程序单元执行权的用户都可以访问程序中的对象。所有具有执行权的用户都有相同的访问权限,在定义者权限下,执行的用户操作的schema为定义者,所操作的对象是定义者在编译时指定的对象。在定义者(definer)权限下,当前用户的权限为角色无效情况下所拥有的权限。

2:调用者权限:调用者权限是指当前用户(而不是程序的创建者)执行PL/SQL程序体的权限。这意味着不同的用户对于某个对象具有的权限很可能是不同的,这个思想的提出,解决了不同用户更新不同表的方法。在调用者权限下,执行的用户操作的schema为当前用户,所操作的对象是当前模式下的对象。在调用者(invoker)权限下,当前用户的权限为当前所拥有的权限(含角色)。

** 所以 **
ORACLE默认为定义者权限,定义者权限在存储过程中ROLE无效,需要显式授权。
执行sql和pl/sql的默认情况下,都是以定义者权限执行的,这也是我们最常用的权限模式,例如在存储过程中调用其他用户的表,但是定义存储过程的当前用户没有显示访问该表的权限,即使当前用户具有dba角色,编译过程中也会出现权限不足的问题,因为role无效。显示授权需要更换用户给当前用户授权


 

GOAL

Two types of subprogram execution privileges are available since Oracle 8i :

Definer rights
Invoker rights

However, USER_OBJECTS or ALL_OBJECTS do not hold information on AUTHID. How to know if AUTHID is DEFINER or CURRENT_USER?

SOLUTION

In order to obtain the type of definition, either

1-- Use DBA_SOURCE view to get the line code of the procedures text :

SQL> select text
from dba_source
where name='TEST_DEF';

2-- or join SYS.PROCEDURE$ and SYS.OBJ$ views to find the AUTHID status,

SQL> select sign(bitand(p.options, 16))
from sys.procedure$ p, sys.obj$ o
where p.obj# = o.obj# and o.name = '&objectname';

A value of 1 indicates invoker's rights (i.e. the procedure was created
with the AUTHID CURRENT_USER clause), 0 indicates definer's rights.

The SELECT ANY TABLE system privilege is required to run the above SELECT
statement and even SELECT ANY DICTIONARY in 9i.


Example:
--------

CREATE OR REPLACE PROCEDURE test_def
AUTHID DEFINER AS
begin
dbms_output.put_line('Definer');
end;

select sign(bitand(p.options, 16)) from sys.procedure$ p, sys.obj$ o where
p.obj# = o.obj# and o.name = 'TEST_DEF';

Returns '0' in this case,

CREATE OR REPLACE PROCEDURE test_inv
AUTHID CURRENT_USER AS
begin
dbms_output.put_line('current user');
end;

select sign(bitand(p.options, 16)) from sys.procedure$ p, sys.obj$ o where
p.obj# = o.obj# and o.name = 'TEST_INV';

Returns '1' in this case.

------------------

GOAL

How To Run Invoker Definer Right Procedure In General And From Dbms_job?
 

SOLUTION

To achive this we need to follow the code and below explanation:
create user u1 identified by u1;

create user u2 identified by u2;

conn sys as sysdba

grant connect,resource to u1,u2;

conn u1/u1
create table u1_t(a number);
insert into u1_t values(12);


create or replace procedure proc1 authid current_user is
vara number;
begin
select a into vara from u1.u1_t;
dbms_output.put_line(vara);
end;
/
grant execute on proc1 to u2;

conn u2/u2

set serverout on

exec u1.proc1;
==>Now it will fail. ---connect,resource only
conn u1/u1
create role ipc_users;
grant select on u1_t to ipc_users;
grant ipc_users to u2;

conn u2/u2
set serverout on

exec u1.proc1;
==>now it will be successful.

VARIABLE jobno number;
BEGIN
DBMS_JOB.SUBMIT(:jobno,
'u1.proc1;',
SYSDATE+1);
commit;
END;
/
print jobno

exec dbms_job.run(<jobno>)



This will fail.

Because dbms_job runs in the background as a separate job it runs without roles enabled so effectively as definers rights.---schedualer job增加了false/true 选项

In order to run this using dbms_job we need to give explicit grant



conn u1/u1

grant select on u1_t to u2;

Then run as:

exec dbms_job.run(<jobno>)

It will suceed.

Please note that with invoker right the roles are enabled in the execution time.

But with definer rights roles remain disabled in execution time.

So we have to make sure to give explicit grant to the object used in definer right package or procedure.

-----------------

在Oracle8i以前,所有已编译存储对象(包括packages, procedures, functions, triggers, and views)只能以定义者(Definer)身份解析运行;
从Oracle8i开始,Oracle引入调用者(invoker)权限,使得对象可以以调用者身份和权限执行。

定义者(Definer)指编译存储对象的所有者.
调用者(Invoker)指拥有当前会话权限的模式,这可能和当前登录用户相同或不同(alter session set current_schema 可以改变调用者Schema).

TOM在他的《Expert One on One》的第23章曾经详细介绍这一特性,本文引用Tom的一个例子用于说明Definer and Invoker权限。

 
create or replace procedure definer_proc
as
begin
    for x in
    ( select sys_context( 'userenv', 'current_user' ) current_user,
             sys_context( 'userenv', 'session_user' ) session_user,
             sys_context( 'userenv', 'current_schema' ) current_schema
        from dual )
    loop
        dbms_output.put_line( 'Current User:   ' || x.current_user );
        dbms_output.put_line( 'Session User:   ' || x.session_user );
        dbms_output.put_line( 'Current Schema: ' || x.current_schema );
    end loop;
end;
/
Procedure created.
SQL> 
SQL> grant execute on definer_proc to test;
Grant succeeded.
SQL> 
 create or replace procedure invoker_proc
 AUTHID CURRENT_USER
 as
 begin
     for x in
     ( select sys_context( 'userenv', 'current_user' ) current_user,
              sys_context( 'userenv', 'session_user' ) session_user,
              sys_context( 'userenv', 'current_schema' ) current_schema
         from dual )
     loop
         dbms_output.put_line( 'Current User:   ' || x.current_user );
         dbms_output.put_line( 'Session User:   ' || x.session_user );
         dbms_output.put_line( 'Current Schema: ' || x.current_schema );
     end loop;
 end;
 
Procedure created.
SQL> 
SQL> grant execute on invoker_proc to test;
Grant succeeded.
注意invoker权限的本质是引入了AUTHID CURRENT_USER子句,通过此句Oracle得以使用invoker身份编译执行对象。


select * from dba_users

create user test identified by test;
grant connect ,resource to test;

2.以test用户(invoker)身份执行

SQL> connect test/test
Connected.
SQL> 
SQL> set serveroutput on
SQL> begin   system.definer_proc;
end;
Current User:   EYGLE
Session User:   TEST
Current Schema: EYGLE
PL/SQL procedure successfully completed.
 
begin   system.invoker_proc;
end;
Current User:   TEST
Session User:   TEST
Current Schema: TEST
PL/SQL procedure successfully completed.
注意只有使用invoker者权限执行时,Schema才转换为TEST.

SQL> alter session set current_schema = system;
Session altered.
SQL> exec eygle.definer_proc
Current User:   EYGLE
Session User:   TEST
Current Schema: EYGLE
PL/SQL procedure successfully completed.
SQL> exec eygle.invoker_proc
Current User:   TEST
Session User:   TEST
Current Schema: SYSTEM
PL/SQL procedure successfully completed.
SQL> 
通过alter session set current_schema方式修改当前模式之后,我们看到仍然是仅当使用invoker权限执行时,Schmea方切换为SYSTEM.

前两天有位朋友,微信公众号提了一个问题,原文描述如下,

1. 我的需求是在tag库中执行一个处理,使得tag中所有用户seq的nextval与src库中一致。

2. 我在tag库的user1中创建了一个存储过程,代码逻辑为通过dblink(指向src库的user1,user1有读取dba视图的权限)查询源库的dba_sequence与tag库的对比,找出两库间nextval相差1000以上的,并在tag中获取create seq的语句,然后用src库中的nextval值替换,并在src库中按src库的nextval重建seq。

3. 问题出在,我没有sys用户或者dba权限,使用的是一个user1用户,过程建在user1中,但程序需要处理所有用户的seq,我写的过程是给dba用的,他能用sys执行。在用sys执行过程时,执行到dbms_metadata.get_ddl('SEQUENCE','SEQ1','USER2')时,会报错用户USER2中没有这个序列号。但如果不通过user1的这个存储过程,而是直接在sys用户中执行语句dbms_metadata.get_ddl。。。就可以正常获取create语句,我不明白,执行者是sys,执行的是user1的过程,权限要按照user1的吗?但我尝试给user1授权其他用户序列号的使用权限也不行。

刚又进行了个实验,
在user1中
create or replace procedure user1.p_seq_test as
  LV_SQL VARCHAR2(1024);
begin                               
  execute immediate 'create table user2.t_dataread_test1(col1 number)';
END;
/
在sys中
begin
  user1.p_seq_test;
end;
执行报错没有权限。但是我用sys进行grant create any table to user1后就可以了。

我之前以为,虽然procedure在user1下,但是我用sys执行,权限应该是按照sys的权限走,但实际实验看即使sys执行存储过程,权限也是按照存储过程的属主用户走的。

只不过是我前面说过的问题中,我始终没有找到能让USER1成功执行dbms_metadata.get_ddl('SEQUENCE','SEQ_TEST','USER2')所需要的权限,也就是user1能操作user2的sequence的权限。

按照理解,总结一下问题,

1. 用户user1定义的存储过程,即使用sys用户执行,需要参考user1权限?

2. 用户user1中创建一个序列,sys用户可以执行dbms_metadata.get_ddl('SEQUENCE','SEQ','USER1')得到序列创建语句,但user1用户看不了属于user2的序列定义?

问题1:用户user1定义的存储过程,即使用sys用户执行,需要参考user1权限?

我们先看问题1,创建测试用户user1和user2,

SQL> create user user1 identified by 123;
User created.

SQL> create user user2 identified by 123;
User created.


SQL> grant connect, resource to user1;
Grant succeeded.

SQL> grant connect, resource to user2;
Grant succeeded.

sys用户创建属于user1的存储过程,

SQL> create or replace procedure user1.p_seq_test as
       LV_SQL VARCHAR2(1024);
     begin                               
       execute immediate 'create table user2.t_dataread_test1(col1 number)';
     END;
     /
Procedure created.

sys用户执行这个存储过程, 提示权限错误,

SQL> begin
       user1.p_seq_test;
     end;
     /
begin
*
ERROR at line 1:
ORA-01031: insufficient privileges
ORA-06512: at "USER1.P_SEQ_TEST", line 4
ORA-06512: at line 2

授予user1用户create any table权限,

SQL> grant create any table to user1;
Grant succeeded.

SQL> begin
       user1.p_seq_test;
     end;
     /
PL/SQL procedure successfully completed.

从现象来看,即使使用sys执行user1的存储过程,权限参考的是user1,不是sys,因此由于user1没有create any table的权限,报错ORA-01031: insufficient privileges,注意编译过程未报错,而是执行过程中报错了。

杨长老有篇文章,其实提及了类似的问题,http://m.blog.itpub.net/4227/viewspace-69047,

SQL> CREATE OR REPLACE PROCEDURE P_TEST AS
  BEGIN

 FOR I IN (SELECT DBMS_METADATA.GET_DDL('TABLE', 'DUAL', 'SYS') DEFINE FROM DUAL) LOOP
 DBMS_OUTPUT.PUT_LINE(SUBSTR(I.DEFINE, 1, 255));
END LOOP;
 END;
 /

       一直就认为是角色导致的问题,而没有继续深究。而这次仔细看了Tom对定义者权限和调用者权限存储过程的描述才真正彻底清楚了导致上述现象的原因。

       一个调用者权限的存储过程,如果在定义者权限存储过程中被调用,则它的行为表现将像一个定义者权限的过程。这时由于定义者权限过程中,CURRENT_SCHEMA和所拥有的权限都是固定的,调用者权限过程中所有可能发生变化的东西都被固定了下来。
       而如果直接调用或者通过调用者权限过程来调用,那么这个调用者权限过程的全部特性得以保留。而这就是上面碰到的那个问题的真正答案。

Tom的书《Expert one-on-one Oracle》中单独有一章节,介绍的就是,调用者和定义者,

定义者(Definer)-指的是编译存储对象的拥有者,包括包、存储过程、函数、触发器和视图。

调用者(Invoker)-指当前会话中生效的schema,不一定就是当前登录的用户。

Oracle 8i之前,所有编译存储对象的执行,都是以定义者权限为准,因此编译阶段就会发现错误,不会像上面,等待运行阶段才报错。

从Oracle 8i开始,引入了invoker rights-调用者,允许包、存储过程、函数、触发器和视图这些对象的权限,以运行时的调用者为准。

引用Tom的实验,首先user1用户,创建如下两个存储过程,分为定义者权限,和调用者权限,并将这两个存储过程,执行权限授予user2,

create or replace procedure definer_proc
as
begin
for x in
( select sys_context('userenv', 'current_user') current_user,

sys_context('userenv', 'session_user') session_user,
sys_context('userenv', 'current_schema') current_schema
from dual )
loop
  dbms_output.put_line('Current User: ' || x.current_user );
  dbms_output.put_line('Session User: ' || x.session_user );
  dbms_output.put_line('Current Schema: ' || x.current_schema );
end loop;
end;
/

Procedure created.

create or replace procedure invoker_proc

AUTHID CURRENT_USER
as
begin
for x in
( select sys_context('userenv', 'current_user') current_user,

sys_context('userenv', 'session_user') session_user,
sys_context('userenv', 'current_schema') current_schema
from dual )
loop
  dbms_output.put_line('Current User: ' || x.current_user );
  dbms_output.put_line('Session User: ' || x.session_user );
  dbms_output.put_line('Current Schema: ' || x.current_schema );
end loop;
end;
/

SQL> grant execute on definer_proc to user2;
Grant succeeded.

SQL> grant execute on invoker_proc to user2;
Grant succeeded.

接着使用user2,分别执行,

SQL> exec user1.definer_proc;
Current User: USER1
Session User: USER2
Current Schema: USER1
PL/SQL procedure successfully completed.

SQL> exec user1.invoker_proc
Current User: USER2
Session User: USER2
Current Schema: USER2
PL/SQL procedure successfully completed.

可以看出,使用定义者权限,Current User和Current Schema均为user1,因为存储过程属于user1,但调用者权限,由于调用者是user2,因此Current User和Current Schema均为user2。

尝试设置current_schema,动态改变用户所用的schema,可以看出,第一个存储过程为定义者,没有任何变化,第二个存储过程为调用者,Current User是user2,Current Schema则变为了之前设置的system,说明定义者权限,是相对静态的,而调用者权限,则相对动态,

SQL> alter session set current_schema=system;
Session altered.

SQL> exec user1.definer_proc;
Current User: USER1
Session User: USER2
Current Schema: USER1
PL/SQL procedure successfully completed.

SQL> exec user1.invoker_proc;
Current User: USER2
Session User: USER2
Current Schema: SYSTEM
PL/SQL procedure successfully completed.

对于存储过程,dba_procedures中AUTHID字段,表示当前的存储过程/函数,定义为“定义者”还是“调用者”,

11.2.0.4下,记录分布如下,

本文开始的问题,CREATE TABLE语句的存储过程,从现象来看,是定义者的权限,即使使用sys创建和执行,参考的是user1是否有相应权限,未参考sys用户自己的权限。

问题二:用户user1中创建一个序列,sys用户可以执行dbms_metadata.get_ddl('SEQUENCE','SEQ','USER1')得到序列创建语句,但user1用户看不了属于user2的序列定义?

其实第一个问题解决了,第二个问题,就容易理解了。

我们先模拟下实验过程,用sys为用户user1和user2创建序列,

SQL> create sequence user1.SEQ_TEST;
Sequence created.

SQL> create sequence user2.SEQ_TEST;
Sequence created.

sys用户执行dbms_metadata.get_ddl,一切ok,

SQL> set serveroutput on
SQL> select dbms_metadata.get_ddl('SEQUENCE','SEQ_TEST','USER2') from dual;
DBMS_METADATA.GET_DDL('SEQUENCE','SEQ_TEST','USER2')
--------------------------------------------------------------------------------
CREATE SEQUENCE  "USER2"."SEQ_TEST"  MINVALUE 1 MAXVALUE 9999999999999999999

SQL> select dbms_metadata.get_ddl('SEQUENCE','SEQ_TEST','USER1') from dual;
DBMS_METADATA.GET_DDL('SEQUENCE','SEQ_TEST','USER1')
--------------------------------------------------------------------------------
CREATE SEQUENCE  "USER1"."SEQ_TEST"  MINVALUE 1 MAXVALUE 9999999999999999999

用户user1检索user1自己的SEQ_TEST序列,可以正常操作,

SQL> select dbms_metadata.get_ddl('SQUENCE','SEQ_TEST','USER1') from dual;
DBMS_METADATA.GET_DDL('SEQUENCE','SEQ_TEST','USER1')
--------------------------------------------------------------------------------
CREATE SEQUENCE  "USER1"."SEQ_TEST"  MINVALUE 1 MAXVALUE 9999999999999999999

用户user1检索user2所属的SEQ_TEST序列, 报错USER2中找不着这个SEQ_TEST序列对象,

SQL> select dbms_metadata.get_ddl('SQUENCE','SEQ_TEST','USER2') from dual;
ERROR:
ORA-31603: object "SEQ_TEST" of type SEQUENCE not found in schema "USER2"
ORA-06512: at "SYS.DBMS_METADATA", line 5805
ORA-06512: at "SYS.DBMS_METADATA", line 8344
ORA-06512: at line 1
no rows selected

对于某一个具体的对象,可以检索AUTHID字段,判断其为定义者,还是调用者权限,可以看出DBMS_METADATA.GET_DDL()就是调用者权限,

因此执行的时候,参考的是执行用户的权限,sys用户有检索user1和user2对象定义的权限,user1有检索自己对象的权限,但没有检索其他用户对象的权限。

这篇文章DBMS_METADATA.GET_DDL Returns Error When Select Types Ora-31603 (文档 ID 312883.1),针对这种问题,指出了原因所在,

The appropriate privileges have not been granted to the schema executing the procedure as the same procedure works fine from the schema that owns the object and from SYS.

给出了两种解决方案,

Step 1: Execute "GRANT SELECT_CATALOG_ROLE TO <schema>;" as SYS or another user with the privilege.
Step 2. Modify the procedure to include "AUTHID CURRENT_USER"

方法1:授予GRANT SELECT_CATALOG_ROLE角色

sys用户执行,

SQL> GRANT SELECT_CATALOG_ROLE TO user1;
Grant succeeded.

user1此时可以检索user2对象定义,

SQL> select dbms_metadata.get_ddl('SEQUENCE','SEQ_TEST','USER2') from dual;
DBMS_METADATA.GET_DDL('SEQUENCE','SEQ_TEST','USER2')
--------------------------------------------------------------------------------
CREATE SEQUENCE  "USER2"."SEQ_TEST"  MINVALUE 1 MAXVALUE 9999999999999999999

但这种方法不很推荐,因为SELECT_CATALOG_ROLE角色,允许用户访问所有数据字典,一般用户不应该有角色,权限级别较高。

方法2:使用“AUTHID CURRENT_USER”定义存储过程

如果将“dbms_metadata.get_ddl”封装于存储过程,可以参考杨长老这篇http://m.blog.itpub.net/4227/viewspace-69047,文章中就用了这种定义,

CREATE OR REPLACE PROCEDURE P_TEST AUTHID CURRENT_USER AS
BEGIN
FOR I IN (SELECT DBMS_METADATA.GET_DDL('TABLE', 'DUAL', 'SYS') DEFINE FROM DUAL) LOOP
DBMS_OUTPUT.PUT_LINE(SUBSTR(I.DEFINE, 1, 255));
END LOOP;
END;
/

提问的兄弟很认真,回复我如下,这种追求问题答案的态度,值得我们学习,

我又折腾了两三个小时,写了个程序把SELECT_CATALOG_ROLE角色对应的2238个表或视图、过程的授权以及被包含在这个角色中的另一个角色HS_ADMIN_SELECT_ROLE都试过了,程序内用sys每授权一个对象后都会用user1重新登录并执行dbms_metadata.get_ddl处理,但实验结果没有像预期那样能知道到底是哪个对象授权有影响,所有对象都授权了仍然无法正常获取seq的create ddl。
最后还是授权角色SELECT_CATALOG_ROLE才管用。
我不打算再试了,感觉oracle可能还会有其他很隐蔽的内部逻辑。

我们使用fyunwrap(之前这篇文章《dbms_space.create_table_cost的unwrap解密和原理解析》介绍过),来看看这个dbms_metadata.get_ddl。

unwrap这个dbms_metadata包,可以看出,get_ddl是一个函数,调用了do_get函数,

FUNCTION GET_DDL (

OBJECT_TYPE IN  VARCHAR2,

NAME IN  VARCHAR2,

SCHEMA IN  VARCHAR2 DEFAULT NULL,

VERSION IN  VARCHAR2 DEFAULT 'COMPATIBLE',

MODEL IN  VARCHAR2 DEFAULT 'ORACLE',

TRANSFORM IN  VARCHAR2 DEFAULT 'DDL')

RETURN CLOB IS

BEGIN

DO_GET(OBJECT_TYPE,NAME,SCHEMA,VERSION,MODEL,TRANSFORM,1,'GET_DDL');

RETURN DBMS_ASSERT.NOOP(GET$_DOC);

END;

从do_get函数定义,可以看出,这是通用函数,其中参数PUBLIC_FUNC,接收的是'GET_DDL'参数,

PROCEDURE DO_GET (

OBJECT_TYPE IN  VARCHAR2,

NAME IN  VARCHAR2,

SCHEMA IN  VARCHAR2,

VERSION IN  VARCHAR2,

MODEL IN  VARCHAR2,

TRANSFORM IN  VARCHAR2,

OBJECT_COUNT IN  NUMBER,

PUBLIC_FUNC IN  VARCHAR2,

NETWORK_LINK    IN  VARCHAR2 DEFAULT NULL)

IS

HANDLE NUMBER;

SAVE_HANDLE NUMBER := CUR_HANDLE;

IND NUMBER;

TR_HANDLE NUMBER;

SCHEMA_NAME VARCHAR2(30);

OBJECT_NAME VARCHAR2(2000) := ' ';

DUMMY_PARSE_ITEMS   SYS.KU$_PARSED_ITEMS := SYS.KU$_PARSED_ITEMS();

DUMMY BOOLEAN;

DUMMY_PROCOBJ_ERRORS SYS.KU$_VCNT;

BEGIN

IF GET$_DOC IS NOT NULL THEN

DBMS_LOB.FREETEMPORARY(GET$_DOC);

END IF;

DBMS_LOB.CREATETEMPORARY(GET$_DOC,TRUE);

HANDLE := DO_OPEN(OBJECT_TYPE, VERSION, MODEL, PUBLIC_FUNC, NETWORK_LINK);

CUR_HANDLE := HANDLE;

IF PUBLIC_FUNC = 'GET_XML'

OR PUBLIC_FUNC = 'GET_SXML'

OR PUBLIC_FUNC = 'GET_SXML_DDL'

OR PUBLIC_FUNC = 'GET_DDL'

THEN

OBJECT_NAME := NAME;

IF LENGTH(NAME) > 30

THEN

SET_FILTER(HANDLE,'LONGNAME',DBMS_ASSERT.NOOP(NAME));

ELSE

SET_FILTER(HANDLE,'NAME',DBMS_ASSERT.NOOP(NAME));

END IF;

IF SCHEMA IS NOT NULL

THEN

SET_FILTER(HANDLE,'SCHEMA',DBMS_ASSERT.NOOP(SCHEMA));

END IF;

ELSIF PUBLIC_FUNC = 'GET_DEPENDENT_XML'

OR    PUBLIC_FUNC = 'GET_DEPENDENT_SXML'

OR    PUBLIC_FUNC = 'GET_DEPENDENT_DDL'

THEN

SET_FILTER(HANDLE,'BASE_OBJECT_NAME',DBMS_ASSERT.NOOP(NAME));

IF SCHEMA IS NOT NULL

THEN

SET_FILTER(HANDLE,'BASE_OBJECT_SCHEMA',DBMS_ASSERT.NOOP(SCHEMA));

END IF;

ELSIF PUBLIC_FUNC = 'GET_GRANTED_XML'

OR    PUBLIC_FUNC = 'GET_GRANTED_DDL'

THEN

IF NAME IS NOT NULL

THEN

SET_FILTER(HANDLE,'GRANTEE',DBMS_ASSERT.NOOP(NAME));

ELSE

SCHEMA_NAME := GET_CURRENT_USER;

SET_FILTER(HANDLE,'GRANTEE',DBMS_ASSERT.NOOP(SCHEMA_NAME));

END IF;

END IF;

SET_COUNT(HANDLE,OBJECT_COUNT,OBJECT_TYPE);

IF TRANSFORM IS NULL THEN

SET_XMLFORMAT(HANDLE,'PRETTY',TRUE);

ELSIF PUBLIC_FUNC = 'GET_SXML_DDL'

AND   TRANSFORM = 'SXMLDDL'

THEN

TR_HANDLE := DBMS_METADATA.ADD_TRANSFORM(HANDLE,'SXML');

TR_HANDLE := DBMS_METADATA.ADD_TRANSFORM(HANDLE,'SXMLDDL');

ELSE

TR_HANDLE := DBMS_METADATA.ADD_TRANSFORM(HANDLE,TRANSFORM);

IF MODEL = 'ORACLE' AND TRANSFORM = 'DDL'

THEN

DBMS_METADATA.SET_TRANSFORM_PARAM(TR_HANDLE,'INHERIT',TRUE);

END IF;

END IF;

IND := GET_CONTEXT_ENTRY(HANDLE,PUBLIC_FUNC,TRUE);

DUMMY := DO_FETCH(IND,GET$_DOC,DUMMY_PARSE_ITEMS,0,DUMMY_PROCOBJ_ERRORS);

DBMS_METADATA.CLOSE(HANDLE);

IF GET$_DOC IS NULL

THEN

IF PUBLIC_FUNC = 'GET_XML'

OR PUBLIC_FUNC = 'GET_SXML'

OR PUBLIC_FUNC = 'GET_SXML_DDL'

OR PUBLIC_FUNC = 'GET_DDL'

THEN

IF SCHEMA IS NOT NULL

THEN

SCHEMA_NAME := SCHEMA;

ELSE

SCHEMA_NAME := GET_CURRENT_USER;

END IF;

CUR_HANDLE := SAVE_HANDLE;

DBMS_SYS_ERROR.RAISE_SYSTEM_ERROR(OBJECT_NOT_FOUND_NUM,

OBJECT_NAME, OBJECT_TYPE, SCHEMA_NAME);

ELSE

CUR_HANDLE := SAVE_HANDLE;

DBMS_SYS_ERROR.RAISE_SYSTEM_ERROR(OBJECT_NOT_FOUND2_NUM, OBJECT_TYPE);

END IF;

END IF;

CUR_HANDLE := SAVE_HANDLE;

EXCEPTION WHEN OTHERS THEN

BEGIN

CUR_HANDLE := SAVE_HANDLE;

RAISE;

END;

END;

其中调用了do_fetch、do_fetch_local、do_fetch_direct等一系列函数,看的有些晕了,但可以说明一点,绝不是仅授权SELECT_CATALOG_ROLE角色中包含的某一个视图就可以执行dbms_metadata包,视图之间是有关联关系的。

总结:

1. 对于Definer和Invoker的含义要理解,Definer权限比较静态,Invoker权限则相对动态,通过procedures视图的AUTHID字段,可以了解对象,属于定义者还是调用者权限。

2. 两种执行DBMS_METADATA.GET_DDL权限相关的workaround,一种是授予SELECT_CATALOG_ROLE角色,一种是使用"AUTHID CURRENT_USER"定义存储过程,针对不同场景,可以选择不同的方案解决。
————————————————

                            版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
                        
原文链接:https://blog.csdn.net/bisal/article/details/78373137


网站公告

今日签到

点亮在社区的每一天
去签到