Author: lindner
Date: Sun Feb 15 23:29:58 2009
New Revision: 744779

URL: http://svn.apache.org/viewvc?rev=744779&view=rev
Log:
SHINDIG-897 | Three legged OAuth support, part 1

Added:
    
incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth/OAuthAuthenticationHandler.java
    
incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/oauth/OAuthDataStore.java
    
incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/oauth/OAuthEntry.java
    
incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/sample/oauth/SampleOAuthDataStore.java
    
incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/sample/oauth/SampleOAuthServlet.java
Removed:
    
incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/oauth/OAuthLookupService.java
    
incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/sample/oauth/SampleContainerOAuthLookupService.java
Modified:
    incubator/shindig/trunk/java/common/conf/shindig.properties
    incubator/shindig/trunk/java/server/src/main/webapp/WEB-INF/web.xml
    
incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth/AuthenticationHandlerProvider.java
    
incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth/OAuthConsumerRequestAuthenticationHandler.java
    
incubator/shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/core/config/SocialApiGuiceModuleTest.java
    
incubator/shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/core/oauth/AuthenticationProviderHandlerTest.java
    incubator/shindig/trunk/javascript/sampledata/canonicaldb.json

Modified: incubator/shindig/trunk/java/common/conf/shindig.properties
URL: 
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/common/conf/shindig.properties?rev=744779&r1=744778&r2=744779&view=diff
==============================================================================
--- incubator/shindig/trunk/java/common/conf/shindig.properties (original)
+++ incubator/shindig/trunk/java/common/conf/shindig.properties Sun Feb 15 
23:29:58 2009
@@ -7,8 +7,10 @@
 # A file containing blacklisted gadgets.
 shindig.blacklist.file=
 
-# OAuth confiugration
+# OAuth confiugration, including the key file for signing requests
+# The URL base to use for full OAuth support (three-legged)
 shindig.oauth.state-key=
+shindig.oauth.base-url=/oauth/
 shindig.signing.key-name=
 shindig.signing.key-file=
 

Modified: incubator/shindig/trunk/java/server/src/main/webapp/WEB-INF/web.xml
URL: 
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/server/src/main/webapp/WEB-INF/web.xml?rev=744779&r1=744778&r2=744779&view=diff
==============================================================================
--- incubator/shindig/trunk/java/server/src/main/webapp/WEB-INF/web.xml 
(original)
+++ incubator/shindig/trunk/java/server/src/main/webapp/WEB-INF/web.xml Sun Feb 
15 23:29:58 2009
@@ -129,6 +129,14 @@
     </servlet-class>
   </servlet>
 
+  <!-- Serve sample OAuth apis -->
+  <servlet>
+    <servlet-name>sampleOAuth</servlet-name>
+    <servlet-class>
+      org.apache.shindig.social.sample.oauth.SampleOAuthServlet
+    </servlet-class>
+  </servlet>
+
   <servlet-mapping>
     <servlet-name>js</servlet-name>
     <url-pattern>/gadgets/js/*</url-pattern>
@@ -173,4 +181,9 @@
     <servlet-name>jsonRpcServlet</servlet-name>
     <url-pattern>/social/rpc/*</url-pattern>
   </servlet-mapping>
+
+  <servlet-mapping>
+    <servlet-name>sampleOAuth</servlet-name>
+    <url-pattern>/oauth/*</url-pattern>
+  </servlet-mapping>
 </web-app>

Modified: 
incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth/AuthenticationHandlerProvider.java
URL: 
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth/AuthenticationHandlerProvider.java?rev=744779&r1=744778&r2=744779&view=diff
==============================================================================
--- 
incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth/AuthenticationHandlerProvider.java
 (original)
+++ 
incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth/AuthenticationHandlerProvider.java
 Sun Feb 15 23:29:58 2009
@@ -17,14 +17,14 @@
  */
 package org.apache.shindig.social.core.oauth;
 
-import org.apache.shindig.auth.AnonymousAuthenticationHandler;
-import org.apache.shindig.auth.AuthenticationHandler;
-import org.apache.shindig.auth.UrlParameterAuthenticationHandler;
-
 import com.google.common.collect.Lists;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 
