Browse Source

[AMBARI-25062] Optionally execute the post user creation hook on existing users during LDAP sync

* [AMBARI-25062] Optionally execute the post user creation hook on existing users during LDAP sync

* [AMBARI-25062] Optionally execute the post user creation hook on existing users during LDAP sync
Robert Levas 7 years ago
parent
commit
fa3c6850f2
16 changed files with 471 additions and 99 deletions
  1. 41 6
      ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementControllerImpl.java
  2. 24 7
      ambari-server/src/main/java/org/apache/ambari/server/controller/LdapSyncRequest.java
  3. 10 4
      ambari-server/src/main/java/org/apache/ambari/server/controller/internal/LdapSyncEventResourceProvider.java
  4. 21 4
      ambari-server/src/main/java/org/apache/ambari/server/orm/entities/LdapSyncSpecEntity.java
  5. 18 1
      ambari-server/src/main/java/org/apache/ambari/server/security/authorization/Users.java
  6. 38 13
      ambari-server/src/main/java/org/apache/ambari/server/security/ldap/AmbariLdapDataPopulator.java
  7. 5 0
      ambari-server/src/main/java/org/apache/ambari/server/security/ldap/LdapBatchDto.java
  8. 2 0
      ambari-server/src/main/python/ambari-server.py
  9. 9 0
      ambari-server/src/main/python/ambari_server/setupSecurity.py
  10. 36 13
      ambari-server/src/test/java/org/apache/ambari/server/controller/AmbariManagementControllerImplTest.java
  11. 36 6
      ambari-server/src/test/java/org/apache/ambari/server/controller/LdapSyncRequestTest.java
  12. 1 1
      ambari-server/src/test/java/org/apache/ambari/server/orm/entities/LdapSyncEventEntityTest.java
  13. 8 8
      ambari-server/src/test/java/org/apache/ambari/server/orm/entities/LdapSyncSpecEntityTest.java
  14. 32 32
      ambari-server/src/test/java/org/apache/ambari/server/security/ldap/AmbariLdapDataPopulatorTest.java
  15. 4 4
      ambari-server/src/test/java/org/apache/ambari/server/security/ldap/LdapPerformanceTest.java
  16. 186 0
      ambari-server/src/test/python/TestAmbariServer.py

+ 41 - 6
ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementControllerImpl.java

@@ -185,6 +185,7 @@ import org.apache.ambari.server.security.encryption.CredentialStoreType;
 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.security.ldap.LdapUserDto;
 import org.apache.ambari.server.serveraction.kerberos.KerberosInvalidConfigurationException;
 import org.apache.ambari.server.serveraction.kerberos.KerberosOperationException;
 import org.apache.ambari.server.stack.ExtensionHelper;
