Skip to Main Content

Breadcrumb

Question and Answer

Tom Kyte

Thanks for the question, KK.

Asked: July 18, 2000 - 11:13 pm UTC

Last updated: May 31, 2011 - 12:40 pm UTC

Version: 4.0.8

Viewed 1000+ times

You Asked

I've created serveral web pages in the OAS. When the user
enters the first page, hs is prompted to input his
username and password. Then I will use the package "OWA_COOKIE"
to store the username in the cookie file.

Then, the second page has some links which calls different
reports generated in the WebDB. Different users have different
priviledges to browse the report. Therefore, when the user
clicks the link, I will get his username from the cookie and
execute a procedure to check whether he can browse the report
or not.

Problem: if another user copies the cookie file from the others
and skip the first login page(directly type the URL of the
second page), then he can bypass the
authentication step and browse any report he likes.

What can I do with OAS in order to resolve the problem?

and Tom said...

Well, this really has nothing to do with OAS -- this has to do with the way you are generating cookies yourself. Every webserver/database/browser configuration in the world would exhibit the above behaviour given the way you have coded this application.

The best method for doing this with WebDB is to use its native database authentication. In this fashion, the browser will prompt you for a username and password. There are no cookies anywhere. In your application, you can simply look at the USER psuedo variable (eg: select user from dual) and that'll tell you who is who. Then you can even use things like VIEWS, Fine Grained Access Control, GRANTS and ROLES to control who can see what data (without coding it all yourself).

If you cannot do that, then you'll need to do a couple of things

1) do not use persistent cookies (cookies with an expiration date/time) as you are now. In that fashion, they (the cookie) will never be stored on disk.

2) obfusicate the cookie, do not put the username into it and encode your own timeout into it.


A common method is to use a sessionid that changes frequently and it randomly generated. You store the user session state in a database table and that table is keyed by the session id. For example -- you might have a table like:

create table session_table
( session_id number primay key,
timestamp date,
username varchar2(30),
<other state variables you need>
)
/


Your login routine would do something like:

procedure login( p_username in varchar2, p_password in varchar2)
as
l_session_id number;
begin
-- validate the username/password is correct here...

if ( it is correct )
then
select session_seq.nextval ||
to_char(random.rand,'fm00000') into l_session_id
from dual;

insert into session_table values
( l_session_id, SYSDATE, p_username, .... );

-- set the cookie to be l_session_id
-- do whatever
else
-- tell them it failed....
end if;
end;

So, here the cookie is NOT the usernam (very very trivial to guess) it is a unique number that is randomized (pretty hard to guess, you would have to guess). See
</code> http://asktom.oracle.com/Misc/Random.html <code>
for info on getting a random number in plsql if you do not know how.


Now, your other routines would all call a common routine to "authenticate" the user. It would look like this:


function authenticated_userame return varchar2
is
l_rec session_table%rowtype;
l_id number;
l_username varchar2(30);
begin

-- get the cookie value into l_id

update session_table
set timestamp = SYSDATE
where session_id = l_id
AND timestamp > sysdate - 1/24
returning username into L_USERNAME; -- expire the session
-- after one hour of
-- inactivity

if ( sql%rowcount = 0 ) then
-- tell them the login either hasn't happened or
-- has timed out
return NULL;
else
return l_username;
end if;
end;


This routine will expire inactive sessions after 1 hour and will either return a valid usename or NULL in the event the user is not logged in.

Rating

  (31 ratings)

Is this answer out of date? If it is, please let us know via a Comment

Comments

Cookie not validated

Dave, February 20, 2003 - 8:34 am UTC

When using the above methodology as described below, I've had some problems with the browser recognizing the cookie:

-setting up a login
-hashing the user/pass against the database
-setting up a cookie with a randomly generated session id
with a timeout etc.


After I set the cookie with the session id after the login I have the user directed to another procedure based on the login. Although, this does not happen. Due to error checking in the procedure the user is being directed to(authenticate user)if there is not a valid cookie with the appropriate info the user is then prompted for a username and password again at a login prompt.

This shouldnt happen, the user based on their initial login should be directed to the appropriate page. Essentially after establishing the cookie the web browser is not seeing it. But after they login a second time the program works fine.

Any insights or workarounds for this would be great!

D


Tom Kyte
February 20, 2003 - 8:41 am UTC

i don't know what you mean by "problems with the browser recognizing the cookie"


