HBASE-7910 Dont use reflection for security (Mike Drob and Andrew Purtell)
Project: http://git-wip-us.apache.org/repos/asf/hbase/repo Commit: http://git-wip-us.apache.org/repos/asf/hbase/commit/354dd40c Tree: http://git-wip-us.apache.org/repos/asf/hbase/tree/354dd40c Diff: http://git-wip-us.apache.org/repos/asf/hbase/diff/354dd40c Branch: refs/heads/0.94 Commit: 354dd40c8ee7363eb6200f7d08a83966f860ba58 Parents: 225561c Author: Andrew Purtell <[email protected]> Authored: Mon Jul 21 10:05:00 2014 -0700 Committer: Andrew Purtell <[email protected]> Committed: Mon Jul 21 10:05:00 2014 -0700 ---------------------------------------------------------------------- .../org/apache/hadoop/hbase/security/User.java | 335 ++----------------- 1 file changed, 27 insertions(+), 308 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/hbase/blob/354dd40c/src/main/java/org/apache/hadoop/hbase/security/User.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/hadoop/hbase/security/User.java b/src/main/java/org/apache/hadoop/hbase/security/User.java index c0eb3a5..96e0a39 100644 --- a/src/main/java/org/apache/hadoop/hbase/security/User.java +++ b/src/main/java/org/apache/hadoop/hbase/security/User.java @@ -1,5 +1,4 @@ /* - * Copyright 2010 The Apache Software Foundation * * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file @@ -20,23 +19,18 @@ package org.apache.hadoop.hbase.security; -import org.apache.commons.logging.LogFactory; -import org.apache.hadoop.conf.Configuration; -import org.apache.hadoop.fs.CommonConfigurationKeys; -import org.apache.hadoop.hbase.HBaseConfiguration; -import org.apache.hadoop.hbase.util.Methods; -import org.apache.hadoop.mapred.JobConf; -import org.apache.hadoop.mapreduce.Job; -import org.apache.hadoop.security.token.Token; -import org.apache.hadoop.security.UserGroupInformation; - import java.io.IOException; -import java.lang.reflect.Constructor; import java.lang.reflect.UndeclaredThrowableException; import java.security.PrivilegedAction; import java.security.PrivilegedExceptionAction; -import org.apache.commons.logging.Log; +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.hbase.util.Methods; +import org.apache.hadoop.mapred.JobConf; +import org.apache.hadoop.mapreduce.Job; +import org.apache.hadoop.security.SecurityUtil; +import org.apache.hadoop.security.UserGroupInformation; +import org.apache.hadoop.security.token.Token; /** * Wrapper to abstract out usage of user and group information in HBase. @@ -53,21 +47,6 @@ public abstract class User { public static final String HBASE_SECURITY_CONF_KEY = "hbase.security.authentication"; - /** - * Flag to differentiate between API-incompatible changes to - * {@link org.apache.hadoop.security.UserGroupInformation} between vanilla - * Hadoop 0.20.x and secure Hadoop 0.20+. - */ - private static boolean IS_SECURE_HADOOP = true; - static { - try { - UserGroupInformation.class.getMethod("isSecurityEnabled"); - } catch (NoSuchMethodException nsme) { - IS_SECURE_HADOOP = false; - } - } - private static Log LOG = LogFactory.getLog(User.class); - protected UserGroupInformation ugi; public UserGroupInformation getUGI() { @@ -157,12 +136,12 @@ public abstract class User { } return ugi.equals(((User) o).ugi); } - + @Override public int hashCode() { return ugi.hashCode(); } - + @Override public String toString() { return ugi.toString(); @@ -172,12 +151,7 @@ public abstract class User { * Returns the {@code User} instance within current execution context. */ public static User getCurrent() throws IOException { - User user; - if (IS_SECURE_HADOOP) { - user = new SecureHadoopUser(); - } else { - user = new HadoopUser(); - } + User user = new SecureHadoopUser(); if (user.getUGI() == null) { return null; } @@ -193,11 +167,7 @@ public abstract class User { if (ugi == null) { return null; } - - if (IS_SECURE_HADOOP) { - return new SecureHadoopUser(ugi); - } - return new HadoopUser(ugi); + return new SecureHadoopUser(ugi); } /** @@ -208,10 +178,7 @@ public abstract class User { */ public static User createUserForTesting(Configuration conf, String name, String[] groups) { - if (IS_SECURE_HADOOP) { - return SecureHadoopUser.createUserForTesting(conf, name, groups); - } - return HadoopUser.createUserForTesting(conf, name, groups); + return SecureHadoopUser.createUserForTesting(conf, name, groups); } /** @@ -232,11 +199,7 @@ public abstract class User { */ public static void login(Configuration conf, String fileConfKey, String principalConfKey, String localhost) throws IOException { - if (IS_SECURE_HADOOP) { - SecureHadoopUser.login(conf, fileConfKey, principalConfKey, localhost); - } else { - HadoopUser.login(conf, fileConfKey, principalConfKey, localhost); - } + SecureHadoopUser.login(conf, fileConfKey, principalConfKey, localhost); } /** @@ -246,11 +209,7 @@ public abstract class User { * {@code UserGroupInformation.isSecurityEnabled()}. */ public static boolean isSecurityEnabled() { - if (IS_SECURE_HADOOP) { - return SecureHadoopUser.isSecurityEnabled(); - } else { - return HadoopUser.isSecurityEnabled(); - } + return SecureHadoopUser.isSecurityEnabled(); } /** @@ -265,236 +224,49 @@ public abstract class User { /* Concrete implementations */ /** - * Bridges {@link User} calls to invocations of the appropriate methods - * in {@link org.apache.hadoop.security.UserGroupInformation} in regular - * Hadoop 0.20 (ASF Hadoop and other versions without the backported security - * features). - */ - private static class HadoopUser extends User { - - private HadoopUser() { - try { - ugi = (UserGroupInformation) callStatic("getCurrentUGI"); - if (ugi == null) { - // Secure Hadoop UGI will perform an implicit login if the current - // user is null. Emulate the same behavior here for consistency - Configuration conf = HBaseConfiguration.create(); - ugi = (UserGroupInformation) callStatic("login", - new Class[]{ Configuration.class }, new Object[]{ conf }); - if (ugi != null) { - callStatic("setCurrentUser", - new Class[]{ UserGroupInformation.class }, new Object[]{ ugi }); - } - } - } catch (RuntimeException re) { - throw re; - } catch (Exception e) { - throw new UndeclaredThrowableException(e, - "Unexpected exception HadoopUser<init>"); - } - } - - private HadoopUser(UserGroupInformation ugi) { - this.ugi = ugi; - } - - @Override - public String getShortName() { - return ugi != null ? ugi.getUserName() : null; - } - - @Override - public <T> T runAs(PrivilegedAction<T> action) { - T result = null; - UserGroupInformation previous = null; - try { - previous = (UserGroupInformation) callStatic("getCurrentUGI"); - try { - if (ugi != null) { - callStatic("setCurrentUser", new Class[]{UserGroupInformation.class}, - new Object[]{ugi}); - } - result = action.run(); - } finally { - callStatic("setCurrentUser", new Class[]{UserGroupInformation.class}, - new Object[]{previous}); - } - } catch (RuntimeException re) { - throw re; - } catch (Exception e) { - throw new UndeclaredThrowableException(e, - "Unexpected exception in runAs()"); - } - return result; - } - - @Override - public <T> T runAs(PrivilegedExceptionAction<T> action) - throws IOException, InterruptedException { - T result = null; - try { - UserGroupInformation previous = - (UserGroupInformation) callStatic("getCurrentUGI"); - try { - if (ugi != null) { - callStatic("setCurrentUGI", new Class[]{UserGroupInformation.class}, - new Object[]{ugi}); - } - result = action.run(); - } finally { - callStatic("setCurrentUGI", new Class[]{UserGroupInformation.class}, - new Object[]{previous}); - } - } catch (Exception e) { - if (e instanceof IOException) { - throw (IOException)e; - } else if (e instanceof InterruptedException) { - throw (InterruptedException)e; - } else if (e instanceof RuntimeException) { - throw (RuntimeException)e; - } else { - throw new UndeclaredThrowableException(e, "Unknown exception in runAs()"); - } - } - return result; - } - - @Override - public void obtainAuthTokenForJob(Configuration conf, Job job) - throws IOException, InterruptedException { - // this is a no-op. token creation is only supported for kerberos - // authenticated clients - } - - @Override - public void obtainAuthTokenForJob(JobConf job) - throws IOException, InterruptedException { - // this is a no-op. token creation is only supported for kerberos - // authenticated clients - } - - /** @see User#createUserForTesting(org.apache.hadoop.conf.Configuration, String, String[]) */ - public static User createUserForTesting(Configuration conf, - String name, String[] groups) { - try { - Class c = Class.forName("org.apache.hadoop.security.UnixUserGroupInformation"); - Constructor constructor = c.getConstructor(String.class, String[].class); - if (constructor == null) { - throw new NullPointerException( - ); - } - UserGroupInformation newUser = - (UserGroupInformation)constructor.newInstance(name, groups); - // set user in configuration -- hack for regular hadoop - conf.set("hadoop.job.ugi", newUser.toString()); - return new HadoopUser(newUser); - } catch (ClassNotFoundException cnfe) { - throw new RuntimeException( - "UnixUserGroupInformation not found, is this secure Hadoop?", cnfe); - } catch (NoSuchMethodException nsme) { - throw new RuntimeException( - "No valid constructor found for UnixUserGroupInformation!", nsme); - } catch (RuntimeException re) { - throw re; - } catch (Exception e) { - throw new UndeclaredThrowableException(e, - "Unexpected exception instantiating new UnixUserGroupInformation"); - } - } - - /** - * No-op since we're running on a version of Hadoop that doesn't support - * logins. - * @see User#login(org.apache.hadoop.conf.Configuration, String, String, String) - */ - public static void login(Configuration conf, String fileConfKey, - String principalConfKey, String localhost) throws IOException { - LOG.info("Skipping login, not running on secure Hadoop"); - } - - /** Always returns {@code false}. */ - public static boolean isSecurityEnabled() { - return false; - } - } - - /** * Bridges {@code User} invocations to underlying calls to * {@link org.apache.hadoop.security.UserGroupInformation} for secure Hadoop * 0.20 and versions 0.21 and above. */ - public static class SecureHadoopUser extends User { + private static class SecureHadoopUser extends User { private String shortName; private SecureHadoopUser() throws IOException { - try { - ugi = (UserGroupInformation) callStatic("getCurrentUser"); - } catch (IOException ioe) { - throw ioe; - } catch (RuntimeException re) { - throw re; - } catch (Exception e) { - throw new UndeclaredThrowableException(e, - "Unexpected exception getting current secure user"); - } + ugi = UserGroupInformation.getCurrentUser(); } - public SecureHadoopUser(UserGroupInformation ugi) { + private SecureHadoopUser(UserGroupInformation ugi) { this.ugi = ugi; } @Override public String getShortName() { if (shortName != null) return shortName; - try { - shortName = (String)call(ugi, "getShortUserName", null, null); + shortName = ugi.getShortUserName(); return shortName; - } catch (RuntimeException re) { - throw re; } catch (Exception e) { - throw new UndeclaredThrowableException(e, - "Unexpected error getting user short name"); + throw new RuntimeException("Unexpected error getting user short name", + e); } } @Override public <T> T runAs(PrivilegedAction<T> action) { - try { - return (T) call(ugi, "doAs", new Class[]{PrivilegedAction.class}, - new Object[]{action}); - } catch (RuntimeException re) { - throw re; - } catch (Exception e) { - throw new UndeclaredThrowableException(e, - "Unexpected exception in runAs()"); - } + return ugi.doAs(action); } @Override public <T> T runAs(PrivilegedExceptionAction<T> action) throws IOException, InterruptedException { - try { - return (T) call(ugi, "doAs", - new Class[]{PrivilegedExceptionAction.class}, - new Object[]{action}); - } catch (IOException ioe) { - throw ioe; - } catch (InterruptedException ie) { - throw ie; - } catch (RuntimeException re) { - throw re; - } catch (Exception e) { - throw new UndeclaredThrowableException(e, - "Unexpected exception in runAs(PrivilegedExceptionAction)"); - } + return ugi.doAs(action); } @Override public void obtainAuthTokenForJob(Configuration conf, Job job) throws IOException, InterruptedException { try { - Class c = Class.forName( + Class<?> c = Class.forName( "org.apache.hadoop.hbase.security.token.TokenUtil"); Methods.call(c, null, "obtainTokenForJob", new Class[]{Configuration.class, UserGroupInformation.class, @@ -519,7 +291,7 @@ public abstract class User { public void obtainAuthTokenForJob(JobConf job) throws IOException, InterruptedException { try { - Class c = Class.forName( + Class<?> c = Class.forName( "org.apache.hadoop.hbase.security.token.TokenUtil"); Methods.call(c, null, "obtainTokenForJob", new Class[]{JobConf.class, UserGroupInformation.class}, @@ -542,18 +314,7 @@ public abstract class User { /** @see User#createUserForTesting(org.apache.hadoop.conf.Configuration, String, String[]) */ public static User createUserForTesting(Configuration conf, String name, String[] groups) { - try { - return new SecureHadoopUser( - (UserGroupInformation)callStatic("createUserForTesting", - new Class[]{String.class, String[].class}, - new Object[]{name, groups}) - ); - } catch (RuntimeException re) { - throw re; - } catch (Exception e) { - throw new UndeclaredThrowableException(e, - "Error creating secure test user"); - } + return new SecureHadoopUser(UserGroupInformation.createUserForTesting(name, groups)); } /** @@ -571,26 +332,7 @@ public abstract class User { public static void login(Configuration conf, String fileConfKey, String principalConfKey, String localhost) throws IOException { if (isSecurityEnabled()) { - // check for SecurityUtil class - try { - Class c = Class.forName("org.apache.hadoop.security.SecurityUtil"); - Class[] types = new Class[]{ - Configuration.class, String.class, String.class, String.class }; - Object[] args = new Object[]{ - conf, fileConfKey, principalConfKey, localhost }; - Methods.call(c, null, "login", types, args); - } catch (ClassNotFoundException cnfe) { - throw new RuntimeException("Unable to login using " + - "org.apache.hadoop.security.SecurityUtil.login(). SecurityUtil class " + - "was not found! Is this a version of secure Hadoop?", cnfe); - } catch (IOException ioe) { - throw ioe; - } catch (RuntimeException re) { - throw re; - } catch (Exception e) { - throw new UndeclaredThrowableException(e, - "Unhandled exception in User.login()"); - } + SecurityUtil.login(conf, fileConfKey, principalConfKey, localhost); } } @@ -598,30 +340,7 @@ public abstract class User { * Returns the result of {@code UserGroupInformation.isSecurityEnabled()}. */ public static boolean isSecurityEnabled() { - try { - return (Boolean)callStatic("isSecurityEnabled"); - } catch (RuntimeException re) { - throw re; - } catch (Exception e) { - throw new UndeclaredThrowableException(e, - "Unexpected exception calling UserGroupInformation.isSecurityEnabled()"); - } + return UserGroupInformation.isSecurityEnabled(); } } - - /* Reflection helper methods */ - private static Object callStatic(String methodName) throws Exception { - return call(null, methodName, null, null); - } - - private static Object callStatic(String methodName, Class[] types, - Object[] args) throws Exception { - return call(null, methodName, types, args); - } - - private static Object call(UserGroupInformation instance, String methodName, - Class[] types, Object[] args) throws Exception { - return Methods.call(UserGroupInformation.class, instance, methodName, types, - args); - } }
