dev@glassfish.java.net

Re: Strange form authentification behaviour

From: <Jan.Luehe_at_Sun.COM>
Date: Thu, 13 Dec 2007 19:43:36 -0800

Hi Ron,

Ron Monzillo wrote:

> Jan.Luehe_at_Sun.COM wrote:
>
>> Hi Ron,
>>
>> Ron Monzillo wrote:
>>
>>> Jan,
>>>
>>> thanks for the explanation.
>>>
>>> If I undersatnd correctly, a request uri always saved. when someone
>>> accesses the login page directly and the saved uri is not protected,
>>> the authenticator will not be called to match the request and the
>>> authentication state (that is stored in the session) is
>>> unrecognizable without further processing by the authenticator
>>> (including request matching). As such it will not be applied to
>>> subsequent requests.
>>>
>>> when the authenticator knows that that the redirection will not
>>> result in the authenticator being recalled to match the saved
>>> request (because the saved uri is not protected), then the
>>> authenticator will need to do the registration, or the effects of
>>> the authentication will be lost.
>>
>>
>>
>>
>> Yes, I believe we're on the same page. :)
>>
>> The code that was added in an effort to fix Issue 1933:
>>
>> requestURI = savedRequestURL(session);
>>
>> // BEGIN FIX FOR Issue 1933
>> if (requestURI == null) {
>> // requestURI will be null if the login form is submitted
>> // directly, i.e., if there has not been any original request
>> // that was stored away before the redirect to the login form was
>> // issued. In this case, assume that the original request has been
>> // for the context root, and have the welcome page mechanism take
>> // care of it
>> requestURI = hreq.getContextPath() + "/";
>> saveRequest(requestURI, hreq.getMethod(), session);
>> }
>> // END FIX FOR Issue 1933
>>
>> needs to check whether the "manufactured" requestURI for the
>> redirect (i.e., "<context_root>/"), which may be mapped to one of the
>> webapp's welcome pages, is mapped by one of the webapp's security
>> constraints. In other words, it needs to determine
>> whether the redirected request will be intercepted by the
>> FormAuthenticator.
>> Only if the redirected request will be intercepted by the
>> FormAuthenticator
>> will matchRequest() and register() be called.
>>
>> If the above code determines that the redirected request will not be
>> intercepted by the FormAuthenticator, it must perform the registration
>> itself.
>>
>> Again, this is an issue only if the login page is accessed
>> directly. Otherwise, the requestURI (for the protected resource) that
>> caused the FormAuthenticator to be invoked in the first place will
>> have been saved away, and it is certain that the redirect request (to
>> this requestURI) will again be intercepted by the FormAuthenticator
>> and cause matchRequest() and register() to be called.
>>
>> Can you confirm that this is your understanding as well?
>
>
> Jan,
>
> yes, I think so. Is there any reason why the registration must be
> delayed to the resubmit of the "matching request" (in this case, and
> maybe even in the general case).
>
> when a form action arrives directly (i.e. no matching request has been
> previously saved), if the registration could be performed independent
> of the nature (wrt to protection) of the redirect uri, would there be
> any need to save and match the subsequent request to the welcome page?


I agree. I wanted the case where the login page is accessed directly to
follow what you
call the general case as closely as possible, to avoid any code
duplication, but I did not take
into account the case where the welcome page target of the redirect was
unprotected.

The attached diffs, which follow your recommendation, fix the problem
(I've already verified
this locally).

I will go ahead and file an issue in IssueTracker.

I'm not sure why the general case delays registration until the
"matching request" is resubmitted.
This is something we inherited from the Tomcat codebase.

Thanks,


Jan


>
> Ron
>
>>
>> Thanks,
>>
>>
>> Jan
>>
>>>
>>> Ron
>>>
>>> Roman Pokhodzhay wrote:
>>>
>>>> Hi Ron, Jan
>>>>
>>>> It seems fix for
>>>> https://glassfish.dev.java.net/issues/show_bug.cgi?id=1933
>>>> <https://glassfish.dev.java.net/issues/show_bug.cgi?id=1933> should
>>>> help, but as for me in some strange manner. Why you can simply
>>>> register user when authentification passed (you can put register
>>>> method call after auth passed), and why user should be registered
>>>> when he/she only requests protected resource but not direct requets
>>>> to login page? Currently such method is called only within "if
>>>> (matchRequest(request)) {" block.
>>>> (FormAuthenticator.java). For example BasicAuthenticator do it
>>>> after successful realm authentication:
>>>>
>>>> if (authorization != null) {
>>>> String username = parseUsername(authorization);
>>>> String password = parsePassword(authorization);
>>>> principal = context.getRealm().authenticate(username, password);
>>>> if (principal != null) {
>>>> register(request, response, principal,
>>>> Constants.BASIC_METHOD,username, password);
>>>> String ssoId = (String) request.getNote(Constants.REQ_SSOID_NOTE);
>>>> if (ssoId != null) {
>>>> getSession(request, true);
>>>> }
>>>> return (true);
>>>> }
>>>> }
>>>>
>>>> ________________________________
>>>>
>>>> From: Jan.Luehe_at_Sun.COM [mailto:Jan.Luehe_at_Sun.COM]
>>>> Sent: Чт 13.12.2007 3:02
>>>> To: dev_at_glassfish.dev.java.net
>>>> Subject: Re: Strange form authentification behaviour
>>>>
>>>>
>>>>
>>>> Hi Ron,
>>>>
>>>> Ron Monzillo wrote:
>>>>
>>>>
>>>>>>
>>>>>> Hello!
>>>>>>
>>>>>> I can’t retrieve currently logged in user from request (When I go to
>>>>>> the login page directly and login via form base method). It happens
>>>>>> because method /authenticate// /in
>>>>>> /org.apache.catalina.authenticator//.//FormAuthenticator// /doesn’t
>>>>>> call method /register//(/org.apache.catalina.authenticator.
>>>>>> AuthenticatorBase/)/ which save principal in request.
>>>>>>
>>>>>> But FormAuthenticator do it only in case when resource is secure and
>>>>>> if the re-submit happens of the original request URI.
>>>>>>
>>>>>>
>>>>>>
>>>>>> Is this standart behavior?
>>>>>>
>>>>>
>>>>> Roman,
>>>>>
>>>>> I think this is a bug (which, fwiw, appears to have been around for
>>>>> some time). Thanks for bringing it to our attention.
>>>>>
>>>>> Have you tested, or can you suggest a fix?
>>>>>
>>>>> It looks to me like maybe the following block from
>>>>> FormAuthenticator.authenticate should be changed to call register,
>>>>> and
>>>>> maybe it need not call saveRequest when the current request contains
>>>>> the loginAction.
>>>>>
>>>>> requestURI = savedRequestURL(session);
>>>>> if (requestURI == null) {
>>>>> // requestURI will be null if the login form is submitted
>>>>> // directly, i.e., if there has not been any original request
>>>>> // that was stored away before the redirect to the login form was
>>>>> // issued. In this case, assume that the original request has been
>>>>> // for the context root, and have the welcome page mechanism take
>>>>> // care of it
>>>>> requestURI = hreq.getContextPath() + "/";
>>>>> saveRequest(requestURI, hreq.getMethod(), session);
>>>>> }
>>>>
>>>>
>>>>
>>>>
>>>>
>>>>
>>>> the above code was added in an effort to fix:
>>>>
>>>> https://glassfish.dev.java.net/issues/show_bug.cgi?id=1933
>>>> ("Admin login screen gives "Error Accessing Page:" error after
>>>> logging in")
>>>>
>>>> Assume the FORM's login page was accessed directly, so the first
>>>> invocation of FormAuthenticator.authenticate() will authenticate the
>>>> user, store the autenticated principal in the session:
>>>>
>>>> session.setNote(Constants.FORM_PRINCIPAL_NOTE, principal);
>>>>
>>>> and then attempt to restore the original request URI (which prompted
>>>> authentication in the first place) and redirect to it:
>>>>
>>>> // Redirect the user to the original request URI (which will cause
>>>> // the original request to be restored)
>>>> requestURI = savedRequestURL(session);
>>>> if (requestURI == null) {
>>>> // requestURI will be null if the login form is submitted
>>>> // directly, i.e., if there has not been any original request
>>>> // that was stored away before the redirect to the login form was
>>>> // issued. In this case, assume that the original request has been
>>>> // for the context root, and have the welcome page mechanism take
>>>> // care of it
>>>> requestURI = hreq.getContextPath() + "/";
>>>> saveRequest(requestURI, hreq.getMethod(), session);
>>>> }
>>>> .
>>>> If the login page was accessed directly, "requestURI" will be null
>>>> (there
>>>> is nothing to restore in this case), so what the above code does is to
>>>> pretend
>>>> that the original request was for "<context_root>/". This "requestURI"
>>>> is then
>>>> saved in the session, using saveRequest() (see further down for why
>>>> calling
>>>> saveRequest() is necessary in this case).
>>>>
>>>> The request is then redirected to the restored "requestURI", which
>>>> in this
>>>> case will be equal to "<context_root>/":
>>>>
>>>> hres.sendRedirect(hres.encodeRedirectURL(requestURI));
>>>>
>>>> This will trigger another invocation of
>>>> FormAuthenticator.authenticate()
>>>> down
>>>> the line, but this time, matchRequest() will return true (this is why
>>>> the above
>>>> code had to call saveRequest(), since matchRequest() matches the
>>>> redirected
>>>> request against whatever was saved):
>>>>
>>>> if (matchRequest(request)) {
>>>>
>>>> Since matchRequest() will return true, the principal is retrieved from
>>>> the session, and register() is called:
>>>>
>>>> session = getSession(request, true);
>>>> principal = (Principal)
>>>> session.getNote(Constants.FORM_PRINCIPAL_NOTE);
>>>> register(request, response, principal, Constants.FORM_METHOD,
>>>> (String) session.getNote(Constants.SESS_USERNAME_NOTE),
>>>> (String) session.getNote(Constants.SESS_PASSWORD_NOTE));
>>>>
>>>> So from what I can tell, the existing code is correct, i.e., it is
>>>> necessary to call
>>>> saveRequest() if the login page was accessed directly, and
>>>> FormAuthenticator.register()
>>>> will still be called.
>>>>
>>>> Let me know if I missed anything.
>>>>
>>>> Thanks,
>>>>
>>>>
>>>> Jan
>>>>
>>>>
>>>> ---------------------------------------------------------------------
>>>> To unsubscribe, e-mail: dev-unsubscribe_at_glassfish.dev.java.net
>>>> For additional commands, e-mail: dev-help_at_glassfish.dev.java.net
>>>>
>>>>
>>>>
>>>>
>>>>
>>>> ------------------------------------------------------------------------
>>>>
>>>>
>>>> ---------------------------------------------------------------------
>>>> To unsubscribe, e-mail: dev-unsubscribe_at_glassfish.dev.java.net
>>>> For additional commands, e-mail: dev-help_at_glassfish.dev.java.net
>>>
>>>
>>>
>>>
>>> ---------------------------------------------------------------------
>>> To unsubscribe, e-mail: dev-unsubscribe_at_glassfish.dev.java.net
>>> For additional commands, e-mail: dev-help_at_glassfish.dev.java.net
>>>
>>
>>
>> ---------------------------------------------------------------------
>> To unsubscribe, e-mail: dev-unsubscribe_at_glassfish.dev.java.net
>> For additional commands, e-mail: dev-help_at_glassfish.dev.java.net
>>
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: dev-unsubscribe_at_glassfish.dev.java.net
> For additional commands, e-mail: dev-help_at_glassfish.dev.java.net
>



Index: FormAuthenticator.java
===================================================================
RCS file: /cvs/glassfish/appserv-webtier/src/java/org/apache/catalina/authenticator/FormAuthenticator.java,v
retrieving revision 1.8
diff -u -r1.8 FormAuthenticator.java
--- FormAuthenticator.java 23 Jul 2007 16:48:23 -0000 1.8
+++ FormAuthenticator.java 14 Dec 2007 03:22:18 -0000
@@ -294,7 +294,13 @@
             // for the context root, and have the welcome page mechanism take
             // care of it
             requestURI = hreq.getContextPath() + "/";
- saveRequest(requestURI, hreq.getMethod(), session);
+ register(request, response, principal, Constants.FORM_METHOD,
+ (String) session.getNote(Constants.SESS_USERNAME_NOTE),
+ (String) session.getNote(Constants.SESS_PASSWORD_NOTE));
+ String ssoId = (String) request.getNote(Constants.REQ_SSOID_NOTE);
+ if (ssoId != null) {
+ associate(ssoId, session);
+ }
         }
 
         if (log.isDebugEnabled()) {