You have a bug in your code -- cause this is what thousands and thousands of sites do (perhaps millions even). If you put up the most SIMPLE and SMALLEST of test cases possible -- stripping out every non-relevant bit of code and just getting it down to "login procedure" and "first page of application", we can take a look.

You do know you cannot "read" a cookie in your procedure after setting it right? I mean -- the cookie set is just writing a http header. you don't "see that" in your procedure. But it is OK you cannot "see it" cause you SET IT and hence you KNOW IT.

I don't understand the references to the browser here, don't see where that fits in as the browser just holds and sends cookies, it doesn't really "read" and "use" them.

Thank you and thank you again.

Todd Smith, March 12, 2003 - 1:28 pm UTC

Cleared all the fuzzy areas I was having with cookies and oas.

What about other applications

Raghu, October 01, 2003 - 8:00 pm UTC

Hi Tom,

Expanding the example a bit further. Say I have two applications - one using the pl/sql iAS and the other is a asp/IIS front end.
What's the 'best practice' to allow a single user signon?

Can you crtique this plan - Create a single USER table with usernames/encrypted passwords and have a single web interface to it that verifies that the correct user/pass are entered and if yes, then writes a cookie to the user's computer.

This way whether the user enters via the ias or iis - or some future third method - each application's step 1 is to check for a cookie and if it does not exist, then hive off to this single web interface I spoke about above. Once it exists, the user can leave this app and go to the other app - whose step 1 again is to also check for a cookie. It now exists and she can come through.

The cookie works only to say - yes, this user is valid. Roles and what each user can or cannot see is left to each app. Expiry times are also left to each app to manage.

This sort of mimicks LDAP/Passport. Does it sounds like a good plan? What are the pitfalls?

Tom Kyte
October 02, 2003 - 7:35 am UTC

don't encrypt passwords -- hash them. no need to encrypt/decrypt ever -- you only need to verify.

encrypt the cookie -- sure, but not the persistently stored password.

i would not let expiry times up to the app -- since they share the cookie, it should be done consistently (eg: they all enforce the exact same policy)


the pitfall is -- how do you manage the key to decrypt the cookie without giving the key away to everyone. once everyone has the key -- you cannot easily change the key and once the key is discovered anyone can easily become anyone else.

What some systems do is:

a) there is a master cookie -- lets call this "M"
b) each app has its OWN cookie, lets call this "A1", "A2", ... and so on


(1) you goto application -- it looks for its cookie A1. if A1 exists, application decrypts it (application knows the key -- how we'll see in a moment) and all goes well.


If A1 does not exist, application looks for M. If M does not exist, then application redirects user to login server site to get M set. We would go back to step (1) above after logging in.

If M does exist -- that means the user has been authenticated but hasn't used A1 as yet. So, A1 makes a web services like call to the login server -- sending the encrypted cookie M (which A1 cannot decrypt -- no key). A1 sends the cookie M and a "secret key". The login server will decrypt M (it knows the key) and recrypt it with A1's secret and send it back to A1. A1 now has the users identity and can set the cookie A1.


Now, each application has its own secret key which can be changed at any time.


A little stronger -- note also that talk between A1 and login server would use SSL so as to not expose the key.

What if use does not logout: just closes the browser.

A reader, May 30, 2004 - 4:25 pm UTC

Hi Tom,

Lets assume, user logs in. Cookie is set. User does not logout, but just closes the browser. Then immediately "someone else" opens the "same page" from "same machine". Our routine will find the l_id (session_id from cookie) and will show everything.

When someone closes the browser, is it possible to log the user out.

Thanks.


Tom Kyte
May 31, 2004 - 12:12 pm UTC

when you close the browser, all session based cookies disappear automagically.

Alessandro Nazzani, November 06, 2004 - 10:50 am UTC

Hi Tom.

<quote>
The best method for doing this with WebDB is to use its native database authentication. In this fashion, the browser will prompt you for a username and password. There are no cookies anywhere. In your application, you can simply look at the USER psuedo variable (eg: select user from dual) and that'll tell you who is who. Then you can even use things like VIEWS, Fine Grained Access Control, GRANTS and ROLES to control who can see what data (without coding it all yourself).
</quote>

I tried this with 10g and mod_plsql (no Application Server) and it seems incredibly simple to use. Do you consider this technique "safe"? Would you use it for a small/medium web application with "normal" security requirements? IOW, is this "The best method" also *without* WebDB? ;-)

As usual, thanks in advance for your time.

Alessandro

Tom Kyte
November 06, 2004 - 11:06 am UTC

