HADOOP-10142. Avoid groups lookup for unprivileged users such as dr.who (vinay via cmccabe)
git-svn-id: https://svn.apache.org/repos/asf/hadoop/common/trunk@1548763 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
045dc880e1
commit
7f059104d2
|
@ -487,6 +487,9 @@ Release 2.3.0 - UNRELEASED
|
|||
|
||||
OPTIMIZATIONS
|
||||
|
||||
HADOOP-10142. Avoid groups lookup for unprivileged users such as "dr.who"
|
||||
(vinay via cmccabe)
|
||||
|
||||
BUG FIXES
|
||||
|
||||
HADOOP-10028. Malformed ssl-server.xml.example. (Haohui Mai via jing9)
|
||||
|
|
|
@ -204,6 +204,14 @@ public class CommonConfigurationKeys extends CommonConfigurationKeysPublic {
|
|||
public static final String DEFAULT_HADOOP_HTTP_STATIC_USER =
|
||||
"dr.who";
|
||||
|
||||
/**
|
||||
* User->groups static mapping to override the groups lookup
|
||||
*/
|
||||
public static final String HADOOP_USER_GROUP_STATIC_OVERRIDES =
|
||||
"hadoop.user.group.static.mapping.overrides";
|
||||
public static final String HADOOP_USER_GROUP_STATIC_OVERRIDES_DEFAULT =
|
||||
"dr.who=;";
|
||||
|
||||
/** Enable/Disable aliases serving from jetty */
|
||||
public static final String HADOOP_JETTY_LOGS_SERVE_ALIASES =
|
||||
"hadoop.jetty.logs.serve.aliases";
|
||||
|
|
|
@ -18,15 +18,20 @@
|
|||
package org.apache.hadoop.security;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.apache.hadoop.HadoopIllegalArgumentException;
|
||||
import org.apache.hadoop.classification.InterfaceAudience;
|
||||
import org.apache.hadoop.classification.InterfaceStability;
|
||||
import org.apache.hadoop.conf.Configuration;
|
||||
import org.apache.hadoop.fs.CommonConfigurationKeys;
|
||||
import org.apache.hadoop.util.ReflectionUtils;
|
||||
import org.apache.hadoop.util.StringUtils;
|
||||
import org.apache.hadoop.util.Time;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
|
@ -49,6 +54,8 @@ public class Groups {
|
|||
|
||||
private final Map<String, CachedGroups> userToGroupsMap =
|
||||
new ConcurrentHashMap<String, CachedGroups>();
|
||||
private final Map<String, List<String>> staticUserToGroupsMap =
|
||||
new HashMap<String, List<String>>();
|
||||
private final long cacheTimeout;
|
||||
private final long warningDeltaMs;
|
||||
|
||||
|
@ -66,12 +73,43 @@ public class Groups {
|
|||
warningDeltaMs =
|
||||
conf.getLong(CommonConfigurationKeys.HADOOP_SECURITY_GROUPS_CACHE_WARN_AFTER_MS,
|
||||
CommonConfigurationKeys.HADOOP_SECURITY_GROUPS_CACHE_WARN_AFTER_MS_DEFAULT);
|
||||
|
||||
parseStaticMapping(conf);
|
||||
|
||||
if(LOG.isDebugEnabled())
|
||||
LOG.debug("Group mapping impl=" + impl.getClass().getName() +
|
||||
"; cacheTimeout=" + cacheTimeout + "; warningDeltaMs=" +
|
||||
warningDeltaMs);
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse the hadoop.user.group.static.mapping.overrides configuration to
|
||||
* staticUserToGroupsMap
|
||||
*/
|
||||
private void parseStaticMapping(Configuration conf) {
|
||||
String staticMapping = conf.get(
|
||||
CommonConfigurationKeys.HADOOP_USER_GROUP_STATIC_OVERRIDES,
|
||||
CommonConfigurationKeys.HADOOP_USER_GROUP_STATIC_OVERRIDES_DEFAULT);
|
||||
Collection<String> mappings = StringUtils.getStringCollection(
|
||||
staticMapping, ";");
|
||||
for (String users : mappings) {
|
||||
Collection<String> userToGroups = StringUtils.getStringCollection(users,
|
||||
"=");
|
||||
if (userToGroups.size() < 1 || userToGroups.size() > 2) {
|
||||
throw new HadoopIllegalArgumentException("Configuration "
|
||||
+ CommonConfigurationKeys.HADOOP_USER_GROUP_STATIC_OVERRIDES
|
||||
+ " is invalid");
|
||||
}
|
||||
String[] userToGroupsArray = userToGroups.toArray(new String[userToGroups
|
||||
.size()]);
|
||||
String user = userToGroupsArray[0];
|
||||
List<String> groups = Collections.emptyList();
|
||||
if (userToGroupsArray.length == 2) {
|
||||
groups = (List<String>) StringUtils
|
||||
.getStringCollection(userToGroupsArray[1]);
|
||||
}
|
||||
staticUserToGroupsMap.put(user, groups);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the group memberships of a given user.
|
||||
|
@ -80,6 +118,11 @@ public class Groups {
|
|||
* @throws IOException
|
||||
*/
|
||||
public List<String> getGroups(String user) throws IOException {
|
||||
// No need to lookup for groups of static users
|
||||
List<String> staticMapping = staticUserToGroupsMap.get(user);
|
||||
if (staticMapping != null) {
|
||||
return staticMapping;
|
||||
}
|
||||
// Return cached value if available
|
||||
CachedGroups groups = userToGroupsMap.get(user);
|
||||
long startMs = Time.monotonicNow();
|
||||
|
|
|
@ -325,10 +325,24 @@ public class StringUtils {
|
|||
* @return an <code>ArrayList</code> of string values
|
||||
*/
|
||||
public static Collection<String> getStringCollection(String str){
|
||||
String delim = ",";
|
||||
return getStringCollection(str, delim);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a collection of strings.
|
||||
*
|
||||
* @param str
|
||||
* String to parse
|
||||
* @param delim
|
||||
* delimiter to separate the values
|
||||
* @return Collection of parsed elements.
|
||||
*/
|
||||
public static Collection<String> getStringCollection(String str, String delim) {
|
||||
List<String> values = new ArrayList<String>();
|
||||
if (str == null)
|
||||
return values;
|
||||
StringTokenizer tokenizer = new StringTokenizer (str,",");
|
||||
StringTokenizer tokenizer = new StringTokenizer(str, delim);
|
||||
values = new ArrayList<String>();
|
||||
while (tokenizer.hasMoreTokens()) {
|
||||
values.add(tokenizer.nextToken());
|
||||
|
|
|
@ -1261,4 +1261,18 @@
|
|||
Specify the port number used by Hadoop mount daemon.
|
||||
</description>
|
||||
</property>
|
||||
|
||||
<property>
|
||||
<name>hadoop.user.group.static.mapping.overrides</name>
|
||||
<value>dr.who=;</value>
|
||||
<description>
|
||||
Static mapping of user to groups. This will override the groups if
|
||||
available in the system for the specified user. In otherwords, groups
|
||||
look-up will not happen for these users, instead groups mapped in this
|
||||
configuration will be used.
|
||||
Mapping should be in this format.
|
||||
user1=group1,group2;user2=;user3=group2;
|
||||
Default, "dr.who=;" will consider "dr.who" as user without groups.
|
||||
</description>
|
||||
</property>
|
||||
</configuration>
|
||||
|
|
|
@ -19,14 +19,17 @@ package org.apache.hadoop.security;
|
|||
|
||||
import java.io.IOException;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
|
@ -40,10 +43,12 @@ import org.apache.hadoop.security.ShellBasedUnixGroupsMapping;
|
|||
|
||||
public class TestGroupsCaching {
|
||||
public static final Log LOG = LogFactory.getLog(TestGroupsCaching.class);
|
||||
private static Configuration conf = new Configuration();
|
||||
private static String[] myGroups = {"grp1", "grp2"};
|
||||
private Configuration conf;
|
||||
|
||||
static {
|
||||
@Before
|
||||
public void setup() {
|
||||
conf = new Configuration();
|
||||
conf.setClass(CommonConfigurationKeys.HADOOP_SECURITY_GROUP_MAPPING,
|
||||
FakeGroupMapping.class,
|
||||
ShellBasedUnixGroupsMapping.class);
|
||||
|
@ -88,7 +93,7 @@ public class TestGroupsCaching {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void TestGroupsCaching() throws Exception {
|
||||
public void testGroupsCaching() throws Exception {
|
||||
Groups groups = new Groups(conf);
|
||||
groups.cacheGroupsAdd(Arrays.asList(myGroups));
|
||||
groups.refresh();
|
||||
|
@ -117,4 +122,45 @@ public class TestGroupsCaching {
|
|||
FakeGroupMapping.clearBlackList();
|
||||
assertTrue(groups.getGroups("user1").size() == 2);
|
||||
}
|
||||
|
||||
public static class FakeunPrivilegedGroupMapping extends FakeGroupMapping {
|
||||
private static boolean invoked = false;
|
||||
@Override
|
||||
public List<String> getGroups(String user) throws IOException {
|
||||
invoked = true;
|
||||
return super.getGroups(user);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Group lookup should not happen for static users
|
||||
*/
|
||||
@Test
|
||||
public void testGroupLookupForStaticUsers() throws Exception {
|
||||
conf.setClass(CommonConfigurationKeys.HADOOP_SECURITY_GROUP_MAPPING,
|
||||
FakeunPrivilegedGroupMapping.class, ShellBasedUnixGroupsMapping.class);
|
||||
conf.set(CommonConfigurationKeys.HADOOP_USER_GROUP_STATIC_OVERRIDES, "me=;user1=group1;user2=group1,group2");
|
||||
Groups groups = new Groups(conf);
|
||||
List<String> userGroups = groups.getGroups("me");
|
||||
assertTrue("non-empty groups for static user", userGroups.isEmpty());
|
||||
assertFalse("group lookup done for static user",
|
||||
FakeunPrivilegedGroupMapping.invoked);
|
||||
|
||||
List<String> expected = new ArrayList<String>();
|
||||
expected.add("group1");
|
||||
|
||||
FakeunPrivilegedGroupMapping.invoked = false;
|
||||
userGroups = groups.getGroups("user1");
|
||||
assertTrue("groups not correct", expected.equals(userGroups));
|
||||
assertFalse("group lookup done for unprivileged user",
|
||||
FakeunPrivilegedGroupMapping.invoked);
|
||||
|
||||
expected.add("group2");
|
||||
FakeunPrivilegedGroupMapping.invoked = false;
|
||||
userGroups = groups.getGroups("user2");
|
||||
assertTrue("groups not correct", expected.equals(userGroups));
|
||||
assertFalse("group lookup done for unprivileged user",
|
||||
FakeunPrivilegedGroupMapping.invoked);
|
||||
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue