Repository: ambari Updated Branches: refs/heads/branch-2.5 298a50e91 -> 9dcc3f11a
AMBARI-18769. Proxy user fixes for Hive view 2.0. (Ashwin Rajeev via dipayanb) Project: http://git-wip-us.apache.org/repos/asf/ambari/repo Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/9dcc3f11 Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/9dcc3f11 Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/9dcc3f11 Branch: refs/heads/branch-2.5 Commit: 9dcc3f11a3001279e387d55471edf5d5fb1b5c6e Parents: 298a50e Author: Dipayan Bhowmick <[email protected]> Authored: Fri Nov 4 17:25:11 2016 +0530 Committer: Dipayan Bhowmick <[email protected]> Committed: Fri Nov 4 17:25:11 2016 +0530 ---------------------------------------------------------------------- .../apache/ambari/view/hive2/AuthParams.java | 102 +++++++++++++++++++ .../ambari/view/hive2/actor/JdbcConnector.java | 30 +++++- .../view/hive2/actor/message/Connect.java | 5 +- .../hive2/internal/HiveConnectionWrapper.java | 43 ++++++-- .../resources/browser/ConnectionService.java | 4 +- .../view/hive2/ConnectionFailuresTest.java | 9 +- .../ambari/view/hive2/JobExecutionTest.java | 6 +- 7 files changed, 179 insertions(+), 20 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ambari/blob/9dcc3f11/contrib/views/hive-next/src/main/java/org/apache/ambari/view/hive2/AuthParams.java ---------------------------------------------------------------------- diff --git a/contrib/views/hive-next/src/main/java/org/apache/ambari/view/hive2/AuthParams.java b/contrib/views/hive-next/src/main/java/org/apache/ambari/view/hive2/AuthParams.java new file mode 100644 index 0000000..53077e3 --- /dev/null +++ b/contrib/views/hive-next/src/main/java/org/apache/ambari/view/hive2/AuthParams.java @@ -0,0 +1,102 @@ +/** + * 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 + * <p> + * http://www.apache.org/licenses/LICENSE-2.0 + * <p> + * 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.ambari.view.hive2; + +import org.apache.ambari.view.ViewContext; +import org.apache.commons.lang3.StringUtils; +import org.apache.hadoop.security.UserGroupInformation; + +import java.io.IOException; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +/** + * Holds session parameters pulled from the + * view context + */ +public class AuthParams { + private static final String HIVE_SESSION_PARAMS = "hive.session.params"; + private Map<String, String> sessionParams = new HashMap<>(); + private final ViewContext context; + + public AuthParams(ViewContext context) { + sessionParams = parseSessionParams(context.getProperties().get(HIVE_SESSION_PARAMS)); + this.context = context; + } + + /** + * Returns a map created by parsing the parameters in view context + * @param params session parameters as string + * @return parsed session parameters + */ + private Map<String, String> parseSessionParams(String params) { + Map<String, String> sessions = new HashMap<>(); + if (StringUtils.isEmpty(params)) + return sessions; + String[] splits = params.split(";"); + for (String split : splits) { + String[] paramSplit = split.trim().split("="); + if ("auth".equals(paramSplit[0]) || "proxyuser".equals(paramSplit[0])) { + sessions.put(paramSplit[0], paramSplit[1]); + } + } + return Collections.unmodifiableMap(sessions); + } + + /** + * Gets the proxy user + * @return User and group information + * @throws IOException + */ + public UserGroupInformation getProxyUser() throws IOException { + UserGroupInformation ugi; + String proxyuser = null; + if(context.getCluster() != null) { + proxyuser = context.getCluster().getConfigurationValue("cluster-env","ambari_principal_name"); + } + + if(StringUtils.isEmpty(proxyuser)) { + if (sessionParams.containsKey("proxyuser")) { + ugi = UserGroupInformation.createRemoteUser(sessionParams.get("proxyuser")); + } else { + ugi = UserGroupInformation.getCurrentUser(); + } + } else { + ugi = UserGroupInformation.createRemoteUser(proxyuser); + } + ugi.setAuthenticationMethod(getAuthenticationMethod()); + return ugi; + } + + /** + * Get the Authentication method + * @return + */ + private UserGroupInformation.AuthenticationMethod getAuthenticationMethod() { + UserGroupInformation.AuthenticationMethod authMethod; + if (sessionParams.containsKey("auth") && !StringUtils.isEmpty(sessionParams.get("auth"))) { + String authName = sessionParams.get("auth"); + authMethod = UserGroupInformation.AuthenticationMethod.valueOf(authName.toUpperCase()); + } else { + authMethod = UserGroupInformation.AuthenticationMethod.SIMPLE; + } + return authMethod; + } +} http://git-wip-us.apache.org/repos/asf/ambari/blob/9dcc3f11/contrib/views/hive-next/src/main/java/org/apache/ambari/view/hive2/actor/JdbcConnector.java ---------------------------------------------------------------------- diff --git a/contrib/views/hive-next/src/main/java/org/apache/ambari/view/hive2/actor/JdbcConnector.java b/contrib/views/hive-next/src/main/java/org/apache/ambari/view/hive2/actor/JdbcConnector.java index fcbef81..d6d5d51 100644 --- a/contrib/views/hive-next/src/main/java/org/apache/ambari/view/hive2/actor/JdbcConnector.java +++ b/contrib/views/hive-next/src/main/java/org/apache/ambari/view/hive2/actor/JdbcConnector.java @@ -24,6 +24,7 @@ import akka.actor.PoisonPill; import akka.actor.Props; import com.google.common.base.Optional; import org.apache.ambari.view.ViewContext; +import org.apache.ambari.view.hive2.AuthParams; import org.apache.ambari.view.hive2.ConnectionDelegate; import org.apache.ambari.view.hive2.actor.message.Connect; import org.apache.ambari.view.hive2.actor.message.FetchError; @@ -78,6 +79,8 @@ public class JdbcConnector extends HiveActor { private final Logger LOG = LoggerFactory.getLogger(getClass()); + public static final String SUFFIX = "validating the login"; + /** * Interval for maximum inactivity allowed */ @@ -116,6 +119,7 @@ public class JdbcConnector extends HiveActor { private final ActorRef parent; private ActorRef statementExecutor = null; private final HdfsApi hdfsApi; + private final AuthParams authParams; /** * true if the actor is currently executing any job. @@ -153,6 +157,8 @@ public class JdbcConnector extends HiveActor { this.storage = storage; this.lastActivityTimestamp = System.currentTimeMillis(); resultSetIterator = null; + + authParams = new AuthParams(viewContext); actorConfiguration = new HiveActorConfiguration(viewContext); } @@ -383,10 +389,30 @@ public class JdbcConnector extends HiveActor { this.failure = new Failure("Cannot connect to hive", ex); if (isAsync()) { updateJobStatus(jobId.get(), Job.JOB_STATE_ERROR); + + if(ex instanceof ConnectionException){ + ConnectionException connectionException = (ConnectionException) ex; + Throwable cause = connectionException.getCause(); + if(cause instanceof SQLException){ + SQLException sqlException = (SQLException) cause; + if(isLoginError(sqlException)) + return; + } + } + } else { sender().tell(new ExecutionFailed("Cannot connect to hive"), ActorRef.noSender()); } - cleanUpWithTermination(); + // Do not clean up in case of failed authorizations + // The failure is bubbled to the user for requesting credentials + + if (!(ex instanceof SQLException) || !((SQLException) ex).getSQLState().equals("AUTHFAIL")) { + cleanUpWithTermination(); + } + } + + private boolean isLoginError(SQLException ce) { + return ce.getCause().getMessage().toLowerCase().endsWith(SUFFIX); } private void keepAlive() { @@ -410,7 +436,7 @@ public class JdbcConnector extends HiveActor { executionType = message.getType(); // check the connectable if (connectable == null) { - connectable = message.getConnectable(); + connectable = message.getConnectable(authParams); } // make the connectable to Hive try { http://git-wip-us.apache.org/repos/asf/ambari/blob/9dcc3f11/contrib/views/hive-next/src/main/java/org/apache/ambari/view/hive2/actor/message/Connect.java ---------------------------------------------------------------------- diff --git a/contrib/views/hive-next/src/main/java/org/apache/ambari/view/hive2/actor/message/Connect.java b/contrib/views/hive-next/src/main/java/org/apache/ambari/view/hive2/actor/message/Connect.java index 49ef3df..360125b 100644 --- a/contrib/views/hive-next/src/main/java/org/apache/ambari/view/hive2/actor/message/Connect.java +++ b/contrib/views/hive-next/src/main/java/org/apache/ambari/view/hive2/actor/message/Connect.java @@ -19,6 +19,7 @@ package org.apache.ambari.view.hive2.actor.message; import com.google.common.base.Optional; +import org.apache.ambari.view.hive2.AuthParams; import org.apache.ambari.view.hive2.internal.Connectable; import org.apache.ambari.view.hive2.internal.HiveConnectionWrapper; @@ -50,8 +51,8 @@ public class Connect { this(HiveJob.Type.SYNC, null, username, password, jdbcUrl); } - public Connectable getConnectable(){ - return new HiveConnectionWrapper(getJdbcUrl(),username,password); + public Connectable getConnectable(AuthParams authParams){ + return new HiveConnectionWrapper(getJdbcUrl(),username,password, authParams); } public String getUsername() { http://git-wip-us.apache.org/repos/asf/ambari/blob/9dcc3f11/contrib/views/hive-next/src/main/java/org/apache/ambari/view/hive2/internal/HiveConnectionWrapper.java ---------------------------------------------------------------------- diff --git a/contrib/views/hive-next/src/main/java/org/apache/ambari/view/hive2/internal/HiveConnectionWrapper.java b/contrib/views/hive-next/src/main/java/org/apache/ambari/view/hive2/internal/HiveConnectionWrapper.java index 3701016..92a8a2c 100644 --- a/contrib/views/hive-next/src/main/java/org/apache/ambari/view/hive2/internal/HiveConnectionWrapper.java +++ b/contrib/views/hive-next/src/main/java/org/apache/ambari/view/hive2/internal/HiveConnectionWrapper.java @@ -20,8 +20,13 @@ package org.apache.ambari.view.hive2.internal; import com.google.common.base.Optional; import com.google.common.base.Supplier; +import org.apache.ambari.view.hive2.AuthParams; +import org.apache.hadoop.security.UserGroupInformation; import org.apache.hive.jdbc.HiveConnection; +import java.io.IOException; +import java.lang.reflect.UndeclaredThrowableException; +import java.security.PrivilegedExceptionAction; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; @@ -31,24 +36,27 @@ import java.sql.SQLException; * This class only provides a connection over which * callers should run their own JDBC statements */ -public class HiveConnectionWrapper implements Connectable,Supplier<HiveConnection> { +public class HiveConnectionWrapper implements Connectable, Supplier<HiveConnection> { private static String DRIVER_NAME = "org.apache.hive.jdbc.HiveDriver"; public static final String SUFFIX = "validating the login"; private final String jdbcUrl; private final String username; private final String password; + private final AuthParams authParams; + + private UserGroupInformation ugi; private HiveConnection connection = null; private boolean authFailed; - public HiveConnectionWrapper(String jdbcUrl, String username, String password) { + public HiveConnectionWrapper(String jdbcUrl, String username, String password, AuthParams authParams) { this.jdbcUrl = jdbcUrl; this.username = username; this.password = password; + this.authParams = authParams; } - @Override public void connect() throws ConnectionException { try { @@ -58,16 +66,33 @@ public class HiveConnectionWrapper implements Connectable,Supplier<HiveConnectio } try { - Connection conn = DriverManager.getConnection(jdbcUrl, username, password); - connection = (HiveConnection)conn; + ugi = UserGroupInformation.createProxyUser(username, authParams.getProxyUser()); + } catch (IOException e) { + throw new ConnectionException(e, "Cannot set kerberos authentication for getting connection."); + } - } catch (SQLException e) { - if(isLoginError(e)) - this.authFailed = true; + try { + Connection conn = ugi.doAs(new PrivilegedExceptionAction<Connection>() { + @Override + public Connection run() throws Exception { + return DriverManager.getConnection(jdbcUrl, username, password); + } + }); + connection = (HiveConnection) conn; + } catch (UndeclaredThrowableException exception) { + // Check if the reason was an auth error + Throwable undeclaredThrowable = exception.getUndeclaredThrowable(); + if (undeclaredThrowable instanceof SQLException) { + SQLException sqlException = (SQLException) undeclaredThrowable; + if (isLoginError(sqlException)) + authFailed = true; + throw new ConnectionException(sqlException, "Cannot open a hive connection with connect string " + jdbcUrl); + } + + } catch (IOException | InterruptedException e) { throw new ConnectionException(e, "Cannot open a hive connection with connect string " + jdbcUrl); } - } @Override http://git-wip-us.apache.org/repos/asf/ambari/blob/9dcc3f11/contrib/views/hive-next/src/main/java/org/apache/ambari/view/hive2/resources/browser/ConnectionService.java ---------------------------------------------------------------------- diff --git a/contrib/views/hive-next/src/main/java/org/apache/ambari/view/hive2/resources/browser/ConnectionService.java b/contrib/views/hive-next/src/main/java/org/apache/ambari/view/hive2/resources/browser/ConnectionService.java index eb1609d..cd4d30e 100644 --- a/contrib/views/hive-next/src/main/java/org/apache/ambari/view/hive2/resources/browser/ConnectionService.java +++ b/contrib/views/hive-next/src/main/java/org/apache/ambari/view/hive2/resources/browser/ConnectionService.java @@ -19,8 +19,8 @@ package org.apache.ambari.view.hive2.resources.browser; import com.google.common.base.Optional; -import com.google.common.collect.Maps; import org.apache.ambari.view.ViewContext; +import org.apache.ambari.view.hive2.AuthParams; import org.apache.ambari.view.hive2.ConnectionFactory; import org.apache.ambari.view.hive2.ConnectionSystem; import org.apache.ambari.view.hive2.client.ConnectionConfig; @@ -97,7 +97,7 @@ public class ConnectionService { private Response attemptHiveConnection(String pass) { ConnectionConfig connectionConfig = ConnectionFactory.create(context); - HiveConnectionWrapper hiveConnectionWrapper = new HiveConnectionWrapper(connectionConfig.getJdbcUrl(), connectionConfig.getUsername(), pass); + HiveConnectionWrapper hiveConnectionWrapper = new HiveConnectionWrapper(connectionConfig.getJdbcUrl(), connectionConfig.getUsername(), pass,new AuthParams(context)); try { hiveConnectionWrapper.connect(); } catch (ConnectionException e) { http://git-wip-us.apache.org/repos/asf/ambari/blob/9dcc3f11/contrib/views/hive-next/src/test/java/org/apache/ambari/view/hive2/ConnectionFailuresTest.java ---------------------------------------------------------------------- diff --git a/contrib/views/hive-next/src/test/java/org/apache/ambari/view/hive2/ConnectionFailuresTest.java b/contrib/views/hive-next/src/test/java/org/apache/ambari/view/hive2/ConnectionFailuresTest.java index e36c1d4..7cb2dd7 100644 --- a/contrib/views/hive-next/src/test/java/org/apache/ambari/view/hive2/ConnectionFailuresTest.java +++ b/contrib/views/hive-next/src/test/java/org/apache/ambari/view/hive2/ConnectionFailuresTest.java @@ -47,6 +47,7 @@ import org.junit.Test; import java.sql.ResultSet; import java.sql.SQLException; +import java.util.HashMap; import static org.easymock.EasyMock.*; @@ -86,7 +87,8 @@ public class ConnectionFailuresTest { Props.create(OperationController.class, actorSystem, deathwatch, viewContext, connectionSupplier, dataStorageSupplier, hdfsApiSupplier), "operationController-test"); expect(hdfsApiSupplier.get(viewContext)).andReturn(Optional.of(hdfsApi)); expect(viewContext.getInstanceName()).andReturn("test").anyTimes(); - expect(connect.getConnectable()).andReturn(connectionWrapper); + expect(viewContext.getProperties()).andReturn(new HashMap<String, String>()).anyTimes(); + expect(connect.getConnectable(anyObject(AuthParams.class))).andReturn(connectionWrapper); expect(connectionWrapper.isOpen()).andReturn(false).anyTimes(); expect(connectionWrapper.getConnection()).andReturn(Optional.<HiveConnection>absent()).anyTimes(); expect(dataStorageSupplier.get(viewContext)).andReturn(storage); @@ -127,7 +129,8 @@ public class ConnectionFailuresTest { ActorRef operationControl = actorSystem.actorOf( Props.create(OperationController.class, actorSystem, deathwatch, viewContext, connectionSupplier, dataStorageSupplier, hdfsApiSupplier), "operationController-test"); expect(hdfsApiSupplier.get(viewContext)).andReturn(Optional.of(hdfsApi)); - expect(connect.getConnectable()).andReturn(connectionWrapper); + expect(viewContext.getProperties()).andReturn(new HashMap<String, String>()).anyTimes(); + expect(connect.getConnectable(anyObject(AuthParams.class))).andReturn(connectionWrapper); expect(connectionWrapper.isOpen()).andReturn(false); expect(connectionWrapper.getConnection()).andReturn(Optional.of(hiveConnection)).anyTimes(); expect(dataStorageSupplier.get(viewContext)).andReturn(storage); @@ -141,7 +144,7 @@ public class ConnectionFailuresTest { connectionWrapper.connect(); jobImpl.setStatus(Job.JOB_STATE_ERROR); storage.store(JobImpl.class, jobImpl); - replay(connect, hdfsApiSupplier, dataStorageSupplier, connectionWrapper, + replay(viewContext, connect, hdfsApiSupplier, dataStorageSupplier, connectionWrapper, storage, jobImpl, connectionSupplier, delegate, statement, resultSet); operationControl.tell(executeJob, ActorRef.noSender()); http://git-wip-us.apache.org/repos/asf/ambari/blob/9dcc3f11/contrib/views/hive-next/src/test/java/org/apache/ambari/view/hive2/JobExecutionTest.java ---------------------------------------------------------------------- diff --git a/contrib/views/hive-next/src/test/java/org/apache/ambari/view/hive2/JobExecutionTest.java b/contrib/views/hive-next/src/test/java/org/apache/ambari/view/hive2/JobExecutionTest.java index fc042ce..f8e88a4 100644 --- a/contrib/views/hive-next/src/test/java/org/apache/ambari/view/hive2/JobExecutionTest.java +++ b/contrib/views/hive-next/src/test/java/org/apache/ambari/view/hive2/JobExecutionTest.java @@ -47,6 +47,7 @@ import org.junit.Before; import org.junit.Test; import java.sql.ResultSet; +import java.util.HashMap; import static org.easymock.EasyMock.*; @@ -85,7 +86,8 @@ public class JobExecutionTest { ActorRef operationControl = actorSystem.actorOf( Props.create(OperationController.class, actorSystem, deathwatch, viewContext, connectionSupplier, dataStorageSupplier, hdfsApiSupplier), "operationController-test"); expect(hdfsApiSupplier.get(viewContext)).andReturn(Optional.of(hdfsApi)); - expect(connect.getConnectable()).andReturn(connectionWrapper); + expect(viewContext.getProperties()).andReturn(new HashMap<String, String>()).anyTimes(); + expect(connect.getConnectable(anyObject(AuthParams.class))).andReturn(connectionWrapper); expect(connectionWrapper.isOpen()).andReturn(false); expect(connectionWrapper.getConnection()).andReturn(Optional.of(hiveConnection)).anyTimes(); expect(dataStorageSupplier.get(viewContext)).andReturn(storage); @@ -100,7 +102,7 @@ public class JobExecutionTest { connectionWrapper.connect(); jobImpl.setStatus(Job.JOB_STATE_FINISHED); storage.store(JobImpl.class, jobImpl); - replay(connect, hdfsApiSupplier, dataStorageSupplier, connectionWrapper, + replay(viewContext, connect, hdfsApiSupplier, dataStorageSupplier, connectionWrapper, storage, jobImpl, connectionSupplier, delegate, statement, resultSet); operationControl.tell(executeJob, ActorRef.noSender());
