You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hbase.apache.org by st...@apache.org on 2010/11/09 17:56:22 UTC
svn commit: r1033089 - in /hbase/trunk/src:
main/java/org/apache/hadoop/hbase/security/
main/java/org/apache/hadoop/hbase/security/User.java
test/java/org/apache/hadoop/hbase/security/
test/java/org/apache/hadoop/hbase/security/TestUser.java
Author: stack
Date: Tue Nov 9 16:56:22 2010
New Revision: 1033089
URL: http://svn.apache.org/viewvc?rev=1033089&view=rev
Log:
HBASE-3194 HBase should run on both secure and vanilla versions of Hadoop 0.20 -- forgot to add classes
Added:
hbase/trunk/src/main/java/org/apache/hadoop/hbase/security/
hbase/trunk/src/main/java/org/apache/hadoop/hbase/security/User.java
hbase/trunk/src/test/java/org/apache/hadoop/hbase/security/
hbase/trunk/src/test/java/org/apache/hadoop/hbase/security/TestUser.java
Added: hbase/trunk/src/main/java/org/apache/hadoop/hbase/security/User.java
URL: http://svn.apache.org/viewvc/hbase/trunk/src/main/java/org/apache/hadoop/hbase/security/User.java?rev=1033089&view=auto
==============================================================================
--- hbase/trunk/src/main/java/org/apache/hadoop/hbase/security/User.java (added)
+++ hbase/trunk/src/main/java/org/apache/hadoop/hbase/security/User.java Tue Nov 9 16:56:22 2010
@@ -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;
+ }
+}
Added: hbase/trunk/src/test/java/org/apache/hadoop/hbase/security/TestUser.java
URL: http://svn.apache.org/viewvc/hbase/trunk/src/test/java/org/apache/hadoop/hbase/security/TestUser.java?rev=1033089&view=auto
==============================================================================
--- hbase/trunk/src/test/java/org/apache/hadoop/hbase/security/TestUser.java (added)
+++ hbase/trunk/src/test/java/org/apache/hadoop/hbase/security/TestUser.java Tue Nov 9 16:56:22 2010
@@ -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;
+ }
+ });
+ }
+}