dev@glassfish.java.net

RE: Re: Strange form authentification behaviour

From: Roman Pokhodzhay <Roman_Pokhodzhay_at_epam.com>
Date: Sat, 15 Dec 2007 06:36:51 +0200

 
________________________________

From: Jan.Luehe_at_Sun.COM [mailto:Jan.Luehe_at_Sun.COM]
Sent: Пт 14.12.2007 23:48
To: dev_at_glassfish.dev.java.net
Subject: Re: Strange form authentification behaviour



Ron Monzillo wrote:

> Jan.Luehe_at_Sun.COM wrote:
>
>> 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
>>
> Jan,
>
> thanks for providing the fix.
>
> I noticed something else in the FBL; which I'd also like your opinion
> of. I think this problem has also always been there.
>
> if someone makes a direct request of the login page, after a
> successful authentication, the form will be returned, they will fill
> it in, and submit it, but when the authenticator is called to process
> the form, it will notice the existing authentication state without
> processing the values in the form (which could be different from those
> of the previous authentication). The request uri with the ending
> j_security_check action will the go through servlet mapping; which may
> throw a resource not found exception.
>
> should these subsequent authentications be allowed to succede?
>
> if not, should the authenticator establish a definitive status code?
>
> if yes, should the existing session be invalidated if the current
> submission features a changed username and password.
>
> I don't think there is much utility in accepting such requests unless
> they can be used to change the authentication identity.



Agreed.

We probably want to support direct login access only as long as the
client has
not been authenticated. Once the client has been authenticated, direct login
access should probably result in a 403 ("FORBIDDEN") response.

As for the issue that prompted this thread, I've recorded it here:

https://glassfish.dev.java.net/issues/show_bug.cgi?id=3922

Thanks!

 

Hi Jan!

Why you want to show 403 page when user access login page directly(when he/she is logged in). As for me it would be nice to show the same page with capability to logout(About logout functionality I mean session invalidation, because as far I know there are no another approach to logout in JEE)


Jan

>
> Ron
>
>>
>>>
>>> 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
>>>
>>
>>
>> ------------------------------------------------------------------------
>>
>> ---------------------------------------------------------------------
>> 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