Migrate to OAuth2
- implement OAuth2 for both Spreadsheets & Fusion Tables - switch cookie name from authsub_token to oauth2_token so there's no confusion - implementation is otherwise unchanged - in particular still uses old Fusion Tables SQL API
This commit is contained in:
parent
d7081415d4
commit
bf2f775c0b
@ -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 html = "text/html";
|
||||||
var encoding = "UTF-8";
|
var encoding = "UTF-8";
|
||||||
var version = "0.2";
|
var version = "0.2";
|
||||||
@ -101,21 +108,13 @@ function process(path, request, response) {
|
|||||||
} else if (path == "authorized") {
|
} else if (path == "authorized") {
|
||||||
var context = {};
|
var context = {};
|
||||||
context.winname = request.getParameter("winname");
|
context.winname = request.getParameter("winname");
|
||||||
context.callback = request.getParameter("callback");
|
context.callback = request.getParameter("cb");
|
||||||
|
|
||||||
(function() {
|
(function() {
|
||||||
var queryString = request.getQueryString();
|
var token = Packages.com.google.refine.extension.gdata.GDataExtension.getTokenFromCode(module,request);
|
||||||
if (queryString != null) {
|
if (token) {
|
||||||
var AuthSubUtil = Packages.com.google.gdata.client.http.AuthSubUtil;
|
Packages.com.google.refine.extension.gdata.TokenCookie.setToken(request, response, token);
|
||||||
|
return;
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Packages.com.google.refine.extension.gdata.TokenCookie.deleteToken(request, response);
|
Packages.com.google.refine.extension.gdata.TokenCookie.deleteToken(request, response);
|
||||||
})();
|
})();
|
||||||
|
@ -34,7 +34,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||||||
var GdataExtension = {};
|
var GdataExtension = {};
|
||||||
|
|
||||||
GdataExtension.isAuthorized = function() {
|
GdataExtension.isAuthorized = function() {
|
||||||
return $.cookie('authsub_token') !== null;
|
return $.cookie('oauth2_token') !== null;
|
||||||
};
|
};
|
||||||
|
|
||||||
GdataExtension.showAuthorizationDialog = function(onAuthorized, onNotAuthorized) {
|
GdataExtension.showAuthorizationDialog = function(onAuthorized, onNotAuthorized) {
|
||||||
@ -57,6 +57,6 @@ GdataExtension.showAuthorizationDialog = function(onAuthorized, onNotAuthorized)
|
|||||||
};
|
};
|
||||||
window[callbackName] = callback;
|
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");
|
var win = window.open(url, "openrefinegdataauth", "resizable=1,width=800,height=600");
|
||||||
};
|
};
|
||||||
|
@ -34,12 +34,18 @@ import javax.servlet.ServletException;
|
|||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
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;
|
import com.google.refine.commands.Command;
|
||||||
|
|
||||||
public class DeAuthorizeCommand extends Command {
|
public class DeAuthorizeCommand extends Command {
|
||||||
|
|
||||||
|
private static final HttpTransport HTTP_TRANSPORT = new NetHttpTransport();
|
||||||
@Override
|
@Override
|
||||||
public void doGet(HttpServletRequest request, HttpServletResponse response)
|
public void doGet(HttpServletRequest request, HttpServletResponse response)
|
||||||
throws ServletException, IOException {
|
throws ServletException, IOException {
|
||||||
@ -50,7 +56,16 @@ public class DeAuthorizeCommand extends Command {
|
|||||||
|
|
||||||
String sessionToken = TokenCookie.getToken(request);
|
String sessionToken = TokenCookie.getToken(request);
|
||||||
if (sessionToken != null) {
|
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);
|
TokenCookie.deleteToken(request, response);
|
||||||
}
|
}
|
||||||
respond(response, "200 OK", "");
|
respond(response, "200 OK", "");
|
||||||
|
@ -30,7 +30,6 @@ package com.google.refine.extension.gdata;
|
|||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.OutputStreamWriter;
|
import java.io.OutputStreamWriter;
|
||||||
import java.net.MalformedURLException;
|
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.net.URLEncoder;
|
import java.net.URLEncoder;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@ -39,19 +38,12 @@ import java.util.Scanner;
|
|||||||
import java.util.regex.MatchResult;
|
import java.util.regex.MatchResult;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
|
||||||
|
|
||||||
import com.google.gdata.client.GoogleService;
|
import com.google.gdata.client.GoogleService;
|
||||||
import com.google.gdata.client.Service.GDataRequest;
|
import com.google.gdata.client.Service.GDataRequest;
|
||||||
import com.google.gdata.client.Service.GDataRequest.RequestType;
|
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.ContentType;
|
||||||
import com.google.gdata.util.ServiceException;
|
import com.google.gdata.util.ServiceException;
|
||||||
|
|
||||||
import com.google.refine.util.ParsingUtilities;
|
|
||||||
|
|
||||||
import edu.mit.simile.butterfly.ButterflyModule;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Tom Morris <tfmorris@gmail.com>
|
* @author Tom Morris <tfmorris@gmail.com>
|
||||||
* @copyright 2010,2013 Thomas F. Morris
|
* @copyright 2010,2013 Thomas F. Morris
|
||||||
@ -61,41 +53,22 @@ public class FusionTableHandler {
|
|||||||
|
|
||||||
final static private String FUSION_TABLES_SERVICE_URL =
|
final static private String FUSION_TABLES_SERVICE_URL =
|
||||||
"https://www.google.com/fusiontables/api/query";
|
"https://www.google.com/fusiontables/api/query";
|
||||||
|
// "https://www.googleapis.com/fusiontables/v1/query";
|
||||||
|
|
||||||
final static private Pattern CSV_VALUE_PATTERN =
|
final static private Pattern CSV_VALUE_PATTERN =
|
||||||
Pattern.compile("([^,\\r\\n\"]*|\"(([^\"]*\"\")*[^\"]*)\")(,|\\r?\\n)");
|
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(
|
static public GDataRequest createFusionTablesPostRequest(
|
||||||
GoogleService service, RequestType requestType, String query)
|
GoogleService service, RequestType requestType, String query)
|
||||||
throws IOException, ServiceException {
|
throws IOException, ServiceException {
|
||||||
|
|
||||||
URL url = new URL(FUSION_TABLES_SERVICE_URL);
|
URL url = new URL(FUSION_TABLES_SERVICE_URL);
|
||||||
GDataRequest request = service.getRequestFactory().getRequest(
|
GDataRequest request = service.getRequestFactory().getRequest(
|
||||||
requestType, url, new ContentType("application/x-www-form-urlencoded"));
|
requestType, url, new ContentType("application/x-www-form-urlencoded"));
|
||||||
|
|
||||||
OutputStreamWriter writer =
|
OutputStreamWriter writer =
|
||||||
new OutputStreamWriter(request.getRequestStream());
|
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.flush();
|
||||||
writer.close();
|
writer.close();
|
||||||
|
|
||||||
@ -106,7 +79,7 @@ public class FusionTableHandler {
|
|||||||
GoogleService service, RequestType requestType, String query)
|
GoogleService service, RequestType requestType, String query)
|
||||||
throws IOException, ServiceException {
|
throws IOException, ServiceException {
|
||||||
URL url = new URL(FUSION_TABLES_SERVICE_URL + "?sql=" +
|
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(
|
return service.getRequestFactory().getRequest(
|
||||||
requestType, url, ContentType.TEXT_PLAIN);
|
requestType, url, ContentType.TEXT_PLAIN);
|
||||||
}
|
}
|
||||||
|
@ -28,13 +28,22 @@
|
|||||||
*/
|
*/
|
||||||
package com.google.refine.extension.gdata;
|
package com.google.refine.extension.gdata;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
import java.net.MalformedURLException;
|
import java.net.MalformedURLException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
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.docs.DocsService;
|
||||||
import com.google.gdata.client.http.AuthSubUtil;
|
|
||||||
import com.google.gdata.client.spreadsheet.SpreadsheetService;
|
import com.google.gdata.client.spreadsheet.SpreadsheetService;
|
||||||
|
|
||||||
import com.google.refine.util.ParsingUtilities;
|
import com.google.refine.util.ParsingUtilities;
|
||||||
@ -48,28 +57,72 @@ import edu.mit.simile.butterfly.ButterflyModule;
|
|||||||
*/
|
*/
|
||||||
abstract public class GDataExtension {
|
abstract public class GDataExtension {
|
||||||
static final String SERVICE_APP_NAME = "OpenRefine-GData-Extension";
|
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)
|
static public String getAuthorizationUrl(ButterflyModule module, HttpServletRequest request)
|
||||||
throws MalformedURLException {
|
throws MalformedURLException {
|
||||||
char[] mountPointChars = module.getMountPoint().getMountPoint().toCharArray();
|
String authorizedUrl = makeRedirectUrl(module, request);
|
||||||
|
|
||||||
StringBuffer sb = new StringBuffer();
|
// New Oauth2
|
||||||
sb.append(mountPointChars, 0, mountPointChars.length);
|
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("authorized?winname=");
|
||||||
sb.append(ParsingUtilities.encode(request.getParameter("winname")));
|
sb.append(ParsingUtilities.encode(request.getParameter("winname")));
|
||||||
sb.append("&callback=");
|
sb.append("&cb=");
|
||||||
sb.append(ParsingUtilities.encode(request.getParameter("callback")));
|
sb.append(ParsingUtilities.encode(request.getParameter("cb")));
|
||||||
|
|
||||||
URL thisUrl = new URL(request.getRequestURL().toString());
|
URL thisUrl = new URL(request.getRequestURL().toString());
|
||||||
URL authorizedUrl = new URL(thisUrl, sb.toString());
|
URL authorizedUrl = new URL(thisUrl, sb.toString());
|
||||||
|
return authorizedUrl.toExternalForm();
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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) {
|
static public DocsService getDocsService(String token) {
|
||||||
DocsService service = new DocsService(SERVICE_APP_NAME);
|
DocsService service = new DocsService(SERVICE_APP_NAME);
|
||||||
|
@ -8,7 +8,7 @@ import com.google.refine.util.CookiesUtilities;
|
|||||||
|
|
||||||
public class TokenCookie {
|
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
|
private static int MAX_AGE = 30 * 24 * 60 * 60; // 30 days
|
||||||
|
|
||||||
public static String getToken(HttpServletRequest request) {
|
public static String getToken(HttpServletRequest request) {
|
||||||
|
Loading…
Reference in New Issue
Block a user