From ad84e16ed977d767b392e88111ad45fef122ad31 Mon Sep 17 00:00:00 2001 From: felmer <franz-josef.elmer@id.ethz.ch> Date: Thu, 26 Apr 2018 08:54:57 +0200 Subject: [PATCH] SSDM-6061: Audit log introduced. More tests more functionality, but not yet complete --- .../task/UserManagementMaintenanceTask.java | 136 ++++--- .../generic/server/task/UserManager.java | 312 ++++++++++++---- .../server/task/UserManagerReport.java | 101 ++++++ .../task/UserManagerExpectationsBuilder.java | 88 ++++- .../systemtest/task/UserManagerTest.java | 339 +++++++++++++++++- 5 files changed, 836 insertions(+), 140 deletions(-) create mode 100644 openbis/source/java/ch/systemsx/cisd/openbis/generic/server/task/UserManagerReport.java diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/task/UserManagementMaintenanceTask.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/task/UserManagementMaintenanceTask.java index d6352988338..494c616e734 100644 --- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/task/UserManagementMaintenanceTask.java +++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/task/UserManagementMaintenanceTask.java @@ -27,6 +27,7 @@ import org.apache.log4j.Logger; import com.fasterxml.jackson.databind.ObjectMapper; +import ch.ethz.sis.openbis.generic.server.asapi.v3.IApplicationServerInternalApi; import ch.systemsx.cisd.authentication.Principal; import ch.systemsx.cisd.authentication.ldap.LDAPAuthenticationService; import ch.systemsx.cisd.common.exceptions.ConfigurationFailureException; @@ -35,6 +36,8 @@ import ch.systemsx.cisd.common.logging.Log4jSimpleLogger; import ch.systemsx.cisd.common.logging.LogCategory; import ch.systemsx.cisd.common.logging.LogFactory; import ch.systemsx.cisd.common.maintenance.IMaintenanceTask; +import ch.systemsx.cisd.common.utilities.ITimeAndWaitingProvider; +import ch.systemsx.cisd.common.utilities.SystemTimeProvider; import ch.systemsx.cisd.openbis.generic.server.CommonServiceProvider; /** @@ -42,18 +45,24 @@ import ch.systemsx.cisd.openbis.generic.server.CommonServiceProvider; */ public class UserManagementMaintenanceTask implements IMaintenanceTask { - private static final String CONFIGURATION_FILE_PATH_PROPERTY = "configuration-file-path"; + static final String CONFIGURATION_FILE_PATH_PROPERTY = "configuration-file-path"; - private static final String DEFAULT_CONFIGURATION_FILE_PATH = "etc/user-management-maintenance-config.json"; + static final String DEFAULT_CONFIGURATION_FILE_PATH = "etc/user-management-maintenance-config.json"; + + static final String AUDIT_LOG_FILE_PATH_PROPERTY = "audit-log-file-path"; + + static final String DEFAULT_AUDIT_LOG_FILE_PATH = "logs/user-management-audit_log.txt"; private static final Logger operationLog = LogFactory.getLogger(LogCategory.OPERATION, UserManagementMaintenanceTask.class); private static final Logger notificationLog = LogFactory.getLogger(LogCategory.NOTIFY, UserManagementMaintenanceTask.class); - + private File configurationFile; - + + private File auditLogFile; + private LDAPAuthenticationService ldapService; @Override @@ -66,8 +75,12 @@ public class UserManagementMaintenanceTask implements IMaintenanceTask throw new ConfigurationFailureException("Configuration file '" + configurationFile.getAbsolutePath() + "' doesn't exist or is a directory."); } - - ldapService = (LDAPAuthenticationService) CommonServiceProvider.getApplicationContext().getBean("ldap-authentication-service"); + auditLogFile = new File(properties.getProperty(AUDIT_LOG_FILE_PATH_PROPERTY, DEFAULT_AUDIT_LOG_FILE_PATH)); + if (auditLogFile.isDirectory()) + { + throw new ConfigurationFailureException("Audit log file '" + auditLogFile.getAbsolutePath() + "' is a directory."); + } + ldapService = getLdapAuthenticationService(); if (ldapService.isConfigured() == false) { throw new ConfigurationFailureException("There is no LDAP authentication service configured. " @@ -75,7 +88,7 @@ public class UserManagementMaintenanceTask implements IMaintenanceTask + "'ldap.security.principal.password' have to be specified in 'service.properties'."); } operationLog.info("Plugin '" + pluginName + "' initialized. Configuration file: " + configurationFile.getAbsolutePath()); - + } @Override @@ -87,46 +100,20 @@ public class UserManagementMaintenanceTask implements IMaintenanceTask return; } Log4jSimpleLogger logger = new Log4jSimpleLogger(operationLog); - UserManager userManager = new UserManager(CommonServiceProvider.getApplicationServerApi(), config.getCommonSpaces(), logger); + UserManager userManager = new UserManager(ldapService, getService(), + config.getCommonSpaces(), logger, getTimeProvider()); for (UserGroup group : config.getGroups()) { - String key = group.getKey(); - List<String> ldapGroupKeys = group.getLdapGroupKeys(); - if (ldapGroupKeys == null || ldapGroupKeys.isEmpty()) + if (addGroup(userManager, group) == false) { - operationLog.error("No ldapGroupKeys specified for group '" + key + "'. Task aborted."); return; } - Map<String, Principal> principalsByUserId = new TreeMap<>(); - for (String ldapGroupKey : ldapGroupKeys) - { - if (StringUtils.isBlank(ldapGroupKey)) - { - operationLog.error("Empty ldapGroupKey for group '" + key + "'. Task aborted."); - return; - - } - List<Principal> principals = ldapService.listPrincipalsByKeyValue("ou", ldapGroupKey); - if (principals.isEmpty()) - { - operationLog.error("No users found for ldapGroupKey '" + ldapGroupKey + "' for group '" + key + "'. Task aborted."); - return; - } - for (Principal principal : principals) - { - principalsByUserId.put(principal.getUserId(), principal); - } - } - userManager.addGroup(key, group, principalsByUserId); - } - String errorReport = userManager.manageUsers(); - if (StringUtils.isNotBlank(errorReport)) - { - notificationLog.error("User management failed for the following reasons:\n\n" + errorReport); } + UserManagerReport userManagerReport = userManager.manage(); + handleReport(userManagerReport); operationLog.info("finished"); } - + private UserManagerConfig readGroupDefinitions() { if (configurationFile.isFile() == false) @@ -137,7 +124,8 @@ public class UserManagementMaintenanceTask implements IMaintenanceTask String serializedConfig = FileUtilities.loadToString(configurationFile); try { - return deserialize(serializedConfig); + ObjectMapper mapper = new ObjectMapper(); + return mapper.readValue(serializedConfig, UserManagerConfig.class); } catch (Exception e) { operationLog.error("Invalid content of configuration file '" + configurationFile.getAbsolutePath() + "': " + e, e); @@ -145,9 +133,71 @@ public class UserManagementMaintenanceTask implements IMaintenanceTask } } - private UserManagerConfig deserialize(String serializedConfig) throws Exception + private boolean addGroup(UserManager userManager, UserGroup group) + { + String key = group.getKey(); + List<String> ldapGroupKeys = group.getLdapGroupKeys(); + if (ldapGroupKeys == null || ldapGroupKeys.isEmpty()) + { + operationLog.error("No ldapGroupKeys specified for group '" + key + "'. Task aborted."); + return false; + } + Map<String, Principal> principalsByUserId = new TreeMap<>(); + for (String ldapGroupKey : ldapGroupKeys) + { + if (StringUtils.isBlank(ldapGroupKey)) + { + operationLog.error("Empty ldapGroupKey for group '" + key + "'. Task aborted."); + return false; + + } + List<Principal> principals = getUsersOfGroup(ldapGroupKey); + if (principals.isEmpty()) + { + operationLog.error("No users found for ldapGroupKey '" + ldapGroupKey + "' for group '" + key + "'. Task aborted."); + return false; + } + for (Principal principal : principals) + { + principalsByUserId.put(principal.getUserId(), principal); + } + } + userManager.addGroup(group, principalsByUserId); + return true; + } + + private void handleReport(UserManagerReport report) { - ObjectMapper mapper = new ObjectMapper(); - return mapper.readValue(serializedConfig, UserManagerConfig.class); + String errorReport = report.getErrorReport(); + if (StringUtils.isNotBlank(errorReport)) + { + notificationLog.error("User management failed for the following reason(s):\n\n" + errorReport); + } + String auditLog = report.getAuditLog(); + if (StringUtils.isNotBlank(auditLog)) + { + FileUtilities.appendToFile(auditLogFile, auditLog, true); + } } + + protected List<Principal> getUsersOfGroup(String ldapGroupKey) + { + return ldapService.listPrincipalsByKeyValue("ou", ldapGroupKey); + } + + protected LDAPAuthenticationService getLdapAuthenticationService() + { + return (LDAPAuthenticationService) CommonServiceProvider.getApplicationContext().getBean("ldap-authentication-service"); + } + + protected IApplicationServerInternalApi getService() + { + return CommonServiceProvider.getApplicationServerApi(); + } + + protected ITimeAndWaitingProvider getTimeProvider() + { + return SystemTimeProvider.SYSTEM_TIME_PROVIDER; + } + } diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/task/UserManager.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/task/UserManager.java index fbd1ddfbaaa..f591664a7fe 100644 --- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/task/UserManager.java +++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/task/UserManager.java @@ -34,7 +34,6 @@ import ch.ethz.sis.openbis.generic.asapi.v3.dto.authorizationgroup.create.Author import ch.ethz.sis.openbis.generic.asapi.v3.dto.authorizationgroup.create.CreateAuthorizationGroupsOperation; import ch.ethz.sis.openbis.generic.asapi.v3.dto.authorizationgroup.fetchoptions.AuthorizationGroupFetchOptions; import ch.ethz.sis.openbis.generic.asapi.v3.dto.authorizationgroup.id.AuthorizationGroupPermId; -import ch.ethz.sis.openbis.generic.asapi.v3.dto.authorizationgroup.id.IAuthorizationGroupId; import ch.ethz.sis.openbis.generic.asapi.v3.dto.authorizationgroup.update.AuthorizationGroupUpdate; import ch.ethz.sis.openbis.generic.asapi.v3.dto.authorizationgroup.update.UpdateAuthorizationGroupsOperation; import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.operation.IOperation; @@ -45,6 +44,7 @@ import ch.ethz.sis.openbis.generic.asapi.v3.dto.person.create.PersonCreation; import ch.ethz.sis.openbis.generic.asapi.v3.dto.person.fetchoptions.PersonFetchOptions; import ch.ethz.sis.openbis.generic.asapi.v3.dto.person.id.IPersonId; import ch.ethz.sis.openbis.generic.asapi.v3.dto.person.id.PersonPermId; +import ch.ethz.sis.openbis.generic.asapi.v3.dto.person.search.PersonSearchCriteria; import ch.ethz.sis.openbis.generic.asapi.v3.dto.person.update.PersonUpdate; import ch.ethz.sis.openbis.generic.asapi.v3.dto.person.update.UpdatePersonsOperation; import ch.ethz.sis.openbis.generic.asapi.v3.dto.roleassignment.Role; @@ -57,35 +57,48 @@ import ch.ethz.sis.openbis.generic.asapi.v3.dto.space.fetchoptions.SpaceFetchOpt import ch.ethz.sis.openbis.generic.asapi.v3.dto.space.id.ISpaceId; import ch.ethz.sis.openbis.generic.asapi.v3.dto.space.id.SpacePermId; import ch.ethz.sis.openbis.generic.server.asapi.v3.IApplicationServerInternalApi; +import ch.systemsx.cisd.authentication.IAuthenticationService; import ch.systemsx.cisd.authentication.Principal; import ch.systemsx.cisd.common.logging.ISimpleLogger; import ch.systemsx.cisd.common.logging.LogLevel; +import ch.systemsx.cisd.common.utilities.ITimeProvider; /** * @author Franz-Josef Elmer */ public class UserManager { + private final IAuthenticationService authenticationService; + private final IApplicationServerInternalApi service; private final ISimpleLogger logger; + private final ITimeProvider timeProvider; + private final Map<Role, List<String>> commonSpacesByRole; private final Map<String, UserInfo> userInfosByUserId = new TreeMap<>(); + private final Map<String, Map<String, Principal>> usersByGroupCode = new TreeMap<>(); + private final List<String> groupCodes = new ArrayList<>(); - public UserManager(IApplicationServerInternalApi service, Map<Role, List<String>> commonSpacesByRole, ISimpleLogger logger) + public UserManager(IAuthenticationService authenticationService, IApplicationServerInternalApi service, + Map<Role, List<String>> commonSpacesByRole, ISimpleLogger logger, ITimeProvider timeProvider) { + this.authenticationService = authenticationService; this.service = service; this.commonSpacesByRole = commonSpacesByRole; this.logger = logger; + this.timeProvider = timeProvider; } - public void addGroup(String key, UserGroup group, Map<String, Principal> principalsByUserId) + public void addGroup(UserGroup group, Map<String, Principal> principalsByUserId) { - groupCodes.add(key.toUpperCase()); + String groupCode = group.getKey().toUpperCase(); + usersByGroupCode.put(groupCode, principalsByUserId); + groupCodes.add(groupCode); Set<String> admins = asSet(group.getAdmins()); for (Principal principal : principalsByUserId.values()) { @@ -96,70 +109,150 @@ public class UserManager userInfo = new UserInfo(principal); userInfosByUserId.put(userId, userInfo); } - userInfo.addGroupInfo(new GroupInfo(key, admins.contains(userId))); + userInfo.addGroupInfo(new GroupInfo(groupCode, admins.contains(userId))); } - logger.log(LogLevel.INFO, principalsByUserId.size() + " users for group " + key); + logger.log(LogLevel.INFO, principalsByUserId.size() + " users for group " + groupCode); } - public String manageUsers() + public UserManagerReport manage() { - String sessionToken = service.loginAsSystem(); - Map<IPersonId, Person> users = getUsersWithRoleAssigments(sessionToken); - Map<IAuthorizationGroupId, AuthorizationGroup> authorizationGroups = getAuthorizationGroups(sessionToken); - StringBuilder builder = new StringBuilder(); + UserManagerReport report = new UserManagerReport(timeProvider); + try + { + String sessionToken = service.loginAsSystem(); + + manageGroups(sessionToken, report); + manageUsers(sessionToken, report); + + service.logout(sessionToken); + } catch (Throwable e) + { + report.addErrorMessage("Error: " + e.toString()); + logger.log(LogLevel.ERROR, "", e); + } + return report; + } + + private void manageGroups(String sessionToken, UserManagerReport report) + { + Map<String, Set<String>> usersByGroupCodes = getUsersByGroupCodes(sessionToken); for (String groupCode : groupCodes) { try { - manageGroup(sessionToken, groupCode, authorizationGroups); + Context context = new Context(sessionToken); + if (usersByGroupCodes.containsKey(groupCode)) + { + manageKnownGroup(context, groupCode, report); + } else + { + manageNewGroup(context, groupCode, report); + } + context.executeOperations(); } catch (Exception e) { String message = String.format("Couldn't manage group '%s' because of the following error: %s", groupCode, e); - builder.append(message).append("\n"); + report.addErrorMessage(message); logger.log(LogLevel.ERROR, message, e); } } - authorizationGroups = getAuthorizationGroups(sessionToken); + } + + private void manageUsers(String sessionToken, UserManagerReport report) + { + revokeUsersUnkownByAuthenticationService(sessionToken, report); + Map<IPersonId, Person> users = getUsersWithRoleAssigments(sessionToken); + Map<String, Set<String>> currentUsersByGroupCodes = getUsersByGroupCodes(sessionToken); + Map<String, Set<String>> currentAdminUsersByGroupCodes = getAdminUsersByGroupCodes(sessionToken); for (UserInfo userInfo : userInfosByUserId.values()) { try { - manageUser(sessionToken, userInfo, users, authorizationGroups); + manageUser(sessionToken, userInfo, users, currentUsersByGroupCodes, currentAdminUsersByGroupCodes, report); } catch (Exception e) { String message = String.format("Couldn't manage user '%s' because of the following error: %s", userInfo.getPrincipal().getUserId(), e); - builder.append(message).append("\n"); + report.addErrorMessage(message); + logger.log(LogLevel.ERROR, message, e); + } + } + + for (String groupCode : currentUsersByGroupCodes.keySet()) + { + try + { + manageUsersRemovedFromGroup(sessionToken, groupCode, currentUsersByGroupCodes, + currentAdminUsersByGroupCodes, report); + } catch (Exception e) + { + String message = String.format("Couldn't manage users removed from group %s because of the following error: %s", + groupCode, e); + report.addErrorMessage(message); logger.log(LogLevel.ERROR, message, e); } } - service.logout(sessionToken); - return builder.toString(); } - private void manageGroup(String sessionToken, String groupCode, Map<IAuthorizationGroupId, AuthorizationGroup> knownAuthorizationGroups) + private void manageUsersRemovedFromGroup(String sessionToken, String groupCode, Map<String, Set<String>> currentUsersByGroupCodes, + Map<String, Set<String>> currentAdminUsersByGroupCodes, UserManagerReport report) { + Set<String> currentUsers = currentUsersByGroupCodes.get(groupCode); + Set<String> newUsers = usersByGroupCode.get(groupCode).keySet(); Context context = new Context(sessionToken); - AuthorizationGroup knownGroup = knownAuthorizationGroups.get(new AuthorizationGroupPermId(groupCode)); - if (knownGroup != null) + for (String currentUser : currentUsers) { - manageKnownGroup(context, knownGroup); - } else - { - manageNewGroup(context, groupCode); + if (newUsers.contains(currentUser) == false) + { + removePersonFromAuthorizationGroup(context, currentUsersByGroupCodes, groupCode, currentUser, report); + String adminGroupCode = createAdminGroupCode(groupCode); + removePersonFromAuthorizationGroup(context, currentAdminUsersByGroupCodes, adminGroupCode, currentUser, report); + } } context.executeOperations(); } - private void manageNewGroup(Context context, String groupCode) + private void revokeUsersUnkownByAuthenticationService(String sessionToken, UserManagerReport report) + { + List<PersonUpdate> updates = new ArrayList<>(); + List<Person> persons = service.searchPersons(sessionToken, new PersonSearchCriteria(), new PersonFetchOptions()).getObjects(); + for (Person person : persons) + { + if (person.isActive()) + { + try + { + authenticationService.getPrincipal(person.getUserId()); + } catch (IllegalArgumentException e) + { + PersonUpdate update = new PersonUpdate(); + update.setUserId(person.getPermId()); + update.deactivate(); + updates.add(update); + report.deactivateUser(person.getUserId()); + } + } + } + if (updates.isEmpty() == false) + { + service.updatePersons(sessionToken, updates); + } + } + + private void manageKnownGroup(Context context, String groupCode, UserManagerReport report) + { + // TODO Auto-generated method stub + } + + private void manageNewGroup(Context context, String groupCode, UserManagerReport report) { String adminGroupCode = createAdminGroupCode(groupCode); assertAuthorizationGroupDoesNotExist(context, adminGroupCode); assertNoCommonSpaceExists(context, groupCode); - createAuthorizationGroup(context, groupCode); - createAuthorizationGroup(context, adminGroupCode); + createAuthorizationGroup(context, groupCode, report); + createAuthorizationGroup(context, adminGroupCode, report); for (Entry<Role, List<String>> entry : commonSpacesByRole.entrySet()) { @@ -167,8 +260,9 @@ public class UserManager for (String space : entry.getValue()) { ISpaceId spaceId = createSpace(context, createCommonSpaceCode(groupCode, space)); - createRoleAssignment(context, new AuthorizationGroupPermId(groupCode), role, spaceId); - createRoleAssignment(context, new AuthorizationGroupPermId(adminGroupCode), Role.ADMIN, spaceId); + report.addSpace(spaceId); + createRoleAssignment(context, new AuthorizationGroupPermId(groupCode), role, spaceId, report); + createRoleAssignment(context, new AuthorizationGroupPermId(adminGroupCode), Role.ADMIN, spaceId, report); } } } @@ -199,57 +293,104 @@ public class UserManager throw new IllegalStateException("The group '" + groupCode + "' has already the following spaces: " + existingSpaces); } - private void manageKnownGroup(Context context, AuthorizationGroup knownGroup) - { - // TODO Auto-generated method stub - - } - private void manageUser(String sessionToken, UserInfo userInfo, Map<IPersonId, Person> knownUsers, - Map<IAuthorizationGroupId, AuthorizationGroup> knownAuthorizationGroups) + Map<String, Set<String>> currentUsersByGroupCodes, Map<String, Set<String>> currentAdminUsersByGroupCodes, + UserManagerReport report) { Context context = new Context(sessionToken); Person knownUser = knownUsers.get(new PersonPermId(userInfo.getPrincipal().getUserId())); if (knownUser != null) { - manageKnownUser(context, userInfo, knownUser, knownAuthorizationGroups); + manageKnownUser(context, userInfo, knownUser, currentUsersByGroupCodes, currentAdminUsersByGroupCodes, report); } else { - manageNewUser(context, userInfo, knownAuthorizationGroups); + manageNewUser(context, userInfo, userInfo.getPrincipal().getUserId().toUpperCase(), + currentUsersByGroupCodes, currentAdminUsersByGroupCodes, report); } context.executeOperations(); } private void manageKnownUser(Context context, UserInfo userInfo, Person knownUser, - Map<IAuthorizationGroupId, AuthorizationGroup> knownAuthorizationGroups) + Map<String, Set<String>> currentUsersByGroupCodes, Map<String, Set<String>> currentAdminUsersByGroupCodes, + UserManagerReport report) { - System.out.println("UserManager.manageKnownUser() " + userInfo); + if (knownUser.isActive() == false) + { + int maxSequenceNumber = getMaxSequenceNumber(context, knownUser); + String homeSpaceCode = knownUser.getUserId() + "_" + (maxSequenceNumber + 1); + manageNewUser(context, userInfo, homeSpaceCode, currentUsersByGroupCodes, currentAdminUsersByGroupCodes, report); + } else + { + String userId = userInfo.principal.getUserId(); + for (GroupInfo groupInfo : userInfo.getGroupInfosByGroupKey().values()) + { + String groupCode = groupInfo.getKey(); + addPersonToAuthorizationGroup(context, currentUsersByGroupCodes, groupCode, userId, report); + String adminGroupCode = createAdminGroupCode(groupCode); + if (groupInfo.isAdmin()) + { + addPersonToAuthorizationGroup(context, currentAdminUsersByGroupCodes, adminGroupCode, userId, report); + } else if (isCurrentAdminUser(currentAdminUsersByGroupCodes, adminGroupCode, userId)) + { + removePersonFromAuthorizationGroup(context, currentAdminUsersByGroupCodes, adminGroupCode, userId, report); + } + } + } } - private void manageNewUser(Context context, UserInfo userInfo, - Map<IAuthorizationGroupId, AuthorizationGroup> knownAuthorizationGroups) + private int getMaxSequenceNumber(Context context, Person knownUser) { - Principal principal = userInfo.getPrincipal(); - String userId = principal.getUserId(); - String spaceCode = userId.toUpperCase(); - if (getSpaces(context.getSessionToken(), Arrays.asList(spaceCode)).isEmpty() == false) + PersonSearchCriteria searchCriteria = new PersonSearchCriteria(); + searchCriteria.withUserId().thatStartsWith(knownUser.getUserId()); + PersonFetchOptions fetchOptions = new PersonFetchOptions(); + int maxSequenceNumber = 0; + for (Person person : service.searchPersons(context.getSessionToken(), searchCriteria, fetchOptions).getObjects()) { - throw new IllegalStateException("There is already a space with code " + spaceCode + "."); + String[] splittedUserId = person.getUserId().split("_"); + if (splittedUserId.length == 2) + { + try + { + maxSequenceNumber = Math.max(maxSequenceNumber, Integer.parseInt(splittedUserId[1])); + } catch (NumberFormatException e) + { + // silently ignored + } + } } + return maxSequenceNumber; + } - ISpaceId homeSpaceId = createSpace(context, spaceCode); + private boolean isCurrentAdminUser(Map<String, Set<String>> currentAdminUsersByGroupCodes, String groupCode, String userId) + { + Set<String> users = currentAdminUsersByGroupCodes.get(groupCode); + return users == null ? false : users.contains(userId); + } + + private void manageNewUser(Context context, UserInfo userInfo, String homeSpaceCode, + Map<String, Set<String>> currentUsersByGroupCodes, Map<String, Set<String>> currentAdminUsersByGroupCodes, + UserManagerReport report) + { + String userId = userInfo.getPrincipal().getUserId(); + if (getSpaces(context.getSessionToken(), Arrays.asList(homeSpaceCode)).isEmpty() == false) + { + throw new IllegalStateException("There is already a space with code " + homeSpaceCode + "."); + } + + ISpaceId homeSpaceId = createSpace(context, homeSpaceCode); IPersonId personId = createPerson(context, userId); assignHomeSpace(context, homeSpaceId, personId); + report.addUser(userId, homeSpaceCode); for (GroupInfo groupInfo : userInfo.getGroupInfosByGroupKey().values()) { String groupCode = groupInfo.getKey(); - addPersonToAuthorizationGroup(context, new AuthorizationGroupPermId(groupCode), personId); - AuthorizationGroupPermId adminGroupId = new AuthorizationGroupPermId(createAdminGroupCode(groupCode)); - createRoleAssignment(context, adminGroupId, Role.ADMIN, homeSpaceId); + addPersonToAuthorizationGroup(context, currentUsersByGroupCodes, groupCode, userId, report); + String adminGroupCode = createAdminGroupCode(groupCode); + createRoleAssignment(context, new AuthorizationGroupPermId(adminGroupCode), Role.ADMIN, homeSpaceId, report); if (groupInfo.isAdmin()) { - addPersonToAuthorizationGroup(context, adminGroupId, personId); + addPersonToAuthorizationGroup(context, currentAdminUsersByGroupCodes, adminGroupCode, userId, report); } } } @@ -267,12 +408,32 @@ public class UserManager context.add(roleCreation); } - private void addPersonToAuthorizationGroup(Context context, AuthorizationGroupPermId groupId, IPersonId personId) + private void addPersonToAuthorizationGroup(Context context, Map<String, Set<String>> currentUsersByGroupCodes, + String groupCode, String userId, UserManagerReport report) + { + Set<String> users = currentUsersByGroupCodes.get(groupCode); + if (users == null || users.contains(userId) == false) + { + AuthorizationGroupUpdate groupUpdate = new AuthorizationGroupUpdate(); + groupUpdate.setAuthorizationGroupId(new AuthorizationGroupPermId(groupCode)); + groupUpdate.getUserIds().add(new PersonPermId(userId)); + context.add(groupUpdate); + report.addUserToGroup(groupCode, userId); + } + } + + private void removePersonFromAuthorizationGroup(Context context, Map<String, Set<String>> currentUsersByGroupCodes, + String groupCode, String userId, UserManagerReport report) { - AuthorizationGroupUpdate groupUpdate = new AuthorizationGroupUpdate(); - groupUpdate.setAuthorizationGroupId(groupId); - groupUpdate.getUserIds().add(personId); - context.add(groupUpdate); + Set<String> users = currentUsersByGroupCodes.get(groupCode); + if (users == null || users.contains(userId)) + { + AuthorizationGroupUpdate groupUpdate = new AuthorizationGroupUpdate(); + groupUpdate.setAuthorizationGroupId(new AuthorizationGroupPermId(groupCode)); + groupUpdate.getUserIds().remove(new PersonPermId(userId)); + context.add(groupUpdate); + report.removeUserFromGroup(groupCode, userId); + } } private ISpaceId createSpace(Context context, String spaceCode) @@ -291,20 +452,23 @@ public class UserManager return new PersonPermId(userId); } - private void createAuthorizationGroup(Context context, String groupCode) + private void createAuthorizationGroup(Context context, String groupCode, UserManagerReport report) { AuthorizationGroupCreation creation = new AuthorizationGroupCreation(); creation.setCode(groupCode); context.add(creation); + report.addGroup(groupCode); } - private void createRoleAssignment(Context context, AuthorizationGroupPermId groupId, Role role, ISpaceId spaceId) + private void createRoleAssignment(Context context, AuthorizationGroupPermId groupId, Role role, ISpaceId spaceId, + UserManagerReport report) { RoleAssignmentCreation roleCreation = new RoleAssignmentCreation(); roleCreation.setAuthorizationGroupId(groupId); roleCreation.setRole(role); roleCreation.setSpaceId(spaceId); context.add(roleCreation); + report.assignRoleTo(groupId, role, spaceId); } private String createCommonSpaceCode(String groupCode, String spaceCode) @@ -312,11 +476,16 @@ public class UserManager return groupCode + "_" + spaceCode; } - private String createAdminGroupCode(String groupCode) + public static String createAdminGroupCode(String groupCode) { return groupCode + "_ADMIN"; } + private AuthorizationGroupPermId createAdminGroupId(String groupCode) + { + return new AuthorizationGroupPermId(createAdminGroupCode(groupCode)); + } + private Map<ISpaceId, Space> getSpaces(String sessionToken, Collection<String> spaceCodes) { SpaceFetchOptions fetchOptions = new SpaceFetchOptions(); @@ -333,13 +502,28 @@ public class UserManager return users; } - private Map<IAuthorizationGroupId, AuthorizationGroup> getAuthorizationGroups(String sessionToken) + private Map<String, Set<String>> getUsersByGroupCodes(String sessionToken) + { + return getUsersByGroupCodes(sessionToken, groupCode -> new AuthorizationGroupPermId(groupCode)); + } + + private Map<String, Set<String>> getAdminUsersByGroupCodes(String sessionToken) + { + return getUsersByGroupCodes(sessionToken, groupCode -> createAdminGroupId(groupCode)); + } + + private Map<String, Set<String>> getUsersByGroupCodes(String sessionToken, Function<String, AuthorizationGroupPermId> mapper) { - Function<String, AuthorizationGroupPermId> mapper = key -> new AuthorizationGroupPermId(key); List<AuthorizationGroupPermId> groupPermIds = groupCodes.stream().map(mapper).collect(Collectors.toList()); AuthorizationGroupFetchOptions fetchOptions = new AuthorizationGroupFetchOptions(); fetchOptions.withUsers(); - return service.getAuthorizationGroups(sessionToken, groupPermIds, fetchOptions); + Map<String, Set<String>> usersByGroupCodes = new TreeMap<>(); + for (AuthorizationGroup group : service.getAuthorizationGroups(sessionToken, groupPermIds, fetchOptions).values()) + { + Set<String> users = group.getUsers().stream().map(Person::getUserId).collect(Collectors.toSet()); + usersByGroupCodes.put(group.getCode(), users); + } + return usersByGroupCodes; } private Set<String> asSet(List<String> users) diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/task/UserManagerReport.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/task/UserManagerReport.java new file mode 100644 index 00000000000..89f57855411 --- /dev/null +++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/task/UserManagerReport.java @@ -0,0 +1,101 @@ +/* + * Copyright 2018 ETH Zuerich, SIS + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ch.systemsx.cisd.openbis.generic.server.task; + +import java.text.MessageFormat; +import java.util.Date; + +import ch.ethz.sis.openbis.generic.asapi.v3.dto.authorizationgroup.id.AuthorizationGroupPermId; +import ch.ethz.sis.openbis.generic.asapi.v3.dto.roleassignment.Role; +import ch.ethz.sis.openbis.generic.asapi.v3.dto.space.id.ISpaceId; +import ch.systemsx.cisd.common.utilities.ITimeProvider; + +/** + * @author Franz-Josef Elmer + */ +public class UserManagerReport +{ + private StringBuilder errorReport = new StringBuilder(); + + private StringBuilder auditLog = new StringBuilder(); + + private ITimeProvider timeProvider; + + public UserManagerReport(ITimeProvider timeProvider) + { + this.timeProvider = timeProvider; + + } + + public String getErrorReport() + { + return errorReport.toString(); + } + + public String getAuditLog() + { + return auditLog.toString(); + } + + void addErrorMessage(String message) + { + errorReport.append(message).append('\n'); + } + + void addGroup(String groupCode) + { + log("ADD-GROUP", groupCode); + } + + void deactivateUser(String userId) + { + log("DEACTIVATE-USER", userId); + } + + void addUser(String userId, String homeSpaceCode) + { + log("ADD-USER", userId + " (home space: " + homeSpaceCode + ")"); + } + + void addSpace(ISpaceId spaceId) + { + log("ADD-SPACE", spaceId); + } + + void assignRoleTo(AuthorizationGroupPermId groupId, Role role, ISpaceId spaceId) + { + log("ASSIGN-ROLE-TO-GROUP", groupId + ", SPACE_" + role + " for " + spaceId); + } + + void addUserToGroup(String groupCode, String userId) + { + log("ADD-USER-TO-GROUP", groupCode + ", " + userId); + } + + void removeUserFromGroup(String groupCode, String userId) + { + log("REMOVE-USER-FROM-GROUP", groupCode + ", " + userId); + } + + private void log(String action, Object details) + { + Date timeStamp = new Date(timeProvider.getTimeInMilliseconds()); + MessageFormat messageFormat = new MessageFormat("{0,date,yyyy-MM-dd HH:mm:ss} [{1}] {2}\n"); + auditLog.append(messageFormat.format(new Object[] { timeStamp, action, details })); + } + +} diff --git a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/systemtest/task/UserManagerExpectationsBuilder.java b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/systemtest/task/UserManagerExpectationsBuilder.java index 04d963a1895..02ff5808bcf 100644 --- a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/systemtest/task/UserManagerExpectationsBuilder.java +++ b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/systemtest/task/UserManagerExpectationsBuilder.java @@ -32,6 +32,9 @@ import java.util.function.Consumer; import java.util.function.Function; import java.util.stream.Collectors; +import ch.ethz.sis.openbis.generic.asapi.v3.dto.authorizationgroup.AuthorizationGroup; +import ch.ethz.sis.openbis.generic.asapi.v3.dto.authorizationgroup.fetchoptions.AuthorizationGroupFetchOptions; +import ch.ethz.sis.openbis.generic.asapi.v3.dto.authorizationgroup.id.AuthorizationGroupPermId; import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.id.ObjectPermId; import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.interfaces.ICodeHolder; import ch.ethz.sis.openbis.generic.asapi.v3.dto.person.Person; @@ -45,6 +48,7 @@ import ch.ethz.sis.openbis.generic.server.asapi.v3.IApplicationServerInternalApi import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.IOperationContext; import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.OperationContext; import ch.systemsx.cisd.authentication.Principal; +import ch.systemsx.cisd.openbis.generic.server.task.UserManager; import ch.systemsx.cisd.openbis.generic.shared.IOpenBisSessionManager; import ch.systemsx.cisd.openbis.generic.shared.dto.Session; import ch.systemsx.cisd.openbis.generic.shared.dto.SpacePE; @@ -65,8 +69,10 @@ class UserManagerExpectationsBuilder private Map<String, List<Principal>> usersByGroup = new TreeMap<>(); - private Map<String, List<Principal>> disabledUsersByGroup = new TreeMap<>(); + private List<Principal> unknownUsers = new ArrayList<>(); + private Map<String, List<Principal>> disabledUsersByGroup = new TreeMap<>(); + private Map<String, List<Principal>> normalUsersByGroup = new TreeMap<>(); private Map<String, List<Principal>> adminUsersByGroup = new TreeMap<>(); @@ -80,6 +86,12 @@ class UserManagerExpectationsBuilder this.commonSpaces = commonSpaces; } + UserManagerExpectationsBuilder unknownUser(Principal user) + { + unknownUsers.add(user); + return this; + } + UserManagerExpectationsBuilder disabledUser(Principal user, String... groups) { return addUser(user, disabledUsersByGroup, groups); @@ -120,6 +132,7 @@ class UserManagerExpectationsBuilder { String sessionToken = v3api.login(TEST_USER, PASSWORD); assertSpaces(sessionToken); + assertUnknownUsers(sessionToken); assertUsers(sessionToken); assertAuthorization(sessionToken); v3api.logout(sessionToken); @@ -155,10 +168,49 @@ class UserManagerExpectationsBuilder List<String> expectedSpaces = extractedSortedPermIds(spaces); SpaceFetchOptions fetchOptions = new SpaceFetchOptions(); List<String> actualSpaces = extractedSortedCodes(v3api.getSpaces(sessionToken, spaces, fetchOptions).values()); - System.out.println("UserManagerTestExpectation: Spaces: " + expectedSpaces); assertEquals(actualSpaces.toString(), expectedSpaces.toString()); } + private void assertUnknownUsers(String sessionToken) + { + Map<String, Set<String>> usersByGroupId = getAllUsersOfGroups(sessionToken); + assertEquals(usersByGroupId.isEmpty(), false); + List<PersonPermId> personIds = unknownUsers.stream().map(p -> new PersonPermId(p.getUserId())).collect(Collectors.toList()); + PersonFetchOptions fetchOptions = new PersonFetchOptions(); + fetchOptions.withRoleAssignments(); + for (Person person : v3api.getPersons(sessionToken, personIds, fetchOptions).values()) + { + String userId = person.getUserId(); + assertEquals(person.isActive(), Boolean.FALSE, "Active flag of user " + userId); + assertEquals(person.getRoleAssignments().size(), 0, "Role assignments of user " + userId); + for (Entry<String, Set<String>> entry : usersByGroupId.entrySet()) + { + if (entry.getValue().contains(userId)) + { + fail("User " + userId + " still in authorization group " + entry.getKey()); + } + } + } + } + + private Map<String, Set<String>> getAllUsersOfGroups(String sessionToken) + { + AuthorizationGroupFetchOptions groupFetchOptions = new AuthorizationGroupFetchOptions(); + groupFetchOptions.withUsers(); + List<AuthorizationGroupPermId> ids = new ArrayList<>(); + for (String groupCode : usersByGroup.keySet()) + { + ids.add(new AuthorizationGroupPermId(groupCode)); + ids.add(new AuthorizationGroupPermId(UserManager.createAdminGroupCode(groupCode))); + } + Map<String, Set<String>> usersByGroupId = new TreeMap<>(); + for (AuthorizationGroup group : v3api.getAuthorizationGroups(sessionToken, ids, groupFetchOptions).values()) + { + usersByGroupId.put(group.getCode(), group.getUsers().stream().map(Person::getUserId).collect(Collectors.toSet())); + } + return usersByGroupId; + } + private void assertUsers(String sessionToken) { List<PersonPermId> allUsers = applyMapperToAllUsers(user -> new PersonPermId(user.getUserId())); @@ -168,7 +220,6 @@ class UserManagerExpectationsBuilder Function<IPersonId, PersonPermId> mapper = id -> (PersonPermId) id; List<String> expectedUsers = extractedSortedPermIds(allUsers); List<String> actualUsers = extractedSortedPermIds(persons.keySet().stream().map(mapper).collect(Collectors.toSet())); - System.out.println("UserManagerTestExpectation: Users: " + expectedUsers); assertEquals(actualUsers.toString(), expectedUsers.toString()); for (PersonPermId id : allUsers) { @@ -192,28 +243,29 @@ class UserManagerExpectationsBuilder { for (Entry<String, List<Principal>> entry : disabledUsersByGroup.entrySet()) { - String groupKey = entry.getKey(); + String groupCode = entry.getKey(); for (Principal user : entry.getValue()) { - commonSpaces.get(Role.USER).forEach(expectForSpace(expectations, user, groupKey, Level.NON)); - commonSpaces.get(Role.OBSERVER).forEach(expectForSpace(expectations, user, groupKey, Level.NON)); + commonSpaces.get(Role.USER).forEach(expectForSpace(expectations, user, groupCode, Level.NON)); + commonSpaces.get(Role.OBSERVER).forEach(expectForSpace(expectations, user, groupCode, Level.NON)); for (List<Principal> users2 : usersByGroup.values()) { - users2.forEach(user2 -> expectations.expect(user, getOwenSpace(user2), Level.NON)); + users2.forEach(user2 -> expectations.expect(user, getOwenSpace(user2), + equals(user, user2) ? Level.SPACE_ADMIN : Level.NON)); } } } } - + private void createExpectationsForNormalUsers(AuthorizationExpectations expectations) { for (Entry<String, List<Principal>> entry : normalUsersByGroup.entrySet()) { - String groupKey = entry.getKey(); + String groupCode = entry.getKey(); for (Principal user : entry.getValue()) { - commonSpaces.get(Role.USER).forEach(expectForSpace(expectations, user, groupKey, Level.SPACE_USER)); - commonSpaces.get(Role.OBSERVER).forEach(expectForSpace(expectations, user, groupKey, Level.SPACE_OBSERVER)); + commonSpaces.get(Role.USER).forEach(expectForSpace(expectations, user, groupCode, Level.SPACE_USER)); + commonSpaces.get(Role.OBSERVER).forEach(expectForSpace(expectations, user, groupCode, Level.SPACE_OBSERVER)); for (List<Principal> users2 : usersByGroup.values()) { users2.forEach(user2 -> expectations.expect(user, getOwenSpace(user2), @@ -227,17 +279,17 @@ class UserManagerExpectationsBuilder { for (Entry<String, List<Principal>> entry : adminUsersByGroup.entrySet()) { - String groupKey = entry.getKey(); + String groupCode = entry.getKey(); for (Principal user : entry.getValue()) { - commonSpaces.get(Role.USER).forEach(expectForSpace(expectations, user, groupKey, Level.SPACE_ADMIN)); - commonSpaces.get(Role.OBSERVER).forEach(expectForSpace(expectations, user, groupKey, Level.SPACE_ADMIN)); + commonSpaces.get(Role.USER).forEach(expectForSpace(expectations, user, groupCode, Level.SPACE_ADMIN)); + commonSpaces.get(Role.OBSERVER).forEach(expectForSpace(expectations, user, groupCode, Level.SPACE_ADMIN)); Set<Entry<String, List<Principal>>> entrySet = usersByGroup.entrySet(); for (Entry<String, List<Principal>> entry2 : entrySet) { - String group2Key = entry2.getKey(); + String groupCode2 = entry2.getKey(); List<Principal> users2 = entry2.getValue(); - if (groupKey.equals(group2Key)) + if (groupCode.equals(groupCode2)) { users2.forEach(user2 -> expectations.expect(user, getOwenSpace(user2), Level.SPACE_ADMIN)); } else @@ -249,9 +301,9 @@ class UserManagerExpectationsBuilder } } - private Consumer<String> expectForSpace(AuthorizationExpectations expectations, Principal user, String groupKey, Level level) + private Consumer<String> expectForSpace(AuthorizationExpectations expectations, Principal user, String groupCode, Level level) { - return space -> expectations.expect(user, createCommonSpaceCode(groupKey, space), level); + return space -> expectations.expect(user, createCommonSpaceCode(groupCode, space), level); } private static final class AuthorizationExpectations diff --git a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/systemtest/task/UserManagerTest.java b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/systemtest/task/UserManagerTest.java index 3560b13297d..bcd42615584 100644 --- a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/systemtest/task/UserManagerTest.java +++ b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/systemtest/task/UserManagerTest.java @@ -22,7 +22,9 @@ import java.util.Arrays; import java.util.EnumMap; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.TreeMap; +import java.util.stream.Collectors; import javax.annotation.Resource; @@ -31,11 +33,14 @@ import org.testng.annotations.Test; import ch.ethz.sis.openbis.generic.asapi.v3.dto.roleassignment.Role; import ch.ethz.sis.openbis.systemtest.asapi.v3.AbstractTest; +import ch.systemsx.cisd.authentication.NullAuthenticationService; import ch.systemsx.cisd.authentication.Principal; import ch.systemsx.cisd.common.logging.MockLogger; +import ch.systemsx.cisd.common.utilities.MockTimeProvider; import ch.systemsx.cisd.openbis.generic.server.ComponentNames; import ch.systemsx.cisd.openbis.generic.server.task.UserGroup; import ch.systemsx.cisd.openbis.generic.server.task.UserManager; +import ch.systemsx.cisd.openbis.generic.server.task.UserManagerReport; import ch.systemsx.cisd.openbis.generic.shared.IOpenBisSessionManager; /** @@ -69,17 +74,37 @@ public class UserManagerTest extends AbstractTest // Given MockLogger logger = new MockLogger(); Map<Role, List<String>> commonSpaces = commonSpaces(); - UserManager userManager = new UserManager(v3api, commonSpaces, logger); + UserManager userManager = createUserManager(commonSpaces, logger); Map<String, Principal> principals = principals(U3, U1, U2); - UserGroup group = new UserGroup(); - group.setAdmins(Arrays.asList(U1.getUserId(), "blabla")); - userManager.addGroup("G1", group, principals); + UserGroup group = group("G1", U1.getUserId(), "blabla"); + userManager.addGroup(group, principals); // When - String errorMessages = userManager.manageUsers(); + UserManagerReport report = manage(userManager); // Then - assertEquals(errorMessages, ""); + assertEquals(report.getErrorReport(), ""); + assertEquals(report.getAuditLog(), "1970-01-01 01:00:00 [ADD-GROUP] G1\n" + + "1970-01-01 01:00:01 [ADD-GROUP] G1_ADMIN\n" + + "1970-01-01 01:00:02 [ADD-SPACE] G1_ALPHA\n" + + "1970-01-01 01:00:03 [ASSIGN-ROLE-TO-GROUP] G1, SPACE_USER for G1_ALPHA\n" + + "1970-01-01 01:00:04 [ASSIGN-ROLE-TO-GROUP] G1_ADMIN, SPACE_ADMIN for G1_ALPHA\n" + + "1970-01-01 01:00:05 [ADD-SPACE] G1_BETA\n" + + "1970-01-01 01:00:06 [ASSIGN-ROLE-TO-GROUP] G1, SPACE_USER for G1_BETA\n" + + "1970-01-01 01:00:07 [ASSIGN-ROLE-TO-GROUP] G1_ADMIN, SPACE_ADMIN for G1_BETA\n" + + "1970-01-01 01:00:08 [ADD-SPACE] G1_GAMMA\n" + + "1970-01-01 01:00:09 [ASSIGN-ROLE-TO-GROUP] G1, SPACE_OBSERVER for G1_GAMMA\n" + + "1970-01-01 01:00:10 [ASSIGN-ROLE-TO-GROUP] G1_ADMIN, SPACE_ADMIN for G1_GAMMA\n" + + "1970-01-01 01:00:11 [ADD-USER] u1 (home space: U1)\n" + + "1970-01-01 01:00:12 [ADD-USER-TO-GROUP] G1, u1\n" + + "1970-01-01 01:00:13 [ASSIGN-ROLE-TO-GROUP] G1_ADMIN, SPACE_ADMIN for U1\n" + + "1970-01-01 01:00:14 [ADD-USER-TO-GROUP] G1_ADMIN, u1\n" + + "1970-01-01 01:00:15 [ADD-USER] u2 (home space: U2)\n" + + "1970-01-01 01:00:16 [ADD-USER-TO-GROUP] G1, u2\n" + + "1970-01-01 01:00:17 [ASSIGN-ROLE-TO-GROUP] G1_ADMIN, SPACE_ADMIN for U2\n" + + "1970-01-01 01:00:18 [ADD-USER] u3 (home space: U3)\n" + + "1970-01-01 01:00:19 [ADD-USER-TO-GROUP] G1, u3\n" + + "1970-01-01 01:00:20 [ASSIGN-ROLE-TO-GROUP] G1_ADMIN, SPACE_ADMIN for U3\n"); UserManagerExpectationsBuilder builder = createBuilder(commonSpaces); builder.adminUser(U1, "G1"); builder.user(U2, "G1"); @@ -93,20 +118,27 @@ public class UserManagerTest extends AbstractTest // Given MockLogger logger = new MockLogger(); Map<Role, List<String>> commonSpaces = commonSpaces(); - UserManager userManager = new UserManager(v3api, commonSpaces, logger); - UserGroup group = new UserGroup(); - group.setAdmins(Arrays.asList(U1.getUserId(), "blabla")); - userManager.addGroup("G2", group, principals(U1)); - assertEquals(userManager.manageUsers(), ""); + UserManager userManager = createUserManager(commonSpaces, logger); + UserGroup group = group("G2", U1.getUserId(), "blabla"); + userManager.addGroup(group, principals(U1)); + assertEquals(manage(userManager).getErrorReport(), ""); + createBuilder(commonSpaces).adminUser(U1, "G2").assertExpectations(); - userManager = new UserManager(v3api, commonSpaces, logger); - userManager.addGroup("G2", group, principals(U2, U3)); + userManager = createUserManager(commonSpaces, logger); + group.setAdmins(Arrays.asList(U1.getUserId())); + userManager.addGroup(group, principals(U1, U2, U3)); // When - String errorMessages = userManager.manageUsers(); + UserManagerReport report = manage(userManager); // Then - assertEquals(errorMessages, ""); + assertEquals(report.getErrorReport(), ""); + assertEquals(report.getAuditLog(), "1970-01-01 01:00:00 [ADD-USER] u2 (home space: U2)\n" + + "1970-01-01 01:00:01 [ADD-USER-TO-GROUP] G2, u2\n" + + "1970-01-01 01:00:02 [ASSIGN-ROLE-TO-GROUP] G2_ADMIN, SPACE_ADMIN for U2\n" + + "1970-01-01 01:00:03 [ADD-USER] u3 (home space: U3)\n" + + "1970-01-01 01:00:04 [ADD-USER-TO-GROUP] G2, u3\n" + + "1970-01-01 01:00:05 [ASSIGN-ROLE-TO-GROUP] G2_ADMIN, SPACE_ADMIN for U3\n"); UserManagerExpectationsBuilder builder = createBuilder(commonSpaces); builder.adminUser(U1, "G2"); builder.user(U2, "G2"); @@ -114,11 +146,281 @@ public class UserManagerTest extends AbstractTest builder.assertExpectations(); } + @Test + public void testRemoveNormalUserFromAGroup() + { + // Given + MockLogger logger = new MockLogger(); + Map<Role, List<String>> commonSpaces = commonSpaces(); + UserManager userManager = createUserManager(commonSpaces, logger); + UserGroup group = group("G2", U1.getUserId(), "blabla"); + userManager.addGroup(group, principals(U1, U2, U3)); + assertEquals(manage(userManager).getErrorReport(), ""); + createBuilder(commonSpaces).adminUser(U1, "G2").user(U2, "G2").user(U3, "G2").assertExpectations(); + + userManager = createUserManager(commonSpaces, logger); + userManager.addGroup(group, principals(U1, U3)); + + // When + UserManagerReport report = manage(userManager); + + // Then + assertEquals(report.getErrorReport(), ""); + assertEquals(report.getAuditLog(), "1970-01-01 01:00:00 [REMOVE-USER-FROM-GROUP] G2, u2\n"); + UserManagerExpectationsBuilder builder = createBuilder(commonSpaces); + builder.adminUser(U1, "G2"); + builder.disabledUser(U2, "G2"); + builder.user(U3, "G2"); + builder.assertExpectations(); + } + + @Test + public void testChangeNormalUserToAdmin() + { + // Given + MockLogger logger = new MockLogger(); + Map<Role, List<String>> commonSpaces = commonSpaces(); + UserManager userManager = createUserManager(commonSpaces, logger); + UserGroup group = group("G2"); + userManager.addGroup(group, principals(U1, U2, U3)); + assertEquals(manage(userManager).getErrorReport(), ""); + createBuilder(commonSpaces).user(U1, "G2").user(U2, "G2").user(U3, "G2").assertExpectations(); + + userManager = createUserManager(commonSpaces, logger); + group = group("G2", U1.getUserId()); + userManager.addGroup(group, principals(U1, U2, U3)); + + // When + UserManagerReport report = manage(userManager); + + // Then + assertEquals(report.getErrorReport(), ""); + assertEquals(report.getAuditLog(), "1970-01-01 01:00:00 [ADD-USER-TO-GROUP] G2_ADMIN, u1\n"); + UserManagerExpectationsBuilder builder = createBuilder(commonSpaces); + builder.adminUser(U1, "G2"); + builder.user(U2, "G2"); + builder.user(U3, "G2"); + builder.assertExpectations(); + } + + @Test + public void testChangeAdminUserToNormalAdmin() + { + // Given + MockLogger logger = new MockLogger(); + Map<Role, List<String>> commonSpaces = commonSpaces(); + UserManager userManager = createUserManager(commonSpaces, logger); + UserGroup group = group("G2", U1.getUserId()); + userManager.addGroup(group, principals(U1, U2, U3)); + assertEquals(manage(userManager).getErrorReport(), ""); + createBuilder(commonSpaces).adminUser(U1, "G2").user(U2, "G2").user(U3, "G2").assertExpectations(); + + userManager = createUserManager(commonSpaces, logger); + group = group("G2"); + userManager.addGroup(group, principals(U1, U2, U3)); + + // When + UserManagerReport report = manage(userManager); + + // Then + assertEquals(report.getErrorReport(), ""); + assertEquals(report.getAuditLog(), "1970-01-01 01:00:00 [REMOVE-USER-FROM-GROUP] G2_ADMIN, u1\n"); + UserManagerExpectationsBuilder builder = createBuilder(commonSpaces); + builder.user(U1, "G2"); + builder.user(U2, "G2"); + builder.user(U3, "G2"); + builder.assertExpectations(); + } + + @Test + public void testRemoveNormalUserFromAGroupAndAddItAgain() + { + // Given + MockLogger logger = new MockLogger(); + Map<Role, List<String>> commonSpaces = commonSpaces(); + UserManager userManager = createUserManager(commonSpaces, logger); + UserGroup group = group("G2", U1.getUserId(), "blabla"); + userManager.addGroup(group, principals(U1, U2, U3)); + assertEquals(manage(userManager).getErrorReport(), ""); + + userManager = createUserManager(commonSpaces, logger); + userManager.addGroup(group, principals(U1, U3)); + assertEquals(manage(userManager).getErrorReport(), ""); + createBuilder(commonSpaces).adminUser(U1, "G2").disabledUser(U2, "G2").user(U3, "G2").assertExpectations(); + + userManager = createUserManager(commonSpaces, logger); + userManager.addGroup(group, principals(U1, U2, U3)); + + // When + UserManagerReport report = manage(userManager); + + // Then + assertEquals(report.getErrorReport(), ""); + assertEquals(report.getAuditLog(), "1970-01-01 01:00:00 [ADD-USER-TO-GROUP] G2, u2\n"); + UserManagerExpectationsBuilder builder = createBuilder(commonSpaces); + builder.adminUser(U1, "G2"); + builder.user(U2, "G2"); + builder.user(U3, "G2"); + builder.assertExpectations(); + } + + @Test + public void testRemoveAdminUserFromAGroup() + { + // Given + MockLogger logger = new MockLogger(); + Map<Role, List<String>> commonSpaces = commonSpaces(); + UserManager userManager = createUserManager(commonSpaces, logger); + UserGroup group = group("G2", U1.getUserId(), "blabla"); + userManager.addGroup(group, principals(U1, U2, U3)); + assertEquals(manage(userManager).getErrorReport(), ""); + createBuilder(commonSpaces).adminUser(U1, "G2").user(U2, "G2").user(U3, "G2").assertExpectations(); + + userManager = createUserManager(commonSpaces, logger); + userManager.addGroup(group, principals(U2, U3)); + + // When + UserManagerReport report = manage(userManager); + + // Then + assertEquals(report.getErrorReport(), ""); + assertEquals(report.getAuditLog(), "1970-01-01 01:00:00 [REMOVE-USER-FROM-GROUP] G2, u1\n" + + "1970-01-01 01:00:01 [REMOVE-USER-FROM-GROUP] G2_ADMIN, u1\n"); + UserManagerExpectationsBuilder builder = createBuilder(commonSpaces); + builder.disabledUser(U1, "G2"); + builder.user(U2, "G2"); + builder.user(U3, "G2"); + builder.assertExpectations(); + } + + @Test + public void testRemoveAdminUserFromAGroupAndAddItAgain() + { + // Given + MockLogger logger = new MockLogger(); + Map<Role, List<String>> commonSpaces = commonSpaces(); + UserManager userManager = createUserManager(commonSpaces, logger); + UserGroup group = group("G2", U1.getUserId(), "blabla"); + userManager.addGroup(group, principals(U1, U2, U3)); + assertEquals(manage(userManager).getErrorReport(), ""); + + userManager = createUserManager(commonSpaces, logger); + userManager.addGroup(group, principals(U2, U3)); + assertEquals(manage(userManager).getErrorReport(), ""); + createBuilder(commonSpaces).disabledUser(U1, "G2").user(U2, "G2").user(U3, "G2").assertExpectations(); + + userManager = createUserManager(commonSpaces, logger); + userManager.addGroup(group, principals(U1, U2, U3)); + + // When + UserManagerReport report = manage(userManager); + + // Then + assertEquals(report.getErrorReport(), ""); + assertEquals(report.getAuditLog(), "1970-01-01 01:00:00 [ADD-USER-TO-GROUP] G2, u1\n" + + "1970-01-01 01:00:01 [ADD-USER-TO-GROUP] G2_ADMIN, u1\n"); + UserManagerExpectationsBuilder builder = createBuilder(commonSpaces); + builder.adminUser(U1, "G2"); + builder.user(U2, "G2"); + builder.user(U3, "G2"); + builder.assertExpectations(); + } + + @Test + public void testUserFromAGroupHasLefted() + { + // Given + MockLogger logger = new MockLogger(); + Map<Role, List<String>> commonSpaces = commonSpaces(); + UserManager userManager = createUserManager(commonSpaces, logger); + UserGroup group = group("G2", U1.getUserId(), "blabla"); + userManager.addGroup(group, principals(U1, U2, U3)); + assertEquals(manage(userManager).getErrorReport(), ""); + createBuilder(commonSpaces).adminUser(U1, "G2").user(U2, "G2").user(U3, "G2").assertExpectations(); + + userManager = createUserManager(commonSpaces, logger, U2); + + // When + UserManagerReport report = manage(userManager); + + // Then + assertEquals(report.getErrorReport(), ""); + assertEquals(report.getAuditLog(), "1970-01-01 01:00:00 [DEACTIVATE-USER] u2\n"); + UserManagerExpectationsBuilder builder = createBuilder(commonSpaces); + builder.adminUser(U1, "G2"); + builder.unknownUser(U2); + builder.user(U3, "G2"); + builder.assertExpectations(); + } + + // @Test + public void testReuseSameUserId() + { + // Given + MockLogger logger = new MockLogger(); + Map<Role, List<String>> commonSpaces = commonSpaces(); + UserManager userManager = createUserManager(commonSpaces, logger); + UserGroup group = group("G2", U1.getUserId(), "blabla"); + userManager.addGroup(group, principals(U1, U2, U3)); + assertEquals(manage(userManager), ""); + createBuilder(commonSpaces).adminUser(U1, "G2").user(U2, "G2").user(U3, "G2").assertExpectations(); + + userManager = createUserManager(commonSpaces, logger, U2); + userManager.addGroup(group, principals(U1, U3)); + assertEquals(manage(userManager), ""); + createBuilder(commonSpaces).adminUser(U1, "G2").unknownUser(U2).user(U3, "G2").assertExpectations(); + + userManager = createUserManager(commonSpaces, logger); + userManager.addGroup(group, principals(U1, U2, U3)); + + // When + UserManagerReport report = manage(userManager); + + // Then + assertEquals(report.getErrorReport(), ""); + UserManagerExpectationsBuilder builder = createBuilder(commonSpaces); + builder.adminUser(U1, "G2"); + builder.user(cloneFor(U2, "u2_2"), "G2"); + builder.user(U3, "G2"); + builder.assertExpectations(); + } + + private Principal cloneFor(Principal user, String userId) + { + return new Principal(userId, user.getFirstName(), user.getLastName(), user.getEmail()); + } + + private UserManager createUserManager(Map<Role, List<String>> commonSpaces, MockLogger logger, + Principal... usersUnknownByAuthenticationService) + { + Set<String> unknownUsers = Arrays.asList(usersUnknownByAuthenticationService).stream().map(Principal::getUserId).collect(Collectors.toSet()); + NullAuthenticationService authenticationService = new NullAuthenticationService() + { + @Override + public Principal getPrincipal(String user) throws IllegalArgumentException + { + if (unknownUsers.contains(user)) + { + throw new IllegalArgumentException("Unknown user " + user); + } + return new Principal(user, "John", "Doe", "jd@abc.de"); + } + }; + return new UserManager(authenticationService, v3api, commonSpaces, logger, new MockTimeProvider(0, 1000)); + } + private UserManagerExpectationsBuilder createBuilder(Map<Role, List<String>> commonSpaces) { return new UserManagerExpectationsBuilder(v3api, testService, sessionManager, commonSpaces); } + private UserManagerReport manage(UserManager userManager) + { + UserManagerReport errorReport = userManager.manage(); + daoFactory.getSessionFactory().getCurrentSession().flush(); + return errorReport; + } + private Map<String, Principal> principals(Principal... principals) { Map<String, Principal> map = new TreeMap<>(); @@ -129,4 +431,11 @@ public class UserManagerTest extends AbstractTest return map; } + private UserGroup group(String groupKey, String... admins) + { + UserGroup userGroup = new UserGroup(); + userGroup.setKey(groupKey); + userGroup.setAdmins(Arrays.asList(admins)); + return userGroup; + } } -- GitLab