Grant Select, Insert, Update to a Tablespace - oracle

I've got a lot of tables in a tablespace, nearly 100. I have to grant Select, Insert, Update privileges on all those tables to a user. Is it possible? When I write:
GRANT USE OF TABLESPACE MYTABLESPACE TO USERNAME
I get oracle error "invalid or missing privilege"

USE OF TABLESPACE is not a documented option, where did you find that?
You can do this to allow a user to create objects in a tablespace:
alter user username quota [amount] on mytablespace;
To grant select, insert, update and delete on objects you have to run a separate grant command for each table:
grant select, insert, update, delete on mytable1 to username;
....

Use the data dictionary view dba_tables (resp. all_tables, if you cannot access dba_tables):
declare
l_SQL varchar2(4000);
begin
for cur in (
select * from dba_tables where tablespace_name = 'mytablespace')
loop
l_sql := 'grant select, insert, update on ' || cur.owner || '.' || cur.table_name || ' to myuser';
--dbms_output.put_line(l_SQL || ';');
execute immediate l_SQL;
end loop;
end;
If you just want to generate a script, comment out the execute immediate and un-comment the dbms_output.

Related

How to delete sequences and procedures during logoff trigger?

Could you please help me in a unique situation I am in. I am receiving "ORA-30511: invalid DDL operation in system triggers" when dropping sequences and procedures during logoff trigger.
I need to delete tables, sequences and procedures of users before logoff event happens. I am writing the table details in DB_OBJECTS table upon create using a separate trigger. Below is my logoff trigger - could you please help me where I am doing wrong. Dropping tables is working fine in the below code. Only Dropping sequences and procedures is giving me "ORA-30511: invalid DDL operation in system triggers" error.
CREATE OR REPLACE TRIGGER DELETE_BEFORE_LOGOFF
BEFORE LOGOFF ON DATABASE
DECLARE
USER_ID NUMBER := SYS_CONTEXT('USERENV', 'SESSIONID');
BEGIN
FOR O IN (SELECT USER, OBJECT_NAME, OBJECT_TYPE
FROM DB_OBJECTS WHERE SID = USER_ID
AND USERNAME = USER AND SYSDATE > CREATED_DTTM) LOOP
IF O.OBJECT_TYPE = 'TABLE' THEN
EXECUTE IMMEDIATE 'DROP TABLE ' || O.USER || '.' || O.OBJECT_NAME || ' CASCADE CONSTRAINTS';
ELSIF O.OBJECT_TYPE = 'SEQUENCE' THEN
EXECUTE IMMEDIATE 'DROP SEQUENCE ' || O.USER || '.' || O.OBJECT_NAME;
ELSIF O.OBJECT_TYPE = 'PROCEDURE' THEN
EXECUTE IMMEDIATE 'DROP PROCEDURE ' || O.USER || '.' || O.OBJECT_NAME;
END IF;
END LOOP;
EXCEPTION WHEN NO_DATA_FOUND THEN NULL;
END;
/
That's a simple one.
Error code: ORA-30511
Description: invalid DDL operation in system triggers
Cause: An attempt was made to perform an invalid DDL operation in a system trigger. Most DDL operations currently are not supported in system triggers. The only currently supported DDL operations are table operations and ALTER/COMPILE operations.
Action: Remove invalid DDL operations in system triggers.
That's why only
Dropping tables is working fine
succeeded.
Therefore, you can't do that using trigger.
You asked (in a comment) how to drop these objects, then. Manually, as far as I can tell. Though, that's quite unusual - what if someone accidentally logs off? You'd drop everything they created. If you use that schema for educational purposes (for example, every student gets their own schema), then you could create a "clean-up" script you'd run once class is over. Something like this:
SET SERVEROUTPUT ON;
DECLARE
l_user VARCHAR2 (30) := 'SCOTT';
l_str VARCHAR2 (200);
BEGIN
IF USER = l_user
THEN
FOR cur_r IN (SELECT object_name, object_type
FROM user_objects
WHERE object_name NOT IN ('EMP',
'DEPT',
'BONUS',
'SALGRADE'))
LOOP
BEGIN
l_str :=
'drop '
|| cur_r.object_type
|| ' "'
|| cur_r.object_name
|| '"';
DBMS_OUTPUT.put_line (l_str);
EXECUTE IMMEDIATE l_str;
EXCEPTION
WHEN OTHERS
THEN
NULL;
END;
END LOOP;
END IF;
END;
/
PURGE RECYCLEBIN;
It is far from being perfect; I use it to clean up my Scott schema I use to answer questions on various sites so - once it becomes a mess, I run that PL/SQL code several times (because of possible foreign key constraint).
Other option is to keep a create user script(s) (along with all grant statements) and - once class is over - drop existing user and simply recreate it.
Or, if that user contains some pre-built tables, keep export file (I mean, result of data pump export) and import it after the user is dropped.
There are various options - I don't know whether I managed to guess correctly, but now you have something to think about.

