GRANT Permission to dynamic user SQL - sql

The username that I give a permission is changeable. Therefore I would like to write a SQL like following;
grant ANALYZE ANY DICTIONARY to (select USERNAME from DBA_USERS where DEFAULT_TABLESPACE = 'MCP_DATA');
However, it gives an error.
All comments and suggestions will be greatly appreciated.

Use EXECUTE IMMEDIATE:
DECLARE
username DBA_USERS.USERNAME%TYPE;
BEGIN
SELECT USERNAME
INTO username
FROM DBA_USERS
WHERE DEFAULT_TABLESPACE = 'MCP_DATA';
EXECUTE IMMEDIATE 'GRANT ANALYZE ANY DICTIONARY TO ' || username;
END;
/

Related

Oracle: grant to role if role exists

How to execute
GRANT SELECT ON <ownschema>.<sometable> TO <somerole>;
but backing off gracefully if somerole does not exist. The user executing the statement is a standard user (think SCOTT) without any special privileges.
Version: Oracle Database 19 or later
I don't think you can.
If you're running it at SQL level, then Oracle will raise an error if role doesn't exist.
If you want to check whether it exists, you'll need some kind of a PL/SQL procedure (which is not a problem), but - DBA should grant you select privilege on dba_roles so that you could check it. Then, if it exists, you'd grant that privilege; otherwise, return some kind of an information (dbms_output.put_line in my example is pretty much stupid; it wouldn't be visible out of SQL*Plus, SQL Developer, TOAD and similar), but you got the idea, I hope.
Something like this:
create or replace procedure p_grant_to_role (par_role in varchar2) is
l_cnt number;
begin
select count(*)
into l_cnt
from dba_roles
where role_name = par_role;
if l_cnt > 0 then
execute immediate 'grant select on emp to ' || par_role;
else
dbms_output.put_line('Role does not exist');
end if;
end;
/
It all depends on the tool, but you can do something like this (very crude as usually you should have better exception handling):
begin
execute immediate 'grant .....';
exception
when others then null;
end;

grant a role to a selected subset

I have a role I want to grant to a selected subset of users. Can someone help me with the syntax? Something like this:
grant my_special_role to (select username from dba_users where username like 'john%')
This is supposed to grant my_special_role to all users named John!
You could do it in a loop, declarating a block of code
declare
begin
for rec in (select username from dba_users where username like 'john%')
loop
execute immediate 'grant my special role to ' || rec.username;
end loop;
end;
You can use a simple PL/SQL loop to execute the grants to all the users
BEGIN
FOR i IN (SELECT username
FROM dba_users
WHERE username LIKE 'john%')
LOOP
EXECUTE IMMEDIATE 'grant my_special_role to ' || i.username;
END LOOP;
END;
/

Oracle Procedure with an IF-THEN-ELSE produces error

I have created this procedure in Oracle, to assign a role to a user based on the grade stored in the grade column of the marketing table. However, when I run it I get errors.
Initial Problem
CREATE OR REPLACE PROCEDURE proc_assign_role IS
vn_grade NUMBER(5);
CURSOR cur_user_grade IS
SELECT grade, username
FROM marketing
WHERE grade BETWEEN 1 AND 3;
BEGIN
FOR rec_cur_user_grade IN cur_user_grade
vn_grade:=
IF grade= 1
THEN
GRANT ROLE admin_staff;
ELSIF grade= 2 THEN
GRANT ROLE marketing_staff;
ELSIF grade= 3 THEN
GRANT ROLE event_staff;
END IF;
DBMS_OUTPUT.PUT_LINE(username||'YOU ARE A GRADE '||vn_grade|| 'USER');
END proc_assign_role;
/
This is the error I get:
ERROR at line 11: PLS-00103: Encountered the symbol "VN_GRADE" when expecting one of the following:
. ( * # % & - + / at loop mod remainder range rem ..
|| multiset
1. CREATE OR REPLACE PROCEDURE proc_assign_role IS
2. vn_grade NUMBER(5);
vn_grade:=
You need to assign a value to that line, or get rid of it. You can't assign an IF statement to a number variable. Probably get rid of it, then change your IF statement to look at the grade from the cursor. You also need to end your loop.
Additionally, you can't do a grant directly within a PL/SQL code block. You have to use the execute immediate statement for that. And you have to tell it who you're granting the role to.
FOR rec_cur_user_grade IN cur_user_grade LOOP
IF rec_cur_user_grade.grade= 1 THEN
execute immediate 'GRANT ROLE admin_staff to ' || rec_cur_user_grade.username;
ELSIF rec_cur_user_grade.grade= 2 THEN
execute immediate 'GRANT ROLE marketing_staff to ' || rec_cur_user_grade.username;
ELSIF rec_cur_user_grade.grade= 3 THEN
execute immediate 'GRANT ROLE event_staff to ' || rec_cur_user_grade.username;
END IF;
DBMS_OUTPUT.PUT_LINE(username||'YOU ARE A GRADE '||rec_cur_user_grade.grade|| 'USER');
END LOOP;
I'm seeing a few things that would keep this from working:
After your FOR statement, there's no LOOP statement (which is what the error is complaining about). There's also no END LOOP after your DBMS_OUTPUT.
vn_grade is followed by the := assignment operator, but nothing is being assigned to it.
The GRANT statements are written as bare DDL, which isn't allowed in PL/SQL. They need to be wrapped in EXECUTE IMMEDIATE.
grade and username need to be qualified by the cursor variable (e.g., rec_cur_user_grade.grade and rec_cur_user_grade.username).
Try something like this (which runs as an anonymous block, rather than a procedure, and uses an implicit cursor):
BEGIN
FOR rec_cur_user_grade IN (
SELECT grade, username
FROM marketing
WHERE grade BETWEEN 1 AND 3
)
LOOP
CASE rec_cur_user_grade.grade
WHEN 1 THEN
EXECUTE IMMEDIATE 'GRANT ROLE admin_staff TO ' || rec_cur_user_grade.username;
WHEN 2 THEN
EXECUTE IMMEDIATE 'GRANT ROLE marketing_staff TO ' || rec_cur_user_grade.username;
WHEN 3 THEN
EXECUTE IMMEDIATE 'GRANT ROLE event_staff TO ' || rec_cur_user_grade.username;
END CASE;
DMBS_OUTPUT.PUT_LINE(rec_cur_user_grade.username || ' YOU ARE A GRADE ' || rec_cur_user_grade.grade || ' USER');
END LOOP;
END;
/
grant is DDL and therefore cannot be used in PL/SQL directly. In order to accomplish this, the DDL needs to be executed dynamically, using execute immediately. Additionally, grant always requires you to specify the recipient of the role. The result would be something like this:
execute immediate 'GRANT ROLE admin_staff to ' || rec_cur_user_grade.username;
An ORA-00990: missing or invalid privilege error is fairly self-descriptive: the owner of the procedure does not have the necessary privileges to take the actions being attempted by the procedure.
The most likely culprit here is roles: permissions granted by a role cannot be used in a procedure. The first step you should take is to make sure that the owner of the procedure has been explicitly granted permission to administer the roles involved.

PL/SQL Loop to Revoke Privileges

I need to create a procedure that does the following:
Revoke system privileges granted directly to the user. (Table: dba_sys_privs)
Revoke object privileges granted directly to the user. (Table: dba_tab_privs)
It revokes roles granted directly to the user. (Table: dba_role_privs)
It has to loop through the tables and remove all of them for a user. So far I have #1 and #3 working. But I need to add #2, and I can't seem to figure out how to do it. This is my code for #2, and I keep getting an error:
//REVOKING OBJECT PRIVILEGES
CREATE or REPLACE PROCEDURE deactivate_user
(p_username IN VARCHAR2) AS
l_username VARCHAR2(30) := UPPER(p_username);
BEGIN
FOR rec IN (SELECT * FROM dba_tab_privs WHERE GRANTEE = p_username)
LOOP
EXECUTE IMMEDIATE 'REVOKE ALL PRIVILEGES ON TABLE '||rec.owner||'.'||rec.table_name||' FROM '||rec.grantee;
END LOOP;
END;
/
When I try to execute the code: SQL> exec deactivate_user('BLAKE'), I get this error:
ERROR: at line 1:
ORA-06550: table or view does not exist
ORA-06512: at "SYS.DEACTIVATE_USER", line 7
Your help is greatly appreciated!
The correct command for table privileges is either:
REVOKE ALL PRIVILEGES FROM ...
or:
REVOKE READ FROM ...
And it's even better to add the TABLE keyword as well.
So the EXECUTE IMMEDIATE statement should be either:
EXECUTE IMMEDIATE 'REVOKE ALL PRIVILEGES ON TABLE ' || rec.owner || '.' || rec.table_name ||' FROM '|| rec.grantee;
or:
EXECUTE IMMEDIATE 'REVOKE ' || rec.privilege || ' ON TABLE ' || rec.owner || '.' || rec.table_name ||' FROM '|| rec.grantee;
Update
The most important thing was missing in my first answer: the table owner.

How do I pass user login name to a function and delete that user from database in oracle

Here is the code:
create or replace procedure getuser(User_ID In VARCHAR2)Is
Username varchar2(30);
cursor c1(Userid VARCHAR2) --Cursor o store the row
IS SELECT USERNAME FROM ALL_USERS WHERE USERNAME = Userid;
BEGIN
Open c1(User_id);
loop
FETCH c1 INTO USERNAME;
EXIT WHEN C1%NOTFOUND;
END LOOP;
execute immediate 'drop' || USERNAME;
CLOSE c1;
END;
/
execute system.getuser('STUDENT_0602053');
the error that am getting is invalid sql statement when i execute the stored procedure
execute system.getuser('STUDENT_0602053');
It should be
EXECUTE IMMEDIATE 'drop user '||username;
You may also want:
EXECUTE IMMEDIATE 'drop user '||username||' cascade';
Or the user won't be dropped if they own any objects.
Also, are you logged in as the user SYSTEM?
just a guess - there might be more issues...
but this line needs a space..
execute immediate 'drop' || USERNAME;
should be
execute immediate 'drop ' || USERNAME;