Resteasy - destroy session after request - serious bug?
larshuber Jan 25, 2011 4:01 AMResteasy can be configured to destroy the websession right after the request (default behaviour). In few circumstances the session can't be destroyed anymore. Example is if using basic authentication you can access the previous authenticated session even if giving wrong credentials in request. This can end up in serious security issues.
Current code - 2.2.1.CR3 - org.jboss.seam.resteasy.ResteasyResourceAdapter.java
public void getResource(...) { ... dispatcher.invoke(in, theResponse); // Prevent anemic sessions clog up the server if (request.getSession().isNew() && application.isDestroySessionAfterRequest() && !Session.instance().isInvalid()) { log.debug("Destroying HttpSession after REST request"); Session.instance().invalidate(); } ... }
Take in account that destroying the session is not placed in finally block.
Scenario:
1. You miss giving the credentials on first service call. AuthenticationFilter throws an exception. From this point, the session.isNew() will always return false but the code in ResteasyResourceAdapter to destroy the session was never reached. Looking at the condition if destroying the session shows, the session will never be destroyed due to the NOT NEW session.
2. Authentication succeeded or even no authentication required. The service throws an exception and the code to invalidate the session is skipped. From now on the old story: the session is not new anymore, never entering the responsible code block.
Personal intepretation:
If resteasy is configured to destroy the session, the session should be invalidated in any case, coming to following code and workaround:
- override the Adapter by your own implementation. Unfortunately you have to mention: @Install(precedence = Install.DEPLOYMENT) due the default implementation is already Install.APPLICATION and not Install.FRAMEWORK
- override getResource(...)
- put responsible code in try finally block as following
public void getResource(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException { try { log.debug("processing REST request"); // TODO: As far as I can tell from tracing RE code: All this thread-local stuff has no effect because // the "default" provider factory is always used. But we do it anyway, just to mimic the servlet handler // in RE... // Wrap in RESTEasy thread-local factory handling ThreadLocalResteasyProviderFactory.push(dispatcher.getProviderFactory()); // Wrap in RESTEasy contexts (this also puts stuff in a thread-local) SeamResteasyProviderFactory.pushContext(HttpServletRequest.class, request); SeamResteasyProviderFactory.pushContext(HttpServletResponse.class, response); SeamResteasyProviderFactory.pushContext(SecurityContext.class, new ServletSecurityContext(request)); // Wrap in Seam contexts new ContextualHttpServletRequest(request) { @Override public void process() throws ServletException, IOException { try { HttpHeaders headers = ServletUtil.extractHttpHeaders(request); UriInfoImpl uriInfo = extractUriInfo(request, application.getResourcePathPrefix()); HttpResponse theResponse = new HttpServletResponseWrapper(response, dispatcher.getProviderFactory()); // TODO: This requires a SynchronousDispatcher HttpRequest in = new HttpServletInputMessage(request, theResponse, headers, uriInfo, request.getMethod().toUpperCase(), (SynchronousDispatcher) dispatcher); dispatcher.invoke(in, theResponse); } finally { // Just take in account if configured to destroy on each request if (application.isDestroySessionAfterRequest()) { log.debug("Destroying HttpSession after REST request"); Session.instance().invalidate(); } } } }.run(); } finally { // Clean up the thread-locals SeamResteasyProviderFactory.clearContextData(); ThreadLocalResteasyProviderFactory.pop(); log.debug("completed processing of REST request"); } }
Is it a bug or what is the reason for this kind of implementation.
greets
Lars