PL/SQL While/For Loop

I'm a DBA, just trying to write a piece of code to capture user privileges in Oracle and write to a table. The below code works ok for one user, but if the ELSE part has multiple users I get the error: "ORA-01422: exact fetch returns more than requested number of rows". Makes sense, I realise I need a for/while loop to handle multiple rows, can someone help me with that?
'''
declare
altersystem varchar2(550);
altersystemconcat varchar2(550);
begin
select grantee
into altersystem
from dba_sys_privs
where privilege = 'ALTER SYSTEM'
and grantee not in ('SYS', 'SYSTEM');
if altersystem = 'No rows selected'
then
insert into catch
values
('900'
,'No custom users with the Alter System privilege.');
else
select concat('The following user/role has the Alter System privilege, revoke if not required: '
,altersystem)
into altersystemconcat
from dual;
insert into catch
values
('100'
,altersystemconcat);
end if;
end;
/
'''
An select ... INTO ... requires exactly one row to be returned, otherwise you get an exception. Usually you have to loop over a cursor, but you can do it even in a single command like this:
BEGIN
INSERT INTO catch
SELECT 900, 'The following user/role has the Alter System privilege, revoke if not required: '||grantee
FROM dba_sys_privs
WHERE PRIVILEGE = 'ALTER SYSTEM'
AND grantee NOT IN ('SYS', 'SYSTEM');
IF SQL%ROWCOUNT = 0 THEN
INSERT INTO catch VALUES (100, 'No custom users with the Alter System privilege.');
END IF;
END;
Umm ... no, ELSE's SELECT doesn't return too-many-rows. It selects from DUAL which has only 1 row so ...
I suspect it is the first statement, select grantee into altersystem. If there's more than one user that satisfies those conditions, you can't put them all into the variable which is declared as varchar2. Therefore, yes - a loop might help, it is simple enough.
But, why? Why don't you simply insert all those offenders using single insert? Something like this:
INSERT INTO catch
SELECT '000', grantee
FROM dba_sys_privs
WHERE privilege = 'ALTER SYSTEM'
AND grantee NOT IN ('SYS', 'SYSTEM');
This can be done in a single SQL statement - no need for PL/SQL (unless you need it as a stored procedure):
INSERT INTO catch (col1, col2) -- change col1 and col2 to the correct column_names in catch that you're inserting into
WITH no_users AS (SELECT 100 no_user_present_id, 'No custom users with the Alter System privilege.' no_user_text FROM dual),
users_there AS (SELECT 900 user_present_id, 'The following user/role has the Alter System privilege, revoke if not required: '||grantee user_there_text
FROM dba_sys_privs
WHERE PRIVILEGE = 'ALTER SYSTEM'
AND grantee NOT IN ('SYS', 'SYSTEM'))
SELECT COALESCE(ut.user_present_id, nu.no_user_present_id) ID,
COALESCE(ut.user_there_text, nu.no_user_text) text
FROM no_users nu
LEFT OUTER JOIN users_there ut ON 1 = 1;
This works by using an outer join so that the row for when there's no user is always returned. If there are no users present, the row that is returned is the default row. If there are users that have the specified grant, you will get the rows for those users returned.
Using the listagg function you can concatenate the resulting string.
Commented out the insert statements as I don't have the catch table. Printing to output instead.
Use an exception handler in case no rows found.
declare
altersystem varchar2(550);
altersystemconcat varchar2(550);
begin
select listagg(grantee, ', ') within group(order by grantee) "Grantee"
into altersystem
from dba_sys_privs
where privilege = 'ALTER SYSTEM'
and grantee not in ('SYS', 'SYSTEM');
dbms_output.put_line('The following user/role has the Alter System privilege, revoke if not required: ' || altersystem);
/*
insert into catch
values
('100'
,'The following user/role has the Alter System privilege, revoke if not required: ' || altersystem);
*/
exception
when no_data_found then
dbms_output.put_line('No custom users with the Alter System privilege.');
/*
insert into catch
values
('900'
,'No custom users with the Alter System privilege.');
*/
end;
/

Permissions for two schemas in Oracle

