/*
* 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.hadoop.hive.accumulo;
import java.io.File;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import org.apache.accumulo.core.client.AccumuloException;
import org.apache.accumulo.core.client.AccumuloSecurityException;
import org.apache.accumulo.core.client.Connector;
import org.apache.accumulo.core.client.Instance;
import org.apache.accumulo.core.client.ZooKeeperInstance;
import org.apache.accumulo.core.client.mock.MockInstance;
import org.apache.accumulo.core.client.security.tokens.AuthenticationToken;
import org.apache.accumulo.core.client.security.tokens.PasswordToken;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hive.common.JavaUtils;
import com.google.common.base.Preconditions;
/**
*
*/
public class AccumuloConnectionParameters {
private static final String KERBEROS_TOKEN_CLASS = "org.apache.accumulo.core.client.security.tokens.KerberosToken";
public static final String USER_NAME = "accumulo.user.name";
public static final String USER_PASS = "accumulo.user.pass";
public static final String ZOOKEEPERS = "accumulo.zookeepers";
public static final String INSTANCE_NAME = "accumulo.instance.name";
public static final String TABLE_NAME = "accumulo.table.name";
// SASL/Kerberos properties
public static final String SASL_ENABLED = "accumulo.sasl.enabled";
public static final String USER_KEYTAB = "accumulo.user.keytab";
public static final String USE_MOCK_INSTANCE = "accumulo.mock.instance";
protected Configuration conf;
protected boolean useMockInstance = false;
public AccumuloConnectionParameters(Configuration conf) {
// TableDesc#getDeserializer will ultimately instantiate the AccumuloSerDe with a null
// Configuration
// We have to accept this and just fail late if data is attempted to be pulled from the
// Configuration
this.conf = conf;
}
public Configuration getConf() {
return conf;
}
public String getAccumuloUserName() {
Preconditions.checkNotNull(conf);
return conf.get(USER_NAME);
}
public String getAccumuloPassword() {
Preconditions.checkNotNull(conf);
return conf.get(USER_PASS);
}
public String getAccumuloInstanceName() {
Preconditions.checkNotNull(conf);
return conf.get(INSTANCE_NAME);
}
public String getZooKeepers() {
Preconditions.checkNotNull(conf);
return conf.get(ZOOKEEPERS);
}
public String getAccumuloTableName() {
Preconditions.checkNotNull(conf);
return conf.get(TABLE_NAME);
}
public boolean useMockInstance() {
Preconditions.checkNotNull(conf);
return conf.getBoolean(USE_MOCK_INSTANCE, false);
}
public boolean useSasl() {
Preconditions.checkNotNull(conf);
return conf.getBoolean(SASL_ENABLED, false);
}
public String getAccumuloKeytab() {
Preconditions.checkNotNull(conf);
return conf.get(USER_KEYTAB);
}
public Instance getInstance() {
String instanceName = getAccumuloInstanceName();
// Fail with a good message
if (null == instanceName) {
throw new IllegalArgumentException("Accumulo instance name must be provided in hiveconf using " + INSTANCE_NAME);
}
if (useMockInstance()) {
return new MockInstance(instanceName);
}
String zookeepers = getZooKeepers();
// Fail with a good message
if (null == zookeepers) {
throw new IllegalArgumentException("ZooKeeper quorum string must be provided in hiveconf using " + ZOOKEEPERS);
}
return new ZooKeeperInstance(instanceName, zookeepers);
}
public Connector getConnector() throws AccumuloException, AccumuloSecurityException {
Instance inst = getInstance();
return getConnector(inst);
}
public Connector getConnector(Instance inst) throws AccumuloException, AccumuloSecurityException {
String username = getAccumuloUserName();
// Fail with a good message
if (null == username) {
throw new IllegalArgumentException("Accumulo user name must be provided in hiveconf using " + USER_NAME);
}
if (useSasl()) {
return inst.getConnector(username, getKerberosToken());
} else {
// Not using SASL/Kerberos -- use the password
String password = getAccumuloPassword();
if (null == password) {
throw new IllegalArgumentException("Accumulo password must be provided in hiveconf using " + USER_PASS);
}
return inst.getConnector(username, new PasswordToken(password));
}
}
public AuthenticationToken getKerberosToken() {
if (!useSasl()) {
throw new IllegalArgumentException("Cannot construct KerberosToken when SASL is disabled");
}
final String keytab = getAccumuloKeytab(), username = getAccumuloUserName();
if (null != keytab) {
// Use the keytab if one was provided
return getKerberosToken(username, keytab);
} else {
// Otherwise, expect the user is already logged in
return getKerberosToken(username);
}
}
/**
* Instantiate a KerberosToken in a backwards compatible manner.
* @param username Kerberos principal
*/
AuthenticationToken getKerberosToken(String username) {
// Get the Class
Class<? extends AuthenticationToken> krbTokenClz = getKerberosTokenClass();
try {
// Invoke the `new KerberosToken(String)` constructor
// Expects that the user is already logged-in
Constructor<? extends AuthenticationToken> constructor = krbTokenClz.getConstructor(String.class);
return constructor.newInstance(username);
} catch (NoSuchMethodException | SecurityException | InstantiationException |
IllegalArgumentException | InvocationTargetException | IllegalAccessException e) {
throw new IllegalArgumentException("Failed to instantiate KerberosToken.", e);
}
}
/**
* Instantiate a KerberosToken in a backwards compatible manner.
* @param username Kerberos principal
* @param keytab Keytab on local filesystem
*/
AuthenticationToken getKerberosToken(String username, String keytab) {
Class<? extends AuthenticationToken> krbTokenClz = getKerberosTokenClass();
File keytabFile = new File(keytab);
if (!keytabFile.isFile() || !keytabFile.canRead()) {
throw new IllegalArgumentException("Keytab must be a readable file: " + keytab);
}
try {
// Invoke the `new KerberosToken(String, File, boolean)` constructor
// Tries to log in as the provided user with the given keytab, overriding an already logged-in user if present
Constructor<? extends AuthenticationToken> constructor = krbTokenClz.getConstructor(String.class, File.class, boolean.class);
return constructor.newInstance(username, keytabFile, true);
} catch (NoSuchMethodException | SecurityException | InstantiationException |
IllegalArgumentException | InvocationTargetException | IllegalAccessException e) {
throw new IllegalArgumentException("Failed to instantiate KerberosToken.", e);
}
}
/**
* Attempt to instantiate the KerberosToken class
*/
Class<? extends AuthenticationToken> getKerberosTokenClass() {
try {
// Instantiate the class
Class<?> clz = JavaUtils.loadClass(KERBEROS_TOKEN_CLASS);
// Cast it to an AuthenticationToken since Connector will need that
return clz.asSubclass(AuthenticationToken.class);
} catch (ClassNotFoundException e) {
throw new IllegalArgumentException("Could not load KerberosToken class. >=Accumulo 1.7.0 required", e);
}
}
}