Allow only adding of column/table/view in Oracle SQL - sql

Suppose I have a platform which allows users to enter some SQL queries and execute them.
DB is Oracle 11.2
Is there any way to limit users' operations to CREATE TABLE, CREATE VIEW and ALTER table ADD COLUMN?
The only way I see is to parse all user provided queries with the grammar for Oracle DB 11.2. But this way is a very tedious one since there is no complete grammar in a free access (at least I couldn't find one) and implementing one will take days, if not weeks.

Grant CREATE TABLE and CREATE VIEW privileges.
For ALTER TABLE, to limit just ADD COLUMN, it's possible to create procedure and grant EXECUTE:
create or replace add_column( tableName varchar2, columnDefinition varchar2 )
as
execute immediate 'alter table '|| tableName || ' add column ' || columnDefinition;
end;
/
(not tested)
and
GRANT EXECUTE on ADD_COLUMN to user2;

Related

Unable to create table from within package

I am attempting to create a package in which I drop and create a table using a CTAS query. This table needs to be refreshed frequently and columns are add/removed all the time from the underlying data. Since the structure of the table is constantly changing, it would be quite cumbersome to update merge/update queries each refresh to account for the new/missing columns. Currently, I have external scripts that do simple drops and creates but I need to centralize this in the database; therefore I am attempting to create a package to do it; however, I am having trouble with privileges.
As proof of concept, the following code works when ran as an anonymous block:
create table my_test_table as select * from dual; --create test table
declare
v_count int;
begin
select count(*) into v_count from all_tab_columns where table_name = upper('my_test_table');
if v_count >= 1 then
execute immediate 'drop table my_test_table';
end if;
execute immediate q'[
create table my_test_table as
select * from dual
]';
end;
select * from my_test_table; -- shows expected results
But when creating a package to do the same thing;
CREATE OR REPLACE PACKAGE test_pkg AS
PROCEDURE test_procedure;
END test_pkg;
CREATE OR REPLACE package body test_pkg as
procedure test_procedure
is
v_count int;
begin
select count(*) into v_count from all_tab_columns where table_name = upper('my_test_table');
if v_count >= 1 then
execute immediate 'drop table my_test_table';
end if;
execute immediate q'[
create table my_test_table as
select * from dual
]';
end test_procedure;
end test_pkg;
/
and testing with the following code:
create table my_test_table as select * from dual; --make sure table exists
execute TEST_PKG.TEST_PROCEDURE; --results in errors
select * from my_test_table; --table does not exist; therefore, DROP statement works but not CREATE
I get the following errors (in regards to executing TEST_PKG.TEST_PROCEDURE):
ORA-01031: insufficient privileges
ORA-06512: at test_pkg, line 15
When testing for the existence of the test table after executing the package, I can see that it no longer exists. This means the DROP statement is working but the CREATE TABLE statement is resulting in the insufficient privileges error.
Any and all insight into what privileges I need to create the table from within the package would be immensely helpful.
Create a table in procedure is only alowed when you have "Create table" or "create any table" privilege but granted directly to user (granted by role is not working).
https://docs.oracle.com/cd/B19306_01/network.102/b14266/authoriz.htm#i1008334
PL/SQL Blocks and Roles
The use of roles in a PL/SQL block depends on whether it is an
anonymous block or a named block (stored procedure, function, or
trigger), and whether it executes with definer's rights or invoker's
rights.
Named Blocks with Definer's Rights
All roles are disabled in any named PL/SQL block (stored procedure,
function, or trigger) that executes with definer's rights. Roles are
not used for privilege checking and you cannot set roles within a
definer's rights procedure.
To check system privileges granted directly to your user (not by role/roles), you can run this query from your user:
SELECT * FROM USER_SYS_PRIVS;
The package you've created, in the absence of a AUTHID CURRENT_USER clause is a definer's rights package. It can only do things that are allowed by privileges granted directly to the definer of the package. "Directly" is the key point here -- privileges granted through enabled roles are not honored during the package execution.
You've probably got the RESOURCE or similar role enabled for your user, which would explain why you can create the table during testing but not via your package procedure.
Try granting the CREATE TABLE and UNLIMITED TABLESPACE system privileges directly to your user and then recreate the package. (If that works, replace UNLIMITED TABLESPACE with quotas on the appropriate tablespace(s) in your database).

