You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@shindig.apache.org by et...@apache.org on 2008/04/05 06:37:59 UTC

svn commit: r645027 [2/2] - in /incubator/shindig/trunk: config/ features/core.io/ java/gadgets/ java/gadgets/src/main/java/org/apache/shindig/gadgets/ java/gadgets/src/main/java/org/apache/shindig/gadgets/http/ java/gadgets/src/main/java/org/apache/sh...

Added: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth/OAuthNoDataException.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth/OAuthNoDataException.java?rev=645027&view=auto
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth/OAuthNoDataException.java (added)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth/OAuthNoDataException.java Fri Apr  4 21:37:53 2008
@@ -0,0 +1,28 @@
+/*
+ * 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;
+
+/**
+ * Exception thrown when a lookup found no data in the OAuth store.
+ */
+public class OAuthNoDataException extends OAuthStoreException {
+
+  public OAuthNoDataException(String message) {
+    super(message);
+  }
+}

Added: 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=645027&view=auto
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth/OAuthRequestParams.java (added)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth/OAuthRequestParams.java Fri Apr  4 21:37:53 2008
@@ -0,0 +1,52 @@
+/*
+ * 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 javax.servlet.http.HttpServletRequest;
+
+/**
+ * Bundles information about a proxy request that requires OAuth
+ */
+public class OAuthRequestParams {
+  public static final String SERVICE_PARAM = "oauthService";
+  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;
+
+  public OAuthRequestParams(HttpServletRequest request) {
+    serviceName = request.getParameter(SERVICE_PARAM);
+    tokenName = request.getParameter(TOKEN_PARAM);
+    origClientState = request.getParameter(CLIENT_STATE_PARAM);
+  }
+  
+  public String getServiceName() {
+    return serviceName;
+  }
+
+  public String getTokenName() {
+    return tokenName;
+  }
+
+  public String getOrigClientState() {
+    return origClientState;
+  }
+}

Added: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth/OAuthStore.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth/OAuthStore.java?rev=645027&view=auto
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth/OAuthStore.java (added)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth/OAuthStore.java Fri Apr  4 21:37:53 2008
@@ -0,0 +1,370 @@
+/*
+ * 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 net.oauth.OAuthServiceProvider;
+
+/**
+ * Interface to an OAuth Data Store. A shindig gadget server can act as an
+ * OAuth consumer, using OAuth tokens to talk to OAuth service providers on
+ * behalf of the gadgets it is proxying requests for. An OAuth consumer needs
+ * to permanently store gadgets it has collected, and retrieve the
+ * appropriate tokens when proxying a request for a gadget.
+ *
+ * An OAuth Data Store stores three things:
+ *  (1) information about OAuth service providers, including the three
+ *      URLs that define OAuth providers (defined in OAuthStore.ProviderInfo)
+ *  (2) information about consumer keys and secrets that gadgets might have
+ *      negotiated with OAuth service providers, or that containers might have
+ *      negotiated on behalf of the gadgets. This information is bound to
+ *      the service provider it pertains to and can only be stored if the
+ *      corresponding service provider information is already stored in the
+ *      OAuth store (defined in OAuthStore.ConsumerKeyAndSecret).
+ *  (3) OAuth access tokens and their corresponding token secrets. (defined
+ *      in OAuthStore.TokenInfo).
+ *
+ *  Note that we do not store request tokens in the OAuth store.
+ */
+public interface OAuthStore {
+
+  /**
+   * Set information about a service provider, as it is known to a certain
+   * gadget.
+   *
+   * @param providerKey the gadget that wants to use the service provider, and
+   *                    a nickName under which the gadget will refer to this
+   *                    service provider in the future.
+   * @param providerInfo the three URLs that define this provider. Also
+   *                     contains the httpMethod used to access the service
+   *                     provider. (Technically, OAuth allows different methods
+   *                     for the different URLs, but the Java OAuth library
+   *                     doesn't support that. So we're also just storing one
+   *                     preference here.) Also contains the signature type
+   *                     (RSA, HMAC, or PLAINTEXT) to be
+   *                     used for this service provider. (Again, OAuth allows
+   *                     different signature types for each of the three
+   *                     URLs, but we just assume they're all the same, and
+   *                     provide just one parameter to specify signature type.)
+   * @throws OAuthStoreException if there was a problem accessing the
+   *                                    store.
+   */
+  public void setOAuthServiceProviderInfo(ProviderKey providerKey,
+                                          ProviderInfo providerInfo)
+      throws OAuthStoreException;
+
+  /**
+   * Gets information about a service provider (access, request, and authorize
+   * URLs with access methods and signature types), given a gadget description
+   * and a service name.
+   *
+   * @param providerKey the gadget that wants to use the service provider, and
+   *                    a nickName under which the gadget refers to this
+   *                    service provider.
+   * @return the provider information.
+   * @throws OAuthStoreException
+   * @throws OAuthNoDataException
+   */
+  public ProviderInfo getOAuthServiceProviderInfo(ProviderKey providerKey)
+      throws OAuthStoreException, OAuthNoDataException;
+
+  /**
+   * If we or a gadget negotiate a separate consumer key and secret with a
+   * service provider, use this method to store it. 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. Even if we never set a consumer secret, an implementation
+   * of this interface may still be able to return useful OAuthAccessors in
+   * the getOAuthAccessor method, e.g. by returning a fallback private key and
+   * default consumer key, which could be used with service providers that we
+   * haven't negotiated a consumer secret with.
+   *
+   * @param providerKey the gadget that wants to use the service provider, and
+   *                    a nickName under which the gadget will refer to this
+   *                    service provider in the future.
+   * @param keyAndSecret the consumer key and secret. If the secret is an RSA
+   *                     private key, it must be PKCS8-then-Base64 encoded.
+   * @throws OAuthStoreException if something goes wrong accessing the
+   *                                   data store.
+   * @throws OAuthNoDataException if no provider info can be found for the
+   *                              provider specified in the providerKey.
+   */
+  public void setOAuthConsumerKeyAndSecret(ProviderKey providerKey,
+                                           ConsumerKeyAndSecret keyAndSecret)
+      throws OAuthStoreException, OAuthNoDataException;
+
+  /**
+   * Stores an access token.
+   * @param tokenKey a structure uniquely identifying the key under which this
+   *                 access token should be retrievable: a userId, a gadgetId,
+   *                 a moduleId (in case there are more than one gadget of the
+   *                 same type on a page), a tokenName (which distinguishes
+   *                 this token from others that the same gadget might hold for
+   *                 the same service provider) and a serviceName (which is the
+   *                 same as the service name in the ProviderKey structure).
+   * @param tokenInfo an access token and token secret.
+   * @throws OAuthStoreException if an error occurs talking to the
+   *                                   data store.
+   */
+  public void setTokenAndSecret(TokenKey tokenKey, TokenInfo tokenInfo)
+      throws OAuthStoreException;
+
+  /**
+   * Retrieve an OAuthAccessor that is ready to sign OAuthMessages for
+   * resource access.
+   * @param tokenKey a structure uniquely identifying the token: a userId,
+   *                 a gadgetId, a moduleId (in case there are more than one
+   *                 gadget of the same type on a page), a tokenName (which
+   *                 distinguishes this token from others that the same gadget
+   *                 might hold for the same service provider) and a serviceName
+   *                 (which is the same as the service name in the ProviderKey
+   *                 structure).
+   * @return an OAuthAccessor object than can be passed to an OAuthMessage.sign
+   *         method.
+   * @throws OAuthNoDataException if the token couldn't be found
+   * @throws OAuthStoreException if an error occurred accessing the data
+   *                                   store.
+   */
+  public AccessorInfo getOAuthAccessor(TokenKey tokenKey)
+      throws OAuthNoDataException, OAuthStoreException;
+
+
+  //////////////////////////////////////////////////////////////////////////////
+  // Auxiliary types needed to work with this interface
+  //////////////////////////////////////////////////////////////////////////////
+
+  public static enum HttpMethod { GET, POST }
+  public static enum SignatureType {HMAC_SHA1, RSA_SHA1, PLAINTEXT}
+  public static enum KeyType { HMAC_SYMMETRIC, RSA_PRIVATE }
+
+  public static class AccessorInfo {
+    OAuthAccessor accessor;
+    HttpMethod httpMethod;
+    SignatureType signatureType;
+    public OAuthAccessor getAccessor() {
+      return accessor;
+    }
+    public void setAccessor(OAuthAccessor accessor) {
+      this.accessor = accessor;
+    }
+    public HttpMethod getHttpMethod() {
+      return httpMethod;
+    }
+    public void setHttpMethod(HttpMethod httpMethod) {
+      this.httpMethod = httpMethod;
+    }
+    public SignatureType getSignatureType() {
+      return signatureType;
+    }
+    public void setSignatureType(SignatureType signatureType) {
+      this.signatureType = signatureType;
+    }
+  }
+
+  public static class ConsumerKeyAndSecret {
+    private String consumerKey;
+    private String consumerSecret;
+    private KeyType keyType;
+
+    public ConsumerKeyAndSecret(String key, String secret, KeyType type) {
+      consumerKey = key;
+      consumerSecret = secret;
+      keyType = type;
+    }
+    public String getConsumerKey() {
+      return consumerKey;
+    }
+    public String getConsumerSecret() {
+      return consumerSecret;
+    }
+    public KeyType getKeyType() {
+      return keyType;
+    }
+  }
+
+  public static class ProviderKey {
+    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 ProviderKey other = (ProviderKey) 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;
+    }
+  }
+
+  public static class ProviderInfo {
+    private OAuthServiceProvider provider;
+    private HttpMethod httpMethod;
+    private SignatureType signatureType;
+
+    // this can be null if we have not negotiated a consumer key and secret
+    // yet with the provider, or if we decided that we want to use a global
+    // public key
+    private ConsumerKeyAndSecret keyAndSecret;
+
+    public ConsumerKeyAndSecret getKeyAndSecret() {
+      return keyAndSecret;
+    }
+    public void setKeyAndSecret(ConsumerKeyAndSecret keyAndSecret) {
+      this.keyAndSecret = keyAndSecret;
+    }
+    public OAuthServiceProvider getProvider() {
+      return provider;
+    }
+    public void setProvider(OAuthServiceProvider provider) {
+      this.provider = provider;
+    }
+    public HttpMethod getHttpMethod() {
+      return httpMethod;
+    }
+    public void setHttpMethod(HttpMethod httpMethod) {
+      this.httpMethod = httpMethod;
+    }
+    public SignatureType getSignatureType() {
+      return signatureType;
+    }
+    public void setSignatureType(SignatureType signatureType) {
+      this.signatureType = signatureType;
+    }
+  }
+
+  public static class TokenKey {
+    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 TokenKey other = (TokenKey) 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;
+    }
+  }
+
+  public static class TokenInfo {
+    private String accessToken;
+    private String tokenSecret;
+    public TokenInfo(String token, String secret) {
+      accessToken = token;
+      tokenSecret = secret;
+    }
+    public String getAccessToken() {
+      return accessToken;
+    }
+    public String getTokenSecret() {
+      return tokenSecret;
+    }
+  }
+}

