How to grant temporary access to an application-user?
First, a brief explanation of the terms of Oracle “user” and “schema”. It is like in Unix where you have a “user”, “login”, an “account” which is similar to the Oracle “user”, and you have a “home” (a directory for your files). Similar, you have a “schema”
You have a production application schema APP1 that owns the objects, but normally no users will login as this application schema. But now, a developer needs temporary access. The developer requested a 2-hour maintenance window, and this was approved by the application manager.
We as DBAs can unlock the account, give the developer the password, wait 2 hours, then lock the password and kill all sessions of the application user.
It would be easier that Oracle will do that for us. Isn’t it?
My approach is to use a logon trigger creating a scheduler job to kill the session.
- Check if it is a logon to APP1
- Check if there is already a scheduler job to kill the sessions of APP1
- If not, it is the 1st login and we create the job
The job will do:
- lock the account
- kill the sessions of APP1
In this example, I will use the SYSTEM user. It is also possible to use another user with the required privileges, e.g. “ADMINISTER DATABASE TRIGGER“.
For the pl/sql part, we need a direct grant to the SYSTEM user:
-- as sysdba:
grant select on v_$session to system;
grant create job to system;
grant alter user to system;
To be flexible I will create a table with the users and timeouts
create table USER_TIMEOUT (
USERNAME varchar2(30) not null,
TIMEOUT number not null
);
alter table USER_TIMEOUT
add constraint USER_TIMEOUT_PD
primary key ( USERNAME ) enable;
First, create the procedure to lock the user and kill all existin sessions of it.
create or replace procedure EXPIRE_USER (v_username in varchar2) as
v_cnt number default 0;
v_error_msg VARCHAR2(4000);
begin
execute immediate 'alter user '|| V_USERNAME ||' ACCOUNT LOCK';
for s in (select SID, SERIAL# from V$SESSION where USERNAME = v_username) loop
begin
execute immediate 'alter system kill session ''' || s.SID || ',' || s.SERIAL# || ''' immediate';
exception
when others then null; -- session already ended?
end;
end loop;
end;
/
This procedure will be called by the job submitted by the logon-trigger
create or replace NONEDITIONABLE TRIGGER app1_logon_timer
AFTER LOGON ON DATABASE
DECLARE
v_cnt NUMBER;
v_timeout number;
v_error_msg VARCHAR2(4000);
BEGIN
select timeout into v_timeout from user_timeout where username=USER;
-- NO_DATA_FOUND is handled by WHEN OTHERS...
select count(*) into v_cnt from user_scheduler_jobs
where job_name = 'EXPIRE_JOB_'||USER;
if v_cnt = 0 then
DBMS_SCHEDULER.CREATE_JOB(
job_name => 'EXPIRE_JOB_'||USER,
job_type => 'STORED_PROCEDURE',
job_action => 'EXPIRE_USER',
number_of_arguments => 1,
start_date => SYSTIMESTAMP + NUMTODSINTERVAL(v_timeout, 'MINUTE'),
enabled => FALSE,
auto_drop => TRUE,
comments => 'Kills '||USER||' sessions and locks account '||v_timeout||' hour after first login');
DBMS_SCHEDULER.SET_JOB_ARGUMENT_VALUE(
job_name => 'EXPIRE_JOB_'||USER,
argument_position => 1,
argument_value => USER
);
DBMS_SCHEDULER.ENABLE('EXPIRE_JOB_'||USER);
end if;
EXCEPTION
WHEN OTHERS THEN NULL; -- never block logons if this fails
END;
/
And for your convenience, a small pl/sql procedure to unlock a user and to set a timeout. There is no errorhandling. It is up to you to add it or to enter correct data.
create or replace NONEDITIONABLE PROCEDURE set_user_timeout (
p_username IN VARCHAR2,
p_timeout IN NUMBER
) AUTHID DEFINER
AS
BEGIN
EXECUTE IMMEDIATE 'ALTER USER ' || p_username || ' ACCOUNT UNLOCK';
MERGE INTO user_timeout t
USING ( SELECT p_username AS username, p_timeout AS timeout FROM dual) s
ON (t.username = s.username)
WHEN MATCHED THEN
UPDATE SET t.timeout = s.timeout
WHEN NOT MATCHED THEN
INSERT (username, timeout) VALUES (s.username, s.timeout);
COMMIT;
END;
/
All the above PL/SQL code is tested in my laboratory environment. Before using the code in a production environment, test it thoroughly to make sure it runs correctly in your environment. I cannot guarantee this. And of course, you can adapt the code to suit your needs.