Author: lryan
Date: Tue Mar 24 20:53:34 2009
New Revision: 758015

URL: http://svn.apache.org/viewvc?rev=758015&view=rev
Log:
Cleanup of OAuth authentication handler
 - Add support for oauth_body_hash signing as per 
http://oauth.googlecode.com/svn/spec/ext/body_hash/1.0/drafts/1/spec.html
 - Add support for legacy body signing mechanism for backward compatability 
with old REST/RPC clients. Deprecated.
 - Merged consumer and 3-legged oauth handlers
 - Added LOTS of tests
 - General cleanup

This change is a precusor to requiring strict content type checks for inbound 
REST/RPC requests

Added:
    
incubator/shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/core/oauth/FakeOAuthRequest.java
    
incubator/shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/core/oauth/OAuthAuthenticationHanderTest.java
Removed:
    
incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth/OAuthConsumerRequestAuthenticationHandler.java
Modified:
    incubator/shindig/trunk/java/common/conf/shindig.properties
    
incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/auth/AuthenticationHandler.java
    
incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/auth/AuthenticationServletFilter.java
    
incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/auth/UrlParameterAuthenticationHandler.java
    
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth/OAuthCommandLine.java
    
incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth/AuthenticationHandlerProvider.java
    
incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth/OAuthAuthenticationHandler.java
    
incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/oauth/OAuthEntry.java
    
incubator/shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/core/config/SocialApiGuiceModuleTest.java
    
incubator/shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/core/oauth/AuthenticationProviderHandlerTest.java

Modified: incubator/shindig/trunk/java/common/conf/shindig.properties
URL: 
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/common/conf/shindig.properties?rev=758015&r1=758014&r2=758015&view=diff
==============================================================================
--- incubator/shindig/trunk/java/common/conf/shindig.properties (original)
+++ incubator/shindig/trunk/java/common/conf/shindig.properties Tue Mar 24 
20:53:34 2009
@@ -12,6 +12,7 @@
 shindig.oauth.state-key=
 shindig.oauth.base-url=/oauth/
 shindig.oauth.authorize-action=/WEB-INF/authorize.jsp
+shindig.oauth.legacy-body-signing=true
 shindig.signing.key-name=
 shindig.signing.key-file=
 

Modified: 
incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/auth/AuthenticationHandler.java
URL: 
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/auth/AuthenticationHandler.java?rev=758015&r1=758014&r2=758015&view=diff
==============================================================================
--- 
incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/auth/AuthenticationHandler.java
 (original)
+++ 
incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/auth/AuthenticationHandler.java
 Tue Mar 24 20:53:34 2009
@@ -17,9 +17,10 @@
  */
 package org.apache.shindig.auth;
 
-import javax.servlet.http.HttpServletRequest;
 import java.util.Map;
 
+import javax.servlet.http.HttpServletRequest;
+
 /**
  * Implements a specific authentication mechanism and produces a SecurityToken 
when authentication
  * is successful.
@@ -27,6 +28,14 @@
 public interface AuthenticationHandler {
 
   /**
+   * Some authentication handlers need to read the request body to perform 
verification. Because
+   * the servlet stream can only be read once, making the content unavailable 
to the receiving
+   * servlet. An authentication handler that fully reads the body should stash 
the raw content
+   * byte array using request.setAttribute(STASHED_BODY, <body byte array>)
+   */
+  public static final String STASHED_BODY = "STASHED_BODY";
+
+  /**
    * @return The name of the authentication handler. This value is bound to 
the security token
    * and can be used to determine the authentication mechanism by which the 
security token was
    * created. The value is expected to be one of the values in 
AuthenticationMode but string
@@ -40,7 +49,8 @@
    * @param request The request to extract a token from.
    * @return A valid security token for the request, or null if it wasn't 
possible to authenticate.
    */
-  SecurityToken getSecurityTokenFromRequest(HttpServletRequest request);
+  SecurityToken getSecurityTokenFromRequest(HttpServletRequest request)
+      throws InvalidAuthenticationException;
 
     /**
      * Return a String to be used for a WWW-Authenticate header. This will be 
called if the
@@ -59,7 +69,7 @@
    * a malformed credential or token is passed. A handler which throws this 
exception
    * is required to include the appropriate error state in the servlet response
    */