I have two schemas MONITORING and MONITORING_CONFIGURATION.
I am trying to create a table to which both schemas have access.
I am running the script as MONITORING_CONFIGURATION, but it is not completely ruled out that it runs as MONITORING. Can I simply ignore the error that i am getting on GRANT and get permissions because I created the table as MONITORING_CONFIGURATION while still achieving the goal of a table to which both schemas have access? The error is SQL-Fehler: ORA-01749: you may not GRANT/REVOKE privileges to/from yourself
01749. 00000 - "you may not GRANT/REVOKE privileges to/from yourself"
CREATE TABLE "MONITORING"."WEB_SERVICE_STATUS"
( "WEB_SERVICE_STATUS_ID" NUMBER,
"WEB_SERVICE_ID" NUMBER,
"STATUS" CHAR(1)
);
GRANT ALTER, DELETE, INDEX, INSERT, SELECT, UPDATE, REFERENCES, ON COMMIT REFRESH, QUERY REWRITE, DEBUG, FLASHBACK ON "MONITORING"."WEB_SERVICE_STATUS" TO "MONITORING_CONFIGURATION";
When I do select * from "MONITORING"."WEB_SERVICE_STATUS"; it appears I have the SELECT permission.
I would drop the table, and then rerun that exact script as MONITORING. Then you will know you have the correct grants for both users.
Does the following help - use PLSQL to add logic around what the current user is then taken the appropriate action via dynamic SQL using EXECUTE IMMEDIATE:
DECLARE
vsql VARCHAR2(1000);
BEGIN
IF user = 'MONITORING' THEN
DBMS_OUTPUT.PUT_LINE ( ' Running as ' || USER || ' - creating table' );
vsql := 'CREATE TABLE "MONITORING"."WEB_SERVICE_STATUS"
( "WEB_SERVICE_STATUS_ID" NUMBER,
"WEB_SERVICE_ID" NUMBER,
"STATUS" CHAR(1)
)';
EXECUTE IMMEDIATE vsql;
DBMS_OUTPUT.PUT_LINE ( ' Running as ' || USER || ' - issuing grant' );
vsql := ' GRANT ALTER, DELETE, INDEX, INSERT, SELECT, UPDATE, REFERENCES, ON COMMIT REFRESH, QUERY REWRITE, DEBUG, FLASHBACK ON "MONITORING"."WEB_SERVICE_STATUS" TO "MONITORING_CONFIGURATION"';
EXECUTE IMMEDIATE vsql;
ELSE
DBMS_OUTPUT.PUT_LINE ( ' Running as ' || USER || ' - no action taken' );
END IF;
END;
/
Are you trying to achieve connection between to schema- ?
CREATE PUBLIC DATABASE LINK MONITORING_CONFIGURATION
USING 'MONITORING_CONFIGURATION';

ORA-01031: insufficient privileges when executing rebuild index from Stored Procedure [duplicate]