"safe" -- it is about as "safe" as you get (might consider using SSL because the userid/password is simply base64 encoded in the http header, using ethereal or any sniffer -- I can pick it off pretty easy -- but that is TRUE for any http conversation)


I do use this all of the time, it is my preferred method for applications that need to be logged in personally.

Alessandro Nazzani, November 08, 2004 - 4:35 am UTC

<quote>
...
(might consider using SSL because the userid/password is simply base64 encoded in the http header
...
I do use this all of the time, it is my preferred method for applications that need to be logged in personally.
</quote>

Thanks Tom.

Sorry for being thick, but whe you say "I do use this all of the time", do you mean with SSL or do you consider SSL overkill for most apps?

Alessandro

Tom Kyte
November 08, 2004 - 10:21 am UTC

I do use the userid/password method all of the time. and SSL when the passwords are considered sensitive, which is often (pretty much always if you are using passwords)

Alessandro Nazzani, November 08, 2004 - 10:38 am UTC

Ok, thanks God you seem to have an infinite patience! :)

Regards.

Alessandro

Alessandro Nazzani, November 19, 2004 - 10:23 am UTC

Sorry to bother you again but...

I read somewhere that SSL does not work with several virtual hosts on single IP: is it true and, if so, what would be your advice for users authentication in this scenario?

Alessandro

Tom Kyte
November 19, 2004 - 12:00 pm UTC

sort of outside of my area of expertise (and it doesn't sound right to me, i don't believe it to be true)

SSL and virtual hosts

tom, November 19, 2004 - 2:47 pm UTC

Tom,

The guy is sort of right. It is impossible to run sever name based virtual hosts under ssl as the ssl protocol layer happens before the name resolution. As explained by the apache documentation...

<quote>
The reason is very technical. Actually it's some sort of a chicken and egg problem: The SSL protocol layer stays below the HTTP protocol layer and encapsulates HTTP. When an SSL connection (HTTPS) is established Apache/mod_ssl has to negotiate the SSL protocol parameters with the client. For this mod_ssl has to consult the configuration of the virtual server (for instance it has to look for the cipher suite, the server certificate, etc.). But in order to dispatch to the correct virtual server Apache has to know the Host HTTP header field. For this the HTTP request header has to be read. This cannot be done before the SSL handshake is finished. But the information is already needed at the SSL handshake phase.
</quote>

This means you need to use ip based virtual hosts. Hope this helps.

Tom Kyte
November 19, 2004 - 7:30 pm UTC

i have the "perfect out" here :)

I said it wasn't my area of expertise! thanks, truly appreciate it.

Alessandro Nazzani, November 19, 2004 - 2:57 pm UTC

Tom,

thanks for the insight.

But I'm wondering now: are name based virtual hosts so "rare" out there? IP addresses are a scarce resource: what are my options provided that I have only one IP available and need to host multiple domains?

Sorry for the OT, TK...

Alessandro

Use different port based virtual hosts

Tom, November 19, 2004 - 4:33 pm UTC

Name based virtual hosts are common, but not if you need a different ssl configuration for each one. One workaround from the apache docs [you really really want to spend an afternoon reading them for this sort of stuff] is to use port based virtual hosts in the form

<VirtualHost 120.0.0.1:443>
SSLEngine On
SSL CONFIG HERE
</VirtualHost>

<VirtualHost 120.0.0.1:444>
SSLEngine On
Different ssl config here
</VirtualHost>

The syntax may not be bang on but that's the concept - single IP but multiple servers based on port number. Got this idea from the Apache Cookbook....might be worth reading or going to the website for details.

Sorry for filling your site with Apache info Tom, but it is at least mod_plsql related [took me ages to work out how to get ssl working on mod_plsq]. Hopefully it will help someone.


Tom Kyte
November 19, 2004 - 7:51 pm UTC

not a problem at all, thanks

Alessandro Nazzani, November 19, 2004 - 5:20 pm UTC

> Hopefully it will help someone.

It sure helped me :-)

