Repository: phoenix Updated Branches: refs/heads/master 437bf6881 -> dddcffad2
PHOENIX-19 Enhance JDBC connection of Phoenix to support connecting to a Secure HBase cluster (Anil Gupta) Project: http://git-wip-us.apache.org/repos/asf/phoenix/repo Commit: http://git-wip-us.apache.org/repos/asf/phoenix/commit/dddcffad Tree: http://git-wip-us.apache.org/repos/asf/phoenix/tree/dddcffad Diff: http://git-wip-us.apache.org/repos/asf/phoenix/diff/dddcffad Branch: refs/heads/master Commit: dddcffad28c1a338e63dfa476619fec3f47db489 Parents: 437bf68 Author: James Taylor <jtay...@salesforce.com> Authored: Sun Jun 8 10:45:38 2014 -0700 Committer: James Taylor <jtay...@salesforce.com> Committed: Sun Jun 8 10:52:34 2014 -0700 ---------------------------------------------------------------------- .../phoenix/jdbc/PhoenixEmbeddedDriver.java | 100 ++++++++++++++++--- .../query/ConnectionQueryServicesImpl.java | 19 +++- .../org/apache/phoenix/query/QueryServices.java | 3 + .../phoenix/jdbc/PhoenixEmbeddedDriverTest.java | 9 ++ 4 files changed, 117 insertions(+), 14 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/phoenix/blob/dddcffad/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixEmbeddedDriver.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixEmbeddedDriver.java b/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixEmbeddedDriver.java index 8cfe3c2..10c24b8 100644 --- a/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixEmbeddedDriver.java +++ b/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixEmbeddedDriver.java @@ -169,7 +169,7 @@ public abstract class PhoenixEmbeddedDriver implements Driver, org.apache.phoeni StringTokenizer tokenizer = new StringTokenizer(url == null ? "" : url.substring(PhoenixRuntime.JDBC_PROTOCOL.length()),DELIMITERS, true); int i = 0; boolean isMalformedUrl = false; - String[] tokens = new String[3]; + String[] tokens = new String[5]; String token = null; while (tokenizer.hasMoreTokens() && !(token=tokenizer.nextToken()).equals(TERMINATOR) && tokenizer.hasMoreTokens() && i < tokens.length) { token = tokenizer.nextToken(); @@ -188,14 +188,41 @@ public abstract class PhoenixEmbeddedDriver implements Driver, org.apache.phoeni try { port = Integer.parseInt(tokens[1]); isMalformedUrl = port < 0; + if(i == 4){ + if(!tokens[2].endsWith(".keytab")){ + isMalformedUrl = true; + } + tokens[4] = tokens[3]; + tokens[3] = tokens[2]; + tokens[2] = null; + } } catch (NumberFormatException e) { // If we have 3 tokens, then the second one must be a port. // If we only have 2 tokens, the second one might be the root node: // Assume that is the case if we get a NumberFormatException - if (! (isMalformedUrl = i == 3) ) { + if (!tokens[1].startsWith("/")) { + isMalformedUrl = true; + } + if (i == 2) { + tokens[4] = null; + tokens[3] = null; + tokens[2] = tokens[1]; + tokens[1] = null; + } else if (i == 3) { + tokens[4] = tokens[2]; + tokens[3] = tokens[1]; + tokens[2] = null; + tokens[1] = null; + } else if (i == 4) { + tokens[4] = tokens[3]; + tokens[3] = tokens[2]; + tokens[2] = tokens[1]; + tokens[1] = null; + } else if (i == 5) { + tokens[4] = tokens[3]; + tokens[3] = tokens[2]; tokens[2] = tokens[1]; } - } } } @@ -203,13 +230,15 @@ public abstract class PhoenixEmbeddedDriver implements Driver, org.apache.phoeni throw new SQLExceptionInfo.Builder(SQLExceptionCode.MALFORMED_CONNECTION_URL) .setMessage(url).build().buildException(); } - return new ConnectionInfo(tokens[0],port,tokens[2]); + return new ConnectionInfo(tokens[0],port,tokens[2], tokens[3], tokens[4]); } public ConnectionInfo normalize(ReadOnlyProps props) throws SQLException { String zookeeperQuorum = this.getZookeeperQuorum(); Integer port = this.getPort(); String rootNode = this.getRootNode(); + String keytab = this.getKeytab(); + String principal = this.getPrincipal(); // Normalize connInfo so that a url explicitly specifying versus implicitly inheriting // the default values will both share the same ConnectionQueryServices. if (zookeeperQuorum == null) { @@ -244,24 +273,45 @@ public abstract class PhoenixEmbeddedDriver implements Driver, org.apache.phoeni throw new SQLExceptionInfo.Builder(SQLExceptionCode.MALFORMED_CONNECTION_URL) .setMessage("Root node may not be specified when using the connectionless url \"" + this.toString() + "\"").build().buildException(); } - return new ConnectionInfo(zookeeperQuorum, port, rootNode); + if(keytab == null){ + if (!isConnectionless) { + keytab = props.get(QueryServices.HBASE_CLIENT_KEYTAB); + } + } + if(principal == null){ + if (!isConnectionless) { + principal = props.get(QueryServices.HBASE_CLIENT_PRINCIPAL); + } + } + if (keytab == null || keytab.equals("")) return new ConnectionInfo(zookeeperQuorum, + port, rootNode); + else return new ConnectionInfo(zookeeperQuorum, port, rootNode, keytab, principal); } private final Integer port; private final String rootNode; private final String zookeeperQuorum; private final boolean isConnectionless; + private final String principal; + private final String keytab; // used for testing - ConnectionInfo(String zookeeperQuorum, Integer port, String rootNode) { + ConnectionInfo(String zookeeperQuorum, Integer port, String rootNode, String keytab, String principal) { this.zookeeperQuorum = zookeeperQuorum; this.port = port; this.rootNode = rootNode; this.isConnectionless = PhoenixRuntime.CONNECTIONLESS.equals(zookeeperQuorum); + this.principal = principal; + this.keytab = keytab; + } + + // used for testing + ConnectionInfo(String zookeeperQuorum, Integer port, String rootNode) { + this(zookeeperQuorum, port, rootNode, null, null); } public ReadOnlyProps asProps() { - Map<String,String> connectionProps = Maps.newHashMapWithExpectedSize(3); + Map<String, String> connectionProps = Maps.newHashMapWithExpectedSize(3); if (getZookeeperQuorum() != null) { connectionProps.put(QueryServices.ZOOKEEPER_QUARUM_ATTRIB, getZookeeperQuorum()); } @@ -271,7 +321,14 @@ public abstract class PhoenixEmbeddedDriver implements Driver, org.apache.phoeni if (getRootNode() != null) { connectionProps.put(QueryServices.ZOOKEEPER_ROOT_NODE_ATTRIB, getRootNode()); } - return connectionProps.isEmpty() ? ReadOnlyProps.EMPTY_PROPS : new ReadOnlyProps(connectionProps.entrySet().iterator()); + if (getKeytab() != null) { + connectionProps.put(QueryServices.HBASE_CLIENT_KEYTAB, getKeytab()); + } + if (getPrincipal() != null) { + connectionProps.put(QueryServices.HBASE_CLIENT_PRINCIPAL, getPrincipal()); + } + return connectionProps.isEmpty() ? ReadOnlyProps.EMPTY_PROPS : new ReadOnlyProps( + connectionProps.entrySet().iterator()); } public boolean isConnectionless() { @@ -289,6 +346,14 @@ public abstract class PhoenixEmbeddedDriver implements Driver, org.apache.phoeni public String getRootNode() { return rootNode; } + + public String getKeytab() { + return keytab; + } + + public String getPrincipal() { + return principal; + } @Override public int hashCode() { @@ -297,6 +362,8 @@ public abstract class PhoenixEmbeddedDriver implements Driver, org.apache.phoeni result = prime * result + ((zookeeperQuorum == null) ? 0 : zookeeperQuorum.hashCode()); result = prime * result + ((port == null) ? 0 : port.hashCode()); result = prime * result + ((rootNode == null) ? 0 : rootNode.hashCode()); + result = prime * result + ((keytab == null) ? 0 : keytab.hashCode()); + result = prime * result + ((principal == null) ? 0 : keytab.hashCode()); return result; } @@ -305,7 +372,7 @@ public abstract class PhoenixEmbeddedDriver implements Driver, org.apache.phoeni if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; - ConnectionInfo other = (ConnectionInfo)obj; + ConnectionInfo other = (ConnectionInfo) obj; if (zookeeperQuorum == null) { if (other.zookeeperQuorum != null) return false; } else if (!zookeeperQuorum.equals(other.zookeeperQuorum)) return false; @@ -315,13 +382,22 @@ public abstract class PhoenixEmbeddedDriver implements Driver, org.apache.phoeni if (rootNode == null) { if (other.rootNode != null) return false; } else if (!rootNode.equals(other.rootNode)) return false; + if (keytab == null) { + if (other.keytab != null) return false; + } else if (!keytab.equals(other.keytab)) return false; + if (principal == null) { + if (other.principal != null) return false; + } else if (!principal.equals(other.principal)) return false; return true; } @Override - public String toString() { - return zookeeperQuorum + (port == null ? "" : ":" + port) + (rootNode == null ? "" : ":" + rootNode); - } + public String toString() { + return zookeeperQuorum + (port == null ? "" : ":" + port) + + (rootNode == null ? "" : ":" + rootNode) + + (keytab == null ? "" : ":" + keytab) + + (principal == null ? "" : ":" + principal); + } } public static boolean isTestUrl(String url) { http://git-wip-us.apache.org/repos/asf/phoenix/blob/dddcffad/phoenix-core/src/main/java/org/apache/phoenix/query/ConnectionQueryServicesImpl.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/main/java/org/apache/phoenix/query/ConnectionQueryServicesImpl.java b/phoenix-core/src/main/java/org/apache/phoenix/query/ConnectionQueryServicesImpl.java index cce5f0a..acb2239 100644 --- a/phoenix-core/src/main/java/org/apache/phoenix/query/ConnectionQueryServicesImpl.java +++ b/phoenix-core/src/main/java/org/apache/phoenix/query/ConnectionQueryServicesImpl.java @@ -64,7 +64,6 @@ import java.util.concurrent.Callable; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; -import com.google.protobuf.HBaseZeroCopyByteString; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.hbase.Cell; import org.apache.hadoop.hbase.HColumnDescriptor; @@ -89,10 +88,12 @@ import org.apache.hadoop.hbase.io.ImmutableBytesWritable; import org.apache.hadoop.hbase.ipc.BlockingRpcCallback; import org.apache.hadoop.hbase.ipc.ServerRpcController; import org.apache.hadoop.hbase.protobuf.generated.ClientProtos.MutationProto; +import org.apache.hadoop.hbase.security.User; import org.apache.hadoop.hbase.util.Bytes; import org.apache.hadoop.hbase.util.Pair; import org.apache.hadoop.hbase.util.VersionInfo; import org.apache.hadoop.hbase.zookeeper.ZKConfig; +import org.apache.hadoop.security.UserGroupInformation; import org.apache.phoenix.compile.MutationPlan; import org.apache.phoenix.coprocessor.GroupedAggregateRegionObserver; import org.apache.phoenix.coprocessor.MetaDataEndpointImpl; @@ -171,6 +172,7 @@ import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import com.google.common.io.Closeables; +import com.google.protobuf.HBaseZeroCopyByteString; public class ConnectionQueryServicesImpl extends DelegateQueryServices implements ConnectionQueryServices { @@ -252,6 +254,15 @@ public class ConnectionQueryServicesImpl extends DelegateQueryServices implement private void openConnection() throws SQLException { try { + // check if we need to authenticate with kerberos + String clientKeytab = config.get(HBASE_CLIENT_KEYTAB); + String clientPrincipal = config.get(HBASE_CLIENT_PRINCIPAL); + if (clientKeytab != null && clientPrincipal != null) { + logger.info("Trying to connect to a secure cluster with keytab:" + clientKeytab); + UserGroupInformation.setConfiguration(config); + User.login(config, HBASE_CLIENT_KEYTAB, HBASE_CLIENT_PRINCIPAL, null); + logger.info("Successfull login to secure cluster!!"); + } this.connection = HBaseFactoryProvider.getHConnectionFactory().createConnection(this.config); } catch (IOException e) { throw new SQLExceptionInfo.Builder(SQLExceptionCode.CANNOT_ESTABLISH_CONNECTION) @@ -410,7 +421,7 @@ public class ConnectionQueryServicesImpl extends DelegateQueryServices implement byte[] currentKey = HConstants.EMPTY_START_ROW; do { HRegionLocation regionLocation = connection.getRegionLocation( - TableName.valueOf(tableName), currentKey, reload); + TableName.valueOf(tableName), currentKey, reload); locations.add(regionLocation); currentKey = regionLocation.getRegionInfo().getEndKey(); } while (!Bytes.equals(currentKey, HConstants.EMPTY_END_ROW)); @@ -1711,6 +1722,7 @@ public class ConnectionQueryServicesImpl extends DelegateQueryServices implement incrementSequenceValues(sequenceKeys, timestamp, values, exceptions, 1, Sequence.Action.RESERVE); } + @SuppressWarnings("deprecation") private void incrementSequenceValues(List<SequenceKey> keys, long timestamp, long[] values, SQLException[] exceptions, int factor, Sequence.Action action) throws SQLException { List<Sequence> sequences = Lists.newArrayListWithExpectedSize(keys.size()); for (SequenceKey key : keys) { @@ -1783,6 +1795,7 @@ public class ConnectionQueryServicesImpl extends DelegateQueryServices implement } } + @SuppressWarnings("deprecation") @Override public void returnSequences(List<SequenceKey> keys, long timestamp, SQLException[] exceptions) throws SQLException { List<Sequence> sequences = Lists.newArrayListWithExpectedSize(keys.size()); @@ -1856,6 +1869,7 @@ public class ConnectionQueryServicesImpl extends DelegateQueryServices implement // Take no locks, as this only gets run when there are no open connections // so there's no danger of contention. + @SuppressWarnings("deprecation") private void returnAllSequences(ConcurrentMap<SequenceKey,Sequence> sequenceMap) throws SQLException { List<Append> mutations = Lists.newArrayListWithExpectedSize(sequenceMap.size()); for (Sequence sequence : sequenceMap.values()) { @@ -2271,6 +2285,7 @@ public class ConnectionQueryServicesImpl extends DelegateQueryServices implement * @throws IOException * @throws SQLException */ + @SuppressWarnings("deprecation") private WhiteList upgradeCoprocessorsTo3_0(HBaseAdmin admin, boolean forceUpgrade) throws IOException, SQLException { String files = config.get(QueryServices.AUTO_UPGRADE_WHITELIST_ATTRIB); WhiteList coprocUpgradeWhiteList = new WhiteList(files); http://git-wip-us.apache.org/repos/asf/phoenix/blob/dddcffad/phoenix-core/src/main/java/org/apache/phoenix/query/QueryServices.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/main/java/org/apache/phoenix/query/QueryServices.java b/phoenix-core/src/main/java/org/apache/phoenix/query/QueryServices.java index 44ec932..9c751ba 100644 --- a/phoenix-core/src/main/java/org/apache/phoenix/query/QueryServices.java +++ b/phoenix-core/src/main/java/org/apache/phoenix/query/QueryServices.java @@ -44,6 +44,9 @@ public interface QueryServices extends SQLCloseable { public static final String QUEUE_SIZE_ATTRIB = "phoenix.query.queueSize"; public static final String THREAD_TIMEOUT_MS_ATTRIB = "phoenix.query.timeoutMs"; public static final String SPOOL_THRESHOLD_BYTES_ATTRIB = "phoenix.query.spoolThresholdBytes"; + public static final String HBASE_CLIENT_KEYTAB = "hbase.myclient.keytab"; + public static final String HBASE_CLIENT_PRINCIPAL = "hbase.myclient.principal"; + /** * max size to spool the the result into http://git-wip-us.apache.org/repos/asf/phoenix/blob/dddcffad/phoenix-core/src/test/java/org/apache/phoenix/jdbc/PhoenixEmbeddedDriverTest.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/test/java/org/apache/phoenix/jdbc/PhoenixEmbeddedDriverTest.java b/phoenix-core/src/test/java/org/apache/phoenix/jdbc/PhoenixEmbeddedDriverTest.java index 23c7951..a9b09e5 100644 --- a/phoenix-core/src/test/java/org/apache/phoenix/jdbc/PhoenixEmbeddedDriverTest.java +++ b/phoenix-core/src/test/java/org/apache/phoenix/jdbc/PhoenixEmbeddedDriverTest.java @@ -47,6 +47,10 @@ public class PhoenixEmbeddedDriverTest { "jdbc:phoenix:v1,v2,v3:/hbase;test=true", "jdbc:phoenix:v1,v2,v3:123:/hbase", "jdbc:phoenix:v1,v2,v3:123:/hbase;test=false", + "jdbc:phoenix:v1,v2,v3:123:/hbase:/user.keytab:user/principal;test=false", + "jdbc:phoenix:v1,v2,v3:123:/user.keytab:user/principal;test=false", + "jdbc:phoenix:v1,v2,v3:/user.keytab:user/principal;test=false", + "jdbc:phoenix:v1,v2,v3:/hbase:/user.keytab:user/principal;test=false" }; ConnectionInfo[] infos = new ConnectionInfo[] { new ConnectionInfo(null,null,null), @@ -65,6 +69,10 @@ public class PhoenixEmbeddedDriverTest { new ConnectionInfo("v1,v2,v3",null,"/hbase"), new ConnectionInfo("v1,v2,v3",123,"/hbase"), new ConnectionInfo("v1,v2,v3",123,"/hbase"), + new ConnectionInfo("v1,v2,v3",123,"/hbase", "/user.keytab","user/principal" ), + new ConnectionInfo("v1,v2,v3",123, null, "/user.keytab","user/principal" ), + new ConnectionInfo("v1,v2,v3", null, null, "/user.keytab","user/principal" ), + new ConnectionInfo("v1,v2,v3",null,"/hbase", "/user.keytab","user/principal" ) }; assertEquals(urls.length,infos.length); for (int i = 0; i < urls.length; i++) { @@ -92,6 +100,7 @@ public class PhoenixEmbeddedDriverTest { "jdbc:phoenix:v1,v2,v3:123a:/hbase;test=true", "jdbc:phoenix:v1,v2,v3:123::/hbase", "jdbc:phoenix:v1,v2,v3:123::/hbase;test=false", + "jdbc:phoenix:v1,v2,v3:123:/hbase:user;test=false" }; for (String url : urls) { try {