Create user from string variables in a PL/SQL block - oracle

I use Oracle XE for the sole purpose of developing PHP applications and version 11g has apparently lost the GUI tool to manage users which 10g used to have so I'd like to prepare a code snippet to create users from command line. I'm trying to define variables so I don't need to type the same user name 16 times but I can't get the syntax right:
DECLARE
my_user VARCHAR2(30) := 'foo';
my_password VARCHAR2(9) := '1234';
BEGIN
CREATE USER my_user IDENTIFIED BY my_password DEFAULT TABLESPACE users;
GRANT CONNECT, RESOURCE TO my_user;
GRANT CREATE DATABASE LINK TO my_user;
GRANT CREATE MATERIALIZED VIEW TO my_user;
GRANT CREATE PROCEDURE TO my_user;
GRANT CREATE PUBLIC SYNONYM TO my_user;
GRANT CREATE ROLE TO my_user;
GRANT CREATE SEQUENCE TO my_user;
GRANT CREATE SYNONYM TO my_user;
GRANT CREATE TABLE TO my_user;
GRANT CREATE TRIGGER TO my_user;
GRANT CREATE TYPE TO my_user;
GRANT CREATE VIEW TO my_user;
GRANT SELECT_CATALOG_ROLE TO my_user;
GRANT SELECT ANY DICTIONARY TO my_user;
END;
/
CREATE USER my_user IDENTIFIED BY my_password DEFAULT TABLESPACE users;
*
ERROR en lĂ­nea 5:
ORA-06550: line 5, column 2:
PLS-00103: Encountered the symbol "CREATE" when expecting one of the following:
( begin case declare exit for goto if loop mod null pragma
raise return select update while with <an identifier>
<a double-quoted delimited-identifier> <a bind variable> <<
continue close current delete fetch lock insert open rollback
savepoint set sql execute commit forall merge pipe purge
Is it forbidden to use a CREATE USER statement from within a PL/SQL block or I simply made a silly typo? Is it mandatory to use SQL*Plus variables?

PLS-00103: Encountered the symbol "CREATE" when expecting one of the following:
The above error is because of the fact that you are using DDL inside PL/SQL. You cannot do that. You must (ab)use EXECUTE IMMEDIATE to issue DDL statements in PL/SQL.
For example,
SQL> DECLARE
2 my_user VARCHAR2(30) := 'foo';
3 my_password VARCHAR2(9) := '1234';
4 BEGIN
5 EXECUTE IMMEDIATE 'CREATE USER '||my_user||' IDENTIFIED BY '||my_password;
6 EXECUTE IMMEDIATE 'GRANT CREATE SESSION TO '||my_user;
7 END;
8 /
PL/SQL procedure successfully completed.
SQL> conn foo/1234#pdborcl
Connected.
SQL> SHOW USER
USER is "FOO"
Quick reference from documentation,
Executing DDL and SCL Statements in PL/SQL
Only dynamic SQL can execute the following types of statements
within PL/SQL program units:
Data definition language (DDL) statements such as CREATE, DROP, GRANT, and REVOKE
Session control language (SCL) statements such as ALTER SESSION and SET ROLE
The TABLE clause in the SELECT statement
On a side note,
Creating users and granting privileges are usually database administration tasks taken care by the DBA. It is not a frequent activity done via PL/SQL program. DBA creates the users and grants the necessary privileges as a one time activity.

Related

PL/SQL: ORA-01031: insufficient privileges 6/1 PL/SQL: SQL Statement ignored GRANT

Oracle Database 12c Standard Edition Release 12.2.0.1.0 - 64bit Production
I'm getting the above message when trying to create a package.
I can simulate with a very simple procedure and select statement.
My understanding is that have SQL select access for a user does not translate to PL/SQL (package) access for the same user, and that an option is to use roles. This has not worked for me.
This is all done with the same user (not apex_180100).
Showing the issue:
This SQL works. It doesn't make sense, but proves that I can select from the tables.
SELECT 1
FROM apex_180100.wwv_flow_activity_log l,
apex_180100.wwv_flow_worksheet_rpts r
WHERE l.ir_report_id IS NOT NULL
AND l.flow_id = 100
AND l.worksheet_id = r.worksheet_id
AND l.ir_report_id = r.id
AND l.flow_id = r.flow_id
AND l.step_id = r.page_id;
I granted select to a role
GRANT SELECT ON apex_180100.wwv_flow_worksheet_rpts TO PRIV_FULL_TABLE;
GRANT SELECT ON apex_180100.wwv_flow_activity_log TO PRIV_FULL_TABLE;
I grant my role to my procedure (ultimately I will grant to my package)
GRANT PRIV_FULL_TABLE TO PROCEDURE p_test;
I get an error when creating this simple sample procedure.
create OR REPLACE procedure p_test is
V_TEST NUMBER;
begin
SELECT 1
INTO V_TEST
FROM apex_180100.wwv_flow_activity_log l,
apex_180100.wwv_flow_worksheet_rpts r
WHERE l.ir_report_id IS NOT NULL
AND l.flow_id = 100
AND l.worksheet_id = r.worksheet_id
AND l.ir_report_id = r.id
AND l.flow_id = r.flow_id
AND l.step_id = r.page_id;
end;
PL/SQL: ORA-01031: insufficient privileges compilation error
Hm, there's something strange in what you are saying. Usually we grant privileges to users, not procedures.
SQL> create procedure p_test as begin
2 null;
3 end;
4 /
Procedure created.
SQL> create role priv_full_table;
Role created.
SQL> grant priv_full_table to procedure p_test;
grant priv_full_table to procedure p_test
*
ERROR at line 1:
ORA-00991: only MAC privileges may be granted to procedures
SQL>
Apart from that, if I understood you correctly, issue is exactly what you thought that solves it: privileges granted to roles won't work in named stored procedures. p_test is a named procedure:
create OR REPLACE procedure p_test is ...
which means that you'll have to grant those privileges directly to user which will be using them.
Thanks to #Littlefoot
I used a workaround. My procedure is relatively simple, I wanted to insert into a custom table from my "problem" tables. I wanted this to be called by an hourly DB job.
As I can select and insert in SQL, but not PL/SQL procedures, I used a SQL script instead of datbase procedure.
i.e I converted my package into a series of SQL statements. I stored this sql script on the server and ran as a DBA job executable.
Not ideal.
PS 'execute immediate' doesn't work either.

can we provide an oracle privilege to user which can alter only password of other user

I have created a procedure as shown below. This procedure is present in schema1 and trying to modify the password of another schema/user let's say schema2.
To achieve this the user schema1 must have altered user privilege but I cannot provide the alter user privilege to schema1 due to some restrictions on the application level.
I tried to query using ALTER SESSION in the procedure but it is not working.
Can someone help me with the solution?
code:
Procedure p_demo(p_schema in varchar, p_pswd in varchar2)
is
begin
execute immediate 'alter session set current_schema ='||p_schema;
execute immediate 'alter user '||p_schema||' identified by '||p_pswd;
end;
Changing the current_schema has no impact on permissions or what user is currently logged in. It merely changes how object name resolution works. If you query an object foo when current_schema is set to schema1, Oracle looks in the schema1 schema rather than in the current user's schema. It does nothing to give you access to the schema1.foo table.
I'm not quite sure that I understand the goal. If you are trying to ensure that only the schema2 user can change the schema2 user's password, you can define the procedure to use invoker's rights rather than definer's rights.
create or replace procedure change_my_password( p_username in varchar2,
p_password in varchar2 )
authid current_user
is
begin
execute immediate 'alter user '||p_username||' identified by '||p_password;
end;
/
If the goal is to have the procedure owned by schema1 and to give users other than the schema2 user permission to change schema2's password (i.e. to allow an application user or a helpdesk user to reset the user's password), schema1 would likely need to have the alter user permission. Otherwise, it's probably not doable.
If you're really desperate, you could potentially use the undocumented (and I emphasize undocumented here, subject to change at any time, may have weird side effects, may tend to make Oracle Support unhappy) dbms_sys_sql package. That's the package that APEX uses internally to run code as other users. I don't imagine that a sane DBA would consider giving an application user execute access on that package rather than the much (much, much) safer alter user permission but if you're trying to work around some corporate policy and you're not much concerned about actual security...
There are 3 users involved in this example:
scott, who is trying to change someone else's password (that's your schema1)
mike, whose password should be changed (your schema2)
mydba, who is granted DBA role in my database (if you don't have such an user, SYS would do, but - you'd rather have your own "DBA" user, don't mess up with SYS if you don't have to)
Connected as scott, I can't modify mike's password:
SQL> alter user mike identified by lion;
alter user mike identified by lion
*
ERROR at line 1:
ORA-01031: insufficient privileges
I'm going to connect as mydba and create a stored procedure which looks like yours:
SQL> connect mydba/mypwd#c11gt
Connected.
SQL> create or replace procedure p_demo (p_schema in varchar2, p_pswd in varchar2) is
2 begin
3 execute immediate 'alter user ' || p_schema || ' identified by ' || p_pswd;
4 end;
5 /
Procedure created.
SQL> exec p_demo('mike', 'lion');
PL/SQL procedure successfully completed.
OK; it works. I'll grant execute privilege on it to scott:
SQL> grant execute on p_demo to scott;
Grant succeeded.
Back to scott; see what he can do now:
SQL> connect scott/tiger#c11gt
Connected.
SQL> exec mydba.p_demo('mike', 'friday');
PL/SQL procedure successfully completed.
Do mike's credentials work?
SQL> connect mike/friday#c11gt
Connected.
SQL>
Yes, everything's fine.
So: you don't have to grant alter user to schema1; let it use procedure owned by a privileged user who can change someone else's password.
Cant we do as shown below: Tried doing this but it didn't work.
What i am trying to do is basically give alter user privileges to one role and assign that role to my oracle procedure.
Create role role_name;
GRANT ALTER USER TO role_name
grant all on role_name to procedure

