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 c0d98921e66941e8b2c2ad916890f19d89f81599..839c81a21829ed3f78a856c71bc1371ca580de18 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,7 +27,6 @@ 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;
@@ -36,7 +35,6 @@ 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;
 
@@ -131,7 +129,7 @@ public class UserManagementMaintenanceTask implements IMaintenanceTask
             return null;
         }
     }
-
+    
     private boolean addGroup(UserManager userManager, UserGroup group)
     {
         String key = group.getKey();
@@ -191,7 +189,11 @@ public class UserManagementMaintenanceTask implements IMaintenanceTask
 
     protected UserManager createUserManager(UserManagerConfig config, Log4jSimpleLogger logger)
     {
-        return new UserManager(ldapService, CommonServiceProvider.getApplicationServerApi(),
-                config.getCommonSpaces(), logger, SystemTimeProvider.SYSTEM_TIME_PROVIDER);
+        UserManager userManager = new UserManager(ldapService, CommonServiceProvider.getApplicationServerApi(),
+                logger, SystemTimeProvider.SYSTEM_TIME_PROVIDER);
+        userManager.setGlobalSpaces(config.getGlobalSpaces());
+        userManager.setCommonSpacesByRole(config.getCommonSpaces());
+        userManager.setSamplesByType(config.getCommonSamples());
+        return userManager;
     }
 }
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 21f1e8828fd6388aa3ab7f913b6cd4a8fa1caf58..e8d6bcf648f1054474a2ab563e306228fe2003ae 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
@@ -20,6 +20,8 @@ import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
@@ -27,13 +29,17 @@ import java.util.Set;
 import java.util.TreeMap;
 import java.util.TreeSet;
 import java.util.function.Function;
+import java.util.function.Predicate;
 import java.util.stream.Collectors;
 
+import org.apache.commons.collections4.map.LinkedMap;
+
 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.search.AuthorizationGroupSearchCriteria;
 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;
@@ -48,14 +54,20 @@ import ch.ethz.sis.openbis.generic.asapi.v3.dto.person.search.PersonSearchCriter
 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.RoleAssignment;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.roleassignment.RoleLevel;
 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.roleassignment.delete.DeleteRoleAssignmentsOperation;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.roleassignment.delete.RoleAssignmentDeletionOptions;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.roleassignment.id.IRoleAssignmentId;
 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.asapi.v3.dto.space.search.SpaceSearchCriteria;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.IApplicationServerInternalApi;
 import ch.systemsx.cisd.authentication.IAuthenticationService;
 import ch.systemsx.cisd.authentication.Principal;
@@ -68,6 +80,10 @@ import ch.systemsx.cisd.common.utilities.ITimeProvider;
  */
 public class UserManager
 {
+    private static final String GLOBAL_AUTHORIZATION_GROUP_CODE = "ALL_GROUPS";
+
+    private static final AuthorizationGroupPermId GLOBAL_AUTHORIZATION_GROUP_ID = new AuthorizationGroupPermId(GLOBAL_AUTHORIZATION_GROUP_CODE);
+
     private final IAuthenticationService authenticationService;
 
     private final IApplicationServerInternalApi service;
@@ -76,24 +92,42 @@ public class UserManager
 
     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 Map<String, Map<String, Principal>> usersByGroupCode = new LinkedHashMap<>();
 
     private final List<String> groupCodes = new ArrayList<>();
 
+    private List<String> globalSpaces = new ArrayList<>();
+
+    private Map<Role, List<String>> commonSpacesByRole = new HashMap<>();
+
+    private Map<String, String> samplesByType = new HashMap<>();
+
     public UserManager(IAuthenticationService authenticationService, IApplicationServerInternalApi service,
-            Map<Role, List<String>> commonSpacesByRole, ISimpleLogger logger, ITimeProvider timeProvider)
+            ISimpleLogger logger, ITimeProvider timeProvider)
     {
         this.authenticationService = authenticationService;
         this.service = service;
-        this.commonSpacesByRole = commonSpacesByRole;
         this.logger = logger;
         this.timeProvider = timeProvider;
     }
 
+    public void setGlobalSpaces(List<String> globalSpaces)
+    {
+        this.globalSpaces = globalSpaces;
+    }
+
+    public void setCommonSpacesByRole(Map<Role, List<String>> commonSpacesByRole)
+    {
+        this.commonSpacesByRole = commonSpacesByRole;
+    }
+
+    public void setSamplesByType(Map<String, String> samplesByType)
+    {
+        this.samplesByType = samplesByType;
+    }
+
     public void addGroup(UserGroup group, Map<String, Principal> principalsByUserId)
     {
         String groupCode = group.getKey().toUpperCase();
@@ -121,8 +155,14 @@ public class UserManager
         {
             String sessionToken = service.loginAsSystem();
 
-            manageGroups(sessionToken, report);
-            manageUsers(sessionToken, report);
+            manageGlobalSpaces(sessionToken, report);
+            revokeUsersUnkownByAuthenticationService(sessionToken, report);
+            CurrentState currentState = loadCurrentState(sessionToken, service);
+            for (Entry<String, Map<String, Principal>> entry : usersByGroupCode.entrySet())
+            {
+                String groupCode = entry.getKey();
+                manageGroup(sessionToken, groupCode, entry.getValue(), currentState, report);
+            }
 
             service.logout(sessionToken);
         } catch (Throwable e)
@@ -133,90 +173,92 @@ public class UserManager
         return report;
     }
 
-    private void manageGroups(String sessionToken, UserManagerReport report)
+    private void manageGlobalSpaces(String sessionToken, UserManagerReport report)
     {
-        Map<String, Set<String>> usersByGroupCodes = getUsersByGroupCodes(sessionToken);
-        for (String groupCode : groupCodes)
+        if (globalSpaces != null && globalSpaces.isEmpty() == false)
         {
-            try
-            {
-                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);
-                report.addErrorMessage(message);
-                logger.log(LogLevel.ERROR, message, e);
-            }
+            createGlobalSpaces(sessionToken, report);
+            Set<String> knownGlobalSpaces = createGlobalGroupAndGetKnownSpaces(sessionToken, GLOBAL_AUTHORIZATION_GROUP_ID, report);
+            createGlobalRoleAssignments(sessionToken, GLOBAL_AUTHORIZATION_GROUP_ID, knownGlobalSpaces, report);
         }
     }
 
-    private void manageUsers(String sessionToken, UserManagerReport report)
+    private void createGlobalSpaces(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())
+        List<SpacePermId> spaceIds = globalSpaces.stream().map(SpacePermId::new).collect(Collectors.toList());
+        Set<ISpaceId> knownSpaces = service.getSpaces(sessionToken, spaceIds, new SpaceFetchOptions()).keySet();
+        List<SpaceCreation> spaceCreations = new ArrayList<>();
+        for (SpacePermId spaceId : spaceIds)
         {
-            try
+            if (knownSpaces.contains(spaceId) == false)
             {
-                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);
-                report.addErrorMessage(message);
-                logger.log(LogLevel.ERROR, message, e);
+                SpaceCreation spaceCreation = new SpaceCreation();
+                spaceCreation.setCode(spaceId.getPermId());
+                spaceCreations.add(spaceCreation);
             }
         }
+        if (spaceCreations.isEmpty() == false)
+        {
+            service.createSpaces(sessionToken, spaceCreations);
+            report.addSpaces(spaceCreations);
+        }
+    }
 
-        for (String groupCode : currentUsersByGroupCodes.keySet())
+    private Set<String> createGlobalGroupAndGetKnownSpaces(String sessionToken, AuthorizationGroupPermId groupId, UserManagerReport report)
+    {
+        AuthorizationGroupFetchOptions fetchOptions = new AuthorizationGroupFetchOptions();
+        fetchOptions.withRoleAssignments().withSpace();
+        AuthorizationGroup group = service.getAuthorizationGroups(sessionToken, Arrays.asList(groupId), fetchOptions).get(groupId);
+        Set<String> knownGlobalSpaces = new TreeSet<>();
+        if (group == null)
         {
-            try
-            {
-                manageUsersRemovedFromGroup(sessionToken, groupCode, currentUsersByGroupCodes,
-                        currentAdminUsersByGroupCodes, report);
-            } catch (Exception e)
+            AuthorizationGroupCreation groupCreation = new AuthorizationGroupCreation();
+            groupCreation.setCode(GLOBAL_AUTHORIZATION_GROUP_CODE);
+            groupCreation.setDescription("Authorization group for all users of all groups");
+            service.createAuthorizationGroups(sessionToken, Arrays.asList(groupCreation));
+            report.addGroup(GLOBAL_AUTHORIZATION_GROUP_CODE);
+        } else
+        {
+            for (RoleAssignment roleAssignment : group.getRoleAssignments())
             {
-                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);
+                if (RoleLevel.SPACE.equals(roleAssignment.getRoleLevel()) && Role.OBSERVER.equals(roleAssignment.getRole()))
+                {
+                    knownGlobalSpaces.add(roleAssignment.getSpace().getCode());
+                }
             }
         }
+        return knownGlobalSpaces;
     }
 
-    private void manageUsersRemovedFromGroup(String sessionToken, String groupCode, Map<String, Set<String>> currentUsersByGroupCodes,
-            Map<String, Set<String>> currentAdminUsersByGroupCodes, UserManagerReport report)
+    private void createGlobalRoleAssignments(String sessionToken, AuthorizationGroupPermId groupId, Set<String> knownGlobalSpaces,
+            UserManagerReport report)
     {
-        Set<String> currentUsers = currentUsersByGroupCodes.get(groupCode);
-        Set<String> newUsers = usersByGroupCode.get(groupCode).keySet();
-        Context context = new Context(sessionToken);
-        for (String currentUser : currentUsers)
+        List<RoleAssignmentCreation> assignmentCreations = new ArrayList<>();
+        for (String spaceCode : globalSpaces)
         {
-            if (newUsers.contains(currentUser) == false)
+            if (knownGlobalSpaces.contains(spaceCode) == false)
             {
-                removePersonFromAuthorizationGroup(context, currentUsersByGroupCodes, groupCode, currentUser, report);
-                String adminGroupCode = createAdminGroupCode(groupCode);
-                removePersonFromAuthorizationGroup(context, currentAdminUsersByGroupCodes, adminGroupCode, currentUser, report);
+                RoleAssignmentCreation assignmentCreation = new RoleAssignmentCreation();
+                assignmentCreation.setAuthorizationGroupId(groupId);
+                assignmentCreation.setRole(Role.OBSERVER);
+                SpacePermId spaceId = new SpacePermId(spaceCode);
+                assignmentCreation.setSpaceId(spaceId);
+                assignmentCreations.add(assignmentCreation);
+                report.assignRoleTo(groupId, assignmentCreation.getRole(), spaceId);
             }
         }
-        context.executeOperations();
+        if (assignmentCreations.isEmpty() == false)
+        {
+            service.createRoleAssignments(sessionToken, assignmentCreations);
+        }
     }
 
     private void revokeUsersUnkownByAuthenticationService(String sessionToken, UserManagerReport report)
     {
         List<PersonUpdate> updates = new ArrayList<>();
-        List<Person> persons = service.searchPersons(sessionToken, new PersonSearchCriteria(), new PersonFetchOptions()).getObjects();
+        PersonSearchCriteria searchCriteria = new PersonSearchCriteria();
+        PersonFetchOptions fetchOptions = new PersonFetchOptions();
+        List<Person> persons = service.searchPersons(sessionToken, searchCriteria, fetchOptions).getObjects();
         for (Person person : persons)
         {
             if (person.isActive())
@@ -240,19 +282,79 @@ public class UserManager
         }
     }
 
-    private void manageKnownGroup(Context context, String groupCode, UserManagerReport report)
+    private CurrentState loadCurrentState(String sessionToken, IApplicationServerInternalApi service)
+    {
+        List<AuthorizationGroup> authorizationGroups = getAllAuthorizationGroups(sessionToken, service);
+        List<Person> users = getAllUsers(sessionToken, service);
+        List<Space> spaces = getAllSpaces(sessionToken, service);
+        List<AuthorizationGroupPermId> ids = Arrays.asList(GLOBAL_AUTHORIZATION_GROUP_ID);
+        AuthorizationGroupFetchOptions fetchOptions = new AuthorizationGroupFetchOptions();
+        fetchOptions.withRoleAssignments().withSpace();
+        fetchOptions.withUsers();
+        AuthorizationGroup group = service.getAuthorizationGroups(sessionToken, ids, fetchOptions).get(GLOBAL_AUTHORIZATION_GROUP_ID);
+        return new CurrentState(authorizationGroups, group, spaces, users);
+    }
+
+    private List<AuthorizationGroup> getAllAuthorizationGroups(String sessionToken, IApplicationServerInternalApi service)
+    {
+        AuthorizationGroupSearchCriteria searchCriteria = new AuthorizationGroupSearchCriteria();
+        AuthorizationGroupFetchOptions fetchOptions = new AuthorizationGroupFetchOptions();
+        fetchOptions.withUsers().withSpace();
+        fetchOptions.withRoleAssignments().withSpace();
+        return service.searchAuthorizationGroups(sessionToken, searchCriteria, fetchOptions).getObjects();
+    }
+
+    private List<Person> getAllUsers(String sessionToken, IApplicationServerInternalApi service)
+    {
+        PersonSearchCriteria searchCriteria = new PersonSearchCriteria();
+        PersonFetchOptions fetchOptions = new PersonFetchOptions();
+        fetchOptions.withRoleAssignments().withSpace();
+        fetchOptions.withSpace();
+        return service.searchPersons(sessionToken, searchCriteria, fetchOptions).getObjects();
+    }
+
+    private List<Space> getAllSpaces(String sessionToken, IApplicationServerInternalApi service)
+    {
+        SpaceSearchCriteria searchCriteria = new SpaceSearchCriteria();
+        SpaceFetchOptions fetchOptions = new SpaceFetchOptions();
+        return service.searchSpaces(sessionToken, searchCriteria, fetchOptions).getObjects();
+    }
+
+    private void manageGroup(String sessionToken, String groupCode, Map<String, Principal> groupUsers,
+            CurrentState currentState, UserManagerReport report)
+    {
+        try
+        {
+            Context context = new Context(sessionToken, service, currentState, report);
+            if (currentState.groupExists(groupCode))
+            {
+                manageKnownGroup(context, groupCode, groupUsers);
+            } else
+            {
+                manageNewGroup(context, groupCode, groupUsers);
+            }
+            context.executeOperations();
+        } catch (Exception e)
+        {
+            String message = String.format("Couldn't manage group '%s' because of the following error: %s",
+                    groupCode, e);
+            report.addErrorMessage(message);
+            logger.log(LogLevel.ERROR, message, e);
+        }
+    }
+
+    private void manageKnownGroup(Context context, String groupCode, Map<String, Principal> groupUsers)
     {
-        // TODO Auto-generated method stub
+        manageUsers(context, groupCode, groupUsers);
     }
 
