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

Source Code of com.ibm.sbt.opensocial.domino.oauth.DominoOAuth2TokenStore$OAuth2SpecInfo

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

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.apache.commons.lang3.StringUtils;
import org.apache.shindig.auth.SecurityToken;
import org.apache.shindig.common.crypto.BlobCrypter;
import org.apache.shindig.common.servlet.Authority;
import org.apache.shindig.common.uri.Uri;
import org.apache.shindig.gadgets.GadgetContext;
import org.apache.shindig.gadgets.GadgetException;
import org.apache.shindig.gadgets.GadgetException.Code;
import org.apache.shindig.gadgets.GadgetSpecFactory;
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.OAuth2GadgetContext;
import org.apache.shindig.gadgets.oauth2.OAuth2RequestException;
import org.apache.shindig.gadgets.oauth2.OAuth2Token;
import org.apache.shindig.gadgets.oauth2.persistence.OAuth2Encrypter;
import org.apache.shindig.gadgets.oauth2.persistence.OAuth2TokenPersistence;
import org.apache.shindig.gadgets.spec.BaseOAuthService.EndPoint;
import org.apache.shindig.gadgets.spec.GadgetSpec;
import org.apache.shindig.gadgets.spec.OAuth2Service;
import org.apache.shindig.gadgets.spec.OAuth2Spec;

import com.google.common.base.Joiner;
import com.ibm.sbt.opensocial.domino.container.ContainerExtPoint;
import com.ibm.sbt.opensocial.domino.container.ContainerExtPointException;
import com.ibm.sbt.opensocial.domino.container.ContainerExtPointManager;

/**
* Stores OAuth2 information.
*
*/
//TODO Right now every container is forced to let every gadget share OAuth tokens.  Some containers may not want that.  We should allow
//containers to specify that in their configuration and handle that here.
public class DominoOAuth2TokenStore {
  private static final String CLASS = DominoOAuth2TokenStore.class.getName();

  private Map<String, DominoOAuth2Accessor> accessorStore = Collections.synchronizedMap(new HashMap<String, DominoOAuth2Accessor>());
  private Map<String, OAuth2Token> accessTokenStore = Collections.synchronizedMap(new HashMap<String, OAuth2Token>());
  private Map<String, OAuth2Token> refreshTokenStore = Collections.synchronizedMap(new HashMap<String, OAuth2Token>());

  private static class OAuth2SpecInfo {
    private final String authorizationUrl;
    private final String scope;
    private final String tokenUrl;

    public OAuth2SpecInfo(final String authorizationUrl, final String tokenUrl, final String scope) {
      this.authorizationUrl = authorizationUrl;
      this.tokenUrl = tokenUrl;
      this.scope = scope;
    }

    public String getAuthorizationUrl() {
      return this.authorizationUrl;
    }

    public String getScope() {
      return this.scope;
    }

    public String getTokenUrl() {
      return this.tokenUrl;
    }
  }

  private ContainerExtPointManager manager;
  private Logger log;
  private GadgetSpecFactory specFactory;
  private OAuth2Encrypter encrypter;
  private Authority authority;
  private String contextRoot;
  private BlobCrypter stateCrypter;
  private String globalRedirectUri;
 
  /**
   * Creates a new DominoOAuth2TokenStore.
   * @param specFactory The spec factory containing the gadget specs.
   * @param manager The container extension point manager.
   * @param log The logger.
   * @param encrypter The OAuth 2.0 encyrpter.
   * @param authority The authority information.
   * @param contextRoot The servers context root.
   * @param stateCrypter The encrypter to use for state information.
   * @param globalRedirectUri The OAuth 2 callback URI.
   */
  public DominoOAuth2TokenStore(GadgetSpecFactory specFactory, ContainerExtPointManager manager,
      Logger log, OAuth2Encrypter encrypter,
      Authority authority, String contextRoot,
      BlobCrypter stateCrypter,
      String globalRedirectUri) {
    this.specFactory = specFactory;
    this.manager = manager;
    this.encrypter = encrypter;
    this.authority = authority;
    this.contextRoot = contextRoot;
    this.stateCrypter = stateCrypter;
    this.globalRedirectUri = globalRedirectUri;
    this.log = log;
  }

  /**
   * Gets an OAuth 2 accessor.  This method will actually merge OAuth 2 information from the gadget spec
   * if it is present and the OAuth 2 client allows it.
   * @param securityToken The security token containing information to lookup the accessor by.
   * @param arguments The OAuth 2 arguments.
   * @param gadgetUri The gadget URI.
   * @return An OAuth 2 accessor if one exists in the store.
   */
  public DominoOAuth2Accessor getOAuth2Accessor(SecurityToken securityToken, OAuth2Arguments arguments, Uri gadgetUri) {
    final String method = "getOAuth2Accessor";
    log.entering(CLASS, method,
        new Object[] { securityToken, arguments, gadgetUri });
    DominoOAuth2Accessor ret = null;
    if ((gadgetUri == null) || (securityToken == null)) {
      ret = new BasicDominoOAuth2Accessor();
      ret.setErrorResponse(null, OAuth2Error.GET_OAUTH2_ACCESSOR_PROBLEM,
          "OAuth2Accessor missing a param --- gadgetUri = "
              + gadgetUri + " , securityToken = " + securityToken, "");
    } else {
      final String serviceName = StringUtils.defaultString(arguments.getServiceName());
      try {
        ret = getOAuth2Accessor(gadgetUri, serviceName, securityToken, arguments);
        storeOAuth2Accessor(ret);
      } catch (Exception e) {
        log.logp(Level.WARNING, CLASS, method, "Error while getting OAuth 2 accessor.", e);
        ret = new BasicDominoOAuth2Accessor();
        ret.setErrorResponse(e, OAuth2Error.GET_OAUTH2_ACCESSOR_PROBLEM, "Error while getting OAuth 2 accessor", "");
      }
    }

    log.exiting(CLASS, method, ret);

    return ret;
  }
 
  /**
   * Gets the scope the OAuth2Arguments.  If the OAuth2Arguments does not have scope information than we will try and get it
   * from the gadget spec.  If the gadget spec does not have scope information than return the empty string.
   * @param arguments OAuth 2 arguments.
   * @param specInfo Gadget spec.
   * @return The OAuth 2 scope.
   */
  private String getScope(OAuth2Arguments arguments, OAuth2SpecInfo specInfo) {
    return StringUtils.isBlank(arguments.getScope()) ? StringUtils.isBlank(specInfo.getScope()) ? "" : specInfo.getScope() : arguments.getScope();
  }
 
  private DominoOAuth2Accessor getOAuth2Accessor(Uri gadgetUri, String serviceName, SecurityToken securityToken, OAuth2Arguments arguments) throws GadgetException, OAuth2RequestException {
    OAuth2SpecInfo specInfo = this.lookupSpecInfo(securityToken, arguments, gadgetUri);
    String scope = getScope(arguments, specInfo);
    String container = securityToken.getContainer();
    DominoOAuth2Accessor persistedAccessor = getOAuth2Accessor(gadgetUri.toString(), serviceName,
          securityToken.getViewerId(), scope, container);
    return addModuleOverrides(persistedAccessor, securityToken, arguments, gadgetUri, specInfo);
  }
 
  private DominoOAuth2Accessor addModuleOverrides(DominoOAuth2Accessor accessor, SecurityToken securityToken, OAuth2Arguments arguments, Uri gadgetUri, OAuth2SpecInfo specInfo)
      throws OAuth2RequestException {
    DominoOAuth2Accessor mergedAccessor = new BasicDominoOAuth2Accessor(accessor);
    mergedAccessor.setContainer(securityToken.getContainer());
    if (accessor.isAllowModuleOverrides()) {
      final String specAuthorizationUrl = specInfo.getAuthorizationUrl();
      final String specTokenUrl = specInfo.getTokenUrl();

      if (!StringUtils.isBlank(specAuthorizationUrl)) {
        mergedAccessor.setAuthorizationUrl(specAuthorizationUrl);
      }
      if (!StringUtils.isBlank(specTokenUrl)) {
        mergedAccessor.setTokenUrl(specTokenUrl);
      }
    }
    return mergedAccessor;
  }

