1919import com .google .common .base .Joiner ;
2020import com .google .common .base .Preconditions ;
2121import com .google .common .collect .ImmutableMap ;
22+ import java .util .HashMap ;
2223import java .util .List ;
2324import java .util .Map ;
2425import javax .annotation .Nonnegative ;
2526import javax .annotation .Nonnull ;
27+ import javax .annotation .Nullable ;
2628import javax .annotation .concurrent .NotThreadSafe ;
29+ import javax .security .sasl .Sasl ;
30+ import javax .security .sasl .SaslException ;
2731import org .apache .thrift .TConfiguration ;
2832import org .apache .thrift .protocol .TBinaryProtocol ;
2933import org .apache .thrift .protocol .TProtocol ;
34+ import org .apache .thrift .transport .TSaslClientTransport ;
3035import org .apache .thrift .transport .TSocket ;
3136import org .apache .thrift .transport .TTransport ;
3237import org .apache .thrift .transport .TTransportException ;
@@ -70,6 +75,7 @@ public static enum UnavailableClientVersionBehavior {
7075 @ Nonnull private String name = "unnamed-thrift-client" ;
7176 @ Nonnull private String host = "localhost" ;
7277 @ Nonnegative private int port ;
78+ @ Nullable private String kerberosUrl ;
7379
7480 @ Nonnull
7581 private UnavailableClientVersionBehavior unavailableClientBehavior =
@@ -89,6 +95,7 @@ public Builder(@Nonnull Builder builder) {
8995 this .port = builder .port ;
9096 this .unavailableClientBehavior = builder .unavailableClientBehavior ;
9197 this .debug = builder .debug ;
98+ this .kerberosUrl = builder .kerberosUrl ;
9299 }
93100
94101 @ Nonnull
@@ -109,6 +116,12 @@ public Builder withPort(@Nonnegative int port) {
109116 return this ;
110117 }
111118
119+ @ Nonnull
120+ public Builder withKerberosUrl (@ Nullable String kerberosUrl ) {
121+ this .kerberosUrl = kerberosUrl ;
122+ return this ;
123+ }
124+
112125 @ Nonnull
113126 public Builder withUnavailableClientVersionBehavior (
114127 @ Nonnull UnavailableClientVersionBehavior behavior ) {
@@ -123,15 +136,7 @@ public Builder withDebug(boolean value) {
123136 }
124137
125138 @ Nonnull
126- public HiveMetastoreThriftClient build () throws TTransportException {
127-
128- // We used to support Kerberos authentication, but that was when we used the Hive metastore
129- // client
130- // wrapper around Thrift. Now that we are connecting via Thrift directly, we would need to
131- // wrap
132- // the TTransport here with a TSaslClientTransport parameterized accordingly. This hasn't been
133- // done yet
134- // in the interest of expediency.
139+ private TTransport createTransport () throws SaslException , TTransportException {
135140 TTransport transport =
136141 new TSocket (
137142 new TConfiguration (
@@ -140,14 +145,41 @@ public HiveMetastoreThriftClient build() throws TTransportException {
140145 TConfiguration .DEFAULT_RECURSION_DEPTH ),
141146 host ,
142147 port );
148+
149+ if (kerberosUrl == null ) {
150+ return transport ;
151+ }
152+
153+ String [] urlParts = kerberosUrl .split ("/" );
154+
155+ if (urlParts .length != 2 ) {
156+ throw new IllegalArgumentException (
157+ "Please provide an URL in the format of `principal/cluster`" );
158+ }
159+
160+ Map <String , String > saslProperties = new HashMap <>();
161+ saslProperties .put (Sasl .SERVER_AUTH , "true" );
162+ saslProperties .put (Sasl .QOP , "auth-conf" );
163+
164+ // See:
165+ // https://docs.oracle.com/javase/8/docs/technotes/guides/security/jgss/single-signon.html
166+ return new TSaslClientTransport (
167+ "GSSAPI" , null , urlParts [0 ], urlParts [1 ], saslProperties , null , transport );
168+ }
169+
170+ @ Nonnull
171+ public HiveMetastoreThriftClient build () throws TTransportException , SaslException {
172+ TTransport transport = createTransport ();
173+
143174 TProtocol protocol = new TBinaryProtocol (transport );
144175 transport .open ();
145176
146177 final HiveMetastoreThriftClient client ;
147178 if (supportedVersionMappings .containsKey (requestedVersionString )) {
148179 if (debug )
149180 LOG .debug (
150- "The request for Hive metastore Thrift client version '{}' is satisfiable; building it now." ,
181+ "The request for Hive metastore Thrift client version '{}' is satisfiable; building"
182+ + " it now." ,
151183 requestedVersionString );
152184 client = supportedVersionMappings .get (requestedVersionString ).provide (name , protocol );
153185 } else {
@@ -161,9 +193,10 @@ public HiveMetastoreThriftClient build() throws TTransportException {
161193 if (unavailableClientBehavior == UnavailableClientVersionBehavior .FALLBACK ) {
162194 LOG .warn (
163195 messagePrefix
164- + " The caller requested fallback behavior, so a client compiled against a superset Thrift specification will be used instead. "
165- + "If you encounter an error when using the fallback client, please contact CompilerWorks support and provide "
166- + "the originally requested version number." );
196+ + " The caller requested fallback behavior, so a client compiled against a"
197+ + " superset Thrift specification will be used instead. If you encounter an error"
198+ + " when using the fallback client, please contact CompilerWorks support and"
199+ + " provide the originally requested version number." );
167200 client = new HiveMetastoreThriftClient_Superset (name , protocol );
168201 } else {
169202 throw new UnsupportedOperationException (
0 commit comments