-    private void manageNewGroup(Context context, String groupCode, UserManagerReport report)
+    private void manageNewGroup(Context context, String groupCode, Map<String, Principal> groupUsers)
     {
         String adminGroupCode = createAdminGroupCode(groupCode);
-        assertAuthorizationGroupDoesNotExist(context, adminGroupCode);
         assertNoCommonSpaceExists(context, groupCode);
 
-        createAuthorizationGroup(context, groupCode, report);
-        createAuthorizationGroup(context, adminGroupCode, report);
+        AuthorizationGroupPermId groupId = createAuthorizationGroup(context, groupCode);
+        AuthorizationGroupPermId adminGroupId = createAuthorizationGroup(context, adminGroupCode);
 
         for (Entry<Role, List<String>> entry : commonSpacesByRole.entrySet())
         {
@@ -260,22 +362,163 @@ public class UserManager
             for (String space : entry.getValue())
             {
                 ISpaceId spaceId = createSpace(context, createCommonSpaceCode(groupCode, space));
-                report.addSpace(spaceId);
-                createRoleAssignment(context, new AuthorizationGroupPermId(groupCode), role, spaceId, report);
-                createRoleAssignment(context, new AuthorizationGroupPermId(adminGroupCode), Role.ADMIN, spaceId, report);
+                createRoleAssignment(context, groupId, role, spaceId);
+                createRoleAssignment(context, adminGroupId, Role.ADMIN, spaceId);
             }
         }
+
+        manageUsers(context, groupCode, groupUsers);
     }
 
-    private void assertAuthorizationGroupDoesNotExist(Context context, String adminGroup)
+    private void manageUsers(Context context, String groupCode, Map<String, Principal> groupUsers)
     {
-        if (service.getAuthorizationGroups(context.getSessionToken(), Arrays.asList(new AuthorizationGroupPermId(adminGroup)),
-                new AuthorizationGroupFetchOptions()).isEmpty() == false)
+        Map<String, Person> currentUsersOfGroup = context.currentState.getCurrentUsersOfGroup(groupCode);
+        Set<String> usersToBeRemoved = new TreeSet<>(currentUsersOfGroup.keySet());
+        AuthorizationGroup globalGroup = context.currentState.getGlobalGroup();
+        String adminGroupCode = createAdminGroupCode(groupCode);
+        AuthorizationGroupPermId adminGroupId = new AuthorizationGroupPermId(adminGroupCode);
+        for (Principal user : groupUsers.values())
         {
-            throw new IllegalStateException("Authorization group " + adminGroup + " already exists.");
+            String userId = user.getUserId();
+            usersToBeRemoved.remove(userId);
+            PersonPermId personId = new PersonPermId(userId);
+            if (currentUsersOfGroup.containsKey(userId) == false)
+            {
+                ISpaceId userSpaceId = createUserSpace(context, groupCode, userId);
+                Person knownUser = context.getCurrentState().getUser(userId);
+                if (context.getCurrentState().userExists(userId) == false)
+                {
+                    PersonCreation personCreation = new PersonCreation();
+                    personCreation.setUserId(userId);
+                    context.add(personCreation);
+                    context.getCurrentState().addNewUser(userId);
+                    context.getReport().addUser(userId);
+                    assignHomeSpace(context, personId, userSpaceId);
+                } else if (knownUser != null)
+                {
+                    if (knownUser.isActive() == false)
+                    {
+                        context.getReport().reuseUser(userId);
+                    }
+                    if (knownUser.getSpace() == null || knownUser.isActive() == false)
+                    {
+                        assignHomeSpace(context, personId, userSpaceId);
+                    }
+                }
+                RoleAssignmentCreation roleCreation = new RoleAssignmentCreation();
+                roleCreation.setUserId(personId);
+                roleCreation.setRole(Role.ADMIN);
+                roleCreation.setSpaceId(userSpaceId);
+                context.add(roleCreation);
+                createRoleAssignment(context, adminGroupId, Role.ADMIN, userSpaceId);
+            }
+            addPersonToAuthorizationGroup(context, groupCode, userId);
+            if (globalGroup != null)
+            {
+                addPersonToAuthorizationGroup(context, globalGroup.getCode(), userId);
+            }
+            if (isAdmin(userId, groupCode))
+            {
+                addPersonToAuthorizationGroup(context, adminGroupCode, userId);
+            } else
+            {
+                removePersonFromAuthorizationGroup(context, adminGroupCode, userId);
+            }
+        }
+        removeUsersFromGroup(context, groupCode, usersToBeRemoved);
+    }
+    
+    private void removeUsersFromGroup(Context context, String groupCode, Set<String> usersToBeRemoved)
+    {
+        String adminGroupCode = createAdminGroupCode(groupCode);
+        Map<String, RoleAssignment> spaceRoles = context.currentState.getCurrentSpaceRolesOfGroup(adminGroupCode);
+        for (String user : usersToBeRemoved)
+        {
+            removePersonFromAuthorizationGroup(context, groupCode, user);
+            removePersonFromAuthorizationGroup(context, adminGroupCode, user);
+            for (RoleAssignment role : spaceRoles.values())
+            {
+                if (role.getSpace().getCode().startsWith(groupCode + "_" + user))
+                {
+                    context.delete(role.getId());
+                }
+            }
         }
     }
 
+    private ISpaceId createUserSpace(Context context, String groupCode, String userId)
+    {
+        String userSpaceCode = createCommonSpaceCode(groupCode, userId.toUpperCase());
+        int n = context.getCurrentState().getNumberOfSpacesStartingWith(userSpaceCode);
+        if (n > 0)
+        {
+            userSpaceCode += "_" + (n + 1);
+        }
+        return createSpace(context, userSpaceCode);
+    }
+    
+    private void assignHomeSpace(Context context, PersonPermId personId, ISpaceId homeSpaceId)
+    {
+        PersonUpdate personUpdate = new PersonUpdate();
+        personUpdate.setUserId(personId);
+        personUpdate.setSpaceId(homeSpaceId);
+        context.add(personUpdate);
+    }
+    
+    private boolean isAdmin(String userId, String groupCode)
+    {
+        UserInfo userInfo = userInfosByUserId.get(userId);
+        if (userInfo == null)
+        {
+            return false;
+        }
+        GroupInfo groupInfo = userInfo.getGroupInfosByGroupKey().get(groupCode);
+        return groupInfo != null && groupInfo.isAdmin();
+    }
+
+    private void addPersonToAuthorizationGroup(Context context, String groupCode, String userId)
+    {
+        if (context.currentState.getCurrentUsersOfGroup(groupCode).keySet().contains(userId) == false)
+        {
+            AuthorizationGroupUpdate groupUpdate = new AuthorizationGroupUpdate();
+            groupUpdate.setAuthorizationGroupId(new AuthorizationGroupPermId(groupCode));
+            groupUpdate.getUserIds().add(new PersonPermId(userId));
+            context.add(groupUpdate);
+            context.getReport().addUserToGroup(groupCode, userId);
+        }
+    }
+
+    private void removePersonFromAuthorizationGroup(Context context, String groupCode, String userId)
+    {
+        if (context.currentState.getCurrentUsersOfGroup(groupCode).keySet().contains(userId))
+        {
+            AuthorizationGroupUpdate groupUpdate = new AuthorizationGroupUpdate();
+            groupUpdate.setAuthorizationGroupId(new AuthorizationGroupPermId(groupCode));
+            groupUpdate.getUserIds().remove(new PersonPermId(userId));
+            context.add(groupUpdate);
+            context.getReport().removeUserFromGroup(groupCode, userId);
+        }
+    }
+
+    private AuthorizationGroupPermId createAuthorizationGroup(Context context, String groupCode)
+    {
+        AuthorizationGroupCreation creation = new AuthorizationGroupCreation();
+        creation.setCode(groupCode);
+        context.add(creation);
+        context.getReport().addGroup(groupCode);
+        return new AuthorizationGroupPermId(groupCode);
+    }
+
+    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);
+        context.getReport().assignRoleTo(groupId, role, spaceId);
+    }
+    
     private void assertNoCommonSpaceExists(Context context, String groupCode)
     {
         Set<String> commonSpaces = new TreeSet<>();
@@ -293,82 +536,270 @@ public class UserManager
         throw new IllegalStateException("The group '" + groupCode + "' has already the following spaces: " + existingSpaces);
     }
 
+    private static final class CurrentState
+    {
+        private Map<String, AuthorizationGroup> groupsByCode = new TreeMap<>();
+
+        private Map<String, Space> spacesByCode = new TreeMap<>();
+
+        private Map<String, Person> usersById = new TreeMap<>();
+
+        private Set<String> newUsers = new TreeSet<>();
+
+        private AuthorizationGroup globalGroup;
+
+        CurrentState(List<AuthorizationGroup> authorizationGroups, AuthorizationGroup globalGroup, List<Space> spaces, List<Person> users)
+        {
+            this.globalGroup = globalGroup;
+            authorizationGroups.forEach(group -> groupsByCode.put(group.getCode(), group));
+            groupsByCode.put(GLOBAL_AUTHORIZATION_GROUP_CODE, globalGroup);
+            spaces.forEach(space -> spacesByCode.put(space.getCode(), space));
+            users.forEach(user -> usersById.put(user.getUserId(), user));
+        }
+
+        public Map<String, RoleAssignment> getCurrentSpaceRolesOfGroup(String groupCode)
+        {
+            Map<String, RoleAssignment> result = new TreeMap<>();
+            AuthorizationGroup group = groupsByCode.get(groupCode);
+            if (group != null)
+            {
+                List<RoleAssignment> roleAssignments = group.getRoleAssignments();
+                for (RoleAssignment roleAssignment : roleAssignments)
+                {
+                    if (RoleLevel.SPACE.equals(roleAssignment.getRoleLevel()) && Role.OBSERVER.equals(roleAssignment.getRole()))
+                    {
+                        Space space = roleAssignment.getSpace();
+                        result.put(space.getCode(), roleAssignment);
+                    }
+                }
+            }
+            return result;
+        }
+
+        public Map<String, Person> getCurrentUsersOfGroup(String groupCode)
+        {
+            Map<String, Person> result = new TreeMap<>();
+            AuthorizationGroup group = groupsByCode.get(groupCode);
+            if (group != null)
+            {
+                group.getUsers().forEach(user -> result.put(user.getUserId(), user));
+            }
+            return result;
+        }
+
+        public AuthorizationGroup getGlobalGroup()
+        {
+            return globalGroup;
+        }
+
+        public boolean userExists(String userId)
+        {
+            return newUsers.contains(userId) || usersById.containsKey(userId);
+        }
+
+        public int getNumberOfSpacesStartingWith(String userSpaceCode)
+        {
+            Predicate<String> predicate = code -> code.startsWith(userSpaceCode);
+            return spacesByCode.keySet().stream().filter(predicate).collect(Collectors.counting()).intValue();
+        }
+
+        public Person getUser(String userId)
+        {
+            return usersById.get(userId);
+        }
+
+        public boolean groupExists(String groupCode)
+        {
+            boolean groupExists = groupsByCode.containsKey(groupCode);
+            String adminGroupCode = createAdminGroupCode(groupCode);
+            boolean adminGroupExists = groupsByCode.containsKey(adminGroupCode);
+            if (groupExists)
+            {
+                if (adminGroupExists == false)
+                {
+                    throw new IllegalArgumentException("Group " + groupCode + " exists but not " + adminGroupCode);
+                }
+                return true;
+            }
+            if (adminGroupExists)
+            {
+                throw new IllegalArgumentException("Group " + groupCode + " does not exist but " + adminGroupCode);
+            }
+            return false;
+        }
+
+        public void addNewUser(String userId)
+        {
+            newUsers.add(userId);
+        }
+    }
+
+    private void manageUsers(String sessionToken, Map<IPersonId, Person> currentUsers, UserManagerReport report)
+    {
+        Map<String, UsersAndRoleAssignments> currentUsersAndSpacesByGroupCodes = getUsersAndSpacesByGroupCodes(sessionToken);
+        Map<String, UsersAndRoleAssignments> currentUsersAndSpacesByAdminGroupCodes = getUsersAndSpacesByAdminGroupCodes(sessionToken);
+        for (UserInfo userInfo : userInfosByUserId.values())
+        {
+            try
+            {
+                manageUser(sessionToken, userInfo, currentUsers, currentUsersAndSpacesByGroupCodes, currentUsersAndSpacesByAdminGroupCodes, report);
+            } catch (Exception e)
+            {
+                String message = String.format("Couldn't manage user '%s' because of the following error: %s",
+                        userInfo.getPrincipal().getUserId(), e);
+                report.addErrorMessage(message);
+                logger.log(LogLevel.ERROR, message, e);
+            }
+        }
+
+        for (String groupCode : currentUsersAndSpacesByGroupCodes.keySet())
+        {
+            try
+            {
+                manageUsersRemovedFromGroup(sessionToken, groupCode, currentUsersAndSpacesByGroupCodes,
+                        currentUsersAndSpacesByAdminGroupCodes, 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);
+            }
+        }
+    }
+
+    private void manageUsersRemovedFromGroup(String sessionToken, String groupCode,
+            Map<String, UsersAndRoleAssignments> currentUsersAndSpacesByGroupCodes,
+            Map<String, UsersAndRoleAssignments> currentUsersAndSpacesByAdminGroupCodes,
+            UserManagerReport report)
+    {
+        Set<String> currentUsers = currentUsersAndSpacesByGroupCodes.get(groupCode).users;
+        Set<String> newUsers = usersByGroupCode.get(groupCode).keySet();
+        Context context = new Context(sessionToken, service, null, report);
+        for (String currentUser : currentUsers)
+        {
+            if (newUsers.contains(currentUser) == false)
+            {
+                removePersonFromAuthorizationGroup(context, currentUsersAndSpacesByGroupCodes, groupCode, currentUser, report);
+                String adminGroupCode = createAdminGroupCode(groupCode);
+                removePersonFromAuthorizationGroup(context, currentUsersAndSpacesByAdminGroupCodes, adminGroupCode, currentUser, report);
+            }
+        }
+        context.executeOperations();
+    }
+
+    private void manageKnownGroup(Context context, String groupCode, Map<IPersonId, Person> currentUsers, UserManagerReport report)
+    {
+        String adminGroupCode = createAdminGroupCode(groupCode);
+        Map<String, UsersAndRoleAssignments> usersAndSpacesByAdminGroupCodes = getUsersAndSpacesByAdminGroupCodes(context.getSessionToken());
+        UsersAndRoleAssignments usersAndSpaces = usersAndSpacesByAdminGroupCodes.get(adminGroupCode);
+        Collection<Principal> users = usersByGroupCode.get(groupCode).values();
+        if (usersAndSpaces != null && users != null)
+        {
+            for (Principal user : users)
+            {
+                String userId = user.getUserId();
+                Person knownUser = currentUsers.get(new PersonPermId(userId));
+                if (knownUser != null)
+                {
+                    Space homeSpace = knownUser.getSpace();
+                    if (homeSpace != null)
+                    {
+                        RoleAssignment roleAssignment = usersAndSpaces.getRoleAssignment(homeSpace.getCode());
+                        if (roleAssignment != null)
+                        {
+                            context.delete(roleAssignment.getId());
+                            report.unassignRoleFrom(new AuthorizationGroupPermId(adminGroupCode), Role.ADMIN, homeSpace.getPermId());
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    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 manageUser(String sessionToken, UserInfo userInfo, Map<IPersonId, Person> knownUsers,
-            Map<String, Set<String>> currentUsersByGroupCodes, Map<String, Set<String>> currentAdminUsersByGroupCodes,
+            Map<String, UsersAndRoleAssignments> currentUsersAndSpacesByGroupCodes,
+            Map<String, UsersAndRoleAssignments> currentUsersAndSpacesByAdminGroupCodes,
             UserManagerReport report)
     {
-        Context context = new Context(sessionToken);
+        Context context = new Context(sessionToken, service, null, report);
         Person knownUser = knownUsers.get(new PersonPermId(userInfo.getPrincipal().getUserId()));
         if (knownUser != null)
         {
-            manageKnownUser(context, userInfo, knownUser, currentUsersByGroupCodes, currentAdminUsersByGroupCodes, report);
+            manageKnownUser(context, userInfo, knownUser, currentUsersAndSpacesByGroupCodes,
+                    currentUsersAndSpacesByAdminGroupCodes, report);
         } else
         {
             manageNewUser(context, userInfo, userInfo.getPrincipal().getUserId().toUpperCase(),
-                    currentUsersByGroupCodes, currentAdminUsersByGroupCodes, report);
+                    currentUsersAndSpacesByGroupCodes, currentUsersAndSpacesByAdminGroupCodes, report);
         }
         context.executeOperations();
     }
 
     private void manageKnownUser(Context context, UserInfo userInfo, Person knownUser,
-            Map<String, Set<String>> currentUsersByGroupCodes, Map<String, Set<String>> currentAdminUsersByGroupCodes,
+            Map<String, UsersAndRoleAssignments> currentUsersAndSpacesByGroupCodes,
+            Map<String, UsersAndRoleAssignments> currentUsersAndSpacesByAdminGroupCodes,
             UserManagerReport report)
     {
+        int numberOfHomeSpaces = getNumberOfHomeSpaces(context, knownUser);
         if (knownUser.isActive() == false)
         {
-            int maxSequenceNumber = getMaxSequenceNumber(context, knownUser);
-            String homeSpaceCode = knownUser.getUserId() + "_" + (maxSequenceNumber + 1);
-            manageNewUser(context, userInfo, homeSpaceCode, currentUsersByGroupCodes, currentAdminUsersByGroupCodes, report);
+            String homeSpaceCode = knownUser.getUserId().toUpperCase() + "_" + (numberOfHomeSpaces + 1);
+            manageNewUser(context, userInfo, homeSpaceCode, currentUsersAndSpacesByGroupCodes,
+                    currentUsersAndSpacesByAdminGroupCodes, report);
         } else
         {
             String userId = userInfo.principal.getUserId();
             for (GroupInfo groupInfo : userInfo.getGroupInfosByGroupKey().values())
             {
                 String groupCode = groupInfo.getKey();
-                addPersonToAuthorizationGroup(context, currentUsersByGroupCodes, groupCode, userId, report);
+                addPersonToAuthorizationGroup(context, currentUsersAndSpacesByGroupCodes, groupCode, userId, report);
                 String adminGroupCode = createAdminGroupCode(groupCode);
+                String homeSpaceCode = knownUser.getUserId().toUpperCase() + (numberOfHomeSpaces > 1 ? "_" + numberOfHomeSpaces : "");
+                UsersAndRoleAssignments usersAndSpaces = currentUsersAndSpacesByAdminGroupCodes.get(adminGroupCode);
+                if (usersAndSpaces != null && usersAndSpaces.getRoleAssignment(homeSpaceCode) == null)
+                {
+                    AuthorizationGroupPermId adminGroupId = new AuthorizationGroupPermId(adminGroupCode);
+                    SpacePermId homeSpaceId = new SpacePermId(homeSpaceCode);
+                    createRoleAssignment(context, adminGroupId, Role.ADMIN, homeSpaceId);
+                }
                 if (groupInfo.isAdmin())
                 {
-                    addPersonToAuthorizationGroup(context, currentAdminUsersByGroupCodes, adminGroupCode, userId, report);
-                } else if (isCurrentAdminUser(currentAdminUsersByGroupCodes, adminGroupCode, userId))
+                    addPersonToAuthorizationGroup(context, currentUsersAndSpacesByAdminGroupCodes, adminGroupCode, userId, report);
+                } else if (isCurrentAdminUser(currentUsersAndSpacesByAdminGroupCodes, adminGroupCode, userId))
                 {
-                    removePersonFromAuthorizationGroup(context, currentAdminUsersByGroupCodes, adminGroupCode, userId, report);
+                    removePersonFromAuthorizationGroup(context, currentUsersAndSpacesByAdminGroupCodes, adminGroupCode, userId, report);
                 }
             }
         }
     }
 
-    private int getMaxSequenceNumber(Context context, Person knownUser)
+    private int getNumberOfHomeSpaces(Context context, Person knownUser)
     {
-        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())
-        {
-            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;
+        SpaceSearchCriteria searchCriteria = new SpaceSearchCriteria();
+        searchCriteria.withCode().thatStartsWith(knownUser.getUserId().toUpperCase());
+        return service.searchSpaces(context.getSessionToken(), searchCriteria, new SpaceFetchOptions()).getTotalCount();
     }
 
-    private boolean isCurrentAdminUser(Map<String, Set<String>> currentAdminUsersByGroupCodes, String groupCode, String userId)
+    private boolean isCurrentAdminUser(Map<String, UsersAndRoleAssignments> currentUsersAndSpacesByAdminGroupCodes,
+            String groupCode, String userId)
     {
-        Set<String> users = currentAdminUsersByGroupCodes.get(groupCode);
+        Set<String> users = currentUsersAndSpacesByAdminGroupCodes.get(groupCode).users;
         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,
+            Map<String, UsersAndRoleAssignments> currentUsersAndSpacesByGroupCodes,
+            Map<String, UsersAndRoleAssignments> currentUsersAndSpacesByAdminGroupCodes,
             UserManagerReport report)
     {
         String userId = userInfo.getPrincipal().getUserId();
@@ -383,12 +814,12 @@ public class UserManager
         for (GroupInfo groupInfo : userInfo.getGroupInfosByGroupKey().values())
         {
             String groupCode = groupInfo.getKey();
-            addPersonToAuthorizationGroup(context, currentUsersByGroupCodes, groupCode, userId, report);
+            addPersonToAuthorizationGroup(context, currentUsersAndSpacesByGroupCodes, groupCode, userId, report);
             String adminGroupCode = createAdminGroupCode(groupCode);
-            createRoleAssignment(context, new AuthorizationGroupPermId(adminGroupCode), Role.ADMIN, homeSpaceId, report);
+            createRoleAssignment(context, new AuthorizationGroupPermId(adminGroupCode), Role.ADMIN, homeSpaceId);
             if (groupInfo.isAdmin())
             {
-                addPersonToAuthorizationGroup(context, currentAdminUsersByGroupCodes, adminGroupCode, userId, report);
+                addPersonToAuthorizationGroup(context, currentUsersAndSpacesByAdminGroupCodes, adminGroupCode, userId, report);
             }
         }
     }
@@ -402,15 +833,12 @@ public class UserManager
             PersonCreation personCreation = new PersonCreation();
             personCreation.setUserId(userId);
             context.add(personCreation);
-            report.addUser(userId, homeSpaceId);
+            report.addUser(userId);
         } else
         {
-            report.reuseUser(userId, homeSpaceId);
+            report.reuseUser(userId);
         }
-        PersonUpdate personUpdate = new PersonUpdate();
-        personUpdate.setUserId(personPermId);
-        personUpdate.setSpaceId(homeSpaceId);
-        context.add(personUpdate);
+        assignHomeSpace(context, personPermId, homeSpaceId);
         RoleAssignmentCreation roleCreation = new RoleAssignmentCreation();
         roleCreation.setUserId(personPermId);
         roleCreation.setRole(Role.ADMIN);
@@ -418,10 +846,11 @@ public class UserManager
         context.add(roleCreation);
     }
 
-    private void addPersonToAuthorizationGroup(Context context, Map<String, Set<String>> currentUsersByGroupCodes,
+    private void addPersonToAuthorizationGroup(Context context,
+            Map<String, UsersAndRoleAssignments> currentUsersAndSpacesByGroupCodes,
             String groupCode, String userId, UserManagerReport report)
     {
-        Set<String> users = currentUsersByGroupCodes.get(groupCode);
+        Set<String> users = currentUsersAndSpacesByGroupCodes.get(groupCode).users;
         if (users == null || users.contains(userId) == false)
         {
             AuthorizationGroupUpdate groupUpdate = new AuthorizationGroupUpdate();
@@ -432,10 +861,11 @@ public class UserManager
         }
     }
 
-    private void removePersonFromAuthorizationGroup(Context context, Map<String, Set<String>> currentUsersByGroupCodes,
+    private void removePersonFromAuthorizationGroup(Context context,
+            Map<String, UsersAndRoleAssignments> currentUsersAndSpacesByGroupCodes,
             String groupCode, String userId, UserManagerReport report)
     {
-        Set<String> users = currentUsersByGroupCodes.get(groupCode);
+        Set<String> users = currentUsersAndSpacesByGroupCodes.get(groupCode).users;
         if (users == null || users.contains(userId))
         {
             AuthorizationGroupUpdate groupUpdate = new AuthorizationGroupUpdate();
@@ -446,31 +876,21 @@ public class UserManager
         }
     }
 
-    private ISpaceId createSpace(Context context, String spaceCode)
+    private void removeSpaceAdminRoleFromAuthorizationGroup(Context context, String groupCode, String userId, UserManagerReport report)
     {
-        SpaceCreation spaceCreation = new SpaceCreation();
-        spaceCreation.setCode(spaceCode);
-        context.add(spaceCreation);
-        return new SpacePermId(spaceCode);
-    }
 
-    private void createAuthorizationGroup(Context context, String groupCode, UserManagerReport report)
-    {
-        AuthorizationGroupCreation creation = new AuthorizationGroupCreation();
-        creation.setCode(groupCode);
-        context.add(creation);
-        report.addGroup(groupCode);
+        AuthorizationGroupUpdate groupUpdate = new AuthorizationGroupUpdate();
+        groupUpdate.setAuthorizationGroupId(new AuthorizationGroupPermId(groupCode));
     }
 
-    private void createRoleAssignment(Context context, AuthorizationGroupPermId groupId, Role role, ISpaceId spaceId,
-            UserManagerReport report)
+    private ISpaceId createSpace(Context context, String spaceCode)
     {
-        RoleAssignmentCreation roleCreation = new RoleAssignmentCreation();
-        roleCreation.setAuthorizationGroupId(groupId);
-        roleCreation.setRole(role);
-        roleCreation.setSpaceId(spaceId);
-        context.add(roleCreation);
-        report.assignRoleTo(groupId, role, spaceId);
+        SpaceCreation spaceCreation = new SpaceCreation();
+        spaceCreation.setCode(spaceCode);
+        context.add(spaceCreation);
+        SpacePermId spaceId = new SpacePermId(spaceCode);
+        context.getReport().addSpace(spaceId);
+        return spaceId;
     }
 
     private String createCommonSpaceCode(String groupCode, String spaceCode)
@@ -499,31 +919,35 @@ public class UserManager
         Function<String, PersonPermId> mapper = userId -> new PersonPermId(userId);
         List<PersonPermId> userIds = userInfosByUserId.keySet().stream().map(mapper).collect(Collectors.toList());
         PersonFetchOptions fetchOptions = new PersonFetchOptions();
+        fetchOptions.withSpace();
         fetchOptions.withRoleAssignments().withSpace();
         Map<IPersonId, Person> users = service.getPersons(sessionToken, userIds, fetchOptions);
         return users;
     }
 
-    private Map<String, Set<String>> getUsersByGroupCodes(String sessionToken)
+    private Map<String, UsersAndRoleAssignments> getUsersAndSpacesByGroupCodes(String sessionToken)
     {
-        return getUsersByGroupCodes(sessionToken, groupCode -> new AuthorizationGroupPermId(groupCode));
+        return getUsersAndSpacesByGroupCodes(sessionToken, groupCode -> new AuthorizationGroupPermId(groupCode));
     }
 
-    private Map<String, Set<String>> getAdminUsersByGroupCodes(String sessionToken)
+    private Map<String, UsersAndRoleAssignments> getUsersAndSpacesByAdminGroupCodes(String sessionToken)
     {
-        return getUsersByGroupCodes(sessionToken, groupCode -> createAdminGroupId(groupCode));
+        return getUsersAndSpacesByGroupCodes(sessionToken, groupCode -> createAdminGroupId(groupCode));
     }
 
-    private Map<String, Set<String>> getUsersByGroupCodes(String sessionToken, Function<String, AuthorizationGroupPermId> mapper)
+    private Map<String, UsersAndRoleAssignments> getUsersAndSpacesByGroupCodes(String sessionToken, Function<String, AuthorizationGroupPermId> mapper)
     {
         List<AuthorizationGroupPermId> groupPermIds = groupCodes.stream().map(mapper).collect(Collectors.toList());
         AuthorizationGroupFetchOptions fetchOptions = new AuthorizationGroupFetchOptions();
         fetchOptions.withUsers();
-        Map<String, Set<String>> usersByGroupCodes = new TreeMap<>();
+        fetchOptions.withRoleAssignments().withSpace();
+        Map<String, UsersAndRoleAssignments> 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);
+            List<RoleAssignment> roleAssignments = group.getRoleAssignments().stream()
+                    .filter(ra -> ra.getSpace() != null).collect(Collectors.toList());
+            usersByGroupCodes.put(group.getCode(), new UsersAndRoleAssignments(users, roleAssignments));
         }
         return usersByGroupCodes;
     }
@@ -593,14 +1017,40 @@ public class UserManager
         {
             return key + (admin ? "*" : "");
         }
+    }
+
+    private static final class UsersAndRoleAssignments
+    {
+        private Set<String> users;
+
+        private Map<String, RoleAssignment> roleAssignmentsBySpace = new TreeMap<>();
+
+        UsersAndRoleAssignments(Set<String> users, List<RoleAssignment> roleAssignments)
+        {
+            this.users = users;
+            for (RoleAssignment roleAssignment : roleAssignments)
+            {
+                roleAssignmentsBySpace.put(roleAssignment.getSpace().getCode(), roleAssignment);
+            }
+        }
+
+        RoleAssignment getRoleAssignment(String spaceCode)
+        {
+            return roleAssignmentsBySpace.get(spaceCode);
+        }
 
+        @Override
+        public String toString()
+        {
+            return "users: " + users + ", spaces: " + roleAssignmentsBySpace.keySet();
+        }
     }
 
-    private final class Context
+    private static final class Context
     {
         private String sessionToken;
 
-        private List<PersonCreation> personCreations = new ArrayList<>();
+        private Map<String, PersonCreation> personCreations = new LinkedMap<>();
 
         private List<PersonUpdate> personUpdates = new ArrayList<>();
 
@@ -612,9 +1062,20 @@ public class UserManager
 
         private List<RoleAssignmentCreation> roleCreations = new ArrayList<>();
 
-        Context(String sessionToken)
+        private List<IRoleAssignmentId> roleDeletions = new ArrayList<>();
+
+        private IApplicationServerInternalApi service;
+
+        private CurrentState currentState;
+
+        private UserManagerReport report;
+
+        Context(String sessionToken, IApplicationServerInternalApi service, CurrentState currentState, UserManagerReport report)
         {
             this.sessionToken = sessionToken;
+            this.service = service;
+            this.currentState = currentState;
+            this.report = report;
         }
 
         public String getSessionToken()
@@ -622,9 +1083,19 @@ public class UserManager
             return sessionToken;
         }
 