As a system user I have this error: ORA-01031: insufficient privileges

I have a stored procedure for deleting partitions. Before starting, I have to delete a constraint.
I installed the stored procedure on system user. When I test the procedure I have this error: 'ORA-01031: insufficient privileges'.
This is a piece of code that I wrote:
BEGIN
EXECUTE IMMEDIATE 'ALTER TABLE USER_NAME.TABLE_NAME DISABLE CONSTRAINT CONSTRAINT_NAME';
EXCEPTION
WHEN OTHERS THEN
O_sCodError := 'NO_OK';
O_sDesError := 'Error at DISABLE CONSTRAINT_NAME: ' || SQLERRM || ';';
RETURN;
END;
Well, as I execute the stored procedure as system, I do not understand the reason for I have that error. And I think I eventually think the same error when I try to delete a partition.
Works for me on 11g XE:
SQL> show user
USER is "SCOTT"
SQL>
SQL> create table test
2 (id number constraint pk_test primary key,
3 name varchar2(20)
4 );
Table created.
SQL> connect system
Enter password:
Connected.
SQL> begin
2 execute immediate 'alter table scott.test disable constraint pk_test';
3 return;
4 end;
5 /
PL/SQL procedure successfully completed.
SQL>
Please, follow that example and execute it in your database. Post the result (by editing the question, not as a comment).
First, you should never install custom code in an Oracle default schema like SYSTEM. Put your code in a dedicated application schema. Since it contains dynamic SQL (execute immediate) you might want to consider making it an "invoker's rights" procedure, then granting execute privileges on it to the user that will execute it.
That said, in Oracle 11g whoever's privileges are used to run the PL/SQL block must have direct permissions on the underlying table, not inherited permissions through a role like DBA. If the procedure has "definer's rights" then the schema that owns the procedure must have direct privileges on the table. If "invoker's rights" then the user executing the procedure must have the privileges.
See this link for additional details:
Execute immediate within Oracle Procedure
You must grant SYSTEM account direct privilege (not with a role) to run ALTER TABLE on the target table because roles are not enabled in stored procedures by default: https://asktom.oracle.com/Misc/RolesAndProcedures.html.
Try:
grant alter any table to system;
or
grant alter table on user_name.table_name to system;

Strange ORA-942 when using DBMS_CRYPTO but table exists

This is very strange, and not easy to explain, so please bear with me.
Oracle 12.2.0.1 on Linux x86_64.
We have a user called BATCH who has minimal privileges.
CREATE USER batch IDENTIFIED BY batch
DEFAULT TABLESPACE users
QUOTA UNLIMITED ON users;
GRANT CREATE SESSION TO batch;
GRANT EXECUTE ON DBMS_CRYPTO TO batch;
The is a PLSQL package in a schema called ATLED which is :
CREATE OR REPLACE PACKAGE ALTED.the_pkh AUTHID current_user AS
PROCEDURE crttab;
END;
/
CREATE OR REPLACE PACKAGE BODY ALTED.the_pkh AS
PROCEDURE crttab IS
BEGIN
EXECUTE IMMEDIATE 'CREATE TABLE atled.the_tab AS SELECT id, DBMS_CRYPTO.HASH(cc,2) AS cc FROM ARCHIVE.table_b';
END crttab;
END;
/
We are using Code Based Access Control (CBAC - 12c feature) to restrict/control/allow certain canned actions to an otherwise toothless user, so we create a wrapper procedure, grant that a high priv role, and grant execute on that to the batch user:
CREATE OR REPLACE PROCEDURE ALTED.wrapper_crttab AS
PROCEDURE p1 IS
CURSOR c1 is SELECT * FROM SESSION_PRIVS;
BEGIN
FOR r1 IN c1 LOOP
DBMS_OUTPUT.PUT_LINE( r1.privilege );
END LOOP;
END;
BEGIN
p1;
ALTED.the_pkh.crttab;
END;
/
GRANT IMP_FULL_DATABASE TO ALTED;
GRANT IMP_FULL_DATABASE ALTED.wrapper_crttab;
GRANT EXECUTE ON ALTED.wrapper_crttab TO batch;
Now let's run it:
CONN batch/batch
SET SERVEROUTPUT ON
EXEC ALTED.wrapper_crttab;
This causes the error:
Error at line 1:
ORA-00942: table or view does not exist
The tables referenced do exist.
The call to the p1 proc confirms that all the privileges bundled with IMP_FULL_DATABASE are present, including CREATE ANY TABLE, DROP ANY TABLE, EXECUTE ANY PROCEDURE.
If I do this:
GRANT CREATE ANY TABLE TO batch;
GRANT SELECT ANY TABLE TO batch;
CONN batch/batch
EXEC EXECUTE IMMEDIATE 'CREATE TABLE atled.the_tab AS SELECT id, DBMS_CRYPTO.HASH(cc,2) AS cc FROM ARCHIVE.table_b;
This works.
If I change the CREATE TABLE stmt to remove the DBMS_CRYPTO call, it works as well.
The actual package/proc that is called creates a number of tables fine when run as above, but fails on the case when DBMS_CRYPTO is called in any CREATE TABLE stmt.
If I grant the batch user the CREATE ANY TABLE, SELECT ANY TABLE and EXECUTE ANY PROCEDURE privs directly and run the CREATE TABLE command as batch directly then that works too.
So this is not (I think) a straight ORA-942 error, but something related to a chain of privileges to DBMS_CRYPTO, and only when executed in a package stored procedure, but what exactly I do not know.
UPDATE 1
If I create a wrapper for DBMS_CRYPTO.HASH as follows:
CREATE OR REPLACE FUNCTION batch.crypto_hash ( pcc IN CLOB ) RETURN VARCHAR2 IS
BEGIN
RETURN DBMS_CRYPTO.HASH(pcc,2);
END;
/
Then replace the DBMS_CRYPTO.HASH(cc,2) in the CREATE TABLE stmt with batch.crypto_hash(cc) then it works!!!
So, DEFINITELY not a issue with grants on teh tables being referenced, but more likely something internal with the way DBMS_CRYPTO works. Perhaps it reads a look up table somewhere. I tried GRANT EXECUTE ON UTL_I18N to batch as well before this but that didn't work.
So I have a workaround, but woudl still like to know why this happens.
You're doing this:
... FROM ARCHIVE.table_b
User, who is supposed to select from that table, has to have SELECT privileges on it. It can be granted
via role
directly
If you granted the privilege via role, it works - but not in named PL/SQL procedures. It will work in anonymous PL/SQL, but not in procedures, function, packages, so - check that and, possibly, grant SELECT on table_b directly to that user.

