Package com.ibm.sbt.opensocial.domino.oauth

Source Code of com.ibm.sbt.opensocial.domino.oauth.DominoOAuth2Request

package com.ibm.sbt.opensocial.domino.oauth;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.apache.shindig.auth.AnonymousSecurityToken;
import org.apache.shindig.auth.SecurityToken;
import org.apache.shindig.common.uri.Uri;
import org.apache.shindig.gadgets.GadgetException;
import org.apache.shindig.gadgets.http.HttpFetcher;
import org.apache.shindig.gadgets.http.HttpRequest;
import org.apache.shindig.gadgets.http.HttpResponse;
import org.apache.shindig.gadgets.http.HttpResponseBuilder;
import org.apache.shindig.gadgets.oauth2.OAuth2Accessor;
import org.apache.shindig.gadgets.oauth2.OAuth2Arguments;
import org.apache.shindig.gadgets.oauth2.OAuth2Error;
import org.apache.shindig.gadgets.oauth2.OAuth2Message;
import org.apache.shindig.gadgets.oauth2.OAuth2Request;
import org.apache.shindig.gadgets.oauth2.OAuth2RequestException;
import org.apache.shindig.gadgets.oauth2.OAuth2RequestParameterGenerator;
import org.apache.shindig.gadgets.oauth2.OAuth2ResponseParams;
import org.apache.shindig.gadgets.oauth2.OAuth2Token;
import org.apache.shindig.gadgets.oauth2.OAuth2Utils;
import org.apache.shindig.gadgets.oauth2.handler.AuthorizationEndpointResponseHandler;
import org.apache.shindig.gadgets.oauth2.handler.ClientAuthenticationHandler;
import org.apache.shindig.gadgets.oauth2.handler.GrantRequestHandler;
import org.apache.shindig.gadgets.oauth2.handler.OAuth2HandlerError;
import org.apache.shindig.gadgets.oauth2.handler.ResourceRequestHandler;
import org.apache.shindig.gadgets.oauth2.handler.TokenEndpointResponseHandler;

import com.google.common.collect.Maps;
import com.google.inject.Inject;
import com.google.inject.name.Named;

public class DominoOAuth2Request implements OAuth2Request {
  private static final String CLASS = DominoOAuth2Request.class.getName();

  private static final short MAX_ATTEMPTS = 3;

  private DominoOAuth2Accessor internalAccessor;

  private OAuth2Arguments arguments;

  private final List<AuthorizationEndpointResponseHandler> authorizationEndpointResponseHandlers;

  private final List<ClientAuthenticationHandler> clientAuthenticationHandlers;

  private final HttpFetcher fetcher;

  private final List<GrantRequestHandler> grantRequestHandlers;

  private HttpRequest realRequest;

  private final List<ResourceRequestHandler> resourceRequestHandlers;

  private OAuth2ResponseParams responseParams;

  private SecurityToken securityToken;

  private final DominoOAuth2TokenStore tokenStore;

  private final List<TokenEndpointResponseHandler> tokenEndpointResponseHandlers;

  private final boolean sendTraceToClient;

  private final OAuth2RequestParameterGenerator requestParameterGenerator;

  private short attemptCounter = 0;

  private final boolean viewerAccessTokensEnabled;

  private final Logger log;

  /**
   * @param fetcherConfig
   *          configuration options for the fetcher
   * @param fetcher
   *          fetcher to use for actually making requests
   */
  @Inject
  public DominoOAuth2Request(final DominoOAuth2TokenStore tokenStore, final HttpFetcher fetcher,
      final List<AuthorizationEndpointResponseHandler> authorizationEndpointResponseHandlers,
      final List<ClientAuthenticationHandler> clientAuthenticationHandlers,
      final List<GrantRequestHandler> grantRequestHandlers,
      final List<ResourceRequestHandler> resourceRequestHandlers,
      final List<TokenEndpointResponseHandler> tokenEndpointResponseHandlers,
      final boolean sendTraceToClient,
      final OAuth2RequestParameterGenerator requestParameterGenerator,
      @Named("shindig.oauth2.viewer-access-tokens-enabled") final boolean viewerAccessTokensEnabled,
      Logger log) {
    this.viewerAccessTokensEnabled = viewerAccessTokensEnabled;
    this.log = log;
    this.tokenStore = tokenStore;
    this.fetcher = fetcher;
    this.authorizationEndpointResponseHandlers = authorizationEndpointResponseHandlers;
    this.clientAuthenticationHandlers = clientAuthenticationHandlers;
    this.grantRequestHandlers = grantRequestHandlers;
    this.resourceRequestHandlers = resourceRequestHandlers;
    this.tokenEndpointResponseHandlers = tokenEndpointResponseHandlers;
    this.sendTraceToClient = sendTraceToClient;
    this.requestParameterGenerator = requestParameterGenerator;
  }

