blob: c3116705cd6d4e3628baf3188a7a8caab1464c1b [file] [log] [blame]
/*
* 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.apache.ranger.plugin.util;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.hadoop.conf.Configuration;
import org.apache.ranger.authorization.hadoop.config.RangerConfiguration;
import org.apache.ranger.authorization.hadoop.utils.RangerCredentialProvider;
import org.apache.ranger.authorization.utils.StringUtil;
import org.codehaus.jackson.jaxrs.JacksonJsonProvider;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.WebResource;
import com.sun.jersey.api.client.config.ClientConfig;
import com.sun.jersey.api.client.config.DefaultClientConfig;
import com.sun.jersey.api.client.filter.HTTPBasicAuthFilter;
import com.sun.jersey.client.urlconnection.HTTPSProperties;
public class RangerRESTClient {
private static final Log LOG = LogFactory.getLog(RangerRESTClient.class);
public static final String RANGER_PROP_POLICYMGR_URL = "ranger.service.store.rest.url";
public static final String RANGER_PROP_POLICYMGR_SSLCONFIG_FILENAME = "ranger.service.store.rest.ssl.config.file";
public static final String RANGER_POLICYMGR_CLIENT_KEY_FILE = "xasecure.policymgr.clientssl.keystore";
public static final String RANGER_POLICYMGR_CLIENT_KEY_FILE_PASSWORD = "xasecure.policymgr.clientssl.keystore.password";
public static final String RANGER_POLICYMGR_CLIENT_KEY_FILE_TYPE = "xasecure.policymgr.clientssl.keystore.type";
public static final String RANGER_POLICYMGR_CLIENT_KEY_FILE_CREDENTIAL = "xasecure.policymgr.clientssl.keystore.credential.file";
public static final String RANGER_POLICYMGR_CLIENT_KEY_FILE_CREDENTIAL_ALIAS = "sslKeyStore";
public static final String RANGER_POLICYMGR_CLIENT_KEY_FILE_TYPE_DEFAULT = "jks";
public static final String RANGER_POLICYMGR_TRUSTSTORE_FILE = "xasecure.policymgr.clientssl.truststore";
public static final String RANGER_POLICYMGR_TRUSTSTORE_FILE_PASSWORD = "xasecure.policymgr.clientssl.truststore.password";
public static final String RANGER_POLICYMGR_TRUSTSTORE_FILE_TYPE = "xasecure.policymgr.clientssl.truststore.type";
public static final String RANGER_POLICYMGR_TRUSTSTORE_FILE_CREDENTIAL = "xasecure.policymgr.clientssl.truststore.credential.file";
public static final String RANGER_POLICYMGR_TRUSTSTORE_FILE_CREDENTIAL_ALIAS = "sslTrustStore";
public static final String RANGER_POLICYMGR_TRUSTSTORE_FILE_TYPE_DEFAULT = "jks";
public static final String RANGER_SSL_KEYMANAGER_ALGO_TYPE = "SunX509" ;
public static final String RANGER_SSL_TRUSTMANAGER_ALGO_TYPE = "SunX509" ;
public static final String RANGER_SSL_CONTEXT_ALGO_TYPE = "SSL" ;
private String mUrl = null;
private String mSslConfigFileName = null;
private String mUsername = null;
private String mPassword = null;
private boolean mIsSSL = false;
private String mKeyStoreURL = null;
private String mKeyStoreAlias = null;
private String mKeyStoreFile = null;
private String mKeyStoreType = null;
private String mTrustStoreURL = null;
private String mTrustStoreAlias = null;
private String mTrustStoreFile = null;
private String mTrustStoreType = null;
private Gson gsonBuilder = null;
private volatile Client client = null;
private int mRestClientConnTimeOutMs;
private int mRestClientReadTimeOutMs;
public RangerRESTClient() {
this(RangerConfiguration.getInstance().get(RANGER_PROP_POLICYMGR_URL),
RangerConfiguration.getInstance().get(RANGER_PROP_POLICYMGR_SSLCONFIG_FILENAME));
}
public RangerRESTClient(String url, String sslConfigFileName) {
mUrl = url;
mSslConfigFileName = sslConfigFileName;
init();
}
public String getUrl() {
return mUrl;
}
public void setUrl(String url) {
this.mUrl = url;
}
public String getUsername() {
return mUsername;
}
public String getPassword() {
return mPassword;
}
public int getRestClientConnTimeOutMs() {
return mRestClientConnTimeOutMs;
}
public void setRestClientConnTimeOutMs(int mRestClientConnTimeOutMs) {
this.mRestClientConnTimeOutMs = mRestClientConnTimeOutMs;
}
public int getRestClientReadTimeOutMs() {
return mRestClientReadTimeOutMs;
}
public void setRestClientReadTimeOutMs(int mRestClientReadTimeOutMs) {
this.mRestClientReadTimeOutMs = mRestClientReadTimeOutMs;
}
public void setBasicAuthInfo(String username, String password) {
mUsername = username;
mPassword = password;
}
public WebResource getResource(String relativeUrl) {
WebResource ret = getClient().resource(getUrl() + relativeUrl);
return ret;
}
public String toJson(Object obj) {
return gsonBuilder.toJson(obj);
}
public <T> T fromJson(String json, Class<T> cls) {
return gsonBuilder.fromJson(json, cls);
}
public Client getClient() {
// result saves on access time when client is built at the time of the call
Client result = client;
if(result == null) {
synchronized(this) {
result = client;
if(result == null) {
client = result = buildClient();
}
}
}
return result;
}
private Client buildClient() {
Client client = null;
if (mIsSSL) {
KeyManager[] kmList = getKeyManagers();
TrustManager[] tmList = getTrustManagers();
SSLContext sslContext = getSSLContext(kmList, tmList);
ClientConfig config = new DefaultClientConfig();
config.getClasses().add(JacksonJsonProvider.class); // to handle List<> unmarshalling
HostnameVerifier hv = new HostnameVerifier() {
public boolean verify(String urlHostName, SSLSession session) {
return session.getPeerHost().equals(urlHostName);
}
};
config.getProperties().put(HTTPSProperties.PROPERTY_HTTPS_PROPERTIES, new HTTPSProperties(hv, sslContext));
client = Client.create(config);
}
if(client == null) {
ClientConfig config = new DefaultClientConfig();
config.getClasses().add(JacksonJsonProvider.class); // to handle List<> unmarshalling
client = Client.create(config);
}
// TODO: for testing only
if(!StringUtils.isEmpty(mUsername) || !StringUtils.isEmpty(mPassword)) {
client.addFilter(new HTTPBasicAuthFilter(mUsername, mPassword));
}
// Set Connection Timeout and ReadTime for the PolicyRefresh
client.setConnectTimeout(mRestClientConnTimeOutMs);
client.setReadTimeout(mRestClientReadTimeOutMs);
return client;
}
private void init() {
try {
gsonBuilder = new GsonBuilder().setDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ").setPrettyPrinting().create();
} catch(Throwable excp) {
LOG.fatal("RangerRESTClient.init(): failed to create GsonBuilder object", excp);
}
mIsSSL = StringUtil.containsIgnoreCase(mUrl, "https");
InputStream in = null ;
try {
Configuration conf = new Configuration() ;
in = getFileInputStream(mSslConfigFileName) ;
if (in != null) {
conf.addResource(in);
}
mKeyStoreURL = conf.get(RANGER_POLICYMGR_CLIENT_KEY_FILE_CREDENTIAL);
mKeyStoreAlias = RANGER_POLICYMGR_CLIENT_KEY_FILE_CREDENTIAL_ALIAS;
mKeyStoreType = conf.get(RANGER_POLICYMGR_CLIENT_KEY_FILE_TYPE, RANGER_POLICYMGR_CLIENT_KEY_FILE_TYPE_DEFAULT);
mKeyStoreFile = conf.get(RANGER_POLICYMGR_CLIENT_KEY_FILE);
mTrustStoreURL = conf.get(RANGER_POLICYMGR_TRUSTSTORE_FILE_CREDENTIAL);
mTrustStoreAlias = RANGER_POLICYMGR_TRUSTSTORE_FILE_CREDENTIAL_ALIAS;
mTrustStoreType = conf.get(RANGER_POLICYMGR_TRUSTSTORE_FILE_TYPE, RANGER_POLICYMGR_TRUSTSTORE_FILE_TYPE_DEFAULT);
mTrustStoreFile = conf.get(RANGER_POLICYMGR_TRUSTSTORE_FILE);
}
catch(IOException ioe) {
LOG.error("Unable to load SSL Config FileName: [" + mSslConfigFileName + "]", ioe);
}
finally {
close(in, mSslConfigFileName);
}
}
private KeyManager[] getKeyManagers() {
KeyManager[] kmList = null;
String keyStoreFilepwd = getCredential(mKeyStoreURL, mKeyStoreAlias);
if (!StringUtil.isEmpty(mKeyStoreFile) && !StringUtil.isEmpty(keyStoreFilepwd)) {
InputStream in = null ;
try {
in = getFileInputStream(mKeyStoreFile) ;
if (in != null) {
KeyStore keyStore = KeyStore.getInstance(mKeyStoreType);
keyStore.load(in, keyStoreFilepwd.toCharArray());
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(RANGER_SSL_KEYMANAGER_ALGO_TYPE);
keyManagerFactory.init(keyStore, keyStoreFilepwd.toCharArray());
kmList = keyManagerFactory.getKeyManagers();
} else {
LOG.error("Unable to obtain keystore from file [" + mKeyStoreFile + "]");
}
} catch (KeyStoreException e) {
LOG.error("Unable to obtain from KeyStore", e);
} catch (NoSuchAlgorithmException e) {
LOG.error("SSL algorithm is available in the environment", e);
} catch (CertificateException e) {
LOG.error("Unable to obtain the requested certification ", e);
} catch (FileNotFoundException e) {
LOG.error("Unable to find the necessary SSL Keystore and TrustStore Files", e);
} catch (IOException e) {
LOG.error("Unable to read the necessary SSL Keystore and TrustStore Files", e);
} catch (UnrecoverableKeyException e) {
LOG.error("Unable to recover the key from keystore", e);
} finally {
close(in, mKeyStoreFile);
}
}
return kmList;
}
private TrustManager[] getTrustManagers() {
TrustManager[] tmList = null;
String trustStoreFilepwd = getCredential(mTrustStoreURL, mTrustStoreAlias);
if (!StringUtil.isEmpty(mTrustStoreFile) && !StringUtil.isEmpty(trustStoreFilepwd)) {
InputStream in = null ;
try {
in = getFileInputStream(mTrustStoreFile) ;
if (in != null) {
KeyStore trustStore = KeyStore.getInstance(mTrustStoreType);
trustStore.load(in, trustStoreFilepwd.toCharArray());
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(RANGER_SSL_TRUSTMANAGER_ALGO_TYPE);
trustManagerFactory.init(trustStore);
tmList = trustManagerFactory.getTrustManagers();
} else {
LOG.error("Unable to obtain keystore from file [" + mTrustStoreFile + "]");
}
} catch (KeyStoreException e) {
LOG.error("Unable to obtain from KeyStore", e);
} catch (NoSuchAlgorithmException e) {
LOG.error("SSL algorithm is available in the environment", e);
} catch (CertificateException e) {
LOG.error("Unable to obtain the requested certification ", e);
} catch (FileNotFoundException e) {
LOG.error("Unable to find the necessary SSL Keystore and TrustStore Files", e);
} catch (IOException e) {
LOG.error("Unable to read the necessary SSL Keystore and TrustStore Files", e);
} finally {
close(in, mTrustStoreFile);
}
}
return tmList;
}
private SSLContext getSSLContext(KeyManager[] kmList, TrustManager[] tmList) {
try {
if(kmList != null && tmList != null) {
SSLContext sslContext = SSLContext.getInstance(RANGER_SSL_CONTEXT_ALGO_TYPE);
sslContext.init(kmList, tmList, new SecureRandom());
return sslContext;
}
} catch (NoSuchAlgorithmException e) {
LOG.error("SSL algorithm is available in the environment", e);
} catch (KeyManagementException e) {
LOG.error("Unable to initials the SSLContext", e);
}
return null;
}
private String getCredential(String url, String alias) {
char[] credStr = RangerCredentialProvider.getInstance().getCredentialString(url, alias);
return credStr == null ? null : new String(credStr);
}
private InputStream getFileInputStream(String fileName) throws IOException {
InputStream in = null ;
if(! StringUtil.isEmpty(fileName)) {
File f = new File(fileName) ;
if (f.exists()) {
in = new FileInputStream(f) ;
}
else {
in = ClassLoader.getSystemResourceAsStream(fileName) ;
}
}
return in ;
}
private void close(InputStream str, String filename) {
if (str != null) {
try {
str.close() ;
} catch (IOException excp) {
LOG.error("Error while closing file: [" + filename + "]", excp) ;
}
}
}
}