/*
* Copyright 2000-2013 Enonic AS
* http://www.enonic.com/license
*/
package com.enonic.cms.core.security.userstore;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.springframework.util.Assert;
import com.google.common.base.Preconditions;
import com.enonic.cms.core.security.group.GroupEntity;
import com.enonic.cms.core.security.group.GroupFactory;
import com.enonic.cms.core.security.group.GroupKey;
import com.enonic.cms.core.security.group.GroupType;
import com.enonic.cms.core.security.user.DisplayNameResolver;
import com.enonic.cms.core.security.user.StoreNewUserCommand;
import com.enonic.cms.core.security.user.UpdateUserCommand;
import com.enonic.cms.core.security.user.UserEntity;
import com.enonic.cms.core.security.user.UserKey;
import com.enonic.cms.core.security.user.UserNotFoundException;
import com.enonic.cms.core.security.user.UserSpecification;
import com.enonic.cms.core.security.user.UserType;
import com.enonic.cms.core.security.userstore.connector.UserAlreadyExistsException;
import com.enonic.cms.core.time.TimeService;
import com.enonic.cms.api.plugin.ext.userstore.UserFields;
import com.enonic.cms.store.dao.CategoryAccessDao;
import com.enonic.cms.store.dao.ContentAccessDao;
import com.enonic.cms.store.dao.DefaultSiteAccessDao;
import com.enonic.cms.store.dao.GroupDao;
import com.enonic.cms.store.dao.MenuItemAccessDao;
import com.enonic.cms.store.dao.RememberedLoginDao;
import com.enonic.cms.store.dao.SiteDao;
import com.enonic.cms.store.dao.UserDao;
public class UserStorer
{
private GroupDao groupDao;
private UserDao userDao;
private TimeService timeService;
private MenuItemAccessDao menuItemAccessDao;
private CategoryAccessDao categoryAccessDao;
private ContentAccessDao contentAccessDao;
private DefaultSiteAccessDao defaultSiteAccessDao;
private RememberedLoginDao rememberedLoginDao;
private SiteDao siteDao;
private boolean resurrectDeletedUsers;
private UserStoreEntity userStore;
public UserKey storeNewUser( StoreNewUserCommand command, DisplayNameResolver displayNameResolver )
{
checkIfUserAlreadyExistsUndeleted( command );
if ( userAlreadyExistsDeleted( command ) )
{
if ( resurrectDeletedUsers )
{
return resurrectDeletedUser( command );
}
else if ( userStore.isRemote() )
{
makeExistingDeletedUsersHaveNonRepeatableSyncValues( command );
}
}
String displayName = command.getDisplayName();
if ( command.getDisplayName() == null || !displayNameManuallyEdited( displayNameResolver, command ) )
{
displayName =
displayNameResolver.resolveDisplayName( command.getUsername(), command.getDisplayName(), command.getUserFields() );
}
final UserEntity newUser = new UserEntity();
newUser.setDeleted( 0 );
newUser.setUserStore( userStore );
newUser.setName( command.getUsername() );
newUser.setSyncValue( command.getSyncValue() == null ? "NA" : command.getSyncValue() );
newUser.setEmail( command.getEmail() );
newUser.setType( command.getType() );
newUser.setTimestamp( timeService.getNowAsDateTime() );
newUser.setDisplayName( displayName );
newUser.encodePassword( command.getPassword() );
newUser.setUserFields( command.getUserFields() );
userDao.storeNew( newUser );
if ( !newUser.isInRemoteUserStore() )
{
Assert.notNull( newUser.getKey() );
newUser.setSyncValue( newUser.getKey().toString() );
}
if ( command.getType() == UserType.ANONYMOUS )
{
final GroupEntity anonymousUserGroup = groupDao.findSingleByGroupType( GroupType.ANONYMOUS );
anonymousUserGroup.setUser( newUser );
newUser.setUserGroup( anonymousUserGroup );
}
else if ( command.getType() == UserType.ADMINISTRATOR )
{
final GroupEntity eaUserGroup = groupDao.findSingleByGroupType( GroupType.ENTERPRISE_ADMINS );
eaUserGroup.setUser( newUser );
newUser.setUserGroup( eaUserGroup );
}
else
{
final GroupEntity newUserGroup = GroupFactory.createUserGroup( newUser );
groupDao.storeNew( newUserGroup );
newUser.setUserGroup( newUserGroup );
if ( command.getMemberships() != null )
{
addMemberships( newUser, command.getMemberships() );
}
}
return newUser.getKey();
}
public void updateUser( final UpdateUserCommand command )
{
UserEntity userToUpdate = userDao.findSingleBySpecification( command.getSpecification() );
if ( userToUpdate == null )
{
throw new UserNotFoundException( command.getSpecification() );
}
boolean modified = updateUserModifyableValues( command, userToUpdate );
if ( modified )
{
userToUpdate.setTimestamp( timeService.getNowAsDateTime() );
}
if ( command.syncMemberships() )
{
syncMemberships( userToUpdate, command.getMemberships() );
}
}
public void changePassword( final String uid, final String newPassword )
{
final UserEntity user = userDao.findByUserStoreKeyAndUsername( userStore.getKey(), uid );
user.encodePassword( newPassword );
}
public void deleteUser( final UserSpecification userSpec )
{
final UserEntity userToDelete = userDao.findSingleBySpecification( userSpec );
if ( userToDelete == null )
{
return;
}
Preconditions.checkArgument( !userToDelete.isBuiltIn(), "Cannot delete a built-in user" );
userToDelete.setDeleted( true );
userToDelete.setTimestamp( timeService.getNowAsDateTime() );
rememberedLoginDao.removeUsage( userToDelete.getKey() );
final GroupEntity userGroup = userToDelete.getUserGroup();
if ( userGroup != null )
{
userGroup.setDeleted( true );
final GroupKey groupKey = userGroup.getGroupKey();
defaultSiteAccessDao.deleteByGroupKey( groupKey );
menuItemAccessDao.deleteByGroupKey( groupKey );
contentAccessDao.deleteByGroupKey( groupKey );
categoryAccessDao.deleteByGroupKey( groupKey );
}
}
private UserKey resurrectDeletedUser( final StoreNewUserCommand command )
{
Preconditions.checkArgument( userStore.isRemote(), "Only resurrection of users in remote userStores is expected" );
final UserSpecification existingUserToResurrectSpec = new UserSpecification();
existingUserToResurrectSpec.setUserStoreKey( userStore.getKey() );
existingUserToResurrectSpec.setDeletedState( UserSpecification.DeletedState.DELETED );
existingUserToResurrectSpec.setSyncValue( command.getSyncValue() );
final List<UserEntity> users = userDao.findBySpecification( existingUserToResurrectSpec );
final UserEntity userToResurrect = geUserWithLatestTimestamp( users );
userToResurrect.setDeleted( false );
userToResurrect.setDisplayName( command.getDisplayName() );
userToResurrect.setEmail( command.getEmail() );
userToResurrect.setUserFields( command.getUserFields() );
userToResurrect.setTimestamp( timeService.getNowAsDateTime() );
final GroupEntity userGroup = userToResurrect.getUserGroup();
userGroup.setDeleted( false );
syncMemberships( userToResurrect, command.getMemberships() );
return userToResurrect.getKey();
}
private void syncMemberships( final UserEntity user, final Collection<GroupKey> expectedMemberships )
{
Preconditions.checkNotNull( user );
Preconditions.checkNotNull( expectedMemberships );
addMemberships( user, expectedMemberships );
removeMembershipsNotExistingInCollection( user, expectedMemberships );
}
private void addMemberships( final UserEntity user, final Iterable<GroupKey> memberships )
{
Preconditions.checkNotNull( user );
Preconditions.checkNotNull( memberships );
final GroupEntity userGroup = user.getUserGroup();
for ( GroupKey groupKey : memberships )
{
final GroupEntity groupToBeMemberOf = groupDao.find( groupKey.toString() );
if ( groupToBeMemberOf == null )
{
continue;
}
final boolean membershipAlreadyExist = userGroup.isMemberOf( groupToBeMemberOf, false );
if ( membershipAlreadyExist )
{
continue;
}
if ( groupToBeMemberOf.isInUserStore() && !groupToBeMemberOf.getUserStore().equals( userStore ) )
{
throw new IllegalArgumentException(
userGroup.getUser().getQualifiedName() + " cannot be member of group " + groupToBeMemberOf.getQualifiedName() +
". The user is not located in the same userStore as the group." );
}
userGroup.addMembership( groupToBeMemberOf );
}
}
private void removeMembershipsNotExistingInCollection( final UserEntity user, final Collection<GroupKey> expectedMemberships )
{
Preconditions.checkNotNull( user );
Preconditions.checkNotNull( expectedMemberships );
final GroupEntity userGroup = user.getUserGroup();
final List<GroupEntity> groupsToRemove = new ArrayList<GroupEntity>();
for ( GroupEntity existingMembership : userGroup.getMemberships( false ) )
{
final boolean removeThisGroup = !expectedMemberships.contains( existingMembership.getGroupKey() );
if ( removeThisGroup )
{
groupsToRemove.add( existingMembership );
}
}
for ( GroupEntity groupToRemove : groupsToRemove )
{
userGroup.removeMembership( groupToRemove );
}
}
private boolean updateUserModifyableValues( UpdateUserCommand command, UserEntity userToUpdate )
{
final boolean isUpdateStrategy = command.isUpdateStrategy();
boolean modified = false;
final String displayName = command.getDisplayName();
if ( displayName != null || isUpdateStrategy )
{
modified = !equals( userToUpdate.getDisplayName(), displayName );
userToUpdate.setDisplayName( displayName );
}
final String email = command.getEmail();
if ( email != null || isUpdateStrategy )
{
modified = modified || !equals( userToUpdate.getEmail(), email );
userToUpdate.setEmail( email );
}
UserFields userFields = command.getUserFields();
if ( userFields == null && isUpdateStrategy )
{
userFields = new UserFields();
}
if ( userFields != null )
{
boolean userInfoModified;
if ( isUpdateStrategy )
{
userInfoModified = userToUpdate.setUserFields( userFields );
}
else
{
userInfoModified = userToUpdate.overwriteUserFields( userFields );
}
modified = modified || userInfoModified;
}
return modified;
}
private boolean equals( Object a, Object b )
{
if ( a == null && b == null )
{
return true;
}
else if ( a == null || b == null )
{
return false;
}
return a.equals( b );
}
private boolean displayNameManuallyEdited( final DisplayNameResolver displayNameResolver, StoreNewUserCommand command )
{
final String displayNameGeneratedFromExistingUser =
displayNameResolver.resolveDisplayName( command.getUsername(), command.getDisplayName(), command.getUserFields() );
return !displayNameGeneratedFromExistingUser.equals( command.getDisplayName() );
}
private void checkIfUserAlreadyExistsUndeleted( final StoreNewUserCommand command )
throws UserAlreadyExistsException
{
final UserSpecification userSpec = new UserSpecification();
userSpec.setUserStoreKey( command.getUserStoreKey() );
userSpec.setDeletedState( UserSpecification.DeletedState.NOT_DELETED );
if ( userStore.isLocal() )
{
userSpec.setName( command.getUsername() );
}
else
{
userSpec.setSyncValue( command.getSyncValue() );
}
final List<UserEntity> users = userDao.findBySpecification( userSpec );
if ( !users.isEmpty() )
{
throw new UserAlreadyExistsException( userStore.getName(), command.getUsername() );
}
}
private boolean userAlreadyExistsDeleted( final StoreNewUserCommand command )
{
final UserSpecification userSpec = new UserSpecification();
userSpec.setUserStoreKey( command.getUserStoreKey() );
userSpec.setDeletedState( UserSpecification.DeletedState.DELETED );
if ( userStore.isLocal() )
{
userSpec.setName( command.getUsername() );
}
else
{
userSpec.setSyncValue( command.getSyncValue() );
}
final List<UserEntity> users = userDao.findBySpecification( userSpec );
return !users.isEmpty();
}
private void makeExistingDeletedUsersHaveNonRepeatableSyncValues( final StoreNewUserCommand command )
{
Preconditions.checkArgument( userStore.isRemote(),
"No use in making sync values for users stored in local userStores non-repeatable." );
final UserSpecification userSpec = new UserSpecification();
userSpec.setUserStoreKey( userStore.getKey() );
userSpec.setSyncValue( command.getSyncValue() );
userSpec.setDeletedState( UserSpecification.DeletedState.DELETED );
final List<UserEntity> users = userDao.findBySpecification( userSpec );
final NonRepeatableSyncValueResolver nonRepeatableSyncValueResolver = new NonRepeatableSyncValueResolver( timeService );
for ( UserEntity user : users )
{
user.setSyncValue( nonRepeatableSyncValueResolver.resolve( user.getSync() ) );
}
}
private UserEntity geUserWithLatestTimestamp( final Iterable<UserEntity> it )
{
UserEntity latestUpdatedUser = null;
for ( UserEntity user : it )
{
if ( latestUpdatedUser == null )
{
latestUpdatedUser = user;
}
else if ( user.getTimestamp().isAfter( latestUpdatedUser.getTimestamp() ) )
{
latestUpdatedUser = user;
}
}
return latestUpdatedUser;
}
public void setGroupDao( GroupDao groupDao )
{
this.groupDao = groupDao;
}
public void setUserDao( UserDao userDao )
{
this.userDao = userDao;
}
public void setTimeService( TimeService timeService )
{
this.timeService = timeService;
}
public void setMenuItemAccessDao( MenuItemAccessDao menuItemAccessDao )
{
this.menuItemAccessDao = menuItemAccessDao;
}
public void setCategoryAccessDao( CategoryAccessDao categoryAccessDao )
{
this.categoryAccessDao = categoryAccessDao;
}
public void setContentAccessDao( ContentAccessDao contentAccessDao )
{
this.contentAccessDao = contentAccessDao;
}
public void setDefaultSiteAccessDao( DefaultSiteAccessDao defaultSiteAccessDao )
{
this.defaultSiteAccessDao = defaultSiteAccessDao;
}
public void setRememberedLoginDao( RememberedLoginDao rememberedLoginDao )
{
this.rememberedLoginDao = rememberedLoginDao;
}
public void setSiteDao( SiteDao siteDao )
{
this.siteDao = siteDao;
}
public void setUserStore( UserStoreEntity userStore )
{
this.userStore = userStore;
}
public void setResurrectDeletedUsers( boolean resurrectDeletedUsers )
{
this.resurrectDeletedUsers = resurrectDeletedUsers;
}
}