  public HttpResponse fetch(final HttpRequest request) {
    final String method = "fetch";
    log.entering(DominoOAuth2Request.CLASS, method, request);

    DominoOAuth2Accessor accessor = null;

    HttpResponse response = null;
    this.responseParams = new OAuth2ResponseParams();

    try {
      // First step is to get an OAuth2Accessor for this request
      if (request == null || request.getSecurityToken() == null) {
        // Any errors before we have an accessor are special cases
        response = this.sendErrorResponse(null, OAuth2Error.MISSING_FETCH_PARAMS,
            "no request or security token");
      } else {
        this.realRequest = request;
        this.securityToken = request.getSecurityToken();
        if(securityToken.isAnonymous() || "@anonymous".equals(securityToken.getViewerId())) {
          return this.sendErrorResponse(null, OAuth2Error.GET_OAUTH2_ACCESSOR_PROBLEM, "Anonymous users cannot use OAuth 2 within gadgets.");
        }
       
        this.arguments = this.realRequest.getOAuth2Arguments();

        if (this.arguments == null) {
          // Any errors before we have an accessor are special cases
          return this.sendErrorResponse(null, OAuth2Error.FETCH_INIT_PROBLEM,
              "no responseParams or arguments");
        }

        accessor = this.getAccessor();

        if (accessor == null) {
          // Any errors before we have an accessor are special cases
          response = this.sendErrorResponse(null, OAuth2Error.FETCH_INIT_PROBLEM,
              "accessor is null");
        } else {
          accessor.setRedirecting(false);

          final Map<String, String> requestParams = this.requestParameterGenerator
              .generateParams(this.realRequest);
          accessor.setAdditionalRequestParams(requestParams);

          HttpResponseBuilder responseBuilder = null;
          if (!accessor.isErrorResponse()) {
            responseBuilder = this.attemptFetch(accessor);
          }

          response = this.processResponse(accessor, responseBuilder);
        }
      }
    } catch (final Throwable t) {
      log.logp(Level.WARNING, CLASS, method, "exception occurred during fetch", t);
      if (accessor == null) {
        accessor = new BasicDominoOAuth2Accessor();
        accessor.setErrorResponse(t, OAuth2Error.FETCH_PROBLEM, "exception occurred during fetch", "");
      } else {
        accessor.setErrorResponse(t, OAuth2Error.FETCH_PROBLEM, "exception occurred during fetch",
            "");
      }
      response = this.processResponse(accessor, this.getErrorResponseBuilder(t,
          OAuth2Error.FETCH_PROBLEM, "exception occurred during fetch"));
    } finally {
      if (accessor != null) {
        if (!accessor.isRedirecting()) {
          if (log.isLoggable(Level.FINEST)) {
            log.logp(Level.FINEST, CLASS, method, "accessor is not redirecting, remove it", accessor);
          }
          accessor.invalidate();
          try {
            this.tokenStore.removeOAuth2Accessor(accessor);
          } catch (GadgetException e) {
            log.logp(Level.WARNING, CLASS, method, "Error removing OAuth2Accessor from store.", e);
          }
          this.internalAccessor = null;
        } else {
          if (!accessor.isValid()) {
            if (log.isLoggable(Level.FINEST)) {
              log.logp(Level.FINEST, CLASS, method, "accesssor is not valid", accessor);
            }
          } else if (accessor.isErrorResponse()) {
            if (log.isLoggable(Level.FINEST)) {
              log.logp(Level.FINEST, CLASS, method, "accessor isErrorResponse",
                  accessor.getErrorContextMessage());
            }
          }
          try {
            this.tokenStore.storeOAuth2Accessor(accessor);
          } catch (GadgetException e) {
            log.logp(Level.WARNING, CLASS, method, "Error removing OAuth2 accessor from store.");
          }
        }
      }
    }

    log.exiting(CLASS, method, response);
    return response;
  }