+        public CurrentState getCurrentState()
+        {
+            return currentState;
+        }
+
+        public UserManagerReport getReport()
+        {
+            return report;
+        }
+
         public void add(PersonCreation personCreation)
         {
-            personCreations.add(personCreation);
+            personCreations.put(personCreation.getUserId(), personCreation);
         }
 
         public void add(PersonUpdate personUpdate)
@@ -652,12 +1123,17 @@ public class UserManager
             groupUpdates.add(groupUpdate);
         }
 
+        public void delete(IRoleAssignmentId roleAssignmentId)
+        {
+            roleDeletions.add(roleAssignmentId);
+        }
+
         public void executeOperations()
         {
             List<IOperation> operations = new ArrayList<>();
             if (personCreations.isEmpty() == false)
             {
-                operations.add(new CreatePersonsOperation(personCreations));
+                operations.add(new CreatePersonsOperation(new ArrayList<>(personCreations.values())));
             }
             if (personUpdates.isEmpty() == false)
             {
@@ -679,6 +1155,12 @@ public class UserManager
             {
                 operations.add(new CreateRoleAssignmentsOperation(roleCreations));
             }
+            if (roleDeletions.isEmpty() == false)
+            {
+                RoleAssignmentDeletionOptions options = new RoleAssignmentDeletionOptions();
+                options.setReason("Users removed from a group");
+                operations.add(new DeleteRoleAssignmentsOperation(roleDeletions, options));
+            }
             if (operations.isEmpty())
             {
                 return;
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
index 328c31e334d92c9ee547d42fa6c6ca55fca634fb..b5ca92f5e3f7c7c7f3312398c613773bdedfc3ad 100644
--- 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
@@ -16,6 +16,7 @@
 
 package ch.systemsx.cisd.openbis.generic.server.task;
 
+import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -24,10 +25,24 @@ import ch.ethz.sis.openbis.generic.asapi.v3.dto.roleassignment.Role;
 
 class UserManagerConfig
 {
+    private List<String> globalSpaces = new ArrayList<>();
+
     private Map<Role, List<String>> commonSpaces = new HashMap<>();
 
+    private Map<String, String> commonSamples = new HashMap<>();
+
     private List<UserGroup> groups;
 
+    public List<String> getGlobalSpaces()
+    {
+        return globalSpaces;
+    }
+
+    public void setGlobalSpaces(List<String> globalSpaces)
+    {
+        this.globalSpaces = globalSpaces;
+    }
+
     public Map<Role, List<String>> getCommonSpaces()
     {
         return commonSpaces;
@@ -38,6 +53,16 @@ class UserManagerConfig
         this.commonSpaces = commonSpaces;
     }
 
+    public Map<String, String> getCommonSamples()
+    {
+        return commonSamples;
+    }
+
+    public void setCommonSamples(Map<String, String> commonSamples)
+    {
+        this.commonSamples = commonSamples;
+    }
+
     public List<UserGroup> getGroups()
     {
         return groups;
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
index 9bf531d0b3b9f0c385d81a4254f484e4b705b99f..781e43affcccb36b26aa6ab1b4ae1a44ce4354b2 100644
--- 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
@@ -18,9 +18,12 @@ package ch.systemsx.cisd.openbis.generic.server.task;
 
 import java.text.MessageFormat;
 import java.util.Date;
+import java.util.List;
+import java.util.stream.Collectors;
 
 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.create.SpaceCreation;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.space.id.ISpaceId;
 import ch.systemsx.cisd.common.utilities.ITimeProvider;
 
@@ -66,25 +69,35 @@ public class UserManagerReport
         log("DEACTIVATE-USER", userId);
     }
 
-    void addUser(String userId, ISpaceId homeSpaceId)
+    void addUser(String userId)
     {
-        log("ADD-USER", userId + " (home space: " + homeSpaceId + ")");
+        log("ADD-USER", userId);
     }
 
-    public void reuseUser(String userId, ISpaceId homeSpaceId)
+    public void reuseUser(String userId)
     {
-        log("REUSE-USER", userId + " (home space: " + homeSpaceId + ")");
+        log("REUSE-USER", userId);
     }
     
     void addSpace(ISpaceId spaceId)
     {
         log("ADD-SPACE", spaceId);
     }
+    
+    void addSpaces(List<SpaceCreation> spaceCreations)
+    {
+        log("ADD-SPACES", spaceCreations.stream().map(SpaceCreation::getCode).collect(Collectors.toList()).toString());
+    }
 
     void assignRoleTo(AuthorizationGroupPermId groupId, Role role, ISpaceId spaceId)
     {
         log("ASSIGN-ROLE-TO-AUTHORIZATION-GROUP", "group: " + groupId + ", role: SPACE_" + role + " for " + spaceId);
     }
+    
+    public void unassignRoleFrom(AuthorizationGroupPermId groupId, Role role, ISpaceId spaceId)
+    {
+        log("UNASSIGN-ROLE-FORM-AUTHORIZATION-GROUP", "group: " + groupId + ", role: SPACE_" + role + " for " + spaceId);
+    }
 
     void addUserToGroup(String groupCode, String userId)
     {
diff --git a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/task/UserManagementMaintenanceTaskTest.java b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/task/UserManagementMaintenanceTaskTest.java
index 041db12c663789d9501aaa3f619947c00de95806..09585f13d9fa3ba808b1c10e46da462210b6c5fe 100644
--- a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/task/UserManagementMaintenanceTaskTest.java
+++ b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/task/UserManagementMaintenanceTaskTest.java
@@ -32,7 +32,6 @@ import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.roleassignment.Role;
-import ch.ethz.sis.openbis.generic.asapi.v3.dto.space.id.SpacePermId;
 import ch.systemsx.cisd.authentication.Principal;
 import ch.systemsx.cisd.authentication.ldap.LDAPAuthenticationService;
 import ch.systemsx.cisd.authentication.ldap.LDAPDirectoryConfiguration;
@@ -188,7 +187,9 @@ public class UserManagementMaintenanceTaskTest extends AbstractFileSystemTestCas
                 + "INFO  OPERATION.UserManagementMaintenanceTask - Plugin '' initialized. Configuration file: "
                 + configFile.getAbsolutePath() + "\n"
                 + "INFO  OPERATION.UserManagementMaintenanceTask - manage 1 groups\n"
+                + "INFO  OPERATION.UserManagementMaintenanceTask - Global spaces: []\n"
                 + "INFO  OPERATION.UserManagementMaintenanceTask - Common spaces: {}\n"
+                + "INFO  OPERATION.UserManagementMaintenanceTask - Samples by type: {}\n"
                 + "ERROR OPERATION.UserManagementMaintenanceTask - No ldapGroupKeys specified for group 'ABC'. Task aborted.",
                 logRecorder.getLogContent());
     }
@@ -210,7 +211,9 @@ public class UserManagementMaintenanceTaskTest extends AbstractFileSystemTestCas
                 + "INFO  OPERATION.UserManagementMaintenanceTask - Plugin '' initialized. Configuration file: "
                 + configFile.getAbsolutePath() + "\n"
                 + "INFO  OPERATION.UserManagementMaintenanceTask - manage 1 groups\n"
+                + "INFO  OPERATION.UserManagementMaintenanceTask - Global spaces: []\n"
                 + "INFO  OPERATION.UserManagementMaintenanceTask - Common spaces: {}\n"
+                + "INFO  OPERATION.UserManagementMaintenanceTask - Samples by type: {}\n"
                 + "ERROR OPERATION.UserManagementMaintenanceTask - Empty ldapGroupKey for group 'ABC'. Task aborted.",
                 logRecorder.getLogContent());
     }
@@ -232,7 +235,9 @@ public class UserManagementMaintenanceTaskTest extends AbstractFileSystemTestCas
                 + "INFO  OPERATION.UserManagementMaintenanceTask - Plugin '' initialized. Configuration file: "
                 + configFile.getAbsolutePath() + "\n"
                 + "INFO  OPERATION.UserManagementMaintenanceTask - manage 1 groups\n"
+                + "INFO  OPERATION.UserManagementMaintenanceTask - Global spaces: []\n"
                 + "INFO  OPERATION.UserManagementMaintenanceTask - Common spaces: {}\n"
+                + "INFO  OPERATION.UserManagementMaintenanceTask - Samples by type: {}\n"
                 + "ERROR OPERATION.UserManagementMaintenanceTask - No users found for ldapGroupKey 'a1' for group 'ABC'. Task aborted.",
                 logRecorder.getLogContent());
     }
@@ -244,12 +249,13 @@ public class UserManagementMaintenanceTaskTest extends AbstractFileSystemTestCas
         UserManagerReport report = new UserManagerReport(new MockTimeProvider(0, 1000));
         report.addErrorMessage("This is a test error message");
         report.addGroup("blabla");
-        report.addUser("a", new SpacePermId("A"));
+        report.addUser("a");
         UserManagementMaintenanceTaskWithMocks task = new UserManagementMaintenanceTaskWithMocks().withGroup("s", U1)
                 .withUserManagerReport(report);
         FileUtilities.writeToFile(configFile, "");
         task.setUp("", properties);
-        FileUtilities.writeToFile(configFile, "{\"commonSpaces\":{\"USER\": [\"ALPHA\"]},"
+        FileUtilities.writeToFile(configFile, "{\"globalSpaces\":[\"ES\"],\"commonSpaces\":{\"USER\": [\"ALPHA\"]},"
+                + "\"commonSamples\":{\"A\":\"B\"},"
                 + "\"groups\": [{\"name\":\"sis\",\"key\":\"SIS\",\"ldapGroupKeys\": [\"s\"],\"admins\": [\"u2\"]}]}");
 
         // When
@@ -260,7 +266,9 @@ public class UserManagementMaintenanceTaskTest extends AbstractFileSystemTestCas
                 + "INFO  OPERATION.UserManagementMaintenanceTask - Plugin '' initialized. Configuration file: "
                 + configFile.getAbsolutePath() + "\n"
                 + "INFO  OPERATION.UserManagementMaintenanceTask - manage 1 groups\n"
+                + "INFO  OPERATION.UserManagementMaintenanceTask - Global spaces: [ES]\n"
                 + "INFO  OPERATION.UserManagementMaintenanceTask - Common spaces: {USER=[ALPHA]}\n"
+                + "INFO  OPERATION.UserManagementMaintenanceTask - Samples by type: {A=B}\n"
                 + "INFO  OPERATION.UserManagementMaintenanceTask - Add group SIS[name:sis, ldapGroupKeys:[s], admins:[u2]] with users [u1=u1]\n"
                 + "INFO  OPERATION.UserManagementMaintenanceTask - 1 users for group SIS\n"
                 + "ERROR NOTIFY.UserManagementMaintenanceTask - User management failed for the following reason(s):\n\n"
@@ -268,7 +276,7 @@ public class UserManagementMaintenanceTaskTest extends AbstractFileSystemTestCas
                 + "INFO  OPERATION.UserManagementMaintenanceTask - finished",
                 logRecorder.getLogContent());
         assertEquals("1970-01-01 01:00:00 [ADD-AUTHORIZATION-GROUP] blabla\n"
-                + "1970-01-01 01:00:01 [ADD-USER] a (home space: A)\n\n",
+                + "1970-01-01 01:00:01 [ADD-USER] a\n\n",
                 FileUtilities.loadToString(auditLogFile));
     }
 
@@ -316,7 +324,11 @@ public class UserManagementMaintenanceTaskTest extends AbstractFileSystemTestCas
         @Override
         protected UserManager createUserManager(UserManagerConfig config, Log4jSimpleLogger logger)
         {
-            return new MockUserManager(config.getCommonSpaces(), logger);
+            MockUserManager userManager = new MockUserManager(logger);
+            userManager.setGlobalSpaces(config.getGlobalSpaces());
+            userManager.setCommonSpacesByRole(config.getCommonSpaces());
+            userManager.setSamplesByType(config.getCommonSamples());
+            return userManager;
         }
 
         private UserManagementMaintenanceTaskWithMocks withGroup(String groupCode, Principal... users)
