You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@shindig.apache.org by wo...@apache.org on 2011/10/12 22:00:12 UTC

svn commit: r1182560 [1/3] - in /shindig/trunk: content/sampledata/ java/common/conf/ java/server/src/main/webapp/WEB-INF/ java/social-api/src/main/java/org/apache/shindig/social/core/oauth/ java/social-api/src/main/java/org/apache/shindig/social/core/...

Author: woodser
Date: Wed Oct 12 20:00:11 2011
New Revision: 1182560

URL: http://svn.apache.org/viewvc?rev=1182560&view=rev
Log:
OAuth 2.0 service provider in Apache Shindig.

Added:
    shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth2/
    shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth2/OAuth2AuthenticationHandler.java
    shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth2/OAuth2AuthorizationHandler.java
    shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth2/OAuth2Client.java
    shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth2/OAuth2Code.java
    shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth2/OAuth2DataService.java
    shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth2/OAuth2DataServiceImpl.java
    shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth2/OAuth2Exception.java
    shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth2/OAuth2NormalizedRequest.java
    shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth2/OAuth2NormalizedResponse.java
    shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth2/OAuth2Service.java
    shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth2/OAuth2ServiceImpl.java
    shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth2/OAuth2Servlet.java
    shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth2/OAuth2TokenHandler.java
    shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth2/OAuth2Types.java
    shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth2/OAuth2Utils.java
    shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth2/validators/
    shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth2/validators/AccessTokenRequestValidator.java
    shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth2/validators/AuthCodeGrantValidator.java
    shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth2/validators/AuthorizationCodeRequestValidator.java
    shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth2/validators/ClientCredentialsGrantValidator.java
    shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth2/validators/DefaultResourceRequestValidator.java
    shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth2/validators/OAuth2GrantValidator.java
    shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth2/validators/OAuth2ProtectedResourceValidator.java
    shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth2/validators/OAuth2RequestValidator.java
    shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/core/oauth/MockServletOutputStream.java
    shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/core/oauth/OAuth2AuthCodeFlowTest.java
    shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/core/oauth/OAuth2AuthenticationHandlerTest.java
    shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/core/oauth/OAuth2ClientCredentialFlowTest.java
    shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/core/oauth/OAuth2ImplicitFlowTest.java
Modified:
    shindig/trunk/content/sampledata/canonicaldb.json
    shindig/trunk/java/common/conf/shindig.properties
    shindig/trunk/java/server/src/main/webapp/WEB-INF/web.xml
    shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth/AuthenticationHandlerProvider.java
    shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/sample/SampleModule.java
    shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/SocialApiTestsGuiceModule.java
    shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/core/config/SocialApiGuiceModuleTest.java
    shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/core/oauth/AuthenticationProviderHandlerTest.java
    shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/core/oauth/OAuthAuthenticationHanderTest.java
    shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/dataservice/integration/AbstractLargeRestfulTests.java

Modified: shindig/trunk/content/sampledata/canonicaldb.json
URL: http://svn.apache.org/viewvc/shindig/trunk/content/sampledata/canonicaldb.json?rev=1182560&r1=1182559&r2=1182560&view=diff
==============================================================================
--- shindig/trunk/content/sampledata/canonicaldb.json (original)
+++ shindig/trunk/content/sampledata/canonicaldb.json Wed Oct 12 20:00:11 2011
@@ -633,6 +633,112 @@
           "consumerSecret" : "secret",
           "icon" : "http://localhost:8080%contextroot%/samplecontainer/examples/icon.png"}
  },
