Pārlūkot izejas kodu

Use separate identifier for push connections (#9150)

By using a separate id we can avoid sending the sessions
CSRF token as a GET parameter when initializing a push connection.

Cherry-picked from #8700 to the 7.7 branch.
tags/7.7.11
Olli Tietäväinen pirms 6 gadiem
vecāks
revīzija
97fa55d3d4

+ 4
- 4
client/src/main/java/com/vaadin/client/communication/AtmospherePushConnection.java Parādīt failu

@@ -207,10 +207,10 @@ public class AtmospherePushConnection implements PushConnection {
String extraParams = UIConstants.UI_ID_PARAMETER + "="
+ connection.getConfiguration().getUIId();

String csrfToken = connection.getMessageHandler().getCsrfToken();
if (!csrfToken.equals(ApplicationConstants.CSRF_TOKEN_DEFAULT_VALUE)) {
extraParams += "&" + ApplicationConstants.CSRF_TOKEN_PARAMETER + "="
+ csrfToken;
String pushId = connection.getMessageHandler().getPushId();
if (pushId != null) {
extraParams += "&" + ApplicationConstants.PUSH_ID_PARAMETER + "="
+ pushId;
}

// uri is needed to identify the right connection when closing

+ 38
- 18
client/src/main/java/com/vaadin/client/communication/MessageHandler.java Parādīt failu

@@ -133,6 +133,9 @@ public class MessageHandler {
// will hold the CSRF token once received
private String csrfToken = ApplicationConstants.CSRF_TOKEN_DEFAULT_VALUE;

// holds the push identifier once received
private String pushId = null;

/** Timer for automatic redirect to SessionExpiredURL */
private Timer redirectTimer;

@@ -349,6 +352,12 @@ public class MessageHandler {
csrfToken = json
.getString(ApplicationConstants.UIDL_SECURITY_TOKEN_ID);
}

// Get push id if present
if (json.containsKey(ApplicationConstants.UIDL_PUSH_ID)) {
pushId = json.getString(ApplicationConstants.UIDL_PUSH_ID);
}

getLogger().info(" * Handling resources from server");

if (json.containsKey("resources")) {
@@ -361,7 +370,7 @@ public class MessageHandler {
}
}
handleUIDLDuration
.logDuration(" * Handling resources from server completed", 10);
.logDuration(" * Handling resources from server completed", 10);

getLogger().info(" * Handling type inheritance map from server");

@@ -489,7 +498,7 @@ public class MessageHandler {
if (json.containsKey("dd")) {
// response contains data for drag and drop service
VDragAndDropManager.get()
.handleServerResponse(json.getValueMap("dd"));
.handleServerResponse(json.getValueMap("dd"));
}

unregisterRemovedConnectors(
@@ -647,13 +656,13 @@ public class MessageHandler {

if (child instanceof ComponentConnector
&& ((ComponentConnector) child)
.delegateCaptionHandling()) {
.delegateCaptionHandling()) {
ServerConnector parent = child.getParent();
if (parent instanceof HasComponentsConnector) {
Profiler.enter(
"HasComponentsConnector.updateCaption");
((HasComponentsConnector) parent)
.updateCaption((ComponentConnector) child);
.updateCaption((ComponentConnector) child);
Profiler.leave(
"HasComponentsConnector.updateCaption");
}
@@ -732,7 +741,7 @@ public class MessageHandler {
throw new RuntimeException(
"Missing data needed to invoke @DelegateToWidget for "
+ component.getClass().getSimpleName(),
e);
e);
}
}

@@ -827,7 +836,7 @@ public class MessageHandler {
}

getLogger().info("* Unregistered " + detachedArray.length()
+ " connectors");
+ " connectors");
Profiler.leave("unregisterRemovedConnectors");
}

@@ -908,7 +917,7 @@ public class MessageHandler {
}

getLogger()
.info(" * Passing UIDL to Vaadin 6 style connectors");
.info(" * Passing UIDL to Vaadin 6 style connectors");
// update paintables
for (int i = 0; i < length; i++) {
try {
@@ -935,14 +944,14 @@ public class MessageHandler {
} else if (legacyConnector == null) {
getLogger().severe(
"Received update for " + uidl.getTag()
+ ", but there is no such paintable ("
+ connectorId + ") rendered.");
+ ", but there is no such paintable ("
+ connectorId + ") rendered.");
} else {
getLogger()
.severe("Server sent Vaadin 6 style updates for "
+ Util.getConnectorString(
legacyConnector)
+ " but this is not a Vaadin 6 Paintable");
.severe("Server sent Vaadin 6 style updates for "
+ Util.getConnectorString(
legacyConnector)
+ " but this is not a Vaadin 6 Paintable");
}

} catch (final Throwable e) {
@@ -1035,8 +1044,8 @@ public class MessageHandler {

if (connector instanceof HasJavaScriptConnectorHelper) {
((HasJavaScriptConnectorHelper) connector)
.getJavascriptConnectorHelper()
.setNativeState(jso);
.getJavascriptConnectorHelper()
.setNativeState(jso);
}

SharedState state = connector.getState();
@@ -1244,7 +1253,7 @@ public class MessageHandler {
newChildren.add(childConnector);
if (childConnector instanceof ComponentConnector) {
newComponents
.add((ComponentConnector) childConnector);
.add((ComponentConnector) childConnector);
} else if (!(childConnector instanceof AbstractExtensionConnector)) {
throw new IllegalStateException(Util
.getConnectorString(childConnector)
@@ -1419,7 +1428,7 @@ public class MessageHandler {
Profiler.enter(
prefix + "recursivelyDetach clear children and parent");
connector
.setChildren(Collections.<ServerConnector> emptyList());
.setChildren(Collections.<ServerConnector> emptyList());
connector.setParent(null);
Profiler.leave(
prefix + "recursivelyDetach clear children and parent");
@@ -1465,7 +1474,7 @@ public class MessageHandler {
Profiler.enter("handleRpcInvocations");

getLogger()
.info(" * Performing server to client RPC calls");
.info(" * Performing server to client RPC calls");

JsonArray rpcCalls = Util
.jso2json(json.getJavaScriptObject("rpc"));
@@ -1693,6 +1702,17 @@ public class MessageHandler {
return csrfToken;
}

/**
* Gets the push connection identifier for this session. Used when
* establishing a push connection with the client.
*
* @return the push connection identifier string
* @since 7.7.11
*/
public String getPushId() {
return pushId;
}

/**
* Checks whether state changes are currently being processed. Certain
* operations are not allowed when the internal state of the application

+ 20
- 6
server/src/main/java/com/vaadin/server/VaadinSession.java Parādīt failu

@@ -508,8 +508,8 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable {
private void refreshLock() {
assert lock == null || lock == service.getSessionLock(
session) : "Cannot change the lock from one instance to another";
assert hasLock(service, session);
lock = service.getSessionLock(session);
assert hasLock(service, session);
lock = service.getSessionLock(session);
}

public void setCommunicationManager(
@@ -747,6 +747,8 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable {

private final String csrfToken = UUID.randomUUID().toString();

private final String pushId = UUID.randomUUID().toString();

/**
* Generate an id for the given Connector. Connectors must not call this
* method more than once, the first time they need an id.
@@ -1008,12 +1010,12 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable {
getLogger().log(Level.SEVERE,
"Exception while cleaning connector map for ui "
+ ui.getUIId(),
e);
e);
} catch (AssertionError e) {
getLogger().log(Level.SEVERE,
"Exception while cleaning connector map for ui "
+ ui.getUIId(),
e);
e);
}
}
}
@@ -1089,7 +1091,7 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable {
}
if (value != null && !type.isInstance(value)) {
throw new IllegalArgumentException("value of type " + type.getName()
+ " expected but got " + value.getClass().getName());
+ " expected but got " + value.getClass().getName());
}
setAttribute(type.getName(), value);
}
@@ -1287,7 +1289,7 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable {
protected void setState(State state) {
assert hasLock();
assert this.state.isValidChange(state) : "Invalid session state change "
+ this.state + "->" + state;
+ this.state + "->" + state;

this.state = state;
}
@@ -1422,6 +1424,18 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable {
return csrfToken;
}

/**
* Gets the push connection identifier for this session. Used when
* establishing a push connection with the client.
*
* @return the push connection identifier string
* @since 7.7.11
*/
public String getPushId() {
assert hasLock();
return pushId;
}

/**
* Override default deserialization logic to account for transient
* {@link #pendingAccessQueue}.

+ 22
- 3
server/src/main/java/com/vaadin/server/communication/PushHandler.java Parādīt failu

@@ -91,10 +91,10 @@ public class PushHandler {
}

String requestToken = resource.getRequest()
.getParameter(ApplicationConstants.CSRF_TOKEN_PARAMETER);
if (!VaadinService.isCsrfTokenValid(session, requestToken)) {
.getParameter(ApplicationConstants.PUSH_ID_PARAMETER);
if (!isPushIdValid(session, requestToken)) {
getLogger().log(Level.WARNING,
"Invalid CSRF token in new connection received from {0}",
"Invalid identifier in new connection received from {0}",
resource.getRequest().getRemoteHost());
// Refresh on client side, create connection just for
// sending a message
@@ -472,6 +472,25 @@ public class PushHandler {
return Logger.getLogger(PushHandler.class.getName());
}

/**
* Checks whether a given push id matches the session's push id.
*
* @param session
* the vaadin session for which the check should be done
* @param requestPushId
* the push id provided in the request
* @return {@code true} if the id is valid, {@code false} otherwise
*/
private static boolean isPushIdValid(VaadinSession session,
String requestPushId) {

String sessionPushId = session.getPushId();
if (requestPushId == null || !requestPushId.equals(sessionPushId)) {
return false;
}
return true;
}

/**
* Called when a new push connection is requested to be opened by the client
*

+ 16
- 2
server/src/main/java/com/vaadin/server/communication/UIInitHandler.java Parādīt failu

@@ -220,7 +220,7 @@ public abstract class UIInitHandler extends SynchronizedRequestHandler {
session.addUI(ui);
if (initException != null) {
ui.getSession().getCommunicationManager()
.handleConnectorRelatedException(ui, initException);
.handleConnectorRelatedException(ui, initException);
}
// Warn if the window can't be preserved
if (embedId == null
@@ -288,6 +288,7 @@ public abstract class UIInitHandler extends SynchronizedRequestHandler {
if (session.getConfiguration().isXsrfProtectionEnabled()) {
writer.write(getSecurityKeyUIDL(session));
}
writer.write(getPushIdUIDL(session));
new UidlWriter().write(uI, writer, false);
writer.write("}");

@@ -310,7 +311,20 @@ public abstract class UIInitHandler extends SynchronizedRequestHandler {
String seckey = session.getCsrfToken();

return "\"" + ApplicationConstants.UIDL_SECURITY_TOKEN_ID + "\":\""
+ seckey + "\",";
+ seckey + "\",";
}

/**
* Gets the push connection identifier as UIDL.
*
* @param session
* the vaadin session to which the security key belongs
* @return the push identifier UIDL
* @since 7.7.11
*/
private static String getPushIdUIDL(VaadinSession session) {
return "\"" + ApplicationConstants.UIDL_PUSH_ID + "\":\""
+ session.getPushId() + "\",";
}

private static final Logger getLogger() {

+ 9
- 0
shared/src/main/java/com/vaadin/shared/ApplicationConstants.java Parādīt failu

@@ -49,6 +49,8 @@ public class ApplicationConstants implements Serializable {

public static final String UIDL_SECURITY_TOKEN_ID = "Vaadin-Security-Key";

public static final String UIDL_PUSH_ID = "Vaadin-Push-ID";

@Deprecated
public static final String UPDATE_VARIABLE_INTERFACE = "v";
@Deprecated
@@ -108,6 +110,13 @@ public class ApplicationConstants implements Serializable {
*/
public static final String CSRF_TOKEN_PARAMETER = "v-csrfToken";

/**
* Name of the parameter used to transmit the push connection identifier.
*
* @since 7.7.11
*/
public static final String PUSH_ID_PARAMETER = "v-pushId";

/**
* The name of the parameter used to transmit RPC invocations
*

Notiek ielāde…
Atcelt
Saglabāt