Alexander Wels has uploaded a new change for review.

Change subject: userportal,webadmin: additional protection for GWT RPC
......................................................................

userportal,webadmin: additional protection for GWT RPC

- Added additional protection to GWT-RPC calls.

Change-Id: Ia4a5ad1f33eb985aa79e1376aecb48ae365d334d
Signed-off-by: Alexander Wels <aw...@redhat.com>
---
M 
frontend/webadmin/modules/frontend/src/main/java/org/ovirt/engine/ui/frontend/communication/GWTRPCCommunicationProvider.java
M 
frontend/webadmin/modules/frontend/src/main/java/org/ovirt/engine/ui/frontend/gwtservices/GenericApiGWTService.java
A 
frontend/webadmin/modules/frontend/src/main/java/org/ovirt/engine/ui/frontend/gwtservices/XsrfProtectedRpcService.java
A 
frontend/webadmin/modules/frontend/src/main/java/org/ovirt/engine/ui/frontend/server/gwt/AbstractXsrfProtectedRpcServlet.java
M 
frontend/webadmin/modules/frontend/src/main/java/org/ovirt/engine/ui/frontend/server/gwt/GenericApiGWTServiceImpl.java
A 
frontend/webadmin/modules/frontend/src/main/java/org/ovirt/engine/ui/frontend/server/gwt/XsrfProtectedRpcServlet.java
M 
frontend/webadmin/modules/frontend/src/main/resources/META-INF/web-fragment.xml
M 
frontend/webadmin/modules/frontend/src/test/java/org/ovirt/engine/ui/frontend/FrontendActionTest.java
M 
frontend/webadmin/modules/frontend/src/test/java/org/ovirt/engine/ui/frontend/FrontendTest.java
M 
frontend/webadmin/modules/frontend/src/test/java/org/ovirt/engine/ui/frontend/communication/GWTRPCCommunicationProviderTest.java
M 
frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/gin/BaseSystemModule.java
M frontend/webadmin/modules/userportal-gwtp/src/main/webapp/WEB-INF/web.xml
M frontend/webadmin/modules/webadmin/src/main/webapp/WEB-INF/web.xml
13 files changed, 488 insertions(+), 95 deletions(-)


  git pull ssh://gerrit.ovirt.org:29418/ovirt-engine refs/changes/08/26408/1

diff --git 
a/frontend/webadmin/modules/frontend/src/main/java/org/ovirt/engine/ui/frontend/communication/GWTRPCCommunicationProvider.java
 
b/frontend/webadmin/modules/frontend/src/main/java/org/ovirt/engine/ui/frontend/communication/GWTRPCCommunicationProvider.java
index a3d8f23..dca3093 100644
--- 
a/frontend/webadmin/modules/frontend/src/main/java/org/ovirt/engine/ui/frontend/communication/GWTRPCCommunicationProvider.java
+++ 
b/frontend/webadmin/modules/frontend/src/main/java/org/ovirt/engine/ui/frontend/communication/GWTRPCCommunicationProvider.java
@@ -15,7 +15,12 @@
 import org.ovirt.engine.core.common.queries.VdcQueryType;
 import org.ovirt.engine.ui.frontend.gwtservices.GenericApiGWTServiceAsync;
 
+import com.google.gwt.core.client.GWT;
 import com.google.gwt.user.client.rpc.AsyncCallback;
+import com.google.gwt.user.client.rpc.HasRpcToken;
+import com.google.gwt.user.client.rpc.ServiceDefTarget;
+import com.google.gwt.user.client.rpc.XsrfToken;
+import com.google.gwt.user.client.rpc.XsrfTokenServiceAsync;
 import com.google.inject.Inject;
 
 /**
@@ -25,25 +30,67 @@
 public class GWTRPCCommunicationProvider implements CommunicationProvider {
 
     /**
+     * Callback interface when retrieving the GWT RPC service. We need the 
callback in order to retrieve the
+     * XSRF token if it is not available yet.
+     */
+    private interface ServiceCallback {
+        /**
+         * The callback method with the service.
+         * @param service The GWT RPC service that contains the appropriate 
token.
+         */
+        void serviceFound(GenericApiGWTServiceAsync service);
+        /**
+         * The failure callback in case we are unable to retrieve the 
approriate token.
+         * @param exception The exception thrown.
+         */
+        void onFailure(Throwable exception);
+    }
+
+    /**
      * GWT RPC service.
      */
     private final GenericApiGWTServiceAsync service;
 
     /**
-     * Get the GWT RPC service.
-     * @return instance of the GWT RPC service.
+     * GWT XSRF service.
      */