@@ -5141,35 +5142,69 @@ public class AmbariManagementControllerImpl implements AmbariManagementControlle
     try {
 
       final LdapBatchDto batchInfo = new LdapBatchDto();
+      boolean postProcessExistingUsers = false;
+      boolean postProcessExistingUsersInGroups = false;
 
       if (userRequest != null) {
+        postProcessExistingUsers = userRequest.getPostProcessExistingUsers();
+
+        if(postProcessExistingUsers && !configs.isUserHookEnabled()) {
+          LOG.warn("Post processing existing users is requested while processing users; however, the user post creation hook is turned off.");
+          postProcessExistingUsers = false;
+        }
+
         switch (userRequest.getType()) {
           case ALL:
-            ldapDataPopulator.synchronizeAllLdapUsers(batchInfo);
+            ldapDataPopulator.synchronizeAllLdapUsers(batchInfo, postProcessExistingUsers);
             break;
           case EXISTING:
-            ldapDataPopulator.synchronizeExistingLdapUsers(batchInfo);
+            ldapDataPopulator.synchronizeExistingLdapUsers(batchInfo, postProcessExistingUsers);
             break;
           case SPECIFIC:
-            ldapDataPopulator.synchronizeLdapUsers(userRequest.getPrincipalNames(), batchInfo);
+            ldapDataPopulator.synchronizeLdapUsers(userRequest.getPrincipalNames(), batchInfo, postProcessExistingUsers);
             break;
         }
       }
       if (groupRequest != null) {
+        postProcessExistingUsersInGroups = groupRequest.getPostProcessExistingUsers();
+
+        if(postProcessExistingUsersInGroups && !configs.isUserHookEnabled()) {
+          LOG.warn("Post processing existing users is requested while processing groups; however, the user post creation hook is turned off.");
+          postProcessExistingUsersInGroups = false;
+        }
+
         switch (groupRequest.getType()) {
           case ALL:
-            ldapDataPopulator.synchronizeAllLdapGroups(batchInfo);
+            ldapDataPopulator.synchronizeAllLdapGroups(batchInfo, postProcessExistingUsersInGroups);
             break;
           case EXISTING:
-            ldapDataPopulator.synchronizeExistingLdapGroups(batchInfo);
+            ldapDataPopulator.synchronizeExistingLdapGroups(batchInfo, postProcessExistingUsersInGroups);
             break;
           case SPECIFIC:
-            ldapDataPopulator.synchronizeLdapGroups(groupRequest.getPrincipalNames(), batchInfo);
+            ldapDataPopulator.synchronizeLdapGroups(groupRequest.getPrincipalNames(), batchInfo, postProcessExistingUsersInGroups);
             break;
         }
       }
 
       users.processLdapSync(batchInfo);
+
+      if (postProcessExistingUsers || postProcessExistingUsersInGroups) {
+        // Execute post user creation hook on ignored users. These users were previously synced with
+        // Ambari but the post user creation script may not have been run on them due to various
+        // reasons
+        Set<LdapUserDto> ignoredUsers = batchInfo.getUsersIgnored();
+        if(CollectionUtils.isNotEmpty(ignoredUsers)) {
+          Map<String, Set<String>> userGroupsMap = new HashMap<>();
+          for (LdapUserDto ignoredUser : ignoredUsers) {
+            // The set of groups is empty here since the groups are not used in the script and the
+            // existing usage of the post user creation hook does not supply a set of groups either.
+            userGroupsMap.put(ignoredUser.getUserName(), Collections.emptySet());
+          }
+
+          users.executeUserHook(userGroupsMap);
+        }
+      }
+
       return batchInfo;
     } finally {
       ldapSyncInProgress = false;

+ 24 - 7
ambari-server/src/main/java/org/apache/ambari/server/controller/LdapSyncRequest.java

@@ -38,18 +38,26 @@ public class LdapSyncRequest {
    */
   private final LdapSyncSpecEntity.SyncType type;
 
+  /**
+   * A Boolean value indicating whether to execute the post user creation hook on previously
+   * existing users (if the post user creation hook feature has been enabled)
+   */
+  private final boolean postProcessExistingUsers;
+
 
   // ----- Constructors ------------------------------------------------------
 
   /**
    * Construct an LdapSyncRequest.
    *
-   * @param type            the request type
-   * @param principalNames  the principal names
+   * @param type                     the request type
+   * @param principalNames           the principal names
+   * @param postProcessExistingUsers true, to process existing users; false, otherwise
    */
-  public LdapSyncRequest(LdapSyncSpecEntity.SyncType type, Set<String> principalNames) {
-    this.type  = type;
+  public LdapSyncRequest(LdapSyncSpecEntity.SyncType type, Set<String> principalNames, boolean postProcessExistingUsers) {
+    this.type = type;
     this.principalNames = principalNames == null ? new HashSet<>() : principalNames;
+    this.postProcessExistingUsers = postProcessExistingUsers;
   }
 
   /**
@@ -57,9 +65,8 @@ public class LdapSyncRequest {
    *
    * @param type  the request type
    */
-  public LdapSyncRequest(LdapSyncSpecEntity.SyncType type) {
-    this.principalNames = new HashSet<>();
-    this.type  = type;
+  public LdapSyncRequest(LdapSyncSpecEntity.SyncType type, boolean postProcessExistingUsers) {
+    this(type, null, postProcessExistingUsers);
   }
 
 
@@ -91,4 +98,14 @@ public class LdapSyncRequest {
   public LdapSyncSpecEntity.SyncType getType() {
     return type;
   }
+
+  /**
+   * Gets whether to (re)exectue the post user creation hook on previously existing users
+   * (if the post user creation hook feature has been enabled), on not.
+   *
+   * @return true, to process existing users; false, otherwise
+   */
+  public boolean getPostProcessExistingUsers() {
+    return postProcessExistingUsers;
+  }
 }

+ 10 - 4
ambari-server/src/main/java/org/apache/ambari/server/controller/internal/LdapSyncEventResourceProvider.java

@@ -129,6 +129,7 @@ public class LdapSyncEventResourceProvider extends AbstractControllerResourcePro
    */
   private static final String PRINCIPAL_TYPE_SPEC_KEY = "principal_type";
   private static final String SYNC_TYPE_SPEC_KEY      = "sync_type";
+  private static final String POST_PROCESS_EXISTING_USERS_SPEC_KEY = "post_process_existing_users";
   private static final String NAMES_SPEC_KEY          = "names";
 
   /**
@@ -318,6 +319,7 @@ public class LdapSyncEventResourceProvider extends AbstractControllerResourcePro
 
       LdapSyncSpecEntity.SyncType      syncType      = null;
       LdapSyncSpecEntity.PrincipalType principalType = null;
+      boolean postProcessExistingUsers = false;
 
       List<String> principalNames = Collections.emptyList();
 
@@ -332,6 +334,10 @@ public class LdapSyncEventResourceProvider extends AbstractControllerResourcePro
         } else if (key.equalsIgnoreCase(NAMES_SPEC_KEY)) {
           String names = entry.getValue();
           principalNames = Arrays.asList(names.split("\\s*,\\s*"));
+
+        } else if (key.equalsIgnoreCase(POST_PROCESS_EXISTING_USERS_SPEC_KEY)) {
+          postProcessExistingUsers = "true".equalsIgnoreCase(entry.getValue());
+
         } else {
           throw new IllegalArgumentException("Unknown spec key " + key + ".");
         }
@@ -341,7 +347,7 @@ public class LdapSyncEventResourceProvider extends AbstractControllerResourcePro
         throw new IllegalArgumentException("LDAP event spec must include both sync-type and principal-type.");
       }
 
-      LdapSyncSpecEntity spec = new LdapSyncSpecEntity(principalType, syncType, principalNames);
+      LdapSyncSpecEntity spec = new LdapSyncSpecEntity(principalType, syncType, principalNames, postProcessExistingUsers);
       specList.add(spec);
     }
     entity.setSpecs(specList);
@@ -502,13 +508,13 @@ public class LdapSyncEventResourceProvider extends AbstractControllerResourcePro
 
     switch (spec.getSyncType()) {
       case ALL:
-        return new LdapSyncRequest(LdapSyncSpecEntity.SyncType.ALL);
+        return new LdapSyncRequest(LdapSyncSpecEntity.SyncType.ALL, spec.getPostProcessExistingUsers());
       case EXISTING:
-        return new LdapSyncRequest(LdapSyncSpecEntity.SyncType.EXISTING);
+        return new LdapSyncRequest(LdapSyncSpecEntity.SyncType.EXISTING, spec.getPostProcessExistingUsers());
       case SPECIFIC:
         Set<String> principalNames = new HashSet<>(spec.getPrincipalNames());
         if (request == null ) {
-          request = new LdapSyncRequest(LdapSyncSpecEntity.SyncType.SPECIFIC, principalNames);
+          request = new LdapSyncRequest(LdapSyncSpecEntity.SyncType.SPECIFIC, principalNames, spec.getPostProcessExistingUsers());
         } else {
           request.addPrincipalNames(principalNames);
         }

+ 21 - 4
ambari-server/src/main/java/org/apache/ambari/server/orm/entities/LdapSyncSpecEntity.java

@@ -40,20 +40,27 @@ public class LdapSyncSpecEntity {
    */
   private List<String> principalNames;
 
+  /**
+   * A Boolean value indicating whether to (re)exectue the post user creation hook on previously
+   * existing users (if the post user creation hook feature has been enabled)
+   */
+  private boolean postProcessExistingUsers;
 
   // ----- Constructors ------------------------------------------------------
 
   /**
    * Construct an LdapSyncSpecEntity.
    *
-   * @param principalType   the principal type
-   * @param syncType        the sync type
-   * @param principalNames  the list of principal names; may not be null
+   * @param principalType            the principal type
+   * @param syncType                 the sync type
+   * @param principalNames           the list of principal names; may not be null
+   * @param postProcessExistingUsers true, to process existing users; false, otherwise
    */
-  public LdapSyncSpecEntity(PrincipalType principalType, SyncType syncType, List<String> principalNames) {
+  public LdapSyncSpecEntity(PrincipalType principalType, SyncType syncType, List<String> principalNames, boolean postProcessExistingUsers) {
     this.principalType  = principalType;
     this.syncType       = syncType;
     this.principalNames = principalNames;
+    this.postProcessExistingUsers = postProcessExistingUsers;
 
     assert principalNames != null;
 
@@ -98,6 +105,16 @@ public class LdapSyncSpecEntity {
     return principalNames;
   }
 
+  /**
+   * Gets whether to execute the post user creation hook on previously existing users
+   * (if the post user creation hook feature has been enabled), on not.
+   *
+   * @return true, to process existing users; false, otherwise
+   */
+  public boolean getPostProcessExistingUsers() {
+    return postProcessExistingUsers;
+  }
+
 
   // ----- enum : PrincipalType ----------------------------------------------
 

+ 18 - 1
ambari-server/src/main/java/org/apache/ambari/server/security/authorization/Users.java

@@ -310,11 +310,28 @@ public class Users {
     userDAO.create(userEntity);
 
     // execute user initialization hook if required ()
-    hookServiceProvider.get().execute(hookContextFactory.createUserHookContext(validatedUserName));
+    executeUserHook(validatedUserName);
 
     return userEntity;
   }
 
+  /**
+   * Triggers the post user creation hook, if enabled
+   *
+   * @param username the username of the user to process
+   */
+  public void executeUserHook(String username) {
+    hookServiceProvider.get().execute(hookContextFactory.createUserHookContext(username));
+  }
+
+  /**
+   * Triggers the post user creation hook, if enabled
+   *
+   * @param userGroupsMap a map of user names to relevant groups
+   */
+  public void executeUserHook(Map<String, Set<String>> userGroupsMap) {
+    hookServiceProvider.get().execute(hookContextFactory.createBatchUserHookContext(userGroupsMap));
+  }
 
   /**
    * Removes a user from the Ambari database.

+ 38 - 13
ambari-server/src/main/java/org/apache/ambari/server/security/ldap/AmbariLdapDataPopulator.java

@@ -190,9 +190,11 @@ public class AmbariLdapDataPopulator {
   /**
    * Performs synchronization of all groups.
    *
+   * @param collectIgnoredUsers true, to collect the set of existing users that would normally be ignored;
+   *                            false, to continue to ignore them
    * @throws AmbariException if synchronization failed for any reason
    */
-  public LdapBatchDto synchronizeAllLdapGroups(LdapBatchDto batchInfo) throws AmbariException {
+  public LdapBatchDto synchronizeAllLdapGroups(LdapBatchDto batchInfo, boolean collectIgnoredUsers) throws AmbariException {
     LOG.trace("Synchronize All LDAP groups...");
     Set<LdapGroupDto> externalLdapGroupInfo = getExternalLdapGroupInfo();
 
@@ -201,7 +203,7 @@ public class AmbariLdapDataPopulator {
 
     for (LdapGroupDto groupDto : externalLdapGroupInfo) {
       addLdapGroup(batchInfo, internalGroupsMap, groupDto);
-      refreshGroupMembers(batchInfo, groupDto, internalUsersMap, internalGroupsMap, null, false);
+      refreshGroupMembers(batchInfo, groupDto, internalUsersMap, internalGroupsMap, null, false, collectIgnoredUsers);
     }
     for (Entry<String, Group> internalGroup : internalGroupsMap.entrySet()) {
       if (internalGroup.getValue().isLdapGroup()) {
@@ -217,9 +219,11 @@ public class AmbariLdapDataPopulator {
   /**
    * Performs synchronization of given sets of all users.
    *
+   * @param collectIgnoredUsers true, to collect the set of existing users that would normally be ignored;
+   *                            false, to continue to ignore them
    * @throws AmbariException if synchronization failed for any reason
    */
-  public LdapBatchDto synchronizeAllLdapUsers(LdapBatchDto batchInfo) throws AmbariException {
+  public LdapBatchDto synchronizeAllLdapUsers(LdapBatchDto batchInfo, boolean collectIgnoredUsers) throws AmbariException {
     LOG.trace("Synchronize All LDAP users...");
     Set<LdapUserDto> externalLdapUserInfo = getExternalLdapUserInfo();
     Map<String, User> internalUsersMap = getInternalUsers();
@@ -236,6 +240,8 @@ public class AmbariLdapDataPopulator {
             batchInfo.getUsersToBecomeLdap().add(userDto);
             LOG.trace("Convert user '{}' to LDAP user.", userName);
           }
+        } else if (collectIgnoredUsers) {
+          batchInfo.getUsersIgnored().add(userDto);
         }
         internalUsersMap.remove(userName);
       } else {
@@ -257,10 +263,12 @@ public class AmbariLdapDataPopulator {
   /**
    * Performs synchronization of given set of groupnames.
    *
-   * @param groups set of groups to synchronize
+   * @param groups              set of groups to synchronize
+   * @param collectIgnoredUsers true, to collect the set of existing users that would normally be ignored;
+   *                            false, to continue to ignore them
    * @throws AmbariException if synchronization failed for any reason
    */
-  public LdapBatchDto synchronizeLdapGroups(Set<String> groups, LdapBatchDto batchInfo) throws AmbariException {
+  public LdapBatchDto synchronizeLdapGroups(Set<String> groups, LdapBatchDto batchInfo, boolean collectIgnoredUsers) throws AmbariException {
     LOG.trace("Synchronize LDAP groups...");
     final Set<LdapGroupDto> specifiedGroups = new HashSet<>();
     for (String group : groups) {
@@ -277,7 +285,7 @@ public class AmbariLdapDataPopulator {
 
     for (LdapGroupDto groupDto : specifiedGroups) {
       addLdapGroup(batchInfo, internalGroupsMap, groupDto);
-      refreshGroupMembers(batchInfo, groupDto, internalUsersMap, internalGroupsMap, null, true);
+      refreshGroupMembers(batchInfo, groupDto, internalUsersMap, internalGroupsMap, null, true, collectIgnoredUsers);
     }
 
     return batchInfo;
@@ -286,10 +294,12 @@ public class AmbariLdapDataPopulator {
   /**
    * Performs synchronization of given set of user names.
    *
-   * @param users set of users to synchronize
+   * @param users               set of users to synchronize
+   * @param collectIgnoredUsers true, to collect the set of existing users that would normally be ignored;
+   *                            false, to continue to ignore them
    * @throws AmbariException if synchronization failed for any reason
    */
-  public LdapBatchDto synchronizeLdapUsers(Set<String> users, LdapBatchDto batchInfo) throws AmbariException {
+  public LdapBatchDto synchronizeLdapUsers(Set<String> users, LdapBatchDto batchInfo, boolean collectIgnoredUsers) throws AmbariException {
     LOG.trace("Synchronize LDAP users...");
     final Set<LdapUserDto> specifiedUsers = new HashSet<>();
 
@@ -314,6 +324,8 @@ public class AmbariLdapDataPopulator {
           } else {
             batchInfo.getUsersToBecomeLdap().add(userDto);
           }
+        } else if (collectIgnoredUsers) {
+          batchInfo.getUsersIgnored().add(userDto);
         }
         internalUsersMap.remove(userName);
       } else {
@@ -327,9 +339,11 @@ public class AmbariLdapDataPopulator {
   /**
    * Performs synchronization of existent users and groups.
    *
+   * @param collectIgnoredUsers true, to collect the set of existing users that would normally be ignored;
+   *                            false, to continue to ignore them
    * @throws AmbariException if synchronization failed for any reason
    */
-  public LdapBatchDto synchronizeExistingLdapGroups(LdapBatchDto batchInfo) throws AmbariException {
+  public LdapBatchDto synchronizeExistingLdapGroups(LdapBatchDto batchInfo, boolean collectIgnoredUsers) throws AmbariException {
     LOG.trace("Synchronize Existing LDAP groups...");
     final Map<String, Group> internalGroupsMap = getInternalGroups();
     final Map<String, User> internalUsersMap = getInternalUsers();
@@ -345,7 +359,7 @@ public class AmbariLdapDataPopulator {
           batchInfo.getGroupsToBeRemoved().add(groupDto);
         } else {
           LdapGroupDto groupDto = groupDtos.iterator().next();
-          refreshGroupMembers(batchInfo, groupDto, internalUsersMap, internalGroupsMap, null, true);
+          refreshGroupMembers(batchInfo, groupDto, internalUsersMap, internalGroupsMap, null, true, collectIgnoredUsers);
         }
       }
     }
@@ -356,9 +370,11 @@ public class AmbariLdapDataPopulator {
   /**
    * Performs synchronization of existent users and groups.
    *
+   * @param collectIgnoredUsers true, to collect the set of existing users that would normally be ignored;
+   *                            false, to continue to ignore them
    * @throws AmbariException if synchronization failed for any reason
    */
-  public LdapBatchDto synchronizeExistingLdapUsers(LdapBatchDto batchInfo) throws AmbariException {
+  public LdapBatchDto synchronizeExistingLdapUsers(LdapBatchDto batchInfo, boolean collectIgnoredUsers) throws AmbariException {
     LOG.trace("Synchronize Existing LDAP users...");
     final Map<String, User> internalUsersMap = getInternalUsers();
 
@@ -370,6 +386,10 @@ public class AmbariLdapDataPopulator {
           userDto.setUserName(user.getUserName());
           userDto.setDn(null);  // Setting to null since we do not know what the DN for this user was.
           batchInfo.getUsersToBeRemoved().add(userDto);
+        } else if (collectIgnoredUsers) {
+          LdapUserDto userDto = new LdapUserDto();
+          userDto.setUserName(user.getUserName());
+          batchInfo.getUsersIgnored().add(userDto);
         }
       }
     }
@@ -385,10 +405,13 @@ public class AmbariLdapDataPopulator {
    * @param internalUsers         map of internal users
    * @param groupMemberAttributes set of group member attributes that have already been refreshed
    * @param recursive             if disabled, it won't refresh members recursively (its not needed in case of all groups are processed)
+   * @param collectIgnoredUsers   true, to collect the set of existing users that would normally be ignored;
+   *                              false, to continue to ignore them
    * @throws AmbariException if group refresh failed
    */
   protected void refreshGroupMembers(LdapBatchDto batchInfo, LdapGroupDto group, Map<String, User> internalUsers,
-                                     Map<String, Group> internalGroupsMap, Set<String> groupMemberAttributes, boolean recursive)
+                                     Map<String, Group> internalGroupsMap, Set<String> groupMemberAttributes, boolean recursive,
+                                     boolean collectIgnoredUsers)
       throws AmbariException {
     Set<LdapUserDto> externalMembers = new HashSet<>();
 
@@ -408,7 +431,7 @@ public class AmbariLdapDataPopulator {
           if (subGroup != null) {
             groupMemberAttributes.add(memberAttributeValue);
             addLdapGroup(batchInfo, internalGroupsMap, subGroup);
-            refreshGroupMembers(batchInfo, subGroup, internalUsers, internalGroupsMap, groupMemberAttributes, true);
+            refreshGroupMembers(batchInfo, subGroup, internalUsers, internalGroupsMap, groupMemberAttributes, true, collectIgnoredUsers);
           }
         }
       }
@@ -435,6 +458,8 @@ public class AmbariLdapDataPopulator {
           } else {
             batchInfo.getUsersToBecomeLdap().add(externalMember);
           }
+        } else if (collectIgnoredUsers) {
+          batchInfo.getUsersIgnored().add(externalMember);
         }
         if (!internalMembers.containsKey(userName)) {
           batchInfo.getMembershipToAdd().add(new LdapUserGroupMemberDto(groupName, externalMember.getUserName()));

+ 5 - 0
ambari-server/src/main/java/org/apache/ambari/server/security/ldap/LdapBatchDto.java

@@ -29,6 +29,7 @@ public class LdapBatchDto {
   private final Set<LdapGroupDto> groupsToBeRemoved = new HashSet<>();
   private final Set<LdapGroupDto> groupsProcessedInternal = new HashSet<>();
   private final Set<LdapUserDto> usersSkipped = new HashSet<>();
+  private final Set<LdapUserDto> usersIgnored = new HashSet<>();
   private final Set<LdapUserDto> usersToBecomeLdap = new HashSet<>();
   private final Set<LdapUserDto> usersToBeCreated = new HashSet<>();
   private final Set<LdapUserDto> usersToBeRemoved = new HashSet<>();
@@ -39,6 +40,10 @@ public class LdapBatchDto {
     return usersSkipped;
   }
 
+  public Set<LdapUserDto> getUsersIgnored() {
+    return usersIgnored;
+  }
+
   public Set<LdapGroupDto> getGroupsToBecomeLdap() {
     return groupsToBecomeLdap;
   }

+ 2 - 0
ambari-server/src/main/python/ambari-server.py

@@ -539,6 +539,8 @@ def init_ldap_sync_parser_options(parser):
                     dest="ldap_sync_users")
   parser.add_option('--groups', default=None, help="LDAP sync groups option.  Specifies the path to a CSV file of group names to be synchronized.",
                     dest="ldap_sync_groups")
+  parser.add_option('--post-process-existing-users', action="store_true", default=False, help="While performing an LDAP sync, reprocess existing users by executing the post creation hook on them, if the feature is enabled",
+                    dest="ldap_sync_post_process_existing_users")
   parser.add_option('--ldap-sync-admin-name', default=None, help="Username for LDAP sync", dest="ldap_sync_admin_name")
   parser.add_option('--ldap-sync-admin-password', default=None, help="Password for LDAP sync", dest="ldap_sync_admin_password")
 

+ 9 - 0
ambari-server/src/main/python/ambari_server/setupSecurity.py

@@ -291,6 +291,11 @@ class LdapSyncOptions:
     except AttributeError:
       self.ldap_sync_admin_password = None
 
+    try:
+      self.ldap_sync_post_process_existing_users = options.ldap_sync_post_process_existing_users
+    except AttributeError:
+      self.ldap_sync_post_process_existing_users = False
+
   def no_ldap_sync_options_set(self):
     return not self.ldap_sync_all and not self.ldap_sync_existing and self.ldap_sync_users is None and self.ldap_sync_groups is None
 
@@ -417,6 +422,10 @@ def sync_ldap(options):
       new_specs = [{"principal_type":"groups","sync_type":"specific","names":""}]
       get_ldap_event_spec_names(ldap_sync_options.ldap_sync_groups, specs, new_specs)
 
+  if ldap_sync_options.ldap_sync_post_process_existing_users:
+    for spec in bodies[0]["Event"]["specs"]:
+      spec["post_process_existing_users"] = "true"
+
   if get_verbose():
     sys.stdout.write('\nCalling API ' + url + ' : ' + str(bodies) + '\n')
 

+ 36 - 13
ambari-server/src/test/java/org/apache/ambari/server/controller/AmbariManagementControllerImplTest.java

@@ -2228,7 +2228,27 @@ public class AmbariManagementControllerImplTest {
   }
 
   @Test
-  public void testSynchronizeLdapUsersAndGroups() throws Exception {
+  public void testSynchronizeLdapUsersAndGroupsHookDisabled() throws Exception {
+    testSynchronizeLdapUsersAndGroups(false, false);
+  }
+
+  @Test
+  public void testSynchronizeLdapUsersAndGroupsHookEnabled() throws Exception {
+    testSynchronizeLdapUsersAndGroups(false, true);
+  }
+
+  @Test
+  public void testSynchronizeLdapUsersAndGroupsPostProcessExistingUsersHookDisabled() throws Exception {
+    testSynchronizeLdapUsersAndGroups(true, false);
+  }
+
+  @Test
+  public void testSynchronizeLdapUsersAndGroupsPostProcessExistingUsersHookEnabled() throws Exception {
+    testSynchronizeLdapUsersAndGroups(true, true);
+  }
+
+  private void testSynchronizeLdapUsersAndGroups(boolean postProcessExistingUsers, boolean postUserCreationHookEnabled) throws Exception {
+    boolean collectIgnoredUsers = postProcessExistingUsers && postUserCreationHookEnabled;
 
     Set<String> userSet = new HashSet<>();
     userSet.add("user1");
@@ -2246,14 +2266,14 @@ public class AmbariManagementControllerImplTest {
     Capture<LdapBatchDto> ldapBatchDtoCapture = EasyMock.newCapture();
 
     // set expectations
-    expect(ldapDataPopulator.synchronizeAllLdapUsers(capture(ldapBatchDtoCapture))).andReturn(ldapBatchDto);
-    expect(ldapDataPopulator.synchronizeAllLdapGroups(capture(ldapBatchDtoCapture))).andReturn(ldapBatchDto);
+    expect(ldapDataPopulator.synchronizeAllLdapUsers(capture(ldapBatchDtoCapture), eq(collectIgnoredUsers))).andReturn(ldapBatchDto);
+    expect(ldapDataPopulator.synchronizeAllLdapGroups(capture(ldapBatchDtoCapture), eq(collectIgnoredUsers))).andReturn(ldapBatchDto);
 
-    expect(ldapDataPopulator.synchronizeExistingLdapUsers(capture(ldapBatchDtoCapture))).andReturn(ldapBatchDto);
-    expect(ldapDataPopulator.synchronizeExistingLdapGroups(capture(ldapBatchDtoCapture))).andReturn(ldapBatchDto);
+    expect(ldapDataPopulator.synchronizeExistingLdapUsers(capture(ldapBatchDtoCapture), eq(collectIgnoredUsers))).andReturn(ldapBatchDto);
+    expect(ldapDataPopulator.synchronizeExistingLdapGroups(capture(ldapBatchDtoCapture), eq(collectIgnoredUsers))).andReturn(ldapBatchDto);
 
-    expect(ldapDataPopulator.synchronizeLdapUsers(eq(userSet), capture(ldapBatchDtoCapture))).andReturn(ldapBatchDto);
-    expect(ldapDataPopulator.synchronizeLdapGroups(eq(groupSet), capture(ldapBatchDtoCapture))).andReturn(ldapBatchDto);
+    expect(ldapDataPopulator.synchronizeLdapUsers(eq(userSet), capture(ldapBatchDtoCapture), eq(collectIgnoredUsers))).andReturn(ldapBatchDto);
+    expect(ldapDataPopulator.synchronizeLdapGroups(eq(groupSet), capture(ldapBatchDtoCapture), eq(collectIgnoredUsers))).andReturn(ldapBatchDto);
 
     users.processLdapSync(capture(ldapBatchDtoCapture));
     expectLastCall().anyTimes();
@@ -2261,20 +2281,23 @@ public class AmbariManagementControllerImplTest {
     //replay
     replay(ldapDataPopulator, clusters, actionDBAccessor, ambariMetaInfo, users, ldapBatchDto);
 
+    Configuration configs = injector.getInstance(Configuration.class);
+    configs.setProperty(Configuration.POST_USER_CREATION_HOOK_ENABLED.getKey(), String.valueOf(postUserCreationHookEnabled));
+
     AmbariManagementControllerImpl controller = injector.getInstance(AmbariManagementControllerImpl.class);
 
-    LdapSyncRequest userRequest = new LdapSyncRequest(LdapSyncSpecEntity.SyncType.ALL);
-    LdapSyncRequest groupRequest = new LdapSyncRequest(LdapSyncSpecEntity.SyncType.ALL);
+    LdapSyncRequest userRequest = new LdapSyncRequest(LdapSyncSpecEntity.SyncType.ALL, postProcessExistingUsers);
+    LdapSyncRequest groupRequest = new LdapSyncRequest(LdapSyncSpecEntity.SyncType.ALL, postProcessExistingUsers);
 
     controller.synchronizeLdapUsersAndGroups(userRequest, groupRequest);
 
-    userRequest = new LdapSyncRequest(LdapSyncSpecEntity.SyncType.EXISTING);
-    groupRequest = new LdapSyncRequest(LdapSyncSpecEntity.SyncType.EXISTING);
+    userRequest = new LdapSyncRequest(LdapSyncSpecEntity.SyncType.EXISTING, postProcessExistingUsers);
+    groupRequest = new LdapSyncRequest(LdapSyncSpecEntity.SyncType.EXISTING, postProcessExistingUsers);
 
     controller.synchronizeLdapUsersAndGroups(userRequest, groupRequest);
 
-    userRequest = new LdapSyncRequest(LdapSyncSpecEntity.SyncType.SPECIFIC, userSet);
-    groupRequest = new LdapSyncRequest(LdapSyncSpecEntity.SyncType.SPECIFIC, groupSet);
+    userRequest = new LdapSyncRequest(LdapSyncSpecEntity.SyncType.SPECIFIC, userSet, postProcessExistingUsers);
+    groupRequest = new LdapSyncRequest(LdapSyncSpecEntity.SyncType.SPECIFIC, groupSet, postProcessExistingUsers);
 
     controller.synchronizeLdapUsersAndGroups(userRequest, groupRequest);
 

+ 36 - 6
ambari-server/src/test/java/org/apache/ambari/server/controller/LdapSyncRequestTest.java

@@ -34,7 +34,7 @@ public class LdapSyncRequestTest {
     Set<String> names = new HashSet<>();
     names.add("name1");
 
-    LdapSyncRequest request = new LdapSyncRequest(LdapSyncSpecEntity.SyncType.SPECIFIC,names);
+    LdapSyncRequest request = new LdapSyncRequest(LdapSyncSpecEntity.SyncType.SPECIFIC,names, false);
 
     names = new HashSet<>();
     names.add("name2");
@@ -56,7 +56,7 @@ public class LdapSyncRequestTest {
     names.add("name2");
     names.add("name3");
 
-    LdapSyncRequest request = new LdapSyncRequest(LdapSyncSpecEntity.SyncType.SPECIFIC,names);
+    LdapSyncRequest request = new LdapSyncRequest(LdapSyncSpecEntity.SyncType.SPECIFIC,names, false);
 
     Set<String> principalNames = request.getPrincipalNames();
     Assert.assertEquals(3, principalNames.size());
@@ -69,16 +69,46 @@ public class LdapSyncRequestTest {
   public void testGetType() throws Exception {
     Set<String> names = new HashSet<>();
 
-    LdapSyncRequest request = new LdapSyncRequest(LdapSyncSpecEntity.SyncType.SPECIFIC, names);
+    LdapSyncRequest request = new LdapSyncRequest(LdapSyncSpecEntity.SyncType.SPECIFIC, names, false);
 
     Assert.assertEquals(LdapSyncSpecEntity.SyncType.SPECIFIC, request.getType());
 
-    request = new LdapSyncRequest(LdapSyncSpecEntity.SyncType.ALL);
+    request = new LdapSyncRequest(LdapSyncSpecEntity.SyncType.ALL, false);
 
     Assert.assertEquals(LdapSyncSpecEntity.SyncType.ALL, request.getType());
 
-    request = new LdapSyncRequest(LdapSyncSpecEntity.SyncType.EXISTING);
+    request = new LdapSyncRequest(LdapSyncSpecEntity.SyncType.EXISTING, false);
 
     Assert.assertEquals(LdapSyncSpecEntity.SyncType.EXISTING, request.getType());
   }
-}
+
+  @Test
+  public void testGetPostProcessExistingUsers() throws Exception {
+    Set<String> names = new HashSet<>();
+
+    LdapSyncRequest request;
+
+    request = new LdapSyncRequest(LdapSyncSpecEntity.SyncType.SPECIFIC, names, false);
+
+    Assert.assertFalse(request.getPostProcessExistingUsers());
+
+    request = new LdapSyncRequest(LdapSyncSpecEntity.SyncType.SPECIFIC, names, true);
+
+    Assert.assertTrue(request.getPostProcessExistingUsers());
+
+    request = new LdapSyncRequest(LdapSyncSpecEntity.SyncType.ALL, false);
+
+    Assert.assertFalse(request.getPostProcessExistingUsers());
+
+    request = new LdapSyncRequest(LdapSyncSpecEntity.SyncType.ALL, true);
+
+    Assert.assertTrue(request.getPostProcessExistingUsers());
+
+    request = new LdapSyncRequest(LdapSyncSpecEntity.SyncType.EXISTING, false);
+
+    Assert.assertFalse(request.getPostProcessExistingUsers());
+
+    request = new LdapSyncRequest(LdapSyncSpecEntity.SyncType.EXISTING, true);
+
+    Assert.assertTrue(request.getPostProcessExistingUsers());
+  }}

+ 1 - 1
ambari-server/src/test/java/org/apache/ambari/server/orm/entities/LdapSyncEventEntityTest.java

@@ -63,7 +63,7 @@ public class LdapSyncEventEntityTest {
   public void testSetGetSpecs() throws Exception {
     LdapSyncEventEntity event = new LdapSyncEventEntity(1L);
     LdapSyncSpecEntity spec = new LdapSyncSpecEntity(LdapSyncSpecEntity.PrincipalType.GROUPS,
-        LdapSyncSpecEntity.SyncType.ALL, Collections.emptyList());
+        LdapSyncSpecEntity.SyncType.ALL, Collections.emptyList(), false);
 
     event.setSpecs(Collections.singletonList(spec));
 

+ 8 - 8
ambari-server/src/test/java/org/apache/ambari/server/orm/entities/LdapSyncSpecEntityTest.java

@@ -32,22 +32,22 @@ public class LdapSyncSpecEntityTest {
   @Test
   public void testGetPrincipalType() throws Exception {
     LdapSyncSpecEntity entity = new LdapSyncSpecEntity(LdapSyncSpecEntity.PrincipalType.USERS,
-        LdapSyncSpecEntity.SyncType.ALL, Collections.emptyList());
+        LdapSyncSpecEntity.SyncType.ALL, Collections.emptyList(), false);
     Assert.assertEquals(LdapSyncSpecEntity.PrincipalType.USERS, entity.getPrincipalType());
 
     entity = new LdapSyncSpecEntity(LdapSyncSpecEntity.PrincipalType.GROUPS,
-        LdapSyncSpecEntity.SyncType.ALL, Collections.emptyList());
+        LdapSyncSpecEntity.SyncType.ALL, Collections.emptyList(), false);
     Assert.assertEquals(LdapSyncSpecEntity.PrincipalType.GROUPS, entity.getPrincipalType());
   }
 
   @Test
   public void testGetSyncType() throws Exception {
     LdapSyncSpecEntity entity = new LdapSyncSpecEntity(LdapSyncSpecEntity.PrincipalType.USERS,
-        LdapSyncSpecEntity.SyncType.ALL, Collections.emptyList());
+        LdapSyncSpecEntity.SyncType.ALL, Collections.emptyList(), false);
     Assert.assertEquals(LdapSyncSpecEntity.SyncType.ALL, entity.getSyncType());
 
     entity = new LdapSyncSpecEntity(LdapSyncSpecEntity.PrincipalType.USERS,
-        LdapSyncSpecEntity.SyncType.EXISTING, Collections.emptyList());
+        LdapSyncSpecEntity.SyncType.EXISTING, Collections.emptyList(), false);
     Assert.assertEquals(LdapSyncSpecEntity.SyncType.EXISTING, entity.getSyncType());
   }
 
@@ -58,7 +58,7 @@ public class LdapSyncSpecEntityTest {
     names.add("fred");
 
     LdapSyncSpecEntity entity = new LdapSyncSpecEntity(LdapSyncSpecEntity.PrincipalType.USERS,
-        LdapSyncSpecEntity.SyncType.SPECIFIC, names);
+        LdapSyncSpecEntity.SyncType.SPECIFIC, names, false);
     Assert.assertEquals(names, entity.getPrincipalNames());
   }
 
@@ -66,7 +66,7 @@ public class LdapSyncSpecEntityTest {
   public void testIllegalConstruction() throws Exception {
     try {
       new LdapSyncSpecEntity(LdapSyncSpecEntity.PrincipalType.USERS,
-          LdapSyncSpecEntity.SyncType.SPECIFIC, Collections.emptyList());
+          LdapSyncSpecEntity.SyncType.SPECIFIC, Collections.emptyList(), false);
       Assert.fail("expected IllegalArgumentException");
     } catch (IllegalArgumentException e) {
       // expected
@@ -78,7 +78,7 @@ public class LdapSyncSpecEntityTest {
 
     try {
       new LdapSyncSpecEntity(LdapSyncSpecEntity.PrincipalType.USERS,
-          LdapSyncSpecEntity.SyncType.ALL, names);
+          LdapSyncSpecEntity.SyncType.ALL, names, false);
       Assert.fail("expected IllegalArgumentException");
     } catch (IllegalArgumentException e) {
       // expected
@@ -86,7 +86,7 @@ public class LdapSyncSpecEntityTest {
 
     try {
       new LdapSyncSpecEntity(LdapSyncSpecEntity.PrincipalType.USERS,
-          LdapSyncSpecEntity.SyncType.EXISTING, names);
+          LdapSyncSpecEntity.SyncType.EXISTING, names, false);
       Assert.fail("expected IllegalArgumentException");
     } catch (IllegalArgumentException e) {
       // expected

+ 32 - 32
ambari-server/src/test/java/org/apache/ambari/server/security/ldap/AmbariLdapDataPopulatorTest.java

@@ -292,7 +292,7 @@ public class AmbariLdapDataPopulatorTest {
     expect(populator.getLdapGroups("group2")).andReturn(Collections.emptySet());
     LdapGroupDto externalGroup1 = createNiceMock(LdapGroupDto.class);
     LdapBatchDto batchInfo = new LdapBatchDto();
-    populator.refreshGroupMembers(eq(batchInfo), eq(externalGroup1), EasyMock.anyObject(), EasyMock.anyObject(), EasyMock.anyObject(), anyBoolean());
+    populator.refreshGroupMembers(eq(batchInfo), eq(externalGroup1), EasyMock.anyObject(), EasyMock.anyObject(), EasyMock.anyObject(), anyBoolean(), eq(false));
     expectLastCall();
     expect(populator.getLdapGroups("group4")).andReturn(Collections.singleton(externalGroup1));
     expect(populator.getLdapGroups("group5")).andReturn(Collections.emptySet());
@@ -301,7 +301,7 @@ public class AmbariLdapDataPopulatorTest {
     populator.setLdapTemplate(ldapTemplate);
     populator.setLdapServerProperties(ldapServerProperties);
 
-    LdapBatchDto result = populator.synchronizeExistingLdapGroups(batchInfo);
+    LdapBatchDto result = populator.synchronizeExistingLdapGroups(batchInfo, false);
 
     verifyGroupsInSet(result.getGroupsToBeRemoved(), Sets.newHashSet("group2", "group5"));
     assertTrue(result.getGroupsToBecomeLdap().isEmpty());
@@ -364,7 +364,7 @@ public class AmbariLdapDataPopulatorTest {
 
     replay(dataPopulator);
     // WHEN
-    dataPopulator.synchronizeExistingLdapGroups(batchInfo);
+    dataPopulator.synchronizeExistingLdapGroups(batchInfo, false);
     // THEN
     verify(dataPopulator, group1, group2);
 
@@ -421,14 +421,14 @@ public class AmbariLdapDataPopulatorTest {
     Set<LdapGroupDto> externalGroups = createSet(externalGroup3, externalGroup4);
     for (LdapGroupDto externalGroup : externalGroups) {
       populator.refreshGroupMembers(eq(batchInfo), eq(externalGroup),
-          EasyMock.anyObject(), EasyMock.anyObject(), EasyMock.anyObject(), anyBoolean());
+          EasyMock.anyObject(), EasyMock.anyObject(), EasyMock.anyObject(), anyBoolean(), eq(false));
       expectLastCall();
     }
     populator.refreshGroupMembers(eq(batchInfo), eq(externalGroup1),
-        EasyMock.anyObject(), EasyMock.anyObject(), EasyMock.anyObject(), anyBoolean());
+        EasyMock.anyObject(), EasyMock.anyObject(), EasyMock.anyObject(), anyBoolean(), eq(false));
     expectLastCall();
     populator.refreshGroupMembers(eq(batchInfo), eq(externalGroup2), EasyMock.anyObject(),
-        EasyMock.anyObject(), EasyMock.anyObject(), anyBoolean());
+        EasyMock.anyObject(), EasyMock.anyObject(), anyBoolean(), eq(false));
     expectLastCall();
     expect(populator.getLdapGroups("x*")).andReturn(externalGroups);
     expect(populator.getLdapGroups("group1")).andReturn(Collections.singleton(externalGroup1));
@@ -438,7 +438,7 @@ public class AmbariLdapDataPopulatorTest {
     populator.setLdapTemplate(ldapTemplate);
     populator.setLdapServerProperties(ldapServerProperties);
 
-    LdapBatchDto result = populator.synchronizeLdapGroups(createSet("x*", "group1", "group2"), batchInfo);
+    LdapBatchDto result = populator.synchronizeLdapGroups(createSet("x*", "group1", "group2"), batchInfo, false);
 
     verifyGroupsInSet(result.getGroupsToBecomeLdap(), Sets.newHashSet("group1"));
     verifyGroupsInSet(result.getGroupsToBeCreated(), Sets.newHashSet("xgroup1", "xgroup2"));
@@ -504,11 +504,11 @@ public class AmbariLdapDataPopulatorTest {
     Set<LdapGroupDto> externalGroups = createSet(externalGroup3, externalGroup4);
     for (LdapGroupDto externalGroup : externalGroups) {
       populator.refreshGroupMembers(eq(batchInfo), eq(externalGroup), EasyMock.anyObject(), EasyMock.anyObject(),
-          EasyMock.anyObject(), anyBoolean());
+          EasyMock.anyObject(), anyBoolean(), eq(false));
       expectLastCall();
     }
     populator.refreshGroupMembers(eq(batchInfo), eq(externalGroup2), EasyMock.anyObject(),
-        EasyMock.anyObject(), EasyMock.anyObject(), anyBoolean());
+        EasyMock.anyObject(), EasyMock.anyObject(), anyBoolean(), eq(false));
     expectLastCall();
     expect(populator.getLdapGroups("x*")).andReturn(externalGroups);
     expect(populator.getLdapGroups("group2")).andReturn(Collections.singleton(externalGroup2));
@@ -517,7 +517,7 @@ public class AmbariLdapDataPopulatorTest {
     populator.setLdapTemplate(ldapTemplate);
     populator.setLdapServerProperties(ldapServerProperties);
 
-    LdapBatchDto result = populator.synchronizeLdapGroups(createSet("x*", "group2"), batchInfo);
+    LdapBatchDto result = populator.synchronizeLdapGroups(createSet("x*", "group2"), batchInfo, false);
 
     verifyGroupsInSet(result.getGroupsToBeCreated(), Sets.newHashSet("xgroup1", "xgroup2"));
     assertTrue(result.getGroupsToBeRemoved().isEmpty());
@@ -581,7 +581,7 @@ public class AmbariLdapDataPopulatorTest {
     Set<LdapGroupDto> externalGroups = createSet(externalGroup1, externalGroup2, externalGroup3, externalGroup4);
     for (LdapGroupDto externalGroup : externalGroups) {
       populator.refreshGroupMembers(eq(batchInfo), eq(externalGroup), EasyMock.anyObject(),
-          EasyMock.anyObject(), EasyMock.anyObject(), anyBoolean());
+          EasyMock.anyObject(), EasyMock.anyObject(), anyBoolean(), eq(false));
       expectLastCall();
     }
     expect(populator.getLdapGroups("group*")).andReturn(externalGroups);
@@ -590,7 +590,7 @@ public class AmbariLdapDataPopulatorTest {
     populator.setLdapTemplate(ldapTemplate);
     populator.setLdapServerProperties(ldapServerProperties);
 
-    LdapBatchDto result = populator.synchronizeLdapGroups(createSet("group*"), batchInfo);
+    LdapBatchDto result = populator.synchronizeLdapGroups(createSet("group*"), batchInfo, false);
 
     verifyGroupsInSet(result.getGroupsToBecomeLdap(), Sets.newHashSet("group1", "group4"));
     assertTrue(result.getGroupsToBeCreated().isEmpty());
@@ -660,7 +660,7 @@ public class AmbariLdapDataPopulatorTest {
     populator.setLdapTemplate(ldapTemplate);
     populator.setLdapServerProperties(ldapServerProperties);
 
-    populator.synchronizeLdapGroups(createSet("x*", "group1", "group2"), batchInfo);
+    populator.synchronizeLdapGroups(createSet("x*", "group1", "group2"), batchInfo, false);
   }
 
   @SuppressWarnings("unchecked")
@@ -716,7 +716,7 @@ public class AmbariLdapDataPopulatorTest {
     Set<LdapGroupDto> externalGroups = createSet(externalGroup1, externalGroup2, externalGroup3, externalGroup4);
     for (LdapGroupDto externalGroup : externalGroups) {
       populator.refreshGroupMembers(eq(batchInfo), eq(externalGroup), EasyMock.anyObject(),
-          EasyMock.anyObject(), EasyMock.anyObject(), anyBoolean());
+          EasyMock.anyObject(), EasyMock.anyObject(), anyBoolean(), eq(false));
       expectLastCall();
     }
 
@@ -727,7 +727,7 @@ public class AmbariLdapDataPopulatorTest {
     populator.setLdapTemplate(ldapTemplate);
     populator.setLdapServerProperties(ldapServerProperties);
 
-    LdapBatchDto result = populator.synchronizeAllLdapGroups(batchInfo);
+    LdapBatchDto result = populator.synchronizeAllLdapGroups(batchInfo, false);
 
     verifyGroupsInSet(result.getGroupsToBeRemoved(), Sets.newHashSet("group2"));
     verifyGroupsInSet(result.getGroupsToBecomeLdap(), Sets.newHashSet("group3"));
@@ -777,7 +777,7 @@ public class AmbariLdapDataPopulatorTest {
     Set<LdapGroupDto> externalGroups = createSet(externalGroup1, externalGroup2);
     for (LdapGroupDto externalGroup : externalGroups) {
       populator.refreshGroupMembers(eq(batchInfo), eq(externalGroup), EasyMock.anyObject(),
-          EasyMock.anyObject(), EasyMock.anyObject(), anyBoolean());
+          EasyMock.anyObject(), EasyMock.anyObject(), anyBoolean(), eq(false));
       expectLastCall();
     }
     expect(populator.getExternalLdapGroupInfo()).andReturn(externalGroups);
@@ -787,7 +787,7 @@ public class AmbariLdapDataPopulatorTest {
     populator.setLdapTemplate(ldapTemplate);
     populator.setLdapServerProperties(ldapServerProperties);
 
-    LdapBatchDto result = populator.synchronizeAllLdapGroups(batchInfo);
+    LdapBatchDto result = populator.synchronizeAllLdapGroups(batchInfo, false);
 
     verifyGroupsInSet(result.getGroupsToBeCreated(), Sets.newHashSet("group3", "group4"));
     assertTrue(result.getGroupsToBecomeLdap().isEmpty());
@@ -843,7 +843,7 @@ public class AmbariLdapDataPopulatorTest {
     Set<LdapGroupDto> externalGroups = createSet(externalGroup1);
     for (LdapGroupDto externalGroup : externalGroups) {
       populator.refreshGroupMembers(eq(batchInfo), eq(externalGroup), EasyMock.anyObject(),
-          EasyMock.anyObject(), EasyMock.anyObject(), anyBoolean());
+          EasyMock.anyObject(), EasyMock.anyObject(), anyBoolean(), eq(false));
       expectLastCall();
     }
     expect(populator.getExternalLdapGroupInfo()).andReturn(externalGroups);
@@ -853,7 +853,7 @@ public class AmbariLdapDataPopulatorTest {
     populator.setLdapTemplate(ldapTemplate);
     populator.setLdapServerProperties(ldapServerProperties);
 
-    LdapBatchDto result = populator.synchronizeAllLdapGroups(batchInfo);
+    LdapBatchDto result = populator.synchronizeAllLdapGroups(batchInfo, false);
 
     verifyGroupsInSet(result.getGroupsToBeRemoved(), Sets.newHashSet("group2", "group4"));
     assertTrue(result.getGroupsToBeCreated().isEmpty());
@@ -908,7 +908,7 @@ public class AmbariLdapDataPopulatorTest {
     Set<LdapGroupDto> externalGroups = createSet(externalGroup1, externalGroup2);
     for (LdapGroupDto externalGroup : externalGroups) {
       populator.refreshGroupMembers(eq(batchInfo), eq(externalGroup), EasyMock.anyObject(),
-          EasyMock.anyObject(), EasyMock.anyObject(), anyBoolean());
+          EasyMock.anyObject(), EasyMock.anyObject(), anyBoolean(), eq(false));
       expectLastCall();
     }
     expect(populator.getExternalLdapGroupInfo()).andReturn(externalGroups);
@@ -918,7 +918,7 @@ public class AmbariLdapDataPopulatorTest {
     populator.setLdapTemplate(ldapTemplate);
     populator.setLdapServerProperties(ldapServerProperties);
 
-    LdapBatchDto result = populator.synchronizeAllLdapGroups(batchInfo);
+    LdapBatchDto result = populator.synchronizeAllLdapGroups(batchInfo, false);
 
     verifyGroupsInSet(result.getGroupsToBecomeLdap(), Sets.newHashSet("group2", "group3"));
     assertTrue(result.getGroupsToBeCreated().isEmpty());
@@ -983,7 +983,7 @@ public class AmbariLdapDataPopulatorTest {
     populator.setLdapTemplate(ldapTemplate);
     populator.setLdapServerProperties(ldapServerProperties);
 
-    LdapBatchDto result = populator.synchronizeAllLdapUsers(new LdapBatchDto());
+    LdapBatchDto result = populator.synchronizeAllLdapUsers(new LdapBatchDto(), false);
 
     verifyUsersInSet(result.getUsersToBeRemoved(), Sets.newHashSet("synced_user1"));
     verifyUsersInSet(result.getUsersToBeCreated(), Sets.newHashSet("external_user1", "external_user2"));
@@ -1044,7 +1044,7 @@ public class AmbariLdapDataPopulatorTest {
     populator.setLdapTemplate(ldapTemplate);
     populator.setLdapServerProperties(ldapServerProperties);
 
-    LdapBatchDto result = populator.synchronizeAllLdapUsers(new LdapBatchDto());
+    LdapBatchDto result = populator.synchronizeAllLdapUsers(new LdapBatchDto(), false);
 
     verifyUsersInSet(result.getUsersSkipped(), Sets.newHashSet("local1", "local2"));
     assertTrue(result.getUsersToBeCreated().isEmpty());
@@ -1098,7 +1098,7 @@ public class AmbariLdapDataPopulatorTest {
     populator.setLdapTemplate(ldapTemplate);
     populator.setLdapServerProperties(ldapServerProperties);
 
-    LdapBatchDto result = populator.synchronizeAllLdapUsers(new LdapBatchDto());
+    LdapBatchDto result = populator.synchronizeAllLdapUsers(new LdapBatchDto(), false);
 
     verifyUsersInSet(result.getUsersToBeCreated(), Sets.newHashSet("user3", "user4"));
     assertTrue(result.getUsersToBecomeLdap().isEmpty());
@@ -1150,7 +1150,7 @@ public class AmbariLdapDataPopulatorTest {
     populator.setLdapTemplate(ldapTemplate);
     populator.setLdapServerProperties(ldapServerProperties);
 
-    LdapBatchDto result = populator.synchronizeAllLdapUsers(new LdapBatchDto());
+    LdapBatchDto result = populator.synchronizeAllLdapUsers(new LdapBatchDto(), false);
 
     verifyUsersInSet(result.getUsersToBeRemoved(), Sets.newHashSet("user3", "user1"));
     assertTrue(result.getUsersToBecomeLdap().isEmpty());
@@ -1210,7 +1210,7 @@ public class AmbariLdapDataPopulatorTest {
     populator.setLdapTemplate(ldapTemplate);
     populator.setLdapServerProperties(ldapServerProperties);
 
-    LdapBatchDto result = populator.synchronizeAllLdapUsers(new LdapBatchDto());
+    LdapBatchDto result = populator.synchronizeAllLdapUsers(new LdapBatchDto(), false);
 
     verifyUsersInSet(result.getUsersToBecomeLdap(), Sets.newHashSet("user3"));
     assertTrue(result.getUsersToBeRemoved().isEmpty());
@@ -1265,7 +1265,7 @@ public class AmbariLdapDataPopulatorTest {
     populator.setLdapTemplate(ldapTemplate);
     populator.setLdapServerProperties(ldapServerProperties);
 
-    LdapBatchDto result = populator.synchronizeExistingLdapUsers(new LdapBatchDto());
+    LdapBatchDto result = populator.synchronizeExistingLdapUsers(new LdapBatchDto(), false);
 
     verifyUsersInSet(result.getUsersToBeRemoved(), Sets.newHashSet("synced_user1"));
     assertTrue(result.getUsersToBeCreated().isEmpty());
@@ -1332,7 +1332,7 @@ public class AmbariLdapDataPopulatorTest {
     populator.setLdapTemplate(ldapTemplate);
     populator.setLdapServerProperties(ldapServerProperties);
 
-    LdapBatchDto result = populator.synchronizeLdapUsers(createSet("user1", "user2", "xuser*"), new LdapBatchDto());
+    LdapBatchDto result = populator.synchronizeLdapUsers(createSet("user1", "user2", "xuser*"), new LdapBatchDto(), false);
 
     verifyUsersInSet(result.getUsersToBeCreated(), Sets.newHashSet("xuser3", "xuser4"));
     verifyUsersInSet(result.getUsersToBecomeLdap(), Sets.newHashSet("user1"));
@@ -1396,7 +1396,7 @@ public class AmbariLdapDataPopulatorTest {
     populator.setLdapTemplate(ldapTemplate);
     populator.setLdapServerProperties(ldapServerProperties);
 
-    LdapBatchDto result = populator.synchronizeLdapUsers(createSet("user2", "xuser*"), new LdapBatchDto());
+    LdapBatchDto result = populator.synchronizeLdapUsers(createSet("user2", "xuser*"), new LdapBatchDto(), false);
 
     verifyUsersInSet(result.getUsersToBeCreated(), Sets.newHashSet("xuser3", "xuser4"));
     assertTrue(result.getUsersToBecomeLdap().isEmpty());
@@ -1461,7 +1461,7 @@ public class AmbariLdapDataPopulatorTest {
     populator.setLdapTemplate(ldapTemplate);
     populator.setLdapServerProperties(ldapServerProperties);
 
-    LdapBatchDto result = populator.synchronizeLdapUsers(createSet("user2", "user1", "user6"), new LdapBatchDto());
+    LdapBatchDto result = populator.synchronizeLdapUsers(createSet("user2", "user1", "user6"), new LdapBatchDto(), false);
 
     verifyUsersInSet(result.getUsersToBecomeLdap(), Sets.newHashSet("user1", "user6"));
     assertTrue(result.getUsersToBeCreated().isEmpty());
@@ -1510,7 +1510,7 @@ public class AmbariLdapDataPopulatorTest {
     populator.setLdapTemplate(ldapTemplate);
     populator.setLdapServerProperties(ldapServerProperties);
 
-    populator.synchronizeLdapUsers(createSet("user1", "user2", "xuser*"), new LdapBatchDto());
+    populator.synchronizeLdapUsers(createSet("user1", "user2", "xuser*"), new LdapBatchDto(), false);
   }
 
   @SuppressWarnings("unchecked")
@@ -1593,7 +1593,7 @@ public class AmbariLdapDataPopulatorTest {
     Map<String, Group> internalGroups = new HashMap<>();
     internalGroups.put("group2", group2);
 
-    populator.refreshGroupMembers(batchInfo, externalGroup, internalUsers, internalGroups, null, true);
+    populator.refreshGroupMembers(batchInfo, externalGroup, internalUsers, internalGroups, null, true, false);
 
     verifyMembershipInSet(batchInfo.getMembershipToAdd(), Sets.newHashSet("user1", "user2", "user6"));
     verifyMembershipInSet(batchInfo.getMembershipToRemove(), Sets.newHashSet("user3", "user4"));

+ 4 - 4
ambari-server/src/test/java/org/apache/ambari/server/security/ldap/LdapPerformanceTest.java

@@ -104,14 +104,14 @@ public class LdapPerformanceTest {
     System.out.println("Data fetch: " + (System.currentTimeMillis() - time));
     time = System.currentTimeMillis();
     LdapBatchDto batchDto = new LdapBatchDto();
-    populator.synchronizeLdapUsers(userNames, batchDto);
-    populator.synchronizeLdapGroups(groupNames, batchDto);
+    populator.synchronizeLdapUsers(userNames, batchDto, false);
+    populator.synchronizeLdapGroups(groupNames, batchDto, false);
     this.users.processLdapSync(batchDto);
     System.out.println("Initial sync: " + (System.currentTimeMillis() - time));
     time = System.currentTimeMillis();
     batchDto = new LdapBatchDto();
-    populator.synchronizeLdapUsers(userNames, batchDto);
-    populator.synchronizeLdapGroups(groupNames, batchDto);
+    populator.synchronizeLdapUsers(userNames, batchDto, false);
+    populator.synchronizeLdapGroups(groupNames, batchDto, false);
     this.users.processLdapSync(batchDto);
     System.out.println("Subsequent sync: " + (System.currentTimeMillis() - time));
   }

+ 186 - 0
ambari-server/src/test/python/TestAmbariServer.py

@@ -7769,6 +7769,7 @@ class TestAmbariServer(TestCase):
     options = self._create_empty_options_mock()
     options.ldap_sync_all = True
     options.ldap_sync_existing = False
+    options.ldap_sync_post_process_existing_users = False
 
     sync_ldap(options)
 
@@ -7782,6 +7783,49 @@ class TestAmbariServer(TestCase):
     self.assertTrue(response.read.called)
     pass
 
+  @patch("urllib2.urlopen")
+  @patch("ambari_server.setupSecurity.get_validated_string_input")
+  @patch("ambari_server.setupSecurity.get_ambari_properties")
+  @patch("ambari_server.setupSecurity.is_ldap_enabled")
+  @patch("ambari_server.setupSecurity.is_server_runing")
+  @patch("ambari_server.setupSecurity.is_root")
+  @patch("ambari_server.setupSecurity.logger")
+  def test_ldap_sync_all_post_process_existing_users(self, logger_mock, is_root_method, is_server_runing_mock, is_ldap_enabled_mock, get_ambari_properties_mock,
+      get_validated_string_input_mock, urlopen_mock):
+
+    is_root_method.return_value = True
+    is_server_runing_mock.return_value = (True, 0)
+    is_ldap_enabled_mock.return_value = 'true'
+    properties = Properties()
+    properties.process_pair(CLIENT_API_PORT_PROPERTY, '8080')
+    get_ambari_properties_mock.return_value = properties
+    get_validated_string_input_mock.side_effect = ['admin', 'admin']
+
+    response = MagicMock()
+    response.getcode.side_effect = [201, 200, 200]
+    response.read.side_effect = ['{"resources" : [{"href" : "http://c6401.ambari.apache.org:8080/api/v1/ldap_sync_events/16","Event" : {"id" : 16}}]}',
+                          '{"Event":{"status" : "RUNNING","summary" : {"groups" : {"created" : 0,"removed" : 0,"updated" : 0},"memberships" : {"created" : 0,"removed" : 0},"users" : {"created" : 0,"removed" : 0,"updated" : 0}}}}',
+                          '{"Event":{"status" : "COMPLETE","summary" : {"groups" : {"created" : 1,"removed" : 0,"updated" : 0},"memberships" : {"created" : 5,"removed" : 0},"users" : {"created" : 5,"removed" : 0,"updated" : 0}}}}']
+
+    urlopen_mock.return_value = response
+
+    options = self._create_empty_options_mock()
+    options.ldap_sync_all = True
+    options.ldap_sync_existing = False
+    options.ldap_sync_post_process_existing_users = True
+
+    sync_ldap(options)
+
+    url = '{0}://{1}:{2!s}{3}'.format('http', '127.0.0.1', '8080', '/api/v1/ldap_sync_events')
+    request = urlopen_mock.call_args_list[0][0][0]
+
+    self.assertEquals(url, str(request.get_full_url()))
+    self.assertEquals('[{"Event": {"specs": [{"post_process_existing_users": "true", "principal_type": "users", "sync_type": "all"}, {"post_process_existing_users": "true", "principal_type": "groups", "sync_type": "all"}]}}]', request.data)
+
+    self.assertTrue(response.getcode.called)
+    self.assertTrue(response.read.called)
+    pass
+
   @patch("__builtin__.open")
   @patch("os.path.exists")
   @patch("urllib2.urlopen")
@@ -7817,6 +7861,7 @@ class TestAmbariServer(TestCase):
     options.ldap_sync_existing = False
     options.ldap_sync_users = 'users.txt'
     options.ldap_sync_groups = None
+    options.ldap_sync_post_process_existing_users = False
 
     sync_ldap(options)
 
@@ -7828,6 +7873,53 @@ class TestAmbariServer(TestCase):
     self.assertTrue(response.read.called)
     pass
 
+  @patch("__builtin__.open")
+  @patch("os.path.exists")
+  @patch("urllib2.urlopen")
+  @patch("ambari_server.setupSecurity.get_validated_string_input")
+  @patch("ambari_server.setupSecurity.get_ambari_properties")
+  @patch("ambari_server.setupSecurity.is_ldap_enabled")
+  @patch("ambari_server.setupSecurity.is_server_runing")
+  @patch("ambari_server.setupSecurity.is_root")
+  @patch("ambari_server.setupSecurity.logger")
+  def test_ldap_sync_users_post_process_existing_users(self, logger_mock, is_root_method, is_server_runing_mock, is_ldap_enabled_mock, get_ambari_properties_mock,
+                         get_validated_string_input_mock, urlopen_mock, os_path_exists_mock, open_mock):
+
+    os_path_exists_mock.return_value = 1
+    f = MagicMock()
+    f.__enter__().read.return_value = "bob, tom"
+
+    open_mock.return_value = f
+    is_root_method.return_value = True
+    is_server_runing_mock.return_value = (True, 0)
+    is_ldap_enabled_mock.return_value = 'true'
+    get_validated_string_input_mock.side_effect = ['admin', 'admin']
+
+    response = MagicMock()
+    response.getcode.side_effect = [201, 200, 200]
+    response.read.side_effect = ['{"resources" : [{"href" : "http://c6401.ambari.apache.org:8080/api/v1/ldap_sync_events/16","Event" : {"id" : 16}}]}',
+                                 '{"Event":{"status" : "RUNNING","summary" : {"groups" : {"created" : 0,"removed" : 0,"updated" : 0},"memberships" : {"created" : 0,"removed" : 0},"users" : {"created" : 0,"removed" : 0,"updated" : 0}}}}',
+                                 '{"Event":{"status" : "COMPLETE","summary" : {"groups" : {"created" : 1,"removed" : 0,"updated" : 0},"memberships" : {"created" : 5,"removed" : 0},"users" : {"created" : 5,"removed" : 0,"updated" : 0}}}}']
+
+    urlopen_mock.return_value = response
+
+    options = self._create_empty_options_mock()
+    options.ldap_sync_all = False
+    options.ldap_sync_existing = False
+    options.ldap_sync_users = 'users.txt'
+    options.ldap_sync_groups = None
+    options.ldap_sync_post_process_existing_users = True
+
+    sync_ldap(options)
+
+    request = urlopen_mock.call_args_list[0][0][0]
+
+    self.assertEquals('[{"Event": {"specs": [{"post_process_existing_users": "true", "principal_type": "users", "sync_type": "specific", "names": "bob, tom"}]}}]', request.data)
+
+    self.assertTrue(response.getcode.called)
+    self.assertTrue(response.read.called)
+    pass
+
   @patch("__builtin__.open")
   @patch("os.path.exists")
   @patch("urllib2.urlopen")
@@ -7863,6 +7955,7 @@ class TestAmbariServer(TestCase):
     options.ldap_sync_existing = False
     options.ldap_sync_users = None
     options.ldap_sync_groups = 'groups.txt'
+    options.ldap_sync_post_process_existing_users = False
 
     sync_ldap(options)
 
@@ -7874,6 +7967,53 @@ class TestAmbariServer(TestCase):
     self.assertTrue(response.read.called)
     pass
 
+  @patch("__builtin__.open")
+  @patch("os.path.exists")
+  @patch("urllib2.urlopen")
+  @patch("ambari_server.setupSecurity.get_validated_string_input")
+  @patch("ambari_server.setupSecurity.get_ambari_properties")
+  @patch("ambari_server.setupSecurity.is_ldap_enabled")
+  @patch("ambari_server.setupSecurity.is_server_runing")
+  @patch("ambari_server.setupSecurity.is_root")
+  @patch("ambari_server.setupSecurity.logger")
+  def test_ldap_sync_groups_post_process_existing_users(self, logger_mock, is_root_method, is_server_runing_mock, is_ldap_enabled_mock, get_ambari_properties_mock,
+                           get_validated_string_input_mock, urlopen_mock, os_path_exists_mock, open_mock):
+
+    os_path_exists_mock.return_value = 1
+    f = MagicMock()
+    f.__enter__().read.return_value = "group1, group2"
+
+    open_mock.return_value = f
+    is_root_method.return_value = True
+    is_server_runing_mock.return_value = (True, 0)
+    is_ldap_enabled_mock.return_value = 'true'
+    get_validated_string_input_mock.side_effect = ['admin', 'admin']
+
+    response = MagicMock()
+    response.getcode.side_effect = [201, 200, 200]
+    response.read.side_effect = ['{"resources" : [{"href" : "http://c6401.ambari.apache.org:8080/api/v1/ldap_sync_events/16","Event" : {"id" : 16}}]}',
+                                 '{"Event":{"status" : "RUNNING","summary" : {"groups" : {"created" : 0,"removed" : 0,"updated" : 0},"memberships" : {"created" : 0,"removed" : 0},"users" : {"created" : 0,"removed" : 0,"updated" : 0}}}}',
+                                 '{"Event":{"status" : "COMPLETE","summary" : {"groups" : {"created" : 1,"removed" : 0,"updated" : 0},"memberships" : {"created" : 5,"removed" : 0},"users" : {"created" : 5,"removed" : 0,"updated" : 0}}}}']
+
+    urlopen_mock.return_value = response
+
+    options = self._create_empty_options_mock()
+    options.ldap_sync_all = False
+    options.ldap_sync_existing = False
+    options.ldap_sync_users = None
+    options.ldap_sync_groups = 'groups.txt'
+    options.ldap_sync_post_process_existing_users = True
+
+    sync_ldap(options)
+
+    request = urlopen_mock.call_args_list[0][0][0]
+
+    self.assertEquals('[{"Event": {"specs": [{"post_process_existing_users": "true", "principal_type": "groups", "sync_type": "specific", "names": "group1, group2"}]}}]', request.data)
+
+    self.assertTrue(response.getcode.called)
+    self.assertTrue(response.read.called)
+    pass
+
   @patch("urllib2.urlopen")
   @patch("ambari_server.setupSecurity.get_validated_string_input")
   @patch("ambari_server.setupSecurity.get_ambari_properties")
@@ -7948,9 +8088,55 @@ class TestAmbariServer(TestCase):
     options.ldap_sync_existing = True
     options.ldap_sync_users = None
     options.ldap_sync_groups = None
+    options.ldap_sync_post_process_existing_users = False
+
+    sync_ldap(options)
+
+    request = urlopen_mock.call_args_list[0][0][0]
+
+    self.assertEquals('[{"Event": {"specs": [{"principal_type": "users", "sync_type": "existing"}, {"principal_type": "groups", "sync_type": "existing"}]}}]', request.data)
+
+    self.assertTrue(response.getcode.called)
+    self.assertTrue(response.read.called)
+    pass
+
+  @patch("urllib2.urlopen")
+  @patch("ambari_server.setupSecurity.get_validated_string_input")
+  @patch("ambari_server.setupSecurity.get_ambari_properties")
+  @patch("ambari_server.setupSecurity.is_ldap_enabled")
+  @patch("ambari_server.setupSecurity.is_server_runing")
+  @patch("ambari_server.setupSecurity.is_root")
+  @patch("ambari_server.setupSecurity.logger")
+  def test_ldap_sync_existing_post_process_existing_users(self, logger_mock, is_root_method, is_server_runing_mock, is_ldap_enabled_mock, get_ambari_properties_mock,
+                         get_validated_string_input_mock, urlopen_mock):
+
+    is_root_method.return_value = True
+    is_server_runing_mock.return_value = (True, 0)
+    is_ldap_enabled_mock.return_value = 'true'
+    get_ambari_properties_mock.return_value = Properties()
+    get_validated_string_input_mock.side_effect = ['admin', 'admin']
+
+    response = MagicMock()
+    response.getcode.side_effect = [201, 200, 200]
+    response.read.side_effect = ['{"resources" : [{"href" : "http://c6401.ambari.apache.org:8080/api/v1/ldap_sync_events/16","Event" : {"id" : 16}}]}',
+                                 '{"Event":{"status" : "RUNNING","summary" : {"groups" : {"created" : 0,"removed" : 0,"updated" : 0},"memberships" : {"created" : 0,"removed" : 0},"users" : {"created" : 0,"removed" : 0,"updated" : 0}}}}',
+                                 '{"Event":{"status" : "COMPLETE","summary" : {"groups" : {"created" : 1,"removed" : 0,"updated" : 0},"memberships" : {"created" : 5,"removed" : 0},"users" : {"created" : 5,"removed" : 0,"updated" : 0}}}}']
+
+    urlopen_mock.return_value = response
+
+    options = self._create_empty_options_mock()
+    options.ldap_sync_all = False
+    options.ldap_sync_existing = True
+    options.ldap_sync_users = None
+    options.ldap_sync_groups = None
+    options.ldap_sync_post_process_existing_users = True
 
     sync_ldap(options)
 
+    request = urlopen_mock.call_args_list[0][0][0]
+
+    self.assertEquals('[{"Event": {"specs": [{"post_process_existing_users": "true", "principal_type": "users", "sync_type": "existing"}, {"post_process_existing_users": "true", "principal_type": "groups", "sync_type": "existing"}]}}]', request.data)
+
     self.assertTrue(response.getcode.called)
     self.assertTrue(response.read.called)
     pass