This change adds reinitializeSession as a static method in VaadinService. The method is static because it affects the sessions of all VaadinService instances that are using the underlying session. Change-Id: Ia37567cb00e1f95aec344451299c99cb279a7275tags/7.0.0.beta3
@@ -23,6 +23,7 @@ import java.lang.reflect.Method; | |||
import java.net.MalformedURLException; | |||
import java.net.URL; | |||
import java.util.ArrayList; | |||
import java.util.HashMap; | |||
import java.util.Iterator; | |||
import java.util.Locale; | |||
import java.util.ServiceLoader; | |||
@@ -49,6 +50,9 @@ import com.vaadin.util.ReflectTools; | |||
* @since 7.0 | |||
*/ | |||
public abstract class VaadinService implements Serializable { | |||
private static final String REINITIALIZING_SESSION_MARKER = VaadinService.class | |||
.getName() + ".reinitializing"; | |||
private static final Method SESSION_INIT_METHOD = ReflectTools.findMethod( | |||
SessionInitListener.class, "sessionInit", SessionInitEvent.class); | |||
@@ -271,6 +275,11 @@ public abstract class VaadinService implements Serializable { | |||
} | |||
public void fireSessionDestroy(VaadinServiceSession vaadinSession) { | |||
// Ignore if the session is being moved to a different backing session | |||
if (vaadinSession.getAttribute(REINITIALIZING_SESSION_MARKER) == Boolean.TRUE) { | |||
return; | |||
} | |||
for (UI ui : new ArrayList<UI>(vaadinSession.getUIs())) { | |||
vaadinSession.cleanupUI(ui); | |||
} | |||
@@ -619,4 +628,58 @@ public abstract class VaadinService implements Serializable { | |||
public boolean preserveUIOnRefresh(UIProvider provider, UICreateEvent event) { | |||
return provider.isPreservedOnRefresh(event); | |||
} | |||
/** | |||
* Discards the current session and creates a new session with the same | |||
* contents. The purpose of this is to introduce a new session key in order | |||
* to avoid session fixation attacks. | |||
* <p> | |||
* Please note that this method makes certain assumptions about how data is | |||
* stored in the underlying session and may thus not be compatible with some | |||
* environments. | |||
* | |||
* @param request | |||
* The Vaadin request for which the session should be | |||
* reinitialized | |||
*/ | |||
public static void reinitializeSession(VaadinRequest request) { | |||
WrappedSession oldSession = request.getWrappedSession(); | |||
// Stores all attributes (security key, reference to this context | |||
// instance) so they can be added to the new session | |||
HashMap<String, Object> attrs = new HashMap<String, Object>(); | |||
for (String name : oldSession.getAttributeNames()) { | |||
Object value = oldSession.getAttribute(name); | |||
if (value instanceof VaadinServiceSession) { | |||
// set flag to avoid cleanup | |||
VaadinServiceSession serviceSession = (VaadinServiceSession) value; | |||
serviceSession.setAttribute(REINITIALIZING_SESSION_MARKER, | |||
Boolean.TRUE); | |||
} | |||
attrs.put(name, value); | |||
} | |||
// Invalidate the current session | |||
oldSession.invalidate(); | |||
// Create a new session | |||
WrappedSession newSession = request.getWrappedSession(); | |||
// Restores all attributes (security key, reference to this context | |||
// instance) | |||
for (String name : attrs.keySet()) { | |||
Object value = attrs.get(name); | |||
newSession.setAttribute(name, value); | |||
// Ensure VaadinServiceSession knows where it's stored | |||
if (value instanceof VaadinServiceSession) { | |||
VaadinServiceSession serviceSession = (VaadinServiceSession) value; | |||
serviceSession.storeInSession(serviceSession.getService(), | |||
newSession); | |||
serviceSession | |||
.setAttribute(REINITIALIZING_SESSION_MARKER, null); | |||
} | |||
} | |||
} | |||
} |
@@ -225,6 +225,7 @@ public class VaadinServiceSession implements HttpSessionBindingListener, | |||
// closing | |||
// Notify the service | |||
service.fireSessionDestroy(this); | |||
session = null; | |||
} | |||
/** |
@@ -16,12 +16,7 @@ | |||
package com.vaadin.server; | |||
import java.util.Enumeration; | |||
import java.util.HashMap; | |||
import javax.servlet.http.HttpServletRequest; | |||
import javax.servlet.http.HttpSession; | |||
import javax.servlet.http.HttpSessionBindingEvent; | |||
import javax.servlet.http.HttpSessionBindingListener; | |||
/** | |||
@@ -39,8 +34,6 @@ import javax.servlet.http.HttpSessionBindingListener; | |||
@SuppressWarnings("serial") | |||
public class VaadinServletSession extends VaadinServiceSession { | |||
private transient boolean reinitializingSession = false; | |||
/** | |||
* Create a servlet service session for the given servlet service | |||
* | |||
@@ -51,60 +44,6 @@ public class VaadinServletSession extends VaadinServiceSession { | |||
super(service); | |||
} | |||
@Override | |||
public void valueUnbound(HttpSessionBindingEvent event) { | |||
if (!reinitializingSession) { | |||
// Avoid closing the application if we are only reinitializing the | |||
// session. Closing the application would cause the state to be lost | |||
// and a new application to be created, which is not what we want. | |||
super.valueUnbound(event); | |||
} | |||
} | |||
/** | |||
* Discards the current session and creates a new session with the same | |||
* contents. The purpose of this is to introduce a new session key in order | |||
* to avoid session fixation attacks. | |||
*/ | |||
public void reinitializeSession() { | |||
HttpServletRequest currentRequest = VaadinServletService | |||
.getCurrentServletRequest(); | |||
if (currentRequest == null) { | |||
throw new IllegalStateException( | |||
"Can not reinitialize session outside normal request handling."); | |||
} | |||
HttpSession oldSession = getHttpSession(); | |||
// Stores all attributes (security key, reference to this context | |||
// instance) so they can be added to the new session | |||
HashMap<String, Object> attrs = new HashMap<String, Object>(); | |||
for (Enumeration<String> e = oldSession.getAttributeNames(); e | |||
.hasMoreElements();) { | |||
String name = e.nextElement(); | |||
attrs.put(name, oldSession.getAttribute(name)); | |||
} | |||
// Invalidate the current session, set flag to avoid call to | |||
// valueUnbound | |||
reinitializingSession = true; | |||
oldSession.invalidate(); | |||
reinitializingSession = false; | |||
// Create a new session | |||
HttpSession newSession = currentRequest.getSession(); | |||
// Restores all attributes (security key, reference to this context | |||
// instance) | |||
for (String name : attrs.keySet()) { | |||
newSession.setAttribute(name, attrs.get(name)); | |||
} | |||
// Update the "current session" variable | |||
storeInSession(VaadinService.getCurrent(), new WrappedHttpSession( | |||
newSession)); | |||
} | |||
/** | |||
* Gets the http-session application is running in. | |||
* |
@@ -16,6 +16,11 @@ | |||
package com.vaadin.server; | |||
import java.util.Collections; | |||
import java.util.Enumeration; | |||
import java.util.HashSet; | |||
import java.util.Set; | |||
import javax.servlet.http.HttpSession; | |||
/** | |||
@@ -63,4 +68,24 @@ public class WrappedHttpSession implements WrappedSession { | |||
return session; | |||
} | |||
@Override | |||
public Set<String> getAttributeNames() { | |||
Enumeration<String> attributeNames = session.getAttributeNames(); | |||
return enumerationToSet(attributeNames); | |||
} | |||
// Helper shared with WrappedPortletSession | |||
static <T> Set<T> enumerationToSet(Enumeration<T> values) { | |||
HashSet<T> set = new HashSet<T>(); | |||
while (values.hasMoreElements()) { | |||
set.add(values.nextElement()); | |||
} | |||
return Collections.unmodifiableSet(set); | |||
} | |||
@Override | |||
public void invalidate() { | |||
session.invalidate(); | |||
} | |||
} |
@@ -16,6 +16,8 @@ | |||
package com.vaadin.server; | |||
import java.util.Set; | |||
import javax.portlet.PortletSession; | |||
/** | |||
@@ -62,4 +64,14 @@ public class WrappedPortletSession implements WrappedSession { | |||
public PortletSession getPortletSession() { | |||
return session; | |||
} | |||
@Override | |||
public Set<String> getAttributeNames() { | |||
return WrappedHttpSession.enumerationToSet(session.getAttributeNames()); | |||
} | |||
@Override | |||
public void invalidate() { | |||
session.invalidate(); | |||
} | |||
} |
@@ -16,6 +16,8 @@ | |||
package com.vaadin.server; | |||
import java.util.Set; | |||
import javax.portlet.PortletSession; | |||
import javax.servlet.http.HttpSession; | |||
@@ -66,4 +68,22 @@ public interface WrappedSession { | |||
* @see javax.portlet.PortletSession#setAttribute(String, Object) | |||
*/ | |||
public void setAttribute(String name, Object value); | |||
/** | |||
* Gets the current set of attribute names stored in this session. | |||
* | |||
* @return an unmodifiable set of the current attribute names | |||
* | |||
* @see HttpSession#getAttributeNames() | |||
* @see PortletSession#getAttributeNames() | |||
*/ | |||
public Set<String> getAttributeNames(); | |||
/** | |||
* Invalidates this session then unbinds any objects bound to it. | |||
* | |||
* @see HttpSession#invalidate() | |||
* @see PortletSession#invalidate() | |||
*/ | |||
public void invalidate(); | |||
} |
@@ -1,5 +1,6 @@ | |||
package com.vaadin.tests.applicationcontext; | |||
import com.vaadin.server.VaadinService; | |||
import com.vaadin.server.VaadinServletSession; | |||
import com.vaadin.tests.components.AbstractTestCase; | |||
import com.vaadin.tests.util.Log; | |||
@@ -35,7 +36,8 @@ public class ChangeSessionId extends AbstractTestCase { | |||
VaadinServletSession context = ((VaadinServletSession) getContext()); | |||
String oldSessionId = context.getHttpSession().getId(); | |||
context.reinitializeSession(); | |||
context.getService().reinitializeSession( | |||
VaadinService.getCurrentRequest()); | |||
String newSessionId = context.getHttpSession().getId(); | |||
if (oldSessionId.equals(newSessionId)) { | |||
log.log("FAILED! Both old and new session id is " |