/* * 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.gadgets.oauth; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import com.google.common.collect.Lists; import net.oauth.OAuth; import net.oauth.OAuth.Parameter; import org.apache.commons.codec.binary.Base64; import org.apache.commons.lang.ArrayUtils; import org.apache.shindig.auth.BasicSecurityToken; import org.apache.shindig.auth.SecurityToken; import org.apache.shindig.common.crypto.BasicBlobCrypter; import org.apache.shindig.common.uri.Uri; import org.apache.shindig.common.uri.UriBuilder; import org.apache.shindig.common.util.CharsetUtil; import org.apache.shindig.common.util.FakeTimeSource; import org.apache.shindig.gadgets.FakeGadgetSpecFactory; import org.apache.shindig.gadgets.GadgetException; import org.apache.shindig.gadgets.GadgetSpecFactory; import org.apache.shindig.gadgets.http.HttpFetcher; import org.apache.shindig.gadgets.http.HttpRequest; import org.apache.shindig.gadgets.http.HttpResponse; import org.apache.shindig.gadgets.oauth.AccessorInfo.OAuthParamLocation; import org.apache.shindig.gadgets.oauth.BasicOAuthStoreConsumerKeyAndSecret.KeyType; import org.apache.shindig.gadgets.oauth.OAuthArguments.UseToken; import org.apache.shindig.gadgets.oauth.testing.FakeOAuthServiceProvider; import org.apache.shindig.gadgets.oauth.testing.MakeRequestClient; import org.apache.shindig.gadgets.oauth.testing.FakeOAuthServiceProvider.TokenPair; import org.json.JSONObject; import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import java.io.PrintWriter; import java.io.StringWriter; import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.logging.Handler; import java.util.logging.Level; import java.util.logging.LogRecord; import java.util.logging.Logger; /** * Tests for signing requests. */ public class OAuthRequestTest { private OAuthFetcherConfig fetcherConfig; private FakeOAuthServiceProvider serviceProvider; private OAuthCallbackGenerator callbackGenerator; private BasicOAuthStore base; private Logger logger; protected final List<LogRecord> logRecords = Lists.newArrayList(); private final FakeTimeSource clock = new FakeTimeSource(); public static final String GADGET_URL = "http://www.example.com/gadget.xml"; public static final String GADGET_URL_NO_KEY = "http://www.example.com/nokey.xml"; public static final String GADGET_URL_HEADER = "http://www.example.com/header.xml"; public static final String GADGET_URL_BODY = "http://www.example.com/body.xml"; public static final String GADGET_URL_BAD_OAUTH_URL = "http://www.example.com/badoauthurl.xml"; public static final String GADGET_URL_APPROVAL_PARAMS = "http://www.example.com/approvalparams.xml"; public static final String GADGET_MAKE_REQUEST_URL = "http://127.0.0.1/gadgets/makeRequest?params=foo"; @Before public void setUp() throws Exception { base = new BasicOAuthStore(); base.setDefaultCallbackUrl(GadgetTokenStoreTest.DEFAULT_CALLBACK); serviceProvider = new FakeOAuthServiceProvider(clock); callbackGenerator = createNullCallbackGenerator(); fetcherConfig = new OAuthFetcherConfig( new BasicBlobCrypter("abcdefghijklmnop".getBytes()), getOAuthStore(base), clock, callbackGenerator, false); logger = Logger.getLogger(OAuthResponseParams.class.getName()); logger.addHandler(new Handler() { @Override public void close() throws SecurityException { } @Override public void flush() { } @Override public void publish(LogRecord arg0) { logRecords.add(arg0); } }); } private OAuthCallbackGenerator createNullCallbackGenerator() { return new OAuthCallbackGenerator() { public String generateCallback(OAuthFetcherConfig fetcherConfig, String baseCallback, HttpRequest request, OAuthResponseParams responseParams) { return null; } }; } private OAuthCallbackGenerator createRealCallbackGenerator() { return new OAuthCallbackGenerator() { public String generateCallback(OAuthFetcherConfig fetcherConfig, String baseCallback, HttpRequest request, OAuthResponseParams responseParams) { SecurityToken st = request.getSecurityToken(); Uri activeUrl = Uri.parse(st.getActiveUrl()); assertEquals(GADGET_MAKE_REQUEST_URL, activeUrl.toString()); assertEquals(GadgetTokenStoreTest.DEFAULT_CALLBACK, baseCallback); return new UriBuilder() .setScheme("http") .setAuthority(activeUrl.getAuthority()) .setPath("/realcallback") .toString(); } }; } /** * Builds a nicely populated fake token store. */ public GadgetOAuthTokenStore getOAuthStore(BasicOAuthStore base) { return getOAuthStore(base, new FakeGadgetSpecFactory()); } private GadgetOAuthTokenStore getOAuthStore(BasicOAuthStore base, GadgetSpecFactory specFactory) { if (base == null) { base = new BasicOAuthStore(); base.setDefaultCallbackUrl(GadgetTokenStoreTest.DEFAULT_CALLBACK); } addValidConsumer(base); addInvalidConsumer(base); addAuthHeaderConsumer(base); addBodyConsumer(base); addBadOAuthUrlConsumer(base); addApprovalParamsConsumer(base); addDefaultKey(base); return new GadgetOAuthTokenStore(base, specFactory); } private static void addValidConsumer(BasicOAuthStore base) { addConsumer( base, GADGET_URL, FakeGadgetSpecFactory.SERVICE_NAME, FakeOAuthServiceProvider.CONSUMER_KEY, FakeOAuthServiceProvider.CONSUMER_SECRET); } private static void addInvalidConsumer(BasicOAuthStore base) { addConsumer( base, GADGET_URL_NO_KEY, FakeGadgetSpecFactory.SERVICE_NAME_NO_KEY, "garbage_key", "garbage_secret"); } private static void addAuthHeaderConsumer(BasicOAuthStore base) { addConsumer( base, GADGET_URL_HEADER, FakeGadgetSpecFactory.SERVICE_NAME, FakeOAuthServiceProvider.CONSUMER_KEY, FakeOAuthServiceProvider.CONSUMER_SECRET); } private static void addBodyConsumer(BasicOAuthStore base) { addConsumer( base, GADGET_URL_BODY, FakeGadgetSpecFactory.SERVICE_NAME, FakeOAuthServiceProvider.CONSUMER_KEY, FakeOAuthServiceProvider.CONSUMER_SECRET); } private static void addBadOAuthUrlConsumer(BasicOAuthStore base) { addConsumer( base, GADGET_URL_BAD_OAUTH_URL, FakeGadgetSpecFactory.SERVICE_NAME, FakeOAuthServiceProvider.CONSUMER_KEY, FakeOAuthServiceProvider.CONSUMER_SECRET); } private static void addApprovalParamsConsumer(BasicOAuthStore base) { addConsumer( base, GADGET_URL_APPROVAL_PARAMS, FakeGadgetSpecFactory.SERVICE_NAME, FakeOAuthServiceProvider.CONSUMER_KEY, FakeOAuthServiceProvider.CONSUMER_SECRET); } private static void addConsumer( BasicOAuthStore base, String gadgetUrl, String serviceName, String consumerKey, String consumerSecret) { BasicOAuthStoreConsumerIndex providerKey = new BasicOAuthStoreConsumerIndex(); providerKey.setGadgetUri(gadgetUrl); providerKey.setServiceName(serviceName); BasicOAuthStoreConsumerKeyAndSecret kas = new BasicOAuthStoreConsumerKeyAndSecret( consumerKey, consumerSecret, KeyType.HMAC_SYMMETRIC, null, null); base.setConsumerKeyAndSecret(providerKey, kas); } private static void addDefaultKey(BasicOAuthStore base) { BasicOAuthStoreConsumerKeyAndSecret defaultKey = new BasicOAuthStoreConsumerKeyAndSecret( "signedfetch", FakeOAuthServiceProvider.PRIVATE_KEY_TEXT, KeyType.RSA_PRIVATE, "foo", null); base.setDefaultKey(defaultKey); } /** * Builds gadget token for testing a service with parameters in the query. */ public static SecurityToken getNormalSecurityToken(String owner, String viewer) throws Exception { return getSecurityToken(owner, viewer, GADGET_URL); } /** * Builds gadget token for testing services without a key. */ public static SecurityToken getNokeySecurityToken(String owner, String viewer) throws Exception { return getSecurityToken(owner, viewer, GADGET_URL_NO_KEY); } /** * Builds gadget token for testing a service that wants parameters in a header. */ public static SecurityToken getHeaderSecurityToken(String owner, String viewer) throws Exception { return getSecurityToken(owner, viewer, GADGET_URL_HEADER); } /** * Builds gadget token for testing a service that wants parameters in the request body. */ public static SecurityToken getBodySecurityToken(String owner, String viewer) throws Exception { return getSecurityToken(owner, viewer, GADGET_URL_BODY); } public static SecurityToken getSecurityToken(String owner, String viewer, String gadget) throws Exception { return new BasicSecurityToken(owner, viewer, "app", "container.com", gadget, "0", "default", GADGET_MAKE_REQUEST_URL, null); } @After public void tearDown() throws Exception { } /** Client that does OAuth and sends opensocial_* params */ private MakeRequestClient makeNonSocialClient(String owner, String viewer, String gadget) throws Exception { SecurityToken securityToken = getSecurityToken(owner, viewer, gadget); MakeRequestClient client = new MakeRequestClient(securityToken, fetcherConfig, serviceProvider, FakeGadgetSpecFactory.SERVICE_NAME); client.getBaseArgs().setSignOwner(true); client.getBaseArgs().setSignViewer(true); return client; } /** Client that does OAuth and does not send opensocial_* params */ private MakeRequestClient makeStrictNonSocialClient(String owner, String viewer, String gadget) throws Exception { SecurityToken securityToken = getSecurityToken(owner, viewer, gadget); return new MakeRequestClient(securityToken, fetcherConfig, serviceProvider, FakeGadgetSpecFactory.SERVICE_NAME); } private MakeRequestClient makeSocialOAuthClient(String owner, String viewer, String gadget) throws Exception { SecurityToken securityToken = getSecurityToken(owner, viewer, gadget); MakeRequestClient client = new MakeRequestClient(securityToken, fetcherConfig, serviceProvider, FakeGadgetSpecFactory.SERVICE_NAME); client.getBaseArgs().setUseToken(UseToken.IF_AVAILABLE); return client; } private MakeRequestClient makeSignedFetchClient(String owner, String viewer, String gadget) throws Exception { SecurityToken securityToken = getSecurityToken(owner, viewer, gadget); MakeRequestClient client = new MakeRequestClient(securityToken, fetcherConfig, serviceProvider, null); client.setBaseArgs(client.makeSignedFetchArguments()); return client; } @Test public void testOAuthFlow() throws Exception { MakeRequestClient client = makeNonSocialClient("owner", "owner", GADGET_URL); HttpResponse response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL); assertEquals("", response.getResponseAsString()); client.approveToken("user_data=hello-oauth"); response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL); assertEquals("User data is hello-oauth", response.getResponseAsString()); checkEmptyLog(); } @Test public void testOAuthFlow_withCallbackVerifier() throws Exception { fetcherConfig = new OAuthFetcherConfig( new BasicBlobCrypter("abcdefghijklmnop".getBytes()), getOAuthStore(base), clock, createRealCallbackGenerator(), false); MakeRequestClient client = makeNonSocialClient("owner", "owner", GADGET_URL); HttpResponse response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL); assertEquals("", response.getResponseAsString()); client.approveToken("user_data=hello-oauth"); response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL); assertEquals("User data is hello-oauth", response.getResponseAsString()); checkEmptyLog(); } @Test public void testOAuthFlow_badCallbackVerifier() throws Exception { fetcherConfig = new OAuthFetcherConfig( new BasicBlobCrypter("abcdefghijklmnop".getBytes()), getOAuthStore(base), clock, createRealCallbackGenerator(), false); MakeRequestClient client = makeNonSocialClient("owner", "owner", GADGET_URL); HttpResponse response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL); assertEquals("", response.getResponseAsString()); client.approveToken("user_data=hello-oauth"); client.setReceivedCallbackUrl("nonsense"); response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL); assertEquals("", response.getResponseAsString()); assertNotNull(response.getMetadata().get("oauthErrorText")); client.approveToken("user_data=try-again"); response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL); assertEquals("User data is try-again", response.getResponseAsString()); } @Test public void testOAuthFlow_tokenReused() throws Exception { MakeRequestClient client = makeNonSocialClient("owner", "owner", GADGET_URL); HttpResponse response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL); assertEquals("", response.getResponseAsString()); client.approveToken("user_data=hello-oauth"); response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL); assertEquals("User data is hello-oauth", response.getResponseAsString()); // Check out what happens if the client-side oauth state vanishes. MakeRequestClient client2 = makeNonSocialClient("owner", "owner", GADGET_URL); response = client2.sendGet(FakeOAuthServiceProvider.RESOURCE_URL); assertEquals("User data is hello-oauth", response.getResponseAsString()); } @Test public void testOAuthFlow_unauthUser() throws Exception { MakeRequestClient client = makeNonSocialClient(null, null, GADGET_URL); HttpResponse response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL); assertEquals("", response.getResponseAsString()); assertEquals(403, response.getHttpStatusCode()); assertEquals(-1, response.getCacheTtl()); assertEquals(OAuthError.UNAUTHENTICATED.name(), response.getMetadata().get("oauthError")); } @Test public void testOAuthFlow_noViewer() throws Exception { for (boolean secureOwner : Arrays.asList(true, false)) { // Test both with/without secure owner pages fetcherConfig = new OAuthFetcherConfig( new BasicBlobCrypter("abcdefghijklmnop".getBytes()), getOAuthStore(base), clock, callbackGenerator, secureOwner); MakeRequestClient client = makeNonSocialClient("owner", null, GADGET_URL); HttpResponse response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL); assertEquals("", response.getResponseAsString()); assertEquals(403, response.getHttpStatusCode()); assertEquals(-1, response.getCacheTtl()); assertEquals(OAuthError.UNAUTHENTICATED.name(), response.getMetadata().get("oauthError")); } } @Test public void testOAuthFlow_noSpec() throws Exception { fetcherConfig = new OAuthFetcherConfig( new BasicBlobCrypter("abcdefghijklmnop".getBytes()), getOAuthStore(base, null), clock, callbackGenerator, false); MakeRequestClient client = makeNonSocialClient("owner", "owner", GADGET_URL); setNoSpecOptions(client); HttpResponse response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL); assertEquals("", response.getResponseAsString()); client.approveToken("user_data=hello-oauth"); response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL); assertEquals("User data is hello-oauth", response.getResponseAsString()); checkEmptyLog(); } private void setNoSpecOptions(MakeRequestClient client) { client.getBaseArgs().setRequestOption(OAuthArguments.PROGRAMMATIC_CONFIG_PARAM, "true"); client.getBaseArgs().setRequestOption(OAuthArguments.PARAM_LOCATION_PARAM, "uri-query"); client.getBaseArgs().setRequestOption(OAuthArguments.REQUEST_METHOD_PARAM, "GET"); client.getBaseArgs().setRequestOption(OAuthArguments.REQUEST_TOKEN_URL_PARAM, FakeOAuthServiceProvider.REQUEST_TOKEN_URL); client.getBaseArgs().setRequestOption(OAuthArguments.ACCESS_TOKEN_URL_PARAM, FakeOAuthServiceProvider.ACCESS_TOKEN_URL); client.getBaseArgs().setRequestOption(OAuthArguments.AUTHORIZATION_URL_PARAM, FakeOAuthServiceProvider.APPROVAL_URL); } @Test public void testOAuthFlow_noSpecNoRequestTokenUrl() throws Exception { fetcherConfig = new OAuthFetcherConfig( new BasicBlobCrypter("abcdefghijklmnop".getBytes()), getOAuthStore(base, null), clock, null, false); MakeRequestClient client = makeNonSocialClient("owner", "owner", GADGET_URL); setNoSpecOptions(client); client.getBaseArgs().removeRequestOption(OAuthArguments.REQUEST_TOKEN_URL_PARAM); HttpResponse response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL); assertEquals("", response.getResponseAsString()); assertEquals(403, response.getHttpStatusCode()); assertEquals(OAuthError.BAD_OAUTH_TOKEN_URL.name(), response.getMetadata().get("oauthError")); String errorText = response.getMetadata().get("oauthErrorText"); assertNotNull(errorText); checkStringContains("should report no request token url", errorText, "No request token URL specified"); } @Test public void testOAuthFlow_noSpecNoAccessTokenUrl() throws Exception { fetcherConfig = new OAuthFetcherConfig( new BasicBlobCrypter("abcdefghijklmnop".getBytes()), getOAuthStore(base, null), clock, callbackGenerator, false); MakeRequestClient client = makeNonSocialClient("owner", "owner", GADGET_URL); setNoSpecOptions(client); client.getBaseArgs().removeRequestOption(OAuthArguments.ACCESS_TOKEN_URL_PARAM); // Get the request token HttpResponse response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL); // try to swap for access token response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL); assertEquals("", response.getResponseAsString()); assertEquals(403, response.getHttpStatusCode()); assertEquals(OAuthError.BAD_OAUTH_TOKEN_URL.name(), response.getMetadata().get("oauthError")); String errorText = response.getMetadata().get("oauthErrorText"); assertNotNull(errorText); checkStringContains("should report no access token url", errorText, "No access token URL specified"); } @Test public void testOAuthFlow_noSpecNoApprovalUrl() throws Exception { fetcherConfig = new OAuthFetcherConfig( new BasicBlobCrypter("abcdefghijklmnop".getBytes()), getOAuthStore(base, null), clock, callbackGenerator, false); MakeRequestClient client = makeNonSocialClient("owner", "owner", GADGET_URL); setNoSpecOptions(client); client.getBaseArgs().removeRequestOption(OAuthArguments.AUTHORIZATION_URL_PARAM); HttpResponse response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL); assertEquals("", response.getResponseAsString()); assertEquals(403, response.getHttpStatusCode()); assertEquals(OAuthError.BAD_OAUTH_TOKEN_URL.name(), response.getMetadata().get("oauthError")); String errorText = response.getMetadata().get("oauthErrorText"); assertNotNull(errorText); checkStringContains("should report no authorization url", errorText, "No authorization URL specified"); } @Test public void testOAuthFlow_noSpecAuthHeader() throws Exception { serviceProvider.setParamLocation(OAuthParamLocation.AUTH_HEADER); fetcherConfig = new OAuthFetcherConfig( new BasicBlobCrypter("abcdefghijklmnop".getBytes()), getOAuthStore(base, null), clock, callbackGenerator, false); MakeRequestClient client = makeNonSocialClient("owner", "owner", GADGET_URL); setNoSpecOptions(client); client.getBaseArgs().setRequestOption(OAuthArguments.PARAM_LOCATION_PARAM, "auth-header"); HttpResponse response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL); assertEquals("", response.getResponseAsString()); client.approveToken("user_data=hello-oauth"); response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL); assertEquals("User data is hello-oauth", response.getResponseAsString()); checkEmptyLog(); } @Test public void testOAuthFlow_noSpecPostBody() throws Exception { serviceProvider.setParamLocation(OAuthParamLocation.POST_BODY); fetcherConfig = new OAuthFetcherConfig( new BasicBlobCrypter("abcdefghijklmnop".getBytes()), getOAuthStore(base, null), clock, callbackGenerator, false); MakeRequestClient client = makeNonSocialClient("owner", "owner", GADGET_URL); setNoSpecOptions(client); client.getBaseArgs().setRequestOption(OAuthArguments.REQUEST_METHOD_PARAM, "POST"); client.getBaseArgs().setRequestOption(OAuthArguments.PARAM_LOCATION_PARAM, "post-body"); HttpResponse response = client.sendFormPost(FakeOAuthServiceProvider.RESOURCE_URL, ""); assertEquals("", response.getResponseAsString()); client.approveToken("user_data=hello-oauth"); response = client.sendFormPost(FakeOAuthServiceProvider.RESOURCE_URL, ""); assertEquals("User data is hello-oauth", response.getResponseAsString()); checkEmptyLog(); } @Test public void testOAuthFlow_noSpecPostBodyAndHeader() throws Exception { serviceProvider.setParamLocation(OAuthParamLocation.POST_BODY); serviceProvider.addParamLocation(OAuthParamLocation.AUTH_HEADER); fetcherConfig = new OAuthFetcherConfig( new BasicBlobCrypter("abcdefghijklmnop".getBytes()), getOAuthStore(base, null), clock, callbackGenerator, false); MakeRequestClient client = makeNonSocialClient("owner", "owner", GADGET_URL); setNoSpecOptions(client); client.getBaseArgs().setRequestOption(OAuthArguments.REQUEST_METHOD_PARAM, "POST"); client.getBaseArgs().setRequestOption(OAuthArguments.PARAM_LOCATION_PARAM, "post-body"); HttpResponse response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL); assertEquals("", response.getResponseAsString()); client.approveToken("user_data=hello-oauth"); response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL); assertEquals("User data is hello-oauth", response.getResponseAsString()); checkEmptyLog(); } @Test public void testOAuthFlow_noSpecInvalidUrl() throws Exception { fetcherConfig = new OAuthFetcherConfig( new BasicBlobCrypter("abcdefghijklmnop".getBytes()), getOAuthStore(base, null), clock, null, false); MakeRequestClient client = makeNonSocialClient("owner", "owner", GADGET_URL); setNoSpecOptions(client); client.getBaseArgs().setRequestOption(OAuthArguments.REQUEST_TOKEN_URL_PARAM, "foo"); HttpResponse response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL); assertEquals("", response.getResponseAsString()); assertEquals(403, response.getHttpStatusCode()); assertEquals(OAuthError.INVALID_URL.name(), response.getMetadata().get("oauthError")); String errorText = response.getMetadata().get("oauthErrorText"); assertNotNull(errorText); checkStringContains("should report invalid url", errorText, "Invalid URL: foo"); } @Test public void testOAuthFlow_noSpecBlankUrl() throws Exception { fetcherConfig = new OAuthFetcherConfig( new BasicBlobCrypter("abcdefghijklmnop".getBytes()), getOAuthStore(base, null), clock, null, false); MakeRequestClient client = makeNonSocialClient("owner", "owner", GADGET_URL); setNoSpecOptions(client); client.getBaseArgs().setRequestOption(OAuthArguments.REQUEST_TOKEN_URL_PARAM, ""); HttpResponse response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL); assertEquals("", response.getResponseAsString()); assertEquals(403, response.getHttpStatusCode()); assertEquals(OAuthError.INVALID_URL.name(), response.getMetadata().get("oauthError")); String errorText = response.getMetadata().get("oauthErrorText"); assertNotNull(errorText); checkStringContains("should report invalid url", errorText, "Invalid URL: "); } @Test public void testAccessTokenNotUsedForSocialPage() throws Exception { MakeRequestClient client = makeNonSocialClient("owner", "owner", GADGET_URL); HttpResponse response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL); assertEquals("", response.getResponseAsString()); client.approveToken("user_data=hello-oauth"); response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL); assertEquals("User data is hello-oauth", response.getResponseAsString()); MakeRequestClient friend = makeNonSocialClient("owner", "friend", GADGET_URL); response = friend.sendGet(FakeOAuthServiceProvider.RESOURCE_URL); assertEquals("", response.getResponseAsString()); assertEquals(403, response.getHttpStatusCode()); assertEquals(OAuthError.NOT_OWNER.name(), response.getMetadata().get("oauthError")); } @Test public void testAccessTokenOkForSecureOwnerPage() throws Exception { fetcherConfig = new OAuthFetcherConfig( new BasicBlobCrypter("abcdefghijklmnop".getBytes()), getOAuthStore(base), clock, callbackGenerator, true); MakeRequestClient client = makeNonSocialClient("owner", "owner", GADGET_URL); HttpResponse response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL); assertEquals("", response.getResponseAsString()); client.approveToken("user_data=hello-oauth"); response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL); assertEquals("User data is hello-oauth", response.getResponseAsString()); MakeRequestClient friend = makeNonSocialClient("owner", "friend", GADGET_URL); response = friend.sendGet(FakeOAuthServiceProvider.RESOURCE_URL); assertEquals("", response.getResponseAsString()); assertEquals(200, response.getHttpStatusCode()); } @Test public void testParamsInHeader() throws Exception { serviceProvider.setParamLocation(OAuthParamLocation.AUTH_HEADER); MakeRequestClient client = makeNonSocialClient("owner", "owner", GADGET_URL_HEADER); HttpResponse response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL); assertEquals("", response.getResponseAsString()); client.approveToken("user_data=hello-oauth"); response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL); assertEquals("User data is hello-oauth", response.getResponseAsString()); String aznHeader = response.getHeader(FakeOAuthServiceProvider.AUTHZ_ECHO_HEADER); assertNotNull(aznHeader); Assert.assertNotSame("azn header: " + aznHeader, aznHeader.indexOf("OAuth"), -1); } @Test public void testParamsInBody() throws Exception { serviceProvider.setParamLocation(OAuthParamLocation.POST_BODY); MakeRequestClient client = makeNonSocialClient("owner", "owner", GADGET_URL_BODY); HttpResponse response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL); assertEquals("", response.getResponseAsString()); client.approveToken("user_data=hello-oauth"); response = client.sendFormPost(FakeOAuthServiceProvider.RESOURCE_URL, ""); assertEquals("User data is hello-oauth", response.getResponseAsString()); String echoedBody = response.getHeader(FakeOAuthServiceProvider.BODY_ECHO_HEADER); assertNotNull(echoedBody); Assert.assertNotSame("body: " + echoedBody, echoedBody.indexOf("oauth_consumer_key="), -1); } @Test public void testParamsInBody_withExtraParams() throws Exception { serviceProvider.setParamLocation(OAuthParamLocation.POST_BODY); MakeRequestClient client = makeNonSocialClient("owner", "owner", GADGET_URL_BODY); HttpResponse response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL); assertEquals("", response.getResponseAsString()); client.approveToken("user_data=hello-oauth"); response = client.sendFormPost(FakeOAuthServiceProvider.RESOURCE_URL, "foo=bar&foo=baz"); assertEquals("User data is hello-oauth", response.getResponseAsString()); String echoedBody = response.getHeader(FakeOAuthServiceProvider.BODY_ECHO_HEADER); assertNotNull(echoedBody); Assert.assertNotSame("body: " + echoedBody, echoedBody.indexOf("oauth_consumer_key="), -1); Assert.assertNotSame("body: " + echoedBody, echoedBody.indexOf("foo=bar&foo=baz"), -1); } @Test public void testParamsInBody_forGetRequest() throws Exception { serviceProvider.setParamLocation(OAuthParamLocation.POST_BODY); serviceProvider.addParamLocation(OAuthParamLocation.AUTH_HEADER); MakeRequestClient client = makeNonSocialClient("owner", "owner", GADGET_URL_BODY); HttpResponse response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL); assertEquals("", response.getResponseAsString()); client.approveToken("user_data=hello-oauth"); response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL); assertEquals("User data is hello-oauth", response.getResponseAsString()); String aznHeader = response.getHeader(FakeOAuthServiceProvider.AUTHZ_ECHO_HEADER); assertNotNull(aznHeader); Assert.assertNotSame("azn header: " + aznHeader, aznHeader.indexOf("OAuth"), -1); } @Test public void testParamsInBody_forGetRequestStrictSp() throws Exception { serviceProvider.setParamLocation(OAuthParamLocation.POST_BODY); MakeRequestClient client = makeNonSocialClient("owner", "owner", GADGET_URL_BODY); HttpResponse response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL); assertEquals("", response.getResponseAsString()); client.approveToken("user_data=hello-oauth"); response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL); assertEquals("", response.getResponseAsString()); assertEquals(HttpResponse.SC_FORBIDDEN, response.getHttpStatusCode()); assertEquals("parameter_absent", response.getMetadata().get("oauthError")); assertNull(response.getMetadata().get("oauthApprovalUrl")); } @Test public void testRevokedAccessToken() throws Exception { MakeRequestClient client = makeNonSocialClient("owner", "owner", GADGET_URL); HttpResponse response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL); assertEquals("", response.getResponseAsString()); client.approveToken("user_data=hello-oauth"); response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL + "?cachebust=1"); assertEquals("User data is hello-oauth", response.getResponseAsString()); serviceProvider.revokeAllAccessTokens(); response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL + "?cachebust=2"); assertEquals("", response.getResponseAsString()); assertNotNull(response.getMetadata().get("oauthApprovalUrl")); assertNull("Should not return oauthError for revoked token", response.getMetadata().get("oauthError")); String errorText = response.getMetadata().get("oauthErrorText"); assertNotNull(errorText); checkStringContains("should return original request", errorText, "GET /data?cachebust=2\n"); checkStringContains("should return signed request", errorText, "GET /data?cachebust=2&"); checkStringContains("should remove secret", errorText, "oauth_token_secret=REMOVED"); checkStringContains("should return response", errorText, "HTTP/1.1 401"); checkStringContains("should return response", errorText, "oauth_problem=\"token_revoked\""); client.approveToken("user_data=reapproved"); response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL + "?cachebust=3"); assertEquals("User data is reapproved", response.getResponseAsString()); } @Test public void testError401() throws Exception { serviceProvider.setVagueErrors(true); MakeRequestClient client = makeNonSocialClient("owner", "owner", GADGET_URL); HttpResponse response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL); assertEquals("", response.getResponseAsString()); client.approveToken("user_data=hello-oauth"); response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL + "?cachebust=1"); assertEquals("User data is hello-oauth", response.getResponseAsString()); serviceProvider.revokeAllAccessTokens(); response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL + "?cachebust=2"); checkLogContains("GET /data?cachebust=2"); checkLogContains("HTTP/1.1 401"); assertEquals("", response.getResponseAsString()); assertNotNull(response.getMetadata().get("oauthApprovalUrl")); client.approveToken("user_data=reapproved"); response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL + "?cachebust=3"); assertEquals("User data is reapproved", response.getResponseAsString()); } @Test public void testUnknownConsumerKey() throws Exception { SecurityToken securityToken = getSecurityToken("owner", "owner", GADGET_URL_NO_KEY); MakeRequestClient client = new MakeRequestClient(securityToken, fetcherConfig, serviceProvider, FakeGadgetSpecFactory.SERVICE_NAME_NO_KEY); HttpResponse response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL); assertEquals("", response.getResponseAsString()); Map<String, String> metadata = response.getMetadata(); assertNotNull(metadata); assertEquals("consumer_key_unknown", metadata.get("oauthError")); String errorText = response.getMetadata().get("oauthErrorText"); checkStringContains("oauthErrorText mismatch", errorText, "Service provider rejected request"); checkStringContains("oauthErrorText mismatch", errorText, "oauth_problem_advice=\"invalid%20consumer%3A%20garbage_key\""); checkStringContains("should return original request", errorText, "GET /data\n"); checkStringContains("should return request token request", errorText, "GET /request?param=foo&"); } @Test public void testBrokenRequestTokenResponse() throws Exception { SecurityToken securityToken = getSecurityToken("owner", "owner", GADGET_URL_BAD_OAUTH_URL); MakeRequestClient client = new MakeRequestClient(securityToken, fetcherConfig, serviceProvider, FakeGadgetSpecFactory.SERVICE_NAME); HttpResponse response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL); assertEquals(403, response.getHttpStatusCode()); assertEquals("", response.getResponseAsString()); Map<String, String> metadata = response.getMetadata(); assertNotNull(metadata); assertEquals("MISSING_OAUTH_PARAMETER", metadata.get("oauthError")); String errorText = response.getMetadata().get("oauthErrorText"); checkStringContains("oauthErrorText mismatch", errorText, "No oauth_token returned from service provider"); checkStringContains("oauthErrorText mismatch", errorText, "GET /echo?mary_had_a_little_lamb"); } @Test public void testBrokenAccessTokenResponse() throws Exception { SecurityToken securityToken = getSecurityToken("owner", "owner", GADGET_URL_BAD_OAUTH_URL); MakeRequestClient client = new MakeRequestClient(securityToken, fetcherConfig, serviceProvider, FakeGadgetSpecFactory.SERVICE_NAME); // This lets us skip the access token step client.getBaseArgs().setRequestToken("reqtoken"); client.getBaseArgs().setRequestTokenSecret("reqtokensecret"); HttpResponse response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL); assertEquals(403, response.getHttpStatusCode()); assertEquals("", response.getResponseAsString()); Map<String, String> metadata = response.getMetadata(); assertNotNull(metadata); assertEquals("MISSING_OAUTH_PARAMETER", metadata.get("oauthError")); String errorText = response.getMetadata().get("oauthErrorText"); checkStringContains("oauthErrorText mismatch", errorText, "No oauth_token_secret returned from service provider"); checkStringContains("oauthErrorText mismatch", errorText, "with_fleece_as_white_as_snow"); } @Test public void testExtraApprovalParams() throws Exception { MakeRequestClient client = makeNonSocialClient("owner", "owner", GADGET_URL_APPROVAL_PARAMS); HttpResponse response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL); assertEquals("", response.getResponseAsString()); String approvalUrl = response.getMetadata().get("oauthApprovalUrl"); Assert.assertSame(approvalUrl, 0, approvalUrl.indexOf( "http://www.example.com/authorize?oauth_callback=foo&oauth_token=")); client.approveToken("user_data=hello-oauth"); response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL); assertEquals("User data is hello-oauth", response.getResponseAsString()); checkEmptyLog(); } @Test public void testError403() throws Exception { serviceProvider.setVagueErrors(true); SecurityToken securityToken = getSecurityToken("owner", "owner", GADGET_URL_NO_KEY); MakeRequestClient client = new MakeRequestClient(securityToken, fetcherConfig, serviceProvider, FakeGadgetSpecFactory.SERVICE_NAME_NO_KEY); HttpResponse response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL); assertEquals("", response.getResponseAsString()); Map<String, String> metadata = response.getMetadata(); assertNotNull(metadata); assertEquals("MISSING_OAUTH_PARAMETER", metadata.get("oauthError")); checkStringContains("oauthErrorText mismatch", metadata.get("oauthErrorText"), "some vague error"); checkStringContains("oauthErrorText mismatch", metadata.get("oauthErrorText"), "HTTP/1.1 403"); checkLogContains("HTTP/1.1 403"); checkLogContains("GET /request"); checkLogContains("some vague error"); } @Test public void testError404() throws Exception { MakeRequestClient client = makeNonSocialClient("owner", "owner", GADGET_URL); HttpResponse response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL); assertEquals("", response.getResponseAsString()); client.approveToken("user_data=hello-oauth"); response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL + "?cachebust=1"); assertEquals("User data is hello-oauth", response.getResponseAsString()); response = client.sendGet(FakeOAuthServiceProvider.NOT_FOUND_URL); assertEquals("not found", response.getResponseAsString()); assertEquals(404, response.getHttpStatusCode()); response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL + "?cachebust=3"); assertEquals("User data is hello-oauth", response.getResponseAsString()); } @Test public void testError400() throws Exception { MakeRequestClient client = makeNonSocialClient("owner", "owner", GADGET_URL); HttpResponse response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL); assertEquals("", response.getResponseAsString()); client.approveToken("user_data=hello-oauth"); response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL + "?cachebust=1"); assertEquals("User data is hello-oauth", response.getResponseAsString()); response = client.sendGet(FakeOAuthServiceProvider.ERROR_400); assertEquals("bad request", response.getResponseAsString()); assertEquals(400, response.getHttpStatusCode()); response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL + "?cachebust=3"); assertEquals("User data is hello-oauth", response.getResponseAsString()); } @Test public void testConsumerThrottled() throws Exception { assertEquals(0, serviceProvider.getRequestTokenCount()); assertEquals(0, serviceProvider.getAccessTokenCount()); assertEquals(0, serviceProvider.getResourceAccessCount()); MakeRequestClient client = makeNonSocialClient("owner", "owner", GADGET_URL); HttpResponse response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL); assertEquals("", response.getResponseAsString()); assertEquals(1, serviceProvider.getRequestTokenCount()); assertEquals(0, serviceProvider.getAccessTokenCount()); assertEquals(0, serviceProvider.getResourceAccessCount()); client.approveToken("user_data=hello-oauth"); response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL); assertEquals("User data is hello-oauth", response.getResponseAsString()); assertEquals(1, serviceProvider.getRequestTokenCount()); assertEquals(1, serviceProvider.getAccessTokenCount()); assertEquals(1, serviceProvider.getResourceAccessCount()); response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL + "?cachebust=1"); assertEquals("User data is hello-oauth", response.getResponseAsString()); assertEquals(1, serviceProvider.getRequestTokenCount()); assertEquals(1, serviceProvider.getAccessTokenCount()); assertEquals(2, serviceProvider.getResourceAccessCount()); serviceProvider.setConsumersThrottled(true); response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL + "?cachebust=2"); assertEquals("", response.getResponseAsString()); Map<String, String> metadata = response.getMetadata(); assertNotNull(metadata); assertEquals("consumer_key_refused", metadata.get("oauthError")); checkStringContains("oauthErrorText mismatch", metadata.get("oauthErrorText"), "Service provider rejected request"); 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&oauth_body_hash=2jm"); assertEquals(1, serviceProvider.getRequestTokenCount()); assertEquals(1, serviceProvider.getAccessTokenCount()); assertEquals(3, serviceProvider.getResourceAccessCount()); serviceProvider.setConsumersThrottled(false); client.clearState(); response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL + "?cachebust=3"); assertEquals("User data is hello-oauth", response.getResponseAsString()); assertEquals(1, serviceProvider.getRequestTokenCount()); assertEquals(1, serviceProvider.getAccessTokenCount()); assertEquals(4, serviceProvider.getResourceAccessCount()); } @Test public void testConsumerThrottled_vagueErrors() throws Exception { serviceProvider.setVagueErrors(true); assertEquals(0, serviceProvider.getRequestTokenCount()); assertEquals(0, serviceProvider.getAccessTokenCount()); assertEquals(0, serviceProvider.getResourceAccessCount()); MakeRequestClient client = makeNonSocialClient("owner", "owner", GADGET_URL); HttpResponse response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL); assertEquals("", response.getResponseAsString()); assertEquals(1, serviceProvider.getRequestTokenCount()); assertEquals(0, serviceProvider.getAccessTokenCount()); assertEquals(0, serviceProvider.getResourceAccessCount()); client.approveToken("user_data=hello-oauth"); response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL); assertEquals("User data is hello-oauth", response.getResponseAsString()); assertEquals(1, serviceProvider.getRequestTokenCount()); assertEquals(1, serviceProvider.getAccessTokenCount()); assertEquals(1, serviceProvider.getResourceAccessCount()); response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL + "?cachebust=1"); assertEquals("User data is hello-oauth", response.getResponseAsString()); assertEquals(1, serviceProvider.getRequestTokenCount()); assertEquals(1, serviceProvider.getAccessTokenCount()); assertEquals(2, serviceProvider.getResourceAccessCount()); serviceProvider.setConsumersThrottled(true); response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL + "?cachebust=2"); assertEquals(403, response.getHttpStatusCode()); assertEquals("some vague error", response.getResponseAsString()); Map<String, String> metadata = response.getMetadata(); assertNotNull(metadata); assertNull(metadata.get("oauthError")); 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&oauth_body_hash=2jm"); assertEquals(1, serviceProvider.getRequestTokenCount()); assertEquals(1, serviceProvider.getAccessTokenCount()); assertEquals(3, serviceProvider.getResourceAccessCount()); serviceProvider.setConsumersThrottled(false); client.clearState(); // remove any cached oauth tokens response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL + "?cachebust=3"); assertEquals("User data is hello-oauth", response.getResponseAsString()); assertEquals(1, serviceProvider.getRequestTokenCount()); assertEquals(1, serviceProvider.getAccessTokenCount()); assertEquals(4, serviceProvider.getResourceAccessCount()); } @Test public void testSocialOAuth_tokenRevoked() throws Exception { MakeRequestClient client = makeNonSocialClient("owner", "owner", GADGET_URL); HttpResponse response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL); assertEquals("", response.getResponseAsString()); client.approveToken("user_data=hello-oauth"); response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL); assertEquals("User data is hello-oauth", response.getResponseAsString()); serviceProvider.revokeAllAccessTokens(); assertEquals(0, base.getAccessTokenRemoveCount()); client = makeSocialOAuthClient("owner", "owner", GADGET_URL); response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL + "?cb=1"); assertEquals("", response.getResponseAsString()); assertEquals(1, base.getAccessTokenRemoveCount()); } @Test public void testWrongServiceName() throws Exception { SecurityToken securityToken = getSecurityToken("owner", "owner", GADGET_URL); MakeRequestClient client = new MakeRequestClient(securityToken, fetcherConfig, serviceProvider, "nosuchservice"); HttpResponse response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL); Map<String, String> metadata = response.getMetadata(); assertNull(metadata.get("oauthApprovalUrl")); assertEquals("BAD_OAUTH_CONFIGURATION", metadata.get("oauthError")); String errorText = metadata.get("oauthErrorText"); assertTrue(errorText, errorText.startsWith( "Failed to retrieve OAuth URLs, spec for gadget does " + "not contain OAuth service nosuchservice. Known services: testservice")); } @Test public void testPreapprovedToken() throws Exception { MakeRequestClient client = makeNonSocialClient("owner", "owner", GADGET_URL); TokenPair reqToken = serviceProvider.getPreapprovedToken("preapproved"); client.getBaseArgs().setRequestToken(reqToken.token); client.getBaseArgs().setRequestTokenSecret(reqToken.secret); HttpResponse response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL); assertEquals("User data is preapproved", response.getResponseAsString()); assertEquals(0, serviceProvider.getRequestTokenCount()); assertEquals(1, serviceProvider.getAccessTokenCount()); assertEquals(1, serviceProvider.getResourceAccessCount()); response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL + "?cachebust=1"); assertEquals("User data is preapproved", response.getResponseAsString()); assertEquals(0, serviceProvider.getRequestTokenCount()); assertEquals(1, serviceProvider.getAccessTokenCount()); assertEquals(2, serviceProvider.getResourceAccessCount()); response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL + "?cachebust=2"); assertEquals("User data is preapproved", response.getResponseAsString()); assertEquals(0, serviceProvider.getRequestTokenCount()); assertEquals(1, serviceProvider.getAccessTokenCount()); assertEquals(3, serviceProvider.getResourceAccessCount()); } @Test public void testPreapprovedToken_invalid() throws Exception { MakeRequestClient client = makeNonSocialClient("owner", "owner", GADGET_URL); client.getBaseArgs().setRequestToken("garbage"); client.getBaseArgs().setRequestTokenSecret("garbage"); HttpResponse response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL); assertEquals("", response.getResponseAsString()); assertEquals(1, serviceProvider.getRequestTokenCount()); assertEquals(1, serviceProvider.getAccessTokenCount()); assertEquals(0, serviceProvider.getResourceAccessCount()); client.approveToken("user_data=hello-oauth"); response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL); assertEquals("User data is hello-oauth", response.getResponseAsString()); assertEquals(1, serviceProvider.getRequestTokenCount()); assertEquals(2, serviceProvider.getAccessTokenCount()); assertEquals(1, serviceProvider.getResourceAccessCount()); } @Test public void testPreapprovedToken_notUsedIfAccessTokenExists() throws Exception { MakeRequestClient client = makeNonSocialClient("owner", "owner", GADGET_URL); TokenPair reqToken = serviceProvider.getPreapprovedToken("preapproved"); client.getBaseArgs().setRequestToken(reqToken.token); client.getBaseArgs().setRequestTokenSecret(reqToken.secret); HttpResponse response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL); assertEquals("User data is preapproved", response.getResponseAsString()); assertEquals(0, serviceProvider.getRequestTokenCount()); assertEquals(1, serviceProvider.getAccessTokenCount()); assertEquals(1, serviceProvider.getResourceAccessCount()); MakeRequestClient client2 = makeNonSocialClient("owner", "owner", GADGET_URL); response = client2.sendGet(FakeOAuthServiceProvider.RESOURCE_URL + "?cachebust=1"); assertEquals("User data is preapproved", response.getResponseAsString()); assertEquals(0, serviceProvider.getRequestTokenCount()); assertEquals(1, serviceProvider.getAccessTokenCount()); assertEquals(2, serviceProvider.getResourceAccessCount()); } @Test public void testSignedFetchParametersSet() throws Exception { MakeRequestClient client = makeSignedFetchClient("o", "v", "http://www.example.com/app"); HttpResponse resp = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL); List<Parameter> queryParams = OAuth.decodeForm(resp.getResponseAsString()); assertTrue(contains(queryParams, "opensocial_owner_id", "o")); assertTrue(contains(queryParams, "opensocial_viewer_id", "v")); assertTrue(contains(queryParams, "opensocial_app_id", "app")); assertTrue(contains(queryParams, OAuth.OAUTH_CONSUMER_KEY, "signedfetch")); assertTrue(contains(queryParams, "xoauth_signature_publickey", "foo")); assertTrue(contains(queryParams, "xoauth_public_key", "foo")); assertFalse(contains(queryParams, "opensocial_proxied_content", "1")); } @Test public void testSignedFetch_authHeader() throws Exception { serviceProvider.setParamLocation(OAuthParamLocation.AUTH_HEADER); MakeRequestClient client = makeSignedFetchClient("o", "v", "http://www.example.com/app"); client.getBaseArgs().setRequestOption(OAuthArguments.PROGRAMMATIC_CONFIG_PARAM, "true"); client.getBaseArgs().setRequestOption(OAuthArguments.PARAM_LOCATION_PARAM, "auth-header"); HttpResponse resp = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL); String auth = resp.getHeader(FakeOAuthServiceProvider.AUTHZ_ECHO_HEADER); assertNotNull("Should have echoed authz header", auth); checkStringContains("should have opensocial params in header", auth, "opensocial_owner_id=\"o\""); } @Test public void testSignedFetchParametersSetProxiedContent() throws Exception { MakeRequestClient client = makeSignedFetchClient("o", "v", "http://www.example.com/app"); client.getBaseArgs().setProxiedContentRequest(true); HttpResponse resp = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL); List<Parameter> queryParams = OAuth.decodeForm(resp.getResponseAsString()); assertTrue(contains(queryParams, "opensocial_owner_id", "o")); assertTrue(contains(queryParams, "opensocial_viewer_id", "v")); assertTrue(contains(queryParams, "opensocial_app_id", "app")); assertTrue(contains(queryParams, OAuth.OAUTH_CONSUMER_KEY, "signedfetch")); assertTrue(contains(queryParams, "xoauth_signature_publickey", "foo")); assertTrue(contains(queryParams, "xoauth_public_key", "foo")); assertTrue(contains(queryParams, "opensocial_proxied_content", "1")); } @Test public void testPostBinaryData() throws Exception { byte[] raw = { 0, 1, 2, 3, 4, 5 }; MakeRequestClient client = makeSignedFetchClient("o", "v", "http://www.example.com/app"); HttpResponse resp = client.sendRawPost(FakeOAuthServiceProvider.RESOURCE_URL, null, raw); List<Parameter> queryParams = OAuth.decodeForm(resp.getResponseAsString()); assertTrue(contains(queryParams, "opensocial_owner_id", "o")); assertTrue(contains(queryParams, OAuth.OAUTH_CONSUMER_KEY, "signedfetch")); String echoed = resp.getHeader(FakeOAuthServiceProvider.RAW_BODY_ECHO_HEADER); byte[] echoedBytes = Base64.decodeBase64(CharsetUtil.getUtf8Bytes(echoed)); assertTrue(Arrays.equals(raw, echoedBytes)); } @Test public void testPostWeirdContentType() throws Exception { byte[] raw = { 0, 1, 2, 3, 4, 5 }; MakeRequestClient client = makeSignedFetchClient("o", "v", "http://www.example.com/app"); HttpResponse resp = client.sendRawPost(FakeOAuthServiceProvider.RESOURCE_URL, "funky-content", raw); List<Parameter> queryParams = OAuth.decodeForm(resp.getResponseAsString()); assertTrue(contains(queryParams, "opensocial_owner_id", "o")); assertTrue(contains(queryParams, OAuth.OAUTH_CONSUMER_KEY, "signedfetch")); String echoed = resp.getHeader(FakeOAuthServiceProvider.RAW_BODY_ECHO_HEADER); byte[] echoedBytes = Base64.decodeBase64(CharsetUtil.getUtf8Bytes(echoed)); assertTrue(Arrays.equals(raw, echoedBytes)); } @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(expected=RuntimeException.class) 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); } }); client.sendGetWithBody(FakeOAuthServiceProvider.RESOURCE_URL, OAuth.FORM_ENCODED, "foo=bar".getBytes()); fail("Should have thrown with oauth signature mismatch"); } @Test(expected=RuntimeException.class) 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); } }); client.sendGetWithBody(FakeOAuthServiceProvider.RESOURCE_URL, "funky-content", raw); fail("Should have thrown with body hash in form encoded request"); } @Test(expected=RuntimeException.class) public void testPostTamperedRawContent() 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); } }); client.sendRawPost(FakeOAuthServiceProvider.RESOURCE_URL, "funky-content", raw); fail("Should have thrown with oauth_body_hash mismatch"); } @Test(expected=RuntimeException.class) public void testPostTamperedFormContent() 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); } }); client.sendFormPost(FakeOAuthServiceProvider.RESOURCE_URL, "foo=bar"); fail("Should have thrown with oauth signature mismatch"); } @Test(expected=RuntimeException.class) public void testPostTamperedRemoveRawContent() 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); } }); client.sendRawPost(FakeOAuthServiceProvider.RESOURCE_URL, "funky-content", raw); fail("Should have thrown with body hash in form encoded request"); } @Test public void testSignedFetch_error401() throws Exception { assertEquals(0, base.getAccessTokenRemoveCount()); serviceProvider.setConsumerUnauthorized(true); serviceProvider.setVagueErrors(true); MakeRequestClient client = makeSignedFetchClient("o", "v", "http://www.example.com/app"); 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"); checkStringContains("Should return response", errorText, "HTTP/1.1 401"); checkStringContains("Should return response", errorText, "some vague error"); assertEquals(0, base.getAccessTokenRemoveCount()); } @Test public void testSignedFetch_error403() throws Exception { assertEquals(0, base.getAccessTokenRemoveCount()); serviceProvider.setConsumersThrottled(true); serviceProvider.setVagueErrors(true); MakeRequestClient client = makeSignedFetchClient("o", "v", "http://www.example.com/app"); 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"); checkStringContains("Should return response", errorText, "HTTP/1.1 403"); checkStringContains("Should return response", errorText, "some vague error"); assertEquals(0, base.getAccessTokenRemoveCount()); } @Test public void testSignedFetch_unnamedConsumerKey() throws Exception { BasicOAuthStoreConsumerKeyAndSecret defaultKey = new BasicOAuthStoreConsumerKeyAndSecret( null, FakeOAuthServiceProvider.PRIVATE_KEY_TEXT, KeyType.RSA_PRIVATE, "foo", null); base.setDefaultKey(defaultKey); MakeRequestClient client = makeSignedFetchClient("o", "v", "http://www.example.com/app"); HttpResponse resp = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL); List<Parameter> queryParams = OAuth.decodeForm(resp.getResponseAsString()); assertTrue(contains(queryParams, "opensocial_owner_id", "o")); assertTrue(contains(queryParams, "opensocial_viewer_id", "v")); assertTrue(contains(queryParams, "opensocial_app_id", "app")); assertTrue(contains(queryParams, OAuth.OAUTH_CONSUMER_KEY, "container.com")); assertTrue(contains(queryParams, "xoauth_signature_publickey", "foo")); assertTrue(contains(queryParams, "xoauth_public_key", "foo")); } @Test public void testSignedFetch_extraQueryParameters() throws Exception { MakeRequestClient client = makeSignedFetchClient("o", "v", "http://www.example.com/app"); HttpResponse resp = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL + "?foo=bar&foo=baz"); List<Parameter> queryParams = OAuth.decodeForm(resp.getResponseAsString()); assertTrue(contains(queryParams, "opensocial_owner_id", "o")); assertTrue(contains(queryParams, "opensocial_viewer_id", "v")); assertTrue(contains(queryParams, "opensocial_app_id", "app")); assertTrue(contains(queryParams, OAuth.OAUTH_CONSUMER_KEY, "signedfetch")); assertTrue(contains(queryParams, "xoauth_signature_publickey", "foo")); assertTrue(contains(queryParams, "xoauth_public_key", "foo")); } @Test public void testNoSignViewer() throws Exception { MakeRequestClient client = makeSignedFetchClient("o", "v", "http://www.example.com/app"); client.getBaseArgs().setSignViewer(false); HttpResponse resp = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL); List<Parameter> queryParams = OAuth.decodeForm(resp.getResponseAsString()); assertTrue(contains(queryParams, "opensocial_owner_id", "o")); assertFalse(contains(queryParams, "opensocial_viewer_id", "v")); } @Test public void testNoSignOwner() throws Exception { MakeRequestClient client = makeSignedFetchClient("o", "v", "http://www.example.com/app"); client.getBaseArgs().setSignOwner(false); HttpResponse resp = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL); List<Parameter> queryParams = OAuth.decodeForm(resp.getResponseAsString()); assertFalse(contains(queryParams, "opensocial_owner_id", "o")); assertTrue(contains(queryParams, "opensocial_viewer_id", "v")); } @Test public void testTrickyParametersInQuery() throws Exception { MakeRequestClient client = makeSignedFetchClient("o", "v", "http://www.example.com/app"); String tricky = "%6fpensocial_owner_id=gotcha"; HttpResponse resp = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL + '?' + tricky); assertEquals(OAuthError.INVALID_PARAMETER.name(), resp.getMetadata().get(OAuthResponseParams.ERROR_CODE)); checkStringContains("Wrong error text", resp.getMetadata().get("oauthErrorText"), "Invalid parameter name opensocial_owner_id, applications may not override " + "oauth, xoauth, or opensocial parameters"); } @Test public void testTrickyParametersInBody() throws Exception { MakeRequestClient client = makeSignedFetchClient("o", "v", "http://www.example.com/app"); String tricky = "%6fpensocial_owner_id=gotcha"; HttpResponse resp = client.sendFormPost(FakeOAuthServiceProvider.RESOURCE_URL, tricky); assertEquals(OAuthError.INVALID_PARAMETER.name(), resp.getMetadata().get(OAuthResponseParams.ERROR_CODE)); checkStringContains("Wrong error text", resp.getMetadata().get("oauthErrorText"), "Invalid parameter name opensocial_owner_id, applications may not override " + "oauth, xoauth, or opensocial parameters"); } @Test public void testGetNoQuery() throws Exception { MakeRequestClient client = makeSignedFetchClient("o", "v", "http://www.example.com/app"); HttpResponse resp = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL); List<Parameter> queryParams = OAuth.decodeForm(resp.getResponseAsString()); assertTrue(contains(queryParams, "opensocial_owner_id", "o")); assertTrue(contains(queryParams, "opensocial_viewer_id", "v")); } @Test public void testGetWithQuery() throws Exception { MakeRequestClient client = makeSignedFetchClient("o", "v", "http://www.example.com/app"); HttpResponse resp = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL + "?a=b"); List<Parameter> queryParams = OAuth.decodeForm(resp.getResponseAsString()); assertTrue(contains(queryParams, "a", "b")); } @Test public void testGetWithQueryMultiParam() throws Exception { MakeRequestClient client = makeSignedFetchClient("o", "v", "http://www.example.com/app"); HttpResponse resp = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL + "?a=b&a=c"); List<Parameter> queryParams = OAuth.decodeForm(resp.getResponseAsString()); assertTrue(contains(queryParams, "a", "b")); assertTrue(contains(queryParams, "a", "c")); } @Test public void testValidParameterCharacters() throws Exception { MakeRequestClient client = makeSignedFetchClient("o", "v", "http://www.example.com/app"); String weird = "~!@$*()-_[]:,./"; HttpResponse resp = client.sendGet( FakeOAuthServiceProvider.RESOURCE_URL + '?' + weird + "=foo"); List<Parameter> queryParams = OAuth.decodeForm(resp.getResponseAsString()); assertTrue(contains(queryParams, weird, "foo")); } @Test public void testPostNoQueryNoData() throws Exception { MakeRequestClient client = makeSignedFetchClient("o", "v", "http://www.example.com/app"); HttpResponse resp = client.sendFormPost(FakeOAuthServiceProvider.RESOURCE_URL, null); List<Parameter> queryParams = OAuth.decodeForm(resp.getResponseAsString()); assertTrue(contains(queryParams, "opensocial_owner_id", "o")); assertEquals("", resp.getHeader(FakeOAuthServiceProvider.BODY_ECHO_HEADER)); } @Test public void testPostWithQueryNoData() throws Exception { MakeRequestClient client = makeSignedFetchClient("o", "v", "http://www.example.com/app"); HttpResponse resp = client.sendFormPost( FakeOAuthServiceProvider.RESOURCE_URL + "?name=value", null); List<Parameter> queryParams = OAuth.decodeForm(resp.getResponseAsString()); assertTrue(contains(queryParams, "name", "value")); assertEquals("", resp.getHeader(FakeOAuthServiceProvider.BODY_ECHO_HEADER)); } @Test public void testPostNoQueryWithData() throws Exception { MakeRequestClient client = makeSignedFetchClient("o", "v", "http://www.example.com/app"); HttpResponse resp = client.sendFormPost( FakeOAuthServiceProvider.RESOURCE_URL, "name=value"); List<Parameter> queryParams = OAuth.decodeForm(resp.getResponseAsString()); assertFalse(contains(queryParams, "name", "value")); assertEquals("name=value", resp.getHeader(FakeOAuthServiceProvider.BODY_ECHO_HEADER)); } @Test public void testPostWithQueryWithData() throws Exception { MakeRequestClient client = makeSignedFetchClient("o", "v", "http://www.example.com/app"); HttpResponse resp = client.sendFormPost( FakeOAuthServiceProvider.RESOURCE_URL + "?queryName=queryValue", "name=value"); List<Parameter> queryParams = OAuth.decodeForm(resp.getResponseAsString()); assertTrue(contains(queryParams, "queryName", "queryValue")); assertEquals("name=value", resp.getHeader(FakeOAuthServiceProvider.BODY_ECHO_HEADER)); } @Test public void testStripOpenSocialParamsFromQuery() throws Exception { MakeRequestClient client = makeSignedFetchClient("o", "v", "http://www.example.com/app"); HttpResponse resp = client.sendFormPost(FakeOAuthServiceProvider.RESOURCE_URL + "?opensocial_foo=bar", null); assertEquals(OAuthError.INVALID_PARAMETER.name(), resp.getMetadata().get(OAuthResponseParams.ERROR_CODE)); checkStringContains("Wrong error text", resp.getMetadata().get("oauthErrorText"), "Invalid parameter name opensocial_foo"); } @Test public void testStripOAuthParamsFromQuery() throws Exception { MakeRequestClient client = makeSignedFetchClient("o", "v", "http://www.example.com/app"); HttpResponse resp = client.sendFormPost(FakeOAuthServiceProvider.RESOURCE_URL + "?oauth_foo=bar", "name=value"); assertEquals(OAuthError.INVALID_PARAMETER.name(), resp.getMetadata().get(OAuthResponseParams.ERROR_CODE)); checkStringContains("Wrong error text", resp.getMetadata().get("oauthErrorText"), "Invalid parameter name oauth_foo"); } @Test public void testStripOpenSocialParamsFromBody() throws Exception { MakeRequestClient client = makeSignedFetchClient("o", "v", "http://www.example.com/app"); HttpResponse resp = client.sendFormPost(FakeOAuthServiceProvider.RESOURCE_URL, "opensocial_foo=bar"); assertEquals(OAuthError.INVALID_PARAMETER.name(), resp.getMetadata().get(OAuthResponseParams.ERROR_CODE)); checkStringContains("Wrong error text", resp.getMetadata().get("oauthErrorText"), "Invalid parameter name opensocial_foo"); } @Test public void testStripOAuthParamsFromBody() throws Exception { MakeRequestClient client = makeSignedFetchClient("o", "v", "http://www.example.com/app"); HttpResponse resp = client.sendFormPost(FakeOAuthServiceProvider.RESOURCE_URL, "oauth_foo=bar"); assertEquals(OAuthError.INVALID_PARAMETER.name(), resp.getMetadata().get(OAuthResponseParams.ERROR_CODE)); checkStringContains("Wrong error text", resp.getMetadata().get("oauthErrorText"), "Invalid parameter name oauth_foo"); } // Test we can refresh an expired access token. @Test public void testAccessTokenExpires_onClient() throws Exception { serviceProvider.setSessionExtension(true); MakeRequestClient client = makeNonSocialClient("owner", "owner", GADGET_URL); HttpResponse response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL); assertEquals("", response.getResponseAsString()); client.approveToken("user_data=hello-oauth"); response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL); assertEquals("User data is hello-oauth", response.getResponseAsString()); assertEquals(1, serviceProvider.getRequestTokenCount()); assertEquals(1, serviceProvider.getAccessTokenCount()); assertEquals(1, serviceProvider.getResourceAccessCount()); clock.incrementSeconds(FakeOAuthServiceProvider.TOKEN_EXPIRATION_SECONDS + 1); response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL + "?cb=1"); assertEquals("User data is hello-oauth", response.getResponseAsString()); assertEquals(1, serviceProvider.getRequestTokenCount()); assertEquals(2, serviceProvider.getAccessTokenCount()); assertEquals(2, serviceProvider.getResourceAccessCount()); response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL + "?cb=3"); assertEquals("User data is hello-oauth", response.getResponseAsString()); assertEquals(1, serviceProvider.getRequestTokenCount()); assertEquals(2, serviceProvider.getAccessTokenCount()); assertEquals(3, serviceProvider.getResourceAccessCount()); clock.incrementSeconds(FakeOAuthServiceProvider.TOKEN_EXPIRATION_SECONDS + 1); response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL + "?cb=4"); assertEquals("User data is hello-oauth", response.getResponseAsString()); assertEquals(1, serviceProvider.getRequestTokenCount()); assertEquals(3, serviceProvider.getAccessTokenCount()); assertEquals(4, serviceProvider.getResourceAccessCount()); checkEmptyLog(); } // Tests the case where the server doesn't tell us when the token will expire. This requires // an extra round trip to discover that the token has expired. @Test public void testAccessTokenExpires_onClientNoPredictedExpiration() throws Exception { serviceProvider.setSessionExtension(true); serviceProvider.setReportExpirationTimes(false); MakeRequestClient client = makeNonSocialClient("owner", "owner", GADGET_URL); HttpResponse response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL); assertEquals("", response.getResponseAsString()); client.approveToken("user_data=hello-oauth"); response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL); assertEquals("User data is hello-oauth", response.getResponseAsString()); assertEquals(1, serviceProvider.getRequestTokenCount()); assertEquals(1, serviceProvider.getAccessTokenCount()); assertEquals(1, serviceProvider.getResourceAccessCount()); clock.incrementSeconds(FakeOAuthServiceProvider.TOKEN_EXPIRATION_SECONDS + 1); response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL + "?cb=1"); assertEquals("User data is hello-oauth", response.getResponseAsString()); assertEquals(1, serviceProvider.getRequestTokenCount()); assertEquals(2, serviceProvider.getAccessTokenCount()); assertEquals(3, serviceProvider.getResourceAccessCount()); response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL + "?cb=3"); assertEquals("User data is hello-oauth", response.getResponseAsString()); assertEquals(1, serviceProvider.getRequestTokenCount()); assertEquals(2, serviceProvider.getAccessTokenCount()); assertEquals(4, serviceProvider.getResourceAccessCount()); clock.incrementSeconds(FakeOAuthServiceProvider.TOKEN_EXPIRATION_SECONDS + 1); response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL + "?cb=4"); assertEquals("User data is hello-oauth", response.getResponseAsString()); assertEquals(1, serviceProvider.getRequestTokenCount()); assertEquals(3, serviceProvider.getAccessTokenCount()); assertEquals(6, serviceProvider.getResourceAccessCount()); } @Test public void testAccessTokenExpires_onServer() throws Exception { serviceProvider.setSessionExtension(true); MakeRequestClient client = makeNonSocialClient("owner", "owner", GADGET_URL); HttpResponse response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL); assertEquals("", response.getResponseAsString()); client.approveToken("user_data=hello-oauth"); response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL); assertEquals("User data is hello-oauth", response.getResponseAsString()); assertEquals(1, serviceProvider.getRequestTokenCount()); assertEquals(1, serviceProvider.getAccessTokenCount()); assertEquals(1, serviceProvider.getResourceAccessCount()); // clears oauthState client = makeNonSocialClient("owner", "owner", GADGET_URL); clock.incrementSeconds(FakeOAuthServiceProvider.TOKEN_EXPIRATION_SECONDS + 1); response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL + "?cb=1"); assertEquals("User data is hello-oauth", response.getResponseAsString()); assertEquals(1, serviceProvider.getRequestTokenCount()); assertEquals(2, serviceProvider.getAccessTokenCount()); assertEquals(2, serviceProvider.getResourceAccessCount()); } @Test public void testAccessTokenExpired_andRevoked() throws Exception { serviceProvider.setSessionExtension(true); MakeRequestClient client = makeNonSocialClient("owner", "owner", GADGET_URL); HttpResponse response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL); assertEquals("", response.getResponseAsString()); client.approveToken("user_data=hello-oauth"); response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL); assertEquals("User data is hello-oauth", response.getResponseAsString()); assertEquals(1, serviceProvider.getRequestTokenCount()); assertEquals(1, serviceProvider.getAccessTokenCount()); assertEquals(1, serviceProvider.getResourceAccessCount()); clock.incrementSeconds(FakeOAuthServiceProvider.TOKEN_EXPIRATION_SECONDS + 1); serviceProvider.revokeAllAccessTokens(); response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL + "?cb=1"); assertEquals("", response.getResponseAsString()); assertEquals(2, serviceProvider.getRequestTokenCount()); assertEquals(2, serviceProvider.getAccessTokenCount()); assertEquals(1, serviceProvider.getResourceAccessCount()); client.approveToken("user_data=renewed"); response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL + "?cb=1"); assertEquals(2, serviceProvider.getRequestTokenCount()); assertEquals(3, serviceProvider.getAccessTokenCount()); assertEquals(2, serviceProvider.getResourceAccessCount()); assertEquals("User data is renewed", response.getResponseAsString()); checkLogContains("oauth_token_secret=REMOVED"); } @Test public void testBadSessionHandle() throws Exception { serviceProvider.setSessionExtension(true); MakeRequestClient client = makeNonSocialClient("owner", "owner", GADGET_URL); HttpResponse response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL); assertEquals("", response.getResponseAsString()); client.approveToken("user_data=hello-oauth"); response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL); assertEquals("User data is hello-oauth", response.getResponseAsString()); assertEquals(1, serviceProvider.getRequestTokenCount()); assertEquals(1, serviceProvider.getAccessTokenCount()); assertEquals(1, serviceProvider.getResourceAccessCount()); clock.incrementSeconds(FakeOAuthServiceProvider.TOKEN_EXPIRATION_SECONDS + 1); serviceProvider.changeAllSessionHandles(); response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL + "?cb=1"); assertEquals("", response.getResponseAsString()); assertEquals(2, serviceProvider.getRequestTokenCount()); assertEquals(2, serviceProvider.getAccessTokenCount()); assertEquals(1, serviceProvider.getResourceAccessCount()); client.approveToken("user_data=renewed"); response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL + "?cb=1"); assertEquals(2, serviceProvider.getRequestTokenCount()); assertEquals(3, serviceProvider.getAccessTokenCount()); assertEquals(2, serviceProvider.getResourceAccessCount()); assertEquals("User data is renewed", response.getResponseAsString()); checkLogContains("oauth_session_handle=REMOVED"); } @Test public void testExtraParamsRejected() throws Exception { serviceProvider.setRejectExtraParams(true); MakeRequestClient client = makeNonSocialClient("owner", "owner", GADGET_URL); HttpResponse response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL); assertEquals("parameter_rejected", response.getMetadata().get("oauthError")); } @Test public void testExtraParamsSuppressed() throws Exception { serviceProvider.setRejectExtraParams(true); MakeRequestClient client = makeStrictNonSocialClient("owner", "owner", GADGET_URL); HttpResponse response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL); assertEquals("", response.getResponseAsString()); client.approveToken("user_data=hello-oauth"); response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL); assertEquals("User data is hello-oauth", response.getResponseAsString()); } @Test public void testCanRetrieveAccessTokenData() throws Exception { serviceProvider.setReturnAccessTokenData(true); MakeRequestClient client = makeNonSocialClient("owner", "owner", GADGET_URL); HttpResponse response = client.sendGet(FakeOAuthServiceProvider.ACCESS_TOKEN_URL); assertEquals("", response.getResponseAsString()); client.approveToken("user_data=hello-oauth"); response = client.sendGet(FakeOAuthServiceProvider.ACCESS_TOKEN_URL); assertEquals("application/json; charset=UTF-8", response.getHeader("Content-Type")); JSONObject json = new JSONObject(response.getResponseAsString()); assertEquals("userid value", json.get("userid")); assertEquals("xoauth_stuff value", json.get("xoauth_stuff")); response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL); assertEquals("User data is hello-oauth", response.getResponseAsString()); } @Test public void testAccessTokenData_noOAuthParams() throws Exception { serviceProvider.setReturnAccessTokenData(true); MakeRequestClient client = makeNonSocialClient("owner", "owner", GADGET_URL); HttpResponse response = client.sendGet(FakeOAuthServiceProvider.ACCESS_TOKEN_URL); assertEquals("", response.getResponseAsString()); client.approveToken("user_data=hello-oauth"); response = client.sendGet(FakeOAuthServiceProvider.ACCESS_TOKEN_URL); JSONObject json = new JSONObject(response.getResponseAsString()); assertEquals("userid value", json.get("userid")); assertEquals("xoauth_stuff value", json.get("xoauth_stuff")); assertEquals(2, json.length()); response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL); assertEquals("User data is hello-oauth", response.getResponseAsString()); } @Test(expected=RuntimeException.class) public void testAccessTokenData_noDirectRequest() throws Exception { serviceProvider.setReturnAccessTokenData(true); MakeRequestClient client = makeNonSocialClient("owner", "owner", GADGET_URL); HttpResponse response = client.sendGet(FakeOAuthServiceProvider.ACCESS_TOKEN_URL); assertEquals("", response.getResponseAsString()); client.approveToken("user_data=hello-oauth"); response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL); assertEquals("User data is hello-oauth", response.getResponseAsString()); client.sendGet(FakeOAuthServiceProvider.ACCESS_TOKEN_URL); fail("Service provider should have rejected bogus request to access token URL"); } @Test public void testNextFetchReturnsNull() throws Exception { serviceProvider.setReturnNull(true); MakeRequestClient client = makeNonSocialClient("owner", "owner", GADGET_URL); HttpResponse response = client.sendGet(FakeOAuthServiceProvider.ACCESS_TOKEN_URL); assertEquals("MISSING_SERVER_RESPONSE", response.getMetadata().get("oauthError")); assertEquals("", response.getResponseAsString()); String oauthErrorText = response.getMetadata().get("oauthErrorText"); checkStringContains("should say no response", oauthErrorText, "No response from server"); checkStringContains("should show request", oauthErrorText, "GET /request?param=foo&opensocial_owner_id=owner"); checkStringContains("should log empty response", oauthErrorText, "Received response 1:\n\n"); checkLogContains("No response from server"); checkLogContains("GET /request?param=foo&opensocial_owner_id=owner"); checkLogContains("OAuth error [MISSING_SERVER_RESPONSE, No response from server] for " + "application http://www.example.com/gadget.xml"); } @Test public void testNextFetchThrowsGadgetException() throws Exception { serviceProvider.setThrow( new GadgetException(GadgetException.Code.FAILED_TO_RETRIEVE_CONTENT, "mildly wrong")); MakeRequestClient client = makeNonSocialClient("owner", "owner", GADGET_URL); HttpResponse response = client.sendGet(FakeOAuthServiceProvider.ACCESS_TOKEN_URL); assertEquals("MISSING_SERVER_RESPONSE", response.getMetadata().get("oauthError")); assertEquals("", response.getResponseAsString()); String oauthErrorText = response.getMetadata().get("oauthErrorText"); checkStringContains("should say no response", oauthErrorText, "No response from server"); checkStringContains("should show request", oauthErrorText, "GET /request?param=foo&opensocial_owner_id=owner"); checkStringContains("should log empty response", oauthErrorText, "Received response 1:\n\n"); checkLogContains("No response from server"); checkLogContains("GET /request?param=foo&opensocial_owner_id=owner"); checkLogContains("OAuth error [MISSING_SERVER_RESPONSE, No response from server] for " + "application http://www.example.com/gadget.xml"); checkLogContains("GadgetException"); checkLogContains("mildly wrong"); } @Test public void testNextFetchThrowsRuntimeException() throws Exception { serviceProvider.setThrow(new RuntimeException("very, very wrong")); MakeRequestClient client = makeNonSocialClient("owner", "owner", GADGET_URL); try { client.sendGet(FakeOAuthServiceProvider.ACCESS_TOKEN_URL); fail("Should have thrown"); } catch (RuntimeException e) { // good } checkLogContains("OAuth fetch unexpected fatal erro"); checkLogContains("GET /request?param=foo&opensocial_owner_id=owner"); checkLogContains("OAuth error [very, very wrong] for " + "application http://www.example.com/gadget.xml"); checkLogContains("RuntimeException"); checkLogContains("very, very wrong"); } @Test public void testTrustedParams() throws Exception { serviceProvider.setCheckTrustedParams(true); MakeRequestClient client = makeNonSocialClient("owner", "owner", GADGET_URL); client.setTrustedParam("oauth_magic", "foo"); client.setTrustedParam("opensocial_magic", "bar"); client.setTrustedParam("xoauth_magic", "quux"); client.setTrustedParam("opensocial_owner_id", "overridden_opensocial_owner_id"); HttpResponse response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL); assertEquals("", response.getResponseAsString()); client.approveToken("user_data=hello-oauth"); response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL); assertEquals("User data is hello-oauth", response.getResponseAsString()); assertEquals(12, serviceProvider.getTrustedParamCount()); } /** * Test different behaviors of trusted parameters. * 1) pass two parameters with same name, the latter will win. * 2) parameter name starting with 'oauth' 'oauth' or 'opensocial'. * 3) trusted parameter can override existing parameter. */ @Test public void testTrustedParamsMisc() throws Exception { serviceProvider.setCheckTrustedParams(true); MakeRequestClient client = makeNonSocialClient("owner", "owner", GADGET_URL); client.setTrustedParam("oauth_magic", "foo"); client.setTrustedParam("opensocial_magic", "bar"); client.setTrustedParam("xoauth_magic", "quux_overridden"); client.setTrustedParam("xoauth_magic", "quux"); client.setTrustedParam("opensocial_owner_id", "overridden_opensocial_owner_id"); HttpResponse response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL); assertEquals("", response.getResponseAsString()); client.approveToken("user_data=hello-oauth"); response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL); assertEquals("User data is hello-oauth", response.getResponseAsString()); assertEquals(12, serviceProvider.getTrustedParamCount()); } /** * Test trusted parameters will always be sent when signOwner and signViewer * are false. */ @Test public void testAlwaysAppendTrustedParams() throws Exception { serviceProvider.setCheckTrustedParams(true); MakeRequestClient client = makeStrictNonSocialClient("owner", "owner", GADGET_URL); client.setTrustedParam("oauth_magic", "foo"); client.setTrustedParam("opensocial_magic", "bar"); client.setTrustedParam("xoauth_magic", "quux"); client.setTrustedParam("opensocial_owner_id", "overridden_opensocial_owner_id"); HttpResponse response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL); assertEquals("", response.getResponseAsString()); client.approveToken("user_data=hello-oauth"); response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL); assertEquals("User data is hello-oauth", response.getResponseAsString()); assertEquals(12, serviceProvider.getTrustedParamCount()); } /** * Test invalid trusted parameters which are not prefixed with 'oauth' 'xoauth' or 'opensocial'. */ @Test public void testTrustedParamsInvalidParameter() throws Exception { serviceProvider.setCheckTrustedParams(true); MakeRequestClient client = makeNonSocialClient("owner", "owner", GADGET_URL); client.setTrustedParam("oauth_magic", "foo"); client.setTrustedParam("opensocial_magic", "bar"); client.setTrustedParam("xoauth_magic", "quux"); client.setTrustedParam("opensocial_owner_id", "overridden_opensocial_owner_id"); client.setTrustedParam("invalid_trusted_parameter", "invalid"); HttpResponse response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL); assertEquals(HttpResponse.SC_FORBIDDEN, response.getHttpStatusCode()); } // Checks whether the given parameter list contains the specified // key/value pair private boolean contains(List<Parameter> params, String key, String value) { for (Parameter p : params) { if (p.getKey().equals(key) && p.getValue().equals(value)) { return true; } } 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(); for (LogRecord record : logRecords) { logText.append(record.getMessage()); if (record.getThrown() != null) { StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw); record.getThrown().printStackTrace(pw); pw.flush(); logText.append(sw.toString()); } } return logText.toString(); } private void checkLogContains(String text) { if ((logger.getLevel()!=null)&&(logger.getLevel().equals(Level.OFF))) { return; } String logText = getLogText(); if (!logText.contains(text)) { fail("Should have logged '" + text + "', instead got " + logText); } } private void checkEmptyLog() { assertEquals("", getLogText()); } private void checkStringContains(String message, String text, String expected) { if (!text.contains(expected)) { fail(message + ", expected [" + expected + "], got + [" + text + ']'); } } }