Package org.jclouds.fujitsu.fgcp.filters

Source Code of org.jclouds.fujitsu.fgcp.filters.RequestAuthenticator

/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements.  See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.jclouds.fujitsu.fgcp.filters;

import static com.google.common.base.Charsets.UTF_8;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Throwables.propagate;
import static com.google.common.io.BaseEncoding.base64;
import static org.jclouds.http.utils.Queries.queryParser;

import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.Signature;
import java.security.SignatureException;
import java.util.Calendar;
import java.util.Locale;
import java.util.concurrent.ExecutionException;

import javax.annotation.Resource;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Provider;
import javax.inject.Singleton;
import javax.ws.rs.HttpMethod;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;

import org.jclouds.Constants;
import org.jclouds.date.TimeStamp;
import org.jclouds.fujitsu.fgcp.FGCPCredentials;
import org.jclouds.fujitsu.fgcp.reference.RequestParameters;
import org.jclouds.http.HttpException;
import org.jclouds.http.HttpRequest;
import org.jclouds.http.HttpRequest.Builder;
import org.jclouds.http.HttpRequestFilter;
import org.jclouds.http.HttpUtils;
import org.jclouds.http.internal.SignatureWire;
import org.jclouds.logging.Logger;
import org.jclouds.rest.RequestSigner;
import org.jclouds.rest.annotations.ApiVersion;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Supplier;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.Multimap;

/**
* Generates and signs the access key id and adds the mandatory http header and
* request parameters to the request.
*
* @author Dies Koper
*/
@Singleton
public class RequestAuthenticator implements HttpRequestFilter, RequestSigner {

   @Resource
   @Named(Constants.LOGGER_SIGNATURE)
   private Logger signatureLog = Logger.NULL;

   private final Supplier<FGCPCredentials> creds;
   private final LoadingCache<FGCPCredentials, Signature> signerCache;
   private final Provider<Calendar> calendarProvider;
   private final HttpUtils utils;
   private final String apiVersion;

   static final String SIGNATURE_VERSION = "1.0";
   static final String SIGNATURE_METHOD = "SHA1withRSA";

   @Inject
   public RequestAuthenticator(Supplier<FGCPCredentials> creds,
         SignatureForCredentials loader, @TimeStamp Provider<Calendar> calendarProvider, HttpUtils utils,
         SignatureWire signatureWire, @ApiVersion String apiVersion) {
      this.calendarProvider = checkNotNull(calendarProvider);
      this.creds = checkNotNull(creds, "creds");
      // throw out the signature related to old keys
      this.signerCache = CacheBuilder.newBuilder().maximumSize(2).build(checkNotNull(loader, "loader"));
      this.utils = checkNotNull(utils, "utils");
      this.apiVersion = checkNotNull(apiVersion, "apiVersion");
   }

   /**
    * It is relatively expensive to create a new signing key. Cache the
    * relationship between current credentials so that the signer is only
    * recalculated once.
    */
   @VisibleForTesting
   static class SignatureForCredentials extends CacheLoader<FGCPCredentials, Signature> {

      @Override
      public Signature load(FGCPCredentials creds) {
         PrivateKey privateKey = checkNotNull(creds.privateKey, "fgcpcredential's privateKey is null");
         try {
            Signature signer = Signature.getInstance(RequestAuthenticator.SIGNATURE_METHOD);
            signer.initSign(privateKey);
            return signer;
         } catch (NoSuchAlgorithmException e) {
            throw propagate(e);
         } catch (InvalidKeyException e) {
            throw propagate(e);
         }
      }
   }

   public HttpRequest filter(HttpRequest request) throws HttpException {
      checkNotNull(request, "request must be present");
      utils.logRequest(signatureLog, request, ">>");

      // create accesskeyid
      String accessKeyId = createStringToSign(request);
      String signature = sign(accessKeyId);

      // only "en" and "ja" are allowed
      String lang = Locale.JAPANESE.getLanguage().equals(Locale.getDefault().getLanguage()) ? Locale.JAPANESE
            .getLanguage() : Locale.ENGLISH.getLanguage();

      if (HttpMethod.GET.equals(request.getMethod())) {
         request = addQueryParamsToRequest(request, accessKeyId, signature, lang);
      } else {

         String payload = request.getPayload().getRawContent().toString();
         payload = createXmlElementWithValue(payload, RequestParameters.VERSION, apiVersion);
         payload = createXmlElementWithValue(payload, RequestParameters.LOCALE, lang);
         payload = createXmlElementWithValue(payload, RequestParameters.ACCESS_KEY_ID, accessKeyId);
         payload = createXmlElementWithValue(payload, RequestParameters.SIGNATURE, signature);

         // ensure there are no other query params left
         request.setPayload(payload);
         request.getPayload().getContentMetadata().setContentType(MediaType.TEXT_XML);
      }

      // may need to do this elsewhere (see ConvertToGaeRequest)
      HttpRequest filteredRequest = request.toBuilder().replaceHeader(HttpHeaders.USER_AGENT, "OViSS-API-CLIENT")
            .build();

      utils.logRequest(signatureLog, filteredRequest, ">>->");
      return filteredRequest;
   }

   @VisibleForTesting
   HttpRequest addQueryParamsToRequest(HttpRequest request, String accessKeyId, String signature, String lang) {
      Multimap<String, String> decodedParams = queryParser().apply(request.getEndpoint().getRawQuery());
      Builder<?> builder = request.toBuilder().endpoint(request.getEndpoint()).method(request.getMethod());
      if (!decodedParams.containsKey("Version")) {
         builder.addQueryParam(RequestParameters.VERSION, apiVersion);
      }
      builder.addQueryParam(RequestParameters.LOCALE, lang).addQueryParam(RequestParameters.ACCESS_KEY_ID, accessKeyId)
            .addQueryParam(RequestParameters.SIGNATURE, signature);

      return builder.build();
   }

   String createXmlElementWithValue(String payload, String tag, String value) {
      String startTag = String.format("<%s>", tag);
      String endTag = String.format("</%s>", tag);

      return payload.replace(startTag + endTag, startTag + value + endTag);
   }

   public String sign(String stringToSign) {
      String signed;
      try {
         Signature signer = signerCache.get(checkNotNull(creds.get(), "credential supplier returned null"));
         signer.update(stringToSign.getBytes(UTF_8));
         signed = base64().withSeparator("\n", 61).encode(signer.sign());
      } catch (SignatureException e) {
         throw new HttpException("error signing request", e);
      } catch (ExecutionException e) {
         throw new HttpException("couldn't load key for signing request", e);
      }
      return signed;
   }

   @VisibleForTesting
   String generateAccessKeyId() {
      Calendar cal = calendarProvider.get();
      String timezone = cal.getTimeZone().getDisplayName(Locale.ENGLISH);
      String expires = String.valueOf(cal.getTime().getTime());

      String signatureData = String.format("%s&%s&%s&%s", timezone, expires, SIGNATURE_VERSION, SIGNATURE_METHOD);
      String accessKeyId = base64().withSeparator("\n", 61).encode(signatureData.getBytes(UTF_8));

      return accessKeyId;
   }

   @Override
   public String createStringToSign(HttpRequest input) {
      return generateAccessKeyId();
   }

}
TOP

Related Classes of org.jclouds.fujitsu.fgcp.filters.RequestAuthenticator

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.