According to section Section 3.8.3.4 of the JSR 196 specification, an auth module can wrap a request and response during validateRequest processing. Such a wrapped request and response should then be available "downstream":
When a ServerAuthModule returns a wrapped message in MessageInfo, or unwraps a message in MessageInfo, the message processing runtime must ensure that the HttpServletRequest and HttpServletResponse objects established by the ServerAuthModule are used in downstream processing.
It is perhaps not entirely clear what "downstream" means.
A user might expect that such a wrapped request and response means that all filters and the Servlet that gets invoked for that request get to see these types (e.g. like a Servlet Filter does when it provides wrapped types to the FilterChain.doFilter method). However, this happens in none of the well known and certified JASPIC implementations (GlassFish, JBoss EAP, Geronimi, WebLogic and WebSphere).
For instance consider the following wrapper class:
public class MyTestWrapper extends HttpServletRequestWrapper {
public MyTestWrapper(HttpServletRequest request) {
super(request);
}
}
and the following fragment in a ServerAuthModule.validateRequest implementation:
HttpServletRequest request = (HttpServletRequest) messageInfo.getRequestMessage();
messageInfo.setRequestMessage(new MyTestWrapper(request));
return SUCCESS;
Then the following assert in an HttpServlet that is invoked during the same request in which the auth module wrapped the HttpServletRequest instance fails for every of the above mentioned JASPIC implementations:
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
assert(request instanceof MyTestWrapper);
}
Looking at the source code of various implementations, it looks like GlassFish could indeed provide the wrapped response downstream. In RealmAdapter the following code fragment appears right after the auth module is invoked:
HttpServletRequest newRequest = (HttpServletRequest) messageInfo.getRequestMessage();
if (newRequest != req) {
request.setNote(Globals.WRAPPED_REQUEST, new HttpRequestWrapper(request, newRequest));
}
Then in StandardPipeline the following code could pass the wrapped request downstream, if chaining were to be true:
Request req = request;
Response resp = response;
if (chaining) {
req = getRequest(request);
resp = getResponse(request, response);
}
basic.invoke(req, resp);
The method getRequest would indeed get the wrapped request when it exists:
private Request getRequest(Request request) {
Request r = (Request) request.getNote(Globals.WRAPPED_REQUEST);
if (r == null) {
r = request;
}
return r;
}
In practice though, the chaining boolean in the StandardPipeline fragment is always false, causing the original objects to be passed along instead of the wrapped ones. Moreover, similar analysis in the source code of e.g. JBoss EAP reveals that there is no code present whatsoever that could pass the wrapped request downstream.
Since wrapping is an important use case (it's a common technique used to "restore" the original request after a form based authentication), I would like to request:
- A clarification about what "downstream available" means, perhaps by providing a code sample such as shown above.
- A TCK test that tests whether a request and response wrapped by an auth module are indeed available in a simple servlet in a way as shown above.