ORACLE PLSQL Create user with trigger identified by :new username and password

I'm writing an online videogame Database in SQL using ORACLE for an accademic project, and i'm trying to create a trigger that for every user that submit their information in my ACCCOUNT TABLE
CREATE TABLE ACCOUNT (
USERNAME VARCHAR(20),
PASSWORD VARCHAR(20) NOT NULL,
NATIONALITY VARCHAR(15),
CREATION DATE DATE,
EMAIL_ACCOUNT VARCHAR(35) NOT NULL,
CONSTRAINT KEYACCOUNT PRIMARY KEY(USERNAME),
CONSTRAINT NO_USER_CSPEC CHECK(REGEXP_LIKE(USERNAME, '^[a-zA-Z0-9._]+$') AND USERNAME NOT LIKE '% %'),
CONSTRAINT NO_EASY_PASS CHECK(REGEXP_LIKE(PASSWORD, '^[a-zA-Z0-9._!#£$%&/()=?]') AND PASSWORD NOT LIKE '% %'),
CONSTRAINT LENGHTUSER CHECK(LENGTH(USERNAME)>3),
CONSTRAINT LENGHTPASS CHECK(LENGTH(PASSWORD)>5),
CONSTRAINT FK_EMAIL FOREIGN KEY(EMAIL_ACCOUNT) REFERENCES PERSONA(EMAIL) ON DELETE CASCADE
);
Will fire a trigger that will create a new user with the new username and password just inserted.
this is the code i tried to wrote
CREATE OR REPLACE TRIGGER NEW_USER
AFTER INSERT ON ACCOUNT
FOR EACH ROW
BEGIN
CREATE USER :NEW.USERNAME IDENTIFIED BY :NEW.PASSWORD;
GRANT ROLE_NAME TO :NEW.USERNAME
END;
Why i'm tyring to do this ?
Basically because i'd like to give specific view on specific row that regards only the specific user. ( imagine if, while managing your account you can access to every other row stored in the table ACCOUNT )
After creating that specific user i can create some procedure that have in input the username ( of a successfully created user ) and give back the view on that specific row.
is there a way to do this ?
At first, you can't use DDL statement in trigger body as a open source, you should put it in execute immediate command. And also you should pay attention to user privileges which will execute then trigger, and role which will be granted to user, are there all priveleges granted, for create session, execute statements and so on. But if I were you I'll put user opening process in separate procedure, I think it won't be so simple code, so it will be easy to edit package procedure.
You can create context for you user sessions, wrap all your table where you want to control access into views and then filter view by user context.
For example you table TAB_A with many rows, in table you store column ACS_USER and wrap table to V_TAB_A , when you can control access to table via view, all user access object will use views like
select * from V_TAB_A where ACSUSER = SYS_CONTEXT('USERENV','SESSION_USER')
The main problem I see here is grant to create user. You probably don't want your schema to be able to create users. So trigger (of course as other answers states this need to be execute immediate) shouldn't directly call create user. I would create procedure that create user in other schema than your working schema. That external schema would have grants to create user and your schema would have only grant to execute that one procedure from strong priviledged schema. In that case trigger will only call single procedure from external schema.
So to recap:
CREATE OR REPLACE TRIGGER your_schema.NEW_USER
AFTER INSERT ON ACCOUNT
FOR EACH ROW
BEGIN
STRONG.CREATE_USER(:NEW.PASSWORD,:NEW.USERNAME);
END;
CREATE OR REPLACE PROCEDURE STRONG.CREATE_USER(PASS VARCHAR2, USERNAME VARCHAR2) AS
DECLARE
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
execute immediate 'CREATE USER ' || USERNAME || ' IDENTIFIED BY ' || PASS;
execute immediate 'GRANT ROLE_NAME, CONNECT, RESOURCE TO ' || USERNAME; --and whatever user needs more
END;
Where STRONG user have rights to create user and your_schema has grant to execute STRONG.CREATE_USER
Additional thing. Never store passwords in plain text. Use some hash.
Alternatively, you can use a database stored procedure instead of a trigger to do the DDL operations.
This is a pseudo code, make necessary changes as per your requirement. You can build your logic on top of this and if you are stuck, always post a question here in SO.
Table
CREATE TABLE account_info
(
user_name VARCHAR (20),
user_password VARCHAR (20) NOT NULL
);
Procedure
CREATE OR REPLACE PROCEDURE test_procedure (
user_name IN account_info.user_name%TYPE,
user_password IN account_info.user_password%TYPE)
AS
BEGIN
INSERT INTO account_info (user_name, user_password)
VALUES ('ABC', 'password123');
-- check the user exists or not, if yes proceed
EXECUTE IMMEDIATE
'CREATE USER ' || user_name || ' IDENTIFIED BY ' || user_password;
-- do the rest of the activities such as grant roles, privilges etc.
END;
/

