/*
* Copyright 2000-2013 Enonic AS
* http://www.enonic.com/license
*/
package com.enonic.cms.core.security.userstore;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import javax.annotation.PostConstruct;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import com.google.common.base.Preconditions;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
import com.enonic.cms.api.plugin.ext.userstore.RemoteGroup;
import com.enonic.cms.api.plugin.ext.userstore.RemoteUser;
import com.enonic.cms.api.plugin.ext.userstore.RemoteUserStore;
import com.enonic.cms.api.plugin.ext.userstore.UserField;
import com.enonic.cms.api.plugin.ext.userstore.UserFieldType;
import com.enonic.cms.api.plugin.ext.userstore.UserFields;
import com.enonic.cms.api.plugin.ext.userstore.UserStoreConfig;
import com.enonic.cms.api.plugin.ext.userstore.UserStoreConfigField;
import com.enonic.cms.core.plugin.ext.AuthenticatorExtensions;
import com.enonic.cms.core.security.group.AddMembershipsCommand;
import com.enonic.cms.core.security.group.CreateGroupAccessException;
import com.enonic.cms.core.security.group.DeleteGroupAccessException;
import com.enonic.cms.core.security.group.DeleteGroupCommand;
import com.enonic.cms.core.security.group.GroupAccessResolver;
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.group.RemoveMembershipsCommand;
import com.enonic.cms.core.security.group.StoreNewGroupCommand;
import com.enonic.cms.core.security.group.UpdateGroupAccessException;
import com.enonic.cms.core.security.group.UpdateGroupCommand;
import com.enonic.cms.core.security.user.DeleteUserCommand;
import com.enonic.cms.core.security.user.DeleteUserStoreCommand;
import com.enonic.cms.core.security.user.ReadOnlyUserFieldValidator;
import com.enonic.cms.core.security.user.RequiredUserFieldsValidator;
import com.enonic.cms.core.security.user.StoreNewUserCommand;
import com.enonic.cms.core.security.user.UpdateUserCommand;
import com.enonic.cms.core.security.user.User;
import com.enonic.cms.core.security.user.UserEntity;
import com.enonic.cms.core.security.user.UserImpl;
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.UserStorageExistingEmailException;
import com.enonic.cms.core.security.user.UserStorageInvalidArgumentException;
import com.enonic.cms.core.security.userstore.config.InvalidUserStoreConfigException;
import com.enonic.cms.core.security.userstore.config.UserStoreConfigParser;
import com.enonic.cms.core.security.userstore.connector.AuthenticationChain;
import com.enonic.cms.core.security.userstore.connector.UserStoreConnector;
import com.enonic.cms.core.security.userstore.connector.config.UserStoreConnectorConfig;
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.remote.RemoteUserStoreManager;
import com.enonic.cms.core.security.userstore.connector.synchronize.status.SynchronizeStatus;
import com.enonic.cms.core.security.userstore.status.LocalGroupsStatus;
import com.enonic.cms.core.security.userstore.status.LocalUsersStatus;
import com.enonic.cms.store.dao.GroupDao;
import com.enonic.cms.store.dao.UserDao;
import com.enonic.cms.store.dao.UserStoreDao;
@Service("userStoreService")
public class UserStoreServiceImpl
implements UserStoreService
{
@Autowired
private UserStoreConnectorManager userConnectorStoreManager;
@Autowired
private UserStoreDao userStoreDao;
@Autowired
private UserDao userDao;
@Autowired
private GroupDao groupDao;
@Value("${cms.admin.email}")
private String adminUserEmail;
@Autowired
private GroupStorerFactory groupStorerFactory;
@Autowired
private UserStorerFactory userStorerFactory;
@Autowired
private UserStoreAccessResolver userStoreAccessResolver;
@Autowired
private GroupAccessResolver groupAccessResolver;
@Autowired
private RemoteUserStoreManager remoteUserStoreFactory;
@Autowired
private AuthenticatorExtensions authenticationInterceptors;
public static UserStoreService INSTANCE;
@PostConstruct
public void initializeStaticReference()
{
INSTANCE = this;
}
private static final String VALID_EMAIL_PATTERN =
"^([a-zA-Z0-9_\\-\\.]+)@((\\[[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.)|(([a-zA-Z0-9\\-]+\\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\\]?)$";
public UserStoreEntity getUserStore( final UserStoreKey userStoreKey )
{
return userStoreDao.findByKey( userStoreKey );
}
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public UserKey storeNewUser( final StoreNewUserCommand command )
{
final UserSpecification storerSpec = new UserSpecification();
storerSpec.setKey( command.getStorer() );
storerSpec.setDeletedStateNotDeleted();
final UserEntity storer = userDao.findSingleBySpecification( storerSpec );
final UserStoreEntity userStore = userStoreDao.findByKey( command.getUserStoreKey() );
if ( !userStoreAccessResolver.hasCreateUserAccess( storer, userStore ) && !command.allowAnyUserAccess() )
{
throw new UserStoreAccessException( UserStoreAccessType.CREATE_USER, storer.getQualifiedName(), command.getUsername() );
}
final UserStoreConnector usc = doGetUSConnector( command.getUserStoreKey() );
verifyMandatoryFieldsForCreate( command );
new RequiredUserFieldsValidator( userStore.getConfig() ).validateAllRequiredFieldsArePresentAndNotEmpty( command.getUserFields() );
verifyUniqueEmailForCreate( command );
new ReadOnlyUserFieldValidator( userStore.getConfig() ).validate( command.getUserFields() );
return usc.storeNewUser( command );
}
private void verifyMandatoryFieldsForCreate( StoreNewUserCommand command )
{
String email = command.getEmail();
if ( !isValidEmailAddress( email ) )
{
throw new UserStorageInvalidArgumentException( "email" );
}
boolean hasUserName = StringUtils.isNotBlank( command.getUsername() );
boolean hasDisplayName = StringUtils.isNotBlank( command.getDisplayName() );
boolean hasFirstName = StringUtils.isNotBlank( command.getUserFields().getFirstName() );
boolean hasLastName = StringUtils.isNotBlank( command.getUserFields().getLastName() );
if ( !hasUserName && !hasDisplayName && !hasFirstName && !hasLastName )
{
String[] oneOfRequiredArguments = new String[]{"user_name", "display_name", "first_name", "last_name"};
throw new UserStorageInvalidArgumentException( Arrays.asList( oneOfRequiredArguments ),
"Invalid arguments in storage operation, missing one of the following arguments: " );
}
}
public void verifyUniqueEmailAddress( String email, UserStoreKey userStoreKey )
{
doVerifyUniqueEmailAdress( email, userStoreKey );
}
private void doVerifyUniqueEmailAdress( String email, UserStoreKey userStoreKey )
{
if ( StringUtils.isEmpty( email ) )
{
return;
}
UserStoreEntity userStore = userStoreDao.findByKey( userStoreKey );
List<UserEntity> usersWithThisEmail = findUsersWithEmail( email, userStoreKey );
if ( usersWithThisEmail.size() > 0 )
{
throw new UserStorageExistingEmailException( email, userStore.getName() );
}
}
private boolean isValidEmailAddress( String email )
{
return StringUtils.isNotBlank( email ) && email.matches( VALID_EMAIL_PATTERN );
}
private void verifyUniqueEmailForCreate( StoreNewUserCommand command )
{
doVerifyUniqueEmailAdress( command.getEmail(), command.getUserStoreKey() );
}
private void verifyUpdateUserCommand( final UpdateUserCommand command, final UserStoreEntity userStore )
{
verifyRestrictedGroupAccess( command );
verifyMandatoryFieldsForUpdate( command );
verifyUniqueEmailForUpdate( command );
final UserFields commandUserFields = command.getUserFields();
if ( command.isUpdateStrategy() )
{
// user-update operation
new RequiredUserFieldsValidator( userStore.getConfig() ).validateAllRequiredFieldsArePresentAndNotEmpty( commandUserFields );
}
else
{
// user-modify operation
new RequiredUserFieldsValidator( userStore.getConfig() ).validatePresentFieldsAreNotBlankIfRequired( commandUserFields );
}
}
private void verifyRestrictedGroupAccess( UpdateUserCommand command )
{
UserEntity userToUpdate = userDao.findSingleBySpecification( command.getSpecification() );
if ( command.syncMemberships() )
{
GroupEntity userGroup = userToUpdate.getUserGroup();
for ( GroupKey groupKey : command.getMemberships() )
{
GroupEntity groupToJoin = groupDao.find( groupKey.toString() );
boolean alreadyExists = userGroup.isMemberOf( groupToJoin, false );
if ( groupToJoin != null && !alreadyExists )
{
if ( command.isUpdateOpenGroupsOnly() && groupToJoin.isRestricted() )
{
throw new UserStoreAccessException( UserStoreAccessType.JOIN_GROUP, userToUpdate.getQualifiedName(), groupToJoin,
"Not allowed to add membership to a restricted group in this context" );
}
}
}
for ( GroupEntity existingMembership : userGroup.getMemberships( false ) )
{
boolean removeThisGroup = !command.getMemberships().contains( existingMembership.getGroupKey() );
if ( removeThisGroup )
{
if ( command.isUpdateOpenGroupsOnly() && existingMembership.isRestricted() )
{
throw new UserStoreAccessException( UserStoreAccessType.LEAVE_GROUP, userToUpdate.getQualifiedName(),
existingMembership,
"Not allowed to remove membership from a restricted group in this context" );
}
}
}
}
}
private void verifyUniqueEmailForUpdate( UpdateUserCommand command )
{
String email = command.getEmail();
if ( email == null )
{
return;
}
UserEntity userToUpdate = userDao.findSingleBySpecification( command.getSpecification() );
List<UserEntity> usersWithThisEmail = findUsersWithEmail( email, userToUpdate.getUserStoreKey() );
if ( usersWithThisEmail.size() == 0 )
{
return;
}
boolean oneUserWithMatchingEmailFoundAndItsMe =
usersWithThisEmail.size() == 1 && userToUpdate.equals( usersWithThisEmail.get( 0 ) );
if ( oneUserWithMatchingEmailFoundAndItsMe )
{
return;
}
throw new UserStorageExistingEmailException( email, userToUpdate.getUserStore().getName() );
}
private List<UserEntity> findUsersWithEmail( String email, UserStoreKey userStoreKey )
{
UserSpecification userByEmailSpec = new UserSpecification();
userByEmailSpec.setEmail( email );
userByEmailSpec.setUserStoreKey( userStoreKey );
userByEmailSpec.setDeletedStateNotDeleted();
return userDao.findBySpecification( userByEmailSpec );
}
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void updateUser( final UpdateUserCommand command )
{
final UserEntity userToUpdate = userDao.findSingleBySpecification( command.getSpecification() );
if ( userToUpdate == null )
{
throw new IllegalArgumentException( "User does not exists: " + command.getSpecification() );
}
final UserSpecification updaterSpec = new UserSpecification();
updaterSpec.setKey( command.getUpdater() );
updaterSpec.setDeletedStateNotDeleted();
final UserEntity updater = userDao.findSingleBySpecification( updaterSpec );
final UserStoreEntity userStore = userToUpdate.getUserStore();
boolean hasUpdateAccessOnUser =
userStoreAccessResolver.hasUpdateUserAccess( updater, userStore, command.allowUpdateSelf(), userToUpdate );
if ( !hasUpdateAccessOnUser )
{
throw new UserStoreAccessException( UserStoreAccessType.UPDATE_USER, updater.getQualifiedName(),
userToUpdate.getQualifiedName() );
}
Preconditions.checkArgument( !userToUpdate.isBuiltIn(), "Cannot update a built-in user" );
final UserStoreConnector usc = doGetUSConnector( userStore.getKey() );
if ( command.getDisplayName() == null )
{
command.setDisplayName( userToUpdate.getDisplayName() );
}
final UserFields userFields = command.getUserFields();
if ( command.removePhoto() )
{
// Make sure a new photo isn't set
userFields.remove( UserFieldType.PHOTO );
}
else if ( !userFields.hasField( UserFieldType.PHOTO ) )
{
// No new photo found - use old photo
userFields.add( new UserField( UserFieldType.PHOTO, userToUpdate.getPhoto() ) );
}
verifyUpdateUserCommand( command, userStore );
usc.updateUser( command );
}
private void verifyMandatoryFieldsForUpdate( final UpdateUserCommand command )
{
boolean allowNullsForMandatoryValues = command.isModifyStrategy();
String email = command.getEmail();
boolean emailValid = allowNullsForMandatoryValues ? email == null || isValidEmailAddress( email ) : isValidEmailAddress( email );
if ( !emailValid )
{
throw new UserStorageInvalidArgumentException( "email" );
}
}
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void deleteUser( final DeleteUserCommand command )
{
final UserEntity deleter = userDao.findByKey( command.getDeleter() );
final UserEntity userToDelete = userDao.findSingleBySpecification( command.getSpecification() );
if ( userToDelete == null )
{
// User does not exist.
return;
}
final UserStoreEntity userStore = userToDelete.getUserStore();
if ( deleter.equals( userToDelete ) )
{
throw new UserStoreAccessException( UserStoreAccessType.DELETE_USER, deleter.getQualifiedName(),
userToDelete.getQualifiedName(), "Cannot delete the deleter." );
}
if ( !userStoreAccessResolver.hasDeleteUserAccess( deleter, userStore ) )
{
throw new UserStoreAccessException( UserStoreAccessType.DELETE_USER, deleter.getQualifiedName(),
userToDelete.getQualifiedName() );
}
final UserStoreConnector usc = doGetUSConnector( userStore.getKey() );
usc.deleteUser( command );
}
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void deleteUserStore( final DeleteUserStoreCommand command )
{
final UserStoreEntity userStoreToDelete = userStoreDao.findByKey( command.getKey() );
if ( userStoreToDelete == null )
{
throw new IllegalArgumentException( "UserStore with key=" + command.getKey() + " not found" );
}
Assert.isTrue( !userStoreToDelete.isDefaultUserStore(), "Not allowed to delete the default UserStore" );
final UserEntity deleter = userDao.findByKey( command.getDeleter() );
if ( !userStoreAccessResolver.hasDeleteUserStoreAccess( deleter ) )
{
throw new UserStoreAccessException( UserStoreAccessType.DELETE_USERSTORE, deleter.getQualifiedName(),
userStoreToDelete.getName() );
}
userStoreToDelete.setDeleted( true );
}
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public GroupKey storeNewGroup( final StoreNewGroupCommand command )
{
UserStoreEntity userStore = null;
if ( command.getUserStoreKey() != null )
{
userStore = userStoreDao.findByKey( command.getUserStoreKey() );
}
final UserEntity creator = command.getExecutor();
if ( !groupAccessResolver.hasCreateGroupAccess( creator, command.getType(), userStore ) )
{
if ( command.isRespondWithException() )
{
throw new CreateGroupAccessException( creator.getQualifiedName(), command.getType() );
}
else
{
return null;
}
}
if ( command.getType().isGlobal() || command.getType().isBuiltIn() )
{
return groupStorerFactory.createForGlobalGroups().storeNewGroup( command );
}
else
{
final UserStoreConnector usc = doGetUSConnector( command.getUserStoreKey() );
return usc.storeNewGroup( command );
}
}
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public UserStoreKey storeNewUserStore( final StoreNewUserStoreCommand command )
{
final UserEntity storer = userDao.findByKey( command.getStorer() );
if ( !userStoreAccessResolver.hasCreateUserStoreAccess( storer ) )
{
throw new UserStoreAccessException( UserStoreAccessType.CREATE_USERSTORE, storer.getQualifiedName(), command.getName() );
}
return doStoreNewUserStore( command );
}
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void initializeUserStores()
{
List<UserStoreEntity> allUserStores = userStoreDao.findAll();
if ( allUserStores.size() != 0 )
{
return;
}
StoreNewUserStoreCommand command = new StoreNewUserStoreCommand();
command.setConfig( UserStoreConfigParser.parse( null ) );
command.setConnectorName( null );
command.setDefaultStore( true );
command.setName( "default" );
command.setDefaultStore( true );
doStoreNewUserStore( command );
}
private UserStoreKey doStoreNewUserStore( final StoreNewUserStoreCommand command )
{
Assert.isTrue( StringUtils.isNotEmpty( command.getName() ), "UserStore name is required" );
// TODO: Add possible other required
final boolean newDefaultUserStore = command.isDefaultStore();
UserStoreEntity currentDefault = null;
if ( newDefaultUserStore )
{
currentDefault = userStoreDao.findDefaultUserStore();
}
final UserStoreEntity userStore = new UserStoreEntity();
userStore.setName( command.getName() );
userStore.setConnectorName( command.getConnectorName() );
userStore.setDefaultStore( command.isDefaultStore() );
userStore.setDeleted( false );
userStore.setConfig( command.getConfig() );
userStoreDao.store( userStore );
if ( newDefaultUserStore && currentDefault != null )
{
currentDefault.setDefaultStore( false );
}
final GroupStorer groupStorer = groupStorerFactory.create( userStore.getKey() );
final StoreNewGroupCommand storeNewUserStoreAdminGroupCommand = new StoreNewGroupCommand();
storeNewUserStoreAdminGroupCommand.setUserStoreKey( userStore.getKey() );
storeNewUserStoreAdminGroupCommand.setName( GroupType.USERSTORE_ADMINS.getName() );
storeNewUserStoreAdminGroupCommand.setType( GroupType.USERSTORE_ADMINS );
storeNewUserStoreAdminGroupCommand.setRestriced( true );
groupStorer.storeNewGroup( storeNewUserStoreAdminGroupCommand );
final StoreNewGroupCommand storeNewUserStoreAuthGroupCommand = new StoreNewGroupCommand();
storeNewUserStoreAuthGroupCommand.setUserStoreKey( userStore.getKey() );
storeNewUserStoreAuthGroupCommand.setName( GroupType.AUTHENTICATED_USERS.getName() );
storeNewUserStoreAuthGroupCommand.setType( GroupType.AUTHENTICATED_USERS );
storeNewUserStoreAuthGroupCommand.setRestriced( true );
groupStorer.storeNewGroup( storeNewUserStoreAuthGroupCommand );
return userStore.getKey();
}
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void updateUserStore( final UpdateUserStoreCommand command )
{
if ( command == null )
{
throw new IllegalArgumentException( "command cannot be null" );
}
Assert.isTrue( command.getKey() != null, "UserStore key is required" );
Assert.isTrue( StringUtils.isNotEmpty( command.getName() ), "UserStore name is required" );
final UserEntity storer = userDao.findByKey( command.getUpdater() );
final UserStoreEntity userStoreToUpdate = userStoreDao.findByKey( command.getKey() );
if ( !userStoreAccessResolver.hasUpdateUserStoreAccess( storer ) )
{
throw new UserStoreAccessException( UserStoreAccessType.UPDATE_USERSTORE, storer.getQualifiedName(), command.getName() );
}
final boolean setAsNewDefaultStore = command.getAsNewDefaultStore();
UserStoreEntity currentDefaultUserStore = null;
if ( setAsNewDefaultStore )
{
currentDefaultUserStore = userStoreDao.findDefaultUserStore();
}
if ( userStoreToUpdate == null )
{
throw new IllegalArgumentException( "UserStore with key=" + command.getKey() + " not found" );
}
userStoreToUpdate.setName( command.getName() );
userStoreToUpdate.setConnectorName( command.getConnectorName() );
userStoreToUpdate.setDeleted( command.isDeleted() );
if ( setAsNewDefaultStore )
{
userStoreToUpdate.setDefaultStore( true );
}
if ( setAsNewDefaultStore && currentDefaultUserStore != null )
{
currentDefaultUserStore.setDefaultStore( false );
}
userStoreToUpdate.setConfig( command.getConfig() );
userStoreDao.getHibernateTemplate().flush();
}
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void updateGroup( final UpdateGroupCommand command )
{
final GroupEntity groupToBeUpdated = groupDao.findByKey( command.getGroupKey() );
final UserEntity updater = userDao.findByKey( command.getUpdater() );
if ( !groupAccessResolver.hasUpdateGroupAccess( updater, groupToBeUpdated ) )
{
throw new UpdateGroupAccessException( updater.getQualifiedName(), groupToBeUpdated.getQualifiedName() );
}
if ( groupToBeUpdated.isGlobal() || groupToBeUpdated.isBuiltIn() )
{
groupStorerFactory.createForGlobalGroups().updateGroup( command );
}
else
{
final UserStoreConnector usc = doGetUSConnector( groupToBeUpdated.getUserStore().getKey() );
usc.updateGroup( command );
}
}
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public List<GroupEntity> addMembershipsToGroup( final AddMembershipsCommand command )
{
final List<GroupEntity> groupsAddedTo = new ArrayList<GroupEntity>();
final UserEntity executor = userDao.findByKey( command.getExecutor() );
final GroupEntity groupToAdd = groupDao.findSingleBySpecification( command.getGroupToAdd() );
for ( final GroupKey groupToAddToKey : command.getGroupsToAddTo() )
{
final GroupEntity groupToAddTo = groupDao.findByKey( groupToAddToKey );
if ( command.isUpdateOpenGroupsOnly() && groupToAddTo.isRestricted() )
{
if ( command.isRespondWithException() )
{
throw new UserStoreAccessException( UserStoreAccessType.JOIN_GROUP, executor.getQualifiedName(), groupToAddTo,
"Not allowed to add membership to a restricted group in this context" );
}
else
{
continue;
}
}
if ( groupAccessResolver.hasAddMembershipAccess( executor, groupToAdd, groupToAddTo ) )
{
if ( groupToAddTo.isGlobal() )
{
groupStorerFactory.createForGlobalGroups().addMembershipToGroup( groupToAdd, groupToAddTo );
groupsAddedTo.add( groupToAddTo );
}
else
{
final UserStoreConnector usc = doGetUSConnector( groupToAddTo.getUserStore().getKey() );
usc.addMembershipToGroup( groupToAdd, groupToAddTo );
groupsAddedTo.add( groupToAddTo );
}
}
else
{
if ( command.isRespondWithException() )
{
throw new UserStoreAccessException( UserStoreAccessType.JOIN_GROUP, executor.getQualifiedName(), groupToAddTo );
}
}
}
return groupsAddedTo;
}
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public List<GroupEntity> removeMembershipsFromGroup( final RemoveMembershipsCommand command )
{
final List<GroupEntity> groupsRemovedFrom = new ArrayList<GroupEntity>();
final UserEntity executor = userDao.findByKey( command.getExecutor() );
final GroupEntity groupToRemove = groupDao.findSingleBySpecification( command.getGroupToRemove() );
for ( final GroupKey groupToRemoveFromKey : command.getGroupsToRemoveFrom() )
{
final GroupEntity groupToRemoveFrom = groupDao.findByKey( groupToRemoveFromKey );
if ( command.isUpdateOpenGroupsOnly() && groupToRemoveFrom.isRestricted() )
{
if ( command.isRespondWithException() )
{
throw new UserStoreAccessException( UserStoreAccessType.LEAVE_GROUP, executor.getQualifiedName(), groupToRemoveFrom,
"Not allowed to remove membership from a restricted group in this context" );
}
else
{
continue;
}
}
if ( groupAccessResolver.hasRemoveMembershipAccess( executor, groupToRemove, groupToRemoveFrom ) )
{
if ( groupToRemoveFrom.isGlobal() )
{
groupStorerFactory.createForGlobalGroups().removeMembershipFromGroup( groupToRemove, groupToRemoveFrom );
groupsRemovedFrom.add( groupToRemoveFrom );
}
else
{
final UserStoreConnector usc = doGetUSConnector( groupToRemoveFrom.getUserStore().getKey() );
usc.removeMembershipFromGroup( groupToRemove, groupToRemoveFrom );
groupsRemovedFrom.add( groupToRemoveFrom );
}
}
else
{
if ( command.isRespondWithException() )
{
throw new UserStoreAccessException( UserStoreAccessType.LEAVE_GROUP, executor.getQualifiedName(), groupToRemoveFrom );
}
}
}
return groupsRemovedFrom;
}
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void deleteGroup( final DeleteGroupCommand command )
{
final UserEntity deleter = command.getDeleter();
final GroupEntity groupToDelete = groupDao.findSingleBySpecification( command.getSpecification() );
Assert.isTrue( !groupToDelete.isBuiltIn(), "Cannot delete a built-in group" );
if ( !groupAccessResolver.hasDeleteGroupAccess( deleter, groupToDelete ) )
{
if ( command.isRespondWithException() )
{
throw new DeleteGroupAccessException( deleter.getQualifiedName(), groupToDelete.getQualifiedName() );
}
else
{
return;
}
}
if ( groupToDelete.isGlobal() )
{
groupStorerFactory.createForGlobalGroups().deleteGroup( command );
}
else
{
final UserStoreEntity userStore = groupToDelete.getUserStore();
final UserStoreKey userStoreKey = userStore.getKey();
final UserStoreConnector usc = doGetUSConnector( userStoreKey );
usc.deleteGroup( command );
}
}
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void authenticateUser( final UserStoreKey userStoreKey, final String uid, final String password )
{
final AuthenticationChain authChain =
new AuthenticationChain( this.authenticationInterceptors );
doGetUSConnector( userStoreKey ).authenticateUser( uid, password, authChain );
}
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void changePassword( final UserStoreKey userStoreKey, final String uid, final String newPassword )
{
if ( UserEntity.isBuiltInUser( uid ) )
{
throw new IllegalArgumentException( "Cannot change password for built in user: " + uid );
}
final UserStoreConnector usc = doGetUSConnector( userStoreKey );
usc.changePassword( uid, newPassword );
}
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void synchronizeUser( final UserStoreKey userStoreKey, final String uid )
throws UserNotFoundException
{
final RemoteUserStoreConnector rusc = doGetRemoteUSConnector( userStoreKey );
if ( rusc != null )
{
rusc.synchronizeUser( uid );
}
}
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void synchronizeUsers( final SynchronizeStatus status, final UserStoreKey userStoreKey, final List<RemoteUser> remoteUsers,
final boolean syncMemberships, final MemberCache memberCache )
{
final RemoteUserStoreConnector rusc = doGetRemoteUSConnector( userStoreKey );
if ( rusc != null )
{
rusc.synchronizeUsers( status, remoteUsers, syncMemberships, memberCache );
}
}
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void synchronizeUserMemberships( final SynchronizeStatus status, final UserStoreKey userStoreKey, final RemoteUser remoteUser,
final MemberCache memberCache )
{
final RemoteUserStoreConnector rusc = doGetRemoteUSConnector( userStoreKey );
if ( rusc != null && rusc.canReadGroup() )
{
rusc.synchronizeUserMemberships( status, remoteUser, memberCache );
}
}
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void deleteUsersLocally( final UserStoreKey userStoreKey, final LocalUsersStatus status, final List<UserKey> users )
{
final UserStorer userStorer = userStorerFactory.create( userStoreKey );
for ( final UserKey userKey : users )
{
final UserSpecification userToDeleteSpec = new UserSpecification();
userToDeleteSpec.setKey( userKey );
userToDeleteSpec.setDeletedState( UserSpecification.DeletedState.ANY );
userStorer.deleteUser( userToDeleteSpec );
status.deleted();
}
}
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public GroupEntity synchronizeGroup( final GroupKey groupKey )
{
final GroupEntity group = groupDao.findByKey( groupKey );
if ( group.isBuiltIn() )
{
throw new IllegalArgumentException( "Cannot synchronize built-in group: " + group.getQualifiedName() );
}
if ( group.getUserStore() == null )
{
throw new IllegalArgumentException( "Can only synchronize groups that belongs to a userstore: " + group.getQualifiedName() );
}
final RemoteUserStoreConnector rusc = doGetRemoteUSConnector( group.getUserStore().getKey() );
if ( rusc != null && rusc.canReadGroup() )
{
// TODO: Disabled sync of members due to timeout caused by large number of members. RemoteUserStorePlugin.geMembers must be batched!
rusc.synchronizeGroup( group, true, false );
}
return group;
}
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void synchronizeGroups( final SynchronizeStatus status, final UserStoreKey userStoreKey, final List<RemoteGroup> remoteGroups,
final boolean syncMemberships, final boolean syncMembers, final MemberCache memberCache )
{
final RemoteUserStoreConnector rusc = doGetRemoteUSConnector( userStoreKey );
if ( rusc != null )
{
rusc.synchronizeGroups( status, remoteGroups, syncMemberships, syncMembers, memberCache );
}
}
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void synchronizeGroupMemberships( final SynchronizeStatus status, final UserStoreKey userStoreKey, final RemoteGroup remoteGroup,
final MemberCache memberCache )
{
final RemoteUserStoreConnector rusc = doGetRemoteUSConnector( userStoreKey );
if ( rusc != null )
{
rusc.synchronizeGroupMemberships( status, remoteGroup, memberCache );
}
}
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void deleteGroupsLocally( final LocalGroupsStatus status, final UserStoreKey userStoreKey, final List<GroupKey> groups )
{
final GroupStorer groupStorer = groupStorerFactory.create( userStoreKey );
for ( final GroupKey groupKey : groups )
{
final GroupSpecification groupToDeleteSpec = new GroupSpecification();
groupToDeleteSpec.setKey( groupKey );
final DeleteGroupCommand command = new DeleteGroupCommand( null, groupToDeleteSpec );
groupStorer.deleteGroup( command );
status.deleted();
}
}
public User getUserByKey( final UserKey userKey )
{
Preconditions.checkNotNull( userKey, "Given userKey cannot be null" );
final UserEntity userEntity = userDao.findByKey( userKey );
Preconditions.checkNotNull( userEntity, "User does not exist in database: " + userKey );
if ( userEntity.isBuiltIn() )
{
UserImpl user = UserImpl.createFrom( userEntity );
if ( user.isEnterpriseAdmin() )
{
// hente email fra cms.properties
user.setEmail( adminUserEmail );
}
return user;
}
final UserStoreConnector usc = doGetUSConnector( userEntity.getUserStore().getKey() );
return usc.getUserByEntity( userEntity );
}
public boolean canSynchronizeUsers( final UserStoreKey userStoreKey )
{
try
{
final RemoteUserStoreConnector rusc = doGetRemoteUSConnector( userStoreKey );
if ( rusc != null )
{
return true;
}
return false;
}
catch ( final InvalidUserStoreConfigException ex )
{
return false;
}
}
public boolean canSynchronizeGroups( final UserStoreKey userStoreKey )
{
try
{
final RemoteUserStoreConnector rusc = doGetRemoteUSConnector( userStoreKey );
if ( rusc != null )
{
return rusc.canReadGroup();
}
return false;
}
catch ( final InvalidUserStoreConfigException ex )
{
return false;
}
}
public UserStoreEntity getDefaultUserStore()
{
UserStoreEntity userStore = userStoreDao.findDefaultUserStore();
if ( userStore != null )
{
return userStore;
}
throw new IllegalStateException( "Default userstore is not set" );
}
public Map<String, UserStoreConnectorConfig> getUserStoreConnectorConfigs()
{
return userConnectorStoreManager.getUserStoreConnectorConfigs();
}
private List<UserEntity> doGetUsers( final UserStoreKey userStoreKey )
{
final UserSpecification userSpec = new UserSpecification();
userSpec.setUserStoreKey( userStoreKey );
userSpec.setDeletedStateNotDeleted();
return userDao.findBySpecification( userSpec );
}
public List<UserEntity> getUsers( final UserStoreKey userStoreKey )
{
return doGetUsers( userStoreKey );
}
public Multimap<String, UserEntity> getUsersAsMapByName( final UserStoreKey userStoreKey )
{
final List<UserEntity> users = doGetUsers( userStoreKey );
final Multimap<String, UserEntity> userMapByName = HashMultimap.create();
for ( final UserEntity user : users )
{
userMapByName.put( user.getName(), user );
}
return userMapByName;
}
public List<GroupEntity> getGroups( GroupSpecification groupSpec )
{
return groupDao.findBySpecification( groupSpec );
}
public void verifyUserStoreConnector( final String connectorName )
{
getRemoteUserStorePlugin( connectorName );
}
private RemoteUserStore getRemoteUserStorePlugin( final String connectorName )
{
final UserStoreConnectorConfig connectorConfig = userConnectorStoreManager.getUserStoreConnectorConfig( connectorName );
final String connectorType = connectorConfig.getPluginType();
final Properties pluginProperties = connectorConfig.getPluginProperties();
return remoteUserStoreFactory.create( connectorType, pluginProperties );
}
public void verifyUserStoreConnectorConfig( final UserStoreConfig config, final String connectorName )
{
final RemoteUserStore remoteUserStorePlugin = getRemoteUserStorePlugin( connectorName );
final Set<UserFieldType> supportedTypes = remoteUserStorePlugin.getSupportedFieldTypes();
for ( final UserStoreConfigField userFieldConfig : config.getRemoteOnlyUserFieldConfigs() )
{
if ( !supportedTypes.contains( userFieldConfig.getType() ) )
{
throw new InvalidUserStoreConfigException(
"Remote plugin '" + connectorName + "' does not support type: " + userFieldConfig.getType().getName() );
}
}
}
@Override
public void invalidateUserStoreCachedConfig( UserStoreKey userStoreKey )
{
userConnectorStoreManager.invalidateCachedConfig( userStoreKey );
}
public boolean canCreateUser( final UserStoreKey userStoreKey )
{
try
{
return doGetUSConnector( userStoreKey ).canCreateUser();
}
catch ( InvalidUserStoreConfigException e )
{
return false;
}
}
public boolean canUpdateUser( final UserStoreKey userStoreKey )
{
try
{
return doGetUSConnector( userStoreKey ).canUpdateUser();
}
catch ( InvalidUserStoreConfigException e )
{
return false;
}
}
public boolean canUpdateUserPassword( final UserStoreKey userStoreKey )
{
try
{
return doGetUSConnector( userStoreKey ).canUpdateUserPassword();
}
catch ( InvalidUserStoreConfigException e )
{
return false;
}
}
public boolean canDeleteUser( final UserStoreKey userStoreKey )
{
try
{
return doGetUSConnector( userStoreKey ).canDeleteUser();
}
catch ( InvalidUserStoreConfigException e )
{
return false;
}
}
public boolean canCreateGroup( final UserStoreKey userStoreKey )
{
try
{
return doGetUSConnector( userStoreKey ).canCreateGroup();
}
catch ( InvalidUserStoreConfigException e )
{
return false;
}
}
public boolean canReadGroup( final UserStoreKey userStoreKey )
{
try
{
return doGetUSConnector( userStoreKey ).canReadGroup();
}
catch ( InvalidUserStoreConfigException e )
{
return false;
}
}
public boolean canUpdateGroup( final UserStoreKey userStoreKey )
{
try
{
return doGetUSConnector( userStoreKey ).canUpdateGroup();
}
catch ( InvalidUserStoreConfigException e )
{
return false;
}
}
public boolean canDeleteGroup( final UserStoreKey userStoreKey )
{
try
{
return doGetUSConnector( userStoreKey ).canDeleteGroup();
}
catch ( InvalidUserStoreConfigException e )
{
return false;
}
}
private UserStoreConnector doGetUSConnector( final UserStoreKey userStoreKey )
{
return userConnectorStoreManager.getUserStoreConnector( userStoreKey );
}
private RemoteUserStoreConnector doGetRemoteUSConnector( final UserStoreKey userStoreKey )
{
return userConnectorStoreManager.getRemoteUserStoreConnector( userStoreKey );
}
}