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"
}
}