/* * Copyright 2000-2013 Enonic AS * http://www.enonic.com/license */ package com.enonic.cms.core.security.userstore.connector.remote; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import com.enonic.cms.api.plugin.ext.userstore.RemoteUserStore; import com.enonic.cms.core.security.group.GroupEntity; 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.UserSpecification; import com.enonic.cms.core.security.userstore.UserStoreEntity; import com.enonic.cms.core.security.userstore.UserStoreKey; 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.RemotePrincipal; import com.enonic.cms.api.plugin.ext.userstore.RemoteUser; import com.enonic.cms.store.dao.GroupDao; import com.enonic.cms.store.dao.UserDao; /** * Jun 30, 2009 */ public abstract class AbstractBaseGroupSynchronizer { protected final boolean syncMemberships; protected final boolean syncMembers; protected final UserStoreEntity userStore; protected UserDao userDao; protected GroupDao groupDao; protected RemoteUserStore remoteUserStorePlugin; protected final boolean syncGroup; protected SynchronizeStatus status = null; void setStatusCollector( final SynchronizeStatus value ) { status = value; } public AbstractBaseGroupSynchronizer( final UserStoreEntity userStore, final boolean syncGroup, final boolean syncMemberships, final boolean syncMembers ) { this.userStore = userStore; this.syncMemberships = syncMemberships; this.syncMembers = syncMembers; this.syncGroup = syncGroup; } protected UserStoreKey getUserStoreKey() { return userStore.getKey(); } protected boolean resurrectGroup( final GroupEntity group ) { final boolean resurrected = group.isDeleted(); group.setDeleted( false ); return resurrected; } protected void syncGroupMemberships( final GroupEntity localGroupToSync, final RemoteGroup remoteGroup, final MemberCache memberCache ) { final List<RemoteGroup> remoteMemberships = remoteUserStorePlugin.getMemberships( remoteGroup ); removeLocalGroupMembershipsNotExistingRemote( localGroupToSync, remoteMemberships ); for ( final RemoteGroup remoteMembership : remoteMemberships ) { syncGroupMembershipOfTypeGroup( localGroupToSync, remoteMembership, memberCache ); } } protected void syncGroupMembers( final GroupEntity localGroupToSync, final RemoteGroup remoteGroup, final MemberCache memberCache ) { // TODO: retrieval of groups in remoteUserStorePlugin.getMembers should be batched due to timeout caused by large number of members. final List<RemotePrincipal> remoteMembers = remoteUserStorePlugin.getMembers( remoteGroup ); removeLocalGroupMembersNotExistingRemote( localGroupToSync, remoteMembers ); for ( final RemotePrincipal remoteMember : remoteMembers ) { if ( remoteMember instanceof RemoteUser ) { final RemoteUser remoteUserMember = (RemoteUser) remoteMember; syncGroupMemberOfTypeUser( localGroupToSync, remoteUserMember, memberCache ); } else { final RemoteGroup remoteGroupMember = (RemoteGroup) remoteMember; syncGroupMemberOfTypeGroup( localGroupToSync, remoteGroupMember, memberCache ); } } } private void removeLocalGroupMembersNotExistingRemote( final GroupEntity localGroupToSync, final List<RemotePrincipal> remoteMembers ) { // Gather remote groups in a map for fast and easy access final Map<String, RemoteGroup> remoteMemberMapOfTypeGroup = new HashMap<String, RemoteGroup>(); final Map<String, RemoteUser> remoteMemberMapOfTypeUser = new HashMap<String, RemoteUser>(); for ( final RemotePrincipal remoteMember : remoteMembers ) { if ( remoteMember instanceof RemoteUser ) { final RemoteUser remoteMemberOfTypeUser = (RemoteUser) remoteMember; remoteMemberMapOfTypeUser.put( remoteMemberOfTypeUser.getId() + "-" + remoteMemberOfTypeUser.getSync(), remoteMemberOfTypeUser ); } else { final RemoteGroup remoteMemberOfTypeGroup = (RemoteGroup) remoteMember; remoteMemberMapOfTypeGroup.put( remoteMemberOfTypeGroup.getId() + "-" + remoteMemberOfTypeGroup.getSync(), remoteMemberOfTypeGroup ); } } // Gather local members that does not exist remote final Set<GroupEntity> localMembersToRemove = new HashSet<GroupEntity>(); for ( final GroupEntity localMember : localGroupToSync.getMembers( false ) ) { if ( localMember.isOfType( GroupType.USER, false ) ) { final boolean notExistsRemote = !remoteMemberMapOfTypeUser.containsKey( localMember.getUser().getName() + "-" + localMember.getSyncValue() ); if ( notExistsRemote ) { localMembersToRemove.add( localMember ); } } else { final boolean notExistsRemote = !remoteMemberMapOfTypeGroup.containsKey( localMember.getName() + "-" + localMember.getSyncValue() ); if ( notExistsRemote ) { localMembersToRemove.add( localMember ); } } } // Remove local members that does not exist remote for ( final GroupEntity localMemberToRemove : localMembersToRemove ) { localMemberToRemove.removeMembership( localGroupToSync ); } } private void syncGroupMemberOfTypeUser( final GroupEntity localGroup, final RemoteUser remoteUserMember, final MemberCache memberCache ) { final UserSpecification spec = new UserSpecification(); spec.setUserStoreKey( getUserStoreKey() ); spec.setSyncValue( remoteUserMember.getSync() ); spec.setDeletedState( UserSpecification.DeletedState.ANY ); UserEntity existingMember = memberCache.getMemberOfTypeUser( spec ); if ( existingMember == null ) { existingMember = userDao.findSingleBySpecification( spec ); if ( existingMember != null ) { memberCache.addMemeberOfTypeUser( existingMember ); } } if ( existingMember == null ) { // skip creation - only supported in full sync } else { final GroupEntity existingUserGroup = existingMember.getUserGroup(); if ( localGroup.hasMember( existingUserGroup ) ) { // all is fine } else { existingUserGroup.addMembership( localGroup ); } } } private void syncGroupMemberOfTypeGroup( final GroupEntity localGroup, final RemoteGroup remoteGroupMember, final MemberCache memberCache ) { GroupEntity existingMember = findGroupBySyncValue( remoteGroupMember.getSync(), memberCache ); if ( existingMember == null ) { // skip creation - only supported in full sync } else { if ( localGroup.hasMember( existingMember ) ) { // all is fine } else { existingMember.addMembership( localGroup ); } } } private void syncGroupMembershipOfTypeGroup( final GroupEntity localGroup, final RemoteGroup remoteGroupMember, final MemberCache memberCache ) { GroupEntity existingMember = findGroupBySyncValue( remoteGroupMember.getSync(), memberCache ); if ( existingMember == null ) { // skip creation - only supported in full sync } else { if ( localGroup.hasMembership( localGroup ) ) { // all is fine if ( status != null ) { status.groupMembershipVerified(); } } else { localGroup.addMembership( existingMember ); if ( status != null ) { status.groupMembershipCreated(); } } } } protected void removeLocalGroupMembershipsNotExistingRemote( final GroupEntity localGroup, final List<RemoteGroup> remoteMemberships ) { // Gather remote users in a map for fast and easy access final Map<String, RemoteGroup> remoteMembershipsMap = new HashMap<String, RemoteGroup>(); for ( final RemoteGroup remoteMembership : remoteMemberships ) { remoteMembershipsMap.put( remoteMembership.getId() + "-" + remoteMembership.getSync(), remoteMembership ); } // Gather local memberships that does not exist remote final Set<GroupEntity> localMembershipsToRemove = new HashSet<GroupEntity>(); for ( final GroupEntity localMembership : localGroup.getMemberships( false ) ) { // We're not removing memberships in built-in or global groups... if ( !localMembership.isBuiltIn() && !localMembership.isGlobal() ) { final RemoteGroup remoteMembership = remoteMembershipsMap.get( localMembership.getName() + "-" + localMembership.getSyncValue() ); if ( remoteMembership == null ) { localMembershipsToRemove.add( localMembership ); } } } // Remove local memberships that does not exist remote for ( final GroupEntity localMembershipToRemove : localMembershipsToRemove ) { localGroup.removeMembership( localMembershipToRemove ); if ( status != null ) { status.groupMembershipDeleted(); } } } private GroupEntity findGroupBySyncValue( final String syncValue, final MemberCache memberCache ) { final GroupSpecification spec = new GroupSpecification(); spec.setUserStoreKey( getUserStoreKey() ); spec.setSyncValue( syncValue ); GroupEntity existingMember = memberCache.getMemberOfTypeGroup( spec ); if ( existingMember == null ) { existingMember = groupDao.findSingleBySpecification( spec ); if ( existingMember != null ) { memberCache.addMemeberOfTypeGroup( existingMember ); } } return existingMember; } public void setGroupDao( final GroupDao value ) { this.groupDao = value; } public void setUserDao( final UserDao value ) { this.userDao = value; } public void setRemoteUserStorePlugin( final RemoteUserStore value ) { this.remoteUserStorePlugin = value; } }