All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.api.server.spi.config.model.ApiConfig Maven / Gradle / Ivy

There is a newer version: 2.2.2
Show newest version
/*
 * Copyright 2016 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.google.api.server.spi.config.model;

import com.google.api.server.spi.Constant;
import com.google.api.server.spi.ServiceContext;
import com.google.api.server.spi.TypeLoader;
import com.google.api.server.spi.config.ApiConfigInconsistency;
import com.google.api.server.spi.config.AuthLevel;
import com.google.api.server.spi.config.Authenticator;
import com.google.api.server.spi.config.PeerAuthenticator;
import com.google.api.server.spi.config.scope.AuthScopeExpression;
import com.google.api.server.spi.config.scope.AuthScopeExpressions;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;

/**
 * Flattened configuration for a swarm endpoint.  Data generally originates from
 * {@link com.google.api.server.spi.config.Api} annotations.
 *
 * @author Eric Orth
 */
// TODO: Clean things up a bit by separating it into an immutable config object and a
// Builder, and merge the new vs copy creation by having the new just copy a default config.  Then
// efficientisize config reload make it so the builder only copies once an actual change is
// detected.
public class ApiConfig {
  // Default value of scopes and clientIds if unset
  private static final AuthScopeExpression DEFAULT_SCOPE_EXPRESSION =
      AuthScopeExpressions.interpret(Constant.API_EMAIL_SCOPE);
  private static final List DEFAULT_CLIENT_IDS =
      ImmutableList.of(Constant.API_EXPLORER_CLIENT_ID);

  private final TypeLoader typeLoader;

  private String root;
  private String name;
  private String canonicalName;
  private String version;
  private String title;
  private String description;
  private String documentationLink;
  private String backendRoot;
  private boolean isAbstract;
  private boolean defaultVersion;
  private boolean discoverable;

  private String resource;
  private boolean useDatastore;

  private AuthLevel authLevel;
  private AuthScopeExpression scopeExpression;
  private List audiences;
  private ApiIssuerConfigs issuers;
  private ApiIssuerAudienceConfig issuerAudiences;
  private List clientIds;
  private List> authenticators;
  private List> peerAuthenticators;
  private boolean apiKeyRequired;

  private final ApiAuthConfig authConfig;
  private final ApiCacheControlConfig cacheControlConfig;
  private final ApiFrontendLimitsConfig frontendLimitsConfig;
  private final ApiSerializationConfig serializationConfig;
  private final ApiNamespaceConfig namespaceConfig;

  private final ApiClassConfig apiClassConfig;

  /**
   * Simple factory to create {@link ApiConfig} instances.
   */
  public static class Factory {
    public ApiConfig create(ServiceContext serviceContext, TypeLoader typeLoader,
        Class endpointClass) {
      return new ApiConfig(serviceContext, typeLoader, endpointClass);
    }

    public ApiConfig copy(ApiConfig old) {
      return new ApiConfig(old);
    }
  }

  /**
   * Hidden constructor.  Instantiate using {@link Factory}.
   */
  protected ApiConfig(ServiceContext serviceContext, TypeLoader typeLoader,
      Class apiClass) {
    this.typeLoader = typeLoader;
    authConfig = createAuthConfig();
    cacheControlConfig = createCacheControlConfig();
    frontendLimitsConfig = createFrontendLimitsConfig();
    serializationConfig = createSerializationConfig();
    namespaceConfig = createNamespaceConfig();
    apiClassConfig = createApiClassConfig(typeLoader, apiClass);

    setDefaults(serviceContext);
  }