truncate and insert within procedure don't work together

I need to do truncate table and then insert data in that table using procedure.
However, one does like dinamic sql but the other one doesn't:
create or replace
procedure RECREATE_AGGREGATE
AUTHID DEFINER
AS
BEGIN
TRUNCATE TABLE AGGREGATE;
INSERT INTO AGGREGATE SELECT * FROM OLD_AGGREGATE;
END;
Error(6,14): PLS-00103: Encountered the symbol "TABLE" when expecting one of the following: := . ( # % ; The symbol ":= was inserted before "TABLE" to continue.
If I add execute immediate around TRUNCATE statement, it works fine, but insert is erroring out.
If I remove it, TRUNCATE TABLE complains...
create or replace
procedure RECREATE_AGGREGATE
AUTHID DEFINER
AS
BEGIN
execute immediate 'TRUNCATE TABLE AGGREGATE';
INSERT INTO AGGREGATE SELECT * FROM OLD_AGGREGATE;
END;
Error(7,5): PL/SQL: SQL Statement ignored
Error(7,84): PL/SQL: ORA-00942: table or view does not exist
Can some shed some light here?
create or replace
procedure RECREATE_AGGREGATE
AUTHID DEFINER
AS
BEGIN
EXECUTE IMMEDIATE 'TRUNCATE TABLE AGGREGATE';
INSERT INTO AGGREGATE SELECT * FROM OLD_AGGREGATE;
END;
will work assuming that you have appropriate privileges.
Based on your edit (and echoing #OracleUser's comment), you're likely getting an error running your INSERT statement because old_aggregate is owned by another user and you only have SELECT privileges on that table via a role. If you want to do something in a definer's rights stored procedure, you'll need to have those privileges granted directly to you (or be using 12c which lets you grant privileges to blocks of code rather than to users).
Assuming you want to use a definer's rights stored procedure, you'd need the owner of old_aggregate (or the DBA) to
GRANT SELECT
ON old_user.old_aggregate
TO new_user;
You can verify that you only have the privilege via a role by disabling roles for the session. I'll wager that if you do
SQL> set role none;
SQL> SELECT * FROM old_aggregate
that you'll get an ORA-00942 error as well. This is a good way of simulating what privileges will be available to the user inside a definer's rights stored procedure.
"TRUNCATE is DDL (data definition language). You cannot perform DDL from within PL/SQL. "
http://www.orafaq.com/forum/t/119427

Resources