Use Query result in ALTER statement (Oracle)

I am trying to create a script to update passwords for a large number of users listed in a given table.
alter user FOO identified by FOOWORD;
I can call the usernames via the following statement:
select owner from usertable_verson where rownum = 1
Is there a way to combine these two statements, so that the alter user command works for each result of the select command?
The eventual goal is to create a loop for each username in the selected column, and apply the password change to each.
you can do this via dynamic SQL
smth like this:
begin
for rc in (select owner from usertable_verson) loop
execute immediate 'alter user '||rc.owner||' identified by FOOWORD';
end loop;
end;

How can i give grant access to specific user-role for a frquently dropped and created table in Vertica?

I have a table which is frequently dropped and created with the same name in a schema. How can i create grant access to a user-role when the table is created in Vertica?
P.S: This question is specifc to vertica, but all ideas are welcome. Also dropping is business requirement and we cannot even truncate the table and clear data out.
Unfortunately Vertica does not support a fixed role that can run a SELECT statement against any table or view in the database(SQL Server db_datareader roles for example).
So depending on how you create the object(DBA creation,workflow or any ETL tool), you need to automate this.
You can create a UDP to do this for you and just call the UDP in your job,script,workflow.
Also you can create a UDP to extract the actual grants,access to the object and also stores it in a temp file to be executed after object recreation.
But the question is ? is recreating the table needed ? What is the use of this ? Maybe there is a better solution !
And #Kermit you gotta stop down voting !
You'll have to do it in steps when you recreate the table.
1) Generate the GRANT script based on the existing grants. (You'll need to change this to match how you handle grants)
select 'GRANT ' || privileges_description || ' ON ' || object_schema || '.' || object_name || ' TO ' || grantee || ';' from grants
where object_schema = 'MYSCHEMA' object_name = 'MYTABLE'
and grantee ilike 'role%';
2) Create the new table like the old one (be sure to save your projections!)
CREATE TABLE MYSCHEMA.NEW_MYTABLE LIKE MYSCHEMA.MYTABLE INCLUDING PROJECTIONS;
3) Drop old table
DROP TABLE MYSCHEMA.MYTABLE;
4) Rename new table
ALTER TABLE MYSCHEMA.NEW_MYTABLE RENAME TO MYTABLE;
5) Apply generated grants
GRANT ROLE_MYROLE TO MYSCHEMA.MYTABLE;

Create/alter from SQL stored procedure

I want to call create table/ alter table command from a procedure. Is it possible?
My requirement is to change the datatype of a column in all tables. So, I am just getting the column name from user_tab_cols. Now I want to create a temp table which requires create statement .. but i am unable to use that within a proc.
Can anyone please help me out?
I presume from the reference to USER_TAB_COLUMNS that this is Oracle. ALTER and CREATE statements are DDL, which we cannot execute directly in PL/SQL. However, there are a couple of ways around this restriction: EXECUTE IMMEDIATE and DBMS_UTILITY.EXEC_DDL(). I will use EXECUTE IMMEDIATE in the following example.
begin
for lrec in ( select table_name from user_tab_columns
where column_name = 'UNIVERSAL_COLUMN_NAME')
loop
execute immediate 'alter table '||lrec.table_name||
' modify UNIVERSAL_COLUMN_NAME varchar2(255)';
end loop;
end;
Note that the usual restrictions apply: the new datatype has to be compatible with the existing datatype (unless the column is empty), and things are trickier with some specilaized datatypes like CLOBs.
edit
I haven't addressed the CREATE TABLE statement. The principle is the same, it is just longer to type out. Besides, I am not entirely clear how it applies to your prior requirement to change the datatype of those columns.
you can generate the query as string and execute it with 'exec' keyword.