  /**
   * Hidden copy constructor.  Use {@link Factory}.
   */
  protected ApiConfig(ApiConfig original) {
    this.typeLoader = original.typeLoader;
    this.root = original.root;
    this.name = original.name;
    this.canonicalName = original.canonicalName;
    this.version = original.version;
    this.title = original.title;
    this.description = original.description;
    this.documentationLink = original.documentationLink;
    this.backendRoot = original.backendRoot;
    this.isAbstract = original.isAbstract;
    this.defaultVersion = original.defaultVersion;
    this.discoverable = original.discoverable;
    this.resource = original.resource;
    this.useDatastore = original.useDatastore;
    this.authLevel = original.authLevel;
    this.scopeExpression = original.scopeExpression;
    this.audiences = original.audiences == null ? null : new ArrayList<>(original.audiences);
    this.issuers = original.issuers;
    this.issuerAudiences = original.issuerAudiences;
    this.clientIds = original.clientIds == null ? null : new ArrayList<>(original.clientIds);
    this.authenticators = original.authenticators;
    this.peerAuthenticators = original.peerAuthenticators;
    this.apiKeyRequired = original.apiKeyRequired;
    this.authConfig = new ApiAuthConfig(original.authConfig);
    this.cacheControlConfig = new ApiCacheControlConfig(original.cacheControlConfig);
    this.frontendLimitsConfig = new ApiFrontendLimitsConfig(original.frontendLimitsConfig);
    this.serializationConfig = new ApiSerializationConfig(original.serializationConfig);
    this.namespaceConfig = new ApiNamespaceConfig(original.namespaceConfig);
    this.apiClassConfig = new ApiClassConfig(original.apiClassConfig, this);
  }

  @Override
  public boolean equals(Object o) {
    if (this == o) {
      return true;
    } else if (o instanceof ApiConfig) {
      ApiConfig config = (ApiConfig) o;
      return Iterables.isEmpty(getConfigurationInconsistencies(config))
          && apiClassConfig.equals(config.apiClassConfig);
    } else {
      return false;
    }
  }

  /**
   * @return {@code true} if all API-level (not class or method specific) configuration is
   * identical.
   */
  public Iterable> getConfigurationInconsistencies(
      ApiConfig config) {
    return ApiConfigInconsistency.listBuilder()
        .addIfInconsistent("typeLoader", typeLoader, config.typeLoader)
        .addIfInconsistent("root", root, config.root)
        .addIfInconsistent("name", name, config.name)
        .addIfInconsistent("cannonicalName", canonicalName, config.canonicalName)
        .addIfInconsistent("version", version, config.version)
        .addIfInconsistent("title", title, config.title)
        .addIfInconsistent("description", description, config.description)
        .addIfInconsistent("documentationLink", documentationLink, config.documentationLink)
        .addIfInconsistent("backendRoot", backendRoot, config.backendRoot)
        .addIfInconsistent("isAbstract", isAbstract, config.isAbstract)
        .addIfInconsistent("defaultVersion", defaultVersion, config.defaultVersion)
        .addIfInconsistent("discoverable", discoverable, config.discoverable)
        .addIfInconsistent("useDatastore", useDatastore, config.useDatastore)
        .addIfInconsistent("resource", resource, config.resource)
        .addIfInconsistent("authLevel", authLevel, config.authLevel)
        .addIfInconsistent("scopeExpression", scopeExpression, config.scopeExpression)
        .addIfInconsistent("audiences", audiences, config.audiences)
        .addIfInconsistent("issuers", issuers, config.issuers)
        .addIfInconsistent("issuerAudiencies", issuerAudiences, config.issuerAudiences)
        .addIfInconsistent("clientIds", clientIds, config.clientIds)
        .addIfInconsistent("authenticators", authenticators, config.authenticators)
        .addIfInconsistent("peerAuthenticators", peerAuthenticators, config.peerAuthenticators)
        .addIfInconsistent("apiKeyRequired", apiKeyRequired, config.apiKeyRequired)
        .addAll(authConfig.getConfigurationInconsistencies(config.authConfig))
        .addAll(cacheControlConfig.getConfigurationInconsistencies(config.cacheControlConfig))
        .addAll(frontendLimitsConfig.getConfigurationInconsistencies(config.frontendLimitsConfig))
        .addAll(serializationConfig.getConfigurationInconsistencies(config.serializationConfig))
        .addAll(namespaceConfig.getConfigurationInconsistencies(config.namespaceConfig))
        .build();
  }

