I'm trying to solve a stored procedure in oracle based on this statement...
Access control on oracle database where
a) Each user is allowed to access the system within certain time limit of a day. For example, user1 is allowed to access the system from 8 am to 4 pm, while user2 is allowed to access the system from 3 pm to 11 pm.
b) For every user, accounts will be locked upon three logon failures.
c) For every user, idle session will be terminated after 10 minutes.
d) Highly privileged users are allowed to have a maximum of two concurrent sessions at one time, while other users are allowed to have one concurrent session only.
I manage to answer b,c,d question using profile. Then I alter the user to the profile. The stored procedure keep give me compilation error. THANKS IN ADVANCE
Here are my work
create role roleUser;
grant create session to roleUser;
grant select on staff_data to roleUser;
create user user1 identified by abc123;
create user user2 identified by abc123;
--common user privilege
create profile userProfile limit
FAILED_LOGIN_ATTEMPTS 3
IDLE_TIME 10
SESSIONS_PER_USER 1
--high user privilege
create profile userHighProfile limit
FAILED_LOGIN_ATTEMPTS 3
IDLE_TIME 10
SESSIONS_PER_USER 2
alter user user1 profile userProfile;
alter user user2 profile userHighProfile;
grant roleUser to user1,user2;
show error;
create or replace trigger limit_logon
after logon on database
begin
if to_char(sysdate,'HH24') between 8 and 16 then
set roleUser to user1;
elsif to_char(sysdate,'HH24') between 15 and 23 then
set roleUser to user2;
else
revoke roleUser from user1;
revoke roleUser from user2;
end if;
end;
I would imagine that you'd have a table containing ranges of times in which a user is allowed to login, and when a user logs in your logon trigger would check the current time against the range(s) for the user. Create a procedure or function to encapsulate the logic, and let it raise an error if the user is not allowed to logon at that time.
Of course this doesn't log the user out at the end of the window.
I have a feeling that what you also need here is a DBMS_Scheduler job that runs periodically to check that all sessions' users are allowed to be logged in at that time, so that users' sessions can be killed if they stay logged in past their window.
set roleUser to user1; must be execute immediate 'grant roleUser to user1';
revoke roleUser from user1; must be execute immediate 'revoke roleUser from user1';
However, it will not work as desired because GRANT/REVOKE ROLE is executed after logon, thus it does not affect the current session, the user first would have to logoff and logon again.
Instead of grant and revoke a role, you can enable and disable granted roles, this take affect immediatly, see SET ROLE.
Anyway, for your purpose the simplest solution should be this one:
create or replace trigger limit_logon
after logon on database
begin
if USER = 'USER1' AND to_char(sysdate,'HH24') NOT between 8 and 16 then
RAISE_APPLICATION_ERROR (-20001, 'Logon allowed only from 8 to 16');
END IF;
if USER = 'USER2' AND to_char(sysdate,'HH24') NOT between 15 and 23 then
RAISE_APPLICATION_ERROR (-20001, 'Logon allowed only from 15 to 23');
END IF;
end;
You could use Oracle Virtual Private Database hooked up to the Axiomatics Data Access Filter (disclaimer - I work for Axiomatics). In the Data Access Filter, you can define access control policies that are time-based and also include other parameters such as user or data attributes.
That's the cleanest and most sustainable way.
Related
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
Begin
Execute immediate 'CREATE SEQUENCE seq1 MINVALUE 1 MAXVALUE 99 START WITH 1INCREMENT BY 1 CACHE 20';
End;
Sequence creating successfully.
Create or replace procedure proc_seq as
Begin
Execute immediate 'CREATE SEQUENCE seq2 MINVALUE 1 MAXVALUE 99 START WITH 1INCREMENT BY 1 CACHE 20';
End;
Exec proc_seq();
Error: Insufficient Privilege...
This is a invokers/definers rights issue. By default a stored procedure has definer's right (DR - emphasis added):
During a server call, when a DR unit is pushed onto the call stack, the database stores the currently enabled roles and the current values of CURRENT_USER and CURRENT_SCHEMA. It then changes both CURRENT_USER and CURRENT_SCHEMA to the owner of the DR unit, and enables only the role PUBLIC.
If your privilege to create a sequence was granted to you via a role, then as that role will be disabled, the privilege isn't active. You can verify that is the case by running set role none, which will cause your anonymous block to fail with the same error.
As #Littlefoot said you can have the privilege granted directly to you instead of (or as well as) via the role.
Alternatively, you can specify that the procedure should have invoker's rights:
Create or replace procedure proc_seq
authid current_user
as
Begin
Execute immediate 'CREATE SEQUENCE seq2 MINVALUE 1 MAXVALUE 99 START WITH 1INCREMENT BY 1 CACHE 20';
End;
/
exec proc_seq;
PL/SQL procedure successfully completed.
select seq2.nextval from dual;
NEXTVAL
----------
1
Hopefully this is a contrived example; you wouldn't normally create database objects at runtime anyway...
The caveat to this approach is that it might not be suitable for how you intend the procedure to be invoked. If you're running it as yourself then that's fine, but if you will be granting execute permission to other users then they would have to have the relevant privileges (granted either directly or via a role); and would be creating objects in their own schema. That may be exactly what you do want. But if it isn't, and you don't want to grant create sequence to the callers or to have them create their own objects, then you can keep the procedure with definer's rights and grant the privilege to your user directly.
If you acquired CREATE SEQUENCE privilege via role (apparently, you did), it will work in SQL layer as well is in anonymous PL/SQL blocks (your first code).
However, it won't work in named PL/SQL procedures (your second code) - to do so, you (which means: user that runs that code) will have to be granted that privilege directly, not via role.
Disclaimer: I do not venture into the realm of database administration often, and usually stick to data analytics. However, I am trying to educate myself and build my DBA skillset on a small sample database I've created. I am NOT trying to edit anything in production.
This is an extension of this question:
How do I show running processes in Oracle DB?
I would like to create a view for my users that shows a read-only copy of all processes running on my database (similar to what you can do in the terminal of a linux operating system).
I do NOT want to give my users the ability to alter or kill processes that do not belong to them, I just want to give them the ability to easily see how busy the database server is at any given time (I.E.-"read-only" access).
I know to run the below command and allow users to access the view, I need to modify the database permissions. Is there a permission I can enable on the user accounts that will allow them to "select * from WHAT_IS_RUNNING_ON_DB", but not alter/kill processes that they don't own?
CREATE VIEW WHAT_IS_RUNNING_ON_DB AS
SELECT
sess.process, sess.status, sess.username, sess.schemaname,
sql.sql_text
FROM v$session sess,
v$sql sql
WHERE sql.sql_id(+) = sess.sql_id
AND sess.type = 'USER'
AND sess.status = 'ACTIVE'
If you only want them to view what is running, simply:
grant select on v$session to USER1;
grant select on v$sql to USER1;
Then create the view in their schemas. Alternatively, if you don't want them querying on these v$ views directly (because you want to hide the other columns or you only want to keep the code for the view all in one place), you can create a user, grant the above to this new user, create the view in that schema, then grant whoever select against the view.
I do NOT want to give my users the ability to alter or kill processes
that do not belong to them
Although you didn't explicitly ask for it, I interpret this as, you may want to grant users the ability to kill processes that they do own.
Asktom has a good article how to do this:
Create the following procedure in that high privileged user's schema:
create or replace procedure kill_session( p_sid in varchar2,
p_serial# in varchar2)
is
cursor_name pls_integer default dbms_sql.open_cursor;
ignore pls_integer;
BEGIN
select count(*) into ignore
from V$session
where username = USER
and sid = p_sid
and serial# = p_serial# ;
if ( ignore = 1 )
then
dbms_sql.parse(cursor_name,
'alter system kill session '''
||p_sid||','||p_serial#||'''',
dbms_sql.native);
ignore := dbms_sql.execute(cursor_name);
else
raise_application_error( -20001,
'You do not own session ''' ||
p_sid || ',' || p_serial# ||
'''' );
end if;
END;
/
The owner of this procedure needs to have
o SELECT on v_$session granted to them by SYS. This grant must be
directly to them, not via a role.
o ALTER SYSTEM granted directly to them -- not via a role.
You would then grant execute on this procedure to anyone you want. It
would allow them to kill any session they own (running under their
username). You would probably want to "grant select on v_$session"
when connected as SYS to these people as well so they can 'see' the
v$session dynamic performance view to get their sid/serial# pairs
I am sorry for a newbie question. I am creating a readonly user in oracle. I want to limit him just to view and execute a function or procedure. I dont want him to modify those func or proc. Please help me on how to achieve this.
Thanks a lot
-- As sysdba:
-- 1) create an user account
create user <username> identified by <password>;
-- 2) allow user to log in
grant create session to <username>;
-- 3) allow user to execute a single procedure in other schema
grant execute on <other_schema.procedure_name> to <username>;
From SYSDBA user login (from where you created the user), give the following grant :
GRANT EXECUTE ANY PROCEDURE TO user;
GRANT SELECT ANY TABLE TO user;
where user = the username you just created.
Then ,to ensure the user has only read priviledges, check from session_privs that he doesnot have any other priviledge, specifically any "CREATE" prviledge. To do this , run :
select * from session_privs;
from the user you just created.
I would like to write kill_inactive sessions stored procedure for Oracle.
something like
create or replace procedure kill_inactive_sessions as
begin
for rec in (select sid, serial# from sys.v_$session where status = 'INACTIVE')
loop
execute immediate 'alter system kill session '''|| rec.sid || ',' || rec.serial# || ''' IMMEDIATE';
end loop;
end kill_inactive_sessions;
Previous doesn't work (table or view does not exists).
But next select is working perfectly for the same user:
select sid, serial# from sys.v_$session where status = 'INACTIVE';
What I'm missing?
The ORA-00942: table or view does not exist error almost certainly indicates that your access to the v$session view is via a role rather than a direct grant.
If you want to write a definer's rights stored procedure, the owner of the procedure must have the necessary privileges granted directly to the user, not via a role (and remember that DBA is just another role). Most likely, if you disabled roles in your session, the SELECT statement outside of the procedure would not work. In SQL*Plus, for example
SQL> set role none;
SQL> select sid, serial# from sys.v_$session where status = 'INACTIVE';
will likely throw the same ORA-00942 error. Assuming it does, you need to grant the user that owns the stored procedure privileges directly. For example
GRANT SELECT ANY DICTIONARY
TO user_that_owns_the_procedure;
The same thing will also apply to the ALTER SYSTEM command that you are building and running. The owner of the stored procedure will need to have privileges to run that command via a direct grant not via a role.
All that said, a procedure that kills all inactive sessions is highly problematic. The vast majority of sessions are going to be inactive the vast majority of the time. That doesn't mean that they should be killed. Even if you're cleaning up after an application server crash, you really ought to be applying some additional predicates (for example, looking for sessions from a particular machine that are logged in as a particular user that were established before the application server crashed). Long-term, though, I'd suggest enabling dead connection detection so that the database can automatically take care of closing sessions when the client process dies unexpectedly.