From c422b19cdd1ad40c83f8a4a66505e92a497d54ed Mon Sep 17 00:00:00 2001 From: srdo Date: Mon, 11 Mar 2019 12:06:55 +0100 Subject: [PATCH] HBase-22027: Split non-MR related parts of TokenUtil off into a ClientTokenUtil, and move ClientTokenUtil to hbase-client --- .../hbase/security/token/ClientTokenUtil.java | 169 ++++++++++++++++++ .../security/token/TestClientTokenUtil.java | 14 +- .../hbase/security/token/TokenUtil.java | 141 ++++----------- 3 files changed, 207 insertions(+), 117 deletions(-) create mode 100644 hbase-client/src/main/java/org/apache/hadoop/hbase/security/token/ClientTokenUtil.java rename hbase-server/src/test/java/org/apache/hadoop/hbase/security/token/TestTokenUtil.java => hbase-client/src/test/java/org/apache/hadoop/hbase/security/token/TestClientTokenUtil.java (82%) diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/security/token/ClientTokenUtil.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/security/token/ClientTokenUtil.java new file mode 100644 index 0000000000..ad3bf6e6a3 --- /dev/null +++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/security/token/ClientTokenUtil.java @@ -0,0 +1,169 @@ +/* + * 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.hbase.security.token; + +import com.google.protobuf.ByteString; +import com.google.protobuf.ServiceException; +import java.io.IOException; +import java.lang.reflect.UndeclaredThrowableException; +import java.security.PrivilegedExceptionAction; +import org.apache.hadoop.hbase.HConstants; +import org.apache.hadoop.hbase.TableName; +import org.apache.hadoop.hbase.client.Connection; +import org.apache.hadoop.hbase.client.Table; +import org.apache.hadoop.hbase.ipc.CoprocessorRpcChannel; +import org.apache.hadoop.hbase.protobuf.generated.AuthenticationProtos; +import org.apache.hadoop.hbase.security.User; +import org.apache.hadoop.io.Text; +import org.apache.hadoop.security.token.Token; +import org.apache.yetus.audience.InterfaceAudience; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil; + +/** + * Utility methods for obtaining authentication tokens, that do not require hbase-server. + */ +@InterfaceAudience.Public +public final class ClientTokenUtil { + private static final Logger LOG = LoggerFactory.getLogger(ClientTokenUtil.class); + + // Set in TestClientTokenUtil via reflection + private static ServiceException injectedException; + + private ClientTokenUtil() {} + + private static void injectFault() throws ServiceException { + if (injectedException != null) { + throw injectedException; + } + } + + /** + * Obtain and return an authentication token for the current user. + * @param conn The HBase cluster connection + * @throws IOException if a remote error or serialization problem occurs. + * @return the authentication token instance + */ + @InterfaceAudience.Private + public static Token obtainToken( + Connection conn) throws IOException { + Table meta = null; + try { + injectFault(); + + meta = conn.getTable(TableName.META_TABLE_NAME); + CoprocessorRpcChannel rpcChannel = meta.coprocessorService( + HConstants.EMPTY_START_ROW); + AuthenticationProtos.AuthenticationService.BlockingInterface service = + AuthenticationProtos.AuthenticationService.newBlockingStub(rpcChannel); + AuthenticationProtos.GetAuthenticationTokenResponse response = + service.getAuthenticationToken(null, + AuthenticationProtos.GetAuthenticationTokenRequest.getDefaultInstance()); + + return toToken(response.getToken()); + } catch (ServiceException se) { + throw ProtobufUtil.handleRemoteException(se); + } finally { + if (meta != null) { + meta.close(); + } + } + } + + /** + * Converts a Token instance (with embedded identifier) to the protobuf representation. + * + * @param token the Token instance to copy + * @return the protobuf Token message + */ + @InterfaceAudience.Private + public static AuthenticationProtos.Token toToken(Token token) { + AuthenticationProtos.Token.Builder builder = AuthenticationProtos.Token.newBuilder(); + builder.setIdentifier(ByteString.copyFrom(token.getIdentifier())); + builder.setPassword(ByteString.copyFrom(token.getPassword())); + if (token.getService() != null) { + builder.setService(ByteString.copyFromUtf8(token.getService().toString())); + } + return builder.build(); + } + + /** + * Converts a protobuf Token message back into a Token instance. + * + * @param proto the protobuf Token message + * @return the Token instance + */ + @InterfaceAudience.Private + public static Token toToken(AuthenticationProtos.Token proto) { + return new Token<>( + proto.hasIdentifier() ? proto.getIdentifier().toByteArray() : null, + proto.hasPassword() ? proto.getPassword().toByteArray() : null, + AuthenticationTokenIdentifier.AUTH_TOKEN_TYPE, + proto.hasService() ? new Text(proto.getService().toStringUtf8()) : null); + } + + /** + * Obtain and return an authentication token for the given user. + * @param conn The HBase cluster connection + * @param user The user to obtain a token for + * @return the authentication token instance + */ + @InterfaceAudience.Private + public static Token obtainToken( + final Connection conn, User user) throws IOException, InterruptedException { + return user.runAs(new PrivilegedExceptionAction>() { + @Override + public Token run() throws Exception { + return obtainToken(conn); + } + }); + } + + /** + * Obtain an authentication token for the given user and add it to the + * user's credentials. + * @param conn The HBase cluster connection + * @param user The user for whom to obtain the token + * @throws IOException If making a remote call to the authentication service fails + * @throws InterruptedException If executing as the given user is interrupted + */ + public static void obtainAndCacheToken(final Connection conn, + User user) + throws IOException, InterruptedException { + try { + Token token = obtainToken(conn, user); + + if (token == null) { + throw new IOException("No token returned for user " + user.getName()); + } + if (LOG.isDebugEnabled()) { + LOG.debug("Obtained token " + token.getKind().toString() + " for user " + + user.getName()); + } + user.addToken(token); + } catch (IOException | InterruptedException | RuntimeException e) { + throw e; + } catch (Exception e) { + throw new UndeclaredThrowableException(e, + "Unexpected exception obtaining token for user " + user.getName()); + } + } +} diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/security/token/TestTokenUtil.java b/hbase-client/src/test/java/org/apache/hadoop/hbase/security/token/TestClientTokenUtil.java similarity index 82% rename from hbase-server/src/test/java/org/apache/hadoop/hbase/security/token/TestTokenUtil.java rename to hbase-client/src/test/java/org/apache/hadoop/hbase/security/token/TestClientTokenUtil.java index 32fcddb85f..ae4d2bbfb5 100644 --- a/hbase-server/src/test/java/org/apache/hadoop/hbase/security/token/TestTokenUtil.java +++ b/hbase-client/src/test/java/org/apache/hadoop/hbase/security/token/TestClientTokenUtil.java @@ -35,27 +35,27 @@ import org.junit.experimental.categories.Category; import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil; @Category(SmallTests.class) -public class TestTokenUtil { +public class TestClientTokenUtil { @ClassRule public static final HBaseClassTestRule CLASS_RULE = - HBaseClassTestRule.forClass(TestTokenUtil.class); + HBaseClassTestRule.forClass(TestClientTokenUtil.class); @Test public void testObtainToken() throws Exception { URL urlPU = ProtobufUtil.class.getProtectionDomain().getCodeSource().getLocation(); - URL urlTU = TokenUtil.class.getProtectionDomain().getCodeSource().getLocation(); + URL urlCTU = ClientTokenUtil.class.getProtectionDomain().getCodeSource().getLocation(); - ClassLoader cl = new URLClassLoader(new URL[] { urlPU, urlTU }, getClass().getClassLoader()); + ClassLoader cl = new URLClassLoader(new URL[] { urlPU, urlCTU }, getClass().getClassLoader()); Throwable injected = new com.google.protobuf.ServiceException("injected"); - Class tokenUtil = cl.loadClass(TokenUtil.class.getCanonicalName()); - Field shouldInjectFault = tokenUtil.getDeclaredField("injectedException"); + Class clientTokenUtil = cl.loadClass(ClientTokenUtil.class.getCanonicalName()); + Field shouldInjectFault = clientTokenUtil.getDeclaredField("injectedException"); shouldInjectFault.setAccessible(true); shouldInjectFault.set(null, injected); try { - tokenUtil.getMethod("obtainToken", Connection.class) + clientTokenUtil.getMethod("obtainToken", Connection.class) .invoke(null, new Object[] { null }); fail("Should have injected exception."); } catch (InvocationTargetException e) { diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/security/token/TokenUtil.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/security/token/TokenUtil.java index c54d905884..6e5a51deee 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/security/token/TokenUtil.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/security/token/TokenUtil.java @@ -20,22 +20,12 @@ package org.apache.hadoop.hbase.security.token; import java.io.IOException; import java.lang.reflect.UndeclaredThrowableException; -import java.security.PrivilegedExceptionAction; - -import com.google.protobuf.ByteString; -import com.google.protobuf.ServiceException; - -import org.apache.hadoop.hbase.zookeeper.ZKWatcher; import org.apache.hadoop.conf.Configuration; -import org.apache.hadoop.hbase.HConstants; -import org.apache.hadoop.hbase.TableName; import org.apache.hadoop.hbase.client.Connection; -import org.apache.hadoop.hbase.client.Table; -import org.apache.hadoop.hbase.ipc.CoprocessorRpcChannel; import org.apache.hadoop.hbase.protobuf.generated.AuthenticationProtos; import org.apache.hadoop.hbase.security.User; -import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil; import org.apache.hadoop.hbase.zookeeper.ZKClusterId; +import org.apache.hadoop.hbase.zookeeper.ZKWatcher; import org.apache.hadoop.io.Text; import org.apache.hadoop.mapred.JobConf; import org.apache.hadoop.mapreduce.Job; @@ -53,115 +43,60 @@ public class TokenUtil { // This class is referenced indirectly by User out in common; instances are created by reflection private static final Logger LOG = LoggerFactory.getLogger(TokenUtil.class); - // Set in TestTokenUtil via reflection - private static ServiceException injectedException; - - private static void injectFault() throws ServiceException { - if (injectedException != null) { - throw injectedException; - } - } - /** - * Obtain and return an authentication token for the current user. - * @param conn The HBase cluster connection - * @throws IOException if a remote error or serialization problem occurs. - * @return the authentication token instance + * See {@link ClientTokenUtil#obtainToken(org.apache.hadoop.hbase.client.Connection)}. + * @deprecated Please use the corresponding method in {@link ClientTokenUtil} instead. */ + @Deprecated public static Token obtainToken( Connection conn) throws IOException { - Table meta = null; - try { - injectFault(); - - meta = conn.getTable(TableName.META_TABLE_NAME); - CoprocessorRpcChannel rpcChannel = meta.coprocessorService(HConstants.EMPTY_START_ROW); - AuthenticationProtos.AuthenticationService.BlockingInterface service = - AuthenticationProtos.AuthenticationService.newBlockingStub(rpcChannel); - AuthenticationProtos.GetAuthenticationTokenResponse response = service.getAuthenticationToken(null, - AuthenticationProtos.GetAuthenticationTokenRequest.getDefaultInstance()); - - return toToken(response.getToken()); - } catch (ServiceException se) { - throw ProtobufUtil.handleRemoteException(se); - } finally { - if (meta != null) { - meta.close(); - } - } + return ClientTokenUtil.obtainToken(conn); } /** - * Converts a Token instance (with embedded identifier) to the protobuf representation. - * - * @param token the Token instance to copy - * @return the protobuf Token message + * See {@link ClientTokenUtil#toToken(org.apache.hadoop.security.token.Token)}. + * @deprecated Please use the corresponding method in {@link ClientTokenUtil} instead. */ + @Deprecated public static AuthenticationProtos.Token toToken(Token token) { - AuthenticationProtos.Token.Builder builder = AuthenticationProtos.Token.newBuilder(); - builder.setIdentifier(ByteString.copyFrom(token.getIdentifier())); - builder.setPassword(ByteString.copyFrom(token.getPassword())); - if (token.getService() != null) { - builder.setService(ByteString.copyFromUtf8(token.getService().toString())); - } - return builder.build(); + return ClientTokenUtil.toToken(token); } /** - * Obtain and return an authentication token for the current user. - * @param conn The HBase cluster connection - * @return the authentication token instance + * See {@link ClientTokenUtil#obtainToken(org.apache.hadoop.hbase.client.Connection, + * org.apache.hadoop.hbase.security.User)}. + * @deprecated Please use the corresponding method in {@link ClientTokenUtil} instead. */ + @Deprecated public static Token obtainToken( final Connection conn, User user) throws IOException, InterruptedException { - return user.runAs(new PrivilegedExceptionAction>() { - @Override - public Token run() throws Exception { - return obtainToken(conn); - } - }); - } - - - private static Text getClusterId(Token token) - throws IOException { - return token.getService() != null - ? token.getService() : new Text("default"); + return ClientTokenUtil.obtainToken(conn, user); } /** - * Obtain an authentication token for the given user and add it to the - * user's credentials. - * @param conn The HBase cluster connection - * @param user The user for whom to obtain the token - * @throws IOException If making a remote call to the authentication service fails - * @throws InterruptedException If executing as the given user is interrupted + * See {@link ClientTokenUtil#obtainAndCacheToken(org.apache.hadoop.hbase.client.Connection, + * org.apache.hadoop.hbase.security.User)}. */ public static void obtainAndCacheToken(final Connection conn, User user) throws IOException, InterruptedException { - try { - Token token = obtainToken(conn, user); + ClientTokenUtil.obtainAndCacheToken(conn, user); + } - if (token == null) { - throw new IOException("No token returned for user " + user.getName()); - } - if (LOG.isDebugEnabled()) { - LOG.debug("Obtained token " + token.getKind().toString() + " for user " + - user.getName()); - } - user.addToken(token); - } catch (IOException ioe) { - throw ioe; - } catch (InterruptedException ie) { - throw ie; - } catch (RuntimeException re) { - throw re; - } catch (Exception e) { - throw new UndeclaredThrowableException(e, - "Unexpected exception obtaining token for user " + user.getName()); - } + /** + * See {@link ClientTokenUtil#toToken(org.apache.hadoop.security.token.Token)}. + * @deprecated Please use the corresponding method in {@link ClientTokenUtil} instead. + */ + @Deprecated + public static Token toToken(AuthenticationProtos.Token proto) { + return ClientTokenUtil.toToken(proto); + } + + private static Text getClusterId(Token token) + throws IOException { + return token.getService() != null + ? token.getService() : new Text("default"); } /** @@ -314,18 +249,4 @@ public class TokenUtil { zkw.close(); } } - - /** - * Converts a protobuf Token message back into a Token instance. - * - * @param proto the protobuf Token message - * @return the Token instance - */ - public static Token toToken(AuthenticationProtos.Token proto) { - return new Token<>( - proto.hasIdentifier() ? proto.getIdentifier().toByteArray() : null, - proto.hasPassword() ? proto.getPassword().toByteArray() : null, - AuthenticationTokenIdentifier.AUTH_TOKEN_TYPE, - proto.hasService() ? new Text(proto.getService().toStringUtf8()) : null); - } } -- 2.19.2