  @Override
  public int hashCode() {
    return Objects.hash(typeLoader, root, name, canonicalName, version, title, description,
        documentationLink, backendRoot, isAbstract, defaultVersion, discoverable, useDatastore,
        resource, authLevel, scopeExpression, audiences, clientIds, authenticators,
        peerAuthenticators, authConfig, cacheControlConfig, frontendLimitsConfig,
        serializationConfig, apiClassConfig, issuers, issuerAudiences, apiKeyRequired);
  }

  /**
   * Creates the auth configuration object.  Override to use a subclass instead.
   */
  protected ApiAuthConfig createAuthConfig() {
    return new ApiAuthConfig();
  }

  /**
   * Creates the cache control configuration object.  Override to use a subclass instead.
   */
  protected ApiCacheControlConfig createCacheControlConfig() {
    return new ApiCacheControlConfig();
  }

  /**
   * Creates the frontend limits configuration object.  Override to use a subclass instead.
   */
  protected ApiFrontendLimitsConfig createFrontendLimitsConfig() {
    return new ApiFrontendLimitsConfig();
  }

  /**
   * Creates the serialization configuration object.  Override to use a subclass instead.
   */
  protected ApiSerializationConfig createSerializationConfig() {
    return new ApiSerializationConfig();
  }

  /**
   * Creates the namespace configuration object.  Override to use a subclass instead.
   */
  protected ApiNamespaceConfig createNamespaceConfig() {
    return new ApiNamespaceConfig();
  }

  protected ApiClassConfig createApiClassConfig(TypeLoader typeLoader, Class apiClass) {
    return new ApiClassConfig(this, typeLoader, apiClass);
  }

  /**
   * Sets all fields to their default value to be used if not set otherwise.  Override to change the
   * default configuration.
   */
  protected void setDefaults(ServiceContext serviceContext) {
    root =
        serviceContext.getTransferProtocol() + "://" + serviceContext.getAppHostName() + "/_ah/api";
    name = serviceContext.getDefaultApiName();
    canonicalName = null;
    version = "v1";
    description = null;
    backendRoot =
        serviceContext.getTransferProtocol() + "://" + serviceContext.getAppHostName() + "/_ah/spi";
    isAbstract = false;
    defaultVersion = false;
    discoverable = true;
    useDatastore = false;
    resource = null;

    authLevel = AuthLevel.NONE;
    scopeExpression = DEFAULT_SCOPE_EXPRESSION;
    audiences = Collections.emptyList();
    issuers = ApiIssuerConfigs.EMPTY;
    issuerAudiences = ApiIssuerAudienceConfig.EMPTY;
    clientIds = DEFAULT_CLIENT_IDS;
    authenticators = null;
    peerAuthenticators = null;
    apiKeyRequired = false;
  }

  public ApiKey getApiKey() {
    return new ApiKey(name, version, root);
  }

  public void setRoot(String root) {
    this.root = root;
  }

  public String getRoot() {
    return root;
  }

  public void setName(String name) {
    this.name = name;
  }

  public String getName() {
    return name;
  }

  public void setCanonicalName(String canonicalName) {
    this.canonicalName = canonicalName;
  }

  public String getCanonicalName() {
    return canonicalName;
  }

  public void setTitle(String title) {
    this.title = title;
  }

  public String getTitle() {
    return title;
  }

  public void setVersion(String version) {
    this.version = version;
  }

  public String getVersion() {
    return version;
  }

  public void setDescription(String description) {
    this.description = description;
  }

  public String getDescription() {
    return description;
  }

  public void setDocumentationLink(String documentationLink) {
    this.documentationLink = documentationLink;
  }

  public String getDocumentationLink() {
    return documentationLink;
  }

  public void setBackendRoot(String backendRoot) {
    this.backendRoot = toHttps(backendRoot);
  }

  public String getBackendRoot() {
    return backendRoot;
  }

  public void setIsAbstract(boolean isAbstract) {
    this.isAbstract = isAbstract;
  }

  public boolean getIsAbstract() {
    return isAbstract;
  }

  public void setIsDefaultVersion(boolean defaultVersion) {
    this.defaultVersion = defaultVersion;
  }

  public boolean getIsDefaultVersion() {
    return defaultVersion;
  }

  public void setIsDiscoverable(boolean discoverable) {
    this.discoverable = discoverable;
  }

  public boolean getIsDiscoverable() {
    return discoverable;
  }

  public void setUseDatastore(boolean useDatastore) {
    this.useDatastore = useDatastore;
  }

  public boolean getUseDatastore() {
    return useDatastore;
  }

  public void setResource(String resource) {
    this.resource = resource;
  }

  public String getResource() {
    return resource;
  }

  public ApiAuthConfig getAuthConfig() {
    return authConfig;
  }

  public ApiCacheControlConfig getCacheControlConfig() {
    return cacheControlConfig;
  }

  public ApiFrontendLimitsConfig getFrontendLimitsConfig() {
    return frontendLimitsConfig;
  }

  public ApiSerializationConfig getSerializationConfig() {
    return serializationConfig;
  }

  public ApiNamespaceConfig getNamespaceConfig() {
    return namespaceConfig;
  }

  // TODO: When builder is split off, these get/setters for default auth properties should
  // only be necessary in the builder.
  public void setAuthLevel(AuthLevel authLevel) {
    this.authLevel = authLevel;
  }

  public AuthLevel getAuthLevel() {
    return authLevel;
  }

  public void setScopeExpression(AuthScopeExpression scopeExpression) {
    this.scopeExpression = scopeExpression;
  }

  public AuthScopeExpression getScopeExpression() {
    return scopeExpression;
  }

  public void setAudiences(List audiences) {
    this.audiences = audiences;
  }

  public List getAudiences() {
    return audiences;
  }

  public void setIssuers(ApiIssuerConfigs issuers) {
    this.issuers = issuers;
  }

  public void ensureGoogleIssuer() {
    this.issuers = issuers.withGoogleIdToken();
  }

  public ApiIssuerConfigs getIssuers() {
    return issuers;
  }

  public void setIssuerAudiences(ApiIssuerAudienceConfig issuerAudiences) {
    Preconditions.checkNotNull(issuerAudiences, "issuerAudiences should never be null");
    this.issuerAudiences = issuerAudiences;
    if (issuerAudiences.hasIssuer(Constant.GOOGLE_ID_TOKEN_NAME)) {
      ensureGoogleIssuer();
    }
  }

  public ApiIssuerAudienceConfig getIssuerAudiences() {
    return issuerAudiences;
  }

  public void setClientIds(List clientIds) {
    this.clientIds = clientIds;
  }

  public List getClientIds() {
    return clientIds;
  }

  public void setAuthenticators(List> authenticators) {
    this.authenticators = authenticators;
  }

  public List> getAuthenticators() {
    return authenticators;
  }

  public void setPeerAuthenticators(List> peerAuthenticators) {
    this.peerAuthenticators = peerAuthenticators;
  }

  public List> getPeerAuthenticators() {
    return peerAuthenticators;
  }

  public void setApiKeyRequired(boolean apiKeyRequired) {
    this.apiKeyRequired = apiKeyRequired;
  }

  public boolean isApiKeyRequired() {
    return apiKeyRequired;
  }

  private String toHttps(String url) {
    if (url != null && url.startsWith("http:")) {
      return "https:" + url.substring(5);
    }
    return url;
  }

  public ApiClassConfig getApiClassConfig() {
    return apiClassConfig;
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy