Execute a stored procedure through a trigger right after a user was created on the database - sql

I would like to write a procedure can grant role permissions to a new created user.
My thoughts were that I first create a procedure like this:
CREATE OR REPLACE PROCEDURE P_CREATE_USER
BEGIN
EXECUTE IMMEDIATE 'GRANT RESOURCE TO'||ora_dict_obj_name;
EXECUTE IMMEDIATE 'GRANT CONNECT TO'||ora_dict_obj_name;
END;
/
Then, I create a trigger, which execute this procedure, after a user is created on the database. Like this:
CREATE OR REPLACE TRIGGER T_CREATE_USER
AFTER CREATE ON DATABASE
WHEN (ora_dict_obj_type = 'USER')
BEGIN
P_CREATE_USER;
END;
/
It did not really work, do you have other suggestions?
I use Oracle as DBMS.

So the problem is this: your trigger throws ORA-30511: invalid DDL operation in system triggers.
The reason is, we cannot commit in triggers. DDL issues implicit commits (before and after the statement). So there is no way your trigger can work, nor could it ever have worked.
The workaround for commits in triggers is pragma AUTONOMOUS TRANSACTION, which causes the trigger to operate in an isolated session. That won't here because the freshly created user won't be visible in the autonomous session.
The best approach you can get to encapsulate the logic would be this:
CREATE OR REPLACE PROCEDURE P_CREATE_USER
(p_user_name in varchar2
, p_password in varchar2)
is
BEGIN
EXECUTE IMMEDIATE ' create user '||p_user_name ||' identified by '||p_password;
EXECUTE IMMEDIATE 'GRANT RESOURCE TO'||p_user_name ;
EXECUTE IMMEDIATE 'GRANT CONNECT TO'||p_user_name ;
END;
/

In SQL server a user, such as you can execute a procedure to grant SQL permission to a newly created user. The condition to make this work is that the user’s account, such as your account need ‘With Grant’ to that SQL permission in order to be able to grant other new user this SQL permission

Related

unable to execute sql procedure on oracle live sql

I created a procedure
create or replace procedure dba_role
as
user varchar2(200);
ref varchar2(200);
begin
insert into dba_role_privs(grantee,granted_role) (select user as grantee,granted_role from dba_role_privs where grantee=ref);
end;
The procedure is getting created but I'm not able to execute the procedure. I've tried different methods to execute it by passing parameters but nothing worked.
Can anyone please tell how to execute this procedure in oracle live SQL
the parameters to be passed are both strings(varchars)
for example:
I've tried "Execute dba_role('alex','hunter');
The error is
**ORA-06550: line 1, column 7:
**PLS-00306: wrong number or types of arguments in call to 'DBA_ROLE' **
As well as missing the two parameters that you are trying to pass (parameters should appear in brackets immediately following the procedure name, as explained in d r's answer), you can't insert into a DBA view. For one thing, it's not in your schema (unless you are creating your procedure as SYS, which you should never do because SYS is reserved for Oracle internals) and you haven't been granted INSERT privilege, but also because it is defined with multiple joins and unions etc and is therefore not an updatable view. Even it it were, your procedure only specifies two of its seven columns.
Even if you did have privileges and it was updatable and you supplied all of the values, directly updating internal data dictionary tables is unsupported and could damage your database. If you want to grant a privilege to a role you should use the GRANT command:
grant reports_user to hr;
To revoke the grant,
revoke reports_user from hr;
create or replace procedure
dba_role(p_user IN VarChar2, p_ref IN VarChar2) AS
begin
insert into dba_role_privs(grantee, granted_role) (select p_user as grantee, granted_role from dba_role_privs where grantee = p_ref);
end dba_role;
/
Above is how it should be defined - with two VarChar2 parameters. And below is how to call it:
Begin
dba_role('alex', 'hunter');
End;
/
The problem with your code was that user and ref were declared as variables within the scope of the procedure (not as parameters) so, when the procedure was called with parameters (like I did above) then you tryed to pass two parameters to the procedure not accepting any. On the other side, if you call it without parameters (just as dba_role;) then user and ref were both Null.

ORACLE - Why does a dynamic SQL statement using DBMS_RANDOM fail when called from a stored procedure but not from an anonymous block

EDIT (8/25)
Thanks to #Alex Poole for providing the answer below but I wanted to share additional detail on these role limitations around PL/SQL objects as it helps explain not only how Oracle is managing things under the hood but why it handles permissions this way.
Now knowing where I was going wrong, I was able to identify this question which discusses the issue at length. This answer describes how the Oracle data structures store the permissions for evaluation.
In addition, someone linked an explanation from Tom Kyte which explains why this behavior was coded intentionally. Long story short: PL/SQL Definer's Rights do not respect role based permissions due to how the Oracle engine compiles these objects. If role based permissions were allowed then any REVOKE statements could have the ability of invalidating large swaths of PL/SQL objects, requiring a costly full database recompile.
Original Question
Could someone help me understand why I can call a dynamic sql script containing a reference to a DBMS_RANDOM procedure when the logic is called from an anonymous block, however, when I take that same logic and drop it into my own stored procedure, the previously runnable script fails to execute with a ORA-00904: "DBMS_RANDOM"."STRING": invalid identifier error?
I feel confident that my privileges are correct. I can run the script that is being passed as a variable directly without issue and run this logic as an anonymous PL/SQL block. Do I need to change my syntax with the stored proc or is it possible that this practice is prevented for security reasons?
Any explanation would be great but if you can point me to the Oracle documentation, I would be ecstatic. I have looked extensively, especially around Oracle's Dynamic SQL documentation but I haven't seen a description of this behavior. I am using Oracle 11g.
To recreate the behavior I am seeing:
Test Data Creation:
SPOOL ON;
SET SERVEROUTPUT ON SIZE UNLIMITED;
--Create Test Table
CREATE TABLE TEST_DYNAMIC_TBL (
ID NUMBER PRIMARY KEY,
MY_COL VARCHAR2(50));
--INSERT a line of data and confirm
INSERT INTO TEST_DYNAMIC_TBL VALUES(1, 'SOME TEXT');
COMMIT;
SELECT MY_COL FROM TEST_DYNAMIC_TBL;
MY_COL
SOME TEXT
PL/SQL Anonymous Block (Successful Example)
DECLARE
l_script VARCHAR2 (32767);
BEGIN
l_script := 'UPDATE TEST_DYNAMIC_TBL SET MY_COL = DBMS_RANDOM.STRING(''U'',5)';
DBMS_OUTPUT.put_line ('Script sent to Exec Immediate: ' || l_script);
EXECUTE IMMEDIATE l_script;
COMMIT;
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.put_line (' ERROR: ' || SUBSTR (SQLERRM, 1, 64));
ROLLBACK;
END;
/
--Check value (This results in a successful update)
SELECT MY_COL FROM TEST_DYNAMIC_TBL;
Script sent to Exec Immediate: UPDATE TEST_DYNAMIC_TBL SET MY_COL = DBMS_RANDOM.STRING('U',5)
PL/SQL procedure successfully completed.
MY_COL
XFTKV
Your query value will vary depending on the seed that DBMS_RANDOM picked
Stored Procedure Example (Failure Example)
--Procedure created with identical logic
CREATE OR REPLACE PROCEDURE TEST_DYNAMIC
AS
l_script VARCHAR2 (32767);
BEGIN
l_script := 'UPDATE TEST_DYNAMIC_TBL SET MY_COL = DBMS_RANDOM.STRING(''U'',5)';
DBMS_OUTPUT.put_line ('Script sent to Exec Immediate: ' || l_script); -- This string will execute successfully if run directly
EXECUTE IMMEDIATE l_script;
COMMIT;
EXCEPTION
WHEN OTHERS
THEN
DBMS_OUTPUT.put_line (' ERROR: ' || SUBSTR (SQLERRM, 1, 64));
ROLLBACK;
END;
/
--Reset and verify Data
UPDATE TEST_DYNAMIC_TBL SET MY_COL = 'SOME TEXT';
COMMIT;
SELECT MY_COL FROM TEST_DYNAMIC_TBL;
--Execute through Procedure (Will throw error)
EXECUTE TEST_DYNAMIC;
--Check Value of Table
SELECT MY_COL FROM TEST_DYNAMIC_TBL;
Stored Procedure Results:
MY_COL
SOME TEXT
Script sent to Exec Immediate: UPDATE TEST_DYNAMIC_TBL SET MY_COL = DBMS_RANDOM.STRING('U',5)
ERROR: ORA-00904: DBMS_RANDOM: invalid identifier
PL/SQL procedure successfully completed.
MY_COL
SOME TEXT
It isn't about it being dynamic, it's about the privileges and how they were granted. You would see the same thing if you had a static insert using dbms_random (and in your example anyway there is no need for it to be dynamic).
It appears that you have execute privilege on dbms_random granted through a role, not directly to the package owner. From the documentation (emphasis added):
If the procedure owner grants to another user the right to use the procedure, then the privileges of the procedure owner (on the objects the procedure references) apply to the grantee's exercise of the procedure. The privileges of the procedure's definer must be granted directly to the procedure owner, not granted through roles. These are called definer's rights.
The user of a procedure who is not its owner is called the invoker. Additional privileges on referenced objects are required for an invoker's rights procedure, but not for a definer's rights procedure.
That only applies to stored PL/SQL - i.e. procedures, functions, packages, triggers etc. - not to anonymous blocks.
You can either get the privilege on dbms_random granted directly to the package owner, or change your procedure to use invoker's rights:
CREATE OR REPLACE PROCEDURE TEST_DYNAMIC
AUTHID CURRENT_USER
AS
...
In the latter case, anyone calling your procedure will then need the privilege on dbms_random - but they can have it through a role.
As access to that package is sometimes locked down, a direct grant to the owner might be preferable, but it depends on your security constraints.
The reason it requires a direct grant, I believe, is that roles can be enabled and disabled, and be default or not, and can be nested. If a direct grant is revoked then it's fairly straightforward to figure out that should invalidate the procedure. And that's possibly true if a role is revoked, but quite a lot more complicated.
But what role-derived privileges should be taken into consideration when the procedure is created - only those that are enabled in that session? Only default roles? Or all roles? (And remember there can be a chain of roles to think about to determine privileges, and you can get the same privilege from multiple roles.)
However you do it will confuse or upset someone - if only enabled then the owner logging in a future session might not be able to perform the actions the procedure does, and what if they want to recompile it? If only default then those defaults can change, with the same issues - or should that invalidate the procedure? If all roles then including disabled ones will be confusing and could have security implications.
And for any of those, role revocation would still have to figure out which privileges that removes - which aren't also granted directly or via another role! - and only once it's really sure which privileges have actually gone, see which objects that affects. Which could be a lot of work - think how many individual privileges could be affected by revoking DBA.
It's much simpler for the invoker - you only need to look at the active privileges from the enabled roles at the moment then call the procedure.
So while at first glance it seems odd that privileges granted through roles aren't included for stored PL/SQL, once you look at the implications and complications - both as it's created, but more what happens afterwards - it seems like a sensible restriction.

Postgres event trigger gives "ERROR: no schema has been selected to create in"

I need an event trigger that gives CRUD access to a particular user each time a schema is created.
Here is my attempt:
CREATE FUNCTION trigger_after_create_schema()
RETURNS event_trigger LANGUAGE plpgsql
AS $$
BEGIN
GRANT USAGE ON SCHEMA TG_TABLE_SCHEMA TO "api_server";
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA TG_TABLE_SCHEMA TO "api_server";
END;
$$;
CREATE EVENT TRIGGER after_create_schema
ON ddl_command_end
WHEN TAG IN ('CREATE SCHEMA')
EXECUTE PROCEDURE trigger_after_create_schema();
This results in the following error:
ERROR: no schema has been selected to create in
SQL state: 3F000
I am executing this in pgAdmin as a superuser, so I don't think it's a permission issue.
What am I missing?
Edit
The CREATE SCHEMA statement looks something like this:
CREATE SCHEMA "tenant" AUTHORIZATION "auth_server";
Where auth_server is a non-superuser with create schema privileges.
As a workaround I could give auth_server the CREATEROLE privilege and execute the GRANT statement in the same transaction as the CREATE SCHEMA, but this seems sub-optimal to me.
auth_server doesn't need CREATEROLE outside of this scenario, and it would also require hacking source code of a third party package, ie. django_tenants.
Grants cannot be run directly in a procedure/function, you need dynamic SQL. Try:
create or replace
function trigger_after_create_schema()
returns event_trigger
language plpgsql
as $$
declare
k_grant_usage constant text =
'grant usage on schema % to "api_server"';
k_grant_dml constant text =
'grant select, insert, update, delete on all tables in schema tg_table_schema to "api_server"';
l_ddl_stmt text;
begin
l_ddl_stmt = format(k_grant_usage,tg_table_schema);
raise notice 'Running statement: %',l_ddl_stmt;
execute l_ddl_stmt;
l_ddl_stmt = format(k_grant_dml,tg_table_schema);
raise notice 'Running statement: %',l_ddl_stmt;
execute l_ddl_stmt;
end;
$$;
However, the error message you are getting in NOT in the trigger. It indicates the CREATE SCHEMA... command in invalid.

insufficient privilieges error

I've been executing various pl/sql procedures on a certain schema and they all execute fine. But while executing a one particular procedure i get the error as
SQL> execute call_para_cursor
BEGIN call_para_cursor; END;
*
ERROR at line 1:
ORA-01031: insufficient privileges
ORA-06512: at "SYSTEM.CALL_PARA_CURSOR", line 4
ORA-06512: at line 1
Though i'm executing the procedure on the same schema, its asking me for more privileges.
How do i get around this situation?
create or replace procedure call_para_cursor as
BEGIN
execute immediate ' create or replace procedure para_cursor_test as
cursor c_p_det(tar_val number) is select name, salary from fees where salary < tar_val;
nname varchar2(30);
ssalary number(5);
BEGIN
Open c_p_det(1000);
LOOP
FETCH c_p_det into nname, ssalary;
DBMS_OUTPUT.PUT_LINE(NNAME || SSALARY);
EXIT WHEN C_P_DET%NOTFOUND;
END LOOP;
CLOSE C_P_DET;
END';
dbms_output.put_line('done processing !!');
END;
The problem is a combination of a few things:
The procedure is run as definer's rights, by default. Only privileges granted directly to the procedure owner will be used when the procedure is executed. Privileges granted through roles are ignored.
The procedure is owned by SYSTEM, which does not have the EXECUTE PROCEDURE directly granted to it by default.
But before you run grant execute procedure to system; - you almost certainly do not want to use SYSTEM for any custom code.
When dealing with functions and procedures, you can grant users the ability to EXECUTE these functions and procedures.
GRANT EXECUTE ON object TO user;
object :The name of the database object that you are granting privileges for.
user: The name of the user that will be granted the EXECUTE privileges.

Executing a Dynamic query having sys objects using Execute Immediate twice

I have a string that is saving a query dynamically from a procedure call.
The query is selecting data from SYS objects like dba_*
I am not able to open the query using execute immediate or Open cursor when calling the string using execute immediate. Any Options to enable this as I cannot get grants on sys objects from DBA to the schema.
Below is an example of how two execute immediate statements are getting used in my code to access the SYS object.
create or replace procedure exp_11 authid current_user as
v_query1 varchar2(100):='select count(1) from dba_objects';
v_query2 varchar2(100):='execute immediate TXT';
v_res number;
v_txt varchar2(100);
begin
v_txt:='select count(1) from dba_objects';
execute immediate v_query1 into v_res;
dbms_output.put_line(v_res);
--This will work with execute immediate directly calling the select statement from dba_objects
v_query2:=replace(v_query2,'TXT',v_txt);
execute immediate v_query2 into v_res;
--This will not work
dbms_output.put_line(v_res);
end;
/
authid current_user is used to execute the procedure with the privileges of the current user (instead of using privileges of user that created the procedure), so if the user you are using does not have SELECT rights (or other needed permissions) on DBA tables, there is no way how to make it work.
If possible, you can make this procedure in a schema that is owned by user that has these rights (like SYS) and mae it without "authid current_user", make public synonym for procedure and grant execute rights to users that need it. This way procedure will have access to whatever it's schema owner has and users executing procedure won't have permission issues.
That said, if the first part with v_query1 works, and you can execute it, it's not a question of permissions. With or without execute immediate, permissions work the same. Your procedure is not working because this line isn't correct:
v_query2:=replace(v_query2,'TXT',v_txt);
You are replacing 'TXT' in query:
'execute immediate TXT';
With v_txt:
v_txt:='select count(1) from dba_objects';
So your SQL statement eventually is:
execute immediate 'execute immediate select count(1) from dba_objects' into v_res;
And that is invalid. You can't use execute immediate twice! (See more about syntax here).
You said that you need to dynamically define queries, but why do you need to put 'execute immediate' in them? Why just not define them without it?
create or replace procedure exp_11 authid current_user as
v_query2 varchar2(100);
begin
v_query2 :='select count(1) from dba_objects';
execute immediate v_query2 into v_res;
dbms_output.put_line(v_res);
end;
/