Author: etnu
Date: Fri May  2 18:08:37 2008
New Revision: 652963

URL: http://svn.apache.org/viewvc?rev=652963&view=rev
Log:
Applied SHINDIG-216 from Brian Eaton and Dirk Balfanz.


Added:
    
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/RequestSigningException.java
    
incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/oauth/FakeOAuthServiceProvider.java
    
incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/oauth/OAuthFetcherTest.java
Modified:
    incubator/shindig/trunk/config/container.js
    
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/ProxyHandler.java
    
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth/OAuthFetcher.java
    
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth/OAuthRequestParams.java

Modified: incubator/shindig/trunk/config/container.js
URL: 
http://svn.apache.org/viewvc/incubator/shindig/trunk/config/container.js?rev=652963&r1=652962&r2=652963&view=diff
==============================================================================
--- incubator/shindig/trunk/config/container.js (original)
+++ incubator/shindig/trunk/config/container.js Fri May  2 18:08:37 2008
@@ -57,7 +57,7 @@
 // See individual feature.xml files for configuration details.
 "gadgets.features" : {
   "core.io" : {
-       // Note: /proxy is an open proxy. Be careful how you explose this!
+    // Note: /proxy is an open proxy. Be careful how you explose this!
     "proxyUrl" : "proxy?url=%url%",
     "jsonProxyUrl" : "proxy?output=js"
   },
@@ -72,8 +72,8 @@
     }
   },
   "rpc" : {
-       // Path to the relay file. Automatically appended to the parent
-       // parameter if it passes input validation and is not null.
+    // Path to the relay file. Automatically appended to the parent
+    /// parameter if it passes input validation and is not null.
     // This should never be on the same host in a production environment!
     // Only use this for TESTING!
     "parentRelayUrl" : "/gadgets/files/container/rpc_relay.html",
@@ -85,12 +85,12 @@
   // Skin defaults
   "skins" : {
     "properties" : {
-         "BG_COLOR": "",
-         "BG_IMAGE": "",
-         "BG_POSITION": "",
-         "BG_REPEAT": "",
-         "FONT_COLOR": "",
-         "ANCHOR_COLOR": ""
+      "BG_COLOR": "",
+      "BG_IMAGE": "",
+      "BG_POSITION": "",
+      "BG_REPEAT": "",
+      "FONT_COLOR": "",
+      "ANCHOR_COLOR": ""
     }
   },
   "opensocial-0.7" : {

Added: 
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/RequestSigningException.java
URL: 
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/RequestSigningException.java?rev=652963&view=auto
==============================================================================
--- 
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/RequestSigningException.java
 (added)
+++ 
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/RequestSigningException.java
 Fri May  2 18:08:37 2008
@@ -0,0 +1,36 @@
+/*
+ * 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;
+
+/**
+ * Exception thrown during request signing.
+ */
+public class RequestSigningException extends GadgetException {
+
+       public RequestSigningException(Throwable cause) {
+               super(GadgetException.Code.REQUEST_SIGNING_FAILURE, cause);
+       }
+       
+       public RequestSigningException(String msg, Throwable cause) {
+               super(GadgetException.Code.REQUEST_SIGNING_FAILURE, msg, cause);
+       }
+       
+       public RequestSigningException(String msg) {
+               super(GadgetException.Code.REQUEST_SIGNING_FAILURE, msg);
+       }
+}

Modified: 
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/ProxyHandler.java
URL: 
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/ProxyHandler.java?rev=652963&r1=652962&r2=652963&view=diff
==============================================================================
--- 
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/ProxyHandler.java
 (original)
+++ 
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/ProxyHandler.java
 Fri May  2 18:08:37 2008
@@ -237,10 +237,9 @@
       RemoteContent results) {
     try {
       JSONObject resp = new JSONObject();
-      if (results != null) {
-        resp.put("body", results.getResponseAsString());
-        resp.put("rc", results.getHttpStatusCode());
-      }
+      
+      resp.put("body", results.getResponseAsString());
+      resp.put("rc", results.getHttpStatusCode());
 
       // Merge in additional response data
       for (Map.Entry<String, String> entry : results.getMetadata().entrySet()) 
{

Modified: 
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth/OAuthFetcher.java
URL: 
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth/OAuthFetcher.java?rev=652963&r1=652962&r2=652963&view=diff
==============================================================================
--- 
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth/OAuthFetcher.java
 (original)
+++ 
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth/OAuthFetcher.java
 Fri May  2 18:08:37 2008
@@ -210,7 +210,7 @@
       buildAznUrl();
       // break out of the content fetching chain, we need permission from
       // the user to do this
-      return null;
+      return buildOAuthApprovalResponse();
     } else if (needAccessToken()) {
       // This is section 6.3 of the OAuth spec
       checkCanApprove();
@@ -438,6 +438,21 @@
     aznUrl = azn.toString();
   }
 
+  private RemoteContent buildOAuthApprovalResponse() {
+    RemoteContent content = new RemoteContent(0, null, null);
+    addResponseMetadata(content);
+    return content;
+  }
+  
+  private void addResponseMetadata(RemoteContent content) {
+    if (newClientState != null) {
+      content.getMetadata().put(CLIENT_STATE, newClientState);
+    }
+    if (aznUrl != null) {
+      content.getMetadata().put(APPROVAL_URL, aznUrl);
+    }
+  }
+
   /**
    * Do we need to exchange a request token for an access token?
    */
@@ -523,12 +538,7 @@
               realRequest.getOptions()));
 
       // Track metadata on the response
-      if (newClientState != null) {
-        content.getMetadata().put(CLIENT_STATE, newClientState);
-      }
-      if (aznUrl != null) {
-        content.getMetadata().put(APPROVAL_URL, aznUrl);
-      }
+      addResponseMetadata(content);
       return content;
     } catch (UnsupportedEncodingException e) {
       throw new GadgetException(GadgetException.Code.INTERNAL_SERVER_ERROR, e);

Modified: 
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth/OAuthRequestParams.java
URL: 
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth/OAuthRequestParams.java?rev=652963&r1=652962&r2=652963&view=diff
==============================================================================
--- 
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth/OAuthRequestParams.java
 (original)
+++ 
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth/OAuthRequestParams.java
 Fri May  2 18:08:37 2008
@@ -28,9 +28,9 @@
   public static final String TOKEN_PARAM = "oauthToken";
   public static final String CLIENT_STATE_PARAM = "oauthState";
 
-  private final String serviceName;
-  private final String tokenName;
-  private final String origClientState;
+  protected final String serviceName;
+  protected final String tokenName;
+  protected final String origClientState;
 
   public OAuthRequestParams(HttpServletRequest request) {
     serviceName = request.getParameter(SERVICE_PARAM);
@@ -38,6 +38,14 @@
     origClientState = request.getParameter(CLIENT_STATE_PARAM);
   }
   
+  // Really only use this for testing, please
+  public OAuthRequestParams(String serviceName, String tokenName,
+      String origClientState) {
+    this.serviceName = serviceName;
+    this.tokenName = tokenName;
+    this.origClientState = origClientState;
+  }
+  
   public String getServiceName() {
     return serviceName;
   }

Added: 
incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/oauth/FakeOAuthServiceProvider.java
URL: 
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/oauth/FakeOAuthServiceProvider.java?rev=652963&view=auto
==============================================================================
--- 
incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/oauth/FakeOAuthServiceProvider.java
 (added)
+++ 
incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/oauth/FakeOAuthServiceProvider.java
 Fri May  2 18:08:37 2008
@@ -0,0 +1,270 @@
+/*
+ * 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 net.oauth.OAuth;
+import net.oauth.OAuthAccessor;
+import net.oauth.OAuthConsumer;
+import net.oauth.OAuthMessage;
+import net.oauth.OAuthServiceProvider;
+import net.oauth.OAuthValidator;
+import net.oauth.SimpleOAuthValidator;
+
+import org.apache.shindig.gadgets.ContentFetcher;
+import org.apache.shindig.gadgets.GadgetException;
+import org.apache.shindig.gadgets.RemoteContent;
+import org.apache.shindig.gadgets.RemoteContentRequest;
+import org.apache.shindig.util.Crypto;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+public class FakeOAuthServiceProvider implements ContentFetcher {
+
+  public final static String SP_HOST = "http://www.example.com";;
+  
+  public final static String REQUEST_TOKEN_URL =
+      SP_HOST + "/request?param=foo";
+  public final static String ACCESS_TOKEN_URL = 
+      SP_HOST + "/access";
+  public final static String APPROVAL_URL =
+      SP_HOST + "/authorize";
+  public final static String RESOURCE_URL = 
+      SP_HOST + "/data";
+  
+  public final static String CONSUMER_KEY = "consumer";
+  public final static String CONSUMER_SECRET = "secret";
+  
+  private static class TokenState {
+    String tokenSecret;
+    OAuthConsumer consumer;
+    State state;
+    String userData;
+    
+    enum State {
+      PENDING,
+      APPROVED,
+    } 
+    
+    public TokenState(String tokenSecret, OAuthConsumer consumer) {
+      this.tokenSecret = tokenSecret;
+      this.consumer = consumer;
+      this.state = State.PENDING;
+      this.userData = null;
+    }
+    
+    public static TokenState makeAccessTokenState(String tokenSecret,
+        OAuthConsumer consumer) {
+      TokenState s = new TokenState(tokenSecret, consumer);
+      s.setState(State.APPROVED);
+      return s;
+    }
+
+    public void setState(State state) {
+      this.state = state;
+    }
+    
+    public State getState() {
+      return state;
+    }
+    
+    public String getSecret() {
+      return tokenSecret;
+    }
+    
+    public void setUserData(String userData) {
+      this.userData = userData;
+    }
+    
+    public String getUserData() {
+      return userData;
+    }
+  }
+  
+  /**
+   * Table of OAuth access tokens
+   */
+  private final HashMap<String, TokenState> tokenState;
+  private final OAuthValidator validator;
+  private final OAuthConsumer consumer;
+  
+  public FakeOAuthServiceProvider() {
+    OAuthServiceProvider provider = new OAuthServiceProvider(
+        REQUEST_TOKEN_URL, APPROVAL_URL, ACCESS_TOKEN_URL);
+    consumer = new OAuthConsumer(
+        null, CONSUMER_KEY, CONSUMER_SECRET, provider);
+    tokenState = new HashMap<String, TokenState>();
+    validator = new SimpleOAuthValidator();
+  }
+  
+  public RemoteContent fetch(RemoteContentRequest request)
+      throws GadgetException {
+    String url = request.getUri().toASCIIString();
+    try {
+      if (url.startsWith(REQUEST_TOKEN_URL)) {
+        return handleRequestTokenUrl(request);
+      } else if (url.startsWith(ACCESS_TOKEN_URL)) {
+        return handleAccessTokenUrl(request);
+      } else if (url.startsWith(RESOURCE_URL)){
+        return handleResourceUrl(request);
+      }
+    } catch (Exception e) {
+      throw new GadgetException(GadgetException.Code.INTERNAL_SERVER_ERROR,
+          "Problem with request for URL " + url, e);
+    }
+    throw new GadgetException(GadgetException.Code.INTERNAL_SERVER_ERROR,
+        "Unexpected request for " + url);
+  }
+
+  private RemoteContent handleRequestTokenUrl(RemoteContentRequest request)
+      throws Exception {
+    OAuthMessage message = parseMessage(request);
+    OAuthAccessor accessor = new OAuthAccessor(consumer);
+    message.validateMessage(accessor, validator);
+    String requestToken = Crypto.getRandomString(16);
+    String requestTokenSecret = Crypto.getRandomString(16);
+    tokenState.put(
+        requestToken, new TokenState(requestToken, accessor.consumer));
+    String resp = OAuth.formEncode(OAuth.newList(
+        "oauth_token", requestToken,
+        "oauth_token_secret", requestToken));
+    return new RemoteContent(resp);
+  }
+
+  // Loosely based off net.oauth.OAuthServlet, and even more loosely related
+  // to the OAuth specification
+  private OAuthMessage parseMessage(RemoteContentRequest request) {
+    String method = request.getMethod();
+    if (!method.equals("GET")) {
+      throw new RuntimeException("Only GET supported for now");
+    }
+    ParsedUrl url = new ParsedUrl(request.getUri().toASCIIString());
+    List<OAuth.Parameter> params = new ArrayList<OAuth.Parameter>();
+    params.addAll(url.getParsedQuery());
+    String aznHeader = request.getHeader("Authorization");
+    if (aznHeader != null) {
+      for (OAuth.Parameter p : OAuthMessage.decodeAuthorization(aznHeader)) {
+        if (!p.getKey().equalsIgnoreCase("realm")) {
+          params.add(p);
+        }
+      }
+    }
+    return new OAuthMessage(method, url.getLocation(), params);
+  }
+  
+  /**
+   * Utility class for parsing OAuth URLs.
+   */
+  private static class ParsedUrl {
+    String location = null;
+    String query = null;
+    List<OAuth.Parameter> decodedQuery = null;
+    
+    public ParsedUrl(String url) {
+      int queryIndex = url.indexOf('?');
+      if (queryIndex != -1) {
+        query = url.substring(queryIndex+1, url.length());
+        location = url.substring(0, queryIndex);
+      } else {
+        location = url;
+      }
+    }
+    
+    public String getLocation() {
+      return location;
+    }
+    
+    public String getRawQuery() {
+      return query;
+    }
+    
+    public List<OAuth.Parameter> getParsedQuery() {
+      if (decodedQuery == null) {
+        if (query != null) {
+          decodedQuery = OAuth.decodeForm(query);
+        } else {
+          decodedQuery = new ArrayList<OAuth.Parameter>();
+        }
+      }
+      return decodedQuery;
+    }
+    
+    public String getQueryParam(String name) {
+      for (OAuth.Parameter p : getParsedQuery()) {
+        if (p.getKey().equals(name)) {
+          return p.getValue();
+        }
+      }
+      return null;
+    }
+  }
+  
+  /**
+   * Used to fake a browser visit to approve a token.
+   * @param url
+   * @throws Exception
+   */
+  public void browserVisit(String url) throws Exception {
+    ParsedUrl parsed = new ParsedUrl(url);
+    String requestToken = parsed.getQueryParam("oauth_token");
+    TokenState state = tokenState.get(requestToken);
+    state.setState(TokenState.State.APPROVED);
+    // Not part of the OAuth spec, just a handy thing for testing.
+    state.setUserData(parsed.getQueryParam("user_data"));
+  }
+
+  private RemoteContent handleAccessTokenUrl(RemoteContentRequest request)
+      throws Exception {
+    OAuthMessage message = parseMessage(request);
+    String requestToken = message.getParameter("oauth_token");
+    TokenState state = tokenState.get(requestToken);
+    if (state.getState() != TokenState.State.APPROVED) {
+      throw new Exception("Token not approved");
+    }
+    OAuthAccessor accessor = new OAuthAccessor(consumer);
+    accessor.requestToken = requestToken;
+    accessor.tokenSecret = state.tokenSecret;
+    message.validateMessage(accessor, validator);
+    String accessToken = Crypto.getRandomString(16);
+    String accessTokenSecret = Crypto.getRandomString(16);
+    state.tokenSecret = accessTokenSecret;
+    tokenState.put(accessToken, state);
+    tokenState.remove(requestToken);
+    String resp = OAuth.formEncode(OAuth.newList(
+        "oauth_token", accessToken,
+        "oauth_token_secret", accessTokenSecret));
+    return new RemoteContent(resp);
+  }
+
+  private RemoteContent handleResourceUrl(RemoteContentRequest request)
+      throws Exception {
+    OAuthMessage message = parseMessage(request);
+    String accessToken = message.getParameter("oauth_token");
+    TokenState state = tokenState.get(accessToken);
+    if (state.getState() != TokenState.State.APPROVED) {
+      throw new Exception("Token not approved");
+    }
+    OAuthAccessor accessor = new OAuthAccessor(consumer);
+    accessor.accessToken = accessToken;
+    accessor.tokenSecret = state.getSecret();
+    message.validateMessage(accessor, validator);
+    return new RemoteContent("User data is " + state.getUserData());
+  }
+
+}

Added: 
incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/oauth/OAuthFetcherTest.java
URL: 
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/oauth/OAuthFetcherTest.java?rev=652963&view=auto
==============================================================================
--- 
incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/oauth/OAuthFetcherTest.java
 (added)
+++ 
incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/oauth/OAuthFetcherTest.java
 Fri May  2 18:08:37 2008
@@ -0,0 +1,170 @@
+/*
+ * 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 junit.framework.TestCase;
+
+import net.oauth.OAuthServiceProvider;
+
+import org.apache.shindig.gadgets.BasicGadgetToken;
+import org.apache.shindig.gadgets.ContentFetcher;
+import org.apache.shindig.gadgets.GadgetException;
+import org.apache.shindig.gadgets.GadgetToken;
+import org.apache.shindig.gadgets.RemoteContent;
+import org.apache.shindig.gadgets.RemoteContentRequest;
+import org.apache.shindig.gadgets.oauth.OAuthStore.HttpMethod;
+import org.apache.shindig.gadgets.oauth.OAuthStore.OAuthParamLocation;
+import org.apache.shindig.gadgets.oauth.OAuthStore.SignatureType;
+import org.apache.shindig.util.BasicBlobCrypter;
+import org.apache.shindig.util.BlobCrypter;
+import org.apache.shindig.util.BlobCrypterException;
+
+import java.net.URI;
+
+/**
+ * Primitive test of the main code paths in OAuthFetcher.
+ * 
+ * This is a fairly crappy regression test, so if you find yourself wanting
+ * to modify this code, you should probably write additional test cases first.
+ */
+public class OAuthFetcherTest extends TestCase {
+
+  private GadgetOAuthTokenStore tokenStore;
+  private BlobCrypter blobCrypter;
+  private FakeOAuthServiceProvider serviceProvider;
+  
+  public static final String SERVICE_NAME = "test";
+  public static final String GADGET_URL = "http://www.example.com/gadget.xml";;
+  
+  @Override
+  protected void setUp() throws Exception {
+    super.setUp();
+    serviceProvider = new FakeOAuthServiceProvider();
+    tokenStore = getOAuthStore();
+    blobCrypter = new BasicBlobCrypter("abcdefghijklmnop".getBytes());
+  }
+  
+  /**
+   * Builds a nicely populated fake token store.
+   */
+  public static GadgetOAuthTokenStore getOAuthStore() {
+    BasicOAuthStore base = new BasicOAuthStore();
+    
+    OAuthStore.ProviderKey providerKey = new OAuthStore.ProviderKey();
+    providerKey.setGadgetUri(GADGET_URL);
+    providerKey.setServiceName(SERVICE_NAME);
+    
+    OAuthStore.ProviderInfo providerInfo = new OAuthStore.ProviderInfo();
+    OAuthServiceProvider provider = new OAuthServiceProvider(
+        FakeOAuthServiceProvider.REQUEST_TOKEN_URL,
+        FakeOAuthServiceProvider.APPROVAL_URL,
+        FakeOAuthServiceProvider.ACCESS_TOKEN_URL);
+    providerInfo.setProvider(provider);
+    providerInfo.setHttpMethod(HttpMethod.GET);
+    OAuthStore.ConsumerKeyAndSecret kas = new OAuthStore.ConsumerKeyAndSecret(
+        FakeOAuthServiceProvider.CONSUMER_KEY,
+        FakeOAuthServiceProvider.CONSUMER_SECRET,
+        OAuthStore.KeyType.HMAC_SYMMETRIC);
+    providerInfo.setKeyAndSecret(kas);
+    providerInfo.setParamLocation(OAuthParamLocation.AUTH_HEADER);
+    providerInfo.setSignatureType(SignatureType.HMAC_SHA1);
+    
+    base.setOAuthServiceProviderInfo(providerKey, providerInfo);
+    return new BasicGadgetOAuthTokenStore(base);
+  }
+  
+  /**
+   * Builds a nicely populated gadget token.
+   */
+  public static GadgetToken getGadgetToken(String owner, String viewer)
+      throws Exception {
+    return new BasicGadgetToken(owner, viewer, "app", "container.com",
+        GADGET_URL, "0");
+  }
+
+  @Override
+  protected void tearDown() throws Exception {
+    super.tearDown();
+  }
+  
+  public ContentFetcher getFetcher(
+      GadgetToken authToken, OAuthRequestParams params) throws GadgetException 
{
+    OAuthFetcher fetcher = new OAuthFetcher(
+        tokenStore, blobCrypter, serviceProvider, authToken, params);
+    fetcher.init();
+    return fetcher;
+  }
+
+  
+  public void testOAuthFlow() throws Exception {
+    ContentFetcher fetcher;
+    RemoteContentRequest request;
+    RemoteContent response;
+    
+    fetcher = getFetcher(
+        getGadgetToken("owner", "owner"),
+        new OAuthRequestParams(SERVICE_NAME, null, null));
+    request = new RemoteContentRequest(
+        new URI(FakeOAuthServiceProvider.RESOURCE_URL));
+    response = fetcher.fetch(request);
+    String clientState = response.getMetadata().get("oauthState");
+    assertNotNull(clientState);
+    String approvalUrl = response.getMetadata().get("approvalUrl");
+    assertNotNull(approvalUrl);
+    
+    serviceProvider.browserVisit(approvalUrl + "&user_data=hello-oauth");
+    
+    fetcher = getFetcher(
+        getGadgetToken("owner", "owner"),
+        new OAuthRequestParams(SERVICE_NAME, null, clientState));
+    request = new RemoteContentRequest(
+        new URI(FakeOAuthServiceProvider.RESOURCE_URL));
+    response = fetcher.fetch(request);
+    assertEquals("User data is hello-oauth", response.getResponseAsString());
+    
+    fetcher = getFetcher(
+        getGadgetToken("owner", "somebody else"),
+        new OAuthRequestParams(SERVICE_NAME, null, null));
+    request = new RemoteContentRequest(
+        new URI(FakeOAuthServiceProvider.RESOURCE_URL));
+    response = fetcher.fetch(request);
+    assertEquals("User data is hello-oauth", response.getResponseAsString());
+    
+    fetcher = getFetcher(
+        getGadgetToken("somebody else", "somebody else"),
+        new OAuthRequestParams(SERVICE_NAME, null, null));
+    request = new RemoteContentRequest(
+        new URI(FakeOAuthServiceProvider.RESOURCE_URL));
+    response = fetcher.fetch(request);
+    clientState = response.getMetadata().get("oauthState");
+    assertNotNull(clientState);
+    approvalUrl = response.getMetadata().get("approvalUrl");
+    assertNotNull(approvalUrl);
+    
+    serviceProvider.browserVisit(approvalUrl + "&user_data=somebody%20else");
+    
+    fetcher = getFetcher(
+        getGadgetToken("somebody else", "somebody else"),
+        new OAuthRequestParams(SERVICE_NAME, null, clientState));
+    request = new RemoteContentRequest(
+        new URI(FakeOAuthServiceProvider.RESOURCE_URL));
+    response = fetcher.fetch(request);
+    assertEquals("User data is somebody else", response.getResponseAsString());
+  }
+
+}


Reply via email to