@@ -341,11 +353,31 @@ public class UserManagementMaintenanceTaskTest extends AbstractFileSystemTestCas
         {
             private ISimpleLogger logger;
 
-            MockUserManager(Map<Role, List<String>> commonSpacesByRole, ISimpleLogger logger)
+            MockUserManager(ISimpleLogger logger)
             {
-                super(null, null, commonSpacesByRole, logger, null);
+                super(null, null, logger, null);
                 this.logger = logger;
+            }
+
+            @Override
+            public void setGlobalSpaces(List<String> globalSpaces)
+            {
+                logger.log(LogLevel.INFO, "Global spaces: " + globalSpaces);
+                super.setGlobalSpaces(globalSpaces);
+            }
+
+            @Override
+            public void setCommonSpacesByRole(Map<Role, List<String>> commonSpacesByRole)
+            {
                 logger.log(LogLevel.INFO, "Common spaces: " + commonSpacesByRole);
+                super.setCommonSpacesByRole(commonSpacesByRole);
+            }
+
+            @Override
+            public void setSamplesByType(Map<String, String> samplesByType)
+            {
+                logger.log(LogLevel.INFO, "Samples by type: " + samplesByType);
+                super.setSamplesByType(samplesByType);
             }
 
             @Override
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 2333895a262dcb31ea349204f23368e5c8596248..8aba98cbc9b7580dd1c4753c8dd8f77cfe445f43 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
@@ -20,8 +20,10 @@ import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.fail;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
@@ -30,6 +32,7 @@ import java.util.TreeMap;
 import java.util.TreeSet;
 import java.util.function.Consumer;
 import java.util.function.Function;
+import java.util.stream.Collector;
 import java.util.stream.Collectors;
 
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.authorizationgroup.AuthorizationGroup;
@@ -55,7 +58,7 @@ import ch.systemsx.cisd.openbis.generic.shared.dto.SpacePE;
 
 class UserManagerExpectationsBuilder
 {
-    static final String HOME_SPACE_KEY = "home-space";
+    static final String USER_SPACE_POSTFIX_KEY = "user-space-postfix";
 
     private static final String TEST_USER = "test";
 
@@ -69,7 +72,7 @@ class UserManagerExpectationsBuilder
 
     private final Map<Role, List<String>> commonSpaces;
 
-    private Map<String, String> homeSpacesByUserId = new TreeMap<>();
+    private Map<String, Map<String, String>> userSpacesByGroupAndUserId = new TreeMap<>();
 
     private Map<String, List<Principal>> usersByGroup = new TreeMap<>();
 
@@ -81,6 +84,14 @@ class UserManagerExpectationsBuilder
 
     private Map<String, List<Principal>> adminUsersByGroup = new TreeMap<>();
 
+    private Map<String, Set<String>> groupsByAdminUsers = new TreeMap<>();
+
+    private Map<String, Set<String>> userSpacesByGroups = new TreeMap<>();
+
+    private Map<String, String> homeSpacesByUserId = new TreeMap<>();
+
+    private List<String> globalSpaces = new ArrayList<>();;
+
     UserManagerExpectationsBuilder(IApplicationServerInternalApi v3api, UserManagerTestService testService,
             IOpenBisSessionManager sessionManager, Map<Role, List<String>> commonSpaces)
     {
@@ -90,6 +101,11 @@ class UserManagerExpectationsBuilder
         this.commonSpaces = commonSpaces;
     }
 
+    public void setGlobalSpaces(List<String> globalSpaces)
+    {
+        this.globalSpaces = globalSpaces;
+    }
+
     UserManagerExpectationsBuilder unknownUser(Principal user)
     {
         unknownUsers.add(user);
@@ -108,18 +124,44 @@ class UserManagerExpectationsBuilder
 
     UserManagerExpectationsBuilder adminUser(Principal user, String... groups)
     {
+        Set<String> usersAdminGroups = groupsByAdminUsers.get(user.getUserId());
+        if (usersAdminGroups == null)
+        {
+            usersAdminGroups = new TreeSet<>();
+            groupsByAdminUsers.put(user.getUserId(), usersAdminGroups);
+        }
+        usersAdminGroups.addAll(Arrays.asList(groups));
         return addUser(user, adminUsersByGroup, groups);
     }
 
     private UserManagerExpectationsBuilder addUser(Principal user, Map<String, List<Principal>> users, String... groups)
     {
+        String userId = user.getUserId();
+        if (groups.length == 0)
+        {
+            fail("No groups specified for " + userId);
+        }
+        String userSpacePostfix = user.getProperty(USER_SPACE_POSTFIX_KEY);
+        if (userSpacePostfix == null)
+        {
+            userSpacePostfix = userId.toUpperCase();
+        }
         for (String group : groups)
         {
+            if (homeSpacesByUserId.containsKey(userId) == false)
+            {
+                homeSpacesByUserId.put(userId, group + "_" + userSpacePostfix);
+            }
+            Map<String, String> userSpacesByUserId = userSpacesByGroupAndUserId.get(group);
+            if (userSpacesByUserId == null)
+            {
+                userSpacesByUserId = new TreeMap<>();
+                userSpacesByGroupAndUserId.put(group, userSpacesByUserId);
+            }
+            userSpacesByUserId.put(userId, group + "_" + userSpacePostfix);
             addUserToGroup(user, group, users);
             addUserToGroup(user, group, usersByGroup);
         }
-        String homeSpace = user.getProperty(HOME_SPACE_KEY);
-        homeSpacesByUserId.put(user.getUserId(), homeSpace == null ? user.getUserId().toUpperCase() : homeSpace);
         return this;
     }
 
@@ -146,35 +188,26 @@ class UserManagerExpectationsBuilder
 
     private void assertSpaces(String sessionToken)
     {
-        List<SpacePermId> allCommonSpaces = getAllCommonSpaces();
-        assertSpacesExist(sessionToken, allCommonSpaces);
-        List<SpacePermId> allUserSpaces = applyMapperToAllUsers(user -> new SpacePermId(getHomeSpaceFor(user.getUserId())));
-        assertSpacesExist(sessionToken, allUserSpaces);
-    }
-
-    private List<SpacePermId> getAllCommonSpaces()
-    {
-        List<SpacePermId> allCommonSpaces = new ArrayList<>();
-        Set<String> groups = usersByGroup.keySet();
-        for (String group : groups)
+        List<SpacePermId> expectedSpaces = globalSpaces.stream().map(SpacePermId::new).collect(Collectors.toList());
+        for (Entry<String, List<Principal>> entry : usersByGroup.entrySet())
         {
+            String groupCode = entry.getKey();
             for (List<String> list : commonSpaces.values())
             {
                 for (String commonSpace : list)
                 {
-                    allCommonSpaces.add(new SpacePermId(createCommonSpaceCode(group, commonSpace)));
+                    expectedSpaces.add(new SpacePermId(createCommonSpaceCode(groupCode, commonSpace)));
                 }
             }
+            for (Principal user : entry.getValue())
+            {
+                expectedSpaces.add(new SpacePermId(userSpacesByGroupAndUserId.get(groupCode).get(user.getUserId())));
+            }
         }
-        return allCommonSpaces;
-    }
-
-    private void assertSpacesExist(String sessionToken, List<SpacePermId> spaces)
-    {
-        List<String> expectedSpaces = extractedSortedPermIds(spaces);
+        List<String> expectedSpaceCodes = extractedSortedPermIds(expectedSpaces);
         SpaceFetchOptions fetchOptions = new SpaceFetchOptions();
-        List<String> actualSpaces = extractedSortedCodes(v3api.getSpaces(sessionToken, spaces, fetchOptions).values());
-        assertEquals(actualSpaces.toString(), expectedSpaces.toString(), "Spaces");
+        List<String> actualSpaces = extractedSortedCodes(v3api.getSpaces(sessionToken, expectedSpaces, fetchOptions).values());
+        assertEquals(actualSpaces.toString(), expectedSpaceCodes.toString(), "Spaces");
     }
 
     private void assertUnknownUsers(String sessionToken)
@@ -233,89 +266,105 @@ class UserManagerExpectationsBuilder
             assertEquals(person.getUserId(), id.getPermId());
             assertEquals(person.isActive(), Boolean.TRUE);
             assertEquals(person.getEmail(), "franz-josef.elmer@systemsx.ch", "Wrong email of " + person);
-            assertEquals(person.getSpace().getCode(), getHomeSpaceFor(person.getUserId()), "Wrong home space of " + person);
+            assertEquals(person.getSpace().getCode(), homeSpacesByUserId.get(person.getUserId()), "Wrong home space of " + person);
         }
     }
 
     private void assertAuthorization(String sessionToken)
     {
-        AuthorizationExpectations expectations = new AuthorizationExpectations(v3api, sessionManager, testService);
-        createExpectationsForDisabledUsers(expectations);
-        createExpectationsForNormalUsers(expectations);
-        createExpectationsForAdminUsers(expectations);
-        expectations.assertExpectations();
+        assertAuthorizationOfGlobalSpaces();
+        assertAuthorizationOfCommonSpaces(disabledUsersByGroup, Level.NON, Level.NON);
+        assertAuthorizationOfCommonSpaces(normalUsersByGroup, Level.SPACE_USER, Level.SPACE_OBSERVER);
+        assertAuthorizationOfCommonSpaces(adminUsersByGroup, Level.SPACE_ADMIN, Level.SPACE_ADMIN);
+        assertAuthorizationOfUserSpaces();
     }
 
-    private void createExpectationsForDisabledUsers(AuthorizationExpectations expectations)
+    private void assertAuthorizationOfGlobalSpaces()
     {
-        for (Entry<String, List<Principal>> entry : disabledUsersByGroup.entrySet())
+        if (globalSpaces.isEmpty() == false)
         {
-            String groupCode = entry.getKey();
-            for (Principal user : entry.getValue())
+            AuthorizationExpectations expectations = new AuthorizationExpectations(v3api, sessionManager, testService);
+            for (List<Principal> users : usersByGroup.values())
             {
-                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())
+                for (Principal user : users)
                 {
-                    users2.forEach(user2 -> expectations.expect(user, getHomeSpace(user2),
-                            equals(user, user2) ? Level.SPACE_ADMIN : Level.NON));
+                    globalSpaces.forEach(space -> expectations.expect(user, space, Level.SPACE_OBSERVER));
                 }
             }
+            expectations.assertExpectations();
         }
     }
 
-    private void createExpectationsForNormalUsers(AuthorizationExpectations expectations)
+    private void assertAuthorizationOfCommonSpaces(Map<String, List<Principal>> users, Level userLevel, Level observerLevel)
     {
-        for (Entry<String, List<Principal>> entry : normalUsersByGroup.entrySet())
+        AuthorizationExpectations expectations = new AuthorizationExpectations(v3api, sessionManager, testService);
+        for (Entry<String, List<Principal>> entry : users.entrySet())
         {
             String groupCode = entry.getKey();
             for (Principal user : entry.getValue())
             {
-                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, getHomeSpace(user2),
-                            equals(user, user2) ? Level.SPACE_ADMIN : Level.NON));
-                }
+                commonSpaces.get(Role.USER).forEach(expectForSpace(expectations, user, groupCode, userLevel));
+                commonSpaces.get(Role.OBSERVER).forEach(expectForSpace(expectations, user, groupCode, observerLevel));
             }
         }
+        expectations.assertExpectations();
     }
-
-    private void createExpectationsForAdminUsers(AuthorizationExpectations expectations)
+    
+    private void assertAuthorizationOfUserSpaces()
     {
-        for (Entry<String, List<Principal>> entry : adminUsersByGroup.entrySet())
+        Set<String> allUserSpaces = new TreeSet<>();
+        for (Entry<String, List<Principal>> entry : usersByGroup.entrySet())
         {
             String groupCode = entry.getKey();
             for (Principal user : entry.getValue())
             {
-                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)
+                allUserSpaces.add(userSpacesByGroupAndUserId.get(groupCode).get(user.getUserId()));
+            }
+        }
+        AuthorizationExpectations expectations = new AuthorizationExpectations(v3api, sessionManager, testService);
+        for (List<Principal> users : usersByGroup.values())
+        {
+            for (Principal user : users)
+            {
+                Set<String> accessibleUserSpaces = getAllAccessibleUserSpacesFor(user.getUserId());
+                for (String space : allUserSpaces)
                 {
-                    String groupCode2 = entry2.getKey();
-                    List<Principal> users2 = entry2.getValue();
-                    if (groupCode.equals(groupCode2))
-                    {
-                        users2.forEach(user2 -> expectations.expect(user, getHomeSpace(user2), Level.SPACE_ADMIN));
-                    } else
-                    {
-                        users2.forEach(user2 -> expectations.expect(user, getHomeSpace(user2), Level.NON));
-                    }
+                    expectations.expect(user, space, accessibleUserSpaces.contains(space) ? Level.SPACE_ADMIN : Level.NON);
                 }
             }
         }
+        expectations.assertExpectations();
     }
 
-    private String getHomeSpace(Principal user)
-    {
-        return getHomeSpaceFor(user.getUserId());
-    }
-
-    private String getHomeSpaceFor(String userId)
+    private Set<String> getAllAccessibleUserSpacesFor(String userId)
     {
-        return homeSpacesByUserId.get(userId);
+        Set<String> spaces = new TreeSet<>();
+        for (String group : usersByGroup.keySet())
+        {
+            Map<String, String> userSpacesByUserId = userSpacesByGroupAndUserId.get(group);
+            if (userSpacesByUserId != null)
+            {
+                String space = userSpacesByUserId.get(userId);
+                if (space != null)
+                {
+                    spaces.add(space);
+                }
+            }
+        }
+        Set<String> groups = groupsByAdminUsers.get(userId);
+        if (groups != null)
+        {
+            for (String group : groups)
+            {
+                Map<String, String> userSpacesByUserId = userSpacesByGroupAndUserId.get(group);
+                if (userSpacesByUserId != null)
+                {
+                    userSpacesByUserId.values().forEach(space -> spaces.add(space));
+                }
+            }
+        }
+        System.err.println(userId + " is admin of " + spaces);
+        return spaces;
     }
 
     private Consumer<String> expectForSpace(AuthorizationExpectations expectations, Principal user, String groupCode, Level level)
@@ -366,6 +415,7 @@ class UserManagerExpectationsBuilder
             {
                 String userId = entry.getKey();
                 String sessionToken = v3api.login(userId, PASSWORD);
+                int count = 0;
                 try
                 {
                     Session session = sessionManager.getSession(sessionToken);
@@ -376,18 +426,19 @@ class UserManagerExpectationsBuilder
                         for (String space : entry2.getValue())
                         {
                             Level actualLevel = getActualLevel(context, space);
-                            System.out.println("UserManagerTestExpectation: " + level + " for user " + userId + " on space " + space);
                             if (level.equals(actualLevel) == false)
                             {
                                 builder.append("Authorization level for user ").append(userId).append(" on space ").append(space);
                                 builder.append(". Expected: ").append(level).append(", but found: ").append(actualLevel).append("\n");
                             }
+                            count++;
                         }
                     }
                 } finally
                 {
                     v3api.logout(sessionToken);
                 }
+                System.out.println(count + " authorization expectations tested for user " + userId);
             }
             if (builder.length() > 0)
             {
@@ -431,12 +482,12 @@ class UserManagerExpectationsBuilder
 
     private <T> List<T> applyMapperToAllUsers(Function<Principal, T> mapper)
     {
-        List<T> result = new ArrayList<>();
+        Set<T> result = new LinkedHashSet<>();
         for (List<Principal> users : usersByGroup.values())
         {
             result.addAll(users.stream().map(mapper).collect(Collectors.toList()));
         }
-        return result;
+        return new ArrayList<>(result);
     }
 
     private static String createCommonSpaceCode(String groupCode, String spaceCode)
@@ -444,11 +495,6 @@ class UserManagerExpectationsBuilder
         return groupCode + "_" + spaceCode;
     }
 
-    private static boolean equals(Principal user1, Principal user2)
-    {
-        return user1.getUserId().equals(user2.getUserId());
-    }
-
     private enum Level
     {
         NON
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 4dc8559ac650a1a5ba38ddb9ae04a9556b9681b5..3d9a33c947a1ba6e5df55b95f8433d93ddec14c4 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
@@ -54,6 +54,8 @@ public class UserManagerTest extends AbstractTest
 
     private static final Principal U3 = new Principal("u3", "Alan", "Turing", "a.t@abc.de");
 
+    private static final Principal U4 = new Principal("u4", "Leonard", "Euler", "l.e@abc.de");
+
     @Autowired
     private UserManagerTestService testService;
 
@@ -69,15 +71,14 @@ public class UserManagerTest extends AbstractTest
     }
 
     @Test
