From: <sha...@us...> - 2012-01-04 06:11:32
|
Revision: 15638 http://exist.svn.sourceforge.net/exist/?rev=15638&view=rev Author: shabanovd Date: 2012-01-04 06:11:25 +0000 (Wed, 04 Jan 2012) Log Message: ----------- [feature] Google OAuth2 Modified Paths: -------------- trunk/eXist/extensions/security/oauth/src/org/exist/security/realm/oauth/Service.java Added Paths: ----------- trunk/eXist/extensions/security/oauth/lib/scribe-1.3.0.jar trunk/eXist/extensions/security/oauth/src/org/exist/security/realm/oauth/Google2Api.java trunk/eXist/extensions/security/oauth/src/org/exist/security/realm/oauth/ServiceGoogle.java Removed Paths: ------------- trunk/eXist/extensions/security/oauth/lib/scribe-1.2.3.jar Deleted: trunk/eXist/extensions/security/oauth/lib/scribe-1.2.3.jar =================================================================== (Binary files differ) Added: trunk/eXist/extensions/security/oauth/lib/scribe-1.3.0.jar =================================================================== (Binary files differ) Property changes on: trunk/eXist/extensions/security/oauth/lib/scribe-1.3.0.jar ___________________________________________________________________ Added: svn:mime-type + application/octet-stream Added: trunk/eXist/extensions/security/oauth/src/org/exist/security/realm/oauth/Google2Api.java =================================================================== --- trunk/eXist/extensions/security/oauth/src/org/exist/security/realm/oauth/Google2Api.java (rev 0) +++ trunk/eXist/extensions/security/oauth/src/org/exist/security/realm/oauth/Google2Api.java 2012-01-04 06:11:25 UTC (rev 15638) @@ -0,0 +1,64 @@ +/* + * eXist Open Source Native XML Database + * Copyright (C) 2012 The eXist Project + * http://exist-db.org + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id$ + */ +package org.exist.security.realm.oauth; + +import org.scribe.builder.api.DefaultApi20; +import org.scribe.model.OAuthConfig; +import org.scribe.utils.OAuthEncoder; +import org.scribe.utils.Preconditions; + +/** + * @author <a href="mailto:sha...@gm...">Dmitriy Shabanov</a> + * + */ +public class Google2Api extends DefaultApi20 { + + private static final String AUTHORIZE_URL = "https://accounts.google.com/o/oauth2/auth?response_type=token&client_id=%s&redirect_uri=%s"; + private static final String SCOPED_AUTHORIZE_URL = AUTHORIZE_URL + "&scope=%s"; + + @Override + public String getAccessTokenEndpoint() { + return "https://accounts.google.com/o/oauth2/token?grant_type=authorization_code"; + } + + @Override + public String getAuthorizationUrl(OAuthConfig config) { + Preconditions + .checkValidUrl(config.getCallback(), + "Must provide a valid url as callback. Google does not support OOB"); + + // Append scope if present + if (config.hasScope()) { + return String.format(SCOPED_AUTHORIZE_URL, + config.getApiKey(), + OAuthEncoder.encode(config.getCallback()), + OAuthEncoder.encode(config.getScope()) + ); + } else { + return String.format(AUTHORIZE_URL, + config.getApiKey(), + OAuthEncoder.encode(config.getCallback()) + ); + } + } + +} Property changes on: trunk/eXist/extensions/security/oauth/src/org/exist/security/realm/oauth/Google2Api.java ___________________________________________________________________ Added: svn:mime-type + text/plain Modified: trunk/eXist/extensions/security/oauth/src/org/exist/security/realm/oauth/Service.java =================================================================== --- trunk/eXist/extensions/security/oauth/src/org/exist/security/realm/oauth/Service.java 2012-01-03 10:14:16 UTC (rev 15637) +++ trunk/eXist/extensions/security/oauth/src/org/exist/security/realm/oauth/Service.java 2012-01-04 06:11:25 UTC (rev 15638) @@ -27,6 +27,7 @@ import org.exist.config.annotation.ConfigurationClass; import org.exist.config.annotation.ConfigurationFieldAsAttribute; import org.scribe.builder.ServiceBuilder; +import org.scribe.builder.api.Api; import org.scribe.builder.api.FacebookApi; /** @@ -49,8 +50,11 @@ @ConfigurationFieldAsAttribute("secret") String apiSecret; - public Service(OAuthRealm realm, Configuration config) { + @ConfigurationFieldAsAttribute("provider") + String provider; + public Service(OAuthRealm realm, Configuration config) { + configuration = Configurator.configure(this, config); } @@ -60,10 +64,19 @@ public ServiceBuilder getServiceBuilder() { return new ServiceBuilder() - .provider(FacebookApi.class) + .provider(getProviderClass()) .apiKey(apiKey) .apiSecret(apiSecret); } + + private Class<? extends Api> getProviderClass() { + if (provider.equalsIgnoreCase("facebook")) + return FacebookApi.class; + else if (provider.equalsIgnoreCase("google")) + return Google2Api.class; + + throw new IllegalArgumentException("Unknown provider '"+provider+"'"); + } public boolean isConfigured() { return configuration != null; Added: trunk/eXist/extensions/security/oauth/src/org/exist/security/realm/oauth/ServiceGoogle.java =================================================================== --- trunk/eXist/extensions/security/oauth/src/org/exist/security/realm/oauth/ServiceGoogle.java (rev 0) +++ trunk/eXist/extensions/security/oauth/src/org/exist/security/realm/oauth/ServiceGoogle.java 2012-01-04 06:11:25 UTC (rev 15638) @@ -0,0 +1,193 @@ +/* + * eXist Open Source Native XML Database + * Copyright (C) 2012 The eXist Project + * http://exist-db.org + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id$ + */ +package org.exist.security.realm.oauth; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.Map; + +import javax.security.auth.Subject; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpSession; + +import org.eclipse.jetty.security.DefaultIdentityService; +import org.eclipse.jetty.server.Authentication; +import org.eclipse.jetty.server.UserIdentity; +import org.exist.security.AXSchemaType; +import org.exist.security.AbstractAccount; +import org.exist.security.Account; +import org.exist.security.SchemaType; +import org.exist.security.internal.HttpSessionAuthentication; +import org.exist.security.internal.SubjectAccreditedImpl; +import org.json.JSONException; +import org.json.JSONObject; +import org.scribe.exceptions.OAuthException; +import org.scribe.model.OAuthRequest; +import org.scribe.model.Response; +import org.scribe.model.Token; +import org.scribe.model.Verb; +import org.scribe.oauth.OAuthService; + +/** + * @author <a href="mailto:sha...@gm...">Dmitriy Shabanov</a> + * + */ +public class ServiceGoogle { + + private static final String PROTECTED_RESOURCE_URL = "https://www.googleapis.com/oauth2/v1/userinfo"; + public static final String GOOGLE_ACCESS_TOKEN_SESSION = "GOOGLE_ACCESS_TOKEN_SESSION"; + + public static void saveAccessToken(HttpServletRequest request, OAuthService service, Token accessToken) throws Exception { + + OAuthRequest _request = new OAuthRequest(Verb.GET, PROTECTED_RESOURCE_URL); + service.signRequest(accessToken, _request); + Response _response = _request.send(); + if (_response.getCode() != 200) + throw new OAuthException("Can query account information."); + + String contentType = _response.getHeader("Content-Type"); + if (contentType == null) contentType = ""; + + String charset = ""; + int semicolonPos = contentType.indexOf(';'); + + if (semicolonPos > 0) { + String _charset = contentType.substring(semicolonPos + 1).trim(); + if (_charset.startsWith("charset")) { + charset = _charset.substring(_charset.indexOf('=') + 1); + } + contentType = contentType.substring(0, semicolonPos); + } + + Map<String, String> responseAttributes = null; + String response = _response.getBody(); + if ("application/json".equals(contentType) || (response.startsWith("{") && response.endsWith("}"))) { + JSONObject jsonResponse = new JSONObject(response); + if (jsonResponse != null) { + if (jsonResponse.has("error")) { + throw new OAuthException("Error getting access token: " + System.getProperty("line.separator") + jsonResponse.toString()); + } + + responseAttributes = parseJSONObject(jsonResponse); + } + } else if ("text/plain".equals(contentType) || (response.contains("=") && response.contains("&"))) { + //responseAttributes = OAuthUtil.parseQueryString(response); + } + + if (responseAttributes == null) + throw new OAuthException("Get response, but no account information."); + + String id = responseAttributes.get("id"); + + String accountName = id + "@facebook.com"; + + Account found = OAuthRealm._.getAccount(accountName); + + if (found == null) { + Map<SchemaType, String> metadata = new HashMap<SchemaType, String>(); + metadata.put(GoogleSchemaType.ID, responseAttributes.get("id")); + metadata.put(AXSchemaType.FIRSTNAME, responseAttributes.get("given_name")); + metadata.put(AXSchemaType.LASTNAME, responseAttributes.get("family_name")); + metadata.put(AXSchemaType.FULLNAME, responseAttributes.get("name")); + metadata.put(AXSchemaType.TIMEZONE, responseAttributes.get("timezone")); + + found = OAuthRealm._.createAccountInDatabase(accountName, metadata); + } + + Account principal = new SubjectAccreditedImpl((AbstractAccount) found, accessToken); + + HttpSession session = request.getSession(true); + + Subject subject = new Subject(); + + //TODO: hardcoded to jetty - rewrite + //******************************************************* + DefaultIdentityService _identityService = new DefaultIdentityService(); + UserIdentity user = _identityService.newUserIdentity(subject, principal, new String[0]); + + Authentication cached=new HttpSessionAuthentication(session, user); + session.setAttribute(HttpSessionAuthentication.__J_AUTHENTICATED, cached); + //******************************************************* + + + request.getSession().setAttribute(GOOGLE_ACCESS_TOKEN_SESSION, accessToken); + } + + private static Map<String, String> parseJSONObject(JSONObject json) throws JSONException { + Map<String, String> parameters = null; + + if (json != null && json.length() > 0) { + parameters = new LinkedHashMap<String, String>(); + @SuppressWarnings("unchecked") + Iterator<String> iter = json.keys(); + + if (iter != null) { + while (iter.hasNext()) { + String key = iter.next(); + parameters.put(key, json.getString(key)); + } + } + } + + return parameters; + } + + public enum GoogleSchemaType implements SchemaType { + + ID("https://www.googleapis.com/oauth2/v1/userinfo", "id"); + + private final String namespace; + private final String alias; + + GoogleSchemaType(String namespace, String alias) { + this.namespace = namespace; + this.alias = alias; + } + + public static GoogleSchemaType valueOfNamespace(String namespace) { + for(GoogleSchemaType schemaType : GoogleSchemaType.values()) { + if(schemaType.getNamespace().equals(namespace)) { + return schemaType; + } + } + return null; + } + + public static GoogleSchemaType valueOfAlias(String alias) { + for(GoogleSchemaType schemaType : GoogleSchemaType.values()) { + if(schemaType.getAlias().equals(alias)) { + return schemaType; + } + } + return null; + } + + public String getNamespace() { + return namespace; + } + + public String getAlias() { + return alias; + } + } +} \ No newline at end of file Property changes on: trunk/eXist/extensions/security/oauth/src/org/exist/security/realm/oauth/ServiceGoogle.java ___________________________________________________________________ Added: svn:mime-type + text/plain This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |