HBASE-3194 HBase should run on both secure and vanilla versions of Hadoop 0.20 -- forgot to add classes
git-svn-id: https://svn.apache.org/repos/asf/hbase/trunk@1033089 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
43f81f1943
commit
022a7372d0
|
@ -0,0 +1,279 @@
|
|||
/*
|
||||
* Copyright 2010 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.security;
|
||||
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.apache.hadoop.conf.Configuration;
|
||||
import org.apache.hadoop.security.UserGroupInformation;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.UndeclaredThrowableException;
|
||||
import java.security.PrivilegedAction;
|
||||
import java.security.PrivilegedExceptionAction;
|
||||
import org.apache.commons.logging.Log;
|
||||
|
||||
/**
|
||||
* Wrapper to abstract out usage of user and group information in HBase.
|
||||
*
|
||||
* <p>
|
||||
* This class provides a common interface for interacting with user and group
|
||||
* information across changing APIs in different versions of Hadoop. It only
|
||||
* provides access to the common set of functionality in
|
||||
* {@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 {
|
||||
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;
|
||||
|
||||
/**
|
||||
* Returns the full user name. For Kerberos principals this will include
|
||||
* the host and realm portions of the principal name.
|
||||
* @return
|
||||
*/
|
||||
public String getName() {
|
||||
return ugi.getUserName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the shortened version of the user name -- the portion that maps
|
||||
* to an operating system user name.
|
||||
* @return
|
||||
*/
|
||||
public abstract String getShortName();
|
||||
|
||||
/**
|
||||
* Executes the given action within the context of this user.
|
||||
*/
|
||||
public abstract <T> T runAs(PrivilegedAction<T> action);
|
||||
|
||||
/**
|
||||
* Executes the given action within the context of this user.
|
||||
*/
|
||||
public abstract <T> T runAs(PrivilegedExceptionAction<T> action)
|
||||
throws IOException, InterruptedException;
|
||||
|
||||
public String toString() {
|
||||
return ugi.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@code User} instance within current execution context.
|
||||
*/
|
||||
public static User getCurrent() {
|
||||
if (IS_SECURE_HADOOP) {
|
||||
return new SecureHadoopUser();
|
||||
} else {
|
||||
return new HadoopUser();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a new {@code User} instance specifically for use in test code.
|
||||
* @param name the full username
|
||||
* @param groups the group names to which the test user will belong
|
||||
* @return a new <code>User</code> instance
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
/* 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() {
|
||||
ugi = (UserGroupInformation) callStatic("getCurrentUGI");
|
||||
}
|
||||
|
||||
private HadoopUser(UserGroupInformation ugi) {
|
||||
this.ugi = ugi;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getShortName() {
|
||||
return ugi.getUserName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T runAs(PrivilegedAction<T> action) {
|
||||
UserGroupInformation previous =
|
||||
(UserGroupInformation) callStatic("getCurrentUGI");
|
||||
if (ugi != null) {
|
||||
callStatic("setCurrentUser", new Class[]{UserGroupInformation.class},
|
||||
new Object[]{ugi});
|
||||
}
|
||||
T result = action.run();
|
||||
callStatic("setCurrentUser", new Class[]{UserGroupInformation.class},
|
||||
new Object[]{previous});
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T runAs(PrivilegedExceptionAction<T> action)
|
||||
throws IOException, InterruptedException {
|
||||
UserGroupInformation previous =
|
||||
(UserGroupInformation) callStatic("getCurrentUGI");
|
||||
if (ugi != null) {
|
||||
callStatic("setCurrentUGI", new Class[]{UserGroupInformation.class},
|
||||
new Object[]{ugi});
|
||||
}
|
||||
T result = null;
|
||||
try {
|
||||
result = action.run();
|
||||
} 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()");
|
||||
}
|
||||
} finally {
|
||||
callStatic("setCurrentUGI", new Class[]{UserGroupInformation.class},
|
||||
new Object[]{previous});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
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) {
|
||||
LOG.error("UnixUserGroupInformation not found, is this secure Hadoop?", cnfe);
|
||||
} catch (NoSuchMethodException nsme) {
|
||||
LOG.error("No valid constructor found for UnixUserGroupInformation!", nsme);
|
||||
} catch (Exception e) {
|
||||
LOG.error("Error instantiating new UnixUserGroupInformation", e);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
private static class SecureHadoopUser extends User {
|
||||
private SecureHadoopUser() {
|
||||
ugi = (UserGroupInformation) callStatic("getCurrentUser");
|
||||
}
|
||||
|
||||
private SecureHadoopUser(UserGroupInformation ugi) {
|
||||
this.ugi = ugi;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getShortName() {
|
||||
return (String)call(ugi, "getShortUserName", null, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T runAs(PrivilegedAction<T> action) {
|
||||
return (T) call(ugi, "doAs", new Class[]{PrivilegedAction.class},
|
||||
new Object[]{action});
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T runAs(PrivilegedExceptionAction<T> action)
|
||||
throws IOException, InterruptedException {
|
||||
return (T) call(ugi, "doAs",
|
||||
new Class[]{PrivilegedExceptionAction.class},
|
||||
new Object[]{action});
|
||||
}
|
||||
|
||||
public static User createUserForTesting(Configuration conf,
|
||||
String name, String[] groups) {
|
||||
return new SecureHadoopUser(
|
||||
(UserGroupInformation)callStatic("createUserForTesting",
|
||||
new Class[]{String.class, String[].class},
|
||||
new Object[]{name, groups})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/* Reflection helper methods */
|
||||
private static Object callStatic(String methodName) {
|
||||
return call(null, methodName, null, null);
|
||||
}
|
||||
|
||||
private static Object callStatic(String methodName, Class[] types,
|
||||
Object[] args) {
|
||||
return call(null, methodName, types, args);
|
||||
}
|
||||
|
||||
private static Object call(UserGroupInformation instance, String methodName,
|
||||
Class[] types, Object[] args) {
|
||||
try {
|
||||
Method m = UserGroupInformation.class.getMethod(methodName, types);
|
||||
return m.invoke(instance, args);
|
||||
} catch (NoSuchMethodException nsme) {
|
||||
LOG.fatal("Can't find method "+methodName+" in UserGroupInformation!",
|
||||
nsme);
|
||||
} catch (Exception e) {
|
||||
LOG.fatal("Error calling method "+methodName, e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
/*
|
||||
* Copyright 2010 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.security;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import org.apache.hadoop.conf.Configuration;
|
||||
import org.apache.hadoop.hbase.HBaseConfiguration;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.security.PrivilegedAction;
|
||||
import java.security.PrivilegedExceptionAction;
|
||||
|
||||
public class TestUser {
|
||||
@Test
|
||||
public void testBasicAttributes() throws Exception {
|
||||
Configuration conf = HBaseConfiguration.create();
|
||||
User user = User.createUserForTesting(conf, "simple", new String[]{"foo"});
|
||||
assertEquals("Username should match", "simple", user.getName());
|
||||
assertEquals("Short username should match", "simple", user.getShortName());
|
||||
// don't test shortening of kerberos names because regular Hadoop doesn't support them
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRunAs() throws Exception {
|
||||
Configuration conf = HBaseConfiguration.create();
|
||||
final User user = User.createUserForTesting(conf, "testuser", new String[]{"foo"});
|
||||
final PrivilegedAction<String> action = new PrivilegedAction<String>(){
|
||||
public String run() {
|
||||
User u = User.getCurrent();
|
||||
return u.getName();
|
||||
}
|
||||
};
|
||||
|
||||
String username = user.runAs(action);
|
||||
assertEquals("Current user within runAs() should match",
|
||||
"testuser", username);
|
||||
|
||||
// ensure the next run is correctly set
|
||||
User user2 = User.createUserForTesting(conf, "testuser2", new String[]{"foo"});
|
||||
String username2 = user2.runAs(action);
|
||||
assertEquals("Second username should match second user",
|
||||
"testuser2", username2);
|
||||
|
||||
// check the exception version
|
||||
username = user.runAs(new PrivilegedExceptionAction<String>(){
|
||||
public String run() throws Exception {
|
||||
return User.getCurrent().getName();
|
||||
}
|
||||
});
|
||||
assertEquals("User name in runAs() should match", "testuser", username);
|
||||
|
||||
// verify that nested contexts work
|
||||
user2.runAs(new PrivilegedAction(){
|
||||
public Object run() {
|
||||
String nestedName = user.runAs(action);
|
||||
assertEquals("Nest name should match nested user", "testuser", nestedName);
|
||||
assertEquals("Current name should match current user",
|
||||
"testuser2", User.getCurrent().getName());
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue