You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@deltaspike.apache.org by gp...@apache.org on 2014/04/11 11:38:06 UTC
git commit: DELTASPIKE-564 optional double submit prevention
Repository: deltaspike
Updated Branches:
refs/heads/master 4399428cd -> 697db84aa
DELTASPIKE-564 optional double submit prevention
Project: http://git-wip-us.apache.org/repos/asf/deltaspike/repo
Commit: http://git-wip-us.apache.org/repos/asf/deltaspike/commit/697db84a
Tree: http://git-wip-us.apache.org/repos/asf/deltaspike/tree/697db84a
Diff: http://git-wip-us.apache.org/repos/asf/deltaspike/diff/697db84a
Branch: refs/heads/master
Commit: 697db84aaf7a460f6d9d596e7ffa17830e61fcba
Parents: 4399428
Author: gpetracek <gp...@apache.org>
Authored: Fri Apr 11 11:25:08 2014 +0200
Committer: gpetracek <gp...@apache.org>
Committed: Fri Apr 11 11:34:13 2014 +0200
----------------------------------------------------------------------
.../jsf/api/config/JsfModuleConfig.java | 5 ++
.../token/PostRequestTokenComponent.java | 57 ++++++++++++
.../token/RequestTokenHtmlRenderer.java | 84 +++++++++++++++++
.../token/DoubleSubmitAwarePhaseListener.java | 95 ++++++++++++++++++++
.../jsf/impl/token/PostRequestTokenManager.java | 70 +++++++++++++++
.../jsf/impl/token/PostRequestTokenMarker.java | 27 ++++++
.../resources/META-INF/deltaspike.taglib.xml | 7 ++
7 files changed, 345 insertions(+)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/deltaspike/blob/697db84a/deltaspike/modules/jsf/api/src/main/java/org/apache/deltaspike/jsf/api/config/JsfModuleConfig.java
----------------------------------------------------------------------
diff --git a/deltaspike/modules/jsf/api/src/main/java/org/apache/deltaspike/jsf/api/config/JsfModuleConfig.java b/deltaspike/modules/jsf/api/src/main/java/org/apache/deltaspike/jsf/api/config/JsfModuleConfig.java
index e8077d7..c6436a2 100644
--- a/deltaspike/modules/jsf/api/src/main/java/org/apache/deltaspike/jsf/api/config/JsfModuleConfig.java
+++ b/deltaspike/modules/jsf/api/src/main/java/org/apache/deltaspike/jsf/api/config/JsfModuleConfig.java
@@ -128,6 +128,11 @@ public class JsfModuleConfig implements DeltaSpikeConfig
return Default.class;
}
+ public boolean isAllowPostRequestWithoutDoubleSubmitPrevention()
+ {
+ return true;
+ }
+
protected boolean isDelegatedWindowHandlingEnabled()
{
if (ClassUtils.tryToLoadClassForName(CLIENT_WINDOW_CLASS_NAME) == null)
http://git-wip-us.apache.org/repos/asf/deltaspike/blob/697db84a/deltaspike/modules/jsf/impl/src/main/java/org/apache/deltaspike/jsf/impl/component/token/PostRequestTokenComponent.java
----------------------------------------------------------------------
diff --git a/deltaspike/modules/jsf/impl/src/main/java/org/apache/deltaspike/jsf/impl/component/token/PostRequestTokenComponent.java b/deltaspike/modules/jsf/impl/src/main/java/org/apache/deltaspike/jsf/impl/component/token/PostRequestTokenComponent.java
new file mode 100644
index 0000000..0a8bba7
--- /dev/null
+++ b/deltaspike/modules/jsf/impl/src/main/java/org/apache/deltaspike/jsf/impl/component/token/PostRequestTokenComponent.java
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.deltaspike.jsf.impl.component.token;
+
+import org.apache.deltaspike.jsf.impl.token.PostRequestTokenMarker;
+
+import javax.faces.component.FacesComponent;
+import javax.faces.component.UIInput;
+
+
+/**
+ * Component for rendering the post-request-token
+ */
+@FacesComponent(PostRequestTokenComponent.COMPONENT_TYPE)
+public class PostRequestTokenComponent extends UIInput
+{
+ public static final String COMPONENT_TYPE = "org.apache.deltaspike.PostRequestTokenHolder";
+
+ private transient String markedId;
+
+ @Override
+ public String getId()
+ {
+ if (this.markedId == null)
+ {
+ String originalId = super.getId();
+
+ if (originalId.contains(PostRequestTokenMarker.POST_REQUEST_TOKEN_KEY))
+ {
+ this.markedId = originalId;
+ }
+ else
+ {
+ this.markedId = originalId + "_" + PostRequestTokenMarker.POST_REQUEST_TOKEN_KEY;
+ }
+ }
+ return this.markedId;
+ }
+
+ //don't use #restoreState - we couldn't support stateless views,...
+}
http://git-wip-us.apache.org/repos/asf/deltaspike/blob/697db84a/deltaspike/modules/jsf/impl/src/main/java/org/apache/deltaspike/jsf/impl/component/token/RequestTokenHtmlRenderer.java
----------------------------------------------------------------------
diff --git a/deltaspike/modules/jsf/impl/src/main/java/org/apache/deltaspike/jsf/impl/component/token/RequestTokenHtmlRenderer.java b/deltaspike/modules/jsf/impl/src/main/java/org/apache/deltaspike/jsf/impl/component/token/RequestTokenHtmlRenderer.java
new file mode 100644
index 0000000..90ce38f
--- /dev/null
+++ b/deltaspike/modules/jsf/impl/src/main/java/org/apache/deltaspike/jsf/impl/component/token/RequestTokenHtmlRenderer.java
@@ -0,0 +1,84 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.deltaspike.jsf.impl.component.token;
+
+import org.apache.deltaspike.core.api.provider.BeanProvider;
+import org.apache.deltaspike.jsf.impl.token.PostRequestTokenManager;
+
+import javax.faces.component.UIComponent;
+import javax.faces.context.FacesContext;
+import javax.faces.context.ResponseWriter;
+import javax.faces.render.FacesRenderer;
+import javax.faces.render.Renderer;
+import java.io.IOException;
+
+@FacesRenderer(
+ componentFamily = PostRequestTokenComponent.COMPONENT_FAMILY,
+ rendererType = PostRequestTokenComponent.COMPONENT_TYPE)
+public class RequestTokenHtmlRenderer extends Renderer
+{
+ private static final String INPUT_ELEMENT = "input";
+ private static final String TYPE_ATTRIBUTE = "type";
+ private static final String INPUT_TYPE_HIDDEN = "hidden";
+
+ private static final String ID_ATTRIBUTE = "id";
+ private static final String NAME_ATTRIBUTE = "name";
+ private static final String VALUE_ATTRIBUTE = "value";
+
+ private volatile PostRequestTokenManager postRequestTokenManager;
+
+ @Override
+ public void encodeBegin(FacesContext facesContext, UIComponent component) throws IOException
+ {
+ ResponseWriter writer = facesContext.getResponseWriter();
+
+ writer.startElement(INPUT_ELEMENT, component);
+ writer.writeAttribute(TYPE_ATTRIBUTE, INPUT_TYPE_HIDDEN, null);
+
+ String clientId = component.getClientId(facesContext);
+ writer.writeAttribute(ID_ATTRIBUTE, clientId, null);
+ writer.writeAttribute(NAME_ATTRIBUTE, clientId, null);
+
+ String currentPostRequestToken = getPostRequestTokenManager().getCurrentToken();
+ if (currentPostRequestToken != null)
+ {
+ writer.writeAttribute(VALUE_ATTRIBUTE, currentPostRequestToken, VALUE_ATTRIBUTE);
+ }
+
+ writer.endElement(INPUT_ELEMENT);
+ }
+
+ //don't use #decode - we couldn't support DSP for immediate actions
+
+ private PostRequestTokenManager getPostRequestTokenManager()
+ {
+ if (this.postRequestTokenManager == null)
+ {
+ synchronized (this)
+ {
+ if (this.postRequestTokenManager == null)
+ {
+ this.postRequestTokenManager = BeanProvider.getContextualReference(PostRequestTokenManager.class);
+ }
+ }
+ }
+
+ return this.postRequestTokenManager;
+ }
+}
http://git-wip-us.apache.org/repos/asf/deltaspike/blob/697db84a/deltaspike/modules/jsf/impl/src/main/java/org/apache/deltaspike/jsf/impl/token/DoubleSubmitAwarePhaseListener.java
----------------------------------------------------------------------
diff --git a/deltaspike/modules/jsf/impl/src/main/java/org/apache/deltaspike/jsf/impl/token/DoubleSubmitAwarePhaseListener.java b/deltaspike/modules/jsf/impl/src/main/java/org/apache/deltaspike/jsf/impl/token/DoubleSubmitAwarePhaseListener.java
new file mode 100644
index 0000000..0eab5a6
--- /dev/null
+++ b/deltaspike/modules/jsf/impl/src/main/java/org/apache/deltaspike/jsf/impl/token/DoubleSubmitAwarePhaseListener.java
@@ -0,0 +1,95 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.deltaspike.jsf.impl.token;
+
+import org.apache.deltaspike.core.spi.activation.Deactivatable;
+import org.apache.deltaspike.jsf.api.listener.phase.JsfPhaseListener;
+
+import javax.faces.context.FacesContext;
+import javax.faces.event.PhaseEvent;
+import javax.faces.event.PhaseId;
+import javax.faces.event.PhaseListener;
+import javax.inject.Inject;
+import java.util.Map;
+
+//ignore jsf-ajax requests since they have to be queued according to the spec.
+//ignore get-requests since they >shouldn't< change the state (we couldn't support them at all)
+//post-requests don't get pipelined -> no need to sync. them per session
+//browser-window-handling is done implicitly (PostRequestTokenManager is window-scoped)
+@JsfPhaseListener(ordinal = 9000)
+public class DoubleSubmitAwarePhaseListener implements PhaseListener, Deactivatable
+{
+ private static final long serialVersionUID = -4247051429332418226L;
+
+ @Inject
+ private PostRequestTokenManager postRequestTokenManager;
+
+ @Override
+ public void afterPhase(PhaseEvent event)
+ {
+ FacesContext facesContext = event.getFacesContext();
+
+ //only check full POST requests
+ if (facesContext.isPostback() && !facesContext.getPartialViewContext().isAjaxRequest())
+ {
+ String receivedPostRequestToken = facesContext.getExternalContext()
+ .getRequestParameterMap().get(PostRequestTokenMarker.POST_REQUEST_TOKEN_KEY);
+
+ if (receivedPostRequestToken == null)
+ {
+ receivedPostRequestToken = findPostRequestTokenWithPrefix(facesContext);
+ }
+
+ if (!this.postRequestTokenManager.isValidRequest(receivedPostRequestToken))
+ {
+ facesContext.renderResponse();
+ }
+ }
+ }
+
+ @Override
+ public void beforePhase(PhaseEvent event)
+ {
+ //refresh the token in case of GET-requests to avoid that the token is re-used on the next page
+ if (!event.getFacesContext().isPostback())
+ {
+ this.postRequestTokenManager.createNewToken();
+ }
+ }
+
+ @Override
+ public PhaseId getPhaseId()
+ {
+ return PhaseId.RESTORE_VIEW;
+ }
+
+ protected String findPostRequestTokenWithPrefix(FacesContext facesContext)
+ {
+ for (Map.Entry<String, String> parameterEntry :
+ facesContext.getExternalContext().getRequestParameterMap().entrySet())
+ {
+ if (parameterEntry.getKey().endsWith(PostRequestTokenMarker.POST_REQUEST_TOKEN_WITH_PREFIX_KEY) ||
+ parameterEntry.getKey().endsWith(PostRequestTokenMarker.POST_REQUEST_TOKEN_WITH_MANUAL_PREFIX_KEY))
+ {
+ return parameterEntry.getValue();
+ }
+ }
+ return null;
+ }
+}
http://git-wip-us.apache.org/repos/asf/deltaspike/blob/697db84a/deltaspike/modules/jsf/impl/src/main/java/org/apache/deltaspike/jsf/impl/token/PostRequestTokenManager.java
----------------------------------------------------------------------
diff --git a/deltaspike/modules/jsf/impl/src/main/java/org/apache/deltaspike/jsf/impl/token/PostRequestTokenManager.java b/deltaspike/modules/jsf/impl/src/main/java/org/apache/deltaspike/jsf/impl/token/PostRequestTokenManager.java
new file mode 100644
index 0000000..d9f9e9b
--- /dev/null
+++ b/deltaspike/modules/jsf/impl/src/main/java/org/apache/deltaspike/jsf/impl/token/PostRequestTokenManager.java
@@ -0,0 +1,70 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.deltaspike.jsf.impl.token;
+
+import org.apache.deltaspike.core.api.scope.WindowScoped;
+import org.apache.deltaspike.jsf.api.config.JsfModuleConfig;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+import java.io.Serializable;
+import java.util.UUID;
+
+@WindowScoped
+@Named("dsPostRequestToken")
+public class PostRequestTokenManager implements Serializable
+{
+ private static final long serialVersionUID = 5387627547198129897L;
+
+ private volatile String currentToken;
+
+ private boolean allowPostRequestWithoutDoubleSubmitPrevention = true;
+
+ protected PostRequestTokenManager()
+ {
+ }
+
+ @Inject
+ public PostRequestTokenManager(JsfModuleConfig config)
+ {
+ this.allowPostRequestWithoutDoubleSubmitPrevention = config.isAllowPostRequestWithoutDoubleSubmitPrevention();
+ }
+
+ public void createNewToken()
+ {
+ this.currentToken = UUID.randomUUID().toString().replace("-", "");
+ }
+
+ public synchronized boolean isValidRequest(String token)
+ {
+ if (token == null)
+ {
+ return this.allowPostRequestWithoutDoubleSubmitPrevention;
+ }
+ String previousToken = this.currentToken;
+ createNewToken();
+
+ return token.equals(previousToken);
+ }
+
+ public String getCurrentToken()
+ {
+ return this.currentToken;
+ }
+}
http://git-wip-us.apache.org/repos/asf/deltaspike/blob/697db84a/deltaspike/modules/jsf/impl/src/main/java/org/apache/deltaspike/jsf/impl/token/PostRequestTokenMarker.java
----------------------------------------------------------------------
diff --git a/deltaspike/modules/jsf/impl/src/main/java/org/apache/deltaspike/jsf/impl/token/PostRequestTokenMarker.java b/deltaspike/modules/jsf/impl/src/main/java/org/apache/deltaspike/jsf/impl/token/PostRequestTokenMarker.java
new file mode 100644
index 0000000..4eb3109
--- /dev/null
+++ b/deltaspike/modules/jsf/impl/src/main/java/org/apache/deltaspike/jsf/impl/token/PostRequestTokenMarker.java
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.deltaspike.jsf.impl.token;
+
+public interface PostRequestTokenMarker
+{
+ String POST_REQUEST_TOKEN_KEY = "dsprt";
+
+ String POST_REQUEST_TOKEN_WITH_PREFIX_KEY = ":" + POST_REQUEST_TOKEN_KEY;
+ String POST_REQUEST_TOKEN_WITH_MANUAL_PREFIX_KEY = "_" + POST_REQUEST_TOKEN_KEY;
+}
http://git-wip-us.apache.org/repos/asf/deltaspike/blob/697db84a/deltaspike/modules/jsf/impl/src/main/resources/META-INF/deltaspike.taglib.xml
----------------------------------------------------------------------
diff --git a/deltaspike/modules/jsf/impl/src/main/resources/META-INF/deltaspike.taglib.xml b/deltaspike/modules/jsf/impl/src/main/resources/META-INF/deltaspike.taglib.xml
index b1cfc44..68cfe58 100644
--- a/deltaspike/modules/jsf/impl/src/main/resources/META-INF/deltaspike.taglib.xml
+++ b/deltaspike/modules/jsf/impl/src/main/resources/META-INF/deltaspike.taglib.xml
@@ -37,4 +37,11 @@
<renderer-type>org.apache.deltaspike.DisableClientWindow</renderer-type>
</component>
</tag>
+ <tag>
+ <tag-name>preventDoubleSubmit</tag-name>
+ <component>
+ <component-type>org.apache.deltaspike.PostRequestTokenHolder</component-type>
+ <renderer-type>org.apache.deltaspike.PostRequestTokenHolder</renderer-type>
+ </component>
+ </tag>
</facelet-taglib>