You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@phoenix.apache.org by ja...@apache.org on 2014/06/08 19:44:04 UTC

git commit: PHOENIX-19 Enhance JDBC connection of Phoenix to support connecting to a Secure HBase cluster (Anil Gupta)

Repository: phoenix
Updated Branches:
  refs/heads/4.0 2d9347e0b -> 3ef7df14c


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/3ef7df14
Tree: http://git-wip-us.apache.org/repos/asf/phoenix/tree/3ef7df14
Diff: http://git-wip-us.apache.org/repos/asf/phoenix/diff/3ef7df14

Branch: refs/heads/4.0
Commit: 3ef7df14ce125996ce77d1bf9e4e06abba988883
Parents: 2d9347e
Author: James Taylor <jt...@salesforce.com>
Authored: Sun Jun 8 10:45:38 2014 -0700
Committer: James Taylor <jt...@salesforce.com>
Committed: Sun Jun 8 10:45:38 2014 -0700

----------------------------------------------------------------------
 .../phoenix/jdbc/PhoenixEmbeddedDriver.java     | 100 ++++++++++++++++---
 .../query/ConnectionQueryServicesImpl.java      |  16 ++-
 .../org/apache/phoenix/query/QueryServices.java |   3 +
 .../phoenix/jdbc/PhoenixEmbeddedDriverTest.java |   9 ++
 4 files changed, 112 insertions(+), 16 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/phoenix/blob/3ef7df14/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/3ef7df14/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 912db88..716ba3d 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)
@@ -388,7 +399,6 @@ public class ConnectionQueryServicesImpl extends DelegateQueryServices implement
         return childQueryService;
     }
 
-    @SuppressWarnings("deprecation")
     @Override
     public void clearTableRegionCache(byte[] tableName) throws SQLException {
         connection.clearRegionCache(TableName.valueOf(tableName));
@@ -410,7 +420,6 @@ public class ConnectionQueryServicesImpl extends DelegateQueryServices implement
                 List<HRegionLocation> locations = Lists.newArrayList();
                 byte[] currentKey = HConstants.EMPTY_START_ROW;
                 do {
-                  @SuppressWarnings("deprecation")
                 HRegionLocation regionLocation = connection.getRegionLocation(
                     TableName.valueOf(tableName), currentKey, reload);
                   locations.add(regionLocation);
@@ -945,7 +954,6 @@ public class ConnectionQueryServicesImpl extends DelegateQueryServices implement
      * Invoke meta data coprocessor with one retry if the key was found to not be in the regions
      * (due to a table split)
      */
-    @SuppressWarnings("deprecation")
     private MetaDataMutationResult metaDataCoprocessorExec(byte[] tableKey,
             Batch.Call<MetaDataService, MetaDataResponse> callable) throws SQLException {
         try {

http://git-wip-us.apache.org/repos/asf/phoenix/blob/3ef7df14/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/3ef7df14/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 {