Skip to content

Commit 3de28d0

Browse files
author
Brandon Li
committed
HADOOP-8943. Support multiple group mapping providers. Contributed by Kai Zheng
git-svn-id: https://svn.apache.org/repos/asf/hadoop/common/trunk@1605857 13f79535-47bb-0310-9956-ffa450edef68
1 parent 3074b34 commit 3de28d0

File tree

5 files changed

+447
-0
lines changed

5 files changed

+447
-0
lines changed

hadoop-common-project/hadoop-common/CHANGES.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,8 @@ Release 2.5.0 - UNRELEASED
378378

379379
HADOOP-9704. Write metrics sink plugin for Hadoop/Graphite (Chu Tong, Alex Newman and Babak Behzad via raviprak)
380380

381+
HADOOP-8943. Support multiple group mapping providers. (Kai Zheng via brandonli)
382+
381383
IMPROVEMENTS
382384

383385
HADOOP-10451. Remove unused field and imports from SaslRpcServer.
Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
/**
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
package org.apache.hadoop.security;
19+
20+
import java.io.IOException;
21+
import java.util.ArrayList;
22+
import java.util.Iterator;
23+
import java.util.List;
24+
import java.util.Map;
25+
import java.util.Set;
26+
import java.util.TreeSet;
27+
28+
import org.apache.commons.logging.Log;
29+
import org.apache.commons.logging.LogFactory;
30+
import org.apache.hadoop.classification.InterfaceAudience;
31+
import org.apache.hadoop.classification.InterfaceStability;
32+
import org.apache.hadoop.conf.Configurable;
33+
import org.apache.hadoop.conf.Configuration;
34+
import org.apache.hadoop.util.ReflectionUtils;
35+
36+
/**
37+
* An implementation of {@link GroupMappingServiceProvider} which
38+
* composites other group mapping providers for determining group membership.
39+
* This allows to combine existing provider implementations and composite
40+
* a virtually new provider without customized development to deal with complex situation.
41+
*/
42+
@InterfaceAudience.LimitedPrivate({"HDFS", "MapReduce"})
43+
@InterfaceStability.Evolving
44+
public class CompositeGroupsMapping
45+
implements GroupMappingServiceProvider, Configurable {
46+
47+
public static final String MAPPING_PROVIDERS_CONFIG_KEY = GROUP_MAPPING_CONFIG_PREFIX + ".providers";
48+
public static final String MAPPING_PROVIDERS_COMBINED_CONFIG_KEY = MAPPING_PROVIDERS_CONFIG_KEY + ".combined";
49+
public static final String MAPPING_PROVIDER_CONFIG_PREFIX = GROUP_MAPPING_CONFIG_PREFIX + ".provider";
50+
51+
private static final Log LOG = LogFactory.getLog(CompositeGroupsMapping.class);
52+
53+
private List<GroupMappingServiceProvider> providersList =
54+
new ArrayList<GroupMappingServiceProvider>();
55+
56+
private Configuration conf;
57+
private boolean combined;
58+
59+
60+
61+
/**
62+
* Returns list of groups for a user.
63+
*
64+
* @param user get groups for this user
65+
* @return list of groups for a given user
66+
*/
67+
@Override
68+
public synchronized List<String> getGroups(String user) throws IOException {
69+
Set<String> groupSet = new TreeSet<String>();
70+
71+
List<String> groups = null;
72+
for (GroupMappingServiceProvider provider : providersList) {
73+
try {
74+
groups = provider.getGroups(user);
75+
} catch (Exception e) {
76+
//LOG.warn("Exception trying to get groups for user " + user, e);
77+
}
78+
if (groups != null && ! groups.isEmpty()) {
79+
groupSet.addAll(groups);
80+
if (!combined) break;
81+
}
82+
}
83+
84+
List<String> results = new ArrayList<String>(groupSet.size());
85+
results.addAll(groupSet);
86+
return results;
87+
}
88+
89+
/**
90+
* Caches groups, no need to do that for this provider
91+
*/
92+
@Override
93+
public void cacheGroupsRefresh() throws IOException {
94+
// does nothing in this provider of user to groups mapping
95+
}
96+
97+
/**
98+
* Adds groups to cache, no need to do that for this provider
99+
*
100+
* @param groups unused
101+
*/
102+
@Override
103+
public void cacheGroupsAdd(List<String> groups) throws IOException {
104+
// does nothing in this provider of user to groups mapping
105+
}
106+
107+
@Override
108+
public synchronized Configuration getConf() {
109+
return conf;
110+
}
111+
112+
@Override
113+
public synchronized void setConf(Configuration conf) {
114+
this.conf = conf;
115+
116+
this.combined = conf.getBoolean(MAPPING_PROVIDERS_COMBINED_CONFIG_KEY, true);
117+
118+
loadMappingProviders();
119+
}
120+
121+
private void loadMappingProviders() {
122+
String[] providerNames = conf.getStrings(MAPPING_PROVIDERS_CONFIG_KEY, new String[]{});
123+
124+
String providerKey;
125+
for (String name : providerNames) {
126+
providerKey = MAPPING_PROVIDER_CONFIG_PREFIX + "." + name;
127+
Class<?> providerClass = conf.getClass(providerKey, null);
128+
if (providerClass == null) {
129+
LOG.error("The mapping provider, " + name + " does not have a valid class");
130+
} else {
131+
addMappingProvider(name, providerClass);
132+
}
133+
}
134+
}
135+
136+
private void addMappingProvider(String providerName, Class<?> providerClass) {
137+
Configuration newConf = prepareConf(providerName);
138+
GroupMappingServiceProvider provider =
139+
(GroupMappingServiceProvider) ReflectionUtils.newInstance(providerClass, newConf);
140+
providersList.add(provider);
141+
142+
}
143+
144+
/*
145+
* For any provider specific configuration properties, such as "hadoop.security.group.mapping.ldap.url"
146+
* and the like, allow them to be configured as "hadoop.security.group.mapping.provider.PROVIDER-X.ldap.url",
147+
* so that a provider such as LdapGroupsMapping can be used to composite a complex one with other providers.
148+
*/
149+
private Configuration prepareConf(String providerName) {
150+
Configuration newConf = new Configuration();
151+
Iterator<Map.Entry<String, String>> entries = conf.iterator();
152+
String providerKey = MAPPING_PROVIDER_CONFIG_PREFIX + "." + providerName;
153+
while (entries.hasNext()) {
154+
Map.Entry<String, String> entry = entries.next();
155+
String key = entry.getKey();
156+
// get a property like "hadoop.security.group.mapping.provider.PROVIDER-X.ldap.url"
157+
if (key.startsWith(providerKey) && !key.equals(providerKey)) {
158+
// restore to be the one like "hadoop.security.group.mapping.ldap.url"
159+
// so that can be used by original provider.
160+
key = key.replace(".provider." + providerName, "");
161+
newConf.set(key, entry.getValue());
162+
}
163+
}
164+
return newConf;
165+
}
166+
}

hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/GroupMappingServiceProvider.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222

2323
import org.apache.hadoop.classification.InterfaceAudience;
2424
import org.apache.hadoop.classification.InterfaceStability;
25+
import org.apache.hadoop.fs.CommonConfigurationKeysPublic;
2526

2627
/**
2728
* An interface for the implementation of a user-to-groups mapping service
@@ -30,6 +31,7 @@
3031
@InterfaceAudience.Public
3132
@InterfaceStability.Evolving
3233
public interface GroupMappingServiceProvider {
34+
public static final String GROUP_MAPPING_CONFIG_PREFIX = CommonConfigurationKeysPublic.HADOOP_SECURITY_GROUP_MAPPING;
3335

3436
/**
3537
* Get all various group memberships of a given user.

hadoop-common-project/hadoop-common/src/main/resources/core-default.xml

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,98 @@
9494
</description>
9595
</property>
9696

97+
<!--
98+
=== Multiple group mapping providers configuration sample ===
99+
This sample illustrates a typical use case for CompositeGroupsMapping where
100+
Hadoop authentication uses MIT Kerberos which trusts an AD realm. In this case, service
101+
principals such as hdfs, mapred, hbase, hive, oozie and etc can be placed in In MIT Kerberos,
102+
but end users are just from the trusted AD. For the service principals, ShellBasedUnixGroupsMapping
103+
provider can be used to query their groups for efficiency, and for end users, LdapGroupsMapping
104+
provider can be used. This avoids to add group entries in AD for service principals when only using
105+
LdapGroupsMapping provider.
106+
In case multiple ADs are involved and trusted by the MIT Kerberos in this use case, LdapGroupsMapping
107+
provider can be used more times with different AD specific configurations. This sample also shows how
108+
to do that. Here are the necessary configurations.
109+
110+
<property>
111+
<name>hadoop.security.group.mapping</name>
112+
<value>org.apache.hadoop.security.CompositeGroupsMapping</value>
113+
<description>
114+
Class for user to group mapping (get groups for a given user) for ACL, which
115+
makes use of other multiple providers to provide the service.
116+
</description>
117+
</property>
118+
119+
<property>
120+
<name>hadoop.security.group.mapping.providers</name>
121+
<value>shell4services,ad4usersX,ad4usersY</value>
122+
<description>
123+
Comma separated of names of other providers to provide user to group mapping.
124+
</description>
125+
</property>
126+
127+
<property>
128+
<name>hadoop.security.group.mapping.providers.combined</name>
129+
<value>true</value>
130+
<description>
131+
true or false to indicate whether groups from the providers are combined or not. The default value is true
132+
If true, then all the providers will be tried to get groups and all the groups are combined to return as
133+
the final results. Otherwise, providers are tried one by one in the configured list order, and if any
134+
groups are retrieved from any provider, then the groups will be returned without trying the left ones.
135+
</description>
136+
</property>
137+
138+
<property>
139+
<name>hadoop.security.group.mapping.provider.shell4services</name>
140+
<value>org.apache.hadoop.security.ShellBasedUnixGroupsMapping</value>
141+
<description>
142+
Class for group mapping provider named by 'shell4services'. The name can then be referenced
143+
by hadoop.security.group.mapping.providers property.
144+
</description>
145+
</property>
146+
147+
<property>
148+
<name>hadoop.security.group.mapping.provider.ad4usersX</name>
149+
<value>org.apache.hadoop.security.LdapGroupsMapping</value>
150+
<description>
151+
Class for group mapping provider named by 'ad4usersX'. The name can then be referenced
152+
by hadoop.security.group.mapping.providers property.
153+
</description>
154+
</property>
155+
156+
<property>
157+
<name>hadoop.security.group.mapping.provider.ad4usersY</name>
158+
<value>org.apache.hadoop.security.LdapGroupsMapping</value>
159+
<description>
160+
Class for group mapping provider named by 'ad4usersY'. The name can then be referenced
161+
by hadoop.security.group.mapping.providers property.
162+
</description>
163+
</property>
164+
165+
<property>
166+
<name>hadoop.security.group.mapping.provider.ad4usersX.ldap.url</name>
167+
<value>ldap://ad-host-for-users-X:389</value>
168+
<description>
169+
ldap url for the provider named by 'ad4usersX'. Note this property comes from
170+
'hadoop.security.group.mapping.ldap.url'.
171+
</description>
172+
</property>
173+
174+
<property>
175+
<name>hadoop.security.group.mapping.provider.ad4usersY.ldap.url</name>
176+
<value>ldap://ad-host-for-users-Y:389</value>
177+
<description>
178+
ldap url for the provider named by 'ad4usersY'. Note this property comes from
179+
'hadoop.security.group.mapping.ldap.url'.
180+
</description>
181+
</property>
182+
183+
You also need to configure other properties like
184+
hadoop.security.group.mapping.ldap.bind.password.file and etc.
185+
for ldap providers in the same way as above does.
186+
187+
-->
188+
97189
<property>
98190
<name>hadoop.security.groups.cache.secs</name>
99191
<value>300</value>

0 commit comments

Comments
 (0)