You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@shindig.apache.org by be...@apache.org on 2008/09/10 03:50:42 UTC

svn commit: r693675 [1/4] - in /incubator/shindig/trunk: config/ java/common/src/main/java/org/apache/shindig/common/crypto/ java/gadgets/conf/ java/gadgets/src/main/java/org/apache/shindig/gadgets/ java/gadgets/src/main/java/org/apache/shindig/gadgets...

Author: beaton
Date: Tue Sep  9 18:50:41 2008
New Revision: 693675

URL: http://svn.apache.org/viewvc?rev=693675&view=rev
Log:
OAuth for social sites.

This unifies the OAuth and signed fetch code paths.  We also get HMAC for
signed fetch from this.


Added:
    incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth/AccessorInfo.java   (with props)
    incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth/AccessorInfoBuilder.java   (with props)
    incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth/BasicOAuthStoreConsumerIndex.java   (with props)
    incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth/BasicOAuthStoreConsumerKeyAndSecret.java   (with props)
    incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth/BasicOAuthStoreTokenIndex.java   (with props)
    incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/oauth/MakeRequestClient.java   (with props)
Removed:
    incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/SigningFetcher.java
    incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/SigningFetcherFactory.java
    incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth/BasicGadgetOAuthTokenStore.java
    incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth/OAuthFetchParams.java
    incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/SigningFetcherTest.java
    incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/oauth/BasicGadgetOAuthTokenStoreTest.java
    incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/oauth/OAuthFetchParamsTest.java
Modified:
    incubator/shindig/trunk/config/oauth.json
    incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/common/crypto/BasicBlobCrypter.java
    incubator/shindig/trunk/java/gadgets/conf/gadgets.properties
    incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/DefaultGuiceModule.java
    incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/GadgetServer.java
    incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/ContentFetcherFactory.java
    incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/HttpRequest.java
    incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth/BasicOAuthStore.java
    incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth/GadgetOAuthTokenStore.java
    incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth/OAuthArguments.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/OAuthFetcherConfig.java
    incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth/OAuthFetcherFactory.java
    incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth/OAuthStore.java
    incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/preload/HttpPreloader.java
    incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/HttpGuiceModule.java
    incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/MakeRequestHandler.java
    incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/GadgetServerTest.java
    incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/GadgetTestFixture.java
    incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/http/HttpRequestTest.java
    incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/oauth/BasicOAuthStoreTest.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/GadgetTokenStoreTest.java
    incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/oauth/OAuthArgumentsTest.java
    incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/oauth/OAuthFetcherTest.java
    incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/preload/HttpPreloaderTest.java
    incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/servlet/GadgetRenderingTaskTest.java
    incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/servlet/MakeRequestHandlerTest.java
    incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/servlet/ServletTestFixture.java
    incubator/shindig/trunk/java/server/src/test/java/org/apache/shindig/server/endtoend/EndToEndServer.java

Modified: incubator/shindig/trunk/config/oauth.json
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/config/oauth.json?rev=693675&r1=693674&r2=693675&view=diff
==============================================================================
--- incubator/shindig/trunk/config/oauth.json (original)
+++ incubator/shindig/trunk/config/oauth.json Tue Sep  9 18:50:41 2008
@@ -17,12 +17,14 @@
  * under the License.
  */
 
