Bug 577561 - Deleting project does not update all Roles

Remove deleted project from all affected roles
So far this was only done for user's project admin roles.
This commit also reader and writer roles and covers groups in addition
to users.

Change-Id: I4a82a18251c134283745c4e7e7ae0b75803d4e3f
Signed-off-by: Lucas Koehler <lkoehler@eclipsesource.com>
diff --git a/bundles/org.eclipse.emf.emfstore.server/src/org/eclipse/emf/emfstore/internal/server/core/subinterfaces/ProjectSubInterfaceImpl.java b/bundles/org.eclipse.emf.emfstore.server/src/org/eclipse/emf/emfstore/internal/server/core/subinterfaces/ProjectSubInterfaceImpl.java
index 2f56d80..8df07dd 100644
--- a/bundles/org.eclipse.emf.emfstore.server/src/org/eclipse/emf/emfstore/internal/server/core/subinterfaces/ProjectSubInterfaceImpl.java
+++ b/bundles/org.eclipse.emf.emfstore.server/src/org/eclipse/emf/emfstore/internal/server/core/subinterfaces/ProjectSubInterfaceImpl.java
@@ -16,7 +16,9 @@
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Date;
+import java.util.LinkedHashSet;
 import java.util.List;
+import java.util.Set;
 
 import org.eclipse.emf.emfstore.internal.common.model.Project;
 import org.eclipse.emf.emfstore.internal.common.model.util.ModelUtil;
@@ -32,8 +34,9 @@
 import org.eclipse.emf.emfstore.internal.server.model.ProjectId;
 import org.eclipse.emf.emfstore.internal.server.model.ProjectInfo;
 import org.eclipse.emf.emfstore.internal.server.model.SessionId;
+import org.eclipse.emf.emfstore.internal.server.model.accesscontrol.ACGroup;
+import org.eclipse.emf.emfstore.internal.server.model.accesscontrol.ACOrgUnit;
 import org.eclipse.emf.emfstore.internal.server.model.accesscontrol.ACUser;
-import org.eclipse.emf.emfstore.internal.server.model.accesscontrol.roles.ProjectAdminRole;
 import org.eclipse.emf.emfstore.internal.server.model.accesscontrol.roles.Role;
 import org.eclipse.emf.emfstore.internal.server.model.accesscontrol.roles.RolesPackage;
 import org.eclipse.emf.emfstore.internal.server.model.versioning.AbstractChangePackage;
@@ -47,6 +50,7 @@
 import org.eclipse.emf.emfstore.server.auth.ESMethod;
 import org.eclipse.emf.emfstore.server.auth.ESMethod.MethodId;
 import org.eclipse.emf.emfstore.server.exceptions.ESException;