Thanks for your replies (yet another bunch of docs to read, grrrr... why didn't I run a grocery or something? :-P).

Alessandro

A reader, February 01, 2005 - 12:33 pm UTC

Tom,
This is good information. Can I modify this to pass the session_id in the URL instead of a cookie? I want to then read this session_id from HTMLDB.

Thanks,
Randy

Tom Kyte
February 01, 2005 - 2:19 pm UTC

sure, you can put it anywhere you want.

Set cookie and redirect after login

Ben, February 28, 2006 - 5:59 pm UTC

In response to Dave from NH, Tom wrote, "You do know you cannot "read" a cookie in your procedure after setting it right? I mean -- the cookie set is just writing a http header. you don't "see that" in your procedure. But it is OK you cannot "see it" cause you SET IT and hence you KNOW IT."

This is true, but it implies that you would need to pass along the information that the user is authenticated by some means other than the cookie. That means passing in some parameter value. Sure, the parameter could be just as secure as the cookie value, but now every single procedure in the entire application has to have an extra parameter. If you're going to do that, why use a cookie at all, just have each page pass along the cookie value as a parameter. Because it's ugly.

Or, it implies you will always take the user to the same page after logging in. Not so friendly.

It defeats the purpose of cookie-based authentication. I want every web page to be able to call a simple procedure to check if the user is authenticated, and if not redirect them to the login form. Once they supply their credentials, it would be nice if I could redirect them back to the original page. Most web sites can do this.

I tried a bunch of variations, and finally found a solution, which I just wanted to share here because it would have saved me hours if I could have found this example code.

After the user logs in, set the cookie this way:

OWA_UTIL.mime_header('text/html',false);
OWA_COOKIE.send(l_cookie_name,l_cookie_value);
OWA_UTIL.redirect_url(l_original_url);

This effectively gets around the problem of not being able to read the cookie immediately after setting it. If you use the typical form of calling OWA_UTIL.http_header_close after OWA_COOKIE.send, then you won't be able to call OWA_UTIL.redirect_url. That's the important distinction. The URL you are redirecting to will be able to get the cookie value. If you called the procedure directly without using redirect_url, then it wouldn't be able to read the cookie.

Tom Kyte
March 01, 2006 - 8:05 am UTC

but the page that does the authentication would redirect to the "real page" and hence the "real page" would "know"


now, reading your posting, I see the problem - I took it for granted that you would set the cookie and redirect (guess when you see/do something yourself 10000 times it becomes 'of course they would know that')

Thanks and sorry about that.

Sniffing Sqlplus session

AR, May 11, 2006 - 3:13 pm UTC

Hi Tom,
1) I am using Ethereal to sniff a simple sqlplus query issued from my laptop - "select * from all_users;". While I am able to see the usernames as ASCII within the packet details, I am not able to decipher the "userid" and "created" columns.

I tried mapping individual hex values..but it doesn't add up to the query output I am seeing. What am I doing wrong?

2) I was under the assumption that passing userid/password in cleartext would show up on a sniff. For eg: "sqlplus system/manager@MYORADB" from commandline is a bad idea because password can be picked up by sniffing. Strangely, I am not able to see passwords within packet details when I do this. Is the schema password always encrypted, even when passed as a commandline string?

Sorry for the inane questions. I have little background with networking.

Thank you for your time.

Tom Kyte
May 11, 2006 - 8:07 pm UTC

1) they are binary, they are numbers and dates. If you are interested in the "internal formats" of this binary data - you can see my chapter on datatypes in Expert Oracle Database Architecture - but - it really isn't "relevant".

numbers are varying length fields from 1 to 22 bytes.
dates are fixed width 7 byte fields with numbers stored in excess 100 format.


2) passwords are and have been encrypted during authentication.

harishnalakath, January 23, 2007 - 9:31 am UTC


harishnalakath, January 23, 2007 - 9:31 am UTC


cookie

sam, July 10, 2009 - 1:45 am UTC

Tom:

Excellent thread the way you did session management above.

I have a 300 page application (pl/sql web toolkit) that is passing userid as a hidden parameter from page to page for session management.

Now I want to secure it using session id and expire pages every 15 mins. it is already using SSL.

Would the best and easiest be using
a) session cookie (like above)
b) passing session id as hidden field
c) passing session id in URL
d) storing session id in table

I do not want to add parameters and post them from page to page so i am thinking of adding a genric subroutine to read the cookie on each page and either accept or reject. If OK, then i will update expiration time in table for cookie.

Do you agree or there is better way?

2. If you have 100 web users would you SESSION_TABLE have a MAX of 100 records. Do you keep updating the session id number for same username every time a user logs in?

3. what other state variables do you usally store in cookie or table (other than session id)

thank you
Tom Kyte
July 14, 2009 - 5:09 pm UTC

... that is passing userid as a
hidden parameter ...

that is not hidden in any way shape or form. It is very clear. It is very changeable. If this is part of your "security", you are as insecure as you can be.


