/* * #%L * Wisdom-Framework * %% * Copyright (C) 2013 - 2015 Wisdom Framework * %% * 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. * #L% */ package org.wisdom.framework.csrf.unit; import org.junit.Before; import org.junit.Test; import org.wisdom.api.configuration.Configuration; import org.wisdom.api.cookies.Cookie; import org.wisdom.api.crypto.Crypto; import org.wisdom.api.crypto.Hash; import org.wisdom.api.http.*; import org.wisdom.api.router.Route; import org.wisdom.crypto.CryptoServiceSingleton; import org.wisdom.framework.csrf.CSRFServiceImpl; import org.wisdom.framework.csrf.DefaultCSRFErrorHandler; import org.wisdom.framework.csrf.api.CSRFErrorHandler; import org.wisdom.framework.csrf.api.CSRFService; import org.wisdom.test.parents.FakeContext; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.fail; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; public class CSRFServiceImplTest { public static final String SECRET = "JYFVq6:^jrh:KIy:yM5Xb<sH58WW80OLL4_gCL4Ne[PnAJ9QC/Z?LG2dbwoSkiBL"; public static final String CSRF_TOKEN = "csrf_token"; public static final String CSRF_COOKIE = "csrf_cookie"; CSRFServiceImpl service = new CSRFServiceImpl(); @Before public void prepare() { service.crypto = new CryptoServiceSingleton(SECRET, Hash.MD5, 128, Crypto.AES_CBC_ALGORITHM, 20); } @Test public void testRegularTokenGeneration() { service.configuration = mock(Configuration.class); when(service.configuration.getBooleanWithDefault("token.sign", true)).thenReturn(false); FakeContext context = new FakeContext(); String token = service.generateToken(context); assertThat(token).isNotNull(); assertThat(context.request().data().get(CSRFService.TOKEN_KEY)).isNotNull().isEqualTo(token); String token2 = service.generateToken(context); assertThat(token2).isNotNull().isNotEqualTo(token); assertThat(context.request().data().get(CSRFService.TOKEN_KEY)).isNotNull().isEqualTo(token2); } @Test public void testSignedRegularTokenGeneration() { service.configuration = mock(Configuration.class); when(service.configuration.getBooleanWithDefault("token.sign", true)).thenReturn(true); FakeContext context = new FakeContext(); String token = service.generateToken(context); assertThat(token).isNotNull(); assertThat(context.request().data().get(CSRFService.TOKEN_KEY)).isNotNull().isEqualTo(token); String token2 = service.generateToken(context); assertThat(token2).isNotNull().isNotEqualTo(token); assertThat(context.request().data().get(CSRFService.TOKEN_KEY)).isNotNull().isEqualTo(token2); } @Test public void testThatWeAcceptRequestContainingTheTokenInTheQuery() { service.configuration = mock(Configuration.class); when(service.configuration.getWithDefault("token.name", "csrfToken")).thenReturn(CSRF_TOKEN); String token = service.crypto.generateToken(); FakeContext context1 = new FakeContext(); context1.addToSession(CSRF_TOKEN, token).setParameter(CSRF_TOKEN, token) .route(new Route(HttpMethod.GET, "/", null, null)); assertThat(service.isValidRequest(context1)).isTrue(); FakeContext context2 = new FakeContext(); context2.addToSession(CSRF_TOKEN, token).setParameter(CSRF_TOKEN, token) .route(new Route(HttpMethod.POST, "/", null, null)); assertThat(service.isValidRequest(context2)).isTrue(); } @Test public void testThatWeAcceptRequestContainingTheTokenInFormBody() { service.configuration = mock(Configuration.class); when(service.configuration.getWithDefault("token.name", "csrfToken")).thenReturn(CSRF_TOKEN); String token = service.crypto.generateToken(); FakeContext context = new FakeContext(); context.addToSession(CSRF_TOKEN, token).setFormField(CSRF_TOKEN, token).setFormField("key", "value") .setHeader(HeaderNames.CONTENT_TYPE, MimeTypes.FORM) .route(new Route(HttpMethod.POST, "/", null, null)); assertThat(service.isValidRequest(context)).isTrue(); context = new FakeContext(); context.addToSession(CSRF_TOKEN, token).setFormField(CSRF_TOKEN, token).setFormField("key", "value") .setHeader(HeaderNames.CONTENT_TYPE, MimeTypes.MULTIPART) .route(new Route(HttpMethod.POST, "/", null, null)); assertThat(service.isValidRequest(context)).isTrue(); } @Test public void testThatWeAcceptRequestContainingTheTokenInHeader() { service.configuration = mock(Configuration.class); when(service.configuration.getWithDefault("token.name", "csrfToken")).thenReturn(CSRF_TOKEN); String token = service.crypto.generateToken(); FakeContext context = new FakeContext(); context.addToSession(CSRF_TOKEN, token).setHeader(CSRFServiceImpl.CSRF_TOKEN_HEADER, token) .setFormField("key", "value") .setHeader(HeaderNames.CONTENT_TYPE, MimeTypes.FORM) .route(new Route(HttpMethod.POST, "/", null, null)); assertThat(service.isValidRequest(context)).isTrue(); } @Test public void testThatWeAcceptRequestContainingTheNoCheckHeader() { service.configuration = mock(Configuration.class); when(service.configuration.getWithDefault("token.name", "csrfToken")).thenReturn(CSRF_TOKEN); String token = service.crypto.generateToken(); FakeContext context = new FakeContext(); context.setHeader(CSRFServiceImpl.CSRF_TOKEN_HEADER, CSRFServiceImpl.NO_CHECK_HEADER_VALUE) .setFormField("key", "value") .setHeader(HeaderNames.CONTENT_TYPE, MimeTypes.FORM) .route(new Route(HttpMethod.POST, "/", null, null)); assertThat(service.isValidRequest(context)).isTrue(); context = new FakeContext(); context .addToSession(CSRF_TOKEN, token) .setHeader(CSRFServiceImpl.CSRF_TOKEN_HEADER, CSRFServiceImpl.NO_CHECK_HEADER_VALUE) .setFormField("key", "value") .setHeader(HeaderNames.CONTENT_TYPE, MimeTypes.FORM) .route(new Route(HttpMethod.POST, "/", null, null)); assertThat(service.isValidRequest(context)).isTrue(); } @Test public void testThatWeAcceptRequestContainingTheAjaxHeader() { service.configuration = mock(Configuration.class); when(service.configuration.getWithDefault("token.name", "csrfToken")).thenReturn(CSRF_TOKEN); String token = service.crypto.generateToken(); FakeContext context = new FakeContext(); context .addToSession(CSRF_TOKEN, token) .setHeader(CSRFServiceImpl.AJAX_HEADER, "I'm a teapot") .setFormField("key", "value") .setHeader(HeaderNames.CONTENT_TYPE, MimeTypes.FORM) .route(new Route(HttpMethod.POST, "/", null, null)); assertThat(service.isValidRequest(context)).isTrue(); context = new FakeContext(); context .setHeader(CSRFServiceImpl.AJAX_HEADER, "I'm a teapot") .setFormField("key", "value") .setHeader(HeaderNames.CONTENT_TYPE, MimeTypes.FORM) .route(new Route(HttpMethod.POST, "/", null, null)); assertThat(service.isValidRequest(context)).isTrue(); } @Test public void testThatWeRejectRequestContainingABadTokenInFormBody() { service.configuration = mock(Configuration.class); when(service.configuration.getWithDefault("token.name", "csrfToken")).thenReturn(CSRF_TOKEN); String token = service.crypto.generateToken(); FakeContext context = new FakeContext(); context.addToSession(CSRF_TOKEN, token).setFormField(CSRF_TOKEN, "I'm a teapot") .setFormField("key", "value") .setHeader(HeaderNames.CONTENT_TYPE, MimeTypes.FORM) .route(new Route(HttpMethod.POST, "/", null, null)); assertThat(service.isValidRequest(context)).isFalse(); } @Test public void testThatWeRejectRequestContainingABadSignedTokenInFormBody() { service.configuration = mock(Configuration.class); when(service.configuration.getWithDefault("token.name", "csrfToken")).thenReturn(CSRF_TOKEN); when(service.configuration.getBooleanWithDefault("token.sign", true)).thenReturn(true); String token = service.crypto.generateSignedToken(); FakeContext context = new FakeContext(); context.addToSession(CSRF_TOKEN, token).setFormField(CSRF_TOKEN, "I-M-T") .setFormField("key", "value") .setHeader(HeaderNames.CONTENT_TYPE, MimeTypes.FORM) .route(new Route(HttpMethod.POST, "/", null, null)); assertThat(service.isValidRequest(context)).isFalse(); } @Test public void testThatWeRejectRequestContainingTokenInSessionButNoneElsewhere() { service.configuration = mock(Configuration.class); when(service.configuration.getWithDefault("token.name", "csrfToken")).thenReturn(CSRF_TOKEN); String token = service.crypto.generateToken(); FakeContext context = new FakeContext(); context .addToSession(CSRF_TOKEN, token) .setFormField("key", "value") .setHeader(HeaderNames.CONTENT_TYPE, MimeTypes.FORM) .route(new Route(HttpMethod.POST, "/", null, null)); assertThat(service.isValidRequest(context)).isFalse(); } @Test public void testThatWeRejectRequestContainingTokenInTheBodyButNotInTheSession() { service.configuration = mock(Configuration.class); when(service.configuration.getWithDefault("token.name", "csrfToken")).thenReturn(CSRF_TOKEN); String token = service.crypto.generateToken(); FakeContext context = new FakeContext(); context .setFormField(CSRF_TOKEN, token) .setFormField("key", "value") .setHeader(HeaderNames.CONTENT_TYPE, MimeTypes.FORM) .route(new Route(HttpMethod.POST, "/", null, null)); assertThat(service.isValidRequest(context)).isFalse(); } @Test public void testAddTokenToResult() { service.configuration = mock(Configuration.class); when(service.configuration.getWithDefault("token.name", "csrfToken")).thenReturn(CSRF_TOKEN); FakeContext context = new FakeContext(); Result result = service.addTokenToResult(context, "token", Results.ok()); assertThat(context.session().get(CSRF_TOKEN)).isEqualToIgnoringCase("token"); assertThat(result.getStatusCode()).isEqualTo(Status.OK); } @Test public void testAddTokenToResultUsingCookie() { service.configuration = mock(Configuration.class); when(service.configuration.getWithDefault("token.name", "csrfToken")).thenReturn(CSRF_TOKEN); when(service.configuration.get("cookie.name")).thenReturn(CSRF_COOKIE); FakeContext context = new FakeContext(); Result result = service.addTokenToResult(context, "token", Results.ok()); assertThat(context.session().get(CSRF_TOKEN)).isNull(); assertThat(result.getCookie(CSRF_COOKIE).value()).isEqualTo("token"); assertThat(result.getStatusCode()).isEqualTo(Status.OK); } @Test public void testAddTokenToResultUsingExistingCookie() { service.configuration = mock(Configuration.class); when(service.configuration.getWithDefault("token.name", "csrfToken")).thenReturn(CSRF_TOKEN); when(service.configuration.get("cookie.name")).thenReturn(CSRF_COOKIE); FakeContext context = new FakeContext().setCookie(Cookie.cookie(CSRF_COOKIE, "another token").build()); Result result = service.addTokenToResult(context, "token", Results.ok()); assertThat(context.session().get(CSRF_TOKEN)).isNull(); assertThat(result.getCookie(CSRF_COOKIE).value()).isEqualTo("token"); assertThat(result.getStatusCode()).isEqualTo(Status.OK); } @Test public void testThatWeRejectRequestContainingUnsignedTokenInTheBody() { service.configuration = mock(Configuration.class); when(service.configuration.getWithDefault("token.name", "csrfToken")).thenReturn(CSRF_TOKEN); when(service.configuration.getBooleanWithDefault("token.sign", true)).thenReturn(true); // Add a signed token in session and a raw token in the body. String raw = service.crypto.generateToken(); FakeContext context = new FakeContext().addToSession(CSRF_TOKEN, service.crypto.generateSignedToken()); context .setFormField(CSRF_TOKEN, raw) .setFormField("key", "value") .setHeader(HeaderNames.CONTENT_TYPE, MimeTypes.FORM) .route(new Route(HttpMethod.POST, "/", null, null)); assertThat(service.isValidRequest(context)).isFalse(); } @Test public void testThatWeRejectRequestContainingUnsignedTokenInTheSession() { service.configuration = mock(Configuration.class); service.handler = new DefaultCSRFErrorHandler(); when(service.configuration.getWithDefault("token.name", "csrfToken")).thenReturn(CSRF_TOKEN); when(service.configuration.getBooleanWithDefault("token.sign", true)).thenReturn(true); // Add a signed token in the body but not in the session String raw = service.crypto.generateToken(); FakeContext context = new FakeContext().addToSession(CSRF_TOKEN, raw); context .setFormField(CSRF_TOKEN, service.crypto.generateSignedToken()) .setFormField("key", "value") .setHeader(HeaderNames.CONTENT_TYPE, MimeTypes.FORM) .route(new Route(HttpMethod.POST, "/", null, null)); assertThat(service.isValidRequest(context)).isFalse(); Result result = service.clearTokenIfInvalid(context, "Bad token"); assertThat(context.session().get(CSRF_TOKEN)).isNull(); assertThat(result.getStatusCode()).isEqualTo(Status.FORBIDDEN); } @Test public void testThatWeGenerateANewTokenInEachRequest() throws InterruptedException { service.configuration = mock(Configuration.class); when(service.configuration.getWithDefault("token.name", "csrfToken")).thenReturn(CSRF_TOKEN); when(service.configuration.getBooleanWithDefault("token.sign", true)).thenReturn(true); String token = service.crypto.generateSignedToken(); FakeContext context = new FakeContext().addToSession(CSRF_TOKEN, token).setFormField(CSRF_TOKEN, token); context.route(new Route(HttpMethod.POST, "/", null, null)); Thread.sleep(2); String resigned = service.extractTokenFromRequest(context); assertThat(token).isNotEqualTo(resigned); assertThat(service.crypto.compareSignedTokens(token, resigned)).isTrue(); assertThat(service.isValidRequest(context)).isTrue(); } @Test public void testThatWeAcceptRequestContainingTheTokenInTheQueryAndInACookie() { service.configuration = mock(Configuration.class); when(service.configuration.getBooleanWithDefault("token.sign", true)).thenReturn(false); when(service.configuration.getWithDefault("token.name", "csrfToken")).thenReturn(CSRF_TOKEN); when(service.configuration.get("cookie.name")).thenReturn(CSRF_COOKIE); String token = service.crypto.generateToken(); FakeContext context1 = new FakeContext(); context1.setCookie(createCSRFCookie(token)).setParameter(CSRF_TOKEN, token) .route(new Route(HttpMethod.GET, "/", null, null)); assertThat(service.isValidRequest(context1)).isTrue(); FakeContext context2 = new FakeContext(); context2.setCookie(createCSRFCookie(token)).setParameter(CSRF_TOKEN, token) .route(new Route(HttpMethod.POST, "/", null, null)); assertThat(service.isValidRequest(context2)).isTrue(); assertThat(context2.session().get(CSRF_TOKEN)).isNull(); } @Test public void testThatWeAcceptRequestContainingASignedTokenInTheQueryAndInACookie() { service.configuration = mock(Configuration.class); when(service.configuration.getBooleanWithDefault("token.sign", true)).thenReturn(true); when(service.configuration.getWithDefault("token.name", "csrfToken")).thenReturn(CSRF_TOKEN); when(service.configuration.get("cookie.name")).thenReturn(CSRF_COOKIE); String token = service.crypto.generateSignedToken(); FakeContext context1 = new FakeContext(); context1.setCookie(createCSRFCookie(token)).setParameter(CSRF_TOKEN, token) .route(new Route(HttpMethod.GET, "/", null, null)); assertThat(service.isValidRequest(context1)).isTrue(); FakeContext context2 = new FakeContext(); context2.setCookie(createCSRFCookie(token)).setParameter(CSRF_TOKEN, token) .route(new Route(HttpMethod.POST, "/", null, null)); assertThat(service.isValidRequest(context2)).isTrue(); assertThat(context2.session().get(CSRF_TOKEN)).isNull(); } @Test public void testThatWeAcceptRequestContainingTheSignedTokenInFormBodyAndInACookie() { service.configuration = mock(Configuration.class); when(service.configuration.getBooleanWithDefault("token.sign", true)).thenReturn(true); when(service.configuration.getWithDefault("token.name", "csrfToken")).thenReturn(CSRF_TOKEN); String token = service.crypto.generateSignedToken(); FakeContext context = new FakeContext(); context.setCookie(createCSRFCookie(token)).setFormField(CSRF_TOKEN, token).setFormField("key", "value") .setHeader(HeaderNames.CONTENT_TYPE, MimeTypes.FORM) .route(new Route(HttpMethod.POST, "/", null, null)); assertThat(service.isValidRequest(context)).isTrue(); context = new FakeContext(); context.setCookie(createCSRFCookie(token)).setFormField(CSRF_TOKEN, token).setFormField("key", "value") .setHeader(HeaderNames.CONTENT_TYPE, MimeTypes.MULTIPART) .route(new Route(HttpMethod.POST, "/", null, null)); assertThat(service.isValidRequest(context)).isTrue(); } @Test public void testThatWeAcceptRequestContainingTheTokenInFormBodyAndInACookie() { service.configuration = mock(Configuration.class); when(service.configuration.getBooleanWithDefault("token.sign", true)).thenReturn(false); when(service.configuration.getWithDefault("token.name", "csrfToken")).thenReturn(CSRF_TOKEN); String token = service.crypto.generateToken(); FakeContext context = new FakeContext(); context.setCookie(createCSRFCookie(token)).setFormField(CSRF_TOKEN, token).setFormField("key", "value") .setHeader(HeaderNames.CONTENT_TYPE, MimeTypes.FORM) .route(new Route(HttpMethod.POST, "/", null, null)); assertThat(service.isValidRequest(context)).isTrue(); context = new FakeContext(); context.setCookie(createCSRFCookie(token)).setFormField(CSRF_TOKEN, token).setFormField("key", "value") .setHeader(HeaderNames.CONTENT_TYPE, MimeTypes.MULTIPART) .route(new Route(HttpMethod.POST, "/", null, null)); assertThat(service.isValidRequest(context)).isTrue(); } @Test public void testThatWeAcceptRequestContainingTheSignedTokenInHeaderUsingCookie() { service.configuration = mock(Configuration.class); when(service.configuration.getBooleanWithDefault("token.sign", true)).thenReturn(true); when(service.configuration.getWithDefault("token.name", "csrfToken")).thenReturn(CSRF_TOKEN); String token = service.crypto.generateSignedToken(); FakeContext context = new FakeContext(); context.setCookie(createCSRFCookie(token)).setHeader(CSRFServiceImpl.CSRF_TOKEN_HEADER, token) .setFormField("key", "value") .setHeader(HeaderNames.CONTENT_TYPE, MimeTypes.FORM) .route(new Route(HttpMethod.POST, "/", null, null)); assertThat(service.isValidRequest(context)).isTrue(); } @Test public void testThatWeAcceptRequestContainingTheTokenInHeaderUsingCookie() { service.configuration = mock(Configuration.class); when(service.configuration.getBooleanWithDefault("token.sign", true)).thenReturn(false); when(service.configuration.getWithDefault("token.name", "csrfToken")).thenReturn(CSRF_TOKEN); String token = service.crypto.generateToken(); FakeContext context = new FakeContext(); context.setCookie(createCSRFCookie(token)).setHeader(CSRFServiceImpl.CSRF_TOKEN_HEADER, token) .setFormField("key", "value") .setHeader(HeaderNames.CONTENT_TYPE, MimeTypes.FORM) .route(new Route(HttpMethod.POST, "/", null, null)); assertThat(service.isValidRequest(context)).isTrue(); } @Test public void testThatWeRejectRequestContainingSignedTokenInACookieButNoneElsewhere() { service.configuration = mock(Configuration.class); when(service.configuration.getWithDefault("token.name", "csrfToken")).thenReturn(CSRF_TOKEN); when(service.configuration.getBooleanWithDefault("token.sign", true)).thenReturn(true); String token = service.crypto.generateSignedToken(); FakeContext context = new FakeContext(); context .setCookie(createCSRFCookie(token)) .setFormField("key", "value") .setHeader(HeaderNames.CONTENT_TYPE, MimeTypes.FORM) .route(new Route(HttpMethod.POST, "/", null, null)); assertThat(service.isValidRequest(context)).isFalse(); } @Test public void testThatWeRejectRequestContainingTokenInACookieButNoneElsewhere() { service.configuration = mock(Configuration.class); when(service.configuration.getWithDefault("token.name", "csrfToken")).thenReturn(CSRF_TOKEN); when(service.configuration.getBooleanWithDefault("token.sign", true)).thenReturn(false); String token = service.crypto.generateToken(); FakeContext context = new FakeContext(); context .setCookie(createCSRFCookie(token)) .setFormField("key", "value") .setHeader(HeaderNames.CONTENT_TYPE, MimeTypes.FORM) .route(new Route(HttpMethod.POST, "/", null, null)); assertThat(service.isValidRequest(context)).isFalse(); } @Test public void testThatWeRejectRequestContainingSignedTokenInTheBodyButNotInTheCookie() { service.configuration = mock(Configuration.class); when(service.configuration.getWithDefault("token.name", "csrfToken")).thenReturn(CSRF_TOKEN); when(service.configuration.getBooleanWithDefault("token.sign", true)).thenReturn(true); when(service.configuration.get("cookie.name")).thenReturn(CSRF_COOKIE); String token = service.crypto.generateSignedToken(); // 1) No cookie at all FakeContext context = new FakeContext(); context .setFormField(CSRF_TOKEN, token) .setFormField("key", "value") .setHeader(HeaderNames.CONTENT_TYPE, MimeTypes.FORM) .route(new Route(HttpMethod.POST, "/", null, null)); assertThat(service.isValidRequest(context)).isFalse(); // 2) Empty cookie context = new FakeContext(); context .setCookie(createCSRFCookie("")) .setFormField(CSRF_TOKEN, token) .setFormField("key", "value") .setHeader(HeaderNames.CONTENT_TYPE, MimeTypes.FORM) .route(new Route(HttpMethod.POST, "/", null, null)); assertThat(service.isValidRequest(context)).isFalse(); } @Test public void testThatWeRejectRequestContainingTokenInTheBodyButNotInTheCookie() { service.configuration = mock(Configuration.class); when(service.configuration.getWithDefault("token.name", "csrfToken")).thenReturn(CSRF_TOKEN); when(service.configuration.getBooleanWithDefault("token.sign", true)).thenReturn(false); when(service.configuration.get("cookie.name")).thenReturn(CSRF_COOKIE); String token = service.crypto.generateToken(); // 1) No cookie at all FakeContext context = new FakeContext(); context .setFormField(CSRF_TOKEN, token) .setFormField("key", "value") .setHeader(HeaderNames.CONTENT_TYPE, MimeTypes.FORM) .route(new Route(HttpMethod.POST, "/", null, null)); assertThat(service.isValidRequest(context)).isFalse(); // 2) Empty cookie context = new FakeContext(); context .setCookie(createCSRFCookie("")) .setFormField(CSRF_TOKEN, token) .setFormField("key", "value") .setHeader(HeaderNames.CONTENT_TYPE, MimeTypes.FORM) .route(new Route(HttpMethod.POST, "/", null, null)); assertThat(service.isValidRequest(context)).isFalse(); } @Test public void testThatWeAcceptRequestContainingTheNoCheckHeaderUsingCookie() { service.configuration = mock(Configuration.class); when(service.configuration.getWithDefault("token.name", "csrfToken")).thenReturn(CSRF_TOKEN); String token = service.crypto.generateToken(); FakeContext context = new FakeContext(); context.setHeader(CSRFServiceImpl.CSRF_TOKEN_HEADER, CSRFServiceImpl.NO_CHECK_HEADER_VALUE) .setFormField("key", "value") .setHeader(HeaderNames.CONTENT_TYPE, MimeTypes.FORM) .route(new Route(HttpMethod.POST, "/", null, null)); assertThat(service.isValidRequest(context)).isTrue(); context = new FakeContext(); context .addToSession(CSRF_TOKEN, token) .setHeader(CSRFServiceImpl.CSRF_TOKEN_HEADER, CSRFServiceImpl.NO_CHECK_HEADER_VALUE) .setFormField("key", "value") .setHeader(HeaderNames.CONTENT_TYPE, MimeTypes.FORM) .route(new Route(HttpMethod.POST, "/", null, null)); assertThat(service.isValidRequest(context)).isTrue(); } @Test public void testThatWeAcceptRequestContainingTheAjaxHeaderUsingCookie() { service.configuration = mock(Configuration.class); when(service.configuration.getWithDefault("token.name", "csrfToken")).thenReturn(CSRF_TOKEN); String token = service.crypto.generateToken(); FakeContext context = new FakeContext(); context .setCookie(createCSRFCookie(token)) .setHeader(CSRFServiceImpl.AJAX_HEADER, "I'm a teapot") .setFormField("key", "value") .setHeader(HeaderNames.CONTENT_TYPE, MimeTypes.FORM) .route(new Route(HttpMethod.POST, "/", null, null)); assertThat(service.isValidRequest(context)).isTrue(); context = new FakeContext(); context .setHeader(CSRFServiceImpl.AJAX_HEADER, "I'm a teapot") .setFormField("key", "value") .setHeader(HeaderNames.CONTENT_TYPE, MimeTypes.FORM) .route(new Route(HttpMethod.POST, "/", null, null)); assertThat(service.isValidRequest(context)).isTrue(); } @Test public void testThatWeRejectRequestContainingABadTokenInFormBodyUsingACookie() { service.configuration = mock(Configuration.class); when(service.configuration.getWithDefault("token.name", "csrfToken")).thenReturn(CSRF_TOKEN); String token = service.crypto.generateToken(); FakeContext context = new FakeContext(); context.setCookie(createCSRFCookie(token)) .setFormField(CSRF_TOKEN, "I'm a teapot") .setFormField("key", "value") .setHeader(HeaderNames.CONTENT_TYPE, MimeTypes.FORM) .route(new Route(HttpMethod.POST, "/", null, null)); assertThat(service.isValidRequest(context)).isFalse(); } @Test public void testThatWeRejectRequestContainingABadSignedTokenInFormBodyUsingACookie() { service.configuration = mock(Configuration.class); when(service.configuration.getWithDefault("token.name", "csrfToken")).thenReturn(CSRF_TOKEN); when(service.configuration.getBooleanWithDefault("token.sign", true)).thenReturn(true); String token = service.crypto.generateSignedToken(); FakeContext context = new FakeContext(); context.setCookie(createCSRFCookie(token)) .setFormField(CSRF_TOKEN, "I'm a teapot") .setFormField("key", "value") .setHeader(HeaderNames.CONTENT_TYPE, MimeTypes.FORM) .route(new Route(HttpMethod.POST, "/", null, null)); assertThat(service.isValidRequest(context)).isFalse(); } @Test public void testThatWeAcceptRequestContainingTheTokenInFormBodyAndInASignedCookie() { service.configuration = mock(Configuration.class); when(service.configuration.getBooleanWithDefault("token.sign", true)).thenReturn(true); when(service.configuration.getBooleanWithDefault("cookie.secure", true)).thenReturn(true); when(service.configuration.getWithDefault("token.name", "csrfToken")).thenReturn(CSRF_TOKEN); String token = service.crypto.generateSignedToken(); FakeContext context = new FakeContext(); context.setCookie(createSecuredCSRFCookie(token)).setFormField(CSRF_TOKEN, token).setFormField("key", "value") .setHeader(HeaderNames.CONTENT_TYPE, MimeTypes.FORM) .route(new Route(HttpMethod.POST, "/", null, null)); assertThat(service.isValidRequest(context)).isTrue(); context = new FakeContext(); context.setCookie(createSecuredCSRFCookie(token)).setFormField(CSRF_TOKEN, token).setFormField("key", "value") .setHeader(HeaderNames.CONTENT_TYPE, MimeTypes.MULTIPART) .route(new Route(HttpMethod.POST, "/", null, null)); assertThat(service.isValidRequest(context)).isTrue(); } @Test public void testThatTheHandlerCanBeReplaced() { service.configuration = mock(Configuration.class); service.handler = new CSRFErrorHandler() { @Override public Result onError(Context context, String reason) { return Results.unauthorized(reason); } }; when(service.configuration.getWithDefault("token.name", "csrfToken")).thenReturn(CSRF_TOKEN); when(service.configuration.getBooleanWithDefault("token.sign", true)).thenReturn(true); // Add a signed token in the body but not in the session String raw = service.crypto.generateToken(); FakeContext context = new FakeContext().addToSession(CSRF_TOKEN, raw); context .setFormField(CSRF_TOKEN, service.crypto.generateSignedToken()) .setFormField("key", "value") .setHeader(HeaderNames.CONTENT_TYPE, MimeTypes.FORM) .route(new Route(HttpMethod.POST, "/", null, null)); assertThat(service.isValidRequest(context)).isFalse(); Result result = service.clearTokenIfInvalid(context, "Bad token"); assertThat(context.session().get(CSRF_TOKEN)).isNull(); assertThat(result.getStatusCode()).isEqualTo(Status.UNAUTHORIZED); } private Cookie createCSRFCookie(String token) { when(service.configuration.get("cookie.name")).thenReturn(CSRF_COOKIE); return Cookie.cookie(CSRF_COOKIE, token).build(); } private Cookie createSecuredCSRFCookie(String token) { when(service.configuration.get("cookie.name")).thenReturn(CSRF_COOKIE); return Cookie.cookie(CSRF_COOKIE, token).setSecure(true).build(); } }