You are viewing a plain text version of this content. The canonical link for it is here.
Posted to common-commits@hadoop.apache.org by yj...@apache.org on 2015/03/12 22:54:08 UTC
hadoop git commit: HADOOP-9477. Add posixGroups support for LDAP
groups mapping service. (Dapeng Sun via Yongjun Zhang)
Repository: hadoop
Updated Branches:
refs/heads/trunk 863079bb8 -> 821287741
HADOOP-9477. Add posixGroups support for LDAP groups mapping service. (Dapeng Sun via Yongjun Zhang)
Project: http://git-wip-us.apache.org/repos/asf/hadoop/repo
Commit: http://git-wip-us.apache.org/repos/asf/hadoop/commit/82128774
Tree: http://git-wip-us.apache.org/repos/asf/hadoop/tree/82128774
Diff: http://git-wip-us.apache.org/repos/asf/hadoop/diff/82128774
Branch: refs/heads/trunk
Commit: 82128774156c30a535b62d764bb6cf9c8d2f3a3b
Parents: 863079b
Author: Yongjun Zhang <yz...@cloudera.com>
Authored: Thu Mar 12 14:42:34 2015 -0700
Committer: Yongjun Zhang <yz...@cloudera.com>
Committed: Thu Mar 12 14:52:36 2015 -0700
----------------------------------------------------------------------
hadoop-common-project/hadoop-common/CHANGES.txt | 3 +
.../hadoop/security/LdapGroupsMapping.java | 54 ++++++++--
.../hadoop/security/TestLdapGroupsMapping.java | 42 +-------
.../security/TestLdapGroupsMappingBase.java | 77 ++++++++++++++
.../TestLdapGroupsMappingWithPosixGroup.java | 103 +++++++++++++++++++
5 files changed, 229 insertions(+), 50 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/hadoop/blob/82128774/hadoop-common-project/hadoop-common/CHANGES.txt
----------------------------------------------------------------------
diff --git a/hadoop-common-project/hadoop-common/CHANGES.txt b/hadoop-common-project/hadoop-common/CHANGES.txt
index 40b5cd0..6970bad 100644
--- a/hadoop-common-project/hadoop-common/CHANGES.txt
+++ b/hadoop-common-project/hadoop-common/CHANGES.txt
@@ -37,6 +37,9 @@ Trunk (Unreleased)
HADOOP-11565. Add --slaves shell option (aw)
+ HADOOP-9477. Add posixGroups support for LDAP groups mapping service.
+ (Dapeng Sun via Yongjun Zhang)
+
IMPROVEMENTS
HADOOP-8017. Configure hadoop-main pom to get rid of M2E plugin execution
http://git-wip-us.apache.org/repos/asf/hadoop/blob/82128774/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/LdapGroupsMapping.java
----------------------------------------------------------------------
diff --git a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/LdapGroupsMapping.java b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/LdapGroupsMapping.java
index d463ac7..df91b70 100644
--- a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/LdapGroupsMapping.java
+++ b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/LdapGroupsMapping.java
@@ -150,6 +150,14 @@ public class LdapGroupsMapping
public static final String GROUP_NAME_ATTR_DEFAULT = "cn";
/*
+ * Posix attributes
+ */
+ public static final String POSIX_UIDNUMBER = "uidNumber";
+ public static final String POSIX_GIDNUMBER = "gidNumber";
+ public static final String POSIX_GROUP = "posixGroup";
+ public static final String POSIX_ACCOUNT = "posixAccount";
+
+ /*
* LDAP {@link SearchControls} attribute to set the time limit
* for an invoked directory search. Prevents infinite wait cases.
*/
@@ -178,6 +186,7 @@ public class LdapGroupsMapping
private String userSearchFilter;
private String groupMemberAttr;
private String groupNameAttr;
+ private boolean isPosix;
public static int RECONNECT_RETRY_COUNT = 3;
@@ -242,15 +251,40 @@ public class LdapGroupsMapping
SearchResult result = results.nextElement();
String userDn = result.getNameInNamespace();
- NamingEnumeration<SearchResult> groupResults =
- ctx.search(baseDN,
- "(&" + groupSearchFilter + "(" + groupMemberAttr + "={0}))",
- new Object[]{userDn},
- SEARCH_CONTROLS);
- while (groupResults.hasMoreElements()) {
- SearchResult groupResult = groupResults.nextElement();
- Attribute groupName = groupResult.getAttributes().get(groupNameAttr);
- groups.add(groupName.get().toString());
+ NamingEnumeration<SearchResult> groupResults = null;
+
+ if (isPosix) {
+ String gidNumber = null;
+ String uidNumber = null;
+ Attribute gidAttribute = result.getAttributes().get(POSIX_GIDNUMBER);
+ Attribute uidAttribute = result.getAttributes().get(POSIX_UIDNUMBER);
+ if (gidAttribute != null) {
+ gidNumber = gidAttribute.get().toString();
+ }
+ if (uidAttribute != null) {
+ uidNumber = uidAttribute.get().toString();
+ }
+ if (uidNumber != null && gidNumber != null) {
+ groupResults =
+ ctx.search(baseDN,
+ "(&"+ groupSearchFilter + "(|(" + POSIX_GIDNUMBER + "={0})" +
+ "(" + groupMemberAttr + "={1})))",
+ new Object[] { gidNumber, uidNumber },
+ SEARCH_CONTROLS);
+ }
+ } else {
+ groupResults =
+ ctx.search(baseDN,
+ "(&" + groupSearchFilter + "(" + groupMemberAttr + "={0}))",
+ new Object[]{userDn},
+ SEARCH_CONTROLS);
+ }
+ if (groupResults != null) {
+ while (groupResults.hasMoreElements()) {
+ SearchResult groupResult = groupResults.nextElement();
+ Attribute groupName = groupResult.getAttributes().get(groupNameAttr);
+ groups.add(groupName.get().toString());
+ }
}
}
@@ -334,6 +368,8 @@ public class LdapGroupsMapping
conf.get(GROUP_SEARCH_FILTER_KEY, GROUP_SEARCH_FILTER_DEFAULT);
userSearchFilter =
conf.get(USER_SEARCH_FILTER_KEY, USER_SEARCH_FILTER_DEFAULT);
+ isPosix = groupSearchFilter.contains(POSIX_GROUP) && userSearchFilter
+ .contains(POSIX_ACCOUNT);
groupMemberAttr =
conf.get(GROUP_MEMBERSHIP_ATTR_KEY, GROUP_MEMBERSHIP_ATTR_DEFAULT);
groupNameAttr =
http://git-wip-us.apache.org/repos/asf/hadoop/blob/82128774/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/security/TestLdapGroupsMapping.java
----------------------------------------------------------------------
diff --git a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/security/TestLdapGroupsMapping.java b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/security/TestLdapGroupsMapping.java
index a021a8a..17a14d1 100644
--- a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/security/TestLdapGroupsMapping.java
+++ b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/security/TestLdapGroupsMapping.java
@@ -29,13 +29,7 @@ import java.util.Arrays;
import java.util.List;
import javax.naming.CommunicationException;
-import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
-import javax.naming.directory.Attribute;
-import javax.naming.directory.Attributes;
-import javax.naming.directory.BasicAttribute;
-import javax.naming.directory.BasicAttributes;
-import javax.naming.directory.DirContext;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;
@@ -49,46 +43,12 @@ import org.junit.Before;
import org.junit.Test;
@SuppressWarnings("unchecked")
-public class TestLdapGroupsMapping {
- private DirContext mockContext;
-
- private LdapGroupsMapping mappingSpy = spy(new LdapGroupsMapping());
- private NamingEnumeration mockUserNamingEnum = mock(NamingEnumeration.class);
- private NamingEnumeration mockGroupNamingEnum = mock(NamingEnumeration.class);
- private String[] testGroups = new String[] {"group1", "group2"};
-
+public class TestLdapGroupsMapping extends TestLdapGroupsMappingBase {
@Before
public void setupMocks() throws NamingException {
- mockContext = mock(DirContext.class);
- doReturn(mockContext).when(mappingSpy).getDirContext();
-
SearchResult mockUserResult = mock(SearchResult.class);
- // We only ever call hasMoreElements once for the user NamingEnum, so
- // we can just have one return value
- when(mockUserNamingEnum.hasMoreElements()).thenReturn(true);
when(mockUserNamingEnum.nextElement()).thenReturn(mockUserResult);
when(mockUserResult.getNameInNamespace()).thenReturn("CN=some_user,DC=test,DC=com");
-
- SearchResult mockGroupResult = mock(SearchResult.class);
- // We're going to have to define the loop here. We want two iterations,
- // to get both the groups
- when(mockGroupNamingEnum.hasMoreElements()).thenReturn(true, true, false);
- when(mockGroupNamingEnum.nextElement()).thenReturn(mockGroupResult);
-
- // Define the attribute for the name of the first group
- Attribute group1Attr = new BasicAttribute("cn");
- group1Attr.add(testGroups[0]);
- Attributes group1Attrs = new BasicAttributes();
- group1Attrs.put(group1Attr);
-
- // Define the attribute for the name of the second group
- Attribute group2Attr = new BasicAttribute("cn");
- group2Attr.add(testGroups[1]);
- Attributes group2Attrs = new BasicAttributes();
- group2Attrs.put(group2Attr);
-
- // This search result gets reused, so return group1, then group2
- when(mockGroupResult.getAttributes()).thenReturn(group1Attrs, group2Attrs);
}
@Test
http://git-wip-us.apache.org/repos/asf/hadoop/blob/82128774/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/security/TestLdapGroupsMappingBase.java
----------------------------------------------------------------------
diff --git a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/security/TestLdapGroupsMappingBase.java b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/security/TestLdapGroupsMappingBase.java
new file mode 100644
index 0000000..c54ac4c
--- /dev/null
+++ b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/security/TestLdapGroupsMappingBase.java
@@ -0,0 +1,77 @@
+/**
+ * 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 static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.BasicAttribute;
+import javax.naming.directory.BasicAttributes;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.SearchResult;
+
+import org.junit.Before;
+
+public class TestLdapGroupsMappingBase {
+ protected DirContext mockContext;
+
+ protected LdapGroupsMapping mappingSpy = spy(new LdapGroupsMapping());
+ protected NamingEnumeration mockUserNamingEnum =
+ mock(NamingEnumeration.class);
+ protected NamingEnumeration mockGroupNamingEnum =
+ mock(NamingEnumeration.class);
+ protected String[] testGroups = new String[] {"group1", "group2"};
+
+ @Before
+ public void setupMocksBase() throws NamingException {
+ mockContext = mock(DirContext.class);
+ doReturn(mockContext).when(mappingSpy).getDirContext();
+
+ // We only ever call hasMoreElements once for the user NamingEnum, so
+ // we can just have one return value
+ when(mockUserNamingEnum.hasMoreElements()).thenReturn(true);
+
+ SearchResult mockGroupResult = mock(SearchResult.class);
+ // We're going to have to define the loop here. We want two iterations,
+ // to get both the groups
+ when(mockGroupNamingEnum.hasMoreElements()).thenReturn(true, true, false);
+ when(mockGroupNamingEnum.nextElement()).thenReturn(mockGroupResult);
+
+ // Define the attribute for the name of the first group
+ Attribute group1Attr = new BasicAttribute("cn");
+ group1Attr.add(testGroups[0]);
+ Attributes group1Attrs = new BasicAttributes();
+ group1Attrs.put(group1Attr);
+
+ // Define the attribute for the name of the second group
+ Attribute group2Attr = new BasicAttribute("cn");
+ group2Attr.add(testGroups[1]);
+ Attributes group2Attrs = new BasicAttributes();
+ group2Attrs.put(group2Attr);
+
+ // This search result gets reused, so return group1, then group2
+ when(mockGroupResult.getAttributes()).thenReturn(group1Attrs, group2Attrs);
+ }
+}
http://git-wip-us.apache.org/repos/asf/hadoop/blob/82128774/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/security/TestLdapGroupsMappingWithPosixGroup.java
----------------------------------------------------------------------
diff --git a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/security/TestLdapGroupsMappingWithPosixGroup.java b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/security/TestLdapGroupsMappingWithPosixGroup.java
new file mode 100644
index 0000000..1d1a354
--- /dev/null
+++ b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/security/TestLdapGroupsMappingWithPosixGroup.java
@@ -0,0 +1,103 @@
+/**
+ * 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 static org.mockito.Matchers.anyString;
+
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.contains;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.List;
+
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.SearchControls;
+import javax.naming.directory.SearchResult;
+
+import org.apache.hadoop.conf.Configuration;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+@SuppressWarnings("unchecked")
+public class TestLdapGroupsMappingWithPosixGroup
+ extends TestLdapGroupsMappingBase {
+
+ @Before
+ public void setupMocks() throws NamingException {
+ SearchResult mockUserResult = mock(SearchResult.class);
+ when(mockUserNamingEnum.nextElement()).thenReturn(mockUserResult);
+
+ Attribute mockUidAttr = mock(Attribute.class);
+ Attribute mockGidAttr = mock(Attribute.class);
+ Attributes mockAttrs = mock(Attributes.class);
+
+ when(mockUidAttr.get()).thenReturn("700");
+ when(mockGidAttr.get()).thenReturn("600");
+ when(mockAttrs.get(eq("uidNumber"))).thenReturn(mockUidAttr);
+ when(mockAttrs.get(eq("gidNumber"))).thenReturn(mockGidAttr);
+
+ when(mockUserResult.getAttributes()).thenReturn(mockAttrs);
+ }
+
+ @Test
+ public void testGetGroups() throws IOException, NamingException {
+ // The search functionality of the mock context is reused, so we will
+ // return the user NamingEnumeration first, and then the group
+ when(mockContext.search(anyString(), contains("posix"),
+ any(Object[].class), any(SearchControls.class)))
+ .thenReturn(mockUserNamingEnum, mockGroupNamingEnum);
+
+ doTestGetGroups(Arrays.asList(testGroups), 2);
+ }
+
+ private void doTestGetGroups(List<String> expectedGroups, int searchTimes)
+ throws IOException, NamingException {
+ Configuration conf = new Configuration();
+ // Set this, so we don't throw an exception
+ conf.set(LdapGroupsMapping.LDAP_URL_KEY, "ldap://test");
+ conf.set(LdapGroupsMapping.GROUP_SEARCH_FILTER_KEY,
+ "(objectClass=posixGroup)(cn={0})");
+ conf.set(LdapGroupsMapping.USER_SEARCH_FILTER_KEY,
+ "(objectClass=posixAccount)");
+ conf.set(LdapGroupsMapping.GROUP_MEMBERSHIP_ATTR_KEY, "memberUid");
+ conf.set(LdapGroupsMapping.GROUP_NAME_ATTR_KEY, "cn");
+
+ mappingSpy.setConf(conf);
+ // Username is arbitrary, since the spy is mocked to respond the same,
+ // regardless of input
+ List<String> groups = mappingSpy.getGroups("some_user");
+
+ Assert.assertEquals(expectedGroups, groups);
+
+ // We should have searched for a user, and then two groups
+ verify(mockContext, times(searchTimes)).search(anyString(),
+ anyString(),
+ any(Object[].class),
+ any(SearchControls.class));
+ }
+}