]> source.dussan.org Git - vaadin-framework.git/commitdiff
Remove csrfToken if disable-xsrf-protection is true (#14111)
authorBogdan Udrescu <bogdan@vaadin.com>
Wed, 9 Jul 2014 15:40:26 +0000 (18:40 +0300)
committerAnthony Guerreiro <anthony@vaadin.com>
Wed, 16 Jul 2014 13:51:38 +0000 (16:51 +0300)
If the server sends no token and the client value remains "init" then
it's not sent back to the server.

Change-Id: I74fc470c5c22d57c4a48eab3e4476ae4cc2dd242

15 files changed:
client/src/com/vaadin/client/ApplicationConnection.java
client/src/com/vaadin/client/communication/AtmospherePushConnection.java
server/src/com/vaadin/server/communication/ServerRpcHandler.java
server/tests/src/com/vaadin/tests/server/CsrfTokenMissingTestServer.java [new file with mode: 0644]
shared/src/com/vaadin/shared/ApplicationConstants.java
uitest/src/com/vaadin/tests/widgetset/TestingWidgetSet.gwt.xml
uitest/src/com/vaadin/tests/widgetset/client/MockApplicationConnection.java [new file with mode: 0644]
uitest/src/com/vaadin/tests/widgetset/client/csrf/CsrfButtonConnector.java [new file with mode: 0644]
uitest/src/com/vaadin/tests/widgetset/server/csrf/CsrfButton.java [new file with mode: 0644]
uitest/src/com/vaadin/tests/widgetset/server/csrf/ui/AbstractCsrfTokenUI.java [new file with mode: 0644]
uitest/src/com/vaadin/tests/widgetset/server/csrf/ui/AbstractCsrfTokenUITest.java [new file with mode: 0644]
uitest/src/com/vaadin/tests/widgetset/server/csrf/ui/CsrfTokenDisabled.java [new file with mode: 0644]
uitest/src/com/vaadin/tests/widgetset/server/csrf/ui/CsrfTokenDisabledTest.java [new file with mode: 0644]
uitest/src/com/vaadin/tests/widgetset/server/csrf/ui/CsrfTokenEnabled.java [new file with mode: 0644]
uitest/src/com/vaadin/tests/widgetset/server/csrf/ui/CsrfTokenEnabledTest.java [new file with mode: 0644]

index 510657561fba8746939aa530c162aff497d06b25..6ac50acde868d9e0acad805bbb135e00dc66b329 100644 (file)
@@ -4,9 +4,9 @@
  * 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
      */
@@ -2725,7 +2727,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
@@ -2747,7 +2749,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
@@ -2770,7 +2772,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
@@ -2793,7 +2795,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
@@ -2816,7 +2818,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
@@ -2839,7 +2841,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
@@ -2862,7 +2864,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
@@ -2885,7 +2887,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
@@ -2902,13 +2904,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
@@ -2925,14 +2927,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
@@ -2949,7 +2951,7 @@ public class ApplicationConnection implements HasHandlers {
 
     /**
      * Does absolutely nothing. Replaced by {@link LayoutManager}.
-     * 
+     *
      * @param container
      * @deprecated As of 7.0, serves no purpose
      */
@@ -2971,7 +2973,7 @@ public class ApplicationConnection implements HasHandlers {
 
     /**
      * Returns false
-     * 
+     *
      * @param paintable
      * @return false, always
      * @deprecated As of 7.0, serves no purpose
@@ -2983,7 +2985,7 @@ public class ApplicationConnection implements HasHandlers {
 
     /**
      * Returns false
-     * 
+     *
      * @param paintable
      * @return false, always
      * @deprecated As of 7.0, serves no purpose
@@ -3004,16 +3006,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
      */
@@ -3026,15 +3028,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,
@@ -3054,7 +3056,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
@@ -3065,7 +3067,7 @@ public class ApplicationConnection implements HasHandlers {
 
     /**
      * Singleton method to get instance of app's context menu.
-     * 
+     *
      * @return VContextMenu object
      */
     public VContextMenu getContextMenu() {
@@ -3080,7 +3082,7 @@ public class ApplicationConnection implements HasHandlers {
 
     /**
      * Gets an {@link Icon} instance corresponding to a URI.
-     * 
+     *
      * @since 7.2
      * @param uri
      * @return Icon object
@@ -3102,7 +3104,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
@@ -3174,7 +3176,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() {
@@ -3184,7 +3186,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;
@@ -3213,7 +3215,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() {
@@ -3223,7 +3225,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
@@ -3235,7 +3237,7 @@ public class ApplicationConnection implements HasHandlers {
 
     /**
      * Gets the main view
-     * 
+     *
      * @return the main view
      */
     public UIConnector getUIConnector() {
@@ -3244,7 +3246,7 @@ public class ApplicationConnection implements HasHandlers {
 
     /**
      * Gets the {@link ApplicationConfiguration} for the current application.
-     * 
+     *
      * @see ApplicationConfiguration
      * @return the configuration for this application
      */
@@ -3257,7 +3259,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
@@ -3277,7 +3279,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
@@ -3330,7 +3332,7 @@ public class ApplicationConnection implements HasHandlers {
 
     /**
      * Get VTooltip instance related to application connection
-     * 
+     *
      * @return VTooltip instance
      */
     public VTooltip getVTooltip() {
@@ -3342,7 +3344,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
@@ -3393,7 +3395,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
@@ -3407,7 +3409,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
@@ -3431,7 +3433,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) {
@@ -3440,7 +3442,7 @@ public class ApplicationConnection implements HasHandlers {
 
     /**
      * Resumes the rendering process once all locks have been removed.
-     * 
+     *
      * @param lock
      */
     public void resumeResponseHandling(Object lock) {
@@ -3485,7 +3487,7 @@ public class ApplicationConnection implements HasHandlers {
 
     /**
      * Sets the delegate that is called whenever a communication error occurrs.
-     * 
+     *
      * @param delegate
      *            the delegate.
      */
@@ -3528,7 +3530,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() {
@@ -3542,7 +3544,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.
@@ -3592,7 +3594,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
      */
index 48e17cde05a522bb63c982ee5d199327c1cb682e..5073e0ce5d2cd77e1a06ca44faeb06dd39ce460d 100644 (file)
@@ -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) {
index 2619c272b47fb73e5d5458ba25a32308e2f9a8da..2e3fd60e2a49bf527bf8b5ae1c59e897ae3ca360 100644 (file)
@@ -78,9 +78,15 @@ public class ServerRpcHandler implements Serializable {
 
         public RpcRequest(String jsonString) throws JSONException {
             json = new JSONObject(jsonString);
-            csrfToken = json.getString(ApplicationConstants.CSRF_TOKEN);
 
             VaadinRequest request = VaadinService.getCurrentRequest();
+
+            String csrfToken = json.optString(ApplicationConstants.CSRF_TOKEN);
+            if (csrfToken.equals("")) {
+                csrfToken = ApplicationConstants.CSRF_TOKEN_DEFAULT_VALUE;
+            }
+            this.csrfToken = csrfToken;
+
             if (request == null
                     || request
                             .getService()
@@ -92,6 +98,7 @@ public class ServerRpcHandler implements Serializable {
             } else {
                 syncId = -1;
             }
+
             invocations = new JSONArray(
                     json.getString(ApplicationConstants.RPC_INVOCATIONS));
         }
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 (file)
index 0000000..30f1c85
--- /dev/null
@@ -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));
+    }
+
+}
index da4ac7450d9020dab9ee79ad10a3bbb64904d3cc..15eefe3b21dc29f9f82eb2e0586dd403572d26ce 100644 (file)
@@ -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";
+
 }
index fd52e5cd0ec6ade26ec03acd9becd0e876cb976b..2c25c54e042bce332fe93b60f96cc4205f7b5c49 100644 (file)
@@ -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 (file)
index 0000000..4ee5b71
--- /dev/null
@@ -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 (file)
index 0000000..cf24ed6
--- /dev/null
@@ -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 (file)
index 0000000..5671279
--- /dev/null
@@ -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 (file)
index 0000000..f8f1754
--- /dev/null
@@ -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 (file)
index 0000000..614eaa0
--- /dev/null
@@ -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 (file)
index 0000000..6283285
--- /dev/null
@@ -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 (file)
index 0000000..504425f
--- /dev/null
@@ -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 (file)
index 0000000..cd02c6d
--- /dev/null
@@ -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 (file)
index 0000000..1d51f1c
--- /dev/null
@@ -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);
+    }
+
+}