diff --git a/extensions/gdata/module/MOD-INF/controller.js b/extensions/gdata/module/MOD-INF/controller.js index 7ab4af00a..428941de3 100644 --- a/extensions/gdata/module/MOD-INF/controller.js +++ b/extensions/gdata/module/MOD-INF/controller.js @@ -31,6 +31,13 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +/* + * Controller for GData extension. + * + * This is run in the Butterfly (ie Refine) server context using the Rhino + * Javascript interpreter. + */ + var html = "text/html"; var encoding = "UTF-8"; var version = "0.2"; @@ -101,21 +108,13 @@ function process(path, request, response) { } else if (path == "authorized") { var context = {}; context.winname = request.getParameter("winname"); - context.callback = request.getParameter("callback"); + context.callback = request.getParameter("cb"); (function() { - var queryString = request.getQueryString(); - if (queryString != null) { - var AuthSubUtil = Packages.com.google.gdata.client.http.AuthSubUtil; - - // FIXME(SM): can we safely assume UTF-8 encoding here? - var onetimeUseToken = AuthSubUtil.getTokenFromReply( - Packages.java.net.URLDecoder.decode(queryString, "UTF-8")); - if (onetimeUseToken) { - var sessionToken = AuthSubUtil.exchangeForSessionToken(onetimeUseToken, null); - Packages.com.google.refine.extension.gdata.TokenCookie.setToken(request, response, sessionToken); - return; - } + var token = Packages.com.google.refine.extension.gdata.GDataExtension.getTokenFromCode(module,request); + if (token) { + Packages.com.google.refine.extension.gdata.TokenCookie.setToken(request, response, token); + return; } Packages.com.google.refine.extension.gdata.TokenCookie.deleteToken(request, response); })(); diff --git a/extensions/gdata/module/scripts/gdata-extension.js b/extensions/gdata/module/scripts/gdata-extension.js index 57e9a2e26..02196c8a4 100644 --- a/extensions/gdata/module/scripts/gdata-extension.js +++ b/extensions/gdata/module/scripts/gdata-extension.js @@ -34,7 +34,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. var GdataExtension = {}; GdataExtension.isAuthorized = function() { - return $.cookie('authsub_token') !== null; + return $.cookie('oauth2_token') !== null; }; GdataExtension.showAuthorizationDialog = function(onAuthorized, onNotAuthorized) { @@ -57,6 +57,6 @@ GdataExtension.showAuthorizationDialog = function(onAuthorized, onNotAuthorized) }; window[callbackName] = callback; - var url = ModuleWirings['gdata'] + "authorize?winname=" + escape(windowName) + "&callback=" + escape(callbackName); + var url = ModuleWirings['gdata'] + "authorize?winname=" + escape(windowName) + "&cb=" + escape(callbackName); var win = window.open(url, "openrefinegdataauth", "resizable=1,width=800,height=600"); }; diff --git a/extensions/gdata/src/com/google/refine/extension/gdata/DeAuthorizeCommand.java b/extensions/gdata/src/com/google/refine/extension/gdata/DeAuthorizeCommand.java index 91dd7b631..7a1dd1ead 100644 --- a/extensions/gdata/src/com/google/refine/extension/gdata/DeAuthorizeCommand.java +++ b/extensions/gdata/src/com/google/refine/extension/gdata/DeAuthorizeCommand.java @@ -34,12 +34,18 @@ import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import com.google.gdata.client.http.AuthSubUtil; +import com.google.api.client.http.GenericUrl; +import com.google.api.client.http.HttpRequest; +import com.google.api.client.http.HttpRequestFactory; +import com.google.api.client.http.HttpResponse; +import com.google.api.client.http.HttpTransport; +import com.google.api.client.http.javanet.NetHttpTransport; import com.google.refine.commands.Command; public class DeAuthorizeCommand extends Command { + private static final HttpTransport HTTP_TRANSPORT = new NetHttpTransport(); @Override public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { @@ -50,7 +56,16 @@ public class DeAuthorizeCommand extends Command { String sessionToken = TokenCookie.getToken(request); if (sessionToken != null) { - AuthSubUtil.revokeToken(sessionToken, null); + + // No method to do this in Google's client lib, so roll our own + HttpRequestFactory factory = HTTP_TRANSPORT.createRequestFactory(); + GenericUrl url = new GenericUrl("https://accounts.google.com/o/oauth2/revoke?token="+sessionToken); + HttpRequest rqst = factory.buildGetRequest(url); + HttpResponse resp = rqst.execute(); + if (resp.getStatusCode() != 200) { + respond(response, String.valueOf(resp.getStatusCode()), resp.getStatusMessage()); + } + TokenCookie.deleteToken(request, response); } respond(response, "200 OK", ""); diff --git a/extensions/gdata/src/com/google/refine/extension/gdata/FusionTableHandler.java b/extensions/gdata/src/com/google/refine/extension/gdata/FusionTableHandler.java index 76b4d852e..8a2f87324 100644 --- a/extensions/gdata/src/com/google/refine/extension/gdata/FusionTableHandler.java +++ b/extensions/gdata/src/com/google/refine/extension/gdata/FusionTableHandler.java @@ -30,7 +30,6 @@ package com.google.refine.extension.gdata; import java.io.IOException; import java.io.OutputStreamWriter; -import java.net.MalformedURLException; import java.net.URL; import java.net.URLEncoder; import java.util.ArrayList; @@ -39,19 +38,12 @@ import java.util.Scanner; import java.util.regex.MatchResult; import java.util.regex.Pattern; -import javax.servlet.http.HttpServletRequest; - import com.google.gdata.client.GoogleService; import com.google.gdata.client.Service.GDataRequest; import com.google.gdata.client.Service.GDataRequest.RequestType; -import com.google.gdata.client.http.AuthSubUtil; import com.google.gdata.util.ContentType; import com.google.gdata.util.ServiceException; -import com.google.refine.util.ParsingUtilities; - -import edu.mit.simile.butterfly.ButterflyModule; - /** * @author Tom Morris * @copyright 2010,2013 Thomas F. Morris @@ -61,41 +53,22 @@ public class FusionTableHandler { final static private String FUSION_TABLES_SERVICE_URL = "https://www.google.com/fusiontables/api/query"; +// "https://www.googleapis.com/fusiontables/v1/query"; final static private Pattern CSV_VALUE_PATTERN = Pattern.compile("([^,\\r\\n\"]*|\"(([^\"]*\"\")*[^\"]*)\")(,|\\r?\\n)"); - - static public String getAuthorizationUrl(ButterflyModule module, HttpServletRequest request) - throws MalformedURLException { - char[] mountPointChars = module.getMountPoint().getMountPoint().toCharArray(); - - StringBuffer sb = new StringBuffer(); - sb.append(mountPointChars, 0, mountPointChars.length); - sb.append("authorized?winname="); - sb.append(ParsingUtilities.encode(request.getParameter("winname"))); - sb.append("&callback="); - sb.append(ParsingUtilities.encode(request.getParameter("callback"))); - - URL thisUrl = new URL(request.getRequestURL().toString()); - URL authorizedUrl = new URL(thisUrl, sb.toString()); - - return AuthSubUtil.getRequestUrl( - authorizedUrl.toExternalForm(), // execution continues at authorized on redirect - "https://www.google.com/fusiontables/api/query", - false, - true); - } static public GDataRequest createFusionTablesPostRequest( GoogleService service, RequestType requestType, String query) throws IOException, ServiceException { + URL url = new URL(FUSION_TABLES_SERVICE_URL); GDataRequest request = service.getRequestFactory().getRequest( requestType, url, new ContentType("application/x-www-form-urlencoded")); OutputStreamWriter writer = new OutputStreamWriter(request.getRequestStream()); - writer.append("sql=" + URLEncoder.encode(query, "UTF-8")); + writer.append("sql=" + URLEncoder.encode(query, "UTF-8") + "&alt=csv"); writer.flush(); writer.close(); @@ -106,7 +79,7 @@ public class FusionTableHandler { GoogleService service, RequestType requestType, String query) throws IOException, ServiceException { URL url = new URL(FUSION_TABLES_SERVICE_URL + "?sql=" + - URLEncoder.encode(query, "UTF-8")); + URLEncoder.encode(query, "UTF-8")+"&alt=csv"); return service.getRequestFactory().getRequest( requestType, url, ContentType.TEXT_PLAIN); } diff --git a/extensions/gdata/src/com/google/refine/extension/gdata/GDataExtension.java b/extensions/gdata/src/com/google/refine/extension/gdata/GDataExtension.java index 8daaf64bb..3a5dcafdf 100644 --- a/extensions/gdata/src/com/google/refine/extension/gdata/GDataExtension.java +++ b/extensions/gdata/src/com/google/refine/extension/gdata/GDataExtension.java @@ -28,13 +28,22 @@ */ package com.google.refine.extension.gdata; +import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; +import java.util.Arrays; import javax.servlet.http.HttpServletRequest; +import com.google.api.client.auth.oauth2.AuthorizationCodeResponseUrl; +import com.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeRequestUrl; +import com.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeTokenRequest; +import com.google.api.client.googleapis.auth.oauth2.GoogleTokenResponse; +import com.google.api.client.http.HttpTransport; +import com.google.api.client.http.javanet.NetHttpTransport; +import com.google.api.client.json.JsonFactory; +import com.google.api.client.json.jackson.JacksonFactory; import com.google.gdata.client.docs.DocsService; -import com.google.gdata.client.http.AuthSubUtil; import com.google.gdata.client.spreadsheet.SpreadsheetService; import com.google.refine.util.ParsingUtilities; @@ -48,28 +57,72 @@ import edu.mit.simile.butterfly.ButterflyModule; */ abstract public class GDataExtension { static final String SERVICE_APP_NAME = "OpenRefine-GData-Extension"; + static final String CLIENT_ID = "647865400439.apps.googleusercontent.com"; + static final String CLIENT_SECRET = "0mW9OJji1yrgJk5AjJc5Pn6I"; // not really that secret, but the protocol accounts for that + + /** Global instance of the HTTP transport. */ + static final HttpTransport HTTP_TRANSPORT = new NetHttpTransport(); + + /** Global instance of the JSON factory. */ + static final JsonFactory JSON_FACTORY = new JacksonFactory(); static public String getAuthorizationUrl(ButterflyModule module, HttpServletRequest request) throws MalformedURLException { - char[] mountPointChars = module.getMountPoint().getMountPoint().toCharArray(); - - StringBuffer sb = new StringBuffer(); - sb.append(mountPointChars, 0, mountPointChars.length); + String authorizedUrl = makeRedirectUrl(module, request); + + // New Oauth2 + GoogleAuthorizationCodeRequestUrl url = new GoogleAuthorizationCodeRequestUrl( + CLIENT_ID, + authorizedUrl, // execution continues at authorized on redirect + Arrays.asList("https://www.googleapis.com/auth/fusiontables", + "https://docs.google.com/feeds", // create new spreadsheets + "https://spreadsheets.google.com/feeds")); + + return url.toString(); + + } + + private static String makeRedirectUrl(ButterflyModule module, HttpServletRequest request) + throws MalformedURLException { + StringBuffer sb = new StringBuffer(module.getMountPoint().getMountPoint()); sb.append("authorized?winname="); sb.append(ParsingUtilities.encode(request.getParameter("winname"))); - sb.append("&callback="); - sb.append(ParsingUtilities.encode(request.getParameter("callback"))); + sb.append("&cb="); + sb.append(ParsingUtilities.encode(request.getParameter("cb"))); URL thisUrl = new URL(request.getRequestURL().toString()); URL authorizedUrl = new URL(thisUrl, sb.toString()); - - return AuthSubUtil.getRequestUrl( - authorizedUrl.toExternalForm(), // execution continues at authorized on redirect - "https://docs.google.com/feeds https://spreadsheets.google.com/feeds https://www.google.com/fusiontables/api/query", - false, - true); + return authorizedUrl.toExternalForm(); } + static public String getTokenFromCode(ButterflyModule module, HttpServletRequest request) + throws MalformedURLException { + String redirectUrl = makeRedirectUrl(module, request); + StringBuffer fullUrlBuf = request.getRequestURL(); + if (request.getQueryString() != null) { + fullUrlBuf.append('?').append(request.getQueryString()); + } + AuthorizationCodeResponseUrl authResponse = + new AuthorizationCodeResponseUrl(fullUrlBuf.toString()); + // check for user-denied error + if (authResponse.getError() != null) { + // authorization denied... + } else { + // request access token using authResponse.getCode()... + String code = authResponse.getCode(); + try { + GoogleTokenResponse response = new GoogleAuthorizationCodeTokenRequest(HTTP_TRANSPORT, + JSON_FACTORY, CLIENT_ID, CLIENT_SECRET, code, redirectUrl).execute(); + String token = response.getAccessToken(); + return token; + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + return null; + } + static public DocsService getDocsService(String token) { DocsService service = new DocsService(SERVICE_APP_NAME); diff --git a/extensions/gdata/src/com/google/refine/extension/gdata/TokenCookie.java b/extensions/gdata/src/com/google/refine/extension/gdata/TokenCookie.java index e8bd7951a..17cd18de7 100644 --- a/extensions/gdata/src/com/google/refine/extension/gdata/TokenCookie.java +++ b/extensions/gdata/src/com/google/refine/extension/gdata/TokenCookie.java @@ -8,7 +8,7 @@ import com.google.refine.util.CookiesUtilities; public class TokenCookie { - private static final String COOKIE_NAME = "authsub_token"; + private static final String COOKIE_NAME = "oauth2_token"; private static int MAX_AGE = 30 * 24 * 60 * 60; // 30 days public static String getToken(HttpServletRequest request) {