  private HttpResponseBuilder attemptFetch(final DominoOAuth2Accessor accessor) {
    final String method = "attemptFetch";
    log.entering(CLASS, method, accessor);
    if (this.attemptCounter > DominoOAuth2Request.MAX_ATTEMPTS) {
      if (log.isLoggable(Level.FINEST)) {
        log.logp(Level.FINEST, CLASS, method, "MAX_ATTEMPTS exceeded {0}", this.attemptCounter);
        // This can be useful to diagnose the recursion
        final StackTraceElement[] stackElements = Thread.currentThread().getStackTrace();
        String stack = "";
        for (final StackTraceElement element : stackElements) {
          stack = stack + element.toString() + "\n";
        }
        log.logp(Level.FINEST, CLASS, method, "MAX_ATTEMPTS stack = {0}", stack);
      }
      return this.fetchData(accessor, true);
    }

    this.attemptCounter++;

    if (log.isLoggable(Level.FINEST)) {
      log.logp(Level.FINEST, CLASS, method, "attempt number {0}", this.attemptCounter);
    }

    HttpResponseBuilder ret = null;

    if (accessor.isErrorResponse()) {
      // If there's an error in the accessor don't continue.
      return this.getErrorResponseBuilder(accessor.getErrorException(), accessor.getError(),
          accessor.getErrorContextMessage(), accessor.getErrorUri(), accessor.getErrorContextMessage());
    }
   
    if(!DominoOAuth2Request.haveAccessToken(accessor) && !DominoOAuth2Request.haveRefreshToken(accessor)) {
      if (!accessor.isRedirecting() && this.checkCanAuthorize(accessor)) {
        final String completeAuthUrl = this.authorize(accessor);
        if (completeAuthUrl != null) {
          // Send a response to redirect to the authorization url
          this.responseParams.setAuthorizationUrl(completeAuthUrl);
          accessor.setRedirecting(true);
        } else {
          // This wasn't a redirect type of authorization. try again
          ret = this.attemptFetch(accessor);
        }
      }
    }
   
    if (DominoOAuth2Request.haveAccessToken(accessor)) {
      // We have an access_token, use it and stop!
      // Don't try more than three times
      ret = this.fetchData(accessor, this.attemptCounter > DominoOAuth2Request.MAX_ATTEMPTS);
    } else if (DominoOAuth2Request.haveRefreshToken(accessor)) {
      // We don't have an access token, we need to try and get one.
      // First step see if we have a refresh token
      ret = refreshAccessToken(accessor);
    }


    if (ret == null) {
      if (accessor.isRedirecting()) {
        // Send redirect response to client
        ret = new HttpResponseBuilder().setHttpStatusCode(HttpResponse.SC_OK).setStrictNoCache();
      } else {
        accessor.setAccessToken(null);
        ret = this.attemptFetch(accessor);
      }
    }


    log.exiting(CLASS, method, ret);

    return ret;
  }
 
 
  protected HttpResponseBuilder refreshAccessToken(DominoOAuth2Accessor accessor) {
    final String method = "refreshAccessToken";
    HttpResponseBuilder ret = null;
    boolean attempt = false;
    final String internedAccessor = getAccessorKey(accessor).intern();
    // This syncrhonized block is less than ideal.
    // It is needed because if a gadget has multiple makeRequests that triggers
    // multiple refreshes they can end up clobbering each other, and cause
    // temporary failures until the gadget is refreshed.
    // Syncrhonizing on the internedAccessor helps.  It is not cluster safe
    // and could be problematic having so much code synchd.
    // TODO : https://issues.apache.org/jira/browse/SHINDIG-1871
    synchronized (internedAccessor) {
      final OAuth2Accessor acc = this.getAccessorInternal();
      if (DominoOAuth2Request.haveAccessToken(acc)) {
        // Another refresh must have won
        if (log.isLoggable(Level.FINEST)) {
          log.logp(Level.FINEST, CLASS, method, "found an access token from another refresh",
              new Object[] {});
        }
        attempt = true;
      } else {
        final OAuth2HandlerError handlerError = this.refreshToken(accessor);
        if (handlerError == null) {
          // No errors refreshing, attempt the fetch again.
          attempt = true;
          if (log.isLoggable(Level.FINEST)) {
            log.logp(Level.FINEST, CLASS, method, "no refresh errors reported");
          }
        } else {
          if (log.isLoggable(Level.FINEST)) {
            log.logp(Level.FINEST, CLASS, method, "refresh errors reported");
          }
          // There was an error refreshing, stop.
          final OAuth2Error error = handlerError.getError();
          ret = this.getErrorResponseBuilder(handlerError.getCause(), error,
              handlerError.getContextMessage(), handlerError.getUri(),
              handlerError.getDescription());
        }
      }
    }
    if (attempt) {
      if (log.isLoggable(Level.FINEST)) {
        log.logp(Level.FINEST, CLASS, method, "going to re-attempt with a clean accesor");
      }
      try {
        this.tokenStore.removeOAuth2Accessor(this.internalAccessor);
      } catch (GadgetException e) {
        log.logp(Level.WARNING, CLASS, method, "Error while removing accessor.", e);
      }
      this.internalAccessor = null;
      ret = this.attemptFetch(this.getAccessor());
    }
    return ret;
  }

  private String authorize(final OAuth2Accessor accessor) {
    final String method = "authorize";
    log.entering(CLASS, method, accessor);

    String ret = null;

    final String grantType = accessor.getGrantType();

    GrantRequestHandler grantRequestHandlerUsed = null;
    for (final GrantRequestHandler grantRequestHandler : this.grantRequestHandlers) {
      if (grantRequestHandler.getGrantType().equalsIgnoreCase(grantType)) {
        grantRequestHandlerUsed = grantRequestHandler;
        break;
      }
    }

    if (grantRequestHandlerUsed == null) {
      accessor.setErrorResponse(null, OAuth2Error.AUTHENTICATION_PROBLEM,
          "no grantRequestHandler found for " + grantType, "");
    } else {
      String completeAuthUrl = null;
      try {
        completeAuthUrl = grantRequestHandlerUsed.getCompleteUrl(accessor);
      } catch (final OAuth2RequestException e) {
        log.logp(Level.WARNING, CLASS, method, "error getting complete url", e);
      }
      if (grantRequestHandlerUsed.isRedirectRequired()) {
        ret = completeAuthUrl;
      } else {
        final OAuth2HandlerError error = this.authorize(accessor, grantRequestHandlerUsed,
            completeAuthUrl);
        if (error != null) {
          accessor.setErrorResponse(error.getCause(), OAuth2Error.AUTHENTICATION_PROBLEM,
              error.getContextMessage() + " , " + error.getDescription(), error.getUri());
        }
      }
    }
    log.exiting(CLASS, method, ret);
    return ret;
  }

  private OAuth2HandlerError authorize(final OAuth2Accessor accessor,
      final GrantRequestHandler grantRequestHandler, final String completeAuthUrl) {
    final String method = "authorize";
    log.entering(CLASS, method, new Object[] {
          accessor, grantRequestHandler, completeAuthUrl });
     
    OAuth2HandlerError ret = null;

    HttpRequest authorizationRequest;
    try {
      authorizationRequest = grantRequestHandler.getAuthorizationRequest(accessor, completeAuthUrl);
    } catch (final OAuth2RequestException e) {
      authorizationRequest = null;
      ret = new OAuth2HandlerError(e.getError(), e.getErrorText(), e);
    }

    if (authorizationRequest != null) {
      HttpResponse authorizationResponse;
      try {
        authorizationResponse = this.fetcher.fetch(authorizationRequest);
      } catch (final GadgetException e) {
        log.logp(Level.WARNING, CLASS, method, "Exception while making authorizating request", e);
        authorizationResponse = null;
        ret = new OAuth2HandlerError(OAuth2Error.AUTHORIZE_PROBLEM,
            "exception thrown fetching authorization", e);
      }

      if (authorizationResponse != null) {
        if (grantRequestHandler.isAuthorizationEndpointResponse()) {
          for (final AuthorizationEndpointResponseHandler authorizationEndpointResponseHandler : this.authorizationEndpointResponseHandlers) {
            if (authorizationEndpointResponseHandler.handlesResponse(accessor,
                authorizationResponse)) {
              ret = authorizationEndpointResponseHandler.handleResponse(accessor,
                  authorizationResponse);
              if (ret != null) {
                // error occurred stop processing
                break;
              }
            }
          }
        }

        if (ret == null) {
          if (grantRequestHandler.isTokenEndpointResponse()) {
            for (final TokenEndpointResponseHandler tokenEndpointResponseHandler : this.tokenEndpointResponseHandlers) {
              if (tokenEndpointResponseHandler.handlesResponse(accessor, authorizationResponse)) {
                ret = tokenEndpointResponseHandler.handleResponse(accessor, authorizationResponse);
                if (ret != null) {
                  // error occurred stop processing
                  break;
                }
              }
            }
          }
        }
      }
    }
    log.exiting(CLASS, method, ret);
    return ret;
  }