using a cookie is most convenient, you know it'll be there on every page, you don't have to pass it yourself.

user hidden fields/url fields can be a pain, you have to remember to do it yourself every time.


(d) doesn't make sense - while you might store it and other data in a table, it doesn't do anything for your application to help identify the end user. So what - you have a row in a table. You have no clue what row is relevant to the current session.




2) re-read the answer, understand the logic, and then you would see that every time you login, you get a new session.


3) anything YOU need for YOUR application. think about it - what I store is quite simply not relevant for you. You are not building my application, my application state is not relevant to you.




Sam - I have told you a couple of dozen (or hundred - I forgot) - start using APEX, it does all of this. That way, it'll be secure and you don't have to think so much about how to do it, you just use it.

session

A reader, July 14, 2009 - 5:39 pm UTC

Tom:

I wish i can use apex. They have not installed it with 9i and even if they did I can't simply convert OLD existing pages to apex format that quickly.

I concluded the best thing and easiest thing is using a session cookie and session table like you have.

My question was about whether you store other data in the cookie (in addition to session id).
My thinking is that all i need is SESSION_ID in COOKIE
and all other variables can be stored in the table with that session id.

For example, I do not need to set expire time in the cookie. TIMESTAMP will tell me when the session was created and AUTHENTICATED_USERNAME routine will check if that date > sysdate - 15/1440 to determine whether sesssion has expired or not.

am i correct?


2. Was this your idea of calling the authenticated function on every page.

begin
v_username := authenticated_username;
IF v_username is NULL Then
raise exc_page_expired;
END IF;

....continue with page

EXCEPTION
WHEN exc_page_expired THEN
htp.p('The page has expired. You have to log in again!..')
END;
Tom Kyte
July 15, 2009 - 11:39 am UTC

1)

You want to put into the cookie only the magic, unguessable, maybe even encrypted - primary key to the session table.

If you start sticking other important stuff in there, you give the user the opportunity to trick you - to change the values (unless of course you encrypt the cookie value).


2)

use that function however you see fit. If you have the magic cookie set, it reads it - looks up the user - and returns NULL if they are not logged in (either not logged in at all - or their session has expired)

many people would redirect to a login page if they are not logged on - you can do whatever you feel appropriate at that point.

session

sam, May 19, 2011 - 12:40 pm UTC

Tom:

Is there a way to implment a "Logout" function (URL on an 9i oracle web application (pl/sql web toolkit) if it does not use session ids and cookies but rather passes username from page to page.

If there is no way, is it possible to only do the session maangement on the main menu page where the link is and when user logs out expire the session id and redirect to the login page? Passing session id through hundreds of pages is timeconsuming right now for dev/testing and we want a quick logout link.

please advise.
Tom Kyte
May 23, 2011 - 10:56 am UTC

you would have to program it all yourself sam.

passing the username from page to page would be "a bad idea" unless you get into encryption. Think about this.

with a randomly generated, unpredictable session key of sufficient size (unguessable) and SSL, you would be safe, but if you just fling usernames around, I'd just need to know someones username to "become them".

session

sam, May 27, 2011 - 10:17 am UTC

tom:

correct me if am wrong.

A logout function is simply expiring a session id in the session table so that a user cant navigate pages anymore, and expiring any web pages in the browser cache so the back button wont display anything?

I can use the session table and autoehtincate username routine you have, but I think also i do not need to use SESSION COOKIE on the client machine. I can pass the session id from page to page using SSL too. correct?

I know the common practice is using a cookie and it is easier than passing a hidden session id parameter from page to page.
Tom Kyte
May 27, 2011 - 12:23 pm UTC

how can a stored procedure "expire any web pages in the browser"???


If you are using the concept of a session that needs authentication and timing out - you shall be using SSL. SSL is what would take care of the browser, they do not cache pages retrieve over SSL.

The concept of "logging out" is a rather personal one. It depends on your system design. How you built it.


You could pass the session id from page to page, of course. APEX does it that way, look at the URL.


session

sam, May 27, 2011 - 2:46 pm UTC

Tom:

1) I am already using SSL. You are saying if I use SSL the browser does not cache pages? How come when I hit the back key I see the old pages I visited.

What I meant by expire, is that the SP will generate HTML metatags to expire the page or tel the browser to not cache it.


2) I am using your authentication mechanism above.
When user logs in i create a sessionid, set a cookie, etc.

Now on all system web pages i need to validate the session id so i use "Authenticated_username" you have above.

