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