  private String buildRefreshTokenUrl(final OAuth2Accessor accessor) {
    final String method = "buildRefreshTokenUrl";
    log.entering(CLASS, method, accessor);

    String ret = null;

    final String refreshUrl = accessor.getTokenUrl();
    if (refreshUrl != null) {
      ret = DominoOAuth2Request.getCompleteRefreshUrl(refreshUrl);
    }
    log.exiting(CLASS, method, ret);
    return ret;
  }

  private boolean checkCanAuthorize(final OAuth2Accessor accessor) {
    final String method = "checkCanAuthorize";
    log.entering(CLASS, method, accessor);
    boolean ret = true;
    final String pageOwner = this.securityToken.getOwnerId();
    final String pageViewer = this.securityToken.getViewerId();

    if (pageOwner == null || pageViewer == null) {
      accessor.setErrorResponse(null, OAuth2Error.AUTHORIZE_PROBLEM,
          "pageOwner or pageViewer is null", "");
      ret = false;
    } else if (!this.viewerAccessTokensEnabled && !pageOwner.equals(pageViewer)) {
      accessor.setErrorResponse(null, OAuth2Error.AUTHORIZE_PROBLEM, "pageViewer is not pageOwner",
          "");
      ret = false;
    }
    log.exiting(CLASS, method, ret);

    return ret;
  }

  private HttpResponseBuilder fetchData(final DominoOAuth2Accessor accessor, final boolean lastAttempt) {
    final String method = "fetchData";
    log.entering(CLASS, method, accessor);
    HttpResponseBuilder ret = null;
    try {
      final HttpResponse response = this.fetchFromServer(accessor, this.realRequest, lastAttempt);
      if (response != null) {
        ret = new HttpResponseBuilder(response);

        if (response.getHttpStatusCode() != HttpResponse.SC_OK && this.sendTraceToClient) {
          this.responseParams.addRequestTrace(this.realRequest, response);
        }
      }
    } catch (final OAuth2RequestException e) {
      ret = this.getErrorResponseBuilder(e, e.getError(), e.getErrorText(), e.getErrorUri(),
          e.getErrorDescription());
    }
    log.exiting(CLASS, method, ret);
    return ret;
  }

