Selaa lähdekoodia

Add reactive CsrfRequestDataValueProcessor

Fixes gh-4762
Rob Winch 7 vuotta sitten
vanhempi
commit
676020321e

+ 76 - 0
web/src/main/java/org/springframework/security/web/reactive/result/view/CsrfRequestDataValueProcessor.java

@@ -0,0 +1,76 @@
+/*
+ * Copyright 2002-2017 the original author or authors.
+ *
+ * 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 org.springframework.security.web.reactive.result.view;
+
+import org.springframework.lang.NonNull;
+import org.springframework.security.web.server.csrf.CsrfToken;
+import org.springframework.web.reactive.result.view.RequestDataValueProcessor;
+import org.springframework.web.server.ServerWebExchange;
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.regex.Pattern;
+
+/**
+ * @author Rob Winch
+ * @since 5.0
+ */
+public class CsrfRequestDataValueProcessor implements RequestDataValueProcessor {
+
+	private static final Pattern DISABLE_CSRF_TOKEN_PATTERN = Pattern
+		.compile("(?i)^(GET|HEAD|TRACE|OPTIONS)$");
+
+	private static final String DISABLE_CSRF_TOKEN_ATTR = "DISABLE_CSRF_TOKEN_ATTR";
+
+	@Override
+	public String processAction(ServerWebExchange exchange, String action,
+		String httpMethod) {
+		if (httpMethod != null && DISABLE_CSRF_TOKEN_PATTERN.matcher(httpMethod).matches()) {
+			exchange.getAttributes().put(DISABLE_CSRF_TOKEN_ATTR, Boolean.TRUE);
+		}
+		else {
+			exchange.getAttributes().remove(DISABLE_CSRF_TOKEN_ATTR);
+		}
+		return action;
+	}
+
+	@Override
+	public String processFormFieldValue(ServerWebExchange exchange,
+		String name, String value, String type) {
+		return value;
+	}
+
+	@NonNull
+	@Override
+	public Map<String, String> getExtraHiddenFields(
+		ServerWebExchange exchange) {
+		if (Boolean.TRUE.equals(exchange.getAttribute(DISABLE_CSRF_TOKEN_ATTR))) {
+			exchange.getAttributes().remove(DISABLE_CSRF_TOKEN_ATTR);
+			return Collections.emptyMap();
+		}
+		CsrfToken token = exchange.getAttribute(CsrfToken.class.getName());
+		if(token == null) {
+			return Collections.emptyMap();
+		}
+		return Collections.singletonMap(token.getParameterName(), token.getToken());
+	}
+
+	@Override
+	public String processUrl(ServerWebExchange exchange, String url) {
+		return url;
+	}
+}

+ 136 - 0
web/src/test/java/org/springframework/security/web/reactive/result/view/CsrfRequestDataValueProcessorTests.java

@@ -0,0 +1,136 @@
+/*
+ * Copyright 2002-2017 the original author or authors.
+ *
+ * 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 org.springframework.security.web.reactive.result.view;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.springframework.http.HttpMethod;
+import org.springframework.mock.http.server.reactive.MockServerHttpRequest;
+import org.springframework.mock.web.server.MockServerWebExchange;
+import org.springframework.security.web.server.csrf.CsrfToken;
+import org.springframework.security.web.server.csrf.DefaultCsrfToken;
+import org.springframework.util.ReflectionUtils;
+
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.assertj.core.api.Assertions.*;
+
+/**
+ * @author Rob Winch
+ * @since 5.0
+ */
+public class CsrfRequestDataValueProcessorTests {
+	private MockServerWebExchange exchange = exchange(HttpMethod.GET);
+
+	private CsrfRequestDataValueProcessor processor = new CsrfRequestDataValueProcessor();
+
+	private CsrfToken token = new DefaultCsrfToken("1", "a", "b");
+	private Map<String, String> expected = new HashMap<String, String>();
+
+	@Before
+	public void setup() {
+		this.expected.put(this.token.getParameterName(), this.token.getToken());
+		this.exchange.getAttributes().put(CsrfToken.class.getName(), this.token);
+	}
+
+	@Test
+	public void assertAllMethodsDeclared() {
+		Method[] expectedMethods = ReflectionUtils
+			.getAllDeclaredMethods(CsrfRequestDataValueProcessor.class);
+		for (Method expected : expectedMethods) {
+			assertThat(
+				ReflectionUtils.findMethod(
+					CsrfRequestDataValueProcessor.class,
+					expected.getName(), expected.getParameterTypes())).as(
+				"Expected to find " + expected + " defined on "
+					+ CsrfRequestDataValueProcessor.class).isNotNull();
+		}
+	}
+
+	@Test
+	public void getExtraHiddenFieldsNoCsrfToken() {
+		this.exchange.getAttributes().clear();
+		assertThat(this.processor.getExtraHiddenFields(this.exchange)).isEmpty();
+	}
+
+	@Test
+	public void getExtraHiddenFieldsHasCsrfTokenNoMethodSet() {
+		assertThat(this.processor.getExtraHiddenFields(this.exchange)).isEqualTo(this.expected);
+	}
+
+	@Test
+	public void getExtraHiddenFieldsHasCsrfToken_GET() {
+		this.processor.processAction(this.exchange, "action", "GET");
+		assertThat(this.processor.getExtraHiddenFields(this.exchange)).isEmpty();
+	}
+
+	@Test
+	public void getExtraHiddenFieldsHasCsrfToken_get() {
+		this.processor.processAction(this.exchange, "action", "get");
+		assertThat(this.processor.getExtraHiddenFields(this.exchange)).isEmpty();
+	}
+
+	@Test
+	public void getExtraHiddenFieldsHasCsrfToken_POST() {
+		this.processor.processAction(this.exchange, "action", "POST");
+		assertThat(this.processor.getExtraHiddenFields(this.exchange)).isEqualTo(
+			this.expected);
+	}
+
+	@Test
+	public void getExtraHiddenFieldsHasCsrfToken_post() {
+		this.processor.processAction(this.exchange, "action", "post");
+		assertThat(this.processor.getExtraHiddenFields(this.exchange)).isEqualTo(
+			this.expected);
+	}
+
+	@Test
+	public void processActionWithMethodArg() {
+		String action = "action";
+		assertThat(this.processor.processAction(this.exchange, action, null)).isEqualTo(action);
+	}
+
+	@Test
+	public void processFormFieldValue() {
+		String value = "action";
+		assertThat(this.processor.processFormFieldValue(this.exchange, "name", value, "hidden"))
+			.isEqualTo(value);
+	}
+
+	@Test
+	public void processUrl() {
+		String url = "url";
+		assertThat(this.processor.processUrl(this.exchange, url)).isEqualTo(url);
+	}
+
+	@Test
+	public void createGetExtraHiddenFieldsHasCsrfToken() {
+		CsrfToken token = new DefaultCsrfToken("1", "a", "b");
+		this.exchange.getAttributes().put(CsrfToken.class.getName(), token);
+		Map<String, String> expected = new HashMap<String, String>();
+		expected.put(token.getParameterName(), token.getToken());
+
+		CsrfRequestDataValueProcessor processor = new CsrfRequestDataValueProcessor();
+		assertThat(this.processor.getExtraHiddenFields(this.exchange)).isEqualTo(expected);
+	}
+
+	private MockServerWebExchange exchange(HttpMethod method) {
+		return MockServerWebExchange.from(MockServerHttpRequest.method(HttpMethod.GET, "/"));
+	}
+}