-{"http://localhost:8080/gadgets/files/samplecontainer/examples/oauth.xml" : {
-  "" : {
-    "consumer_key" : "gadgetConsumer",
-    "consumer_secret" : "gadgetSecret",
-    "key_type" : "HMAC_SYMMETRIC"
+{
+  "http://localhost:8080/gadgets/files/samplecontainer/examples/oauth.xml" : {
+    "" : {
+      "consumer_key" : "gadgetConsumer",
+      "consumer_secret" : "gadgetSecret",
+      "key_type" : "HMAC_SYMMETRIC"
+    }
   }
-}}
+}
   
 

Modified: incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/common/crypto/BasicBlobCrypter.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/common/crypto/BasicBlobCrypter.java?rev=693675&r1=693674&r2=693675&view=diff
==============================================================================
--- incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/common/crypto/BasicBlobCrypter.java (original)
+++ incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/common/crypto/BasicBlobCrypter.java Tue Sep  9 18:50:41 2008
@@ -18,11 +18,17 @@
  */
 package org.apache.shindig.common.crypto;
 
+import org.apache.shindig.common.util.CharsetUtil;
 import org.apache.shindig.common.util.TimeSource;
 
 import org.apache.commons.codec.binary.Base64;
 import org.apache.commons.codec.digest.DigestUtils;
 
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
 import java.io.UnsupportedEncodingException;
 import java.net.URLDecoder;
 import java.net.URLEncoder;
@@ -55,11 +61,46 @@
   private byte[] hmacKey;
   
   /**
+   * Creates a crypter based on a key in a file.  The key is the first line
+   * in the file, whitespace trimmed from either end, as UTF-8 bytes.
+   * 
+   * The following *nix command line will create an excellent key:
+   * 
+   * dd if=/dev/random bs=32 count=1  | openssl base64 > /tmp/key.txt
+   * 
+   * @throws IOException if the file can't be read.
+   */
+  public BasicBlobCrypter(File keyfile) throws IOException {
+    FileInputStream openFile = null;
+    try {
+      openFile = new FileInputStream(keyfile);
+      BufferedReader reader = new BufferedReader(
+          new InputStreamReader(openFile, CharsetUtil.UTF8));
+      String line = reader.readLine();
+      line = line.trim();
+      byte[] keyBytes = CharsetUtil.getUtf8Bytes(line);
+      init(keyBytes);
+    } finally {
+      try {
+        if (openFile != null) {
+          openFile.close();
+        }
+      } catch (IOException e) {
+        // oh well.
+      }
+    }
+  }
+  
+  /**
    * Builds a BlobCrypter from the specified master key
    * 
    * @param masterKey
    */
   public BasicBlobCrypter(byte[] masterKey) {
+    init(masterKey);
+  }
+  
+  private void init(byte[] masterKey) {
     if (masterKey.length < MASTER_KEY_MIN_LEN) {
       throw new IllegalArgumentException("Master key needs at least " +
           MASTER_KEY_MIN_LEN + " bytes");

Modified: incubator/shindig/trunk/java/gadgets/conf/gadgets.properties
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/conf/gadgets.properties?rev=693675&r1=693674&r2=693675&view=diff
==============================================================================
--- incubator/shindig/trunk/java/gadgets/conf/gadgets.properties (original)
+++ incubator/shindig/trunk/java/gadgets/conf/gadgets.properties Tue Sep  9 18:50:41 2008
@@ -3,8 +3,8 @@
 shindig.blacklist.file=
 shindig.urls.iframe.prefix=/gadgets/ifr?
 shindig.urls.js.prefix=/gadgets/js/
-shindig.signing.key-name=
-shindig.signing.key-file=
+#shindig.signing.key-name=
+#shindig.signing.key-file=
 shindig.locked-domain.enabled=false
 shindig.locked-domain.embed-host=127.0.0.1:8080
 shindig.content-rewrite.include-urls=.*

Modified: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/DefaultGuiceModule.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/DefaultGuiceModule.java?rev=693675&r1=693674&r2=693675&view=diff
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/DefaultGuiceModule.java (original)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/DefaultGuiceModule.java Tue Sep  9 18:50:41 2008
@@ -18,14 +18,23 @@
  */
 package org.apache.shindig.gadgets;
 
+import org.apache.commons.io.IOUtils;
+import org.apache.shindig.common.crypto.BasicBlobCrypter;
+import org.apache.shindig.common.crypto.BlobCrypter;
+import org.apache.shindig.common.crypto.Crypto;
 import org.apache.shindig.common.util.ResourceLoader;
 import org.apache.shindig.gadgets.http.HttpResponse;
+import org.apache.shindig.gadgets.oauth.BasicOAuthStoreConsumerKeyAndSecret;
+import org.apache.shindig.gadgets.oauth.OAuthStore;
+import org.apache.shindig.gadgets.oauth.BasicOAuthStore;
+import org.apache.shindig.gadgets.oauth.BasicOAuthStoreConsumerKeyAndSecret.KeyType;
 
 import com.google.inject.AbstractModule;
 import com.google.inject.CreationException;
 import com.google.inject.name.Names;
 import com.google.inject.spi.Message;
 
+import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.Arrays;
@@ -33,13 +42,26 @@
 import java.util.concurrent.Executor;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
+import java.util.logging.Level;
+import java.util.logging.Logger;
 
 /**
  * Creates a module to supply all of the Basic* classes
  */
 public class DefaultGuiceModule extends AbstractModule {
+
+  private static final Logger logger
+      = Logger.getLogger(DefaultGuiceModule.class.getName());
+  
   private final Properties properties;
+  private final String oauthConfig;
+  
   private final static String DEFAULT_PROPERTIES = "gadgets.properties";
+  private final static String OAUTH_CONFIG = "config/oauth.json";
+  
+  public final static String OAUTH_STATE_CRYPTER_ANNOTATION = "shindig.oauth.state-key";
+  public final static String OAUTH_SIGNING_KEY_NAME = "shindig.signing.key-name";
+  public final static String OAUTH_SIGNING_KEY_FILE = "shindig.signing.key-file";
 
   /** {@inheritDoc} */
   @Override
@@ -49,13 +71,89 @@
     ExecutorService service = Executors.newCachedThreadPool();
     bind(Executor.class).toInstance(service);
     bind(ExecutorService.class).toInstance(service);
-
+    
+    try {
+      configureOAuthStore();
+      configureOAuthStateCrypter();
+    } catch (Throwable t) {
+      // Since this happens at startup, we don't want to kill the server just
+      // because we can't initialize the OAuth config.
+      logger.log(Level.WARNING, "Failed to initialize OAuth", t);
+    }
+    
     // We perform static injection on HttpResponse for cache TTLs.
     requestStaticInjection(HttpResponse.class);
   }
+  
+  /**
+   * Create a store for OAuth consumer keys and access tokens.  By default consumer keys are read
+   * from config/oauth.json, and access tokens are stored in memory.
+   * 
+   * We read the default key from disk, in a location specified in our properties file.
+   */
+  private void configureOAuthStore() throws GadgetException {
+    BasicOAuthStore store = new BasicOAuthStore();
+    bind(OAuthStore.class).toInstance(store);
+    store.initFromConfigString(oauthConfig);
+    
+    String keyName = properties.getProperty(OAUTH_SIGNING_KEY_NAME);
+    String keyFile = properties.getProperty(OAUTH_SIGNING_KEY_FILE);
+    BasicOAuthStoreConsumerKeyAndSecret defaultKey = null;
+    if (keyFile != null) {
+      try {
+        logger.info("Loading OAuth signing key from " + keyFile);
+        String privateKey = IOUtils.toString(ResourceLoader.open(keyFile), "UTF-8");
+        privateKey = BasicOAuthStore.convertFromOpenSsl(privateKey);
+        defaultKey = new BasicOAuthStoreConsumerKeyAndSecret(null, privateKey, KeyType.RSA_PRIVATE,
+            keyName);
+      } catch (IOException e) {
+        logger.log(Level.WARNING, "Couldn't load key file " + keyFile, e);
+      }
+    }
+    if (defaultKey != null) {
+      store.setDefaultKey(defaultKey);
+    } else {
+      logger.log(Level.WARNING, "Couldn't load OAuth signing key.  To create a key, run:\n" +
+      		"  openssl req -newkey rsa:1024 -days 365 -nodes -x509 -keyout testkey.pem \\\n" +
+      		"     -out testkey.pem -subj '/CN=mytestkey'\n" +
+      		"  openssl pkcs8 -in testkey.pem -out oauthkey.pem -topk8 -nocrypt -outform PEM\n" +
+      		"\n" +
+      		"Then edit gadgets.properties and add these lines:\n" +
+      		OAUTH_SIGNING_KEY_FILE + "=<path-to-oauthkey.pem>\n" +
+      		OAUTH_SIGNING_KEY_NAME + "=mykey\n");
+    }
+  }
+  
+  /**
+   * Create a crypter to handle OAuth state.  This can be loaded from disk, if
+   * shindig.oauth.state-key-file is specified in your gadgets.properties file, or it can be
+   * created using a random key.
+   */
+  private void configureOAuthStateCrypter() {
+    // Create the oauth state crypter based on a file from disk
+    BasicBlobCrypter oauthCrypter = null;
+    String keyFileName = properties.getProperty("shindig.oauth.state-key-file");
+    if (keyFileName != null) {
+      logger.info("Loading OAuth state crypter from " + keyFileName);
+      try {
+        oauthCrypter = new BasicBlobCrypter(new File(keyFileName));
+      } catch (IOException e) {
+        logger.log(Level.SEVERE, "Failed to load " + keyFileName, e);
+      }
+    }
+    if (oauthCrypter == null) {
+      logger.info("Creating OAuth state crypter with random key");
+      oauthCrypter = new BasicBlobCrypter(
+          Crypto.getRandomBytes(BasicBlobCrypter.MASTER_KEY_MIN_LEN));
+    }        
+    bind(BlobCrypter.class).annotatedWith(
+        Names.named(OAUTH_STATE_CRYPTER_ANNOTATION))
+        .toInstance(oauthCrypter);
+  }
 
-  public DefaultGuiceModule(Properties properties) {
+  public DefaultGuiceModule(Properties properties, String oauthConfig) {
     this.properties = properties;
+    this.oauthConfig = oauthConfig;
   }
 
   /**
@@ -63,14 +161,21 @@
    */
   public DefaultGuiceModule() {
     Properties properties = null;
+    String oauthConfig = null;
     try {
       InputStream is = ResourceLoader.openResource(DEFAULT_PROPERTIES);
       properties = new Properties();
       properties.load(is);
+      try {
+        oauthConfig = ResourceLoader.getContent(OAUTH_CONFIG);
+      } catch (IOException e) {
+        logger.log(Level.WARNING, "Can't load " + OAUTH_CONFIG, e);
+      }
     } catch (IOException e) {
       throw new CreationException(Arrays.asList(
           new Message("Unable to load properties: " + DEFAULT_PROPERTIES)));
     }
     this.properties = properties;
+    this.oauthConfig = oauthConfig;
   }
 }

Modified: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/GadgetServer.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/GadgetServer.java?rev=693675&r1=693674&r2=693675&view=diff
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/GadgetServer.java (original)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/GadgetServer.java Tue Sep  9 18:50:41 2008
@@ -193,8 +193,7 @@
     public HttpResponse call() {
       try {
         HttpRequest request = new HttpRequest(Uri.fromJavaUri(preload.getHref()))
-            .setSignOwner(preload.isSignOwner())
-            .setSignViewer(preload.isSignViewer())
+            .setOAuthArguments(new OAuthArguments(preload))
             .setContainer(context.getContainer())
             .setSecurityToken(context.getToken())
             .setGadget(Uri.fromJavaUri(context.getUrl()));
@@ -202,11 +201,8 @@
           case NONE:
             return preloadFetcherFactory.get().fetch(request);
           case SIGNED:
-            return preloadFetcherFactory.getSigningFetcher(context.getToken())
-                .fetch(request);
           case OAUTH:
-            return preloadFetcherFactory.getOAuthFetcher(context.getToken(),
-                new OAuthArguments(preload)).fetch(request);
+            return preloadFetcherFactory.getOAuthFetcher(request).fetch(request);
           default:
             return HttpResponse.error();
         }

Modified: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/ContentFetcherFactory.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/ContentFetcherFactory.java?rev=693675&r1=693674&r2=693675&view=diff
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/ContentFetcherFactory.java (original)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/ContentFetcherFactory.java Tue Sep  9 18:50:41 2008
@@ -17,10 +17,7 @@
  */
 package org.apache.shindig.gadgets.http;
 
-import org.apache.shindig.auth.SecurityToken;
 import org.apache.shindig.gadgets.GadgetException;
-import org.apache.shindig.gadgets.SigningFetcherFactory;
-import org.apache.shindig.gadgets.oauth.OAuthArguments;
 import org.apache.shindig.gadgets.oauth.OAuthFetcherFactory;
 
 import com.google.inject.Inject;
@@ -34,41 +31,23 @@
 public class ContentFetcherFactory implements Provider<HttpFetcher> {
 
   private final RemoteContentFetcherFactory remoteContentFetcherFactory;
-  private final SigningFetcherFactory signingFetcherFactory;
   private final OAuthFetcherFactory oauthFetcherFactory;
 
   @Inject
   public ContentFetcherFactory(RemoteContentFetcherFactory remoteContentFetcherFactory,
-      SigningFetcherFactory signingFetcherFactory,
       OAuthFetcherFactory oauthFetcherFactory) {
-    this.signingFetcherFactory = signingFetcherFactory;
     this.remoteContentFetcherFactory = remoteContentFetcherFactory;
     this.oauthFetcherFactory = oauthFetcherFactory;
   }
 
   /**
-   * @param token
-   * @return A signing content fetcher
-   * @throws GadgetException
-   */
-  public HttpFetcher getSigningFetcher(SecurityToken token)
-      throws GadgetException {
-    return signingFetcherFactory.getSigningFetcher(
-            remoteContentFetcherFactory.get(), token);
-  }
-
-  /**
-   * @param token
-   * @param params
+   * @param request HttpRequest that will be sent through the fetcher
    * @return an OAuth fetcher
    * @throws GadgetException
    */
-  public HttpFetcher getOAuthFetcher(
-      SecurityToken token,
-      OAuthArguments params)
+  public HttpFetcher getOAuthFetcher(HttpRequest request)
       throws GadgetException {
-    return oauthFetcherFactory.getOAuthFetcher(
-        remoteContentFetcherFactory.get(), token, params);
+    return oauthFetcherFactory.getOAuthFetcher(remoteContentFetcherFactory.get(), request);
   }
 
   /**

Modified: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/HttpRequest.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/HttpRequest.java?rev=693675&r1=693674&r2=693675&view=diff
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/HttpRequest.java (original)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/HttpRequest.java Tue Sep  9 18:50:41 2008
@@ -21,6 +21,7 @@
 import org.apache.shindig.auth.SecurityToken;
 import org.apache.shindig.common.ContainerConfig;
 import org.apache.shindig.common.uri.Uri;
+import org.apache.shindig.gadgets.oauth.OAuthArguments;
 
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
@@ -62,8 +63,7 @@
 
   // For signed fetch & OAuth
   private SecurityToken securityToken;
-  private boolean signOwner = true;
-  private boolean signViewer = true;
+  private OAuthArguments oauthArguments;
 
   private String rewriteMimeType;
 
@@ -87,8 +87,9 @@
     gadget = request.gadget;
     container = request.container;
     securityToken = request.securityToken;
-    signOwner = request.signOwner;
-    signViewer = request.signViewer;
+    if (request.oauthArguments != null) {
+      oauthArguments = new OAuthArguments(request.oauthArguments);
+    }
     rewriteMimeType = request.rewriteMimeType;
   }
 
@@ -221,20 +222,10 @@
   }
 
   /**
-   * @param signOwner Whether to include the owner id when making authenticated requests. Defaults
-   * to true.
-   */
-  public HttpRequest setSignOwner(boolean signOwner) {
-    this.signOwner = signOwner;
-    return this;
-  }
-
-  /**
-   * @param signViewer Whether to include the viewer id when making authenticated requests. Defaults
-   * to true.
+   * @param oauthArguments arguments for OAuth/signed fetched
    */
-  public HttpRequest setSignViewer(boolean signViewer) {
-    this.signViewer = signViewer;
+  public HttpRequest setOAuthArguments(OAuthArguments oauthArguments) {
+    this.oauthArguments = oauthArguments;
     return this;
   }
 
@@ -366,17 +357,10 @@
   }
 
   /**
-   * @return True if the owner id should be passed in the request parameters.
-   */
-  public boolean getSignOwner() {
-    return signOwner;
-  }
-
-  /**
-   * @return True if the viewer id should be passed in the request parameters.
+   * @return arguments for OAuth and signed fetch
    */
-  public boolean getSignViewer() {
-    return signViewer;
+  public OAuthArguments getOAuthArguments() {
+    return oauthArguments;
   }
 
   /**

Added: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth/AccessorInfo.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth/AccessorInfo.java?rev=693675&view=auto
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth/AccessorInfo.java (added)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth/AccessorInfo.java Tue Sep  9 18:50:41 2008
@@ -0,0 +1,65 @@
+/*
+ * 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.OAuthAccessor;
+
+import org.apache.shindig.gadgets.oauth.OAuthStore.ConsumerInfo;
+
+public class AccessorInfo {
+  
+  public static enum HttpMethod {
+    GET,
+    POST
+  }
+
+  public static enum OAuthParamLocation {
+    AUTH_HEADER,
+    POST_BODY,
+    URI_QUERY
+  }
+  
+  private final OAuthAccessor accessor;
+  private final ConsumerInfo consumer;
+  private final HttpMethod httpMethod;
+  private final OAuthParamLocation paramLocation;
+  
+  public AccessorInfo(OAuthAccessor accessor, ConsumerInfo consumer, HttpMethod httpMethod,
+      OAuthParamLocation paramLocation) {
+    this.accessor = accessor;
+    this.consumer = consumer;
+    this.httpMethod = httpMethod;
+    this.paramLocation = paramLocation;
+  }
+
+  public OAuthParamLocation getParamLocation() {
+    return paramLocation;
+  }
+
+  public OAuthAccessor getAccessor() {
+    return accessor;
+  }
+  
+  public ConsumerInfo getConsumer() {
+    return consumer;
+  }
+
+  public HttpMethod getHttpMethod() {
+    return httpMethod;
+  }
+}
\ No newline at end of file

Propchange: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth/AccessorInfo.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth/AccessorInfoBuilder.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth/AccessorInfoBuilder.java?rev=693675&view=auto
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth/AccessorInfoBuilder.java (added)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth/AccessorInfoBuilder.java Tue Sep  9 18:50:41 2008
@@ -0,0 +1,85 @@
+/*
+ * 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.OAuthAccessor;
+
+import org.apache.shindig.gadgets.GadgetException;
+import org.apache.shindig.gadgets.oauth.AccessorInfo.HttpMethod;
+import org.apache.shindig.gadgets.oauth.AccessorInfo.OAuthParamLocation;
+import org.apache.shindig.gadgets.oauth.OAuthStore.ConsumerInfo;
+
+/**
+ * Builder for AccessorInfo object.
+ */
+public class AccessorInfoBuilder {
+
+  private ConsumerInfo consumer;
+  private String requestToken;
+  private String accessToken;
+  private String tokenSecret;
+  private OAuthParamLocation location;
+  private HttpMethod method;
+
+  public AccessorInfoBuilder() {
+  }
+  
+  public AccessorInfo create() throws GadgetException {
+    if (location == null) {
+      throw new GadgetException(GadgetException.Code.INTERNAL_SERVER_ERROR, "no location");
+    }
+    if (consumer == null) {
+      throw new GadgetException(GadgetException.Code.INTERNAL_SERVER_ERROR, "no consumer");
+    }
+    
+    OAuthAccessor accessor = new OAuthAccessor(consumer.getConsumer());
+        
+    // request token/access token/token secret can all be null, for signed fetch, or if the OAuth
+    // dance is just beginning
+    accessor.requestToken = requestToken;
+    accessor.accessToken = accessToken;
+    accessor.tokenSecret = tokenSecret;
+    return new AccessorInfo(accessor, consumer, method, location);
+  }
+
+  public void setConsumer(ConsumerInfo consumer) {
+    this.consumer = consumer;
+  }
+
+  public void setRequestToken(String requestToken) {
+    this.requestToken = requestToken;
+  }
+  
+  public void setAccessToken(String accessToken) {
+    this.accessToken = accessToken;
+  }
+
+  public void setTokenSecret(String tokenSecret) {
+    this.tokenSecret = tokenSecret;
+  }
+
+  public void setParameterLocation(OAuthParamLocation location) {
+    this.location = location;
+  }
+
+  public void setMethod(HttpMethod method) {
+    this.method = method;
+  }
+}

Propchange: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth/AccessorInfoBuilder.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth/BasicOAuthStore.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth/BasicOAuthStore.java?rev=693675&r1=693674&r2=693675&view=diff
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth/BasicOAuthStore.java (original)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth/BasicOAuthStore.java Tue Sep  9 18:50:41 2008
@@ -17,12 +17,23 @@
  */
 package org.apache.shindig.gadgets.oauth;
 
-import net.oauth.OAuthAccessor;
+import com.google.inject.Singleton;
+
+import net.oauth.OAuth;
 import net.oauth.OAuthConsumer;
 import net.oauth.OAuthServiceProvider;
 import net.oauth.signature.RSA_SHA1;
 
+import org.apache.shindig.auth.SecurityToken;
+import org.apache.shindig.gadgets.GadgetException;
+import org.apache.shindig.gadgets.oauth.BasicOAuthStoreConsumerKeyAndSecret.KeyType;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.net.URI;
+import java.net.URISyntaxException;
 import java.util.HashMap;
+import java.util.Iterator;
 import java.util.Map;
 
 /**
@@ -31,178 +42,160 @@
  * return an OAuthAccessor in {@code getOAuthAccessor} that uses that private
  * key if no consumer key and secret could be found.
  */
+@Singleton
 public class BasicOAuthStore implements OAuthStore {
 
+  private static final String CONSUMER_SECRET_KEY = "consumer_secret";
+  private static final String CONSUMER_KEY_KEY = "consumer_key";
+  private static final String KEY_TYPE_KEY = "key_type";
+  
   /**
-   * HashMap of provider and consumer information. Maps ProviderKeys (i.e.
+   * HashMap of provider and consumer information. Maps BasicOAuthStoreConsumerIndexs (i.e.
    * nickname of a service provider and the gadget that uses that nickname) to
-   * {@link OAuthStore.ProviderInfo}s.
+   * {@link BasicOAuthStoreConsumerKeyAndSecret}s.
    */
-  private Map<ProviderKey, ConsumerKeyAndSecret> consumerInfos =
-      new HashMap<ProviderKey, ConsumerKeyAndSecret>();
+  private final Map<BasicOAuthStoreConsumerIndex, BasicOAuthStoreConsumerKeyAndSecret> consumerInfos;
 
   /**
-   * HashMap of token information. Maps TokenKeys (i.e. gadget id, token
+   * HashMap of token information. Maps BasicOAuthStoreTokenIndexs (i.e. gadget id, token
    * nickname, module id, etc.) to TokenInfos (i.e. access token and token
    * secrets).
    */
-  private Map<TokenKey, TokenInfo> tokens = new HashMap<TokenKey, TokenInfo>();
-
-  /**
-   * The default consumer key to be used if no consumer key could be found in
-   * the store.
-   */
-  private String defaultConsumerKey;
-
+  private final Map<BasicOAuthStoreTokenIndex, TokenInfo> tokens;
+  
   /**
-   * The default consumer "secret" to be used when no secret could be found in
-   * store. This <b>must</b> be a PKCS8-then-Base64-encoded RSA private key.
-   * (Can also be null.)
+   * Key to use when no other key is found.
    */
-  private String defaultConsumerSecret;
+  private BasicOAuthStoreConsumerKeyAndSecret defaultKey;
 
-  /**
-   * Public constructor. Makes an OAuthStoreImpl that doesn't have a fallback
-   * RSA key to sign outgoing requests. When no consumer secret can be found,
-   * {@code getOAuthAccessor} will throw an exception.
-   */
   public BasicOAuthStore() {
-    this(null, null);
-  }
-
-  /**
-   * Public constructor. Makes an OAuthStoreImpl with a fallback
-   * RSA key to sign outgoing requests. When no consumer secret can be found,
-   * {@code getOAuthAccessor} will create an OAuthAccessor that uses the
-   * fallback RSA private key.
-   *
-   * @param consumerKey the consumer key to be used when no consumer key
-   *        can be found in the oauth store for a specific
-   *        {@code getOAuthAccessor} request.
-   * @param privateKey the RSA private key to be used when no consumer secret
-   *        can be found in the oauth store for a specific
-   *        {@code getOAuthAccessor} request.
-   */
-  public BasicOAuthStore(String consumerKey, String privateKey) {
-    defaultConsumerKey = consumerKey;
-    defaultConsumerSecret = privateKey;
+    consumerInfos = new HashMap<BasicOAuthStoreConsumerIndex, BasicOAuthStoreConsumerKeyAndSecret>();
+    tokens = new HashMap<BasicOAuthStoreTokenIndex, TokenInfo>();
   }
-
-  void setHashMapsForTesting(
-      Map<ProviderKey, ConsumerKeyAndSecret> consumers,
-      Map<TokenKey, TokenInfo> tokens) {
-    this.consumerInfos = consumers;
-    this.tokens = tokens;
-  }
-
-  /**
-   * {@inheritDoc}
-   */
-  public AccessorInfo getOAuthAccessor(TokenKey tokenKey, ProviderInfo provInfo)
-      throws OAuthNoDataException {
-
-    ProviderKey provKey = new ProviderKey();
-    provKey.setGadgetUri(tokenKey.getGadgetUri());
-    provKey.setServiceName(tokenKey.getServiceName());
-
-    AccessorInfo result = getOAuthAccessor(provKey, provInfo);
-
-    TokenInfo accessToken = tokens.get(tokenKey);
-
-    if (accessToken != null) {
-      result.getAccessor().accessToken = accessToken.getAccessToken();
-      result.getAccessor().tokenSecret = accessToken.getTokenSecret();
+  
+  public void initFromConfigString(String oauthConfigStr) throws GadgetException {
+    try {
+      JSONObject oauthConfigs = new JSONObject(oauthConfigStr);
+      for (Iterator<?> i = oauthConfigs.keys(); i.hasNext();) {
+        String url = (String) i.next();
+        URI gadgetUri = new URI(url);
+        JSONObject oauthConfig = oauthConfigs.getJSONObject(url);
+        storeConsumerInfos(gadgetUri, oauthConfig);
+      }
+    } catch (JSONException e) {
+      throw new GadgetException(GadgetException.Code.OAUTH_STORAGE_ERROR, e);
+    } catch (URISyntaxException e) {
+      throw new GadgetException(GadgetException.Code.OAUTH_STORAGE_ERROR, e);
     }
-
-    return result;
   }
 
-  private AccessorInfo getOAuthAccessor(ProviderKey providerKey,
-      ProviderInfo provInfo)
-      throws OAuthNoDataException {
-
-    if (provInfo == null) {
-      throw new OAuthNoDataException("must pass non-null provider info to" +
-          " getOAuthAccessor");
-    }
-
-    AccessorInfo result = new AccessorInfo();
-    result.setHttpMethod(provInfo.getHttpMethod());
-    result.setParamLocation(provInfo.getParamLocation());
-
-    ConsumerKeyAndSecret consumerKeyAndSecret = consumerInfos.get(providerKey);
-
-    if (consumerKeyAndSecret == null) {
-      if (defaultConsumerKey == null || defaultConsumerSecret == null) {
-        // if we don't have a fallback key and secret, that's all we can do
-        throw new OAuthNoDataException("ConsumerKeyAndSecret " +
-                                       "was null in oauth store");
-      } else {
-        // we'll use the fallback key and secret.
-        consumerKeyAndSecret =
-          new ConsumerKeyAndSecret(defaultConsumerKey,
-              defaultConsumerSecret,
-              OAuthStore.KeyType.RSA_PRIVATE);
-      }
+  private void storeConsumerInfos(URI gadgetUri, JSONObject oauthConfig)
+      throws JSONException, GadgetException {
+    for (String serviceName : JSONObject.getNames(oauthConfig)) {
+      JSONObject consumerInfo = oauthConfig.getJSONObject(serviceName);
+      storeConsumerInfo(gadgetUri, serviceName, consumerInfo);
     }
+  }
 
-    OAuthServiceProvider oauthProvider = provInfo.getProvider();
+  @SuppressWarnings("unused")
+  private void storeConsumerInfo(URI gadgetUri, String serviceName, JSONObject consumerInfo)
+      throws JSONException, GadgetException {
+    realStoreConsumerInfo(gadgetUri, serviceName, consumerInfo);
+  }
+  
+  private void realStoreConsumerInfo(URI gadgetUri, String serviceName, JSONObject consumerInfo)
+      throws JSONException {
+    String consumerSecret = consumerInfo.getString(CONSUMER_SECRET_KEY);
+    String consumerKey = consumerInfo.getString(CONSUMER_KEY_KEY);
+    String keyTypeStr = consumerInfo.getString(KEY_TYPE_KEY);
+    KeyType keyType = KeyType.HMAC_SYMMETRIC;
 
-    if (oauthProvider == null) {
-      throw new OAuthNoDataException("OAuthService provider " +
-      "was null in provider info");
-    }
-
-    boolean usePublicKeyCrypto =
-      (consumerKeyAndSecret.getKeyType() == OAuthStore.KeyType.RSA_PRIVATE);
-
-    OAuthConsumer consumer = usePublicKeyCrypto
-                             ? new OAuthConsumer(null,  // no callback URL
-                                 consumerKeyAndSecret.getConsumerKey(),
-                                 null,
-                                 oauthProvider)
-                             : new OAuthConsumer(null,  // no callback URL
-                                 consumerKeyAndSecret.getConsumerKey(),
-                                 consumerKeyAndSecret.getConsumerSecret(),
-                                 oauthProvider);
-
-    if (usePublicKeyCrypto) {
-      consumer.setProperty(RSA_SHA1.PRIVATE_KEY,
-                           consumerKeyAndSecret.getConsumerSecret());
-      result.setSignatureType(OAuthStore.SignatureType.RSA_SHA1);
-    } else {
-      result.setSignatureType(OAuthStore.SignatureType.HMAC_SHA1);
+    if (keyTypeStr.equals("RSA_PRIVATE")) {
+      keyType = KeyType.RSA_PRIVATE;
+      consumerSecret = convertFromOpenSsl(consumerSecret);
     }
 
-    result.setAccessor(new OAuthAccessor(consumer));
+    BasicOAuthStoreConsumerKeyAndSecret kas = new BasicOAuthStoreConsumerKeyAndSecret(
+        consumerKey, consumerSecret, keyType, null);
 
-    return result;
+    BasicOAuthStoreConsumerIndex index = new BasicOAuthStoreConsumerIndex();
+    index.setGadgetUri(gadgetUri.toASCIIString());
+    index.setServiceName(serviceName);
+    setConsumerKeyAndSecret(index, kas);
+  }
+  
+  // Support standard openssl keys by stripping out the headers and blank lines
+  public static String convertFromOpenSsl(String privateKey) {
+    return privateKey.replaceAll("-----[A-Z ]*-----", "").replace("\n", "");
+  }
+    
+  public void setDefaultKey(BasicOAuthStoreConsumerKeyAndSecret defaultKey) {
+    this.defaultKey = defaultKey;
   }
 
-  /**
-   * {@inheritDoc}
-   */
-  public void setOAuthConsumerKeyAndSecret(ProviderKey providerKey,
-                                           ConsumerKeyAndSecret keyAndSecret) {
+  public void setConsumerKeyAndSecret(
+      BasicOAuthStoreConsumerIndex providerKey, BasicOAuthStoreConsumerKeyAndSecret keyAndSecret) {
     consumerInfos.put(providerKey, keyAndSecret);
   }
 
-  /**
-   * {@inheritDoc}
-   */
-  public void setTokenAndSecret(TokenKey tokenKey, TokenInfo tokenInfo) {
+  public ConsumerInfo getConsumerKeyAndSecret(
+      SecurityToken securityToken, String serviceName, OAuthServiceProvider provider)
+      throws GadgetException {
+    BasicOAuthStoreConsumerIndex pk = new BasicOAuthStoreConsumerIndex();
+    pk.setGadgetUri(securityToken.getAppUrl());
+    pk.setServiceName(serviceName);
+    BasicOAuthStoreConsumerKeyAndSecret cks = consumerInfos.get(pk);
+    if (cks == null) {
+      cks = defaultKey;
+    }
+    if (cks == null) {
+      throw new GadgetException(GadgetException.Code.INTERNAL_SERVER_ERROR,
+          "No key for gadget " + securityToken.getAppUrl() + " and service " + serviceName);
+    }
+    OAuthConsumer consumer = null;
+    if (cks.getKeyType() == KeyType.RSA_PRIVATE) {
+      consumer = new OAuthConsumer(null, cks.getConsumerKey(), null, provider);
+      // The oauth.net java code has lots of magic.  By setting this property here, code thousands
+      // of lines away knows that the consumerSecret value in the consumer should be treated as
+      // an RSA private key and not an HMAC key.
+      consumer.setProperty(OAuth.OAUTH_SIGNATURE_METHOD, OAuth.RSA_SHA1);
+      consumer.setProperty(RSA_SHA1.PRIVATE_KEY, cks.getConsumerSecret());
+    } else {
+      consumer = new OAuthConsumer(null, cks.getConsumerKey(), cks.getConsumerSecret(), provider);
+      consumer.setProperty(OAuth.OAUTH_SIGNATURE_METHOD, OAuth.HMAC_SHA1);
+    }
+    return new ConsumerInfo(consumer, cks.getKeyName());
+  }
+  
+  private BasicOAuthStoreTokenIndex makeBasicOAuthStoreTokenIndex(
+      SecurityToken securityToken, String serviceName, String tokenName) {
+    BasicOAuthStoreTokenIndex tokenKey = new BasicOAuthStoreTokenIndex();
+    tokenKey.setGadgetUri(securityToken.getAppUrl());
+    tokenKey.setModuleId(securityToken.getModuleId());
+    tokenKey.setServiceName(serviceName);
+    tokenKey.setTokenName(tokenName);
+    tokenKey.setUserId(securityToken.getViewerId());
+    return tokenKey;
+  }
+
+  public TokenInfo getTokenInfo(SecurityToken securityToken, ConsumerInfo consumerInfo,
+      String serviceName, String tokenName) {
+    BasicOAuthStoreTokenIndex tokenKey =
+        makeBasicOAuthStoreTokenIndex(securityToken, serviceName, tokenName);
+    return tokens.get(tokenKey);
+  }
+
+  public void setTokenInfo(SecurityToken securityToken, ConsumerInfo consumerInfo,
+      String serviceName, String tokenName, TokenInfo tokenInfo) {
+    BasicOAuthStoreTokenIndex tokenKey =
+        makeBasicOAuthStoreTokenIndex(securityToken, serviceName, tokenName);
     tokens.put(tokenKey, tokenInfo);
   }
 
-  /**
-   * {@inheritDoc}
-   */
-  public void removeToken(TokenKey tokenKey) throws OAuthStoreException,
-      OAuthNoDataException {
-    if (tokens.containsKey(tokenKey)) {
-      tokens.remove(tokenKey);
-    } else {
-      throw new OAuthNoDataException("Could not find access token");
-    }
+  public void removeToken(SecurityToken securityToken, ConsumerInfo consumerInfo,
+      String serviceName, String tokenName) {
+    BasicOAuthStoreTokenIndex tokenKey =
+        makeBasicOAuthStoreTokenIndex(securityToken, serviceName, tokenName);
+    tokens.remove(tokenKey);    
   }
 }

Added: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth/BasicOAuthStoreConsumerIndex.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth/BasicOAuthStoreConsumerIndex.java?rev=693675&view=auto
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth/BasicOAuthStoreConsumerIndex.java (added)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth/BasicOAuthStoreConsumerIndex.java Tue Sep  9 18:50:41 2008
@@ -0,0 +1,67 @@
+/*
+ * 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;
+
+/**
+ * Index into the token store by  
+ */
+public class BasicOAuthStoreConsumerIndex {
+  private String gadgetUri;
+  private String serviceName;
+
+  public String getGadgetUri() {
+    return gadgetUri;
+  }
+  public void setGadgetUri(String gadgetUri) {
+    this.gadgetUri = gadgetUri;
+  }
+  public String getServiceName() {
+    return serviceName;
+  }
+  public void setServiceName(String serviceName) {
+    this.serviceName = serviceName;
+  }
+
+  @Override
+  public int hashCode() {
+    final int prime = 31;
+    int result = 1;
+    result =
+        prime * result + ((gadgetUri == null) ? 0 : gadgetUri.hashCode());
+    result =
+        prime * result + ((serviceName == null) ? 0 : serviceName.hashCode());
+    return result;
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    if (this == obj) return true;
+    if (obj == null) return false;
+    if (getClass() != obj.getClass()) return false;
+    final BasicOAuthStoreConsumerIndex other = (BasicOAuthStoreConsumerIndex) obj;
+    if (gadgetUri == null) {
+      if (other.gadgetUri != null) return false;
+    } else if (!gadgetUri.equals(other.gadgetUri)) return false;
+    if (serviceName == null) {
+      if (other.serviceName != null) return false;
+    } else if (!serviceName.equals(other.serviceName)) return false;
+    return true;
+  }
+}

Propchange: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth/BasicOAuthStoreConsumerIndex.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth/BasicOAuthStoreConsumerKeyAndSecret.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth/BasicOAuthStoreConsumerKeyAndSecret.java?rev=693675&view=auto
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth/BasicOAuthStoreConsumerKeyAndSecret.java (added)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth/BasicOAuthStoreConsumerKeyAndSecret.java Tue Sep  9 18:50:41 2008
@@ -0,0 +1,63 @@
+/*
+ * 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;
+
+/**
+ * @author beaton@google.com (Your Name Here)
+ *
+ */
+public class BasicOAuthStoreConsumerKeyAndSecret {
+
+  public static enum KeyType { HMAC_SYMMETRIC, RSA_PRIVATE }
+
+  /** Value for oauth_consumer_key */
+  private final String consumerKey;
+
+  /** HMAC secret, or RSA private key, depending on keyType */
+  private final String consumerSecret;
+
+  /** Type of key */
+  private final KeyType keyType;
+
+  /** Name of public key to use with xoauth_public_key parameter.  May be null */
+  private final String keyName;
+
+  public BasicOAuthStoreConsumerKeyAndSecret(String key, String secret, KeyType type, String name) {
+    consumerKey = key;
+    consumerSecret = secret;
+    keyType = type;
+    keyName = name;
+  }
+  
+  public String getConsumerKey() {
+    return consumerKey;
+  }
+  
+  public String getConsumerSecret() {
+    return consumerSecret;
+  }
+  
+  public KeyType getKeyType() {
+    return keyType;
+  }
+  
+  public String getKeyName() {
+    return keyName;
+  }
+}

Propchange: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth/BasicOAuthStoreConsumerKeyAndSecret.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth/BasicOAuthStoreTokenIndex.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth/BasicOAuthStoreTokenIndex.java?rev=693675&view=auto
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth/BasicOAuthStoreTokenIndex.java (added)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth/BasicOAuthStoreTokenIndex.java Tue Sep  9 18:50:41 2008
@@ -0,0 +1,100 @@
+/*
+ * 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;
+
+/**
+ * 
+ */
+public class BasicOAuthStoreTokenIndex {
+
+  private String userId;
+  private String gadgetUri;
+  private long moduleId;
+  private String tokenName;
+  private String serviceName;
+
+  public String getUserId() {
+    return userId;
+  }
+  public void setUserId(String userId) {
+    this.userId = userId;
+  }
+  public String getGadgetUri() {
+    return gadgetUri;
+  }
+  public void setGadgetUri(String gadgetUri) {
+    this.gadgetUri = gadgetUri;
+  }
+  public long getModuleId() {
+    return moduleId;
+  }
+  public void setModuleId(long moduleId) {
+    this.moduleId = moduleId;
+  }
+  public String getTokenName() {
+    return tokenName;
+  }
+  public void setTokenName(String tokenName) {
+    this.tokenName = tokenName;
+  }
+  public String getServiceName() {
+    return serviceName;
+  }
+  public void setServiceName(String serviceName) {
+    this.serviceName = serviceName;
+  }
+
+  @Override
+  public int hashCode() {
+    final int prime = 31;
+    int result = 1;
+    result =
+        prime * result + ((gadgetUri == null) ? 0 : gadgetUri.hashCode());
+    result = prime * result + (int) (moduleId ^ (moduleId >>> 32));
+    result =
+        prime * result + ((serviceName == null) ? 0 : serviceName.hashCode());
+    result =
+        prime * result + ((tokenName == null) ? 0 : tokenName.hashCode());
+    result = prime * result + ((userId == null) ? 0 : userId.hashCode());
+    return result;
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    if (this == obj) return true;
+    if (obj == null) return false;
+    if (getClass() != obj.getClass()) return false;
+    final BasicOAuthStoreTokenIndex other = (BasicOAuthStoreTokenIndex) obj;
+    if (gadgetUri == null) {
+      if (other.gadgetUri != null) return false;
+    } else if (!gadgetUri.equals(other.gadgetUri)) return false;
+    if (moduleId != other.moduleId) return false;
+    if (serviceName == null) {
+      if (other.serviceName != null) return false;
+    } else if (!serviceName.equals(other.serviceName)) return false;
+    if (tokenName == null) {
+      if (other.tokenName != null) return false;
+    } else if (!tokenName.equals(other.tokenName)) return false;
+    if (userId == null) {
+      if (other.userId != null) return false;
+    } else if (!userId.equals(other.userId)) return false;
+    return true;
+  }
+}

Propchange: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth/BasicOAuthStoreTokenIndex.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth/GadgetOAuthTokenStore.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth/GadgetOAuthTokenStore.java?rev=693675&r1=693674&r2=693675&view=diff
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth/GadgetOAuthTokenStore.java (original)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth/GadgetOAuthTokenStore.java Tue Sep  9 18:50:41 2008
@@ -17,18 +17,26 @@
  */
 package org.apache.shindig.gadgets.oauth;
 
+import com.google.inject.Inject;
+
 import net.oauth.OAuthServiceProvider;
 
+import org.apache.commons.lang.StringUtils;
+import org.apache.shindig.auth.SecurityToken;
 import org.apache.shindig.gadgets.GadgetException;
 import org.apache.shindig.gadgets.GadgetSpecFactory;
+import org.apache.shindig.gadgets.oauth.AccessorInfo.HttpMethod;
+import org.apache.shindig.gadgets.oauth.AccessorInfo.OAuthParamLocation;
+import org.apache.shindig.gadgets.oauth.OAuthStore.ConsumerInfo;
+import org.apache.shindig.gadgets.oauth.OAuthStore.TokenInfo;
 import org.apache.shindig.gadgets.spec.GadgetSpec;
 import org.apache.shindig.gadgets.spec.OAuthService;
 import org.apache.shindig.gadgets.spec.OAuthSpec;
+import org.apache.shindig.gadgets.spec.OAuthService.Location;
+import org.apache.shindig.gadgets.spec.OAuthService.Method;
 
 import java.net.URI;
 import java.net.URISyntaxException;
-import java.util.Iterator;
-import java.util.logging.Logger;
 
 /**
  * Higher-level interface that allows callers to store and retrieve
@@ -38,33 +46,7 @@
  */
 public class GadgetOAuthTokenStore {
 
-  /**
-   * Internal class used to communicate results of parsing the gadget spec
-   * between methods.
-   */
-  static class GadgetInfo {
-    private String serviceName;
-    private OAuthStore.ProviderInfo providerInfo;
-
-    public String getServiceName() {
-      return serviceName;
-    }
-    public void setServiceName(String serviceName) {
-      this.serviceName = serviceName;
-    }
-    public OAuthStore.ProviderInfo getProviderInfo() {
-      return providerInfo;
-    }
-    public void setProviderInfo(OAuthStore.ProviderInfo providerInfo) {
-      this.providerInfo = providerInfo;
-    }
-  }
-
-  private static final Logger log =
-      Logger.getLogger(GadgetOAuthTokenStore.class.getName());
-
   private final OAuthStore store;
-
   private final GadgetSpecFactory specFactory;
 
   /**
@@ -73,201 +55,199 @@
    * @param store an {@link OAuthStore} that can store and retrieve OAuth
    *              tokens, as well as information about service providers.
    */
-  public GadgetOAuthTokenStore(OAuthStore store,
-      GadgetSpecFactory specFactory) {
+  @Inject
+  public GadgetOAuthTokenStore(OAuthStore store, GadgetSpecFactory specFactory) {
     this.store = store;
     this.specFactory = specFactory;
   }
 
   /**
-   * Stores a negotiated consumer key and secret in the gadget store.
-   * The "secret" can either be a consumer secret in the strict OAuth sense,
-   * or it can be a PKCS8-then-Base64 encoded private key that we'll be using
-   * with this service provider.
-   *
-   * @param gadgetUrl the URL of the gadget
-   * @param serviceName the service provider with whom we have negotiated a
-   *                    consumer key and secret.
-   * @throws OAuthStoreException if there is a problem talking to the
-   *                             backend store.
-   * @throws OAuthNoDataException if there is no data about this service
-   *                              provider stored for this gadget.
-   */
-  public void storeConsumerKeyAndSecret(
-      URI gadgetUrl,
-      String serviceName,
-      OAuthStore.ConsumerKeyAndSecret keyAndSecret)
-        throws OAuthStoreException, OAuthNoDataException {
-
-    OAuthStore.ProviderKey providerKey = new OAuthStore.ProviderKey();
-    providerKey.setGadgetUri(gadgetUrl.toString());
-    providerKey.setServiceName(serviceName);
-
-    store.setOAuthConsumerKeyAndSecret(providerKey, keyAndSecret);
-  }
-
-  /**
-   * Stores an access token in the OAuth Data Store.
-   * @param tokenKey information about the Gadget storing the token.
-   * @param tokenInfo the TokenInfo to be stored in the OAuth data store.
-   * @throws OAuthStoreException
+   * Retrieve an AccessorInfo and OAuthAccessor that are ready for signing OAuthMessages.  To do
+   * this, we need to figure out:
+   * 
+   * - what consumer key/secret to use for signing.
+   * - if an access token should be used for the request, and if so what it is.   *   
+   * - the OAuth request/authorization/access URLs.
+   * - what HTTP method to use for request token and access token requests
+   * - where the OAuth parameters are located.
+   * 
+   * Note that most of that work gets skipped for signed fetch, we just look up the consumer key
+   * and secret for that.  Signed fetch always sticks the parameters in the query string.
    */
-  public void storeTokenKeyAndSecret(OAuthStore.TokenKey tokenKey,
-                                     OAuthStore.TokenInfo tokenInfo)
-      throws OAuthStoreException {
+  public AccessorInfo getOAuthAccessor(SecurityToken securityToken,
+      OAuthArguments arguments, OAuthClientState clientState) throws GadgetException {
+    
+    AccessorInfoBuilder accessorBuilder = new AccessorInfoBuilder();
 
-    if (isEmpty(tokenKey.getGadgetUri())) {
-      throw new IllegalArgumentException("found empty gadget URI in TokenKey");
+    // Does the gadget spec tell us any details about the service provider, like where to put the
+    // OAuth parameters and what methods to use for their URLs?
+    OAuthServiceProvider provider = null;
+    if (arguments.mayUseToken()) {
+      provider = lookupSpecInfo(securityToken, arguments, accessorBuilder);
+    } else {
+      // This is plain old signed fetch.
+      accessorBuilder.setParameterLocation(AccessorInfo.OAuthParamLocation.URI_QUERY);
     }
-
-    if (isEmpty(tokenKey.getUserId())) {
-      throw new IllegalArgumentException("found empty userId in TokenKey");
+    
+    // What consumer key/secret should we use?
+    ConsumerInfo consumer = store.getConsumerKeyAndSecret(
+        securityToken, arguments.getServiceName(), provider);
+    accessorBuilder.setConsumer(consumer);
+
+    // Should we use the OAuth access token?  We never do this unless the client allows it, and
+    // if owner == viewer.
+    if (arguments.mayUseToken()
+        && securityToken.getOwnerId() != null
+        && securityToken.getViewerId().equals(securityToken.getOwnerId())) {  
+      lookupToken(securityToken, consumer, arguments, clientState, accessorBuilder);
+    }
+   
+    return accessorBuilder.create();
+  }
+  
+  /**
+   * Lookup information contained in the gadget spec.
+   */
+  private OAuthServiceProvider lookupSpecInfo(SecurityToken securityToken, OAuthArguments arguments,
+      AccessorInfoBuilder accessorBuilder) throws GadgetException {
+    GadgetSpec spec = findSpec(securityToken, arguments);
+    OAuthSpec oauthSpec = spec.getModulePrefs().getOAuthSpec();
+    if (oauthSpec == null) {
+      throw oauthNotFoundEx(securityToken);
     }
-
-    store.setTokenAndSecret(tokenKey, tokenInfo);
-  }
-
-  /**
-   * Removes an access token from the OAuth data store.
-   *
-   * @return true if the token is removed, false if the token was not found.
-   * @throws OAuthStoreException if we can't communicate with the token store.
-   */
-  public boolean removeToken(OAuthStore.TokenKey tokenKey)
-      throws OAuthStoreException {
-    try {
-      store.removeToken(tokenKey);
-      return true;
-    } catch (OAuthNoDataException e) {
-      return false;
+    OAuthService service = oauthSpec.getServices().get(arguments.getServiceName());
+    if (service == null) {
+      throw serviceNotFoundEx(securityToken, oauthSpec, arguments.getServiceName());
     }
+    // In theory some one could specify different parameter locations for request token and
+    // access token requests, but that's probably not useful.  We just use the request token
+    // rules for everything.
+    accessorBuilder.setParameterLocation(getStoreLocation(service.getRequestUrl().location));
+    accessorBuilder.setMethod(getStoreMethod(service.getRequestUrl().method));
+    OAuthServiceProvider provider = new OAuthServiceProvider(
+        service.getRequestUrl().url.toASCIIString(),
+        service.getAuthorizationUrl().toASCIIString(),
+        service.getAccessUrl().url.toASCIIString());
+    return provider;
   }
-
+  
   /**
-   * Retrieve an OAuthAccessor that is ready to sign OAuthMessages.
-   *
-   * @param tokenKey information about the gadget retrieving the accessor.
-   *
-   * @return an OAuthAccessorInfo containing an OAuthAccessor (which can be
-   *         passed to an OAuthMessage.sign method), as well as httpMethod and
-   *         signatureType fields.
-   * @throws OAuthNoDataException if the token couldn't be found
-   * @throws OAuthStoreException if an error occurred accessing the data
-   *                             store.
+   * Figure out the OAuth token that should be used with this request.  We check for this in three
+   * places.  In order of priority:
+   * 
+   * 1) From information we cached on the client.
+   *    We encrypt the token and cache on the client for performance.
+   *    
+   * 2) From information we have in our persistent state.
+   *    We persist the token server-side so we can look it up if necessary.
+   *    
+   * 3) From information the gadget developer tells us to use (a preapproved request token.)
+   *    Gadgets can be initialized with preapproved request tokens.  If the user tells the service
+   *    provider they want to add a gadget to a gadget container site, the service provider can
+   *    create a preapproved request token for that site and pass it to the gadget as a user
+   *    preference.
+   * @throws GadgetException 
    */
-  public OAuthStore.AccessorInfo getOAuthAccessor(OAuthStore.TokenKey tokenKey,
-      boolean bypassSpecCache)
+  private void lookupToken(SecurityToken securityToken, ConsumerInfo consumerInfo,
+      OAuthArguments arguments, OAuthClientState clientState, AccessorInfoBuilder accessorBuilder)
       throws GadgetException {
-
-    if (isEmpty(tokenKey.getGadgetUri())) {
-      throw new OAuthStoreException("found empty gadget URI in TokenKey");
+    if (clientState.getRequestToken() != null) {
+      // We cached the request token on the client.
+      accessorBuilder.setRequestToken(clientState.getRequestToken());
+      accessorBuilder.setTokenSecret(clientState.getRequestTokenSecret());
+    } else if (clientState.getAccessToken() != null) {
+      // We cached the access token on the client
+      accessorBuilder.setAccessToken(clientState.getAccessToken());
+      accessorBuilder.setTokenSecret(clientState.getAccessTokenSecret());
+    } else {
+      // No useful client-side state, check persistent storage
+      TokenInfo tokenInfo = store.getTokenInfo(securityToken, consumerInfo,
+          arguments.getServiceName(), arguments.getTokenName());
+      if (tokenInfo != null && tokenInfo.getAccessToken() != null) {
+        // We have an access token in persistent storage, use that.
+        accessorBuilder.setAccessToken(tokenInfo.getAccessToken());
+        accessorBuilder.setTokenSecret(tokenInfo.getTokenSecret());
+      } else {
+        // We don't have an access token yet, but the client sent us a (hopefully) preapproved
+        // request token.
+        accessorBuilder.setRequestToken(arguments.getRequestToken());
+        accessorBuilder.setTokenSecret(arguments.getRequestTokenSecret());
+      }
     }
+  }
 
-    if (isEmpty(tokenKey.getUserId())) {
-      throw new OAuthStoreException("found empty userId in TokenKey");
+  private OAuthParamLocation getStoreLocation(Location location) throws GadgetException {
+    switch(location) {
+    case HEADER:
+      return OAuthParamLocation.AUTH_HEADER;
+    case URL:
+      return OAuthParamLocation.URI_QUERY;
+    case BODY:
+      return OAuthParamLocation.POST_BODY;
+    }
+    throw new GadgetException(GadgetException.Code.INTERNAL_SERVER_ERROR,
+        "Unknown parameter location " + location);
+  }
+  
+  private HttpMethod getStoreMethod(Method method) throws GadgetException {
+    switch(method) {
+    case GET:
+      return HttpMethod.GET;
+    case POST:
+      return HttpMethod.POST;
     }
+    throw new GadgetException(GadgetException.Code.INTERNAL_SERVER_ERROR,
+        "Unknown method " + method);
+  }
 
-    GadgetSpec spec;
+  private GadgetSpec findSpec(SecurityToken securityToken, OAuthArguments arguments)
+      throws GadgetException {
     try {
-      spec = specFactory.getGadgetSpec(
-          new URI(tokenKey.getGadgetUri()),
-          bypassSpecCache);
+      return specFactory.getGadgetSpec(
+          new URI(securityToken.getAppUrl()),
+          arguments.getBypassSpecCache());
     } catch (URISyntaxException e) {
-      throw new OAuthStoreException("could not fetch gadget spec, gadget URI " +
-          "invalid", e);
+      throw new OAuthStoreException("could not fetch gadget spec, gadget URI invalid", e);
     }
-
-    OAuthStore.ProviderInfo provInfo;
-    provInfo = getProviderInfo(spec, tokenKey.getServiceName());
-
-    return store.getOAuthAccessor(tokenKey, provInfo);
+  }
+  
+  private GadgetException serviceNotFoundEx(SecurityToken securityToken, OAuthSpec oauthSpec,
+      String serviceName) {
+    StringBuilder message = new StringBuilder()
+        .append("Spec for gadget ")
+        .append(securityToken.getAppUrl())
+        .append(" does not contain OAuth service ")
+        .append(serviceName)
+        .append(".  Known services: ")
+        .append(StringUtils.join(oauthSpec.getServices().keySet(), ','));
+    return new GadgetException(GadgetException.Code.INVALID_PARAMETER,
+        message.toString());
   }
 
+  private GadgetException oauthNotFoundEx(SecurityToken securityToken) {
+    StringBuilder message = new StringBuilder()
+        .append("Spec for gadget ")
+        .append(securityToken.getAppUrl())
+        .append(" does not contain OAuth element.");
+    return new GadgetException(GadgetException.Code.INVALID_PARAMETER,
+        message.toString());
+  }
+  
   /**
-   * Reads OAuth provider information out of gadget spec.
-   * @param spec
-   * @return a GadgetInfo
-   * @throws GadgetException if some information is missing, or something else
-   *                         is wrong with the spec.
+   * Store an access token for the given user/gadget/service/token name
    */
-  public static OAuthStore.ProviderInfo getProviderInfo(
-      GadgetSpec spec, String serviceName) throws GadgetException {
-
-    OAuthSpec oauthSpec = spec.getModulePrefs().getOAuthSpec();
-
-    if (oauthSpec == null) {
-      String message = "gadget spec is missing /ModulePrefs/OAuth section";
-      log.warning(message);
-      throw new GadgetException(GadgetException.Code.MISSING_PARAMETER,
-                                message);
-    }
-    
-    OAuthService service = oauthSpec.getServices().get(serviceName);
-    if (service == null) {
-      StringBuilder message = new StringBuilder();
-      message.append("Spec does not contain OAuth service '");
-      message.append(serviceName);
-      message.append("'.  Known services: ");
-      Iterator<String> known = oauthSpec.getServices().keySet().iterator();
-      while (known.hasNext()) {
-        message.append('\'');
-        message.append(known.next());
-        message.append('\'');
-        if (known.hasNext()) {
-          message.append(", ");
-        }
-      }
-      log.warning(message.toString());
-      throw new GadgetException(GadgetException.Code.INVALID_PARAMETER,
-          message.toString());
-    }
-
-    OAuthServiceProvider provider = new OAuthServiceProvider(
-        service.getRequestUrl().url.toASCIIString(),
-        service.getAuthorizationUrl().toASCIIString(),
-        service.getAccessUrl().url.toASCIIString());
-
-    OAuthStore.HttpMethod httpMethod;
-    switch(service.getRequestUrl().method) {
-      case GET:
-        httpMethod = OAuthStore.HttpMethod.GET;
-        break;
-      case POST:
-      default:
-        httpMethod = OAuthStore.HttpMethod.POST;
-        break;
-    }
- 
-    OAuthStore.OAuthParamLocation paramLocation;
-    switch(service.getRequestUrl().location) {
-      case URL:
-        paramLocation = OAuthStore.OAuthParamLocation.URI_QUERY;
-        break;
-      case BODY:
-        paramLocation = OAuthStore.OAuthParamLocation.POST_BODY;
-        break;
-      case HEADER:
-      default:
-        paramLocation = OAuthStore.OAuthParamLocation.AUTH_HEADER;
-        break;
-    }
-
-    OAuthStore.ProviderInfo provInfo = new OAuthStore.ProviderInfo();
-    provInfo.setHttpMethod(httpMethod);
-    provInfo.setParamLocation(paramLocation);
-
-    // TODO: for now, we'll just set the signature type to HMAC_SHA1
-    // as this will be ignored later on when retrieving consumer information.
-    // There, if we find a negotiated HMAC key, we will use HMAC_SHA1. If we
-    // find a negotiated RSA key, we will use RSA_SHA1. And if we find neither,
-    // we may use RSA_SHA1 with a default signing key.
-    provInfo.setSignatureType(OAuthStore.SignatureType.HMAC_SHA1);
-    provInfo.setProvider(provider);
-
-    return provInfo;
+  public void storeTokenKeyAndSecret(SecurityToken securityToken, ConsumerInfo consumerInfo,
+      OAuthArguments arguments, TokenInfo tokenInfo) throws GadgetException {
+    store.setTokenInfo(securityToken, consumerInfo, arguments.getServiceName(),
+        arguments.getTokenName(), tokenInfo);
   }
 
-  static boolean isEmpty(String string) {
-    return (string == null) || (string.trim().length() == 0);
+  /**
+   * Remove an access token for the given user/gadget/service/token name
+   */
+  public void removeToken(SecurityToken securityToken, ConsumerInfo consumerInfo,
+      OAuthArguments arguments) throws GadgetException {
+    store.removeToken(securityToken, consumerInfo, arguments.getServiceName(),
+        arguments.getTokenName());
   }
 }

Modified: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth/OAuthArguments.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth/OAuthArguments.java?rev=693675&r1=693674&r2=693675&view=diff
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth/OAuthArguments.java (original)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth/OAuthArguments.java Tue Sep  9 18:50:41 2008
@@ -18,10 +18,13 @@
  */
 package org.apache.shindig.gadgets.oauth;
 
-import org.apache.shindig.gadgets.spec.RequestAuthenticationInfo;
+import com.google.common.collect.Maps;
+
+import org.apache.shindig.gadgets.GadgetException;
+import org.apache.shindig.gadgets.spec.Auth;
+import org.apache.shindig.gadgets.spec.Preload;
 
 import java.util.Map;
-import java.util.TreeMap;
 
 import javax.servlet.http.HttpServletRequest;
 
@@ -29,93 +32,242 @@
  * Arguments to an OAuth fetch sent by the client.
  */
 public class OAuthArguments {
-  public static final String SERVICE_PARAM = "OAUTH_SERVICE_NAME";
-  public static final String TOKEN_PARAM = "OAUTH_TOKEN_NAME";
-  public static final String REQUEST_TOKEN_PARAM = "OAUTH_REQUEST_TOKEN";
-  public static final String REQUEST_TOKEN_SECRET_PARAM =
-      "OAUTH_REQUEST_TOKEN_SECRET";
-  public static final String CLIENT_STATE_PARAM = "oauthState";
-  public static final String BYPASS_SPEC_CACHE_PARAM = "bypassSpecCache";
-
-  protected String serviceName;
-  protected String tokenName;
-  protected String requestToken;
-  protected String requestTokenSecret;
-  protected String origClientState;
-  protected boolean bypassSpecCache;
-
-  @SuppressWarnings("unchecked")
-  public OAuthArguments(HttpServletRequest request) {
-    Map<String, String> params = new TreeMap<String, String>(String.CASE_INSENSITIVE_ORDER);
-    Map<String, String[]> reqParams = request.getParameterMap();
-    for (String name : reqParams.keySet()) {
-      params.put(name, reqParams.get(name)[0]);
-    }
-    init(params);
-  }
-
-  public OAuthArguments(RequestAuthenticationInfo request) {
-    Map<String, String> attrs = new TreeMap<String, String>(String.CASE_INSENSITIVE_ORDER);
-    attrs.putAll(request.getAttributes());
-    init(attrs);
-  }
-
-  private void init(Map<String, String> attrs) {
-    serviceName = getParam(attrs, SERVICE_PARAM, "");
-    tokenName = getParam(attrs, TOKEN_PARAM, "");
-    requestToken = getParam(attrs, REQUEST_TOKEN_PARAM, null);
-    requestTokenSecret = getParam(attrs, REQUEST_TOKEN_SECRET_PARAM, null);
-    origClientState = getParam(attrs, CLIENT_STATE_PARAM, null);
-    bypassSpecCache = "1".equals(getParam(attrs, BYPASS_SPEC_CACHE_PARAM, null));
+  private static final String SERVICE_PARAM = "OAUTH_SERVICE_NAME";
+  private static final String TOKEN_PARAM = "OAUTH_TOKEN_NAME";
+  private static final String REQUEST_TOKEN_PARAM = "OAUTH_REQUEST_TOKEN";
+  private static final String REQUEST_TOKEN_SECRET_PARAM = "OAUTH_REQUEST_TOKEN_SECRET";
+  private static final String USE_TOKEN_PARAM = "OAUTH_USE_TOKEN";
+  private static final String CLIENT_STATE_PARAM = "oauthState";
+  private static final String BYPASS_SPEC_CACHE_PARAM = "bypassSpecCache";
+  private static final String SIGN_OWNER_PARAM = "signOwner";
+  private static final String SIGN_VIEWER_PARAM = "signViewer";
+ 
+  /**
+   * Should the OAuth access token be used?
+   */
+  public static enum UseToken {
+    /** Do not use the OAuth access token */
+    NEVER,
+    /** Use the access token if it exists already, but don't prompt for permission */
+    IF_AVAILABLE,
+    /** Use the access token if it exists, and prompt if it doesn't */
+    ALWAYS,
+  }
+  
+  /** Should we attempt to use an access token for the request */
+  private UseToken useToken = UseToken.ALWAYS;
+  
+  /** OAuth service nickname.  Signed fetch uses the empty string */
+  private String serviceName = "";
+  
+  /** OAuth token nickname.  Signed fetch uses the empty string */
+  private String tokenName = "";
+  
+  /** Request token the client wants us to use, may be null */
+  private String requestToken = null;
+  
+  /** Token secret that goes with the request token */
+  private String requestTokenSecret = null;
+  
+  /** Encrypted state blob stored on the client */
+  private String origClientState = null;
+  
+  /** Whether we should bypass the gadget spec cache */
+  private boolean bypassSpecCache = false;
+  
+  /** Include information about the owner? */
+  private boolean signOwner = false;
+  
+  /** Include information about the viewer? */
+  private boolean signViewer = false;
+
+  /**
+   * Parse OAuthArguments from parameters to the makeRequest servlet.
+   * 
+   * @param auth authentication type for the request
+   * @param request servlet request
+   * @throws GadgetException if any parameters are invalid.
+   */
+  public OAuthArguments(Auth auth, HttpServletRequest request) throws GadgetException {
+    useToken = parseUseToken(auth, getRequestParam(request, USE_TOKEN_PARAM, ""));
+    serviceName = getRequestParam(request, SERVICE_PARAM, "");
+    tokenName = getRequestParam(request, TOKEN_PARAM, "");
+    requestToken = getRequestParam(request, REQUEST_TOKEN_PARAM, null);
+    requestTokenSecret = getRequestParam(request, REQUEST_TOKEN_SECRET_PARAM, null);
+    origClientState = getRequestParam(request, CLIENT_STATE_PARAM, null);
+    bypassSpecCache = "1".equals(getRequestParam(request, BYPASS_SPEC_CACHE_PARAM, null));
+    signOwner = Boolean.parseBoolean(getRequestParam(request, SIGN_OWNER_PARAM, "true"));
+    signViewer = Boolean.parseBoolean(getRequestParam(request, SIGN_VIEWER_PARAM, "true"));
+  }
+  
+  public OAuthArguments(Preload preload) throws GadgetException {
+    Map<String, String> attrs = Maps.newTreeMap(String.CASE_INSENSITIVE_ORDER);
+    attrs.putAll(preload.getAttributes());
+    useToken = parseUseToken(preload.getAuthType(), getPreloadParam(attrs, USE_TOKEN_PARAM, ""));
+    serviceName = getPreloadParam(attrs, SERVICE_PARAM, "");
+    tokenName = getPreloadParam(attrs, TOKEN_PARAM, "");
+    requestToken = getPreloadParam(attrs, REQUEST_TOKEN_PARAM, null);
+    requestTokenSecret = getPreloadParam(attrs, REQUEST_TOKEN_SECRET_PARAM, null);
+    origClientState = null;
+    bypassSpecCache = false;
+    signOwner = preload.isSignOwner();
+    signViewer = preload.isSignViewer();
+  }
+  
+  /**
+   * @return the named attribute from the Preload tag attributes, or default if the attribute is
+   * not present.
+   */
+  private String getPreloadParam(Map<String, String> attrs, String name, String def) {
+    String val = attrs.get(name);
+    if (val == null) {
+      val = def;
+    }
+    return val;
   }
 
-  private String getParam(Map<String, String> attrs, String name, String def) {
-    String val = attrs.get(name);
+  /**
+   * @return the named parameter from the request, or default if the named parameter is not present.
+   */
+  private static String getRequestParam(HttpServletRequest request, String name, String def) {
+    String val = request.getParameter(name);
     if (val == null) {
       val = def;
     }
     return val;
   }
 
-  // Testing only
-  public OAuthArguments(String serviceName, String tokenName,
-      String origClientState, boolean bypassSpecCache) {
-    this(serviceName, tokenName, origClientState, bypassSpecCache, null, null);
+
+  /**
+   * Figure out what the client wants us to do with the OAuth access token.
+   */
+  private static UseToken parseUseToken(Auth auth, String useTokenStr) throws GadgetException {
+    if (useTokenStr.length() == 0) {
+      if (auth == Auth.SIGNED) {
+        // signed fetch defaults to not using the token
+        return UseToken.NEVER;
+      } else {
+        // OAuth defaults to always using it.
+        return UseToken.ALWAYS;
+      }
+    }
+    useTokenStr = useTokenStr.toLowerCase();
+    if ("always".equals(useTokenStr)) {
+      return UseToken.ALWAYS;
+    }
+    if ("if_available".equals(useTokenStr)) {
+      return UseToken.IF_AVAILABLE;
+    }
+    if ("never".equals(useTokenStr)) {
+      return UseToken.NEVER;
+    }
+    throw new GadgetException(GadgetException.Code.INVALID_PARAMETER,
+        "Unknown use token value " + useTokenStr);
+  }
+  
+  /**
+   * Create an OAuthArguments object with all default values.  The details can be filled in later
+   * using the setters.
+   * 
+   * Be careful using this in anything except test code.  If you find yourself wanting to use this
+   * method in real code, consider writing a new constructor instead.
+   */
+  public OAuthArguments() {
+  }
+  
+  
+  /**
+   * Copy constructor.
+   */
+  public OAuthArguments(OAuthArguments orig) {
+    useToken = orig.useToken;
+    serviceName = orig.serviceName;
+    tokenName = orig.tokenName;
+    requestToken = orig.requestToken;
+    requestTokenSecret = orig.requestTokenSecret;
+    origClientState = orig.origClientState;
+    bypassSpecCache = orig.bypassSpecCache;
+    signOwner = orig.signOwner;
+    signViewer = orig.signViewer;
+  }
+
+  public boolean mustUseToken() {
+    return (useToken == UseToken.ALWAYS);
+  }
+  
+  public boolean mayUseToken() {
+    return (useToken == UseToken.IF_AVAILABLE || useToken == UseToken.ALWAYS);
   }
 
-  // Testing only
-  public OAuthArguments(String serviceName, String tokenName,
-      String origClientState, boolean bypassSpecCache, String requestToken,
-      String requestTokenSecret) {
-    this.serviceName = serviceName;
-    this.tokenName = tokenName;
-    this.requestToken = requestToken;
-    this.requestTokenSecret = requestTokenSecret;
-    this.origClientState = origClientState;
-    this.bypassSpecCache = bypassSpecCache;
+  public UseToken getUseToken() {
+    return useToken;
+  }
+
+  public void setUseToken(UseToken useToken) {
+    this.useToken = useToken;
   }
 
   public String getServiceName() {
     return serviceName;
   }
 
+  public void setServiceName(String serviceName) {
+    this.serviceName = serviceName;
+  }
+
   public String getTokenName() {
     return tokenName;
   }
 
+  public void setTokenName(String tokenName) {
+    this.tokenName = tokenName;
+  }
+
+  public String getRequestToken() {
+    return requestToken;
+  }
+
+  public void setRequestToken(String requestToken) {
+    this.requestToken = requestToken;
+  }
+
+  public String getRequestTokenSecret() {
+    return requestTokenSecret;
+  }
+
+  public void setRequestTokenSecret(String requestTokenSecret) {
+    this.requestTokenSecret = requestTokenSecret;
+  }
+
   public String getOrigClientState() {
     return origClientState;
   }
 
+  public void setOrigClientState(String origClientState) {
+    this.origClientState = origClientState;
+  }
+
   public boolean getBypassSpecCache() {
     return bypassSpecCache;
   }
 
-  public String getRequestToken() {
-    return requestToken;
+  public void setBypassSpecCache(boolean bypassSpecCache) {
+    this.bypassSpecCache = bypassSpecCache;
   }
 
-  public String getRequestTokenSecret() {
-    return requestTokenSecret;
+  public boolean getSignOwner() {
+    return signOwner;
+  }
+
+  public void setSignOwner(boolean signOwner) {
+    this.signOwner = signOwner;
+  }
+
+  public boolean getSignViewer() {
+    return signViewer;
+  }
+
+  public void setSignViewer(boolean signViewer) {
+    this.signViewer = signViewer;
   }
 }