package org.fluxtream.connectors.controllers; import net.sf.json.JSONObject; import org.fluxtream.core.Configuration; import org.fluxtream.core.aspects.FlxLogger; import org.fluxtream.core.connectors.updaters.UpdateFailedException; import org.fluxtream.core.domain.ApiKey; import org.fluxtream.core.domain.Notification; import org.fluxtream.core.services.GuestService; import org.fluxtream.core.services.NotificationsService; import org.fluxtream.core.utils.HttpUtils; import org.fluxtream.core.utils.UnexpectedHttpResponseCodeException; import org.fluxtream.core.utils.Utils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import java.io.IOException; import java.util.HashMap; import java.util.Map; /** * * @author Candide Kemmler (candide@fluxtream.com) */ @Component public class GoogleOAuth2Helper { @Autowired GuestService guestService; @Autowired protected NotificationsService notificationsService; @Autowired Configuration env; FlxLogger logger = FlxLogger.getLogger(GoogleOAuth2Helper.class); public String getAccessToken(final ApiKey apiKey) throws IOException, UnexpectedHttpResponseCodeException, UpdateFailedException { final String expiresString = guestService.getApiKeyAttribute(apiKey, "tokenExpires"); long expires = Long.valueOf(expiresString); if (expires<System.currentTimeMillis()) refreshToken(apiKey); return guestService.getApiKeyAttribute(apiKey, "accessToken"); } private void refreshToken(final ApiKey apiKey) throws IOException, UnexpectedHttpResponseCodeException, UpdateFailedException { // Check to see if we are running on a mirrored test instance // and should therefore refrain from swapping tokens lest we // invalidate an existing token instance String disableTokenSwap = env.get("disableTokenSwap"); if(disableTokenSwap!=null && disableTokenSwap.equals("true")) { String msg = "**** Skipping refreshToken for google latitude connector instance because disableTokenSwap is set on this server"; ; StringBuilder sb2 = new StringBuilder("module=GoogleOAuth2Helper component=GoogleOAuth2Helper action=replaceToken apiKeyId=" + apiKey.getId()) .append(" message=\"").append(msg).append("\""); logger.info(sb2.toString()); System.out.println(msg); // Notify the user that the tokens need to be manually renewed notificationsService.addNotification(apiKey.getGuestId(), Notification.Type.WARNING, "Heads Up. This server cannot automatically refresh your authentication tokens.<br>" + "Please head to <a href=\"javascript:App.manageConnectors()\">Manage Connectors</a>,<br>" + "scroll to the " + apiKey.getConnector().getName() + " connector, and renew your tokens (look for the <i class=\"icon-resize-small icon-large\"></i> icon)"); // 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); throw new UpdateFailedException("requires token reauthorization", true, ApiKey.PermanentFailReason.NEEDS_REAUTH); } // We're not on a mirrored test server. Try to swap the expired // access token for a fresh one. String swapTokenUrl = "https://accounts.google.com/o/oauth2/token"; final String refreshToken = guestService.getApiKeyAttribute(apiKey, "refreshToken"); Map<String,String> params = new HashMap<String,String>(); params.put("refresh_token", refreshToken); params.put("client_id", env.get("google.client.id")); params.put("client_secret", env.get("google.client.secret")); params.put("grant_type", "refresh_token"); String fetched; try { fetched = HttpUtils.fetch(swapTokenUrl, params); logger.info("component=background_updates action=refreshToken" + " connector=" + apiKey.getConnector().getName() + " guestId=" + apiKey.getGuestId() + " status=success"); // Record that this connector is now up guestService.setApiKeyStatus(apiKey.getId(), ApiKey.Status.STATUS_UP, null, null); } catch (IOException e) { logger.warn("component=background_updates action=refreshToken" + " connector=" + apiKey.getConnector().getName() + " guestId=" + apiKey.getGuestId() + " status=failed"); // Notify the user that the tokens need to be manually renewed notificationsService.addNotification(apiKey.getGuestId(), Notification.Type.WARNING, "Heads Up. We failed in our attempt to automatically refresh your authentication tokens.<br>" + "Please head to <a href=\"javascript:App.manageConnectors()\">Manage Connectors</a>,<br>" + "scroll to the " + apiKey.getConnector().getName() + " connector, and renew your tokens (look for the <i class=\"icon-resize-small icon-large\"></i> icon)"); // Record permanent update failure since this connector is never // going to succeed guestService.setApiKeyStatus(apiKey.getId(), ApiKey.Status.STATUS_PERMANENT_FAILURE, Utils.stackTrace(e), ApiKey.PermanentFailReason.NEEDS_REAUTH); throw new UpdateFailedException("refresh token attempt failed", e, true, ApiKey.PermanentFailReason.NEEDS_REAUTH); } JSONObject token = JSONObject.fromObject(fetched); final long expiresIn = token.getLong("expires_in"); final String access_token = token.getString("access_token"); final long now = System.currentTimeMillis(); long tokenExpires = now + (expiresIn*1000); guestService.setApiKeyAttribute(apiKey, "accessToken", access_token); guestService.setApiKeyAttribute(apiKey, "tokenExpires", String.valueOf(tokenExpires)); } }