  private OAuth2SpecInfo lookupSpecInfo(final SecurityToken securityToken,
      final OAuth2Arguments arguments, final Uri gadgetUri) throws OAuth2RequestException {
    log.entering(CLASS, "lookupSpecInfo", new Object[] { securityToken, arguments, gadgetUri });

    final GadgetSpec spec = this.findSpec(securityToken, arguments, gadgetUri);
    final OAuth2Spec oauthSpec = spec.getModulePrefs().getOAuth2Spec();
    if (oauthSpec == null) {
      throw new OAuth2RequestException(OAuth2Error.LOOKUP_SPEC_PROBLEM,
          "Failed to retrieve OAuth URLs, spec for gadget " + securityToken.getAppUrl()
          + " does not contain OAuth element.", null);
    }
    final OAuth2Service service = oauthSpec.getServices().get(arguments.getServiceName());
    if (service == null) {
      throw new OAuth2RequestException(OAuth2Error.LOOKUP_SPEC_PROBLEM,
          "Failed to retrieve OAuth URLs, spec for gadget does not contain OAuth service "
              + arguments.getServiceName() + ".  Known services: "
              + Joiner.on(',').join(oauthSpec.getServices().keySet()) + '.', null);
    }

    String authorizationUrl = null;
    final EndPoint authorizationUrlEndpoint = service.getAuthorizationUrl();
    if (authorizationUrlEndpoint != null) {
      authorizationUrl = authorizationUrlEndpoint.url.toString();
    }

    String tokenUrl = null;
    final EndPoint tokenUrlEndpoint = service.getTokenUrl();
    if (tokenUrlEndpoint != null) {
      tokenUrl = tokenUrlEndpoint.url.toString();
    }

    final OAuth2SpecInfo ret = new OAuth2SpecInfo(authorizationUrl, tokenUrl, service.getScope());

    log.exiting(CLASS, "lookupSpecInfo", ret);

    return ret;
  }

  private GadgetSpec findSpec(final SecurityToken securityToken, final OAuth2Arguments arguments,
      final Uri gadgetUri) throws OAuth2RequestException {
    final String method = "findSpec";
    log.entering(CLASS, method, new Object[] { arguments, gadgetUri });

    GadgetSpec ret;

    try {
      final GadgetContext context = new OAuth2GadgetContext(securityToken, arguments, gadgetUri);
      ret = this.specFactory.getGadgetSpec(context);
    } catch (final GadgetException e) {
      log.logp(Level.WARNING, CLASS, method, "Error finding GadgetContext " + gadgetUri.toString(), e);
      throw new OAuth2RequestException(OAuth2Error.GADGET_SPEC_PROBLEM, gadgetUri.toString(), e);
    }

    // this is cumbersome in the logs, just return whether or not it's null
    if (ret == null) {
      log.exiting(CLASS, method, null);
    } else {
      log.exiting(CLASS, method, "non-null spec omitted from logs");
    }

    return ret;
  }

  private DominoOAuth2Store getOAuth2Store(String container) throws GadgetException {
    final String method = "getOAuth2Store";
    ContainerExtPoint extPoint = manager.getExtPoint(container);
    if(extPoint != null) {
      try {
        return extPoint.getContainerOAuth2Store();
      } catch (ContainerExtPointException e) {
        log.logp(Level.WARNING, CLASS, method, "There was an error getting the OAuth2Store for container " + container, e);
        throw new GadgetException(GadgetException.Code.OAUTH_STORAGE_ERROR,
            "There was an error getting the OAuth2Store for container " + container, e);
      }
    } else {
      log.logp(Level.WARNING, CLASS, method, "There was no ContainerExtPoint for container {0}.", new Object[] {container});
      throw new GadgetException(GadgetException.Code.OAUTH_STORAGE_ERROR, "No ContainerExtPoint for container " + container);
    }
  }

  /**
   * Removes an OAuth 2 accessor from the store.
   * @param accessor The accessor to remove.
   * @throws GadgetException Thrown if there is an issue removing the accessor.
   */
  public void removeOAuth2Accessor(DominoOAuth2Accessor accessor) throws GadgetException {
    synchronized (accessorStore) {
      accessorStore.remove(generateKey(accessor))
    }
  }

  /**
   * Stores an OAuth 2 accessor in the store.
   * @param accessor The accessor to store.
   * @throws GadgetException Thrown if there is an issue storing the accessor.
   */
  public void storeOAuth2Accessor(DominoOAuth2Accessor accessor) throws GadgetException {
    synchronized (accessorStore) {
      accessorStore.put(generateKey(accessor), accessor)
    }
  }

