diff --git a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/RuleBasedLdapGroupsMapping.java b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/RuleBasedLdapGroupsMapping.java new file mode 100644 index 00000000000..be7a5a4c7ea --- /dev/null +++ b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/RuleBasedLdapGroupsMapping.java @@ -0,0 +1,95 @@ +/** + * 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 org.apache.hadoop.classification.InterfaceAudience; +import org.apache.hadoop.classification.InterfaceStability; +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.util.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.List; + +/** + * This class uses {@link LdapGroupsMapping} for group lookup and applies the + * rule configured on the group names. + */ +@InterfaceAudience.LimitedPrivate({"HDFS"}) +@InterfaceStability.Evolving +public class RuleBasedLdapGroupsMapping extends LdapGroupsMapping { + + public static final String CONVERSION_RULE_KEY = LDAP_CONFIG_PREFIX + + ".conversion.rule"; + + private static final String CONVERSION_RULE_DEFAULT = "none"; + private static final Logger LOG = + LoggerFactory.getLogger(RuleBasedLdapGroupsMapping.class); + + private Rule rule; + + /** + * Supported rules applicable for group name modification. + */ + private enum Rule { + TO_UPPER, TO_LOWER, NONE + } + + @Override + public synchronized void setConf(Configuration conf) { + super.setConf(conf); + String value = conf.get(CONVERSION_RULE_KEY, CONVERSION_RULE_DEFAULT); + try { + rule = Rule.valueOf(value.toUpperCase()); + } catch (IllegalArgumentException iae) { + LOG.warn("Invalid {} configured: '{}'. Using default value: '{}'", + CONVERSION_RULE_KEY, value, CONVERSION_RULE_DEFAULT); + } + } + + /** + * Returns list of groups for a user. + * This calls {@link LdapGroupsMapping}'s getGroups and applies the + * configured rules on group names before returning. + * + * @param user get groups for this user + * @return list of groups for a given user + */ + @Override + public synchronized List getGroups(String user) { + List groups = super.getGroups(user); + List result = new ArrayList<>(groups.size()); + switch (rule) { + case TO_UPPER: + for (String group : groups) { + result.add(StringUtils.toUpperCase(group)); + } + return result; + case TO_LOWER: + for (String group : groups) { + result.add(StringUtils.toLowerCase(group)); + } + return result; + case NONE: + default: + return groups; + } + } + +} diff --git a/hadoop-common-project/hadoop-common/src/main/resources/core-default.xml b/hadoop-common-project/hadoop-common/src/main/resources/core-default.xml index 156b25b9758..f18bc9a919c 100644 --- a/hadoop-common-project/hadoop-common/src/main/resources/core-default.xml +++ b/hadoop-common-project/hadoop-common/src/main/resources/core-default.xml @@ -477,6 +477,19 @@ + + hadoop.security.group.mapping.ldap.conversion.rule + none + + The rule is applied on the group names received from LDAP when + RuleBasedLdapGroupsMapping is configured. + Supported rules are "to_upper", "to_lower" and "none". + to_upper: This will convert all the group names to uppercase. + to_lower: This will convert all the group names to lowercase. + none: This will retain the source formatting, this is default value. + + + hadoop.security.group.mapping.providers diff --git a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/conf/TestCommonConfigurationFields.java b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/conf/TestCommonConfigurationFields.java index 2a6cdc2f6a2..ef441db9f2c 100644 --- a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/conf/TestCommonConfigurationFields.java +++ b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/conf/TestCommonConfigurationFields.java @@ -33,6 +33,7 @@ import org.apache.hadoop.security.CompositeGroupsMapping; import org.apache.hadoop.security.HttpCrossOriginFilterInitializer; import org.apache.hadoop.security.LdapGroupsMapping; +import org.apache.hadoop.security.RuleBasedLdapGroupsMapping; import org.apache.hadoop.security.http.CrossOriginFilter; import org.apache.hadoop.security.ssl.SSLFactory; @@ -71,7 +72,8 @@ public void initializeMemberVariables() { LdapGroupsMapping.class, ZKFailoverController.class, SSLFactory.class, - CompositeGroupsMapping.class + CompositeGroupsMapping.class, + RuleBasedLdapGroupsMapping.class }; // Initialize used variables diff --git a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/security/TestRuleBasedLdapGroupsMapping.java b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/security/TestRuleBasedLdapGroupsMapping.java new file mode 100644 index 00000000000..6592c79e3f5 --- /dev/null +++ b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/security/TestRuleBasedLdapGroupsMapping.java @@ -0,0 +1,99 @@ +/** + * 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 org.apache.hadoop.conf.Configuration; +import org.junit.Assert; +import org.junit.Test; +import org.mockito.Mockito; + +import javax.naming.NamingException; +import java.util.ArrayList; +import java.util.List; + +import static org.apache.hadoop.security.RuleBasedLdapGroupsMapping + .CONVERSION_RULE_KEY; +import static org.mockito.Matchers.anyInt; +import static org.mockito.Matchers.eq; + +/** + * Test cases to verify the rules supported by RuleBasedLdapGroupsMapping. + */ +public class TestRuleBasedLdapGroupsMapping { + + @Test + public void testGetGroupsToUpper() throws NamingException { + RuleBasedLdapGroupsMapping groupsMapping = Mockito.spy( + new RuleBasedLdapGroupsMapping()); + List groups = new ArrayList<>(); + groups.add("group1"); + groups.add("group2"); + Mockito.doReturn(groups).when((LdapGroupsMapping) groupsMapping) + .doGetGroups(eq("admin"), anyInt()); + + Configuration conf = new Configuration(); + conf.set(LdapGroupsMapping.LDAP_URL_KEY, "ldap://test"); + conf.set(CONVERSION_RULE_KEY, "to_upper"); + groupsMapping.setConf(conf); + + List groupsUpper = new ArrayList<>(); + groupsUpper.add("GROUP1"); + groupsUpper.add("GROUP2"); + Assert.assertEquals(groupsUpper, groupsMapping.getGroups("admin")); + } + + @Test + public void testGetGroupsToLower() throws NamingException { + RuleBasedLdapGroupsMapping groupsMapping = Mockito.spy( + new RuleBasedLdapGroupsMapping()); + List groups = new ArrayList<>(); + groups.add("GROUP1"); + groups.add("GROUP2"); + Mockito.doReturn(groups).when((LdapGroupsMapping) groupsMapping) + .doGetGroups(eq("admin"), anyInt()); + + Configuration conf = new Configuration(); + conf.set(LdapGroupsMapping.LDAP_URL_KEY, "ldap://test"); + conf.set(CONVERSION_RULE_KEY, "to_lower"); + groupsMapping.setConf(conf); + + List groupsLower = new ArrayList<>(); + groupsLower.add("group1"); + groupsLower.add("group2"); + Assert.assertEquals(groupsLower, groupsMapping.getGroups("admin")); + } + + @Test + public void testGetGroupsInvalidRule() throws NamingException { + RuleBasedLdapGroupsMapping groupsMapping = Mockito.spy( + new RuleBasedLdapGroupsMapping()); + List groups = new ArrayList<>(); + groups.add("group1"); + groups.add("GROUP2"); + Mockito.doReturn(groups).when((LdapGroupsMapping) groupsMapping) + .doGetGroups(eq("admin"), anyInt()); + + Configuration conf = new Configuration(); + conf.set(LdapGroupsMapping.LDAP_URL_KEY, "ldap://test"); + conf.set(CONVERSION_RULE_KEY, "none"); + groupsMapping.setConf(conf); + + Assert.assertEquals(groups, groupsMapping.getGroups("admin")); + } + +}