You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by ma...@apache.org on 2014/09/10 00:15:40 UTC
[1/2] AMBARI-7230. LDAP Sync Scale issues for thousands of
users/100's of groups. (mahadev)
Repository: ambari
Updated Branches:
refs/heads/trunk fbe8b876a -> 17b48bf40
http://git-wip-us.apache.org/repos/asf/ambari/blob/17b48bf4/ambari-server/src/main/java/org/apache/ambari/server/security/ldap/LdapUserGroupMemberDto.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/security/ldap/LdapUserGroupMemberDto.java b/ambari-server/src/main/java/org/apache/ambari/server/security/ldap/LdapUserGroupMemberDto.java
new file mode 100644
index 0000000..319b831
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/security/ldap/LdapUserGroupMemberDto.java
@@ -0,0 +1,82 @@
+/**
+ * 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.ambari.server.security.ldap;
+
+/**
+ * Pojo with information about LDAP membership.
+ */
+public class LdapUserGroupMemberDto {
+ /**
+ * Name of the group.
+ */
+ private final String groupName;
+
+ /**
+ * Name of the user.
+ */
+ private final String userName;
+
+ /**
+ * Constructor.
+ *
+ * @param groupName group name
+ * @param userName user name
+ */
+ public LdapUserGroupMemberDto(String groupName, String userName) {
+ this.groupName = groupName;
+ this.userName = userName;
+ }
+
+ /**
+ * Get the group name.
+ *
+ * @return the group name
+ */
+ public String getGroupName() {
+ return groupName;
+ }
+
+ /**
+ * Get the user name.
+ *
+ * @return the user name
+ */
+ public String getUserName() {
+ return userName;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ LdapUserGroupMemberDto that = (LdapUserGroupMemberDto) o;
+
+ if (userName != null ? !userName.equals(that.userName) : that.userName != null) return false;
+ if (groupName != null ? !groupName.equals(that.groupName) : that.groupName != null) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = userName != null ? userName.hashCode() : 0;
+ result = 31 * result + (groupName != null ? groupName.hashCode() : 0);
+ return result;
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/17b48bf4/ambari-server/src/main/python/ambari-server.py
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/python/ambari-server.py b/ambari-server/src/main/python/ambari-server.py
index 81b79d1..35ba17b 100755
--- a/ambari-server/src/main/python/ambari-server.py
+++ b/ambari-server/src/main/python/ambari-server.py
@@ -2940,11 +2940,11 @@ def setup_ldap():
LDAP_PRIMARY_URL_DEFAULT = get_value_from_properties(properties, ldap_property_list_reqd[0])
LDAP_SECONDARY_URL_DEFAULT = get_value_from_properties(properties, ldap_property_list_reqd[1])
LDAP_USE_SSL_DEFAULT = get_value_from_properties(properties, ldap_property_list_reqd[2], "false")
- LDAP_USER_CLASS_DEFAULT = get_value_from_properties(properties, ldap_property_list_reqd[3], "person")
+ LDAP_USER_CLASS_DEFAULT = get_value_from_properties(properties, ldap_property_list_reqd[3], "posixAccount")
LDAP_USER_ATT_DEFAULT = get_value_from_properties(properties, ldap_property_list_reqd[4], "uid")
- LDAP_GROUP_CLASS_DEFAULT = get_value_from_properties(properties, ldap_property_list_reqd[5], "groupOfUniqueNames")
+ LDAP_GROUP_CLASS_DEFAULT = get_value_from_properties(properties, ldap_property_list_reqd[5], "posixGroup")
LDAP_GROUP_ATT_DEFAULT = get_value_from_properties(properties, ldap_property_list_reqd[6], "cn")
- LDAP_GROUP_MEMBER_DEFAULT = get_value_from_properties(properties, ldap_property_list_reqd[7], "uniqueMember")
+ LDAP_GROUP_MEMBER_DEFAULT = get_value_from_properties(properties, ldap_property_list_reqd[7], "memberUid")
LDAP_BASE_DN_DEFAULT = get_value_from_properties(properties, ldap_property_list_reqd[8])
LDAP_BIND_DEFAULT = get_value_from_properties(properties, ldap_property_list_reqd[9], "false")
LDAP_MGR_DN_DEFAULT = get_value_from_properties(properties, ldap_property_list_opt[0])
http://git-wip-us.apache.org/repos/asf/ambari/blob/17b48bf4/ambari-server/src/main/resources/META-INF/persistence.xml
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/resources/META-INF/persistence.xml b/ambari-server/src/main/resources/META-INF/persistence.xml
index dfbf93e..2d8fedb 100644
--- a/ambari-server/src/main/resources/META-INF/persistence.xml
+++ b/ambari-server/src/main/resources/META-INF/persistence.xml
@@ -75,7 +75,11 @@
<!--<property name="javax.persistence.jdbc.driver" value="org.postgresql.Driver" />-->
<property name="eclipselink.cache.size.default" value="10000" />
<property name="eclipselink.jdbc.batch-writing" value="JDBC"/>
+ <property name="eclipselink.jdbc.batch-writing.size" value="4000"/>
+ <property name="eclipselink.jdbc.sequence-connection-pool" value="true" />
<property name="eclipselink.weaving" value="static" />
+
+ <!--<property name="eclipselink.logging.level.sql" value="FINEST" />-->
<!--<property name="eclipselink.id-validation" value="NULL" />-->
</properties>
http://git-wip-us.apache.org/repos/asf/ambari/blob/17b48bf4/ambari-server/src/test/java/org/apache/ambari/server/security/authorization/AmbariLdapDataPopulatorTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/security/authorization/AmbariLdapDataPopulatorTest.java b/ambari-server/src/test/java/org/apache/ambari/server/security/authorization/AmbariLdapDataPopulatorTest.java
deleted file mode 100644
index 23f53cf..0000000
--- a/ambari-server/src/test/java/org/apache/ambari/server/security/authorization/AmbariLdapDataPopulatorTest.java
+++ /dev/null
@@ -1,261 +0,0 @@
-/**
- * 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.ambari.server.security.authorization;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-import junit.framework.Assert;
-
-import org.apache.ambari.server.AmbariException;
-import org.apache.ambari.server.configuration.Configuration;
-import org.apache.ambari.server.orm.entities.GroupEntity;
-import org.apache.ambari.server.orm.entities.MemberEntity;
-import org.apache.ambari.server.orm.entities.PrincipalEntity;
-import org.apache.ambari.server.orm.entities.PrivilegeEntity;
-import org.apache.ambari.server.orm.entities.UserEntity;
-import org.easymock.Capture;
-import org.easymock.EasyMock;
-import org.easymock.IAnswer;
-import org.junit.Test;
-import org.springframework.ldap.core.LdapTemplate;
-
-public class AmbariLdapDataPopulatorTest {
- private static class AmbariLdapDataPopulatorTestInstance extends AmbariLdapDataPopulator {
-
- public AmbariLdapDataPopulatorTestInstance(Configuration configuration,
- Users users) {
- super(configuration, users);
- this.ldapServerProperties = EasyMock.createNiceMock(LdapServerProperties.class);
- }
-
- final LdapTemplate ldapTemplate = EasyMock.createNiceMock(LdapTemplate.class);
-
- @Override
- protected LdapTemplate loadLdapTemplate() {
- return ldapTemplate;
- }
-
- public LdapServerProperties getLdapServerProperties() {
- return this.ldapServerProperties;
- }
- }
-
- @Test
- public void testRefreshGroupMembers() throws AmbariException {
- final Configuration configuration = EasyMock.createNiceMock(Configuration.class);
- final Users users = EasyMock.createNiceMock(Users.class);
-
- final GroupEntity ldapGroup = new GroupEntity();
- ldapGroup.setGroupId(1);
- ldapGroup.setGroupName("ldapGroup");
- ldapGroup.setLdapGroup(true);
- ldapGroup.setMemberEntities(new HashSet<MemberEntity>());
-
- final User ldapUserWithoutGroup = createLdapUserWithoutGroup();
- final User ldapUserWithGroup = createLdapUserWithGroup(ldapGroup);
- final User localUserWithoutGroup = createLocalUserWithoutGroup();
- final User localUserWithGroup = createLocalUserWithGroup(ldapGroup);
-
- final AmbariLdapDataPopulator populator = new AmbariLdapDataPopulatorTestInstance(configuration, users) {
- @Override
- protected Set<String> getExternalLdapGroupMembers(String groupName) {
- return new HashSet<String>() {
- {
- add(ldapUserWithGroup.getUserName());
- add(ldapUserWithoutGroup.getUserName());
- }
- };
- }
-
- @Override
- protected Map<String, User> getInternalUsers() {
- return new HashMap<String, User>() {
- {
- put(localUserWithGroup.getUserName(), localUserWithGroup);
- put(localUserWithoutGroup.getUserName(), localUserWithoutGroup);
- }
- };
- }
-
- @Override
- protected Map<String, User> getInternalMembers(String groupName) {
- return new HashMap<String, User>() {
- {
- put(localUserWithGroup.getUserName(), localUserWithGroup);
- }
- };
- }
- };
-
- users.createUser(EasyMock.<String> anyObject(), EasyMock.<String> anyObject());
- EasyMock.expectLastCall().times(2);
-
- users.addMemberToGroup(EasyMock.<String> anyObject(), EasyMock.<String> anyObject());
- EasyMock.expectLastCall().times(2);
-
- EasyMock.replay(users);
-
- populator.refreshGroupMembers(ldapGroup.getGroupName());
-
- EasyMock.verify(users);
- }
-
- @Test
- public void testIsLdapEnabled_badConfiguration() {
- final Configuration configuration = EasyMock.createNiceMock(Configuration.class);
- final Users users = EasyMock.createNiceMock(Users.class);
-
- final AmbariLdapDataPopulator populator = new AmbariLdapDataPopulatorTestInstance(configuration, users);
-
- EasyMock.expect(configuration.isLdapConfigured()).andReturn(true);
- EasyMock.expect(populator.loadLdapTemplate().list(EasyMock. <String>anyObject())).andThrow(new NullPointerException()).once();
- EasyMock.replay(populator.loadLdapTemplate(), configuration);
-
- Assert.assertFalse(populator.isLdapEnabled());
- EasyMock.verify(populator.loadLdapTemplate(), configuration);
- }
-
- @Test
- public void testIsLdapEnabled_reallyEnabled() {
- final Configuration configuration = EasyMock.createNiceMock(Configuration.class);
- final Users users = EasyMock.createNiceMock(Users.class);
-
- final AmbariLdapDataPopulator populator = new AmbariLdapDataPopulatorTestInstance(configuration, users);
-
- EasyMock.expect(configuration.isLdapConfigured()).andReturn(true);
- EasyMock.expect(populator.loadLdapTemplate().list(EasyMock. <String>anyObject())).andReturn(Collections.emptyList()).once();
- EasyMock.replay(populator.loadLdapTemplate(),configuration);
-
- Assert.assertTrue(populator.isLdapEnabled());
- EasyMock.verify(populator.loadLdapTemplate(), configuration);
- }
-
- @Test
- public void testIsLdapEnabled_reallyDisabled() {
- final Configuration configuration = EasyMock.createNiceMock(Configuration.class);
- final Users users = EasyMock.createNiceMock(Users.class);
-
- final AmbariLdapDataPopulator populator = new AmbariLdapDataPopulatorTestInstance(configuration, users);
-
- EasyMock.expect(configuration.isLdapConfigured()).andReturn(false);
- EasyMock.replay(populator.loadLdapTemplate(), configuration);
-
- Assert.assertFalse(populator.isLdapEnabled());
- EasyMock.verify(populator.loadLdapTemplate(), configuration);
- }
-
- @Test
- @SuppressWarnings("serial")
- public void testCleanUpLdapUsersWithoutGroup() throws AmbariException {
- final Configuration configuration = EasyMock.createNiceMock(Configuration.class);
- final Users users = EasyMock.createNiceMock(Users.class);
-
- final GroupEntity ldapGroup = new GroupEntity();
- ldapGroup.setGroupId(1);
- ldapGroup.setGroupName("ldapGroup");
- ldapGroup.setLdapGroup(true);
- ldapGroup.setMemberEntities(new HashSet<MemberEntity>());
-
- final User ldapUserWithoutGroup = createLdapUserWithoutGroup();
- final User ldapUserWithGroup = createLdapUserWithGroup(ldapGroup);
- final User localUserWithoutGroup = createLocalUserWithoutGroup();
- final User localUserWithGroup = createLocalUserWithGroup(ldapGroup);
-
- final List<User> allUsers = new ArrayList<User>() {
- {
- add(ldapUserWithoutGroup);
- add(ldapUserWithGroup);
- add(localUserWithoutGroup);
- add(localUserWithGroup);
- }
- };
- EasyMock.expect(users.getAllUsers()).andReturn(new ArrayList<User>(allUsers));
-
- final List<User> removedUsers = new ArrayList<User>();
- final Capture<User> userCapture = new Capture<User>();
- users.removeUser(EasyMock.capture(userCapture));
- EasyMock.expectLastCall().andAnswer(new IAnswer<Void>() {
- @Override
- public Void answer() throws Throwable {
- removedUsers.add(userCapture.getValue());
- allUsers.remove(userCapture.getValue());
- return null;
- }
- });
-
- EasyMock.replay(users);
-
- final AmbariLdapDataPopulator populator = new AmbariLdapDataPopulatorTestInstance(configuration, users);
- populator.cleanUpLdapUsersWithoutGroup();
-
- Assert.assertEquals(removedUsers.size(), 1);
- Assert.assertEquals(allUsers.size(), 3);
- Assert.assertTrue(allUsers.contains(ldapUserWithGroup));
- Assert.assertTrue(allUsers.contains(localUserWithoutGroup));
- Assert.assertTrue(allUsers.contains(localUserWithGroup));
- Assert.assertEquals(removedUsers.get(0), ldapUserWithoutGroup);
-
- EasyMock.verify(users);
- }
-
- private static int userIdCounter = 1;
-
- private User createUser(String name, boolean ldapUser, GroupEntity group) {
- final UserEntity userEntity = new UserEntity();
- userEntity.setUserId(userIdCounter++);
- userEntity.setUserName(name);
- userEntity.setCreateTime(new Date());
- userEntity.setLdapUser(ldapUser);
- userEntity.setActive(true);
- userEntity.setMemberEntities(new HashSet<MemberEntity>());
- final PrincipalEntity principalEntity = new PrincipalEntity();
- principalEntity.setPrivileges(new HashSet<PrivilegeEntity>());
- userEntity.setPrincipal(principalEntity);
- if (group != null) {
- final MemberEntity member = new MemberEntity();
- member.setUser(userEntity);
- member.setGroup(group);
- group.getMemberEntities().add(member);
- userEntity.getMemberEntities().add(member);
- }
- return new User(userEntity);
- }
-
- private User createLdapUserWithoutGroup() {
- return createUser("LdapUserWithoutGroup", true, null);
- }
-
- private User createLocalUserWithoutGroup() {
- return createUser("LocalUserWithoutGroup", false, null);
- }
-
- private User createLdapUserWithGroup(GroupEntity group) {
- return createUser("LdapUserWithGroup", true, group);
- }
-
- private User createLocalUserWithGroup(GroupEntity group) {
- return createUser("LocalUserWithGroup", false, group);
- }
-}
http://git-wip-us.apache.org/repos/asf/ambari/blob/17b48bf4/ambari-server/src/test/java/org/apache/ambari/server/security/ldap/AmbariLdapDataPopulatorTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/security/ldap/AmbariLdapDataPopulatorTest.java b/ambari-server/src/test/java/org/apache/ambari/server/security/ldap/AmbariLdapDataPopulatorTest.java
new file mode 100644
index 0000000..906d695
--- /dev/null
+++ b/ambari-server/src/test/java/org/apache/ambari/server/security/ldap/AmbariLdapDataPopulatorTest.java
@@ -0,0 +1,202 @@
+/**
+ * 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.ambari.server.security.ldap;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.List;
+import junit.framework.Assert;
+
+import org.apache.ambari.server.AmbariException;
+import org.apache.ambari.server.configuration.Configuration;
+import org.apache.ambari.server.orm.entities.GroupEntity;
+import org.apache.ambari.server.orm.entities.MemberEntity;
+import org.apache.ambari.server.orm.entities.PrincipalEntity;
+import org.apache.ambari.server.orm.entities.PrivilegeEntity;
+import org.apache.ambari.server.orm.entities.UserEntity;
+import org.apache.ambari.server.security.authorization.LdapServerProperties;
+import org.apache.ambari.server.security.authorization.User;
+import org.apache.ambari.server.security.authorization.Users;
+import org.apache.ambari.server.security.ldap.AmbariLdapDataPopulator;
+import org.easymock.Capture;
+import org.easymock.EasyMock;
+import org.easymock.IAnswer;
+import org.junit.Test;
+import org.springframework.ldap.core.AttributesMapper;
+import org.springframework.ldap.core.LdapTemplate;
+
+public class AmbariLdapDataPopulatorTest {
+ private static class AmbariLdapDataPopulatorTestInstance extends AmbariLdapDataPopulator {
+
+ public AmbariLdapDataPopulatorTestInstance(Configuration configuration,
+ Users users) {
+ super(configuration, users);
+ this.ldapServerProperties = EasyMock.createNiceMock(LdapServerProperties.class);
+ }
+
+ final LdapTemplate ldapTemplate = EasyMock.createNiceMock(LdapTemplate.class);
+
+ @Override
+ protected LdapTemplate loadLdapTemplate() {
+ return ldapTemplate;
+ }
+
+ public LdapServerProperties getLdapServerProperties() {
+ return this.ldapServerProperties;
+ }
+ }
+
+ @Test
+ public void testIsLdapEnabled_badConfiguration() {
+ final Configuration configuration = EasyMock.createNiceMock(Configuration.class);
+ final Users users = EasyMock.createNiceMock(Users.class);
+
+ final AmbariLdapDataPopulator populator = new AmbariLdapDataPopulatorTestInstance(configuration, users);
+
+ EasyMock.expect(configuration.isLdapConfigured()).andReturn(true);
+ EasyMock.expect(populator.loadLdapTemplate().search(EasyMock. <String>anyObject(), EasyMock. <String>anyObject(), EasyMock. <AttributesMapper>anyObject())).andThrow(new NullPointerException()).once();
+ EasyMock.replay(populator.loadLdapTemplate(), configuration);
+
+ Assert.assertFalse(populator.isLdapEnabled());
+ EasyMock.verify(populator.loadLdapTemplate(), configuration);
+ }
+
+ @Test
+ public void testIsLdapEnabled_reallyEnabled() {
+ final Configuration configuration = EasyMock.createNiceMock(Configuration.class);
+ final Users users = EasyMock.createNiceMock(Users.class);
+
+ final AmbariLdapDataPopulator populator = new AmbariLdapDataPopulatorTestInstance(configuration, users);
+
+ EasyMock.expect(configuration.isLdapConfigured()).andReturn(true);
+ EasyMock.expect(populator.loadLdapTemplate().search(EasyMock. <String>anyObject(), EasyMock. <String>anyObject(), EasyMock. <AttributesMapper>anyObject())).andReturn(Collections.emptyList()).once();
+ EasyMock.replay(populator.loadLdapTemplate(), configuration);
+
+ Assert.assertTrue(populator.isLdapEnabled());
+ EasyMock.verify(populator.loadLdapTemplate(), configuration);
+ }
+
+ @Test
+ public void testIsLdapEnabled_reallyDisabled() {
+ final Configuration configuration = EasyMock.createNiceMock(Configuration.class);
+ final Users users = EasyMock.createNiceMock(Users.class);
+
+ final AmbariLdapDataPopulator populator = new AmbariLdapDataPopulatorTestInstance(configuration, users);
+
+ EasyMock.expect(configuration.isLdapConfigured()).andReturn(false);
+ EasyMock.replay(populator.loadLdapTemplate(), configuration);
+
+ Assert.assertFalse(populator.isLdapEnabled());
+ EasyMock.verify(populator.loadLdapTemplate(), configuration);
+ }
+
+ @Test
+ @SuppressWarnings("serial")
+ public void testCleanUpLdapUsersWithoutGroup() throws AmbariException {
+ final Configuration configuration = EasyMock.createNiceMock(Configuration.class);
+ final Users users = EasyMock.createNiceMock(Users.class);
+
+ final GroupEntity ldapGroup = new GroupEntity();
+ ldapGroup.setGroupId(1);
+ ldapGroup.setGroupName("ldapGroup");
+ ldapGroup.setLdapGroup(true);
+ ldapGroup.setMemberEntities(new HashSet<MemberEntity>());
+
+ final User ldapUserWithoutGroup = createLdapUserWithoutGroup();
+ final User ldapUserWithGroup = createLdapUserWithGroup(ldapGroup);
+ final User localUserWithoutGroup = createLocalUserWithoutGroup();
+ final User localUserWithGroup = createLocalUserWithGroup(ldapGroup);
+
+ final List<User> allUsers = new ArrayList<User>() {
+ {
+ add(ldapUserWithoutGroup);
+ add(ldapUserWithGroup);
+ add(localUserWithoutGroup);
+ add(localUserWithGroup);
+ }
+ };
+ EasyMock.expect(users.getAllUsers()).andReturn(new ArrayList<User>(allUsers));
+
+ final List<User> removedUsers = new ArrayList<User>();
+ final Capture<User> userCapture = new Capture<User>();
+ users.removeUser(EasyMock.capture(userCapture));
+ EasyMock.expectLastCall().andAnswer(new IAnswer<Void>() {
+ @Override
+ public Void answer() throws Throwable {
+ removedUsers.add(userCapture.getValue());
+ allUsers.remove(userCapture.getValue());
+ return null;
+ }
+ });
+
+ EasyMock.replay(users);
+
+ final AmbariLdapDataPopulator populator = new AmbariLdapDataPopulatorTestInstance(configuration, users);
+ populator.cleanUpLdapUsersWithoutGroup();
+
+ Assert.assertEquals(removedUsers.size(), 1);
+ Assert.assertEquals(allUsers.size(), 3);
+ Assert.assertTrue(allUsers.contains(ldapUserWithGroup));
+ Assert.assertTrue(allUsers.contains(localUserWithoutGroup));
+ Assert.assertTrue(allUsers.contains(localUserWithGroup));
+ Assert.assertEquals(removedUsers.get(0), ldapUserWithoutGroup);
+
+ EasyMock.verify(users);
+ }
+
+ private static int userIdCounter = 1;
+
+ private User createUser(String name, boolean ldapUser, GroupEntity group) {
+ final UserEntity userEntity = new UserEntity();
+ userEntity.setUserId(userIdCounter++);
+ userEntity.setUserName(name);
+ userEntity.setCreateTime(new Date());
+ userEntity.setLdapUser(ldapUser);
+ userEntity.setActive(true);
+ userEntity.setMemberEntities(new HashSet<MemberEntity>());
+ final PrincipalEntity principalEntity = new PrincipalEntity();
+ principalEntity.setPrivileges(new HashSet<PrivilegeEntity>());
+ userEntity.setPrincipal(principalEntity);
+ if (group != null) {
+ final MemberEntity member = new MemberEntity();
+ member.setUser(userEntity);
+ member.setGroup(group);
+ group.getMemberEntities().add(member);
+ userEntity.getMemberEntities().add(member);
+ }
+ return new User(userEntity);
+ }
+
+ private User createLdapUserWithoutGroup() {
+ return createUser("LdapUserWithoutGroup", true, null);
+ }
+
+ private User createLocalUserWithoutGroup() {
+ return createUser("LocalUserWithoutGroup", false, null);
+ }
+
+ private User createLdapUserWithGroup(GroupEntity group) {
+ return createUser("LdapUserWithGroup", true, group);
+ }
+
+ private User createLocalUserWithGroup(GroupEntity group) {
+ return createUser("LocalUserWithGroup", false, group);
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/17b48bf4/ambari-server/src/test/java/org/apache/ambari/server/security/ldap/LdapPerformanceTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/security/ldap/LdapPerformanceTest.java b/ambari-server/src/test/java/org/apache/ambari/server/security/ldap/LdapPerformanceTest.java
new file mode 100644
index 0000000..a2cb388
--- /dev/null
+++ b/ambari-server/src/test/java/org/apache/ambari/server/security/ldap/LdapPerformanceTest.java
@@ -0,0 +1,94 @@
+/*
+ * 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.ambari.server.security.ldap;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import org.apache.ambari.server.AmbariException;
+import org.apache.ambari.server.configuration.Configuration;
+import org.apache.ambari.server.orm.GuiceJpaInitializer;
+import org.apache.ambari.server.security.ClientSecurityType;
+import org.apache.ambari.server.security.authorization.AuthorizationTestModule;
+import org.apache.ambari.server.security.authorization.Users;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+
+import com.google.inject.Guice;
+import com.google.inject.Inject;
+import com.google.inject.Injector;
+
+/**
+ * Performs sync request to real LDAP server.
+ */
+@Ignore
+public class LdapPerformanceTest {
+
+ private static Injector injector;
+
+ @Inject
+ private AmbariLdapDataPopulator populator;
+
+ @Inject
+ private Users users;
+
+ @Inject
+ Configuration configuration;
+
+ final String SPRING_CONTEXT_LOCATION = "classpath:webapp/WEB-INF/spring-security.xml";
+
+ @Before
+ public void setUp() {
+ injector = Guice.createInjector(new AuthorizationTestModule());
+
+ injector.injectMembers(this);
+ injector.getInstance(GuiceJpaInitializer.class);
+ configuration.setClientSecurityType(ClientSecurityType.LDAP);
+ configuration.setLdap("c6402.ambari.apache.org:389", "posixAccount", "uid",
+ "posixGroup", "cn", "memberUid", "dc=apache,dc=org", false,
+ "uid=hdfs,ou=people,ou=dev,dc=apache,dc=org", "hdfs");
+ }
+
+ @Test
+ public void testLdapSync() throws AmbariException, InterruptedException {
+ long time = System.currentTimeMillis();
+ Set<LdapGroupDto> groups = populator.getExternalLdapGroupInfo();
+ Set<LdapUserDto> users = populator.getExternalLdapUserInfo();
+ Set<String> userNames = new HashSet<String>();
+ for (LdapUserDto user : users) {
+ userNames.add(user.getUserName());
+ }
+ Set<String> groupNames = new HashSet<String>();
+ for (LdapGroupDto group : groups) {
+ groupNames.add(group.getGroupName());
+ }
+ System.out.println("Data fetch: " + (System.currentTimeMillis() - time));
+ time = System.currentTimeMillis();
+ LdapBatchDto batchDto = populator.synchronizeLdapUsersAndGroups(userNames, groupNames);
+ batchDto = populator.synchronizeLdapUsersAndGroups(userNames, groupNames);
+ this.users.processLdapSync(batchDto);
+ System.out.println("Initial sync: " + (System.currentTimeMillis() - time));
+ time = System.currentTimeMillis();
+ batchDto = populator.synchronizeLdapUsersAndGroups(userNames, groupNames);
+ this.users.processLdapSync(batchDto);
+ System.out.println("Subsequent sync: " + (System.currentTimeMillis() - time));
+ time = System.currentTimeMillis();
+ }
+}
[2/2] git commit: AMBARI-7230. LDAP Sync Scale issues for thousands
of users/100's of groups. (mahadev)
Posted by ma...@apache.org.
AMBARI-7230. LDAP Sync Scale issues for thousands of users/100's of groups. (mahadev)
Project: http://git-wip-us.apache.org/repos/asf/ambari/repo
Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/17b48bf4
Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/17b48bf4
Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/17b48bf4
Branch: refs/heads/trunk
Commit: 17b48bf40ff58a1bf429d1fbc14b3049ef79391d
Parents: fbe8b87
Author: Mahadev Konar <ma...@apache.org>
Authored: Tue Sep 9 15:09:00 2014 -0700
Committer: Mahadev Konar <ma...@apache.org>
Committed: Tue Sep 9 15:09:08 2014 -0700
----------------------------------------------------------------------
.../server/configuration/Configuration.java | 14 +
.../controller/AmbariManagementController.java | 16 +-
.../AmbariManagementControllerImpl.java | 34 +-
.../ambari/server/controller/AmbariServer.java | 6 +-
.../internal/ControllerResourceProvider.java | 22 +-
.../apache/ambari/server/orm/dao/GroupDAO.java | 30 +-
.../apache/ambari/server/orm/dao/MemberDAO.java | 34 +-
.../ambari/server/orm/dao/PrincipalDAO.java | 27 +-
.../ambari/server/orm/dao/PrincipalTypeDAO.java | 29 +-
.../apache/ambari/server/orm/dao/UserDAO.java | 49 ++-
.../server/orm/entities/MemberEntity.java | 7 +-
.../server/orm/entities/PrincipalEntity.java | 2 +-
.../ambari/server/orm/entities/UserEntity.java | 5 +-
.../authorization/AmbariLdapDataPopulator.java | 409 ------------------
.../server/security/authorization/User.java | 2 +-
.../server/security/authorization/Users.java | 158 ++++++-
.../security/ldap/AmbariLdapDataPopulator.java | 430 +++++++++++++++++++
.../server/security/ldap/LdapBatchDto.java | 67 +++
.../server/security/ldap/LdapGroupDto.java | 113 +++++
.../server/security/ldap/LdapSyncDto.java | 72 ++++
.../server/security/ldap/LdapUserDto.java | 133 ++++++
.../security/ldap/LdapUserGroupMemberDto.java | 82 ++++
ambari-server/src/main/python/ambari-server.py | 6 +-
.../src/main/resources/META-INF/persistence.xml | 4 +
.../AmbariLdapDataPopulatorTest.java | 261 -----------
.../ldap/AmbariLdapDataPopulatorTest.java | 202 +++++++++
.../security/ldap/LdapPerformanceTest.java | 94 ++++
27 files changed, 1561 insertions(+), 747 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/ambari/blob/17b48bf4/ambari-server/src/main/java/org/apache/ambari/server/configuration/Configuration.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/configuration/Configuration.java b/ambari-server/src/main/java/org/apache/ambari/server/configuration/Configuration.java
index ae20d43..a21f98c 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/configuration/Configuration.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/configuration/Configuration.java
@@ -576,6 +576,20 @@ public class Configuration {
properties.setProperty(CLIENT_SECURITY_KEY, type.toString());
}
+ public void setLdap(String host, String userClass, String userNameAttr, String groupClass, String groupName, String groupMember,
+ String baseDN, boolean anon, String managerDN, String managerPass) {
+ properties.setProperty(LDAP_PRIMARY_URL_KEY, host);
+ properties.setProperty(LDAP_USER_OBJECT_CLASS_KEY, userClass);
+ properties.setProperty(LDAP_USERNAME_ATTRIBUTE_KEY, userNameAttr);
+ properties.setProperty(LDAP_GROUP_OBJECT_CLASS_KEY, groupClass);
+ properties.setProperty(LDAP_GROUP_NAMING_ATTR_KEY, groupName);
+ properties.setProperty(LDAP_GROUP_MEMEBERSHIP_ATTR_KEY, groupMember);
+ properties.setProperty(LDAP_BASE_DN_KEY, baseDN);
+ properties.setProperty(LDAP_BIND_ANONYMOUSLY_KEY, String.valueOf(anon));
+ properties.setProperty(LDAP_MANAGER_DN_KEY, managerDN);
+ properties.setProperty(LDAP_MANAGER_PASSWORD_KEY, managerPass);
+ }
+
public String getWebAppDir() {
LOG.info("Web App DIR test " + properties.getProperty(WEBAPP_DIR));
return properties.getProperty(WEBAPP_DIR, "web");
http://git-wip-us.apache.org/repos/asf/ambari/blob/17b48bf4/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementController.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementController.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementController.java
index 1a5d933..13efd32 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementController.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementController.java
@@ -24,6 +24,7 @@ import org.apache.ambari.server.api.services.AmbariMetaInfo;
import org.apache.ambari.server.controller.internal.RequestStageContainer;
import org.apache.ambari.server.metadata.RoleCommandOrder;
import org.apache.ambari.server.scheduler.ExecutionScheduleManager;
+import org.apache.ambari.server.security.ldap.LdapSyncDto;
import org.apache.ambari.server.state.Cluster;
import org.apache.ambari.server.state.Clusters;
import org.apache.ambari.server.state.ConfigHelper;
@@ -36,6 +37,7 @@ import org.apache.ambari.server.state.ServiceFactory;
import org.apache.ambari.server.state.State;
import org.apache.ambari.server.state.configgroup.ConfigGroupFactory;
import org.apache.ambari.server.state.scheduler.RequestExecutionFactory;
+
import java.util.Collection;
import java.util.List;
import java.util.Map;
@@ -689,20 +691,12 @@ public interface AmbariManagementController {
public boolean checkLdapConfigured();
/**
- * Retrieves users from external LDAP.
- *
- * @return key-value pairs UserName-Synced
- * @throws AmbariException if LDAP is configured incorrectly
- */
- public Map<String, Boolean> getLdapUsersSyncInfo() throws AmbariException;
-
- /**
- * Retrieves groups from external LDAP.
+ * Retrieves groups and users from external LDAP.
*
- * @return key-value pairs GroupName-Synced
+ * @return ldap sync DTO
* @throws AmbariException if LDAP is configured incorrectly
*/
- public Map<String, Boolean> getLdapGroupsSyncInfo() throws AmbariException;
+ public LdapSyncDto getLdapSyncInfo() throws AmbariException;
/**
* Synchronizes local users and groups with given data.
http://git-wip-us.apache.org/repos/asf/ambari/blob/17b48bf4/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementControllerImpl.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementControllerImpl.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementControllerImpl.java
index 99552e5..97137a2 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementControllerImpl.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementControllerImpl.java
@@ -91,11 +91,13 @@ import org.apache.ambari.server.customactions.ActionDefinition;
import org.apache.ambari.server.metadata.ActionMetadata;
import org.apache.ambari.server.metadata.RoleCommandOrder;
import org.apache.ambari.server.scheduler.ExecutionScheduleManager;
-import org.apache.ambari.server.security.authorization.AmbariLdapDataPopulator;
import org.apache.ambari.server.security.authorization.AuthorizationHelper;
import org.apache.ambari.server.security.authorization.Group;
import org.apache.ambari.server.security.authorization.User;
import org.apache.ambari.server.security.authorization.Users;
+import org.apache.ambari.server.security.ldap.AmbariLdapDataPopulator;
+import org.apache.ambari.server.security.ldap.LdapBatchDto;
+import org.apache.ambari.server.security.ldap.LdapSyncDto;
import org.apache.ambari.server.stageplanner.RoleGraph;
import org.apache.ambari.server.state.Cluster;
import org.apache.ambari.server.state.Clusters;
@@ -143,7 +145,6 @@ import org.slf4j.LoggerFactory;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
-import com.google.common.collect.Multimaps;
import com.google.gson.Gson;
import com.google.inject.Inject;
import com.google.inject.Injector;
@@ -724,11 +725,7 @@ public class AmbariManagementControllerImpl implements AmbariManagementControlle
throw new AmbariException("User already exists.");
}
- users.createUser(request.getUsername(), request.getPassword(), request.isActive(), request.isAdmin());
-
- if (null != request.isActive() && null != user) {
- users.setUserActive(user, request.isActive());
- }
+ users.createUser(request.getUsername(), request.getPassword(), request.isActive(), request.isAdmin(), false);
}
}
@@ -1805,7 +1802,7 @@ public class AmbariManagementControllerImpl implements AmbariManagementControlle
RoleCommand roleCommand;
State oldSchState = scHost.getState();
ServiceComponentHostEvent event;
-
+
switch (newState) {
case INSTALLED:
if (oldSchState == State.INIT
@@ -1960,7 +1957,7 @@ public class AmbariManagementControllerImpl implements AmbariManagementControlle
break;
}
}
-
+
if (null == requestParameters) {
requestParameters = new HashMap<String, String>();
}
@@ -1972,7 +1969,7 @@ public class AmbariManagementControllerImpl implements AmbariManagementControlle
}
}
}
-
+
for (String serviceName : smokeTestServices) { // Creates smoke test commands
Service s = cluster.getService(serviceName);
// find service component host
@@ -3313,7 +3310,7 @@ public class AmbariManagementControllerImpl implements AmbariManagementControlle
}
return response;
}
-
+
@Override
public Set<StackConfigurationResponse> getStackLevelConfigurations(
Set<StackLevelConfigurationRequest> requests) throws AmbariException {
@@ -3322,7 +3319,7 @@ public class AmbariManagementControllerImpl implements AmbariManagementControlle
String stackName = request.getStackName();
String stackVersion = request.getStackVersion();
-
+
Set<StackConfigurationResponse> stackConfigurations = getStackLevelConfigurations(request);
for (StackConfigurationResponse stackConfigurationResponse : stackConfigurations) {
@@ -3673,19 +3670,14 @@ public class AmbariManagementControllerImpl implements AmbariManagementControlle
}
@Override
- public Map<String, Boolean> getLdapUsersSyncInfo() throws AmbariException {
- return ldapDataPopulator.getLdapUsersSyncInfo();
- }
-
- @Override
- public Map<String, Boolean> getLdapGroupsSyncInfo() throws AmbariException {
- return ldapDataPopulator.getLdapGroupsSyncInfo();
+ public LdapSyncDto getLdapSyncInfo() throws AmbariException {
+ return ldapDataPopulator.getLdapSyncInfo();
}
@Override
public synchronized void synchronizeLdapUsersAndGroups(Set<String> users,
Set<String> groups) throws AmbariException {
- ldapDataPopulator.synchronizeLdapUsersAndGroups(users, groups);
+ final LdapBatchDto batchInfo = ldapDataPopulator.synchronizeLdapUsersAndGroups(users, groups);
+ this.users.processLdapSync(batchInfo);
}
-
}
http://git-wip-us.apache.org/repos/asf/ambari/blob/17b48bf4/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariServer.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariServer.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariServer.java
index d83138a..4e0d092 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariServer.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariServer.java
@@ -76,11 +76,11 @@ import org.apache.ambari.server.scheduler.ExecutionScheduleManager;
import org.apache.ambari.server.security.CertificateManager;
import org.apache.ambari.server.security.SecurityFilter;
import org.apache.ambari.server.security.authorization.AmbariLdapAuthenticationProvider;
-import org.apache.ambari.server.security.authorization.AmbariLdapDataPopulator;
import org.apache.ambari.server.security.authorization.AmbariLocalUserDetailsService;
import org.apache.ambari.server.security.authorization.Users;
import org.apache.ambari.server.security.authorization.AmbariAuthorizationFilter;
import org.apache.ambari.server.security.authorization.internal.AmbariInternalAuthenticationProvider;
+import org.apache.ambari.server.security.ldap.AmbariLdapDataPopulator;
import org.apache.ambari.server.security.unsecured.rest.CertificateDownload;
import org.apache.ambari.server.security.unsecured.rest.CertificateSign;
import org.apache.ambari.server.security.unsecured.rest.ConnectionInfo;
@@ -461,8 +461,8 @@ public class AmbariServer {
LOG.info("Database init needed - creating default data");
Users users = injector.getInstance(Users.class);
- users.createUser("admin", "admin", true, true);
- users.createUser("user", "user", true, false);
+ users.createUser("admin", "admin");
+ users.createUser("user", "user");
MetainfoEntity schemaVersion = new MetainfoEntity();
schemaVersion.setMetainfoName(Configuration.SERVER_VERSION_KEY);
http://git-wip-us.apache.org/repos/asf/ambari/blob/17b48bf4/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ControllerResourceProvider.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ControllerResourceProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ControllerResourceProvider.java
index 9434026..a3bd6d5 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ControllerResourceProvider.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ControllerResourceProvider.java
@@ -23,7 +23,6 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
-import java.util.Map.Entry;
import java.util.Set;
import org.apache.ambari.server.AmbariException;
@@ -40,6 +39,9 @@ import org.apache.ambari.server.controller.spi.ResourceAlreadyExistsException;
import org.apache.ambari.server.controller.spi.SystemException;
import org.apache.ambari.server.controller.spi.UnsupportedPropertyException;
import org.apache.ambari.server.controller.utilities.PropertyHelper;
+import org.apache.ambari.server.security.ldap.LdapGroupDto;
+import org.apache.ambari.server.security.ldap.LdapSyncDto;
+import org.apache.ambari.server.security.ldap.LdapUserDto;
import org.apache.commons.lang.StringUtils;
/**
@@ -137,12 +139,14 @@ class ControllerResourceProvider extends AbstractControllerResourceProvider {
ldapConfigured, requestedIds);
if (ldapConfigured) {
try {
+ final LdapSyncDto syncInfo = getManagementController().getLdapSyncInfo();
+
final List<String> allUsers = new ArrayList<String>();
final List<String> syncedUsers = new ArrayList<String>();
- for (Entry<String, Boolean> user : getManagementController().getLdapUsersSyncInfo().entrySet()) {
- allUsers.add(user.getKey());
- if (user.getValue()) {
- syncedUsers.add(user.getKey());
+ for (LdapUserDto user : syncInfo.getUsers()) {
+ allUsers.add(user.getUserName());
+ if (user.isSynced()) {
+ syncedUsers.add(user.getUserName());
}
}
setResourceProperty(resource, CONTROLLER_LDAP_USERS_PROPERTY_ID,
@@ -151,10 +155,10 @@ class ControllerResourceProvider extends AbstractControllerResourceProvider {
syncedUsers, requestedIds);
final List<String> allGroups = new ArrayList<String>();
final List<String> syncedGroups = new ArrayList<String>();
- for (Entry<String, Boolean> group : getManagementController().getLdapGroupsSyncInfo().entrySet()) {
- allGroups.add(group.getKey());
- if (group.getValue()) {
- syncedGroups.add(group.getKey());
+ for (LdapGroupDto group : syncInfo.getGroups()) {
+ allGroups.add(group.getGroupName());
+ if (group.isSynced()) {
+ syncedGroups.add(group.getGroupName());
}
}
setResourceProperty(resource, CONTROLLER_LDAP_GROUPS_PROPERTY_ID,
http://git-wip-us.apache.org/repos/asf/ambari/blob/17b48bf4/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/GroupDAO.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/GroupDAO.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/GroupDAO.java
index a2caefa..7b300dc 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/GroupDAO.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/GroupDAO.java
@@ -17,8 +17,11 @@
*/
package org.apache.ambari.server.orm.dao;
+import java.util.Arrays;
import java.util.Collections;
+import java.util.HashSet;
import java.util.List;
+import java.util.Set;
import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
@@ -30,6 +33,7 @@ import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
import com.google.inject.persist.Transactional;
+
import org.apache.ambari.server.orm.entities.PrincipalEntity;
@Singleton
@@ -79,8 +83,15 @@ public class GroupDAO {
@Transactional
public void create(GroupEntity group) {
- group.setGroupName(group.getGroupName().toLowerCase());
- entityManagerProvider.get().persist(group);
+ create(new HashSet<GroupEntity>(Arrays.asList(group)));
+ }
+
+ @Transactional
+ public void create(Set<GroupEntity> groups) {
+ for (GroupEntity group: groups) {
+ group.setGroupName(group.getGroupName().toLowerCase());
+ entityManagerProvider.get().persist(group);
+ }
}
@Transactional
@@ -90,12 +101,27 @@ public class GroupDAO {
}
@Transactional
+ public void merge(Set<GroupEntity> groups) {
+ for (GroupEntity group: groups) {
+ group.setGroupName(group.getGroupName().toLowerCase());
+ entityManagerProvider.get().merge(group);
+ }
+ }
+
+ @Transactional
public void remove(GroupEntity group) {
entityManagerProvider.get().remove(merge(group));
entityManagerProvider.get().getEntityManagerFactory().getCache().evictAll();
}
@Transactional
+ public void remove(Set<GroupEntity> groups) {
+ for (GroupEntity groupEntity: groups) {
+ entityManagerProvider.get().remove(entityManagerProvider.get().merge(groupEntity));
+ }
+ }
+
+ @Transactional
public void removeByPK(Integer groupPK) {
remove(findByPK(groupPK));
}
http://git-wip-us.apache.org/repos/asf/ambari/blob/17b48bf4/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/MemberDAO.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/MemberDAO.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/MemberDAO.java
index 5788b81..e831db2 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/MemberDAO.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/MemberDAO.java
@@ -17,19 +17,23 @@
*/
package org.apache.ambari.server.orm.dao;
+import java.util.Arrays;
+import java.util.HashSet;
import java.util.List;
+import java.util.Set;
import javax.persistence.EntityManager;
+import javax.persistence.NoResultException;
import javax.persistence.TypedQuery;
import org.apache.ambari.server.orm.RequiresSession;
import org.apache.ambari.server.orm.entities.MemberEntity;
+import org.apache.ambari.server.orm.entities.UserEntity;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
import com.google.inject.persist.Transactional;
-import org.apache.ambari.server.orm.entities.UserEntity;
@Singleton
public class MemberDAO {
@@ -44,6 +48,18 @@ public class MemberDAO {
}
@RequiresSession
+ public MemberEntity findByUserAndGroup(String userName, String groupName) {
+ final TypedQuery<MemberEntity> query = entityManagerProvider.get().createNamedQuery("memberByUserAndGroup", MemberEntity.class);
+ query.setParameter("username", userName.toLowerCase());
+ query.setParameter("groupname", groupName.toLowerCase());
+ try {
+ return query.getSingleResult();
+ } catch (NoResultException e) {
+ return null;
+ }
+ }
+
+ @RequiresSession
public List<MemberEntity> findAll() {
final TypedQuery<MemberEntity> query = entityManagerProvider.get().createQuery("SELECT m FROM MemberEntity m", MemberEntity.class);
return daoUtils.selectList(query);
@@ -57,7 +73,14 @@ public class MemberDAO {
@Transactional
public void create(MemberEntity member) {
- entityManagerProvider.get().persist(member);
+ create(new HashSet<MemberEntity>(Arrays.asList(member)));
+ }
+
+ @Transactional
+ public void create(Set<MemberEntity> members) {
+ for (MemberEntity member: members) {
+ entityManagerProvider.get().persist(member);
+ }
}
@Transactional
@@ -71,6 +94,13 @@ public class MemberDAO {
}
@Transactional
+ public void remove(Set<MemberEntity> members) {
+ for (MemberEntity member: members) {
+ entityManagerProvider.get().remove(entityManagerProvider.get().merge(member));
+ }
+ }
+
+ @Transactional
public void removeByPK(Integer memberPK) {
remove(findByPK(memberPK));
}
http://git-wip-us.apache.org/repos/asf/ambari/blob/17b48bf4/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/PrincipalDAO.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/PrincipalDAO.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/PrincipalDAO.java
index 334e978..7ac4f05 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/PrincipalDAO.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/PrincipalDAO.java
@@ -18,17 +18,18 @@
package org.apache.ambari.server.orm.dao;
+import java.util.Arrays;
+import java.util.List;
+import javax.persistence.EntityManager;
+import javax.persistence.TypedQuery;
+
+import org.apache.ambari.server.orm.entities.PrincipalEntity;
+
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
import com.google.inject.persist.Transactional;
-import org.apache.ambari.server.orm.entities.PrincipalEntity;
-import javax.persistence.EntityManager;
-import javax.persistence.TypedQuery;
-
-import java.util.List;
-
/**
* Principal Data Access Object.
*/
@@ -82,7 +83,19 @@ public class PrincipalDAO {
*/
@Transactional
public void create(PrincipalEntity entity) {
- entityManagerProvider.get().persist(entity);
+ create(Arrays.asList(entity));
+ }
+
+ /**
+ * Make instances managed and persistent.
+ *
+ * @param entities entities to store
+ */
+ @Transactional
+ public void create(List<PrincipalEntity> entities) {
+ for (PrincipalEntity entity: entities) {
+ entityManagerProvider.get().persist(entity);
+ }
}
/**
http://git-wip-us.apache.org/repos/asf/ambari/blob/17b48bf4/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/PrincipalTypeDAO.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/PrincipalTypeDAO.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/PrincipalTypeDAO.java
index 041ad5c..046345a 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/PrincipalTypeDAO.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/PrincipalTypeDAO.java
@@ -22,10 +22,11 @@ import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
import com.google.inject.persist.Transactional;
-import org.apache.ambari.server.orm.entities.PrincipalTypeEntity;
+import org.apache.ambari.server.orm.entities.PrincipalTypeEntity;
import javax.persistence.EntityManager;
import javax.persistence.TypedQuery;
+
import java.util.List;
/**
@@ -80,5 +81,31 @@ public class PrincipalTypeDAO {
public PrincipalTypeEntity merge(PrincipalTypeEntity entity) {
return entityManagerProvider.get().merge(entity);
}
+
+ /**
+ * Creates and returns principal type if it wasn't persisted yet.
+ *
+ * @param principalType id of principal type
+ * @return principal type
+ */
+ public PrincipalTypeEntity ensurePrincipalTypeCreated(int principalType) {
+ PrincipalTypeEntity principalTypeEntity = findById(principalType);
+ if (principalTypeEntity == null) {
+ principalTypeEntity = new PrincipalTypeEntity();
+ principalTypeEntity.setId(principalType);
+ switch (principalType) {
+ case PrincipalTypeEntity.USER_PRINCIPAL_TYPE:
+ principalTypeEntity.setName(PrincipalTypeEntity.USER_PRINCIPAL_TYPE_NAME);
+ break;
+ case PrincipalTypeEntity.GROUP_PRINCIPAL_TYPE:
+ principalTypeEntity.setName(PrincipalTypeEntity.GROUP_PRINCIPAL_TYPE_NAME);
+ break;
+ default:
+ throw new IllegalArgumentException("Unknown principal type ID=" + principalType);
+ }
+ create(principalTypeEntity);
+ }
+ return principalTypeEntity;
+ }
}
http://git-wip-us.apache.org/repos/asf/ambari/blob/17b48bf4/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/UserDAO.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/UserDAO.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/UserDAO.java
index dcbd64c..06040be 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/UserDAO.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/UserDAO.java
@@ -17,19 +17,24 @@
*/
package org.apache.ambari.server.orm.dao;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import com.google.inject.Singleton;
-import com.google.inject.persist.Transactional;
-import org.apache.ambari.server.orm.RequiresSession;
-import org.apache.ambari.server.orm.entities.PrincipalEntity;
-import org.apache.ambari.server.orm.entities.UserEntity;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.persistence.TypedQuery;
-import java.util.Collections;
-import java.util.List;
+
+import org.apache.ambari.server.orm.RequiresSession;
+import org.apache.ambari.server.orm.entities.PrincipalEntity;
+import org.apache.ambari.server.orm.entities.UserEntity;
+
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import com.google.inject.Singleton;
+import com.google.inject.persist.Transactional;
@Singleton
public class UserDAO {
@@ -90,8 +95,15 @@ public class UserDAO {
@Transactional
public void create(UserEntity user) {
- user.setUserName(user.getUserName().toLowerCase());
- entityManagerProvider.get().persist(user);
+ create(new HashSet<UserEntity>(Arrays.asList(user)));
+ }
+
+ @Transactional
+ public void create(Set<UserEntity> users) {
+ for (UserEntity user: users) {
+ user.setUserName(user.getUserName().toLowerCase());
+ entityManagerProvider.get().persist(user);
+ }
}
@Transactional
@@ -101,12 +113,27 @@ public class UserDAO {
}
@Transactional
+ public void merge(Set<UserEntity> users) {
+ for (UserEntity user: users) {
+ user.setUserName(user.getUserName().toLowerCase());
+ entityManagerProvider.get().merge(user);
+ }
+ }
+
+ @Transactional
public void remove(UserEntity user) {
entityManagerProvider.get().remove(merge(user));
entityManagerProvider.get().getEntityManagerFactory().getCache().evictAll();
}
@Transactional
+ public void remove(Set<UserEntity> users) {
+ for (UserEntity userEntity: users) {
+ entityManagerProvider.get().remove(entityManagerProvider.get().merge(userEntity));
+ }
+ }
+
+ @Transactional
public void removeByPK(Integer userPK) {
remove(findByPK(userPK));
}
http://git-wip-us.apache.org/repos/asf/ambari/blob/17b48bf4/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/MemberEntity.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/MemberEntity.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/MemberEntity.java
index 5d9e6b1..4e80a08 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/MemberEntity.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/MemberEntity.java
@@ -24,19 +24,24 @@ import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
+import javax.persistence.NamedQueries;
+import javax.persistence.NamedQuery;
import javax.persistence.Table;
import javax.persistence.TableGenerator;
import javax.persistence.UniqueConstraint;
@Entity
@Table(name = "members", uniqueConstraints = {@UniqueConstraint(columnNames = {"group_id", "user_id"})})
+@NamedQueries({
+ @NamedQuery(name = "memberByUserAndGroup", query = "SELECT memberEnt FROM MemberEntity memberEnt where lower(memberEnt.user.userName)=:username AND lower(memberEnt.group.groupName)=:groupname")
+})
@TableGenerator(name = "member_id_generator",
table = "ambari_sequences",
pkColumnName = "sequence_name",
valueColumnName = "sequence_value",
pkColumnValue = "member_id_seq",
initialValue = 1,
- allocationSize = 1
+ allocationSize = 500
)
public class MemberEntity {
@Id
http://git-wip-us.apache.org/repos/asf/ambari/blob/17b48bf4/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/PrincipalEntity.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/PrincipalEntity.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/PrincipalEntity.java
index d05ff5c..4b826df 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/PrincipalEntity.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/PrincipalEntity.java
@@ -44,7 +44,7 @@ import javax.persistence.TableGenerator;
table = "ambari_sequences", pkColumnName = "sequence_name", valueColumnName = "sequence_value"
, pkColumnValue = "principal_id_seq"
, initialValue = 2
- , allocationSize = 1
+ , allocationSize = 500
)
@NamedQueries({
@NamedQuery(name = "principalByPrivilegeId", query = "SELECT principal FROM PrincipalEntity principal JOIN principal.privileges privilege WHERE privilege.permission.id=:permission_id")
http://git-wip-us.apache.org/repos/asf/ambari/blob/17b48bf4/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/UserEntity.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/UserEntity.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/UserEntity.java
index 4e1f1f3..e551c38 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/UserEntity.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/UserEntity.java
@@ -20,6 +20,7 @@ package org.apache.ambari.server.orm.entities;
import javax.persistence.*;
import java.util.Date;
+import java.util.HashSet;
import java.util.Set;
@Table(name = "users", uniqueConstraints = {@UniqueConstraint(columnNames = {"user_name", "ldap_user"})})
@@ -32,7 +33,7 @@ import java.util.Set;
table = "ambari_sequences", pkColumnName = "sequence_name", valueColumnName = "sequence_value"
, pkColumnValue = "user_id_seq"
, initialValue = 2
- , allocationSize = 1
+ , allocationSize = 500
)
public class UserEntity {
@@ -60,7 +61,7 @@ public class UserEntity {
private Integer active = 1;
@OneToMany(mappedBy = "user", cascade = CascadeType.ALL)
- private Set<MemberEntity> memberEntities;
+ private Set<MemberEntity> memberEntities = new HashSet<MemberEntity>();
@OneToOne
@JoinColumns({
http://git-wip-us.apache.org/repos/asf/ambari/blob/17b48bf4/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/AmbariLdapDataPopulator.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/AmbariLdapDataPopulator.java b/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/AmbariLdapDataPopulator.java
deleted file mode 100644
index a160716..0000000
--- a/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/AmbariLdapDataPopulator.java
+++ /dev/null
@@ -1,409 +0,0 @@
-/**
- * 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.ambari.server.security.authorization;
-
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Set;
-
-import javax.naming.NamingException;
-import javax.naming.directory.Attributes;
-
-import org.apache.ambari.server.AmbariException;
-import org.apache.ambari.server.configuration.Configuration;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.springframework.ldap.core.AttributesMapper;
-import org.springframework.ldap.core.ContextMapper;
-import org.springframework.ldap.core.DirContextAdapter;
-import org.springframework.ldap.core.LdapTemplate;
-import org.springframework.ldap.core.support.LdapContextSource;
-import org.springframework.ldap.filter.AndFilter;
-import org.springframework.ldap.filter.EqualsFilter;
-import org.springframework.security.core.userdetails.UsernameNotFoundException;
-
-import com.google.inject.Inject;
-
-/**
- * Provides users, groups and membership population from LDAP catalog.
- */
-public class AmbariLdapDataPopulator {
- /**
- * Log.
- */
- private static final Log LOG = LogFactory.getLog(AmbariLdapDataPopulator.class);
-
- /**
- * Ambari configuration.
- */
- private Configuration configuration;
-
- /**
- * Highlevel facade for management of users and groups.
- */
- private Users users;
-
- /**
- * LDAP specific properties.
- */
- protected LdapServerProperties ldapServerProperties;
-
- /**
- * LDAP template for making search queries.
- */
- private LdapTemplate ldapTemplate;
-
- @Inject
- public AmbariLdapDataPopulator(Configuration configuration, Users users) {
- this.configuration = configuration;
- this.users = users;
- }
-
- /**
- * Check if LDAP is enabled in server properties.
- *
- * @return true if enabled
- */
- public boolean isLdapEnabled() {
- if (!configuration.isLdapConfigured()) {
- return false;
- }
- try {
- final LdapTemplate ldapTemplate = loadLdapTemplate();
- ldapTemplate.list(ldapServerProperties.getBaseDN());
- return true;
- } catch (Exception ex) {
- LOG.error("Could not connect to LDAP server - " + ex.getMessage());
- return false;
- }
- }
-
- /**
- * Retrieves a key-value map of all LDAP groups.
- *
- * @return map of GroupName-Synced pairs
- */
- public Map<String, Boolean> getLdapGroupsSyncInfo() {
- final Map<String, Boolean> ldapGroups = new HashMap<String, Boolean>();
- final Map<String, Group> internalGroupsMap = getInternalGroups();
- final Set<String> externalGroups = getExternalLdapGroupNames();
- for (String externalGroup : externalGroups) {
- if (internalGroupsMap.containsKey(externalGroup)
- && internalGroupsMap.get(externalGroup).isLdapGroup()) {
- ldapGroups.put(externalGroup, true);
- } else {
- ldapGroups.put(externalGroup, false);
- }
- }
-
- return ldapGroups;
- }
-
- /**
- * Retrieves a key-value map of all LDAP users.
- *
- * @return map of UserName-Synced pairs.
- */
- public Map<String, Boolean> getLdapUsersSyncInfo() {
- final Map<String, Boolean> ldapUsers = new HashMap<String, Boolean>();
- final List<User> internalUsers = users.getAllUsers();
- final Map<String, User> internalUsersMap = new HashMap<String, User>();
- for (User user : internalUsers) {
- internalUsersMap.put(user.getUserName(), user);
- }
- final Set<String> externalUsers = getExternalLdapUserNames();
- for (String externalUser : externalUsers) {
- if (internalUsersMap.containsKey(externalUser)
- && internalUsersMap.get(externalUser).isLdapUser()) {
- ldapUsers.put(externalUser, true);
- } else {
- ldapUsers.put(externalUser, false);
- }
- }
-
- return ldapUsers;
- }
-
- /**
- * Performs synchronization of given sets of usernames and groupnames.
- *
- * @param users set of users to synchronize
- * @param groups set of groups to synchronize
- * @throws AmbariException if synchronization failed for any reason
- */
- public void synchronizeLdapUsersAndGroups(Set<String> users,
- Set<String> groups) throws AmbariException {
- // validate request
- final Set<String> externalUsers = getExternalLdapUserNames();
- for (String user : users) {
- if (!externalUsers.contains(user)) {
- throw new AmbariException("Couldn't sync LDAP user " + user
- + ", it doesn't exist");
- }
- }
- final Set<String> externalGroups = getExternalLdapGroupNames();
- for (String group : groups) {
- if (!externalGroups.contains(group)) {
- throw new AmbariException("Couldn't sync LDAP group " + group
- + ", it doesn't exist");
- }
- }
-
- // processing groups
- final Map<String, Group> internalGroupsMap = getInternalGroups();
- for (String groupName : groups) {
- if (internalGroupsMap.containsKey(groupName)) {
- final Group group = internalGroupsMap.get(groupName);
- if (!group.isLdapGroup()) {
- this.users.setGroupLdap(groupName);
- }
- } else {
- this.users.createGroup(groupName);
- this.users.setGroupLdap(groupName);
- }
- refreshGroupMembers(groupName);
- internalGroupsMap.remove(groupName);
- }
- for (Entry<String, Group> internalGroup : internalGroupsMap.entrySet()) {
- if (internalGroup.getValue().isLdapGroup()) {
- this.users.removeGroup(internalGroup.getValue());
- }
- }
-
- cleanUpLdapUsersWithoutGroup();
-
- // processing users
- final Map<String, User> internalUsersMap = getInternalUsers();
- for (String userName : users) {
- if (internalUsersMap.containsKey(userName)) {
- final User user = internalUsersMap.get(userName);
- if (!user.isLdapUser()) {
- this.users.setUserLdap(userName);
- }
- } else {
- this.users.createUser(userName, "", true, false);
- this.users.setUserLdap(userName);
- }
- }
-
- }
-
- /**
- * Check group members of the synced group: add missing ones and remove the ones absent in external LDAP.
- *
- * @param groupName group name
- * @throws AmbariException if group refresh failed
- */
- protected void refreshGroupMembers(String groupName) throws AmbariException {
- final Set<String> externalMembers = getExternalLdapGroupMembers(groupName);
- final Map<String, User> internalUsers = getInternalUsers();
- final Map<String, User> internalMembers = getInternalMembers(groupName);
- for (String externalMember: externalMembers) {
- if (internalUsers.containsKey(externalMember)) {
- final User user = internalUsers.get(externalMember);
- if (!user.isLdapUser()) {
- users.setUserLdap(externalMember);
- }
- if (!internalMembers.containsKey(externalMember)) {
- users.addMemberToGroup(groupName, externalMember);
- }
- internalMembers.remove(externalMember);
- internalUsers.remove(externalMember);
- } else {
- users.createUser(externalMember, "");
- users.setUserLdap(externalMember);
- users.addMemberToGroup(groupName, externalMember);
- }
- }
- for (Entry<String, User> userToBeUnsynced: internalMembers.entrySet()) {
- final User user = userToBeUnsynced.getValue();
- users.removeMemberFromGroup(groupName, user.getUserName());
- }
- }
-
- /**
- * Removes synced users which are not present in any of group.
- *
- * @throws AmbariException
- */
- protected void cleanUpLdapUsersWithoutGroup() throws AmbariException {
- final List<User> allUsers = users.getAllUsers();
- for (User user: allUsers) {
- if (user.isLdapUser() && user.getGroups().isEmpty()) {
- users.removeUser(user);
- }
- }
- }
-
- // Utility methods
-
- /**
- * Retrieves groups from external LDAP server.
- *
- * @return set of user names
- */
- protected Set<String> getExternalLdapGroupNames() {
- final Set<String> groups = new HashSet<String>();
- final LdapTemplate ldapTemplate = loadLdapTemplate();
- final EqualsFilter equalsFilter = new EqualsFilter("objectClass",
- ldapServerProperties.getGroupObjectClass());
- String baseDn = ldapServerProperties.getBaseDN();
- ldapTemplate.search(baseDn, equalsFilter.encode(), new AttributesMapper() {
-
- public Object mapFromAttributes(Attributes attributes)
- throws NamingException {
- groups.add(attributes.get(ldapServerProperties.getGroupNamingAttr())
- .get().toString().toLowerCase());
- return null;
- }
- });
- return groups;
- }
-
- /**
- * Retrieves users from external LDAP server.
- *
- * @return set of user names
- */
- protected Set<String> getExternalLdapUserNames() {
- final Set<String> users = new HashSet<String>();
- final LdapTemplate ldapTemplate = loadLdapTemplate();
- final EqualsFilter equalsFilter = new EqualsFilter("objectClass",
- ldapServerProperties.getUserObjectClass());
- String baseDn = ldapServerProperties.getBaseDN();
- ldapTemplate.search(baseDn, equalsFilter.encode(), new AttributesMapper() {
-
- public Object mapFromAttributes(Attributes attributes)
- throws NamingException {
- users.add(attributes.get(ldapServerProperties.getUsernameAttribute())
- .get().toString().toLowerCase());
- return null;
- }
- });
- return users;
- }
-
- /**
- * Retrieves members of the specified group from external LDAP server.
- *
- * @param groupName group name
- * @return set of group names
- */
- protected Set<String> getExternalLdapGroupMembers(String groupName) {
- final Set<String> members = new HashSet<String>();
- final LdapTemplate ldapTemplate = loadLdapTemplate();
- final AndFilter andFilter = new AndFilter();
- andFilter.and(new EqualsFilter("objectClass", ldapServerProperties.getGroupObjectClass()));
- andFilter.and(new EqualsFilter(ldapServerProperties.getGroupNamingAttr(), groupName));
- String baseDn = ldapServerProperties.getBaseDN();
- ldapTemplate.search(baseDn, andFilter.encode(), new ContextMapper() {
-
- public Object mapFromContext(Object ctx) {
- final DirContextAdapter adapter = (DirContextAdapter) ctx;
- for (String uniqueMember: adapter.getStringAttributes(ldapServerProperties.getGroupMembershipAttr())) {
- final DirContextAdapter userAdapter = (DirContextAdapter) ldapTemplate.lookup(uniqueMember);
- members.add(userAdapter.getStringAttribute(ldapServerProperties.getUsernameAttribute()).toLowerCase());
- }
- return null;
- }
- });
- return members;
- }
-
- /**
- * Creates a map of internal groups.
- *
- * @return map of GroupName-Group pairs
- */
- protected Map<String, Group> getInternalGroups() {
- final List<Group> internalGroups = users.getAllGroups();
- final Map<String, Group> internalGroupsMap = new HashMap<String, Group>();
- for (Group group : internalGroups) {
- internalGroupsMap.put(group.getGroupName(), group);
- }
- return internalGroupsMap;
- }
-
- /**
- * Creates a map of internal users.
- *
- * @return map of UserName-User pairs
- */
- protected Map<String, User> getInternalUsers() {
- final List<User> internalUsers = users.getAllUsers();
- final Map<String, User> internalUsersMap = new HashMap<String, User>();
- for (User user : internalUsers) {
- internalUsersMap.put(user.getUserName(), user);
- }
- return internalUsersMap;
- }
-
- /**
- * Creates a map of internal users present in specified group.
- *
- * @param groupName group name
- * @return map of UserName-User pairs
- */
- protected Map<String, User> getInternalMembers(String groupName) {
- final Collection<User> internalMembers = users.getGroupMembers(groupName);
- final Map<String, User> internalMembersMap = new HashMap<String, User>();
- for (User user : internalMembers) {
- internalMembersMap.put(user.getUserName(), user);
- }
- return internalMembersMap;
- }
-
- /**
- * Checks LDAP configuration for changes and reloads LDAP template if they occured.
- *
- * @return LdapTemplate instance
- */
- protected LdapTemplate loadLdapTemplate() {
- final LdapServerProperties properties = configuration
- .getLdapServerProperties();
- if (ldapTemplate == null || !properties.equals(ldapServerProperties)) {
- LOG.info("Reloading properties");
- ldapServerProperties = properties;
-
- final LdapContextSource ldapContextSource = new LdapContextSource();
- final List<String> ldapUrls = ldapServerProperties.getLdapUrls();
- ldapContextSource.setUrls(ldapUrls.toArray(new String[ldapUrls.size()]));
-
- if (!ldapServerProperties.isAnonymousBind()) {
- ldapContextSource.setUserDn(ldapServerProperties.getManagerDn());
- ldapContextSource
- .setPassword(ldapServerProperties.getManagerPassword());
- }
-
- try {
- ldapContextSource.afterPropertiesSet();
- } catch (Exception e) {
- LOG.error("LDAP Context Source not loaded ", e);
- throw new UsernameNotFoundException("LDAP Context Source not loaded", e);
- }
-
- ldapTemplate = new LdapTemplate(ldapContextSource);
- }
- return ldapTemplate;
- }
-
-}
http://git-wip-us.apache.org/repos/asf/ambari/blob/17b48bf4/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/User.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/User.java b/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/User.java
index db8ad19..e72d958 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/User.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/User.java
@@ -38,7 +38,7 @@ public class User {
final Collection<String> groups = new ArrayList<String>();
boolean admin = false;
- User(UserEntity userEntity) {
+ public User(UserEntity userEntity) {
userId = userEntity.getUserId();
userName = userEntity.getUserName();
createTime = userEntity.getCreateTime();
http://git-wip-us.apache.org/repos/asf/ambari/blob/17b48bf4/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/Users.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/Users.java b/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/Users.java
index 4fd5f47..1cd5b95 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/Users.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/Users.java
@@ -19,10 +19,14 @@ package org.apache.ambari.server.security.authorization;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
+import java.util.Map;
import java.util.Set;
+import javax.persistence.EntityManager;
+
import org.apache.ambari.server.AmbariException;
import org.apache.ambari.server.configuration.Configuration;
import org.apache.ambari.server.orm.dao.GroupDAO;
@@ -40,6 +44,8 @@ import org.apache.ambari.server.orm.entities.PrincipalEntity;
import org.apache.ambari.server.orm.entities.PrincipalTypeEntity;
import org.apache.ambari.server.orm.entities.PrivilegeEntity;
import org.apache.ambari.server.orm.entities.UserEntity;
+import org.apache.ambari.server.security.ldap.LdapBatchDto;
+import org.apache.ambari.server.security.ldap.LdapUserGroupMemberDto;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.authentication.BadCredentialsException;
@@ -49,6 +55,7 @@ import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.password.PasswordEncoder;
import com.google.inject.Inject;
+import com.google.inject.Provider;
import com.google.inject.Singleton;
import com.google.inject.persist.Transactional;
@@ -61,6 +68,8 @@ public class Users {
private final static Logger LOG = LoggerFactory.getLogger(Users.class);
@Inject
+ Provider<EntityManager> entityManagerProvider;
+ @Inject
protected UserDAO userDAO;
@Inject
protected GroupDAO groupDAO;
@@ -227,7 +236,7 @@ public class Users {
* Creates new local user with provided userName and password.
*/
public void createUser(String userName, String password) {
- createUser(userName, password, true, false);
+ createUser(userName, password, true, false, false);
}
/**
@@ -237,9 +246,10 @@ public class Users {
* @param password password
* @param active is user active
* @param admin is user admin
+ * @param ldapUser is user LDAP
*/
@Transactional
- public synchronized void createUser(String userName, String password, Boolean active, Boolean admin) {
+ public synchronized void createUser(String userName, String password, Boolean active, Boolean admin, Boolean ldapUser) {
// create an admin principal to represent this user
PrincipalTypeEntity principalTypeEntity = principalTypeDAO.findById(PrincipalTypeEntity.USER_PRINCIPAL_TYPE);
@@ -260,6 +270,9 @@ public class Users {
if (active != null) {
userEntity.setActive(active);
}
+ if (ldapUser != null) {
+ userEntity.setLdapUser(ldapUser);
+ }
userDAO.create(userEntity);
@@ -510,4 +523,145 @@ public class Users {
return false;
}
+ /**
+ * Executes batch queries to database to insert large amounts of LDAP data.
+ *
+ * @param batchInfo DTO with batch information
+ */
+ public void processLdapSync(LdapBatchDto batchInfo) {
+ final Map<String, UserEntity> allUsers = new HashMap<String, UserEntity>();
+ final Map<String, GroupEntity> allGroups = new HashMap<String, GroupEntity>();
+
+ // prefetch all user and group data to avoid heavy queries in membership creation
+
+ for (UserEntity userEntity: userDAO.findAll()) {
+ allUsers.put(userEntity.getUserName(), userEntity);
+ }
+
+ for (GroupEntity groupEntity: groupDAO.findAll()) {
+ allGroups.put(groupEntity.getGroupName(), groupEntity);
+ }
+
+ final PrincipalTypeEntity userPrincipalType = principalTypeDAO
+ .ensurePrincipalTypeCreated(PrincipalTypeEntity.USER_PRINCIPAL_TYPE);
+ final PrincipalTypeEntity groupPrincipalType = principalTypeDAO
+ .ensurePrincipalTypeCreated(PrincipalTypeEntity.GROUP_PRINCIPAL_TYPE);
+
+ // remove users
+ final Set<UserEntity> usersToRemove = new HashSet<UserEntity>();
+ for (String userName: batchInfo.getUsersToBeRemoved()) {
+ UserEntity userEntity = userDAO.findLocalUserByName(userName);
+ if (userEntity == null) {
+ userEntity = userDAO.findLdapUserByName(userName);
+ if (userEntity == null) {
+ continue;
+ }
+ }
+ allUsers.remove(userEntity.getUserName());
+ usersToRemove.add(userEntity);
+ }
+ userDAO.remove(usersToRemove);
+
+ // remove groups
+ final Set<GroupEntity> groupsToRemove = new HashSet<GroupEntity>();
+ for (String groupName: batchInfo.getGroupsToBeRemoved()) {
+ final GroupEntity groupEntity = groupDAO.findGroupByName(groupName);
+ allGroups.remove(groupEntity.getGroupName());
+ groupsToRemove.add(groupEntity);
+ }
+ groupDAO.remove(groupsToRemove);
+
+ // update users
+ final Set<UserEntity> usersToBecomeLdap = new HashSet<UserEntity>();
+ for (String userName: batchInfo.getUsersToBecomeLdap()) {
+ UserEntity userEntity = userDAO.findLocalUserByName(userName);
+ if (userEntity == null) {
+ userEntity = userDAO.findLdapUserByName(userName);
+ if (userEntity == null) {
+ continue;
+ }
+ }
+ userEntity.setLdapUser(true);
+ allUsers.put(userEntity.getUserName(), userEntity);
+ usersToBecomeLdap.add(userEntity);
+ }
+ userDAO.merge(usersToBecomeLdap);
+
+ // update groups
+ final Set<GroupEntity> groupsToBecomeLdap = new HashSet<GroupEntity>();
+ for (String groupName: batchInfo.getGroupsToBecomeLdap()) {
+ final GroupEntity groupEntity = groupDAO.findGroupByName(groupName);
+ groupEntity.setLdapGroup(true);
+ allGroups.put(groupEntity.getGroupName(), groupEntity);
+ groupsToBecomeLdap.add(groupEntity);
+ }
+ groupDAO.merge(groupsToBecomeLdap);
+
+ // prepare create principals
+ final List<PrincipalEntity> principalsToCreate = new ArrayList<PrincipalEntity>();
+
+ // prepare create users
+ final Set<UserEntity> usersToCreate = new HashSet<UserEntity>();
+ for (String userName: batchInfo.getUsersToBeCreated()) {
+ final PrincipalEntity principalEntity = new PrincipalEntity();
+ principalEntity.setPrincipalType(userPrincipalType);
+ principalsToCreate.add(principalEntity);
+
+ final UserEntity userEntity = new UserEntity();
+ userEntity.setUserName(userName);
+ userEntity.setUserPassword("");
+ userEntity.setPrincipal(principalEntity);
+ userEntity.setLdapUser(true);
+
+ allUsers.put(userEntity.getUserName(), userEntity);
+ usersToCreate.add(userEntity);
+ }
+
+ // prepare create groups
+ final Set<GroupEntity> groupsToCreate = new HashSet<GroupEntity>();
+ for (String groupName: batchInfo.getGroupsToBeCreated()) {
+ final PrincipalEntity principalEntity = new PrincipalEntity();
+ principalEntity.setPrincipalType(groupPrincipalType);
+ principalsToCreate.add(principalEntity);
+
+ final GroupEntity groupEntity = new GroupEntity();
+ groupEntity.setGroupName(groupName);
+ groupEntity.setPrincipal(principalEntity);
+ groupEntity.setLdapGroup(true);
+
+ allGroups.put(groupEntity.getGroupName(), groupEntity);
+ groupsToCreate.add(groupEntity);
+ }
+
+ // create users and groups
+ principalDAO.create(principalsToCreate);
+ userDAO.create(usersToCreate);
+ groupDAO.create(groupsToCreate);
+
+ // remove membership
+ final Set<MemberEntity> membersToRemove = new HashSet<MemberEntity>();
+ for (LdapUserGroupMemberDto member: batchInfo.getMembershipToRemove()) {
+ membersToRemove.add(memberDAO.findByUserAndGroup(member.getUserName(), member.getGroupName()));
+ }
+ memberDAO.remove(membersToRemove);
+
+ // create membership
+ final Set<MemberEntity> membersToCreate = new HashSet<MemberEntity>();
+ final Set<GroupEntity> groupsToUpdate = new HashSet<GroupEntity>();
+ for (LdapUserGroupMemberDto member: batchInfo.getMembershipToAdd()) {
+ final MemberEntity memberEntity = new MemberEntity();
+ final GroupEntity groupEntity = allGroups.get(member.getGroupName());
+ memberEntity.setGroup(groupEntity);
+ memberEntity.setUser(allUsers.get(member.getUserName()));
+ groupEntity.getMemberEntities().add(memberEntity);
+ groupsToUpdate.add(groupEntity);
+ membersToCreate.add(memberEntity);
+ }
+ memberDAO.create(membersToCreate);
+ groupDAO.merge(groupsToUpdate); // needed for Derby DB as it doesn't fetch newly added members automatically
+
+ // clear cached entities
+ entityManagerProvider.get().getEntityManagerFactory().getCache().evictAll();
+ }
+
}
http://git-wip-us.apache.org/repos/asf/ambari/blob/17b48bf4/ambari-server/src/main/java/org/apache/ambari/server/security/ldap/AmbariLdapDataPopulator.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/security/ldap/AmbariLdapDataPopulator.java b/ambari-server/src/main/java/org/apache/ambari/server/security/ldap/AmbariLdapDataPopulator.java
new file mode 100644
index 0000000..2342bd4
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/security/ldap/AmbariLdapDataPopulator.java
@@ -0,0 +1,430 @@
+/**
+ * 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.ambari.server.security.ldap;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import javax.naming.NamingException;
+import javax.naming.directory.Attributes;
+
+import org.apache.ambari.server.AmbariException;
+import org.apache.ambari.server.configuration.Configuration;
+import org.apache.ambari.server.security.authorization.Group;
+import org.apache.ambari.server.security.authorization.LdapServerProperties;
+import org.apache.ambari.server.security.authorization.User;
+import org.apache.ambari.server.security.authorization.Users;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.springframework.ldap.core.AttributesMapper;
+import org.springframework.ldap.core.ContextMapper;
+import org.springframework.ldap.core.DirContextAdapter;
+import org.springframework.ldap.core.LdapTemplate;
+import org.springframework.ldap.core.support.LdapContextSource;
+import org.springframework.ldap.filter.EqualsFilter;
+import org.springframework.security.core.userdetails.UsernameNotFoundException;
+
+import com.google.inject.Inject;
+
+/**
+ * Provides users, groups and membership population from LDAP catalog.
+ */
+public class AmbariLdapDataPopulator {
+ /**
+ * Log.
+ */
+ private static final Log LOG = LogFactory.getLog(AmbariLdapDataPopulator.class);
+
+ /**
+ * Ambari configuration.
+ */
+ private Configuration configuration;
+
+ /**
+ * Highlevel facade for management of users and groups.
+ */
+ private Users users;
+
+ /**
+ * LDAP specific properties.
+ */
+ protected LdapServerProperties ldapServerProperties;
+
+ /**
+ * LDAP template for making search queries.
+ */
+ private LdapTemplate ldapTemplate;
+
+ @Inject
+ public AmbariLdapDataPopulator(Configuration configuration, Users users) {
+ this.configuration = configuration;
+ this.users = users;
+ }
+
+ /**
+ * Check if LDAP is enabled in server properties.
+ *
+ * @return true if enabled
+ */
+ public boolean isLdapEnabled() {
+ if (!configuration.isLdapConfigured()) {
+ return false;
+ }
+ try {
+ final LdapTemplate ldapTemplate = loadLdapTemplate();
+ ldapTemplate.search(ldapServerProperties.getBaseDN(), "uid=dummy_search", new AttributesMapper() {
+
+ @Override
+ public Object mapFromAttributes(Attributes arg0) throws NamingException {
+ return null;
+ }
+ });
+ return true;
+ } catch (Exception ex) {
+ LOG.error("Could not connect to LDAP server - " + ex.getMessage());
+ return false;
+ }
+ }
+
+ /**
+ * Retrieves information about external groups and users and their synced/unsynced state.
+ *
+ * @return dto with information
+ */
+ public LdapSyncDto getLdapSyncInfo() {
+ final LdapSyncDto syncInfo = new LdapSyncDto();
+
+ final Map<String, Group> internalGroupsMap = getInternalGroups();
+ final Set<LdapGroupDto> externalGroups = getExternalLdapGroupInfo();
+ for (LdapGroupDto externalGroup : externalGroups) {
+ if (internalGroupsMap.containsKey(externalGroup.getGroupName())
+ && internalGroupsMap.get(externalGroup.getGroupName()).isLdapGroup()) {
+ externalGroup.setSynced(true);
+ } else {
+ externalGroup.setSynced(false);
+ }
+ }
+
+ final Map<String, User> internalUsersMap = getInternalUsers();
+ final Set<LdapUserDto> externalUsers = getExternalLdapUserInfo();
+ for (LdapUserDto externalUser : externalUsers) {
+ if (internalUsersMap.containsKey(externalUser)
+ && internalUsersMap.get(externalUser).isLdapUser()) {
+ externalUser.setSynced(true);
+ } else {
+ externalUser.setSynced(false);
+ }
+ }
+
+ syncInfo.setGroups(externalGroups);
+ syncInfo.setUsers(externalUsers);
+ return syncInfo;
+ }
+
+ /**
+ * Performs synchronization of given sets of usernames and groupnames.
+ *
+ * @param users set of users to synchronize
+ * @param groups set of groups to synchronize
+ * @throws AmbariException if synchronization failed for any reason
+ */
+ public LdapBatchDto synchronizeLdapUsersAndGroups(Set<String> users,
+ Set<String> groups) throws AmbariException {
+ final LdapBatchDto batchInfo = new LdapBatchDto();
+
+ // validate request
+ final Set<LdapUserDto> externalUsers = getExternalLdapUserInfo();
+ final Map<String, LdapUserDto> externalUsersMap = new HashMap<String, LdapUserDto>();
+ for (LdapUserDto user: externalUsers) {
+ externalUsersMap.put(user.getUserName(), user);
+ }
+ for (String user : users) {
+ if (!externalUsersMap.containsKey(user)) {
+ throw new AmbariException("Couldn't sync LDAP user " + user
+ + ", it doesn't exist");
+ }
+ }
+ final Set<LdapGroupDto> externalGroups = getExternalLdapGroupInfo();
+ final Map<String, LdapGroupDto> externalGroupsMap = new HashMap<String, LdapGroupDto>();
+ for (LdapGroupDto group: externalGroups) {
+ externalGroupsMap.put(group.getGroupName(), group);
+ }
+ for (String group : groups) {
+ if (!externalGroupsMap.containsKey(group)) {
+ throw new AmbariException("Couldn't sync LDAP group " + group
+ + ", it doesn't exist");
+ }
+ }
+
+ final Map<String, Group> internalGroupsMap = getInternalGroups();
+ final Map<String, User> internalUsersMap = getInternalUsers();
+
+ // processing groups
+ for (String groupName : groups) {
+ if (internalGroupsMap.containsKey(groupName)) {
+ final Group group = internalGroupsMap.get(groupName);
+ if (!group.isLdapGroup()) {
+ batchInfo.getGroupsToBecomeLdap().add(groupName);
+ }
+ } else {
+ batchInfo.getGroupsToBeCreated().add(groupName);
+ }
+ refreshGroupMembers(batchInfo, externalGroupsMap.get(groupName), internalUsersMap, externalUsers);
+ internalGroupsMap.remove(groupName);
+ }
+ for (Entry<String, Group> internalGroup : internalGroupsMap.entrySet()) {
+ if (internalGroup.getValue().isLdapGroup()) {
+ batchInfo.getGroupsToBeRemoved().add(internalGroup.getValue().getGroupName());
+ }
+ }
+
+ // processing users
+ for (String userName : users) {
+ if (internalUsersMap.containsKey(userName)) {
+ final User user = internalUsersMap.get(userName);
+ if (user != null && !user.isLdapUser()) {
+ batchInfo.getUsersToBecomeLdap().add(userName);
+ }
+ } else {
+ batchInfo.getUsersToBeCreated().add(userName);
+ }
+ }
+
+ return batchInfo;
+ }
+
+ /**
+ * Check group members of the synced group: add missing ones and remove the ones absent in external LDAP.
+ *
+ * @param groupName group name
+ * @param internalUsers map of internal users
+ * @param externalUsers set of external users
+ * @throws AmbariException if group refresh failed
+ */
+ protected void refreshGroupMembers(LdapBatchDto batchInfo, LdapGroupDto group, Map<String, User> internalUsers, Set<LdapUserDto> externalUsers) throws AmbariException {
+ final Set<String> externalMembers = new HashSet<String>();
+ for (String memberAttribute: group.getMemberAttributes()) {
+ for (LdapUserDto externalUser: externalUsers) {
+ // memberAttribute may be either DN or UID, check both
+ if (externalUser.getDn().equals(memberAttribute) || externalUser.getUid().equals(memberAttribute)) {
+ externalMembers.add(externalUser.getUserName());
+ break;
+ }
+ }
+ }
+ final Map<String, User> internalMembers = getInternalMembers(group.getGroupName());
+ for (String externalMember: externalMembers) {
+ if (internalUsers.containsKey(externalMember)) {
+ final User user = internalUsers.get(externalMember);
+ if (user == null) {
+ // user is fresh and is already added to batch info
+ if (!internalMembers.containsKey(externalMember)) {
+ batchInfo.getMembershipToAdd().add(new LdapUserGroupMemberDto(group.getGroupName(), externalMember));
+ }
+ continue;
+ }
+ if (!user.isLdapUser()) {
+ batchInfo.getUsersToBecomeLdap().add(externalMember);
+ }
+ if (!internalMembers.containsKey(externalMember)) {
+ batchInfo.getMembershipToAdd().add(new LdapUserGroupMemberDto(group.getGroupName(), externalMember));
+ }
+ internalMembers.remove(externalMember);
+ } else {
+ batchInfo.getUsersToBeCreated().add(externalMember);
+ batchInfo.getMembershipToAdd().add(new LdapUserGroupMemberDto(group.getGroupName(), externalMember));
+ internalUsers.put(externalMember, null);
+ }
+ }
+ for (Entry<String, User> userToBeUnsynced: internalMembers.entrySet()) {
+ final User user = userToBeUnsynced.getValue();
+ batchInfo.getMembershipToRemove().add(new LdapUserGroupMemberDto(group.getGroupName(), user.getUserName()));
+ }
+ }
+
+ /**
+ * Removes synced users which are not present in any of group.
+ *
+ * @throws AmbariException
+ */
+ protected void cleanUpLdapUsersWithoutGroup() throws AmbariException {
+ final List<User> allUsers = users.getAllUsers();
+ for (User user: allUsers) {
+ if (user.isLdapUser() && user.getGroups().isEmpty()) {
+ users.removeUser(user);
+ }
+ }
+ }
+
+ // Utility methods
+
+ /**
+ * Retrieves groups from external LDAP server.
+ *
+ * @return set of info about LDAP groups
+ */
+ protected Set<LdapGroupDto> getExternalLdapGroupInfo() {
+ final Set<LdapGroupDto> groups = new HashSet<LdapGroupDto>();
+ final LdapTemplate ldapTemplate = loadLdapTemplate();
+ final EqualsFilter equalsFilter = new EqualsFilter("objectClass",
+ ldapServerProperties.getGroupObjectClass());
+ String baseDn = ldapServerProperties.getBaseDN();
+ ldapTemplate.search(baseDn, equalsFilter.encode(), new ContextMapper() {
+
+ @Override
+ public Object mapFromContext(Object ctx) {
+ final DirContextAdapter adapter = (DirContextAdapter) ctx;
+
+ final LdapGroupDto group = new LdapGroupDto();
+ final String groupNameAttribute = adapter.getStringAttribute(ldapServerProperties.getGroupNamingAttr());
+ group.setGroupName(groupNameAttribute.toLowerCase());
+
+ final String[] uniqueMembers = adapter.getStringAttributes(ldapServerProperties.getGroupMembershipAttr());
+ if (uniqueMembers != null) {
+ for (String uniqueMember: uniqueMembers) {
+ group.getMemberAttributes().add(uniqueMember.toLowerCase());
+ }
+ }
+
+ groups.add(group);
+ return null;
+ }
+ });
+ return groups;
+ }
+
+ /**
+ * Retrieves users from external LDAP server.
+ *
+ * @return set of info about LDAP users
+ */
+ protected Set<LdapUserDto> getExternalLdapUserInfo() {
+ final Set<LdapUserDto> users = new HashSet<LdapUserDto>();
+ final LdapTemplate ldapTemplate = loadLdapTemplate();
+ final EqualsFilter equalsFilter = new EqualsFilter("objectClass",
+ ldapServerProperties.getUserObjectClass());
+ String baseDn = ldapServerProperties.getBaseDN();
+ ldapTemplate.search(baseDn, equalsFilter.encode(), new ContextMapper() {
+
+ @Override
+ public Object mapFromContext(Object ctx) {
+ final LdapUserDto user = new LdapUserDto();
+ final DirContextAdapter adapter = (DirContextAdapter) ctx;
+ final String usernameAttribute = adapter.getStringAttribute(ldapServerProperties.getUsernameAttribute());
+ final String uidAttribute = adapter.getStringAttribute("uid");
+ if (usernameAttribute != null && uidAttribute != null) {
+ user.setUserName(usernameAttribute.toLowerCase());
+ user.setUid(uidAttribute.toLowerCase());
+ user.setDn(adapter.getNameInNamespace().toLowerCase());
+ } else {
+ LOG.warn("Ignoring LDAP user " + adapter.getNameInNamespace() + " as it doesn't have required" +
+ " attributes uid and " + ldapServerProperties.getUsernameAttribute());
+ }
+ users.add(user);
+ return null;
+ }
+ });
+ return users;
+ }
+
+ /**
+ * Creates a map of internal groups.
+ *
+ * @return map of GroupName-Group pairs
+ */
+ protected Map<String, Group> getInternalGroups() {
+ final List<Group> internalGroups = users.getAllGroups();
+ final Map<String, Group> internalGroupsMap = new HashMap<String, Group>();
+ for (Group group : internalGroups) {
+ internalGroupsMap.put(group.getGroupName(), group);
+ }
+ return internalGroupsMap;
+ }
+
+ /**
+ * Creates a map of internal users.
+ *
+ * @return map of UserName-User pairs
+ */
+ protected Map<String, User> getInternalUsers() {
+ final List<User> internalUsers = users.getAllUsers();
+ final Map<String, User> internalUsersMap = new HashMap<String, User>();
+ for (User user : internalUsers) {
+ internalUsersMap.put(user.getUserName(), user);
+ }
+ return internalUsersMap;
+ }
+
+ /**
+ * Creates a map of internal users present in specified group.
+ *
+ * @param groupName group name
+ * @return map of UserName-User pairs
+ */
+ protected Map<String, User> getInternalMembers(String groupName) {
+ final Collection<User> internalMembers = users.getGroupMembers(groupName);
+ if (internalMembers == null) {
+ return Collections.emptyMap();
+ }
+ final Map<String, User> internalMembersMap = new HashMap<String, User>();
+ for (User user : internalMembers) {
+ internalMembersMap.put(user.getUserName(), user);
+ }
+ return internalMembersMap;
+ }
+
+ /**
+ * Checks LDAP configuration for changes and reloads LDAP template if they occurred.
+ *
+ * @return LdapTemplate instance
+ */
+ protected LdapTemplate loadLdapTemplate() {
+ final LdapServerProperties properties = configuration
+ .getLdapServerProperties();
+ if (ldapTemplate == null || !properties.equals(ldapServerProperties)) {
+ LOG.info("Reloading properties");
+ ldapServerProperties = properties;
+
+ final LdapContextSource ldapContextSource = new LdapContextSource();
+ final List<String> ldapUrls = ldapServerProperties.getLdapUrls();
+ ldapContextSource.setUrls(ldapUrls.toArray(new String[ldapUrls.size()]));
+
+ if (!ldapServerProperties.isAnonymousBind()) {
+ ldapContextSource.setUserDn(ldapServerProperties.getManagerDn());
+ ldapContextSource.setPassword(ldapServerProperties.getManagerPassword());
+ }
+
+ try {
+ ldapContextSource.afterPropertiesSet();
+ } catch (Exception e) {
+ LOG.error("LDAP Context Source not loaded ", e);
+ throw new UsernameNotFoundException("LDAP Context Source not loaded", e);
+ }
+
+ ldapTemplate = new LdapTemplate(ldapContextSource);
+ }
+ return ldapTemplate;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/17b48bf4/ambari-server/src/main/java/org/apache/ambari/server/security/ldap/LdapBatchDto.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/security/ldap/LdapBatchDto.java b/ambari-server/src/main/java/org/apache/ambari/server/security/ldap/LdapBatchDto.java
new file mode 100644
index 0000000..9247f38
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/security/ldap/LdapBatchDto.java
@@ -0,0 +1,67 @@
+/**
+ * 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.ambari.server.security.ldap;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Contains information for batch database update on LDAP synchronization.
+ */
+public class LdapBatchDto {
+ private final Set<String> groupsToBecomeLdap = new HashSet<String>();
+ private final Set<String> groupsToBeCreated = new HashSet<String>();
+ private final Set<String> groupsToBeRemoved = new HashSet<String>();
+ private final Set<String> usersToBecomeLdap = new HashSet<String>();
+ private final Set<String> usersToBeCreated = new HashSet<String>();
+ private final Set<String> usersToBeRemoved = new HashSet<String>();
+ private final Set<LdapUserGroupMemberDto> membershipToAdd = new HashSet<LdapUserGroupMemberDto>();
+ private final Set<LdapUserGroupMemberDto> membershipToRemove = new HashSet<LdapUserGroupMemberDto>();
+
+ public Set<String> getGroupsToBecomeLdap() {
+ return groupsToBecomeLdap;
+ }
+
+ public Set<String> getGroupsToBeCreated() {
+ return groupsToBeCreated;
+ }
+
+ public Set<String> getUsersToBecomeLdap() {
+ return usersToBecomeLdap;
+ }
+
+ public Set<String> getUsersToBeCreated() {
+ return usersToBeCreated;
+ }
+
+ public Set<LdapUserGroupMemberDto> getMembershipToAdd() {
+ return membershipToAdd;
+ }
+
+ public Set<LdapUserGroupMemberDto> getMembershipToRemove() {
+ return membershipToRemove;
+ }
+
+ public Set<String> getGroupsToBeRemoved() {
+ return groupsToBeRemoved;
+ }
+
+ public Set<String> getUsersToBeRemoved() {
+ return usersToBeRemoved;
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/17b48bf4/ambari-server/src/main/java/org/apache/ambari/server/security/ldap/LdapGroupDto.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/security/ldap/LdapGroupDto.java b/ambari-server/src/main/java/org/apache/ambari/server/security/ldap/LdapGroupDto.java
new file mode 100644
index 0000000..132cbe5
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/security/ldap/LdapGroupDto.java
@@ -0,0 +1,113 @@
+/**
+ * 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.ambari.server.security.ldap;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Pojo with information about LDAP group of users.
+ */
+public class LdapGroupDto {
+ /**
+ * Name of the group.
+ */
+ private String groupName;
+
+ /**
+ * Set of member attributes. Usually it's either UID or DN of users.
+ */
+ private Set<String> memberAttributes = new HashSet<String>();
+
+ /**
+ * Determines if the LDAP group is synchronized with internal group in database.
+ */
+ private boolean synced;
+
+ /**
+ * Get the group name.
+ *
+ * @return the group name
+ */
+ public String getGroupName() {
+ return groupName;
+ }
+
+ /**
+ * Set the group name.
+ *
+ * @param groupName the group name
+ */
+ public void setGroupName(String groupName) {
+ this.groupName = groupName;
+ }
+
+ /**
+ * Get the member attributes.
+ *
+ * @return the set of member attributes
+ */
+ public Set<String> getMemberAttributes() {
+ return memberAttributes;
+ }
+
+ /**
+ * Set the member attributes.
+ *
+ * @param memberAttributes the member attributes
+ */
+ public void setMemberAttributes(Set<String> memberAttributes) {
+ this.memberAttributes = memberAttributes;
+ }
+
+ /**
+ * Get the synced flag.
+ *
+ * @return the synced flag
+ */
+ public boolean isSynced() {
+ return synced;
+ }
+
+ /**
+ * Set the synced flag
+ *
+ * @param synced the synced flag
+ */
+ public void setSynced(boolean synced) {
+ this.synced = synced;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = groupName != null ? groupName.hashCode() : 0;
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ LdapGroupDto that = (LdapGroupDto) o;
+
+ if (groupName != null ? !groupName.equals(that.getGroupName()) : that.getGroupName() != null) return false;
+
+ return true;
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/17b48bf4/ambari-server/src/main/java/org/apache/ambari/server/security/ldap/LdapSyncDto.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/security/ldap/LdapSyncDto.java b/ambari-server/src/main/java/org/apache/ambari/server/security/ldap/LdapSyncDto.java
new file mode 100644
index 0000000..6bd2ab9
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/security/ldap/LdapSyncDto.java
@@ -0,0 +1,72 @@
+/**
+ * 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.ambari.server.security.ldap;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Pojo with information about LDAP groups and users.
+ */
+public class LdapSyncDto {
+ /**
+ * LDAP groups.
+ */
+ private Set<LdapGroupDto> groups = new HashSet<LdapGroupDto>();
+
+ /**
+ * LDAP users.
+ */
+ private Set<LdapUserDto> users = new HashSet<LdapUserDto>();
+
+ /**
+ * Get the LDAP groups.
+ *
+ * @return the LDAP groups
+ */
+ public Set<LdapGroupDto> getGroups() {
+ return groups;
+ }
+
+ /**
+ * Set the LDAP groups.
+ *
+ * @param groups the LDAP groups
+ */
+ public void setGroups(Set<LdapGroupDto> groups) {
+ this.groups = groups;
+ }
+
+ /**
+ * Get the LDAP users.
+ *
+ * @return the LDAP users
+ */
+ public Set<LdapUserDto> getUsers() {
+ return users;
+ }
+
+ /**
+ * Set the LDAP users.
+ *
+ * @param users the LDAP users
+ */
+ public void setUsers(Set<LdapUserDto> users) {
+ this.users = users;
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/17b48bf4/ambari-server/src/main/java/org/apache/ambari/server/security/ldap/LdapUserDto.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/security/ldap/LdapUserDto.java b/ambari-server/src/main/java/org/apache/ambari/server/security/ldap/LdapUserDto.java
new file mode 100644
index 0000000..dbb13bf
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/security/ldap/LdapUserDto.java
@@ -0,0 +1,133 @@
+/**
+ * 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.ambari.server.security.ldap;
+
+/**
+ * Pojo with information about LDAP user.
+ */
+public class LdapUserDto {
+ /**
+ * Name of the user. Should be always unique.
+ */
+ private String userName;
+
+ /**
+ * Determines if the LDAP user is synchronized with internal user in database.
+ */
+ private boolean synced;
+
+ /**
+ * Unique identifier from LDAP.
+ */
+ private String uid;
+
+ /**
+ * Distinguished name from LDAP.
+ */
+ private String dn;
+
+ /**
+ * Get the user name.
+ *
+ * @return the user name
+ */
+ public String getUserName() {
+ return userName;
+ }
+
+ /**
+ * Set the user name.
+ *
+ * @param userName the user name
+ */
+ public void setUserName(String userName) {
+ this.userName = userName;
+ }
+
+ /**
+ * Get the synced flag.
+ *
+ * @return the synced flag
+ */
+ public boolean isSynced() {
+ return synced;
+ }
+
+ /**
+ * Set the synced flag
+ *
+ * @param synced the synced flag
+ */
+ public void setSynced(boolean synced) {
+ this.synced = synced;
+ }
+
+ /**
+ * Get the UID.
+ *
+ * @return the UID
+ */
+ public String getUid() {
+ return uid;
+ }
+
+ /**
+ * Set the UID.
+ *
+ * @param uid the UID
+ */
+ public void setUid(String uid) {
+ this.uid = uid;
+ }
+
+ /**
+ * Get the DN.
+ *
+ * @return the DN
+ */
+ public String getDn() {
+ return dn;
+ }
+
+ /**
+ * Set the DN.
+ *
+ * @param dn the DN
+ */
+ public void setDn(String dn) {
+ this.dn = dn;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = userName != null ? userName.hashCode() : 0;
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ LdapUserDto that = (LdapUserDto) o;
+
+ if (userName != null ? !userName.equals(that.getUserName()) : that.getUserName() != null) return false;
+
+ return true;
+ }
+}