  /**
   * Removes an OAuth 2 accessor from the store.
   * @param accessor The accessor to remove.
   * @throws GadgetException Thrown if there is an issue removing the accessor.
   */
  public void removeAccessToken(DominoOAuth2Accessor accessor) throws GadgetException {
    synchronized (accessTokenStore) {
      accessTokenStore.remove(generateKey(accessor));
    }
  }

  /**
   * Removes an OAuth 2 refresh token from the store.
   * @param accessor The accessor containing the refresh token to remove.
   * @throws GadgetException Thrown is there is an issue removing the refresh token.
   */
  public void removeRefreshToken(DominoOAuth2Accessor accessor) throws GadgetException {
    synchronized (refreshTokenStore) {
      refreshTokenStore.remove(generateKey(accessor));
    }
  }

  private String generateKey(DominoOAuth2Accessor accessor) throws GadgetException {
    return generateKey(accessor.getContainer(), accessor.getServiceName(), accessor.getScope(), accessor.getUser());
  }

  private String generateKey(DominoOAuth2CallbackState state) throws GadgetException {
    return generateKey(state.getContainer(), state.getServiceName(), state.getScope(), state.getUser());
  }

  private String generateKey(String container, OAuth2Token token) throws GadgetException {
    return generateKey(container, token.getServiceName(), token.getScope(), token.getUser());
  }

  private String generateKey(String container, String serviceName, String scope, String user) throws GadgetException {
    if(container == null || serviceName == null || user == null) {
      throw new GadgetException(GadgetException.Code.OAUTH_STORAGE_ERROR, "Invalid key parameters, container: " + container + " user: " + user + " service name: " + serviceName);
    }
    return container + ":" + serviceName + ":" + scope + ":" + user;
  }

  /**
   * Creates a new OAuth 2 token that can be used as a refresh or access token.
   * @return A new OAuth 2 token.
   */
  public OAuth2Token createToken() {
    return new OAuth2TokenPersistence(this.encrypter);
  }

  /**
   * Stores an OAuth 2 refresh token in the store.
   * @param container The container the refresh token belongs to.
   * @param token The token to store.
   * @throws GadgetException Thrown if there is an issue storing the refresh token.
   */
  public void storeRefreshToken(String container, OAuth2Token token) throws GadgetException {
    synchronized (refreshTokenStore) {
      refreshTokenStore.put(generateKey(container, token), token);
    }
  }

  /**
   * Stores an OAuth 2 access token in the store.
   * @param container The container the access token belongs to.
   * @param token The token to store.
   * @throws GadgetException Thrown if there is an issue storing the access token.
   */
  public void storeAccessToken(String container, OAuth2Token token) throws GadgetException {
    synchronized (accessTokenStore) {
      accessTokenStore.put(generateKey(container, token), token);
    }
  }


  /**
   * Gets an OAuth 2 accessor.  This method should only be called if you are sure the token is already in the store.
   * It will not consider any OAuth2 information from within the gadget.
   * @param state The OAuth 2 callback state information.
   * @return An OAuth 2 accessor.
   * @throws GadgetException Thrown if there is a problem retrieving the accessor.
   */
  public DominoOAuth2Accessor getOAuth2Accessor(DominoOAuth2CallbackState state) throws GadgetException {
    DominoOAuth2Accessor ret = accessorStore.get(generateKey(state));
    if (ret == null || !ret.isValid()) {
      final DominoOAuth2Client client = getClient(state);
      if (client == null) {
        throw new GadgetException(Code.OAUTH_STORAGE_ERROR,
            "Could not find OAuth2 client information where container = " + state.getContainer() +
            ", user = " + state.getUser() + ", serviceName = " + state.getServiceName() + ", and scope = " + state.getScope() + ".");
      } else {
        ret = createAccessor(state, client);
        this.storeOAuth2Accessor(ret);
      }
    }
    return ret;
  }
 
  private DominoOAuth2Client getClient(DominoOAuth2CallbackState state) throws GadgetException {
    return getClient(state.getUser(), state.getServiceName(), state.getContainer(),
        state.getScope(), state.getGadgetUri());
  }
 
  private DominoOAuth2Client getClient(String user, String serviceName, String container, String scope,
      String gadgetUri) throws GadgetException {
    final String method = "getClient";
    DominoOAuth2Store store = getOAuth2Store(container);
    if(store == null) {
      log.logp(Level.WARNING, CLASS, method,
          "Could not find an OAuth2 store for the container {0}, returning an error accessor.",
          new Object[]{container});
      return null;
    }
    final DominoOAuth2Client client = store.getClient(user, serviceName, container, scope, gadgetUri);

    if (client == null) {
      log.logp(Level.WARNING, CLASS, method,
          "Could not find OAuth2 client information where container = {0}, user = {1}, serviceName = {2}, and scope = {3}.",
          new Object[]{container, user, serviceName, scope});
      return null;
    }
    return client;
  }
 
  private DominoOAuth2Accessor createAccessor(DominoOAuth2CallbackState state, DominoOAuth2Client client)
      throws GadgetException {
    return createAccessor(state.getGadgetUri(), state.getServiceName(), state.getUser(), state.getScope(), state.getContainer(),
        client);
  }
 
  private DominoOAuth2Accessor createAccessor(String gadgetUri, String serviceName, String user, String scope, String container,
      DominoOAuth2Client client) throws GadgetException {
    final OAuth2Token accessToken = this.getAccessToken(gadgetUri, serviceName, user, scope, container);
    final OAuth2Token refreshToken = this.getRefreshToken(gadgetUri, serviceName, user, scope, container);
    String authType = client.getClientAuthenticationType() == null ? null : client.getClientAuthenticationType().toString();
    final BasicDominoOAuth2Accessor newAccessor = new BasicDominoOAuth2Accessor(gadgetUri, serviceName,
        user, scope, client.isAllowModuleOverride(), this.stateCrypter, this.globalRedirectUri,
        this.authority, this.contextRoot, container);
    newAccessor.setAccessToken(accessToken);
    newAccessor.setAuthorizationUrl(client.getAuthorizationUrl());
    newAccessor.setClientAuthenticationType(authType);
    newAccessor.setAuthorizationHeader(client.useAuthorizationHeader());
    newAccessor.setUrlParameter(client.useUrlParameter());
    newAccessor.setClientId(client.getClientId());
    newAccessor.setClientSecret(client.getClientSecret().getBytes());
    newAccessor.setGrantType(client.getGrantType().toString());
    newAccessor.setRefreshToken(refreshToken);
    newAccessor.setTokenUrl(client.getTokenUrl());
    //TODO do we want to support other types?
    newAccessor.setType(OAuth2Accessor.Type.CONFIDENTIAL);
    newAccessor.setAllowedDomains(new String[0]);
    return newAccessor;
  }
 
  /**
   * Gets an OAuth 2 access token.
   * @param gadgetUri The gadget URI.
   * @param serviceName The OAuth 2 service name.
   * @param user The user.
   * @param scope The OAuth 2 scope for the service.
   * @param container The container.
   * @return An OAuth 2 access token.
   * @throws GadgetException Thrown if there is a problem retrieving the access token.
   */
  public OAuth2Token getAccessToken(String gadgetUri, String serviceName, String user, String scope, String container) throws GadgetException {
    return accessTokenStore.get(generateKey(container, serviceName, scope, user));
  }
 
  /**
   * Gets an OAuth 2 refresh token.
   * @param gadgetUri The gadget URI.
   * @param serviceName The OAuth 2 service name.
   * @param user The user.
   * @param scope The OAuth 2 scope for the service.
   * @param container The container.
   * @return An OAuth 2 refresh token.
   * @throws GadgetException Thrown if there is a problem retrieving the refresh token.
   */
  public OAuth2Token getRefreshToken(String gadgetUri, String serviceName, String user, String scope, String container) throws GadgetException {
    return refreshTokenStore.get(generateKey(container, serviceName, scope, user));
  }

  private DominoOAuth2Accessor getOAuth2Accessor(String gadgetUri, String serviceName,
      String user, String scope, String container) throws GadgetException {
    final DominoOAuth2CallbackState state = new DominoOAuth2CallbackState(this.stateCrypter);
    state.setGadgetUri(gadgetUri);
    state.setServiceName(serviceName);
    state.setUser(user);
    state.setScope(scope);
    state.setContainer(container);
    return getOAuth2Accessor(state);
  }

}
TOP

Related Classes of com.ibm.sbt.opensocial.domino.oauth.DominoOAuth2TokenStore$OAuth2SpecInfo

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.