Oracle select privilege on DBA_ROLES - sql

I am connected into Oracle 10g database with user "user", when i run a select on DBA_ROLES it displays the result without any problem, but when the select is inside a procedure it returns the error ORA-00942: table or view does not exist
is the user missing some privileges? why is it possible to execute a SELECT but not to include it inside a procedure?
SQL> SELECT COUNT(*) FROM DBA_ROLES;
COUNT(*)
----------
18
SQL> CREATE OR REPLACE PROCEDURE tst_role IS
v VARCHAR2(100);
BEGIN
v := '';
FOR rec IN (SELECT ROLE FROM DBA_ROLES) LOOP
v := rec.role;
DBMS_OUTPUT.put_line(v);
END LOOP;
END;
/
Warning: Procedure created with compilation errors.
SQL> show errors
Errors for PROCEDURE TST_ROLE:
LINE/COL ERROR
-------- -----------------------------------------------------------------
5/13 PL/SQL: SQL Statement ignored
5/30 PL/SQL: ORA-00942: table or view does not exist
6/3 PL/SQL: Statement ignored
6/8 PLS-00364: loop index variable 'REC' use is invalid

within a definer's rights stored procedure, you don't have access to privileges that are granted via a role. This most likely means that whatever Oracle user you are using has been granted access to DBA_ROLES via a role rather than via a direct grant. Most likely, you can ask your DBA to grant your account the SELECT ANY DICTIONARY privilege
GRANT SELECT ANY DICTIONARY
TO your_oracle_user
You can verify that the problem is, indeed, that the privilege is granted via a role by disabling roles in your session and verifying that you get an error. If you
SQL> set role none;
SQL> SELECT COUNT(*) FROM DBA_ROLES;
I'll wager that you get the same ORA-00942 error.

Related

Unable to create table from within package

I am attempting to create a package in which I drop and create a table using a CTAS query. This table needs to be refreshed frequently and columns are add/removed all the time from the underlying data. Since the structure of the table is constantly changing, it would be quite cumbersome to update merge/update queries each refresh to account for the new/missing columns. Currently, I have external scripts that do simple drops and creates but I need to centralize this in the database; therefore I am attempting to create a package to do it; however, I am having trouble with privileges.
As proof of concept, the following code works when ran as an anonymous block:
create table my_test_table as select * from dual; --create test table
declare
v_count int;
begin
select count(*) into v_count from all_tab_columns where table_name = upper('my_test_table');
if v_count >= 1 then
execute immediate 'drop table my_test_table';
end if;
execute immediate q'[
create table my_test_table as
select * from dual
]';
end;
select * from my_test_table; -- shows expected results
But when creating a package to do the same thing;
CREATE OR REPLACE PACKAGE test_pkg AS
PROCEDURE test_procedure;
END test_pkg;
CREATE OR REPLACE package body test_pkg as
procedure test_procedure
is
v_count int;
begin
select count(*) into v_count from all_tab_columns where table_name = upper('my_test_table');
if v_count >= 1 then
execute immediate 'drop table my_test_table';
end if;
execute immediate q'[
create table my_test_table as
select * from dual
]';
end test_procedure;
end test_pkg;
/
and testing with the following code:
create table my_test_table as select * from dual; --make sure table exists
execute TEST_PKG.TEST_PROCEDURE; --results in errors
select * from my_test_table; --table does not exist; therefore, DROP statement works but not CREATE
I get the following errors (in regards to executing TEST_PKG.TEST_PROCEDURE):
ORA-01031: insufficient privileges
ORA-06512: at test_pkg, line 15
When testing for the existence of the test table after executing the package, I can see that it no longer exists. This means the DROP statement is working but the CREATE TABLE statement is resulting in the insufficient privileges error.
Any and all insight into what privileges I need to create the table from within the package would be immensely helpful.
Create a table in procedure is only alowed when you have "Create table" or "create any table" privilege but granted directly to user (granted by role is not working).
https://docs.oracle.com/cd/B19306_01/network.102/b14266/authoriz.htm#i1008334
PL/SQL Blocks and Roles
The use of roles in a PL/SQL block depends on whether it is an
anonymous block or a named block (stored procedure, function, or
trigger), and whether it executes with definer's rights or invoker's
rights.
Named Blocks with Definer's Rights
All roles are disabled in any named PL/SQL block (stored procedure,
function, or trigger) that executes with definer's rights. Roles are
not used for privilege checking and you cannot set roles within a
definer's rights procedure.
To check system privileges granted directly to your user (not by role/roles), you can run this query from your user:
SELECT * FROM USER_SYS_PRIVS;
The package you've created, in the absence of a AUTHID CURRENT_USER clause is a definer's rights package. It can only do things that are allowed by privileges granted directly to the definer of the package. "Directly" is the key point here -- privileges granted through enabled roles are not honored during the package execution.
You've probably got the RESOURCE or similar role enabled for your user, which would explain why you can create the table during testing but not via your package procedure.
Try granting the CREATE TABLE and UNLIMITED TABLESPACE system privileges directly to your user and then recreate the package. (If that works, replace UNLIMITED TABLESPACE with quotas on the appropriate tablespace(s) in your database).

Why does my query return no records inside a program (PL/SQL), but does when ran manually in SQL?

Hi everyone and thank you for taking the time to help me.
I have the following query:
SELECT owner, object_name
FROM all_objects
WHERE owner IN ('EDI')
ORDER BY object_type, object_name;
As you can see in the screenshot it returns some values.
When I call the query from inside a program, it is not returning any values (see second screenshot).
Test code to illustrate this is:
CREATE OR REPLACE PROCEDURE my_test
AS
BEGIN
DBMS_OUTPUT.put_line('Pre-Loop');
FOR indx IN (SELECT owner, object_name
FROM all_objects
WHERE owner IN ('EDI')
ORDER BY object_type, object_name)
LOOP
DBMS_OUTPUT.put_line('Object: ' || indx.owner || '.' || indx.object_name);
END LOOP;
DBMS_OUTPUT.put_line('Post-Loop');
END;
/
BEGIN
my_test();
END;
/
The EDI schema is brand new, so I suspect this is a grants/privileges issue, but I can't seem to find what I may be missing in order for this to work. I have tried running this as both the EDI user and SYS.
EDIT after getting an answer:
I mentioned in a comment about finding an alternative to the official answer to this question and wanted to make sure it was shared for anyone reading this later so they can weigh the decision the same.
Applying grants like EXECUTE ANY PROCEDURE or SELECT ANY TABLE to the user that is expected to run the code will work, but I am sure there are reasons not to give such wide open grants.
Your stored procedure is a definer's rights stored procedure. That means that it doesn't have access to privileges that are granted via roles only those privileges that are granted directly to the owner of the procedure. Ad hoc SQL, on the other hand, runs with the privileges of whatever roles are enabled for the current session in addition to the user's direct grants. Most likely, the owner of the procedure has access to the tables in question via roles rather than via direct grants.
You can test this by running
set role none;
and then running the ad hoc SQL statement. If my wager is right, the ad hoc SQL will now return 0 rows since you've disabled all the roles for the session.
Depending on what you are going to do with the procedure, you may be able to solve the problem by turning it into an invoker's rights stored procedure.
CREATE OR REPLACE PROCEDURE my_test
AUTHID CURRENT_USER
AS
That will cause the procedure to run with the privileges of the invoker's session (including privileges granted through roles) rather than those of the definer. Assuming that all the users you want to call the procedure will have access to the EDI tables, that should be sufficient.
This is what you have now - no result at all:
SQL> CREATE OR REPLACE PROCEDURE my_test
2 AS
3 BEGIN
4 DBMS_OUTPUT.put_line('Pre-Loop');
5
6 FOR indx IN (SELECT owner, object_name
7 FROM all_objects
8 WHERE owner IN ('SCOTT')
9 AND rownum < 5 -- you don't have that
10 ORDER BY object_type, object_name)
11 LOOP
12 DBMS_OUTPUT.put_line('Object: ' || indx.owner || '.' || indx.object_name);
13 END LOOP;
14
15 DBMS_OUTPUT.put_line('Post-Loop');
16 END;
17 /
Procedure created.
SQL> BEGIN
2 my_test();
3 END;
4 /
PL/SQL procedure successfully completed.
But, if you enable serveroutput, here it is!
SQL> set serveroutput on --> this!
SQL>
SQL> BEGIN
2 my_test();
3 END;
4 /
Pre-Loop
Object: SCOTT.BONUS
Object: SCOTT.DEPT
Object: SCOTT.EMP
Object: SCOTT.SALGRADE
Post-Loop
PL/SQL procedure successfully completed.
SQL>

PL/SQL: ORA-00942: table or view does not exist. Inside Function