+import org.apache.shindig.auth.AnonymousAuthenticationHandler;
+import org.apache.shindig.auth.AuthenticationHandler;
+import org.apache.shindig.auth.UrlParameterAuthenticationHandler;
+
 import java.util.List;
 
 public class AuthenticationHandlerProvider implements 
Provider<List<AuthenticationHandler>> {
@@ -33,8 +33,9 @@
   @Inject
   public AuthenticationHandlerProvider(UrlParameterAuthenticationHandler 
urlParam,
       OAuthConsumerRequestAuthenticationHandler twoLeggedOAuth,
+      OAuthAuthenticationHandler threeLeggedOAuth,
       AnonymousAuthenticationHandler anonymous) {
-    handlers = Lists.newArrayList(urlParam, twoLeggedOAuth, anonymous);
+    handlers = Lists.newArrayList(urlParam, twoLeggedOAuth, threeLeggedOAuth, 
anonymous);
   }
 
   public List<AuthenticationHandler> get() {

Added: 
incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth/OAuthAuthenticationHandler.java
URL: 
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth/OAuthAuthenticationHandler.java?rev=744779&view=auto
==============================================================================
--- 
incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth/OAuthAuthenticationHandler.java
 (added)
+++ 
incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth/OAuthAuthenticationHandler.java
 Sun Feb 15 23:29:58 2009
@@ -0,0 +1,104 @@
+/*
+ * 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.social.core.oauth;
+
+import com.google.inject.Inject;
+
+import net.oauth.OAuthAccessor;
+import net.oauth.OAuthConsumer;
+import net.oauth.OAuthException;
+import net.oauth.OAuthMessage;
+import net.oauth.OAuthServiceProvider;
+import net.oauth.SimpleOAuthValidator;
+import net.oauth.server.OAuthServlet;
+import org.apache.shindig.auth.AuthenticationHandler;
+import org.apache.shindig.auth.SecurityToken;
+import org.apache.shindig.social.opensocial.oauth.OAuthDataStore;
+import org.apache.shindig.social.opensocial.oauth.OAuthEntry;
+
+import java.io.IOException;
+import java.net.URISyntaxException;
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * Normal three legged OAuth handler
+ */
+public class OAuthAuthenticationHandler implements AuthenticationHandler {
+  private static String AUTH_OAUTH_REQUEST = "OAuth";
+  private OAuthDataStore store;
+
+  @Inject
+  public OAuthAuthenticationHandler(OAuthDataStore store) {
+    this.store = store;
+  }
+
+  public String getName() {
+    return AUTH_OAUTH_REQUEST;
+  }
+
+  public String getWWWAuthenticateHeader(String realm) {
+    return String.format("OAuth realm=\"%s\"", realm);
+  }
+
+  public SecurityToken getSecurityTokenFromRequest(HttpServletRequest request) 
{
+    OAuthMessage message = OAuthServlet.getMessage(request, null);
+    OAuthEntry entry;
+
+
+    try {
+      // no token available...
+      if (message.getToken() == null) return null;
+
+      entry = store.getEntry(message.getToken());
+    } catch (IOException e) {
+      return null;
+    }
+
+    if (!isValidOAuthRequest(message, entry)) {
+      return null;
+    }
+
+    return new OAuthSecurityToken(entry.userId, entry.callbackUrl, entry.appId,
+        entry.domain, entry.container);
+  }
+
+  private boolean isValidOAuthRequest(OAuthMessage message, OAuthEntry entry) {
+    if (entry == null || entry.type != OAuthEntry.Type.ACCESS || 
entry.isExpired()) {
+      throw new InvalidAuthenticationException("access token is invalid.", 
null);
+    }
+
+    OAuthServiceProvider provider = new OAuthServiceProvider(null, null, null);
+    OAuthAccessor accessor = new OAuthAccessor(new OAuthConsumer(null, 
entry.consumerKey,
+        entry.consumerSecret, provider));
+
+    accessor.tokenSecret = entry.tokenSecret;
+    accessor.accessToken = entry.token;
+
+    try {
+      message.validateMessage(accessor, new SimpleOAuthValidator());
+    } catch (OAuthException e) {
+      throw new InvalidAuthenticationException(e.getMessage(), e);
+    } catch (IOException e) {
+      return false;
+    } catch (URISyntaxException e) {
+      return false;
+    }
+
+    return true;
+  }
+}

Modified: 
incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth/OAuthConsumerRequestAuthenticationHandler.java
URL: 
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth/OAuthConsumerRequestAuthenticationHandler.java?rev=744779&r1=744778&r2=744779&view=diff
==============================================================================
--- 
incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth/OAuthConsumerRequestAuthenticationHandler.java
 (original)
+++ 
incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth/OAuthConsumerRequestAuthenticationHandler.java
 Sun Feb 15 23:29:58 2009
@@ -17,21 +17,24 @@
  */
 package org.apache.shindig.social.core.oauth;
 
-import org.apache.shindig.auth.AuthenticationHandler;
-import org.apache.shindig.auth.SecurityToken;
-import org.apache.shindig.social.opensocial.oauth.OAuthLookupService;
-
 import com.google.inject.Inject;
-
-import org.apache.commons.lang.StringUtils;
+import com.google.inject.name.Named;
 
 import net.oauth.OAuth;
+import net.oauth.OAuthAccessor;
+import net.oauth.OAuthConsumer;
 import net.oauth.OAuthException;
 import net.oauth.OAuthMessage;
+import net.oauth.OAuthServiceProvider;
+import net.oauth.SimpleOAuthValidator;
 import net.oauth.server.OAuthServlet;
+import org.apache.commons.lang.StringUtils;
+import org.apache.shindig.auth.AuthenticationHandler;
+import org.apache.shindig.auth.SecurityToken;
+import org.apache.shindig.social.opensocial.oauth.OAuthDataStore;
 
 import java.io.IOException;
-
+import java.net.URISyntaxException;
 import javax.servlet.http.HttpServletRequest;
 
 /**
@@ -42,47 +45,64 @@
 public class OAuthConsumerRequestAuthenticationHandler implements 
AuthenticationHandler {
   public static final String AUTH_OAUTH_CONSUMER_REQUEST = 
"OAuth-ConsumerRequest";
   public static final String REQUESTOR_ID_PARAM = "xoauth_requestor_id";
-  private final OAuthLookupService service;
+  private OAuthDataStore store;
+  private String baseUrl;
 
   @Inject
-  public OAuthConsumerRequestAuthenticationHandler(OAuthLookupService service) 
{
-    this.service = service;
+  public OAuthConsumerRequestAuthenticationHandler(OAuthDataStore store,
+                  @Named("shindig.oauth.base-url") String baseUrl) {
+    this.store = store;
+    this.baseUrl = baseUrl;
   }
 
   public String getName() {
     return AUTH_OAUTH_CONSUMER_REQUEST;
   }
 
+  public String getWWWAuthenticateHeader(String realm) {
+    return String.format("OAuth realm=\"%s\"", realm);
+  }
+
   public SecurityToken getSecurityTokenFromRequest(HttpServletRequest request) 
{
     OAuthMessage requestMessage = OAuthServlet.getMessage(request, null);
+    String token = getParameter(requestMessage, OAuth.OAUTH_TOKEN);
 
-    String containerKey = getParameter(requestMessage, 
OAuth.OAUTH_CONSUMER_KEY);
-    String containerSignature = getParameter(requestMessage, 
OAuth.OAUTH_SIGNATURE);
-    String userId = StringUtils.trim(request.getParameter(REQUESTOR_ID_PARAM));
-
-    if (containerKey == null || containerSignature == null || 
StringUtils.isBlank(userId)) {
-      // This isn't a proper OAuth request
+    if (StringUtils.isBlank(token) || !isValidOAuthRequest(requestMessage)) {
       return null;
     }
 
+    String userId = StringUtils.trim(request.getParameter(REQUESTOR_ID_PARAM));
     try {
-      if (service.thirdPartyHasAccessToUser(requestMessage, containerKey, 
userId)) {
-        return service.getSecurityToken(containerKey, userId);
-      } else {
-        throw new InvalidAuthenticationException("Access for app not 
allowed",null);
-      }
-    } catch (OAuthException oae) {
-      throw new InvalidAuthenticationException(oae.getMessage(), oae);
+      return 
store.getSecurityTokenForConsumerRequest(requestMessage.getToken(), userId);
+    } catch (IOException e) {
+      throw new InvalidAuthenticationException(e.getMessage(), e);
     }
   }
 
-    public String getWWWAuthenticateHeader(String realm) {
-       return String.format("OAuth realm=\"%s\"", realm);
+  private boolean isValidOAuthRequest(OAuthMessage requestMessage) {
+    String consumerKey = getParameter(requestMessage, 
OAuth.OAUTH_CONSUMER_KEY);
+    String consumerSecret = store.getConsumerSecret(consumerKey);
+
+    OAuthServiceProvider provider = new OAuthServiceProvider(baseUrl + 
"reqeustToken", baseUrl + "authorize", baseUrl + "accessToken");
+    OAuthConsumer consumer = new OAuthConsumer(null, consumerKey, 
consumerSecret, provider);
+    OAuthAccessor accessor = new OAuthAccessor(consumer);
+
+    SimpleOAuthValidator validator = new SimpleOAuthValidator();
+    try {
+      validator.validateMessage(requestMessage, accessor);
+      return true;
+    } catch (IOException e) {
+      return false;
+    } catch (URISyntaxException e) {
+      return false;
+    } catch (OAuthException e) {
+      return false;
     }
+  }
 
-    private String getParameter(OAuthMessage requestMessage, String key) {
+  private String getParameter(OAuthMessage requestMessage, String key) {
     try {
-      return requestMessage.getParameter(key);
+      return StringUtils.trim(requestMessage.getParameter(key));
     } catch (IOException e) {
       return null;
     }

Added: 
incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/oauth/OAuthDataStore.java
URL: 
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/oauth/OAuthDataStore.java?rev=744779&view=auto
==============================================================================
--- 
incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/oauth/OAuthDataStore.java
 (added)
+++ 
incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/oauth/OAuthDataStore.java
 Sun Feb 15 23:29:58 2009
@@ -0,0 +1,92 @@
+/*
+ * 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.social.opensocial.oauth;
+
+import com.google.inject.ImplementedBy;
+import com.google.common.base.Preconditions;
+
+import org.apache.shindig.auth.SecurityToken;
+import org.apache.shindig.social.sample.oauth.SampleOAuthDataStore;
+
+import java.util.UUID;
+import java.util.Date;
+
+...@implementedby(SampleOAuthDataStore.class)
+
+/**
+ * A class that manages the OAuth data for Shindig, including
+ * storing the map of consumer key/secrets, storing request and
+ * access tokens, and providing a way to upgrade tokens to
+ * authorized values.
+ */
+
+public interface OAuthDataStore {
+  /**
+   * Get the OAuthEntry that corresponds to the oauthToken.
+   *
+   * @param oauthToken a non-null oauthToken
+   * @return a valid OAuthEntry or null if no match
+   */
+  OAuthEntry getEntry(String oauthToken);
+
+
+  /**
+   * Return the proper security token for a 2 legged oauth request that has 
been validated
+   * for the given consumerKey. App specific checks like making sure the 
requested user has the
+   * app installed should take place in this method.
+   *
+   * @param consumerKey
+   * @param userId
+   * @return A valid Security Token
+   */
+  SecurityToken getSecurityTokenForConsumerRequest(String consumerKey, String 
userId);
+
+  /**
+   * If the passed in consumerKey is valid, pass back the consumerSecret.
+   *
+   * @param consumerKey A consumer key to test.
+   * @return the consumer secret for the specific consumer key.
+   */
+  String getConsumerSecret(String consumerKey);
+
+  /**
+   * Generate a valid requestToken for the given consumerKey.
+   *
+   * @param consumerKey A valid consumer key
+   * @return An OAuthEntry containing a valid request token.
+   */
+  OAuthEntry generateRequestToken(String consumerKey);
+
+
+  /**
+   * Called when converting a request token to an access token.  This is called
+   * in the final phase of 3-legged OAuth after the user authorizes the app.
+   *
+   * @param entry
+   */
+  OAuthEntry convertToAccessToken(OAuthEntry entry);
+
+
+  /**
+   * Authorize the request token for the given user id.
+   *
+   * @param entry A valid OAuthEntry
+   * @param userId A user id
+   */
+  void authorizeToken(OAuthEntry entry, String userId);
+}

Added: 
incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/oauth/OAuthEntry.java
URL: 
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/oauth/OAuthEntry.java?rev=744779&view=auto
==============================================================================
--- 
incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/oauth/OAuthEntry.java
 (added)
+++ 
incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/oauth/OAuthEntry.java
 Sun Feb 15 23:29:58 2009
@@ -0,0 +1,92 @@
+/*
+ * 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.social.opensocial.oauth;
+
+import com.google.inject.util.Objects;
+
+import java.util.Date;
+import java.io.Serializable;
+
+/**
+ * The OAuthEntry class contains state information about OAuth Tokens and
+ * Authorization.
+ */
+public class OAuthEntry implements Serializable {
+  private static final long ONE_YEAR = 365 * 24 * 60 * 60 * 1000L;
+  private static final long FIVE_MINUTES = 5 * 60 * 1000L;
+
+  // Change this when incompatible changes occur..
+  static final long serialVersionUID = 1;
+
+  public static enum Type {
+    REQUEST, ACCESS
+  }
+
+  public String appId;
+  public String callbackUrl;
+  public String userId;
+  public String token;
+  public String tokenSecret;
+
+  public boolean authorized;
+
+  public String consumerKey;
+  public String consumerSecret;
+
+  public Type type;
+  public Date issueTime;
+
+  public String domain;
+  public String container;
+
+  public OAuthEntry() {}
+
+  /**
+   * A copy constructor
+   * @param old the OAuthEntry to duplicate
+   */
+  public OAuthEntry(OAuthEntry old) {
+    this.appId = old.appId;
+    this.callbackUrl = old.callbackUrl;
+    this.userId = old.userId;
+    this.token = old.token;
+    this.tokenSecret= old.tokenSecret;
+    this.authorized = old.authorized;
+    this.consumerKey = old.consumerKey;
+    this.consumerSecret = old.consumerSecret;
+    this.type = old.type;
+    this.issueTime = old.issueTime;
+    this.domain = old.domain;
+    this.container = old.container;
+  }
+
+  public boolean isExpired() {
+    long expirationTime = issueTime.getTime();
+    switch (type) {
+      case REQUEST:
+        expirationTime += FIVE_MINUTES;
+        break;
+      case ACCESS:
+        expirationTime += ONE_YEAR;
+        break;
+    }
+
+    Date currentDate = new Date();
+    return currentDate.getTime() > expirationTime;
+  }
+}

Added: 
incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/sample/oauth/SampleOAuthDataStore.java
URL: 
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/sample/oauth/SampleOAuthDataStore.java?rev=744779&view=auto
==============================================================================
--- 
incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/sample/oauth/SampleOAuthDataStore.java
 (added)
+++ 
incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/sample/oauth/SampleOAuthDataStore.java
 Sun Feb 15 23:29:58 2009
@@ -0,0 +1,117 @@
+/*
+ * 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.social.sample.oauth;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Maps;
+import com.google.inject.Inject;
+
+import org.apache.shindig.auth.SecurityToken;
+import org.apache.shindig.social.core.oauth.OAuthSecurityToken;
+import org.apache.shindig.social.opensocial.oauth.OAuthDataStore;
+import org.apache.shindig.social.opensocial.oauth.OAuthEntry;
+import org.apache.shindig.social.sample.spi.JsonDbOpensocialService;
+import org.json.JSONException;
+
+import java.util.Date;
+import java.util.Map;
+import java.util.UUID;
+import java.util.concurrent.ConcurrentHashMap;
+
+// Sample implementation for OAuth data store
+public class SampleOAuthDataStore implements OAuthDataStore {
+  // used to get samplecontainer data from canonicaldb.json
+  private JsonDbOpensocialService service;
+
+  @Inject
+  public SampleOAuthDataStore(JsonDbOpensocialService dbService) {
+    this.service = dbService;
+  }
+
+  // All valid OAuth tokens
+  private static ConcurrentHashMap<String,OAuthEntry> oauthTokens = 
Maps.newConcurrentHashMap();
+
+  // Get the OAuthEntry that corresponds to the oauthToken
+  public OAuthEntry getEntry(String oauthToken) {
+    Preconditions.checkNotNull(oauthToken);
+    return oauthTokens.get(oauthToken);
+  }
+
+  // If the passed in consumerKey is valid, pass back the consumerSecret
+  public String getConsumerSecret(String consumerKey) {
+    try {
+       return 
service.getDb().getJSONObject("consumerSecrets").getString(Preconditions.checkNotNull(consumerKey));
+    } catch (JSONException e) {
+       return null;
+    }
+  }
+
+  // Generate a valid requestToken for the given consumerKey
+  public OAuthEntry generateRequestToken(String consumerKey) {
+    OAuthEntry entry = new OAuthEntry();
+    entry.appId = consumerKey;
+    entry.consumerKey = consumerKey;
+    entry.consumerSecret = getConsumerSecret(consumerKey);
+    entry.domain = "samplecontainer.com";
+    entry.container = "default";
+
+    entry.token = UUID.randomUUID().toString();
+    entry.tokenSecret = UUID.randomUUID().toString();
+      
+    entry.type = OAuthEntry.Type.REQUEST;
+    entry.issueTime = new Date();
+
+    oauthTokens.put(entry.token, entry);
+    return entry;
+  }
+
+  // Turns the request token into an access token
+  public OAuthEntry convertToAccessToken(OAuthEntry entry) {
+    Preconditions.checkNotNull(entry);
+    Preconditions.checkState(entry.type == OAuthEntry.Type.REQUEST, "Token 
must be a request token");
+
+    OAuthEntry accessEntry = new OAuthEntry(entry);
+
+    accessEntry.token = UUID.randomUUID().toString();
+    accessEntry.tokenSecret = UUID.randomUUID().toString();
+
+    accessEntry.type = OAuthEntry.Type.ACCESS;
+    accessEntry.issueTime = new Date();
+
+    oauthTokens.put(entry.token, entry);
+
+    return entry;
+  }
+
+  // Authorize the request token for the given user id
+  public void authorizeToken(OAuthEntry entry, String userId) {
+    Preconditions.checkNotNull(entry);
+    entry.authorized = true;
+    entry.userId = Preconditions.checkNotNull(userId);
+  }
+
+  // Return the proper security token for a 2 legged oauth request that has 
been validated
+  // for the given consumerKey. App specific checks like making sure the 
requested user has the
+  // app installed should take place in this method
+  public SecurityToken getSecurityTokenForConsumerRequest(String consumerKey, 
String userId) {
+    String domain = "samplecontainer.com";
+    String container = "default";
+    
+    return new OAuthSecurityToken(userId, null, consumerKey, domain, 
container);
+  }
+}

Added: 
incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/sample/oauth/SampleOAuthServlet.java
URL: 
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/sample/oauth/SampleOAuthServlet.java?rev=744779&view=auto
==============================================================================
--- 
incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/sample/oauth/SampleOAuthServlet.java
 (added)
+++ 
incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/sample/oauth/SampleOAuthServlet.java
 Sun Feb 15 23:29:58 2009
@@ -0,0 +1,191 @@
+/*
+ * 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.social.sample.oauth;
+
+import com.google.inject.Inject;
+
+import net.oauth.OAuth;
+import net.oauth.OAuthAccessor;
+import net.oauth.OAuthConsumer;
+import net.oauth.OAuthException;
+import net.oauth.OAuthMessage;
+import net.oauth.OAuthValidator;
+import net.oauth.SimpleOAuthValidator;
+import net.oauth.server.OAuthServlet;
+import org.apache.shindig.common.servlet.InjectedServlet;
+import org.apache.shindig.social.opensocial.oauth.OAuthEntry;
+import org.apache.shindig.social.opensocial.oauth.OAuthDataStore;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.net.URISyntaxException;
+import java.util.List;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * This is a sample class that demonstrates how oauth tokens can be handed out 
and authorized.
+ * This is most certainly not production code. Your server should have clear 
ui, require user
+ * login for creating consumer secrets and authorizing request tokens, do 
better patch dispatching,
+ * and use a non-in memory data store.
+ */
+public class SampleOAuthServlet extends InjectedServlet {
+  public static final OAuthValidator VALIDATOR = new SimpleOAuthValidator();
+  private OAuthDataStore dataStore;
+
+  @Inject
+  public void setDataStore(OAuthDataStore dataStore) {
+    this.dataStore = dataStore;
+  }
+
+  @Override
+  protected void doGet(HttpServletRequest servletRequest,
+      HttpServletResponse servletResponse) throws ServletException, 
IOException {
+    String path = servletRequest.getPathInfo();
+
+    if (path.endsWith("requestToken")) {
+      createRequestToken(servletRequest, servletResponse);
+    } else if (path.endsWith("authorize")) {
+      authorizeRequestToken(servletRequest, servletResponse);
+    } else if (path.endsWith("accessToken")) {
+      createAccessToken(servletRequest, servletResponse);
+    }
+  }
+
+  // Hand out a request token if the consumer key and secret are valid
+  private void createRequestToken(HttpServletRequest servletRequest,
+      HttpServletResponse servletResponse) throws ServletException, 
IOException {
+    OAuthMessage requestMessage = OAuthServlet.getMessage(servletRequest, 
null);
+
+    String consumerKey = requestMessage.getConsumerKey();
+    String consumerSecret = dataStore.getConsumerSecret(consumerKey);
+
+    OAuthAccessor accessor = new OAuthAccessor(new OAuthConsumer(null, 
consumerKey,
+        consumerSecret, null));
+    try {
+      VALIDATOR.validateMessage(requestMessage, accessor);
+    } catch (OAuthException e) {
+      handleException(e, servletRequest, servletResponse, true);
+    } catch (URISyntaxException e) {
+      handleException(e, servletRequest, servletResponse, true);
+    }
+
+    // generate request_token and secret
+    OAuthEntry entry = dataStore.generateRequestToken(consumerKey);
+
+    sendResponse(servletResponse, OAuth.newList(OAuth.OAUTH_TOKEN, entry.token,
+        OAuth.OAUTH_TOKEN_SECRET, entry.tokenSecret));
+  }
+
+  private void authorizeRequestToken(HttpServletRequest servletRequest,
+      HttpServletResponse servletResponse) throws ServletException, 
IOException {
+    OAuthMessage requestMessage = OAuthServlet.getMessage(servletRequest, 
null);
+    OAuthEntry entry = getRequestToken(servletRequest, servletResponse, 
requestMessage);
+
+    // NOTE: Generally there would be a ui flow here, where the currently 
logged in user would be
+    // asked if they want to share their data with the third party that holds 
the consumer key
+    // We are simply going to assume that "canonical" has already granted 
access
+    dataStore.authorizeToken(entry, "canonical");
+
+    if (!entry.authorized) {
+        
+    }
+    // redirect to callback param oauth_callback
+    String callback = requestMessage.getParameter("oauth_callback");
+    if (callback == null) {
+      servletResponse.setContentType("text/plain");
+      OutputStream out = servletResponse.getOutputStream();
+      out.write("Token successfully authorized.".getBytes());
+      out.close();
+    } else {
+      callback = OAuth.addParameters(callback, OAuth.OAUTH_TOKEN, entry.token);
+      servletResponse.setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY);
+      servletResponse.setHeader("Location", callback);
+    }
+  }
+
+  // Hand out an access token if the consumer key and secret are valid and the 
user authorized
+  // the requestToken
+  private void createAccessToken(HttpServletRequest servletRequest,
+      HttpServletResponse servletResponse) throws ServletException, 
IOException {
+    OAuthMessage requestMessage = OAuthServlet.getMessage(servletRequest, 
null);
+
+    OAuthEntry entry = getRequestToken(servletRequest, servletResponse, 
requestMessage);
+    if (!entry.authorized) {
+      throw new ServletException("permission denied. request token has not 
been authorized.");
+    }
+
+    // turn request token into access token
+    OAuthEntry accessEntry = dataStore.convertToAccessToken(entry);
+
+    sendResponse(servletResponse, OAuth.newList(OAuth.OAUTH_TOKEN, 
accessEntry.token,
+        OAuth.OAUTH_TOKEN_SECRET, accessEntry.tokenSecret));
+  }
+
+  private OAuthEntry getRequestToken(HttpServletRequest servletRequest,
+      HttpServletResponse servletResponse, OAuthMessage requestMessage)
+      throws IOException, ServletException {
+
+    System.out.println("Getting a request Token for message: " + 
requestMessage);
+
+    OAuthEntry entry = dataStore.getEntry(requestMessage.getToken());
+    if (entry == null || entry.type != OAuthEntry.Type.REQUEST || 
entry.isExpired()) {
+      throw new ServletException("permission denied. request token is 
invalid.");
+    }
+
+    // find consumer key, compare with supplied value, if present.
+    String consumerKey = entry.consumerKey;
+    if (requestMessage.getConsumerKey() != null && 
!consumerKey.equals(requestMessage.getConsumerKey())) {
+        throw new ServletException("permission denied. consumer keys don't 
match.");
+    }
+
+    String consumerSecret = dataStore.getConsumerSecret(consumerKey);
+
+    OAuthAccessor accessor = new OAuthAccessor(new OAuthConsumer(null, 
consumerKey,
+        consumerSecret, null));
+    accessor.requestToken = entry.token;
+    accessor.tokenSecret = entry.tokenSecret;
+
+    try {
+      VALIDATOR.validateMessage(requestMessage, accessor);
+    } catch (OAuthException e) {
+      handleException(e, servletRequest, servletResponse, true);
+    } catch (URISyntaxException e) {
+      handleException(e, servletRequest, servletResponse, true);
+    }
+    return entry;
+  }
+
+  private void sendResponse(HttpServletResponse servletResponse, 
List<OAuth.Parameter> parameters)
+      throws IOException {
+    servletResponse.setContentType("text/plain");
+    OutputStream out = servletResponse.getOutputStream();
+    OAuth.formEncode(parameters, out);
+    out.close();
+  }
+
+  private static void handleException(Exception e, HttpServletRequest request,
+      HttpServletResponse response, boolean sendBody)
+      throws IOException, ServletException {
+    String realm = (request.isSecure()) ? "https://"; : "http://";;
+    realm += request.getLocalName();
+    OAuthServlet.handleException(response, e, realm, sendBody);
+  }
+
+}

Modified: 
incubator/shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/core/config/SocialApiGuiceModuleTest.java
URL: 
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/core/config/SocialApiGuiceModuleTest.java?rev=744779&r1=744778&r2=744779&view=diff
==============================================================================
--- 
incubator/shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/core/config/SocialApiGuiceModuleTest.java
 (original)
+++ 
incubator/shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/core/config/SocialApiGuiceModuleTest.java
 Sun Feb 15 23:29:58 2009
@@ -45,11 +45,11 @@
 
     AuthenticationHandlerProvider provider =
         injector.getInstance(AuthenticationHandlerProvider.class);
-    assertEquals(3, provider.get().size());
+    assertEquals(4, provider.get().size());
 
     List<AuthenticationHandler> handlers = injector.getInstance(
         Key.get(new TypeLiteral<List<AuthenticationHandler>>(){}));
 
-    assertEquals(3, handlers.size());
+    assertEquals(4, handlers.size());
   }
 }

Modified: 
incubator/shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/core/oauth/AuthenticationProviderHandlerTest.java
URL: 
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/core/oauth/AuthenticationProviderHandlerTest.java?rev=744779&r1=744778&r2=744779&view=diff
==============================================================================
--- 
incubator/shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/core/oauth/AuthenticationProviderHandlerTest.java
 (original)
+++ 
incubator/shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/core/oauth/AuthenticationProviderHandlerTest.java
 Sun Feb 15 23:29:58 2009
@@ -56,7 +56,7 @@
    */
   public static class ProvidesNoHandlers extends AuthenticationHandlerProvider 
{
     public ProvidesNoHandlers() {
-      super(null, null, null);
+      super(null, null, null, null);
     }
 
     @Override

Modified: incubator/shindig/trunk/javascript/sampledata/canonicaldb.json
URL: 
http://svn.apache.org/viewvc/incubator/shindig/trunk/javascript/sampledata/canonicaldb.json?rev=744779&r1=744778&r2=744779&view=diff
==============================================================================
--- incubator/shindig/trunk/javascript/sampledata/canonicaldb.json (original)
+++ incubator/shindig/trunk/javascript/sampledata/canonicaldb.json Sun Feb 15 
23:29:58 2009
@@ -351,5 +351,13 @@
 "jane.doe" : ["9158", "9703"],
 "george.doe" : ["9143"],
 "maija.m" : []
+},
+//
+// Consumer Secrets for OAuth REST validations
+// In this case the consumerKey is the App URL.
+//
+"consumerSecrets" : {
+    
"http://localhost:8080/gadgets/files/samplecontainer/examples/SocialHelloWorld.xml":
 "secret",
+    
"http://localhost:8080/gadgets/files/samplecontainer/examples/SocialActivitiesWorld.xml";
 : "secret"
 }
 }


Reply via email to