+import org.eclipse.emf.emfstore.server.model.ESOrgUnit;
 
 /**
  * This sub-interface implements all project related functionality.
@@ -307,7 +311,8 @@
 			try {
 				final ProjectHistory project = getProject(projectId);
 				getServerSpace().getProjects().remove(project);
-				removeAllProjectAdmins(projectId);
+				removeAllUsers(projectId);
+				removeAllGroups(projectId);
 				try {
 					save(getServerSpace());
 				} catch (final FatalESException e) {
@@ -339,27 +344,28 @@
 		}
 	}
 
-	private void removeAllProjectAdmins(ProjectId projectId) {
+	private void removeAllUsers(ProjectId projectId) {
 		final List<ACUser> users = getServerSpace().getUsers();
-		for (final ACUser acUser : users) {
-			ProjectAdminRole obsoletePARole = null;
-			boolean paRoleIsObsolete = false;
-			for (final Role role : acUser.getRoles()) {
-				if (role.eClass().equals(RolesPackage.eINSTANCE.getProjectAdminRole())) {
-					final ProjectAdminRole paRole = ProjectAdminRole.class.cast(role);
-					final int indexOf = paRole.getProjects().indexOf(projectId);
-					if (indexOf != -1) {
-						paRole.getProjects().remove(indexOf);
-					}
-					if (paRole.getProjects().size() == 0) {
-						paRoleIsObsolete = true;
-						obsoletePARole = paRole;
-					}
+		removeAllRolesButServerAdmin(projectId, users);
+	}
+
+	private void removeAllGroups(ProjectId projectId) {
+		final List<ACGroup> groups = getServerSpace().getGroups();
+		removeAllRolesButServerAdmin(projectId, groups);
+	}
+
+	private void removeAllRolesButServerAdmin(ProjectId projectId,
+		List<? extends ACOrgUnit<? extends ESOrgUnit>> acUnits) {
+		for (final ACOrgUnit<? extends ESOrgUnit> acUnit : acUnits) {
+			final Set<Role> obsoleteRoles = new LinkedHashSet<Role>();
+			for (final Role role : acUnit.getRoles()) {
+				role.getProjects().remove(projectId);
+				if (!RolesPackage.Literals.SERVER_ADMIN.equals(role.eClass()) && role.getProjects().isEmpty()) {
+					// Except for server admin roles, empty roles do not provide any benefits and can be removed.
+					obsoleteRoles.add(role);
 				}
 			}
-			if (paRoleIsObsolete) {
-				acUser.getRoles().remove(obsoletePARole);
-			}
+			acUnit.getRoles().removeAll(obsoleteRoles);
 		}
 	}
 
diff --git a/tests/org.eclipse.emf.emfstore.server.test/src/org/eclipse/emf/emfstore/server/accesscontrol/test/DeleteProjectTest.java b/tests/org.eclipse.emf.emfstore.server.test/src/org/eclipse/emf/emfstore/server/accesscontrol/test/DeleteProjectTest.java
index 39b937b..b48ee3b 100644
--- a/tests/org.eclipse.emf.emfstore.server.test/src/org/eclipse/emf/emfstore/server/accesscontrol/test/DeleteProjectTest.java
+++ b/tests/org.eclipse.emf.emfstore.server.test/src/org/eclipse/emf/emfstore/server/accesscontrol/test/DeleteProjectTest.java
@@ -20,12 +20,15 @@
 import java.util.List;
 
 import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.emf.emfstore.client.test.common.dsl.Roles;
 import org.eclipse.emf.emfstore.client.test.common.mocks.ConnectionMock;
 import org.eclipse.emf.emfstore.client.test.common.util.ProjectUtil;
 import org.eclipse.emf.emfstore.client.test.common.util.ServerUtil;
 import org.eclipse.emf.emfstore.internal.client.model.ESWorkspaceProviderImpl;
 import org.eclipse.emf.emfstore.internal.server.exceptions.AccessControlException;
+import org.eclipse.emf.emfstore.internal.server.model.ProjectId;
 import org.eclipse.emf.emfstore.internal.server.model.ProjectInfo;
+import org.eclipse.emf.emfstore.internal.server.model.accesscontrol.ACOrgUnitId;
 import org.eclipse.emf.emfstore.internal.server.model.accesscontrol.ACUser;
 import org.eclipse.emf.emfstore.internal.server.model.impl.api.ESSessionIdImpl;
 import org.eclipse.emf.emfstore.server.auth.ESProjectAdminPrivileges;
@@ -38,13 +41,19 @@
 
 /**
  * Test whether a project admin can delete a project without deleting
- * its files on the server
+ * its files on the server.
+ * Also test that roles are correctly removed from affected users and groups when a project is deleted.
  *
  * @author emueller
  *
  */
 public class DeleteProjectTest extends ProjectAdminTest {
 
+	private static final String READER_USER = "JonReader"; //$NON-NLS-1$
+	private static final String WRITER_USER = "JaneWriter"; //$NON-NLS-1$
+
+	private static final String PA_GROUP = "PaGroup"; //$NON-NLS-1$
+
 	@BeforeClass
 	public static void beforeClass() {
 		startEMFStoreWithPAProperties(
@@ -63,6 +72,15 @@
 		try {
 			ServerUtil.deleteGroup(getSuperUsersession(), getNewGroupName());
 			ServerUtil.deleteGroup(getSuperUsersession(), getNewOtherGroupName());
+			ServerUtil.deleteGroup(getSuperUsersession(), PA_GROUP);
+			final ACUser readerUser = ServerUtil.getUser(getSuperUsersession(), READER_USER);
+			if (readerUser != null) {
+				getSuperAdminBroker().deleteUser(readerUser.getId());
+			}
+			final ACUser writerUser = ServerUtil.getUser(getSuperUsersession(), WRITER_USER);
+			if (readerUser != null) {
+				getSuperAdminBroker().deleteUser(writerUser.getId());
+			}
 		} catch (final ESException ex) {
 			fail(ex.getMessage());
 		}
@@ -76,7 +94,7 @@
 	}
 
 	@Test
-	public void delteProjectSA() throws ESException {
+	public void deleteProjectSA() throws ESException {
 		makeUserSA();
 		// TODO:
 		getUsersession().logout();
@@ -93,7 +111,7 @@
 	}
 
 	@Test(expected = AccessControlException.class)
-	public void delteProjectNotPA() throws ESException {
+	public void deleteProjectNotSA() throws ESException {
 		ProjectUtil.share(getUsersession(), getLocalProject());
 		getLocalProject().getRemoteProject().delete(new NullProgressMonitor());
 	}
@@ -122,4 +140,43 @@
 		getLocalProject().getRemoteProject().delete(new NullProgressMonitor());
 	}
 
+	@Test
+	public void deleteProjectCleanAllRoles() throws ESException, IOException {
+		makeUserPA();
+		ProjectUtil.share(getUsersession(), getLocalProject());
+		final ProjectId projectId = getProjectSpace().getProjectId();
+
+		final ACOrgUnitId readerUser = ServerUtil.createUser(getSuperUsersession(), READER_USER);
+		final ACOrgUnitId writerUser = ServerUtil.createUser(getSuperUsersession(), WRITER_USER);
+		final ACOrgUnitId readerGroup = ServerUtil.createGroup(getSuperUsersession(), getNewGroupName());
+		final ACOrgUnitId writerGroup = ServerUtil.createGroup(getSuperUsersession(), getNewOtherGroupName());
+		final ACOrgUnitId paGroup = ServerUtil.createGroup(getSuperUsersession(), PA_GROUP);
+
+		getSuperAdminBroker().changeRole(projectId, readerUser, Roles.reader());
+		getSuperAdminBroker().changeRole(projectId, writerUser, Roles.writer());
+
+		getSuperAdminBroker().changeRole(projectId, readerGroup, Roles.reader());
+		getSuperAdminBroker().changeRole(projectId, writerGroup, Roles.writer());
+		getSuperAdminBroker().changeRole(projectId, paGroup, Roles.projectAdmin());
+
+		getLocalProject().getRemoteProject().delete(new NullProgressMonitor());
+
+		assertFalse(hasReaderRole(readerUser));
+		assertFalse(hasWriterRole(writerUser));
+		assertFalse(hasProjectAdminRole(ServerUtil.getUser(getSuperUsersession(), getUser()).getId()));
+
+		assertFalse(hasReaderRole(readerGroup));
+		assertFalse(hasWriterRole(writerGroup));
+		assertFalse(hasProjectAdminRole(paGroup));
+	}
+
+	@Test
+	public void deleteProjectServerAdminRoleNotRemoved() throws ESException, IOException {
+		makeUserSA();
+		final ACOrgUnitId saGroup = ServerUtil.createGroup(getSuperUsersession(), getNewGroupName());
+		getSuperAdminBroker().assignRole(saGroup, Roles.serverAdmin());
+		getProjectSpace().delete(new NullProgressMonitor());
+		assertTrue(hasServerAdminRole(ServerUtil.getUser(getSuperUsersession(), getUser()).getId()));
+		assertTrue(hasServerAdminRole(saGroup));
+	}
 }
\ No newline at end of file