Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for RequestDispatcher#forward in auth module #7

Open
glassfishrobot opened this issue Feb 17, 2013 · 8 comments
Open

Support for RequestDispatcher#forward in auth module #7

glassfishrobot opened this issue Feb 17, 2013 · 8 comments

Comments

@glassfishrobot
Copy link

In the Servlet Container Profile of JASPIC, a RequestDispatcher can be obtained from the request message in the ServerAuthModule.validateRequest method, and subsequently used to forward to a specific resource.

The fact that this can be done is not described in the JASPIC specification, and in practice not all currently certified JASPIC implementations are capable of handling this.

E.g. consider the following fragment in a ServerAuthModule.validateRequest implementation:

HttpServletRequest request = (HttpServletRequest) messageInfo.getRequestMessage();
HttpServletResponse response = (HttpServletResponse) messageInfo.getResponseMessage();

RequestDispatcher requestDispatcher = request.getRequestDispatcher("/index.xhtml");
requestDispatcher.forward(request, response);

return AuthStatus.SEND_CONTINUE;

(index.xhtml is a JSF Facelets page backing by a CDI backing bean)

This works is GlassFish 3.1.2.2 and Geronimo V3.0, but fails in some way or the other on JBoss AS 7.1.3/EAP 6.0.1, WebLogic 12.1.1 and WebSphere 8.5.

On JBoss EAP 6.0.1 the following exception is immediately thrown when invoking the forward() method:

An exception or error occurred in the container during the request processing: 
java.lang.ClassCastException: org.apache.catalina.connector.Request cannot be cast to javax.servlet.ServletRequestWrapper
	at org.apache.catalina.core.ApplicationFilterFactory.createFilterChain(ApplicationFilterFactory.java:164) [jbossweb-7.0.17.Final-redhat-1.jar:]
	at org.apache.catalina.core.ApplicationDispatcher.invoke(ApplicationDispatcher.java:827) [jbossweb-7.0.17.Final-redhat-1.jar:]
	at org.apache.catalina.core.ApplicationDispatcher.processRequest(ApplicationDispatcher.java:622) [jbossweb-7.0.17.Final-redhat-1.jar:]
	at org.apache.catalina.core.ApplicationDispatcher.doForward(ApplicationDispatcher.java:560) [jbossweb-7.0.17.Final-redhat-1.jar:]
	at org.apache.catalina.core.ApplicationDispatcher.forward(ApplicationDispatcher.java:488) [jbossweb-7.0.17.Final-redhat-1.jar:]
	at jaspic.TestServerAuthModule.validateRequest(TestServerAuthModule.java:120) [classes:]

On WebLogic 12.1.1. the following exception is immediately thrown:

javax.servlet.ServletException: Must call associate() before calling activate()
	at javax.faces.webapp.FacesServlet.service(FacesServlet.java:606)
	at weblogic.servlet.internal.StubSecurityHelper$ServletServiceAction.run(StubSecurityHelper.java:242)
	at weblogic.servlet.internal.StubSecurityHelper$ServletServiceAction.run(StubSecurityHelper.java:216)
	at weblogic.servlet.internal.StubSecurityHelper.invokeServlet(StubSecurityHelper.java:132)
	at weblogic.servlet.internal.ServletStubImpl.execute(ServletStubImpl.java:338)
	at weblogic.servlet.internal.ServletStubImpl.execute(ServletStubImpl.java:221)
	at weblogic.servlet.internal.RequestDispatcherImpl.invokeServlet(RequestDispatcherImpl.java:564)
	at weblogic.servlet.internal.RequestDispatcherImpl.forward(RequestDispatcherImpl.java:263)
	at jaspic.TestServerAuthModule.validateRequest(TestServerAuthModule.java:120)

On WebSpere 8.5 the following exception is thrown after the auth module returns:

0000006c WebCollaborat A   
SECJ0056E: Authentication failed for reason Jaspi authentication failed, unexpected HttpServletResponse status: 200
0000006c webapp E com.ibm.ws.webcontainer.webapp.WebApp logServletError 
SRVE0293E: [Servlet Error]-[servlet.TestServlet]: 
java.lang.IllegalStateException: Response already committed.
	at com.ibm.ws.webcontainer.webapp.WebAppDispatcherContext.sendError(WebAppDispatcherContext.java:599)
	at com.ibm.ws.webcontainer.webapp.WebAppDispatcherContext.sendError(WebAppDispatcherContext.java:656)
	at com.ibm.ws.webcontainer.srt.SRTServletResponse.sendError(SRTServletResponse.java:1255)
	at com.ibm.ws.security.web.WebReply.sendError(WebReply.java:61)
	at com.ibm.ws.security.web.DenyReply.writeResponse(DenyReply.java:35)
	at com.ibm.ws.security.web.EJSWebCollaborator.handleException(EJSWebCollaborator.java:735)
	at com.ibm.ws.webcontainer.collaborator.WebAppSecurityCollaboratorImpl.handleException(WebAppSecurityCollaboratorImpl.java:267)
	at com.ibm.wsspi.webcontainer.collaborator.CollaboratorHelper.processSecurityPreInvokeException(CollaboratorHelper.java:153)

On WebSphere 8.5 the forwarded resource is processed correctly. After some experimentation, it seems WebSphere 8.5 only allows http status codes 302, 303, 307 and 401 after an auth module returns SEND_CONTINUE. On WebLogic, the forward does work when the application hasn't activated CDI (e.g. when no beans.xml is present in WEB-INF). On JBoss EAP, forwarding works if the request is casted to a proprietary type and the result of a getRequest() call is provided to the dispatcher.

