Permalink
Browse files

add OAuth2 support for using Google Storage

  • Loading branch information...
1 parent e912378 commit 02b8f3c9e4081a838cc8cc6a5e47c5d613357b78 @rgerganov committed Jul 3, 2011
@@ -2,6 +2,8 @@
import com.google.appengine.api.datastore.DatastoreService;
import com.google.appengine.api.datastore.DatastoreServiceFactory;
+import com.google.appengine.api.memcache.MemcacheService;
+import com.google.appengine.api.memcache.MemcacheServiceFactory;
import com.lz2zg.qsl.model.QslCard;
import com.lz2zg.qsl.model.QslCardDao;
import com.lz2zg.qsl.model.QslCardDaoImpl;
@@ -18,6 +20,9 @@ public void contextInitialized(ServletContextEvent sce) {
QslCardDao dao = new QslCardDaoImpl(datastore);
ServletContext context = sce.getServletContext();
context.setAttribute("dao", dao);
+ MemcacheService memcacheService = MemcacheServiceFactory.getMemcacheService();
+ OAuth2ClientImpl oauth2client = new OAuth2ClientImpl(datastore, memcacheService);
+ context.setAttribute("oauth2client", oauth2client);
//populateTestData(dao);
}
@@ -0,0 +1,34 @@
+package com.lz2zg.qsl.servlet;
+
+import java.io.IOException;
+import java.util.logging.Logger;
+
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+public class OAuth2CallbackServlet extends HttpServlet {
+
+ static final Logger log = Logger.getLogger(OAuth2CallbackServlet.class.getName());
+ private static final long serialVersionUID = 1L;
+ OAuth2Client oauth2client;
+
+ @Override
+ public void init(ServletConfig config) throws ServletException {
+ super.init(config);
+ ServletContext context = config.getServletContext();
+ oauth2client = (OAuth2Client) context.getAttribute("oauth2client");
+ }
+
+ @Override
+ protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+ String authCode = req.getParameter("code");
+ log.info("authCode: " + authCode);
+ oauth2client.fetchAccessAndRefreshTokens(authCode);
+ resp.setContentType("text/html");
+ resp.getWriter().println("<html><body>Authorization in Google Storage successful</body></html>");
+ }
+}
@@ -0,0 +1,21 @@
+package com.lz2zg.qsl.servlet;
+
+
+public interface OAuth2Client {
+
+ String CLIENT_ID = "348460834037.apps.googleusercontent.com";
+
+ // this is useless until coupled with refresh token,
+ // so it safe to be hardcoded as a constant
+ String CLIENT_SECRET = "11DrV8S8kCglvYrxlG5Gt61Y";
+
+ String REDIRECT_URI = "http://localhost:8080/admin/oauth2callback";
+
+ String getAccessToken();
+
+ String getAccessToken(String refreshToken);
+
+ void fetchAccessAndRefreshTokens(String authCode);
+
+ String getRefreshToken();
+}
@@ -0,0 +1,129 @@
+package com.lz2zg.qsl.servlet;
+
+import com.google.appengine.api.datastore.DatastoreService;
+import com.google.appengine.api.datastore.Entity;
+import com.google.appengine.api.datastore.EntityNotFoundException;
+import com.google.appengine.api.datastore.Key;
+import com.google.appengine.api.datastore.KeyFactory;
+import com.google.appengine.api.memcache.Expiration;
+import com.google.appengine.api.memcache.MemcacheService;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.net.URL;
+import java.net.URLConnection;
+import java.net.URLEncoder;
+import java.util.logging.Logger;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class OAuth2ClientImpl implements OAuth2Client {
+
+ static final Logger log = Logger.getLogger(OAuth2ClientImpl.class.getName());
+ static final String OAUTH_URL = "https://accounts.google.com/o/oauth2/token";
+ final DatastoreService datastore;
+ final MemcacheService memcacheService;
+ final static String ENC = "UTF-8";
+ final static String ACCESS_TOKEN_KEY = "accessToken";
+
+ public OAuth2ClientImpl(DatastoreService datastore, MemcacheService memcacheService) {
+ this.datastore = datastore;
+ this.memcacheService = memcacheService;
+ }
+
+ @Override
+ public String getAccessToken() {
+ return (String) memcacheService.get(ACCESS_TOKEN_KEY);
+ }
+
+ @Override
+ public String getAccessToken(String refreshToken) {
+ try {
+ String data = String.format("client_id=%s&client_secret=%s&refresh_token=%s&grant_type=refresh_token",
+ URLEncoder.encode(CLIENT_ID, ENC),
+ URLEncoder.encode(CLIENT_SECRET, ENC),
+ URLEncoder.encode(refreshToken, ENC));
+ String response = doPost(data);
+ log.info("Response after sending refreshToken: " + response);
+ String accessToken = parseStringField("access_token", response);
+ int expiresIn = parseIntField("expires_in", response);
+ memcacheService.put(ACCESS_TOKEN_KEY, accessToken, Expiration.byDeltaSeconds(expiresIn));
+ return accessToken;
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public void fetchAccessAndRefreshTokens(String authCode) {
+ try {
+ String data = String.format("code=%s&client_id=%s&client_secret=%s&redirect_uri=%s&grant_type=authorization_code",
+ URLEncoder.encode(authCode, ENC),
+ URLEncoder.encode(CLIENT_ID, ENC),
+ URLEncoder.encode(CLIENT_SECRET, ENC),
+ URLEncoder.encode(REDIRECT_URI, ENC)); // what is this for?
+ String response = doPost(data);
+ log.info("Response after sending authCode: " + response);
+ String accessToken = parseStringField("access_token", response);
+ String refreshToken = parseStringField("refresh_token", response);
+ int expiresIn = parseIntField("expires_in", response);
+ memcacheService.put(ACCESS_TOKEN_KEY, accessToken, Expiration.byDeltaSeconds(expiresIn));
+ saveRefreshToken(refreshToken);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ Integer parseIntField(String field, String text) {
+ Pattern p = Pattern.compile(String.format("\"%s\":(\\d+)", field));
+ Matcher m = p.matcher(text);
+ if (m.find()) {
+ return Integer.parseInt(m.group(1));
+ }
+ return null;
+ }
+
+ String parseStringField(String field, String text) {
+ Pattern p = Pattern.compile(String.format("\"%s\":\"(.+?)\"", field));
+ Matcher m = p.matcher(text);
+ if (m.find()) {
+ return m.group(1);
+ }
+ return null;
+ }
+
+ String doPost(String data) throws IOException {
+ URL url = new URL(OAUTH_URL);
+ URLConnection conn = url.openConnection();
+ conn.setDoOutput(true);
+ conn.getOutputStream().write(data.getBytes(ENC));
+ conn.getOutputStream().flush();
+ BufferedReader rd = new BufferedReader(new InputStreamReader(conn.getInputStream()));
+ String line = null;
+ String response = "";
+ while ((line = rd.readLine()) != null) {
+ response += line;
+ }
+ rd.close();
+ return response;
+ }
+
+ @Override
+ public String getRefreshToken() {
+ Key key = KeyFactory.createKey("refreshToken", "GoogleStorage");
+ try {
+ Entity entity = datastore.get(key);
+ return (String) entity.getProperty("value");
+ } catch (EntityNotFoundException e) {
+ return null;
+ }
+ }
+
+ void saveRefreshToken(String refreshToken) {
+ Key key = KeyFactory.createKey("refreshToken", "GoogleStorage");
+ Entity entity = new Entity(key);
+ entity.setProperty("value", refreshToken);
+ datastore.put(entity);
+ }
+}
@@ -0,0 +1,70 @@
+package com.lz2zg.qsl.servlet;
+
+import java.io.IOException;
+import java.net.URLEncoder;
+import java.util.logging.Logger;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+
+public class OAuth2Filter implements Filter {
+
+ static final Logger log = Logger.getLogger(OAuth2ClientImpl.class.getName());
+ final static String GS_SCOPE = "https://www.googleapis.com/auth/devstorage.read_write";
+ OAuth2Client oauth2client;
+
+ @Override
+ public void init(FilterConfig config) throws ServletException {
+ ServletContext context = config.getServletContext();
+ oauth2client = (OAuth2Client) context.getAttribute("oauth2client");
+ }
+
+ @Override
+ public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
+ HttpServletRequest req = (HttpServletRequest) request;
+ HttpServletResponse resp = (HttpServletResponse) response;
+ if (req.getRequestURL().toString().equals(OAuth2Client.REDIRECT_URI)) {
+ log.info("callback has been requested, pass forward");
+ chain.doFilter(request, response);
+ return;
+ }
+ String accessToken = oauth2client.getAccessToken();
+ if (accessToken != null) {
+ log.info("accessToken has been found");
+ req.setAttribute("accessToken", accessToken);
+ chain.doFilter(request, response);
+ } else {
+ String refreshToken = oauth2client.getRefreshToken();
+ if (refreshToken != null) {
+ log.info("getting accessToken using refreshToken");
+ accessToken = oauth2client.getAccessToken(refreshToken);
+ if (accessToken != null) {
+ req.setAttribute("accessToken", accessToken);
+ chain.doFilter(request, response);
+ } else {
+ resp.setStatus(401);
+ resp.setContentType("text/html");
+ resp.getWriter().println("<html><body>Not Authorized</body></html>");
+ }
+ } else {
+ log.info("refreshToken not found, redirect to get authCode");
+ resp.sendRedirect(String.format("https://accounts.google.com/o/oauth2/auth?client_id=%s&redirect_uri=%s&scope=%s&response_type=code",
+ OAuth2Client.CLIENT_ID,
+ URLEncoder.encode(OAuth2Client.REDIRECT_URI, "UTF-8"),
+ URLEncoder.encode(GS_SCOPE, "UTF-8")));
+ }
+ }
+ }
+
+ @Override
+ public void destroy() {
+ }
+}
@@ -0,0 +1,21 @@
+package com.lz2zg.qsl.servlet;
+
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+public class UploadServlet extends HttpServlet {
+
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+ String accessToken = (String) req.getAttribute("accessToken");
+ resp.setContentType("text/html");
+ resp.getWriter().println("Access token: " + accessToken);
+ }
+
+}
@@ -2,4 +2,7 @@
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
<application>qsl-lz2zg</application>
<version>1</version>
+ <system-properties>
+ <property name="java.util.logging.config.file" value="WEB-INF/logging.properties" />
+ </system-properties>
</appengine-web-app>
@@ -0,0 +1,13 @@
+# A default java.util.logging configuration.
+# (All App Engine logging is through java.util.logging by default).
+#
+# To use this configuration, copy it into your application's WEB-INF
+# folder and add the following to your appengine-web.xml:
+#
+# <system-properties>
+# <property name="java.util.logging.config.file" value="WEB-INF/logging.properties"/>
+# </system-properties>
+#
+
+# Set the default logging level for all loggers to WARNING
+.level = INFO
@@ -58,6 +58,17 @@
<servlet-name>EditCardServlet</servlet-name>
<url-pattern>/admin/edit</url-pattern>
</servlet-mapping>
+
+ <servlet>
+ <servlet-name>OAuth2CallbackServlet</servlet-name>
+ <servlet-class>com.lz2zg.qsl.servlet.OAuth2CallbackServlet</servlet-class>
+ </servlet>
+
+ <servlet-mapping>
+ <servlet-name>OAuth2CallbackServlet</servlet-name>
+ <url-pattern>/admin/oauth2callback</url-pattern>
+ </servlet-mapping>
+
<!--
<security-constraint>
<web-resource-collection>
@@ -78,6 +89,16 @@
<filter-name>AuthenticationFilter</filter-name>
<url-pattern>/admin/*</url-pattern>
</filter-mapping>
+
+ <filter>
+ <filter-name>OAuth2Filter</filter-name>
+ <filter-class>com.lz2zg.qsl.servlet.OAuth2Filter</filter-class>
+ </filter>
+
+ <filter-mapping>
+ <filter-name>OAuth2Filter</filter-name>
+ <url-pattern>/admin/*</url-pattern>
+ </filter-mapping>
<welcome-file-list>
<welcome-file>main</welcome-file>

0 comments on commit 02b8f3c

Please sign in to comment.