DELETE ANY TABLE TO TEST_USER_ROLE (except one table?) - oracle

Is there any possibility to revoke "delete" from one table over "any table" privilege?
My user_role has GRANT DELETE ANY TABLE TO TEST_USER_ROLE privilege.
I would like leave it except one table.
I tried with REVOKE DELETE on TEST_TABLE from TEST_USER_ROLE but I got "cannot REVOKE privileges you did not grant" error which is correct. Any advice?
Thanks!

Revoke DELETE ANY TABLE and grant DELETE, separately. A lot of grants? Certainly, but you don't have to do it manually - write query that'll create those statements for you. Or, do it in a PL/SQL procedure, in a loop (omit table you don't want to include).
Say if you need help in composing such a code.
Here's a PL/SQL procedure which does the job:
SQL> set serveroutput on
SQL>
SQL> declare
2 l_grantee varchar2(30) := 'MIKE';
3 l_str varchar2(200);
4 begin
5 for cur_r in (select table_name
6 from user_tables
7 where table_name <> 'DEPT'
8 and rownum <= 5
9 )
10 loop
11 l_str := 'grant delete on ' || cur_r.table_name || ' to ' || l_grantee;
12 dbms_output.put_line(l_str);
13 execute immediate l_str;
14 end loop;
15 end;
16 /
grant delete on DPT to MIKE
grant delete on TABLE_B to MIKE
grant delete on TABLE_A to MIKE
grant delete on EMPLOYEES to MIKE
grant delete on TABLE_NAME to MIKE
PL/SQL procedure successfully completed.
SQL>
. What does it do?
line #2, l_grantee: user I'm going to grant the privilege to
cursor FOR loop: just for example, I'll omit table dept and (to make a list shorter) grant delete for only 5 tables
line #11: compose the grant statement
line #12: display it (so that you'd have evidence of what you did)
line #13: grant!
If there are more owners included, you'd use ALL_TABLES or even DBA_TABLES instead of USER_TABLES (line #6) and include the OWNER column (because many users can have a table with the same name).

Related

Oracle Read only access to specific user

I have a schema and many users in it. For development I would like to give permission to the user to only read the tables and not having permission to manipulate database. Are there any commands to set access rights for a particular user to read only?
As Ed commented, you aren't allowed to do anything unless granted. For read-only users, you'd grant only the SELECT privilege on your tables. If you have only a few of them, do it manually. Otherwise, create a procedure which will do it for you. Here's an example.
These are my tables:
SQL> select * from tab;
TNAME TABTYPE CLUSTERID
------------------------------ ------- ----------
BONUS TABLE
DEPT TABLE
EMP TABLE
LINKS TABLE
SALGRADE TABLE
A procedure which loops through all tables in my schema and grants SELECT to user passed as a parameter:
SQL> create or replace procedure p_grant_ro(par_user in varchar2) is
2 l_str varchar2(200);
3 begin
4 for cur_r in (select table_name from user_tables
5 order by table_name
6 )
7 loop
8 l_str := 'grant select on ' || cur_r.table_name ||
9 ' to ' || dbms_assert.schema_name(par_user);
10 dbms_output.put_line(l_str);
11 execute immediate(l_str);
12 end loop;
13 end;
14 /
Procedure created.
Testing:
SQL> set serveroutput on;
SQL> exec p_grant_ro('MIKE');
grant select on BONUS to MIKE
grant select on DEPT to MIKE
grant select on EMP to MIKE
grant select on LINKS to MIKE
grant select on SALGRADE to MIKE
PL/SQL procedure successfully completed.
SQL>
If you wonder what's the purpose of dbms_assert function's call: preventing possible SQL injection. Function takes care that parameter is an existing schema name.
SQL> exec p_grant_ro('TECH_DELHI');
BEGIN p_grant_ro('TECH_DELHI'); END;
*
ERROR at line 1:
ORA-44001: invalid schema
ORA-06512: at "SYS.DBMS_ASSERT", line 266
ORA-06512: at "SCOTT.P_GRANT_RO", line 8
ORA-06512: at line 1
SQL>
You should be able to configure this in e.g. MYSQL Workbench.
I think there is also a way to link this to an Apex user.

Anonymous Code Block with DDL works but when created as procedure, get permissions error

In our Oracle 12c database, I am trying to create a package that will drop and create tables as needed.
For my first test, I wrote an anonymous code block and it works fine:
begin
begin
execute immediate 'Drop Table my_table';
exception
when OTHERS then
if sqlcode = -942 then
null;
end if;
end;
begin
execute immediate 'Create Table my_table as
WITH
sub_qy AS(
SELECT DISTINCT
...
)
SELECT * FROM sub_qy';
end;
execute immediate 'GRANT SELECT ON my_table to my_role';
I then tried to create a package and put the anonymous code block in a procedure but when executing the procedure I get the error:
ORA-01031: insufficient privileges
Here is the logic:
create package my_refresh_pkg as
procedure create_first_table;
end my_refresh_pkg;
create package body my_refresh_pkg as
procedure create_first_table is
begin
begin
execute immediate 'Drop Table my_table';
exception
when OTHERS then
if sqlcode = -942 then
null;
end if;
end;
begin
execute immediate 'Create Table my_table as
WITH
sub_qy AS(
SELECT DISTINCT
...
)
SELECT * FROM sub_qy';
end;
execute immediate 'GRANT SELECT ON my_table to my_role';
end create_first_table;
end my_refresh_pkg;
I created the package in my own schema, so I am not sure why I am getting this error. Any insight would be appreciated.
Suspicious part is this:
SELECT DISTINCT
...
as it smells like a know issue with privileges. If table(s) you used in the above statement's FROM clause don't belong to you but someone else, and you acquired select (or any other) privileges via role, well - it won't work in named PL/SQL procedures. Yes, that's a stored procedure, or a function, or - a package you wrote.
Solution is to grant those privileges directly to you, not via role.
[EDIT: running your code in my database - no errors]
SQL> create or replace package my_refresh_pkg as
2 procedure create_first_table;
3 end my_refresh_pkg;
4 /
Package created.
SQL> create or replace package body my_refresh_pkg as
2 procedure create_first_table is
3 begin
4 begin
5 execute immediate 'Drop Table my_table';
6 exception
7 when OTHERS then
8 if sqlcode = -942 then
9 null;
10 end if;
11 end;
12 begin
13 execute immediate 'Create Table my_table as
14 WITH
15 sub_qy AS(
16 SELECT DISTINCT
17 job from emp
18 )
19 SELECT * FROM sub_qy';
20 end;
21 execute immediate 'GRANT SELECT ON my_table to my_role';
22 end create_first_table;
23 end my_refresh_pkg;
24 /
Package body created.
SQL> create role my_role;
Role created.
Testing:
SQL> exec my_refresh_pkg.create_first_table;
PL/SQL procedure successfully completed.
SQL> select * From my_table;
JOB
---------
CLERK
SALESMAN
PRESIDENT
MANAGER
ANALYST
SQL>

How to run a query on different schemes at once

I want to run a query on different schemes to get data and export it. I use the following code
DECLARE
sql_statment VARCHAR2(2000);
BEGIN
FOR c IN (SELECT brchcode FROM brchs) LOOP
sql_statment := 'select distinct ''' || c.brchcode ||''', t.risuid from ' || c.brchcode ||
'.reg_individualacnt_detail t
where t.historytypecode = 60';
EXECUTE IMMEDIATE sql_statment;
END LOOP;
END;
where brchcode is the name of different schemes
I can't see any output. what can I do?
Code you wrote can't work as you have to return the result into something; it is PL/SQL and requires an INTO clause. As you chose to return two values (columns) and multiple rows, that can't be a scalar variable; you could pick ref cursor or a collection, for example.
Here's an example which shows one option.
I'll be using two schemas: SCOTT (current schema) and HR. Both will be having the DEPT table.
As Scott already has it, I'll create one in HR schema and grant access to Scott (otherwise, Scott won't even see it and the procedure (i.e. the function) will fail):
SQL> connect hr/hr
Connected.
SQL> create table dept (deptno number, dname varchar2(10), loc varchar2(10));
Table created.
SQL> insert into dept values (55, 'IT', 'Zagreb');
1 row created.
SQL> grant select on dept to scott;
Grant succeeded.
SQL> commit;
Commit complete.
Back to Scott, to create a table (which contains schema names I'll be selecting from) and a function. I chose to return REF CURSOR; you could return something else, if you want.
SQL> connect scott/tiger
Connected.
SQL> create table brchs (brchcode varchar2(10));
Table created.
SQL> insert into brchs (brchcode)
2 select 'scott' from dual union all
3 select 'hr' from dual;
2 rows created.
SQL> create or replace function f_br
2 return sys_refcursor
3 is
4 l_str varchar2(4000);
5 l_rc sys_refcursor;
6 begin
7 for cur_r in (select brchcode from brchs) loop
8 l_str := l_str ||
9 'union all
10 select ' || chr(39)|| cur_r.brchcode ||chr(39) || ', d.dname
11 from ' || cur_r.brchcode ||'.dept d
12 where d.deptno > 0';
13 end loop;
14
15 l_str := ltrim(l_str, 'union all');
16
17 open l_rc for l_str;
18 return l_rc;
19 end;
20 /
Function created.
SQL>
Finally, testing:
SQL> select f_br from dual;
F_BR
--------------------
CURSOR STATEMENT : 1
CURSOR STATEMENT : 1
'SCOT DNAME
----- --------------
scott ACCOUNTING
scott RESEARCH
scott SALES
scott OPERATIONS
hr IT
SQL>

How to grant trigger access to a table

I'm getting an error message for a trigger I've successfully created.
I've tried...
GRANT SELECT ON OTHER_TABLE TO [me];
call SYS.DBA_ASSIST.GRANT_OBJ_PERMS('dbo.MYTABLE','SELECT','dbo.OTHER_TABLE');`
Here is the code for the trigger...
CREATE OR REPLACE TRIGGER PREVENT_INVALID_ID
BEFORE INSERT OR UPDATE ON dbo.MYTABLE
FOR EACH ROW
DECLARE ROW_COUNT NUMBER;
BEGIN
SELECT COUNT(*) INTO ROW_COUNT
FROM [OTHER_TABLE] WHERE OTHER_TABLE_ID = :new.MYTABLE_ID;
IF ROW_COUNT = 0 THEN
RAISE_APPLICATION_ERROR(-20101, 'The ID provided is invalid.');
END IF;
END;`
The error message in user_errors says, "PL/SQL: ORA-00942: table or view does not exist"
Any idea how I can get the trigger to access OTHER_TABLE?
I understand that it is not possible to grant access/authorizations to triggers. But I'm unclear what code I need to run in order to allow the trigger to work.
Thanks in advance,
josh
If both MYTABLE and OTHER_TABLE belong to the same user, no privilege is required:
SQL> show user
USER is "SCOTT"
SQL> create table other_table (other_Table_id number);
Table created.
SQL> create table mytable (mytable_id number);
Table created.
SQL> create or replace trigger prevent_invalid_id
2 before insert or update on mytable
3 for each row
4 declare
5 row_count number;
6 begin
7 select count(*) into row_count
8 from other_table where other_table_id = :new.mytable_id;
9
10 if row_count = 0 then
11 raise_application_error(-20101, 'The ID provided is invalid');
12 end if;
13 end;
14 /
Trigger created.
SQL>
However, if they belong to different user, then OTHER_TABLE's owner has to grant SELECT privilege to me. Though, that's not enough - you have to either precede OTHER_TABLE with its owner name (e.g. other_user.other_table), or create a synonym in my own schema which points to other user's OTHER_TABLE. For example:
SQL> drop table other_table;
Table dropped.
SQL> connect mike/lion#xe
Connected.
SQL> create table other_table (other_Table_id number);
Table created.
SQL> grant select on other_table to scott;
Grant succeeded.
SQL> connect scott/tiger#xe
Connected.
SQL> create or replace trigger prevent_invalid_id
2 before insert or update on mytable
3 for each row
4 declare
5 row_count number;
6 begin
7 select count(*) into row_count
8 from MIKE.other_table where other_table_id = :new.mytable_id;
9
10 if row_count = 0 then
11 raise_application_error(-20101, 'The ID provided is invalid');
12 end if;
13 end;
14 /
Trigger created.
SQL>
Note line 8: MIKE.other_table.
Just to show what happens if you omit/remove owner's name:
SQL> l8
8* from MIKE.other_table where other_table_id = :new.mytable_id;
SQL> c/mike.//
8* from other_table where other_table_id = :new.mytable_id;
SQL> l
1 create or replace trigger prevent_invalid_id
2 before insert or update on mytable
3 for each row
4 declare
5 row_count number;
6 begin
7 select count(*) into row_count
8 from other_table where other_table_id = :new.mytable_id;
9
10 if row_count = 0 then
11 raise_application_error(-20101, 'The ID provided is invalid');
12 end if;
13* end;
SQL> /
Warning: Trigger created with compilation errors.
SQL> show err
Errors for TRIGGER PREVENT_INVALID_ID:
LINE/COL ERROR
-------- -----------------------------------------------------------------
4/3 PL/SQL: SQL Statement ignored
5/8 PL/SQL: ORA-00942: table or view does not exist
SQL>
See? ORA-00942.
Figured it out: GRANT SELECT ON OTHER_TABLE TO dbo
What threw me off is the use the words 'table owner' when I was looking for instructions on how to do this. I thought I was the table owner. Rather, it's the schema (dbo) that needs the privileges.

Passing DataTable to Oracle Procedure

I want to pass DataTable to Oracle procedure which creates a Temporary table.
Even My procedure needs changes to accept datatable as parameter.
I want datatable in place of existing_table.
Below is the Procedure:
CREATE OR REPLACE procedure temptable
is
begin
EXECUTE IMMEDIATE'CREATE GLOBAL TEMPORARY TABLE TEMP_TABLE
ON COMMIT PRESERVE ROWS
AS
select * from existing_table';
End;
How do I work through this. Please help!
Here's an example of how you might do that:
SQL> create or replace procedure temptable (par_table_name in varchar2)
2 is
3 l_cnt number;
4 l_str varchar2(200);
5 begin
6 -- if TEMP_TABLE already exists, drop it
7 select count(*)
8 into l_cnt
9 from user_tables
10 where table_name = 'TEMP_TABLE';
11
12 if l_cnt > 0 then
13 execute immediate 'drop table temp_table';
14 end if;
15
16 -- create a new TEMP_TABLE
17 l_str := 'create global temporary table temp_table on commit preserve rows ' ||
18 'as select * from ' || par_table_name;
19 execute immediate (l_str);
20 end;
21 /
Procedure created.
SQL> exec temptable('dept');
PL/SQL procedure successfully completed.
SQL> select * from temp_table;
DEPTNO DNAME LOC
---------- -------------------- --------------------
10 ACCOUNTING NEW YORK
20 RESEARCH DALLAS
30 SALES CHICAGO
40 OPERATIONS BOSTON
SQL>
However, in Oracle, you don't really create/drop tables dynamically - you create them once and use them many times. For a global temporary table, you'd create it once and populate as necessary in different sessions.
There might be requirements as yours (i.e. to use the same table name for different data sources), so - if that's your case, see if the above code helps.

Resources