-    public void testAddNewGroupWithUsers()
+    public void testCreateOneGroupWithAUserWhichAlreadyTriedLoggedIn()
     {
         // Given
+        assertEquals(v3api.login(U2.getUserId(), PASSWORD), null);
         MockLogger logger = new MockLogger();
         Map<Role, List<String>> commonSpaces = commonSpaces();
         UserManager userManager = createUserManager(commonSpaces, logger);
-        Map<String, Principal> principals = principals(U3, U1, U2);
-        UserGroup group = group("G1", U1.getUserId(), "blabla");
-        userManager.addGroup(group, principals);
+        userManager.addGroup(group("G1", U1.getUserId(), "blabla"), users(U1, U2));
 
         // When
         UserManagerReport report = manage(userManager);
@@ -95,20 +96,162 @@ public class UserManagerTest extends AbstractTest
                 + "1970-01-01 01:00:08 [ADD-SPACE] G1_GAMMA\n"
                 + "1970-01-01 01:00:09 [ASSIGN-ROLE-TO-AUTHORIZATION-GROUP] group: G1, role: SPACE_OBSERVER for G1_GAMMA\n"
                 + "1970-01-01 01:00:10 [ASSIGN-ROLE-TO-AUTHORIZATION-GROUP] group: G1_ADMIN, role: 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-AUTHORIZATION-GROUP] group: G1, user: u1\n"
-                + "1970-01-01 01:00:13 [ASSIGN-ROLE-TO-AUTHORIZATION-GROUP] group: G1_ADMIN, role: SPACE_ADMIN for U1\n"
+                + "1970-01-01 01:00:11 [ADD-SPACE] G1_U1\n"
+                + "1970-01-01 01:00:12 [ADD-USER] u1\n"
+                + "1970-01-01 01:00:13 [ADD-USER-TO-AUTHORIZATION-GROUP] group: G1, user: u1\n"
                 + "1970-01-01 01:00:14 [ADD-USER-TO-AUTHORIZATION-GROUP] group: G1_ADMIN, user: u1\n"
-                + "1970-01-01 01:00:15 [ADD-USER] u2 (home space: U2)\n"
-                + "1970-01-01 01:00:16 [ADD-USER-TO-AUTHORIZATION-GROUP] group: G1, user: u2\n"
-                + "1970-01-01 01:00:17 [ASSIGN-ROLE-TO-AUTHORIZATION-GROUP] group: G1_ADMIN, role: 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-AUTHORIZATION-GROUP] group: G1, user: u3\n"
-                + "1970-01-01 01:00:20 [ASSIGN-ROLE-TO-AUTHORIZATION-GROUP] group: G1_ADMIN, role: SPACE_ADMIN for U3\n");
+                + "1970-01-01 01:00:15 [ASSIGN-ROLE-TO-AUTHORIZATION-GROUP] group: G1_ADMIN, role: SPACE_ADMIN for G1_U1\n"
+                + "1970-01-01 01:00:16 [ADD-SPACE] G1_U2\n"
+                + "1970-01-01 01:00:17 [ADD-USER-TO-AUTHORIZATION-GROUP] group: G1, user: u2\n"
+                + "1970-01-01 01:00:18 [ASSIGN-ROLE-TO-AUTHORIZATION-GROUP] group: G1_ADMIN, role: SPACE_ADMIN for G1_U2\n");
         UserManagerExpectationsBuilder builder = createBuilder(commonSpaces);
         builder.adminUser(U1, "G1");
         builder.user(U2, "G1");
+        builder.assertExpectations();
+    }
+
+    @Test
+    public void testCreateTwoGroupsWithDistinctUsers()
+    {
+        // Given
+        MockLogger logger = new MockLogger();
+        Map<Role, List<String>> commonSpaces = commonSpaces();
+        UserManager userManager = createUserManager(commonSpaces, logger);
+        List<String> globalSpaces = Arrays.asList("A", "B");
+        userManager.setGlobalSpaces(globalSpaces);
+        userManager.addGroup(group("G1", U1.getUserId(), "blabla"), users(U3, U1, U2));
+        userManager.addGroup(group("G2", U4.getUserId()), users(U4));
+
+        // When
+        UserManagerReport report = manage(userManager);
+
+        // Then
+        assertEquals(report.getErrorReport(), "");
+        assertEquals(report.getAuditLog(), "1970-01-01 01:00:00 [ADD-SPACES] [A, B]\n"
+                + "1970-01-01 01:00:01 [ADD-AUTHORIZATION-GROUP] ALL_GROUPS\n"
+                + "1970-01-01 01:00:02 [ASSIGN-ROLE-TO-AUTHORIZATION-GROUP] group: ALL_GROUPS, role: SPACE_OBSERVER for A\n"
+                + "1970-01-01 01:00:03 [ASSIGN-ROLE-TO-AUTHORIZATION-GROUP] group: ALL_GROUPS, role: SPACE_OBSERVER for B\n"
+                + "1970-01-01 01:00:04 [ADD-AUTHORIZATION-GROUP] G1\n"
+                + "1970-01-01 01:00:05 [ADD-AUTHORIZATION-GROUP] G1_ADMIN\n"
+                + "1970-01-01 01:00:06 [ADD-SPACE] G1_ALPHA\n"
+                + "1970-01-01 01:00:07 [ASSIGN-ROLE-TO-AUTHORIZATION-GROUP] group: G1, role: SPACE_USER for G1_ALPHA\n"
+                + "1970-01-01 01:00:08 [ASSIGN-ROLE-TO-AUTHORIZATION-GROUP] group: G1_ADMIN, role: SPACE_ADMIN for G1_ALPHA\n"
+                + "1970-01-01 01:00:09 [ADD-SPACE] G1_BETA\n"
+                + "1970-01-01 01:00:10 [ASSIGN-ROLE-TO-AUTHORIZATION-GROUP] group: G1, role: SPACE_USER for G1_BETA\n"
+                + "1970-01-01 01:00:11 [ASSIGN-ROLE-TO-AUTHORIZATION-GROUP] group: G1_ADMIN, role: SPACE_ADMIN for G1_BETA\n"
+                + "1970-01-01 01:00:12 [ADD-SPACE] G1_GAMMA\n"
+                + "1970-01-01 01:00:13 [ASSIGN-ROLE-TO-AUTHORIZATION-GROUP] group: G1, role: SPACE_OBSERVER for G1_GAMMA\n"
+                + "1970-01-01 01:00:14 [ASSIGN-ROLE-TO-AUTHORIZATION-GROUP] group: G1_ADMIN, role: SPACE_ADMIN for G1_GAMMA\n"
+                + "1970-01-01 01:00:15 [ADD-SPACE] G1_U1\n"
+                + "1970-01-01 01:00:16 [ADD-USER] u1\n"
+                + "1970-01-01 01:00:17 [ASSIGN-ROLE-TO-AUTHORIZATION-GROUP] group: G1_ADMIN, role: SPACE_ADMIN for G1_U1\n"
+                + "1970-01-01 01:00:18 [ADD-USER-TO-AUTHORIZATION-GROUP] group: G1, user: u1\n"
+                + "1970-01-01 01:00:19 [ADD-USER-TO-AUTHORIZATION-GROUP] group: ALL_GROUPS, user: u1\n"
+                + "1970-01-01 01:00:20 [ADD-USER-TO-AUTHORIZATION-GROUP] group: G1_ADMIN, user: u1\n"
+                + "1970-01-01 01:00:21 [ADD-SPACE] G1_U2\n"
+                + "1970-01-01 01:00:22 [ADD-USER] u2\n"
+                + "1970-01-01 01:00:23 [ASSIGN-ROLE-TO-AUTHORIZATION-GROUP] group: G1_ADMIN, role: SPACE_ADMIN for G1_U2\n"
+                + "1970-01-01 01:00:24 [ADD-USER-TO-AUTHORIZATION-GROUP] group: G1, user: u2\n"
+                + "1970-01-01 01:00:25 [ADD-USER-TO-AUTHORIZATION-GROUP] group: ALL_GROUPS, user: u2\n"
+                + "1970-01-01 01:00:26 [ADD-SPACE] G1_U3\n"
+                + "1970-01-01 01:00:27 [ADD-USER] u3\n"
+                + "1970-01-01 01:00:28 [ASSIGN-ROLE-TO-AUTHORIZATION-GROUP] group: G1_ADMIN, role: SPACE_ADMIN for G1_U3\n"
+                + "1970-01-01 01:00:29 [ADD-USER-TO-AUTHORIZATION-GROUP] group: G1, user: u3\n"
+                + "1970-01-01 01:00:30 [ADD-USER-TO-AUTHORIZATION-GROUP] group: ALL_GROUPS, user: u3\n"
+                + "1970-01-01 01:00:31 [ADD-AUTHORIZATION-GROUP] G2\n"
+                + "1970-01-01 01:00:32 [ADD-AUTHORIZATION-GROUP] G2_ADMIN\n"
+                + "1970-01-01 01:00:33 [ADD-SPACE] G2_ALPHA\n"
+                + "1970-01-01 01:00:34 [ASSIGN-ROLE-TO-AUTHORIZATION-GROUP] group: G2, role: SPACE_USER for G2_ALPHA\n"
+                + "1970-01-01 01:00:35 [ASSIGN-ROLE-TO-AUTHORIZATION-GROUP] group: G2_ADMIN, role: SPACE_ADMIN for G2_ALPHA\n"
+                + "1970-01-01 01:00:36 [ADD-SPACE] G2_BETA\n"
+                + "1970-01-01 01:00:37 [ASSIGN-ROLE-TO-AUTHORIZATION-GROUP] group: G2, role: SPACE_USER for G2_BETA\n"
+                + "1970-01-01 01:00:38 [ASSIGN-ROLE-TO-AUTHORIZATION-GROUP] group: G2_ADMIN, role: SPACE_ADMIN for G2_BETA\n"
+                + "1970-01-01 01:00:39 [ADD-SPACE] G2_GAMMA\n"
+                + "1970-01-01 01:00:40 [ASSIGN-ROLE-TO-AUTHORIZATION-GROUP] group: G2, role: SPACE_OBSERVER for G2_GAMMA\n"
+                + "1970-01-01 01:00:41 [ASSIGN-ROLE-TO-AUTHORIZATION-GROUP] group: G2_ADMIN, role: SPACE_ADMIN for G2_GAMMA\n"
+                + "1970-01-01 01:00:42 [ADD-SPACE] G2_U4\n"
+                + "1970-01-01 01:00:43 [ADD-USER] u4\n"
+                + "1970-01-01 01:00:44 [ASSIGN-ROLE-TO-AUTHORIZATION-GROUP] group: G2_ADMIN, role: SPACE_ADMIN for G2_U4\n"
+                + "1970-01-01 01:00:45 [ADD-USER-TO-AUTHORIZATION-GROUP] group: G2, user: u4\n"
+                + "1970-01-01 01:00:46 [ADD-USER-TO-AUTHORIZATION-GROUP] group: ALL_GROUPS, user: u4\n"
+                + "1970-01-01 01:00:47 [ADD-USER-TO-AUTHORIZATION-GROUP] group: G2_ADMIN, user: u4\n");
+        UserManagerExpectationsBuilder builder = createBuilder(commonSpaces);
+        builder.setGlobalSpaces(globalSpaces);
+        builder.adminUser(U1, "G1");
+        builder.user(U2, "G1");
+        builder.user(U3, "G1");
+        builder.adminUser(U4, "G2");
+        builder.assertExpectations();
+    }
+
+    @Test
+    public void testCreateTwoGroupsWithSharedUsers()
+    {
+        // Given
+        MockLogger logger = new MockLogger();
+        Map<Role, List<String>> commonSpaces = commonSpaces();
+        UserManager userManager = createUserManager(commonSpaces, logger);
+        userManager.addGroup(group("G1", U1.getUserId()), users(U1, U2, U3));
+        userManager.addGroup(group("G2", U3.getUserId(), U4.getUserId()), users(U2, U3, U4));
+
+        // When
+        UserManagerReport report = manage(userManager);
+
+        // Then
+        assertEquals(report.getErrorReport(), "");
+        assertEquals(report.getAuditLog(), "1970-01-01 01:00:00 [ADD-AUTHORIZATION-GROUP] G1\n"
+                + "1970-01-01 01:00:01 [ADD-AUTHORIZATION-GROUP] G1_ADMIN\n"
+                + "1970-01-01 01:00:02 [ADD-SPACE] G1_ALPHA\n"
+                + "1970-01-01 01:00:03 [ASSIGN-ROLE-TO-AUTHORIZATION-GROUP] group: G1, role: SPACE_USER for G1_ALPHA\n"
+                + "1970-01-01 01:00:04 [ASSIGN-ROLE-TO-AUTHORIZATION-GROUP] group: G1_ADMIN, role: 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-AUTHORIZATION-GROUP] group: G1, role: SPACE_USER for G1_BETA\n"
+                + "1970-01-01 01:00:07 [ASSIGN-ROLE-TO-AUTHORIZATION-GROUP] group: G1_ADMIN, role: 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-AUTHORIZATION-GROUP] group: G1, role: SPACE_OBSERVER for G1_GAMMA\n"
+                + "1970-01-01 01:00:10 [ASSIGN-ROLE-TO-AUTHORIZATION-GROUP] group: G1_ADMIN, role: SPACE_ADMIN for G1_GAMMA\n"
+                + "1970-01-01 01:00:11 [ADD-SPACE] G1_U1\n"
+                + "1970-01-01 01:00:12 [ADD-USER] u1\n"
+                + "1970-01-01 01:00:13 [ASSIGN-ROLE-TO-AUTHORIZATION-GROUP] group: G1_ADMIN, role: SPACE_ADMIN for G1_U1\n"
+                + "1970-01-01 01:00:14 [ADD-USER-TO-AUTHORIZATION-GROUP] group: G1, user: u1\n"
+                + "1970-01-01 01:00:15 [ADD-USER-TO-AUTHORIZATION-GROUP] group: G1_ADMIN, user: u1\n"
+                + "1970-01-01 01:00:16 [ADD-SPACE] G1_U2\n"
+                + "1970-01-01 01:00:17 [ADD-USER] u2\n"
+                + "1970-01-01 01:00:18 [ASSIGN-ROLE-TO-AUTHORIZATION-GROUP] group: G1_ADMIN, role: SPACE_ADMIN for G1_U2\n"
+                + "1970-01-01 01:00:19 [ADD-USER-TO-AUTHORIZATION-GROUP] group: G1, user: u2\n"
+                + "1970-01-01 01:00:20 [ADD-SPACE] G1_U3\n"
+                + "1970-01-01 01:00:21 [ADD-USER] u3\n"
+                + "1970-01-01 01:00:22 [ASSIGN-ROLE-TO-AUTHORIZATION-GROUP] group: G1_ADMIN, role: SPACE_ADMIN for G1_U3\n"
+                + "1970-01-01 01:00:23 [ADD-USER-TO-AUTHORIZATION-GROUP] group: G1, user: u3\n"
+                + "1970-01-01 01:00:24 [ADD-AUTHORIZATION-GROUP] G2\n"
+                + "1970-01-01 01:00:25 [ADD-AUTHORIZATION-GROUP] G2_ADMIN\n"
+                + "1970-01-01 01:00:26 [ADD-SPACE] G2_ALPHA\n"
+                + "1970-01-01 01:00:27 [ASSIGN-ROLE-TO-AUTHORIZATION-GROUP] group: G2, role: SPACE_USER for G2_ALPHA\n"
+                + "1970-01-01 01:00:28 [ASSIGN-ROLE-TO-AUTHORIZATION-GROUP] group: G2_ADMIN, role: SPACE_ADMIN for G2_ALPHA\n"
+                + "1970-01-01 01:00:29 [ADD-SPACE] G2_BETA\n"
+                + "1970-01-01 01:00:30 [ASSIGN-ROLE-TO-AUTHORIZATION-GROUP] group: G2, role: SPACE_USER for G2_BETA\n"
+                + "1970-01-01 01:00:31 [ASSIGN-ROLE-TO-AUTHORIZATION-GROUP] group: G2_ADMIN, role: SPACE_ADMIN for G2_BETA\n"
+                + "1970-01-01 01:00:32 [ADD-SPACE] G2_GAMMA\n"
+                + "1970-01-01 01:00:33 [ASSIGN-ROLE-TO-AUTHORIZATION-GROUP] group: G2, role: SPACE_OBSERVER for G2_GAMMA\n"
+                + "1970-01-01 01:00:34 [ASSIGN-ROLE-TO-AUTHORIZATION-GROUP] group: G2_ADMIN, role: SPACE_ADMIN for G2_GAMMA\n"
+                + "1970-01-01 01:00:35 [ADD-SPACE] G2_U2\n"
+                + "1970-01-01 01:00:36 [ASSIGN-ROLE-TO-AUTHORIZATION-GROUP] group: G2_ADMIN, role: SPACE_ADMIN for G2_U2\n"
+                + "1970-01-01 01:00:37 [ADD-USER-TO-AUTHORIZATION-GROUP] group: G2, user: u2\n"
+                + "1970-01-01 01:00:38 [ADD-SPACE] G2_U3\n"
+                + "1970-01-01 01:00:39 [ASSIGN-ROLE-TO-AUTHORIZATION-GROUP] group: G2_ADMIN, role: SPACE_ADMIN for G2_U3\n"
+                + "1970-01-01 01:00:40 [ADD-USER-TO-AUTHORIZATION-GROUP] group: G2, user: u3\n"
+                + "1970-01-01 01:00:41 [ADD-USER-TO-AUTHORIZATION-GROUP] group: G2_ADMIN, user: u3\n"
+                + "1970-01-01 01:00:42 [ADD-SPACE] G2_U4\n"
+                + "1970-01-01 01:00:43 [ADD-USER] u4\n"
+                + "1970-01-01 01:00:44 [ASSIGN-ROLE-TO-AUTHORIZATION-GROUP] group: G2_ADMIN, role: SPACE_ADMIN for G2_U4\n"
+                + "1970-01-01 01:00:45 [ADD-USER-TO-AUTHORIZATION-GROUP] group: G2, user: u4\n"
+                + "1970-01-01 01:00:46 [ADD-USER-TO-AUTHORIZATION-GROUP] group: G2_ADMIN, user: u4\n");
+        UserManagerExpectationsBuilder builder = createBuilder(commonSpaces);
+        builder.adminUser(U1, "G1");
+        builder.user(U2, "G1", "G2");
         builder.user(U3, "G1");
+        builder.adminUser(U3, "G2");
+        builder.adminUser(U4, "G2");
         builder.assertExpectations();
     }
 
@@ -116,30 +259,30 @@ public class UserManagerTest extends AbstractTest
     public void testAddUsersToAnExistingGroup()
     {
         // Given
-        // 1. create group G2 with user U1
+        // 1. create group G2 with user U1 (admin)
         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));
+        userManager.addGroup(group("G2", U1.getUserId(), "blabla"), users(U1));
         assertEquals(manage(userManager).getErrorReport(), "");
         createBuilder(commonSpaces).adminUser(U1, "G2").assertExpectations();
         // 2. add users U2 and U3 to group G2
         userManager = createUserManager(commonSpaces, logger);
-        group.setAdmins(Arrays.asList(U1.getUserId()));
-        userManager.addGroup(group, principals(U1, U2, U3));
+        userManager.addGroup(group("G2", U1.getUserId()), users(U1, U2, U3));
 
         // When
         UserManagerReport report = manage(userManager);
 
         // Then
         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-AUTHORIZATION-GROUP] group: G2, user: u2\n"
-                + "1970-01-01 01:00:02 [ASSIGN-ROLE-TO-AUTHORIZATION-GROUP] group: G2_ADMIN, role: 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-AUTHORIZATION-GROUP] group: G2, user: u3\n"
-                + "1970-01-01 01:00:05 [ASSIGN-ROLE-TO-AUTHORIZATION-GROUP] group: G2_ADMIN, role: SPACE_ADMIN for U3\n");
+        assertEquals(report.getAuditLog(), "1970-01-01 01:00:00 [ADD-SPACE] G2_U2\n"
+                + "1970-01-01 01:00:01 [ADD-USER] u2\n"
+                + "1970-01-01 01:00:02 [ASSIGN-ROLE-TO-AUTHORIZATION-GROUP] group: G2_ADMIN, role: SPACE_ADMIN for G2_U2\n"
+                + "1970-01-01 01:00:03 [ADD-USER-TO-AUTHORIZATION-GROUP] group: G2, user: u2\n"
+                + "1970-01-01 01:00:04 [ADD-SPACE] G2_U3\n"
+                + "1970-01-01 01:00:05 [ADD-USER] u3\n"
+                + "1970-01-01 01:00:06 [ASSIGN-ROLE-TO-AUTHORIZATION-GROUP] group: G2_ADMIN, role: SPACE_ADMIN for G2_U3\n"
+                + "1970-01-01 01:00:07 [ADD-USER-TO-AUTHORIZATION-GROUP] group: G2, user: u3\n");
         UserManagerExpectationsBuilder builder = createBuilder(commonSpaces);
         builder.adminUser(U1, "G2");
         builder.user(U2, "G2");
@@ -155,13 +298,12 @@ public class UserManagerTest extends AbstractTest
         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));
+        userManager.addGroup(group("G2", U1.getUserId()), users(U1, U2, U3));
         assertEquals(manage(userManager).getErrorReport(), "");
         createBuilder(commonSpaces).adminUser(U1, "G2").user(U2, "G2").user(U3, "G2").assertExpectations();
         // 2. remove U2 from group G2
         userManager = createUserManager(commonSpaces, logger);
-        userManager.addGroup(group, principals(U1, U3));
+        userManager.addGroup(group("G2", U1.getUserId()), users(U1, U3));
 
         // When
         UserManagerReport report = manage(userManager);
@@ -185,13 +327,13 @@ public class UserManagerTest extends AbstractTest
         Map<Role, List<String>> commonSpaces = commonSpaces();
         UserManager userManager = createUserManager(commonSpaces, logger);
         UserGroup group = group("G2");
-        userManager.addGroup(group, principals(U1, U2, U3));
+        userManager.addGroup(group, users(U1, U2, U3));
         assertEquals(manage(userManager).getErrorReport(), "");
         createBuilder(commonSpaces).user(U1, "G2").user(U2, "G2").user(U3, "G2").assertExpectations();
         // 2. make U1 admin
         userManager = createUserManager(commonSpaces, logger);
         group = group("G2", U1.getUserId());
-        userManager.addGroup(group, principals(U1, U2, U3));
+        userManager.addGroup(group, users(U1, U2, U3));
 
         // When
         UserManagerReport report = manage(userManager);
@@ -215,13 +357,13 @@ public class UserManagerTest extends AbstractTest
         Map<Role, List<String>> commonSpaces = commonSpaces();
         UserManager userManager = createUserManager(commonSpaces, logger);
         UserGroup group = group("G2", U1.getUserId());
-        userManager.addGroup(group, principals(U1, U2, U3));
+        userManager.addGroup(group, users(U1, U2, U3));
         assertEquals(manage(userManager).getErrorReport(), "");
         createBuilder(commonSpaces).adminUser(U1, "G2").user(U2, "G2").user(U3, "G2").assertExpectations();
         // 2. make U1 normal user
         userManager = createUserManager(commonSpaces, logger);
         group = group("G2");
-        userManager.addGroup(group, principals(U1, U2, U3));
+        userManager.addGroup(group, users(U1, U2, U3));
 
         // When
         UserManagerReport report = manage(userManager);
@@ -245,16 +387,16 @@ public class UserManagerTest extends AbstractTest
         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));
+        userManager.addGroup(group, users(U1, U2, U3));
         assertEquals(manage(userManager).getErrorReport(), "");
         // 2. remove U2 from group G2
         userManager = createUserManager(commonSpaces, logger);
-        userManager.addGroup(group, principals(U1, U3));
+        userManager.addGroup(group, users(U1, U3));
         assertEquals(manage(userManager).getErrorReport(), "");
         createBuilder(commonSpaces).adminUser(U1, "G2").disabledUser(U2, "G2").user(U3, "G2").assertExpectations();
         // 3. add U2 again to group G2
         userManager = createUserManager(commonSpaces, logger);
-        userManager.addGroup(group, principals(U1, U2, U3));
+        userManager.addGroup(group, users(U1, U2, U3));
 
         // When
         UserManagerReport report = manage(userManager);
@@ -278,12 +420,12 @@ public class UserManagerTest extends AbstractTest
         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));
+        userManager.addGroup(group, users(U1, U2, U3));
         assertEquals(manage(userManager).getErrorReport(), "");
         createBuilder(commonSpaces).adminUser(U1, "G2").user(U2, "G2").user(U3, "G2").assertExpectations();
         // 2. remove U1 from group G2
         userManager = createUserManager(commonSpaces, logger);
-        userManager.addGroup(group, principals(U2, U3));
+        userManager.addGroup(group, users(U2, U3));
 
         // When
         UserManagerReport report = manage(userManager);
@@ -308,16 +450,17 @@ public class UserManagerTest extends AbstractTest
         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));
+        userManager.addGroup(group, users(U1, U2, U3));
         assertEquals(manage(userManager).getErrorReport(), "");
+        createBuilder(commonSpaces).adminUser(U1, "G2").user(U2, "G2").user(U3, "G2").assertExpectations();
         // 2. remove U1 from group G2
         userManager = createUserManager(commonSpaces, logger);
-        userManager.addGroup(group, principals(U2, U3));
+        userManager.addGroup(group, users(U2, U3));
         assertEquals(manage(userManager).getErrorReport(), "");
         createBuilder(commonSpaces).disabledUser(U1, "G2").user(U2, "G2").user(U3, "G2").assertExpectations();
         // 3. add U1 again to group G2
         userManager = createUserManager(commonSpaces, logger);
-        userManager.addGroup(group, principals(U1, U2, U3));
+        userManager.addGroup(group, users(U1, U2, U3));
 
         // When
         UserManagerReport report = manage(userManager);
@@ -333,6 +476,180 @@ public class UserManagerTest extends AbstractTest
         builder.assertExpectations();
     }
 
