/* * Copyright 2000-2013 Enonic AS * http://www.enonic.com/license */ package com.enonic.cms.core.security.userstore.connector.synchronize; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Set; import com.google.common.collect.HashMultimap; import com.google.common.collect.Multimap; import com.enonic.cms.framework.util.BatchedList; import com.enonic.cms.core.security.group.GroupEntity; import com.enonic.cms.core.security.group.GroupKey; import com.enonic.cms.core.security.group.GroupSpecification; import com.enonic.cms.core.security.group.GroupType; import com.enonic.cms.core.security.user.UserEntity; import com.enonic.cms.core.security.user.UserKey; import com.enonic.cms.core.security.userstore.UserStoreKey; import com.enonic.cms.core.security.userstore.UserStoreService; import com.enonic.cms.core.security.userstore.connector.remote.MemberCache; import com.enonic.cms.core.security.userstore.connector.remote.RemoteUserStoreConnector; import com.enonic.cms.core.security.userstore.connector.synchronize.status.SynchronizeStatus; import com.enonic.cms.api.plugin.ext.userstore.RemoteGroup; import com.enonic.cms.api.plugin.ext.userstore.RemoteUser; public class SynchronizeUserStoreJobImpl implements SynchronizeUserStoreJob { private final UserStoreKey userStoreKey; private final SynchronizeUserStoreType type; private final int batchSize; private UserStoreService userStoreService; private RemoteUserStoreConnector userStoreConnector; private MemberCache memberCache; private SynchronizeStatus status; public SynchronizeUserStoreJobImpl( final UserStoreKey userStoreKey, final SynchronizeUserStoreType type, final int batchSize ) { this.userStoreKey = userStoreKey; this.type = type; this.batchSize = batchSize; this.memberCache = new MemberCache(); this.status = new SynchronizeStatus( type ); } public void start() { try { doStart(); } finally { status.setCompleted(); } } private void doStart() { switch ( type ) { case USERS_ONLY: { final List<RemoteUser> remoteUsers = userStoreConnector.getAllUsers(); status.setTotalRemoteUserCount( remoteUsers.size() ); synchronizeAllUsers( remoteUsers, false ); synchronizeAllUserMemberships( remoteUsers ); break; } case GROUPS_ONLY: { final List<RemoteGroup> remoteGroups = userStoreConnector.getAllGroups(); status.setTotalRemoteGroupCount( remoteGroups.size() ); synchronizeAllGroups( remoteGroups, false, false ); synchronizeAllGroupMemberships( remoteGroups ); break; } case USERS_AND_GROUPS: { final List<RemoteUser> remoteUsers = userStoreConnector.getAllUsers(); final List<RemoteGroup> remoteGroups = userStoreConnector.getAllGroups(); status.setTotalRemoteUserCount( remoteUsers.size() ); status.setTotalRemoteGroupCount( remoteGroups.size() ); synchronizeAllUsers( remoteUsers, false ); synchronizeAllGroups( remoteGroups, false, false ); synchronizeAllUserMemberships( remoteUsers ); synchronizeAllGroupMemberships( remoteGroups ); break; } } } private void synchronizeAllUsers( final List<RemoteUser> remoteUsers, final boolean syncMemberships ) { Set<String> usedUserNames = new HashSet<String>(); for ( int i = 0; i < remoteUsers.size(); i++ ) { RemoteUser remoteUser = remoteUsers.get( i ); boolean existingUserName = !usedUserNames.add( remoteUser.getId().toLowerCase() ); if ( existingUserName ) { status.userSkipped(); remoteUsers.remove( i ); --i; } } final BatchedList<RemoteUser> batchedList = new BatchedList<RemoteUser>( remoteUsers, batchSize ); while ( batchedList.hasMoreBatches() ) { userStoreService.synchronizeUsers( status, userStoreKey, batchedList.getNextBatch(), syncMemberships, memberCache ); } final Multimap<String, UserEntity> allUsersMapByName = userStoreService.getUsersAsMapByName( userStoreKey ); final List<UserKey> allUsersToDelete = resolveUsersToDelete( allUsersMapByName, remoteUsers ); status.setTotalLocalUserCount( allUsersToDelete.size() ); final BatchedList<UserKey> usersToDeleteAsBatchedList = new BatchedList<UserKey>( allUsersToDelete, batchSize ); while ( usersToDeleteAsBatchedList.hasMoreBatches() ) { userStoreService.deleteUsersLocally( userStoreKey, status.getLocalUsersStatus(), usersToDeleteAsBatchedList.getNextBatch() ); } } private List<UserKey> resolveUsersToDelete( final Multimap<String, UserEntity> usersMapByName, final Collection<RemoteUser> remoteUsers ) { // Remove all remote users from usersMapByName , so that we are only left with users not existing remote. final List<UserEntity> usersToRemoveFromMap = new ArrayList<UserEntity>(); for ( final RemoteUser remoteUser : remoteUsers ) { final Collection<UserEntity> candidates = usersMapByName.get( remoteUser.getId() ); for ( final UserEntity candidate : candidates ) { if ( candidate != null && remoteUser.getSync().equals( candidate.getSync() ) ) { usersToRemoveFromMap.add( candidate ); } } } for ( final UserEntity userToRemoveFromMap : usersToRemoveFromMap ) { usersMapByName.remove( userToRemoveFromMap.getName(), userToRemoveFromMap ); } final List<UserKey> usersToDelete = new ArrayList<UserKey>(); for ( final UserEntity userToDelete : usersMapByName.values() ) { usersToDelete.add( userToDelete.getKey() ); } return usersToDelete; } private void synchronizeAllGroups( final List<RemoteGroup> remoteGroups, final boolean syncMemberships, final boolean syncMembers ) { final BatchedList<RemoteGroup> batchedList = new BatchedList<RemoteGroup>( remoteGroups, batchSize ); while ( batchedList.hasMoreBatches() ) { userStoreService.synchronizeGroups( status, userStoreKey, batchedList.getNextBatch(), syncMemberships, syncMembers, memberCache ); } final Multimap<String, GroupEntity> allUsersMapByUid = getGroupsAsMapByName( userStoreKey ); final List<GroupKey> allGroupsToDelete = resolveGroupsToDelete( allUsersMapByUid, remoteGroups ); status.setTotalLocalGroupCount( allGroupsToDelete.size() ); final BatchedList<GroupKey> groupsToDeleteAsBatchedList = new BatchedList<GroupKey>( allGroupsToDelete, batchSize ); while ( groupsToDeleteAsBatchedList.hasMoreBatches() ) { userStoreService.deleteGroupsLocally( status.getLocalGroupsStatus(), userStoreKey, groupsToDeleteAsBatchedList.getNextBatch() ); } } private Multimap<String, GroupEntity> getGroupsAsMapByName( final UserStoreKey userStoreKey ) { final GroupSpecification groupSpec = new GroupSpecification(); groupSpec.setUserStoreKey( userStoreKey ); groupSpec.setDeletedState( GroupSpecification.DeletedState.NOT_DELETED ); groupSpec.setType( GroupType.USERSTORE_GROUP ); final List<GroupEntity> groups = userStoreService.getGroups( groupSpec ); final Multimap<String, GroupEntity> groupMapByName = HashMultimap.create(); for ( final GroupEntity group : groups ) { groupMapByName.put( group.getName(), group ); } return groupMapByName; } private List<GroupKey> resolveGroupsToDelete( final Multimap<String, GroupEntity> groupsMapByName, final Collection<RemoteGroup> remoteGroups ) { // Remove all remote groups from groupsMapByName, so that we are only left with groups not existing remote. final List<GroupEntity> groupsToRemoveFromMap = new ArrayList<GroupEntity>(); for ( final RemoteGroup remoteGroup : remoteGroups ) { final Collection<GroupEntity> candidates = groupsMapByName.get( remoteGroup.getId() ); for ( final GroupEntity candidate : candidates ) { if ( candidate != null && remoteGroup.getSync().equals( candidate.getSyncValue() ) ) { groupsToRemoveFromMap.add( candidate ); } } } for ( final GroupEntity groupToRemoveFromMap : groupsToRemoveFromMap ) { groupsMapByName.remove( groupToRemoveFromMap.getName(), groupToRemoveFromMap ); } final List<GroupKey> groupsToDelete = new ArrayList<GroupKey>(); for ( final GroupEntity groupToDelete : groupsMapByName.values() ) { groupsToDelete.add( groupToDelete.getGroupKey() ); } return groupsToDelete; } private void synchronizeAllUserMemberships( final List<RemoteUser> remoteUsers ) { status.setTotalUserMembershipsCount( remoteUsers.size() ); for ( final RemoteUser remoteUser : remoteUsers ) { userStoreService.synchronizeUserMemberships( status, userStoreKey, remoteUser, memberCache ); status.nextUserMemberships(); } } private void synchronizeAllGroupMemberships( final List<RemoteGroup> remoteGroups ) { status.setTotalGroupMembershipsCount( remoteGroups.size() ); for ( final RemoteGroup remoteGroup : remoteGroups ) { userStoreService.synchronizeGroupMemberships( status, userStoreKey, remoteGroup, memberCache ); status.nextGroupMemberships(); } } public SynchronizeStatus getStatus() { return status; } public void setUserStoreService( final UserStoreService value ) { userStoreService = value; } public void setUserStoreConnector( final RemoteUserStoreConnector value ) { userStoreConnector = value; } }