Permalink
Browse files

Add a new concept of "signer" in order to delegate the process of coo…

…kie signing, and cookie signature check.

The cookie filter will no more do this work, but delegates signing and signature verification to its injected signer.

Create a DefaultCookieSigner component which use the existing SignatureKey in order to stay compatible with previous releases.
The DefaultCookieSigner get the signature key by dependency injection and use the HMAC-SHA1 algorithm (defined in Crypto class) to sign the cookie.
  • Loading branch information...
a-peyrard committed Mar 1, 2014
1 parent 4773b9e commit 547f50e7c7b123d2ed3c640497efe22d5eaffbb7
@@ -0,0 +1,33 @@
+package restx.security;
+
+import javax.inject.Named;
+
+import com.google.common.base.Optional;
+
+import restx.common.Crypto;
+import restx.factory.Component;
+
+/**
+ * Default cookie signer, using HMAC-SHA1 algorithm to sign the cookie.
+ *
+ * @author apeyrard
+ */
+@Component
+@Named(RestxSessionCookieFilter.COOKIE_SIGNER_NAME)
+public class DefaultCookieSigner implements Signer {
+ private final SignatureKey signatureKey;
+
+ public DefaultCookieSigner(Optional<SignatureKey> signatureKey) {
+ this.signatureKey = signatureKey.or(SignatureKey.DEFAULT);
+ }
+
+ @Override
+ public String sign(String cookie) {
+ return Crypto.sign(cookie, signatureKey.getKey());
+ }
+
+ @Override
+ public boolean verify(String cookie, String signedCookie) {
+ return sign(cookie).equals(signedCookie);
+ }
+}
@@ -1,27 +1,39 @@
package restx.security;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.inject.Named;
+
+import org.joda.time.DateTime;
+import org.joda.time.Duration;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.base.Optional;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
-import org.joda.time.DateTime;
-import org.joda.time.Duration;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import restx.*;
-import restx.common.Crypto;
+
+import restx.AbstractRouteLifecycleListener;
+import restx.RestxContext;
+import restx.RestxFilter;
+import restx.RestxHandler;
+import restx.RestxHandlerMatch;
+import restx.RestxRequest;
+import restx.RestxRequestMatch;
+import restx.RestxResponse;
+import restx.RouteLifecycleListener;
+import restx.StdRestxRequestMatch;
+import restx.WebException;
import restx.factory.Component;
import restx.factory.Name;
import restx.http.HttpStatus;
import restx.jackson.FrontObjectMapperFactory;
-import javax.inject.Named;
-import java.io.IOException;
-import java.util.HashMap;
-import java.util.Map;
-
/**
* User: xavierhanin
* Date: 2/8/13
@@ -30,31 +42,33 @@
@Component(priority = -200)
public class RestxSessionCookieFilter implements RestxFilter, RestxHandler {
public static final Name<RestxSessionCookieFilter> NAME = Name.of(RestxSessionCookieFilter.class, "RestxSessionCookieFilter");
+ public static final String COOKIE_SIGNER_NAME = "CookieSigner";
private static final String EXPIRES = "_expires";
private final static Logger logger = LoggerFactory.getLogger(RestxSessionCookieFilter.class);
private final RestxSession.Definition sessionDefinition;
private final ObjectMapper mapper;
- private final SignatureKey signatureKey;
- private final RestxSessionCookieDescriptor restxSessionCookieDescriptor;
+ private final Signer signer;
+ private final RestxSessionCookieDescriptor restxSessionCookieDescriptor;
private final RestxSession emptySession;
- public RestxSessionCookieFilter(
- RestxSession.Definition sessionDefinition,
- @Named(FrontObjectMapperFactory.MAPPER_NAME) ObjectMapper mapper,
- Optional<SignatureKey> signatureKey,
- RestxSessionCookieDescriptor restxSessionCookieDescriptor) {
- this.sessionDefinition = sessionDefinition;
- this.mapper = mapper;
- this.signatureKey = signatureKey.or(SignatureKey.DEFAULT);
- this.restxSessionCookieDescriptor = restxSessionCookieDescriptor;
- this.emptySession = new RestxSession(sessionDefinition, ImmutableMap.<String,String>of(),
- Optional.<RestxPrincipal>absent(), Duration.ZERO);
- }
+ public RestxSessionCookieFilter(
+ RestxSession.Definition sessionDefinition,
+ @Named(FrontObjectMapperFactory.MAPPER_NAME) ObjectMapper mapper,
+ @Named(COOKIE_SIGNER_NAME) Signer signer,
+ RestxSessionCookieDescriptor restxSessionCookieDescriptor) {
- @Override
+ this.sessionDefinition = sessionDefinition;
+ this.mapper = mapper;
+ this.signer = signer;
+ this.restxSessionCookieDescriptor = restxSessionCookieDescriptor;
+ this.emptySession = new RestxSession(sessionDefinition, ImmutableMap.<String, String>of(),
+ Optional.<RestxPrincipal>absent(), Duration.ZERO);
+ }
+
+ @Override
public Optional<RestxHandlerMatch> match(RestxRequest req) {
return Optional.of(new RestxHandlerMatch(
new StdRestxRequestMatch("*", req.getRestxPath()),
@@ -96,8 +110,8 @@ public RestxSession buildContextFromRequest(RestxRequest req) throws IOException
return emptySession;
} else {
String sig = req.getCookieValue(restxSessionCookieDescriptor.getCookieSignatureName()).or("");
- if (!Crypto.sign(cookie, signatureKey.getKey()).equals(sig)) {
- logger.warn("invalid restx session signature. session was: {}. Ignoring session cookie.", cookie);
+ if (!signer.verify(cookie, sig)) {
+ logger.warn("invalid restx session signature. session was: {}. Ignoring session cookie.", cookie);
return emptySession;
}
Map<String, String> entries = readEntries(cookie);
@@ -159,8 +173,8 @@ private void updateSessionInClient(RestxResponse resp, RestxSession session) {
map.put(EXPIRES, DateTime.now().plusDays(30).toString());
String sessionJson = mapper.writeValueAsString(map);
return ImmutableMap.of(restxSessionCookieDescriptor.getCookieName(), sessionJson,
- restxSessionCookieDescriptor.getCookieSignatureName(), Crypto.sign(sessionJson, signatureKey.getKey()));
- }
+ restxSessionCookieDescriptor.getCookieSignatureName(), signer.sign(sessionJson));
+ }
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
@@ -0,0 +1,24 @@
+package restx.security;
+
+/**
+ * Permits to sign and verify messages.
+ *
+ * @author apeyrard
+ */
+public interface Signer {
+
+ /**
+ * Sign the specified message.
+ * @param message The message to sign.
+ * @return The signed message.
+ */
+ String sign(String message);
+
+ /**
+ * Verify if the specified message correspond to the signed one.
+ * @param message The message to verify.
+ * @param signedMessage The signed message.
+ * @return True if the message is corresponding to the signed message, false otherwise.
+ */
+ boolean verify(String message, String signedMessage);
+}
@@ -1,12 +1,11 @@
package restx.specs;
-import com.google.common.base.Optional;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import restx.security.SignatureKey;
-import restx.common.Crypto;
+
import restx.factory.Component;
import restx.security.RestxSessionCookieDescriptor;
+import restx.security.Signer;
/**
* @author fcamblor
@@ -17,11 +16,11 @@
private static final Logger logger = LoggerFactory.getLogger(WhenRestxSessionHeaderLoader.class);
private final RestxSessionCookieDescriptor restxSessionCookieDescriptor;
- private final SignatureKey signature;
+ private final Signer signer;
- public WhenRestxSessionHeaderLoader(RestxSessionCookieDescriptor restxSessionCookieDescriptor, Optional<SignatureKey> signature) {
+ public WhenRestxSessionHeaderLoader(RestxSessionCookieDescriptor restxSessionCookieDescriptor, Signer signer) {
this.restxSessionCookieDescriptor = restxSessionCookieDescriptor;
- this.signature = signature.or(SignatureKey.DEFAULT);
+ this.signer = signer;
}
@Override
@@ -39,6 +38,6 @@ public void loadHeader(String headerValue, WhenHttpRequest.Builder whenHttpReque
}
whenHttpRequestBuilder.addCookie(restxSessionCookieDescriptor.getCookieName(), sessionContent);
- whenHttpRequestBuilder.addCookie(restxSessionCookieDescriptor.getCookieSignatureName(), Crypto.sign(sessionContent, signature.getKey()));
+ whenHttpRequestBuilder.addCookie(restxSessionCookieDescriptor.getCookieSignatureName(), signer.sign(sessionContent));
}
}
@@ -1,18 +1,18 @@
package restx.tests;
+import static restx.RestxMainRouterFactory.Blade;
+
+import java.util.Map;
+
+import org.joda.time.DateTime;
+
import com.github.kevinsawicki.http.HttpRequest;
import com.google.common.collect.ImmutableMap;
-import org.joda.time.DateTime;
-import restx.RestxMainRouterFactory;
-import restx.common.Crypto;
+
import restx.common.UUIDGenerator;
import restx.factory.Factory;
import restx.security.RestxSessionCookieDescriptor;
-import restx.security.SignatureKey;
-
-import java.util.Map;
-
-import static restx.RestxMainRouterFactory.Blade;
+import restx.security.Signer;
/**
* HttpTestClient is a helper to create com.github.kevinsawicki.http.HttpRequest
@@ -43,15 +43,15 @@ private HttpTestClient(String baseUrl, String principal, ImmutableMap<String, St
public HttpTestClient authenticatedAs(String principal) {
Factory factory = Factory.newInstance();
RestxSessionCookieDescriptor restxSessionCookieDescriptor = factory.getComponent(RestxSessionCookieDescriptor.class);
- SignatureKey signature = factory.queryByClass(SignatureKey.class).findOneAsComponent().or(SignatureKey.DEFAULT);
+ Signer signer = factory.queryByClass(Signer.class).findOneAsComponent().get();
ImmutableMap.Builder<String, String> cookiesBuilder = ImmutableMap.<String, String>builder().putAll(cookies);
String uuid = factory.getComponent(UUIDGenerator.class).doGenerate();
String expires = DateTime.now().plusHours(1).toString();
String sessionContent = String.format(
"{\"_expires\":\"%s\",\"principal\":\"%s\",\"sessionKey\":\"%s\"}", expires, principal, uuid);
cookiesBuilder.put(restxSessionCookieDescriptor.getCookieName(), sessionContent);
- cookiesBuilder.put(restxSessionCookieDescriptor.getCookieSignatureName(), Crypto.sign(sessionContent, signature.getKey()));
+ cookiesBuilder.put(restxSessionCookieDescriptor.getCookieSignatureName(), signer.sign(sessionContent));
return new HttpTestClient(baseUrl, principal, cookiesBuilder.build());
}

0 comments on commit 547f50e

Please sign in to comment.