I have to protocol some Information concerning logon/logoff to an Oracle database, a short code snipped als follows:
(create table for logs, create Trigger on logon/logoff)
CREATE TABLE WHEREVER.LOG_TABLE (
TIMESTAMP DATE DEFAULT SYSDATE,
WHAT VARCHAR2(32),
TERMINAL VARCHAR2(32)
)
/
CREATE OR REPLACE TRIGGER WHEREVER.TRIGGER_LOGON AFTER LOGON ON DATABASE
DECLARE PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
INSERT INTO WHEREVER.LOG_TABLE (
WHAT,
TERMINAL
)
SELECT
'LOGON',
SYS_CONTEXT('USERENV','TERMINAL')
FROM DUAL
;
COMMIT;
END
/
CREATE OR REPLACE TRIGGER WHEREVER.TRIGGER_LOGOFF BEFORE LOGOFF ON DATABASE
DECLARE PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
INSERT INTO WHEREVER.LOG_TABLE (
WHAT,
TERMINAL
)
SELECT
'LOGOFF',
SYS_CONTEXT('USERENV','TERMINAL')
FROM DUAL
;
COMMIT;
END
/
If I start this script (w/o logon- trigger) as sys as sysdba i get no errors.
But user, who wants to disconnect gets an error
ORA 04045: errr during recompilation/revalidation of Trigger
ORA 01031: insufficient privileges
I assume, that inserting is not allowed to user.
What could be the minimum Privileg situation, that this table/trigger - combination works, even for users, that are inserted later? who should be owner for table and triggers and in which schema I should put these things (WHEREVER)
Related
This issue has been found in Oracle 12.2 only. Oracle 12.1 seems OK.
I provide everything to reproduce, but you also need to create an Oracle directory. I named it DUMP_DIRECTORY.
We have 2 SQL procedures:
EXPORT_TABLE: Export a table.
IMPORT_TABLE: Import a table.
First we create a simple table:
create table MY_TABLE as select 1 as my_column from dual;
Secondly we call the export procedure:
call EXPORT_TABLE('MY_EXPORT_JOB', 'DUMP_DIRECTORY', 'file.dat', 'MY_TABLE', 'export.log');
Thirdly we drop the table:
drop table MY_TABLE;
Finally we call the import procedure:
call IMPORT_TABLE('MY_IMPORT_JOB', 'DUMP_DIRECTORY', 'file.dat', 'import.log');
Almost everything seems fine:
The procedures are successfully executed.
There are no errors in export.log and import.log.
The table is correctly imported back.
But there are errors in the import job master table:
select ERROR_COUNT, OBJECT_TYPE_PATH from MY_IMPORT_JOB where ERROR_COUNT > 0;
ERROR_COUNT OBJECT_TYPE_PATH
----------- -------------------------------------------------------------------
1 SCHEMA_EXPORT/TABLE/TABLE
1 SCHEMA_EXPORT/TABLE/TABLE
How to fix or avoid these errors?
Here is EXPORT_TABLE procedure:
create or replace procedure EXPORT_TABLE
(
JOB_NAME varchar2,
DIRECTORY_NAME varchar2,
DMP_FILE_NAME varchar2,
TABLE_NAME varchar2,
LOG_FILE_NAME varchar2
)
as
begin
declare
handle number(24);
begin
-- Open the handle
handle := DBMS_DATAPUMP.open('EXPORT', 'SCHEMA', NULL, JOB_NAME);
-- Keep master table to be able to use get_status
DBMS_DATAPUMP.set_parameter(handle, 'KEEP_MASTER', 1);
-- Configure files
DBMS_DATAPUMP.add_file(handle, DMP_FILE_NAME, DIRECTORY_NAME);
DBMS_DATAPUMP.add_file(handle, LOG_FILE_NAME, DIRECTORY_NAME, null, DBMS_DATAPUMP.KU$_FILE_TYPE_LOG_FILE);
-- Filter on given table names
DBMS_DATAPUMP.metadata_filter(handle, 'NAME_LIST', ''''||TABLE_NAME||'''', 'TABLE');
DBMS_DATAPUMP.metadata_filter(handle, 'INCLUDE_PATH_EXPR', '= ''TABLE''');
DBMS_DATAPUMP.start_job(handle);
-- Disconnects a user session from a job.
DBMS_DATAPUMP.detach(handle);
end;
end;
Here is IMPORT_TABLE procedure:
create or replace procedure IMPORT_TABLE
(
JOB_NAME varchar2,
DIRECTORY_NAME varchar2,
DMP_FILE_NAME varchar2,
LOG_FILE_NAME varchar2
)
as
begin
declare
handle number(24);
begin
handle := DBMS_DATAPUMP.open('IMPORT', 'FULL', NULL, JOB_NAME);
-- Keep master table to be able to use get_status
DBMS_DATAPUMP.set_parameter(handle, 'KEEP_MASTER', 1);
DBMS_DATAPUMP.set_parameter(handle, 'METRICS', 1);
-- Configure files
DBMS_DATAPUMP.add_file(handle, DMP_FILE_NAME, DIRECTORY_NAME);
DBMS_DATAPUMP.add_file(handle, LOG_FILE_NAME, DIRECTORY_NAME, null, DBMS_DATAPUMP.KU$_FILE_TYPE_LOG_FILE);
-- Change default behavior
DBMS_DATAPUMP.set_parameter(handle, 'TABLE_EXISTS_ACTION', 'REPLACE');
-- Start the job
DBMS_DATAPUMP.start_job(handle);
-- Disconnects a user session from a job.
DBMS_DATAPUMP.detach(handle);
end;
end;
Two questons during creation of Virtual Private Database.
Which privileges are needed to run a database logon trigger? The users need special privileges?
Error message: problem with the function, but the function works well.
The codes from the user and table creation:
-- as SYS:
-- in the pdb:
alter session set container = orclpdb;
create user orders identified by orders;
create user vpd_admin identified by vpd_admin;
create user hanna identified by hanna;
create user smith identified by smith;
grant create session to orders, vpd_admin, hanna, smith;
grant create table, unlimited tablespace to orders, vpd_admin;
grant execute on dbms_rls to vpd_admin;
grant create procedure to vpd_admin;
-- in SQL Developer, you can build connections now
-- service name = orclpdb
-- in ORDERS schema:
create table orderinfo2
(ordid number,
product varchar2(10),
custid number);
create table customers
(custid number,
name varchar2(10));
insert into orderinfo2 values (6001, 'coctail', 101);
insert into orderinfo2 values (6002, 'wine', 101);
insert into orderinfo2 values (6003, 'coctail', 102);
insert into customers values (101, 'hanna');
insert into customers values (102, 'smith');
grant select on customers to vpd_admin, hanna, smith;
grant select on orderinfo2 to vpd_admin, hanna, smith;
-- in SYS:
alter session set container = orclpdb;
grant create any trigger to vpd_admin;
grant administer database trigger to vpd_admin;
Package01: custid into kod variable
-- in vpd_admin schema:
create or replace package
vpd_admin.order_sec_ident
is procedure kod_variable;
end;
/
create or replace package body
vpd_admin.order_sec_ident
is procedure
kod_variable
is
kod number;
begin
select custid into kod
from orders.customers where
trim(upper(name)) =
sys_context('USERENV', 'SESSION_USER');
dbms_session.set_context
('ORDER_NS', 'KOD_ARG', to_char(kod));
exception
when no_data_found then
dbms_session.set_context
('ORDER_NS', 'KOD_ARG', '-1');
end;
end order_sec_ident;
/
grant execute on order_sec_ident to public;
logon trigger:
create or replace trigger logon_trigger
after logon
on database
begin vpd_admin.order_sec_ident.kod_variable;
end;
/
package2: condition into policy
create or replace package vpd_admin.orders_cond as
function cond
(schema_v varchar2,
table_v varchar2)
return varchar2;
pragma restrict_references (cond, wnds);
end;
/
create or replace package body vpd_admin.orders_cond as
function cond
(schema_v varchar2,
table_v varchar2)
return varchar2
is
wherevalue varchar2(2000);
begin
if
trim(upper(user)) <> schema_v
and trim(upper(user)) <> 'SYS'
and trim(upper(user)) <>'SYSTEM' then
wherevalue := 'trim(upper(name)) =
sys_context (''ORDER_NS'', ''KOD_ARG'')';
else wherevalue := '1=1';
end if;
return wherevalue;
end cond;
end;
/
dbms_rls.add_policy
begin
dbms_rls.add_policy
('orders',
'orderinfo2',
'ord_sec_pol2',
'vpd_admin',
'orders_cond.cond',
'SELECT');
end;
/
problem1: user cannot logon due to the logon_trigger
question1: the users have not enough privileges to run the trigger, what privileges need to be granted?
An error was encountered performing the requested operation:
ORA-04088: error during execution of trigger 'VPD_ADMIN.LOGON_TRIGGER'
ORA-00604: error occurred at recursive SQL level 1
ORA-01031: insufficient privileges
ORA-06512: at "SYS.DBMS_SESSION", line 130
ORA-06512: at "VPD_ADMIN.ORDER_SEC_IDENT", line 12
ORA-06512: at line 1
04088. 00000 - "error during execution of trigger '%s.%s'"
*Cause: A runtime error occurred during execution of a trigger.
*Action: Check the triggers which were involved in the operation.
Vendor code 4088
-- (as sys granted dba to smith so I can continue testing)
question2-1: how can I review the trace file?
question2-2: what is the problem with the function?
-- in smith schema:
select * from orders.orderinfo2;
ORA-28113: policy predicate has error
28113. 00000 - "policy predicate has error"
*Cause: Policy function generates invalid predicate.
*Action: Review the trace file for detailed error information.
running the fucntion script separetely, it works:
-- in vpd_admin:
create or replace
function cond
(schema_v varchar2,
table_v varchar2)
return varchar2
is
wherevalue varchar2(2000);
begin
if
trim(upper(user)) <> schema_v
and trim(upper(user)) <> 'SYS'
and trim(upper(user)) <>'SYSTEM' then
wherevalue := 'trim(upper(name)) =
sys_context (''ORDER_NS'', ''KOD_ARG'')';
else wherevalue := '1=1';
end if;
return wherevalue;
end cond;
/
declare
x varchar2(20) := 'aa';
y varchar2(20) := 'bb';
begin
dbms_output.put_line(cond(x, y));
end;
/
-- result: trim(upper(name)) =
sys_context ('ORDER_NS', 'KOD_ARG')
In response to:
question1: the users have not enough privileges to run the trigger,
what privileges need to be granted?
The problem here is that the trigger executes but throws an exception. An exception in login stops the logon and can have widespread impact on the database, which is of course undesirable. Because of this, logon triggers are often created to be exception-free or carefully control any possible exceptions.
In this example, vpd_admin.order_sec_ident has some handling for no data found, but can throw other exceptions and the handler itself could throw an exception. Some implementations make use of EXCEPTION WHEN OTHERS in database-wide logon triggers to ensure no exceptions. If the exception in the trigger is resolved here, users will be able to log in again.
The priv problem you are seeing may be from the use of DBMS_SESSION.SET_CONTEXT in the procedure. Ensuring vpd_admin has access and runs the context setup in its auth can address the priv problem.
In response to:
question2-1: how can I review the trace file?
Alerts, trace file conditions, locations, etc. are configurable. Documentation has more info
In response to:
question2-2: what is the problem with the function?
Table orderinfo2 does not have a name column. The name in the clause 'trim(upper(name)) = sys_context ('ORDER_NS', 'KOD_ARG')' yields invalid sql when it is executed.
A policy on orderinfo2 must be valid for columns ordid, product, or custid (or valid and not include any columns, as with the 1=1 in your example).
This exception will be corrected by changing the 'trim(upper(name)) = sys_context ('ORDER_NS', 'KOD_ARG')' condition so it would be valid in a statement like SELECT * FROM ORDERINFO2 WHERE <<predicate>>;
I created a table that is used for tracking the creation of tables in the schema but when the trigger compiles it is throwing an error "no more data from socket" and I lose connection to the database.
My audit table is as follows:
CREATE TABLE TABLE_AUDIT(
DATE_CREATED DATE,
USERNAME VARCHAR2(30),
TABLE_NAME VARCHAR2(30))
And my trigger:
create or replace TRIGGER tableCreationTrigger
AFTER CREATE
ON SCHEMA
DECLARE
date_created TABLE_AUDIT.DATE_CREATED%type;
username TABLE_AUDIT.USERNAME%type;
table_name TABLE_AUDIT.TABLE_NAME%type;
BEGIN
date_created := sysdate;
SELECT USER
into username
from dual;
:New.table_Name := table_name;
INSERT INTO TABLE_AUDIT (DATE_CREATED, USERNAME, TABLE_NAME)
Values(DATE_CREATED, USERNAME, TABLE_NAME);
EXCEPTION
WHEN others THEN
DBMS_OUTPUT.PUT_LINE('Error on insert into table_audit');
RAISE;
END;
/
Please try to restart the DB and try again .
"no more data from socket" is due to DB is not able to respond to the query.
Is it possible to parameterize schema and table names used in queries within stored procedures using dynamic synonyms?
What we have tried is
setting parameters in a name/value pair table
reading those parameters at run time to determine schema (and table names), as the targets change based on the mode of the
application
dropping any existing synonym and recreating for the schema and tables
referencing the synonyms in the queries
The reason we are attempting this approach is because the queries to be executed are merge statements that are hundreds of lines long, not suited for dynamic SQL.
Maybe you can cover your problem when you use Invoker Rights for your procedure. Have a look at this example:
CREATE USER SCOTT_1 IDENTIFIED BY "tiger";
GRANT UNLIMITED TABLESPACE TO SCOTT_1;
CREATE USER SCOTT_2 IDENTIFIED BY "tiger";
GRANT UNLIMITED TABLESPACE TO SCOTT_2;
CREATE TABLE SCOTT_1.EMP (EMP_NAME VARCHAR2(30));
CREATE TABLE SCOTT_2.EMP (EMP_NAME VARCHAR2(30));
INSERT INTO SCOTT_1.EMP VALUES ('Schema 1');
INSERT INTO SCOTT_2.EMP VALUES ('Schema 2');
COMMIT;
CREATE SYNONYM EMP FOR SCOTT_1.EMP; -- Just needed to compile the procedure
CREATE OR REPLACE FUNCTION GetSchema(p_schema IN VARCHAR2) RETURN VARCHAR2
AUTHID CURRENT_USER
AS
res VARCHAR2(30);
BEGIN
EXECUTE IMMEDIATE 'ALTER SESSION SET CURRENT_SCHEMA = ' || p_schema;
SELECT EMP_NAME
INTO res
FROM EMP;
-- Just switch back to own schema to avoid unexpected behaviors
EXECUTE IMMEDIATE 'ALTER SESSION SET CURRENT_SCHEMA = '||USER;
RETURN res;
END;
/
SELECT GetSchema('SCOTT_1') FROM dual;
GETSCHEMA('SCOTT_1')
---------------------------------
Schema 1
1 row selected.
SELECT GetSchema('SCOTT_2') FROM dual;
GETSCHEMA('SCOTT_2')
---------------------------------
Schema 2
1 row selected.
I've a stored procedure created by EMP_DBA and part of the query will check whether the existing tables exist or not, if exist then drop table. This query works fine if I connect as EMP_DBA, now I want to run this stored procedure with other account let say USER1 and I've grant all the necessary rights to USER1. How to rewrite below statement in order count return 1 if the table MARKET_DATA exist in schema EMP_DBA ?
BEGIN
SELECT COUNT(*) INTO c
FROM all_tables
WHERE
table_name = 'MARKET_DATA' AND OWNER = 'EMP_DBA';
IF C = 1 THEN
EXECUTE IMMEDIATE 'DROP TABLE MARKET_DATA';
--exception when others then null;
END IF;
"I've grant all the necessary rights to USER1"
This is a slightly worrying statement. What do you mean by all the necessary rights ? The only appropriate right is execute on a stored procedure owned by EMP_DBA. That procedure should encapsulate everything. EMP_DBA doesn't (or shouldn't) want USER1to drop their tables independently. Besides it isn't possible to grant DDL statements on specific objects, or even specific schemas. And DROP ANY is a powerful privilege to hand out.
The best way to write the stored procedure is to use definer's rights (which is the default). This ensures that the code is executed with the privileges of the stored procedure's owner, not those of the executing user. That your code doesn't work - presumably because you haven't specified the table owner - suggests you haven't got your security model quite right.
In my version I've used ALL_TABLES just like you did, to show the difference between CURRENT_USER and SESSION_USER, but actually USER_TABLES would work just as well.
create or replace procedure recreate_tab
(p_tab_name in all_tables.table_name%type)
authid definer
is
n pls_integer;
begin
select count(*)
into n
from all_tables
where owner = (sys_context('userenv','current_user'))
and table_name = p_tab_name;
if n = 1
then
-- no need to specify schema because it's the procedure owner
execute immediate 'drop table '|| p_tab_name;
end if;
execute immediate 'create table '||p_tab_name
||' ( id number, descr varchar2(30))';
-- grant rights on the new table to the user executing the procedure
execute immediate 'grant select on '||p_tab_name||' to '
|| sys_context('userenv','session_user');
end recreate_tab;
/
grant execute on recreate_tab to user1
/
So. Nothing up my sleeve ...
SQL> conn user1/user1
Connected.
SQL> select count(*) from t42
2 /
select count(*) from t42
*
ERROR at line 1:
ORA-00942: table or view does not exist
SQL> select count(*) from emp_dba.t42
2 /
COUNT(*)
----------
56179
SQL> exec emp_dba.recreate_tab('T42')
PL/SQL procedure successfully completed.
SQL> select count(*) from emp_dba.t42
2 /
COUNT(*)
----------
0
SQL>
Your select is correct. You should rewrite the EXECUTE IMMEDIATE to do
DROP TABLE EMP_DBA.MARKET_DATA