You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@shindig.apache.org by li...@apache.org on 2009/02/18 10:02:31 UTC
svn commit: r745440 - in /incubator/shindig/trunk/java: common/conf/
server/src/main/webapp/WEB-INF/
social-api/src/main/java/org/apache/shindig/social/core/oauth/
social-api/src/main/java/org/apache/shindig/social/opensocial/oauth/
social-api/src/main...
Author: lindner
Date: Wed Feb 18 09:02:30 2009
New Revision: 745440
URL: http://svn.apache.org/viewvc?rev=745440&view=rev
Log:
SHINDIG-897 | Three legged OAuth support, part 2 | Authorization servlet working, OAuthDataStore refactor, more
Added:
incubator/shindig/trunk/java/server/src/main/webapp/WEB-INF/authorize.jsp
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/OAuthConsumerRequestAuthenticationHandler.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/sample/oauth/SampleOAuthDataStore.java
incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/sample/oauth/SampleOAuthServlet.java
Modified: incubator/shindig/trunk/java/common/conf/shindig.properties
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/common/conf/shindig.properties?rev=745440&r1=745439&r2=745440&view=diff
==============================================================================
--- incubator/shindig/trunk/java/common/conf/shindig.properties (original)
+++ incubator/shindig/trunk/java/common/conf/shindig.properties Wed Feb 18 09:02:30 2009
@@ -11,6 +11,7 @@
# The URL base to use for full OAuth support (three-legged)
shindig.oauth.state-key=
shindig.oauth.base-url=/oauth/
+shindig.oauth.authorize-action=/WEB-INF/authorize.jsp
shindig.signing.key-name=
shindig.signing.key-file=
Added: incubator/shindig/trunk/java/server/src/main/webapp/WEB-INF/authorize.jsp
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/server/src/main/webapp/WEB-INF/authorize.jsp?rev=745440&view=auto
==============================================================================
--- incubator/shindig/trunk/java/server/src/main/webapp/WEB-INF/authorize.jsp (added)
+++ incubator/shindig/trunk/java/server/src/main/webapp/WEB-INF/authorize.jsp Wed Feb 18 09:02:30 2009
@@ -0,0 +1,50 @@
+<%@ page import="net.oauth.OAuthConsumer" %>
+<%@ page import="org.apache.shindig.social.opensocial.oauth.OAuthEntry" %>
+<%@ page import="org.apache.shindig.social.opensocial.oauth.OAuthDataStore" %>
+<%@ page contentType="text/html;charset=UTF-8" language="java" %>
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd">
+<%
+ // Gather data passed in to us.
+ OAuthConsumer consumer = (OAuthConsumer)request.getAttribute("CONSUMER");
+ OAuthEntry entry = (OAuthEntry) request.getAttribute("OAUTH_ENTRY");
+ OAuthDataStore dataStore = (OAuthDataStore) request.getAttribute("OAUTH_DATASTORE");
+
+ String appDesc = (String)consumer.getProperty("description");
+ if (appDesc == null)
+ appDesc = consumer.consumerKey;
+
+ String token = (String)request.getAttribute("TOKEN");
+ String callback = (String)request.getAttribute("CALLBACK");
+
+ if (request.getParameter("userId") != null) {
+ // User posted the form with the user_id setting. Let's mark the token authorized and redirect back
+ // This is ugly and insecure. A production form would perform
+ // proper authentication and use the container provided user id.
+ dataStore.authorizeToken(entry, request.getParameter("userId"));
+ response.sendRedirect("/oauth/authorize?oauth_token=" + token + "&oauth_callback=" + callback);
+ }
+%>
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd">
+
+<html>
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+ <title>Your Friendly OAuth Provider</title>
+</head>
+<body>
+
+<h3>"<%=appDesc%>" is trying to access your information.</h3>
+
+Enter the userId you want to be known as:
+<form name="authZForm" action="authorize" method="POST">
+ <input type="text" name="userId" value="" size="20"/><br>
+ <input type="hidden" name="oauth_token" value="<%= token %>"/>
+ <input type="hidden" name="oauth_callback" value="<%= callback %>"/>
+ <input type="submit" name="Authorize" value="Authorize"/>
+</form>
+
+</body>
+</html>
+</html>
\ No newline at end of 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=745440&r1=745439&r2=745440&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 Wed Feb 18 09:02:30 2009
@@ -35,6 +35,14 @@
</param-value>
</context-param>
+ <login-config>
+ <auth-method>FORM</auth-method>
+ <form-login-config>
+ <form-login-page>/sample_login.jsp</form-login-page>
+ <form-error-page>/sample_fail.jsp</form-error-page>
+ </form-login-config>
+ </login-config>
+
<filter>
<filter-name>authFilter</filter-name>
<filter-class>org.apache.shindig.auth.AuthenticationServletFilter</filter-class>
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=745440&r1=745439&r2=745440&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 Wed Feb 18 09:02:30 2009
@@ -81,10 +81,7 @@
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);
+ OAuthConsumer consumer = store.getConsumer(consumerKey);
OAuthAccessor accessor = new OAuthAccessor(consumer);
SimpleOAuthValidator validator = new SimpleOAuthValidator();
Modified: 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=745440&r1=745439&r2=745440&view=diff
==============================================================================
--- incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/oauth/OAuthDataStore.java (original)
+++ incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/oauth/OAuthDataStore.java Wed Feb 18 09:02:30 2009
@@ -26,6 +26,8 @@
import java.util.UUID;
import java.util.Date;
+import net.oauth.OAuthConsumer;
+
@ImplementedBy(SampleOAuthDataStore.class)
/**
@@ -57,12 +59,18 @@
SecurityToken getSecurityTokenForConsumerRequest(String consumerKey, String userId);
/**
- * If the passed in consumerKey is valid, pass back the consumerSecret.
+ * Lookup consumers. Generally this corresponds to an opensocial Application
+ * but could be abstracted in other ways. If you have multiple containers you
+ * may want to include the container as part of the identifier.
+ *
+ * Your consumer object should have the key and secret, a link to your provider
+ * plus you should consider setting properties that correspond to the metadata
+ * in the opensocial app like icon, description, etc.
*
- * @param consumerKey A consumer key to test.
- * @return the consumer secret for the specific consumer key.
+ * @param consumerKey A valid, non-null ConsumerKey
+ * @return the consumer object corresponding to the specified key.
*/
- String getConsumerSecret(String consumerKey);
+ OAuthConsumer getConsumer(String consumerKey);
/**
* Generate a valid requestToken for the given consumerKey.
Modified: 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=745440&r1=745439&r2=745440&view=diff
==============================================================================
--- incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/sample/oauth/SampleOAuthDataStore.java (original)
+++ incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/sample/oauth/SampleOAuthDataStore.java Wed Feb 18 09:02:30 2009
@@ -20,6 +20,7 @@
import com.google.common.base.Preconditions;
import com.google.common.collect.Maps;
import com.google.inject.Inject;
+import com.google.inject.name.Named;
import org.apache.shindig.auth.SecurityToken;
import org.apache.shindig.social.core.oauth.OAuthSecurityToken;
@@ -29,33 +30,43 @@
import org.json.JSONException;
import java.util.Date;
-import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
+import net.oauth.OAuthConsumer;
+import net.oauth.OAuthServiceProvider;
+
// Sample implementation for OAuth data store
public class SampleOAuthDataStore implements OAuthDataStore {
// used to get samplecontainer data from canonicaldb.json
- private JsonDbOpensocialService service;
+ private final JsonDbOpensocialService service;
+ private final OAuthServiceProvider SERVICE_PROVIDER;
@Inject
- public SampleOAuthDataStore(JsonDbOpensocialService dbService) {
+ public SampleOAuthDataStore(JsonDbOpensocialService dbService, @Named("shindig.oauth.base-url") String baseUrl) {
this.service = dbService;
+ this.SERVICE_PROVIDER = new OAuthServiceProvider(baseUrl + "requestToken", baseUrl + "authorize", baseUrl + "accessToken");
}
// All valid OAuth tokens
- private static ConcurrentHashMap<String,OAuthEntry> oauthTokens = Maps.newConcurrentHashMap();
+ private static ConcurrentHashMap<String,OAuthEntry> oauthEntries = Maps.newConcurrentHashMap();
// Get the OAuthEntry that corresponds to the oauthToken
public OAuthEntry getEntry(String oauthToken) {
Preconditions.checkNotNull(oauthToken);
- return oauthTokens.get(oauthToken);
+ return oauthEntries.get(oauthToken);
}
- // If the passed in consumerKey is valid, pass back the consumerSecret
- public String getConsumerSecret(String consumerKey) {
+ public OAuthConsumer getConsumer(String consumerKey) {
try {
- return service.getDb().getJSONObject("consumerSecrets").getString(Preconditions.checkNotNull(consumerKey));
+ String consumerSecret = service.getDb().getJSONObject("consumerSecrets").getString(Preconditions.checkNotNull(consumerKey));
+ if (consumerSecret == null)
+ return null;
+ // null below is for the callbackUrl, which we don't have in the db
+ OAuthConsumer consumer = new OAuthConsumer(null, consumerKey, consumerSecret, SERVICE_PROVIDER);
+ consumer.setProperty("samplecontainer-attribute", "value");
+ return consumer;
+
} catch (JSONException e) {
return null;
}
@@ -66,7 +77,7 @@
OAuthEntry entry = new OAuthEntry();
entry.appId = consumerKey;
entry.consumerKey = consumerKey;
- entry.consumerSecret = getConsumerSecret(consumerKey);
+ entry.consumerSecret = getConsumer(consumerKey).consumerSecret;
entry.domain = "samplecontainer.com";
entry.container = "default";
@@ -76,7 +87,7 @@
entry.type = OAuthEntry.Type.REQUEST;
entry.issueTime = new Date();
- oauthTokens.put(entry.token, entry);
+ oauthEntries.put(entry.token, entry);
return entry;
}
@@ -93,7 +104,7 @@
accessEntry.type = OAuthEntry.Type.ACCESS;
accessEntry.issueTime = new Date();
- oauthTokens.put(entry.token, entry);
+ oauthEntries.put(entry.token, entry);
return entry;
}
Modified: 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=745440&r1=745439&r2=745440&view=diff
==============================================================================
--- incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/sample/oauth/SampleOAuthServlet.java (original)
+++ incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/sample/oauth/SampleOAuthServlet.java Wed Feb 18 09:02:30 2009
@@ -18,6 +18,7 @@
package org.apache.shindig.social.sample.oauth;
import com.google.inject.Inject;
+import com.google.inject.name.Named;
import net.oauth.OAuth;
import net.oauth.OAuthAccessor;
@@ -48,12 +49,24 @@
public class SampleOAuthServlet extends InjectedServlet {
public static final OAuthValidator VALIDATOR = new SimpleOAuthValidator();
private OAuthDataStore dataStore;
+ private String oauthAuthorizeAction;
@Inject
public void setDataStore(OAuthDataStore dataStore) {
this.dataStore = dataStore;
}
+ @Inject void setAuthorizeAction(@Named("shindig.oauth.authorize-action") String authorizeAction) {
+ this.oauthAuthorizeAction = authorizeAction;
+ }
+
+ @Override
+ protected void doPost(HttpServletRequest servletRequest,
+ HttpServletResponse servletResponse) throws ServletException, IOException {
+
+ doGet(servletRequest, servletResponse);
+ }
+
@Override
protected void doGet(HttpServletRequest servletRequest,
HttpServletResponse servletResponse) throws ServletException, IOException {
@@ -74,10 +87,8 @@
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));
+ OAuthConsumer consumer = dataStore.getConsumer(consumerKey);
+ OAuthAccessor accessor = new OAuthAccessor(consumer);
try {
VALIDATOR.validateMessage(requestMessage, accessor);
} catch (OAuthException e) {
@@ -93,21 +104,50 @@
OAuth.OAUTH_TOKEN_SECRET, entry.tokenSecret));
}
+
+ /////////////////////
+ // deal with authorization request
private void authorizeRequestToken(HttpServletRequest servletRequest,
HttpServletResponse servletResponse) throws ServletException, IOException {
+
OAuthMessage requestMessage = OAuthServlet.getMessage(servletRequest, null);
- OAuthEntry entry = getRequestToken(servletRequest, servletResponse, requestMessage);
+ OAuthEntry entry = getToken(servletRequest, servletResponse, requestMessage, false);
- // 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 == null)
+ return;
+
+ OAuthConsumer consumer = dataStore.getConsumer(entry.consumerKey);
+ String callback = requestMessage.getParameter("oauth_callback");
+ if (callback == null) {
+ // see if the consumer has a callback
+ callback = consumer.callbackURL;
+ }
+ // Redirect to a UI flow if the token is not authorized
if (!entry.authorized) {
+ // TBD -- need to decode encrypted payload somehow..
+ if (this.oauthAuthorizeAction.startsWith("http")) {
+ // Redirect to authorization page with params
+ // Supply standard set of params
+ // TBD
+ } else {
+ // Use internal forward to a jsp page
+ servletRequest.setAttribute("OAUTH_DATASTORE", dataStore);
+
+ servletRequest.setAttribute("OAUTH_ENTRY", entry);
+ servletRequest.setAttribute("CALLBACK", callback);
+
+ servletRequest.setAttribute("TOKEN", entry.token);
+ servletRequest.setAttribute("CONSUMER", consumer);
+ servletRequest.getRequestDispatcher(oauthAuthorizeAction).forward(servletRequest,servletResponse);
+ }
+ return;
}
+
+ // If we're here then the entry has been authorized out of band.
+
// redirect to callback param oauth_callback
- String callback = requestMessage.getParameter("oauth_callback");
if (callback == null) {
servletResponse.setContentType("text/plain");
OutputStream out = servletResponse.getOutputStream();
@@ -115,6 +155,8 @@
out.close();
} else {
callback = OAuth.addParameters(callback, OAuth.OAUTH_TOKEN, entry.token);
+ callback = OAuth.addParameters(callback, "user_id", entry.userId);
+
servletResponse.setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY);
servletResponse.setHeader("Location", callback);
}
@@ -126,7 +168,7 @@
HttpServletResponse servletResponse) throws ServletException, IOException {
OAuthMessage requestMessage = OAuthServlet.getMessage(servletRequest, null);
- OAuthEntry entry = getRequestToken(servletRequest, servletResponse, requestMessage);
+ OAuthEntry entry = getToken(servletRequest, servletResponse, requestMessage, true);
if (!entry.authorized) {
throw new ServletException("permission denied. request token has not been authorized.");
}
@@ -134,19 +176,20 @@
// 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));
+ sendResponse(servletResponse, OAuth.newList(
+ OAuth.OAUTH_TOKEN, accessEntry.token,
+ OAuth.OAUTH_TOKEN_SECRET, accessEntry.tokenSecret,
+ "user_id", entry.userId));
}
- private OAuthEntry getRequestToken(HttpServletRequest servletRequest,
- HttpServletResponse servletResponse, OAuthMessage requestMessage)
- throws IOException, ServletException {
- System.out.println("Getting a request Token for message: " + requestMessage);
+ private OAuthEntry getToken(HttpServletRequest servletRequest,
+ HttpServletResponse servletResponse, OAuthMessage requestMessage, boolean validate)
+ throws IOException, ServletException {
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.");
+ throw new ServletException("permission denied. token is invalid.");
}
// find consumer key, compare with supplied value, if present.
@@ -155,19 +198,23 @@
throw new ServletException("permission denied. consumer keys don't match.");
}
- String consumerSecret = dataStore.getConsumerSecret(consumerKey);
+ OAuthConsumer consumer = dataStore.getConsumer(consumerKey);
+
+ OAuthAccessor accessor = new OAuthAccessor(consumer);
- 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);
+ if (validate) {
+ try {
+ VALIDATOR.validateMessage(requestMessage, accessor);
+ } catch (OAuthException e) {
+ handleException(e, servletRequest, servletResponse, true);
+ return null;
+ } catch (URISyntaxException e) {
+ handleException(e, servletRequest, servletResponse, true);
+ return null;
+ }
}
return entry;
}