+ 
+ // Registry of OAuth 2.0 clients with Shindig's service provider.
+ "oauth2" : {
+  "advancedAuthorizationCodeClient" : {
+    "registration" : {
+      "id" : "advancedAuthorizationCodeClient",
+      "secret": "advancedAuthorizationCodeClient_secret",
+      "title": "Most Advanced Authorization Code Client Ever!",
+      "redirectURI" : "http://localhost:8080/oauthclients/AuthorizationCodeClient/friends",
+      "type" : "confidential",
+      "flow" : "authorization_code"
+    },
+    "authorizationCodes" : {
+      "advancedClient_authcode_1" : {
+        // Authentication code has been consumed since associatedSignature exists
+        "redirectURI" : "http://localhost:8080/oauthclients/AuthorizationCodeClient/friends",
+        //Setting expiration to -1 makes code permanent
+        "expiration" : -1
+      },
+      "advancedClient_authcode_2" : {
+        "redirectURI" : "http://localhost:8080/oauthclients/AuthorizationCodeClient/friends",
+        "expiration" : -1
+      }
+    },
+    "accessTokens" : {
+      "advancedClient_accesstoken_1" : {
+        "redirectURI" : "http://localhost:8080/oauthclients/AuthorizationCodeClient/friends",
+        "expiration" : -1
+      }
+    }
+  },
+  "advancedImplicitClient" : {
+    "registration" : {
+      "id" : "advancedImplicitClient",
+      "title" : "Most Advanced Implicit Client Ever!",
+      "type" : "public",
+      "redirectURI" : "http://localhost:8080/oauthclients/ImplicitClientHelper.html",
+      "flow" : "implicit"
+    }
+  },
+  "testClient" : {
+    "registration" : {
+      "id" : "testClient",
+      "redirectURI" : "http://localhost:8080/oauthclients/AuthorizationCodeClient",
+      "type" : "public",
+      "flow" : "authorization_code"
+    },
+    "authorizationCodes" : {
+      "testClient_authcode_1" : {
+        "redirectURI" : "http://localhost:8080/oauthclients/AuthorizationCodeClient",
+        "expiration" : -1
+      },
+      "testClient_authcode_2" : {
+        "redirectURI" : "http://localhost:8080/oauthclients/AuthorizationCodeClient",
+        "expiration" : -1
+      }
+    },
+    "accessTokens" : {
+      "testClient_accesstoken_1" : {
+        "redirectURI" : "http://localhost:8080/oauthclients/AuthorizationCodeClient",
+        "expiration" : -1
+      }
+    }
+  },
+  "testClientCredentialsClient" : {
+    "registration" : {
+      "id" : "testClientCredentialsClient",
+      "secret": "clientCredentialsClient_secret",
+      "type" : "confidential",
+      "flow" : "client_credentials"
+    },
+      "accessTokens" : {
+        "testClientCredentialsClient_accesstoken_1" : {
+        "expiration" : -1
+      }
+    }
+  },
+  "shindigClient" : {
+    "registration" : {
+      "id" : "shindigClient",
+      "secret": "U78KJM98372AMGL87612993M",
+      "title": "shindig client registered for authorization",
+      "redirectURI" : "http://localhost:8080%contextRoot%/gadgets/oauth2callback",
+      "type" : "confidential",
+      "flow" : "authorization_code"
+  },
+    "authorizationCodes" : {
+      "shindigClient_authcode_1" : {
+        // Authentication code has been consumed since associatedSignature exists
+        "redirectURI" : "http://localhost:8080%contextRoot%/gadgets/oauth2callback",
+        //Setting expiration to -1 makes code permanent
+        "expiration" : -1
+      },
+      "shindigClient_authcode_2" : {
+        "redirectURI" : "http://localhost:8080%contextRoot%/gadgets/oauth2callback",
+        "expiration" : -1
+      }
+    }, 
+    "accessTokens" : {
+      "shindigClient_accesstoken_1" : {
+        "redirectURI" : "http://localhost:8080%contextRoot%/gadgets/oauth2callback",
+        "expiration" : -1
+      }
+    }
+  }
+},
 
  // duplicates userApplications as above..
  "permissions": {

Modified: shindig/trunk/java/common/conf/shindig.properties
URL: http://svn.apache.org/viewvc/shindig/trunk/java/common/conf/shindig.properties?rev=1182560&r1=1182559&r2=1182560&view=diff
==============================================================================
--- shindig/trunk/java/common/conf/shindig.properties (original)
+++ shindig/trunk/java/common/conf/shindig.properties Wed Oct 12 20:00:11 2011
@@ -174,4 +174,12 @@ org.apache.shindig.gadgets.servlet.longL
 
 # Closure compiler optimization level.  One of advanced|simple|whitespace_only|none.
 # Defaults to simple.
-shindig.closure.compile.level=simple
\ No newline at end of file
+shindig.closure.compile.level=simple
+
+# OAuth 2.0 authorization code, access token, and refresh token expiration times.
+# 5 * 60 * 1000 = 300000 = 5 minutes
+# 5 * 60 * 60 * 1000 = 18000000 = 5 hours
+# 5 * 60 * 60 * 1000 * 24 = 432000000 = 5 days
+shindig.oauth2.authCodeExpiration=300000
+shindig.oauth2.accessTokenExpiration=18000000
+shindig.oauth2.refreshTokenExpiration=432000000
\ No newline at end of file

Modified: shindig/trunk/java/server/src/main/webapp/WEB-INF/web.xml
URL: http://svn.apache.org/viewvc/shindig/trunk/java/server/src/main/webapp/WEB-INF/web.xml?rev=1182560&r1=1182559&r2=1182560&view=diff
==============================================================================
--- shindig/trunk/java/server/src/main/webapp/WEB-INF/web.xml (original)
+++ shindig/trunk/java/server/src/main/webapp/WEB-INF/web.xml Wed Oct 12 20:00:11 2011
@@ -103,6 +103,7 @@
                 /login.jsp = authc
 
                 /oauth/authorize/** = authc
+                /oauth2/authorize/** = authc
 
             </param-value>
         </init-param>
@@ -123,6 +124,11 @@
       <filter-name>ShiroFilter</filter-name>
       <url-pattern>/oauth/authorize</url-pattern>
   </filter-mapping>
+  
+  <filter-mapping>
+      <filter-name>ShiroFilter</filter-name>
+      <url-pattern>/oauth2/authorize</url-pattern>
+  </filter-mapping>
 
   <filter-mapping>
       <filter-name>ShiroFilter</filter-name>
@@ -240,6 +246,14 @@
       org.apache.shindig.social.sample.oauth.SampleOAuthServlet
     </servlet-class>
   </servlet>
+  
+  <!-- Serve OAuth 2 APIs -->
+  <servlet>
+    <servlet-name>OAuth2Servlet</servlet-name>
+    <servlet-class>
+      org.apache.shindig.social.core.oauth2.OAuth2Servlet
+    </servlet-class>
+  </servlet>
 
   <servlet>
     <servlet-name>rpcSwf</servlet-name>
@@ -306,6 +320,11 @@
     <servlet-name>sampleOAuth</servlet-name>
     <url-pattern>/oauth/*</url-pattern>
   </servlet-mapping>
+  
+  <servlet-mapping>
+    <servlet-name>OAuth2Servlet</servlet-name>
+    <url-pattern>/oauth2/*</url-pattern>
+  </servlet-mapping>
 
   <servlet-mapping>
     <servlet-name>rpcSwf</servlet-name>

Modified: shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth/AuthenticationHandlerProvider.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth/AuthenticationHandlerProvider.java?rev=1182560&r1=1182559&r2=1182560&view=diff
==============================================================================
--- shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth/AuthenticationHandlerProvider.java (original)
+++ shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth/AuthenticationHandlerProvider.java Wed Oct 12 20:00:11 2011
@@ -24,6 +24,7 @@ import com.google.inject.Provider;
 import org.apache.shindig.auth.AnonymousAuthenticationHandler;
 import org.apache.shindig.auth.AuthenticationHandler;
 import org.apache.shindig.auth.UrlParameterAuthenticationHandler;
+import org.apache.shindig.social.core.oauth2.OAuth2AuthenticationHandler;
 
 import java.util.List;
 
@@ -34,10 +35,10 @@ public class AuthenticationHandlerProvid
   protected List<AuthenticationHandler> handlers;
 
   @Inject
-  public AuthenticationHandlerProvider(UrlParameterAuthenticationHandler urlParam,
-      OAuthAuthenticationHandler threeLeggedOAuth,
+  public AuthenticationHandlerProvider(OAuth2AuthenticationHandler oauth2Handler, UrlParameterAuthenticationHandler urlParam,
+      OAuthAuthenticationHandler threeLeggedOAuth, 
       AnonymousAuthenticationHandler anonymous) {
-    handlers = Lists.newArrayList(urlParam, threeLeggedOAuth, anonymous);
+    handlers = Lists.newArrayList(urlParam, threeLeggedOAuth, oauth2Handler, anonymous);
   }
 
   public List<AuthenticationHandler> get() {

Added: shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth2/OAuth2AuthenticationHandler.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth2/OAuth2AuthenticationHandler.java?rev=1182560&view=auto
==============================================================================
--- shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth2/OAuth2AuthenticationHandler.java (added)
+++ shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth2/OAuth2AuthenticationHandler.java Wed Oct 12 20:00:11 2011
@@ -0,0 +1,83 @@
+/*
+ * 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.oauth2;
+
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.shindig.auth.AnonymousSecurityToken;
+import org.apache.shindig.auth.AuthenticationHandler;
+import org.apache.shindig.auth.SecurityToken;
+import org.apache.shindig.common.logging.i18n.MessageKeys;
+
+import com.google.inject.Inject;
+
+/**
+ * Authentication handler for OAuth 2.0.  Authenticates requests for resources
+ * using one of the OAuth 2.0 flows.
+ */
+public class OAuth2AuthenticationHandler implements AuthenticationHandler {
+  
+  //class name for logging purpose
+  private static final String classname = OAuth2AuthenticationHandler.class.getName();
+  private static final Logger LOG = Logger.getLogger(classname,MessageKeys.MESSAGES);
+
+  private OAuth2Service store;
+
+  public String getName() {
+    return "OAuth2";
+  }
+
+  @Inject
+  public OAuth2AuthenticationHandler(OAuth2Service store) {
+    this.store = store;
+  }
+
+  /**
+   * Only denies authentication when an invalid bearer token is received.
+   * Unauthenticated requests can pass through to other AuthenticationHandlers.
+   */
+  public SecurityToken getSecurityTokenFromRequest(HttpServletRequest request)
+      throws InvalidAuthenticationException {
+
+    OAuth2NormalizedRequest normalizedReq;
+    try {
+      normalizedReq = new OAuth2NormalizedRequest(request);
+    } catch (OAuth2Exception oae) {   // request failed to normalize
+      LOG.logp(Level.WARNING, classname, "getSecurityTokenFromRequest", MessageKeys.INVALID_OAUTH);
+      return null;
+    }
+    try {
+      if (normalizedReq.getAccessToken() != null) {
+        store.validateRequestForResource(normalizedReq, null);
+        return new AnonymousSecurityToken(); // Return your valid security token
+      }
+    } catch (OAuth2Exception oae) {
+      // TODO (Eric): process OAuth2Exception properly
+      throw new InvalidAuthenticationException("Something went wrong: ", oae);
+    }
+    return null;
+  }
+
+  public String getWWWAuthenticateHeader(String realm) {
+    return String.format("Bearer realm=\"%s\"", realm);
+  }
+
+}
\ No newline at end of file

Added: shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth2/OAuth2AuthorizationHandler.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth2/OAuth2AuthorizationHandler.java?rev=1182560&view=auto
==============================================================================
--- shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth2/OAuth2AuthorizationHandler.java (added)
+++ shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth2/OAuth2AuthorizationHandler.java Wed Oct 12 20:00:11 2011
@@ -0,0 +1,115 @@
+/*
+ * 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.oauth2;
+
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.shindig.social.core.oauth2.OAuth2Types.ErrorType;
+import org.apache.shindig.social.core.oauth2.OAuth2Types.TokenFormat;
+
+/**
+ * Handles requests to the OAuth 2.0 authorization end-point.
+ */
+public class OAuth2AuthorizationHandler {
+
+  private OAuth2Service service;
+
+  public OAuth2AuthorizationHandler(OAuth2Service service) {
+    this.service = service;
+  }
+
+  /**
+   * Handles an OAuth 2.0 authorization request.
+   * 
+   * @param request is the original request
+   * @param response is the response of the request
+   * @return OAuth2NormalizedResponse represents the OAuth 2.0 response
+   * 
+   * @throws ServletException
+   * @throws IOException
+   */
+  public OAuth2NormalizedResponse handle(HttpServletRequest request,
+      HttpServletResponse response) throws ServletException, IOException {
+    try {
+      // normalize the request
+      OAuth2NormalizedRequest normalizedReq = new OAuth2NormalizedRequest(
+          request);
+
+      // process request according to flow
+      OAuth2NormalizedResponse normalizedResp = new OAuth2NormalizedResponse();
+      if (normalizedReq.getResponseType() != null) {
+        switch (normalizedReq.getEnumeratedResponseType()) {
+        case CODE:
+          // authorization code flow
+          service.validateRequestForAuthCode(normalizedReq);
+          OAuth2Code authCode = service.grantAuthorizationCode(normalizedReq);
+
+          // send response
+          normalizedResp.setCode(authCode.getValue());
+          if (normalizedReq.getState() != null)
+            normalizedResp.setState(normalizedReq.getState());
+          normalizedResp.setHeader(
+              "Location",
+              OAuth2Utils.buildUrl(authCode.getRedirectURI(),
+                  normalizedResp.getResponseParameters(), null));
+          normalizedResp.setStatus(HttpServletResponse.SC_FOUND);
+          normalizedResp.setBodyReturned(false);
+          return normalizedResp;
+        case TOKEN:
+          // implicit flow
+          service.validateRequestForAccessToken(normalizedReq);
+          OAuth2Code accessToken = service.grantAccessToken(normalizedReq);
+
+          // send response
+          normalizedResp.setAccessToken(accessToken.getValue());
+          normalizedResp.setTokenType(TokenFormat.BEARER.toString());
+          normalizedResp.setExpiresIn((accessToken.getExpiration() - System
+              .currentTimeMillis()) + "");
+          if (normalizedReq.getState() != null)
+            normalizedResp.setState(normalizedReq.getState());
+          normalizedResp.setHeader("Location", OAuth2Utils.buildUrl(
+              accessToken.getRedirectURI(), null,
+              normalizedResp.getResponseParameters()));
+          normalizedResp.setStatus(HttpServletResponse.SC_FOUND);
+          normalizedResp.setBodyReturned(false);
+          return normalizedResp;
+        default:
+          OAuth2NormalizedResponse resp = new OAuth2NormalizedResponse();
+          resp.setError(ErrorType.UNSUPPORTED_RESPONSE_TYPE.toString());
+          resp.setErrorDescription("Unsupported response type");
+          resp.setStatus(HttpServletResponse.SC_FOUND);
+          resp.setBodyReturned(false);
+          throw new OAuth2Exception(resp);
+        }
+      } else {
+        OAuth2NormalizedResponse resp = new OAuth2NormalizedResponse();
+        resp.setError(ErrorType.UNSUPPORTED_RESPONSE_TYPE.toString());
+        resp.setErrorDescription("Unsupported response type");
+        resp.setStatus(HttpServletResponse.SC_FOUND);
+        resp.setBodyReturned(false);
+        throw new OAuth2Exception(resp);
+      }
+    } catch (OAuth2Exception oae) {
+      return oae.getNormalizedResponse();
+    }
+  }
+}
\ No newline at end of file

Added: shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth2/OAuth2Client.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth2/OAuth2Client.java?rev=1182560&view=auto
==============================================================================
--- shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth2/OAuth2Client.java (added)
+++ shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth2/OAuth2Client.java Wed Oct 12 20:00:11 2011
@@ -0,0 +1,213 @@
+/*
+ * 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.oauth2;
+
+/**
+ * Represents an OAuth 2.0 client.
+ */
+public class OAuth2Client {
+
+  protected String id;
+  protected String secret;
+  protected String redirectURI;
+  protected String title;
+  protected String iconUrl;
+  protected ClientType type;
+  private Flow flow;
+
+  /**
+   * Gets the client's ID.
+   * 
+   * @return String represents the client's ID.
+   */
+  public String getId() {
+    return id;
+  }
+
+  /**
+   * Sets the client's ID.
+   * 
+   * @param id
+   *          represents the client's ID.
+   */
+  public void setId(String id) {
+    this.id = id;
+  }
+
+  /**
+   * Gets the client's secret.
+   * 
+   * @return String represents the client's secret
+   */
+  public String getSecret() {
+    return secret;
+  }
+
+  /**
+   * Sets the client's secret.
+   * 
+   * @param secret
+   *          represents the client's secret
+   */
+  public void setSecret(String secret) {
+    this.secret = secret;
+  }
+
+  /**
+   * Gets the client's redirect URI.
+   * 
+   * @return String represents the client's redirect URI
+   */
+  public String getRedirectURI() {
+    return redirectURI;
+  }
+
+  /**
+   * Sets the client's redirect URI.
+   * 
+   * @param redirectUri
+   *          represents the client's redirect URI
+   */
+  public void setRedirectURI(String redirectUri) {
+    this.redirectURI = redirectUri;
+  }
+
+  /**
+   * Gets the client's title.
+   * 
+   * @return String represents the client's title
+   */
+  public String getTitle() {
+    return title;
+  }
+
+  /**
+   * Sets the client's title.
+   * 
+   * @param title
+   *          represents the client's title
+   */
+  public void setTitle(String title) {
+    this.title = title;
+  }
+
+  /**
+   * Gets the client's icon URL.
+   * 
+   * @return String represents the client's icon URL
+   */
+  public String getIconUrl() {
+    return iconUrl;
+  }
+
+  /**
+   * Sets the client's icon URL.
+   * 
+   * @param iconUrl
+   *          represents the client's icon URL
+   */
+  public void setIconUrl(String iconUrl) {
+    this.iconUrl = iconUrl;
+  }
+
+  /**
+   * Gets the client's type.
+   * 
+   * @return ClientType represents the client's type
+   */
+  public ClientType getType() {
+    return type;
+  }
+
+  /**
+   * Sets the client's type.
+   * 
+   * @param clientType
+   *          represents the client's type
+   */
+  public void setType(ClientType type) {
+    this.type = type;
+  }
+
+  /**
+   * Sets the client's OAuth2 flow (via a String flow identifier)
+   * 
+   * @param flow
+   */
+  public void setFlow(String flow) {
+    if (Flow.CLIENT_CREDENTIALS.toString().equals(flow)) {
+      this.flow = Flow.CLIENT_CREDENTIALS;
+    } else if (Flow.AUTHORIZATION_CODE.toString().equals(flow)) {
+      this.flow = Flow.AUTHORIZATION_CODE;
+    } else if (Flow.IMPLICIT.toString().equals(flow)) {
+      this.flow = Flow.IMPLICIT;
+    } else {
+      this.flow = null;
+    }
+  }
+
+  /**
+   * Sets the client's OAuth2 flow
+   * 
+   * @param flow
+   */
+  public void setFlowEnum(Flow flow) {
+    this.flow = flow;
+  }
+
+  /**
+   * Gets the client's OAuth2 flow
+   * 
+   * @return
+   */
+  public Flow getFlow() {
+    return flow;
+  }
+
+  /**
+   * Enumerated client types in the OAuth 2.0 specification.
+   */
+  public static enum ClientType {
+    PUBLIC("public"), CONFIDENTIAL("confidential");
+
+    private final String name;
+
+    private ClientType(String name) {
+      this.name = name;
+    }
+
+    public String toString() {
+      return name;
+    }
+  }
+
+  public static enum Flow {
+    CLIENT_CREDENTIALS("client_credentials"), AUTHORIZATION_CODE(
+        "authorization_code"), IMPLICIT("implicit");
+
+    private final String name;
+
+    private Flow(String name) {
+      this.name = name;
+    }
+
+    public String toString() {
+      return name;
+    }
+  }
+}
\ No newline at end of file

Added: shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth2/OAuth2Code.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth2/OAuth2Code.java?rev=1182560&view=auto
==============================================================================
--- shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth2/OAuth2Code.java (added)
+++ shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth2/OAuth2Code.java Wed Oct 12 20:00:11 2011
@@ -0,0 +1,243 @@
+/*
+ * 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.oauth2;
+
+import java.util.List;
+
+import org.apache.shindig.social.core.oauth2.OAuth2Types.CodeType;
+
+/**
+ * Represents a "code" string in an OAuth 2.0 handshake, including authorization
+ * code, access token, or refresh token. These signatures may all expire. They
+ * may also be associated with a redirect_url and/or another code.
+ */
+public class OAuth2Code implements Comparable<OAuth2Code> {
+
+  private String value;
+  private String redirectURI;
+  private long expiration;
+  private List<String> scope; // TODO (Eric): should be a string, interpret as a list during validation
+  private OAuth2Client client;
+  private OAuth2Code relatedAuthCode;
+  private OAuth2Code relatedRefreshToken;
+  private OAuth2Code relatedAccessToken;
+  private CodeType type;
+
+  public OAuth2Code() {
+
+  }
+
+  /**
+   * Constructs an OAuth2Code.
+   * 
+   * @param value is the String key that makes up the code
+   * @param redirectURI is redirect URI associated with this code
+   * @param expiration indicates when this code expires
+   * @param scope indicates the scope of this code
+   */
+  public OAuth2Code(String value, String redirectURI, long expiration,
+      List<String> scope) {
+    this.value = value;
+    this.redirectURI = redirectURI;
+    this.expiration = expiration;
+    this.scope = scope;
+  }
+
+  /**
+   * Constructs an OAuth2Code with a value.
+   * 
+   * @param value is the String key that makes up the code
+   */
+  public OAuth2Code(String value) {
+    this.value = value;
+  }
+
+  /**
+   * Returns the value of this code.
+   * 
+   * @return String is the key of this code
+   */
+  public String getValue() {
+    return value;
+  }
+
+  /**
+   * Sets the value of this code.
+   * 
+   * @param value is the value to set this code to
+   */
+  public void setValue(String value) {
+    this.value = value;
+  }
+
+  /**
+   * Returns the redirect URI associated with this code.
+   * 
+   * @return String represents this code's redirect URI
+   */
+  public String getRedirectURI() {
+    return redirectURI;
+  }
+
+  /**
+   * Sets the redirect URI associated with this code.
+   * 
+   * @param redirectURI represents the redirect URI of this code
+   */
+  public void setRedirectURI(String redirectURI) {
+    this.redirectURI = redirectURI;
+  }
+
+  /**
+   * Returns when this code expires.
+   * 
+   * @return long represents when this code will expire
+   */
+  public long getExpiration() {
+    return expiration;
+  }
+
+  /**
+   * Sets the expiration of this code.
+   * 
+   * @param expiration is when this code will expire
+   */
+  public void setExpiration(long expiration) {
+    this.expiration = expiration;
+  }
+
+  /**
+   * Compares this code to another code.
+   * 
+   * @return int indicates how the value of this code compares to another
+   */
+  public int compareTo(OAuth2Code target) {
+    return value.compareTo(target.getValue());
+  }
+
+  /**
+   * Returns the scope of this code.
+   * 
+   * @return List<String> represents the scope of this code
+   */
+  public List<String> getScope() {
+    return scope;
+  }
+
+  /**
+   * Sets the scope of this code.
+   * 
+   * @param scope is this code's authorized scope
+   */
+  public void setScope(List<String> scope) {
+    this.scope = scope;
+  }
+
+  /**
+   * Sets the client associated with this code.
+   * 
+   * @param client is the client to associate with this code
+   */
+  public void setClient(OAuth2Client client) {
+    this.client = client;
+  }
+
+  /**
+   * Returns the client associated with this code.
+   * 
+   * @return OAuth2Client represents the client associated with this code
+   */
+  public OAuth2Client getClient() {
+    return client;
+  }
+
+  /**
+   * Sets the type of this code; one of
+   *  AUTHORIZATION_CODE,
+   *  ACCESS_TOKEN,
+   *  REFRESH_TOKEN
+   *  
+   * @param type is this code's type
+   */
+  public void setType(CodeType type) {
+    this.type = type;
+  }
+
+  /**
+   * Returns the type of this code.
+   * 
+   * @return CodeType represents the type of this code
+   */
+  public CodeType getType() {
+    return type;
+  }
+
+  /**
+   * Sets the authorization code that this code is related to, if applicable.
+   * 
+   * @param code is the authorization code to associate with this code
+   */
+  public void setRelatedAuthCode(OAuth2Code code) {
+    this.relatedAuthCode = code;
+  }
+
+  /**
+   * Returns the authorization code related to this code.
+   * 
+   * @return OAuth2Code is the authorization code related to this code
+   */
+  public OAuth2Code getRelatedAuthCode() {
+    return relatedAuthCode;
+  }
+
+  /**
+   * Sets the related refresh token.
+   * 
+   * @param relatedRefreshToken is the refresh token related to this code
+   */
+  public void setRelatedRefreshToken(OAuth2Code relatedRefreshToken) {
+    this.relatedRefreshToken = relatedRefreshToken;
+  }
+
+  /**
+   * Gets the related refresh token.
+   * 
+   * @return OAuth2Code is the refresh token related to this code
+   */
+  public OAuth2Code getRelatedRefreshToken() {
+    return relatedRefreshToken;
+  }
+
+  /**
+   * Sets the related access token.
+   * 
+   * @param relatedAccessToken is the access token related to this code
+   */
+  public void setRelatedAccessToken(OAuth2Code relatedAccessToken) {
+    this.relatedAccessToken = relatedAccessToken;
+  }
+
+  /**
+   * Gets the related access token.
+   * 
+   * @return OAuth2Code is the access token related to this code
+   */
+  public OAuth2Code getRelatedAccessToken() {
+    return relatedAccessToken;
+  }
+}
\ No newline at end of file

Added: shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth2/OAuth2DataService.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth2/OAuth2DataService.java?rev=1182560&view=auto
==============================================================================
--- shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth2/OAuth2DataService.java (added)
+++ shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth2/OAuth2DataService.java Wed Oct 12 20:00:11 2011
@@ -0,0 +1,113 @@
+/*
+ * 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.oauth2;
+
+/**
+ * Services to support the management of data for the OAuth 2.0 specification.
+ * Includes management of clients, authorization codes, access tokens, and
+ * refresh tokens.
+ * 
+ * TODO (Eric): client registration services
+ */
+public interface OAuth2DataService {
+
+  /**
+   * Retrieves a pre-registered client by ID.
+   * 
+   * @param clientId identifies the client to retrieve
+   * 
+   * @param OAuth2Client is the retrieved client
+   */
+  public OAuth2Client getClient(String clientId);
+
+  /**
+   * Retrieves an authorization code by its value.
+   * 
+   * @param clientId identifies the client who owns the authorization code
+   * @param authCode is the value of the authorization code to get
+   * 
+   * @return OAuth2Code is the retrieved authorization code
+   */
+  public OAuth2Code getAuthorizationCode(String clientId, String authCode);
+
+  /**
+   * Registers an authorization code with a client.
+   * 
+   * @param clientId identifies the client who owns the authorization code
+   * @param authCode is the authorization code to register with the client
+   */
+  public void registerAuthorizationCode(String clientId, OAuth2Code authCode);
+
+  /**
+   * Unregisters an authorization code with a client.
+   * 
+   * @param clientId identifies the client who owns the authorization code
+   * @param authCode is the value of the authorization code to unregister
+   */
+  public void unregisterAuthorizationCode(String clientId, String authCode);
+
+  /**
+   * Retrieves an access token by its value.
+   * 
+   * @param accessToken is the value of the accessToken to retrieve
+   * 
+   * @return OAuth2Code is the retrieved access token; null if not found
+   */
+  public OAuth2Code getAccessToken(String accessToken);
+
+  /**
+   * Registers an access token with a client.
+   * 
+   * @param clientId identifies the client to register the access token with
+   * @param accessToken is the access token to register with the client
+   */
+  public void registerAccessToken(String clientId, OAuth2Code accessToken);
+
+  /**
+   * Unregisters an access token with a client.
+   * 
+   * @param clientId identifies the client who owns the access token
+   * @param accessToken is the value of the access token to unregister
+   */
+  public void unregisterAccessToken(String clientId, String accessToken);
+
+  /**
+   * Retrieves a refresh token by its value.
+   * 
+   * @param refreshToken is the value of the refresh token to retrieve
+   * 
+   * @return OAuth2Code is the retrieved refresh token; null if not found
+   */
+  public OAuth2Code getRefreshToken(String refreshToken);
+
+  /**
+   * Registers a refresh token with a client.
+   * 
+   * @param clientId identifies the client who owns the refresh token
+   * @param refreshToken is the refresh token to register with the client
+   */
+  public void registerRefreshToken(String clientId, OAuth2Code refreshToken);
+
+  /**
+   * Unregisters a refresh token with a client.
+   * 
+   * @param clientId identifies the client who owns the refresh token
+   * @param refreshToken is the value of the refresh token to unregister
+   */
+  public void unregisterRefreshToken(String clientId, String refreshToken);
+}
\ No newline at end of file

Added: shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth2/OAuth2DataServiceImpl.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth2/OAuth2DataServiceImpl.java?rev=1182560&view=auto
==============================================================================
--- shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth2/OAuth2DataServiceImpl.java (added)
+++ shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth2/OAuth2DataServiceImpl.java Wed Oct 12 20:00:11 2011
@@ -0,0 +1,187 @@
+/*
+ * 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.oauth2;
+
+import java.util.List;
+import java.util.Map;
+
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.shindig.common.util.ResourceLoader;
+import org.apache.shindig.protocol.ProtocolException;
+import org.apache.shindig.protocol.conversion.BeanConverter;
+import org.apache.shindig.social.core.oauth2.OAuth2Client.ClientType;
+import org.apache.shindig.social.core.oauth2.OAuth2Types.CodeType;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.inject.Inject;
+import com.google.inject.name.Named;
+
+public class OAuth2DataServiceImpl implements OAuth2DataService {
+
+  private JSONObject oauthDB; // the OAuth 2.0 JSON DB
+  private BeanConverter converter; // the JSON<->Bean converter
+  private List<OAuth2Client> clients; // list of clients
+  private Map<String, List<OAuth2Code>> authCodes; // authorization codes per client
+  private Map<String, List<OAuth2Code>> accessTokens; // access tokens per client
+
+  @Inject
+  public OAuth2DataServiceImpl(
+      @Named("shindig.canonical.json.db") String jsonLocation,
+      @Named("shindig.bean.converter.json") BeanConverter converter,
+      @Named("shindig.contextroot") String contextroot) throws Exception {
+    String content = IOUtils.toString(ResourceLoader.openResource(jsonLocation), "UTF-8");
+    content = content.replace("%contextRoot%", contextroot);
+    this.oauthDB = new JSONObject(content).getJSONObject("oauth2");
+    this.converter = converter;
+    this.clients = Lists.newArrayList();
+    this.authCodes = Maps.newHashMap();
+    this.accessTokens = Maps.newHashMap();
+    loadClientsFromCanonical();
+  }
+
+  public OAuth2Client getClient(String clientId) {
+    for (OAuth2Client client : clients) {
+      if (client.getId().equals(clientId)) {
+        return client;
+      }
+    }
+    return null;
+  }
+
+  public OAuth2Code getAuthorizationCode(String clientId, String authCode) {
+    if (authCodes.containsKey(clientId)) {
+      List<OAuth2Code> codes = authCodes.get(clientId);
+      for (OAuth2Code code : codes) {
+        if (code.getValue().equals(authCode)) {
+          return code;
+        }
+      }
+    }
+    return null;
+  }
+
+  public void registerAuthorizationCode(String clientId, OAuth2Code authCode) {
+    if (authCodes.containsKey(clientId)) {
+      ((List<OAuth2Code>) authCodes.get(clientId)).add(authCode);
+    } else {
+      List<OAuth2Code> list = Lists.newArrayList();
+      list.add(authCode);
+      authCodes.put(clientId, list);
+    }
+  }
+
+  public void unregisterAuthorizationCode(String clientId, String authCode) {
+    if (authCodes.containsKey(clientId)) {
+      List<OAuth2Code> codes = authCodes.get(clientId);
+      for (OAuth2Code code : codes) {
+        if (code.getValue().equals(authCode)) {
+          codes.remove(code);
+          return;
+        }
+      }
+    }
+    throw new RuntimeException("signature not found"); // TODO (Eric): handle error
+  }
+
+  public OAuth2Code getAccessToken(String accessToken) {
+    for (String clientId : accessTokens.keySet()) {
+      List<OAuth2Code> tokens = accessTokens.get(clientId);
+      for (OAuth2Code token : tokens) {
+        if (token.getValue().equals(accessToken)) {
+          return token;
+        }
+      }
+    }
+    return null;
+  }
+
+  public void registerAccessToken(String clientId, OAuth2Code accessToken) {
+    if (accessTokens.containsKey(clientId)) {
+      ((List<OAuth2Code>) accessTokens.get(clientId)).add(accessToken);
+    } else {
+      List<OAuth2Code> list = Lists.newArrayList();
+      list.add(accessToken);
+      accessTokens.put(clientId, list);
+    }
+  }
+
+  public void unregisterAccessToken(String clientId, String accessToken) {
+    if (accessTokens.containsKey(clientId)) {
+      List<OAuth2Code> tokens = accessTokens.get(clientId);
+      for (OAuth2Code token : tokens) {
+        if (token.getValue().equals(accessToken)) {
+          tokens.remove(token);
+          return;
+        }
+      }
+    }
+    throw new RuntimeException("access token not found"); // TODO (Eric): handle error
+  }
+
+  public OAuth2Code getRefreshToken(String refreshToken) {
+    throw new RuntimeException("not yet implemented");
+  }
+
+  public void registerRefreshToken(String clientId, OAuth2Code refreshToken) {
+    throw new RuntimeException("not yet implemented");
+  }
+
+  public void unregisterRefreshToken(String clientId, String refreshToken) {
+    throw new RuntimeException("not yet implemented");
+  }
+
+  private void loadClientsFromCanonical() {
+    for (String clientId : JSONObject.getNames(oauthDB)) {
+      JSONObject clientJson;
+      try {
+        clientJson = oauthDB.getJSONObject(clientId).getJSONObject("registration");
+        OAuth2Client client = converter.convertToObject(clientJson.toString(), OAuth2Client.class);
+        client.setType(clientJson.getString("type").equals("public") ? ClientType.PUBLIC : ClientType.CONFIDENTIAL);
+        clients.add(client);
+        JSONObject clientJS = oauthDB.getJSONObject(clientId);
+        if (clientJS.has("authorizationCodes")) {
+          JSONObject authCodes = clientJS.getJSONObject("authorizationCodes");
+          for (String authCodeId : JSONObject.getNames(authCodes)) {
+            OAuth2Code code = converter.convertToObject(authCodes
+                .getJSONObject(authCodeId).toString(), OAuth2Code.class);
+            code.setValue(authCodeId);
+            code.setClient(client);
+            registerAuthorizationCode(clientId, code);
+          }
+        }
+        if (clientJS.has("accessTokens")) {
+          JSONObject accessTokens = clientJS.getJSONObject("accessTokens");
+          for (String accessTokenId : JSONObject.getNames(accessTokens)) {
+            OAuth2Code code = converter.convertToObject(accessTokens.getJSONObject(accessTokenId).toString(), OAuth2Code.class);
+            code.setValue(accessTokenId);
+            code.setClient(client);
+            code.setType(CodeType.ACCESS_TOKEN);
+            registerAccessToken(clientId, code);
+          }
+        }
+      } catch (JSONException je) {
+        throw new ProtocolException(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, je.getMessage(), je);
+      }
+    }
+  }
+}
\ No newline at end of file

Added: shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth2/OAuth2Exception.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth2/OAuth2Exception.java?rev=1182560&view=auto
==============================================================================
--- shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth2/OAuth2Exception.java (added)
+++ shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth2/OAuth2Exception.java Wed Oct 12 20:00:11 2011
@@ -0,0 +1,47 @@
+/*
+ * 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.oauth2;
+
+/**
+ * Represents an exception while dancing with OAuth 2.0.
+ */
+public class OAuth2Exception extends Exception {
+
+  private static final long serialVersionUID = -5892464438773813010L;
+  private OAuth2NormalizedResponse response;
+
+  /**
+   * Constructs an OAuth2Exception.
+   * 
+   * @param response is the normalized response that should be used to
+   * formulate a server response.
+   */
+  public OAuth2Exception(OAuth2NormalizedResponse response) {
+    super(response.getErrorDescription());
+    this.response = response;
+  }
+
+  /**
+   * Retrieves the normalized response.
+   * 
+   * @return OAuth2NormalizedResponse encapsulates the OAuth error
+   */
+  public OAuth2NormalizedResponse getNormalizedResponse() {
+    return response;
+  }
+}
\ No newline at end of file

Added: shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth2/OAuth2NormalizedRequest.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth2/OAuth2NormalizedRequest.java?rev=1182560&view=auto
==============================================================================
--- shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth2/OAuth2NormalizedRequest.java (added)
+++ shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth2/OAuth2NormalizedRequest.java Wed Oct 12 20:00:11 2011
@@ -0,0 +1,273 @@
+/*
+ * 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.oauth2;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.UnsupportedEncodingException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.commons.codec.binary.Base64;
+import org.apache.http.NameValuePair;
+import org.apache.http.client.utils.URLEncodedUtils;
+import org.apache.shindig.common.logging.i18n.MessageKeys;
+import org.apache.shindig.social.core.oauth2.OAuth2Types.ErrorType;
+import org.apache.shindig.social.core.oauth2.OAuth2Types.GrantType;
+import org.apache.shindig.social.core.oauth2.OAuth2Types.ResponseType;
+
+/**
+ * Normalizes an OAuth 2.0 request by extracting OAuth 2.0 related fields.
+ * 
+ * TODO (Eric): implement scope handling.
+ */
+public class OAuth2NormalizedRequest extends HashMap<String, Object> {
+
+  private static final long serialVersionUID = -7849581704967135322L;
+  private HttpServletRequest httpReq = null;
+  private static final Pattern FORM_URL_REGEX = Pattern
+      .compile("application/(x-www-)?form-url(-)?encoded");
+  
+  //class name for logging purpose
+  private static final String classname = OAuth2NormalizedRequest.class.getName();
+  private static final Logger LOG = Logger.getLogger(classname,MessageKeys.MESSAGES);
+
+  @SuppressWarnings("unchecked")
+  public OAuth2NormalizedRequest(HttpServletRequest request) throws OAuth2Exception {
+    super();
+    setHttpServletRequest(request);
+    String contentType = request.getContentType();
+    if (contentType != null) {
+      Matcher match = FORM_URL_REGEX.matcher(contentType);
+      if (match.matches()) {
+        normalizeBody(getBodyAsString(request));
+      }
+    }
+    Enumeration<String> keys = request.getParameterNames();
+    while (keys.hasMoreElements()) {
+      String key = keys.nextElement();
+      put(key, request.getParameter(key));
+    }
+    normalizeClientSecret(request);
+    normalizeAccessToken(request);
+  }
+
+  // --------------------------- NORMALIZED GETTERS ---------------------------
+  public String getClientId() {
+    return getString("client_id");
+  }
+
+  public String getClientSecret() {
+    return getString("client_secret");
+  }
+
+  public String getResponseType() {
+    return getString("response_type");
+  }
+
+  public String getGrantType() {
+    return getString("grant_type");
+  }
+
+  public String getRedirectURI() {
+    return getString("redirect_uri");
+  }
+
+  public String getAccessToken() {
+    return getString("access_token");
+  }
+
+  public String getAuthorizationCode() {
+    return getString("code");
+  }
+
+  public String getState() {
+    return getString("state");
+  }
+
+  public String getScope() {
+    return getString("scope");
+  }
+
+  public ResponseType getEnumeratedResponseType() throws OAuth2Exception {
+    String respType = getResponseType();
+    if (respType == null)
+      return null;
+    if (respType.equals("code")) {
+      return ResponseType.CODE;
+    } else if (respType.equals("token")) {
+      return ResponseType.TOKEN;
+    } else {
+      OAuth2NormalizedResponse resp = new OAuth2NormalizedResponse();
+      resp.setError(ErrorType.UNSUPPORTED_RESPONSE_TYPE.toString());
+      resp.setErrorDescription("Unsupported response type");
+      resp.setStatus(HttpServletResponse.SC_FOUND);
+      resp.setBodyReturned(false);
+      resp.setHeader("Location", OAuth2Utils.buildUrl(getRedirectURI(),
+          resp.getResponseParameters(), null));
+      throw new OAuth2Exception(resp);
+    }
+  }
+
+  public GrantType getEnumeratedGrantType() {
+    String grantType = getGrantType();
+    if (grantType == null)
+      return null;
+    if (grantType.equals("refresh_token")) {
+      return GrantType.REFRESH_TOKEN;
+    } else if (grantType.equals("authorization_code")) {
+      return GrantType.AUTHORIZATION_CODE;
+    } else if (grantType.equals("password")) {
+      return GrantType.PASSWORD;
+    } else if (grantType.equals("client_credentials")) {
+      return GrantType.CLIENT_CREDENTIALS;
+    } else {
+      return GrantType.CUSTOM;
+    }
+  }
+
+  public String getString(String key) {
+    if (!containsKey(key)) return null;
+    return (String) get(key);
+  }
+
+  public String toString() {
+    StringBuilder sb = new StringBuilder();
+    for (String key : keySet()) {
+      sb.append(key);
+      sb.append(": ");
+      sb.append(get(key));
+      sb.append('\n');
+    }
+    return sb.toString();
+  }
+
+  // -------------------------- PRIVATE HELPERS -------------------------------
+
+  private void normalizeAccessToken(HttpServletRequest req) {
+    String bearerToken = getString("access_token");
+    if (bearerToken == null || bearerToken.equals("")) {
+      String header = req.getHeader("Authorization");
+      if (header != null && header.toLowerCase().startsWith("bearer")) {
+        String[] parts = header.split("[ \\t]+");
+        bearerToken = parts[parts.length - 1];
+      }
+    }
+    put("access_token", bearerToken);
+  }
+
+  private void normalizeClientSecret(HttpServletRequest request)
+      throws OAuth2Exception {
+    String secret = getClientSecret();
+    if (secret == null || secret.equals("")) {
+      String header = request.getHeader("Authorization");
+      if (header != null && header.toLowerCase().startsWith("basic")) {
+        String[] parts = header.split("[ \\t]+");
+        String temp = parts[parts.length - 1];
+        byte[] decodedSecret = Base64.decodeBase64(temp);
+        try {
+          temp = new String(decodedSecret, "UTF-8");
+          parts = temp.split(":");
+          if (parts != null && parts.length == 2) {
+            secret = parts[1];
+            String queryId = getString("client_id");
+            if (queryId != null && !queryId.equals(parts[0])) {
+              OAuth2NormalizedResponse response = new OAuth2NormalizedResponse();
+              response.setError(ErrorType.INVALID_REQUEST.toString());
+              response
+                  .setErrorDescription("Request contains mismatched client ids");
+              response.setStatus(HttpServletResponse.SC_FORBIDDEN);
+              throw new OAuth2Exception(response);
+            }
+            // Lets set the client id from the Basic auth header if not already
+            // set in query,
+            // needed for client_credential flow.
+            if (queryId == null) {
+              put("client_id", parts[0]);
+            }
+          }
+        } catch (UnsupportedEncodingException e) {
+          LOG.logp(Level.WARNING, classname, "normalizeClientSecret", MessageKeys.INVALID_OAUTH, e);
+          return;
+        }
+      }
+    }
+    put("client_secret", secret);
+  }
+
+  private void normalizeBody(String body) throws OAuth2Exception {
+    if (body == null || body.length() == 0)
+      return;
+    List<NameValuePair> params;
+    try {
+      params = URLEncodedUtils.parse(new URI("http://localhost:8080?" + body),
+          "UTF-8");
+      for (NameValuePair param : params) {
+        put(param.getName(), param.getValue());
+      }
+    } catch (URISyntaxException e) {
+      OAuth2NormalizedResponse response = new OAuth2NormalizedResponse();
+      response.setError(ErrorType.INVALID_REQUEST.toString());
+      response.setErrorDescription("The message body's syntax is incorrect");
+      response.setStatus(HttpServletResponse.SC_FORBIDDEN);
+      throw new OAuth2Exception(response);
+    }
+  }
+
+  private String getBodyAsString(HttpServletRequest request) {
+    if (request.getContentLength() == 0)
+      return "";
+    InputStream is = null;
+    try {
+      String line = null;
+      StringBuffer sb = new StringBuffer();
+      is = request.getInputStream();
+      BufferedReader reader = new BufferedReader(new InputStreamReader(is));
+      while ((line = reader.readLine()) != null) {
+        sb.append(line);
+      }
+      is.close();
+      return sb.toString();
+    } catch (IOException ioe) {
+      LOG.logp(Level.WARNING, classname, "getBodyAsString", MessageKeys.INVALID_OAUTH, ioe);
+      return null;
+    } finally {
+      try { is.close(); } catch (IOException ignore) { }
+    }
+  }
+
+  public void setHttpServletRequest(HttpServletRequest httpReq) {
+    this.httpReq = httpReq;
+  }
+
+  public HttpServletRequest getHttpServletRequest() {
+    return httpReq;
+  }
+}
\ No newline at end of file

Added: shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth2/OAuth2NormalizedResponse.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth2/OAuth2NormalizedResponse.java?rev=1182560&view=auto
==============================================================================
--- shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth2/OAuth2NormalizedResponse.java (added)
+++ shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth2/OAuth2NormalizedResponse.java Wed Oct 12 20:00:11 2011
@@ -0,0 +1,171 @@
+/*
+ * 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.oauth2;
+
+import java.util.Map;
+
+import com.google.common.collect.Maps;
+
+/**
+ * Wraps OAuth 2.0 response elements including headers and body parameters.
+ * 
+ * TODO (Eric): document this class, including bodyReturned
+ */
+public class OAuth2NormalizedResponse {
+
+  private Map<String, String> headers;
+  private Map<String, String> respParams;
+  private int status;
+  private boolean bodyReturned;
+  
+  private static final String ERROR = "error";
+  private static final String ERROR_DESCRIPTION = "error_description";
+  private static final String ERROR_URI = "error_uri";
+  private static final String STATE = "state";
+  private static final String CODE = "code";
+  private static final String ACCESS_TOKEN = "access_token";
+  private static final String TOKEN_TYPE = "token_type";
+  private static final String EXPIRES_IN = "expires_in";
+  private static final String REFRESH_TOKEN = "refresh_token";
+  private static final String SCOPE = "scope";
+
+  public OAuth2NormalizedResponse() {
+    this.headers = Maps.newHashMap();
+    this.respParams = Maps.newHashMap();
+    this.status = -1;
+    this.bodyReturned = false;
+  }
+
+  public void setStatus(int status) {
+    this.status = status;
+  }
+
+  public int getStatus() {
+    return status;
+  }
+
+  public void setBodyReturned(boolean bodyReturned) {
+    this.bodyReturned = bodyReturned;
+  }
+
+  public boolean isBodyReturned() {
+    return bodyReturned;
+  }
+
+  // ------------------------------- HEADER FIELDS ----------------------------
+  public Map<String, String> getHeaders() {
+    return headers;
+  }
+
+  public void setHeaders(Map<String, String> headers) {
+    this.headers = headers;
+  }
+
+  public void setHeader(String key, String value) {
+    headers.put(key, value);
+  }
+
+  // ------------------------------ RESPONSE FIELDS ---------------------------
+  public Map<String, String> getResponseParameters() {
+    return respParams;
+  }
+
+  public void setResponseParameters(Map<String, String> responseParams) {
+    this.respParams = responseParams;
+  }
+
+  public void setError(String error) {
+    respParams.put(ERROR, error);
+  }
+
+  public String getError() {
+    return respParams.get(ERROR);
+  }
+
+  public void setErrorDescription(String errorDescription) {
+    respParams.put(ERROR_DESCRIPTION, errorDescription);
+  }
+
+  public String getErrorDescription() {
+    return respParams.get(ERROR_DESCRIPTION);
+  }
+
+  public void setErrorUri(String errorUri) {
+    respParams.put(ERROR_URI, errorUri);
+  }
+
+  public String getErrorUri() {
+    return respParams.get(ERROR_URI);
+  }
+
+  public void setState(String state) {
+    respParams.put(STATE, state);
+  }
+
+  public String getState() {
+    return respParams.get(STATE);
+  }
+
+  public void setCode(String code) {
+    respParams.put(CODE, code);
+  }
+
+  public String getCode() {
+    return respParams.get(CODE);
+  }
+
+  public void setAccessToken(String accessToken) {
+    respParams.put(ACCESS_TOKEN, accessToken);
+  }
+
+  public String getAccessToken() {
+    return respParams.get(ACCESS_TOKEN);
+  }
+
+  public void setTokenType(String tokenType) {
+    respParams.put(TOKEN_TYPE, tokenType);
+  }
+
+  public String getTokenType() {
+    return respParams.get(TOKEN_TYPE);
+  }
+
+  public void setExpiresIn(String expiresIn) {
+    respParams.put(EXPIRES_IN, expiresIn);
+  }
+
+  public String getExpiresIn() {
+    return respParams.get(EXPIRES_IN);
+  }
+
+  public void setRefreshToken(String refreshToken) {
+    respParams.put(REFRESH_TOKEN, refreshToken);
+  }
+
+  public String getRefreshToken() {
+    return respParams.get(REFRESH_TOKEN);
+  }
+
+  public void setScope(String scope) {
+    respParams.put(SCOPE, scope);
+  }
+
+  public String getScope() {
+    return respParams.get(SCOPE);
+  }
+}
\ No newline at end of file

Added: shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth2/OAuth2Service.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth2/OAuth2Service.java?rev=1182560&view=auto
==============================================================================
--- shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth2/OAuth2Service.java (added)
+++ shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth2/OAuth2Service.java Wed Oct 12 20:00:11 2011
@@ -0,0 +1,91 @@
+/*
+ * 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.oauth2;
+
+/**
+ * Services to support the OAuth 2.0 specification flows and enforcement.
+ * 
+ * TODO (Eric): include grant methods?
+ */
+public interface OAuth2Service {
+
+  /**
+   * Retrieves the underlying data service.
+   */
+  public OAuth2DataService getDataService();
+
+  // --------------------------- VALIDATION SERVICES --------------------------
+  /**
+   * Validates a client.
+   */
+  public void authenticateClient(OAuth2NormalizedRequest req)
+      throws OAuth2Exception;
+
+  /**
+   * Validates a client's request for an authorization token.
+   */
+  public void validateRequestForAuthCode(OAuth2NormalizedRequest req)
+      throws OAuth2Exception;
+
+  /**
+   * Validates a client's request for an access token.
+   */
+  public void validateRequestForAccessToken(OAuth2NormalizedRequest req)
+      throws OAuth2Exception;
+
+  /**
+   * Validates a client's request to use access a resource.
+   */
+  public void validateRequestForResource(OAuth2NormalizedRequest req,
+      Object resourceRequest) throws OAuth2Exception;
+
+  // ------------------- GENERATION & REGISTRATION OF CODES -------------------
+  /**
+   * Grants an authorization code to the given client by generating and
+   * registering the code.
+   */
+  public OAuth2Code grantAuthorizationCode(OAuth2NormalizedRequest req);
+
+  /**
+   * Grants an access token to the given client by generating and registering
+   * the access token.
+   */
+  public OAuth2Code grantAccessToken(OAuth2NormalizedRequest req);
+
+  /**
+   * Grants a refresh token to the given client by generating and registering
+   * the refresh token.
+   */
+  public OAuth2Code grantRefreshToken(OAuth2NormalizedRequest req);
+
+  // ------------------------ TOKEN GENERATION SERVICES -----------------------
+  /**
+   * Generates an authorization code from a client OAuth 2.0 request.
+   */
+  public OAuth2Code generateAuthorizationCode(OAuth2NormalizedRequest req);
+
+  /**
+   * Generates an access token from a client OAuth 2.0 request.
+   */
+  public OAuth2Code generateAccessToken(OAuth2NormalizedRequest req);
+
+  /**
+   * Generates a refresh token from a client OAuth 2.0 request.
+   */
+  public OAuth2Code generateRefreshToken(OAuth2NormalizedRequest req);
+}

Added: shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth2/OAuth2ServiceImpl.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth2/OAuth2ServiceImpl.java?rev=1182560&view=auto
==============================================================================
--- shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth2/OAuth2ServiceImpl.java (added)
+++ shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth2/OAuth2ServiceImpl.java Wed Oct 12 20:00:11 2011
@@ -0,0 +1,207 @@
+/*
+ * 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.oauth2;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Properties;
+import java.util.UUID;
+
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.shindig.common.util.ResourceLoader;
+import org.apache.shindig.social.core.oauth2.OAuth2Client.ClientType;
+import org.apache.shindig.social.core.oauth2.OAuth2Types.CodeType;
+import org.apache.shindig.social.core.oauth2.OAuth2Types.ErrorType;
+import org.apache.shindig.social.core.oauth2.validators.AccessTokenRequestValidator;
+import org.apache.shindig.social.core.oauth2.validators.AuthorizationCodeRequestValidator;
+import org.apache.shindig.social.core.oauth2.validators.DefaultResourceRequestValidator;
+import org.apache.shindig.social.core.oauth2.validators.OAuth2ProtectedResourceValidator;
+import org.apache.shindig.social.core.oauth2.validators.OAuth2RequestValidator;
+
+import com.google.inject.CreationException;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+import com.google.inject.name.Named;
+import com.google.inject.spi.Message;
+
+/**
+ * A simple in-memory implementation of the OAuth 2 services.
+ */
+@Singleton
+public class OAuth2ServiceImpl implements OAuth2Service {
+
+  private OAuth2DataService store; // underlying OAuth data store
+  
+  private long authCodeExpires;
+  private long accessTokenExpires;
+  
+  // validators
+  private OAuth2RequestValidator accessTokenValidator;
+  private OAuth2RequestValidator authCodeValidator;
+  private OAuth2ProtectedResourceValidator resourceReqValidator;
+
+
+  @Inject
+  public OAuth2ServiceImpl(OAuth2DataService store) {
+    this.store = store;
+    
+    // TODO (Eric): properties should be injected, but getting "no implementation bound"
+    Properties props = readPropertyFile("shindig.properties");
+    this.authCodeExpires = Long.valueOf(props.getProperty("shindig.oauth2.authCodeExpiration"));
+    this.accessTokenExpires = Long.valueOf(props.getProperty("shindig.oauth2.accessTokenExpiration"));
+    
+    System.out.print("YOUDAMAN: " + this.authCodeExpires);
+    
+    // TODO (Matt): validators should be injected
+    authCodeValidator = new AuthorizationCodeRequestValidator(store);
+    accessTokenValidator = new AccessTokenRequestValidator(store);
+    resourceReqValidator = new DefaultResourceRequestValidator(store);
+  }
+
+  public OAuth2DataService getDataService() {
+    return store;
+  }
+
+  public void authenticateClient(OAuth2NormalizedRequest req)
+      throws OAuth2Exception {
+    OAuth2Client client = store.getClient(req.getClientId());
+    if (client == null) {
+      OAuth2NormalizedResponse resp = new OAuth2NormalizedResponse();
+      resp.setError(ErrorType.INVALID_CLIENT.toString());
+      resp.setErrorDescription("The client ID is invalid or not registered");
+      resp.setBodyReturned(true);
+      resp.setStatus(HttpServletResponse.SC_BAD_REQUEST);
+      throw new OAuth2Exception(resp);
+    }
+    String realSecret = client.getSecret();
+    String reqSecret = req.getClientSecret();
+    if (realSecret != null || reqSecret != null
+        || client.getType() == ClientType.CONFIDENTIAL) {
+      if (realSecret == null || reqSecret == null
+          || !realSecret.equals(reqSecret)) {
+        OAuth2NormalizedResponse resp = new OAuth2NormalizedResponse();
+        resp.setError(ErrorType.UNAUTHORIZED_CLIENT.toString());
+        resp.setErrorDescription("The client failed to authorize");
+        resp.setBodyReturned(true);
+        resp.setStatus(HttpServletResponse.SC_BAD_REQUEST);
+        throw new OAuth2Exception(resp);
+      }
+    }
+  }
+  
+  public void validateRequestForAuthCode(OAuth2NormalizedRequest req)
+      throws OAuth2Exception {
+    authCodeValidator.validateRequest(req);
+  }
+
+  public void validateRequestForAccessToken(OAuth2NormalizedRequest req)
+      throws OAuth2Exception {
+    accessTokenValidator.validateRequest(req);
+  }
+
+  public void validateRequestForResource(OAuth2NormalizedRequest req,
+      Object resourceRequest) throws OAuth2Exception {
+    resourceReqValidator.validateRequestForResource(req, resourceRequest);
+  }
+
+  public OAuth2Code grantAuthorizationCode(OAuth2NormalizedRequest req) {
+    OAuth2Code authCode = generateAuthorizationCode(req);
+    store.registerAuthorizationCode(req.getClientId(), authCode);
+    return authCode;
+  }
+
+  public OAuth2Code grantAccessToken(OAuth2NormalizedRequest req) {
+    OAuth2Code accessToken = generateAccessToken(req);
+    OAuth2Code authCode = store.getAuthorizationCode(req.getClientId(),
+        req.getAuthorizationCode());
+    if (authCode != null) {
+      authCode.setRelatedAccessToken(accessToken);
+    }
+    store.registerAccessToken(req.getClientId(), accessToken);
+    return accessToken;
+  }
+
+  public OAuth2Code grantRefreshToken(OAuth2NormalizedRequest req) {
+    OAuth2Code refreshToken = generateRefreshToken(req);
+    store.registerRefreshToken(req.getClientId(), refreshToken);
+    return refreshToken;
+  }
+
+  public OAuth2Code generateAuthorizationCode(OAuth2NormalizedRequest req) {
+    OAuth2Code authCode = new OAuth2Code();
+    authCode.setValue(UUID.randomUUID().toString());
+    authCode.setExpiration(System.currentTimeMillis() + authCodeExpires);
+    OAuth2Client client = store.getClient(req.getString("client_id"));
+    authCode.setClient(client);
+    if (req.getRedirectURI() != null) {
+      authCode.setRedirectURI(req.getRedirectURI());
+    } else {
+      authCode.setRedirectURI(client.getRedirectURI());
+    }
+    return authCode;
+  }
+
+  public OAuth2Code generateAccessToken(OAuth2NormalizedRequest req) {
+    // generate token value
+    OAuth2Code accessToken = new OAuth2Code();
+    accessToken.setType(CodeType.ACCESS_TOKEN);
+    accessToken.setValue(UUID.randomUUID().toString());
+    accessToken.setExpiration(System.currentTimeMillis() + accessTokenExpires);
+    if (req.getRedirectURI() != null) {
+      accessToken.setRedirectURI(req.getRedirectURI());
+    } else {
+      accessToken.setRedirectURI(store.getClient(req.getClientId()).getRedirectURI());
+    }
+
+    // associate with existing authorization code, if an auth code exists.
+    if (req.getAuthorizationCode() != null) {
+      OAuth2Code authCode = store.getAuthorizationCode(req.getClientId(), req.getAuthorizationCode());
+      accessToken.setRelatedAuthCode(authCode);
+      accessToken.setClient(authCode.getClient());
+      if (authCode.getScope() != null) {
+        accessToken.setScope(new ArrayList<String>(authCode.getScope()));
+      }
+    }
+
+    return accessToken;
+  }
+
+  // TODO (Eric): Refresh tokens are not yet supported.
+  public OAuth2Code generateRefreshToken(OAuth2NormalizedRequest req) {
+    throw new RuntimeException("not yet implemented");
+  }
+  
+  private Properties readPropertyFile(String propertyFile) {
+    Properties properties = new Properties();
+    InputStream is = null;
+    try {
+      is = ResourceLoader.openResource(propertyFile);
+      properties.load(is);
+    } catch (IOException e) {
+      throw new CreationException(Arrays.asList(
+          new Message("Unable to load properties: " + propertyFile)));
+    } finally {
+      IOUtils.closeQuietly( is );
+    }
+    return properties;
+  }
+}
\ No newline at end of file

Added: shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth2/OAuth2Servlet.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth2/OAuth2Servlet.java?rev=1182560&view=auto
==============================================================================
--- shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth2/OAuth2Servlet.java (added)
+++ shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth2/OAuth2Servlet.java Wed Oct 12 20:00:11 2011
@@ -0,0 +1,119 @@
+/*
+ * 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.oauth2;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.shindig.common.logging.i18n.MessageKeys;
+import org.apache.shindig.common.servlet.HttpUtil;
+import org.apache.shindig.common.servlet.InjectedServlet;
+import org.json.JSONObject;
+
+import com.google.inject.Inject;
+
+/**
+ * Main servlet to catch OAuth 2.0 requests.
+ */
+public class OAuth2Servlet extends InjectedServlet {
+
+  private static final long serialVersionUID = -4257719224664564922L;
+  private static OAuth2AuthorizationHandler authorizationHandler;
+  private static OAuth2TokenHandler tokenHandler;
+  
+  //class name for logging purpose
+  private static final String classname = OAuth2Servlet.class.getName();
+  private static final Logger LOG = Logger.getLogger(classname,MessageKeys.MESSAGES);
+
+  @Inject
+  public void setOAuth2Service(OAuth2Service oauthService) {
+    authorizationHandler = new OAuth2AuthorizationHandler(oauthService);
+    tokenHandler = new OAuth2TokenHandler(oauthService);
+  }
+
+  @Override
+  public void init(ServletConfig config) throws ServletException {
+    super.init(config);
+  }
+
+  @Override
+  protected void doGet(HttpServletRequest request, HttpServletResponse response)
+      throws ServletException, IOException {
+    HttpUtil.setNoCache(response);
+    String path = request.getPathInfo();
+    if (path.endsWith("authorize")) {
+      sendOAuth2Response(response, authorizationHandler.handle(request, response));
+    } else if (path.endsWith("token")) {
+      sendOAuth2Response(response, tokenHandler.handle(request, response));
+    } else {
+      response.sendError(HttpServletResponse.SC_NOT_FOUND, "Unknown URL");
+    }
+  }
+
+  @Override
+  protected void doPost(HttpServletRequest request, HttpServletResponse response)
+      throws ServletException, IOException {
+    doGet(request, response);
+  }
+
+  /**
+   * Sends an OAuth 2.0 response based on an OAuth2NormalizedResponse object.
+   * 
+   * @param servletResp is the servlet's response object
+   * @param normalizedResp maintains the headers and body fields to respond with
+   * @param createBody defines whether or not to create a body from the response parameters
+   */
+  private void sendOAuth2Response(HttpServletResponse servletResp,
+      OAuth2NormalizedResponse normalizedResp) {
+    // set status
+    servletResp.setStatus(normalizedResp.getStatus());
+
+    // set body parameters
+    Map<String, String> respParams = normalizedResp.getResponseParameters();
+    if (normalizedResp.isBodyReturned() && respParams != null) {
+      PrintWriter out = null;
+      try {
+        servletResp.setHeader("Content-Type", "application/json");
+        out = servletResp.getWriter();
+        out.println(new JSONObject(respParams).toString());
+        out.flush();
+      } catch (IOException e) {
+        LOG.logp(Level.WARNING, classname, "getBodyAsString", MessageKeys.INVALID_OAUTH, e);
+        throw new RuntimeException(e);
+      } finally {
+        out.close();
+      }
+    }
+
+    // set headers
+    Map<String, String> headers = normalizedResp.getHeaders();
+    if (headers != null) {
+      for (String key : headers.keySet()) {
+        servletResp.setHeader(key, headers.get(key));
+      }
+    }
+  }
+}
\ No newline at end of file

Added: shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth2/OAuth2TokenHandler.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth2/OAuth2TokenHandler.java?rev=1182560&view=auto
==============================================================================
--- shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth2/OAuth2TokenHandler.java (added)
+++ shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth2/OAuth2TokenHandler.java Wed Oct 12 20:00:11 2011
@@ -0,0 +1,97 @@
+/*
+ * 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.oauth2;
+
+import java.io.IOException;
+import java.util.List;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.shindig.social.core.oauth2.OAuth2Types.TokenFormat;
+
+/**
+ * Handles operations to the OAuth 2.0 token end point.
+ * 
+ * TODO (Eric): generate refreshToken & associate with accessToken
+ */
+public class OAuth2TokenHandler {
+
+  private OAuth2Service service;
+
+  /**
+   * Constructs the token handler with the OAuth2Service.
+   * 
+   * @param service is the service that will support this handler
+   */
+  public OAuth2TokenHandler(OAuth2Service service) {
+    this.service = service;
+  }
+
+  /**
+   * Handles an OAuth 2.0 request to the token endpoint.
+   * 
+   * @param request is the servlet request object
+   * @param response is the servlet response object
+   * @return OAuth2NormalizedResponse encapsulates the request's response
+   * 
+   * @throws ServletException
+   * @throws IOException
+   */
+  public OAuth2NormalizedResponse handle(HttpServletRequest request,
+      HttpServletResponse response) throws ServletException, IOException {
+    try {
+      // normalize the request
+      OAuth2NormalizedRequest normalizedReq = new OAuth2NormalizedRequest(request);
+
+      // grant access token
+      service.authenticateClient(normalizedReq);
+      service.validateRequestForAccessToken(normalizedReq);
+      OAuth2Code accessToken = service.grantAccessToken(normalizedReq);
+
+      // send response
+      OAuth2NormalizedResponse normalizedResp = new OAuth2NormalizedResponse();
+      normalizedResp.setAccessToken(accessToken.getValue());
+      normalizedResp.setTokenType(TokenFormat.BEARER.toString());
+      normalizedResp.setExpiresIn((accessToken.getExpiration() - System.currentTimeMillis() + ""));
+      normalizedResp.setScope(listToString(accessToken.getScope()));
+      normalizedResp.setStatus(HttpServletResponse.SC_OK);
+      normalizedResp.setBodyReturned(true);
+      if (normalizedReq.getState() != null) normalizedResp.setState(normalizedReq.getState());
+      return normalizedResp;
+    } catch (OAuth2Exception oae) {
+      return oae.getNormalizedResponse();
+    }
+  }
+
+  /**
+   * Private utility to comma-delimit a list of Strings
+   */
+  private static String listToString(List<String> list) {
+    if (list == null || list.isEmpty())
+      return "";
+    StringBuilder sb = new StringBuilder();
+    for (String item : list) {
+      sb.append(item);
+      sb.append(',');
+    }
+    sb.deleteCharAt(sb.length());
+    return sb.toString();
+  }
+}
\ No newline at end of file