Package co.cask.cdap.security.authentication.client

Source Code of co.cask.cdap.security.authentication.client.AbstractAuthenticationClient

/*
* Copyright © 2014 Cask Data, Inc.
*
* 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 co.cask.cdap.security.authentication.client;

import co.cask.cdap.common.conf.Constants;
import co.cask.cdap.common.http.HttpRequest;
import co.cask.cdap.common.http.HttpRequestConfig;
import co.cask.cdap.common.http.HttpRequests;
import co.cask.cdap.common.http.HttpResponse;
import co.cask.cdap.common.http.ObjectResponse;
import co.cask.cdap.common.http.exception.HttpFailureException;
import com.google.common.collect.Multimap;
import com.google.common.reflect.TypeToken;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URI;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.TimeUnit;

/**
* Abstract authentication client implementation with common methods.
*/
public abstract class AbstractAuthenticationClient implements AuthenticationClient {
  private static final Logger LOG = LoggerFactory.getLogger(AbstractAuthenticationClient.class);

  private static final Random RANDOM = new Random();
  private static final String AUTH_URI_KEY = "auth_uri";
  private static final String HTTP_PROTOCOL = "http";
  private static final String HTTPS_PROTOCOL = "https";
  private static final String ACCESS_TOKEN_KEY = "access_token";
  private static final String EXPIRES_IN_KEY = "expires_in";
  private static final String TOKEN_TYPE_KEY = "token_type";
  private static final long SPARE_TIME_IN_MILLIS = 5000;

  private long expirationTime;
  private AccessToken accessToken;
  private URI baseURI;
  private URI authURI;
  private Boolean authEnabled;
  private boolean verifySSLCert;

  /**
   * Returns HTTP headers required for authentication.
   */
  protected abstract Multimap<String, String> getAuthenticationHeaders();

  @Override
  public void invalidateToken() {
    accessToken = null;
  }

  @Override
  public boolean isAuthEnabled() throws IOException {
    if (authEnabled == null) {
      String strAuthURI = fetchAuthURI();
      authEnabled = StringUtils.isNotEmpty(strAuthURI);
      if (authEnabled) {
        authURI = URI.create(strAuthURI);
      }
    }
    return authEnabled;
  }

  @Override
  public void setConnectionInfo(String host, int port, boolean ssl) {
    if (baseURI != null) {
      throw new IllegalStateException("Connection info is already configured!");
    }
    baseURI = URI.create(String.format("%s://%s:%d%s/ping", ssl ? HTTPS_PROTOCOL : HTTP_PROTOCOL, host, port,
                                       Constants.Gateway.GATEWAY_VERSION));
  }

  @Override
  public AccessToken getAccessToken() throws IOException {
    if (!isAuthEnabled()) {
      throw new IOException("Authentication is disabled in the gateway server.");
    }

    if (accessToken == null || isTokenExpired()) {
      long requestTime = System.currentTimeMillis();
      accessToken = fetchAccessToken();
      expirationTime = requestTime + TimeUnit.SECONDS.toMillis(accessToken.getExpiresIn()) - SPARE_TIME_IN_MILLIS;
      LOG.debug("Received the access token successfully. Expiration date is {}.", new Date(expirationTime));
    }
    return accessToken;
  }

  /**
   * @return the authentication server URL or empty value if authentication is not enabled in the gateway server
   */
  protected URI getAuthURI() {
    return authURI;
  }

  public boolean isVerifySSLCert() {
    return verifySSLCert;
  }

  protected void setVerifySSLCert(boolean verifySSLCert) {
    this.verifySSLCert = verifySSLCert;
  }

  /**
   * Checks if the access token has expired.
   *
   * @return true, if the access token has expired
   */
  private boolean isTokenExpired() {
    return expirationTime < System.currentTimeMillis();
  }

  /**
   * Fetches the available authentication server URL, if authentication is enabled in the gateway server,
   * otherwise, empty string will be returned.
   *
   * @return string value of the authentication server URL
   * @throws IOException IOException in case of a problem or the connection was aborted or if url list is empty
   */
  private String fetchAuthURI() throws IOException {
    if (baseURI == null) {
      throw new IllegalStateException("Connection information not set!");
    }

    LOG.debug("Try to get the authentication URI from the gateway server: {}.", baseURI);
    HttpResponse response = HttpRequests.execute(HttpRequest.get(baseURI.toURL()).build(), getHttpRequestConfig());

    LOG.debug("Got response {} - {} from {}", response.getResponseCode(), response.getResponseMessage(), baseURI);
    if (response.getResponseCode() != HttpURLConnection.HTTP_UNAUTHORIZED) {
      return "";
    }

    Map<String, List<String>> responseMap =
      ObjectResponse.fromJsonBody(response,
                                  new TypeToken<Map<String, List<String>>>() { }).getResponseObject();
    LOG.debug("Response map from gateway server: {}", responseMap);

    String result;
    List<String> uriList = responseMap.get(AUTH_URI_KEY);
    if (uriList != null && !uriList.isEmpty()) {
      result = uriList.get(RANDOM.nextInt(uriList.size()));
    } else {
      throw new IOException("Authentication servers list is empty.");
    }
    return result;
  }

  /**
   * Executes fetch access token request.
   *
   * @param request the http request to fetch access token from the authentication server
   * @return {@link AccessToken} object containing the access token
   * @throws IOException IOException in case of a problem or the connection was aborted or if the access token is not
   * received successfully from the authentication server
   */
  private AccessToken execute(HttpRequest request) throws IOException {
    HttpResponse response = HttpRequests.execute(request, getHttpRequestConfig());

    LOG.debug("Got response {} - {} from {}", response.getResponseCode(), response.getResponseMessage(), baseURI);
    if (response.getResponseCode() != HttpURLConnection.HTTP_OK) {
      throw new HttpFailureException(response.getResponseMessage(), response.getResponseCode());
    }

    Map<String, String> responseMap =
      ObjectResponse.fromJsonBody(response, new TypeToken<Map<String, String>>() { }).getResponseObject();
    String tokenValue = responseMap.get(ACCESS_TOKEN_KEY);
    String tokenType = responseMap.get(TOKEN_TYPE_KEY);
    String expiresInStr = responseMap.get(EXPIRES_IN_KEY);

    LOG.debug("Response map from auth server: {}", responseMap);

    if (StringUtils.isEmpty(tokenValue) || StringUtils.isEmpty(tokenType) || StringUtils.isEmpty(expiresInStr)) {
      throw new IOException("Unexpected response was received from the authentication server.");
    }

    return new AccessToken(tokenValue, Long.valueOf(expiresInStr), tokenType);
  }

  private AccessToken fetchAccessToken() throws IOException {
    LOG.debug("Authentication is enabled in the gateway server. Authentication URI {}.", getAuthURI());

    return execute(HttpRequest.get(getAuthURI().toURL())
                     .addHeaders(getAuthenticationHeaders())
                     .build()
    );
  }

  private HttpRequestConfig getHttpRequestConfig() {
    return new HttpRequestConfig(0, 0, isVerifySSLCert());
  }
}
TOP

Related Classes of co.cask.cdap.security.authentication.client.AbstractAuthenticationClient

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.