I want to restrict Oracle users from logging into database except for a couple of terminal. I have written below trigger.
CREATE OR REPLACE TRIGGER TRG_IP_RESTRICT
AFTER LOGON ON DATABASE
DECLARE
V_USER VARCHAR2(30);
V_GRP VARCHAR2(50);
BEGIN
SELECT USER INTO V_USER FROM DUAL;
V_GRP := SYS_CONTEXT('USERENV', 'TERMINAL');
IF V_USER IN ('<list of users>') THEN
IF V_GRP NOT IN ('<list of terminals>') THEN
RAISE_APPLICATION_ERROR(-20001,
'Access Denied by DBA TEAM : ' || V_GRP ||
' on ' || V_USER || ' from ' ||
SYS_CONTEXT('USERENV', 'IP_ADDRESS'));
END IF;
END IF;
END;
Even though it's working fine and going in exception part, which should happen technically. But at the same time, it is allowing the connection instead of showing the error message to the user.
Can someone please help?
Such trigger works only for non-DBA users, none precisely users which do not have adminster database trigger privilege.
Otherwise you may block your entire database.
According to the PL/SQL Language Reference:
If the system trigger is a DATABASE LOGON trigger and the user has
ADMINISTER DATABASE TRIGGER privilege, then the user is able to log on
successfully even if the trigger raises an exception. For SCHEMA LOGON
triggers, if the user logging on is the trigger owner or has ALTER ANY
TRIGGER privileges then logon is permitted. Only the trigger action is
rolled back and an error is logged in the trace files and alert log.
You can workaround this restriction by raising an ORA-600 error that will break the entire session. The error message won't be helpful to the user but it at least stops them.
The sample code below will stop absolutely everyone from connecting to the database, even SYSDBA. Be very careful running this. Make sure you have another session connected to the database and run drop trigger TRG_IP_RESTRICT; when you are done testing it.
CREATE OR REPLACE TRIGGER TRG_IP_RESTRICT
AFTER LOGON ON DATABASE
DECLARE
V_USER VARCHAR2(30);
V_GRP VARCHAR2(50);
--Only an ORA-600 error can stop logons for users with either
--"ADMINISTER DATABASE TRIGGER" or "ALTER ANY TRIGGER".
--The ORA-600 also generates an alert log entry and may warn an admin.
internal_exception exception;
pragma exception_init( internal_exception, -600 );
BEGIN
SELECT USER INTO V_USER FROM DUAL;
V_GRP := SYS_CONTEXT('USERENV', 'TERMINAL');
IF V_USER IN ('<list of users>') THEN
IF V_GRP NOT IN ('<list of terminals>') THEN
raise internal_exception;
-- RAISE_APPLICATION_ERROR(-20001,
-- 'Access Denied by DBA TEAM : ' || V_GRP ||
-- ' on ' || V_USER || ' from ' ||
-- SYS_CONTEXT('USERENV', 'IP_ADDRESS'));
END IF;
END IF;
END;
/
With that trigger in place, even DBA users will get this error message when they connect:
ERROR:
ORA-00600: internal error code, arguments: [600], [], [], [], [], [], [], [],
[], [], [], []
ORA-06512: at line 21
Related
Could you please help me in a unique situation I am in. I am receiving "ORA-30511: invalid DDL operation in system triggers" when dropping sequences and procedures during logoff trigger.
I need to delete tables, sequences and procedures of users before logoff event happens. I am writing the table details in DB_OBJECTS table upon create using a separate trigger. Below is my logoff trigger - could you please help me where I am doing wrong. Dropping tables is working fine in the below code. Only Dropping sequences and procedures is giving me "ORA-30511: invalid DDL operation in system triggers" error.
CREATE OR REPLACE TRIGGER DELETE_BEFORE_LOGOFF
BEFORE LOGOFF ON DATABASE
DECLARE
USER_ID NUMBER := SYS_CONTEXT('USERENV', 'SESSIONID');
BEGIN
FOR O IN (SELECT USER, OBJECT_NAME, OBJECT_TYPE
FROM DB_OBJECTS WHERE SID = USER_ID
AND USERNAME = USER AND SYSDATE > CREATED_DTTM) LOOP
IF O.OBJECT_TYPE = 'TABLE' THEN
EXECUTE IMMEDIATE 'DROP TABLE ' || O.USER || '.' || O.OBJECT_NAME || ' CASCADE CONSTRAINTS';
ELSIF O.OBJECT_TYPE = 'SEQUENCE' THEN
EXECUTE IMMEDIATE 'DROP SEQUENCE ' || O.USER || '.' || O.OBJECT_NAME;
ELSIF O.OBJECT_TYPE = 'PROCEDURE' THEN
EXECUTE IMMEDIATE 'DROP PROCEDURE ' || O.USER || '.' || O.OBJECT_NAME;
END IF;
END LOOP;
EXCEPTION WHEN NO_DATA_FOUND THEN NULL;
END;
/
That's a simple one.
Error code: ORA-30511
Description: invalid DDL operation in system triggers
Cause: An attempt was made to perform an invalid DDL operation in a system trigger. Most DDL operations currently are not supported in system triggers. The only currently supported DDL operations are table operations and ALTER/COMPILE operations.
Action: Remove invalid DDL operations in system triggers.
That's why only
Dropping tables is working fine
succeeded.
Therefore, you can't do that using trigger.
You asked (in a comment) how to drop these objects, then. Manually, as far as I can tell. Though, that's quite unusual - what if someone accidentally logs off? You'd drop everything they created. If you use that schema for educational purposes (for example, every student gets their own schema), then you could create a "clean-up" script you'd run once class is over. Something like this:
SET SERVEROUTPUT ON;
DECLARE
l_user VARCHAR2 (30) := 'SCOTT';
l_str VARCHAR2 (200);
BEGIN
IF USER = l_user
THEN
FOR cur_r IN (SELECT object_name, object_type
FROM user_objects
WHERE object_name NOT IN ('EMP',
'DEPT',
'BONUS',
'SALGRADE'))
LOOP
BEGIN
l_str :=
'drop '
|| cur_r.object_type
|| ' "'
|| cur_r.object_name
|| '"';
DBMS_OUTPUT.put_line (l_str);
EXECUTE IMMEDIATE l_str;
EXCEPTION
WHEN OTHERS
THEN
NULL;
END;
END LOOP;
END IF;
END;
/
PURGE RECYCLEBIN;
It is far from being perfect; I use it to clean up my Scott schema I use to answer questions on various sites so - once it becomes a mess, I run that PL/SQL code several times (because of possible foreign key constraint).
Other option is to keep a create user script(s) (along with all grant statements) and - once class is over - drop existing user and simply recreate it.
Or, if that user contains some pre-built tables, keep export file (I mean, result of data pump export) and import it after the user is dropped.
There are various options - I don't know whether I managed to guess correctly, but now you have something to think about.
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 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';
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';
This question already has an answer here:
Closed 10 years ago.
Possible Duplicate:
ORACLE After update trigger: solving ORA-04091 mutating table error
So I have a trigger to check if a an admin has been locked out of login (if they are they will have a 1 set to temp_pw. It then send the admin a four digit pass code to unlock their account. Problem is I update the failed_logins field, incrementing it by 1 for every failed login before the trigger is called.
The rest of the trigger checks if there is an admin has a locked account before sending them an email with a pass code.
If I take out the Update Pi_admin_table set blah blah it sends the email but if I include it to insert the new pass code in the table it errors with this:
Message: 60 ORA-00060: deadlock detected while waiting for resource
ORA-06512: at "PI_USER_ADMIN.TR_ADMIN_LOCKOUT", line 17
ORA-04088: error during execution of trigger
'PI_USER_ADMIN.TR_ADMIN_LOCKOUT' UPDATE *pi_admin_table SET
failed_logins = failed_logins + 1 where
EMAIL='nathan#perceptive.co.uk' returning failed_logins into :bind_var
Here's my trigger:
create or replace
TRIGGER "TR_ADMIN_LOCKOUT"
AFTER UPDATE ON PI_ADMIN_TABLE
for each row
declare
-- pragma autonomous_transaction seems to fix trigger mutation errors.
-- Look at rewriting trigger later.
--pragma autonomous_transaction;
tempEmail varchar2(80 BYTE);
tempID varchar2(80 BYTE);
mail_host varchar2(255);
mail_port varchar2(255);
mail_from varchar2(255);
tempPW int;
begin
SELECT EMAIL, ADMINID
into tempEmail, tempID
from pi_admin_table
where TEMP_PW = :NEW.TEMP_PW;
SELECT MAIL_HOST, MAIL_PORT, MAIL_FROM
into mail_host, mail_port, mail_from
from pi_settings_table;
select dbms_random.value(1,10000)
into tempPW
from dual;
if tempEmail IS NOT NULL then
UPDATE PI_ADMIN_TABLE SET RESET_PW=round(tempPW) where adminid=tempID;
send_mail(tempEmail,
mail_host,
mail_port,
mail_from,
'Locked Out Event',
'Your administrator account was locked out. '|| chr(10) || chr(10) ||
'Please use this four digit pass code next time try to log in' ||
chr(10) || chr(10) ||
'Temp pass code: '|| round(tempPW) );
end if;
END;
Oracle does not allow code in a ROW trigger to issue a SELECT, INSERT, UPDATE, or DELETE against the table on which the trigger is defined. Your choices are to use an AUTONOMOUS TRANSACTION (but see the warning at the post referenced in #Ben's comment above) or use a COMPOUND TRIGGER.
Share and enjoy.
I would recommend you not to use trigger for that task. Encapsulate the logic you're trying to achieve in stored procedure.