  private HttpResponse fetchFromServer(final DominoOAuth2Accessor accessor, final HttpRequest request,
      final boolean lastAttempt) throws OAuth2RequestException {
    final String method = "fetchFromServer";
    log.entering(CLASS, method, new Object[] { accessor, lastAttempt });
    HttpResponse ret;
    final long currentTime = System.currentTimeMillis();
    OAuth2Token accessToken = accessor.getAccessToken();
    if (accessToken != null) {
      final long expiresAt = accessToken.getExpiresAt();
      if (expiresAt != 0) {
        if (currentTime >= expiresAt) {
          if (log.isLoggable(Level.FINEST)) {
            log.logp(Level.FINEST, CLASS, method, "accessToken has expired at {0}", new Object[]{expiresAt});
          }
          try {
            this.tokenStore.removeAccessToken(accessor);
          } catch (final GadgetException e) {
            throw new OAuth2RequestException(OAuth2Error.MISSING_SERVER_RESPONSE,
                "error removing access_token", null);
          }
          accessToken = null;
          accessor.setAccessToken(null);
          if (!lastAttempt) {
            return null;
          }
        }
      }
    }

    OAuth2Token refreshToken = accessor.getRefreshToken();
    if (refreshToken != null) {
      final long expiresAt = refreshToken.getExpiresAt();
      if (expiresAt != 0) {
        if (currentTime >= expiresAt) {
          if (log.isLoggable(Level.FINEST)) {
            log.logp(Level.FINEST, CLASS, method, "refreshToken has expired at {0}", new Object[]{expiresAt});
          }
          try {
            this.tokenStore.removeRefreshToken(accessor);
          } catch (final GadgetException e) {
            throw new OAuth2RequestException(OAuth2Error.MISSING_SERVER_RESPONSE,
                "error removing refresh_token", null);
          }
          refreshToken = null;
          accessor.setRefreshToken(null);
          if (!lastAttempt) {
            return null;
          }
        }
      }
    }

    if (accessToken != null) {
      final boolean isAllowed = isUriAllowed(request.getUri(), accessor.getAllowedDomains());
      if (isAllowed) {
        String tokenType = accessToken.getTokenType();
        if (tokenType == null || tokenType.length() == 0) {
          tokenType = OAuth2Message.BEARER_TOKEN_TYPE;
        }

        for (final ResourceRequestHandler resourceRequestHandler : this.resourceRequestHandlers) {
          if (tokenType.equalsIgnoreCase(resourceRequestHandler.getTokenType())) {
            resourceRequestHandler.addOAuth2Params(accessor, request);
          }
        }
      } else {
        log.logp(Level.WARNING, CLASS, method,
            "Gadget {0} attempted to send OAuth2 Token to an unauthorized domain: {1}.",
            new Object[] { accessor.getGadgetUri(), request.getUri() });
        throw new OAuth2RequestException(OAuth2Error.SERVER_REJECTED_REQUEST,
            "The accessor is not allowed to be sent to the domain of the request.", null);
      }
    }

    try {
      ret = this.fetcher.fetch(request);
    } catch (final GadgetException e) {
      throw new OAuth2RequestException(OAuth2Error.MISSING_SERVER_RESPONSE,
          "GadgetException fetchFromServer", e);
    }

    final int responseCode = ret.getHttpStatusCode();

    if (log.isLoggable(Level.FINEST)) {
      log.logp(Level.FINEST, CLASS, method, "responseCode = {0}", new Object[]{responseCode});
    }

    if (responseCode == HttpResponse.SC_UNAUTHORIZED) {
      if (accessToken != null) {
        try {
          this.tokenStore.removeAccessToken(accessor);
        } catch (final GadgetException e) {
          throw new OAuth2RequestException(OAuth2Error.MISSING_SERVER_RESPONSE,
              "error removing access_token", null);
        }
        accessor.setAccessToken(null);
      }

      if (!lastAttempt) {
        ret = null;
      }
    }
    log.exiting(CLASS, method, ret);
    return ret;
  }

  private DominoOAuth2Accessor getAccessorInternal() {
    DominoOAuth2Accessor ret = tokenStore.getOAuth2Accessor(this.securityToken, this.arguments,
          this.realRequest.getGadget());
    return ret;
  }

  private DominoOAuth2Accessor getAccessor() {
    if (this.internalAccessor == null || !this.internalAccessor.isValid()) {
      this.internalAccessor = this.getAccessorInternal();
    }

    return this.internalAccessor;
  }

  private static String getCompleteRefreshUrl(final String refreshUrl) {
    return OAuth2Utils.buildUrl(refreshUrl, null, null);
  }

  private HttpResponseBuilder getErrorResponseBuilder(final Throwable t, final OAuth2Error error,
      final String contextMessage) {
    return getErrorResponseBuilder(t, error, contextMessage, null, null);
  }

  private HttpResponseBuilder getErrorResponseBuilder(final Throwable t, final OAuth2Error error,
      final String contextMessage, final String errorUri, final String errorDescription) {
    final String method = "getErrorResponseBuilder";
    log.entering(CLASS, method, new Object[] { t, error, contextMessage, errorUri, errorDescription });

    final HttpResponseBuilder ret = new HttpResponseBuilder().setHttpStatusCode(
        HttpResponse.SC_FORBIDDEN).setStrictNoCache();

    if (t != null && this.sendTraceToClient) {
      final StringWriter sw = new StringWriter();
      t.printStackTrace(new PrintWriter(sw));
      final String message = sw.toString();
      this.responseParams.addDebug(message);
    }

    if (this.sendTraceToClient) {
      this.responseParams.addToResponse(ret, error.getErrorCode(),
          error.getErrorDescription(contextMessage) + " , " + errorDescription, errorUri,
          error.getErrorExplanation());
    } else {
      this.responseParams.addToResponse(ret, error.getErrorCode(), "", "",
          error.getErrorExplanation());
    }
    log.exiting(CLASS, method, ret);
    return ret;
  }

  private String getRefreshBody(final OAuth2Accessor accessor) {
    final String method = "getRefreshBody";
    log.entering(CLASS, method, accessor);

    String ret = "";

    Map<String, String> queryParams;
    try {
      queryParams = Maps.newHashMap();
      queryParams.put(OAuth2Message.GRANT_TYPE, OAuth2Message.REFRESH_TOKEN);
      queryParams.put(OAuth2Message.REFRESH_TOKEN, new String(accessor.getRefreshToken()
          .getSecret(), "UTF-8"));
      if (accessor.getScope() != null && accessor.getScope().length() > 0) {
        queryParams.put(OAuth2Message.SCOPE, accessor.getScope());
      }

      final String clientId = accessor.getClientId();
      final byte[] secret = accessor.getClientSecret();
      queryParams.put(OAuth2Message.CLIENT_ID, clientId);
      queryParams.put(OAuth2Message.CLIENT_SECRET, new String(secret, "UTF-8"));

      ret = OAuth2Utils.buildUrl(ret, queryParams, null);

      final char firstChar = ret.charAt(0);
      if (firstChar == '?' || firstChar == '&') {
        ret = ret.substring(1);
      }
    } catch (final UnsupportedEncodingException e) {
      log.logp(Level.WARNING, CLASS, method, "error generating refresh body", e);
        ret = null;
    }
    log.exiting(CLASS, method, ret);
    return ret;
  }

  private HttpResponse processResponse(final OAuth2Accessor accessor,
      final HttpResponseBuilder responseBuilder) {
    final String method = "processResponse";
    log.entering(CLASS, method,new Object[] { accessor, responseBuilder });

    if (accessor.isErrorResponse() || responseBuilder == null) {
      return this.sendErrorResponse(accessor.getErrorException(), accessor.getError(),
          accessor.getErrorContextMessage(), accessor.getErrorUri(), "");
    }

    if (this.responseParams.getAuthorizationUrl() != null) {
      responseBuilder.setMetadata(OAuth2ResponseParams.APPROVAL_URL,
          this.responseParams.getAuthorizationUrl());
      accessor.setRedirecting(true);
    } else {
      accessor.setRedirecting(false);
    }

    final HttpResponse ret = responseBuilder.create();
    log.exiting(CLASS, method);
    return ret;
  }

  private OAuth2HandlerError refreshToken(final DominoOAuth2Accessor accessor) {
    final String method = "refreshToken";
    log.entering(CLASS, method, new Object[] { accessor });
    OAuth2HandlerError ret = null;
    String refershTokenUrl;
    refershTokenUrl = buildRefreshTokenUrl(accessor);

    if (log.isLoggable(Level.FINEST)) {
      log.logp(Level.FINEST, CLASS, method, "refershTokenUrl = {0}", new Object[]{refershTokenUrl});
    }

    if (refershTokenUrl != null) {
      HttpResponse response = null;
      final HttpRequest request = new HttpRequest(Uri.parse(refershTokenUrl));
      request.setSecurityToken(new AnonymousSecurityToken("", 0L, accessor.getGadgetUri()));
      request.setMethod("POST");
      request.setHeader("Content-Type", "application/x-www-form-urlencoded; charset=utf-8");

      for (final ClientAuthenticationHandler clientAuthenticationHandler : this.clientAuthenticationHandlers) {
        if (clientAuthenticationHandler.geClientAuthenticationType().equalsIgnoreCase(
            accessor.getClientAuthenticationType())) {
          clientAuthenticationHandler.addOAuth2Authentication(request, accessor);
        }
      }

      try {
        final byte[] body = getRefreshBody(accessor).getBytes("UTF-8");
        request.setPostBody(body);
      } catch (final Exception e) {
        log.logp(Level.WARNING, CLASS, method, "Error while getting body for refresh request.", e);
        ret = new OAuth2HandlerError(OAuth2Error.REFRESH_TOKEN_PROBLEM,
            "error generating refresh body", e);
      }

      if (!isUriAllowed(request.getUri(), accessor.getAllowedDomains())) {
        ret = new OAuth2HandlerError(OAuth2Error.REFRESH_TOKEN_PROBLEM,
            "error fetching refresh token - domain not allowed", null);
      }

      if (ret == null) {
        try {
          response = this.fetcher.fetch(request);
        } catch (final GadgetException e) {
          log.logp(Level.WARNING, CLASS, method, "Error while making refresh request.", e);
          ret = new OAuth2HandlerError(OAuth2Error.REFRESH_TOKEN_PROBLEM,
              "error fetching refresh token", e);
        }

        if (log.isLoggable(Level.FINEST)) {
          log.logp(Level.FINEST, CLASS, method, "response = {0}", new Object[] {response});
        }

        if (ret == null) {
          // response is not null..
          final int statusCode = response.getHttpStatusCode();
          if (statusCode == HttpResponse.SC_UNAUTHORIZED
              || statusCode == HttpResponse.SC_BAD_REQUEST) {
            try {
              this.tokenStore.removeRefreshToken(accessor);
            } catch (final GadgetException e) {
              ret = new OAuth2HandlerError(OAuth2Error.REFRESH_TOKEN_PROBLEM,
                  "failed to remove refresh token", e);
            }
            accessor.setRefreshToken(null);
            if (log.isLoggable(Level.FINEST)) {
              log.logp(Level.FINEST, CLASS, method,
                  "received {0} from provider, removed refresh token.  response = {1}",
                  new Object[] { statusCode, response.getResponseAsString() });
            }
            return ret;
          } else if (statusCode != HttpResponse.SC_OK) {
            ret = new OAuth2HandlerError(OAuth2Error.REFRESH_TOKEN_PROBLEM,
                "bad response from server : " + statusCode, null, "",
                response.getResponseAsString());
          }

          if (ret == null) {
            for (final TokenEndpointResponseHandler tokenEndpointResponseHandler : this.tokenEndpointResponseHandlers) {
              if (tokenEndpointResponseHandler.handlesResponse(accessor, response)) {
                final OAuth2HandlerError error = tokenEndpointResponseHandler.handleResponse(
                    accessor, response);
                if (error != null) {
                  try {
                    this.tokenStore.removeRefreshToken(accessor);
                  } catch (GadgetException e) {
                    log.logp(Level.WARNING, CLASS, method,
                        "There was an error removing the refresh token after an error occured processing the token refresh response.", e);
                  }
                  accessor.setRefreshToken(null);
                  return error;
                }
              }
            }
          }
        }
      }
    }
    log.exiting(CLASS, method, ret);
    return ret;
  }