Here is the definition of the stored procedure:
CREATE OR REPLACE PROCEDURE usp_dropTable(schema VARCHAR, tblToDrop VARCHAR) IS
BEGIN
DECLARE v_cnt NUMBER;
BEGIN
SELECT COUNT(*)
INTO v_cnt
FROM all_tables
WHERE owner = schema
AND table_name = tblToDrop;
IF v_cnt > 0 THEN
EXECUTE IMMEDIATE('DROP TABLE someschema.some_table PURGE');
END IF;
END;
END;
Here is the call:
CALL usp_dropTable('SOMESCHEMA', 'SOME_TABLE');
For some reason, I keep getting insufficient privileges error for the EXECUTE IMMEDIATE command. I looked online and found out that the insufficient privileges error usually means the oracle user account does not have privileges for the command used in the query that is passes, which in this case is DROP. However, I have drop privileges. I am really confused and I can't seem to find a solution that works for me.
Thanks to you in advance.
SOLUTION:
As Steve mentioned below, Oracle security model is weird in that it needs to know explicitly somewhere in the procedure what kind of privileges to use. The way to let Oracle know that is to use AUTHID keyword in the CREATE OR REPLACE statement. If you want the same level of privileges as the creator of the procedure, you use AUTHID DEFINER. If you want Oracle to use the privileges of the user currently running the stored procedure, you want to use AUTHID CURRENT_USER. The procedure declaration looks as follows:
CREATE OR REPLACE PROCEDURE usp_dropTable(schema VARCHAR, tblToDrop VARCHAR)
AUTHID CURRENT_USER IS
BEGIN
DECLARE v_cnt NUMBER;
BEGIN
SELECT COUNT(*)
INTO v_cnt
FROM all_tables
WHERE owner = schema
AND table_name = tblToDrop;
IF v_cnt > 0 THEN
EXECUTE IMMEDIATE('DROP TABLE someschema.some_table PURGE');
END IF;
END;
END;
Thank you everyone for responding. This was definitely very annoying problem to get to the solution.
Oracle's security model is such that when executing dynamic SQL using Execute Immediate (inside the context of a PL/SQL block or procedure), the user does not have privileges to objects or commands that are granted via role membership. Your user likely has "DBA" role or something similar. You must explicitly grant "drop table" permissions to this user. The same would apply if you were trying to select from tables in another schema (such as sys or system) - you would need to grant explicit SELECT privileges on that table to this user.
You should use this example with AUTHID CURRENT_USER :
CREATE OR REPLACE PROCEDURE Create_sequence_for_tab (VAR_TAB_NAME IN VARCHAR2)
AUTHID CURRENT_USER
IS
SEQ_NAME VARCHAR2 (100);
FINAL_QUERY VARCHAR2 (100);
COUNT_NUMBER NUMBER := 0;
cur_id NUMBER;
BEGIN
SEQ_NAME := 'SEQ_' || VAR_TAB_NAME;
SELECT COUNT (*)
INTO COUNT_NUMBER
FROM USER_SEQUENCES
WHERE SEQUENCE_NAME = SEQ_NAME;
DBMS_OUTPUT.PUT_LINE (SEQ_NAME || '>' || COUNT_NUMBER);
IF COUNT_NUMBER = 0
THEN
--DBMS_OUTPUT.PUT_LINE('DROP SEQUENCE ' || SEQ_NAME);
-- EXECUTE IMMEDIATE 'DROP SEQUENCE ' || SEQ_NAME;
-- ELSE
SELECT 'CREATE SEQUENCE COMPTABILITE.' || SEQ_NAME || ' START WITH ' || ROUND (DBMS_RANDOM.VALUE (100000000000, 999999999999), 0) || ' INCREMENT BY 1'
INTO FINAL_QUERY
FROM DUAL;
DBMS_OUTPUT.PUT_LINE (FINAL_QUERY);
cur_id := DBMS_SQL.OPEN_CURSOR;
DBMS_SQL.parse (cur_id, FINAL_QUERY, DBMS_SQL.v7);
DBMS_SQL.CLOSE_CURSOR (cur_id);
-- EXECUTE IMMEDIATE FINAL_QUERY;
END IF;
COMMIT;
END;
/
you could use "AUTHID CURRENT_USER" in body of your procedure definition for your requirements.
Alternatively you can grant the user DROP_ANY_TABLE privilege if need be and the procedure will run as is without the need for any alteration. Dangerous maybe but depends what you're doing :)

Issues with user/system oracle role access and permissions

I am having a bit of an odd issue when it comes to oracle roles and my schema creation code, I will do my best to describe my problem below:
On schema create a role is created using the schema name provided.
EXECUTE IMMEDIATE 'CREATE ROLE ' || USER || 'ADMIN_R';
This role is then granted to specific users associated with that schema.
DECLARE
V_ROLE_NAME CONSTANT VARCHAR2(30) := USER || 'ADMIN_R';
CURSOR C_ADMIN_USERS IS
SELECT USERNAME FROM DBUSERS WHERE ROLE = 'ADMINISTRATION';
BEGIN
FOR REC IN C_ADMIN_USERS
LOOP
EXECUTE IMMEDIATE 'GRANT ' || V_ROLE_NAME || ' TO ' || REC.USERNAME || ' WITH ADMIN OPTION';
END LOOP;
END;
Users with this role have access to a special administration package.
EXECUTE IMMEDIATE 'GRANT EXECUTE ON P_ADMINISTRATION TO ' || USER || 'ADMIN_R';
The problem is When I drop the schema it does not drop this role (belongs to oracle), so when I recreate this schema the whole process fails because this role already exists. I also can't grant access to this role without logging into the system users which I want to avoid. The simple solution would be to just drop it, but my concern in that instance is that the DBA has been using that role to grant access to additional users and potentially additional packages, which would all be lost if we just arbitrarily drop the role to recreate it.
Any assistance would be greatly appreciated.
You could surround your role create with something like this:
SELECT COUNT(*)
INTO v_count
FROM DBA_ROLES
WHERE ROLE = USER || 'ADMIN_R';
IF v_count = 0 THEN
--grant your roles
END IF;
You can grant the same privilege as many times as you like.
you can drop the role before create
declare
i int;
begin
select count(*) into i
from dba_roles where role = USER||'ADMIN_R';
if (0 != i) then
execute immediate 'drop role '||USER||'ADMIN_R';
dbms_output.put_line('Role ' ||USER||'ADMIN_R has been dropped');
end if;
execute immediate 'create role '||USER||'ADMIN_R';
dbms_output.put_line('Role ' ||USER||'ADMIN_R has been created');
end;
1st try:
Role SCMADMIN_R has been created
2nd try:
Role SCMADMIN_R has been dropped
Role SCMADMIN_R has been created

Resources