diff --git a/gwtp-core/gwtp-dispatch-server-guice/pom.xml b/gwtp-core/gwtp-dispatch-server-guice/pom.xml index 213d012605..2d8d4294cc 100644 --- a/gwtp-core/gwtp-dispatch-server-guice/pom.xml +++ b/gwtp-core/gwtp-dispatch-server-guice/pom.xml @@ -34,6 +34,10 @@ com.gwtplatform gwtp-dispatch-test test - - - \ No newline at end of file + + + org.jukito + jukito + + + diff --git a/gwtp-core/gwtp-dispatch-server-guice/src/test/java/com/gwtplatform/dispatch/server/ActionExceptionThrownByHandler.java b/gwtp-core/gwtp-dispatch-server-guice/src/test/java/com/gwtplatform/dispatch/server/ActionExceptionThrownByHandler.java new file mode 100644 index 0000000000..96610f5e07 --- /dev/null +++ b/gwtp-core/gwtp-dispatch-server-guice/src/test/java/com/gwtplatform/dispatch/server/ActionExceptionThrownByHandler.java @@ -0,0 +1,25 @@ +/** + * Copyright 2011 ArcBees Inc. + * + * 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.gwtplatform.dispatch.server; + +import com.gwtplatform.dispatch.shared.ActionException; + +public class ActionExceptionThrownByHandler extends ActionException { + public ActionExceptionThrownByHandler(Throwable cause) { + super("", cause); + } +} diff --git a/gwtp-core/gwtp-dispatch-server-guice/src/test/java/com/gwtplatform/dispatch/server/ActionExceptionThrownByValidator.java b/gwtp-core/gwtp-dispatch-server-guice/src/test/java/com/gwtplatform/dispatch/server/ActionExceptionThrownByValidator.java new file mode 100644 index 0000000000..f82d2be8bc --- /dev/null +++ b/gwtp-core/gwtp-dispatch-server-guice/src/test/java/com/gwtplatform/dispatch/server/ActionExceptionThrownByValidator.java @@ -0,0 +1,25 @@ +/** + * Copyright 2011 ArcBees Inc. + * + * 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.gwtplatform.dispatch.server; + +import com.gwtplatform.dispatch.shared.ActionException; + +public class ActionExceptionThrownByValidator extends ActionException { + public ActionExceptionThrownByValidator(Throwable cause) { + super("", cause); + } +} diff --git a/gwtp-core/gwtp-dispatch-server-guice/src/test/java/com/gwtplatform/dispatch/server/ActionThrownByHandlerTest.java b/gwtp-core/gwtp-dispatch-server-guice/src/test/java/com/gwtplatform/dispatch/server/ActionThrownByHandlerTest.java new file mode 100644 index 0000000000..f608ad3ae2 --- /dev/null +++ b/gwtp-core/gwtp-dispatch-server-guice/src/test/java/com/gwtplatform/dispatch/server/ActionThrownByHandlerTest.java @@ -0,0 +1,59 @@ +/** + * Copyright 2011 ArcBees Inc. + * + * 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.gwtplatform.dispatch.server; + +import javax.inject.Inject; + +import org.jukito.JukitoModule; +import org.jukito.JukitoRunner; +import org.junit.Test; +import org.junit.runner.RunWith; + +import com.gwtplatform.dispatch.server.guice.DispatchServiceImpl; +import com.gwtplatform.dispatch.server.guice.actionvalidator.DefaultActionValidator; +import com.gwtplatform.dispatch.shared.ActionException; +import com.gwtplatform.dispatch.shared.ServiceException; + +import static org.hamcrest.CoreMatchers.instanceOf; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +@RunWith(JukitoRunner.class) +public class ActionThrownByHandlerTest { + public static class MyModule extends JukitoModule { + @Override + protected void configureTest() { + install(new ServiceModule(DefaultActionValidator.class)); + } + } + + @Inject + DispatchServiceImpl service; + + @Test + public void exceptionThrownByHandlerIsNotWrappedInActionException() throws ServiceException { + try { + service.execute("", new SomeAction()); + fail(); + } catch (ActionException e) { + assertThat(e, instanceOf(ActionExceptionThrownByHandler.class)); + assertEquals(0, e.getStackTrace().length); + assertEquals(0, e.getCause().getStackTrace().length); + } + } +} diff --git a/gwtp-core/gwtp-dispatch-server-guice/src/test/java/com/gwtplatform/dispatch/server/ActionThrownByValidatorTest.java b/gwtp-core/gwtp-dispatch-server-guice/src/test/java/com/gwtplatform/dispatch/server/ActionThrownByValidatorTest.java new file mode 100644 index 0000000000..6545b351c1 --- /dev/null +++ b/gwtp-core/gwtp-dispatch-server-guice/src/test/java/com/gwtplatform/dispatch/server/ActionThrownByValidatorTest.java @@ -0,0 +1,58 @@ +/** + * Copyright 2011 ArcBees Inc. + * + * 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.gwtplatform.dispatch.server; + +import javax.inject.Inject; + +import org.jukito.JukitoModule; +import org.jukito.JukitoRunner; +import org.junit.Test; +import org.junit.runner.RunWith; + +import com.gwtplatform.dispatch.server.guice.DispatchServiceImpl; +import com.gwtplatform.dispatch.shared.ActionException; +import com.gwtplatform.dispatch.shared.ServiceException; + +import static org.hamcrest.CoreMatchers.instanceOf; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +@RunWith(JukitoRunner.class) +public class ActionThrownByValidatorTest { + public static class MyModule extends JukitoModule { + @Override + protected void configureTest() { + install(new ServiceModule(ActionValidatorThatThrows.class)); + } + } + + @Inject + DispatchServiceImpl service; + + @Test + public void exceptionThrownByValidatorIsNotWrappedInActionException() throws ServiceException { + try { + service.execute("", new SomeAction()); + fail(); + } catch (ActionException e) { + assertThat(e, instanceOf(ActionExceptionThrownByValidator.class)); + assertEquals(0, e.getStackTrace().length); + assertEquals(0, e.getCause().getStackTrace().length); + } + } +} diff --git a/gwtp-core/gwtp-dispatch-server-guice/src/test/java/com/gwtplatform/dispatch/server/ActionValidatorThatThrows.java b/gwtp-core/gwtp-dispatch-server-guice/src/test/java/com/gwtplatform/dispatch/server/ActionValidatorThatThrows.java new file mode 100644 index 0000000000..992df0d805 --- /dev/null +++ b/gwtp-core/gwtp-dispatch-server-guice/src/test/java/com/gwtplatform/dispatch/server/ActionValidatorThatThrows.java @@ -0,0 +1,29 @@ +/** + * Copyright 2011 ArcBees Inc. + * + * 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.gwtplatform.dispatch.server; + +import com.gwtplatform.dispatch.server.actionvalidator.ActionValidator; +import com.gwtplatform.dispatch.shared.Action; +import com.gwtplatform.dispatch.shared.ActionException; +import com.gwtplatform.dispatch.shared.Result; + +public class ActionValidatorThatThrows implements ActionValidator { + @Override + public boolean isValid(Action action) throws ActionException { + throw new ActionExceptionThrownByValidator(new Exception()); + } +} diff --git a/gwtp-core/gwtp-dispatch-server-guice/src/test/java/com/gwtplatform/dispatch/server/HandlerThatThrowsActionException.java b/gwtp-core/gwtp-dispatch-server-guice/src/test/java/com/gwtplatform/dispatch/server/HandlerThatThrowsActionException.java new file mode 100644 index 0000000000..4ca686085b --- /dev/null +++ b/gwtp-core/gwtp-dispatch-server-guice/src/test/java/com/gwtplatform/dispatch/server/HandlerThatThrowsActionException.java @@ -0,0 +1,39 @@ +/** + * Copyright 2011 ArcBees Inc. + * + * 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.gwtplatform.dispatch.server; + +import javax.inject.Inject; + +import com.gwtplatform.dispatch.server.actionhandler.AbstractActionHandler; +import com.gwtplatform.dispatch.shared.ActionException; +import com.gwtplatform.dispatch.shared.NoResult; + +public class HandlerThatThrowsActionException extends AbstractActionHandler { + @Inject + HandlerThatThrowsActionException() { + super(SomeAction.class); + } + + @Override + public NoResult execute(SomeAction action, ExecutionContext context) throws ActionException { + throw new ActionExceptionThrownByHandler(new Exception()); + } + + @Override + public void undo(SomeAction action, NoResult result, ExecutionContext context) throws ActionException { + } +} diff --git a/gwtp-core/gwtp-dispatch-server-guice/src/test/java/com/gwtplatform/dispatch/server/ServiceModule.java b/gwtp-core/gwtp-dispatch-server-guice/src/test/java/com/gwtplatform/dispatch/server/ServiceModule.java new file mode 100644 index 0000000000..5d1a7647b1 --- /dev/null +++ b/gwtp-core/gwtp-dispatch-server-guice/src/test/java/com/gwtplatform/dispatch/server/ServiceModule.java @@ -0,0 +1,58 @@ +/** + * Copyright 2011 ArcBees Inc. + * + * 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.gwtplatform.dispatch.server; + +import com.google.inject.AbstractModule; +import com.google.inject.Singleton; +import com.gwtplatform.dispatch.server.actionhandler.ActionHandler; +import com.gwtplatform.dispatch.server.actionhandlervalidator.ActionHandlerValidatorClass; +import com.gwtplatform.dispatch.server.actionhandlervalidator.ActionHandlerValidatorMap; +import com.gwtplatform.dispatch.server.actionhandlervalidator.ActionHandlerValidatorMapImpl; +import com.gwtplatform.dispatch.server.actionhandlervalidator.ActionHandlerValidatorRegistry; +import com.gwtplatform.dispatch.server.actionvalidator.ActionValidator; +import com.gwtplatform.dispatch.server.guice.DispatchImpl; +import com.gwtplatform.dispatch.server.guice.actionhandlervalidator.ActionHandlerValidatorLinker; +import com.gwtplatform.dispatch.server.guice.actionhandlervalidator.LazyActionHandlerValidatorRegistryImpl; +import com.gwtplatform.dispatch.shared.Action; +import com.gwtplatform.dispatch.shared.Result; + +public class ServiceModule extends AbstractModule { + private final Class actionValidator; + + public ServiceModule(Class actionValidator) { + this.actionValidator = actionValidator; + } + + @Override + protected void configure() { + bind(Dispatch.class).to(DispatchImpl.class); + bind(ActionHandlerValidatorRegistry.class).to( + LazyActionHandlerValidatorRegistryImpl.class).in(Singleton.class); + + bindHandler(SomeAction.class, HandlerThatThrowsActionException.class, actionValidator); + requestStaticInjection(ActionHandlerValidatorLinker.class); + } + + protected , R extends Result> void bindHandler( + Class actionClass, Class> handlerClass, + Class actionValidator) { + + bind(ActionHandlerValidatorMap.class).toInstance( + new ActionHandlerValidatorMapImpl(actionClass, + new ActionHandlerValidatorClass(handlerClass, actionValidator))); + } +} diff --git a/gwtp-core/gwtp-dispatch-server-guice/src/test/java/com/gwtplatform/dispatch/server/SomeAction.java b/gwtp-core/gwtp-dispatch-server-guice/src/test/java/com/gwtplatform/dispatch/server/SomeAction.java new file mode 100644 index 0000000000..0d7afae00f --- /dev/null +++ b/gwtp-core/gwtp-dispatch-server-guice/src/test/java/com/gwtplatform/dispatch/server/SomeAction.java @@ -0,0 +1,32 @@ +/** + * Copyright 2011 ArcBees Inc. + * + * 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.gwtplatform.dispatch.server; + +import com.gwtplatform.dispatch.shared.Action; +import com.gwtplatform.dispatch.shared.NoResult; + +public class SomeAction implements Action { + @Override + public String getServiceName() { + return null; + } + + @Override + public boolean isSecured() { + return false; + } +} diff --git a/gwtp-core/gwtp-dispatch-server/src/main/java/com/gwtplatform/dispatch/server/AbstractDispatchServiceImpl.java b/gwtp-core/gwtp-dispatch-server/src/main/java/com/gwtplatform/dispatch/server/AbstractDispatchServiceImpl.java index 81f596a154..c58099a04c 100644 --- a/gwtp-core/gwtp-dispatch-server/src/main/java/com/gwtplatform/dispatch/server/AbstractDispatchServiceImpl.java +++ b/gwtp-core/gwtp-dispatch-server/src/main/java/com/gwtplatform/dispatch/server/AbstractDispatchServiceImpl.java @@ -48,15 +48,13 @@ public abstract class AbstractDispatchServiceImpl extends RemoteServiceServlet implements DispatchService { private static final long serialVersionUID = -4753225025940949024L; private static final String noSecurityCookieMessage = "You have to define a security cookie in order to use " + - "secured actions. See com.gwtplatform.dispatch.shared" + - ".SecurityCookie for details."; + "secured actions. See com.gwtplatform.dispatch.shared" + + ".SecurityCookie for details."; private static final String xsrfAttackMessage = "Cookie provided by RPC doesn't match request cookie, " + - "aborting action, possible XSRF attack. (Maybe you forgot to set " + - "the security cookie?)"; - + "aborting action, possible XSRF attack. (Maybe you forgot to set " + + "the security cookie?)"; protected final Dispatch dispatch; protected final Logger logger; - protected RequestProvider requestProvider; protected AbstractDispatchServiceImpl(Logger logger, @@ -71,6 +69,12 @@ public String getSecurityCookieName() { return null; } + /** + * {@link ActionException} and {@link ServiceException} will have their stacktraces (and stacktraces of their + * causes) removed for security purposes. + * + * @see {@link DispatchService} for further API docs + */ @Override public Result execute(String cookieSentByRPC, Action action) throws ActionException, ServiceException { @@ -86,22 +90,24 @@ public Result execute(String cookieSentByRPC, Action action) throws ActionExc } catch (ActionException e) { if (logger.isLoggable(Level.WARNING)) { String newMessage = "Action exception while executing " + action.getClass().getName() + ": " + - e.getMessage(); + e.getMessage(); logger.log(Level.WARNING, newMessage, e); } - throw new ActionException(e.getMessage()); + removeStacktraces(e); + + throw e; } catch (ServiceException e) { if (logger.isLoggable(Level.WARNING)) { logger.log(Level.WARNING, "Service exception while executing " + action.getClass().getName() + ": " + - e.getMessage(), e); + e.getMessage(), e); } throw new ServiceException(e.getMessage()); } catch (RuntimeException e) { if (logger.isLoggable(Level.WARNING)) { logger.log(Level.WARNING, "Unexpected exception while executing " + action.getClass().getName() + ": " + - "" + e.getMessage(), e); + "" + e.getMessage(), e); } throw new ServiceException(e.getMessage()); @@ -178,4 +184,16 @@ private boolean cookieMatch(String cookieSentByRPC) throws ServiceException { return cookieInRequest.equals(cookieSentByRPC); } + + /** + * Recursively removes all stacktraces from a Throwable and its cause + */ + private void removeStacktraces(Throwable e) { + if (e == null) { + return; + } + + e.setStackTrace(new StackTraceElement[]{}); + removeStacktraces(e.getCause()); + } }