-    private GenericApiGWTServiceAsync getService() {
-        return service;
+    private final XsrfTokenServiceAsync xsrfService;
+
+    /**
+     * Get the GWT RPC service.
+     * @param callback The callback to use when determining the service.
+     */
+    private void getService(final ServiceCallback callback) {
+        final HasRpcToken serviceWithToken = (HasRpcToken) service;
+        if (serviceWithToken.getRpcToken() != null) {
+            callback.serviceFound(service);
+        } else {
+            xsrfService.getNewXsrfToken(new AsyncCallback<XsrfToken>() {
+                @Override
+                public void onSuccess(XsrfToken token) {
+                    serviceWithToken.setRpcToken(token);
+                    callback.serviceFound(service);
+                }
+
+                @Override
+                public void onFailure(Throwable caught) {
+                    callback.onFailure(caught);
+                }
+            });
+        }
     }
 
     /**
      * Constructor.
      * @param asyncService GWT RPC service.
+     * @param xsrfTokenService The GWT XSRF token service, used to retrieve a 
new XSRF token.
      */
     @Inject
-    public GWTRPCCommunicationProvider(final GenericApiGWTServiceAsync 
asyncService) {
+    public GWTRPCCommunicationProvider(final GenericApiGWTServiceAsync 
asyncService,
+            final XsrfTokenServiceAsync xsrfTokenService) {
         this.service = asyncService;
+        this.xsrfService = xsrfTokenService;
+        ((ServiceDefTarget) 
this.xsrfService).setServiceEntryPoint(GWT.getModuleBaseURL() + "xsrf"); 
//$NON-NLS-1$
     }
 
     /**
@@ -70,16 +117,26 @@
      * @param operation The operation to run.
      */
     private void runPublicQuery(final VdcOperation<?, ?> operation) {
-        getService().RunPublicQuery((VdcQueryType) operation.getOperation(),
-                (VdcQueryParametersBase) operation.getParameter(), new 
AsyncCallback<VdcQueryReturnValue>() {
+        getService(new ServiceCallback() {
             @Override
-            public void onFailure(final Throwable exception) {
-                operation.getCallback().onFailure(operation, exception);
+            public void serviceFound(GenericApiGWTServiceAsync service) {
+                service.RunPublicQuery((VdcQueryType) operation.getOperation(),
+                        (VdcQueryParametersBase) operation.getParameter(), new 
AsyncCallback<VdcQueryReturnValue>() {
+                    @Override
+                    public void onFailure(final Throwable exception) {
+                        operation.getCallback().onFailure(operation, 
exception);
+                    }
+
+                    @Override
+                    public void onSuccess(final VdcQueryReturnValue result) {
+                        operation.getCallback().onSuccess(operation, result);
+                    }
+                });
             }
 
             @Override
-            public void onSuccess(final VdcQueryReturnValue result) {
-                operation.getCallback().onSuccess(operation, result);
+            public void onFailure(Throwable exception) {
+                operation.getCallback().onFailure(operation, exception);
             }
         });
     }
@@ -89,16 +146,26 @@
      * @param operation The operation to run.
      */
     private void runQuery(final VdcOperation<?, ?> operation) {
-        getService().RunQuery((VdcQueryType) operation.getOperation(),
-                (VdcQueryParametersBase) operation.getParameter(), new 
AsyncCallback<VdcQueryReturnValue>() {
+        getService(new ServiceCallback() {
             @Override
-            public void onFailure(final Throwable exception) {
-                operation.getCallback().onFailure(operation, exception);
+            public void serviceFound(GenericApiGWTServiceAsync service) {
+                service.RunQuery((VdcQueryType) operation.getOperation(),
+                        (VdcQueryParametersBase) operation.getParameter(), new 
AsyncCallback<VdcQueryReturnValue>() {
+                    @Override
+                    public void onFailure(final Throwable exception) {
+                        operation.getCallback().onFailure(operation, 
exception);
+                    }
+
+                    @Override
+                    public void onSuccess(final VdcQueryReturnValue result) {
+                        operation.getCallback().onSuccess(operation, result);
+                    }
+                });
             }
 
             @Override
-            public void onSuccess(final VdcQueryReturnValue result) {
-                operation.getCallback().onSuccess(operation, result);
+            public void onFailure(Throwable exception) {
+                operation.getCallback().onFailure(operation, exception);
             }
         });
     }
@@ -108,16 +175,26 @@
      * @param operation The operation to run.
      */
     private void runAction(final VdcOperation<?, ?> operation) {
-        getService().RunAction((VdcActionType) operation.getOperation(),
-                (VdcActionParametersBase) operation.getParameter(), new 
AsyncCallback<VdcReturnValueBase>() {
+        getService(new ServiceCallback() {
             @Override
-            public void onFailure(final Throwable exception) {
-                operation.getCallback().onFailure(operation, exception);
+            public void serviceFound(GenericApiGWTServiceAsync service) {
+                service.RunAction((VdcActionType) operation.getOperation(),
+                        (VdcActionParametersBase) operation.getParameter(), 
new AsyncCallback<VdcReturnValueBase>() {
+                    @Override
+                    public void onFailure(final Throwable exception) {
+                        operation.getCallback().onFailure(operation, 
exception);
+                    }
+
+                    @Override
+                    public void onSuccess(final VdcReturnValueBase result) {
+                        operation.getCallback().onSuccess(operation, result);
+                    }
+                });
             }
 
             @Override
-            public void onSuccess(final VdcReturnValueBase result) {
-                operation.getCallback().onSuccess(operation, result);
+            public void onFailure(Throwable exception) {
+                operation.getCallback().onFailure(operation, exception);
             }
         });
     }
@@ -161,8 +238,8 @@
     private void transmitMultipleQueries(final List<VdcOperation<?, ?>> 
queriesList) {
         if (queriesList.size() > 1 || (queriesList.size() == 1
                 && queriesList.get(0).getCallback() instanceof 
VdcOperationCallbackList)) {
-            List<VdcQueryType> queryTypes = new ArrayList<VdcQueryType>();
-            List<VdcQueryParametersBase> parameters = new 
ArrayList<VdcQueryParametersBase>();
+            final List<VdcQueryType> queryTypes = new 
ArrayList<VdcQueryType>();
+            final List<VdcQueryParametersBase> parameters = new 
ArrayList<VdcQueryParametersBase>();
 
             for (VdcOperation<?, ?> operation: new ArrayList<VdcOperation<?, 
?>>(queriesList)) {
                 if (operation.isPublic()) {
@@ -174,38 +251,62 @@
                 }
             }
 
-            getService().RunMultipleQueries((ArrayList<VdcQueryType>) 
queryTypes,
-                    (ArrayList<VdcQueryParametersBase>) parameters,
-                    new AsyncCallback<ArrayList<VdcQueryReturnValue>>() {
+            getService(new ServiceCallback() {
                 @Override
-                public void onFailure(final Throwable exception) {
-                    Map<VdcOperationCallback<?, ?>, List<VdcOperation<?, ?>>> 
callbackMap = getCallbackMap(queriesList);
-                    for (Map.Entry<VdcOperationCallback<?, ?>, 
List<VdcOperation<?, ?>>> callbackEntry: callbackMap.entrySet()) {
-                        if (callbackEntry.getKey() instanceof 
VdcOperationCallbackList) {
-                            ((VdcOperationCallbackList) 
callbackEntry.getKey()).onFailure(callbackEntry.getValue(), exception);
-                        } else {
-                            ((VdcOperationCallback) 
callbackEntry.getKey()).onFailure(callbackEntry.getValue().get(0), exception);
+                public void serviceFound(GenericApiGWTServiceAsync service) {
+                    service.RunMultipleQueries((ArrayList<VdcQueryType>) 
queryTypes,
+                            (ArrayList<VdcQueryParametersBase>) parameters,
+                            new 
AsyncCallback<ArrayList<VdcQueryReturnValue>>() {
+                        @Override
+                        public void onFailure(final Throwable exception) {
+                            handleMultipleQueriesFailure(queriesList, 
exception);
                         }
-                    }
+
+                        @Override
+                        public void onSuccess(final 
ArrayList<VdcQueryReturnValue> result) {
+                            Map<VdcOperationCallback<?, ?>, 
List<VdcOperation<?, ?>>> callbackMap =
+                                    getCallbackMap(queriesList);
+                            for (Map.Entry<VdcOperationCallback<?, ?>,
+                                    List<VdcOperation<?, ?>>> callbackEntry: 
callbackMap.entrySet()) {
+                                List<VdcQueryReturnValue> queryResult = 
(List<VdcQueryReturnValue>) getOperationResult(
+                                        callbackEntry.getValue(), queriesList, 
result);
+                                if (callbackEntry.getKey() instanceof 
VdcOperationCallbackList) {
+                                    ((VdcOperationCallbackList) 
callbackEntry.getKey())
+                                        .onSuccess(callbackEntry.getValue(), 
queryResult);
+                                } else {
+                                    ((VdcOperationCallback) 
callbackEntry.getKey())
+                                        
.onSuccess(callbackEntry.getValue().get(0), queryResult.get(0));
+                                }
+                            }
+                        }
+                    });
+
                 }
 
                 @Override
-                public void onSuccess(final ArrayList<VdcQueryReturnValue> 
result) {
-                    Map<VdcOperationCallback<?, ?>, List<VdcOperation<?, ?>>> 
callbackMap = getCallbackMap(queriesList);
-                    for (Map.Entry<VdcOperationCallback<?, ?>, 
List<VdcOperation<?, ?>>> callbackEntry: callbackMap.entrySet()) {
-                        List<VdcQueryReturnValue> queryResult = 
(List<VdcQueryReturnValue>) getOperationResult(
-                                callbackEntry.getValue(), queriesList, result);
-                        if (callbackEntry.getKey() instanceof 
VdcOperationCallbackList) {
-                            ((VdcOperationCallbackList) 
callbackEntry.getKey()).onSuccess(callbackEntry.getValue(), queryResult);
-                        } else {
-                            ((VdcOperationCallback) 
callbackEntry.getKey()).onSuccess(callbackEntry.getValue().get(0),
-                                    queryResult.get(0));
-                        }
-                    }
+                public void onFailure(Throwable exception) {
+                    handleMultipleQueriesFailure(queriesList, exception);
                 }
             });
         } else if (queriesList.size() == 1) {
             transmitOperation(queriesList.get(0));
+        }
+    }
+
+    /**
+     * Multiple queries failure handler.
+     * @param queriesList The queries list.
+     * @param exception The exception causing the failure.
+     */
+    private void handleMultipleQueriesFailure(final List<VdcOperation<?, ?>> 
queriesList,
+            final Throwable exception) {
+        Map<VdcOperationCallback<?, ?>, List<VdcOperation<?, ?>>> callbackMap 
= getCallbackMap(queriesList);
+        for (Map.Entry<VdcOperationCallback<?, ?>, List<VdcOperation<?, ?>>> 
callbackEntry: callbackMap.entrySet()) {
+            if (callbackEntry.getKey() instanceof VdcOperationCallbackList) {
+                ((VdcOperationCallbackList) 
callbackEntry.getKey()).onFailure(callbackEntry.getValue(), exception);
+            } else {
+                ((VdcOperationCallback) 
callbackEntry.getKey()).onFailure(callbackEntry.getValue().get(0), exception);
+            }
         }
     }
 
@@ -256,43 +357,60 @@
     }
 
     private void runMultipleActions(final VdcActionType actionType, final 
List<VdcOperation<?, ?>> operations,
-            List<VdcActionParametersBase> parameters, final 
List<VdcOperation<?, ?>> allActionOperations,
+            final List<VdcActionParametersBase> parameters, final 
List<VdcOperation<?, ?>> allActionOperations,
             final boolean waitForResults) {
-        getService().RunMultipleActions(actionType, 
(ArrayList<VdcActionParametersBase>) parameters,
-                false, waitForResults, new 
AsyncCallback<ArrayList<VdcReturnValueBase>>() {
-
+        getService(new ServiceCallback() {
             @Override
-            public void onFailure(final Throwable exception) {
-                Map<VdcOperationCallback<?, ?>, List<VdcOperation<?, ?>>> 
callbackMap =
-                        getCallbackMap(operations);
-                for (Map.Entry<VdcOperationCallback<?, ?>, 
List<VdcOperation<?, ?>>> callbackEntry: callbackMap.entrySet()) {
-                    if (callbackEntry.getKey() instanceof 
VdcOperationCallbackList) {
-                        ((VdcOperationCallbackList) 
callbackEntry.getKey()).onFailure(callbackEntry.getValue(), exception);
-                    } else {
-                        ((VdcOperationCallback) 
callbackEntry.getKey()).onFailure(callbackEntry.getValue().get(0),
-                                exception);
+            public void serviceFound(GenericApiGWTServiceAsync service) {
+                service.RunMultipleActions(actionType, 
(ArrayList<VdcActionParametersBase>) parameters,
+                        false, waitForResults, new 
AsyncCallback<ArrayList<VdcReturnValueBase>>() {
+
+                    @Override
+                    public void onFailure(final Throwable exception) {
+                        handleRunMultipleActionFailure(operations, exception);
                     }
-                }
+
+                    @Override
+                    public void onSuccess(final ArrayList<VdcReturnValueBase> 
result) {
+                        Map<VdcOperationCallback<?, ?>, List<VdcOperation<?, 
?>>> callbackMap =
+                                getCallbackMap(operations);
+                        for (Map.Entry<VdcOperationCallback<?, ?>,
+                                List<VdcOperation<?, ?>>> callbackEntry: 
callbackMap.entrySet()) {
+                            List<VdcReturnValueBase> actionResult = 
(List<VdcReturnValueBase>)
+                                    
getOperationResult(callbackEntry.getValue(), allActionOperations, result);
+                            if (callbackEntry.getKey() instanceof 
VdcOperationCallbackList) {
+                                ((VdcOperationCallbackList) 
callbackEntry.getKey())
+                                    .onSuccess(callbackEntry.getValue(), 
actionResult);
+                            } else {
+                                ((VdcOperationCallback) callbackEntry.getKey())
+                                    
.onSuccess(callbackEntry.getValue().get(0), actionResult.get(0));
+                            }
+                        }
+                    }
+                });
             }
 
             @Override
-            public void onSuccess(final ArrayList<VdcReturnValueBase> result) {
-                Map<VdcOperationCallback<?, ?>, List<VdcOperation<?, ?>>> 
callbackMap =
-                        getCallbackMap(operations);
-                for (Map.Entry<VdcOperationCallback<?, ?>, 
List<VdcOperation<?, ?>>> callbackEntry: callbackMap.entrySet()) {
-                    List<VdcReturnValueBase> actionResult = 
(List<VdcReturnValueBase>)
-                            getOperationResult(callbackEntry.getValue(), 
allActionOperations, result);
-                    if (callbackEntry.getKey() instanceof 
VdcOperationCallbackList) {
-                        ((VdcOperationCallbackList) 
callbackEntry.getKey()).onSuccess(callbackEntry.getValue(),
-                                actionResult);
-                    } else {
-                        ((VdcOperationCallback) 
callbackEntry.getKey()).onSuccess(callbackEntry.getValue().get(0),
-                                actionResult.get(0));
-                    }
-                }
+            public void onFailure(Throwable exception) {
+                handleRunMultipleActionFailure(operations, exception);
             }
         });
     }
+
+    private void handleRunMultipleActionFailure(final List<VdcOperation<?, ?>> 
operations,
+            final Throwable exception) {
+        Map<VdcOperationCallback<?, ?>, List<VdcOperation<?, ?>>> callbackMap =
+                getCallbackMap(operations);
+        for (Map.Entry<VdcOperationCallback<?, ?>, List<VdcOperation<?, ?>>> 
callbackEntry: callbackMap.entrySet()) {
+            if (callbackEntry.getKey() instanceof VdcOperationCallbackList) {
+                ((VdcOperationCallbackList) 
callbackEntry.getKey()).onFailure(callbackEntry.getValue(), exception);
+            } else {
+                ((VdcOperationCallback) 
callbackEntry.getKey()).onFailure(callbackEntry.getValue().get(0),
+                        exception);
+            }
+        }
+    }
+
     /**
      * Map operations by callback, so we can properly call a single callback 
for all related operations.
      * @param operationList The list of operations to determine the map for.
@@ -344,17 +462,27 @@
      */
     @Override
     public void login(final VdcOperation<VdcActionType, LoginUserParameters> 
loginOperation) {
-        getService().Login(loginOperation.getParameter().getLoginName(), 
loginOperation.getParameter().getPassword(),
-                loginOperation.getParameter().getProfileName(), 
loginOperation.getOperation(),
-                new AsyncCallback<VdcReturnValueBase>() {
+        getService(new ServiceCallback() {
             @Override
-            public void onSuccess(final VdcReturnValueBase result) {
-                loginOperation.getCallback().onSuccess(loginOperation, result);
+            public void serviceFound(GenericApiGWTServiceAsync service) {
+                service.Login(loginOperation.getParameter().getLoginName(), 
loginOperation.getParameter().getPassword(),
+                        loginOperation.getParameter().getProfileName(), 
loginOperation.getOperation(),
+                        new AsyncCallback<VdcReturnValueBase>() {
+                    @Override
+                    public void onSuccess(final VdcReturnValueBase result) {
+                        loginOperation.getCallback().onSuccess(loginOperation, 
result);
+                    }
+
+                    @Override
+                    public void onFailure(final Throwable caught) {
+                        loginOperation.getCallback().onFailure(loginOperation, 
caught);
+                    }
+                });
             }
 
             @Override
-            public void onFailure(final Throwable caught) {
-                loginOperation.getCallback().onFailure(loginOperation, caught);
+            public void onFailure(Throwable exception) {
+                loginOperation.getCallback().onFailure(loginOperation, 
exception);
             }
         });
     }
@@ -366,15 +494,29 @@
      */
     @Override
     public void logout(final Object userObject, final UserCallback callback) {
-        getService().logOff((DbUser) userObject, new 
AsyncCallback<VdcReturnValueBase>() {
+        getService(new ServiceCallback() {
+
             @Override
-            public void onSuccess(final VdcReturnValueBase result) {
-                callback.onSuccess(result);
+            public void serviceFound(GenericApiGWTServiceAsync foundService) {
+                foundService.logOff((DbUser) userObject, new 
AsyncCallback<VdcReturnValueBase>() {
+                    @Override
+                    public void onSuccess(final VdcReturnValueBase result) {
+                        //Remove the rpc token when logging out.
+                        ((HasRpcToken)service).setRpcToken(null);
+                        callback.onSuccess(result);
+                    }
+
+                    @Override
+                    public void onFailure(final Throwable caught) {
+                        callback.onFailure(caught);
+                    }
+                });
+
             }
 
             @Override
-            public void onFailure(final Throwable caught) {
-                callback.onFailure(caught);
+            public void onFailure(Throwable exception) {
+                callback.onFailure(exception);
             }
         });
     }
diff --git 
a/frontend/webadmin/modules/frontend/src/main/java/org/ovirt/engine/ui/frontend/gwtservices/GenericApiGWTService.java
 
b/frontend/webadmin/modules/frontend/src/main/java/org/ovirt/engine/ui/frontend/gwtservices/GenericApiGWTService.java
index 69d102d..1a0b919 100644
--- 
a/frontend/webadmin/modules/frontend/src/main/java/org/ovirt/engine/ui/frontend/gwtservices/GenericApiGWTService.java
+++ 
b/frontend/webadmin/modules/frontend/src/main/java/org/ovirt/engine/ui/frontend/gwtservices/GenericApiGWTService.java
@@ -10,11 +10,11 @@
 import org.ovirt.engine.core.common.queries.VdcQueryReturnValue;
 import org.ovirt.engine.core.common.queries.VdcQueryType;
 
-import com.google.gwt.rpc.client.RpcService;
 import com.google.gwt.user.client.rpc.RemoteServiceRelativePath;
+import com.google.gwt.user.server.rpc.NoXsrfProtect;
 
 @RemoteServiceRelativePath("GenericApiGWTService")
-public interface GenericApiGWTService extends RpcService {
+public interface GenericApiGWTService extends XsrfProtectedRpcService {
 
     public VdcQueryReturnValue RunQuery(VdcQueryType search,
             VdcQueryParametersBase searchParameters);
@@ -22,6 +22,7 @@
     public VdcReturnValueBase RunAction(VdcActionType actionType,
             VdcActionParametersBase params);
 
+    @NoXsrfProtect
     public VdcQueryReturnValue RunPublicQuery(VdcQueryType queryType,
             VdcQueryParametersBase params);
 
@@ -41,8 +42,10 @@
 
     public DbUser getLoggedInUser();
 
+    @NoXsrfProtect
     public VdcReturnValueBase logOff(DbUser userToLogoff);
 
+    @NoXsrfProtect
     public VdcReturnValueBase Login(String userName, String password, String 
profileName, VdcActionType loginType);
 
 }
diff --git 
a/frontend/webadmin/modules/frontend/src/main/java/org/ovirt/engine/ui/frontend/gwtservices/XsrfProtectedRpcService.java
 
b/frontend/webadmin/modules/frontend/src/main/java/org/ovirt/engine/ui/frontend/gwtservices/XsrfProtectedRpcService.java
new file mode 100644
index 0000000..87dafd1
--- /dev/null
+++ 
b/frontend/webadmin/modules/frontend/src/main/java/org/ovirt/engine/ui/frontend/gwtservices/XsrfProtectedRpcService.java
@@ -0,0 +1,8 @@
+package org.ovirt.engine.ui.frontend.gwtservices;
+
+import com.google.gwt.rpc.client.RpcService;
+import com.google.gwt.user.server.rpc.XsrfProtect;
+
+@XsrfProtect
+public interface XsrfProtectedRpcService extends RpcService {
+}
diff --git 
a/frontend/webadmin/modules/frontend/src/main/java/org/ovirt/engine/ui/frontend/server/gwt/AbstractXsrfProtectedRpcServlet.java
 
b/frontend/webadmin/modules/frontend/src/main/java/org/ovirt/engine/ui/frontend/server/gwt/AbstractXsrfProtectedRpcServlet.java
new file mode 100644
index 0000000..b8d82ad
--- /dev/null
+++ 
b/frontend/webadmin/modules/frontend/src/main/java/org/ovirt/engine/ui/frontend/server/gwt/AbstractXsrfProtectedRpcServlet.java
@@ -0,0 +1,118 @@
+package org.ovirt.engine.ui.frontend.server.gwt;
+
+import java.io.OutputStream;
+import java.lang.reflect.Method;
+import java.util.Collections;
+
+import com.google.gwt.rpc.client.ast.StringValueCommand;
+import com.google.gwt.rpc.client.impl.RemoteException;
+import com.google.gwt.rpc.server.ClientOracle;
+import com.google.gwt.rpc.server.CommandServerSerializationStreamReader;
+import com.google.gwt.rpc.server.RpcServlet;
+import com.google.gwt.rpc.server.SimplePayloadDecoder;
+import com.google.gwt.user.client.rpc.IncompatibleRemoteServiceException;
+import com.google.gwt.user.client.rpc.RpcToken;
+import com.google.gwt.user.client.rpc.SerializationException;
+import com.google.gwt.user.server.Util;
+import com.google.gwt.user.server.rpc.NoXsrfProtect;
+import com.google.gwt.user.server.rpc.RPCRequest;
+import com.google.gwt.user.server.rpc.XsrfProtect;
+
+public abstract class AbstractXsrfProtectedRpcServlet extends RpcServlet {
+
+    /**
+     * Serial version UID for serialization.
+     */
+    static final long serialVersionUID = -7274292100456700624L;
+
+    /**
+     * The default constructor used by service implementations that extend 
this class. The servlet will delegate AJAX
+     * requests to the appropriate method in the subclass.
+     */
+    public AbstractXsrfProtectedRpcServlet() {
+        super();
+    }
+
+    /**
+     * The wrapping constructor used by service implementations that are 
separate from this class. The servlet will
+     * delegate AJAX requests to the appropriate method in the given object.
+     *
+     * @param delegate
+     *            The delegate object.
+     */
+    public AbstractXsrfProtectedRpcServlet(Object delegate) {
+        super(delegate);
+    }
+
+    @Override
+    public void processCall(ClientOracle clientOracle, String payload, 
OutputStream stream)
+            throws SerializationException {
+        try {
+            SimplePayloadDecoder decoder;
+            try {
+                decoder = new SimplePayloadDecoder(clientOracle, payload);
+            } catch (ClassNotFoundException e) {
+                throw new IncompatibleRemoteServiceException(
+                        "Client does not have a type sent by the server", e); 
//$NON-NLS-1$
+            }
+            CommandServerSerializationStreamReader streamReader = new 
CommandServerSerializationStreamReader();
+            if (decoder.getThrownValue() != null) {
+                
streamReader.prepareToRead(Collections.singletonList(decoder.getThrownValue()));
+                try {
+                    throw new RemoteException((Throwable) 
streamReader.readObject());
+                } catch (ClassCastException e) {
+                    throw new SerializationException(
+                            "The remote end threw something other than a 
Throwable", e); //$NON-NLS-1$
+                } catch (SerializationException e) {
+                    throw new IncompatibleRemoteServiceException(
+                            "The remote end threw an exception which could not 
be deserialized", //$NON-NLS-1$
+                            e);
+                }
+            } else {
+                streamReader.prepareToRead(decoder.getValues());
+            }
+            // Read the name of the RemoteService interface
+            Object firstObject = streamReader.readObject();
+            if (firstObject instanceof RpcToken) {
+                String strippedString = payload.substring(0, 3)
+                        + 
payload.substring(payload.indexOf(((StringValueCommand)decoder.getValues().get(1)).getValue()));
+
+                super.processCall(clientOracle, strippedString, stream);
+            } else {
+                super.processCall(clientOracle, payload, stream);
+            }
+
+        } catch (SerializationException ex) {
+            throw new IncompatibleRemoteServiceException(ex.getMessage(), ex);
+        }
+    }
+
+    @Override
+    protected void onAfterRequestDeserialized(RPCRequest rpcRequest) {
+        if (shouldValidateXsrfToken(rpcRequest.getMethod())) {
+            validateXsrfToken(rpcRequest.getRpcToken(), 
rpcRequest.getMethod());
+        }
+    }
+
+    /**
+     * Override this method to change default XSRF enforcement logic.
+     *
+     * @param method
+     *            Method being invoked
+     * @return {@code true} if XSRF token should be verified, {@code false} 
otherwise
+     */
+    protected boolean shouldValidateXsrfToken(Method method) {
+        return Util.isMethodXsrfProtected(method, XsrfProtect.class,
+                NoXsrfProtect.class, RpcToken.class);
+    }
+
+    /**
+     * Override this method to perform XSRF token verification.
+     *
+     * @param token
+     *            {@link RpcToken} included with an RPC request.
+     * @param method
+     *            method being invoked via this RPC call.
+     */
+    protected abstract void validateXsrfToken(RpcToken token, Method method);
+}
diff --git 
a/frontend/webadmin/modules/frontend/src/main/java/org/ovirt/engine/ui/frontend/server/gwt/GenericApiGWTServiceImpl.java
 
b/frontend/webadmin/modules/frontend/src/main/java/org/ovirt/engine/ui/frontend/server/gwt/GenericApiGWTServiceImpl.java
index 68979c0..57f4dc3 100644
--- 
a/frontend/webadmin/modules/frontend/src/main/java/org/ovirt/engine/ui/frontend/server/gwt/GenericApiGWTServiceImpl.java
+++ 
b/frontend/webadmin/modules/frontend/src/main/java/org/ovirt/engine/ui/frontend/server/gwt/GenericApiGWTServiceImpl.java
@@ -21,10 +21,9 @@
 import org.ovirt.engine.core.common.queries.VdcQueryType;
 import org.ovirt.engine.ui.frontend.gwtservices.GenericApiGWTService;
 
-import com.google.gwt.rpc.server.RpcServlet;
 import com.google.gwt.user.client.rpc.SerializationException;
 
-public class GenericApiGWTServiceImpl extends RpcServlet implements 
GenericApiGWTService {
+public class GenericApiGWTServiceImpl extends XsrfProtectedRpcServlet 
implements GenericApiGWTService {
 
     private static final long serialVersionUID = 7395780289048030855L;
 
diff --git 
a/frontend/webadmin/modules/frontend/src/main/java/org/ovirt/engine/ui/frontend/server/gwt/XsrfProtectedRpcServlet.java
 
b/frontend/webadmin/modules/frontend/src/main/java/org/ovirt/engine/ui/frontend/server/gwt/XsrfProtectedRpcServlet.java
new file mode 100644
index 0000000..3081ea6
--- /dev/null
+++ 
b/frontend/webadmin/modules/frontend/src/main/java/org/ovirt/engine/ui/frontend/server/gwt/XsrfProtectedRpcServlet.java
@@ -0,0 +1,93 @@
+package org.ovirt.engine.ui.frontend.server.gwt;
+
+import java.lang.reflect.Method;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.Cookie;
+
+import com.google.gwt.user.client.rpc.RpcToken;
+import com.google.gwt.user.client.rpc.RpcTokenException;
+import com.google.gwt.user.client.rpc.XsrfToken;
+import com.google.gwt.user.server.Util;
+import com.google.gwt.user.server.rpc.XsrfTokenServiceServlet;
+import com.google.gwt.util.tools.shared.Md5Utils;
+import com.google.gwt.util.tools.shared.StringUtils;
+
+public class XsrfProtectedRpcServlet extends AbstractXsrfProtectedRpcServlet {
+
+    // Can't use the one from XsrfTokenServiceServlet as it is not public.
+    static final String COOKIE_NAME_NOT_SET_ERROR_MSG =
+            "Session cookie name not set! Use context-param to specify session 
cookie name"; //$NON-NLS-1$
+
+    String sessionCookieName = null;
+
+    /**
+     * Default constructor.
+     */
+    public XsrfProtectedRpcServlet() {
+        this(null);
+    }
+
+    /**
+     * Constructor with session cookie name.
+     * @param cookieName The session cookie name.
+     */
+    public XsrfProtectedRpcServlet(String cookieName) {
+        this.sessionCookieName = cookieName;
+    }
+
+    /**
+     * Constructor with delegate.
+     * @param delegate The delegate object
+     */
+    public XsrfProtectedRpcServlet(Object delegate) {
+        this(delegate, null);
+    }
+
+    /**
+     * Constructor with cookie name and delegate.
+     * @param delegate The delegate object
+     * @param cookieName The name of the session cookie.
+     */
+    public XsrfProtectedRpcServlet(Object delegate, String cookieName) {
+        super(delegate);
+        this.sessionCookieName = cookieName;
+    }
+
+    @Override
+    public void init() throws ServletException {
+        super.init();
+        // do not overwrite if value is supplied in constructor
+        if (sessionCookieName == null) {
+            // servlet configuration precedes context configuration
+            sessionCookieName = 
getServletConfig().getInitParameter(XsrfTokenServiceServlet.COOKIE_NAME_PARAM);
+            if (sessionCookieName == null) {
+                sessionCookieName = 
getServletContext().getInitParameter(XsrfTokenServiceServlet.COOKIE_NAME_PARAM);
+            }
+            if (sessionCookieName == null) {
+                throw new IllegalStateException(COOKIE_NAME_NOT_SET_ERROR_MSG);
+            }
+        }
+    }
+
+    @Override
+    protected void validateXsrfToken(RpcToken token, Method method) {
+        if (token == null) {
+            throw new RpcTokenException("XSRF token missing"); //$NON-NLS-1$
+        }
+        Cookie sessionCookie = Util.getCookie(getThreadLocalRequest(), 
sessionCookieName, false);
+        if (sessionCookie == null || sessionCookie.getValue() == null
+                || sessionCookie.getValue().length() == 0) {
+            throw new RpcTokenException("Session cookie is missing or empty! " 
+ //$NON-NLS-1$
+                    "Unable to verify XSRF cookie"); //$NON-NLS-1$
+        }
+
+        String expectedToken = StringUtils.toHexString(
+                Md5Utils.getMd5Digest(sessionCookie.getValue().getBytes()));
+        XsrfToken xsrfToken = (XsrfToken) token;
+
+        if (!expectedToken.equals(xsrfToken.getToken())) {
+            throw new RpcTokenException("Invalid XSRF token"); //$NON-NLS-1$
+        }
+    }
+}
diff --git 
a/frontend/webadmin/modules/frontend/src/main/resources/META-INF/web-fragment.xml
 
b/frontend/webadmin/modules/frontend/src/main/resources/META-INF/web-fragment.xml
index 86dabbd7..0f5d44e 100644
--- 
a/frontend/webadmin/modules/frontend/src/main/resources/META-INF/web-fragment.xml
+++ 
b/frontend/webadmin/modules/frontend/src/main/resources/META-INF/web-fragment.xml
@@ -3,6 +3,11 @@
 
     <display-name>Frontend Web Fragment for GWT UI Projects</display-name>
 
+    <context-param>
+        <param-name>gwt.xsrf.session_cookie_name</param-name>
+        <param-value>JSESSIONID</param-value>
+    </context-param>
+
     <filter>
         <filter-name>GwtCachingFilter</filter-name>
         
<filter-class>org.ovirt.engine.ui.frontend.server.gwt.GwtCachingFilter</filter-class>
@@ -81,6 +86,10 @@
         
<servlet-class>org.ovirt.engine.core.branding.BrandingCascadingResourceServlet</servlet-class>
     </servlet>
 
+    <servlet>
+        <servlet-name>xsrf</servlet-name>
+        
<servlet-class>com.google.gwt.user.server.rpc.XsrfTokenServiceServlet</servlet-class>
+    </servlet>
     <!-- PageNotFound Servlet -->
     <servlet>
         <servlet-name>PageNotFoundForwardServlet</servlet-name>
diff --git 
a/frontend/webadmin/modules/frontend/src/test/java/org/ovirt/engine/ui/frontend/FrontendActionTest.java
 
b/frontend/webadmin/modules/frontend/src/test/java/org/ovirt/engine/ui/frontend/FrontendActionTest.java
index 3c41bbd..09e6f4a 100644
--- 
a/frontend/webadmin/modules/frontend/src/test/java/org/ovirt/engine/ui/frontend/FrontendActionTest.java
+++ 
b/frontend/webadmin/modules/frontend/src/test/java/org/ovirt/engine/ui/frontend/FrontendActionTest.java
@@ -44,6 +44,7 @@
 import com.google.gwt.event.shared.EventBus;
 import com.google.gwt.user.client.rpc.AsyncCallback;
 import com.google.gwt.user.client.rpc.StatusCodeException;
+import com.google.gwt.user.client.rpc.XsrfTokenServiceAsync;
 
 @RunWith(MockitoJUnitRunner.class)
 /**
@@ -63,6 +64,8 @@
 
     @Mock
     GenericApiGWTServiceAsync mockService;
+    @Mock
+    XsrfTokenServiceAsync mockXsrfService;
     @Mock
     ErrorTranslator mockVdsmErrorsTranslator;
     @Mock
@@ -99,7 +102,7 @@
     @Before
     public void setUp() throws Exception {
         fakeScheduler = new FakeGWTScheduler();
-        CommunicationProvider communicationsProvider = new 
GWTRPCCommunicationProvider(mockService);
+        CommunicationProvider communicationsProvider = new 
GWTRPCCommunicationProvider(mockService, mockXsrfService);
         OperationProcessor operationProcessor = new 
OperationProcessor(communicationsProvider);
         operationProcessor.setScheduler(fakeScheduler);
         VdcOperationManager operationsManager = new 
VdcOperationManager(operationProcessor);
diff --git 
a/frontend/webadmin/modules/frontend/src/test/java/org/ovirt/engine/ui/frontend/FrontendTest.java
 
b/frontend/webadmin/modules/frontend/src/test/java/org/ovirt/engine/ui/frontend/FrontendTest.java
index 1607ced..4c0ac68 100644
--- 
a/frontend/webadmin/modules/frontend/src/test/java/org/ovirt/engine/ui/frontend/FrontendTest.java
+++ 
b/frontend/webadmin/modules/frontend/src/test/java/org/ovirt/engine/ui/frontend/FrontendTest.java
@@ -41,6 +41,7 @@
 import com.google.gwt.event.shared.EventBus;
 import com.google.gwt.user.client.rpc.AsyncCallback;
 import com.google.gwt.user.client.rpc.StatusCodeException;
+import com.google.gwt.user.client.rpc.XsrfTokenServiceAsync;
 
 @RunWith(MockitoJUnitRunner.class)
 public class FrontendTest {
@@ -59,6 +60,8 @@
 
     @Mock
     GenericApiGWTServiceAsync mockService;
+    @Mock
+    XsrfTokenServiceAsync mockXsrfService;
     @Mock
     ErrorTranslator mockVdsmErrorsTranslator;
     @Mock
@@ -93,7 +96,7 @@
     @Before
     public void setUp() throws Exception {
         fakeScheduler = new FakeGWTScheduler();
-        CommunicationProvider communicationsProvider = new 
GWTRPCCommunicationProvider(mockService);
+        CommunicationProvider communicationsProvider = new 
GWTRPCCommunicationProvider(mockService, mockXsrfService);
         OperationProcessor operationProcessor = new 
OperationProcessor(communicationsProvider);
         operationProcessor.setScheduler(fakeScheduler);
         VdcOperationManager operationsManager = new 
VdcOperationManager(operationProcessor);
diff --git 
a/frontend/webadmin/modules/frontend/src/test/java/org/ovirt/engine/ui/frontend/communication/GWTRPCCommunicationProviderTest.java
 
b/frontend/webadmin/modules/frontend/src/test/java/org/ovirt/engine/ui/frontend/communication/GWTRPCCommunicationProviderTest.java
index a8283c2..d768c1b 100644
--- 
a/frontend/webadmin/modules/frontend/src/test/java/org/ovirt/engine/ui/frontend/communication/GWTRPCCommunicationProviderTest.java
+++ 
b/frontend/webadmin/modules/frontend/src/test/java/org/ovirt/engine/ui/frontend/communication/GWTRPCCommunicationProviderTest.java
@@ -24,11 +24,14 @@
 import org.ovirt.engine.ui.frontend.gwtservices.GenericApiGWTServiceAsync;
 
 import com.google.gwt.user.client.rpc.AsyncCallback;
+import com.google.gwt.user.client.rpc.XsrfTokenServiceAsync;
 
 @RunWith(MockitoJUnitRunner.class)
 public class GWTRPCCommunicationProviderTest {
     @Mock
     GenericApiGWTServiceAsync mockService;
+    @Mock
+    XsrfTokenServiceAsync mockXsrfService;
     @Mock
     VdcOperationCallback mockOperationCallbackSingle1;
     @Mock
@@ -53,7 +56,7 @@
 
     @Before
     public void setUp() throws Exception {
-        testProvider = new GWTRPCCommunicationProvider(mockService);
+        testProvider = new GWTRPCCommunicationProvider(mockService, 
mockXsrfService);
     }
 
     @Test
diff --git 
a/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/gin/BaseSystemModule.java
 
b/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/gin/BaseSystemModule.java
index a37a081..aebd36c 100644
--- 
a/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/gin/BaseSystemModule.java
+++ 
b/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/gin/BaseSystemModule.java
@@ -25,6 +25,7 @@
 
 import com.google.gwt.event.shared.SimpleEventBus;
 import com.google.gwt.inject.client.AbstractGinModule;
+import com.google.gwt.user.client.rpc.XsrfTokenServiceAsync;
 import com.google.inject.Singleton;
 import com.gwtplatform.mvp.client.RootPresenter;
 import com.gwtplatform.mvp.client.proxy.ParameterTokenFormatter;
@@ -66,6 +67,7 @@
         bind(OperationProcessor.class).in(Singleton.class);
         
bind(CommunicationProvider.class).to(GWTRPCCommunicationProvider.class).in(Singleton.class);
         bind(GenericApiGWTServiceAsync.class).in(Singleton.class);
+        bind(XsrfTokenServiceAsync.class).in(Singleton.class);
     }
 
     protected void bindResourceConfiguration(
diff --git 
a/frontend/webadmin/modules/userportal-gwtp/src/main/webapp/WEB-INF/web.xml 
b/frontend/webadmin/modules/userportal-gwtp/src/main/webapp/WEB-INF/web.xml
index 8abdb78..62c0774 100644
--- a/frontend/webadmin/modules/userportal-gwtp/src/main/webapp/WEB-INF/web.xml
+++ b/frontend/webadmin/modules/userportal-gwtp/src/main/webapp/WEB-INF/web.xml
@@ -70,6 +70,11 @@
     </servlet-mapping>
 
     <servlet-mapping>
+        <servlet-name>xsrf</servlet-name>
+        <url-pattern>/xsrf</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
         <servlet-name>BrandingServlet</servlet-name>
         <url-pattern>/theme/*</url-pattern>
     </servlet-mapping>
diff --git a/frontend/webadmin/modules/webadmin/src/main/webapp/WEB-INF/web.xml 
b/frontend/webadmin/modules/webadmin/src/main/webapp/WEB-INF/web.xml
index 90e8b93..9e92abb 100644
--- a/frontend/webadmin/modules/webadmin/src/main/webapp/WEB-INF/web.xml
+++ b/frontend/webadmin/modules/webadmin/src/main/webapp/WEB-INF/web.xml
@@ -70,6 +70,11 @@
                <url-pattern>/GenericApiGWTService</url-pattern>
        </servlet-mapping>
 
+    <servlet-mapping>
+        <servlet-name>xsrf</servlet-name>
+        <url-pattern>/xsrf</url-pattern>
+    </servlet-mapping>
+
        <servlet-mapping>
                <servlet-name>PluginResourceServlet</servlet-name>
                <url-pattern>/plugin/*</url-pattern>


-- 
To view, visit http://gerrit.ovirt.org/26408
To unsubscribe, visit http://gerrit.ovirt.org/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: Ia4a5ad1f33eb985aa79e1376aecb48ae365d334d
Gerrit-PatchSet: 1
Gerrit-Project: ovirt-engine
Gerrit-Branch: master
Gerrit-Owner: Alexander Wels <aw...@redhat.com>
_______________________________________________
Engine-patches mailing list
Engine-patches@ovirt.org
http://lists.ovirt.org/mailman/listinfo/engine-patches

Reply via email to