I can view the result when I write the query outside the block but writing it inside function shows an error.
SELECT max(logid) FROM hawk.log_patch_execution_result;
Output:
461
Function:
CREATE OR REPLACE FUNCTION latest_log_id RETURN NUMBER IS
v_log_id NUMBER;
v_sql VARCHAR2(4000);
BEGIN
SELECT max(logid) INTO v_log_id FROM hawk.log_patch_execution_result;
RETURN v_log_id;
END latest_log_id;
/
Output:
Create function, executed in 16 ms
PL/SQL: ORA-00942: table or view does not exist
PL/SQL: SQL Statement ignored
Total execution time 16 ms
The table you're selecting from belongs to user hawk.
User, which uses the function, should be granted the SELECT privilege (on that table). I presume that you did that (as SELECT itself works OK, but not as part of a function) - via some role. However, that won't work - you should grant the privilege directly to user, not via role.
It seems an issue in the schema/user you are working at.
Try to compile it in such way adding the schema name in the function.
CREATE OR REPLACE FUNCTION hawk.latest_log_id RETURN NUMBER IS
v_log_id NUMBER;
v_sql VARCHAR2(4000);
BEGIN
SELECT max(logid) INTO v_log_id FROM hawk.log_patch_execution_result;
RETURN v_log_id;
END latest_log_id;
/

Revoking privileges for a Table in Oracle

I am trying to revoke the SELECT privilege for Employee table using the Query
REVOKE SELECT ON ODS_INSTALL.employee FROM ODS_INSTALL;
Currently i am connected in as SYSTEM user, but after getting connected to ODS_INSTALL and firing the query as:
select * from employee;
i am getting the output, but it should give a error regarding insufficient privilege.
What may be the issue?
As in Oracle the schema is the user, as far as I can tell, you cannot REVOKE a privilege on a table from its owner.
Strange you didn't have an error, as on my test system (Oracle 11g):
(as system)
SQL> revoke select on sylvain.n from sylvain
*
ERROR at line 1:
ORA-01927: cannot REVOKE privileges you did not grant
(as sylvain)
SQL> revoke select on n from sylvain;
revoke select on n from sylvain
*
ERROR at line 1:
ORA-01749: you may not GRANT/REVOKE privileges to/from yourself
(issuing a GRANT before does not change anything)
Basically, object privileges are designed to grant/revoke access to your objects from other users. Maybe you should move your table to a dedicated schema, and grant insert/update/delete to ODS_INSTALL in that schema?
If you really really need to restrict access to its own table to an user, the only way I can see is to use Virtual Private Database. Broadly speaking, you will write a function that dynamically generate an extra WHERE clause that Oracle will automagically append to every user query.
CREATE OR REPLACE FUNCTION auth_orders(
schema_var IN VARCHAR2,
table_var IN VARCHAR2
)
RETURN VARCHAR2
IS
return_val VARCHAR2 (400);
BEGIN
RETURN '1=0'; -- always false: "hide" all rows
END auth_orders;
/
And install it using:
BEGIN
DBMS_RLS.ADD_POLICY (
object_schema => 'ODS_INSTALL',
object_name => 'employee',
policy_name => 'orders_policy',
function_schema => 'sys',
policy_function => 'auth_orders',
statement_types => 'select'
);
END;
Take a look at Is to possible to forbid access to tables in own schema - Oracle? [dba se] for some details.

How to access Oracle system tables from inside of a PL/SQL function or procedure?

I am trying to access information from an Oracle meta-data table from within a function. For example (purposefully simplified):
CREATE OR REPLACE PROCEDURE MyProcedure
IS
users_datafile_path VARCHAR2(100);
BEGIN
SELECT file_name INTO users_datafile_path
FROM dba_data_files
WHERE tablespace_name='USERS'
AND rownum=1;
END MyProcedure;
/
When I try to execute this command in an sqlplus process, I get the following errors:
LINE/COL ERROR
-------- -----------------------------------------------------------------
5/5 PL/SQL: SQL Statement ignored
6/12 PL/SQL: ORA-00942: table or view does not exist
I know the user has access to the table, because when I execute the following command from the same sqlplus process, it displays the expected information:
SELECT file_name
FROM dba_data_files
WHERE tablespace_name='USERS'
AND rownum=1;
Which results in:
FILE_NAME
--------------------------------------------------------------------------------
/usr/lib/oracle/xe/oradata/XE/users.dbf
Is there something I need to do differently?
Make sure that SELECT is not only grantet through a role, but that the user actually has the grant. Grants by roles do not apply to packages. See this post at asktom.oracle.com.
Also, try sys.dba_data_files instead of dba_data_files.
Specify WITH GRANT OPTION to enable the grantee to grant the object privileges to other users and roles.
GRANT SELECT ON dba_data_files TO YOUR_USER WITH GRANT OPTION;
Have you tried prefixing the table name with sys. as in
FROM sys.dba_data_files
For selecting data from dba_data_files, grant select from SYS user to USER. Example:
GRANT SELECT ON dba_data_files TO YOUR_USER;
After that recompile your Procedure.