+    @Test
+    public void testCreateSecondGroupWithNormalUserInBothGroups()
+    {
+        // Given
+        // 1. create group G1 with users U1 (admin) and U2
+        MockLogger logger = new MockLogger();
+        Map<Role, List<String>> commonSpaces = commonSpaces();
+        UserManager userManager = createUserManager(commonSpaces, logger);
+        userManager.addGroup(group("G1", U1.getUserId()), users(U1, U2));
+        assertEquals(manage(userManager).getErrorReport(), "");
+        createBuilder(commonSpaces).adminUser(U1, "G1").user(U2, "G1").unknownUser(U3).assertExpectations();
+        // 2. create group G2 with users U2 and U3 (admin)
+        userManager = createUserManager(commonSpaces, logger);
+        userManager.addGroup(group("G1", U1.getUserId()), users(U1, U2));
+        userManager.addGroup(group("G2", U3.getUserId()), users(U2, U3));
+
+        // When
+        UserManagerReport report = manage(userManager);
+
+        // Then
+        assertEquals(report.getErrorReport(), "");
+        assertEquals(report.getAuditLog(), "1970-01-01 01:00:00 [ADD-AUTHORIZATION-GROUP] G2\n"
+                + "1970-01-01 01:00:01 [ADD-AUTHORIZATION-GROUP] G2_ADMIN\n"
+                + "1970-01-01 01:00:02 [ADD-SPACE] G2_ALPHA\n"
+                + "1970-01-01 01:00:03 [ASSIGN-ROLE-TO-AUTHORIZATION-GROUP] group: G2, role: SPACE_USER for G2_ALPHA\n"
+                + "1970-01-01 01:00:04 [ASSIGN-ROLE-TO-AUTHORIZATION-GROUP] group: G2_ADMIN, role: SPACE_ADMIN for G2_ALPHA\n"
+                + "1970-01-01 01:00:05 [ADD-SPACE] G2_BETA\n"
+                + "1970-01-01 01:00:06 [ASSIGN-ROLE-TO-AUTHORIZATION-GROUP] group: G2, role: SPACE_USER for G2_BETA\n"
+                + "1970-01-01 01:00:07 [ASSIGN-ROLE-TO-AUTHORIZATION-GROUP] group: G2_ADMIN, role: SPACE_ADMIN for G2_BETA\n"
+                + "1970-01-01 01:00:08 [ADD-SPACE] G2_GAMMA\n"
+                + "1970-01-01 01:00:09 [ASSIGN-ROLE-TO-AUTHORIZATION-GROUP] group: G2, role: SPACE_OBSERVER for G2_GAMMA\n"
+                + "1970-01-01 01:00:10 [ASSIGN-ROLE-TO-AUTHORIZATION-GROUP] group: G2_ADMIN, role: SPACE_ADMIN for G2_GAMMA\n"
+                + "1970-01-01 01:00:11 [ADD-USER-TO-AUTHORIZATION-GROUP] group: G2, user: u2\n"
+                + "1970-01-01 01:00:12 [ASSIGN-ROLE-TO-AUTHORIZATION-GROUP] group: G2_ADMIN, role: SPACE_ADMIN for U2\n"
+                + "1970-01-01 01:00:13 [ADD-USER] u3 (home space: U3)\n"
+                + "1970-01-01 01:00:14 [ADD-USER-TO-AUTHORIZATION-GROUP] group: G2, user: u3\n"
+                + "1970-01-01 01:00:15 [ASSIGN-ROLE-TO-AUTHORIZATION-GROUP] group: G2_ADMIN, role: SPACE_ADMIN for U3\n"
+                + "1970-01-01 01:00:16 [ADD-USER-TO-AUTHORIZATION-GROUP] group: G2_ADMIN, user: u3\n");
+        UserManagerExpectationsBuilder builder = createBuilder(commonSpaces);
+        builder.adminUser(U1, "G1");
+        builder.user(U2, "G1", "G2");
+        builder.adminUser(U3, "G2");
+        builder.assertExpectations();
+    }
+
+    @Test
+    public void testCreateSecondGroupWithAdminUserInBothGroups()
+    {
+        // Given
+        // 1. create group G1 with users U1 (admin) and U2
+        MockLogger logger = new MockLogger();
+        Map<Role, List<String>> commonSpaces = commonSpaces();
+        UserManager userManager = createUserManager(commonSpaces, logger);
+        userManager.addGroup(group("G1", U1.getUserId()), users(U1, U2));
+        assertEquals(manage(userManager).getErrorReport(), "");
+        createBuilder(commonSpaces).adminUser(U1, "G1").user(U2, "G1").unknownUser(U3).assertExpectations();
+        // 2. create group G2 with users U1 (admin) and U3
+        userManager = createUserManager(commonSpaces, logger);
+        userManager.addGroup(group("G1", U1.getUserId()), users(U1, U2));
+        userManager.addGroup(group("G2", U1.getUserId()), users(U1, U3));
+
+        // When
+        UserManagerReport report = manage(userManager);
+
+        // Then
+        assertEquals(report.getErrorReport(), "");
+        assertEquals(report.getAuditLog(), "1970-01-01 01:00:00 [ADD-AUTHORIZATION-GROUP] G2\n"
+                + "1970-01-01 01:00:01 [ADD-AUTHORIZATION-GROUP] G2_ADMIN\n"
+                + "1970-01-01 01:00:02 [ADD-SPACE] G2_ALPHA\n"
+                + "1970-01-01 01:00:03 [ASSIGN-ROLE-TO-AUTHORIZATION-GROUP] group: G2, role: SPACE_USER for G2_ALPHA\n"
+                + "1970-01-01 01:00:04 [ASSIGN-ROLE-TO-AUTHORIZATION-GROUP] group: G2_ADMIN, role: SPACE_ADMIN for G2_ALPHA\n"
+                + "1970-01-01 01:00:05 [ADD-SPACE] G2_BETA\n"
+                + "1970-01-01 01:00:06 [ASSIGN-ROLE-TO-AUTHORIZATION-GROUP] group: G2, role: SPACE_USER for G2_BETA\n"
+                + "1970-01-01 01:00:07 [ASSIGN-ROLE-TO-AUTHORIZATION-GROUP] group: G2_ADMIN, role: SPACE_ADMIN for G2_BETA\n"
+                + "1970-01-01 01:00:08 [ADD-SPACE] G2_GAMMA\n"
+                + "1970-01-01 01:00:09 [ASSIGN-ROLE-TO-AUTHORIZATION-GROUP] group: G2, role: SPACE_OBSERVER for G2_GAMMA\n"
+                + "1970-01-01 01:00:10 [ASSIGN-ROLE-TO-AUTHORIZATION-GROUP] group: G2_ADMIN, role: SPACE_ADMIN for G2_GAMMA\n"
+                + "1970-01-01 01:00:11 [ADD-USER-TO-AUTHORIZATION-GROUP] group: G2, user: u1\n"
+                + "1970-01-01 01:00:12 [ASSIGN-ROLE-TO-AUTHORIZATION-GROUP] group: G2_ADMIN, role: SPACE_ADMIN for U1\n"
+                + "1970-01-01 01:00:13 [ADD-USER-TO-AUTHORIZATION-GROUP] group: G2_ADMIN, user: u1\n"
+                + "1970-01-01 01:00:14 [ADD-USER] u3 (home space: U3)\n"
+                + "1970-01-01 01:00:15 [ADD-USER-TO-AUTHORIZATION-GROUP] group: G2, user: u3\n"
+                + "1970-01-01 01:00:16 [ASSIGN-ROLE-TO-AUTHORIZATION-GROUP] group: G2_ADMIN, role: SPACE_ADMIN for U3\n");
+        UserManagerExpectationsBuilder builder = createBuilder(commonSpaces);
+        builder.adminUser(U1, "G1", "G2");
+        builder.user(U2, "G1");
+        builder.user(U3, "G2");
+        builder.assertExpectations();
+    }
+
+    @Test
+    public void testCreateSecondGroupWithUserNormalInFirstGroupAndAdminInSecondGroup()
+    {
+        // Given
+        // 1. create group G1 with users U1 (admin) and U2
+        MockLogger logger = new MockLogger();
+        Map<Role, List<String>> commonSpaces = commonSpaces();
+        UserManager userManager = createUserManager(commonSpaces, logger);
+        userManager.addGroup(group("G1", U1.getUserId()), users(U1, U2));
+        assertEquals(manage(userManager).getErrorReport(), "");
+        createBuilder(commonSpaces).adminUser(U1, "G1").user(U2, "G1").unknownUser(U3).assertExpectations();
+        // 2. create group G2 with users U2 (admin) and U3
+        userManager = createUserManager(commonSpaces, logger);
+        userManager.addGroup(group("G1", U1.getUserId()), users(U1, U2));
+        userManager.addGroup(group("G2", U2.getUserId()), users(U2, U3));
+
+        // When
+        UserManagerReport report = manage(userManager);
+
+        // Then
+        assertEquals(report.getErrorReport(), "");
+        assertEquals(report.getAuditLog(), "1970-01-01 01:00:00 [ADD-AUTHORIZATION-GROUP] G2\n"
+                + "1970-01-01 01:00:01 [ADD-AUTHORIZATION-GROUP] G2_ADMIN\n"
+                + "1970-01-01 01:00:02 [ADD-SPACE] G2_ALPHA\n"
+                + "1970-01-01 01:00:03 [ASSIGN-ROLE-TO-AUTHORIZATION-GROUP] group: G2, role: SPACE_USER for G2_ALPHA\n"
+                + "1970-01-01 01:00:04 [ASSIGN-ROLE-TO-AUTHORIZATION-GROUP] group: G2_ADMIN, role: SPACE_ADMIN for G2_ALPHA\n"
+                + "1970-01-01 01:00:05 [ADD-SPACE] G2_BETA\n"
+                + "1970-01-01 01:00:06 [ASSIGN-ROLE-TO-AUTHORIZATION-GROUP] group: G2, role: SPACE_USER for G2_BETA\n"
+                + "1970-01-01 01:00:07 [ASSIGN-ROLE-TO-AUTHORIZATION-GROUP] group: G2_ADMIN, role: SPACE_ADMIN for G2_BETA\n"
+                + "1970-01-01 01:00:08 [ADD-SPACE] G2_GAMMA\n"
+                + "1970-01-01 01:00:09 [ASSIGN-ROLE-TO-AUTHORIZATION-GROUP] group: G2, role: SPACE_OBSERVER for G2_GAMMA\n"
+                + "1970-01-01 01:00:10 [ASSIGN-ROLE-TO-AUTHORIZATION-GROUP] group: G2_ADMIN, role: SPACE_ADMIN for G2_GAMMA\n"
+                + "1970-01-01 01:00:11 [ADD-USER-TO-AUTHORIZATION-GROUP] group: G2, user: u2\n"
+                + "1970-01-01 01:00:12 [ASSIGN-ROLE-TO-AUTHORIZATION-GROUP] group: G2_ADMIN, role: SPACE_ADMIN for U2\n"
+                + "1970-01-01 01:00:13 [ADD-USER-TO-AUTHORIZATION-GROUP] group: G2_ADMIN, user: u2\n"
+                + "1970-01-01 01:00:14 [ADD-USER] u3 (home space: U3)\n"
+                + "1970-01-01 01:00:15 [ADD-USER-TO-AUTHORIZATION-GROUP] group: G2, user: u3\n"
+                + "1970-01-01 01:00:16 [ASSIGN-ROLE-TO-AUTHORIZATION-GROUP] group: G2_ADMIN, role: SPACE_ADMIN for U3\n");
+        UserManagerExpectationsBuilder builder = createBuilder(commonSpaces);
+        builder.adminUser(U1, "G1");
+        builder.user(U2, "G1");
+        builder.adminUser(U2, "G2");
+        builder.user(U3, "G2");
+        builder.assertExpectations();
+    }
+
+    @Test
+    public void testCreateTwoGroupsAndMoveUserBetweenGroups()
+    {
+        // Given
+        // 1. create group G1 with users U1 (admin) and U2
+        MockLogger logger = new MockLogger();
+        Map<Role, List<String>> commonSpaces = commonSpaces();
+        UserManager userManager = createUserManager(commonSpaces, logger);
+        userManager.addGroup(group("G1", U1.getUserId()), users(U1, U2));
+        assertEquals(manage(userManager).getErrorReport(), "");
+        createBuilder(commonSpaces).adminUser(U1, "G1").user(U2, "G1").unknownUser(U3).assertExpectations();
+        // 2. create group G2 with users U3 (admin) and U4
+        userManager = createUserManager(commonSpaces, logger);
+        userManager.addGroup(group("G1", U1.getUserId()), users(U1, U2));
+        userManager.addGroup(group("G2", U3.getUserId()), users(U3, U4));
+        assertEquals(manage(userManager).getErrorReport(), "");
+        createBuilder(commonSpaces).adminUser(U1, "G1").user(U2, "G1").adminUser(U3, "G2").user(U4, "G2").assertExpectations();
+        // 3. Move U2 from G1 -> G2
+        userManager = createUserManager(commonSpaces, logger);
+        userManager.addGroup(group("G1", U1.getUserId()), users(U1));
+        userManager.addGroup(group("G2", U3.getUserId()), users(U2, U3, U4));
+
+        // When
+        UserManagerReport report = manage(userManager);
+
+        // Then
+        assertEquals(report.getErrorReport(), "");
+        assertEquals(report.getAuditLog(), "1970-01-01 01:00:00 [ADD-USER-TO-AUTHORIZATION-GROUP] group: G2, user: u2\n"
+                + "1970-01-01 01:00:01 [ASSIGN-ROLE-TO-AUTHORIZATION-GROUP] group: G2_ADMIN, role: SPACE_ADMIN for U2\n"
+                + "1970-01-01 01:00:02 [REMOVE-USER-FROM-AUTHORIZATION-GROUP] group: G1, user: u2\n");
+        UserManagerExpectationsBuilder builder = createBuilder(commonSpaces);
+        builder.adminUser(U1, "G1");
+        builder.user(U2, "G2");
+        builder.adminUser(U3, "G2");
+        builder.user(U4, "G2");
+        builder.assertExpectations();
+    }
+
     @Test
     public void testUserFromAGroupHasLefted()
     {
@@ -342,7 +659,7 @@ public class UserManagerTest extends AbstractTest
         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));
+        userManager.addGroup(group, users(U1, U2, U3));
         assertEquals(manage(userManager).getErrorReport(), "");
         createBuilder(commonSpaces).adminUser(U1, "G2").user(U2, "G2").user(U3, "G2").assertExpectations();
         // 2. U2 is no longer known by the authentication service
@@ -370,37 +687,83 @@ public class UserManagerTest extends AbstractTest
         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));
+        userManager.addGroup(group, users(U1, U2, U3));
+        assertEquals(manage(userManager).getErrorReport(), "");
+        createBuilder(commonSpaces).adminUser(U1, "G2").user(U2, "G2").user(U3, "G2").assertExpectations();
+        // 2. U2 is no longer known by the authentication service
+        userManager = createUserManager(commonSpaces, logger, U2);
+        userManager.addGroup(group, users(U1, U3));
+        assertEquals(manage(userManager).getErrorReport(), "");
+        createBuilder(commonSpaces).adminUser(U1, "G2").unknownUser(U2).user(U3, "G2").assertExpectations();
+        // 3. U2 is reused and added to group G2
+        userManager = createUserManager(commonSpaces, logger);
+        userManager.addGroup(group, users(U1, U2, U3));
+
+        // When
+        UserManagerReport report = manage(userManager);
+
+        // Then
+        assertEquals(report.getErrorReport(), "");
+        assertEquals(report.getAuditLog(), "1970-01-01 01:00:00 [REUSE-USER] u2 (home space: U2_2)\n"
+                + "1970-01-01 01:00:01 [ADD-USER-TO-AUTHORIZATION-GROUP] group: G2, user: u2\n"
+                + "1970-01-01 01:00:02 [ASSIGN-ROLE-TO-AUTHORIZATION-GROUP] group: G2_ADMIN, role: SPACE_ADMIN for U2_2\n");
+        UserManagerExpectationsBuilder builder = createBuilder(commonSpaces);
+        builder.adminUser(U1, "G2");
+        builder.user(reuse(U2, "U2_2"), "G2");
+        builder.user(U3, "G2");
+        builder.assertExpectations();
+    }
+
+    @Test
+    public void testReuseSameUserIdTwice()
+    {
+        // Given
+        // 1. create group G2 with users U1 (admin), U2 and U3
+        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, users(U1, U2, U3));
         assertEquals(manage(userManager).getErrorReport(), "");
         createBuilder(commonSpaces).adminUser(U1, "G2").user(U2, "G2").user(U3, "G2").assertExpectations();
         // 2. U2 is no longer known by the authentication service
         userManager = createUserManager(commonSpaces, logger, U2);
-        userManager.addGroup(group, principals(U1, U3));
+        userManager.addGroup(group, users(U1, U3));
         assertEquals(manage(userManager).getErrorReport(), "");
         createBuilder(commonSpaces).adminUser(U1, "G2").unknownUser(U2).user(U3, "G2").assertExpectations();
         // 3. U2 is reused and added to group G2
         userManager = createUserManager(commonSpaces, logger);
-        userManager.addGroup(group, principals(U1, U2, U3));
+        userManager.addGroup(group, users(U1, U2, U3));
+        assertEquals(manage(userManager).getErrorReport(), "");
+        createBuilder(commonSpaces).adminUser(U1, "G2").user(reuse(U2, "U2_2"), "G2").user(U3, "G2").assertExpectations();
+        // 4. U2 is no longer known by the authentication service
+        userManager = createUserManager(commonSpaces, logger, U2);
+        userManager.addGroup(group, users(U1, U3));
+        assertEquals(manage(userManager).getErrorReport(), "");
+        createBuilder(commonSpaces).adminUser(U1, "G2").unknownUser(U2).user(U3, "G2").assertExpectations();
+        // 5. U2 is reused and added to group G2
+        userManager = createUserManager(commonSpaces, logger);
+        userManager.addGroup(group, users(U1, U2, U3));
 
         // When
         UserManagerReport report = manage(userManager);
 
         // Then
         assertEquals(report.getErrorReport(), "");
-        assertEquals(report.getAuditLog(), "1970-01-01 01:00:00 [REUSE-USER] u2 (home space: U2_1)\n"
+        assertEquals(report.getAuditLog(), "1970-01-01 01:00:00 [REUSE-USER] u2 (home space: U2_3)\n"
                 + "1970-01-01 01:00:01 [ADD-USER-TO-AUTHORIZATION-GROUP] group: G2, user: u2\n"
-                + "1970-01-01 01:00:02 [ASSIGN-ROLE-TO-AUTHORIZATION-GROUP] group: G2_ADMIN, role: SPACE_ADMIN for U2_1\n");
+                + "1970-01-01 01:00:02 [ASSIGN-ROLE-TO-AUTHORIZATION-GROUP] group: G2_ADMIN, role: SPACE_ADMIN for U2_3\n");
         UserManagerExpectationsBuilder builder = createBuilder(commonSpaces);
         builder.adminUser(U1, "G2");
-        builder.user(reuse(U2, "U2_1"), "G2");
+        builder.user(reuse(U2, "U2_3"), "G2");
         builder.user(U3, "G2");
         builder.assertExpectations();
     }
 
-    private Principal reuse(Principal user, String homeSpace)
+    private Principal reuse(Principal user, String userSpacePostfix)
     {
         Map<String, String> props = new TreeMap<>();
-        props.put(UserManagerExpectationsBuilder.HOME_SPACE_KEY, homeSpace);
+        props.put(UserManagerExpectationsBuilder.USER_SPACE_POSTFIX_KEY, userSpacePostfix);
         return new Principal(user.getUserId(), user.getFirstName(), user.getLastName(), user.getEmail(), true, props);
     }
 
@@ -420,7 +783,9 @@ public class UserManagerTest extends AbstractTest
                     return new Principal(user, "John", "Doe", "jd@abc.de");
                 }
             };
-        return new UserManager(authenticationService, v3api, commonSpaces, logger, new MockTimeProvider(0, 1000));
+        UserManager userManager = new UserManager(authenticationService, v3api, logger, new MockTimeProvider(0, 1000));
+        userManager.setCommonSpacesByRole(commonSpaces);
+        return userManager;
     }
 
     private UserManagerExpectationsBuilder createBuilder(Map<Role, List<String>> commonSpaces)
@@ -435,7 +800,7 @@ public class UserManagerTest extends AbstractTest
         return errorReport;
     }
 
-    private Map<String, Principal> principals(Principal... principals)
+    private Map<String, Principal> users(Principal... principals)
     {
         Map<String, Principal> map = new TreeMap<>();
         for (Principal principal : principals)