在Oracle中,系统权限、对象权限以及他们的集合角色权限,是一个相对复杂的安全体系。在前一篇《使用Role权限体系》(http://space.itpub.net/17203031/viewspace-691917)中,已经进行初步介绍。存储过程作为Schema对象下的程序单元,在进行Role处理时有一些特殊之处。今天我们继续介绍存储过程的权限体系:所有者权限和调用者权限。
在存储过程中,我们常常面对这样一个场景:用户A下有一个存储过程(或者函数体、包体)P,中间引用了对象X。在编译存储过程时,是要求用户A有对象X的权限的,如果没有,则系统报编译错误。当成功进行编译之后,用户A将执行execute存储过程P的权限赋给了用户B。但是用户B这时候不一定拥有X的使用权限,此时B能够成功执行存储过程P呢?我们通过一个简单实验,来证实一下。
实验环境准备
先准备用户test,除了具备基本的connect和resource角色权限之外,处于实验目的,赋给select any dictionary的系统权限给test。
SQL> conn sys/sys@otstest as sysdba; Connected to Oracle Database 10g EnterpriseEdition Release10.2.0.1.0 Connected as SYS SQL> SQL> create user test 2 identified by test; User created SQL> grant resource to test; Grant succeeded SQL> grant connect to test; Grant succeeded SQL> grant select any dictionary to test; Grant succeeded
Select any dictionary的系统权限意味着用户可以访问数据字典视图层面的视图对象数据,而且不会因为存储过程对角色权限的剥离效应而受到影响。
SQL> conn test/test @otstest; Connected to Oracle Database10gEnterpriseEdition Release10.2.0.1.0 Connected as test SQL> select count(*) fromdba_objects; COUNT(*) -------------- 53305 SQL> create or replace procedure p_test_nc 2 is 3 i number; 4 begin 5 select count(*) 6 into i 7 from dba_objects; 8 9 dbms_output.put_line(to_char(i)); 10 end; 11 / Procedure created SQL> set serveroutput on size 1000; SQL> exec p_test_nc; 53306 PL/SQL procedure successfully completed
可见,授予select any dictionary的用户test可以对dba_objects视图进行访问操作。同时,存储过程p_test_nc也可以顺利的编译执行。
实验一 —— 所有者权限
准备好的实验环境,我们准备进行第一个项目实验。建立一个新用户ts,只有执行test用户下p_test_nc存储过程权限,但是没有访问dba_objects视图权限,看实际效果。
SQL> conn sys/sys@otstest as sysdba; Connected to Oracle Database10gEnterpriseEdition Release10.2.0.1.0 Connected as SYS SQL> create user ts 2 identified by ts; User created SQL> grant resource to ts; Grant succeeded SQL> grant connect to ts; Grant succeeded //用户ts只具有基本的连接和创建对象权限。 SQL> conn test/test@otstest; Connected to Oracle Database10gEnterpriseEdition Release10.2.0.1.0 Connected as test //将p_test_nc执行权限授权给ts SQL> grant execute on p_test_nc to ts; Grant succeeded
之后,我们检查ts用户下,p_test_nc的执行情况。
SQL> conn ts/ts@otstest; Connected to Oracle Database10gEnterpriseEdition Release10.2.0.1.0 Connected as ts SQL> select count(*) from dba_objects; select count(*) from dba_objects //ts用户没有dba_objects权限,显示访问必然没有结果; ORA-00942:表或视图不存在 SQL> set serveroutput on size 1000; SQL> exec test.p_test_nc; 53306 PL/SQL procedure successfully completed
结果显而易见,ts虽然没有访问dba_objects权限,但是因为拥有执行p_test_nc的权限,在执行p_test_nc的时候,也是可以在方法中访问到dba_objects。显然,此时ts在p_test_nc上借用了test用户对于dba_objects用户的权限,也就是对象所有者权限。
进一步证明我们的实验,可以进行些变化。
--当所有者权限失去时,即使调用者拥有权限也是无用的。
SQL> conn sys/sys@otstest as sysdba; Connected to Oracle Database10gEnterpriseEdition Release10.2.0.1.0 Connected as SYS //回收了test用户上的select any dictionary权限,此时test对dba_objects对象权限消失; SQL> revoke select any dictionary from test; Revoke succeeded //赋予ts用户select any dictionary权限,这样ts就能访问dba_objects了; SQL> grant select any dictionary to ts; Grant succeeded SQL> conn ts/ts@otstest; Connected to Oracle Database10gEnterpriseEdition Release10.2.0.1.0 Connected as ts SQL> set serveroutput on size 1000; SQL> select count(*) from dba_objects;//可以访问对象 COUNT(*) ---------- 53306 SQL> exec test.p_test_nc; begin test.p_test_nc; end; ORA-06550:第1行,第12列: PLS-00905:对象TEST.P_TEST_NC无效 ORA-06550:第1行,第7列: PL/SQL: Statement ignored
我们看到了一些“诡异现象”,ts用户拥有dba_objects对象访问权限,同时也有执行p_test_nc的权限,但是执行的时候却报错,认为对象无效。
唯一的原因就是因为test用户失去了dba_objects对象的权限。而ts在调用p_test_nc时使用的是dba_objects的权限。
上面,我们就介绍了Oracle在存储过程中使用的权限配置“所有者权限”。简单的说,当执行一个程序体(存储过程、函数和包等)的时候,方法体内部使用的权限体系为当前该程序体所有者的权限体系,而与调用方法的用户无关。存储过程p_test_nc此时,无论是谁在执行,权限体系都是该存储过程的所有者test的权限。
所有者权限是Oracle使用的默认权限选择方式,在使用的时候很方便。使用者只要拥有简单的对象执行权限就可以了,无需顾及自己是否有权限访问方法中使用的对象。
有了前面对所有者权限的介绍,调用者权限的含义就相对容易理解了。调用者权限体系就是执行方法体的时候,使用的权限按照调用者权限体系来判断。一个方法的执行,调用者除了要拥有方法的执行权限,还要拥有方法中使用对象的权限才可以。
实验二——调用者权限
我们继续实验一的环境。注意,此时test用户没有select any dictionary权限,而ts拥有。
SQL> conn test/test@otstest; Connected to Oracle Database10gEnterpriseEdition Release10.2.0.1.0 Connected as test SQL> SQL> create or replace procedure p_test_nc 2 is 3 i number; 4 begin 5 select count(*) 6 into i 7 from dba_objects; 8 9 dbms_output.put_line(to_char(i)); 10 end; 11 / Warning: Procedure created with compilation errors
检查报错信息,还是因为没有dba_objects的权限。
SQL> select * from user_errors; NAME TYPE SEQUENCE LINE POSITION TEXT ------------------------------- ---------- ---------- -------------------------------------- P_TEST_NC PROCEDURE 1 7 8 PL/SQL: ORA-00942:表或视图不存在 P_TEST_NC PROCEDURE 2 5 3 PL/SQL: SQL Statement ignored
此时,我们如果在方法定义上加入authid current_user关键字,就可以将存储过程变化为调用者权限。
SQL> create or replace procedure p_test_nc 2 authid current_user 3 is 4 i number; 5 begin 6 select count(*) 7 into i 8 from dba_objects; 9 10 dbms_output.put_line(to_char(i)); 11 end; 12 / Warning: Procedure created with compilation errors
显然,还在因为dba_objects没有权限而报错,毕竟不管是什么体系,test目前是没有对象权限的。不过,为了实验成功,还是要让test能顺利编译过p_test_nc方法。
SQL> conn sys/acca@otstest as sysdba; Connected to Oracle Database10gEnterpriseEdition Release10.2.0.1.0 Connected as SYS SQL> grant select any dictionary to test; Grant succeeded
切换回ts用户,注意此时他是拥有select any dictionary权限的。
SQL> conn ts/ts@otstest; Connected to Oracle Database10gEnterpriseEdition Release10.2.0.1.0 Connected as ts SQL> exec test.p_test_nc; PL/SQL procedure successfully completed SQL> set serveroutput on size 1000; SQL> exec test.p_test_nc; 53306 PL/SQL procedure successfully completed SQL> select count(*) from dba_objects; COUNT(*) ---------------------- 53306
此时,执行顺利成功。因为此时test和ts都拥有select any dictionary权限,所以即使在调用者权限下,也是会成功的。那么,如果我们收回ts上的权限,会如何呢?
SQL> conn sys/acca@otstest as sysdba; Connected to Oracle Database10gEnterpriseEdition Release10.2.0.1.0 Connected as SYS SQL>revoke select any dictionary from ts; //权限回收 Revoke succeeded SQL> conn ts/ts@otstest; Connected to Oracle Database10gEnterpriseEdition Release10.2.0.1.0 Connected as ts SQL> select count(*) from otstest;
'select count(*) from otstest' ORA-00942:表或视图不存在
SQL> exec test.p_test_nc;
'begin test.p_test_nc; end;' ORA-00942:表或视图不存在 ORA-06512:在"TEST.P_TEST_NC", line 6 ORA-06512:在line 1
此时,就看出调用者权限的差异了。Test始终有dba_objects的权限,而ts在之后被取消了select any dictionary的权限。如果是所有者权限,ts调用p_test_nc是没有问题的。但是此时报错,说明此处使用的ts调用者权限。
调用者权限与role权限剥离现象
结合之前介绍的role权限剥离的现象,在调用者权限下,这种现象还有吗?我们下面的实验来证实。
继续上面实验的环境,用户ts已经失去了对dba_objects能访问的权限。Test用户拥有select any dictionary系统权限。方法p_test_nc调节为调用者权限。
--调用者权限与role剥离情况 SQL> conn sys/acca@otstest as sysdba; Connected to Oracle Database10gEnterpriseEdition Release10.2.0.1.0 Connected as SYS SQL> grant select_catalog_role to ts; Grant succeeded
对对象ts赋予select_catalog_role角色,该角色是能够访问dba_objects的,但是在所有者权限体系下,角色权限会被剔除。
SQL> conn ts/ts@otstest; Connected to Oracle Database10gEnterpriseEdition Release10.2.0.1.0 Connected as ts SQL> select count(*) from dba_objects; COUNT(*) -------------------- 53306 SQL> set serveroutput on size 1000; SQL> exec test.p_test_nc; 53306 PL/SQL procedure successfully completed
注意:我们发现,调用者权限p_test_nc方法应该使用调用者ts的权限。此时ts只有一个select_catalog_role角色权限与dba_objects有关联。可以猜想:在调用者方式下,非所有者调用时不会发生角色权限被剔除的现象。
SQL> conn sys/acca@otstest as sysdba; Connected to Oracle Database10gEnterpriseEdition Release10.2.0.1.0 Connected as SYS SQL> revoke select any dictionary from test; Revoke succeeded SQL> grant select_catalog_role to test; Grant succeeded SQL> conn test/test@otstest; Connected to Oracle Database10gEnterpriseEdition Release10.2.0.1.0 Connected as test SQL> select count(*) from dba_objects; COUNT(*) -------------------------- 53306 SQL> set serveroutput on size 10000; SQL> exec p_test_nc;
'begin p_test_nc; end;' ORA-06550:第1行,第7列: PLS-00905:对象TEST.P_TEST_NC无效 ORA-06550:第1行,第7列: PL/SQL: Statement ignored
但是对于p_test_nc方法的所有者test来说,调用权限并没有解决role权限剔除的问题。依然存在存储过程角色剔除问题。
SQL> select * from dba_role_privs where grantee in ('TS','TEST'); GRANTEE GRANTED_ROLE ------------------------------ ----------------------- TEST RESOURCE TS CONNECT TEST SELECT_CATALOG_ROLE TS SELECT_CATALOG_ROLE TEST CONNECT TS RESOURCE 6 rows selected
通过上述的实验,我们可以得到如下的经验:
- 所有者权限和调用者权限是Oracle对存储过程等代码结构提供的独特权限组织模式。二者互为补充,应对不同的需求状况;
- 即使在调用者模式下,可以一定程度的避免role剔除现象,我们还是不建议使用role权限管理用户;
- 调用者权限体系在Oracle预定义方法、过程中大量采用,在进行开发的时候,如果遇到类似的问题和Bug,可以从调用者权限的角度去寻求解决方法;