diff --git a/authentication/source/java/ch/systemsx/cisd/authentication/ldap/LDAPAuthenticationService.java b/authentication/source/java/ch/systemsx/cisd/authentication/ldap/LDAPAuthenticationService.java index 9652cd18ec44e30daefa0f139253835c055742cb..ce85b4b1410bf5bd5be837988f4bc1dce9d67781 100644 --- a/authentication/source/java/ch/systemsx/cisd/authentication/ldap/LDAPAuthenticationService.java +++ b/authentication/source/java/ch/systemsx/cisd/authentication/ldap/LDAPAuthenticationService.java @@ -207,4 +207,8 @@ public class LDAPAuthenticationService implements IAuthenticationService return configured; } + public List<Principal> listPrincipalsByGroup(String group) + { + return query.listPrincipalsByKeyValue("memberOf", group); + } } diff --git a/commonbase/sourceTest/java/ch/systemsx/cisd/common/test/ToStringMatcher.java b/commonbase/sourceTest/java/ch/systemsx/cisd/common/test/ToStringMatcher.java new file mode 100644 index 0000000000000000000000000000000000000000..328b82754304b6d0677f941a3e251d100238ee60 --- /dev/null +++ b/commonbase/sourceTest/java/ch/systemsx/cisd/common/test/ToStringMatcher.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.common.test; + +import org.hamcrest.BaseMatcher; +import org.hamcrest.Description; + +/** + * @author Franz-Josef Elmer + */ +public class ToStringMatcher<T> extends BaseMatcher<T> +{ + private String expectedToStringString; + + public ToStringMatcher(T expectedItem) + { + this(String.valueOf(expectedItem)); + } + + public ToStringMatcher(String expectedToStringString) + { + this.expectedToStringString = expectedToStringString; + } + + @Override + public boolean matches(Object item) + { + return String.valueOf(item).equals(expectedToStringString); + } + + @Override + public void describeTo(Description description) + { + description.appendText(expectedToStringString); + } + +} diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/ApplicationServerApi.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/ApplicationServerApi.java index 6065148dce5d454bbf4d69b34f294bb95830b09e..200dd53708fcf5bfdd68b710f14455a82a430e2e 100644 --- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/ApplicationServerApi.java +++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/ApplicationServerApi.java @@ -453,7 +453,7 @@ import ch.systemsx.cisd.openbis.generic.shared.managed_property.IManagedProperty */ @Component(ApplicationServerApi.INTERNAL_SERVICE_NAME) public class ApplicationServerApi extends AbstractServer<IApplicationServerApi> implements - IApplicationServerApi + IApplicationServerInternalApi { /** * Name of this service for which it is registered as Spring bean @@ -486,6 +486,12 @@ public class ApplicationServerApi extends AbstractServer<IApplicationServerApi> return session == null ? null : session.getSessionToken(); } + @Override + public String loginAsSystem() + { + return tryToAuthenticateAsSystem().getSessionToken(); + } + @Override @Transactional public String loginAsAnonymousUser() diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/IApplicationServerInternalApi.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/IApplicationServerInternalApi.java new file mode 100644 index 0000000000000000000000000000000000000000..6e623105fb4aad9348231d0af71b37e2bcb57004 --- /dev/null +++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/IApplicationServerInternalApi.java @@ -0,0 +1,33 @@ +/* + * 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.ethz.sis.openbis.generic.server.asapi.v3; + +import org.springframework.transaction.annotation.Transactional; + +import ch.ethz.sis.openbis.generic.asapi.v3.IApplicationServerApi; + +/** + * Extension of {@link IApplicationServerApi} which are only for internal use. These methods are not accessible remotely. + * + * @author Franz-Josef Elmer + */ +public interface IApplicationServerInternalApi extends IApplicationServerApi +{ + @Transactional + public String loginAsSystem(); + +} diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/AbstractServer.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/AbstractServer.java index d17c567bd75ed5b73d867adb9b616f1f5c367042..0c8be1bc0d4ff0ca450ad880f2a0e0454fa8c139 100644 --- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/AbstractServer.java +++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/AbstractServer.java @@ -465,6 +465,22 @@ public abstract class AbstractServer<T> extends AbstractServiceWithLogger<T> imp return getDAOFactory().getPersonDAO().countActivePersons(); } + public SessionContextDTO tryToAuthenticateAsSystem() + { + final PersonPE systemUser = getSystemUser(); + HibernateUtils.initialize(systemUser.getAllPersonRoles()); + RoleAssignmentPE role = new RoleAssignmentPE(); + role.setRole(RoleCode.ADMIN); + systemUser.addRoleAssignment(role); + String sessionToken = + sessionManager.tryToOpenSession(systemUser.getUserId(), + new AuthenticatedPersonBasedPrincipalProvider(systemUser)); + Session session = sessionManager.getSession(sessionToken); + session.setPerson(systemUser); + session.setCreatorPerson(systemUser); + return tryGetSession(sessionToken); + } + @Override public SessionContextDTO tryAuthenticateAnonymously() { diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/CommonServer.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/CommonServer.java index b3bbb5e222a37fc108d5ff524695a3425b167fcc..585e93d97d65573b195fb37c12ec1f4035bd242a 100644 --- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/CommonServer.java +++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/CommonServer.java @@ -211,7 +211,6 @@ import ch.systemsx.cisd.openbis.generic.shared.dto.SampleUpdatesDTO; import ch.systemsx.cisd.openbis.generic.shared.dto.ScriptPE; import ch.systemsx.cisd.openbis.generic.shared.dto.SearchableEntity; import ch.systemsx.cisd.openbis.generic.shared.dto.Session; -import ch.systemsx.cisd.openbis.generic.shared.dto.SessionContextDTO; import ch.systemsx.cisd.openbis.generic.shared.dto.SpacePE; import ch.systemsx.cisd.openbis.generic.shared.dto.VocabularyPE; import ch.systemsx.cisd.openbis.generic.shared.dto.VocabularyTermWithStats; @@ -323,27 +322,6 @@ public final class CommonServer extends AbstractCommonServer<ICommonServerForInt return new CommonServerLogger(getSessionManager(), context); } - // - // ISystemAuthenticator - // - - @Override - public SessionContextDTO tryToAuthenticateAsSystem() - { - final PersonPE systemUser = getSystemUser(); - HibernateUtils.initialize(systemUser.getAllPersonRoles()); - RoleAssignmentPE role = new RoleAssignmentPE(); - role.setRole(RoleCode.ADMIN); - systemUser.addRoleAssignment(role); - String sessionToken = - sessionManager.tryToOpenSession(systemUser.getUserId(), - new AuthenticatedPersonBasedPrincipalProvider(systemUser)); - Session session = sessionManager.getSession(sessionToken); - session.setPerson(systemUser); - session.setCreatorPerson(systemUser); - return tryGetSession(sessionToken); - } - // // IGenericServer // diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/CommonServiceProvider.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/CommonServiceProvider.java index b311ae93581827d73962be8973c7ee904c168b4a..e5dbc63c5a9b7c2b97ac51211b109e59234c87cd 100644 --- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/CommonServiceProvider.java +++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/CommonServiceProvider.java @@ -18,8 +18,8 @@ package ch.systemsx.cisd.openbis.generic.server; import org.springframework.context.ApplicationContext; -import ch.ethz.sis.openbis.generic.asapi.v3.IApplicationServerApi; import ch.ethz.sis.openbis.generic.server.asapi.v3.ApplicationServerApi; +import ch.ethz.sis.openbis.generic.server.asapi.v3.IApplicationServerInternalApi; import ch.systemsx.cisd.common.mail.IMailClient; import ch.systemsx.cisd.common.mail.MailClient; import ch.systemsx.cisd.common.mail.MailClientParameters; @@ -66,9 +66,9 @@ public class CommonServiceProvider return new MailClient(mailClientParameters); } - public static IApplicationServerApi getApplicationServerApi() + public static IApplicationServerInternalApi getApplicationServerApi() { - return (IApplicationServerApi) applicationContext.getBean(ApplicationServerApi.INTERNAL_SERVICE_NAME); + return (IApplicationServerInternalApi) applicationContext.getBean(ApplicationServerApi.INTERNAL_SERVICE_NAME); } public static Object tryToGetBean(String beanName) 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/Group.java new file mode 100644 index 0000000000000000000000000000000000000000..db1fe91cf09dea6233ac680df34b6779da3e2bc1 --- /dev/null +++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/task/Group.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.List; + +class Group +{ + private String name; + + private List<String> ldapGroupKeys; + + private List<String> admins; + + private List<String> usersBlackList; + + public String getName() + { + return name; + } + + public List<String> getAdmins() + { + return admins; + } + + public List<String> getLdapGroupKeys() + { + return ldapGroupKeys; + } + + public List<String> getUsersBlackList() + { + return usersBlackList; + } + +} \ 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 new file mode 100644 index 0000000000000000000000000000000000000000..f836afd0ba7d33ce2fbaccde71d00a2ac6a1990d --- /dev/null +++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/task/UserManagementMaintenanceTask.java @@ -0,0 +1,156 @@ +/* + * 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.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; +import ch.systemsx.cisd.authentication.ldap.LDAPAuthenticationService; +import ch.systemsx.cisd.authentication.ldap.LDAPDirectoryConfiguration; +import ch.systemsx.cisd.authentication.ldap.LDAPPrincipalQuery; +import ch.systemsx.cisd.common.exceptions.ConfigurationFailureException; +import ch.systemsx.cisd.common.filesystem.FileUtilities; +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.openbis.generic.server.CommonServiceProvider; + +/** + * @author Franz-Josef Elmer + */ +public class UserManagementMaintenanceTask implements IMaintenanceTask +{ + private static final String DISTINGUISHED_NAME_TEMPLATE_PROPERTY = "distinguished-name-template"; + + private static final String DEFAULT_DISTINGUISHED_NAME_TEMPLATE = "CN=%s,OU=EthLists,DC=d,DC=ethz,DC=ch"; + + private static final String CONFIGURATION_FILE_PATH_PROPERTY = "configuration-file-path"; + + private static final String DEFAULT_CONFIGURATION_FILE_PATH = "etc/user-management-maintenance-config.json"; + + private static final Logger operationLog = LogFactory.getLogger(LogCategory.OPERATION, + UserManagementMaintenanceTask.class); + + private File configurationFile; + + private LDAPAuthenticationService ldapService; + + private String dnTemplate; + + @Override + public void setUp(String pluginName, Properties properties) + { + operationLog.info("Setup plugin " + pluginName); + configurationFile = new File(properties.getProperty(CONFIGURATION_FILE_PATH_PROPERTY, DEFAULT_CONFIGURATION_FILE_PATH)); + if (configurationFile.isFile() == false) + { + throw new ConfigurationFailureException("Configuration file '" + configurationFile.getAbsolutePath() + + "' doesn't exist or is a directory."); + } + dnTemplate = properties.getProperty(DISTINGUISHED_NAME_TEMPLATE_PROPERTY, DEFAULT_DISTINGUISHED_NAME_TEMPLATE); + if (dnTemplate.contains("%s") == false) + { + throw new ConfigurationFailureException("Property '" + DISTINGUISHED_NAME_TEMPLATE_PROPERTY + "' doesn't contain '%s' as placeholder."); + } + ldapService = (LDAPAuthenticationService) CommonServiceProvider.getApplicationContext().getBean("ldap-authentication-service"); + operationLog.info("Plugin '" + pluginName + "' initialized. Configuration file: " + configurationFile.getAbsolutePath()); + + } + + @Override + public void execute() + { + Map<String, Group> groups = readGroupDefinitions(); + if (groups == null) + { + return; + } + Log4jSimpleLogger logger = new Log4jSimpleLogger(operationLog); + UserManager userManager = new UserManager(CommonServiceProvider.getApplicationServerApi(), logger); + for (Entry<String, Group> entry : groups.entrySet()) + { + String key = entry.getKey(); + Group group = entry.getValue(); + List<String> ldapGroupKeys = group.getLdapGroupKeys(); + if (ldapGroupKeys == null || ldapGroupKeys.isEmpty()) + { + 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.listPrincipalsByGroup(String.format(dnTemplate, 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); + } + userManager.manageUsers(); + operationLog.info("finished"); + } + + private Map<String, Group> readGroupDefinitions() + { + if (configurationFile.isFile() == false) + { + operationLog.error("Configuration file '" + configurationFile.getAbsolutePath() + "' doesn't exist or is a directory."); + return null; + } + String serializedConfig = FileUtilities.loadToString(configurationFile); + try + { + return deserialize(serializedConfig); + } catch (Exception e) + { + operationLog.error("Invalid content of configuration file '" + configurationFile.getAbsolutePath() + "': " + e, e); + return null; + } + } + + private Map<String, Group> deserialize(String serializedConfig) throws Exception + { + ObjectMapper mapper = new ObjectMapper(); + return mapper.readValue(serializedConfig, new TypeReference<Map<String, Group>>(){}); + } + +} 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 new file mode 100644 index 0000000000000000000000000000000000000000..12effa23e0a731ec0140264e0f36beb0c51fb1c0 --- /dev/null +++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/task/UserManager.java @@ -0,0 +1,188 @@ +/* + * 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.ArrayList; +import java.util.Collections; +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 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.authorizationgroup.id.IAuthorizationGroupId; +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.server.asapi.v3.IApplicationServerInternalApi; +import ch.systemsx.cisd.authentication.Principal; +import ch.systemsx.cisd.common.logging.ISimpleLogger; +import ch.systemsx.cisd.common.logging.LogLevel; + +/** + * @author Franz-Josef Elmer + */ +class UserManager +{ + private final IApplicationServerInternalApi service; + + private final ISimpleLogger logger; + + private final Map<String, UserInfo> userInfosByUserId = new TreeMap<>(); + + private final List<String> groupCodes = new ArrayList<>(); + + UserManager(IApplicationServerInternalApi service, ISimpleLogger logger) + { + this.service = service; + this.logger = logger; + } + + public void addGroup(String key, Group group, Map<String, Principal> principalsByUserId) + { + groupCodes.add(key); + Set<String> admins = asSet(group.getAdmins()); + Set<String> blackListedUsers = asSet(group.getUsersBlackList()); + for (Principal principal : principalsByUserId.values()) + { + String userId = principal.getUserId(); + UserInfo userInfo = userInfosByUserId.get(userId); + if (userInfo == null) + { + userInfo = new UserInfo(principal); + userInfosByUserId.put(userId, userInfo); + } + userInfo.addGroupInfo(new GroupInfo(key, admins.contains(userId), blackListedUsers.contains(userId))); + } + logger.log(LogLevel.INFO, principalsByUserId.size() + " users for group " + key); + } + + public void manageUsers() + { + String sessionToken = service.loginAsSystem(); + Map<IPersonId, Person> users = getUsersWithRoleAssigments(sessionToken); + Map<IAuthorizationGroupId, AuthorizationGroup> authorizationGroups = getAuthorizationGroups(sessionToken); + for (UserInfo userInfo : userInfosByUserId.values()) + { + manageUser(userInfo, users, authorizationGroups); + } + service.logout(sessionToken); + } + + private void manageUser(UserInfo userInfo, Map<IPersonId, Person> knownUsers, + Map<IAuthorizationGroupId, AuthorizationGroup> knownAuthorizationGroups) + { + + } + + private Map<IPersonId, Person> getUsersWithRoleAssigments(String sessionToken) + { + Function<String, PersonPermId> mapper = userId -> new PersonPermId(userId); + List<PersonPermId> userIds = userInfosByUserId.keySet().stream().map(mapper).collect(Collectors.toList()); + PersonFetchOptions fetchOptions = new PersonFetchOptions(); + fetchOptions.withRoleAssignments().withSpace(); + Map<IPersonId, Person> users = service.getPersons(sessionToken, userIds, fetchOptions); + return users; + } + + private Map<IAuthorizationGroupId, AuthorizationGroup> getAuthorizationGroups(String sessionToken) + { + 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); + } + + private Set<String> asSet(List<String> users) + { + return users == null ? Collections.emptySet() : new TreeSet<>(users); + } + + private static class UserInfo + { + private Principal principal; + + private Map<String, GroupInfo> groupInfosByGroupKey = new TreeMap<>(); + + public UserInfo(Principal principal) + { + this.principal = principal; + } + + public Principal getPrincipal() + { + return principal; + } + + public void addGroupInfo(GroupInfo groupInfo) + { + groupInfosByGroupKey.put(groupInfo.getKey(), groupInfo); + } + + @Override + public String toString() + { + return principal.getUserId() + " " + groupInfosByGroupKey.values(); + } + } + + private static class GroupInfo + { + private String key; + + private boolean admin; + + private boolean onBlackList; + + GroupInfo(String key, boolean admin, boolean onBlackList) + { + this.key = key; + this.admin = admin; + this.onBlackList = onBlackList; + } + + public String getKey() + { + return key; + } + + public boolean isAdmin() + { + return admin; + } + + public boolean isOnBlackList() + { + return onBlackList; + } + + @Override + public String toString() + { + return (onBlackList ? "." : "") + key + (admin ? "*" : ""); + } + + } + +} diff --git a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/task/UserManagerTest.java b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/task/UserManagerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..e0fe3386b39b0cfb6e9051eacd946116f932c381 --- /dev/null +++ b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/task/UserManagerTest.java @@ -0,0 +1,295 @@ +/* + * 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 static org.testng.Assert.assertEquals; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; +import java.util.function.Function; +import java.util.stream.Collectors; + +import org.jmock.Expectations; +import org.jmock.Mockery; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +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.authorizationgroup.id.IAuthorizationGroupId; +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.roleassignment.RoleAssignment; +import ch.ethz.sis.openbis.generic.asapi.v3.dto.roleassignment.RoleLevel; +import ch.ethz.sis.openbis.generic.asapi.v3.dto.roleassignment.fetchoptions.RoleAssignmentFetchOptions; +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.SpacePermId; +import ch.ethz.sis.openbis.generic.server.asapi.v3.IApplicationServerInternalApi; +import ch.systemsx.cisd.authentication.Principal; +import ch.systemsx.cisd.common.logging.MockLogger; +import ch.systemsx.cisd.common.test.RecordingMatcher; +import ch.systemsx.cisd.common.test.ToStringMatcher; + +/** + * @author Franz-Josef Elmer + */ +public class UserManagerTest +{ + private static final String SESSION_TOKEN = "session-123"; + + 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"); + + private Mockery context; + + private IApplicationServerInternalApi service; + + private UserManager userManager; + + private MockLogger logger; + + @BeforeMethod + public void setUp() + { + context = new Mockery(); + service = context.mock(IApplicationServerInternalApi.class); + context.checking(new Expectations() + { + { + one(service).loginAsSystem(); + will(returnValue(SESSION_TOKEN)); + + one(service).logout(SESSION_TOKEN); + } + }); + logger = new MockLogger(); + userManager = new UserManager(service, logger); + } + + @AfterMethod + public void tearDown() + { + // To following line of code should also be called at the end of each test method. + // Otherwise one does not known which test failed. + context.assertIsSatisfied(); + } + + @Test + public void testAddNewNormalUser() + { + // Given + RecordingMatcher<List<IPersonId>> personsMatcher = prepareGetUsersWithRoleAssigments(new PersonBuilder(U1).get()); + RecordingMatcher<List<AuthorizationGroupPermId>> groupsMatcher = prepareGetAuthorizationGroups(); + + userManager.addGroup("G1", new Group(), principals(U2, U1)); + + // When + userManager.manageUsers(); + + // Then + assertEquals(personsMatcher.recordedObject().toString(), "[u1, u2]"); + assertEquals(groupsMatcher.recordedObject().toString(), "[G1]"); + context.assertIsSatisfied(); + } + + // @Test + public void test2() + { + // Given + + // When + userManager.manageUsers(); + + // Then + context.assertIsSatisfied(); + } + + // @Test + public void test3() + { + // Given + + // When + userManager.manageUsers(); + + // Then + context.assertIsSatisfied(); + } + + private RecordingMatcher<List<IPersonId>> prepareGetUsersWithRoleAssigments(Person... persons) + { + Map<IPersonId, Person> result = new LinkedHashMap<>(); + for (Person person : persons) + { + result.put(person.getPermId(), person); + } + RecordingMatcher<List<IPersonId>> matcher = new RecordingMatcher<>(); + context.checking(new Expectations() + { + { + PersonFetchOptions fetchOptions = new PersonFetchOptions(); + fetchOptions.withRoleAssignments().withSpace(); + one(service).getPersons(with(SESSION_TOKEN), with(matcher), with(new ToStringMatcher<>(fetchOptions))); + will(returnValue(result)); + } + }); + return matcher; + } + + private RecordingMatcher<List<AuthorizationGroupPermId>> prepareGetAuthorizationGroups(AuthorizationGroup... authorizationGroups) + { + Map<IAuthorizationGroupId, AuthorizationGroup> result = new LinkedHashMap<>(); + for (AuthorizationGroup authorizationGroup : authorizationGroups) + { + result.put(authorizationGroup.getPermId(), authorizationGroup); + } + RecordingMatcher<List<AuthorizationGroupPermId>> matcher = new RecordingMatcher<>(); + context.checking(new Expectations() + { + { + AuthorizationGroupFetchOptions fetchOptions = new AuthorizationGroupFetchOptions(); + fetchOptions.withUsers(); + one(service).getAuthorizationGroups(with(SESSION_TOKEN), with(matcher), with(new ToStringMatcher<>(fetchOptions))); + will(returnValue(result)); + } + }); + return matcher; + } + + 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 RoleAssignment ra(Role role) + { + return ra(role, null); + } + + private RoleAssignment ra(Role role, String spaceCodeOrNull) + { + RoleAssignment roleAssignment = new RoleAssignment(); + RoleAssignmentFetchOptions fetchOptions = new RoleAssignmentFetchOptions(); + fetchOptions.withSpace(); + roleAssignment.setFetchOptions(fetchOptions); + roleAssignment.setRole(role); + roleAssignment.setRoleLevel(RoleLevel.INSTANCE); + if (spaceCodeOrNull != null) + { + Space space = new Space(); + space.setCode(spaceCodeOrNull); + space.setPermId(new SpacePermId(spaceCodeOrNull)); + space.setFetchOptions(new SpaceFetchOptions()); + roleAssignment.setSpace(space); + roleAssignment.setRoleLevel(RoleLevel.SPACE); + } + return roleAssignment; + } + + private static Person createPerson(Principal principal, PersonFetchOptions fetchOptions) + { + Person person = new Person(); + person.setFetchOptions(fetchOptions); + person.setUserId(principal.getUserId()); + person.setPermId(new PersonPermId(principal.getUserId())); + person.setEmail(principal.getEmail()); + person.setFirstName(principal.getFirstName()); + person.setLastName(principal.getLastName()); + person.setRoleAssignments(new ArrayList<RoleAssignment>()); + person.setActive(true); + return person; + } + + private static final class PersonBuilder + { + private Person person; + + PersonBuilder(Principal principal) + { + PersonFetchOptions fetchOptions = new PersonFetchOptions(); + fetchOptions.withRoleAssignments().withSpace(); + person = createPerson(principal, fetchOptions); + } + + Person get() + { + return person; + } + + PersonBuilder roleAssignments(RoleAssignment... roleAssignments) + { + for (RoleAssignment roleAssignment : roleAssignments) + { + person.getRoleAssignments().add(roleAssignment); + } + return this; + } + + PersonBuilder deactive() + { + person.setActive(false); + return this; + } + } + + private static final class AuthorizationGroupBuilder + { + private AuthorizationGroup authorizationGroup; + + AuthorizationGroupBuilder(String code) + { + authorizationGroup = new AuthorizationGroup(); + AuthorizationGroupFetchOptions fetchOptions = new AuthorizationGroupFetchOptions(); + fetchOptions.withUsers(); + authorizationGroup.setFetchOptions(fetchOptions); + authorizationGroup.setCode(code); + authorizationGroup.setPermId(new AuthorizationGroupPermId(code)); + authorizationGroup.setUsers(new ArrayList<>()); + } + + public AuthorizationGroupBuilder users(Principal... principals) + { + PersonFetchOptions personFetchOptions = new PersonFetchOptions(); + Function<Principal, Person> mapper = p -> createPerson(p, personFetchOptions); + authorizationGroup.getUsers().addAll(Arrays.asList(principals).stream().map(mapper).collect(Collectors.toList())); + return this; + } + + AuthorizationGroup get() + { + return authorizationGroup; + } + } +}