But instead of repeating same lines of code for calling function and checking the value returned, i create a routine "validate_session" that i need to the top of every single webpage to validate the sessionid.

here is the routine.

The problem is that when i add it to procedure A, it does not stop (if session expired) at the message. it prints the session expired message and then continue on. I tried adding the same expcetion name "session_expired" to procedure A but still it did not work.

My question, is do i need this extra routine or not?
if yes, What is the best way to raise the "validate_session" exception on all pages that have expired sessions so processing wont continue?



function authenticated_username return varchar2 is
l_rec session_table%rowtype;
l_id number;
l_username varchar2(30);
begin

-- get the cookie value into l_id
l_id := get_user_cookie;

update session_table
set timestamp = SYSDATE
where session_id = l_id
AND timestamp > sysdate - 1/24
returning username into L_USERNAME; -- expire the session
-- after one hour of
-- inactivity

if ( sql%rowcount = 0 ) then
-- tell them the login either hasn't happened or
-- has timed out
return NULL;
else
return l_username;
end if;
end;



procedure validate_session
IS
l_username varchar2(50);
session_expired exception;
begin
l_username := authenticated_username;

if (l_username is null)
then
raise session_Expired;
end if;

exception
when session_expired then
htp.p('You session has expired! You need to re-login to system...!');
-- RAISE;
end;



for all other web page i do this


Procedure A
begin
validate_session;
....
..
end;

Tom Kyte
May 27, 2011 - 3:51 pm UTC

1) browsers do not cache the pages. hitting back does not mean you are hitting a cache. hitting back means "go get me this page", if the page happened to have been in the cache - so be it, if not, it'll get it.

You probably also see messages like "the results of this page were due to a POST, do you want to really re-execute this page"


You can do whatever you like with html meta tags - but - they affect only the page they are on, not past pages. You cannot tell the browser "hey, get rid of these pages" - not unless you told the browser to never cache them at all in the first place.

2) are you asking me to debug your logic? If you don't like what I've provided and wish to design something new on your own - go for it, but please don't ask me to debug it

session

sam, May 27, 2011 - 4:05 pm UTC

Tom:

No, iam not asking you to debug.

You provided 80% of the solution.

I am trying to see how best to use your "authenticate_username" routine on every single page.

I was thinking that i would need to do the following on every page:

my_var := authenticated_username;
if (my_var is NULL) then
RAISE session_expired;
end if;

exception
when session_expired then
htp.p('Your session expired! you need to relogin in.');

I was trying to avoid that on every single page and write a routine instead and just call that routine.

What would be the best way here?

2) How can browsers not cache pages?

Do not you set your browser to visited pages or not.

http://www.mnot.net/cache_docs/

http://www.wikihow.com/Clear-Your-Browser's-Cache
Tom Kyte
May 27, 2011 - 4:46 pm UTC

then you need to figure out your custom 20% :)



Your routine - re-raises the exception, think about it....

You could have authenticated_username raise that exception, then your code would be:

begin
my_var := authenticated_username;
...

exception
when session_expired then htp.P( 'you lose' );
end;






browsers can do whatever they like, they are just code after all. They may or may not cache a page *at will*. Some browsers cache https protocol generated pages, some do not. It is entirely up to them.

session

sam, May 27, 2011 - 5:28 pm UTC

Tom:

so there is no way to do this with one procedure line call on every page.

Is this what you are suggesting to do?

function authenticated_username return varchar2 is
l_rec session_table%rowtype;
l_id number;
l_username varchar2(30);
begin

-- get the cookie value into l_id
l_id := get_user_cookie;

update session_table
set timestamp = SYSDATE
where session_id = l_id
AND timestamp > sysdate - 1/24
returning username into L_USERNAME; -- expire the session
-- after one hour of
-- inactivity

if ( sql%rowcount = 0 ) then
-- tell them the login either hasn't happened or
-- has timed out
RAISE SESSION_EXPIRED; <-----------new line
return NULL;
else
return l_username;
end if;
end;


procedure A
IS
my_var exception; (new line)
begin
my_var := authenicated_username; (new line)

exception

when session_expired (new line)
htp.p('Your session expired!');
end;

Sam, have you tried your proposal?

Mark Williams, May 27, 2011 - 9:49 pm UTC

Sam,

I hope a little bit of constructive criticism is not out of place, but have you even tried what you are asking/proposing?

You have written:

my_var exception;

and then:

my_var := authenicated_username; [sic]

where the function returns a varchar2 value.