  private HttpResponse sendErrorResponse(final Throwable t, final OAuth2Error error,
      final String contextMessage) {
    final HttpResponseBuilder responseBuilder = this.getErrorResponseBuilder(t, error,
        contextMessage);
    return responseBuilder.create();
  }

  private HttpResponse sendErrorResponse(final Throwable t, final OAuth2Error error,
      final String contextMessage, final String errorUri, final String errorDescription) {
    final HttpResponseBuilder responseBuilder = this.getErrorResponseBuilder(t, error,
        contextMessage, errorUri, errorDescription);
    return responseBuilder.create();
  }

  private static boolean haveAccessToken(final OAuth2Accessor accessor) {
    OAuth2Token token = accessor.getAccessToken();
    return token != null && DominoOAuth2Request.validateAccessToken(token);
  }

  private static boolean haveRefreshToken(final OAuth2Accessor accessor) {
    OAuth2Token token = accessor.getRefreshToken();
    return token != null && DominoOAuth2Request.validateRefreshToken(token);
  }

  private static boolean isUriAllowed(final Uri uri, final String[] allowedDomains) {
    if (allowedDomains == null || allowedDomains.length == 0) {
      // if white list is not specified, allow client to access any domain
      return true;
    }
    String host = uri.getAuthority();
    final int pos = host.indexOf(':');
    if (pos != -1) {
      host = host.substring(0, pos);
    }
    for (String domain : allowedDomains) {
      if (domain != null) {
        domain = domain.trim();
        if (domain.startsWith(".") && host.endsWith(domain) || domain.equals(host)) {
          return true;
        }
      }
    }
    return false;
  }

  private static boolean validateAccessToken(final OAuth2Token accessToken) {
    return accessToken != null;
  }

  private static boolean validateRefreshToken(final OAuth2Token refreshToken) {
    return refreshToken != null;
  }

  private static String getAccessorKey(final OAuth2Accessor accessor) {
    if (accessor != null) {
      return "accessor:" + accessor.getGadgetUri() + ':' + accessor.getServiceName() + ':'
          + accessor.getUser() + ':' + accessor.getScope();
    }

    return null;
  }

}
TOP

Related Classes of com.ibm.sbt.opensocial.domino.oauth.DominoOAuth2Request

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.