You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@shindig.apache.org by be...@apache.org on 2009/04/12 00:16:23 UTC
svn commit: r764256 - in /incubator/shindig/trunk/java:
common/src/main/java/org/apache/shindig/auth/
gadgets/src/main/java/org/apache/shindig/gadgets/oauth/
gadgets/src/main/java/org/apache/shindig/gadgets/oauth/testing/
gadgets/src/test/java/org/apac...
Author: beaton
Date: Sat Apr 11 22:16:22 2009
New Revision: 764256
URL: http://svn.apache.org/viewvc?rev=764256&view=rev
Log:
Support for final version of oauth_body_hash.
Modified:
incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/auth/OAuthUtil.java
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth/OAuthRequest.java
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth/testing/FakeOAuthServiceProvider.java
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth/testing/MakeRequestClient.java
incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/oauth/OAuthRequestTest.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/test/java/org/apache/shindig/social/core/oauth/OAuthAuthenticationHanderTest.java
Modified: incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/auth/OAuthUtil.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/auth/OAuthUtil.java?rev=764256&r1=764255&r2=764256&view=diff
==============================================================================
--- incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/auth/OAuthUtil.java (original)
+++ incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/auth/OAuthUtil.java Sat Apr 11 22:16:22 2009
@@ -86,11 +86,15 @@
URL_AND_BODY_HASH,
}
- public static SignatureType getSignatureType(String method, String contentType) {
+ /**
+ * @param tokenEndpoint true if this is a request token or access token request. We don't check
+ * oauth_body_hash on those.
+ */
+ public static SignatureType getSignatureType(boolean tokenEndpoint, String contentType) {
if (OAuth.isFormEncoded(contentType)) {
return SignatureType.URL_AND_FORM_PARAMS;
}
- if ("GET".equals(method) || "HEAD".equals(method)) {
+ if (tokenEndpoint) {
return SignatureType.URL_ONLY;
}
return SignatureType.URL_AND_BODY_HASH;
Modified: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth/OAuthRequest.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth/OAuthRequest.java?rev=764256&r1=764255&r2=764256&view=diff
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth/OAuthRequest.java (original)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth/OAuthRequest.java Sat Apr 11 22:16:22 2009
@@ -336,7 +336,7 @@
request.setHeader("Content-Type", OAuth.FORM_ENCODED);
}
- HttpRequest signed = sanitizeAndSign(request, null);
+ HttpRequest signed = sanitizeAndSign(request, null, true);
OAuthMessage reply = sendOAuthMessage(signed);
@@ -455,8 +455,8 @@
* Add OAuth parameters to new request.
* Send it.
*/
- public HttpRequest sanitizeAndSign(HttpRequest base, List<Parameter> params)
- throws OAuthRequestException {
+ public HttpRequest sanitizeAndSign(HttpRequest base, List<Parameter> params,
+ boolean tokenEndpoint) throws OAuthRequestException {
if (params == null) {
params = Lists.newArrayList();
}
@@ -465,7 +465,7 @@
target.setQuery(null);
params.addAll(sanitize(OAuth.decodeForm(query)));
- switch(OAuthUtil.getSignatureType(base.getMethod(), base.getHeader("Content-Type"))) {
+ switch(OAuthUtil.getSignatureType(tokenEndpoint, base.getHeader("Content-Type"))) {
case URL_ONLY:
break;
case URL_AND_FORM_PARAMS:
@@ -669,7 +669,7 @@
accessorInfo.getSessionHandle()));
}
- HttpRequest signed = sanitizeAndSign(request, msgParams);
+ HttpRequest signed = sanitizeAndSign(request, msgParams, true);
OAuthMessage reply = sendOAuthMessage(signed);
@@ -748,7 +748,7 @@
// This is a request for access token data, return it.
builder = formatAccessTokenData();
} else {
- HttpRequest signed = sanitizeAndSign(realRequest, null);
+ HttpRequest signed = sanitizeAndSign(realRequest, null, false);
HttpResponse response = fetchFromServer(signed);
Modified: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth/testing/FakeOAuthServiceProvider.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth/testing/FakeOAuthServiceProvider.java?rev=764256&r1=764255&r2=764256&view=diff
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth/testing/FakeOAuthServiceProvider.java (original)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth/testing/FakeOAuthServiceProvider.java Sat Apr 11 22:16:22 2009
@@ -303,7 +303,7 @@
}
}
OAuthAccessor accessor = new OAuthAccessor(consumer);
- validateMessage(accessor, info);
+ validateMessage(accessor, info, true);
String requestToken = Crypto.getRandomString(16);
String requestTokenSecret = Crypto.getRandomString(16);
tokenState.put(
@@ -378,30 +378,23 @@
}
// Parse body
- switch(OAuthUtil.getSignatureType(request.getMethod(), request.getHeader("Content-Type"))) {
- case URL_AND_FORM_PARAMS:
- String body = request.getPostBodyAsString();
- info.body = body;
- params.addAll(OAuth.decodeForm(request.getPostBodyAsString()));
- // If we're not configured to pass oauth parameters in the post body, double check
- // that they didn't end up there.
- if (!validParamLocations.contains(OAuthParamLocation.POST_BODY)) {
- if (body.contains("oauth_")) {
- throw new RuntimeException("Found unexpected post body data" + body);
- }
- }
- break;
- case URL_AND_BODY_HASH:
- try {
- info.rawBody = IOUtils.toByteArray(request.getPostBody());
- } catch (IOException e) {
- throw new RuntimeException(e);
+ info.body = request.getPostBodyAsString();
+ try {
+ info.rawBody = IOUtils.toByteArray(request.getPostBody());
+ } catch (IOException e) {
+ throw new RuntimeException("Can't read post body bytes", e);
+ }
+ if (OAuth.isFormEncoded(request.getHeader("Content-Type"))) {
+ params.addAll(OAuth.decodeForm(request.getPostBodyAsString()));
+ // If we're not configured to pass oauth parameters in the post body, double check
+ // that they didn't end up there.
+ if (!validParamLocations.contains(OAuthParamLocation.POST_BODY)) {
+ if (info.body.contains("oauth_")) {
+ throw new RuntimeException("Found unexpected post body data" + info.body);
}
- break;
- case URL_ONLY:
- break;
+ }
}
-
+
// Return the lot
info.message = new OAuthMessage(method, parsed.getLocation(), params);
@@ -561,7 +554,7 @@
OAuthAccessor accessor = new OAuthAccessor(oauthConsumer);
accessor.requestToken = requestToken;
accessor.tokenSecret = state.tokenSecret;
- validateMessage(accessor, info);
+ validateMessage(accessor, info, true);
if (state.getState() == State.APPROVED_UNCLAIMED) {
state.claimToken();
@@ -634,7 +627,7 @@
// Check the signature
accessor.accessToken = accessToken;
accessor.tokenSecret = state.getSecret();
- validateMessage(accessor, info);
+ validateMessage(accessor, info, false);
if (state.getState() != State.APPROVED) {
return makeOAuthProblemReport(
@@ -649,7 +642,7 @@
responseBody = "User data is " + state.getUserData();
} else {
// Check the signature
- validateMessage(accessor, info);
+ validateMessage(accessor, info, false);
// For signed fetch, just echo back the query parameters in the body
responseBody = request.getUri().getQuery();
@@ -672,11 +665,14 @@
return resp.create();
}
- private void validateMessage(OAuthAccessor accessor, MessageInfo info)
+ private void validateMessage(OAuthAccessor accessor, MessageInfo info, boolean tokenEndpoint)
throws OAuthException, IOException, URISyntaxException {
info.message.validateMessage(accessor, new FakeTimeOAuthValidator());
String bodyHash = info.message.getParameter("oauth_body_hash");
- SignatureType sigType = OAuthUtil.getSignatureType(info.request.getMethod(),
+ if (tokenEndpoint && bodyHash != null) {
+ throw new RuntimeException("Can't have body hash on token endpoints");
+ }
+ SignatureType sigType = OAuthUtil.getSignatureType(tokenEndpoint,
info.request.getHeader("Content-Type"));
switch (sigType) {
case URL_ONLY:
Modified: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth/testing/MakeRequestClient.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth/testing/MakeRequestClient.java?rev=764256&r1=764255&r2=764256&view=diff
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth/testing/MakeRequestClient.java (original)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth/testing/MakeRequestClient.java Sat Apr 11 22:16:22 2009
@@ -128,6 +128,21 @@
return response;
}
+ // Yes, this is really allowed by the HTTP spec and supported by real servers.
+ public HttpResponse sendGetWithBody(String target, String type, byte[] body) {
+ HttpRequest request = new HttpRequest(Uri.parse(target));
+ request.setOAuthArguments(recallState());
+ OAuthRequest dest = createRequest();
+ if (type != null) {
+ request.setHeader("Content-Type", type);
+ }
+ request.setPostBody(body);
+ request.setSecurityToken(securityToken);
+ HttpResponse response = dest.fetch(request);
+ saveState(response);
+ return response;
+ }
+
/**
* Send an OAuth POST request to the given URL.
*/
Modified: incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/oauth/OAuthRequestTest.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/oauth/OAuthRequestTest.java?rev=764256&r1=764255&r2=764256&view=diff
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/oauth/OAuthRequestTest.java (original)
+++ incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/oauth/OAuthRequestTest.java Sat Apr 11 22:16:22 2009
@@ -851,7 +851,7 @@
checkStringContains("oauthErrorText missing request entry", metadata.get("oauthErrorText"),
"GET /data?cachebust=2\n");
checkStringContains("oauthErrorText missing request entry", metadata.get("oauthErrorText"),
- "GET /data?cachebust=2&opensocial_owner_id=owner");
+ "GET /data?cachebust=2&oauth_body_hash=2jm");
assertEquals(1, serviceProvider.getRequestTokenCount());
assertEquals(1, serviceProvider.getAccessTokenCount());
@@ -1046,6 +1046,84 @@
}
@Test
+ public void testGetWithFormEncodedBody() throws Exception {
+ MakeRequestClient client = makeSignedFetchClient("o", "v", "http://www.example.com/app");
+ HttpResponse resp = client.sendGetWithBody(FakeOAuthServiceProvider.RESOURCE_URL,
+ OAuth.FORM_ENCODED, "war=peace&yes=no".getBytes());
+ assertEquals("war=peace&yes=no", resp.getHeader(FakeOAuthServiceProvider.BODY_ECHO_HEADER));
+ }
+
+ @Test
+ public void testGetWithRawBody() throws Exception {
+ MakeRequestClient client = makeSignedFetchClient("o", "v", "http://www.example.com/app");
+ HttpResponse resp = client.sendGetWithBody(FakeOAuthServiceProvider.RESOURCE_URL,
+ "application/json", "war=peace&yes=no".getBytes());
+ assertEquals("war=peace&yes=no", resp.getHeader(FakeOAuthServiceProvider.BODY_ECHO_HEADER));
+ List<Parameter> queryParams = OAuth.decodeForm(resp.getResponseAsString());
+ checkContains(queryParams, "oauth_body_hash", "MfhwxPN6ns5CwQAZN9OcJXu3Jv4=");
+ }
+
+ @Test
+ public void testGetTamperedRawContent() throws Exception {
+ byte[] raw = { 0, 1, 2, 3, 4, 5 };
+ MakeRequestClient client = makeSignedFetchClient("o", "v", "http://www.example.com/app");
+ // Tamper with the body before it hits the service provider
+ client.setNextFetcher(new HttpFetcher() {
+ public HttpResponse fetch(HttpRequest request) throws GadgetException {
+ request.setPostBody("yo momma".getBytes());
+ return serviceProvider.fetch(request);
+ }
+ });
+ try {
+ client.sendGetWithBody(FakeOAuthServiceProvider.RESOURCE_URL,
+ "funky-content", raw);
+ fail("Should have thrown with oauth_body_hash mismatch");
+ } catch (RuntimeException e) {
+ // good
+ }
+ }
+
+ @Test
+ public void testGetTamperedFormContent() throws Exception {
+ MakeRequestClient client = makeSignedFetchClient("o", "v", "http://www.example.com/app");
+ // Tamper with the body before it hits the service provider
+ client.setNextFetcher(new HttpFetcher() {
+ public HttpResponse fetch(HttpRequest request) throws GadgetException {
+ request.setPostBody("foo=quux".getBytes());
+ return serviceProvider.fetch(request);
+ }
+ });
+ try {
+ client.sendGetWithBody(FakeOAuthServiceProvider.RESOURCE_URL,
+ OAuth.FORM_ENCODED, "foo=bar".getBytes());
+ fail("Should have thrown with oauth signature mismatch");
+ } catch (RuntimeException e) {
+ // good
+ }
+ }
+
+ @Test
+ public void testGetTamperedRemoveRawContent() throws Exception {
+ byte[] raw = { 0, 1, 2, 3, 4, 5 };
+ MakeRequestClient client = makeSignedFetchClient("o", "v", "http://www.example.com/app");
+ // Tamper with the body before it hits the service provider
+ client.setNextFetcher(new HttpFetcher() {
+ public HttpResponse fetch(HttpRequest request) throws GadgetException {
+ request.setPostBody(ArrayUtils.EMPTY_BYTE_ARRAY);
+ request.setHeader("Content-Type", "application/x-www-form-urlencoded");
+ return serviceProvider.fetch(request);
+ }
+ });
+ try {
+ client.sendGetWithBody(FakeOAuthServiceProvider.RESOURCE_URL,
+ "funky-content", raw);
+ fail("Should have thrown with body hash in form encoded request");
+ } catch (RuntimeException e) {
+ // good
+ }
+ }
+
+ @Test
public void testPostTamperedRawContent() throws Exception {
byte[] raw = { 0, 1, 2, 3, 4, 5 };
MakeRequestClient client = makeSignedFetchClient("o", "v", "http://www.example.com/app");
@@ -1103,7 +1181,7 @@
// good
}
}
-
+
@Test
public void testSignedFetch_error401() throws Exception {
assertEquals(0, base.getAccessTokenRemoveCount());
@@ -1114,7 +1192,7 @@
HttpResponse response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL);
assertNull(response.getMetadata().get("oauthError"));
String errorText = response.getMetadata().get("oauthErrorText");
- checkStringContains("Should return sent request", errorText, "GET /data?opensocial_owner");
+ checkStringContains("Should return sent request", errorText, "GET /data");
checkStringContains("Should return response", errorText, "HTTP/1.1 401");
checkStringContains("Should return response", errorText, "some vague error");
assertEquals(0, base.getAccessTokenRemoveCount());
@@ -1573,9 +1651,12 @@
response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL);
assertEquals("User data is hello-oauth", response.getResponseAsString());
- response = client.sendGet(FakeOAuthServiceProvider.ACCESS_TOKEN_URL);
- assertEquals("", response.getResponseAsString());
- assertTrue(response.getMetadata().containsKey("oauthApprovalUrl"));
+ try {
+ client.sendGet(FakeOAuthServiceProvider.ACCESS_TOKEN_URL);
+ fail("Service provider should have rejected bogus request to access token URL");
+ } catch (RuntimeException e) {
+ // good
+ }
}
@Test
@@ -1662,6 +1743,16 @@
}
return false;
}
+
+ private void checkContains(List<Parameter> params, String key, String value) {
+ for (Parameter p : params) {
+ if (p.getKey().equals(key)) {
+ assertEquals(value, p.getValue());
+ return;
+ }
+ }
+ fail("List did not contain " + key + "=" + value + "; instead was " + params);
+ }
private String getLogText() {
StringBuilder logText = new StringBuilder();
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=764256&r1=764255&r2=764256&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 Sat Apr 11 22:16:22 2009
@@ -94,11 +94,11 @@
// 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
+ // hope that receiver decoding of the body didnt have encoding issues. This didn't 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 && requestHasBody(request) &&
+ if (allowLegacyBodySigning &&
(StringUtils.isEmpty(request.getContentType()) ||
!request.getContentType().contains(OAuth.FORM_ENCODED))) {
try {
@@ -207,9 +207,6 @@
throw new AuthenticationHandler.InvalidAuthenticationException(
"Cannot use oauth_body_hash with a Content-Type of application/x-www-form-urlencoded",
null);
- } else if (!requestHasBody(request)) {
- throw new AuthenticationHandler.InvalidAuthenticationException(
- "Cannot use oauth_body_hash with a GET or HEAD request",null);
} else {
try {
byte[] rawBody = readBody(request);
@@ -233,8 +230,4 @@
return null;
}
}
-
- public static boolean requestHasBody(HttpServletRequest request) {
- return !("GET".equals(request.getMethod()) || "HEAD".equals(request.getMethod()));
- }
}
Modified: 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=764256&r1=764255&r2=764256&view=diff
==============================================================================
--- incubator/shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/core/oauth/OAuthAuthenticationHanderTest.java (original)
+++ incubator/shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/core/oauth/OAuthAuthenticationHanderTest.java Sat Apr 11 22:16:22 2009
@@ -434,40 +434,4 @@
// 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(OAuthConstants.OAUTH_BODY_HASH, hash);
- try {
- OAuthAuthenticationHandler.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(OAuthConstants.OAUTH_BODY_HASH, hash);
- try {
- OAuthAuthenticationHandler.verifyBodyHash(req, hash);
- fail("Body verification should fail");
- } catch (AuthenticationHandler.InvalidAuthenticationException iae) {
- // Pass
- }
- }
}