HADOOP-4656. Add a user to groups mapping service (boryas and acmurthy_)
git-svn-id: https://svn.apache.org/repos/asf/hadoop/common/trunk@892066 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
ea5db0c5ab
commit
ae93ba7501
|
@ -68,6 +68,8 @@ Trunk (unreleased changes)
|
||||||
|
|
||||||
HADOOP-6426. Create ant build for running EC2 unit tests. (tomwhite)
|
HADOOP-6426. Create ant build for running EC2 unit tests. (tomwhite)
|
||||||
|
|
||||||
|
HADOOP-4656. Add a user to groups mapping service. (boryas, acmurthy)
|
||||||
|
|
||||||
OPTIMIZATIONS
|
OPTIMIZATIONS
|
||||||
|
|
||||||
BUG FIXES
|
BUG FIXES
|
||||||
|
|
|
@ -126,5 +126,7 @@ public class CommonConfigurationKeys {
|
||||||
public static final String HADOOP_JOB_UGI_KEY = "hadoop.job.ugi";
|
public static final String HADOOP_JOB_UGI_KEY = "hadoop.job.ugi";
|
||||||
public static final String HADOOP_UTIL_HASH_TYPE_KEY = "hadoop.util.hash.type";
|
public static final String HADOOP_UTIL_HASH_TYPE_KEY = "hadoop.util.hash.type";
|
||||||
public static final String HADOOP_UTIL_HASH_TYPE_DEFAULT = "murmur";
|
public static final String HADOOP_UTIL_HASH_TYPE_DEFAULT = "murmur";
|
||||||
|
public static final String HADOOP_SECURITY_GROUP_MAPPING = "hadoop.security.group.mapping";
|
||||||
|
public static final String HADOOP_SECURITY_GROUPS_CACHE_SECS = "hadoop.security.groups.cache.secs";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -67,6 +67,7 @@ import org.apache.hadoop.util.ReflectionUtils;
|
||||||
import org.apache.hadoop.util.StringUtils;
|
import org.apache.hadoop.util.StringUtils;
|
||||||
import org.apache.hadoop.ipc.metrics.RpcMetrics;
|
import org.apache.hadoop.ipc.metrics.RpcMetrics;
|
||||||
import org.apache.hadoop.security.authorize.AuthorizationException;
|
import org.apache.hadoop.security.authorize.AuthorizationException;
|
||||||
|
import org.apache.hadoop.security.UserGroupInformation;
|
||||||
|
|
||||||
/** An abstract IPC service. IPC calls take a single {@link Writable} as a
|
/** An abstract IPC service. IPC calls take a single {@link Writable} as a
|
||||||
* parameter, and return a {@link Writable} as their value. A service runs on
|
* parameter, and return a {@link Writable} as their value. A service runs on
|
||||||
|
@ -893,8 +894,13 @@ public abstract class Server {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Get the user name from the GSS API for Kerberbos-based security
|
// TODO: Get the user name from the GSS API for Kerberbos-based security
|
||||||
// Create the user subject
|
// Create the user subject; however use the groups as defined on the
|
||||||
user = SecurityUtil.getSubject(header.getUgi());
|
// server-side, don't trust the user groups provided by the client
|
||||||
|
UserGroupInformation ugi = header.getUgi();
|
||||||
|
user = null;
|
||||||
|
if(ugi != null) {
|
||||||
|
user = SecurityUtil.getSubject(conf, header.getUgi().getUserName());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void processData() throws IOException, InterruptedException {
|
private void processData() throws IOException, InterruptedException {
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
/**
|
||||||
|
* 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.security;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An interface for the implementation of a user-to-groups mapping service
|
||||||
|
* used by {@link Groups}.
|
||||||
|
*/
|
||||||
|
interface GroupMappingServiceProvider {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all various {@link Group} memberships of a given {@link User}.
|
||||||
|
* Returns EMPTY list in case of non-existing user
|
||||||
|
* @param user <code>User</code> name
|
||||||
|
* @return <code>Group</code> memberships of <code>user</code>
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
public List<String> getGroups(String user) throws IOException;
|
||||||
|
}
|
|
@ -0,0 +1,112 @@
|
||||||
|
/**
|
||||||
|
* 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.security;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
import org.apache.hadoop.conf.Configuration;
|
||||||
|
import org.apache.hadoop.fs.CommonConfigurationKeys;
|
||||||
|
import org.apache.hadoop.util.ReflectionUtils;
|
||||||
|
|
||||||
|
import org.apache.commons.logging.Log;
|
||||||
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A user-to-groups mapping service.
|
||||||
|
*
|
||||||
|
* {@link Groups} allows for server to get the various {@link Group} memberships
|
||||||
|
* of a given {@link User} via the {@link #getGroups(String)} call, thus ensuring
|
||||||
|
* a consistent user-to-groups mapping and protects against vagaries of different
|
||||||
|
* mappings on servers and clients in a Hadoop cluster.
|
||||||
|
*/
|
||||||
|
public class Groups {
|
||||||
|
private static final Log LOG = LogFactory.getLog(Groups.class);
|
||||||
|
|
||||||
|
private final GroupMappingServiceProvider impl;
|
||||||
|
|
||||||
|
private final Map<String, CachedGroups> userToGroupsMap =
|
||||||
|
new ConcurrentHashMap<String, CachedGroups>();
|
||||||
|
private final long cacheTimeout;
|
||||||
|
|
||||||
|
public Groups(Configuration conf) {
|
||||||
|
impl =
|
||||||
|
ReflectionUtils.newInstance(
|
||||||
|
conf.getClass(CommonConfigurationKeys.HADOOP_SECURITY_GROUP_MAPPING,
|
||||||
|
ShellBasedUnixGroupsMapping.class,
|
||||||
|
GroupMappingServiceProvider.class),
|
||||||
|
conf);
|
||||||
|
|
||||||
|
cacheTimeout =
|
||||||
|
conf.getLong(CommonConfigurationKeys.HADOOP_SECURITY_GROUPS_CACHE_SECS, 5*60) * 1000;
|
||||||
|
|
||||||
|
LOG.info("Group mapping impl=" + impl.getClass().getName() +
|
||||||
|
"; cacheTimeout=" + cacheTimeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the {@link Group} memberships of a given {@link User}.
|
||||||
|
* @param user <code>User</code> name
|
||||||
|
* @return the <code>Group</code> memberships of <code>user</code>
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
public List<String> getGroups(String user) throws IOException {
|
||||||
|
// Return cached value if available
|
||||||
|
CachedGroups groups = userToGroupsMap.get(user);
|
||||||
|
long now = System.currentTimeMillis();
|
||||||
|
// if cache has a value and it hasn't expired
|
||||||
|
if (groups != null && (groups.getTimestamp() + cacheTimeout > now)) {
|
||||||
|
LOG.info("Returning cached groups for '" + user + "'");
|
||||||
|
return groups.getGroups();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create and cache user's groups
|
||||||
|
groups = new CachedGroups(impl.getGroups(user));
|
||||||
|
userToGroupsMap.put(user, groups);
|
||||||
|
LOG.info("Returning fetched groups for '" + user + "'");
|
||||||
|
return groups.getGroups();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Refresh all user-to-groups mappings.
|
||||||
|
*/
|
||||||
|
public void refresh() {
|
||||||
|
LOG.info("clearing userToGroupsMap cache");
|
||||||
|
userToGroupsMap.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class CachedGroups {
|
||||||
|
final long timestamp;
|
||||||
|
final List<String> groups;
|
||||||
|
|
||||||
|
CachedGroups(List<String> groups) {
|
||||||
|
this.groups = groups;
|
||||||
|
this.timestamp = System.currentTimeMillis();
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getTimestamp() {
|
||||||
|
return timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getGroups() {
|
||||||
|
return groups;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
/**
|
||||||
|
* 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.security;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import org.apache.hadoop.conf.Configuration;
|
||||||
|
import org.apache.hadoop.ipc.VersionedProtocol;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Protocol use
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public interface RefreshUserToGroupMappingsProtocol extends VersionedProtocol {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Version 1: Initial version.
|
||||||
|
*/
|
||||||
|
public static final long versionID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Refresh {@link User} to {@link Group} mappings.
|
||||||
|
* @param conf
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
public void refreshUserToGroupsMappings(Configuration conf) throws IOException;
|
||||||
|
}
|
|
@ -17,9 +17,11 @@
|
||||||
*/
|
*/
|
||||||
package org.apache.hadoop.security;
|
package org.apache.hadoop.security;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
import java.security.Policy;
|
import java.security.Policy;
|
||||||
import java.security.Principal;
|
import java.security.Principal;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.TreeSet;
|
import java.util.TreeSet;
|
||||||
|
|
||||||
|
@ -41,6 +43,8 @@ public class SecurityUtil {
|
||||||
PolicyProvider.DEFAULT_POLICY_PROVIDER));
|
PolicyProvider.DEFAULT_POLICY_PROVIDER));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static Groups GROUPS = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the global security policy for Hadoop.
|
* Set the global security policy for Hadoop.
|
||||||
*
|
*
|
||||||
|
@ -61,6 +65,18 @@ public class SecurityUtil {
|
||||||
return Policy.getPolicy();
|
return Policy.getPolicy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the {@link Groups} being used to map user-to-groups.
|
||||||
|
* @return the <code>Groups</code> being used to map user-to-groups.
|
||||||
|
*/
|
||||||
|
public static Groups getUserToGroupsMappingService(Configuration conf) {
|
||||||
|
if(GROUPS == null) {
|
||||||
|
LOG.info(" Creating new Groups object");
|
||||||
|
GROUPS = new Groups(conf);
|
||||||
|
}
|
||||||
|
return GROUPS;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the {@link Subject} for the user identified by <code>ugi</code>.
|
* Get the {@link Subject} for the user identified by <code>ugi</code>.
|
||||||
* @param ugi user
|
* @param ugi user
|
||||||
|
@ -70,9 +86,9 @@ public class SecurityUtil {
|
||||||
if (ugi == null) {
|
if (ugi == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
// Number of principals = username + #groups + ugi
|
||||||
Set<Principal> principals = // Number of principals = username + #groups
|
Set<Principal> principals =
|
||||||
new HashSet<Principal>(ugi.getGroupNames().length+1);
|
new HashSet<Principal>(ugi.getGroupNames().length+1+1);
|
||||||
User userPrincipal = new User(ugi.getUserName());
|
User userPrincipal = new User(ugi.getUserName());
|
||||||
principals.add(userPrincipal);
|
principals.add(userPrincipal);
|
||||||
for (String group : ugi.getGroupNames()) {
|
for (String group : ugi.getGroupNames()) {
|
||||||
|
@ -86,6 +102,44 @@ public class SecurityUtil {
|
||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the {@link Subject} for the user identified by <code>userName</code>.
|
||||||
|
* @param userName user name
|
||||||
|
* @return the {@link Subject} for the user identified by <code>userName</code>
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
public static Subject getSubject(Configuration conf, String userName)
|
||||||
|
throws IOException {
|
||||||
|
if (userName == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Set<Principal> principals = new HashSet<Principal>();
|
||||||
|
User userPrincipal = new User(userName);
|
||||||
|
principals.add(userPrincipal);
|
||||||
|
|
||||||
|
// Get user's groups
|
||||||
|
List<String> groups = getUserToGroupsMappingService(conf).getGroups(userName);
|
||||||
|
StringBuffer sb = new StringBuffer("Groups for '" + userName + "': <");
|
||||||
|
for (String group : groups) {
|
||||||
|
Group groupPrincipal = new Group(group);
|
||||||
|
principals.add(groupPrincipal);
|
||||||
|
sb.append(group + " ");
|
||||||
|
}
|
||||||
|
sb.append(">");
|
||||||
|
LOG.info(sb);
|
||||||
|
|
||||||
|
// Create the ugi with the right groups
|
||||||
|
UserGroupInformation ugi =
|
||||||
|
new UnixUserGroupInformation(userName,
|
||||||
|
groups.toArray(new String[groups.size()]));
|
||||||
|
principals.add(ugi);
|
||||||
|
Subject user =
|
||||||
|
new Subject(false, principals, new HashSet<Object>(), new HashSet<Object>());
|
||||||
|
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class representing a configured access control list.
|
* Class representing a configured access control list.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -0,0 +1,76 @@
|
||||||
|
/**
|
||||||
|
* 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.security;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.StringTokenizer;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
import org.apache.commons.logging.Log;
|
||||||
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
import org.apache.hadoop.util.Shell;
|
||||||
|
import org.apache.hadoop.util.Shell.ExitCodeException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A simple shell-based implementation of {@link GroupMappingServiceProvider} which
|
||||||
|
* exec's the <code>groups</code> shell command to fetch the {@link Group}
|
||||||
|
* memberships of a given {@link User}.
|
||||||
|
*/
|
||||||
|
public class ShellBasedUnixGroupsMapping implements GroupMappingServiceProvider {
|
||||||
|
Map<String, List<String>> userGroups =
|
||||||
|
new ConcurrentHashMap<String, List<String>>();
|
||||||
|
|
||||||
|
private static final Log LOG = LogFactory.getLog(ShellBasedUnixGroupsMapping.class);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> getGroups(String user) throws IOException {
|
||||||
|
List<String> groups = userGroups.get(user);
|
||||||
|
if (groups == null) {
|
||||||
|
groups = getUnixGroups(user);
|
||||||
|
userGroups.put(user, groups);
|
||||||
|
}
|
||||||
|
return groups;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the current user's group list from Unix by running the command 'groups'
|
||||||
|
* NOTE. For non-existing user it will return EMPTY list
|
||||||
|
* @param user user name
|
||||||
|
* @return the groups list that the <code>user</code> belongs to
|
||||||
|
* @throws IOException if encounter any error when running the command
|
||||||
|
*/
|
||||||
|
private static List<String> getUnixGroups(final String user) throws IOException {
|
||||||
|
String result = "";
|
||||||
|
try {
|
||||||
|
result = Shell.execCommand(Shell.getGROUPS_FOR_USER_COMMAND(user));
|
||||||
|
} catch (ExitCodeException e) {
|
||||||
|
// if we didn't get the group - just return empty list;
|
||||||
|
LOG.warn("got exception trying to get groups for user " + user, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
StringTokenizer tokenizer = new StringTokenizer(result);
|
||||||
|
List<String> groups = new LinkedList<String>();
|
||||||
|
while (tokenizer.hasMoreTokens()) {
|
||||||
|
groups.add(tokenizer.nextToken());
|
||||||
|
}
|
||||||
|
return groups;
|
||||||
|
}
|
||||||
|
}
|
|
@ -95,10 +95,13 @@ public class UnixUserGroupInformation extends UserGroupInformation {
|
||||||
* @exception IllegalArgumentException if any argument is null
|
* @exception IllegalArgumentException if any argument is null
|
||||||
*/
|
*/
|
||||||
private void setUserGroupNames(String userName, String[] groupNames) {
|
private void setUserGroupNames(String userName, String[] groupNames) {
|
||||||
if (userName==null || userName.length()==0 ||
|
if (userName==null || userName.length()==0) {
|
||||||
groupNames== null || groupNames.length==0) {
|
|
||||||
throw new IllegalArgumentException(
|
throw new IllegalArgumentException(
|
||||||
"Parameters should not be null or an empty string/array");
|
"username should not be null nor empty");
|
||||||
|
}
|
||||||
|
if(groupNames == null) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"group names array should not be null");
|
||||||
}
|
}
|
||||||
for (int i=0; i<groupNames.length; i++) {
|
for (int i=0; i<groupNames.length; i++) {
|
||||||
if(groupNames[i] == null || groupNames[i].length() == 0) {
|
if(groupNames[i] == null || groupNames[i].length() == 0) {
|
||||||
|
|
|
@ -47,6 +47,11 @@ abstract public class Shell {
|
||||||
public static String[] getGROUPS_COMMAND() {
|
public static String[] getGROUPS_COMMAND() {
|
||||||
return new String[]{"bash", "-c", "groups"};
|
return new String[]{"bash", "-c", "groups"};
|
||||||
}
|
}
|
||||||
|
/** a Unix command to get a given user's groups list */
|
||||||
|
public static String[] getGROUPS_FOR_USER_COMMAND(final String user) {
|
||||||
|
//'groups username' command return is non-consistent across different unixes
|
||||||
|
return new String [] {"bash", "-c", "id -Gn " + user};
|
||||||
|
}
|
||||||
/** a Unix command to set permission */
|
/** a Unix command to set permission */
|
||||||
public static final String SET_PERMISSION_COMMAND = "chmod";
|
public static final String SET_PERMISSION_COMMAND = "chmod";
|
||||||
/** a Unix command to set owner */
|
/** a Unix command to set owner */
|
||||||
|
|
|
@ -18,11 +18,21 @@
|
||||||
|
|
||||||
package org.apache.hadoop.security;
|
package org.apache.hadoop.security;
|
||||||
|
|
||||||
import org.apache.hadoop.conf.Configuration;
|
import java.io.BufferedReader;
|
||||||
import org.apache.hadoop.io.TestWritable;
|
import java.io.IOException;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.security.Principal;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import javax.security.auth.Subject;
|
||||||
|
|
||||||
import junit.framework.TestCase;
|
import junit.framework.TestCase;
|
||||||
|
|
||||||
|
import org.apache.hadoop.conf.Configuration;
|
||||||
|
import org.apache.hadoop.io.TestWritable;
|
||||||
|
import org.apache.hadoop.util.Shell;
|
||||||
|
|
||||||
/** Unit tests for UnixUserGroupInformation */
|
/** Unit tests for UnixUserGroupInformation */
|
||||||
public class TestUnixUserGroupInformation extends TestCase {
|
public class TestUnixUserGroupInformation extends TestCase {
|
||||||
final private static String USER_NAME = "user1";
|
final private static String USER_NAME = "user1";
|
||||||
|
@ -31,6 +41,7 @@ public class TestUnixUserGroupInformation extends TestCase {
|
||||||
final private static String GROUP3_NAME = "group3";
|
final private static String GROUP3_NAME = "group3";
|
||||||
final private static String[] GROUP_NAMES =
|
final private static String[] GROUP_NAMES =
|
||||||
new String[]{GROUP1_NAME, GROUP2_NAME, GROUP3_NAME};
|
new String[]{GROUP1_NAME, GROUP2_NAME, GROUP3_NAME};
|
||||||
|
final private static Configuration conf = new Configuration();
|
||||||
|
|
||||||
/** Test login method */
|
/** Test login method */
|
||||||
public void testLogin() throws Exception {
|
public void testLogin() throws Exception {
|
||||||
|
@ -63,7 +74,6 @@ public class TestUnixUserGroupInformation extends TestCase {
|
||||||
testConstructorFailures(null, GROUP_NAMES);
|
testConstructorFailures(null, GROUP_NAMES);
|
||||||
testConstructorFailures("", GROUP_NAMES);
|
testConstructorFailures("", GROUP_NAMES);
|
||||||
testConstructorFailures(USER_NAME, null);
|
testConstructorFailures(USER_NAME, null);
|
||||||
testConstructorFailures(USER_NAME, new String[0]);
|
|
||||||
testConstructorFailures(USER_NAME, new String[]{null});
|
testConstructorFailures(USER_NAME, new String[]{null});
|
||||||
testConstructorFailures(USER_NAME, new String[]{""});
|
testConstructorFailures(USER_NAME, new String[]{""});
|
||||||
testConstructorFailures(USER_NAME, new String[]{GROUP1_NAME, null});
|
testConstructorFailures(USER_NAME, new String[]{GROUP1_NAME, null});
|
||||||
|
@ -98,6 +108,69 @@ public class TestUnixUserGroupInformation extends TestCase {
|
||||||
public void testWritable() throws Exception {
|
public void testWritable() throws Exception {
|
||||||
UnixUserGroupInformation ugi = new UnixUserGroupInformation(
|
UnixUserGroupInformation ugi = new UnixUserGroupInformation(
|
||||||
USER_NAME, GROUP_NAMES);
|
USER_NAME, GROUP_NAMES);
|
||||||
TestWritable.testWritable(ugi, new Configuration());
|
TestWritable.testWritable(ugi, conf);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* given user name - get all the groups.
|
||||||
|
*/
|
||||||
|
public void testGetServerSideGroups() throws IOException {
|
||||||
|
|
||||||
|
// get the user name
|
||||||
|
Process pp = Runtime.getRuntime().exec("whoami");
|
||||||
|
BufferedReader br = new BufferedReader(new InputStreamReader(pp.getInputStream()));
|
||||||
|
String userName = br.readLine().trim();
|
||||||
|
// get the groups
|
||||||
|
pp = Runtime.getRuntime().exec("id -Gn");
|
||||||
|
br = new BufferedReader(new InputStreamReader(pp.getInputStream()));
|
||||||
|
String line = br.readLine();
|
||||||
|
System.out.println(userName + ":" + line);
|
||||||
|
|
||||||
|
List<String> groups = new ArrayList<String> ();
|
||||||
|
for(String s: line.split("[\\s]")) {
|
||||||
|
groups.add(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean ugiIsIn = false;
|
||||||
|
|
||||||
|
// get groups on the server side
|
||||||
|
int numberOfGroups = 0;
|
||||||
|
Subject subject = SecurityUtil.getSubject(conf, userName);
|
||||||
|
System.out.println("for user="+userName+" prinicipals are:");
|
||||||
|
for(Principal p : subject.getPrincipals()) {
|
||||||
|
if(p instanceof User) {
|
||||||
|
System.out.println("USER: " + p.getName());
|
||||||
|
assertTrue("user name is not the same as in the Subject: " + p.getName(),
|
||||||
|
userName.equals(p.getName()));
|
||||||
|
}
|
||||||
|
if(p instanceof Group) {
|
||||||
|
numberOfGroups++;
|
||||||
|
System.out.println("GROUP: " + p.getName());
|
||||||
|
assertTrue("Subject contains invalid group " + p.getName(),
|
||||||
|
groups.contains(p.getName()));
|
||||||
|
}
|
||||||
|
if(p instanceof UserGroupInformation) {
|
||||||
|
System.out.println("UGI: " + p.getName());
|
||||||
|
ugiIsIn = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assertTrue("UGI object is not in the Subject", ugiIsIn);
|
||||||
|
assertEquals("number of groups in subject doesn't match actual # groups",
|
||||||
|
numberOfGroups, groups.size());
|
||||||
|
|
||||||
|
// negative test - get Subject for non-existing user
|
||||||
|
// should return empty groups
|
||||||
|
subject = SecurityUtil.getSubject(conf, "fakeUser");
|
||||||
|
for(Principal p : subject.getPrincipals()) {
|
||||||
|
if(p instanceof User) {
|
||||||
|
System.out.println("USER: " + p.getName());
|
||||||
|
assertTrue("user name (fakeUser) is not the same as in the Subject: " +
|
||||||
|
p.getName(), "fakeUser".equals(p.getName()));
|
||||||
|
}
|
||||||
|
if(p instanceof Group) {
|
||||||
|
fail("fakeUser should have no groups");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue