DECLARE
BEGIN
FOR s IN ( SELECT first_name
FROM EA_marketing_table
WHERE town = 'LONDON'
)
LOOP
EXECUTE IMMEDIATE 'CREATE USER ' ||(s.first_name)|| ' IDENTIFIED BY LOL';
dbms_output.put_line (s.first_name || ' IDENTIFIED BY LOL');
END LOOP;
END;
/
I am trying to create a user and the code above lets me create a user but when I try to create a procedure it wont work.
This is the new code
CREATE OR REPLACE PROCEDURE proc_it_development AS
first_name VARCHAR2 (25);
BEGIN
FOR r IN ( SELECT first_name
FROM EA_marketing_table
WHERE town = 'lONDON ')
LOOP
EXECUTE IMMEDIATE 'CREATE USER '||(r.first_name)|| ' IDENTIFIED BY POOP ';
END LOOP;
EXCEPTION
WHEN NO_DATA_FOUND THEN dbms_output.put_line ('r SELECT...INTO did not return any row.');
END;
/
any help please?
:)
Roles are not enabled in procedures/packages so you need to get the privilege granted to your user directly. Most likely you are connecting through a user that has been given the CREATE USER privilege via a role (eg: the DBA role?)
To see what you can do in a procedure, you can disable the active roles for your current session:
set role none
then try your first code block. If it works, it will also work in a procedure.
To create a new schema with a procedure you will need the privilege granted directly to your schema:
GRANT CREATE USER TO <your_schema>;
You should enclose the password in double-quotes, in Oracle 11 passwords are case-sensitive by default. Then you only create a user without any privilege, i.e.CREATE SESSION Privilege is missing.
Related
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';
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 :)
I want to grant the CREATE ANY DIRECTORY permission to a user, with the following restriction: all directories created by this user must be inside of /foo/bar, and any attempt to create a directory outside of this should fail with a permission error. How may I do this on Oracle 11G or 12C?
That depends, if you want to restrict which OS directories Oracle can access from utl_file commands, you can set the utl_file_dir parameter. Unfortunately, this parameter is system wide, so you won't be able to grant/revoke for a specific user using this parameter. Also keep in mind that if you make changes to this parameter, those changes won't go into effect until the Oracle database is restarted:
alter system set utl_file_dir = '/foo/bar' scope=spfile;
shutdown immediate;
startup open;
Consult the 12.1 Oracle Docs for more information regarding utl_file_dir.
That said, if you really want to restrict who can create Oracle Directories to specific OS directories, a procedure would be appropriate for that task since that would allow you to have finer grained control (and limit who has the very powerful create any directory privilege to the owner of the procedure):
sqlplus kjohnston
create or replace procedure mydircreate (p_dir varchar2)
as
ex_custom EXCEPTION;
PRAGMA EXCEPTION_INIT( ex_custom, -20001 );
begin
if lower(p_dir) not like '/foo/bar/%' then
raise_application_error( -20001, 'Not authorized' );
end if;
execute immediate 'create or replace directory mydir as ''' || p_dir || '''';
end mydircreate;
create user testuser identified by <password>;
grant create session to testuser;
grant execute on kjohnston.mydircreate to testuser;
exit;
sqlplus testuser
SQL> exec kjohnston.mydircreate('mydir', '/randomdir');
ORA-20001: Not authorized
SQL> exec kjohnston.mydircreate('mydir', '/foo/bar/baz');
PL/SQL procedure successfully completed.
You can include this restriction in trigger. List of system events and attributes Working with system events
CREATE OR REPLACE TRIGGER trg_before_ddl
BEFORE DDL ON DATABASE
declare
v_sql ORA_NAME_LIST_T;
v_ddl varchar2(4000);
v_cnt BINARY_INTEGER;
is_valid number;
begin
if ora_sysevent in ('CREATE') and ora_dict_obj_type = 'DIRECTORY' then
v_cnt := ora_sql_txt (v_sql);
FOR i IN 1..v_cnt LOOP
v_ddl := v_ddl || RTRIM (v_sql (i), CHR (0));
END LOOP;
v_ddl := regexp_substr(v_ddl,'AS ''(.*)''', 1, 1, 'i', 1 ); -- get path from ddl_statement
-- check valid directory here, path is in v_ddl ;
is_valid := REGEXP_instr(v_ddl,'^/valid_dir/.*$');
if (is_valid = 0) then
raise_application_error(-20000,'Directory is not valid' || v_ddl);
end if;
end if;
END;
/
CREATE DIRECTORY valid_dir AS '/valid_dir/xyz';
CREATE DIRECTORY invalid_dir AS '/invalid_dir/xyz';
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
I am trying to create a script that will create users if they do not already exist.
CREATE USER "Kyle" PROFILE "DEFAULT" IDENTIFIED BY "password" ACCOUNT UNLOCK
WHERE NOT IN //Also tried 'WHERE NOT EXISTS'
(
SELECT username FROM all_users WHERE username = 'Kyle'
)
The following error is given:
SQL Error: ORA-00922: missing or invalid option
I was able to do this in SQL Server 2008 by using:
IF NOT EXISTS
(SELECT name FROM master.sys.server_principals
WHERE name = 'Kyle')
BEGIN
CREATE LOGIN Kyle WITH PASSWORD = 'temppassword' MUST_CHANGE, CHECK_EXPIRATION=ON, CHECK_POLICY=ON
END
Is there a similar way in Oracle to check if a user already exists before attempting to create a new user?
The IF NOT EXISTS syntax available in SQL Server, is not available in Oracle.
In general, Oracle scripts simply execute the CREATE statement, and if the object already exist, you'll get an error indicating that, which you can ignore. This is what all the standard Oracle deployment scripts do.
However, if you really want to check for existence, and only execute if object doesn't exist, thereby avoiding the error, you can code a PL/SQL block. Write a SQL that checks for user existence, and if it doesn't exist, use EXECUTE IMMEDIATE to do CREATE USER from the PL/SQL block.
An example of such a PL/SQL block might be:
declare
userexist integer;
begin
select count(*) into userexist from dba_users where username='SMITH';
if (userexist = 0) then
execute immediate 'create user smith identified by smith';
end if;
end;
/
You need to write a pl/sql block. See an example here
You can check if the user exists in the all_users table using some pl/sql code like:
SELECT count(*) INTO v_count_user
FROM all_users
WHERE username = 'Kyle'
and then use v_count_user in an IF condition to conditionally execute the create user statement.
From the previous answers, it is clear that if not exists is not supported in Oracle. To clarify which error(s) are thrown by Oracle when attempting to create an already existing user (and as a bonus, when attempting to drop a non existing user):
drop user foo;
ORA-01918: user 'foo' does not exist
create user existing_user IDENTIFIED BY existing_user;
ORA-01920: user name 'existing_user' conflicts with another user or role name
The statements above were executed on Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - 64bit Production
Another way to do it is to create the user in a PL/SQL block and catch the ORA-01920 error. That way, the block does not throw any errors when the user exists.
DECLARE
sqlStatement varchar2(512);
user_exists EXCEPTION;
PRAGMA EXCEPTION_INIT(user_exists, -1920);
BEGIN
sqlStatement := 'CREATE USER "Kyle" ' ||
'IDENTIFIED BY "password" ' ||
'PROFILE "Default" ' ||
'ACCOUNT UNLOCK';
EXECUTE IMMEDIATE sqlStatement;
dbms_output.put_line(' OK: ' || sqlStatement);
EXCEPTION
WHEN user_exists THEN
dbms_output.put_line('WARN: ' || sqlStatement);
dbms_output.put_line('Already exists');
WHEN OTHERS THEN
dbms_output.put_line('FAIL: ' || sqlStatement);
RAISE;
END;
/