Skip to content

Commit

Permalink
feature: Add failure handler for cookie verification
Browse files Browse the repository at this point in the history
This feature allows to customize the behaviour of what should be done in
case the cookie could not be verified.

By default the cookie is silently ignored, as before.

Closes #1
  • Loading branch information
mvitz committed Jul 11, 2018
1 parent 0472318 commit c5e8562
Show file tree
Hide file tree
Showing 5 changed files with 128 additions and 4 deletions.
10 changes: 10 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,11 @@
<artifactId>assertj-core</artifactId>
<version>3.10.0</version>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>2.19.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson</groupId>
<artifactId>jackson-bom</artifactId>
Expand Down Expand Up @@ -145,6 +150,11 @@
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
package com.innoq.spring.cookie.flash;

import com.innoq.spring.cookie.flash.codec.FlashMapListCodec;
import com.innoq.spring.cookie.flash.verification.CookieVerificationFailureHandler;
import com.innoq.spring.cookie.security.CookieValueSigner;
import org.springframework.util.Assert;
import org.springframework.web.servlet.FlashMap;
Expand All @@ -37,6 +38,8 @@ public final class CookieFlashMapManager extends AbstractFlashMapManager {
private final FlashMapListCodec codec;
private final CookieValueSigner signer;
private final String cookieName;
private CookieVerificationFailureHandler verificationFailureHandler =
CookieVerificationFailureHandler.ignoreFailures();

public CookieFlashMapManager(FlashMapListCodec codec,
CookieValueSigner signer) {
Expand All @@ -53,6 +56,12 @@ public CookieFlashMapManager(FlashMapListCodec codec,
this.cookieName = cookieName;
}

public void setVerificationFailureHandler(
CookieVerificationFailureHandler verificationFailureHandler) {
Assert.notNull(verificationFailureHandler, "CookieVerificationFailureHandler must not be null");
this.verificationFailureHandler = verificationFailureHandler;
}

@Override
protected List<FlashMap> retrieveFlashMaps(HttpServletRequest request) {
final Cookie cookie = getCookie(request, cookieName);
Expand Down Expand Up @@ -88,16 +97,14 @@ protected Object getFlashMapsMutex(HttpServletRequest request) {
private List<FlashMap> decode(String value) {
final String[] signatureAndPayload = reverse(value).split("--", 2);
if (signatureAndPayload.length != 2) {
// TODO logging
return null;
return verificationFailureHandler.onInvalidValue(value);
}

final String signature = reverse(signatureAndPayload[0]);
final String payload = reverse(signatureAndPayload[1]);

if (!isVerified(payload, signature)) {
// TODO logging
return null;
return verificationFailureHandler.onInvalidSignature(payload, signature);
}

return codec.decode(payload);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/**
* Copyright 2018 innoQ Deutschland GmbH
*
* 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.innoq.spring.cookie.flash.verification;

import org.springframework.web.servlet.FlashMap;

import java.util.List;

public interface CookieVerificationFailureHandler {

List<FlashMap> onInvalidValue(String value);

List<FlashMap> onInvalidSignature(String payload, String signature);

static CookieVerificationFailureHandler ignoreFailures() {
return IgnoreCookieVerificationFailureHandler.INSTANCE;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/**
* Copyright 2018 innoQ Deutschland GmbH
*
* 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.innoq.spring.cookie.flash.verification;

import org.springframework.web.servlet.FlashMap;

import java.util.List;

enum IgnoreCookieVerificationFailureHandler
implements CookieVerificationFailureHandler {
INSTANCE;

@Override
public List<FlashMap> onInvalidValue(String value) {
return null;
}

@Override
public List<FlashMap> onInvalidSignature(String payload, String signature) {
return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
package com.innoq.spring.cookie.flash;

import com.innoq.spring.cookie.flash.codec.jackson.JacksonFlashMapListCodec;
import com.innoq.spring.cookie.flash.verification.CookieVerificationFailureHandler;
import com.innoq.spring.cookie.security.CookieValueSigner;
import org.junit.jupiter.api.Test;
import org.springframework.mock.web.MockHttpServletRequest;
Expand All @@ -30,6 +31,9 @@
import static java.util.Collections.emptyList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.entry;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

class CookieFlashMapManagerTest {

Expand Down Expand Up @@ -65,6 +69,43 @@ void retrieveFlashMaps_withValidCookie_returnsFlashMaps() {
assertThat(flashMap.getTargetRequestPath()).isEqualTo("/foo");
}

@Test
void retrieveFlashMaps_withInvalidCookieValue_callsGivenVerificationFailureHandlerAndUsesItsReturnValue() {
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/");
request.setCookies(new Cookie("flash", "ABCDEF"));

CookieVerificationFailureHandler verificationFailureHandler =
mock(CookieVerificationFailureHandler.class);
when(verificationFailureHandler.onInvalidValue("ABCDEF")).thenReturn(null);

sut.setVerificationFailureHandler(verificationFailureHandler);

List<FlashMap> flashMaps = sut.retrieveFlashMaps(request);

assertThat(flashMaps).isNull();
verify(verificationFailureHandler).onInvalidValue("ABCDEF");
}

@Test
void retrieveFlashMaps_withInvalidCookieValueSignatur_callsGivenVerificationFailureHandlerAndUsesItsReturnValue() {
String cookieValue = "abcd--efgh";

MockHttpServletRequest request = new MockHttpServletRequest("GET", "/");
request.setCookies(new Cookie("flash", cookieValue));

CookieVerificationFailureHandler verificationFailureHandler =
mock(CookieVerificationFailureHandler.class);
when(verificationFailureHandler.onInvalidSignature("abcd", "efgh"))
.thenReturn(null);

sut.setVerificationFailureHandler(verificationFailureHandler);

List<FlashMap> flashMaps = sut.retrieveFlashMaps(request);

assertThat(flashMaps).isNull();
verify(verificationFailureHandler).onInvalidSignature("abcd", "efgh");
}

@Test
void updateFlashMaps_withSingleFlashMap_writesCookie() {
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/");
Expand Down

0 comments on commit c5e8562

Please sign in to comment.