Now, how well do you think that will work? Can you assign a varchar2 to a variable declared as an exception? Did you try it? Does it even make sense to try it? A variable declared as an exception seems just a bit out of place on the left hand side of an assignment statement, eh?

I don't mean this in an unkind way, but what would happen if Tom was unavailable for a month? This is something that should be concerning. Do you have colleagues who can participate in code reviews or similar? Everyone is different and has different learning styles, etc. but I would be a bit concerned if I had to rely on Tom on an almost daily basis for my routine job tasks.

Wasn't it you that said you were going to be an expert in APEX by mid-April 2011? i.e. "in a month" from mid-March.

http://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:12691263416650#3182207400346587685

APEX is a fantastic product and makes a lot of the things you seem to be involved in so much easier. Have you started down that path?

As I say, this is intended to be constructive so hopefully it comes across that way. I thought about not posting, but decided to in the end because it was a sincere sentiment.

Regards,

Mark

session

sam, May 27, 2011 - 10:19 pm UTC

Mark:

geeez, all this writeup because of a rushed typo.

Of course tom knows i mean this

procedure A
IS
my_var varchar2(30); (new line)
expired_Session exception; (new line)
begin
my_var := authenicated_username; (new line)

exception

when session_expired (new line)
htp.p('Your session expired!');
end;

A reader, May 27, 2011 - 11:24 pm UTC

I would be a bit concerned if I had to rely on Tom on an almost daily basis for my routine job tasks.


No kidding. This is a community forum. It works best when people ask questions in moderation. Tom's time is finite. Sam, perhaps if you did not bombard this forum with an almost daily barrage of questions, we would have more time for new questions.

This may sound rude, but it is equally inconsiderate to dominate a community forum for your own personal benefit.

Play it again, Sam

rsergio, May 30, 2011 - 2:02 pm UTC

What would life be without fun? The dialogues between Sam and Tom are precious (even informative, sometimes). Keep on rollin', guys.

session function

sam, May 30, 2011 - 5:45 pm UTC

Tom:

I hope you had a nice hot relaxing memorial weekend!

<<<<
You could have authenticated_username raise that exception, then your code would be: 
begin 
  my_var := authenticated_username; 
  ... 
exception 
  when session_expired then htp.P( 'you lose' ); 
end; 
>>>>

I tried what you suggested above to add expcetion to the authenticate_user function but it does not work as i thought it should.

I added an exception "session_expired" to the authenticate_user function first.
When i run the other main routine that calls that it gives me an error in that the exception needs to be defined in the function.

When i add the exception "session expired" to the function, The main program will continue executing and does not stop even though session has expired.

It seems the only way i managed to stop program execution is using "raise_application_error" but i cant use this for web page message.

Am i missing something here?

SQL> create table session_table (session_id number, timestamp date, username varchar2(30));

Table created.

SQL> insert into session_table values (21, sysdate, 'The Great Tom');

1 row created.

SQL> commit;

Commit complete.

SQL> create or replace function get_user_cookie return number is
  2  begin
  3      return 22;
  4  end;
  5  /

Function created.

SQL> create or replace function authenticate_user return varchar2 is
  2       l_rec session_table%rowtype;
  3       l_id  number;
  4       l_username varchar2(30);
  5       session_expired  exception;
  6   begin
  7       l_id := get_user_cookie;
  8       update session_table
  9          set timestamp = SYSDATE
 10           where session_id = l_id
 11             and timestamp > sysdate - 1/24
 12              returning username into L_USERNAME;
 13         IF (SQL%ROWCOUNT = 0) THEN
 14           --return NULL;
 15           raise session_expired;
 16            -- raise_application_error(-20001, 'You session has expired! You need to re-login to 
system...!');
 17         else
 18            return l_username;
 19         end if;
 20   end;
 21  /

Function created.


 SQL> declare
   2     indx  number;
   3     session_expired  exception;
   4     l_user   varchar2(30);
   5   begin
   6     for indx in 1..10
   7     loop
   8      l_user := authenticate_user;
   9      dbms_output.put_line('Procedure called '||indx||' times');
  10     end loop;
  11   exception
  12    when session_expired then
  13     dbms_output.put_line('no session!');
  14   end;
  15  /
 declare
 *
 ERROR at line 1:
 ORA-06510: PL/SQL: unhandled user-defined exception
 ORA-06512: at "SYSTEM.AUTHENTICATE_USER", line 15
ORA-06512: at line 8
 
 
 After I add the expcetion to the function, the calling procedure keeps running!
 
 
 SQL>  declare
   2      indx  number;
   3      session_expired  exception;
   4      l_user   varchar2(30);
   5    begin
   6      for indx in 1..10
   7      loop
   8       l_user := authenticate_user;
   9       dbms_output.put_line('Procedure called '||indx||' times');
  10      end loop;
  11    exception
  12     when session_expired then
  13      dbms_output.put_line('no session!');
  14    end;
  15  /
 Procedure called 1 times
 Procedure called 2 times
 Procedure called 3 times
 Procedure called 4 times
 Procedure called 5 times
 Procedure called 6 times
 Procedure called 7 times
 Procedure called 8 times
 Procedure called 9 times
 Procedure called 10 times
 
PL/SQL procedure successfully completed.

Tom Kyte
May 31, 2011 - 12:40 pm UTC

I tried what you suggested above to add expcetion to the authenticate_user
function but it does not work as i thought it should.
...
Am i missing something here?


Yes, the basics about scoping in plsql and how exceptions work.

things work the way they are documented to work, not the way we THINK they should work



You have TWO exceptions above, they might have the same name - but they are TWO different exceptions.

Tell you what - define two variables in two subroutines and you have what? Two separate and distinct variables whose scope is in two different subroutines.


You'll want to use a package.

create or replace package my_exceptions
as
session_expired exception;
end;
/


and throw and catch my_exceptions.session_expired - an exception has scope just like variables, cursors, etc etc etc do.

listener blocked in the rac environment

nycle, May 31, 2011 - 1:02 am UTC

I establish a rac environment. And the enviromnnt is:redhat Enterprise 5.6+oracle10.2.0.5.0+ASM.Execute the 'lsnrctl stat',the result is:
Services Summary...
Service "+ASM" has 1 instance(s).
Instance "+ASM1", status BLOCKED, has 1 handler(s) for this service...
Service "+ASM_XPT" has 1 instance(s).
Instance "+ASM1", status BLOCKED, has 1 handler(s) for this service...

And check the listener.log see some errors:
TNS-01153: Failed to process string: (ADDRESS=(PROTOCOL=TCP)(HOST=192.168.100.101)(PORT=1521))
NL-00305: the specified path name does not exist

session management

sam, May 31, 2011 - 7:46 pm UTC

Tom:

Thank you! Yes you were right. I got that part working today.

One common exception variable declared in a package that will be raised by "validate_session" and the calling procedure.

too bad you can only define declaration of exception in a package and not the handler too.

I have to define the handler in every web page procedure.




After fixing that issue, I found myself running into another issue.

The login procedure which authenticates user, set the session cookie and create a row in the session table, keeps running whenever user hits
REFRESH.

I tried the OWA_Util.redirect_url but it would not work because the function "set_user_cookie" create HTML tags.
If i add htp.init before it, it works but the session cookie value seems to become incorrect.



procedure Login( i_user_id in varchar2,
i_password in varchar2)
as
l_session_id session_table.session_id%type;
begin

-- validate the username/password is correct here...
if verify_user(i_user_id, i_passwod)
then
-- create a session for the user here...
select SYS_GUID() into l_session_id from dual;

insert into session_table (session_id, timestamp, username, created_date)
values (l_session_id, sysdate, i_user_id, sysdate);

-- set the cookie to be l_session_id

set_user_cookie(l_session_id);

main_menu(i_user_id);
--htp.init;
-- owa_util.redirect_url('main_menu?i_user_id='||i_user_id);

else

re_login;
end if;
end;


procedure set_user_cookie (i_session_id IN varchar2)
IS
begin

OWA_UTIL.mime_header('text/html', FALSE);
OWA_COOKIE.send('my_user',i_session_id);
OWA_UTIL.http_header_close;
END;


What would you do:

1) Leave it as is since REFRESH will create another session id and it should not hurt.
2) Move the SET_USER_COOKIE procedure call to the MAIN_MENU procedure. Need to add a new parameter to distinguish
whether we are coming from Login form or normal navigation from other pages so i only set cookie on login.

3) Use META HTML tag in the Login procedure with no wait to redirecto main menu.

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<title>Your Page Title</title>
<meta http-equiv="REFRESH" content="0;url= http://www.the-domain-you-want-to-redirect-to.com" ></HEAD>
<BODY>
Optional page text here.
</BODY>
</HTML>


anything else?


More to Explore

Security

All of the vital components for a secure database are covered in the Security guide.