Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

fix for GRAILS-5921 "Enhance withForm to allow for forms on multiple …

…tabs"
  • Loading branch information...
commit 5a9fd652fae7100cae38b32ea53c990d02bc37bd 1 parent e4013a7
@graemerocher graemerocher authored
View
5 grails-plugin-controllers/src/main/groovy/org/codehaus/groovy/grails/web/metaclass/WithFormMethod.groovy
@@ -89,8 +89,11 @@ class WithFormMethod {
final request = webRequest.getCurrentRequest()
SynchronizerTokensHolder tokensHolderInSession = request.getSession(false)?.getAttribute(SynchronizerTokensHolder.HOLDER)
String urlInRequest = webRequest.params[SynchronizerTokensHolder.TOKEN_URI]
+ String tokenInRequest = webRequest.params[SynchronizerTokensHolder.TOKEN_KEY]
- tokensHolderInSession.resetToken(urlInRequest)
+ if (urlInRequest && tokenInRequest) {
+ tokensHolderInSession.resetToken(urlInRequest, tokenInRequest)
+ }
if (tokensHolderInSession.isEmpty()) request.getSession(false)?.removeAttribute(SynchronizerTokensHolder.HOLDER)
}
}
View
43 grails-test-suite-uber/src/test/groovy/org/codehaus/groovy/grails/web/metaclass/WithFormMethodTests.groovy
@@ -242,4 +242,47 @@ class WithFormMethodTests extends GroovyTestCase {
assertEquals "bar", result2.foo
}
+ void testHandleSubmitOfTwoFormsWithSameURL() {
+ def withForm = new WithFormMethod()
+ def url1 = "http://grails.org/submit"
+ def url2 = "http://grails.org/submit"
+
+ SynchronizerTokensHolder tokensHolder = new SynchronizerTokensHolder()
+ def token1 = tokensHolder.generateToken(url1)
+ def token2 = tokensHolder.generateToken(url2)
+
+ def request1 = GrailsWebUtil.bindMockWebRequest()
+ request1.session.setAttribute(SynchronizerTokensHolder.HOLDER,tokensHolder)
+ request1.currentRequest.addParameter(SynchronizerTokensHolder.TOKEN_URI,url1)
+ request1.currentRequest.addParameter(SynchronizerTokensHolder.TOKEN_KEY,token1)
+
+ def result1 = withForm.withForm(request1) {
+ return [foo:"bar"]
+ }.invalidToken {
+ throw new GrailsRuntimeException("invalid token")
+ }
+
+ assertEquals "bar", result1.foo
+
+ def request2 = GrailsWebUtil.bindMockWebRequest()
+ request2.session.setAttribute(SynchronizerTokensHolder.HOLDER,tokensHolder)
+ request2.currentRequest.addParameter(SynchronizerTokensHolder.TOKEN_URI,url2)
+ request2.currentRequest.addParameter(SynchronizerTokensHolder.TOKEN_KEY,token2)
+
+ def result2 = withForm.withForm(request2) {
+ return [foo:"bar"]
+ }.invalidToken {
+ throw new GrailsRuntimeException("invalid token")
+ }
+
+ assertEquals "bar", result2.foo
+
+ shouldFail(GrailsRuntimeException) {
+ withForm.withForm(request2) {
+ return [foo:"bar"]
+ }.invalidToken {
+ throw new GrailsRuntimeException("invalid token")
+ }
+ }
+ }
}
View
25 grails-web/src/main/groovy/org/codehaus/groovy/grails/web/servlet/mvc/SynchronizerTokensHolder.groovy
@@ -15,6 +15,7 @@
package org.codehaus.groovy.grails.web.servlet.mvc
import javax.servlet.http.HttpSession
+import java.util.concurrent.CopyOnWriteArraySet
/**
* A token used to handle double-submits.
@@ -29,28 +30,38 @@ class SynchronizerTokensHolder implements Serializable {
public static final String TOKEN_KEY = "org.codehaus.groovy.grails.SYNCHRONIZER_TOKEN"
public static final String TOKEN_URI = "org.codehaus.groovy.grails.SYNCHRONIZER_URI"
- Map<String, UUID> currentTokens;
+ Map<String, Set<UUID>> currentTokens= [:].withDefault { new CopyOnWriteArraySet<UUID>() };
SynchronizerTokensHolder() {
- // generateToken(url)
- currentTokens = new HashMap<String, UUID>();
}
boolean isValid(String url, String token) {
- currentTokens[url]?.equals(UUID.fromString(token))
+ final uuid = UUID.fromString(token)
+ currentTokens[url]?.contains(uuid)
}
String generateToken(String url) {
- currentTokens[url] = UUID.randomUUID()
- return currentTokens[url]
+ final uuid = UUID.randomUUID()
+ currentTokens[url].add(uuid)
+ return uuid
}
void resetToken(String url) {
currentTokens.remove(url)
}
+ void resetToken(String url, String token) {
+ if (url && token) {
+ final set = currentTokens[url]
+ set.remove(UUID.fromString(token))
+ if (set.isEmpty()) {
+ currentTokens.remove(url)
+ }
+ }
+ }
+
boolean isEmpty() {
- return currentTokens.isEmpty()
+ return currentTokens.isEmpty() || currentTokens.every { String url, Set<UUID> uuids -> uuids.isEmpty() }
}
static SynchronizerTokensHolder store(HttpSession session) {
Please sign in to comment.
Something went wrong with that request. Please try again.