diff --git a/authentication/source/java/ch/systemsx/cisd/authentication/ldap/LDAPDirectoryConfiguration.java b/authentication/source/java/ch/systemsx/cisd/authentication/ldap/LDAPDirectoryConfiguration.java index 13729994ed398288ac3019a62ce70c8fa05a9e62..80b63a21b3d283a62ab5ccc66809f8e019f66005 100644 --- a/authentication/source/java/ch/systemsx/cisd/authentication/ldap/LDAPDirectoryConfiguration.java +++ b/authentication/source/java/ch/systemsx/cisd/authentication/ldap/LDAPDirectoryConfiguration.java @@ -432,7 +432,10 @@ public final class LDAPDirectoryConfiguration */ public void setServerUrl(String ldapUrl) { - this.serverUrl = ldapUrl; + if (isResolved(ldapUrl)) + { + this.serverUrl = ldapUrl; + } } private static boolean isResolved(String name) diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/task/Group.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/task/UserGroup.java similarity index 79% rename from openbis/source/java/ch/systemsx/cisd/openbis/generic/server/task/Group.java rename to openbis/source/java/ch/systemsx/cisd/openbis/generic/server/task/UserGroup.java index db1fe91cf09dea6233ac680df34b6779da3e2bc1..7dc251453a73ff0c5e2917d6a20654d30277ad77 100644 --- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/task/Group.java +++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/task/UserGroup.java @@ -18,34 +18,43 @@ package ch.systemsx.cisd.openbis.generic.server.task; import java.util.List; -class Group +public class UserGroup { private String name; + + private String key; private List<String> ldapGroupKeys; private List<String> admins; - private List<String> usersBlackList; - public String getName() { return name; } - public List<String> getAdmins() + public String getKey() { - return admins; + return key; } - public List<String> getLdapGroupKeys() + public void setKey(String key) { - return ldapGroupKeys; + this.key = key; } - public List<String> getUsersBlackList() + public List<String> getAdmins() { - return usersBlackList; + return admins; } + public void setAdmins(List<String> admins) + { + this.admins = admins; + } + + public List<String> getLdapGroupKeys() + { + return ldapGroupKeys; + } } \ No newline at end of file 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 42b697b343c84f13e1d0604c754fcf19cd58c2c1..d63529883383e616c9d5bfb24cd8e144e6777923 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 @@ -19,14 +19,12 @@ package ch.systemsx.cisd.openbis.generic.server.task; import java.io.File; import java.util.List; import java.util.Map; -import java.util.Map.Entry; import java.util.Properties; import java.util.TreeMap; import org.apache.commons.lang.StringUtils; import org.apache.log4j.Logger; -import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import ch.systemsx.cisd.authentication.Principal; @@ -51,8 +49,11 @@ public class UserManagementMaintenanceTask implements IMaintenanceTask 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 LDAPAuthenticationService ldapService; @Override @@ -65,7 +66,14 @@ 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"); + if (ldapService.isConfigured() == false) + { + throw new ConfigurationFailureException("There is no LDAP authentication service configured. " + + "At least 'ldap.server.url', 'ldap.security.principal.distinguished.name', " + + "'ldap.security.principal.password' have to be specified in 'service.properties'."); + } operationLog.info("Plugin '" + pluginName + "' initialized. Configuration file: " + configurationFile.getAbsolutePath()); } @@ -73,17 +81,16 @@ public class UserManagementMaintenanceTask implements IMaintenanceTask @Override public void execute() { - Map<String, Group> groups = readGroupDefinitions(); - if (groups == null) + UserManagerConfig config = readGroupDefinitions(); + if (config == null || config.getGroups() == null) { return; } Log4jSimpleLogger logger = new Log4jSimpleLogger(operationLog); - UserManager userManager = new UserManager(CommonServiceProvider.getApplicationServerApi(), logger); - for (Entry<String, Group> entry : groups.entrySet()) + UserManager userManager = new UserManager(CommonServiceProvider.getApplicationServerApi(), config.getCommonSpaces(), logger); + for (UserGroup group : config.getGroups()) { - String key = entry.getKey(); - Group group = entry.getValue(); + String key = group.getKey(); List<String> ldapGroupKeys = group.getLdapGroupKeys(); if (ldapGroupKeys == null || ldapGroupKeys.isEmpty()) { @@ -112,11 +119,15 @@ public class UserManagementMaintenanceTask implements IMaintenanceTask } userManager.addGroup(key, group, principalsByUserId); } - userManager.manageUsers(); + String errorReport = userManager.manageUsers(); + if (StringUtils.isNotBlank(errorReport)) + { + notificationLog.error("User management failed for the following reasons:\n\n" + errorReport); + } operationLog.info("finished"); } - private Map<String, Group> readGroupDefinitions() + private UserManagerConfig readGroupDefinitions() { if (configurationFile.isFile() == false) { @@ -134,10 +145,9 @@ public class UserManagementMaintenanceTask implements IMaintenanceTask } } - private Map<String, Group> deserialize(String serializedConfig) throws Exception + private UserManagerConfig deserialize(String serializedConfig) throws Exception { ObjectMapper mapper = new ObjectMapper(); - return mapper.readValue(serializedConfig, new TypeReference<Map<String, Group>>(){}); + return mapper.readValue(serializedConfig, UserManagerConfig.class); } - } 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 12effa23e0a731ec0140264e0f36beb0c51fb1c0..fbd1ddfbaaa79fe1853b8a1e2cd4d6b2953c3775 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 @@ -17,9 +17,12 @@ package ch.systemsx.cisd.openbis.generic.server.task; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.Map.Entry; import java.util.Set; import java.util.TreeMap; import java.util.TreeSet; @@ -27,13 +30,32 @@ 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.create.AuthorizationGroupCreation; +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; +import ch.ethz.sis.openbis.generic.asapi.v3.dto.operation.SynchronousOperationExecutionOptions; import ch.ethz.sis.openbis.generic.asapi.v3.dto.person.Person; +import ch.ethz.sis.openbis.generic.asapi.v3.dto.person.create.CreatePersonsOperation; +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.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; +import ch.ethz.sis.openbis.generic.asapi.v3.dto.roleassignment.create.CreateRoleAssignmentsOperation; +import ch.ethz.sis.openbis.generic.asapi.v3.dto.roleassignment.create.RoleAssignmentCreation; +import ch.ethz.sis.openbis.generic.asapi.v3.dto.space.Space; +import ch.ethz.sis.openbis.generic.asapi.v3.dto.space.create.CreateSpacesOperation; +import ch.ethz.sis.openbis.generic.asapi.v3.dto.space.create.SpaceCreation; +import ch.ethz.sis.openbis.generic.asapi.v3.dto.space.fetchoptions.SpaceFetchOptions; +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.Principal; import ch.systemsx.cisd.common.logging.ISimpleLogger; @@ -42,27 +64,29 @@ import ch.systemsx.cisd.common.logging.LogLevel; /** * @author Franz-Josef Elmer */ -class UserManager +public class UserManager { private final IApplicationServerInternalApi service; private final ISimpleLogger logger; - + + private final Map<Role, List<String>> commonSpacesByRole; + private final Map<String, UserInfo> userInfosByUserId = new TreeMap<>(); - + private final List<String> groupCodes = new ArrayList<>(); - UserManager(IApplicationServerInternalApi service, ISimpleLogger logger) + public UserManager(IApplicationServerInternalApi service, Map<Role, List<String>> commonSpacesByRole, ISimpleLogger logger) { this.service = service; + this.commonSpacesByRole = commonSpacesByRole; this.logger = logger; } - public void addGroup(String key, Group group, Map<String, Principal> principalsByUserId) + public void addGroup(String key, UserGroup group, Map<String, Principal> principalsByUserId) { - groupCodes.add(key); + groupCodes.add(key.toUpperCase()); Set<String> admins = asSet(group.getAdmins()); - Set<String> blackListedUsers = asSet(group.getUsersBlackList()); for (Principal principal : principalsByUserId.values()) { String userId = principal.getUserId(); @@ -72,27 +96,231 @@ class UserManager userInfo = new UserInfo(principal); userInfosByUserId.put(userId, userInfo); } - userInfo.addGroupInfo(new GroupInfo(key, admins.contains(userId), blackListedUsers.contains(userId))); + userInfo.addGroupInfo(new GroupInfo(key, admins.contains(userId))); } logger.log(LogLevel.INFO, principalsByUserId.size() + " users for group " + key); } - public void manageUsers() + public String manageUsers() { String sessionToken = service.loginAsSystem(); Map<IPersonId, Person> users = getUsersWithRoleAssigments(sessionToken); Map<IAuthorizationGroupId, AuthorizationGroup> authorizationGroups = getAuthorizationGroups(sessionToken); + StringBuilder builder = new StringBuilder(); + for (String groupCode : groupCodes) + { + try + { + manageGroup(sessionToken, groupCode, authorizationGroups); + } 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"); + logger.log(LogLevel.ERROR, message, e); + } + } + authorizationGroups = getAuthorizationGroups(sessionToken); for (UserInfo userInfo : userInfosByUserId.values()) { - manageUser(userInfo, users, authorizationGroups); + try + { + manageUser(sessionToken, userInfo, users, authorizationGroups); + } 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"); + logger.log(LogLevel.ERROR, message, e); + } } service.logout(sessionToken); + return builder.toString(); } - - private void manageUser(UserInfo userInfo, Map<IPersonId, Person> knownUsers, + + private void manageGroup(String sessionToken, String groupCode, Map<IAuthorizationGroupId, AuthorizationGroup> knownAuthorizationGroups) + { + Context context = new Context(sessionToken); + AuthorizationGroup knownGroup = knownAuthorizationGroups.get(new AuthorizationGroupPermId(groupCode)); + if (knownGroup != null) + { + manageKnownGroup(context, knownGroup); + } else + { + manageNewGroup(context, groupCode); + } + context.executeOperations(); + } + + private void manageNewGroup(Context context, String groupCode) + { + String adminGroupCode = createAdminGroupCode(groupCode); + assertAuthorizationGroupDoesNotExist(context, adminGroupCode); + assertNoCommonSpaceExists(context, groupCode); + + createAuthorizationGroup(context, groupCode); + createAuthorizationGroup(context, adminGroupCode); + + for (Entry<Role, List<String>> entry : commonSpacesByRole.entrySet()) + { + Role role = entry.getKey(); + 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); + } + } + } + + private void assertAuthorizationGroupDoesNotExist(Context context, String adminGroup) + { + if (service.getAuthorizationGroups(context.getSessionToken(), Arrays.asList(new AuthorizationGroupPermId(adminGroup)), + new AuthorizationGroupFetchOptions()).isEmpty() == false) + { + throw new IllegalStateException("Authorization group " + adminGroup + " already exists."); + } + } + + private void assertNoCommonSpaceExists(Context context, String groupCode) + { + Set<String> commonSpaces = new TreeSet<>(); + for (List<String> set : commonSpacesByRole.values()) + { + commonSpaces.addAll(set.stream().map(s -> createCommonSpaceCode(groupCode, s)).collect(Collectors.toList())); + } + Map<ISpaceId, Space> spaces = getSpaces(context.getSessionToken(), commonSpaces); + if (spaces.isEmpty()) + { + return; + } + List<String> existingSpaces = new ArrayList<>(spaces.values()).stream().map(Space::getCode).collect(Collectors.toList()); + Collections.sort(existingSpaces); + 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) + { + Context context = new Context(sessionToken); + Person knownUser = knownUsers.get(new PersonPermId(userInfo.getPrincipal().getUserId())); + if (knownUser != null) + { + manageKnownUser(context, userInfo, knownUser, knownAuthorizationGroups); + } else + { + manageNewUser(context, userInfo, knownAuthorizationGroups); + } + context.executeOperations(); + } + + private void manageKnownUser(Context context, UserInfo userInfo, Person knownUser, Map<IAuthorizationGroupId, AuthorizationGroup> knownAuthorizationGroups) { - + System.out.println("UserManager.manageKnownUser() " + userInfo); + } + + private void manageNewUser(Context context, UserInfo userInfo, + Map<IAuthorizationGroupId, AuthorizationGroup> knownAuthorizationGroups) + { + Principal principal = userInfo.getPrincipal(); + String userId = principal.getUserId(); + String spaceCode = userId.toUpperCase(); + if (getSpaces(context.getSessionToken(), Arrays.asList(spaceCode)).isEmpty() == false) + { + throw new IllegalStateException("There is already a space with code " + spaceCode + "."); + } + + ISpaceId homeSpaceId = createSpace(context, spaceCode); + IPersonId personId = createPerson(context, userId); + assignHomeSpace(context, homeSpaceId, personId); + + 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); + if (groupInfo.isAdmin()) + { + addPersonToAuthorizationGroup(context, adminGroupId, personId); + } + } + } + + private void assignHomeSpace(Context context, ISpaceId homeSpaceId, IPersonId personId) + { + PersonUpdate personUpdate = new PersonUpdate(); + personUpdate.setUserId(personId); + personUpdate.setSpaceId(homeSpaceId); + context.add(personUpdate); + RoleAssignmentCreation roleCreation = new RoleAssignmentCreation(); + roleCreation.setUserId(personId); + roleCreation.setRole(Role.ADMIN); + roleCreation.setSpaceId(homeSpaceId); + context.add(roleCreation); + } + + private void addPersonToAuthorizationGroup(Context context, AuthorizationGroupPermId groupId, IPersonId personId) + { + AuthorizationGroupUpdate groupUpdate = new AuthorizationGroupUpdate(); + groupUpdate.setAuthorizationGroupId(groupId); + groupUpdate.getUserIds().add(personId); + context.add(groupUpdate); + } + + private ISpaceId createSpace(Context context, String spaceCode) + { + SpaceCreation spaceCreation = new SpaceCreation(); + spaceCreation.setCode(spaceCode); + context.add(spaceCreation); + return new SpacePermId(spaceCode); + } + + private IPersonId createPerson(Context context, String userId) + { + PersonCreation personCreation = new PersonCreation(); + personCreation.setUserId(userId); + context.add(personCreation); + return new PersonPermId(userId); + } + + private void createAuthorizationGroup(Context context, String groupCode) + { + AuthorizationGroupCreation creation = new AuthorizationGroupCreation(); + creation.setCode(groupCode); + context.add(creation); + } + + private void createRoleAssignment(Context context, AuthorizationGroupPermId groupId, Role role, ISpaceId spaceId) + { + RoleAssignmentCreation roleCreation = new RoleAssignmentCreation(); + roleCreation.setAuthorizationGroupId(groupId); + roleCreation.setRole(role); + roleCreation.setSpaceId(spaceId); + context.add(roleCreation); + } + + private String createCommonSpaceCode(String groupCode, String spaceCode) + { + return groupCode + "_" + spaceCode; + } + + private String createAdminGroupCode(String groupCode) + { + return groupCode + "_ADMIN"; + } + + private Map<ISpaceId, Space> getSpaces(String sessionToken, Collection<String> spaceCodes) + { + SpaceFetchOptions fetchOptions = new SpaceFetchOptions(); + return service.getSpaces(sessionToken, spaceCodes.stream().map(SpacePermId::new).collect(Collectors.toList()), fetchOptions); } private Map<IPersonId, Person> getUsersWithRoleAssigments(String sessionToken) @@ -113,7 +341,7 @@ class UserManager fetchOptions.withUsers(); return service.getAuthorizationGroups(sessionToken, groupPermIds, fetchOptions); } - + private Set<String> asSet(List<String> users) { return users == null ? Collections.emptySet() : new TreeSet<>(users); @@ -140,6 +368,11 @@ class UserManager groupInfosByGroupKey.put(groupInfo.getKey(), groupInfo); } + public Map<String, GroupInfo> getGroupInfosByGroupKey() + { + return groupInfosByGroupKey; + } + @Override public String toString() { @@ -153,13 +386,10 @@ class UserManager private boolean admin; - private boolean onBlackList; - - GroupInfo(String key, boolean admin, boolean onBlackList) + GroupInfo(String key, boolean admin) { this.key = key; this.admin = admin; - this.onBlackList = onBlackList; } public String getKey() @@ -172,17 +402,103 @@ class UserManager return admin; } - public boolean isOnBlackList() - { - return onBlackList; - } - @Override public String toString() { - return (onBlackList ? "." : "") + key + (admin ? "*" : ""); + return key + (admin ? "*" : ""); } } + private final class Context + { + private String sessionToken; + + private List<PersonCreation> personCreations = new ArrayList<>(); + + private List<PersonUpdate> personUpdates = new ArrayList<>(); + + private List<SpaceCreation> spaceCreations = new ArrayList<>(); + + private List<AuthorizationGroupCreation> groupCreations = new ArrayList<>(); + + private List<AuthorizationGroupUpdate> groupUpdates = new ArrayList<>(); + + private List<RoleAssignmentCreation> roleCreations = new ArrayList<>(); + + Context(String sessionToken) + { + this.sessionToken = sessionToken; + } + + public String getSessionToken() + { + return sessionToken; + } + + public void add(PersonCreation personCreation) + { + personCreations.add(personCreation); + } + + public void add(PersonUpdate personUpdate) + { + personUpdates.add(personUpdate); + } + + public void add(SpaceCreation spaceCreation) + { + spaceCreations.add(spaceCreation); + } + + public void add(AuthorizationGroupCreation creation) + { + groupCreations.add(creation); + } + + public void add(RoleAssignmentCreation roleCreation) + { + roleCreations.add(roleCreation); + } + + public void add(AuthorizationGroupUpdate groupUpdate) + { + groupUpdates.add(groupUpdate); + } + + public void executeOperations() + { + List<IOperation> operations = new ArrayList<>(); + if (personCreations.isEmpty() == false) + { + operations.add(new CreatePersonsOperation(personCreations)); + } + if (personUpdates.isEmpty() == false) + { + operations.add(new UpdatePersonsOperation(personUpdates)); + } + if (spaceCreations.isEmpty() == false) + { + operations.add(new CreateSpacesOperation(spaceCreations)); + } + if (groupCreations.isEmpty() == false) + { + operations.add(new CreateAuthorizationGroupsOperation(groupCreations)); + } + if (groupUpdates.isEmpty() == false) + { + operations.add(new UpdateAuthorizationGroupsOperation(groupUpdates)); + } + if (roleCreations.isEmpty() == false) + { + operations.add(new CreateRoleAssignmentsOperation(roleCreations)); + } + if (operations.isEmpty()) + { + return; + } + SynchronousOperationExecutionOptions options = new SynchronousOperationExecutionOptions(); + service.executeOperations(sessionToken, operations, options); + } + } } diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/task/UserManagerConfig.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/task/UserManagerConfig.java new file mode 100644 index 0000000000000000000000000000000000000000..328c31e334d92c9ee547d42fa6c6ca55fca634fb --- /dev/null +++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/task/UserManagerConfig.java @@ -0,0 +1,51 @@ +/* + * 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.util.HashMap; +import java.util.List; +import java.util.Map; + +import ch.ethz.sis.openbis.generic.asapi.v3.dto.roleassignment.Role; + +class UserManagerConfig +{ + private Map<Role, List<String>> commonSpaces = new HashMap<>(); + + private List<UserGroup> groups; + + public Map<Role, List<String>> getCommonSpaces() + { + return commonSpaces; + } + + public void setCommonSpaces(Map<Role, List<String>> commonSpaces) + { + this.commonSpaces = commonSpaces; + } + + public List<UserGroup> getGroups() + { + return groups; + } + + public void setGroups(List<UserGroup> groups) + { + this.groups = groups; + } + +} \ No newline at end of file diff --git a/openbis/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/AbstractTest.java b/openbis/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/AbstractTest.java index 31c0181d0d82b933a1777919d71151d83f3621e9..54498508abdc3bccb99202244dad536f3290f771 100644 --- a/openbis/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/AbstractTest.java +++ b/openbis/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/AbstractTest.java @@ -47,7 +47,6 @@ import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeClass; import org.testng.annotations.BeforeMethod; -import ch.ethz.sis.openbis.generic.asapi.v3.IApplicationServerApi; import ch.ethz.sis.openbis.generic.asapi.v3.dto.attachment.Attachment; import ch.ethz.sis.openbis.generic.asapi.v3.dto.attachment.create.AttachmentCreation; import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.id.IObjectId; @@ -97,6 +96,7 @@ import ch.ethz.sis.openbis.generic.asapi.v3.dto.vocabulary.id.VocabularyTermPerm import ch.ethz.sis.openbis.generic.asapi.v3.exceptions.NotFetchedException; import ch.ethz.sis.openbis.generic.asapi.v3.exceptions.ObjectNotFoundException; import ch.ethz.sis.openbis.generic.asapi.v3.exceptions.UnauthorizedObjectAccessException; +import ch.ethz.sis.openbis.generic.server.asapi.v3.IApplicationServerInternalApi; import ch.ethz.sis.openbis.systemtest.asapi.v3.index.IndexOperation; import ch.ethz.sis.openbis.systemtest.asapi.v3.index.IndexState; import ch.ethz.sis.openbis.systemtest.asapi.v3.index.ReindexingState; @@ -119,7 +119,6 @@ import ch.systemsx.cisd.openbis.generic.shared.dto.PersonPE; import ch.systemsx.cisd.openbis.generic.shared.dto.SamplePE; import ch.systemsx.cisd.openbis.systemtest.SystemTestCase; import ch.systemsx.cisd.openbis.util.LogRecordingUtils; - import junit.framework.Assert; /** @@ -139,7 +138,7 @@ public class AbstractTest extends SystemTestCase private BufferedAppender logRecorder; @Autowired - protected IApplicationServerApi v3api; + protected IApplicationServerInternalApi v3api; @Autowired protected IGeneralInformationService generalInformationService; 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 new file mode 100644 index 0000000000000000000000000000000000000000..4645e33b8da619dfaa9ac5f7903aa1de53dbc53a --- /dev/null +++ b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/systemtest/task/UserManagerExpectationsBuilder.java @@ -0,0 +1,401 @@ +/* + * 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.systemtest.task; + +import static org.testng.Assert.assertEquals; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.TreeMap; +import java.util.TreeSet; +import java.util.function.Function; +import java.util.stream.Collectors; + +import org.testng.Assert; + +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; +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.roleassignment.Role; +import ch.ethz.sis.openbis.generic.asapi.v3.dto.space.Space; +import ch.ethz.sis.openbis.generic.asapi.v3.dto.space.fetchoptions.SpaceFetchOptions; +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.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.shared.IOpenBisSessionManager; +import ch.systemsx.cisd.openbis.generic.shared.dto.Session; +import ch.systemsx.cisd.openbis.generic.shared.dto.SpacePE; + +class UserManagerExpectationsBuilder +{ + private static final String TEST_USER = "test"; + + private static final String PASSWORD = "password"; + + private final IApplicationServerInternalApi v3api; + + private final UserManagerTestService testService; + + private final IOpenBisSessionManager sessionManager; + + private final Map<Role, List<String>> commonSpaces; + + private Map<String, List<Principal>> usersByGroup = new TreeMap<>(); + + private Map<String, List<Principal>> noUsersByGroup = new TreeMap<>(); + + private Map<String, List<Principal>> normalUsersByGroup = new TreeMap<>(); + + private Map<String, List<Principal>> adminUsersByGroup = new TreeMap<>(); + + UserManagerExpectationsBuilder(IApplicationServerInternalApi v3api, UserManagerTestService testService, + IOpenBisSessionManager sessionManager, Map<Role, List<String>> commonSpaces) + { + this.v3api = v3api; + this.testService = testService; + this.sessionManager = sessionManager; + this.commonSpaces = commonSpaces; + } + + UserManagerExpectationsBuilder noUser(Principal user, String... groups) + { + return addUser(user, noUsersByGroup, groups); + } + + UserManagerExpectationsBuilder user(Principal user, String... groups) + { + return addUser(user, normalUsersByGroup, groups); + } + + UserManagerExpectationsBuilder adminUser(Principal user, String... groups) + { + return addUser(user, adminUsersByGroup, groups); + } + + private UserManagerExpectationsBuilder addUser(Principal user, Map<String, List<Principal>> users, String... groups) + { + for (String group : groups) + { + addUserToGroup(user, group, users); + addUserToGroup(user, group, usersByGroup); + } + return this; + } + + private void addUserToGroup(Principal user, String group, Map<String, List<Principal>> users) + { + List<Principal> principals = users.get(group); + if (principals == null) + { + principals = new ArrayList<>(); + users.put(group, principals); + } + principals.add(user); + } + + void assertExpectations() + { + String sessionToken = v3api.login(TEST_USER, PASSWORD); + assertSpaces(sessionToken); + assertUsers(sessionToken); + assertAuthorization(sessionToken); + v3api.logout(sessionToken); + } + + private void assertSpaces(String sessionToken) + { + List<SpacePermId> allCommonSpaces = getAllCommonSpaces(); + assertSpacesExist(sessionToken, allCommonSpaces); + List<SpacePermId> allUserSpaces = applyMapperToAllUsers(user -> new SpacePermId(user.getUserId().toUpperCase())); + assertSpacesExist(sessionToken, allUserSpaces); + } + + private List<SpacePermId> getAllCommonSpaces() + { + List<SpacePermId> allCommonSpaces = new ArrayList<>(); + Set<String> groups = usersByGroup.keySet(); + for (String group : groups) + { + for (List<String> list : commonSpaces.values()) + { + for (String commonSpace : list) + { + allCommonSpaces.add(new SpacePermId(group + "_" + commonSpace)); + } + } + } + return allCommonSpaces; + } + + private void assertSpacesExist(String sessionToken, List<SpacePermId> spaces) + { + List<String> expectedSpaces = extractedSortedPermIds(spaces); + SpaceFetchOptions fetchOptions = new SpaceFetchOptions(); + List<String> actualSpaces = extractedSortedCodes(v3api.getSpaces(sessionToken, spaces, fetchOptions).values()); + assertEquals(actualSpaces.toString(), expectedSpaces.toString()); + } + + private void assertUsers(String sessionToken) + { + List<PersonPermId> allUsers = applyMapperToAllUsers(user -> new PersonPermId(user.getUserId())); + PersonFetchOptions fetchOptions = new PersonFetchOptions(); + fetchOptions.withSpace(); + Map<IPersonId, Person> persons = v3api.getPersons(sessionToken, allUsers, fetchOptions); + Function<IPersonId, PersonPermId> mapper = id -> (PersonPermId) id; + List<String> expectedUsers = extractedSortedPermIds(allUsers); + List<String> actualUsers = extractedSortedPermIds(persons.keySet().stream().map(mapper).collect(Collectors.toSet())); + assertEquals(actualUsers.toString(), expectedUsers.toString()); + for (PersonPermId id : allUsers) + { + Person person = persons.get(id); + assertEquals(person.getUserId(), id.getPermId()); + assertEquals(person.getEmail(), "franz-josef.elmer@systemsx.ch", "Wrong email of " + person); + assertEquals(person.getSpace().getCode(), person.getUserId().toUpperCase(), "Wrong home space of " + person); + } + } + + private void assertAuthorization(String sessionToken) + { + AuthorizationExpectations expectations = new AuthorizationExpectations(v3api, sessionManager, testService); + for (Entry<String, List<Principal>> entry : normalUsersByGroup.entrySet()) + { + String groupKey = entry.getKey(); + List<Principal> users = entry.getValue(); + users.forEach(user -> expectations.expect(user, groupKey, Level.SPACE_USER)); + } + } + + private static final class AuthorizationExpectations + { + private Map<String, Map<Level, Set<String>>> groupsByLevelsByUsers = new TreeMap<>(); + + private IApplicationServerInternalApi v3api; + + private IOpenBisSessionManager sessionManager; + + private UserManagerTestService testService; + + public AuthorizationExpectations(IApplicationServerInternalApi v3api, IOpenBisSessionManager sessionManager, + UserManagerTestService testService) + { + this.v3api = v3api; + this.sessionManager = sessionManager; + this.testService = testService; + } + + void expect(Principal user, String group, Level level) + { + String userId = user.getUserId(); + Map<Level, Set<String>> groupsByLevels = groupsByLevelsByUsers.get(userId); + if (groupsByLevels == null) + { + groupsByLevels = new TreeMap<>(); + groupsByLevelsByUsers.put(userId, groupsByLevels); + } + Set<String> groups = groupsByLevels.get(level); + if (groups == null) + { + groups = new TreeSet<>(); + groupsByLevels.put(level, groups); + } + groups.add(group); + } + + void assertExpectations() + { + + for (Entry<String, Map<Level, Set<String>>> entry : groupsByLevelsByUsers.entrySet()) + { + String userId = entry.getKey(); + String sessionToken = v3api.login(userId, PASSWORD); + + try + { + Session session = sessionManager.getSession(sessionToken); + IOperationContext context = new OperationContext(session); + Set<Entry<Level, Set<String>>> entrySet = entry.getValue().entrySet(); + for (Entry<Level, Set<String>> entry2 : entrySet) + { + Level level = entry2.getKey(); + Set<String> groups = entry2.getValue(); + for (String group : groups) + { +// getActualLevel(context, spaceCode); + } +// probeAuthorizationLevel(user, spaceCodes); + } + } finally + { + v3api.logout(sessionToken); + } + + } + } + + private Level getActualLevel(IOperationContext context, String spaceCode) + { + SpacePE space = new SpacePE(); + space.setCode(spaceCode); + Level previousLevel = null; + for (Level level : Level.values()) + { + try + { + level.probe(testService, context, space); + } catch (Exception e) + { + break; + } + previousLevel = level; + } + return previousLevel; + } + + private static final class LevelAndSpace + { + private Level level; + private String spaceCode; + + LevelAndSpace(Level level, String spaceCode) + { + this.level = level; + this.spaceCode = spaceCode; + } + } + + } + + private List<String> extractedSortedPermIds(Collection<? extends ObjectPermId> ids) + { + List<String> result = ids.stream().map(ObjectPermId::getPermId).collect(Collectors.toList()); + Collections.sort(result); + return result; + } + + private List<String> extractedSortedCodes(Collection<? extends ICodeHolder> codeHolders) + { + List<String> result = codeHolders.stream().map(ICodeHolder::getCode).collect(Collectors.toList()); + Collections.sort(result); + return result; + } + + private <T> List<T> applyMapperToAllUsers(Function<Principal, T> mapper) + { + List<T> result = new ArrayList<>(); + for (List<Principal> users : usersByGroup.values()) + { + result.addAll(users.stream().map(mapper).collect(Collectors.toList())); + } + return result; + } + + private List<Level> probeAuthorizationLevel(String userId, List<String> spaceCodes) + { + String sessionToken = v3api.login(userId, PASSWORD); + try + { + List<Level> levels = new ArrayList<>(); + Session session = sessionManager.getSession(sessionToken); + IOperationContext context = new OperationContext(session); + for (String spaceCode : spaceCodes) + { + Level previousLevel = getActualLevel(context, spaceCode); + levels.add(previousLevel); + } + return levels; + } finally + { + v3api.logout(sessionToken); + } + } + + private Level getActualLevel(IOperationContext context, String spaceCode) + { + SpacePE space = new SpacePE(); + space.setCode(spaceCode); + Level previousLevel = null; + for (Level level : Level.values()) + { + try + { + level.probe(testService, context, space); + } catch (Exception e) + { + break; + } + previousLevel = level; + } + return previousLevel; + } + + private enum Level + { + NON + { + @Override + public void probe(UserManagerTestService testService, IOperationContext context, SpacePE space) + { + } + }, + SPACE_OBSERVER + { + @Override + public void probe(UserManagerTestService testService, IOperationContext context, SpacePE space) + { + testService.allowedForSpaceObservers(context, space); + } + }, + SPACE_USER + { + @Override + public void probe(UserManagerTestService testService, IOperationContext context, SpacePE space) + { + testService.allowedForSpaceUsers(context, space); + } + }, + SPACE_ADMIN + { + @Override + public void probe(UserManagerTestService testService, IOperationContext context, SpacePE space) + { + testService.allowedForSpaceAdmins(context, space); + } + }, + INSTANCE_ADMIN + { + @Override + public void probe(UserManagerTestService testService, IOperationContext context, SpacePE space) + { + testService.allowedForInstanceAdmins(context, space); + } + }; + + public abstract void probe(UserManagerTestService testService, IOperationContext context, SpacePE space); + } + +} \ No newline at end of file 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 new file mode 100644 index 0000000000000000000000000000000000000000..7779ca4ed26c8b234b321e8d295deb4d66cb1112 --- /dev/null +++ b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/systemtest/task/UserManagerTest.java @@ -0,0 +1,326 @@ +/* + * 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.systemtest.task; + +import static org.testng.Assert.assertEquals; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.EnumMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; +import java.util.TreeSet; +import java.util.function.Function; +import java.util.stream.Collectors; + +import javax.annotation.Resource; + +import org.springframework.beans.factory.annotation.Autowired; +import org.testng.Assert; +import org.testng.annotations.Test; + +import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.search.SearchResult; +import ch.ethz.sis.openbis.generic.asapi.v3.dto.person.Person; +import ch.ethz.sis.openbis.generic.asapi.v3.dto.person.fetchoptions.PersonFetchOptions; +import ch.ethz.sis.openbis.generic.asapi.v3.dto.person.id.PersonPermId; +import ch.ethz.sis.openbis.generic.asapi.v3.dto.roleassignment.Role; +import ch.ethz.sis.openbis.generic.asapi.v3.dto.space.Space; +import ch.ethz.sis.openbis.generic.asapi.v3.dto.space.fetchoptions.SpaceFetchOptions; +import ch.ethz.sis.openbis.generic.asapi.v3.dto.space.search.SpaceSearchCriteria; +import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.IOperationContext; +import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.OperationContext; +import ch.ethz.sis.openbis.systemtest.asapi.v3.AbstractTest; +import ch.systemsx.cisd.authentication.Principal; +import ch.systemsx.cisd.common.logging.MockLogger; +import ch.systemsx.cisd.common.test.AssertionUtil; +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.shared.IOpenBisSessionManager; +import ch.systemsx.cisd.openbis.generic.shared.dto.Session; +import ch.systemsx.cisd.openbis.generic.shared.dto.SpacePE; + +/** + * @author Franz-Josef Elmer + */ +public class UserManagerTest extends AbstractTest +{ + private static final Principal U1 = new Principal("u1", "Albert", "Einstein", "a.e@abc.de"); + + private static final Principal U2 = new Principal("u2", "Isaac", "Newton", "i.n@abc.de"); + + private static final Principal U3 = new Principal("u3", "Alan", "Turing", "a.t@abc.de"); + + @Autowired + private UserManagerTestService testService; + + @Resource(name = ComponentNames.SESSION_MANAGER) + private IOpenBisSessionManager sessionManager; + + private static Map<Role, List<String>> commonSpaces() + { + Map<Role, List<String>> commonSpacesByRole = new EnumMap<>(Role.class); + commonSpacesByRole.put(Role.USER, Arrays.asList("ALPHA", "BETA")); + commonSpacesByRole.put(Role.OBSERVER, Arrays.asList("GAMMA")); + return commonSpacesByRole; + } + + @Test + public void testAddNewGroupWithNewUsers() + { + // Given + MockLogger logger = new MockLogger(); + UserManager userManager = new UserManager(v3api, 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); + + // When + String errorMessages = userManager.manageUsers(); + + // Then + assertEquals(errorMessages, ""); + List<String> allSpaces = getAllSpaces(); + System.err.println("all spaces: " + allSpaces); + for (Principal user : principals.values()) + { + List<Person> persons = getPersons(user.getUserId()); + Person person = persons.get(0); + assertEquals(person.getUserId(), user.getUserId()); + assertEquals(person.getEmail(), "franz-josef.elmer@systemsx.ch"); + String space = getOwnSpace(user); + assertEquals(person.getSpace().getCode(), space); + if (group.getAdmins() != null && group.getAdmins().contains(user.getUserId())) + { + assertAdminUserLevels("G1", user, principals.values()); + } else + { + assertNormalUserLevels("G1", user); + } + } + } + + @Test + public void testAddUsersToAnExistingGroup() + { + // 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 = new UserManager(v3api, commonSpaces, logger); + userManager.addGroup("G2", group, principals(U2, U3)); + + // When + String errorMessages = userManager.manageUsers(); + + // Then + assertEquals(errorMessages, ""); + Collection<Principal> users = principals(U1, U2, U3).values(); + for (Principal user : users) + { + List<Person> persons = getPersons(user.getUserId()); + Person person = persons.get(0); + assertEquals(person.getUserId(), user.getUserId()); + assertEquals(person.getEmail(), "franz-josef.elmer@systemsx.ch"); + String space = getOwnSpace(user); + assertEquals(person.getSpace().getCode(), space); + if (group.getAdmins() != null && group.getAdmins().contains(user.getUserId())) + { + assertAdminUserLevels("G2", user, users); + } else + { + assertNormalUserLevels("G2", user); + } + } + UserManagerExpectationsBuilder builder = createBuilder(commonSpaces); + builder.adminUser(U1, "G2"); + builder.user(U2, "G2"); + builder.user(U3, "G2"); + builder.assertExpectations(); + } + + private UserManagerExpectationsBuilder createBuilder(Map<Role, List<String>> commonSpaces) + { + return new UserManagerExpectationsBuilder(v3api, testService, sessionManager, commonSpaces); + } + + private void assertNormalUserLevels(String groupCode, Principal user) + { + String userId = user.getUserId(); + Map<Role, List<String>> commonSpaces = commonSpaces(); + assertLevel(userId, Level.SPACE_OBSERVER, getCommonSpaces(groupCode, commonSpaces, Role.OBSERVER)); + assertLevel(userId, Level.SPACE_USER, getCommonSpaces(groupCode, commonSpaces, Role.USER)); + assertLevel(userId, Level.SPACE_ADMIN, Arrays.asList(userId.toUpperCase())); + } + + private void assertAdminUserLevels(String groupCode, Principal adminUser, Collection<Principal> users) + { + List<String> spaces = users.stream().map(p -> getOwnSpace(p)).collect(Collectors.toList()); + Map<Role, List<String>> commonSpaces = commonSpaces(); + spaces.addAll(getCommonSpaces(groupCode, commonSpaces, Role.OBSERVER)); + spaces.addAll(getCommonSpaces(groupCode, commonSpaces, Role.USER)); + assertLevel(adminUser.getUserId(), Level.SPACE_ADMIN, spaces); + } + + private List<String> getCommonSpaces(String groupCode, Map<Role, List<String>> commonSpaces, Role role) + { + return commonSpaces.get(role).stream().map(s -> groupCode + "_" + s).collect(Collectors.toList()); + } + + private void assertLevel(String userId, Level level, List<String> spaceCodes) + { + List<Level> levels = probeAuthorizationLevel(userId, spaceCodes); + for (int i = 0; i < spaceCodes.size(); i++) + { + assertLevel(levels.get(i), level, userId, spaceCodes.get(i)); + } + } + + private void assertLevel(Level actualLevel, Level expectedLevel, String userId, String spaceCode) + { + assertEquals(userId + ":" + spaceCode + ":" + actualLevel, userId + ":" + spaceCode + ":" + expectedLevel); + } + + private List<String> getAllSpaces() + { + String sessionToken = v3api.login(TEST_USER, PASSWORD); + SearchResult<Space> spaces = v3api.searchSpaces(sessionToken, new SpaceSearchCriteria(), new SpaceFetchOptions()); + v3api.logout(sessionToken); + List<String> spaceCodes = spaces.getObjects().stream().map(Space::getCode).collect(Collectors.toList()); + Collections.sort(spaceCodes); + return spaceCodes; + } + + private List<Person> getPersons(String... userIds) + { + String sessionToken = v3api.login(TEST_USER, PASSWORD); + Function<String, PersonPermId> mapper = u -> new PersonPermId(u); + List<PersonPermId> permIds = Arrays.asList(userIds).stream().map(mapper).collect(Collectors.toList()); + PersonFetchOptions fetchOptions = new PersonFetchOptions(); + fetchOptions.withSpace(); + Collection<Person> persons = v3api.getPersons(sessionToken, permIds, fetchOptions).values(); + v3api.logout(sessionToken); + List<Person> result = new ArrayList<>(persons); + Collections.sort(result, Comparator.comparing(Person::getUserId)); + return result; + } + + private Map<String, Principal> principals(Principal... principals) + { + Map<String, Principal> map = new TreeMap<>(); + for (Principal principal : principals) + { + map.put(principal.getUserId(), principal); + } + return map; + } + + private String getOwnSpace(Principal principal) + { + return principal.getUserId().toUpperCase(); + } + + private List<Level> probeAuthorizationLevel(String userId, List<String> spaceCodes) + { + String sessionToken = v3api.login(userId, PASSWORD); + try + { + List<Level> levels = new ArrayList<>(); + Session session = sessionManager.getSession(sessionToken); + IOperationContext context = new OperationContext(session); + for (String spaceCode : spaceCodes) + { + SpacePE space = new SpacePE(); + space.setCode(spaceCode); + Level previousLevel = null; + for (Level level : Level.values()) + { + try + { + level.probe(testService, context, space); + } catch (Exception e) + { + break; + } + previousLevel = level; + } + levels.add(previousLevel); + } + return levels; + } finally + { + v3api.logout(sessionToken); + } + } + + private enum Level + { + NON + { + @Override + public void probe(UserManagerTestService testService, IOperationContext context, SpacePE space) + { + } + }, + SPACE_OBSERVER + { + @Override + public void probe(UserManagerTestService testService, IOperationContext context, SpacePE space) + { + testService.allowedForSpaceObservers(context, space); + } + }, + SPACE_USER + { + @Override + public void probe(UserManagerTestService testService, IOperationContext context, SpacePE space) + { + testService.allowedForSpaceUsers(context, space); + } + }, + SPACE_ADMIN + { + @Override + public void probe(UserManagerTestService testService, IOperationContext context, SpacePE space) + { + testService.allowedForSpaceAdmins(context, space); + } + }, + INSTANCE_ADMIN + { + @Override + public void probe(UserManagerTestService testService, IOperationContext context, SpacePE space) + { + testService.allowedForInstanceAdmins(context, space); + } + }; + + public abstract void probe(UserManagerTestService testService, IOperationContext context, SpacePE space); + } + +} diff --git a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/systemtest/task/UserManagerTestService.java b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/systemtest/task/UserManagerTestService.java new file mode 100644 index 0000000000000000000000000000000000000000..1f8c74c79fe559138103c93a4278e615b9c5b0bb --- /dev/null +++ b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/systemtest/task/UserManagerTestService.java @@ -0,0 +1,55 @@ +/* + * 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.systemtest.task; + +import org.springframework.stereotype.Component; + +import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.IOperationContext; +import ch.systemsx.cisd.openbis.generic.server.authorization.annotation.AuthorizationGuard; +import ch.systemsx.cisd.openbis.generic.server.authorization.annotation.RolesAllowed; +import ch.systemsx.cisd.openbis.generic.server.authorization.predicate.SpacePEPredicate; +import ch.systemsx.cisd.openbis.generic.shared.basic.dto.RoleWithHierarchy; +import ch.systemsx.cisd.openbis.generic.shared.dto.SpacePE; + +/** + * @author Franz-Josef Elmer + * + */ +@Component +public class UserManagerTestService +{ + @RolesAllowed(RoleWithHierarchy.SPACE_OBSERVER) + public void allowedForSpaceObservers(IOperationContext context, @AuthorizationGuard(guardClass = SpacePEPredicate.class) SpacePE space) + { + } + + @RolesAllowed(RoleWithHierarchy.SPACE_USER) + public void allowedForSpaceUsers(IOperationContext context, @AuthorizationGuard(guardClass = SpacePEPredicate.class) SpacePE space) + { + } + + @RolesAllowed(RoleWithHierarchy.SPACE_ADMIN) + public void allowedForSpaceAdmins(IOperationContext context, @AuthorizationGuard(guardClass = SpacePEPredicate.class) SpacePE space) + { + } + + @RolesAllowed(RoleWithHierarchy.INSTANCE_ADMIN) + public void allowedForInstanceAdmins(IOperationContext context, @AuthorizationGuard(guardClass = SpacePEPredicate.class) SpacePE space) + { + } + +}