summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBogdan Udrescu <bogdan@vaadin.com>2014-07-09 18:40:26 +0300
committerLeif Åstrand <leif@vaadin.com>2014-07-15 08:50:38 +0000
commitdf13b061f2f6ba10beae64d9b358c0365736370e (patch)
tree0a2800b1e383f3f7fb648ea5ec9ec9512e54906d
parent9696e6c3e7e952b66ac3f5c9ddc3dfca4233451e (diff)
downloadvaadin-framework-df13b061f2f6ba10beae64d9b358c0365736370e.tar.gz
vaadin-framework-df13b061f2f6ba10beae64d9b358c0365736370e.zip
Remove csrfToken if disable-xsrf-protection is true (#14111)
If the server sends no token and the client value remains "init" then it's not sent back to the server. Change-Id: I74fc470c5c22d57c4a48eab3e4476ae4cc2dd242
-rw-r--r--client/src/com/vaadin/client/ApplicationConnection.java198
-rw-r--r--client/src/com/vaadin/client/communication/AtmospherePushConnection.java26
-rw-r--r--server/src/com/vaadin/server/communication/ServerRpcHandler.java8
-rw-r--r--server/tests/src/com/vaadin/tests/server/CsrfTokenMissingTestServer.java248
-rw-r--r--shared/src/com/vaadin/shared/ApplicationConstants.java20
-rw-r--r--uitest/src/com/vaadin/tests/widgetset/TestingWidgetSet.gwt.xml5
-rw-r--r--uitest/src/com/vaadin/tests/widgetset/client/MockApplicationConnection.java81
-rw-r--r--uitest/src/com/vaadin/tests/widgetset/client/csrf/CsrfButtonConnector.java82
-rw-r--r--uitest/src/com/vaadin/tests/widgetset/server/csrf/CsrfButton.java30
-rw-r--r--uitest/src/com/vaadin/tests/widgetset/server/csrf/ui/AbstractCsrfTokenUI.java60
-rw-r--r--uitest/src/com/vaadin/tests/widgetset/server/csrf/ui/AbstractCsrfTokenUITest.java116
-rw-r--r--uitest/src/com/vaadin/tests/widgetset/server/csrf/ui/CsrfTokenDisabled.java35
-rw-r--r--uitest/src/com/vaadin/tests/widgetset/server/csrf/ui/CsrfTokenDisabledTest.java43
-rw-r--r--uitest/src/com/vaadin/tests/widgetset/server/csrf/ui/CsrfTokenEnabled.java25
-rw-r--r--uitest/src/com/vaadin/tests/widgetset/server/csrf/ui/CsrfTokenEnabledTest.java43
15 files changed, 903 insertions, 117 deletions
diff --git a/client/src/com/vaadin/client/ApplicationConnection.java b/client/src/com/vaadin/client/ApplicationConnection.java
index 6bbca98042..6abcdac487 100644
--- a/client/src/com/vaadin/client/ApplicationConnection.java
+++ b/client/src/com/vaadin/client/ApplicationConnection.java
@@ -1,12 +1,12 @@
/*
* Copyright 2000-2014 Vaadin Ltd.
- *
+ *
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
@@ -111,14 +111,14 @@ import com.vaadin.shared.ui.ui.UIState.PushConfigurationState;
* This is the client side communication "engine", managing client-server
* communication with its server side counterpart
* com.vaadin.server.VaadinService.
- *
+ *
* Client-side connectors receive updates from the corresponding server-side
* connector (typically component) as state updates or RPC calls. The connector
* has the possibility to communicate back with its server side counter part
* through RPC calls.
- *
+ *
* TODO document better
- *
+ *
* Entry point classes (widgetsets) define <code>onModuleLoad()</code>.
*/
public class ApplicationConnection implements HasHandlers {
@@ -150,12 +150,12 @@ public class ApplicationConnection implements HasHandlers {
* A string that, if found in a non-JSON response to a UIDL request, will
* cause the browser to refresh the page. If followed by a colon, optional
* whitespace, and a URI, causes the browser to synchronously load the URI.
- *
+ *
* <p>
* This allows, for instance, a servlet filter to redirect the application
* to a custom login page when the session expires. For example:
* </p>
- *
+ *
* <pre>
* if (sessionExpired) {
* response.setHeader(&quot;Content-Type&quot;, &quot;text/html&quot;);
@@ -168,7 +168,7 @@ public class ApplicationConnection implements HasHandlers {
public static final String UIDL_REFRESH_TOKEN = "Vaadin-Refresh";
// will hold the CSRF token once received
- private String csrfToken = "init";
+ private String csrfToken = ApplicationConstants.CSRF_TOKEN_DEFAULT_VALUE;
private final HashMap<String, String> resourcesMap = new HashMap<String, String>();
@@ -346,7 +346,7 @@ public class ApplicationConnection implements HasHandlers {
/**
* Event triggered when a XHR request has finished with the status code of
* the response.
- *
+ *
* Useful for handlers observing network failures like online/off-line
* monitors.
*/
@@ -402,12 +402,12 @@ public class ApplicationConnection implements HasHandlers {
/**
* Event triggered when a application is stopped by calling
* {@link ApplicationConnection#setApplicationRunning(false)}.
- *
+ *
* To listen for the event add a {@link ApplicationStoppedHandler} by
* invoking
* {@link ApplicationConnection#addHandler(ApplicationConnection.ApplicationStoppedEvent.Type, ApplicationStoppedHandler)}
* to the {@link ApplicationConnection}
- *
+ *
* @since 7.1.8
* @author Vaadin Ltd
*/
@@ -434,7 +434,7 @@ public class ApplicationConnection implements HasHandlers {
/**
* Called when a communication error has occurred. Returning
* <code>true</code> from this method suppresses error handling.
- *
+ *
* @param details
* A string describing the error.
* @param statusCode
@@ -449,7 +449,7 @@ public class ApplicationConnection implements HasHandlers {
* A listener for listening to application stopped events. The listener can
* be added to a {@link ApplicationConnection} by invoking
* {@link ApplicationConnection#addHandler(ApplicationStoppedEvent.Type, ApplicationStoppedHandler)}
- *
+ *
* @since 7.1.8
* @author Vaadin Ltd
*/
@@ -459,7 +459,7 @@ public class ApplicationConnection implements HasHandlers {
* Triggered when the {@link ApplicationConnection} marks a previously
* running application as stopped by invoking
* {@link ApplicationConnection#setApplicationRunning(false)}
- *
+ *
* @param event
* the event triggered by the {@link ApplicationConnection}
*/
@@ -570,7 +570,7 @@ public class ApplicationConnection implements HasHandlers {
* called once this application has started (first response received) or
* failed to start. This ensures that the applications are started in order,
* to avoid session-id problems.
- *
+ *
*/
public void start() {
String jsonText = configuration.getUIDL();
@@ -671,7 +671,7 @@ public class ApplicationConnection implements HasHandlers {
* <li><code>vaadin.postRequestHooks</code> is a map of functions which gets
* called after each XHR made by vaadin application. Note, that it is
* attaching js functions responsibility to create the variable like this:
- *
+ *
* <code><pre>
* if(!vaadin.postRequestHooks) {vaadin.postRequestHooks = new Object();}
* postRequestHooks.myHook = function(appId) {
@@ -682,7 +682,7 @@ public class ApplicationConnection implements HasHandlers {
* </pre></code> First parameter passed to these functions is the identifier
* of Vaadin application that made the request.
* </ul>
- *
+ *
* TODO make this multi-app aware
*/
private native void initializeClientHooks()
@@ -713,7 +713,7 @@ public class ApplicationConnection implements HasHandlers {
/**
* Runs possibly registered client side post request hooks. This is expected
* to be run after each uidl request made by Vaadin application.
- *
+ *
* @param appId
*/
private static native void runPostRequestHooks(String appId)
@@ -733,7 +733,7 @@ public class ApplicationConnection implements HasHandlers {
/**
* If on Liferay and logged in, ask the client side session management
* JavaScript to extend the session duration.
- *
+ *
* Otherwise, Liferay client side JavaScript will explicitly expire the
* session even though the server side considers the session to be active.
* See ticket #8305 for more information.
@@ -752,7 +752,7 @@ public class ApplicationConnection implements HasHandlers {
/**
* Indicates whether or not there are currently active UIDL requests. Used
* internally to sequence requests properly, seldom needed in Widgets.
- *
+ *
* @return true if there are active requests
*/
public boolean hasActiveRequest() {
@@ -772,7 +772,7 @@ public class ApplicationConnection implements HasHandlers {
/**
* Requests an analyze of layouts, to find inconsistencies. Exclusively used
* for debugging during development.
- *
+ *
* @deprecated as of 7.1. Replaced by {@link UIConnector#analyzeLayouts()}
*/
@Deprecated
@@ -784,7 +784,7 @@ public class ApplicationConnection implements HasHandlers {
* Sends a request to the server to print details to console that will help
* the developer to locate the corresponding server-side connector in the
* source code.
- *
+ *
* @param serverConnector
* @deprecated as of 7.1. Replaced by
* {@link UIConnector#showServerDebugInfo(ServerConnector)}
@@ -796,7 +796,7 @@ public class ApplicationConnection implements HasHandlers {
/**
* Makes an UIDL request to the server.
- *
+ *
* @param reqInvocations
* Data containing RPC invocations and all related information.
* @param extraParams
@@ -810,8 +810,10 @@ public class ApplicationConnection implements HasHandlers {
startRequest();
JSONObject payload = new JSONObject();
- payload.put(ApplicationConstants.CSRF_TOKEN, new JSONString(
- getCsrfToken()));
+ if (!getCsrfToken().equals(ApplicationConstants.CSRF_TOKEN_DEFAULT_VALUE)) {
+ payload.put(ApplicationConstants.CSRF_TOKEN, new JSONString(
+ getCsrfToken()));
+ }
payload.put(ApplicationConstants.RPC_INVOCATIONS, reqInvocations);
payload.put(ApplicationConstants.SERVER_SYNC_ID, new JSONNumber(
lastSeenServerSyncId));
@@ -833,7 +835,7 @@ public class ApplicationConnection implements HasHandlers {
/**
* Sends an asynchronous or synchronous UIDL request to the server using the
* given URI.
- *
+ *
* @param uri
* The URI to use for the request. May includes GET parameters
* @param payload
@@ -971,7 +973,7 @@ public class ApplicationConnection implements HasHandlers {
/**
* Handles received UIDL JSON text, parsing it, and passing it on to the
* appropriate handlers, while logging timing information.
- *
+ *
* @param jsonText
* @param statusCode
*/
@@ -999,7 +1001,7 @@ public class ApplicationConnection implements HasHandlers {
/**
* Sends an asynchronous UIDL request to the server using the given URI.
- *
+ *
* @param uri
* The URI to use for the request. May includes GET parameters
* @param payload
@@ -1134,7 +1136,7 @@ public class ApplicationConnection implements HasHandlers {
/**
* Checks whether or not the CSS is loaded. By default checks the size of
* the loading indicator element.
- *
+ *
* @return
*/
protected boolean isCSSLoaded() {
@@ -1144,12 +1146,12 @@ public class ApplicationConnection implements HasHandlers {
/**
* Shows the communication error notification.
- *
+ *
* @param details
* Optional details for debugging.
* @param statusCode
* The status code returned for the request
- *
+ *
*/
protected void showCommunicationError(String details, int statusCode) {
VConsole.error("Communication error: " + details);
@@ -1158,7 +1160,7 @@ public class ApplicationConnection implements HasHandlers {
/**
* Shows the authentication error notification.
- *
+ *
* @param details
* Optional details for debugging.
*/
@@ -1169,7 +1171,7 @@ public class ApplicationConnection implements HasHandlers {
/**
* Shows the session expiration notification.
- *
+ *
* @param details
* Optional details for debugging.
*/
@@ -1180,7 +1182,7 @@ public class ApplicationConnection implements HasHandlers {
/**
* Shows an error notification.
- *
+ *
* @param details
* Optional details for debugging.
* @param message
@@ -1193,7 +1195,7 @@ public class ApplicationConnection implements HasHandlers {
/**
* Shows the error notification.
- *
+ *
* @param details
* Optional details for debugging.
*/
@@ -1281,7 +1283,7 @@ public class ApplicationConnection implements HasHandlers {
/**
* This method is called after applying uidl change set to application.
- *
+ *
* It will clean current and queued variable change sets. And send next
* change set if it exists.
*/
@@ -1300,7 +1302,7 @@ public class ApplicationConnection implements HasHandlers {
/**
* Cleans given queue of variable changes of such changes that came from
* components that do not exist anymore.
- *
+ *
* @param variableBurst
*/
private void cleanVariableBurst(
@@ -1325,7 +1327,7 @@ public class ApplicationConnection implements HasHandlers {
* <p>
* Used by the native "client.isActive" function.
* </p>
- *
+ *
* @return true if deferred commands are (potentially) being executed, false
* otherwise
*/
@@ -1340,7 +1342,7 @@ public class ApplicationConnection implements HasHandlers {
/**
* Returns the loading indicator used by this ApplicationConnection
- *
+ *
* @return The loading indicator for this ApplicationConnection
*/
public VLoadingIndicator getLoadingIndicator() {
@@ -1349,7 +1351,7 @@ public class ApplicationConnection implements HasHandlers {
/**
* Determines whether or not the loading indicator is showing.
- *
+ *
* @return true if the loading indicator is visible
* @deprecated As of 7.1. Use {@link #getLoadingIndicator()} and
* {@link VLoadingIndicator#isVisible()}.isVisible() instead.
@@ -1383,7 +1385,7 @@ public class ApplicationConnection implements HasHandlers {
* server is received.
* <p>
* The initial id when no request has yet been processed is -1.
- *
+ *
* @return and id identifying the response
*/
public int getLastResponseId() {
@@ -1806,13 +1808,13 @@ public class ApplicationConnection implements HasHandlers {
/**
* Sends the state change events created while updating the state
* information.
- *
+ *
* This must be called after hierarchy change listeners have been
* called. At least caption updates for the parent are strange if
* fired from state change listeners and thus calls the parent
* BEFORE the parent is aware of the child (through a
* ConnectorHierarchyChangedEvent)
- *
+ *
* @param pendingStateChangeEvents
* The events to send
*/
@@ -2127,7 +2129,7 @@ public class ApplicationConnection implements HasHandlers {
* Updates the connector hierarchy and returns a list of events that
* should be fired after update of the hierarchy and the state is
* done.
- *
+ *
* @param json
* The JSON containing the hierarchy information
* @return A collection of events that should be fired when update
@@ -2524,9 +2526,9 @@ public class ApplicationConnection implements HasHandlers {
/**
* Adds an explicit RPC method invocation to the send queue.
- *
+ *
* @since 7.0
- *
+ *
* @param invocation
* RPC method invocation
* @param delayed
@@ -2566,7 +2568,7 @@ public class ApplicationConnection implements HasHandlers {
/**
* Removes any pending invocation of the given method from the queue
- *
+ *
* @param invocation
* The invocation to remove
*/
@@ -2584,12 +2586,12 @@ public class ApplicationConnection implements HasHandlers {
/**
* This method sends currently queued variable changes to server. It is
* called when immediate variable update must happen.
- *
+ *
* To ensure correct order for variable changes (due servers multithreading
* or network), we always wait for active request to be handler before
* sending a new one. If there is an active request, we will put varible
* "burst" to queue that will be purged after current request is handled.
- *
+ *
*/
public void sendPendingVariableChanges() {
if (!deferedSendPending) {
@@ -2630,11 +2632,11 @@ public class ApplicationConnection implements HasHandlers {
/**
* Build the variable burst and send it to server.
- *
+ *
* When sync is forced, we also force sending of all pending variable-bursts
* at the same time. This is ok as we can assume that DOM will never be
* updated after this.
- *
+ *
* @param pendingInvocations
* List of RPC method invocations to send
*/
@@ -2721,7 +2723,7 @@ public class ApplicationConnection implements HasHandlers {
* is true, the update is sent as soon as possible. If immediate is false,
* the update will be sent along with the next immediate update.
* </p>
- *
+ *
* @param paintableId
* the id of the paintable that owns the variable
* @param variableName
@@ -2743,7 +2745,7 @@ public class ApplicationConnection implements HasHandlers {
* is true, the update is sent as soon as possible. If immediate is false,
* the update will be sent along with the next immediate update.
* </p>
- *
+ *
* @param paintableId
* the id of the paintable that owns the variable
* @param variableName
@@ -2766,7 +2768,7 @@ public class ApplicationConnection implements HasHandlers {
* is true, the update is sent as soon as possible. If immediate is false,
* the update will be sent along with the next immediate update.
* </p>
- *
+ *
* @param paintableId
* the id of the paintable that owns the variable
* @param variableName
@@ -2789,7 +2791,7 @@ public class ApplicationConnection implements HasHandlers {
* is true, the update is sent as soon as possible. If immediate is false,
* the update will be sent along with the next immediate update.
* </p>
- *
+ *
* @param paintableId
* the id of the paintable that owns the variable
* @param variableName
@@ -2812,7 +2814,7 @@ public class ApplicationConnection implements HasHandlers {
* is true, the update is sent as soon as possible. If immediate is false,
* the update will be sent along with the next immediate update.
* </p>
- *
+ *
* @param paintableId
* the id of the paintable that owns the variable
* @param variableName
@@ -2835,7 +2837,7 @@ public class ApplicationConnection implements HasHandlers {
* is true, the update is sent as soon as possible. If immediate is false,
* the update will be sent along with the next immediate update.
* </p>
- *
+ *
* @param paintableId
* the id of the paintable that owns the variable
* @param variableName
@@ -2858,7 +2860,7 @@ public class ApplicationConnection implements HasHandlers {
* is true, the update is sent as soon as possible. If immediate is false,
* the update will be sent along with the next immediate update.
* </p>
- *
+ *
* @param paintableId
* the id of the paintable that owns the variable
* @param variableName
@@ -2881,7 +2883,7 @@ public class ApplicationConnection implements HasHandlers {
* is true, the update is sent as soon as possible. If immediate is false,
* the update will be sent along with the next immediate update.
* </p>
- *
+ *
* @param paintableId
* the id of the paintable that owns the variable
* @param variableName
@@ -2898,13 +2900,13 @@ public class ApplicationConnection implements HasHandlers {
/**
* Sends a new value for the given paintables given variable to the server.
- *
+ *
* The update is actually queued to be sent at a suitable time. If immediate
* is true, the update is sent as soon as possible. If immediate is false,
* the update will be sent along with the next immediate update.
- *
+ *
* A null array is sent as an empty array.
- *
+ *
* @param paintableId
* the id of the paintable that owns the variable
* @param variableName
@@ -2921,14 +2923,14 @@ public class ApplicationConnection implements HasHandlers {
/**
* Sends a new value for the given paintables given variable to the server.
- *
+ *
* The update is actually queued to be sent at a suitable time. If immediate
* is true, the update is sent as soon as possible. If immediate is false,
* the update will be sent along with the next immediate update. </p>
- *
+ *
* A null array is sent as an empty array.
- *
- *
+ *
+ *
* @param paintableId
* the id of the paintable that owns the variable
* @param variableName
@@ -2945,7 +2947,7 @@ public class ApplicationConnection implements HasHandlers {
/**
* Does absolutely nothing. Replaced by {@link LayoutManager}.
- *
+ *
* @param container
* @deprecated As of 7.0, serves no purpose
*/
@@ -2967,7 +2969,7 @@ public class ApplicationConnection implements HasHandlers {
/**
* Returns false
- *
+ *
* @param paintable
* @return false, always
* @deprecated As of 7.0, serves no purpose
@@ -2979,7 +2981,7 @@ public class ApplicationConnection implements HasHandlers {
/**
* Returns false
- *
+ *
* @param paintable
* @return false, always
* @deprecated As of 7.0, serves no purpose
@@ -3000,16 +3002,16 @@ public class ApplicationConnection implements HasHandlers {
/**
* Get either an existing ComponentConnector or create a new
* ComponentConnector with the given type and id.
- *
+ *
* If a ComponentConnector with the given id already exists, returns it.
* Otherwise creates and registers a new ComponentConnector of the given
* type.
- *
+ *
* @param connectorId
* Id of the paintable
* @param connectorType
* Type of the connector, as passed from the server side
- *
+ *
* @return Either an existing ComponentConnector or a new ComponentConnector
* of the given type
*/
@@ -3022,15 +3024,15 @@ public class ApplicationConnection implements HasHandlers {
/**
* Creates a new ServerConnector with the given type and id.
- *
+ *
* Creates and registers a new ServerConnector of the given type. Should
* never be called with the connector id of an existing connector.
- *
+ *
* @param connectorId
* Id of the new connector
* @param connectorType
* Type of the connector, as passed from the server side
- *
+ *
* @return A new ServerConnector of the given type
*/
private ServerConnector createAndRegisterConnector(String connectorId,
@@ -3050,7 +3052,7 @@ public class ApplicationConnection implements HasHandlers {
/**
* Gets a recource that has been pre-loaded via UIDL, such as custom
* layouts.
- *
+ *
* @param name
* identifier of the resource to get
* @return the resource
@@ -3061,7 +3063,7 @@ public class ApplicationConnection implements HasHandlers {
/**
* Singleton method to get instance of app's context menu.
- *
+ *
* @return VContextMenu object
*/
public VContextMenu getContextMenu() {
@@ -3076,7 +3078,7 @@ public class ApplicationConnection implements HasHandlers {
/**
* Gets an {@link Icon} instance corresponding to a URI.
- *
+ *
* @since 7.2
* @param uri
* @return Icon object
@@ -3098,7 +3100,7 @@ public class ApplicationConnection implements HasHandlers {
* Translates custom protocols in UIDL URI's to be recognizable by browser.
* All uri's from UIDL should be routed via this method before giving them
* to browser due URI's in UIDL may contain custom protocols like theme://.
- *
+ *
* @param uidlUri
* Vaadin URI from uidl
* @return translated URI ready for browser
@@ -3170,7 +3172,7 @@ public class ApplicationConnection implements HasHandlers {
/**
* Gets the URI for the current theme. Can be used to reference theme
* resources.
- *
+ *
* @return URI to the current theme
*/
public String getThemeUri() {
@@ -3180,7 +3182,7 @@ public class ApplicationConnection implements HasHandlers {
/**
* Listens for Notification hide event, and redirects. Used for system
* messages, such as session expired.
- *
+ *
*/
private class NotificationRedirect implements VNotification.EventListener {
String url;
@@ -3209,7 +3211,7 @@ public class ApplicationConnection implements HasHandlers {
/**
* Gets the token (aka double submit cookie) that the server uses to protect
* against Cross Site Request Forgery attacks.
- *
+ *
* @return the CSRF token string
*/
public String getCsrfToken() {
@@ -3219,7 +3221,7 @@ public class ApplicationConnection implements HasHandlers {
/**
* Use to notify that the given component's caption has changed; layouts may
* have to be recalculated.
- *
+ *
* @param component
* the Paintable whose caption has changed
* @deprecated As of 7.0.2, has not had any effect for a long time
@@ -3231,7 +3233,7 @@ public class ApplicationConnection implements HasHandlers {
/**
* Gets the main view
- *
+ *
* @return the main view
*/
public UIConnector getUIConnector() {
@@ -3240,7 +3242,7 @@ public class ApplicationConnection implements HasHandlers {
/**
* Gets the {@link ApplicationConfiguration} for the current application.
- *
+ *
* @see ApplicationConfiguration
* @return the configuration for this application
*/
@@ -3253,7 +3255,7 @@ public class ApplicationConnection implements HasHandlers {
* list of events which has server side listeners is updated automatically
* before the component is updated so the value is correct if called from
* updatedFromUIDL.
- *
+ *
* @param paintable
* The connector to register event listeners for
* @param eventIdentifier
@@ -3273,7 +3275,7 @@ public class ApplicationConnection implements HasHandlers {
/**
* Adds the get parameters to the uri and returns the new uri that contains
* the parameters.
- *
+ *
* @param uri
* The uri to which the parameters should be added.
* @param extraParams
@@ -3326,7 +3328,7 @@ public class ApplicationConnection implements HasHandlers {
/**
* Get VTooltip instance related to application connection
- *
+ *
* @return VTooltip instance
*/
public VTooltip getVTooltip() {
@@ -3338,7 +3340,7 @@ public class ApplicationConnection implements HasHandlers {
* this method is now handled by the state change event handler in
* AbstractComponentConnector. The only function this method has is to
* return true if the UIDL is a "cached" update.
- *
+ *
* @param component
* @param uidl
* @param manageCaption
@@ -3389,7 +3391,7 @@ public class ApplicationConnection implements HasHandlers {
* Schedules a heartbeat request to occur after the configured heartbeat
* interval elapses if the interval is a positive number. Otherwise, does
* nothing.
- *
+ *
* @deprecated as of 7.2, use {@link Heartbeat#schedule()} instead
*/
@Deprecated
@@ -3403,7 +3405,7 @@ public class ApplicationConnection implements HasHandlers {
* Heartbeat requests are used to inform the server that the client-side is
* still alive. If the client page is closed or the connection lost, the
* server will eventually close the inactive UI.
- *
+ *
* @deprecated as of 7.2, use {@link Heartbeat#send()} instead
*/
@Deprecated
@@ -3427,7 +3429,7 @@ public class ApplicationConnection implements HasHandlers {
/**
* This method can be used to postpone rendering of a response for a short
* period of time (e.g. to avoid the rendering process during animation).
- *
+ *
* @param lock
*/
public void suspendReponseHandling(Object lock) {
@@ -3436,7 +3438,7 @@ public class ApplicationConnection implements HasHandlers {
/**
* Resumes the rendering process once all locks have been removed.
- *
+ *
* @param lock
*/
public void resumeResponseHandling(Object lock) {
@@ -3481,7 +3483,7 @@ public class ApplicationConnection implements HasHandlers {
/**
* Sets the delegate that is called whenever a communication error occurrs.
- *
+ *
* @param delegate
* the delegate.
*/
@@ -3524,7 +3526,7 @@ public class ApplicationConnection implements HasHandlers {
/**
* Gets the active connector for focused element in browser.
- *
+ *
* @return Connector for focused element or null.
*/
private ComponentConnector getActiveConnector() {
@@ -3538,7 +3540,7 @@ public class ApplicationConnection implements HasHandlers {
/**
* Sets the status for the push connection.
- *
+ *
* @param enabled
* <code>true</code> to enable the push connection;
* <code>false</code> to disable the push connection.
@@ -3588,7 +3590,7 @@ public class ApplicationConnection implements HasHandlers {
/**
* Returns a human readable string representation of the method used to
* communicate with the server.
- *
+ *
* @since 7.1
* @return A string representation of the current transport type
*/
diff --git a/client/src/com/vaadin/client/communication/AtmospherePushConnection.java b/client/src/com/vaadin/client/communication/AtmospherePushConnection.java
index 48e17cde05..5073e0ce5d 100644
--- a/client/src/com/vaadin/client/communication/AtmospherePushConnection.java
+++ b/client/src/com/vaadin/client/communication/AtmospherePushConnection.java
@@ -1,12 +1,12 @@
/*
* Copyright 2000-2014 Vaadin Ltd.
- *
+ *
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
@@ -37,7 +37,7 @@ import com.vaadin.shared.ui.ui.UIState.PushConfigurationState;
/**
* The default {@link PushConnection} implementation that uses Atmosphere for
* handling the communication channel.
- *
+ *
* @author Vaadin Ltd
* @since 7.1
*/
@@ -171,8 +171,12 @@ public class AtmospherePushConnection implements PushConnection {
+ ApplicationConstants.PUSH_PATH + '/');
String extraParams = UIConstants.UI_ID_PARAMETER + "="
+ connection.getConfiguration().getUIId();
- extraParams += "&" + ApplicationConstants.CSRF_TOKEN_PARAMETER + "="
- + connection.getCsrfToken();
+
+ if (!connection.getCsrfToken().equals(
+ ApplicationConstants.CSRF_TOKEN_DEFAULT_VALUE)) {
+ extraParams += "&" + ApplicationConstants.CSRF_TOKEN_PARAMETER
+ + "=" + connection.getCsrfToken();
+ }
// uri is needed to identify the right connection when closing
uri = ApplicationConnection.addGetParameters(baseUrl, extraParams);
@@ -239,9 +243,9 @@ public class AtmospherePushConnection implements PushConnection {
/**
* Called whenever a server push connection is established (or
* re-established).
- *
+ *
* @param response
- *
+ *
* @since 7.2
*/
protected void onConnect(AtmosphereResponse response) {
@@ -330,7 +334,7 @@ public class AtmospherePushConnection implements PushConnection {
/**
* Called if the push connection fails. Atmosphere will automatically retry
* the connection until successful.
- *
+ *
*/
protected void onError(AtmosphereResponse response) {
state = State.DISCONNECTED;
@@ -448,7 +452,7 @@ public class AtmospherePushConnection implements PushConnection {
contentType: 'application/json; charset=UTF-8',
reconnectInterval: 5000,
timeout: -1,
- maxReconnectOnClose: 10000000,
+ maxReconnectOnClose: 10000000,
trackMessageLength: true,
enableProtocol: true,
messageDelimiter: String.fromCharCode(@com.vaadin.shared.communication.PushConstants::MESSAGE_DELIMITER)
@@ -501,7 +505,7 @@ public class AtmospherePushConnection implements PushConnection {
private static native boolean isAtmosphereLoaded()
/*-{
- return $wnd.jQueryVaadin != undefined;
+ return $wnd.jQueryVaadin != undefined;
}-*/;
private void runWhenAtmosphereLoaded(final Command command) {
diff --git a/server/src/com/vaadin/server/communication/ServerRpcHandler.java b/server/src/com/vaadin/server/communication/ServerRpcHandler.java
index 6f1d44d087..9107a4e049 100644
--- a/server/src/com/vaadin/server/communication/ServerRpcHandler.java
+++ b/server/src/com/vaadin/server/communication/ServerRpcHandler.java
@@ -78,7 +78,13 @@ public class ServerRpcHandler implements Serializable {
public RpcRequest(String jsonString, VaadinRequest request)
throws JSONException {
json = new JSONObject(jsonString);
- csrfToken = json.getString(ApplicationConstants.CSRF_TOKEN);
+
+ String csrfToken = json.optString(ApplicationConstants.CSRF_TOKEN);
+ if (csrfToken.equals("")) {
+ csrfToken = ApplicationConstants.CSRF_TOKEN_DEFAULT_VALUE;
+ }
+ this.csrfToken = csrfToken;
+
if (request.getService().getDeploymentConfiguration()
.isSyncIdCheckEnabled()) {
syncId = json.getInt(ApplicationConstants.SERVER_SYNC_ID);
diff --git a/server/tests/src/com/vaadin/tests/server/CsrfTokenMissingTestServer.java b/server/tests/src/com/vaadin/tests/server/CsrfTokenMissingTestServer.java
new file mode 100644
index 0000000000..30f1c85fef
--- /dev/null
+++ b/server/tests/src/com/vaadin/tests/server/CsrfTokenMissingTestServer.java
@@ -0,0 +1,248 @@
+/*
+ * Copyright 2000-2014 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.tests.server;
+
+import java.util.UUID;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.easymock.EasyMock;
+import org.json.JSONException;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+import com.vaadin.server.ServiceException;
+import com.vaadin.server.VaadinService;
+import com.vaadin.server.VaadinServlet;
+import com.vaadin.server.VaadinServletRequest;
+import com.vaadin.server.VaadinServletService;
+import com.vaadin.server.VaadinSession;
+import com.vaadin.server.communication.ServerRpcHandler.RpcRequest;
+import com.vaadin.shared.ApplicationConstants;
+import com.vaadin.tests.util.AlwaysLockedVaadinSession;
+import com.vaadin.tests.util.MockDeploymentConfiguration;
+
+/**
+ * Test the actual csrf token validation by the server.
+ *
+ * @since
+ * @author Vaadin Ltd
+ */
+public class CsrfTokenMissingTestServer {
+
+ // Dummy fields just to run the test.
+ private VaadinServlet mockServlet;
+
+ // The mock deployment configuration.
+ private MockDeploymentConfiguration mockDeploymentConfiguration;
+
+ private VaadinServletService mockService;
+
+ // The mock UI session.
+ private VaadinSession mockSession;
+
+ // The mock vaadin request.
+ private VaadinServletRequest vaadinRequest;
+
+ /**
+ * Initialize the mock servlet and other stuff for our tests.
+ */
+ @Before
+ public void initMockStuff() throws ServiceException {
+ mockServlet = new VaadinServlet();
+ mockDeploymentConfiguration = new MockDeploymentConfiguration();
+
+ mockService = new VaadinServletService(mockServlet,
+ mockDeploymentConfiguration);
+
+ mockSession = new AlwaysLockedVaadinSession(mockService);
+
+ vaadinRequest = new VaadinServletRequest(
+ EasyMock.createMock(HttpServletRequest.class), mockService);
+
+ }
+
+ private enum TokenType {
+ MISSING, INVALID, VALID
+ }
+
+ private TokenType tokenType;
+
+ private String invalidToken;
+
+ public String getInvalidToken() {
+ if (invalidToken == null) {
+ // Just making sure this will never be in the same format as a valid
+ // token.
+ invalidToken = UUID.randomUUID().toString().substring(1);
+ }
+ return invalidToken;
+ }
+
+ private String getValidToken() {
+ return mockSession.getCsrfToken();
+ }
+
+ /*
+ * Gets the payload with the default token.
+ */
+ private String getPayload() {
+ switch (tokenType) {
+ case MISSING:
+ return getPayload(null);
+
+ case INVALID:
+ return getPayload(getInvalidToken());
+
+ case VALID:
+ return getPayload(getValidToken());
+ }
+
+ return null;
+ }
+
+ /*
+ * Gets the payload with the specified token.
+ */
+ private String getPayload(String token) {
+ return "{"
+ + (token != null ? "\"csrfToken\":" + "\"" + token + "\", "
+ : "")
+ + "\"rpc\":[[\"0\",\"com.vaadin.shared.ui.ui.UIServerRpc\",\"resize\",[\"449\",\"1155\",\"1155\",\"449\"]],[\"4\",\"com.vaadin.shared.ui.button.ButtonServerRpc\",\"click\",[{\"clientY\":\"53\", \"clientX\":\"79\", \"shiftKey\":false, \"button\":\"LEFT\", \"ctrlKey\":false, \"type\":\"1\", \"metaKey\":false, \"altKey\":false, \"relativeY\":\"17\", \"relativeX\":\"61\"}]]], \"syncId\":1}";
+ }
+
+ /*
+ * Init the test parameters.
+ */
+ private void initTest(boolean enableSecurity, TokenType tokenType) {
+ mockDeploymentConfiguration.setXsrfProtectionEnabled(enableSecurity);
+ this.tokenType = tokenType;
+ }
+
+ /*
+ * Create the requets.
+ */
+ private RpcRequest createRequest() {
+ try {
+ return new RpcRequest(getPayload(), vaadinRequest);
+ } catch (JSONException e) {
+ LOGGER.log(Level.SEVERE, "", e);
+
+ Assert.assertTrue(false);
+ return null;
+ }
+ }
+
+ /*
+ * Gets whether the token from the request is the default one.
+ */
+ private boolean isDefaultToken(RpcRequest rpcRequest) {
+ return ApplicationConstants.CSRF_TOKEN_DEFAULT_VALUE.equals(rpcRequest
+ .getCsrfToken());
+ }
+
+ /*
+ * Gets whether the token from the request is the invalid one.
+ */
+ private boolean isInvalidToken(RpcRequest rpcRequest) {
+ return getInvalidToken().equals(rpcRequest.getCsrfToken());
+ }
+
+ /*
+ * Gets whether the token from the request is the valid one.
+ */
+ private boolean isValidToken(RpcRequest rpcRequest) {
+ return getValidToken().equals(rpcRequest.getCsrfToken());
+ }
+
+ /*
+ * Gets whether the token from the request is valid.
+ */
+ private boolean isRequestValid(RpcRequest rpcRequest) {
+ return VaadinService.isCsrfTokenValid(mockSession,
+ rpcRequest.getCsrfToken());
+ }
+
+ private static Logger LOGGER = Logger
+ .getLogger(CsrfTokenMissingTestServer.class.getName());
+ static {
+ LOGGER.setLevel(Level.ALL);
+ }
+
+ @Test
+ public void securityOnAndNoToken() {
+ initTest(true, TokenType.MISSING);
+
+ RpcRequest rpcRequest = createRequest();
+
+ Assert.assertTrue(isDefaultToken(rpcRequest));
+ Assert.assertFalse(isRequestValid(rpcRequest));
+ }
+
+ @Test
+ public void securityOffAndNoToken() {
+ initTest(false, TokenType.MISSING);
+
+ RpcRequest rpcRequest = createRequest();
+
+ Assert.assertTrue(isDefaultToken(rpcRequest));
+ Assert.assertTrue(isRequestValid(rpcRequest));
+ }
+
+ @Test
+ public void securityOnAndInvalidToken() {
+ initTest(true, TokenType.INVALID);
+
+ RpcRequest rpcRequest = createRequest();
+
+ Assert.assertTrue(isInvalidToken(rpcRequest));
+ Assert.assertFalse(isRequestValid(rpcRequest));
+ }
+
+ @Test
+ public void securityOffAndInvalidToken() {
+ initTest(false, TokenType.INVALID);
+
+ RpcRequest rpcRequest = createRequest();
+
+ Assert.assertTrue(isInvalidToken(rpcRequest));
+ Assert.assertTrue(isRequestValid(rpcRequest));
+ }
+
+ @Test
+ public void securityOnAndValidToken() {
+ initTest(true, TokenType.VALID);
+
+ RpcRequest rpcRequest = createRequest();
+
+ Assert.assertTrue(isValidToken(rpcRequest));
+ Assert.assertTrue(isRequestValid(rpcRequest));
+ }
+
+ @Test
+ public void securityOffAndValidToken() {
+ initTest(false, TokenType.VALID);
+
+ RpcRequest rpcRequest = createRequest();
+
+ Assert.assertTrue(isValidToken(rpcRequest));
+ Assert.assertTrue(isRequestValid(rpcRequest));
+ }
+
+}
diff --git a/shared/src/com/vaadin/shared/ApplicationConstants.java b/shared/src/com/vaadin/shared/ApplicationConstants.java
index da4ac7450d..15eefe3b21 100644
--- a/shared/src/com/vaadin/shared/ApplicationConstants.java
+++ b/shared/src/com/vaadin/shared/ApplicationConstants.java
@@ -1,12 +1,12 @@
/*
* Copyright 2000-2014 Vaadin Ltd.
- *
+ *
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
@@ -86,7 +86,7 @@ public class ApplicationConstants implements Serializable {
/**
* The name of the debug version of the javascript containing push support.
* The file is located in the VAADIN directory.
- *
+ *
* @since 7.1.6
*/
public static final String VAADIN_PUSH_DEBUG_JS = "vaadinPush.debug.js";
@@ -98,14 +98,14 @@ public class ApplicationConstants implements Serializable {
/**
* The name of the parameter used to transmit RPC invocations
- *
+ *
* @since 7.2
*/
public static final String RPC_INVOCATIONS = "rpc";
/**
* The name of the parameter used to transmit the CSRF token
- *
+ *
* @since 7.2
*/
public static final String CSRF_TOKEN = "csrfToken";
@@ -114,9 +114,15 @@ public class ApplicationConstants implements Serializable {
* The name of the parameter used to transmit the sync id. The value can be
* set to -1 e.g. when testing with pre-recorded requests to make the
* framework ignore the sync id.
- *
+ *
* @see com.vaadin.ui.ConnectorTracker#getCurrentSyncId()
* @since 7.2
*/
public static final String SERVER_SYNC_ID = "syncId";
+
+ /**
+ * Default value to use in case the security protection is disabled.
+ */
+ public static final String CSRF_TOKEN_DEFAULT_VALUE = "init";
+
}
diff --git a/uitest/src/com/vaadin/tests/widgetset/TestingWidgetSet.gwt.xml b/uitest/src/com/vaadin/tests/widgetset/TestingWidgetSet.gwt.xml
index fd52e5cd0e..2c25c54e04 100644
--- a/uitest/src/com/vaadin/tests/widgetset/TestingWidgetSet.gwt.xml
+++ b/uitest/src/com/vaadin/tests/widgetset/TestingWidgetSet.gwt.xml
@@ -13,4 +13,9 @@
<when-type-is class="com.vaadin.client.communication.PushConnection" />
</replace-with>
+ <replace-with
+ class="com.vaadin.tests.widgetset.client.MockApplicationConnection">
+ <when-type-is class="com.vaadin.client.ApplicationConnection" />
+ </replace-with>
+
</module>
diff --git a/uitest/src/com/vaadin/tests/widgetset/client/MockApplicationConnection.java b/uitest/src/com/vaadin/tests/widgetset/client/MockApplicationConnection.java
new file mode 100644
index 0000000000..4ee5b71387
--- /dev/null
+++ b/uitest/src/com/vaadin/tests/widgetset/client/MockApplicationConnection.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2000-2014 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.tests.widgetset.client;
+
+import java.util.Date;
+import java.util.logging.Logger;
+
+import com.google.gwt.json.client.JSONObject;
+import com.google.gwt.json.client.JSONValue;
+import com.vaadin.client.ApplicationConnection;
+import com.vaadin.client.ValueMap;
+import com.vaadin.shared.ApplicationConstants;
+import com.vaadin.tests.widgetset.server.csrf.ui.CsrfTokenDisabled;
+
+/**
+ * Mock ApplicationConnection for several issues where we need to hack it.
+ *
+ * @since
+ * @author Vaadin Ltd
+ */
+public class MockApplicationConnection extends ApplicationConnection {
+
+ private static final Logger LOGGER = Logger
+ .getLogger(MockApplicationConnection.class.getName());
+
+ // The last token received from the server.
+ private String lastCsrfTokenReceiver;
+
+ // The last token sent to the server.
+ private String lastCsrfTokenSent;
+
+ /**
+ * Provide the last token received from the server. <br/>
+ * We added this to test the change done on CSRF token.
+ *
+ * @see CsrfTokenDisabled
+ */
+ public String getLastCsrfTokenReceiver() {
+ return lastCsrfTokenReceiver;
+ }
+
+ /**
+ * Provide the last token sent to the server. <br/>
+ * We added this to test the change done on CSRF token.
+ *
+ * @see CsrfTokenDisabled
+ */
+ public String getLastCsrfTokenSent() {
+ return lastCsrfTokenSent;
+ }
+
+ @Override
+ protected void handleUIDLMessage(Date start, String jsonText, ValueMap json) {
+ lastCsrfTokenReceiver = json
+ .getString(ApplicationConstants.UIDL_SECURITY_TOKEN_ID);
+
+ super.handleUIDLMessage(start, jsonText, json);
+ }
+
+ @Override
+ protected void doUidlRequest(String uri, JSONObject payload) {
+ JSONValue jsonValue = payload.get(ApplicationConstants.CSRF_TOKEN);
+ lastCsrfTokenSent = jsonValue != null ? jsonValue.toString() : null;
+
+ super.doUidlRequest(uri, payload);
+ }
+
+}
diff --git a/uitest/src/com/vaadin/tests/widgetset/client/csrf/CsrfButtonConnector.java b/uitest/src/com/vaadin/tests/widgetset/client/csrf/CsrfButtonConnector.java
new file mode 100644
index 0000000000..cf24ed6921
--- /dev/null
+++ b/uitest/src/com/vaadin/tests/widgetset/client/csrf/CsrfButtonConnector.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2000-2014 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.tests.widgetset.client.csrf;
+
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import com.google.gwt.core.shared.GWT;
+import com.google.gwt.event.dom.client.ClickEvent;
+import com.google.gwt.event.dom.client.ClickHandler;
+import com.vaadin.client.ui.AbstractComponentConnector;
+import com.vaadin.client.ui.VButton;
+import com.vaadin.shared.ui.Connect;
+import com.vaadin.tests.widgetset.client.MockApplicationConnection;
+import com.vaadin.tests.widgetset.server.csrf.CsrfButton;
+
+/**
+ * Dummy connector to test our CSRF bug. See #14111.
+ *
+ * @author Vaadin Ltd
+ */
+@SuppressWarnings("serial")
+@Connect(CsrfButton.class)
+public class CsrfButtonConnector extends AbstractComponentConnector {
+
+ static Logger logger = Logger
+ .getLogger(CsrfButtonConnector.class.getName());
+ static {
+ logger.setLevel(Level.ALL);
+ }
+
+ @Override
+ public VButton getWidget() {
+ return (VButton) super.getWidget();
+ }
+
+ @Override
+ protected VButton createWidget() {
+ return GWT.create(VButton.class);
+ }
+
+ public final static String ID = "CsrfButton";
+
+ @Override
+ public void init() {
+ super.init();
+
+ getWidget().getElement().setId(ID);
+ getWidget().setText(csrfTokenInfo());
+ getWidget().addClickHandler(new ClickHandler() {
+
+ @Override
+ public void onClick(ClickEvent event) {
+ getWidget().setText(csrfTokenInfo());
+ }
+ });
+ }
+
+ private String csrfTokenInfo() {
+ return getMockConnection().getCsrfToken() + ", "
+ + getMockConnection().getLastCsrfTokenReceiver() + ", "
+ + getMockConnection().getLastCsrfTokenSent();
+ }
+
+ private MockApplicationConnection getMockConnection() {
+ return (MockApplicationConnection) getConnection();
+ }
+
+}
diff --git a/uitest/src/com/vaadin/tests/widgetset/server/csrf/CsrfButton.java b/uitest/src/com/vaadin/tests/widgetset/server/csrf/CsrfButton.java
new file mode 100644
index 0000000000..567127927d
--- /dev/null
+++ b/uitest/src/com/vaadin/tests/widgetset/server/csrf/CsrfButton.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2000-2014 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.tests.widgetset.server.csrf;
+
+import com.vaadin.ui.AbstractComponent;
+
+/**
+ * Dummy client connector to link with the client functionality if the
+ * CsrfToken.
+ *
+ * @since
+ * @author Vaadin Ltd
+ */
+@SuppressWarnings("serial")
+public class CsrfButton extends AbstractComponent {
+
+}
diff --git a/uitest/src/com/vaadin/tests/widgetset/server/csrf/ui/AbstractCsrfTokenUI.java b/uitest/src/com/vaadin/tests/widgetset/server/csrf/ui/AbstractCsrfTokenUI.java
new file mode 100644
index 0000000000..f8f1754385
--- /dev/null
+++ b/uitest/src/com/vaadin/tests/widgetset/server/csrf/ui/AbstractCsrfTokenUI.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2000-2014 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.tests.widgetset.server.csrf.ui;
+
+import com.vaadin.annotations.Widgetset;
+import com.vaadin.server.VaadinRequest;
+import com.vaadin.tests.components.AbstractTestUI;
+import com.vaadin.tests.widgetset.TestingWidgetSet;
+import com.vaadin.tests.widgetset.server.csrf.CsrfButton;
+import com.vaadin.ui.Button;
+import com.vaadin.ui.Label;
+
+/**
+ * Abstract UI to test the CSRF token issue as reported in (#14111)
+ *
+ * @since
+ * @author Vaadin Ltd
+ */
+@SuppressWarnings("serial")
+@Widgetset(TestingWidgetSet.NAME)
+public abstract class AbstractCsrfTokenUI extends AbstractTestUI {
+
+ public static final String PRESS_ID = "PressMe";
+
+ @Override
+ protected void setup(VaadinRequest request) {
+
+ addComponent(new Label("The button's text is the client token:"));
+ addComponent(new CsrfButton());
+ addComponent(new Label("This one is from the server"));
+ addComponent(new Label(getSession().getCsrfToken()));
+ Button pressMe = new Button("Click me to send a request");
+ pressMe.setId(PRESS_ID);
+ addComponent(pressMe);
+ }
+
+ @Override
+ protected String getTestDescription() {
+ return "Remove csrfToken from the request if security protection is disabled.";
+ }
+
+ @Override
+ protected Integer getTicketNumber() {
+ return 14111;
+ }
+
+}
diff --git a/uitest/src/com/vaadin/tests/widgetset/server/csrf/ui/AbstractCsrfTokenUITest.java b/uitest/src/com/vaadin/tests/widgetset/server/csrf/ui/AbstractCsrfTokenUITest.java
new file mode 100644
index 0000000000..614eaa063e
--- /dev/null
+++ b/uitest/src/com/vaadin/tests/widgetset/server/csrf/ui/AbstractCsrfTokenUITest.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright 2000-2014 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.tests.widgetset.server.csrf.ui;
+
+import java.util.StringTokenizer;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.junit.Test;
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebDriver;
+import org.openqa.selenium.support.ui.ExpectedCondition;
+
+import com.vaadin.tests.tb3.MultiBrowserTest;
+import com.vaadin.tests.widgetset.client.csrf.CsrfButtonConnector;
+
+public abstract class AbstractCsrfTokenUITest extends MultiBrowserTest {
+
+ static final Logger LOGGER = Logger.getLogger(AbstractCsrfTokenUITest.class
+ .getName());
+
+ @Test
+ public void testTokens() {
+ openTestURL();
+
+ final By debugButton = By.id(CsrfButtonConnector.ID);
+
+ final String debugMessage1 = getDriver().findElement(debugButton)
+ .getText();
+
+ getDriver().findElement(By.id(CsrfTokenDisabled.PRESS_ID)).click();
+
+ waitUntil(new ExpectedCondition<Boolean>() {
+
+ @Override
+ public Boolean apply(WebDriver input) {
+ getDriver().findElement(debugButton).click();
+ String debugMessage2 = input.findElement(debugButton).getText();
+
+ LOGGER.log(Level.INFO, "1: " + debugMessage1);
+ LOGGER.log(Level.INFO, "2: " + debugMessage2);
+
+ if (!debugMessage1.equals(debugMessage2)) {
+
+ compareMessage(split(debugMessage1), split(debugMessage2));
+
+ LOGGER.log(Level.INFO, "DONE");
+
+ return true;
+
+ } else {
+ return false;
+ }
+ }
+ });
+ }
+
+ private TokenGroup split(String debugMessage) {
+ StringTokenizer tokenizer = new StringTokenizer(debugMessage, ", \"");
+
+ return new TokenGroup(tokenizer.nextToken(), tokenizer.nextToken(),
+ tokenizer.nextToken());
+ }
+
+ /*
+ * Just implement this.
+ */
+ protected abstract boolean compareMessage(TokenGroup tokenGroup1,
+ TokenGroup tokenGroup2);
+
+ boolean isNullOrUndefined(String value) {
+ return isNull(value) || isUndefined(value);
+ }
+
+ boolean isUndefined(String value) {
+ return value.equals("undefined");
+ }
+
+ boolean isNull(String value) {
+ return value.equals("null");
+ }
+
+ /*
+ * Wrapps all tokens from the client app.
+ */
+ static class TokenGroup {
+
+ public final String clientToken;
+
+ public final String tokenReceivedFromServer;
+
+ public final String tokenSentToServer;
+
+ public TokenGroup(String clientToken, String tokenReceivedFromServer,
+ String tokenSentToServer) {
+ this.clientToken = clientToken;
+ this.tokenReceivedFromServer = tokenReceivedFromServer;
+ this.tokenSentToServer = tokenSentToServer;
+ }
+
+ }
+
+}
diff --git a/uitest/src/com/vaadin/tests/widgetset/server/csrf/ui/CsrfTokenDisabled.java b/uitest/src/com/vaadin/tests/widgetset/server/csrf/ui/CsrfTokenDisabled.java
new file mode 100644
index 0000000000..6283285b40
--- /dev/null
+++ b/uitest/src/com/vaadin/tests/widgetset/server/csrf/ui/CsrfTokenDisabled.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2000-2014 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.tests.widgetset.server.csrf.ui;
+
+import com.vaadin.launcher.CustomDeploymentConfiguration;
+import com.vaadin.launcher.CustomDeploymentConfiguration.Conf;
+
+/**
+ * When the disable-xsrf-protection is true csrfToken is not present anymore
+ * with the requests.<br/>
+ * This is useful mostly when the client is not Vaadin and so it will not push
+ * the parameter anyway. So now the server knows how to deal the issue if the
+ * csrfToken is not present.
+ *
+ * @since
+ * @author Vaadin Ltd
+ */
+@SuppressWarnings("serial")
+@CustomDeploymentConfiguration({ @Conf(name = "disable-xsrf-protection", value = "true") })
+public class CsrfTokenDisabled extends AbstractCsrfTokenUI {
+
+}
diff --git a/uitest/src/com/vaadin/tests/widgetset/server/csrf/ui/CsrfTokenDisabledTest.java b/uitest/src/com/vaadin/tests/widgetset/server/csrf/ui/CsrfTokenDisabledTest.java
new file mode 100644
index 0000000000..504425fead
--- /dev/null
+++ b/uitest/src/com/vaadin/tests/widgetset/server/csrf/ui/CsrfTokenDisabledTest.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2000-2014 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.tests.widgetset.server.csrf.ui;
+
+import com.vaadin.shared.ApplicationConstants;
+
+/**
+ * Test the CSRF Token issue.
+ *
+ * @since
+ * @author Vaadin Ltd
+ */
+public class CsrfTokenDisabledTest extends AbstractCsrfTokenUITest {
+
+ @Override
+ protected boolean compareMessage(TokenGroup tokenGroup1,
+ TokenGroup tokenGroup2) {
+
+ return tokenGroup1.clientToken
+ .equals(ApplicationConstants.CSRF_TOKEN_DEFAULT_VALUE)
+ && isUndefined(tokenGroup1.tokenReceivedFromServer)
+ && isUndefined(tokenGroup1.tokenSentToServer)
+ && tokenGroup2.clientToken
+ .equals(ApplicationConstants.CSRF_TOKEN_DEFAULT_VALUE)
+ && isUndefined(tokenGroup2.tokenReceivedFromServer)
+ // This is it actually, no token sent to the server.
+ && isNull(tokenGroup2.tokenSentToServer);
+ }
+
+}
diff --git a/uitest/src/com/vaadin/tests/widgetset/server/csrf/ui/CsrfTokenEnabled.java b/uitest/src/com/vaadin/tests/widgetset/server/csrf/ui/CsrfTokenEnabled.java
new file mode 100644
index 0000000000..cd02c6da77
--- /dev/null
+++ b/uitest/src/com/vaadin/tests/widgetset/server/csrf/ui/CsrfTokenEnabled.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2000-2014 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.tests.widgetset.server.csrf.ui;
+
+import com.vaadin.launcher.CustomDeploymentConfiguration;
+import com.vaadin.launcher.CustomDeploymentConfiguration.Conf;
+
+@SuppressWarnings("serial")
+@CustomDeploymentConfiguration({ @Conf(name = "disable-xsrf-protection", value = "false") })
+public class CsrfTokenEnabled extends AbstractCsrfTokenUI {
+
+}
diff --git a/uitest/src/com/vaadin/tests/widgetset/server/csrf/ui/CsrfTokenEnabledTest.java b/uitest/src/com/vaadin/tests/widgetset/server/csrf/ui/CsrfTokenEnabledTest.java
new file mode 100644
index 0000000000..1d51f1c372
--- /dev/null
+++ b/uitest/src/com/vaadin/tests/widgetset/server/csrf/ui/CsrfTokenEnabledTest.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2000-2014 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.tests.widgetset.server.csrf.ui;
+
+/**
+ * Test the CSRF Token issue.
+ *
+ * @since
+ * @author Vaadin Ltd
+ */
+public class CsrfTokenEnabledTest extends AbstractCsrfTokenUITest {
+
+ @Override
+ protected boolean compareMessage(TokenGroup tokenGroup1,
+ TokenGroup tokenGroup2) {
+
+ return tokenGroup1.clientToken.equals(tokenGroup2.clientToken)
+ // Valid token received and set on the client
+ && tokenGroup1.clientToken
+ .equals(tokenGroup1.tokenReceivedFromServer)
+ // No token sent yet to the server.
+ && isUndefined(tokenGroup1.tokenSentToServer)
+ // Token is sent to the server.
+ && tokenGroup2.clientToken
+ .equals(tokenGroup2.tokenSentToServer)
+ // And no more token received from the server.
+ && isUndefined(tokenGroup2.tokenReceivedFromServer);
+ }
+
+}