Package org.fluxtream.connectors.controllers

Source Code of org.fluxtream.connectors.controllers.GoogleOAuth2Controller

package org.fluxtream.connectors.controllers;

import net.sf.json.JSONObject;
import org.codehaus.plexus.util.ExceptionUtils;
import org.fluxtream.core.Configuration;
import org.fluxtream.core.aspects.FlxLogger;
import org.fluxtream.core.auth.AuthHelper;
import org.fluxtream.core.connectors.Connector;
import org.fluxtream.core.connectors.OAuth2Helper;
import org.fluxtream.core.domain.ApiKey;
import org.fluxtream.core.domain.Guest;
import org.fluxtream.core.domain.Notification;
import org.fluxtream.core.services.ConnectorUpdateService;
import org.fluxtream.core.services.GuestService;
import org.fluxtream.core.services.NotificationsService;
import org.fluxtream.core.services.SystemService;
import org.fluxtream.core.utils.HttpUtils;
import org.fluxtream.core.utils.UnexpectedHttpResponseCodeException;
import org.fluxtream.mvc.controllers.ErrorController;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Map;

@Controller
@RequestMapping(value = "/google/oauth2")
public class GoogleOAuth2Controller {

    FlxLogger logger = FlxLogger.getLogger(GoogleOAuth2Controller.class);

  @Autowired
  Configuration env;
 
  @Autowired
  SystemService systemService;
 
  @Autowired
  GuestService guestService;

    @Autowired
    NotificationsService notificationsService;

    @Autowired
    ConnectorUpdateService connectorUpdateService;

    @Autowired
    OAuth2Helper oAuth2Helper;

    @Autowired
    ErrorController errorController;

    private final static String APIKEYID_ATTRIBUTE = "google.oauth2.apiKeyId";

    @RequestMapping(value = "/{apiKeyId}/token")
    public String renewToken(@PathVariable("apiKeyId") String apiKeyId, HttpServletRequest request) throws IOException, ServletException {
        request.getSession().setAttribute(APIKEYID_ATTRIBUTE, apiKeyId);
        final ApiKey apiKey = guestService.getApiKey(Long.valueOf(apiKeyId));

        // Check if the stored client ID matches the one in our properties file, and only in that
        // case try to revoke.  TODO: in that case skip next two lines
        String propertiesClientId = env.get("google.client.id");
        String storedClientId = guestService.getApiKeyAttribute(apiKey,"google.client.id");

        if(propertiesClientId !=null && storedClientId!=null && propertiesClientId.equals(storedClientId)) {
            // Renewing token for the same server, do revoke attempt first
            final String refreshTokenRemoveURL = guestService.getApiKeyAttribute(apiKey,"refreshTokenRemoveURL");
            oAuth2Helper.revokeRefreshToken(apiKey.getGuestId(), apiKey.getConnector(), refreshTokenRemoveURL);

            String msg="Called revokeRefreshToken with refreshTokenRemoveURL='" + refreshTokenRemoveURL + "'";
            StringBuilder sb = new StringBuilder("module=GoogleOauth2Controller component=renewToken action=renewToken apiKeyId=" + apiKeyId)
                    .append(" guestId=").append(apiKey.getGuestId())
                    .append(" message=\"").append(msg).append("\"");
            logger.info(sb.toString());
        }
        else {
            String msg="Skipped revokeRefreshToken, stored and properties google.client.id do not match (" + storedClientId + " vs " + propertiesClientId + ")" ;
                        StringBuilder sb = new StringBuilder("module=GoogleOauth2Controller component=renewToken action=renewToken apiKeyId=" + apiKeyId)
                                .append(" guestId=").append(apiKey.getGuestId())
                                .append(" message=\"").append(msg).append("\"");
                        logger.info(sb.toString());
        }
        // Continue on to ask user for authorization whether or not you did a revoke
        return getToken(request);
    }