Since all the mentioned implementations are Java EE 6 certified, the JASPIC spec probably has to be clarified here and if possible a TCK test for this should be added.

Since forwarding can be an important use case, e.g. for displaying a login form as response to the original request, I would like to request that forwarding is being covered by the JASPIC spec. Specifically:

  • Specify that forwarding is a supported operation
  • Specify which status the auth module should return when forwarding
  • Specify which HTTP response codes are legal following a SEND_CONTINUE return value (perhaps there should be no limitation?)
  • Add a TCK test in which an auth module does a forward (if possible for a TCK: forward to a complex resource like a JSF page, with CDI being enabled, and where the JSF page uses a CDI backing bean)
@glassfishrobot
Copy link
Author

@glassfishrobot Commented
Reported by arjan_t

@glassfishrobot
Copy link
Author

@glassfishrobot Commented
Was assigned to monzillo

@glassfishrobot
Copy link
Author

@glassfishrobot Commented
monzillo said:
I think the websphere implementation is probably most correct (in terms of what the spec currently requires, as
they are apparently enforcing the following for SEND_CONTINUE -

"The module must have set the HTTP status code of the response to a value (for example, HTTP 401 unauthorized, HTTP 303 see other, or HTTP 307 temporary redirect) that will indicate to the client that it should retry the request."

I will add a clarification in the section around SEND_CONTINUE, but we will have to better understand all the ramifications of doing forwards, from auth modules before I will be able to require support for doing so. I will add some mention of being able to do forwards for example to a login form, to raise awareness.

is redirecting to the login-form a suitable work-around?

@glassfishrobot
Copy link
Author

@glassfishrobot Commented
arjan_t said:

I will add a clarification in the section around SEND_CONTINUE, but we will have to better understand all the ramifications of doing forwards, from auth modules before I will be able to require support for doing so. I will add some mention of being able to do forwards for example to a login form, to raise awareness.

Okay, that would be great. I agree that if it's not fully clear what impact forwards can have that this should be investigated first. I did notice that JBoss ships a JASPIC module that mimics the Servlet form based authentication, and uses a forward for this (they use proprietary API for this though). It's at org.jboss.as.web.security.jaspi.modules.HTTPFormServerAuthModule.

is redirecting to the login-form a suitable work-around?

It's a workaround for sure. And we have indeed used this for a SAM.

A forward is more natural though for those use cases where the session can't be used to temporarily store data from the original request like headers, (post) parameters, cookies, and such. With a forward this data could be passed to the login form within the same request. The login form can then render this (encoded and encrypted if necessary) in a hidden field, so it can be posted back when the user authenticates. Theoretically this could be done via one huge GET request parameter, but that's not really ideal.

@glassfishrobot
Copy link
Author

@glassfishrobot Commented
monzillo said:
I am thinking that the servlet profile needs to allow for a third case, that where validateRequest is called before the service invocation and returns after the effective service invocation; which is one way to look at what happens when an auth module does a forward.

In that case, the module could return SEND_SUCCESS, which would cause the runtime to skip the authorization check, the dispatch to the component, and the call to secureResponse (as it does when validateRequest is called after the service invocation). In this case, the module would also need to do whatever it would do if secureResponse were to be called.

this can also work beneath authenticate, in which case the runtime knows it is calling validateRequest after the service invocation (and must never dispatch to the component, independent of what the module returns). Perhaps this is another reason why Websphere sets an authenticate indication in MessageInfo, so that the module will know not to return SUCCESS; although I don't think that really matters, since the runtime knows that the call to validateRequest was made in the context of a call to authenticate. As above (in this case, if a forward is done by the auth module, it could return SEND_SUCCESS, and it would need to do whatever secure response processing it would do if secureResponse were to be called.

what do you think, and if you have a chance to try this out let me know what you find

@glassfishrobot
Copy link
Author

@glassfishrobot Commented
arjan_t said:

I am thinking that the servlet profile needs to allow for a third case, that where validateRequest is called before the service invocation and returns after the effective service invocation; which is one way to look at what happens when an auth module does a forward.

This sounds useful. If I understand correctly, it would work a little like a Servlet Filter when it calls FilterChain.doFilter, wouldn't it? This could also address the use case for supplying a wrapped request and/or response then (see also #8).

In code, would it then look something like this?

public AuthStatus validateRequest(MessageInfo messageInfo, Subject clientSubject, Subject serviceSubject) throws AuthException {

    SomeType someType = messageInfo.getMap().get("someKey");

    someType.invokeService(
         (HttpServletRequest) messageInfo.getRequestMessage(),
         (HttpServletResponse) messageInfo.getResponseMessage()
    };

    return SEND_CONTINUE;
}

what do you think, and if you have a chance to try this out let me know what you find

It sounds like a good idea. If there's any code or prototype available for this I can surely try it out.

In case some extra type of some sort is needed to invoke the service during the call to validateRequest, then could it also be an idea to introduce a specific sub-interface of ServerAuthModule for the Servlet Profile?

The Servlet spec has something similar. There's a javax.servlet.GenericServlet which is a protocol-independent servlet and there's javax.servlet.http.HttpServlet, which contains Http specific functionality. I experimented a little with this idea here: HttpServerAuthModule (but this may be better addressed via a separate issue)

@glassfishrobot
Copy link
Author

@glassfishrobot Commented
This issue was imported from java.net JIRA JASPIC_SPEC-7

@glassfishrobot
Copy link
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants