HBASE-4180 Check isSecurityEnabled before User.login for 0.21 compatibility

git-svn-id: https://svn.apache.org/repos/asf/hbase/trunk@1169902 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Gary Helmling 2011-09-12 19:59:17 +00:00
parent eaa40e323e
commit c9efe7d495
4 changed files with 178 additions and 72 deletions

View File

@ -567,6 +567,7 @@ Release 0.90.5 - Unreleased
(Gaojinchao)
HBASE-4294 HLogSplitter sleeps with 1-second granularity (todd)
HBASE-4270 IOE ignored during flush-on-close causes dataloss
HBASE-4180 HBase should check the isSecurityEnabled flag before login
IMPROVEMENT
HBASE-4205 Enhance HTable javadoc (Eric Charles)

View File

@ -22,12 +22,13 @@ package org.apache.hadoop.hbase.security;
import org.apache.commons.logging.LogFactory;
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.UserGroupInformation;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.UndeclaredThrowableException;
import java.security.PrivilegedAction;
import java.security.PrivilegedExceptionAction;
@ -43,15 +44,13 @@ import org.apache.commons.logging.Log;
* {@link org.apache.hadoop.security.UserGroupInformation} currently needed by
* HBase, but can be extended as needs change.
* </p>
*
* <p>
* Note: this class does not attempt to support any of the Kerberos
* authentication methods exposed in security-enabled Hadoop (for the moment
* at least), as they're not yet needed. Properly supporting
* authentication is left up to implementation in secure HBase.
* </p>
*/
public abstract class User {
/**
* 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 {
@ -144,6 +143,20 @@ public abstract class User {
}
}
/**
* Returns whether or not Kerberos authentication is configured. For
* non-secure Hadoop, this always returns <code>false</code>.
* For secure Hadoop, it will return the value from
* {@code UserGroupInformation.isSecurityEnabled()}.
*/
public static boolean isSecurityEnabled() {
if (IS_SECURE_HADOOP) {
return SecureHadoopUser.isSecurityEnabled();
} else {
return HadoopUser.isSecurityEnabled();
}
}
/* Concrete implementations */
/**
@ -230,6 +243,7 @@ public abstract class User {
return result;
}
/** @see User#createUserForTesting(org.apache.hadoop.conf.Configuration, String, String[]) */
public static User createUserForTesting(Configuration conf,
String name, String[] groups) {
try {
@ -258,10 +272,20 @@ public abstract class User {
}
}
/**
* 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;
}
}
/**
@ -331,6 +355,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 {
@ -347,8 +372,21 @@ public abstract class User {
}
}
/**
* Obtain credentials for the current process using the configured
* Kerberos keytab file and principal.
* @see User#login(org.apache.hadoop.conf.Configuration, String, String, String)
*
* @param conf the Configuration to use
* @param fileConfKey Configuration property key used to store the path
* to the keytab file
* @param principalConfKey Configuration property key used to store the
* principal name to login as
* @param localhost the local hostname
*/
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");
@ -356,10 +394,10 @@ public abstract class User {
Configuration.class, String.class, String.class, String.class };
Object[] args = new Object[]{
conf, fileConfKey, principalConfKey, localhost };
call(c, null, "login", types, args);
Methods.call(c, null, "login", types, args);
} catch (ClassNotFoundException cnfe) {
throw new RuntimeException("Unable to login using " +
"org.apache.hadoop.security.Security.login(). SecurityUtil class " +
"org.apache.hadoop.security.SecurityUtil.login(). SecurityUtil class " +
"was not found! Is this a version of secure Hadoop?", cnfe);
} catch (IOException ioe) {
throw ioe;
@ -372,6 +410,21 @@ 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()");
}
}
}
/* Reflection helper methods */
private static Object callStatic(String methodName) throws Exception {
return call(null, methodName, null, null);
@ -384,54 +437,7 @@ public abstract class User {
private static Object call(UserGroupInformation instance, String methodName,
Class[] types, Object[] args) throws Exception {
return call(UserGroupInformation.class, instance, methodName, types, args);
}
private static <T> Object call(Class<T> clazz, T instance, String methodName,
Class[] types, Object[] args) throws Exception {
try {
Method m = clazz.getMethod(methodName, types);
return m.invoke(instance, args);
} catch (IllegalArgumentException arge) {
LOG.fatal("Constructed invalid call. class="+clazz.getName()+
" method=" + methodName + " types=" + stringify(types), arge);
throw arge;
} catch (NoSuchMethodException nsme) {
throw new IllegalArgumentException(
"Can't find method "+methodName+" in "+clazz.getName()+"!", nsme);
} catch (InvocationTargetException ite) {
// unwrap the underlying exception and rethrow
if (ite.getTargetException() != null) {
if (ite.getTargetException() instanceof Exception) {
throw (Exception)ite.getTargetException();
} else if (ite.getTargetException() instanceof Error) {
throw (Error)ite.getTargetException();
}
}
throw new UndeclaredThrowableException(ite,
"Unknown exception invoking "+clazz.getName()+"."+methodName+"()");
} catch (IllegalAccessException iae) {
throw new IllegalArgumentException(
"Denied access calling "+clazz.getName()+"."+methodName+"()", iae);
} catch (SecurityException se) {
LOG.fatal("SecurityException calling method. class="+clazz.getName()+
" method=" + methodName + " types=" + stringify(types), se);
throw se;
}
}
private static String stringify(Class[] classes) {
StringBuilder buf = new StringBuilder();
if (classes != null) {
for (Class c : classes) {
if (buf.length() > 0) {
buf.append(",");
}
buf.append(c.getName());
}
} else {
buf.append("NULL");
}
return buf.toString();
return Methods.call(UserGroupInformation.class, instance, methodName, types,
args);
}
}

View File

@ -1,3 +1,23 @@
/*
* Copyright The Apache Software Foundation
*
* 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.util;
/**
@ -41,4 +61,18 @@ public class Classes {
return valueType;
}
public static String stringify(Class[] classes) {
StringBuilder buf = new StringBuilder();
if (classes != null) {
for (Class c : classes) {
if (buf.length() > 0) {
buf.append(",");
}
buf.append(c.getName());
}
} else {
buf.append("NULL");
}
return buf.toString();
}
}

View File

@ -0,0 +1,65 @@
/*
* Copyright The Apache Software Foundation
*
* 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.util;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.UndeclaredThrowableException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
public class Methods {
private static Log LOG = LogFactory.getLog(Methods.class);
public static <T> Object call(Class<T> clazz, T instance, String methodName,
Class[] types, Object[] args) throws Exception {
try {
Method m = clazz.getMethod(methodName, types);
return m.invoke(instance, args);
} catch (IllegalArgumentException arge) {
LOG.fatal("Constructed invalid call. class="+clazz.getName()+
" method=" + methodName + " types=" + Classes.stringify(types), arge);
throw arge;
} catch (NoSuchMethodException nsme) {
throw new IllegalArgumentException(
"Can't find method "+methodName+" in "+clazz.getName()+"!", nsme);
} catch (InvocationTargetException ite) {
// unwrap the underlying exception and rethrow
if (ite.getTargetException() != null) {
if (ite.getTargetException() instanceof Exception) {
throw (Exception)ite.getTargetException();
} else if (ite.getTargetException() instanceof Error) {
throw (Error)ite.getTargetException();
}
}
throw new UndeclaredThrowableException(ite,
"Unknown exception invoking "+clazz.getName()+"."+methodName+"()");
} catch (IllegalAccessException iae) {
throw new IllegalArgumentException(
"Denied access calling "+clazz.getName()+"."+methodName+"()", iae);
} catch (SecurityException se) {
LOG.fatal("SecurityException calling method. class="+clazz.getName()+
" method=" + methodName + " types=" + Classes.stringify(types), se);
throw se;
}
}
}