-  public static final class InvalidAuthenticationException extends 
RuntimeException {
+  public static final class InvalidAuthenticationException extends Exception {
 
      private Map<String,String> additionalHeaders;
      private String redirect;

Modified: 
incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/auth/AuthenticationServletFilter.java
URL: 
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/auth/AuthenticationServletFilter.java?rev=758015&r1=758014&r2=758015&view=diff
==============================================================================
--- 
incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/auth/AuthenticationServletFilter.java
 (original)
+++ 
incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/auth/AuthenticationServletFilter.java
 Tue Mar 24 20:53:34 2009
@@ -17,11 +17,17 @@
  */
 package org.apache.shindig.auth;
 
-import org.apache.shindig.common.servlet.InjectedFilter;
-
 import com.google.inject.Inject;
 
+import org.apache.shindig.common.servlet.InjectedFilter;
+import org.apache.shindig.common.util.CharsetUtil;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
 import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.nio.charset.Charset;
 import java.util.List;
 import java.util.Map;
 import java.util.logging.Level;
@@ -29,9 +35,11 @@
 
 import javax.servlet.FilterChain;
 import javax.servlet.ServletException;
+import javax.servlet.ServletInputStream;
 import javax.servlet.ServletRequest;
 import javax.servlet.ServletResponse;
 import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletRequestWrapper;
 import javax.servlet.http.HttpServletResponse;
 
 /**
@@ -75,7 +83,7 @@
         SecurityToken token = handler.getSecurityTokenFromRequest(req);
         if (token != null) {
           new 
AuthInfo(req).setAuthType(handler.getName()).setSecurityToken(token);
-          chain.doFilter(req, response);
+          callChain(chain, req, resp);
           return;
         } else {
           String authHeader = handler.getWWWAuthenticateHeader(realm);
@@ -86,7 +94,7 @@
       }
 
       // We did not find a security token so we will just pass null
-      chain.doFilter(req, response);
+      callChain(chain, req, resp);
     } catch (AuthenticationHandler.InvalidAuthenticationException iae) {
       logger.log(Level.INFO, iae.getMessage(), iae.getCause());
       if (iae.getAdditionalHeaders() != null) {
@@ -101,4 +109,59 @@
       }
     }
   }
+
+  private void callChain(FilterChain chain, HttpServletRequest request,
+      HttpServletResponse response) throws IOException, ServletException {
+    if (request.getAttribute(AuthenticationHandler.STASHED_BODY) != null) {
+      chain.doFilter(new StashedBodyRequestwrapper(request), response);
+    } else {
+      chain.doFilter(request, response);      
+    }
+  }
+
+  private static class StashedBodyRequestwrapper extends 
HttpServletRequestWrapper {
+
+    final InputStream rawStream;
+    ServletInputStream stream;
+    BufferedReader reader;
+
+
+    StashedBodyRequestwrapper(HttpServletRequest wrapped) {
+      super(wrapped);
+      rawStream = new ByteArrayInputStream(
+          (byte[])wrapped.getAttribute(AuthenticationHandler.STASHED_BODY));
+    }
+
+    @Override
+    public ServletInputStream getInputStream() throws IOException {
+      if (reader != null) {
+        throw new IllegalStateException(
+            "The methods getInputStream() and getReader() are mutually 
exclusive.");
+      }
+      if (stream == null) {
+        stream = new ServletInputStream() {
+          public int read() throws IOException {
+            return rawStream.read();
+          }
+        };
+      }
+      return stream;
+    }
+
+    @Override
+    public BufferedReader getReader() throws IOException {
+      if (stream != null) {
+        throw new IllegalStateException(
+            "The methods getInputStream() and getReader() are mutually 
exclusive.");
+      }
+      if (reader == null) {
+        Charset charset = Charset.forName(getCharacterEncoding());
+        if (charset == null) {
+          charset =  CharsetUtil.UTF8;
+        }
+        reader = new BufferedReader(new InputStreamReader(rawStream, charset));
+      }
+      return reader;
+    }
+  }
 }

Modified: 
incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/auth/UrlParameterAuthenticationHandler.java
URL: 
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/auth/UrlParameterAuthenticationHandler.java?rev=758015&r1=758014&r2=758015&view=diff
==============================================================================
--- 
incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/auth/UrlParameterAuthenticationHandler.java
 (original)
+++ 
incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/auth/UrlParameterAuthenticationHandler.java
 Tue Mar 24 20:53:34 2009
@@ -19,10 +19,11 @@
 
 import com.google.inject.Inject;
 
-import javax.servlet.http.HttpServletRequest;
 import java.util.Collections;
 import java.util.Map;
 
+import javax.servlet.http.HttpServletRequest;
+
 /**
  * Produces security tokens by extracting the "st" parameter from the request 
url or post body.
  */
@@ -40,7 +41,8 @@
     return AuthenticationMode.SECURITY_TOKEN_URL_PARAMETER.name();
   }
 
-  public SecurityToken getSecurityTokenFromRequest(HttpServletRequest request) 
{
+  public SecurityToken getSecurityTokenFromRequest(HttpServletRequest request)
+      throws InvalidAuthenticationException {
     Map<String, String> parameters = getMappedParameters(request);
     try {
       if (parameters.get(SecurityTokenDecoder.SECURITY_TOKEN_NAME) == null) {
@@ -48,7 +50,8 @@
       }
       return securityTokenDecoder.createToken(parameters);
     } catch (SecurityTokenException e) {
-      throw new InvalidAuthenticationException("Malformed security token " + 
parameters.get(SecurityTokenDecoder.SECURITY_TOKEN_NAME), e);
+      throw new InvalidAuthenticationException("Malformed security token " +
+          parameters.get(SecurityTokenDecoder.SECURITY_TOKEN_NAME), e);
     }
   }
 
@@ -66,5 +69,4 @@
     return Collections.singletonMap(SecurityTokenDecoder.SECURITY_TOKEN_NAME,
         token);
   }
-
 }

Modified: 
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth/OAuthCommandLine.java
URL: 
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth/OAuthCommandLine.java?rev=758015&r1=758014&r2=758015&view=diff
==============================================================================
--- 
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth/OAuthCommandLine.java
 (original)
+++ 
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth/OAuthCommandLine.java
 Tue Mar 24 20:53:34 2009
@@ -24,6 +24,8 @@
 import net.oauth.OAuthConsumer;
 import net.oauth.OAuthMessage;
 
+import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.codec.digest.DigestUtils;
 import org.apache.commons.io.IOUtils;
 import org.apache.shindig.common.uri.Uri;
 import org.apache.shindig.common.uri.UriBuilder;
@@ -53,9 +55,16 @@
  *  --postBody <encoded post body>
  *  --postFile <file path of post body contents>
  *  --paramLocation <URI_QUERY | POST_BODY | AUTH_HEADER>
+ *  --bodySigning hash|legacy|none
  */
 public class OAuthCommandLine {
 
+  public static enum BodySigning {
+    none,
+    hash,
+    legacy
+  }
+
   public static void main(String[] argv) throws Exception {
     Map<String, String> params = Maps.newHashMap();
     for (int i = 0; i < argv.length; i+=2) {
@@ -71,6 +80,7 @@
     String postBody = params.get("--postBody");
     String postFile = params.get("--postFile");
     String paramLocation = params.get("--paramLocation");
+    String bodySigning = params.get("--bodySigning");
 
     HttpRequest request = new HttpRequest(Uri.parse(url));
     if (contentType != null) {
@@ -90,6 +100,10 @@
       paramLocationEnum = OAuthParamLocation.valueOf(paramLocation);
     }
 
+    BodySigning bodySigningEnum = BodySigning.none;
+    if (bodySigning != null) {
+      bodySigningEnum = BodySigning.valueOf(bodySigning);
+    }
 
     List<OAuth.Parameter> oauthParams = Lists.newArrayList();
     UriBuilder target = new UriBuilder(Uri.parse(url));
@@ -98,7 +112,14 @@
     oauthParams.addAll(OAuth.decodeForm(query));
     if (OAuth.isFormEncoded(contentType) && request.getPostBodyAsString() != 
null) {
       oauthParams.addAll(OAuth.decodeForm(request.getPostBodyAsString()));
+    } else if (bodySigningEnum == BodySigning.legacy) {
+      oauthParams.add(new OAuth.Parameter(request.getPostBodyAsString(), ""));
+    } else if (bodySigningEnum == BodySigning.hash) {
+      oauthParams.add(
+            new OAuth.Parameter("oauth_body_hash",
+                new 
String(Base64.encodeBase64(DigestUtils.sha(postBody.getBytes())), "UTF-8")));
     }
+
     if (consumerKey != null) {
       oauthParams.add(new OAuth.Parameter(OAuth.OAUTH_CONSUMER_KEY, 
consumerKey));
     }
@@ -124,12 +145,8 @@
               "OAuth param location can only be post_body if post body if of " 
+
                   "type x-www-form-urlencoded");
         }
-        String oauthData = OAuthUtil.formEncode(oauthParams);
-        if (request.getPostBodyLength() == 0) {
-          request.setPostBody(CharsetUtil.getUtf8Bytes(oauthData));
-        } else {
-          request.setPostBody((request.getPostBodyAsString() + '&' + 
oauthData).getBytes());
-        }
+        String oauthData = OAuthUtil.formEncode(message.getParameters());
+        request.setPostBody(CharsetUtil.getUtf8Bytes(oauthData));
         break;
 
       case URI_QUERY:

Modified: 
incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth/AuthenticationHandlerProvider.java
URL: 
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth/AuthenticationHandlerProvider.java?rev=758015&r1=758014&r2=758015&view=diff
==============================================================================
--- 
incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth/AuthenticationHandlerProvider.java
 (original)
+++ 
incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth/AuthenticationHandlerProvider.java
 Tue Mar 24 20:53:34 2009
@@ -32,10 +32,9 @@
 
   @Inject
   public AuthenticationHandlerProvider(UrlParameterAuthenticationHandler 
urlParam,
-      OAuthConsumerRequestAuthenticationHandler twoLeggedOAuth,
       OAuthAuthenticationHandler threeLeggedOAuth,
       AnonymousAuthenticationHandler anonymous) {
-    handlers = Lists.newArrayList(urlParam, twoLeggedOAuth, threeLeggedOAuth, 
anonymous);
+    handlers = Lists.newArrayList(urlParam, threeLeggedOAuth, anonymous);
   }
 
   public List<AuthenticationHandler> get() {

Modified: 
incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth/OAuthAuthenticationHandler.java
URL: 
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth/OAuthAuthenticationHandler.java?rev=758015&r1=758014&r2=758015&view=diff
==============================================================================
--- 
incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth/OAuthAuthenticationHandler.java
 (original)
+++ 
incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth/OAuthAuthenticationHandler.java
 Tue Mar 24 20:53:34 2009
@@ -17,13 +17,10 @@
  */
 package org.apache.shindig.social.core.oauth;
 
-import org.apache.shindig.auth.AuthenticationHandler;
-import org.apache.shindig.auth.AuthenticationMode;
-import org.apache.shindig.auth.SecurityToken;
-import org.apache.shindig.social.opensocial.oauth.OAuthDataStore;
-import org.apache.shindig.social.opensocial.oauth.OAuthEntry;
-
 import com.google.inject.Inject;
+import com.google.inject.name.Named;
+
+import net.oauth.OAuth;
 import net.oauth.OAuthAccessor;
 import net.oauth.OAuthConsumer;
 import net.oauth.OAuthException;
@@ -32,68 +29,194 @@
 import net.oauth.SimpleOAuthValidator;
 import net.oauth.server.OAuthServlet;
 
-import javax.servlet.http.HttpServletRequest;
+import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.codec.digest.DigestUtils;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang.StringUtils;
+import org.apache.shindig.auth.AuthenticationHandler;
+import org.apache.shindig.auth.SecurityToken;
+import org.apache.shindig.common.util.CharsetUtil;
+import org.apache.shindig.social.opensocial.oauth.OAuthDataStore;
+import org.apache.shindig.social.opensocial.oauth.OAuthEntry;
+
+import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.net.URISyntaxException;
+import java.util.Arrays;
+
+import javax.servlet.http.HttpServletRequest;
 
 /**
- * Normal three legged OAuth handler
+ * Handle both 2-legged consumer and full 3-legged OAuth requests
  */
 public class OAuthAuthenticationHandler implements AuthenticationHandler {
-  private OAuthDataStore store;
+
+  public static final String REQUESTOR_ID_PARAM = "xoauth_requestor_id";
+  public static final String OAUTH_BODY_HASH = "oauth_body_hash";
+
+  private final OAuthDataStore store;
+
+  @Deprecated
+  private final boolean allowLegacyBodySigning;
 
   @Inject
-  public OAuthAuthenticationHandler(OAuthDataStore store) {
+  public OAuthAuthenticationHandler(OAuthDataStore store,
+      @Named("shindig.oauth.legacy-body-signing") boolean 
allowLegacyBodySigning) {
     this.store = store;
+    this.allowLegacyBodySigning = allowLegacyBodySigning;
   }
 
   public String getName() {
-    return AuthenticationMode.OAUTH.name();
+    return "OAuth";
   }
 
   public String getWWWAuthenticateHeader(String realm) {
     return String.format("OAuth realm=\"%s\"", realm);
   }
 
-  public SecurityToken getSecurityTokenFromRequest(HttpServletRequest request) 
{
+  public SecurityToken getSecurityTokenFromRequest(HttpServletRequest request)
+      throws InvalidAuthenticationException {
     OAuthMessage message = OAuthServlet.getMessage(request, null);
-    OAuthEntry entry;
-
-    try {
-      // We only return null if this request 
-      if (message.getToken() == null) return null;
-      // no token available...
-
-      entry = store.getEntry(message.getToken());
-    } catch (IOException e) {
+    if (StringUtils.isEmpty(getParameter(message, OAuth.OAUTH_SIGNATURE))) {
+      // Is not an oauth request
       return null;
     }
+    String bodyHash = getParameter(message, OAUTH_BODY_HASH);
+    if (!StringUtils.isEmpty(bodyHash)) {
+      verifyBodyHash(request, bodyHash);
+    }
+    try {
+      return verifyMessage(message);
+    } catch (InvalidAuthenticationException iae) {
+      // Legacy body signing is intended for backwards compatability with 
opensocial clients
+      // that assumed they could use the raw request body as a pseudo query 
param to get
+      // body signing. This assumption was born out of the limitations of the 
OAuth 1.0 spec which
+      // states that request bodies are only signed if they are form-encoded. 
This lead many clients
+      // to force a content type of application/x-www-form-urlencoded for 
xml/json bodies and then
+      // hope that recevier decoding of the body didnt have encoding issues. 
This didnt work out
+      // to well so now these clients are required to specify the correct 
content type. This code
+      // lets clients which sign using the old technique to work if they 
specify the correct content
+      // type. This support is deprecated and should be removed later.
+      if (allowLegacyBodySigning && 
!request.getContentType().contains(OAuth.FORM_ENCODED)) {
+        try {
+          message.addParameter(readBodyString(request), "");
+          return verifyMessage(message);
+        } catch (IOException ioe) {
+          throw iae;
+        }
+      }
+      throw iae;
+    }
+  }
 
-    if (entry == null)
-      throw new InvalidAuthenticationException("access token not found.", 
null);
-    if (entry.type != OAuthEntry.Type.ACCESS)
-      throw new InvalidAuthenticationException("token is not an access 
token.", null);
-    if (entry.isExpired())
-      throw new InvalidAuthenticationException("access token has expired.", 
null);
+  protected SecurityToken verifyMessage(OAuthMessage message)
+      throws InvalidAuthenticationException {
+    OAuthEntry entry = getOAuthEntry(message);
+    OAuthConsumer authConsumer = getConsumer(message);
 
     OAuthServiceProvider provider = new OAuthServiceProvider(null, null, null);
-    OAuthAccessor accessor = new OAuthAccessor(new OAuthConsumer(null, 
entry.consumerKey,
-        store.getConsumer(entry.consumerKey).consumerSecret, provider));
+    OAuthAccessor accessor = new OAuthAccessor(new OAuthConsumer(null, 
authConsumer.consumerKey,
+        authConsumer.consumerSecret, provider));
 
-    accessor.tokenSecret = entry.tokenSecret;
-    accessor.accessToken = entry.token;
+    if (entry != null) {
+      accessor.tokenSecret = entry.tokenSecret;
+      accessor.accessToken = entry.token;
+    }
 
     try {
       message.validateMessage(accessor, new SimpleOAuthValidator());
     } catch (OAuthException e) {
-      throw new InvalidAuthenticationException(e.getMessage(), e);
+      throw new InvalidAuthenticationException("Unable to verify OAuth 
request", e);
     } catch (IOException e) {
-      throw new InvalidAuthenticationException(e.getMessage(), e);
+      throw new InvalidAuthenticationException("Unable to verify OAuth 
request", e);
     } catch (URISyntaxException e) {
-      throw new InvalidAuthenticationException(e.getMessage(), e);
+      throw new InvalidAuthenticationException("Unable to verify OAuth 
request", e);
+    }
+    return getTokenFromVerifiedRequest(message, entry, authConsumer);
+  }
+
+  protected OAuthEntry getOAuthEntry(OAuthMessage message) throws 
InvalidAuthenticationException {
+    OAuthEntry entry = null;
+    String token = getParameter(message, OAuth.OAUTH_TOKEN);
+    if (!StringUtils.isEmpty(token))  {
+      entry = store.getEntry(token);
+      if (entry == null) {
+        throw new InvalidAuthenticationException("No oauth entry for token: " 
+ token, null);
+      } else if (entry.type != OAuthEntry.Type.ACCESS) {
+        throw new InvalidAuthenticationException("token is not an access 
token.", null);
+      } else if (entry.isExpired()) {
+        throw new InvalidAuthenticationException("access token has expired.", 
null);
+      }
+    }
+    return entry;
+  }
+
+  protected OAuthConsumer getConsumer(OAuthMessage message) throws 
InvalidAuthenticationException {
+    String consumerKey = getParameter(message, OAuth.OAUTH_CONSUMER_KEY);
+    OAuthConsumer authConsumer = store.getConsumer(consumerKey);
+    if (authConsumer == null) {
+      throw new InvalidAuthenticationException("No consumer registered for key 
" + consumerKey,
+          null);
+    }
+    return authConsumer;
+  }
+
+  protected SecurityToken getTokenFromVerifiedRequest(OAuthMessage message, 
OAuthEntry entry,
+      OAuthConsumer authConsumer) {
+    if (entry != null) {
+      return new OAuthSecurityToken(entry.userId, entry.callbackUrl, 
entry.appId,
+          entry.domain, entry.container);
+    } else {
+      String userId = getParameter(message, REQUESTOR_ID_PARAM);
+      return 
store.getSecurityTokenForConsumerRequest(authConsumer.consumerKey, userId);
+    }
+  }
+
+  protected byte[] readBody(HttpServletRequest request) throws IOException {
+    if (request.getAttribute(AuthenticationHandler.STASHED_BODY) != null) {
+      return (byte[])request.getAttribute(AuthenticationHandler.STASHED_BODY);
+    }
+    byte[] rawBody = IOUtils.toByteArray(request.getInputStream());
+    request.setAttribute(AuthenticationHandler.STASHED_BODY, rawBody);
+    return rawBody;
+  }
+
+  protected String readBodyString(HttpServletRequest request) throws 
IOException {
+    byte[] rawBody = readBody(request);
+    return IOUtils.toString(new ByteArrayInputStream(rawBody), 
request.getCharacterEncoding());
+  }
+
+  protected void verifyBodyHash(HttpServletRequest request, String 
oauthBodyHash)
+      throws InvalidAuthenticationException {
+    // we are doing body hash signing which is not permitted for form-encoded 
data
+    if (request.getContentType().contains(OAuth.FORM_ENCODED)) {
+      throw new AuthenticationHandler.InvalidAuthenticationException(
+          "Cannot use oauth_body_hash with a Content-Type of 
application/x-www-form-urlencoded",
+          null);
+    } else if ("GET".equals(request.getMethod()) || 
"HEAD".equals(request.getMethod())) {
+      throw new AuthenticationHandler.InvalidAuthenticationException(
+          "Cannot use oauth_body_hash with a GET or HEAD request",null);
+    } else {
+      try {
+        byte[] rawBody = readBody(request);
+        byte[] received = 
Base64.decodeBase64(CharsetUtil.getUtf8Bytes(oauthBodyHash));
+        byte[] expected = DigestUtils.sha(rawBody);
+        if (!Arrays.equals(received, expected)) {
+          throw new AuthenticationHandler.InvalidAuthenticationException(
+            "oauth_body_hash failed verification", null);
+        }
+      } catch (IOException ioe) {
+        throw new AuthenticationHandler.InvalidAuthenticationException(
+          "Unable to read content body for oauth_body_hash verification", 
null);
+      }
     }
+  }
 
-    return new OAuthSecurityToken(entry.userId, entry.callbackUrl, entry.appId,
-        entry.domain, entry.container);
+  protected String getParameter(OAuthMessage requestMessage, String key) {
+    try {
+      return StringUtils.trim(requestMessage.getParameter(key));
+    } catch (IOException e) {
+      return null;
+    }
   }
 }

Modified: 
incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/oauth/OAuthEntry.java
URL: 
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/oauth/OAuthEntry.java?rev=758015&r1=758014&r2=758015&view=diff
==============================================================================
--- 
incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/oauth/OAuthEntry.java
 (original)
+++ 
incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/oauth/OAuthEntry.java
 Tue Mar 24 20:53:34 2009
@@ -17,16 +17,16 @@
  */
 package org.apache.shindig.social.opensocial.oauth;
 
-import java.util.Date;
 import java.io.Serializable;
+import java.util.Date;
 
 /**
  * The OAuthEntry class contains state information about OAuth Tokens and
  * Authorization.
  */
 public class OAuthEntry implements Serializable {
-  private static final long ONE_YEAR = 365 * 24 * 60 * 60 * 1000L;
-  private static final long FIVE_MINUTES = 5 * 60 * 1000L;
+  public static final long ONE_YEAR = 365 * 24 * 60 * 60 * 1000L;
+  public static final long FIVE_MINUTES = 5 * 60 * 1000L;
 
   // Change this when incompatible changes occur..
   static final long serialVersionUID = 2;

Modified: 
incubator/shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/core/config/SocialApiGuiceModuleTest.java
URL: 
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/core/config/SocialApiGuiceModuleTest.java?rev=758015&r1=758014&r2=758015&view=diff
==============================================================================
--- 
incubator/shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/core/config/SocialApiGuiceModuleTest.java
 (original)
+++ 
incubator/shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/core/config/SocialApiGuiceModuleTest.java
 Tue Mar 24 20:53:34 2009
@@ -17,19 +17,20 @@
  */
 package org.apache.shindig.social.core.config;
 
-import org.apache.shindig.auth.AuthenticationHandler;
-import org.apache.shindig.common.PropertiesModule;
-import org.apache.shindig.social.core.oauth.AuthenticationHandlerProvider;
-import org.apache.shindig.social.opensocial.oauth.OAuthDataStore;
-import org.easymock.EasyMock;
-
 import com.google.inject.AbstractModule;
 import com.google.inject.Guice;
 import com.google.inject.Injector;
 import com.google.inject.Key;
 import com.google.inject.TypeLiteral;
+
 import junit.framework.TestCase;
 
+import org.apache.shindig.auth.AuthenticationHandler;
+import org.apache.shindig.common.PropertiesModule;
+import org.apache.shindig.social.core.oauth.AuthenticationHandlerProvider;
+import org.apache.shindig.social.opensocial.oauth.OAuthDataStore;
+import org.easymock.EasyMock;
+
 import java.util.List;
 
 public class SocialApiGuiceModuleTest extends TestCase {
@@ -54,11 +55,11 @@
 
     AuthenticationHandlerProvider provider =
         injector.getInstance(AuthenticationHandlerProvider.class);
-    assertEquals(4, provider.get().size());
+    assertEquals(3, provider.get().size());
 
     List<AuthenticationHandler> handlers = injector.getInstance(
         Key.get(new TypeLiteral<List<AuthenticationHandler>>(){}));
 
-    assertEquals(4, handlers.size());
+    assertEquals(3, handlers.size());
   }
 }

Modified: 
incubator/shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/core/oauth/AuthenticationProviderHandlerTest.java
URL: 
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/core/oauth/AuthenticationProviderHandlerTest.java?rev=758015&r1=758014&r2=758015&view=diff
==============================================================================
--- 
incubator/shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/core/oauth/AuthenticationProviderHandlerTest.java
 (original)
+++ 
incubator/shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/core/oauth/AuthenticationProviderHandlerTest.java
 Tue Mar 24 20:53:34 2009
@@ -17,10 +17,6 @@
  */
 package org.apache.shindig.social.core.oauth;
 
-import org.apache.shindig.auth.AuthenticationHandler;
-import org.apache.shindig.common.PropertiesModule;
-import org.apache.shindig.social.core.config.SocialApiGuiceModule;
-
 import com.google.inject.AbstractModule;
 import com.google.inject.Guice;
 import com.google.inject.Injector;
@@ -29,6 +25,10 @@
 
 import junit.framework.TestCase;
 
+import org.apache.shindig.auth.AuthenticationHandler;
+import org.apache.shindig.common.PropertiesModule;
+import org.apache.shindig.social.core.config.SocialApiGuiceModule;
+
 import java.util.Collections;
 import java.util.List;
 
@@ -56,7 +56,7 @@
    */
   public static class ProvidesNoHandlers extends AuthenticationHandlerProvider 
{
     public ProvidesNoHandlers() {
-      super(null, null, null, null);
+      super(null, null, null);
     }
 
     @Override

Added: 
incubator/shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/core/oauth/FakeOAuthRequest.java
URL: 
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/core/oauth/FakeOAuthRequest.java?rev=758015&view=auto
==============================================================================
--- 
incubator/shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/core/oauth/FakeOAuthRequest.java
 (added)
+++ 
incubator/shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/core/oauth/FakeOAuthRequest.java
 Tue Mar 24 20:53:34 2009
@@ -0,0 +1,187 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.shindig.social.core.oauth;
+
+import com.google.common.collect.Lists;
+
+import net.oauth.OAuth;
+import net.oauth.OAuthAccessor;
+import net.oauth.OAuthConsumer;
+import net.oauth.OAuthMessage;
+
+import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.codec.digest.DigestUtils;
+import org.apache.commons.lang.StringUtils;
+import org.apache.shindig.common.testing.FakeHttpServletRequest;
+import org.apache.shindig.common.uri.Uri;
+import org.apache.shindig.common.uri.UriBuilder;
+import org.apache.shindig.common.util.CharsetUtil;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * This is largely a copy of OAuthCommandLine with some tweaks for 
FakeHttpServletRequest
+ */
+public class FakeOAuthRequest {
+
+  public static enum BodySigning {
+    NONE,
+    HASH,
+    LEGACY
+  }
+
+  public static enum OAuthParamLocation {
+    AUTH_HEADER,
+    POST_BODY,
+    URI_QUERY
+  }
+
+  public static final String CONSUMER_KEY = "gadget:12345";
+  public static final String CONSUMER_SECRET = "secret";
+  public static final String REQUESTOR = "requestor12345";
+
+  final String method;
+  final String url;
+  final String body;
+  final String contentType;
+
+  public FakeOAuthRequest(String method, String url, String body, String 
contentType) {
+    this.method = method;
+    this.url = url;
+    this.body = body;
+    this.contentType = contentType;
+  }
+
+  public FakeHttpServletRequest sign(String token, OAuthParamLocation 
paramLocationEnum,
+      BodySigning bodySigning)
+      throws Exception {
+    return sign(CONSUMER_KEY, CONSUMER_SECRET, REQUESTOR, token,
+        (token == null) ? null :CONSUMER_SECRET,
+        paramLocationEnum, bodySigning);
+  }
+
+  public FakeHttpServletRequest sign(String consumerKey, String 
consumerSecret, String requestor,
+      String token, String tokenSecret, OAuthParamLocation paramLocationEnum,
+      BodySigning bodySigning)
+      throws Exception {
+    FakeHttpServletRequest request = new FakeHttpServletRequest(url);
+
+    List<OAuth.Parameter> oauthParams = Lists.newArrayList();
+    UriBuilder target = new UriBuilder(Uri.parse(url));
+    String query = target.getQuery();
+    target.setQuery(null);
+    oauthParams.addAll(OAuth.decodeForm(query));
+
+    if (body != null) {
+      if (OAuth.isFormEncoded(contentType)) {
+        oauthParams.addAll(OAuth.decodeForm(body));
+      } else if (bodySigning == BodySigning.LEGACY) {
+        oauthParams.add(new OAuth.Parameter(body, ""));
+      } else if (bodySigning == BodySigning.HASH) {
+        oauthParams.add(
+            new OAuth.Parameter(OAuthAuthenticationHandler.OAUTH_BODY_HASH,
+                new 
String(Base64.encodeBase64(DigestUtils.sha(body.getBytes())), "UTF-8")));
+      }
+    }
+
+    oauthParams.add(new OAuth.Parameter(OAuth.OAUTH_CONSUMER_KEY, 
consumerKey));
+    oauthParams.add(new OAuth.Parameter("xoauth_requestor_id", requestor));
+
+    OAuthConsumer consumer = new 
OAuthConsumer(null,consumerKey,consumerSecret, null);
+    OAuthAccessor accessor = new OAuthAccessor(consumer);
+    if (!StringUtils.isEmpty(token)) {
+      accessor.accessToken = token;
+      accessor.tokenSecret = tokenSecret;
+    }
+    OAuthMessage message = accessor.newRequestMessage(method, 
target.toString(), oauthParams);
+
+    List<Map.Entry<String, String>> entryList = selectOAuthParams(message);
+
+    switch (paramLocationEnum) {
+      case AUTH_HEADER:
+        request.setHeader("Authorization", getAuthorizationHeader(entryList));
+        break;
+      case POST_BODY:
+        if (!OAuth.isFormEncoded(contentType)) {
+          throw new RuntimeException(
+              "OAuth param location can only be post_body if post body is of " 
+
+                  "type x-www-form-urlencoded");
+        }
+        // All message params should be added if oauth params are added to body
+        for (Map.Entry<String, String> param : message.getParameters()) {
+          request.setParameter(param.getKey(), true, param.getValue());
+        }
+        String oauthData = OAuth.formEncode(message.getParameters());
+        request.setPostData(CharsetUtil.getUtf8Bytes(oauthData));
+        break;
+      case URI_QUERY:
+        request.setQueryString(Uri.parse(OAuth.addParameters(url, 
entryList)).getQuery());
+        break;
+    }
+
+    if (body != null && paramLocationEnum != OAuthParamLocation.POST_BODY) {
+      request.setContentType(contentType);
+      request.setPostData(body, "UTF-8");
+      if (contentType.contains(OAuth.FORM_ENCODED)) {
+        List<OAuth.Parameter> bodyParams = OAuth.decodeForm(body);
+        for (OAuth.Parameter bodyParam : bodyParams) {
+          request.setParameter(bodyParam.getKey(), bodyParam.getValue());
+        }
+      }
+    }
+    request.setMethod(method);
+
+    return request;
+  }
+
+  private static String getAuthorizationHeader(List<Map.Entry<String, String>> 
oauthParams) {
+    StringBuilder result = new StringBuilder("OAuth ");
+
+    boolean first = true;
+    for (Map.Entry<String, String> parameter : oauthParams) {
+      if (!first) {
+        result.append(", ");
+      } else {
+        first = false;
+      }
+      result.append(OAuth.percentEncode(parameter.getKey()))
+          .append("=\"")
+          .append(OAuth.percentEncode(parameter.getValue()))
+          .append('"');
+    }
+    return result.toString();
+  }
+
+  private static List<Map.Entry<String, String>> 
selectOAuthParams(OAuthMessage message)
+      throws IOException {
+    List<Map.Entry<String, String>> result = Lists.newArrayList();
+    for (Map.Entry<String, String> param : message.getParameters()) {
+      if (isContainerInjectedParameter(param.getKey())) {
+        result.add(param);
+      }
+    }
+    return result;
+  }
+
+  private static boolean isContainerInjectedParameter(String key) {
+    key = key.toLowerCase();
+    return key.startsWith("oauth") || key.startsWith("xoauth") || 
key.startsWith("opensocial");
+  }
+}

Added: 
incubator/shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/core/oauth/OAuthAuthenticationHanderTest.java
URL: 
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/core/oauth/OAuthAuthenticationHanderTest.java?rev=758015&view=auto
==============================================================================
--- 
incubator/shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/core/oauth/OAuthAuthenticationHanderTest.java
 (added)
+++ 
incubator/shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/core/oauth/OAuthAuthenticationHanderTest.java
 Tue Mar 24 20:53:34 2009
@@ -0,0 +1,463 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.shindig.social.core.oauth;
+
+import net.oauth.OAuth;
+import net.oauth.OAuthConsumer;
+import net.oauth.OAuthServiceProvider;
+
+import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.codec.digest.DigestUtils;
+import org.apache.shindig.auth.AnonymousSecurityToken;
+import org.apache.shindig.auth.AuthenticationHandler;
+import org.apache.shindig.auth.SecurityToken;
+import org.apache.shindig.common.EasyMockTestCase;
+import org.apache.shindig.common.testing.FakeHttpServletRequest;
+import org.apache.shindig.common.util.CharsetUtil;
+import org.apache.shindig.social.opensocial.oauth.OAuthDataStore;
+import org.apache.shindig.social.opensocial.oauth.OAuthEntry;
+import org.easymock.classextension.EasyMock;
+import org.junit.Test;
+
+import java.util.Arrays;
+import java.util.Date;
+
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * Verify behavior of OAuth handler for consumer and 3-legged requests.
+ */
+public class OAuthAuthenticationHanderTest extends EasyMockTestCase {
+
+  OAuthDataStore mockStore = mock(OAuthDataStore.class);
+
+  OAuthAuthenticationHandler reqHandler;
+
+  private FakeOAuthRequest formEncodedPost;
+  private FakeOAuthRequest nonFormEncodedPost;
+
+  private static final String TEST_URL = "http://www.example.org/a/b?x=y";;
+  private static final String TOKEN = "atoken";
+  private static final String APP_ID = "app:12345";
+  private static final String DOMAIN = "example.org";
+  private static final String CONTAINER = "sandbox";
+
+  @Override
+  protected void setUp() throws Exception {
+    reqHandler = new OAuthAuthenticationHandler(mockStore, true);
+    formEncodedPost = new FakeOAuthRequest("POST", TEST_URL, "a=b&c=d",
+        OAuth.FORM_ENCODED);
+    nonFormEncodedPost = new FakeOAuthRequest("POST", TEST_URL, "BODY",
+        "text/plain");
+  }
+
+  private void expectTokenEntry() {
+    expectTokenEntry(createOAuthEntry());
+  }
+
+  private void expectTokenEntry(OAuthEntry authEntry) {
+    EasyMock.expect(mockStore.getEntry(
+        EasyMock.eq(TOKEN))).
+          andReturn(authEntry).anyTimes();
+  }
+
+  private OAuthEntry createOAuthEntry() {
+    OAuthEntry authEntry = new OAuthEntry();
+    authEntry.appId = APP_ID;
+    authEntry.authorized = true;
+    authEntry.consumerKey = FakeOAuthRequest.CONSUMER_KEY;
+    authEntry.token = TOKEN;
+    authEntry.tokenSecret = FakeOAuthRequest.CONSUMER_SECRET;
+    authEntry.type = OAuthEntry.Type.ACCESS;
+    authEntry.userId = FakeOAuthRequest.REQUESTOR;
+    authEntry.issueTime = new Date();
+    authEntry.domain = DOMAIN;
+    authEntry.container = CONTAINER;
+    return authEntry;
+  }
+
+  private void expectConsumer() {
+    EasyMock.expect(mockStore.getConsumer(
+        EasyMock.eq(FakeOAuthRequest.CONSUMER_KEY))).
+          andReturn(new OAuthConsumer(null, FakeOAuthRequest.CONSUMER_KEY,
+              FakeOAuthRequest.CONSUMER_SECRET, new OAuthServiceProvider(null, 
null, null)))
+        .anyTimes();
+  }
+
+  private void expectSecurityToken() {
+    EasyMock.expect(mockStore.getSecurityTokenForConsumerRequest(
+        EasyMock.eq(FakeOAuthRequest.CONSUMER_KEY), 
EasyMock.eq(FakeOAuthRequest.REQUESTOR))).
+          andReturn(new AnonymousSecurityToken());
+  }
+
+  @Test
+  public void testVerifyOAuthRequest() throws Exception {
+    expectTokenEntry();
+    expectConsumer();
+    replay();
+    HttpServletRequest request = formEncodedPost.sign(TOKEN,
+        FakeOAuthRequest.OAuthParamLocation.URI_QUERY, 
FakeOAuthRequest.BodySigning.NONE);
+    SecurityToken token = reqHandler.getSecurityTokenFromRequest(request);
+    assertEquals(token.getViewerId(), FakeOAuthRequest.REQUESTOR);
+    assertEquals(token.getAppId(), APP_ID);
+    assertEquals(token.getDomain(), DOMAIN);
+    assertEquals(token.getContainer(), CONTAINER);
+    assertNotNull(token);
+    assertTrue(token instanceof OAuthSecurityToken);
+    verify();
+  }
+
+  @Test
+  public void testVerifyGet() throws Exception {
+    expectTokenEntry();
+    expectConsumer();
+    replay();
+    FakeOAuthRequest get =
+        new FakeOAuthRequest("GET", TEST_URL, null, null);
+    FakeHttpServletRequest request = get.sign(TOKEN,
+        FakeOAuthRequest.OAuthParamLocation.URI_QUERY, 
FakeOAuthRequest.BodySigning.NONE);
+    assertNotNull(reqHandler.getSecurityTokenFromRequest(request));
+  }
+
+  @Test
+  public void testVerifyGetSignatureInHeader() throws Exception {
+    expectTokenEntry();
+    expectConsumer();
+    replay();
+    FakeOAuthRequest get =
+        new FakeOAuthRequest("GET", TEST_URL, null, null);
+    FakeHttpServletRequest request = get.sign(TOKEN,
+        FakeOAuthRequest.OAuthParamLocation.AUTH_HEADER, 
FakeOAuthRequest.BodySigning.NONE);
+    assertNotNull(reqHandler.getSecurityTokenFromRequest(request));
+  }
+
+  @Test
+  public void testVerifyRequestSignatureInBody() throws Exception {
+    expectTokenEntry();
+    expectConsumer();
+    replay();
+    HttpServletRequest request = formEncodedPost.sign(TOKEN,
+        FakeOAuthRequest.OAuthParamLocation.POST_BODY, 
FakeOAuthRequest.BodySigning.NONE);
+    SecurityToken token = reqHandler.getSecurityTokenFromRequest(request);
+    assertNotNull(token);
+    verify();
+  }
+
+
+  @Test
+  public void testVerifyFailNoTokenEntry() throws Exception {
+    expectTokenEntry(null);
+    expectConsumer();
+    replay();
+    HttpServletRequest request = formEncodedPost.sign(TOKEN,
+        FakeOAuthRequest.OAuthParamLocation.URI_QUERY, 
FakeOAuthRequest.BodySigning.NONE);
+    try {
+      reqHandler.getSecurityTokenFromRequest(request);
+      fail("Expect failure as no token entry in store");
+    } catch (AuthenticationHandler.InvalidAuthenticationException iae) {
+      // Pass
+    }
+    verify();
+  }
+
+  @Test
+  public void testVerifyFailTokenSecretMismatch() throws Exception {
+    OAuthEntry authEntry = createOAuthEntry();
+    authEntry.tokenSecret = "badsecret";
+    expectTokenEntry(authEntry);
+    expectConsumer();
+    replay();
+    HttpServletRequest request = formEncodedPost.sign(TOKEN,
+        FakeOAuthRequest.OAuthParamLocation.URI_QUERY, 
FakeOAuthRequest.BodySigning.NONE);
+    try {
+      reqHandler.getSecurityTokenFromRequest(request);
+      fail("Expect failure as token secrets mismatch");
+    } catch (AuthenticationHandler.InvalidAuthenticationException iae) {
+      // Pass
+    }
+    verify();
+  }
+
+  @Test
+  public void testVerifyFailTokenIsRequest() throws Exception {
+    OAuthEntry authEntry = createOAuthEntry();
+    authEntry.type = OAuthEntry.Type.REQUEST;
+    expectTokenEntry(authEntry);
+    expectConsumer();
+    replay();
+    HttpServletRequest request = formEncodedPost.sign(TOKEN,
+        FakeOAuthRequest.OAuthParamLocation.URI_QUERY, 
FakeOAuthRequest.BodySigning.NONE);
+    try {
+      reqHandler.getSecurityTokenFromRequest(request);
+      fail("Expect failure as token is a request token not an access token");
+    } catch (AuthenticationHandler.InvalidAuthenticationException iae) {
+      // Pass
+    }
+    verify();
+  }
+
+  @Test
+  public void testVerifyFailTokenIsExpired() throws Exception {
+    OAuthEntry authEntry = createOAuthEntry();
+    authEntry.issueTime = new Date(System.currentTimeMillis() - 
(OAuthEntry.ONE_YEAR + 1));
+    authEntry.type = OAuthEntry.Type.REQUEST;
+    expectTokenEntry(authEntry);
+    expectConsumer();
+    replay();
+    HttpServletRequest request = formEncodedPost.sign(TOKEN,
+        FakeOAuthRequest.OAuthParamLocation.URI_QUERY, 
FakeOAuthRequest.BodySigning.NONE);
+    try {
+      reqHandler.getSecurityTokenFromRequest(request);
+      fail("Expect failure as token is expired");
+    } catch (AuthenticationHandler.InvalidAuthenticationException iae) {
+      // Pass
+    }
+    verify();
+  }
+
+  @Test
+  public void testVerifyConsumerRequest() throws Exception {
+    expectConsumer();
+    expectSecurityToken();
+    replay();
+    HttpServletRequest request = formEncodedPost.sign(null,
+        FakeOAuthRequest.OAuthParamLocation.URI_QUERY, 
FakeOAuthRequest.BodySigning.NONE);
+    SecurityToken token = reqHandler.getSecurityTokenFromRequest(request);
+    assertNotNull(token);
+    assertFalse(token instanceof OAuthSecurityToken);
+    verify();
+  }
+
+  @Test
+  public void testVerifyConsumerGet() throws Exception {
+    expectConsumer();
+    expectSecurityToken();
+    replay();
+    FakeOAuthRequest get =
+        new FakeOAuthRequest("GET", TEST_URL, null, null);
+    FakeHttpServletRequest request = get.sign(null,
+        FakeOAuthRequest.OAuthParamLocation.URI_QUERY, 
FakeOAuthRequest.BodySigning.NONE);
+    assertNotNull(reqHandler.getSecurityTokenFromRequest(request));
+  }
+
+  @Test
+  public void testVerifyConsumerGetSignatureInHeader() throws Exception {
+    expectConsumer();
+    expectSecurityToken();
+    replay();
+    FakeOAuthRequest get =
+        new FakeOAuthRequest("GET", TEST_URL, null, null);
+    FakeHttpServletRequest request = get.sign(null,
+        FakeOAuthRequest.OAuthParamLocation.AUTH_HEADER, 
FakeOAuthRequest.BodySigning.NONE);
+    assertNotNull(reqHandler.getSecurityTokenFromRequest(request));
+  }
+
+  @Test
+  public void testVerifyConsumerRequestSignatureInAuthHeader() throws 
Exception {
+    expectConsumer();
+    expectSecurityToken();
+    replay();
+    HttpServletRequest request = formEncodedPost.sign(null,
+        FakeOAuthRequest.OAuthParamLocation.AUTH_HEADER, 
FakeOAuthRequest.BodySigning.NONE);
+    reqHandler.getSecurityTokenFromRequest(request);
+    verify();
+  }
+
+  @Test
+  public void testVerifyConsumerRequestSignatureInBody() throws Exception {
+    expectConsumer();
+    expectSecurityToken();
+    replay();
+    HttpServletRequest request = formEncodedPost.sign(null,
+        FakeOAuthRequest.OAuthParamLocation.POST_BODY, 
FakeOAuthRequest.BodySigning.NONE);
+    reqHandler.getSecurityTokenFromRequest(request);
+    verify();
+  }
+
+  @Test
+  public void testNoSignature() throws Exception {
+    replay();
+    FakeHttpServletRequest request = formEncodedPost.sign(null,
+        FakeOAuthRequest.OAuthParamLocation.URI_QUERY, 
FakeOAuthRequest.BodySigning.NONE);
+    // A request without a signature is not an OAuth request
+    request.setParameter(OAuth.OAUTH_SIGNATURE, "");
+    SecurityToken st = reqHandler.getSecurityTokenFromRequest(request);
+    assertNull(st);
+  }
+
+
+  @Test
+  public void testBodyHashSigning() throws Exception {
+    expectConsumer();
+    expectSecurityToken();
+    replay();
+
+    FakeHttpServletRequest request = nonFormEncodedPost.sign(null,
+        FakeOAuthRequest.OAuthParamLocation.URI_QUERY, 
FakeOAuthRequest.BodySigning.HASH);
+    assertNotNull(reqHandler.getSecurityTokenFromRequest(request));
+  }
+
+  @Test
+  public void testLegacyBodySigning() throws Exception {
+    expectConsumer();
+    expectSecurityToken();
+    replay();
+    FakeHttpServletRequest request = nonFormEncodedPost.sign(null,
+        FakeOAuthRequest.OAuthParamLocation.URI_QUERY, 
FakeOAuthRequest.BodySigning.LEGACY);
+    assertNotNull(reqHandler.getSecurityTokenFromRequest(request));
+  }
+
+  @Test
+  public void testLegacyBodySigningNotEnabled() throws Exception {
+    reqHandler = new OAuthAuthenticationHandler(mockStore, false);
+    expectConsumer();
+    expectSecurityToken();
+    replay();
+    FakeHttpServletRequest request = nonFormEncodedPost.sign(null,
+        FakeOAuthRequest.OAuthParamLocation.URI_QUERY, 
FakeOAuthRequest.BodySigning.LEGACY);
+    try {
+      reqHandler.getSecurityTokenFromRequest(request);
+      fail("Legacy signing not enabled");
+    } catch (AuthenticationHandler.InvalidAuthenticationException iae) {
+      // Pass
+    }
+  }
+
+  @Test
+  public void testConsumerFailBodyHashSigningWithFormEncoding() throws 
Exception {
+    replay();
+    FakeOAuthRequest bodyHashPost =
+        new FakeOAuthRequest("POST", TEST_URL, "a=b&c=d&oauth_body_hash=hash",
+        OAuth.FORM_ENCODED);
+    FakeHttpServletRequest request = bodyHashPost
+        .sign(null, FakeOAuthRequest.OAuthParamLocation.URI_QUERY,
+            FakeOAuthRequest.BodySigning.NONE);
+    try {
+      reqHandler.getSecurityTokenFromRequest(request);
+      fail("Cant have body signing with form-encoded post bodies");
+    } catch (AuthenticationHandler.InvalidAuthenticationException iae) {
+      // Pass
+    }
+  }
+
+  @Test
+  public void testLegacyBodySigningJson() throws Exception {
+    expectConsumer();
+    expectSecurityToken();
+    replay();
+    FakeOAuthRequest jsonPost =
+        new FakeOAuthRequest("POST", TEST_URL,
+        "{a:b,b:'c=d&d==f&&g=y'}",
+        "application/json");
+    FakeHttpServletRequest request = jsonPost.sign(null,
+        FakeOAuthRequest.OAuthParamLocation.URI_QUERY, 
FakeOAuthRequest.BodySigning.LEGACY);
+    assertNotNull(reqHandler.getSecurityTokenFromRequest(request));
+  }
+
+  @Test
+  public void testStashBody() throws Exception {
+    FakeHttpServletRequest req = new FakeHttpServletRequest();
+    String body = "BODY";
+    req.setPostData(CharsetUtil.getUtf8Bytes(body));
+    byte[] bytes = reqHandler.readBody(req);
+    assertTrue(Arrays.equals(bytes, CharsetUtil.getUtf8Bytes(body)));
+    assertEquals(req.getAttribute(AuthenticationHandler.STASHED_BODY), bytes);
+  }
+
+  @Test
+  public void testBodySigning() throws Exception {
+    FakeHttpServletRequest req = new FakeHttpServletRequest();
+    req.setContentType("text/plain");
+    String body = "BODY";
+    req.setPostData(CharsetUtil.getUtf8Bytes(body));
+    String hash = new 
String(Base64.encodeBase64(DigestUtils.sha(CharsetUtil.getUtf8Bytes(body))),
+        "UTF-8");
+    req.setParameter(OAuthAuthenticationHandler.OAUTH_BODY_HASH, hash);
+    reqHandler.verifyBodyHash(req, hash);
+  }
+
+  @Test
+  public void testFailBodySigning() throws Exception {
+    FakeHttpServletRequest req = new FakeHttpServletRequest();
+    req.setContentType("text/plain");
+    String body = "BODY";
+    req.setPostData(CharsetUtil.getUtf8Bytes(body));
+    String hash = new String(Base64.encodeBase64(
+        DigestUtils.sha(CharsetUtil.getUtf8Bytes("NOTBODY"))), "UTF-8");
+    req.setParameter(OAuthAuthenticationHandler.OAUTH_BODY_HASH, hash);
+    try {
+      reqHandler.verifyBodyHash(req, hash);
+      fail("Body verification should fail");
+    } catch (AuthenticationHandler.InvalidAuthenticationException iae) {
+      // Pass
+    }
+  }
+
+  @Test
+  public void testFailBodySigningWithFormEncoded() throws Exception {
+    FakeHttpServletRequest req = new FakeHttpServletRequest();
+    req.setContentType(OAuth.FORM_ENCODED);
+    String body = "BODY";
+    req.setPostData(CharsetUtil.getUtf8Bytes(body));
+    String hash = new 
String(Base64.encodeBase64(DigestUtils.sha(CharsetUtil.getUtf8Bytes(body))),
+        "UTF-8");
+    req.setParameter(OAuthAuthenticationHandler.OAUTH_BODY_HASH, hash);
+    try {
+      reqHandler.verifyBodyHash(req, hash);
+      fail("Body verification should fail");
+    } catch (AuthenticationHandler.InvalidAuthenticationException iae) {
+      // Pass
+    }
+  }
+
+  @Test
+  public void testFailBodyForGet() throws Exception {
+    FakeHttpServletRequest req = new FakeHttpServletRequest();
+    req.setContentType("text/plain");
+    String body = "BODY";
+    req.setPostData(CharsetUtil.getUtf8Bytes(body));
+    req.setMethod("GET");
+    String hash = new 
String(Base64.encodeBase64(DigestUtils.sha(CharsetUtil.getUtf8Bytes(body))),
+        "UTF-8");
+    req.setParameter(OAuthAuthenticationHandler.OAUTH_BODY_HASH, hash);
+    try {
+      reqHandler.verifyBodyHash(req, hash);
+      fail("Body verification should fail");
+    } catch (AuthenticationHandler.InvalidAuthenticationException iae) {
+      // Pass
+    }
+  }
+
+  @Test
+  public void testFailBodyForHead() throws Exception {
+    FakeHttpServletRequest req = new FakeHttpServletRequest();
+    req.setContentType("text/plain");
+    String body = "BODY";
+    req.setPostData(CharsetUtil.getUtf8Bytes(body));
+    req.setMethod("HEAD");
+    String hash = new 
String(Base64.encodeBase64(DigestUtils.sha(CharsetUtil.getUtf8Bytes(body))),
+        "UTF-8");
+    req.setParameter(OAuthAuthenticationHandler.OAUTH_BODY_HASH, hash);
+    try {
+      reqHandler.verifyBodyHash(req, hash);
+      fail("Body verification should fail");
+    } catch (AuthenticationHandler.InvalidAuthenticationException iae) {
+      // Pass
+    }
+  }
+}


Reply via email to