    @RequestMapping(value = "/token")
  public String getToken(HttpServletRequest request) throws IOException, ServletException{
   
    String scope = request.getParameter("scope");
    request.getSession().setAttribute("oauth2Scope", scope);
    String redirectUri = ControllerSupport.getLocationBase(request, env) + "google/oauth2/swapToken";
    String clientId = env.get("google.client.id");

    String authorizeUrl = "https://accounts.google.com/o/oauth2/auth?client_id=" + clientId +
      "&redirect_uri=" + redirectUri +
      "&scope=" + scope +
      "&response_type=code&" +
            "access_type=offline&" +
            "approval_prompt=force";
   
    return "redirect:" + authorizeUrl;
  }
 
  @RequestMapping(value = "/swapToken")
  public ModelAndView upgradeToken(HttpServletRequest request) throws IOException, UnexpectedHttpResponseCodeException {

        String scope = (String) request.getSession().getAttribute("oauth2Scope");
        Connector scopedApi = systemService.getApiFromGoogleScope(scope);

        Guest guest = AuthHelper.getGuest();

        String errorParameter = request.getParameter("error");
        if (errorParameter!=null) {
            notificationsService.addNamedNotification(guest.getId(), Notification.Type.WARNING,
                                                      scopedApi.statusNotificationName(),
                                                      "There was a problem importing your " + scopedApi.prettyName() + " data: " + errorParameter);
            return new ModelAndView("redirect:/app/");
        }

    String swapTokenUrl = "https://accounts.google.com/o/oauth2/token";
    String code = request.getParameter("code");
    String redirectUri = ControllerSupport.getLocationBase(request, env) + "google/oauth2/swapToken";
   
    Map<String,String> params = new HashMap<String,String>();
    params.put("code", code);
    params.put("client_id", env.get("google.client.id"));
    params.put("client_secret", env.get("google.client.secret"));
    params.put("redirect_uri", redirectUri);
    params.put("grant_type", "authorization_code");

      // Get the google branding info.  Default to fluxtream if not set, but can override in
      // oauth.properties by setting the default google.client.brandName parameter
      String brandName = env.get("google.client.brandName");
      if(brandName == null) {
         // Not set in oauth.properties file, default to "Fluxtream"
         brandName="Fluxtream";
      }

      // Try to renew the token.  On failure leave token=null
      JSONObject token = null;

      try {
         String fetched = HttpUtils.fetch(swapTokenUrl, params);
         token = JSONObject.fromObject(fetched);
      } catch(Throwable e) {
         token = null;
      }

        ApiKey apiKey;
        final boolean isRenewToken = request.getSession().getAttribute(APIKEYID_ATTRIBUTE) != null;
        if (isRenewToken) {
            String apiKeyId = (String)request.getSession().getAttribute(APIKEYID_ATTRIBUTE);
            apiKey = guestService.getApiKey(Long.valueOf(apiKeyId));
            if (apiKey==null) {
                Exception e = new Exception();
                String stackTrace = ExceptionUtils.getStackTrace(e);
                String errorMessage = "no apiKey with id '%s'... It looks like you are trying to renew the tokens of a non-existing Connector (/ApiKey)";
                return errorController.handleError(500, errorMessage, stackTrace);
            }

            if (token == null || !token.has("refresh_token")) {
                String message = (new StringBuilder("<p>We couldn't get your oauth2 refresh token.  "))
                        .append("Something went wrong.</p>")
                        .append("<p>You'll have to surf to your ")
                        .append("<a target='_new'  href='https://accounts.google.com/b/0/IssuedAuthSubTokens'>token mgmt page at Google</a> ")
                        .append("and hit \"Revoke Access\" next to \"").append(brandName).append(" — ").append(getGooglePrettyName(scopedApi)).append("\"</p>")
                      .append("<p>Then please, head to <a href=\"javascript:App.manageConnectors()\">Manage Connectors</a> ")
                      .append("and renew your tokens (look for the <i class=\"icon-resize-small icon-large\"></i> icon)</p>")
                        .append("<p>We apologize for the inconvenience</p>").toString();

                notificationsService.addNamedNotification(guest.getId(),
                                                     Notification.Type.ERROR,
                                                     apiKey.getConnector().statusNotificationName(),
                                                     message);
                // Record permanent failure since this connector won't work again until
                // it is reauthenticated
                guestService.setApiKeyStatus(apiKey.getId(), ApiKey.Status.STATUS_PERMANENT_FAILURE, null, ApiKey.PermanentFailReason.NEEDS_REAUTH);
                return new ModelAndView("redirect:/app");
            }

            // Remove oauth1 keys if upgrading from previous connector version.
            // Remember whether or not we're upgrading from previous connector version.
            // If so, do a full history update.  Otherwise don't force a full
            // history update and allow the update to be whatever it normally would be
            boolean upgradeFromOauth1 = false;

            if (guestService.getApiKeyAttribute(apiKey, "googleConsumerKey")!=null) {
                guestService.removeApiKeyAttribute(apiKey.getId(), "googleConsumerKey");
                upgradeFromOauth1 = true;
            }
            if (guestService.getApiKeyAttribute(apiKey, "googleConsumerSecret")!=null) {
                guestService.removeApiKeyAttribute(apiKey.getId(), "googleConsumerSecret");
                upgradeFromOauth1 = true;
            }

            // If upgradeFromOauth1 reset the connector to force a full reimport on google calendar,
            // otherwise just do a normal update
            if (apiKey.getConnector().getName().equals("google_calendar")) {
                connectorUpdateService.flushUpdateWorkerTasks(apiKey, upgradeFromOauth1);
            }

        } else {
            apiKey = guestService.createApiKey(guest.getId(), scopedApi);
        }

        // We need to store google.client.id and google.client.secret with the
        // apiKeyAttributes in either the case of original creation of the key
        // or token renewal.  createApiKey actually handles the former case, but
        // not the latter.  Do it in all cases here.
        guestService.setApiKeyAttribute(apiKey, "google.client.id", env.get("google.client.id"));
        guestService.setApiKeyAttribute(apiKey, "google.client.secret", env.get("google.client.secret"));

        final String refresh_token = token.getString("refresh_token");

        guestService.setApiKeyAttribute(apiKey,
        "accessToken", token.getString("access_token"));
    guestService.setApiKeyAttribute(apiKey,
        "tokenExpires", String.valueOf(System.currentTimeMillis() + (token.getLong("expires_in")*1000)));
        guestService.setApiKeyAttribute(apiKey,
        "refreshToken", refresh_token);
        final String encodedRefreshToken = URLEncoder.encode(refresh_token, "UTF-8");
        guestService.setApiKeyAttribute(apiKey,
                                        "refreshTokenRemoveURL",
                                        "https://accounts.google.com/o/oauth2/revoke?token="
                                        + encodedRefreshToken);

        // Record this connector as having status up
        guestService.setApiKeyStatus(apiKey.getId(), ApiKey.Status.STATUS_UP, null, null);
        // Schedule an update for this connector
        connectorUpdateService.updateConnector(apiKey, false);

        if (isRenewToken) {
            request.getSession().removeAttribute(APIKEYID_ATTRIBUTE);
            return new ModelAndView("redirect:/app/tokenRenewed/"+scopedApi.getName());
        }
        return new ModelAndView("redirect:/app/from/"+scopedApi.getName());
    }

    private Object getGooglePrettyName(final Connector scopedApi) {
        if (scopedApi.getName().equals("google_latitude"))
            return "Latitude";
        else if (scopedApi.getName().equals("google_calendar"))
            return "Google Calendar (or Google Agenda)";
        else {
            logger.warn("Please check Google's pretty name for API " + scopedApi.getName() + " (see GoogleOAuth2Controller.java)");
            return scopedApi.prettyName();
        }
    }
}
TOP

Related Classes of org.fluxtream.connectors.controllers.GoogleOAuth2Controller

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.