package com.constellio.model.services.users; import static com.constellio.model.entities.schemas.Schemas.LOGICALLY_DELETED_STATUS; import static com.constellio.model.services.search.query.logical.LogicalSearchQueryOperators.from; import static org.apache.commons.collections.CollectionUtils.isEmpty; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.commons.lang3.StringUtils; import org.joda.time.LocalDateTime; import org.joda.time.ReadableDuration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.constellio.data.dao.services.idGenerator.UniqueIdGenerator; import com.constellio.data.utils.ImpossibleRuntimeException; import com.constellio.data.utils.LangUtils; import com.constellio.data.utils.LangUtils.ListComparisonResults; import com.constellio.data.utils.TimeProvider; import com.constellio.model.conf.ModelLayerConfiguration; import com.constellio.model.conf.ldap.LDAPConfigurationManager; import com.constellio.model.entities.records.Record; import com.constellio.model.entities.records.Transaction; import com.constellio.model.entities.records.wrappers.Group; import com.constellio.model.entities.records.wrappers.User; import com.constellio.model.entities.schemas.Metadata; import com.constellio.model.entities.schemas.MetadataSchema; import com.constellio.model.entities.schemas.MetadataSchemaTypes; import com.constellio.model.entities.schemas.MetadataSchemasRuntimeException; import com.constellio.model.entities.schemas.Schemas; import com.constellio.model.entities.security.Role; import com.constellio.model.entities.security.global.GlobalGroup; import com.constellio.model.entities.security.global.GlobalGroupStatus; import com.constellio.model.entities.security.global.SolrGlobalGroup; import com.constellio.model.entities.security.global.SolrUserCredential; import com.constellio.model.entities.security.global.UserCredential; import com.constellio.model.entities.security.global.UserCredentialStatus; import com.constellio.model.services.collections.CollectionsListManager; import com.constellio.model.services.factories.ModelLayerFactory; import com.constellio.model.services.records.RecordServices; import com.constellio.model.services.records.RecordServicesException; import com.constellio.model.services.schemas.MetadataSchemasManager; import com.constellio.model.services.schemas.builders.CommonMetadataBuilder; import com.constellio.model.services.search.SearchServices; import com.constellio.model.services.search.StatusFilter; import com.constellio.model.services.search.query.logical.LogicalSearchQuery; import com.constellio.model.services.search.query.logical.condition.LogicalSearchCondition; import com.constellio.model.services.search.query.logical.ongoing.OngoingLogicalSearchCondition; import com.constellio.model.services.search.query.logical.ongoing.OngoingLogicalSearchConditionWithDataStoreFields; import com.constellio.model.services.security.AuthorizationsServices; import com.constellio.model.services.security.authentification.AuthenticationService; import com.constellio.model.services.security.roles.RolesManager; import com.constellio.model.services.security.roles.RolesManagerRuntimeException; import com.constellio.model.services.users.UserServicesRuntimeException.UserServicesRuntimeException_CannotExcuteTransaction; import com.constellio.model.services.users.UserServicesRuntimeException.UserServicesRuntimeException_CannotRemoveAdmin; import com.constellio.model.services.users.UserServicesRuntimeException.UserServicesRuntimeException_InvalidGroup; import com.constellio.model.services.users.UserServicesRuntimeException.UserServicesRuntimeException_InvalidToken; import com.constellio.model.services.users.UserServicesRuntimeException.UserServicesRuntimeException_InvalidUserNameOrPassword; import com.constellio.model.services.users.UserServicesRuntimeException.UserServicesRuntimeException_NoSuchGroup; import com.constellio.model.services.users.UserServicesRuntimeException.UserServicesRuntimeException_NoSuchUser; import com.constellio.model.services.users.UserServicesRuntimeException.UserServicesRuntimeException_UserIsNotInCollection; import com.constellio.model.services.users.UserServicesRuntimeException.UserServicesRuntimeException_UserPermissionDeniedToDelete; import com.google.common.base.Predicate; import com.google.common.collect.Collections2; public class UserServices { private static final Logger LOGGER = LoggerFactory.getLogger(UserServices.class); public static final String ADMIN = "admin"; private final UserCredentialsManager userCredentialsManager; private final GlobalGroupsManager globalGroupsManager; private final CollectionsListManager collectionsListManager; private final RecordServices recordServices; private final SearchServices searchServices; private final MetadataSchemasManager metadataSchemasManager; private final AuthenticationService authenticationService; private final LDAPConfigurationManager ldapConfigurationManager; private final RolesManager rolesManager; private final ModelLayerConfiguration modelLayerConfiguration; private final UniqueIdGenerator secondaryUniqueIdGenerator; private final AuthorizationsServices authorizationsServices; public UserServices(ModelLayerFactory modelLayerFactory) { this.userCredentialsManager = modelLayerFactory.getUserCredentialsManager(); this.globalGroupsManager = modelLayerFactory.getGlobalGroupsManager(); this.collectionsListManager = modelLayerFactory.getCollectionsListManager(); this.recordServices = modelLayerFactory.newRecordServices(); this.searchServices = modelLayerFactory.newSearchServices(); this.metadataSchemasManager = modelLayerFactory.getMetadataSchemasManager(); this.authenticationService = modelLayerFactory.newAuthenticationService(); this.modelLayerConfiguration = modelLayerFactory.getConfiguration(); this.ldapConfigurationManager = modelLayerFactory.getLdapConfigurationManager(); this.rolesManager = modelLayerFactory.getRolesManager(); this.secondaryUniqueIdGenerator = modelLayerFactory.getDataLayerFactory().getSecondaryUniqueIdGenerator(); this.authorizationsServices = modelLayerFactory.newAuthorizationsServices(); } public UserCredential createUserCredential(String username, String firstName, String lastName, String email, List<String> globalGroups, List<String> collections, UserCredentialStatus status) { return userCredentialsManager.create(username, firstName, lastName, email, globalGroups, collections, status); } public UserCredential createUserCredential(String username, String firstName, String lastName, String email, List<String> globalGroups, List<String> collections, UserCredentialStatus status, String domain, List<String> msExchDelegateListBL, String dn) { return userCredentialsManager.create( username, firstName, lastName, email, globalGroups, collections, status, domain, msExchDelegateListBL, dn); } public UserCredential createUserCredential(String username, String firstName, String lastName, String email, String serviceKey, boolean systemAdmin, List<String> globalGroups, List<String> collections, Map<String, LocalDateTime> tokens, UserCredentialStatus status) { return userCredentialsManager.create( username, firstName, lastName, email, serviceKey, systemAdmin, globalGroups, collections, tokens, status); } public UserCredential createUserCredential(String username, String firstName, String lastName, String email, String serviceKey, boolean systemAdmin, List<String> globalGroups, List<String> collections, Map<String, LocalDateTime> tokens, UserCredentialStatus status, String domain, List<String> msExchDelegateListBL, String dn) { return userCredentialsManager.create( username, firstName, lastName, email, serviceKey, systemAdmin, globalGroups, collections, tokens, status, domain, msExchDelegateListBL, dn); } public UserCredential createUserCredential(String username, String firstName, String lastName, String email, List<String> personalEmails, String serviceKey, boolean systemAdmin, List<String> globalGroups, List<String> collections, Map<String, LocalDateTime> tokens, UserCredentialStatus status, String domain, List<String> msExchDelegateListBL, String dn) { return userCredentialsManager.create( username, firstName, lastName, email, personalEmails, serviceKey, systemAdmin, globalGroups, collections, tokens, status, domain, msExchDelegateListBL, dn); } public UserCredential createUserCredential(String username, String firstName, String lastName, String email, List<String> personalEmails, String serviceKey, boolean systemAdmin, List<String> globalGroups, List<String> collections, Map<String, LocalDateTime> tokens, UserCredentialStatus status, String domain, List<String> msExchDelegateListBL, String dn, String jobTitle, String phone, String fax, String address) { return userCredentialsManager.create( username, firstName, lastName, email, personalEmails, serviceKey, systemAdmin, globalGroups, collections, tokens, status, domain, msExchDelegateListBL, dn, jobTitle, phone, fax, address); } public void addUpdateUserCredential(UserCredential userCredential) { List<String> collections = collectionsListManager.getCollectionsExcludingSystem(); validateAdminIsActive(userCredential); UserCredential savedUserCredential = userCredential; for (String groupCode : userCredential.getGlobalGroups()) { GlobalGroup group = globalGroupsManager.getGlobalGroupWithCode(groupCode); if (group == null) { throw new UserServicesRuntimeException_InvalidGroup(groupCode); } for (String collection : group.getUsersAutomaticallyAddedToCollections()) { if (collections.contains(collection)) { savedUserCredential = savedUserCredential.withNewCollection(collection); } } } userCredentialsManager.addUpdate(savedUserCredential); sync(savedUserCredential); } public GlobalGroup createGlobalGroup( String code, String name, List<String> collections, String parent, GlobalGroupStatus status, boolean locallyCreated) { return globalGroupsManager.create(code, name, collections, parent, status, locallyCreated); } public void addUpdateGlobalGroup(GlobalGroup globalGroup) { GlobalGroup currentGroup = globalGroupsManager.getGlobalGroupWithCode(globalGroup.getCode()); List<String> wasAutomaticCollections = currentGroup == null ? new ArrayList<String>() : currentGroup .getUsersAutomaticallyAddedToCollections(); ListComparisonResults<String> comparisonResults = new LangUtils().compare(wasAutomaticCollections, globalGroup.getUsersAutomaticallyAddedToCollections()); for (String newAutomaticCollection : comparisonResults.getNewItems()) { for (UserCredential userInGroup : getGlobalGroupActifUsers(globalGroup.getCode())) { addUserToCollection(userInGroup, newAutomaticCollection); } } if (globalGroup.getStatus() == GlobalGroupStatus.ACTIVE) { activateGlobalGroupHierarchyWithoutUserValidation(globalGroup); } else { logicallyRemoveGroupHierarchyWithoutUserValidation(globalGroup); } globalGroupsManager.addUpdate(globalGroup); sync(globalGroup); } public void addUserToCollection(UserCredential userCredential, String collection) { if (!userCredential.getCollections().contains(collection)) { try { addUpdateUserCredential(userCredential.withNewCollection(collection)); } catch (UserServicesRuntimeException_CannotExcuteTransaction e) { // Revert change in XML config userCredentialsManager.addUpdate(userCredential.withRemovedCollection(collection)); throw e; } } else { // This apparently redundant sync allows to add a user to a collection // in case the user credential configuration file and the solr state are out of sync sync(userCredential); } } public void setGlobalGroupUsers(String groupCode, List<UserCredential> newUserList) { List<String> newUsernameList = toUserNames(newUserList); for (UserCredential userCredential : newUserList) { UserCredential latestCrendential = getUser(userCredential.getUsername()); if (!latestCrendential.getGlobalGroups().contains(groupCode)) { List<String> groupCodes = new ArrayList<>(); groupCodes.addAll(latestCrendential.getGlobalGroups()); groupCodes.add(groupCode); addUpdateUserCredential(latestCrendential.withGlobalGroups(groupCodes)); } } List<UserCredential> currentList = getGlobalGroupActifUsers(groupCode); for (UserCredential currentListUser : currentList) { if (!newUsernameList.contains(currentListUser.getUsername())) { List<String> groupCodes = new ArrayList<>(); groupCodes.addAll(currentListUser.getGlobalGroups()); groupCodes.remove(groupCode); addUpdateUserCredential(currentListUser.withGlobalGroups(groupCodes)); } } } public List<UserCredential> getGlobalGroupActifUsers(String groupCode) { return userCredentialsManager.getUserCredentialsInGlobalGroup(groupCode); } public UserCredential getUser(String username) { UserCredential credential = userCredentialsManager.getUserCredential(username); if (credential == null) { throw new UserServicesRuntimeException_NoSuchUser(username); } return credential; } public User getUserInCollection(String username, String collection) { UserCredential userCredential = getUser(username); // Case insensitive username = userCredential.getUsername(); if (!userCredential.getCollections().contains(collection)) { throw new UserServicesRuntimeException_UserIsNotInCollection(username, collection); } return getUserRecordInCollection(username, collection); } public User getUserInCollectionCaseSensitive(String username, String collection) { return getUserRecordInCollection(username, collection); } public User getUserRecordInCollection(String username, String collection) { return User.wrapNullable(recordServices.getRecordByMetadata(usernameMetadata(collection), username), schemaTypes(collection), rolesManager.getCollectionRoles(collection)); } public GlobalGroup getGroup(String groupCode) { GlobalGroup group = globalGroupsManager.getGlobalGroupWithCode(groupCode); if (group == null) { throw new UserServicesRuntimeException_NoSuchGroup(groupCode); } return group; } public GlobalGroup getActiveGroup(String groupCode) { GlobalGroup group = globalGroupsManager.getActiveGlobalGroupWithCode(groupCode); if (group == null) { throw new UserServicesRuntimeException_NoSuchGroup(groupCode); } return group; } public Group getGroupInCollection(String groupCode, String collection) { LogicalSearchCondition condition = fromGroupsIn(collection).where(groupCodeMetadata(collection)).is(groupCode); return Group.wrapNullable(searchServices.searchSingleResult(condition), schemaTypes(collection)); } public void addGlobalGroupsInCollection(String collection) { List<GlobalGroup> groups = globalGroupsManager.getActiveGroups(); Set<String> addedGroups = new HashSet<>(); for (int i = 0; i < 1000 && addedGroups.size() != groups.size(); i++) { for (GlobalGroup globalGroup : groups) { if (!addedGroups.contains(globalGroup.getCode())) { if (globalGroup.getParent() == null || addedGroups.contains(globalGroup.getParent())) { sync(globalGroup); addedGroups.add(globalGroup.getCode()); } } } } } public void removeUserFromCollection(UserCredential userCredential, String collection) { //permissionValidateCredential(userCredential); userCredentialsManager.removeUserCredentialFromCollection(userCredential, collection); LogicalSearchCondition condition = fromUsersIn(collection) .where(usernameMetadata(collection)).is(userCredential.getUsername()); Record userCredentialRecord = searchServices.searchSingleResult(condition); recordServices.logicallyDelete(userCredentialRecord, User.GOD); } public void activateGlobalGroupHierarchy(UserCredential userCredential, GlobalGroup globalGroup) { permissionValidateCredential(userCredential); activateGlobalGroupHierarchyWithoutUserValidation(globalGroup); } private void activateGlobalGroupHierarchyWithoutUserValidation(GlobalGroup globalGroup) { List<String> collections = collectionsListManager.getCollections(); restoreGroupHierarchyInBigVault(globalGroup.getCode(), collections); globalGroupsManager.activateGlobalGroupHierarchy(globalGroup); } public void removeUserCredentialAndUser(UserCredential userCredential) { userCredential = userCredential.withStatus(UserCredentialStatus.DELETED); addUpdateUserCredential(userCredential); } public void setUserCredentialAndUserStatusPendingApproval(UserCredential userCredential) { userCredential = userCredential.withStatus(UserCredentialStatus.PENDING); addUpdateUserCredential(userCredential); } public void suspendUserCredentialAndUser(UserCredential userCredential) { userCredential = userCredential.withStatus(UserCredentialStatus.SUSPENDED); addUpdateUserCredential(userCredential); } public void activeUserCredentialAndUser(UserCredential userCredential) { userCredential = userCredential.withStatus(UserCredentialStatus.ACTIVE); addUpdateUserCredential(userCredential); List<String> collections = userCredential.getCollections(); restoreUserInBigVault(userCredential.getUsername(), collections); } private void restoreUserInBigVault(String username, List<String> collections) { LogicalSearchQuery query = new LogicalSearchQuery(); LogicalSearchCondition condition; for (String collection : collections) { condition = fromUsersIn(collection).where(usernameMetadata(collection)) .isEqualTo(username).andWhere(LOGICALLY_DELETED_STATUS).isTrue(); query.setCondition(condition); Record recordGroup = searchServices.searchSingleResult(condition); if (recordGroup != null) { recordServices.restore(recordGroup, User.GOD); } } } private void restoreGroupHierarchyInBigVault(String globalGroupCode, List<String> collections) { for (GlobalGroup group : globalGroupsManager.getHierarchy(globalGroupCode)) { LogicalSearchQuery query = new LogicalSearchQuery(); LogicalSearchCondition condition; for (String collection : collections) { condition = fromGroupsIn(collection) .where(groupCodeMetadata(collection)).isEqualTo(group.getCode()) .andWhere(LOGICALLY_DELETED_STATUS).isTrue(); query.setCondition(condition); Record recordGroup = searchServices.searchSingleResult(condition); if (recordGroup != null) { recordServices.restore(recordGroup, User.GOD); } } } } public void logicallyRemoveGroupHierarchy(UserCredential userCredential, GlobalGroup globalGroup) { permissionValidateCredential(userCredential); logicallyRemoveGroupHierarchyWithoutUserValidation(globalGroup); } private void logicallyRemoveGroupHierarchyWithoutUserValidation(GlobalGroup globalGroup) { // List<String> collections = globalGroup.getUsersAutomaticallyAddedToCollections(); List<String> collections = collectionsListManager.getCollections(); List<UserCredential> users = getGlobalGroupActifUsers(globalGroup.getCode()); userCredentialsManager.removeGroup(globalGroup.getCode()); globalGroupsManager.logicallyRemoveGroup(globalGroup); removeGroupFromCollectionsWithoutUserValidation(globalGroup.getCode(), collections); syncUsersCredentials(users); } public void removeGroupFromCollections(UserCredential userCredential, String group, List<String> collections) { permissionValidateCredential(userCredential); removeGroupFromCollectionsWithoutUserValidation(group, collections); // removeChildren(group, collections); // removeFromBigVault(group, collections); } private void removeGroupFromCollectionsWithoutUserValidation(String group, List<String> collections) { removeChildren(group, collections); removeFromBigVault(group, collections); } private void removeChildren(String group, List<String> collections) { for (String collection : collections) { for (Group child : getChildrenOfGroupInCollection(group, collection)) { removeFromBigVault(child.getCode(), collections); removeChildren(child.getCode(), collections); } } } public Group createCustomGroupInCollectionWithCodeAndName(String collection, String code, String name) { MetadataSchemaTypes types = metadataSchemasManager.getSchemaTypes(collection); MetadataSchema schema = types.getSchemaType(Group.SCHEMA_TYPE).getDefaultSchema(); Record groupRecord = recordServices.newRecordWithSchema(schema); groupRecord.set(schema.getMetadata(Group.CODE), code); groupRecord.set(schema.getMetadata(Group.TITLE), name); try { recordServices.add(groupRecord); } catch (RecordServicesException e) { throw new UserServicesRuntimeException_CannotExcuteTransaction(e); } return new Group(groupRecord, types); } public void givenSystemAdminPermissionsToUser(UserCredential user) { addUpdateUserCredential(user.withSystemAdminPermission()); } public String giveNewServiceToken(UserCredential user) { UserCredential modifiedUser = user.withServiceKey(secondaryUniqueIdGenerator.next()); addUpdateUserCredential(modifiedUser); return modifiedUser.getServiceKey(); } public void sync(UserCredential user) { List<String> availableCollections = collectionsListManager.getCollectionsExcludingSystem(); List<String> removedCollections = new ArrayList<>(); for (String collection : user.getCollections()) { if (availableCollections.contains(collection)) { Transaction transaction = new Transaction().setSkippingReferenceToLogicallyDeletedValidation(true); sync(user, collection, transaction); try { recordServices.execute(transaction); } catch (RecordServicesException e) { throw new UserServicesRuntimeException_CannotExcuteTransaction(e); } } else { removedCollections.add(collection); LOGGER.warn("User '" + user.getUsername() + "' is in invalid collection '" + collection + "'"); } } if (!removedCollections.isEmpty()) { List<String> collections = new ArrayList<>(user.getCollections()); collections.removeAll(removedCollections); addUpdateUserCredential(user.withCollections(collections)); } } public boolean canAddOrModifyUserAndGroup() { return !(ldapConfigurationManager.isLDAPAuthentication() && ldapConfigurationManager.idUsersSynchActivated()); } public boolean canModifyPassword(UserCredential userInEdition, UserCredential currentUser) { return (userInEdition.getUsername().equals("admin") && currentUser.getUsername().equals("admin")) || !ldapConfigurationManager.isLDAPAuthentication(); } public boolean isLDAPAuthentication() { return ldapConfigurationManager.isLDAPAuthentication(); } private List<String> toUserNames(List<UserCredential> users) { List<String> usernames = new ArrayList<>(); for (UserCredential user : users) { usernames.add(user.getUsername()); } return usernames; } private void sync(UserCredential user, String collection, Transaction transaction) { User userInCollection = getUserInCollection(user.getUsername(), collection); if (userInCollection == null) { userInCollection = newUserInCollection(collection); } else { userInCollection.set(CommonMetadataBuilder.LOGICALLY_DELETED, false); } userInCollection.setEmail(StringUtils.isBlank(user.getEmail()) ? null : user.getEmail()); if (userInCollection.getSchema().hasMetadataWithCode(SolrUserCredential.PERSONAL_EMAILS)) { userInCollection.setPersonalEmails(isEmpty(user.getPersonalEmails()) ? null : user.getPersonalEmails()); } userInCollection.setFirstName(user.getFirstName()); userInCollection.setLastName(user.getLastName()); userInCollection.setUsername(user.getUsername()); userInCollection.setSystemAdmin(user.isSystemAdmin()); try { userInCollection.setPhone(user.getPhone()); userInCollection.setJobTitle(user.getJobTitle()); userInCollection.setAddress(user.getAddress()); userInCollection.setFax(user.getFax()); } catch (MetadataSchemasRuntimeException.NoSuchMetadata e) { //Normal with versions before 6.2 } setRoles(userInCollection); changeUserStatus(userInCollection, user); List<String> groupIds = getGroupIds(user.getGlobalGroups(), collection); List<String> UserInCollectionGroupIds = userInCollection.getUserGroups(); if (!hasSameGroups(groupIds, UserInCollectionGroupIds)) { userInCollection.setUserGroups(groupIds); } if (userInCollection.isDirty()) { transaction.add(userInCollection.getWrappedRecord()); } } private void setRoles(User userInCollection) { if (userInCollection.getUserRoles() != null && userInCollection.getUserRoles().isEmpty()) { try { Role role = rolesManager.getRole(userInCollection.getCollection(), "U"); if (role != null) { userInCollection.setUserRoles("U"); } } catch (RolesManagerRuntimeException e) { // } } } private void changeUserStatus(User userInCollection, UserCredential userCredential) { if (userCredential.getStatus() != null) { userInCollection.setStatus(userCredential.getStatus()); if (userCredential.getStatus() == UserCredentialStatus.ACTIVE) { userInCollection.set("deleted", false); } else { validateAdminIsActive(userCredential); userInCollection.set("deleted", true); } } } List<String> getGroupIds(List<String> groupCodes, String collection) { List<String> groupIds = new ArrayList<>(); for (String groupCode : groupCodes) { String groupId = getGroupIdInCollection(groupCode, collection); if (groupId != null) { groupIds.add(groupId); //throw new ImpossibleRuntimeException("No group with code '" + groupCode + "' in collection '" + collection + "'"); } } return groupIds; } String getGroupIdInCollection(String groupCode, String collection) { Group group = getGroupInCollection(groupCode, collection); return group == null ? null : group.getId(); } private boolean hasSameGroups(List<String> groupIds1, List<String> groupIds2) { ListComparisonResults<String> comparisonResults = new LangUtils().compare(groupIds1, groupIds2); return comparisonResults.getNewItems().isEmpty() && comparisonResults.getRemovedItems().isEmpty(); } public void sync(GlobalGroup group) { Transaction transaction; for (String collection : collectionsListManager.getCollections()) { transaction = new Transaction(); sync(group, collection, transaction); try { recordServices.execute(transaction); } catch (RecordServicesException e) { throw new UserServicesRuntimeException_CannotExcuteTransaction(e); } } } private void sync(GlobalGroup group, String collection, Transaction transaction) { String groupCode = group.getCode(); Group groupInCollection = getGroupInCollection(groupCode, collection); if (groupInCollection == null) { groupInCollection = newGroupInCollection(collection); } groupInCollection.set(Group.CODE, group.getCode()); String parentId = getGroupParentId(group, collection); groupInCollection.set(Group.PARENT, parentId); groupInCollection.set(Group.IS_GLOBAL, true); groupInCollection.setTitle(group.getName()); if (groupInCollection.isDirty()) { transaction.add(groupInCollection.getWrappedRecord()); } } private String getGroupParentId(GlobalGroup group, String collection) { String parentId = null; if (group.getParent() != null) { parentId = getGroupIdInCollection(group.getParent(), collection); if (parentId == null) { throw new ImpossibleRuntimeException("No group with code '" + parentId + "' in collection '" + collection + "'"); } } return parentId; } private OngoingLogicalSearchCondition fromUsersIn(String collection) { return from(userSchema(collection)); } private OngoingLogicalSearchCondition fromGroupsIn(String collection) { return from(groupSchema(collection)); } private MetadataSchemaTypes schemaTypes(String collection) { return metadataSchemasManager.getSchemaTypes(collection); } MetadataSchema userSchema(String collection) { MetadataSchemaTypes schemaTypes = schemaTypes(collection); return schemaTypes.getSchema(User.SCHEMA_TYPE + "_default"); } Metadata usernameMetadata(String collection) { return userSchema(collection).getMetadata(User.USERNAME); } Metadata userGroupsMetadata(String collection) { return userSchema(collection).getMetadata(User.GROUPS); } MetadataSchema groupSchema(String collection) { return schemaTypes(collection).getSchema(Group.SCHEMA_TYPE + "_default"); } Metadata groupCodeMetadata(String collection) { return groupSchema(collection).getMetadata(Group.CODE); } Metadata groupParentMetadata(String collection) { return groupSchema(collection).getMetadata(Group.PARENT); } User newUserInCollection(String collection) { Record record = recordServices.newRecordWithSchema(userSchema(collection)); return new User(record, schemaTypes(collection), rolesManager.getCollectionRoles(collection)); } Group newGroupInCollection(String collection) { Record record = recordServices.newRecordWithSchema(groupSchema(collection)); return new Group(record, schemaTypes(collection)); } List<Group> getAllGroupsInCollections(String collection) { MetadataSchemaTypes collectionTypes = metadataSchemasManager.getSchemaTypes(collection); LogicalSearchQuery query = new LogicalSearchQuery(allGroups(collectionTypes).returnAll()); query.filteredByStatus(StatusFilter.ACTIVES); return Group.wrap(searchServices.search(query), collectionTypes); } public List<Group> getCollectionGroups(String collection) { MetadataSchemaTypes collectionTypes = metadataSchemasManager.getSchemaTypes(collection); LogicalSearchQuery query = new LogicalSearchQuery(allGroupsWhereGlobalGroupFlag(collectionTypes).isFalseOrNull()); query.filteredByStatus(StatusFilter.ACTIVES); return Group.wrap(searchServices.search(query), collectionTypes); } List<Group> getGlobalGroupsInCollections(String collection) { MetadataSchemaTypes collectionTypes = metadataSchemasManager.getSchemaTypes(collection); LogicalSearchQuery query = new LogicalSearchQuery(allGroupsWhereGlobalGroupFlag(collectionTypes).isTrue()); return Group.wrap(searchServices.search(query), collectionTypes); } OngoingLogicalSearchCondition allGroups(MetadataSchemaTypes types) { MetadataSchema groupSchema = types.getSchemaType(Group.SCHEMA_TYPE).getDefaultSchema(); return from(groupSchema); } OngoingLogicalSearchConditionWithDataStoreFields allGroupsWhereGlobalGroupFlag(MetadataSchemaTypes types) { MetadataSchema groupSchema = types.getSchemaType(Group.SCHEMA_TYPE).getDefaultSchema(); Metadata isGlobalGroup = groupSchema.getMetadata(Group.IS_GLOBAL); return from(groupSchema).where(isGlobalGroup); } private void syncUsersCredentials(List<UserCredential> users) { for (UserCredential userCredential : users) { UserCredential userCredentialUpdated = getUser(userCredential.getUsername()); sync(userCredentialUpdated); } } private void removeFromBigVault(String group, List<String> collections) { for (String collection : collections) { MetadataSchemaTypes types = metadataSchemasManager.getSchemaTypes(collection); LogicalSearchCondition condition = from(types.getSchemaType(Group.SCHEMA_TYPE)) .where(groupCodeMetadata(collection)).isEqualTo(group); Record recordGroup = searchServices.searchSingleResult(condition); if (recordGroup != null) { recordServices.logicallyDelete(recordGroup, User.GOD); } } } private void permissionValidateCredential(UserCredential userCredential) { if (userCredential == null || !userCredential.isSystemAdmin()) { String username = null; if (userCredential != null) { username = userCredential.getUsername(); } throw new UserServicesRuntimeException_UserPermissionDeniedToDelete(username); } } public void removeUserFromGlobalGroup(String username, String globalGroupCode) { UserCredential user = getUser(username); List<String> newGlobalGroups = new ArrayList<>(); newGlobalGroups.addAll(user.getGlobalGroups()); if (!newGlobalGroups.remove(globalGroupCode)) { throw new UserServicesRuntimeException_NoSuchGroup(globalGroupCode); } addUpdateUserCredential(user.withGlobalGroups(newGlobalGroups)); } public String getToken(String serviceKey, String username, String password) { ReadableDuration tokenDuration = modelLayerConfiguration.getTokenDuration(); return getToken(serviceKey, username, password, tokenDuration); } public String getToken(String serviceKey, String username, String password, ReadableDuration duration) { if (authenticationService.authenticate(username, password) && serviceKey.equals(getUser(username).getServiceKey())) { String token = generateToken(username, duration); return token; } else { throw new UserServicesRuntimeException_InvalidUserNameOrPassword(username); } } public String getToken(String serviceKey, String token) { if (userCredentialsManager.getServiceKeyByToken(token) != null) { userCredentialsManager.removeToken(token); String username = userCredentialsManager.getUsernameByServiceKey(serviceKey); String newToken = generateToken(username); return newToken; } else { throw new UserServicesRuntimeException_InvalidToken(); } } public String generateToken(String username) { ReadableDuration duration = modelLayerConfiguration.getTokenDuration(); return generateToken(username, duration); } public String generateToken(String username, ReadableDuration duration) { String token = secondaryUniqueIdGenerator.next(); LocalDateTime expiry = TimeProvider.getLocalDateTime().plus(duration); UserCredential userCredential = getUser(username).withAccessToken(token, expiry); userCredentialsManager.addUpdate(userCredential); return token; } public String generateToken(String username, String unitTime, int duration) { String token = secondaryUniqueIdGenerator.next(); LocalDateTime expiry = unitTime.equals("hours") ? TimeProvider.getLocalDateTime().plusHours(duration) : TimeProvider.getLocalDateTime().plusDays(duration); UserCredential userCredential = getUser(username).withAccessToken(token, expiry); userCredentialsManager.addUpdate(userCredential); return token; } void validateAdminIsActive(UserCredential userCredential) { if (userCredential.getUsername().equals(ADMIN) && userCredential.getStatus() != UserCredentialStatus.ACTIVE) { throw new UserServicesRuntimeException_CannotRemoveAdmin(); } } public String getTokenUser(String serviceKey, String token) { String retrivedServiceKey = getServiceKeyByToken(token); if (retrivedServiceKey == null || !retrivedServiceKey.equals(serviceKey)) { throw new UserServicesRuntimeException_InvalidToken(); } return userCredentialsManager.getUsernameByServiceKey(serviceKey); } public String getServiceKeyByToken(String token) { String retrivedServiceKey = userCredentialsManager.getServiceKeyByToken(token); if (retrivedServiceKey != null) { return retrivedServiceKey; } else { throw new UserServicesRuntimeException_InvalidToken(); } } public void removeToken(String token) { userCredentialsManager.removeToken(token); } public String getUserCredentialByServiceKey(String serviceKey) { return userCredentialsManager.getUsernameByServiceKey(serviceKey); } public UserCredential getUserCredential(String username) { return userCredentialsManager.getUserCredential(username); } public List<UserCredential> getActiveUserCredentials() { return userCredentialsManager.getActiveUserCredentials(); } public List<UserCredential> getAllUserCredentials() { return userCredentialsManager.getUserCredentials(); } public List<Group> getChildrenOfGroupInCollection(String groupParentCode, String collection) { List<Group> groups = new ArrayList<>(); String parentId = getGroupIdInCollection(groupParentCode, collection); LogicalSearchCondition condition = from(groupSchema(collection)) .where(groupParentMetadata(collection)) .is(parentId).andWhere(LOGICALLY_DELETED_STATUS).isFalseOrNull(); LogicalSearchQuery query = new LogicalSearchQuery().setCondition(condition); for (Record record : searchServices.search(query)) { groups.add(Group.wrapNullable(record, schemaTypes(collection))); } return groups; } public CredentialUserPermissionChecker has(User user) { return has(user.getUsername()); } public CredentialUserPermissionChecker has(UserCredential userCredential) { return has(userCredential.getUsername()); } public CredentialUserPermissionChecker has(String username) { List<User> users = new ArrayList<>(); UserCredential user = getUser(username); for (String collection : user.getCollections()) { users.add(getUserInCollection(username, collection)); } return new CredentialUserPermissionChecker(users); } public List<User> getAllUsersInCollection(String collection) { List<User> usersInCollection = new ArrayList<>(); for (UserCredential userCredential : getAllUserCredentials()) { if (userCredential.getCollections().contains(collection)) { usersInCollection.add(getUserInCollection(userCredential.getUsername(), collection)); } } return usersInCollection; } public boolean isAuthenticated(String userServiceKey, String userToken) { if (userToken == null || userServiceKey == null) { return false; } else { try { String tokenServiceKey = getServiceKeyByToken(userToken); return tokenServiceKey != null && tokenServiceKey.equals(userServiceKey); } catch (UserServicesRuntimeException_InvalidToken e) { return false; } } } public List<GlobalGroup> safePhysicalDeleteAllUnusedGlobalGroups() { return physicallyRemoveGlobalGroup(globalGroupsManager.getAllGroups().toArray(new GlobalGroup[0])); } public List<GlobalGroup> physicallyRemoveGlobalGroup(GlobalGroup... globalGroups) { List<GlobalGroup> groupWithUserList = new ArrayList<>(); for (GlobalGroup group : globalGroups) { List<UserCredential> userInGroup = this.getGlobalGroupActifUsers(group.getCode()); if ((group.getStatus().equals(GlobalGroupStatus.INACTIVE) && userInGroup.size() == 0)) { globalGroupsManager.logicallyRemoveGroup(group); recordServices.physicallyDelete(((SolrGlobalGroup) group).getWrappedRecord(), User.GOD); } else if (userInGroup.size() != 0) { groupWithUserList.add(group); } } return groupWithUserList; } public List<Group> safePhysicalDeleteAllUnusedGroups(String collection) { List<Group> nonDeletedGroups = new ArrayList<>(); MetadataSchemaTypes collectionTypes = metadataSchemasManager.getSchemaTypes(collection); LogicalSearchQuery query = new LogicalSearchQuery(allGroups(collectionTypes).returnAll()); query.filteredByStatus(StatusFilter.DELETED); List<Group> deletedGroups = Group.wrap(searchServices.search(query), collectionTypes); for (Group group : deletedGroups) { LOGGER.info("safePhysicalDeleteAllUnusedGroups : " + group.getCode()); try { physicallyRemoveGroup(group, collection); } catch (UserServicesRuntimeException.UserServicesRuntimeException_CannotSafeDeletePhysically e) { LOGGER.warn("Exception on safePhysicalDeleteAllUnusedGroups : " + group.getCode()); nonDeletedGroups.add(group); } } return nonDeletedGroups; } public void physicallyRemoveGroup(Group group, String collection) { LOGGER.info("physicallyRemoveGroup : " + group.getCode()); List<Record> userInGroup = authorizationsServices.getUserRecordsInGroup(group.getWrappedRecord()); if (userInGroup.size() != 0 || searchServices.hasResults( from(metadataSchemasManager.getSchemaTypes(collection).getSchemaTypes()).where(Schemas.ALL_REFERENCES) .isEqualTo(group.getId()))) { LOGGER.warn("Exception on physicallyRemoveGroup : " + group.getCode()); throw new UserServicesRuntimeException.UserServicesRuntimeException_CannotSafeDeletePhysically(group.getCode()); } recordServices.logicallyDelete(group.getWrappedRecord(), User.GOD); recordServices.physicallyDelete(group.getWrappedRecord(), User.GOD); } public List<UserCredential> safePhysicalDeleteAllUnusedUserCredentials() { List<UserCredential> nonDeletedUsers = new ArrayList<>(); Predicate<UserCredential> filter = new Predicate<UserCredential>() { @Override public boolean apply(UserCredential input) { return input.getStatus().equals(UserCredentialStatus.DELETED); } }; List<UserCredential> userCredentials = this.getAllUserCredentials(); LOGGER.info("safePhysicalDeleteAllUnusedUsers getAllUserCredentials : " + userCredentials.size()); Collection<UserCredential> usersToDelete = Collections2.filter(userCredentials, filter); LOGGER.info("safePhysicalDeleteAllUnusedUsers usersToDelete : " + usersToDelete.size()); for (UserCredential credential : usersToDelete) { try { safePhysicalDeleteUserCredential(credential.getUsername()); } catch (UserServicesRuntimeException.UserServicesRuntimeException_CannotSafeDeletePhysically e) { nonDeletedUsers.add(credential); } } return nonDeletedUsers; } public void safePhysicalDeleteUserCredential(String username) throws UserServicesRuntimeException.UserServicesRuntimeException_CannotSafeDeletePhysically { LOGGER.info("safePhysicalDeleteUser : " + username); UserCredential user = getUser(username); for (String collection : user.getCollections()) { String userId = this.getUserInCollection(user.getUsername(), collection).getId(); if (searchServices.hasResults( from(metadataSchemasManager.getSchemaTypes(collection).getSchemaTypes()).where(Schemas.ALL_REFERENCES) .isEqualTo(userId))) { LOGGER.warn("Exception on safePhysicalDeleteUser : " + username); throw new UserServicesRuntimeException.UserServicesRuntimeException_CannotSafeDeletePhysically(username); } } recordServices.logicallyDelete(((SolrUserCredential) user).getWrappedRecord(), User.GOD); recordServices.physicallyDelete(((SolrUserCredential) user).getWrappedRecord(), User.GOD); } public List<User> safePhysicalDeleteAllUnusedUsers(String collection) { List<User> nonDeletedUsers = new ArrayList<>(); MetadataSchemaTypes collectionTypes = metadataSchemasManager.getSchemaTypes(collection); LogicalSearchQuery query = new LogicalSearchQuery( from(collectionTypes.getSchemaType(User.SCHEMA_TYPE).getDefaultSchema()).returnAll()); query.filteredByStatus(StatusFilter.DELETED); List<User> deletedUsers = new ArrayList<>(); for (Record record : searchServices.search(query)) { deletedUsers.add(new User(record, collectionTypes, null)); } for (User user : deletedUsers) { LOGGER.info("safePhysicalDeleteAllUnusedUsers : " + user.getUsername()); try { physicallyRemoveUser(user, collection); } catch (UserServicesRuntimeException.UserServicesRuntimeException_CannotSafeDeletePhysically e) { LOGGER.warn("Exception on safePhysicalDeleteAllUnusedUsers : " + user.getUsername()); nonDeletedUsers.add(user); } } return nonDeletedUsers; } public void physicallyRemoveUser(User user, String collection) { LOGGER.info("physicallyRemoveUser : " + user.getUsername()); if (searchServices.hasResults( from(metadataSchemasManager.getSchemaTypes(collection).getSchemaTypes()).where(Schemas.ALL_REFERENCES) .isEqualTo(user.getId()))) { LOGGER.warn("Exception on physicallyRemoveUser : " + user.getUsername()); throw new UserServicesRuntimeException.UserServicesRuntimeException_CannotSafeDeletePhysically(user.getUsername()); } recordServices.logicallyDelete(user.getWrappedRecord(), User.GOD); recordServices.physicallyDelete(user.getWrappedRecord(), User.GOD); } public void restoreDeletedGroup(String groupCode, String collection) { GlobalGroup globalGroup = globalGroupsManager.getGlobalGroupWithCode(groupCode); if (globalGroup.getStatus().equals(GlobalGroupStatus.INACTIVE)) { globalGroupsManager.addUpdate(globalGroup.withStatus(GlobalGroupStatus.ACTIVE)); } MetadataSchemaTypes collectionTypes = metadataSchemasManager.getSchemaTypes(collection); MetadataSchema groupSchema = collectionTypes.getSchemaType(Group.SCHEMA_TYPE).getDefaultSchema(); LogicalSearchCondition condition = fromGroupsIn(collection).where(groupCodeMetadata(collection)).is(groupCode); LogicalSearchQuery query = new LogicalSearchQuery(condition); query.filteredByStatus(StatusFilter.DELETED); List<Group> groups = Group.wrap(searchServices.search(query), collectionTypes); for (Group group : groups) { LOGGER.info("restoreDeletedGroup : " + group.getCode()); recordServices.restore(group.getWrappedRecord(), User.GOD); } } }