/*
* Copyright (C) 2014 Intel Corporation
* All rights reserved.
*/
package com.intel.mountwilson.util;
import com.intel.dcsg.cpg.crypto.HmacCredential;
import com.intel.dcsg.cpg.crypto.RsaCredential;
import com.intel.dcsg.cpg.crypto.SimpleKeystore;
import com.intel.dcsg.cpg.http.MutableQuery;
import com.intel.dcsg.cpg.rfc822.Headers;
import com.intel.dcsg.cpg.tls.policy.TlsPolicy;
import com.intel.mtwilson.ApiClient;
import com.intel.mtwilson.api.ApiRequest;
import com.intel.mtwilson.api.ApiResponse;
import com.intel.mtwilson.api.ClientException;
import java.awt.PageAttributes;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URL;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.List;
import java.util.Properties;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.ws.rs.core.MediaType;
import org.apache.commons.configuration.Configuration;
import org.apache.commons.io.IOUtils;
/**
*
* @author jbuhacoff
*/
public class ProxyApiClient extends ApiClient {
private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(ProxyApiClient.class);
public ProxyApiClient(Configuration config) throws ClientException {
super(config);
}
public ProxyApiClient(File configurationFile) throws ClientException, IOException {
super(configurationFile);
}
public ProxyApiClient(URL baseURL, HmacCredential credential, Properties properties) throws ClientException {
super(baseURL, credential, properties);
}
public ProxyApiClient(URL baseURL, RsaCredential credential, Properties properties) throws ClientException {
super(baseURL, credential, properties);
}
public ProxyApiClient(URL baseURL, HmacCredential credential, SimpleKeystore keystore, Configuration config) throws ClientException {
super(baseURL, credential, keystore, config);
}
public ProxyApiClient(URL baseURL, RsaCredential credential, SimpleKeystore keystore, Configuration config) throws ClientException {
super(baseURL, credential, keystore, config);
}
public ProxyApiClient(URL baseURL, RsaCredential credential, SimpleKeystore keystore, TlsPolicy tlsPolicy) throws ClientException {
super(baseURL, credential, keystore, tlsPolicy);
}
private Headers copyRequestHeaders(HttpServletRequest request) {
Headers headers = new Headers();
Enumeration headerNames = request.getHeaderNames();
if( headerNames == null ) { return headers; }
while(headerNames.hasMoreElements()) {
String headerName = (String)headerNames.nextElement();
Enumeration headerValues = request.getHeaders(headerName);
if( headerValues == null ) { continue; }
while(headerValues.hasMoreElements()) {
String headerValue = (String)headerValues.nextElement();
headers.add(headerName, headerValue);
}
}
return headers;
}
/**
* Facilitates integration of tag management UI into mtwilson-portal by
* allowing it to access mtwilson APIs using the credentials of the
* portal user.
* See also V2Proxy.java
*
* @param request
* @param response
* @throws Exception
*/
public void proxy(HttpServletRequest request, HttpServletResponse response) throws Exception {
log.debug("path info: {}", request.getPathInfo()); // example: path info: /configurations
// log.debug("context path: {}", request.getContextPath()); // example: context path: /mtwilson-portal
// log.debug("path translated: {}", request.getPathTranslated()); // example: path translated: /usr/share/glassfish4/glassfish/domains/domain1/applications/mtwilson-portal-2.0-SNAPSHOT/configurations (assumes file on disk which of course doesn't make sense for an api unless we have some static content for some GET urls)
// log.debug("request uri: {}", request.getRequestURI()); // example: request uri: /mtwilson-portal/v2proxy/configurations
// log.debug("sevlet path: {}", request.getServletPath()); // example: servlet path: /v2proxy
log.debug("query string: {}", request.getQueryString());
String proxyUrl = request.getPathInfo();
/*
String proxyUrl = request.getParameter("proxyUrl");
// if( proxyUrl.startsWith("/v2proxy") ) {
// proxyUrl = proxyUrl.replaceFirst("/v2proxy", "");
// }
*/
if( proxyUrl.startsWith("/") ) {
proxyUrl = proxyUrl.replaceFirst("/", "");
}
/*
// reconstruct the query string without the proxyUrl parameter, instead of using request.getQueryString() which would also include it
// unfortunately because of the way the proxy is configured we get every parameter twice
MutableQuery query = new MutableQuery(request.getParameterMap());
query.removeAll("proxyUrl");
removeDuplicateParameters(query);
String queryString = query.toString();
*/
String queryString = request.getQueryString();
if( queryString == null ) { queryString = ""; }
if( queryString.startsWith("?") ) {
queryString = queryString.replaceFirst("?", "");
}
String querySeparator = "?";
if( queryString.isEmpty()) { querySeparator = ""; }
log.debug("Request URI: {}", request.getRequestURI()); // looks like this: /mtwilson-portal/v2proxy/configurations
log.debug("Request URL: {}", request.getRequestURL()); // looks like this: https://10.1.71.49:8443/mtwilson-portal/v2proxy/configurations
int pathIndex = request.getRequestURL().toString().indexOf(request.getRequestURI());
String server = request.getRequestURL().toString().substring(0, pathIndex);
// String server = String.format("%s://%s:%d", request.getScheme(), request.getLocalName(), request.getLocalPort()); // this is wrong because if client sends https://192.168.1.100:8443 but the local /etc/hosts file has 192.168.1.100 mapped to "testserver" then we would see here https://testserver:8443 which is fine for networking but will cause the signature on the original request to be unverifiable by the server (since the client signed the URL *they* used, not the URL we are rewriting - so we have to keep it the same)
String urltext = String.format("%s/mtwilson/v2/%s%s%s", server, proxyUrl, querySeparator, queryString);
log.debug("Proxy URL: {}", urltext);
log.debug("Proxy Content-Type: {}", request.getContentType()); // example: application/json; charset=UTF-8
// MediaType.valueof(...) can't handle the parameters like "; charset=UTF-8" so we have to strip them out
String contentType = request.getContentType();
if( contentType != null ) {
String[] contentTypeParts = contentType.split(";");
if( contentTypeParts.length > 0 ) {
contentType = contentTypeParts[0];
}
}
String content = null;
Headers headers = copyRequestHeaders(request);
ApiRequest proxyRequest;
ApiResponse proxyResponse;
switch (request.getMethod()) {
case "GET":
proxyResponse = httpGet(urltext, headers);
break;
case "DELETE":
proxyResponse = httpDelete(urltext, headers);
break;
case "PUT":
content = IOUtils.toString(request.getInputStream());
proxyRequest = new ApiRequest(MediaType.valueOf(contentType), content);
proxyResponse = httpPut(urltext, proxyRequest, headers);
break;
case "POST":
content = IOUtils.toString(request.getInputStream());
proxyRequest = new ApiRequest(MediaType.valueOf(contentType), content);
proxyResponse = httpPost(urltext, proxyRequest, headers);
break;
default:
throw new UnsupportedOperationException("Method not supported by proxy: " + request.getMethod());
}
if (proxyResponse != null) {
response.setStatus(proxyResponse.httpStatusCode);
response.setContentType(proxyResponse.contentType.toString());
OutputStream out = response.getOutputStream();
IOUtils.write(proxyResponse.content, out);
out.close();
return;
}
throw new IOException("Proxy failed for request: " + urltext);
}
/***** UNUSED
private void removeDuplicateParameters(MutableQuery query) {
HashSet<String> keys = new HashSet<String>();
keys.addAll(query.keySet()); // iterate on this copy instead of on the query's keySet so we don't get ConcurrentModificationException when we remove keys inside the loop
for(String key : keys) {
List<String> values = query.getAll(key);
HashSet<String> uniqueValues = new HashSet<String>();
uniqueValues.addAll(values);
query.removeAll(key);
query.add(key, uniqueValues);
}
}*/
}