Added: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth/OAuthStoreException.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth/OAuthStoreException.java?rev=645027&view=auto
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth/OAuthStoreException.java (added)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth/OAuthStoreException.java Fri Apr  4 21:37:53 2008
@@ -0,0 +1,39 @@
+/*
+ * 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 org.apache.shindig.gadgets.GadgetException;
+
+/**
+ * Base class of exception that can be thrown when interacting with the
+ * OAuthStore.
+ */
+public class OAuthStoreException extends GadgetException {
+
+  public OAuthStoreException(String message) {
+    super(GadgetException.Code.OAUTH_STORAGE_ERROR, message);
+  }
+
+  public OAuthStoreException(String message, Throwable cause) {
+    super(GadgetException.Code.OAUTH_STORAGE_ERROR, message, cause);
+  }
+
+  public OAuthStoreException(Throwable cause) {
+    super(GadgetException.Code.OAUTH_STORAGE_ERROR, cause);
+  }
+}

Added: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/util/BasicBlobCrypter.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/util/BasicBlobCrypter.java?rev=645027&view=auto
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/util/BasicBlobCrypter.java (added)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/util/BasicBlobCrypter.java Fri Apr  4 21:37:53 2008
@@ -0,0 +1,186 @@
+/*
+ * 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.util;
+
+import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.codec.digest.DigestUtils;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLDecoder;
+import java.net.URLEncoder;
+import java.security.GeneralSecurityException;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Simple implementation of BlobCrypter.
+ */
+public class BasicBlobCrypter implements BlobCrypter {
+
+  // Labels for key derivation
+  private static final byte CIPHER_KEY_LABEL = 0;
+  private static final byte HMAC_KEY_LABEL = 1;
+  
+  /** Key used for time stamp (in seconds) of data */
+  public static final String TIMESTAMP_KEY = "t";
+  
+  /** minimum length of master key */
+  public static final int MASTER_KEY_MIN_LEN = 16;
+  
+  /** allow three minutes for clock skew */
+  private static final long CLOCK_SKEW_ALLOWANCE = 180;
+  
+  private static final String UTF8 = "UTF-8";
+  
+  public TimeSource timeSource = new TimeSource();  
+  private byte[] cipherKey;
+  private byte[] hmacKey;
+  
+  /**
+   * Builds a BlobCrypter from the specified master key
+   * 
+   * @param masterKey
+   */
+  public BasicBlobCrypter(byte[] masterKey) {
+    if (masterKey.length < MASTER_KEY_MIN_LEN) {
+      throw new IllegalArgumentException("Master key needs at least " +
+          MASTER_KEY_MIN_LEN + " bytes");
+    }
+    cipherKey = deriveKey(CIPHER_KEY_LABEL, masterKey, Crypto.CIPHER_KEY_LEN);
+    hmacKey = deriveKey(HMAC_KEY_LABEL, masterKey, 0);
+  }
+
+  /**
+   * Generates unique keys from a master key.
+   * 
+   * @param label type of key to derive
+   * @param masterKey master key
+   * @param len length of key needed, less than 20 bytes.  20 bytes are 
+   * returned if len is 0.  
+   * 
+   * @return a derived key of the specified length
+   */
+  private byte[] deriveKey(byte label, byte[] masterKey, int len) {
+    byte[] base = Crypto.concat(new byte[] { label }, masterKey);
+    byte[] hash = DigestUtils.sha(base);
+    if (len == 0) {
+      return hash;
+    }
+    byte[] out = new byte[len];
+    System.arraycopy(hash, 0, out, 0, out.length);
+    return out;
+  }
+  
+  /* (non-Javadoc)
+   * @see org.apache.shindig.util.BlobCrypter#wrap(java.util.Map)
+   */
+  public String wrap(Map<String, String> in)
+  throws BlobCrypterException {
+    if (in.containsKey(TIMESTAMP_KEY)) {
+      throw new IllegalArgumentException("No 't' keys allowed for BlobCrypter");
+    }
+    try {
+      byte[] encoded = serializeAndTimestamp(in);
+      byte[] cipherText = Crypto.aes128cbcEncrypt(cipherKey, encoded);
+      byte[] hmac = Crypto.hmacSha1(hmacKey, cipherText);
+      byte[] b64 = Base64.encodeBase64(Crypto.concat(cipherText, hmac));
+      return new String(b64, UTF8);
+    } catch (UnsupportedEncodingException e) {
+      throw new BlobCrypterException(e);
+    } catch (GeneralSecurityException e) {
+      throw new BlobCrypterException(e);
+    }
+  }
+
+  /**
+   * Encode the input for transfer.  We use something a lot like HTML form
+   * encodings.  The time stamp is in seconds since the epoch.
+   */
+  private byte[] serializeAndTimestamp(Map<String, String> in)
+  throws UnsupportedEncodingException {
+    StringBuilder sb = new StringBuilder();
+
+    for (Map.Entry<String, String> stringStringEntry : in.entrySet()) {
+      Map.Entry<String, String> val = stringStringEntry;
+      sb.append(URLEncoder.encode(val.getKey(), UTF8));
+      sb.append('=');
+      sb.append(URLEncoder.encode(val.getValue(), UTF8));
+      sb.append('&');
+    }
+    sb.append(TIMESTAMP_KEY);
+    sb.append('=');
+    sb.append(timeSource.currentTimeMillis()/1000);
+    return sb.toString().getBytes(UTF8);
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.shindig.util.BlobCrypter#unwrap(java.lang.String, int)
+   */
+  public Map<String, String> unwrap(String in, int maxAgeSec)
+  throws BlobCrypterException {
+    try {
+      byte[] bin = Base64.decodeBase64(in.getBytes());
+      byte[] hmac = new byte[Crypto.HMAC_SHA1_LEN];
+      byte[] cipherText = new byte[bin.length-Crypto.HMAC_SHA1_LEN];
+      System.arraycopy(bin, 0, cipherText, 0, cipherText.length);
+      System.arraycopy(bin, cipherText.length, hmac, 0, hmac.length);
+      Crypto.hmacSha1Verify(hmacKey, cipherText, hmac);
+      byte[] plain = Crypto.aes128cbcDecrypt(cipherKey, cipherText);
+      Map<String, String> out = deserialize(plain);
+      checkTimestamp(out, maxAgeSec);
+      return out;
+    } catch (GeneralSecurityException e) {
+      throw new BlobCrypterException("Invalid token signature", e);
+    } catch (ArrayIndexOutOfBoundsException e) {
+      throw new BlobCrypterException("Invalid token format", e);
+    } catch (UnsupportedEncodingException e) {
+      throw new BlobCrypterException(e);
+    }
+
+  }
+
+  private Map<String, String> deserialize(byte[] plain)
+  throws UnsupportedEncodingException {
+    String base = new String(plain, UTF8);
+    String[] items = base.split("[&=]");
+    Map<String, String> map = new HashMap<String, String>();
+    for (int i=0; i < items.length; ) {
+      String key = URLDecoder.decode(items[i++], UTF8);
+      String val = URLDecoder.decode(items[i++], UTF8);
+      map.put(key, val);
+    }
+    return map;
+  }
+  
+  /**
+   * We allow a few minutes on either side of the validity window to account
+   * for clock skew.
+   */
+  private void checkTimestamp(Map<String, String> out, int maxAge)
+  throws BlobExpiredException {
+    long origin = Long.parseLong(out.get(TIMESTAMP_KEY));
+    long minTime = origin - CLOCK_SKEW_ALLOWANCE;
+    long maxTime = origin + maxAge + CLOCK_SKEW_ALLOWANCE;
+    long now = timeSource.currentTimeMillis()/1000;
+    if (!(minTime < now && now < maxTime)) {
+      throw new BlobExpiredException(minTime, now, maxTime);
+    }    
+  }
+
+}

Modified: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/util/BlobCrypter.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/util/BlobCrypter.java?rev=645027&r1=645026&r2=645027&view=diff
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/util/BlobCrypter.java (original)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/util/BlobCrypter.java Fri Apr  4 21:37:53 2008
@@ -18,80 +18,18 @@
  */
 package org.apache.shindig.util;
 
-import org.apache.commons.codec.binary.Base64;
-import org.apache.commons.codec.digest.DigestUtils;
-
-import java.io.UnsupportedEncodingException;
-import java.net.URLDecoder;
-import java.net.URLEncoder;
-import java.security.GeneralSecurityException;
-import java.util.HashMap;
 import java.util.Map;
 
 /**
- * Utility class for managing signed, encrypted, and time stamped blobs.
+ * Utility interface for managing signed, encrypted, and time stamped blobs.
  * Blobs are made up of name/value pairs.  Time stamps are automatically
- * included under BlobCrypter.TIMESTAMP_KEY.
+ * included and checked.
  * 
  * Thread safe.
  */
-public class BlobCrypter {
-
-  // Labels for key derivation
-  private static final byte CIPHER_KEY_LABEL = 0;
-  private static final byte HMAC_KEY_LABEL = 1;
-  
-  /** Key used for time stamp (in seconds) of data */
-  public static final String TIMESTAMP_KEY = "t";
-  
-  /** minimum length of master key */
-  public static final int MASTER_KEY_MIN_LEN = 16;
-  
-  /** allow three minutes for clock skew */
-  private static final long CLOCK_SKEW_ALLOWANCE = 180;
-  
-  private static final String UTF8 = "UTF-8";
-  
-  public TimeSource timeSource = new TimeSource();  
-  private byte[] cipherKey;
-  private byte[] hmacKey;
-  
-  /**
-   * Builds a BlobCrypter from the specified master key
-   * 
-   * @param masterKey
-   */
-  public BlobCrypter(byte[] masterKey) {
-    if (masterKey.length < MASTER_KEY_MIN_LEN) {
-      throw new IllegalArgumentException("Master key needs at least " +
-          MASTER_KEY_MIN_LEN + " bytes");
-    }
-    cipherKey = deriveKey(CIPHER_KEY_LABEL, masterKey, Crypto.CIPHER_KEY_LEN);
-    hmacKey = deriveKey(HMAC_KEY_LABEL, masterKey, 0);
-  }
+public interface BlobCrypter {
 
   /**
-   * Generates unique keys from a master key.
-   * 
-   * @param label type of key to derive
-   * @param masterKey master key
-   * @param len length of key needed, less than 20 bytes.  20 bytes are returned
-   * if len is 0.  
-   * 
-   * @return a derived key of the specified length
-   */
-  private byte[] deriveKey(byte label, byte[] masterKey, int len) {
-    byte[] base = Crypto.concat(new byte[] { label }, masterKey);
-    byte[] hash = DigestUtils.sha(base);
-    if (len == 0) {
-      return hash;
-    }
-    byte[] out = new byte[len];
-    System.arraycopy(hash, 0, out, 0, out.length);
-    return out;
-  }
-  
-  /**
    * Time stamps, encrypts, and signs a blob.
    * 
    * @param in name/value pairs to encrypt
@@ -99,44 +37,7 @@
    * 
    * @throws BlobCrypterException
    */
-  public String wrap(Map<String, String> in)
-  throws BlobCrypterException {
-    if (in.containsKey(TIMESTAMP_KEY)) {
-      throw new IllegalArgumentException("No 't' keys allowed for BlobCrypter");
-    }
-    try {
-      byte[] encoded = serializeAndTimestamp(in);
-      byte[] cipherText = Crypto.aes128cbcEncrypt(cipherKey, encoded);
-      byte[] hmac = Crypto.hmacSha1(hmacKey, cipherText);
-      byte[] b64 = Base64.encodeBase64(Crypto.concat(cipherText, hmac));
-      return new String(b64, UTF8);
-    } catch (UnsupportedEncodingException e) {
-      throw new BlobCrypterException(e);
-    } catch (GeneralSecurityException e) {
-      throw new BlobCrypterException(e);
-    }
-  }
-
-  /**
-   * Encode the input for transfer.  We use something a lot like HTML form
-   * encodings.  The time stamp is in seconds since the epoch.
-   */
-  private byte[] serializeAndTimestamp(Map<String, String> in)
-  throws UnsupportedEncodingException {
-    StringBuilder sb = new StringBuilder();
-
-    for (Map.Entry<String, String> stringStringEntry : in.entrySet()) {
-      Map.Entry<String, String> val = stringStringEntry;
-      sb.append(URLEncoder.encode(val.getKey(), UTF8));
-      sb.append('=');
-      sb.append(URLEncoder.encode(val.getValue(), UTF8));
-      sb.append('&');
-    }
-    sb.append(TIMESTAMP_KEY);
-    sb.append('=');
-    sb.append(timeSource.currentTimeMillis()/1000);
-    return sb.toString().getBytes(UTF8);
-  }
+  public String wrap(Map<String, String> in) throws BlobCrypterException;
 
   /**
    * Unwraps a blob.
@@ -149,53 +50,6 @@
    * @throws BlobCrypterException if the blob can't be decoded.
    */
   public Map<String, String> unwrap(String in, int maxAgeSec)
-  throws BlobCrypterException {
-    try {
-      byte[] bin = Base64.decodeBase64(in.getBytes());
-      byte[] hmac = new byte[Crypto.HMAC_SHA1_LEN];
-      byte[] cipherText = new byte[bin.length-Crypto.HMAC_SHA1_LEN];
-      System.arraycopy(bin, 0, cipherText, 0, cipherText.length);
-      System.arraycopy(bin, cipherText.length, hmac, 0, hmac.length);
-      Crypto.hmacSha1Verify(hmacKey, cipherText, hmac);
-      byte[] plain = Crypto.aes128cbcDecrypt(cipherKey, cipherText);
-      Map<String, String> out = deserialize(plain);
-      checkTimestamp(out, maxAgeSec);
-      return out;
-    } catch (GeneralSecurityException e) {
-      throw new BlobCrypterException("Invalid token signature", e);
-    } catch (ArrayIndexOutOfBoundsException e) {
-      throw new BlobCrypterException("Invalid token format", e);
-    } catch (UnsupportedEncodingException e) {
-      throw new BlobCrypterException(e);
-    }
-
-  }
-
-  private Map<String, String> deserialize(byte[] plain) throws UnsupportedEncodingException {
-    String base = new String(plain, UTF8);
-    String[] items = base.split("[&=]");
-    Map<String, String> map = new HashMap<String, String>();
-    for (int i=0; i < items.length; ) {
-      String key = URLDecoder.decode(items[i++], UTF8);
-      String val = URLDecoder.decode(items[i++], UTF8);
-      map.put(key, val);
-    }
-    return map;
-  }
-  
-  /**
-   * We allow a few minutes on either side of the validity window to account
-   * for clock skew.
-   */
-  private void checkTimestamp(Map<String, String> out, int maxAge)
-  throws BlobExpiredException {
-    long origin = Long.parseLong(out.get(TIMESTAMP_KEY));
-    long minTime = origin - CLOCK_SKEW_ALLOWANCE;
-    long maxTime = origin + maxAge + CLOCK_SKEW_ALLOWANCE;
-    long now = timeSource.currentTimeMillis()/1000;
-    if (!(minTime < now && now < maxTime)) {
-      throw new BlobExpiredException(minTime, now, maxTime);
-    }    
-  }
+      throws BlobCrypterException;
 
 }

Added: incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/FakeGadgetToken.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/FakeGadgetToken.java?rev=645027&view=auto
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/FakeGadgetToken.java (added)
+++ incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/FakeGadgetToken.java Fri Apr  4 21:37:53 2008
@@ -0,0 +1,52 @@
+/*
+ * 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;
+
+/**
+ * A fake GadgetToken implementation to help testing.
+ */
+public class FakeGadgetToken implements GadgetToken {
+  public String getOwnerId() {
+    return "owner";
+  }
+
+  public String getViewerId() {
+    return "viewer";
+  }
+
+  public String getAppId() {
+    return "app1234";
+  }
+
+  public String getDomain() {
+    return "domain";
+  }
+
+  public String toSerialForm() {
+    return "";
+  }
+
+  public String getAppUrl() {
+    return "http://www.example.com/app.xml";
+  }
+
+  public long getModuleId() {
+    return 0;
+  }
+}

Modified: incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/GadgetTestFixture.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/GadgetTestFixture.java?rev=645027&r1=645026&r2=645027&view=diff
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/GadgetTestFixture.java (original)
+++ incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/GadgetTestFixture.java Fri Apr  4 21:37:53 2008
@@ -24,7 +24,7 @@
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
-public class GadgetTestFixture extends EasyMockTestCase {
+public abstract class GadgetTestFixture extends EasyMockTestCase {
   public final HttpServletRequest request = mock(HttpServletRequest.class);
   public final HttpServletResponse response = mock(HttpServletResponse.class);
   public final GadgetServer gadgetServer;

Modified: incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/SigningFetcherTest.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/SigningFetcherTest.java?rev=645027&r1=645026&r2=645027&view=diff
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/SigningFetcherTest.java (original)
+++ incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/SigningFetcherTest.java Fri Apr  4 21:37:53 2008
@@ -70,7 +70,7 @@
   @Override
   public void setUp() throws Exception {
     interceptor = new InterceptingContentFetcher();
-    authToken = new BasicGadgetToken("o", "v", "a", "d");
+    authToken = new BasicGadgetToken("o", "v", "a", "d", "u", "m");
     signer = SigningFetcher.makeFromB64PrivateKey(
         interceptor, authToken, "foo", PRIVATE_KEY_TEXT);
     OAuthConsumer consumer = new OAuthConsumer(null, null, null, null);

Modified: incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/http/HttpTestFixture.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/http/HttpTestFixture.java?rev=645027&r1=645026&r2=645027&view=diff
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/http/HttpTestFixture.java (original)
+++ incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/http/HttpTestFixture.java Fri Apr  4 21:37:53 2008
@@ -23,7 +23,7 @@
 import org.apache.shindig.gadgets.SigningFetcherFactory;
 
 
-public class HttpTestFixture extends GadgetTestFixture {
+public abstract class HttpTestFixture extends GadgetTestFixture {
   public final ProxyHandler proxyHandler;
   public final GadgetRenderer gadgetRenderer;
   public final JsonRpcHandler jsonRpcHandler;
@@ -35,8 +35,11 @@
 
   public HttpTestFixture() {
     super();
-    proxyHandler
-        = new ProxyHandler(fetcher, gadgetTokenDecoder, signingFetcherFactory);
+    proxyHandler = new ProxyHandler(
+        fetcher,
+        gadgetTokenDecoder,
+        signingFetcherFactory,
+        null);
     gadgetRenderer = new GadgetRenderer(
           gadgetServer, registry, syndicatorConfig, urlGenerator);
     jsonRpcHandler = new JsonRpcHandler(executor, gadgetServer, urlGenerator);

Modified: incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/http/ProxyHandlerTest.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/http/ProxyHandlerTest.java?rev=645027&r1=645026&r2=645027&view=diff
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/http/ProxyHandlerTest.java (original)
+++ incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/http/ProxyHandlerTest.java Fri Apr  4 21:37:53 2008
@@ -23,6 +23,7 @@
 import static org.easymock.EasyMock.expect;
 import static org.easymock.EasyMock.isA;
 
+import org.apache.shindig.gadgets.FakeGadgetToken;
 import org.apache.shindig.gadgets.GadgetException;
 import org.apache.shindig.gadgets.GadgetToken;
 import org.apache.shindig.gadgets.RemoteContent;
@@ -40,27 +41,7 @@
   private final static String URL_ONE = "http://www.example.com/test.html";
   private final static String DATA_ONE = "hello world";
 
-  private static final GadgetToken DUMMY_TOKEN = new GadgetToken() {
-    public String getOwnerId() {
-      return "owner";
-    }
-
-    public String getViewerId() {
-      return "viewer";
-    }
-
-    public String getAppId() {
-      return "app";
-    }
-
-    public String getDomain() {
-      return "domain";
-    }
-
-    public String toSerialForm() {
-      return "";
-    }
-  };
+  private static final GadgetToken DUMMY_TOKEN = new FakeGadgetToken();
 
   final ByteArrayOutputStream baos = new ByteArrayOutputStream();
   final PrintWriter writer = new PrintWriter(baos);

Added: incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/oauth/BasicOAuthStoreTest.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/oauth/BasicOAuthStoreTest.java?rev=645027&view=auto
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/oauth/BasicOAuthStoreTest.java (added)
+++ incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/oauth/BasicOAuthStoreTest.java Fri Apr  4 21:37:53 2008
@@ -0,0 +1,426 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.shindig.gadgets.oauth;
+
+import static org.easymock.EasyMock.eq;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.same;
+
+import junit.framework.TestCase;
+
+import net.oauth.OAuthAccessor;
+import net.oauth.OAuthServiceProvider;
+import net.oauth.signature.RSA_SHA1;
+
+import org.easymock.classextension.EasyMock;
+import org.easymock.classextension.IMocksControl;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class BasicOAuthStoreTest extends TestCase {
+
+  private IMocksControl control;
+  private Map<OAuthStore.ProviderKey, BasicOAuthStore.ProviderInfo>
+      mockProviders;
+  private Map<OAuthStore.TokenKey, OAuthStore.TokenInfo> mockTokens;
+  private BasicOAuthStore noDefaultStore;
+  private BasicOAuthStore withDefaultStore;
+  private String defaultKey = "defaultkey";
+  private String defaultSecret = "defaultsecret";  // not quite a PKCS8-encoded
+                                                   // RSA key, but that's ok
+
+  @Override
+  @SuppressWarnings("unchecked")
+  protected void setUp() throws Exception {
+    control = EasyMock.createStrictControl();
+    mockProviders = control.createMock(
+        new HashMap<OAuthStore.ProviderKey,
+                    BasicOAuthStore.ProviderInfo>().getClass());
+    mockTokens = control.createMock(
+        new HashMap<OAuthStore.TokenKey, OAuthStore.TokenInfo>().getClass());
+
+    noDefaultStore = new BasicOAuthStore();
+    noDefaultStore.setHashMapsForTesting(mockProviders, mockTokens);
+
+    withDefaultStore = new BasicOAuthStore(defaultKey, defaultSecret);
+    withDefaultStore.setHashMapsForTesting(mockProviders, mockTokens);
+  }
+
+  public void testGetOAuthAccessor() throws Exception {
+    OAuthStore.TokenKey tokenKey = new OAuthStore.TokenKey();
+    tokenKey.setGadgetUri("http://foo.bar.com/gadget.xml");
+    tokenKey.setModuleId(2348709L);
+    tokenKey.setUserId("testuser");
+    tokenKey.setServiceName("testservice");
+    tokenKey.setTokenName("testtoken");
+
+    OAuthStore.TokenInfo tokenInfo = new OAuthStore.TokenInfo("accesstoken",
+                                                              "tokensecret");
+
+    OAuthStore.ProviderKey provKey = new OAuthStore.ProviderKey();
+    provKey.setGadgetUri(tokenKey.getGadgetUri());
+    provKey.setServiceName(tokenKey.getServiceName());
+
+    OAuthServiceProvider provider = new OAuthServiceProvider(
+        "request", "authorize", "access");
+
+    BasicOAuthStore.ProviderInfo info = new BasicOAuthStore.ProviderInfo();
+
+    info.setHttpMethod(OAuthStore.HttpMethod.GET);
+    info.setSignatureType(OAuthStore.SignatureType.HMAC_SHA1);
+    info.setProvider(provider);
+
+    ////////////////////////////////////////////////////////////////////////////
+    // first, the case where we don't have a consumer key/secret
+
+    control.checkOrder(false);
+
+    expect(mockProviders.get(eq(provKey))).andReturn(info).once();
+    expect(mockTokens.get(tokenKey)).andReturn(tokenInfo);
+
+    control.replay();
+
+    OAuthStore.AccessorInfo accessorInfo =
+        withDefaultStore.getOAuthAccessor(tokenKey);
+
+    control.verify();
+
+    OAuthAccessor accessor = accessorInfo.getAccessor();
+
+    assertEquals("accesstoken", accessor.accessToken);
+    assertEquals("tokensecret", accessor.tokenSecret);
+    assertEquals(defaultKey, accessor.consumer.consumerKey);
+    assertNull(accessor.consumer.consumerSecret);
+    assertEquals(defaultSecret, accessor.consumer
+        .getProperty(RSA_SHA1.PRIVATE_KEY));
+    assertEquals("access", accessor.consumer.serviceProvider.accessTokenURL);
+    assertEquals("request", accessor.consumer.serviceProvider.requestTokenURL);
+    assertEquals("authorize",
+                 accessor.consumer.serviceProvider.userAuthorizationURL);
+
+    // now for the one that doesn't have default keys set
+    control.reset();
+    control.checkOrder(false);
+
+    expect(mockProviders.get(eq(provKey))).andReturn(info).once();
+    expect(mockTokens.get(tokenKey)).andStubReturn(tokenInfo);
+
+    control.replay();
+
+    try {
+      accessorInfo = noDefaultStore.getOAuthAccessor(tokenKey);
+      fail("expected exception, but didn't get it");
+    } catch (OAuthNoDataException e) {
+      // this is expected
+    }
+
+    control.verify();
+
+    ////////////////////////////////////////////////////////////////////////////
+    // now the case where we have negotiated an HMAC consumer secret
+
+    OAuthStore.ConsumerKeyAndSecret kas =
+        new OAuthStore.ConsumerKeyAndSecret("negotiatedkey",
+                                            "negotiatedsecret",
+                                            OAuthStore.KeyType.HMAC_SYMMETRIC);
+    info.setKeyAndSecret(kas);
+
+    control.reset();
+    control.checkOrder(false);
+
+    expect(mockProviders.get(eq(provKey))).andReturn(info).once();
+    expect(mockTokens.get(tokenKey)).andReturn(tokenInfo);
+
+    control.replay();
+
+    accessorInfo = noDefaultStore.getOAuthAccessor(tokenKey);
+
+    control.verify();
+
+    accessor = accessorInfo.getAccessor();
+
+    assertEquals("accesstoken", accessor.accessToken);
+    assertEquals("tokensecret", accessor.tokenSecret);
+    assertEquals("negotiatedkey", accessor.consumer.consumerKey);
+    assertEquals("negotiatedsecret", accessor.consumer.consumerSecret);
+    assertEquals("access", accessor.consumer.serviceProvider.accessTokenURL);
+    assertEquals("request", accessor.consumer.serviceProvider.requestTokenURL);
+    assertEquals("authorize",
+                 accessor.consumer.serviceProvider.userAuthorizationURL);
+
+    // the store with fallback keys should do the same thing
+
+    control.reset();
+    control.checkOrder(false);
+
+    expect(mockProviders.get(eq(provKey))).andReturn(info).once();
+    expect(mockTokens.get(tokenKey)).andReturn(tokenInfo);
+
+    control.replay();
+
+    accessorInfo = withDefaultStore.getOAuthAccessor(tokenKey);
+
+    control.verify();
+
+    accessor = accessorInfo.getAccessor();
+
+    assertEquals("accesstoken", accessor.accessToken);
+    assertEquals("tokensecret", accessor.tokenSecret);
+    assertEquals("negotiatedkey", accessor.consumer.consumerKey);
+    assertEquals("negotiatedsecret", accessor.consumer.consumerSecret);
+    assertEquals("access", accessor.consumer.serviceProvider.accessTokenURL);
+    assertEquals("request", accessor.consumer.serviceProvider.requestTokenURL);
+    assertEquals("authorize",
+                 accessor.consumer.serviceProvider.userAuthorizationURL);
+
+    ////////////////////////////////////////////////////////////////////////////
+    // now the case where we have negotiated an RSA consumer key
+
+    kas = new OAuthStore.ConsumerKeyAndSecret("negotiatedkey",
+                                              "negotiatedsecret",
+                                              OAuthStore.KeyType.RSA_PRIVATE);
+    info.setKeyAndSecret(kas);
+
+    control.reset();
+    control.checkOrder(false);
+
+    expect(mockProviders.get(eq(provKey))).andReturn(info).once();
+    expect(mockTokens.get(tokenKey)).andReturn(tokenInfo);
+
+    control.replay();
+
+    accessorInfo = noDefaultStore.getOAuthAccessor(tokenKey);
+
+    control.verify();
+
+    accessor = accessorInfo.getAccessor();
+
+    assertEquals("accesstoken", accessor.accessToken);
+    assertEquals("tokensecret", accessor.tokenSecret);
+    assertEquals("negotiatedkey", accessor.consumer.consumerKey);
+    assertNull(accessor.consumer.consumerSecret);
+    assertEquals("negotiatedsecret",
+                 accessor.consumer.getProperty(RSA_SHA1.PRIVATE_KEY));
+    assertEquals("access", accessor.consumer.serviceProvider.accessTokenURL);
+    assertEquals("request", accessor.consumer.serviceProvider.requestTokenURL);
+    assertEquals("authorize",
+                 accessor.consumer.serviceProvider.userAuthorizationURL);
+
+    // store with fallback keys should do the same
+
+    control.reset();
+    control.checkOrder(false);
+
+    expect(mockProviders.get(eq(provKey))).andReturn(info).once();
+    expect(mockTokens.get(tokenKey)).andReturn(tokenInfo);
+
+    control.replay();
+
+    accessorInfo = noDefaultStore.getOAuthAccessor(tokenKey);
+
+    control.verify();
+
+    accessor = accessorInfo.getAccessor();
+
+    assertEquals("accesstoken", accessor.accessToken);
+    assertEquals("tokensecret", accessor.tokenSecret);
+    assertEquals("negotiatedkey", accessor.consumer.consumerKey);
+    assertNull(accessor.consumer.consumerSecret);
+    assertEquals("negotiatedsecret",
+                 accessor.consumer.getProperty(RSA_SHA1.PRIVATE_KEY));
+    assertEquals("access", accessor.consumer.serviceProvider.accessTokenURL);
+    assertEquals("request", accessor.consumer.serviceProvider.requestTokenURL);
+    assertEquals("authorize",
+                 accessor.consumer.serviceProvider.userAuthorizationURL);
+
+    ////////////////////////////////////////////////////////////////////////////
+    // now, test some error conditions. Fist, that no such service exists...
+
+    control.reset();
+    control.checkOrder(false);
+
+    expect(mockProviders.get(eq(provKey))).andReturn(null);
+    expect(mockTokens.get(tokenKey)).andStubReturn(tokenInfo);
+
+    control.replay();
+
+    try {
+      accessorInfo = noDefaultStore.getOAuthAccessor(tokenKey);
+      fail("expected OAuthNoDataException, but didn't get it");
+    } catch (OAuthNoDataException e) {
+      // this is expected
+    }
+
+    control.verify();
+
+    // same with the store with fallback keys
+
+    control.reset();
+    control.checkOrder(false);
+
+    expect(mockProviders.get(eq(provKey))).andReturn(null);
+    expect(mockTokens.get(tokenKey)).andStubReturn(tokenInfo);
+
+    control.replay();
+
+    try {
+      accessorInfo = noDefaultStore.getOAuthAccessor(tokenKey);
+      fail("expected OAuthNoDataException, but didn't get it");
+    } catch (OAuthNoDataException e) {
+      // this is expected
+    }
+
+    control.verify();
+
+    ////////////////////////////////////////////////////////////////////////////
+    // service info is there, but no token:
+
+    control.reset();
+    control.checkOrder(false);
+
+    expect(mockProviders.get(eq(provKey))).andStubReturn(info);
+    expect(mockTokens.get(tokenKey)).andReturn(null);
+
+    control.replay();
+
+    accessorInfo = noDefaultStore.getOAuthAccessor(tokenKey);
+
+    control.verify();
+
+    assertNull(accessorInfo.getAccessor().accessToken);
+    assertNull(accessorInfo.getAccessor().requestToken);
+
+    // same with the store with fallback keys
+
+    control.reset();
+    control.checkOrder(false);
+
+    expect(mockProviders.get(eq(provKey))).andStubReturn(info);
+    expect(mockTokens.get(tokenKey)).andReturn(null);
+
+    control.replay();
+
+    accessorInfo = withDefaultStore.getOAuthAccessor(tokenKey);
+    assertNull(accessorInfo.getAccessor().accessToken);
+    assertNull(accessorInfo.getAccessor().requestToken);
+
+    control.verify();
+
+  }
+
+  public void testSetOAuthConsumerKeyAndSecret() throws Exception {
+    OAuthStore.ProviderKey provKey = new OAuthStore.ProviderKey();
+    provKey.setGadgetUri("http://foo.bar.com/gadget.xml");
+    provKey.setServiceName("testservice");
+
+    OAuthStore.ConsumerKeyAndSecret kas =
+        new OAuthStore.ConsumerKeyAndSecret("consumerkey",
+                                            "consumersecret",
+                                            OAuthStore.KeyType.HMAC_SYMMETRIC);
+
+    OAuthServiceProvider provider = new OAuthServiceProvider(
+        "request", "authorize", "access");
+
+    OAuthStore.ProviderInfo info = new OAuthStore.ProviderInfo();
+    info.setHttpMethod(OAuthStore.HttpMethod.GET);
+    info.setSignatureType(OAuthStore.SignatureType.HMAC_SHA1);
+    info.setProvider(provider);
+
+    expect(mockProviders.get(provKey)).andReturn(info);
+
+    control.replay();
+
+    noDefaultStore.setOAuthConsumerKeyAndSecret(provKey, kas);
+
+    control.verify();
+
+    // make sure that consumer key and secret got attached
+    assertSame(info.getKeyAndSecret(), kas);
+
+    ////////////////////////////////////////////////////////////////////////////
+    // now, test some error conditions
+
+    control.reset();
+
+    expect(mockProviders.get(provKey)).andReturn(null);
+
+    control.replay();
+
+    try {
+      noDefaultStore.setOAuthConsumerKeyAndSecret(provKey, kas);
+      fail("expected OAuthNoDataException, but didn't get it");
+    } catch (OAuthNoDataException ex) {
+      // this is expected
+    }
+
+    control.verify();
+  }
+
+  public void testSetOAuthServiceProviderInfo() throws Exception {
+
+    OAuthStore.ProviderKey provKey = new OAuthStore.ProviderKey();
+    provKey.setGadgetUri("http://foo.bar.com/gadget.xml");
+    provKey.setServiceName("testservice");
+
+    OAuthStore.ConsumerKeyAndSecret kas =
+        new OAuthStore.ConsumerKeyAndSecret("consumerkey",
+                                            "consumersecret",
+                                            OAuthStore.KeyType.HMAC_SYMMETRIC);
+
+    OAuthServiceProvider provider = new OAuthServiceProvider(
+        "request", "authorize", "access");
+
+    BasicOAuthStore.ProviderInfo info = new BasicOAuthStore.ProviderInfo();
+
+    info.setHttpMethod(OAuthStore.HttpMethod.GET);
+    info.setSignatureType(OAuthStore.SignatureType.HMAC_SHA1);
+    info.setProvider(provider);
+    info.setKeyAndSecret(kas);
+
+    expect(mockProviders.put(same(provKey), same(info))).andReturn(null);
+
+    control.replay();
+
+    noDefaultStore.setOAuthServiceProviderInfo(provKey, info);
+
+    control.verify();
+  }
+
+  public void testSetTokenAndSecret() throws Exception {
+    OAuthStore.TokenKey tokenKey = new OAuthStore.TokenKey();
+    tokenKey.setGadgetUri("http://foo.bar.com/gadget.xml");
+    tokenKey.setServiceName("testservice");
+    tokenKey.setModuleId(9843243278L);
+    tokenKey.setTokenName("testtoken");
+    tokenKey.setUserId("test user");
+
+    OAuthStore.TokenInfo tokenInfo =
+        new OAuthStore.TokenInfo("token", "secret");
+
+
+    expect(mockTokens.put(tokenKey, tokenInfo)).andReturn(null);
+
+    control.replay();
+
+    noDefaultStore.setTokenAndSecret(tokenKey, tokenInfo);
+
+    control.verify();
+  }
+}

Added: incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/oauth/GadgetTokenStoreTest.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/oauth/GadgetTokenStoreTest.java?rev=645027&view=auto
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/oauth/GadgetTokenStoreTest.java (added)
+++ incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/oauth/GadgetTokenStoreTest.java Fri Apr  4 21:37:53 2008
@@ -0,0 +1,344 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.shindig.gadgets.oauth;
+
+import static org.easymock.EasyMock.eq;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.expectLastCall;
+import static org.easymock.EasyMock.same;
+
+import junit.framework.TestCase;
+
+import org.apache.shindig.gadgets.GadgetException;
+import org.apache.shindig.gadgets.oauth.GadgetOAuthTokenStore.GadgetInfo;
+import org.apache.shindig.gadgets.spec.GadgetSpec;
+import org.easymock.IArgumentMatcher;
+import org.easymock.classextension.EasyMock;
+import org.easymock.classextension.IMocksControl;
+
+import java.net.URI;
+import java.util.HashMap;
+
+public class GadgetTokenStoreTest extends TestCase {
+
+  private static final String specStr =
+      "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n" +
+      "  <Module>\n" +
+      "    <ModulePrefs title=\"hello world example\">\n" +
+      "   \n" +
+      "    <Require feature=\"oauth\">\n" +
+      "      <Param name=\"service_name\">\n" +
+      "       netflix\n" +
+      "      </Param>\n" +
+      "      <Param name=\"access_url\">\n" +
+      "        http://www.netflix.com.notreally/access\n" +
+      "      </Param>\n" +
+      "      <Param name=\"access_method\">\n" +
+      "        GET\n" +
+      "      </Param>\n" +
+      "      \n" +
+      "      <Param name=\"request_url\">\n" +
+      "        http://www.netflix.com.notreally/request\n" +
+      "      </Param>\n" +
+      "      <Param name=\"request_method\">\n" +
+      "        GET\n" +
+      "      </Param>\n" +
+      "      \n" +
+      "      <Param name=\"authorize_url\">\n" +
+      "        http://www.netflix.com.notreally/authorize\n" +
+      "      </Param>\n" +
+      "    </Require>\n" +
+      "    \n" +
+      "    </ModulePrefs>\n" +
+      "    <Content type=\"html\">\n" +
+      "       <![CDATA[\n" +
+      "         Hello, world!\n" +
+      "       ]]>\n" +
+      "       \n" +
+      "    </Content>\n" +
+      "  </Module>";
+
+  private GadgetOAuthTokenStore store;
+  private GadgetSpec spec;
+  private IMocksControl control;
+  private BasicOAuthStore mockStoreImpl;
+
+  private static class ProviderInfoMatcher implements IArgumentMatcher {
+
+    private OAuthStore.ProviderInfo expectedInfo;
+
+    public ProviderInfoMatcher(OAuthStore.ProviderInfo info) {
+      expectedInfo = info;
+    }
+
+    public void appendTo(StringBuffer sb) {
+      sb.append("eqProvInfo(");
+      sb.append("access: ");
+      sb.append(expectedInfo.getProvider().accessTokenURL);
+      sb.append(", request: ");
+      sb.append(expectedInfo.getProvider().requestTokenURL);
+      sb.append(", authorize: ");
+      sb.append(expectedInfo.getProvider().userAuthorizationURL);
+      sb.append(", http_method: ");
+      sb.append(expectedInfo.getHttpMethod());
+      sb.append(", signature_type: ");
+      sb.append(expectedInfo.getSignatureType());
+      sb.append(')');
+    }
+
+    public boolean matches(Object actual) {
+      if (! (actual instanceof OAuthStore.ProviderInfo)) {
+        return false;
+      }
+
+      OAuthStore.ProviderInfo actualInfo = (OAuthStore.ProviderInfo)actual;
+
+      return (actualInfo.getHttpMethod() == expectedInfo.getHttpMethod())
+             && (actualInfo.getSignatureType()
+                 == expectedInfo.getSignatureType())
+             && actualInfo.getProvider().accessTokenURL.equals(
+                 expectedInfo.getProvider().accessTokenURL)
+             && actualInfo.getProvider().requestTokenURL.equals(
+                 expectedInfo.getProvider().requestTokenURL)
+             && actualInfo.getProvider().userAuthorizationURL.equals(
+                 expectedInfo.getProvider().userAuthorizationURL);
+    }
+  }
+
+  private static OAuthStore.ProviderInfo eqProvInfo(
+      OAuthStore.ProviderInfo info) {
+    EasyMock.reportMatcher(new ProviderInfoMatcher(info));
+    return null;
+  }
+
+  @Override
+  protected void setUp() throws Exception {
+    control = EasyMock.createStrictControl();
+    mockStoreImpl = control.createMock(BasicOAuthStore.class);
+    store = new GadgetOAuthTokenStore(mockStoreImpl);
+    spec = new GadgetSpec(new URI("http://foo.bar.com/gadget.xml"), specStr);
+  }
+
+  public void testStoreServiceInfoFromGadgetSpec() throws Exception {
+
+    String gadgetUrl = "http://foo.bar.com/gadget.xml";
+
+    // this method is tested in another test below
+    GadgetInfo gadgetInfo = GadgetOAuthTokenStore.getGadgetOAuthInfo(spec);
+
+    OAuthStore.ProviderKey providerKey = new OAuthStore.ProviderKey();
+    providerKey.setGadgetUri(gadgetUrl.toString());
+    providerKey.setServiceName(gadgetInfo.getServiceName());
+
+    mockStoreImpl.setOAuthServiceProviderInfo(
+        eq(providerKey), eqProvInfo(gadgetInfo.getProviderInfo()));
+    expectLastCall().once();
+
+    control.replay();
+
+    store.storeServiceInfoFromGadgetSpec(new URI(gadgetUrl), spec);
+
+    control.verify();
+  }
+
+  public void testStoreConsumerKeyAndSecret() throws Exception {
+
+    URI gadgetUrl = new URI("http://foo.bar.com/gadget.xml");
+    String serviceName = "testservice";
+
+    OAuthStore.ProviderKey providerKey = new OAuthStore.ProviderKey();
+    providerKey.setGadgetUri(gadgetUrl.toString());
+    providerKey.setServiceName(serviceName);
+
+    OAuthStore.ConsumerKeyAndSecret kas = new OAuthStore.ConsumerKeyAndSecret(
+        "key", "secret", OAuthStore.KeyType.HMAC_SYMMETRIC);
+
+    mockStoreImpl.setOAuthConsumerKeyAndSecret(eq(providerKey), same(kas));
+    expectLastCall().once();
+
+    control.replay();
+
+    store.storeConsumerKeyAndSecret(gadgetUrl, serviceName, kas);
+
+    control.verify();
+  }
+
+  public void testStoreTokenKeyAndSecret() throws Exception {
+
+    final String url = "http://foo.bar.com/gadget.xml";
+    final int moduleId = 42957;
+    final String userId = "testuser";
+    final String serviceName = "testservice";
+    final String tokenName = "testtoken";
+
+    OAuthStore.TokenInfo tokenInfo = new OAuthStore.TokenInfo("", "");
+
+    OAuthStore.TokenKey tokenKey = new OAuthStore.TokenKey();
+    tokenKey.setGadgetUri(url);
+    tokenKey.setModuleId(moduleId);
+    tokenKey.setServiceName(serviceName);
+    tokenKey.setTokenName(tokenName);
+    tokenKey.setUserId(userId);
+
+    mockStoreImpl.setTokenAndSecret(eq(tokenKey), same(tokenInfo));
+    expectLastCall().once();
+
+    control.replay();
+
+    store.storeTokenKeyAndSecret(tokenKey, tokenInfo);
+
+    control.verify();
+  }
+
+  public void testGetOAuthAccessor() throws Exception {
+
+    final String url = "http://foo.bar.com/gadget.xml";
+    final int moduleId = 42957;
+    final String userId = "testuser";
+    final String serviceName = "testservice";
+    final String tokenName = "testtoken";
+
+    OAuthStore.TokenKey tokenKey = new OAuthStore.TokenKey();
+    tokenKey.setGadgetUri(url);
+    tokenKey.setModuleId(moduleId);
+    tokenKey.setServiceName(serviceName);
+    tokenKey.setTokenName(tokenName);
+    tokenKey.setUserId(userId);
+
+    OAuthStore.AccessorInfo info = new OAuthStore.AccessorInfo();
+
+    expect(mockStoreImpl.getOAuthAccessor(eq(tokenKey))).andReturn(info);
+
+    control.replay();
+
+    OAuthStore.AccessorInfo compare =
+        store.getOAuthAccessor(tokenKey);
+
+    control.verify();
+
+    assertSame(info, compare);
+  }
+
+  public void testGetGadgetOAuthInfo() throws Exception {
+    GadgetOAuthTokenStore.GadgetInfo info =
+        GadgetOAuthTokenStore.getGadgetOAuthInfo(spec);
+    OAuthStore.ProviderInfo provInfo = info.getProviderInfo();
+
+    assertEquals("netflix", info.getServiceName());
+    assertEquals("http://www.netflix.com.notreally/access",
+                 provInfo.getProvider().accessTokenURL);
+    assertEquals("http://www.netflix.com.notreally/request",
+                 provInfo.getProvider().requestTokenURL);
+    assertEquals("http://www.netflix.com.notreally/authorize",
+                 provInfo.getProvider().userAuthorizationURL);
+    assertEquals(OAuthStore.HttpMethod.GET, provInfo.getHttpMethod());
+    assertEquals(OAuthStore.SignatureType.HMAC_SHA1,
+                 provInfo.getSignatureType());
+
+    // now, let's change the spec a bit
+
+    String newSpecStr = specStr.replaceAll("GET", "POST");
+    spec = new GadgetSpec(new URI("http://foo.bar.com/gadget.xml"), newSpecStr);
+
+    info = GadgetOAuthTokenStore.getGadgetOAuthInfo(spec);
+    provInfo = info.getProviderInfo();
+    assertEquals(OAuthStore.HttpMethod.POST, provInfo.getHttpMethod());
+
+    // if we only change one of the GETs, it's an error
+
+    newSpecStr = specStr.replaceFirst("GET", "POST");
+    spec = new GadgetSpec(new URI("http://foo.bar.com/gadget.xml"), newSpecStr);
+
+    try {
+      info = GadgetOAuthTokenStore.getGadgetOAuthInfo(spec);
+      fail("expected exception, but didn't get it");
+    } catch (GadgetException e) {
+      // this is expected
+    }
+
+    // let's test some other error conditions
+
+    newSpecStr = specStr.replaceAll("GET", "BLAH");
+    spec = new GadgetSpec(new URI("http://foo.bar.com/gadget.xml"), newSpecStr);
+    try {
+      info = GadgetOAuthTokenStore.getGadgetOAuthInfo(spec);
+      fail("expected exception, but didn't get it");
+    } catch (GadgetException e) {
+      // this is expected
+    }
+
+    newSpecStr = specStr.replaceFirst("request_url", "blah");
+    spec = new GadgetSpec(new URI("http://foo.bar.com/gadget.xml"), newSpecStr);
+    try {
+      info = GadgetOAuthTokenStore.getGadgetOAuthInfo(spec);
+      fail("expected exception, but didn't get it");
+    } catch (GadgetException e) {
+      // this is expected
+    }
+
+    newSpecStr = specStr.replaceFirst("access_url", "blah");
+    spec = new GadgetSpec(new URI("http://foo.bar.com/gadget.xml"), newSpecStr);
+    try {
+      info = GadgetOAuthTokenStore.getGadgetOAuthInfo(spec);
+      fail("expected exception, but didn't get it");
+    } catch (GadgetException e) {
+      // this is expected
+    }
+
+    newSpecStr = specStr.replaceFirst("authorize_url", "blah");
+    spec = new GadgetSpec(new URI("http://foo.bar.com/gadget.xml"), newSpecStr);
+    try {
+      info = GadgetOAuthTokenStore.getGadgetOAuthInfo(spec);
+      fail("expected exception, but didn't get it");
+    } catch (GadgetException e) {
+      // this is expected
+    }
+
+    // what if the gadget doesn't require oauth?
+
+    newSpecStr = specStr.replaceFirst("feature=\"oauth\"", "feature=\"blah\"");
+    spec = new GadgetSpec(
+        new URI("http://foo.bar.com/gadget.xml"),
+        newSpecStr);
+    try {
+      info = GadgetOAuthTokenStore.getGadgetOAuthInfo(spec);
+      fail("expected exception, but didn't get it");
+    } catch (GadgetException e) {
+      // this is expected
+    }
+  }
+
+  public void testGetOAuthParameter() throws Exception {
+    HashMap<String, String> params = new HashMap<String, String>();
+    params.put("foo", "bar");
+
+    assertEquals("bar",
+                 GadgetOAuthTokenStore.getOAuthParameter(params, "foo", true));
+    assertEquals("bar",
+                 GadgetOAuthTokenStore.getOAuthParameter(params, "foo", false));
+    assertNull(GadgetOAuthTokenStore.getOAuthParameter(params, "bla", true));
+
+    try {
+      GadgetOAuthTokenStore.getOAuthParameter(params, "bla", false);
+      fail("expected GadgetException, but didn't get it");
+    } catch (GadgetException e) {
+      // this is expected
+    }
+  }
+}

Modified: incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/util/BlobCrypterTest.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/util/BlobCrypterTest.java?rev=645027&r1=645026&r2=645027&view=diff
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/util/BlobCrypterTest.java (original)
+++ incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/util/BlobCrypterTest.java Fri Apr  4 21:37:53 2008
@@ -33,11 +33,11 @@
     return new JUnit4TestAdapter(BlobCrypterTest.class);
   }
 
-  private BlobCrypter crypter;
+  private BasicBlobCrypter crypter;
   private FakeTimeSource timeSource;
   
   public BlobCrypterTest() {
-    crypter = new BlobCrypter("0123456789abcdef".getBytes());
+    crypter = new BasicBlobCrypter("0123456789abcdef".getBytes());
     timeSource = new FakeTimeSource();
     crypter.timeSource = timeSource;
   }
@@ -148,7 +148,7 @@
   
   @Test
   public void testFixedKey() throws Exception {
-    BlobCrypter alt = new BlobCrypter("0123456789abcdef".getBytes());
+    BlobCrypter alt = new BasicBlobCrypter("0123456789abcdef".getBytes());
     Map<String, String> in = new HashMap<String, String>();
     in.put("a", "b");
     String blob = crypter.wrap(in);
@@ -158,12 +158,12 @@
   
   @Test
   public void testBadKey() throws Exception {
-    BlobCrypter alt = new BlobCrypter("1123456789abcdef".getBytes());
+    BlobCrypter alt = new BasicBlobCrypter("1123456789abcdef".getBytes());
     Map<String, String> in = new HashMap<String, String>();
     in.put("a", "b");
     String blob = crypter.wrap(in);
     try {
-      Map<String, String> out = alt.unwrap(blob, 30);
+      alt.unwrap(blob, 30);
       fail("Decryption should have failed");
     } catch (BlobCrypterException e) {
       // Good.
@@ -173,7 +173,7 @@
   @Test
   public void testShortKeyFails() throws Exception {
     try {
-      new BlobCrypter("0123456789abcde".getBytes());
+      new BasicBlobCrypter("0123456789abcde".getBytes());
       fail("Short key should fail");
     } catch (IllegalArgumentException e) {
       // good.

Modified: incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/util/CryptoTest.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/util/CryptoTest.java?rev=645027&r1=645026&r2=645027&view=diff
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/util/CryptoTest.java (original)
+++ incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/util/CryptoTest.java Fri Apr  4 21:37:53 2008
@@ -31,10 +31,10 @@
     return new JUnit4TestAdapter(CryptoTest.class);
   }
 
-  private BlobCrypter crypter;
+  private BasicBlobCrypter crypter;
   
   public CryptoTest() {
-    crypter = new BlobCrypter("0123456789abcdef".getBytes());
+    crypter = new BasicBlobCrypter("0123456789abcdef".getBytes());
     crypter.timeSource = new FakeTimeSource();
   }
   

Added: incubator/shindig/trunk/javascript/samplecontainer/examples/oauth.xml
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/javascript/samplecontainer/examples/oauth.xml?rev=645027&view=auto
==============================================================================
--- incubator/shindig/trunk/javascript/samplecontainer/examples/oauth.xml (added)
+++ incubator/shindig/trunk/javascript/samplecontainer/examples/oauth.xml Fri Apr  4 21:37:53 2008
@@ -0,0 +1,104 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<Module>
+  <ModulePrefs title="Pankow Gadget">
+
+  <Optional feature="oauth">
+    <Param name="service_name">
+      demo
+    </Param>
+    <Param name="access_url">
+      http://localhost:9090/oauth-provider/access_token
+    </Param>
+    <Param name="access_method">
+      GET
+    </Param>
+
+    <Param name="request_url">
+      http://localhost:9090/oauth-provider/request_token
+    </Param>
+    <Param name="request_method">
+      GET
+    </Param>
+
+    <Param name="authorize_url">
+      http://localhost:9090/oauth-provider/authorize
+    </Param>
+  </Optional>
+
+  </ModulePrefs>
+  <Content type="html">
+      <![CDATA[
+
+    <style>
+    #main {
+        margin: 0px;
+        padding: 0px;
+        font-size: small;
+    }
+    </style>
+
+    <div id="main" style="display: none">
+    </div>
+
+    <div id="approval" style="display: none">
+      <img src="http://localhost:8080/gadgets/files/samplecontainer/examples/new.gif">
+      <a id="personalize"
+        onclick="javascript:showOneSection('waiting'); return true"
+        target="approve">Personalize this gadget</a>
+    </div>
+
+    <div id="waiting" style="display: none">
+      Please click 
+      <a onclick="javascript:fetchData()">I've approved access</a>
+      once you've approved access to your data.
+    </div>
+
+    <script type="text/javascript">
+      function showOneSection(toshow) {
+        var sections = [ 'main', 'approval', 'waiting' ];
+        for (var i=0; i < sections.length; ++i) {
+          var s = sections[i];
+          var el = document.getElementById(s);
+          if (s === toshow) {
+            el.style.display = "block";
+          } else {
+            el.style.display = "none";
+          }
+        }
+      }
+
+      function fetchData() {
+        url = "http://localhost:9090/oauth-provider/echo";
+        var params = {};
+        params[gadgets.io.RequestParameters.CONTENT_TYPE] =
+          gadgets.io.ContentType.TEXT;
+        params[gadgets.io.RequestParameters.AUTHORIZATION] =
+          gadgets.io.AuthorizationType.AUTHENTICATED;
+        params[gadgets.io.RequestParameters.OAUTH_SERVICE] = "demo";
+        params[gadgets.io.RequestParameters.METHOD] =
+          gadgets.io.MethodType.GET;
+
+        gadgets.io.makeRequest(url, function (response) {
+          if (response.approvalUrl) {
+            var personalize = document.getElementById('personalize');
+            personalize.href = response.approvalUrl;
+            showOneSection('approval');
+          } else if (response.data) {
+            var main = document.getElementById('main');
+            var txt = document.createTextNode(response.data);
+            main.appendChild(txt);
+            showOneSection('main');
+          } else {
+            var main = document.getElementById('main');
+            var whoops = document.createTextNode('Something went wrong');
+            main.appendChild(whoops);
+            showOneSection('main');
+          }
+        }, params);
+      }
+
+      gadgets.util.registerOnLoadHandler(fetchData);
+    </script>
+        ]]>
+  </Content>
+</Module>

Modified: incubator/shindig/trunk/javascript/samplecontainer/samplecontainer.html
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/javascript/samplecontainer/samplecontainer.html?rev=645027&r1=645026&r2=645027&view=diff
==============================================================================
--- incubator/shindig/trunk/javascript/samplecontainer/samplecontainer.html (original)
+++ incubator/shindig/trunk/javascript/samplecontainer/samplecontainer.html Fri Apr  4 21:37:53 2008
@@ -85,7 +85,7 @@
   // Viewer and Owner
   document.getElementById("viewerId").value = viewerId;
   document.getElementById("ownerId").value = ownerId;
-  gadget.secureToken = generateSecureToken();
+  gadget.secureToken = escape(generateSecureToken());
 
   gadgets.container.addGadget(gadget);
   gadgets.container.layoutManager.setGadgetChromeIds(['gadget-chrome']);
@@ -107,7 +107,7 @@
 
   viewerId = document.getElementById("viewerId").value;
   ownerId = document.getElementById("ownerId").value;
-  gadget.secureToken = generateSecureToken();
+  gadget.secureToken = escape(generateSecureToken());
 
   gadgetUrl = document.getElementById("gadgetUrl").value;
   gadget.specUrl = gadgetUrl;
@@ -117,8 +117,17 @@
 };
 
 function generateSecureToken() {
-  // TODO: Use a real app id
-  return [ownerId, viewerId, "samplecontainerapp", "shindig"].join(":");
+  // TODO: Use a less silly mechanism of mapping a gadget URL to an appid
+  var appId = 0;
+  for (var i=0; i < gadgetUrl.length; i++) {
+    appId += gadgetUrl.charCodeAt(i);
+  }
+  var fields = [ownerId, viewerId, appId, "shindig", gadgetUrl, "0"];
+  for (var i=0; i < fields.length; i++) {
+    // escape each field individually, for metachars in URL
+    fields[i] = escape(fields[i]);
+  }
+  return fields.join(":");
 }
 
 SampleContainerGadget = function(opt_params) {