Implemented delete user-group and member tasks.

Change-Id: Iacb799ca03234fbbd4b6cc06126cb78ed3521aad
diff --git a/full/Changes b/full/Changes
index fa2d6e7..0a8299a 100644
--- a/full/Changes
+++ b/full/Changes
@@ -1,10 +1,12 @@
-0.59.10	2018-01-23
+0.59.10	2018-01-25 
 	- added sort VC by id (margaretha)
 	- added test cases regarding VC sharing (margaretha)
 	- implemented withdraw VC from publication (margaretha)
 	- added Changes file (margaretha)
 	- implemented add users to group (margaretha)
-	
+	- implemented delete user-group and member tasks (margaretha)
+	- added userMemberStatus in group lists (margaretha)
+	- updated sql test data (margaretha)
 	
 0.59.9 2018-01-19
 	- restructured basic authentication (margaretha)
@@ -19,7 +21,7 @@
 	- implemented create user-group, subscribe to usergroup, unsubscribe to user group tasks(margaretha)
 	- fixed handling JSON mapping exception for missing enums (margaretha)
     - implemented list VC task (margaretha)
-    - include KoralQuery in VC list (margaretha)
+    - added KoralQuery in VC lists (margaretha)
 	- implemented edit VC task (margaretha)
 	- implemented publish VC task (margaretha)
     - implemented share VC task (margaretha)
diff --git a/full/pom.xml b/full/pom.xml
index 9b18889..02fc984 100644
--- a/full/pom.xml
+++ b/full/pom.xml
@@ -161,16 +161,9 @@
 		<dependency>
 			<groupId>de.ids_mannheim.korap</groupId>
 			<artifactId>Kustvakt-core</artifactId>
-			<version>0.59.9</version>
+			<version>0.59.10</version>
 			<type>jar</type>
 		</dependency>
-		<dependency>
-			<groupId>de.ids_mannheim.korap</groupId>
-			<artifactId>Kustvakt-core</artifactId>
-			<version>0.59.9</version>
-			<type>test-jar</type>
-			<scope>test</scope>
-		</dependency>
 		<!-- LDAP -->
 		<dependency>
 			<groupId>com.novell.ldap</groupId>
diff --git a/full/src/main/java/de/ids_mannheim/korap/authentication/KustvaktAuthenticationManager.java b/full/src/main/java/de/ids_mannheim/korap/authentication/KustvaktAuthenticationManager.java
index d673427..46649eb 100644
--- a/full/src/main/java/de/ids_mannheim/korap/authentication/KustvaktAuthenticationManager.java
+++ b/full/src/main/java/de/ids_mannheim/korap/authentication/KustvaktAuthenticationManager.java
@@ -324,7 +324,7 @@
 		}
 
 		boolean isAdmin = adminHandler.isAdmin(unknown.getId());
-		unknown.setAdmin(isAdmin);
+		unknown.setSystemAdmin(isAdmin);
 		jlog.debug("Authentication: found username " + unknown.getUsername());
 
 		if (unknown instanceof KorAPUser) {
@@ -697,7 +697,7 @@
 
 		String o = (String) attributes.get(Attributes.IS_ADMIN);
 		boolean b = Boolean.parseBoolean(o);
-		user.setAdmin(b);
+		user.setSystemAdmin(b);
 
 		try {
 			UserDetails details = new UserDetails();
@@ -708,7 +708,7 @@
 
 			jlog.info("Creating new user account for user {}", user.getUsername());
 			entHandler.createAccount(user);
-			if (user.isAdmin() && user instanceof KorAPUser) {
+			if (user.isSystemAdmin() && user instanceof KorAPUser) {
 				adminHandler.addAccount(user);
 				user.setCorpusAccess(CorpusAccess.ALL);
 			}
diff --git a/full/src/main/java/de/ids_mannheim/korap/dao/UserGroupDao.java b/full/src/main/java/de/ids_mannheim/korap/dao/UserGroupDao.java
index 6bd621f..6885f3d 100644
--- a/full/src/main/java/de/ids_mannheim/korap/dao/UserGroupDao.java
+++ b/full/src/main/java/de/ids_mannheim/korap/dao/UserGroupDao.java
@@ -172,8 +172,10 @@
                         UserGroupStatus.ACTIVE),
                 criteriaBuilder.equal(members.get(UserGroupMember_.userId),
                         userId),
-                criteriaBuilder.equal(members.get(UserGroupMember_.status),
-                        GroupMemberStatus.ACTIVE));
+                criteriaBuilder.notEqual(members.get(UserGroupMember_.status),
+                        GroupMemberStatus.DELETED));
+//                criteriaBuilder.equal(members.get(UserGroupMember_.status),
+//                        GroupMemberStatus.ACTIVE));
 
 
         query.select(root);
diff --git a/full/src/main/java/de/ids_mannheim/korap/dto/UserGroupDto.java b/full/src/main/java/de/ids_mannheim/korap/dto/UserGroupDto.java
index 10fc1c4..6b3e8b6 100644
--- a/full/src/main/java/de/ids_mannheim/korap/dto/UserGroupDto.java
+++ b/full/src/main/java/de/ids_mannheim/korap/dto/UserGroupDto.java
@@ -2,6 +2,7 @@
 
 import java.util.List;
 
+import de.ids_mannheim.korap.constant.GroupMemberStatus;
 import lombok.Getter;
 import lombok.Setter;
 
@@ -13,4 +14,5 @@
     private String name;
     private String owner;
     private List<UserGroupMemberDto> members;
+    private GroupMemberStatus userMemberStatus;
 }
diff --git a/full/src/main/java/de/ids_mannheim/korap/dto/converter/UserGroupConverter.java b/full/src/main/java/de/ids_mannheim/korap/dto/converter/UserGroupConverter.java
index 8925d20..273677b 100644
--- a/full/src/main/java/de/ids_mannheim/korap/dto/converter/UserGroupConverter.java
+++ b/full/src/main/java/de/ids_mannheim/korap/dto/converter/UserGroupConverter.java
@@ -5,6 +5,7 @@
 
 import org.springframework.stereotype.Component;
 
+import de.ids_mannheim.korap.constant.GroupMemberStatus;
 import de.ids_mannheim.korap.dto.UserGroupDto;
 import de.ids_mannheim.korap.dto.UserGroupMemberDto;
 import de.ids_mannheim.korap.entity.Role;
@@ -15,12 +16,13 @@
 public class UserGroupConverter {
 
     public UserGroupDto createUserGroupDto (UserGroup group,
-            List<UserGroupMember> members) {
+            List<UserGroupMember> members, GroupMemberStatus userMemberStatus) {
 
         UserGroupDto dto = new UserGroupDto();
         dto.setId(group.getId());
         dto.setName(group.getName());
         dto.setOwner(group.getCreatedBy());
+        dto.setUserMemberStatus(userMemberStatus);
 
         if (members != null) {
             ArrayList<UserGroupMemberDto> memberDtos =
diff --git a/full/src/main/java/de/ids_mannheim/korap/service/UserGroupService.java b/full/src/main/java/de/ids_mannheim/korap/service/UserGroupService.java
index c238ae4..02cff45 100644
--- a/full/src/main/java/de/ids_mannheim/korap/service/UserGroupService.java
+++ b/full/src/main/java/de/ids_mannheim/korap/service/UserGroupService.java
@@ -66,30 +66,44 @@
         Collections.sort(userGroups);
         ArrayList<UserGroupDto> dtos = new ArrayList<>(userGroups.size());
 
-        List<UserGroupMember> groupAdmins;
+        UserGroupMember userAsMember;
+        List<UserGroupMember> members;
         for (UserGroup group : userGroups) {
-            groupAdmins = groupMemberDao.retrieveMemberByRole(group.getId(),
-                    PredefinedRole.USER_GROUP_ADMIN.getId());
-
-            List<UserGroupMember> members = null;
-            for (UserGroupMember admin : groupAdmins) {
-                if (admin.getUserId().equals(username)) {
-                    members = groupMemberDao
-                            .retrieveMemberByGroupId(group.getId());
-                    break;
-                }
-            }
-            dtos.add(converter.createUserGroupDto(group, members));
+            members = retrieveMembers(group.getId(), username);
+            userAsMember =
+                    groupMemberDao.retrieveMemberById(username, group.getId());
+            dtos.add(converter.createUserGroupDto(group, members,
+                    userAsMember.getStatus()));
         }
 
         return dtos;
     }
 
+    private List<UserGroupMember> retrieveMembers (int groupId, String username)
+            throws KustvaktException {
+        List<UserGroupMember> groupAdmins = groupMemberDao.retrieveMemberByRole(
+                groupId, PredefinedRole.USER_GROUP_ADMIN.getId());
+
+        List<UserGroupMember> members = null;
+        for (UserGroupMember admin : groupAdmins) {
+            if (admin.getUserId().equals(username)) {
+                members = groupMemberDao.retrieveMemberByGroupId(groupId);
+                break;
+            }
+        }
+        
+        return members;
+    }
+
     public UserGroup retrieveUserGroupById (int groupId)
             throws KustvaktException {
         return userGroupDao.retrieveGroupById(groupId);
     }
 
+    public UserGroup retrieveHiddenGroup (int vcId) throws KustvaktException {
+        return userGroupDao.retrieveHiddenGroupByVC(vcId);
+    }
+
     public List<UserGroupMember> retrieveVCAccessAdmins (UserGroup userGroup)
             throws KustvaktException {
         List<UserGroupMember> groupAdmins = groupMemberDao.retrieveMemberByRole(
@@ -104,6 +118,15 @@
         return groupAdmins;
     }
 
+    private void setMemberRoles () {
+        if (memberRoles == null) {
+            memberRoles = new ArrayList<Role>(2);
+            memberRoles.add(roleDao.retrieveRoleById(
+                    PredefinedRole.USER_GROUP_MEMBER.getId()));
+            memberRoles.add(roleDao
+                    .retrieveRoleById(PredefinedRole.VC_ACCESS_MEMBER.getId()));
+        }
+    }
 
     /** Group owner is automatically added when creating a group. 
      *  Do not include owners in group members. 
@@ -122,43 +145,37 @@
      * @see /full/src/main/resources/db/predefined/V3.2__insert_predefined_roles.sql
      * 
      * @param groupJson UserGroupJson object from json
-     * @param username the user creating the group
+     * @param createdBy the user creating the group
      * @throws KustvaktException
      * 
      * 
      */
-    public void createUserGroup (UserGroupJson groupJson, String username)
+    public void createUserGroup (UserGroupJson groupJson, String createdBy)
             throws KustvaktException {
 
-        int groupId = userGroupDao.createGroup(groupJson.getName(), username,
+        int groupId = userGroupDao.createGroup(groupJson.getName(), createdBy,
                 UserGroupStatus.ACTIVE);
-        UserGroup group = userGroupDao.retrieveGroupById(groupId);
+        UserGroup userGroup = userGroupDao.retrieveGroupById(groupId);
 
         setMemberRoles();
 
-        UserGroupMember m;
+
         for (String memberId : groupJson.getMembers()) {
-            if (memberId.equals(username)) {
+            if (memberId.equals(createdBy)) {
                 // skip owner, already added while creating group.
                 continue;
             }
-
-            m = new UserGroupMember();
-            m.setUserId(memberId);
-            m.setCreatedBy(username);
-            m.setGroup(group);
-            m.setStatus(GroupMemberStatus.PENDING);
-            m.setRoles(memberRoles);
+            addGroupMember(memberId, userGroup, createdBy,
+                    GroupMemberStatus.PENDING);
         }
     }
 
-    private void setMemberRoles () {
-        if (memberRoles == null) {
-            memberRoles = new ArrayList<Role>(2);
-            memberRoles.add(roleDao.retrieveRoleById(
-                    PredefinedRole.USER_GROUP_MEMBER.getId()));
-            memberRoles.add(roleDao
-                    .retrieveRoleById(PredefinedRole.VC_ACCESS_MEMBER.getId()));
+    public void deleteGroup (int groupId, String username)
+            throws KustvaktException {
+        User user = authManager.getUser(username);
+        UserGroup userGroup = userGroupDao.retrieveGroupById(groupId);
+        if (userGroup.getCreatedBy().equals(username) || user.isSystemAdmin()) {
+            userGroupDao.deleteGroup(groupId, username, false);
         }
     }
 
@@ -191,7 +208,7 @@
      * @param status the status of the membership
      * @throws KustvaktException
      */
-    public void addUserToGroup (String username, UserGroup userGroup,
+    public void addGroupMember (String username, UserGroup userGroup,
             String createdBy, GroupMemberStatus status)
             throws KustvaktException {
 
@@ -227,11 +244,12 @@
             return false;
         }
 
-        GroupMemberStatus memberStatus = existingMember.getStatus();
-        if (memberStatus.equals(status)) {
+        GroupMemberStatus existingStatus = existingMember.getStatus();
+        if (existingStatus.equals(GroupMemberStatus.ACTIVE)
+                || existingStatus.equals(status)) {
             return true;
         }
-        else if (memberStatus.equals(GroupMemberStatus.DELETED)) {
+        else if (existingStatus.equals(GroupMemberStatus.DELETED)) {
             // hard delete
             groupMemberDao.deleteMember(username, groupId, "system", false);
         }
@@ -242,15 +260,15 @@
     public void addUsersToGroup (UserGroupJson group, String username)
             throws KustvaktException {
         int groupId = group.getId();
-        List<String> members = group.getMembers();
+        String[] members = group.getMembers();
         ParameterChecker.checkIntegerValue(groupId, "id");
         ParameterChecker.checkObjectValue(members, "members");
 
         UserGroup userGroup = retrieveUserGroupById(groupId);
         User user = authManager.getUser(username);
-        if (isUserGroupAdmin(username, userGroup) || user.isAdmin()) {
+        if (isUserGroupAdmin(username, userGroup) || user.isSystemAdmin()) {
             for (String memberName : members) {
-                addUserToGroup(memberName, userGroup, username,
+                addGroupMember(memberName, userGroup, username,
                         GroupMemberStatus.PENDING);
             }
         }
@@ -311,9 +329,23 @@
         return false;
     }
 
-    public UserGroup retrieveHiddenGroup (int vcId) throws KustvaktException {
-        return userGroupDao.retrieveHiddenGroupByVC(vcId);
+    public void deleteGroupMember (String memberId, int groupId,
+            String deletedBy) throws KustvaktException {
+        User user = authManager.getUser(deletedBy);
+        UserGroup userGroup = userGroupDao.retrieveGroupById(groupId);
+        if (memberId.equals(userGroup.getCreatedBy())) {
+            throw new KustvaktException(StatusCodes.NOT_ALLOWED,
+                    "Operation " + "'delete group owner'" + "is not allowed.",
+                    "delete group owner");
+        }
+        else if (isUserGroupAdmin(deletedBy, userGroup)
+                || user.isSystemAdmin()) {
+            groupMemberDao.deleteMember(memberId, groupId, deletedBy, false);
+        }
+        else {
+            throw new KustvaktException(StatusCodes.AUTHORIZATION_FAILED,
+                    "Unauthorized operation for user: " + deletedBy, deletedBy);
+        }
     }
 
-
 }
diff --git a/full/src/main/java/de/ids_mannheim/korap/service/VirtualCorpusService.java b/full/src/main/java/de/ids_mannheim/korap/service/VirtualCorpusService.java
index 832dca0..b4e7fbc 100644
--- a/full/src/main/java/de/ids_mannheim/korap/service/VirtualCorpusService.java
+++ b/full/src/main/java/de/ids_mannheim/korap/service/VirtualCorpusService.java
@@ -14,7 +14,6 @@
 
 import de.ids_mannheim.korap.config.FullConfiguration;
 import de.ids_mannheim.korap.constant.GroupMemberStatus;
-import de.ids_mannheim.korap.constant.UserGroupStatus;
 import de.ids_mannheim.korap.constant.VirtualCorpusAccessStatus;
 import de.ids_mannheim.korap.constant.VirtualCorpusType;
 import de.ids_mannheim.korap.dao.VirtualCorpusAccessDao;
@@ -110,7 +109,7 @@
         User user = authManager.getUser(username);
         VirtualCorpus vc = vcDao.retrieveVCById(vcId);
 
-        if (vc.getCreatedBy().equals(username) || user.isAdmin()) {
+        if (vc.getCreatedBy().equals(username) || user.isSystemAdmin()) {
             vcDao.deleteVirtualCorpus(vcId);
         }
         else {
@@ -126,7 +125,7 @@
         VirtualCorpus vc = vcDao.retrieveVCById(vcId);
 
         User user = authManager.getUser(username);
-        if (!username.equals(vc.getCreatedBy()) && !user.isAdmin()) {
+        if (!username.equals(vc.getCreatedBy()) && !user.isSystemAdmin()) {
             throw new KustvaktException(StatusCodes.AUTHORIZATION_FAILED,
                     "Unauthorized operation for user: " + username, username);
         }
@@ -192,7 +191,7 @@
         User user = authManager.getUser(username);
 
         if (vc.getType().equals(VirtualCorpusType.PREDEFINED)
-                && !user.isAdmin()) {
+                && !user.isSystemAdmin()) {
             throw new KustvaktException(StatusCodes.AUTHORIZATION_FAILED,
                     "Unauthorized operation for user: " + username, username);
         }
@@ -266,20 +265,22 @@
         User user = authManager.getUser(username);
 
         VirtualCorpus vc = vcDao.retrieveVCById(vcId);
-        if (!username.equals(vc.getCreatedBy()) && !user.isAdmin()) {
+        if (!username.equals(vc.getCreatedBy()) && !user.isSystemAdmin()) {
             throw new KustvaktException(StatusCodes.AUTHORIZATION_FAILED,
                     "Unauthorized operation for user: " + username, username);
         }
 
         UserGroup userGroup = userGroupService.retrieveUserGroupById(groupId);
 
-        if (!isVCAccessAdmin(userGroup, username) && !user.isAdmin()) {
+        if (!isVCAccessAdmin(userGroup, username) && !user.isSystemAdmin()) {
             throw new KustvaktException(StatusCodes.AUTHORIZATION_FAILED,
                     "Unauthorized operation for user: " + username, username);
         }
         else {
             accessDao.createAccessToVC(vc, userGroup, username,
                     VirtualCorpusAccessStatus.ACTIVE);
+            vcDao.editVirtualCorpus(vc, null, VirtualCorpusType.PUBLISHED, null,
+                    null, null, null, null);
         }
     }
 
@@ -304,7 +305,7 @@
                 userGroupService.retrieveVCAccessAdmins(userGroup);
 
         User user = authManager.getUser(username);
-        if (!user.isAdmin()) {
+        if (!user.isSystemAdmin()) {
             throw new KustvaktException(StatusCodes.AUTHORIZATION_FAILED,
                     "Unauthorized operation for user: " + username, username);
         }
@@ -315,7 +316,7 @@
 
         List<VirtualCorpusAccess> accessList;
         User user = authManager.getUser(username);
-        if (user.isAdmin()) {
+        if (user.isSystemAdmin()) {
             accessList = accessDao.retrieveAllAccessByVC(vcId);
         }
         else {
@@ -338,7 +339,7 @@
         UserGroup userGroup = userGroupService.retrieveUserGroupById(groupId);
 
         List<VirtualCorpusAccess> accessList;
-        if (user.isAdmin()) {
+        if (user.isSystemAdmin()) {
             accessList = accessDao.retrieveAllAccessByGroup(groupId);
         }
         else if (isVCAccessAdmin(userGroup, username)) {
@@ -359,7 +360,7 @@
 
         VirtualCorpusAccess access = accessDao.retrieveAccessById(accessId);
         UserGroup userGroup = access.getUserGroup();
-        if (isVCAccessAdmin(userGroup, username) || user.isAdmin()) {
+        if (isVCAccessAdmin(userGroup, username) || user.isSystemAdmin()) {
             accessDao.deleteAccess(access, username);
         }
         else {
@@ -376,7 +377,7 @@
         VirtualCorpus vc = vcDao.retrieveVCById(vcId);
         VirtualCorpusType type = vc.getType();
 
-        if (!user.isAdmin() && !username.equals(vc.getCreatedBy())) {
+        if (!user.isSystemAdmin() && !username.equals(vc.getCreatedBy())) {
             if (type.equals(VirtualCorpusType.PRIVATE)
                     || (type.equals(VirtualCorpusType.PROJECT)
                             && !hasAccess(username, vcId))) {
@@ -392,9 +393,9 @@
                 //                UserGroup userGroup = access.getUserGroup();
                 UserGroup userGroup =
                         userGroupService.retrieveHiddenGroup(vcId);
-//                if (!userGroupService.isMember(username, userGroup)) {
-                try{
-                    userGroupService.addUserToGroup(username, userGroup,
+                //                if (!userGroupService.isMember(username, userGroup)) {
+                try {
+                    userGroupService.addGroupMember(username, userGroup,
                             "system", GroupMemberStatus.ACTIVE);
                 }
                 catch (KustvaktException e) {
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/controller/UserGroupController.java b/full/src/main/java/de/ids_mannheim/korap/web/controller/UserGroupController.java
index 8679437..2ddec6f 100644
--- a/full/src/main/java/de/ids_mannheim/korap/web/controller/UserGroupController.java
+++ b/full/src/main/java/de/ids_mannheim/korap/web/controller/UserGroupController.java
@@ -3,11 +3,13 @@
 import java.util.List;
 
 import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
 import javax.ws.rs.FormParam;
 import javax.ws.rs.GET;
 import javax.ws.rs.POST;
 import javax.ws.rs.Path;
 import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
 import javax.ws.rs.core.Context;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
@@ -31,13 +33,14 @@
 import de.ids_mannheim.korap.web.filter.PiwikFilter;
 import de.ids_mannheim.korap.web.input.UserGroupJson;
 
-/** UserGroupController defines web APIs related to user groups, 
- *  such as creating a user group,  listing groups of a user, 
- *  adding members to a group and subscribing (confirming an 
- *  invitation) to a group. 
- *  
- *  These APIs are only available to logged-in users.
- *   
+/**
+ * UserGroupController defines web APIs related to user groups,
+ * such as creating a user group, listing groups of a user,
+ * adding members to a group and subscribing (confirming an
+ * invitation) to a group.
+ * 
+ * These APIs are only available to logged-in users.
+ * 
  * @author margaretha
  *
  */
@@ -95,7 +98,7 @@
     @POST
     @Path("create")
     @Consumes(MediaType.APPLICATION_JSON)
-    public Response createGroup (@Context SecurityContext securityContext,
+    public Response createUserGroup (@Context SecurityContext securityContext,
             UserGroupJson group) {
         TokenContext context =
                 (TokenContext) securityContext.getUserPrincipal();
@@ -108,8 +111,50 @@
         }
     }
 
+    @DELETE
+    @Path("delete")
+    @Consumes(MediaType.APPLICATION_JSON)
+    public Response deleteUserGroup (
+            @Context SecurityContext securityContext,
+            @QueryParam("groupId") int groupId) {
+        TokenContext context =
+                (TokenContext) securityContext.getUserPrincipal();
+        try {
+            service.deleteGroup(groupId, context.getUsername());
+            return Response.ok().build();
+        }
+        catch (KustvaktException e) {
+            throw responseHandler.throwit(e);
+        }
+    }
+    
+    /** Group owner cannot be deleted.
+     * 
+     * @param securityContext
+     * @param memberId a username of a group member
+     * @param groupId a group id
+     * @return if successful, HTTP response status OK
+     */
+    @DELETE
+    @Path("member/delete")
+    @Consumes(MediaType.APPLICATION_JSON)
+    public Response deleteUserFromGroup (
+            @Context SecurityContext securityContext,
+            @QueryParam("memberId") String memberId,
+            @QueryParam("groupId") int groupId) {
+        TokenContext context =
+                (TokenContext) securityContext.getUserPrincipal();
+        try {
+            service.deleteGroupMember(memberId, groupId, context.getUsername());
+            return Response.ok().build();
+        }
+        catch (KustvaktException e) {
+            throw responseHandler.throwit(e);
+        }
+    }
+
     @POST
-    @Path("add")
+    @Path("member/invite")
     @Consumes(MediaType.APPLICATION_JSON)
     public Response addUserToGroup (@Context SecurityContext securityContext,
             UserGroupJson group) {
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/filter/AdminFilter.java b/full/src/main/java/de/ids_mannheim/korap/web/filter/AdminFilter.java
index b1cc781..bac128c 100644
--- a/full/src/main/java/de/ids_mannheim/korap/web/filter/AdminFilter.java
+++ b/full/src/main/java/de/ids_mannheim/korap/web/filter/AdminFilter.java
@@ -71,7 +71,7 @@
             // EM: fix me: AuthenticationType based on header value
             User user = authManager.authenticate(AuthenticationMethod.LDAP,
                     data.getUsername(), data.getPassword(), attributes);
-            if (!user.isAdmin()) {
+            if (!user.isSystemAdmin()) {
                 throw new KustvaktException(StatusCodes.AUTHENTICATION_FAILED,
                         "Admin authentication failed.");
             }
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/filter/BlockingFilter.java b/full/src/main/java/de/ids_mannheim/korap/web/filter/BlockingFilter.java
index 758baf8..4cc7f99 100644
--- a/full/src/main/java/de/ids_mannheim/korap/web/filter/BlockingFilter.java
+++ b/full/src/main/java/de/ids_mannheim/korap/web/filter/BlockingFilter.java
@@ -44,7 +44,7 @@
         if (context == null || context.isDemo()) {
             throw kustvaktResponseHandler.throwit(new KustvaktException(
                     StatusCodes.AUTHORIZATION_FAILED,
-                    "Operation is not permitted for user: guest", "guest"));
+                    "Unauthorized operation for user: guest", "guest"));
         }
 
 
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/input/UserGroupJson.java b/full/src/main/java/de/ids_mannheim/korap/web/input/UserGroupJson.java
index 87f17cb..e3c25bd 100644
--- a/full/src/main/java/de/ids_mannheim/korap/web/input/UserGroupJson.java
+++ b/full/src/main/java/de/ids_mannheim/korap/web/input/UserGroupJson.java
@@ -1,7 +1,5 @@
 package de.ids_mannheim.korap.web.input;
 
-import java.util.List;
-
 import lombok.Getter;
 import lombok.Setter;
 
@@ -11,5 +9,5 @@
 
     private int id;
     private String name;
-    private List<String> members;
+    private String[] members;
 }
diff --git a/full/src/main/resources/db/insert/V3.3__insert_member_roles.sql b/full/src/main/resources/db/insert/V3.3__insert_member_roles.sql
index 9cb523f..effbbcb 100644
--- a/full/src/main/resources/db/insert/V3.3__insert_member_roles.sql
+++ b/full/src/main/resources/db/insert/V3.3__insert_member_roles.sql
@@ -4,49 +4,49 @@
 INSERT INTO group_member_role(group_member_id,role_id)
 SELECT
 	(SELECT id FROM user_group_member WHERE user_id="marlin" AND group_id=1),
-	(SELECT id FROM role WHERE name = "group admin");
+	(SELECT id FROM role WHERE name = "USER_GROUP_ADMIN");
 	
 INSERT INTO group_member_role(group_member_id,role_id)
 SELECT
 	(SELECT id FROM user_group_member WHERE user_id="marlin" AND group_id=1),
-	(SELECT id FROM role WHERE name = "vc admin");
+	(SELECT id FROM role WHERE name = "VC_ACCESS_ADMIN");
 	
 INSERT INTO group_member_role(group_member_id,role_id)
 SELECT
 	(SELECT id FROM user_group_member WHERE user_id="dory" AND group_id=1),
-	(SELECT id FROM role WHERE name = "group member");
+	(SELECT id FROM role WHERE name = "USER_GROUP_ADMIN");
 	
 INSERT INTO group_member_role(group_member_id,role_id)
 SELECT
 	(SELECT id FROM user_group_member WHERE user_id="dory" AND group_id=1),
-	(SELECT id FROM role WHERE name = "vc admin");
+	(SELECT id FROM role WHERE name = "VC_ACCESS_ADMIN");
 	
 	
 -- dory group
 INSERT INTO group_member_role(group_member_id,role_id)
 SELECT
 	(SELECT id FROM user_group_member WHERE user_id="dory" AND group_id=2),
-	(SELECT id FROM role WHERE name = "group admin");
+	(SELECT id FROM role WHERE name = "USER_GROUP_ADMIN");
 	
 INSERT INTO group_member_role(group_member_id,role_id)
 SELECT
 	(SELECT id FROM user_group_member WHERE user_id="dory" AND group_id=2),
-	(SELECT id FROM role WHERE name = "vc admin");
+	(SELECT id FROM role WHERE name = "VC_ACCESS_ADMIN");
 	
 INSERT INTO group_member_role(group_member_id,role_id)
 SELECT
 	(SELECT id FROM user_group_member WHERE user_id="nemo" AND group_id=2),
-	(SELECT id FROM role WHERE name = "group member");
+	(SELECT id FROM role WHERE name = "USER_GROUP_MEMBER");
 	
 INSERT INTO group_member_role(group_member_id,role_id)
 SELECT
 	(SELECT id FROM user_group_member WHERE user_id="nemo" AND group_id=2),
-	(SELECT id FROM role WHERE name = "vc member");
+	(SELECT id FROM role WHERE name = "VC_ACCESS_MEMBER");
 
 
 -- auto group
 INSERT INTO group_member_role(group_member_id,role_id)
 SELECT
 	(SELECT id FROM user_group_member WHERE user_id="pearl" AND group_id=3),
-	(SELECT id FROM role WHERE name = "vc member");
+	(SELECT id FROM role WHERE name = "VC_ACCESS_MEMBER");
 
diff --git a/full/src/main/resources/db/predefined/V3.2__insert_predefined_roles.sql b/full/src/main/resources/db/predefined/V3.2__insert_predefined_roles.sql
index c9409ea..6fb6991 100644
--- a/full/src/main/resources/db/predefined/V3.2__insert_predefined_roles.sql
+++ b/full/src/main/resources/db/predefined/V3.2__insert_predefined_roles.sql
@@ -1,8 +1,8 @@
 -- roles
-INSERT INTO role(name) VALUES ("group admin");
-INSERT INTO role(name) VALUES ("group member");
-INSERT INTO role(name) VALUES ("vc admin");
-INSERT INTO role(name) VALUES ("vc member");
+INSERT INTO role(name) VALUES ("USER_GROUP_ADMIN");
+INSERT INTO role(name) VALUES ("USER_GROUP_MEMBER");
+INSERT INTO role(name) VALUES ("VC_ACCESS_ADMIN");
+INSERT INTO role(name) VALUES ("VC_ACCESS_MEMBER");
 
 -- privileges
 INSERT INTO privilege(name,role_id)
diff --git a/full/src/test/java/de/ids_mannheim/korap/config/BeanConfigBaseTest.java b/full/src/test/java/de/ids_mannheim/korap/config/BeanConfigBaseTest.java
new file mode 100644
index 0000000..a625556
--- /dev/null
+++ b/full/src/test/java/de/ids_mannheim/korap/config/BeanConfigBaseTest.java
@@ -0,0 +1,83 @@
+package de.ids_mannheim.korap.config;
+
+import static org.junit.Assert.assertNotNull;
+
+import org.apache.log4j.Logger;
+import org.junit.runner.RunWith;
+import org.junit.runner.notification.RunNotifier;
+import org.junit.runners.model.InitializationError;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.ApplicationContext;
+import org.springframework.test.annotation.DirtiesContext;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+import net.jcip.annotations.NotThreadSafe;
+
+/**
+ * @author hanl
+ * @date 09/03/2016
+ */
+@Deprecated
+@NotThreadSafe
+@RunWith(BeanConfigBaseTest.SpringExtendedSetupListener.class)
+@ContextConfiguration("classpath:test-config.xml")
+@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS)
+public abstract class BeanConfigBaseTest {
+
+    private static Logger jlog = Logger.getLogger(BeanConfigBaseTest.class);
+    @Autowired
+    protected ApplicationContext context;
+    
+    public void init () throws Exception {
+        assertNotNull("Application context must not be null!", this.context);
+        jlog.debug("running one-time before init for class "
+                + this.getClass().getSimpleName() + " ...");
+        BeansFactory.setKustvaktContext(getContext());
+        assertNotNull(BeansFactory.getKustvaktContext());
+        initMethod();
+    }
+
+    protected abstract ContextHolder getContext();
+
+    public abstract void initMethod () throws KustvaktException;
+    
+    public void close () {
+        BeansFactory.closeApplication();
+    }
+
+    public static class SpringExtendedSetupListener extends
+            SpringJUnit4ClassRunner {
+
+        private BeanConfigBaseTest instanceSetupListener;
+
+
+        public SpringExtendedSetupListener (Class<?> clazz)
+                throws InitializationError {
+            super(clazz);
+        }
+
+
+        @Override
+        protected Object createTest () throws Exception {
+            Object test = super.createTest();
+            // Note that JUnit4 will call this createTest() multiple times for each
+            // test method, so we need to ensure to call "beforeClassSetup" only once.
+            if (test instanceof BeanConfigBaseTest && instanceSetupListener == null) {
+                instanceSetupListener = (BeanConfigBaseTest) test;
+                instanceSetupListener.init();
+            }
+            return test;
+        }
+
+
+        @Override
+        public void run (RunNotifier notifier) {
+            super.run(notifier);
+            if (instanceSetupListener != null){
+                instanceSetupListener.close();
+            }
+        }
+    }
+}
diff --git a/full/src/test/java/de/ids_mannheim/korap/config/BeanConfigTest.java b/full/src/test/java/de/ids_mannheim/korap/config/BeanConfigTest.java
index ff19b62..7603c18 100644
--- a/full/src/test/java/de/ids_mannheim/korap/config/BeanConfigTest.java
+++ b/full/src/test/java/de/ids_mannheim/korap/config/BeanConfigTest.java
@@ -1,12 +1,84 @@
 package de.ids_mannheim.korap.config;
 
+import static org.junit.Assert.assertNotNull;
+
+import org.apache.log4j.Logger;
+import org.junit.runner.RunWith;
+import org.junit.runner.notification.RunNotifier;
+import org.junit.runners.model.InitializationError;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.ApplicationContext;
+import org.springframework.test.annotation.DirtiesContext;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+import net.jcip.annotations.NotThreadSafe;
+
 /**
  * @author hanl
  * @date 09/03/2016
  */
-public abstract class BeanConfigTest extends BeanConfigBaseTest{
+@NotThreadSafe
+@RunWith(BeanConfigTest.SpringExtendedSetupListener.class)
+@ContextConfiguration("classpath:test-config.xml")
+@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS)
+public abstract class BeanConfigTest {
 
-    @Override
+    private static Logger jlog = Logger.getLogger(BeanConfigTest.class);
+    @Autowired
+    protected ApplicationContext context;
+
+    public void init () throws Exception {
+        assertNotNull("Application context must not be null!", this.context);
+        jlog.debug("running one-time before init for class "
+                + this.getClass().getSimpleName() + " ...");
+        BeansFactory.setKustvaktContext(getContext());
+        assertNotNull(BeansFactory.getKustvaktContext());
+        initMethod();
+    }
+
+    public abstract void initMethod () throws KustvaktException;
+
+    public void close () {
+        BeansFactory.closeApplication();
+    }
+
+    public static class SpringExtendedSetupListener
+            extends SpringJUnit4ClassRunner {
+
+        private BeanConfigTest instanceSetupListener;
+
+
+        public SpringExtendedSetupListener (Class<?> clazz)
+                throws InitializationError {
+            super(clazz);
+        }
+
+
+        @Override
+        protected Object createTest () throws Exception {
+            Object test = super.createTest();
+            // Note that JUnit4 will call this createTest() multiple times for each
+            // test method, so we need to ensure to call "beforeClassSetup" only once.
+            if (test instanceof BeanConfigTest
+                    && instanceSetupListener == null) {
+                instanceSetupListener = (BeanConfigTest) test;
+                instanceSetupListener.init();
+            }
+            return test;
+        }
+
+
+        @Override
+        public void run (RunNotifier notifier) {
+            super.run(notifier);
+            if (instanceSetupListener != null) {
+                instanceSetupListener.close();
+            }
+        }
+    }
+
     protected ContextHolder getContext () {
         return helper().getContext();
     }
@@ -19,5 +91,4 @@
             return null;
         }
     }
-
 }
diff --git a/full/src/test/java/de/ids_mannheim/korap/dao/RolePrivilegeDaoTest.java b/full/src/test/java/de/ids_mannheim/korap/dao/RolePrivilegeDaoTest.java
index d7317a6..a4d770b 100644
--- a/full/src/test/java/de/ids_mannheim/korap/dao/RolePrivilegeDaoTest.java
+++ b/full/src/test/java/de/ids_mannheim/korap/dao/RolePrivilegeDaoTest.java
@@ -49,21 +49,21 @@
 
     @Test
     public void updateRole () {
-        Role role = roleDao.retrieveRoleByName("group member");
-        roleDao.editRoleName(role.getId(), "group member role");
+        Role role = roleDao.retrieveRoleByName("USER_GROUP_MEMBER");
+        roleDao.editRoleName(role.getId(), "USER_GROUP_MEMBER role");
 
         role = roleDao.retrieveRoleById(role.getId());
-        assertEquals("group member role", role.getName());
+        assertEquals("USER_GROUP_MEMBER role", role.getName());
 
-        roleDao.editRoleName(role.getId(), "group member");
+        roleDao.editRoleName(role.getId(), "USER_GROUP_MEMBER");
         role = roleDao.retrieveRoleById(role.getId());
-        assertEquals("group member", role.getName());
+        assertEquals("USER_GROUP_MEMBER", role.getName());
     }
 
 
     @Test
     public void addDeletePrivilegeOfExistingRole () {
-        Role role = roleDao.retrieveRoleByName("group member");
+        Role role = roleDao.retrieveRoleByName("USER_GROUP_MEMBER");
         List<Privilege> privileges = role.getPrivileges();
         assertEquals(1, role.getPrivileges().size());
         assertEquals(privileges.get(0).getName(), PrivilegeType.DELETE);
@@ -73,13 +73,13 @@
         privilegeTypes.add(PrivilegeType.READ);
         privilegeDao.addPrivilegesToRole(role, privilegeTypes);
 
-        role = roleDao.retrieveRoleByName("group member");
+        role = roleDao.retrieveRoleByName("USER_GROUP_MEMBER");
         assertEquals(2, role.getPrivileges().size());
 
         //delete privilege
         privilegeDao.deletePrivilegeFromRole(role.getId(), PrivilegeType.READ);
 
-        role = roleDao.retrieveRoleByName("group member");
+        role = roleDao.retrieveRoleByName("USER_GROUP_MEMBER");
         assertEquals(1, role.getPrivileges().size());
         assertEquals(privileges.get(0).getName(), PrivilegeType.DELETE);
     }
diff --git a/full/src/test/java/de/ids_mannheim/korap/web/FastJerseyTest.java b/full/src/test/java/de/ids_mannheim/korap/web/FastJerseyTest.java
index 3ac9366..d576a26 100644
--- a/full/src/test/java/de/ids_mannheim/korap/web/FastJerseyTest.java
+++ b/full/src/test/java/de/ids_mannheim/korap/web/FastJerseyTest.java
@@ -22,11 +22,9 @@
 import com.sun.jersey.test.framework.spi.container.grizzly.GrizzlyTestContainerFactory;
 import com.sun.jersey.test.framework.spi.container.grizzly.web.GrizzlyWebTestContainerFactory;
 
-import de.ids_mannheim.korap.config.BeanConfigBaseTest;
-import de.ids_mannheim.korap.config.ContextHolder;
-import de.ids_mannheim.korap.config.TestHelper;
+import de.ids_mannheim.korap.config.BeanConfigTest;
 
-public abstract class FastJerseyTest extends BeanConfigBaseTest{
+public abstract class FastJerseyTest extends BeanConfigTest{
 
     private static String[] classPackages =
             new String[] { "de.ids_mannheim.korap.web.service.full",
@@ -114,20 +112,20 @@
         return client.resource(getBaseUri());
     }
     
-    protected TestHelper helper () {
-        try {
-            return TestHelper.newInstance(this.context);
-        }
-        catch (Exception e) {
-            return null;
-        }
-    }
-
-
-    @Override
-    protected ContextHolder getContext () {
-        return helper().getContext();
-    }
+//    protected TestHelper helper () {
+//        try {
+//            return TestHelper.newInstance(this.context);
+//        }
+//        catch (Exception e) {
+//            return null;
+//        }
+//    }
+//
+//
+//    @Override
+//    protected ContextHolder getContext () {
+//        return helper().getContext();
+//    }
 
 
     public static void startServer () {
diff --git a/full/src/test/java/de/ids_mannheim/korap/web/controller/SearchWithAvailabilityTest.java b/full/src/test/java/de/ids_mannheim/korap/web/controller/SearchWithAvailabilityTest.java
index 73e6880..02abd36 100644
--- a/full/src/test/java/de/ids_mannheim/korap/web/controller/SearchWithAvailabilityTest.java
+++ b/full/src/test/java/de/ids_mannheim/korap/web/controller/SearchWithAvailabilityTest.java
@@ -14,20 +14,14 @@
 
 import de.ids_mannheim.korap.authentication.http.HttpAuthorizationHandler;
 import de.ids_mannheim.korap.config.Attributes;
-import de.ids_mannheim.korap.config.TokenType;
+import de.ids_mannheim.korap.config.SpringJerseyTest;
 import de.ids_mannheim.korap.exceptions.KustvaktException;
 import de.ids_mannheim.korap.utils.JsonUtils;
-import de.ids_mannheim.korap.web.FastJerseyTest;
 
-public class SearchWithAvailabilityTest extends FastJerseyTest {
+public class SearchWithAvailabilityTest extends SpringJerseyTest {
     @Autowired
-    HttpAuthorizationHandler handler;
+    private HttpAuthorizationHandler handler;
     
-    @Override
-    public void initMethod () throws KustvaktException {
-        //        helper().runBootInterfaces();
-    }
-
     private void checkAndFree (String json) throws KustvaktException {
         JsonNode node = JsonUtils.readTree(json);
         assertEquals("availability",
diff --git a/full/src/test/java/de/ids_mannheim/korap/web/controller/StatisticsControllerTest.java b/full/src/test/java/de/ids_mannheim/korap/web/controller/StatisticsControllerTest.java
index b043d69..6fb0dd7 100644
--- a/full/src/test/java/de/ids_mannheim/korap/web/controller/StatisticsControllerTest.java
+++ b/full/src/test/java/de/ids_mannheim/korap/web/controller/StatisticsControllerTest.java
@@ -1,33 +1,28 @@
 package de.ids_mannheim.korap.web.controller;
 
+import static org.junit.Assert.assertEquals;
+
 import java.io.IOException;
 
 import org.junit.Test;
-import static org.junit.Assert.assertEquals;
 
 import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.sun.jersey.api.client.ClientResponse;
 
-import de.ids_mannheim.korap.exceptions.KustvaktException;
-import de.ids_mannheim.korap.web.FastJerseyTest;
+import de.ids_mannheim.korap.config.SpringJerseyTest;
 
 /** 
  * @author margaretha
  * @date 27/09/2017
  *
  */
-public class StatisticsControllerTest extends FastJerseyTest {
+public class StatisticsControllerTest extends SpringJerseyTest {
 
     private ObjectMapper mapper = new ObjectMapper();
 
 
-    @Override
-    public void initMethod () throws KustvaktException {
-
-    }
-
     @Test
     public void testGetStatisticsNoResource ()
             throws JsonProcessingException, IOException {
diff --git a/full/src/test/java/de/ids_mannheim/korap/web/controller/UserGroupControllerTest.java b/full/src/test/java/de/ids_mannheim/korap/web/controller/UserGroupControllerTest.java
index fea07fb..edafea8 100644
--- a/full/src/test/java/de/ids_mannheim/korap/web/controller/UserGroupControllerTest.java
+++ b/full/src/test/java/de/ids_mannheim/korap/web/controller/UserGroupControllerTest.java
@@ -2,8 +2,6 @@
 
 import static org.junit.Assert.assertEquals;
 
-import java.util.ArrayList;
-
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.MultivaluedMap;
 
@@ -12,13 +10,17 @@
 import org.springframework.beans.factory.annotation.Autowired;
 
 import com.fasterxml.jackson.databind.JsonNode;
+import com.sun.jersey.api.client.ClientHandlerException;
 import com.sun.jersey.api.client.ClientResponse;
 import com.sun.jersey.api.client.ClientResponse.Status;
+import com.sun.jersey.api.client.UniformInterfaceException;
 import com.sun.jersey.core.util.MultivaluedMapImpl;
 
 import de.ids_mannheim.korap.authentication.http.HttpAuthorizationHandler;
 import de.ids_mannheim.korap.config.Attributes;
 import de.ids_mannheim.korap.config.SpringJerseyTest;
+import de.ids_mannheim.korap.constant.GroupMemberStatus;
+import de.ids_mannheim.korap.constant.PredefinedRole;
 import de.ids_mannheim.korap.exceptions.KustvaktException;
 import de.ids_mannheim.korap.exceptions.StatusCodes;
 import de.ids_mannheim.korap.utils.JsonUtils;
@@ -28,10 +30,11 @@
 
     @Autowired
     private HttpAuthorizationHandler handler;
+    private String username = "UserGroupControllerTest";
 
     // dory is a group admin in dory group
     @Test
-    public void testRetrieveDoryGroups () throws KustvaktException {
+    public void testListDoryGroups () throws KustvaktException {
         ClientResponse response = resource().path("group").path("list")
                 .header(Attributes.AUTHORIZATION,
                         handler.createBasicAuthorizationHeaderValue("dory",
@@ -52,7 +55,7 @@
 
     // nemo is a group member in dory group
     @Test
-    public void testRetrieveNemoGroups () throws KustvaktException {
+    public void testListNemoGroups () throws KustvaktException {
         ClientResponse response = resource().path("group").path("list")
                 .header(Attributes.AUTHORIZATION,
                         handler.createBasicAuthorizationHeaderValue("nemo",
@@ -71,9 +74,9 @@
         assertEquals(0, node.at("/0/members").size());
     }
 
-    // marlin has a group
+    // marlin has 2 groups
     @Test
-    public void testRetrieveMarlinGroups () throws KustvaktException {
+    public void testListMarlinGroups () throws KustvaktException {
         ClientResponse response = resource().path("group").path("list")
                 .header(Attributes.AUTHORIZATION,
                         handler.createBasicAuthorizationHeaderValue("marlin",
@@ -83,12 +86,12 @@
         String entity = response.getEntity(String.class);
         assertEquals(Status.OK.getStatusCode(), response.getStatus());
         JsonNode node = JsonUtils.readTree(entity);
-        assertEquals(1, node.size());
+        assertEquals(2, node.size());
     }
 
 
     @Test
-    public void testRetrieveUserGroupUnauthorized () throws KustvaktException {
+    public void testListUserGroupUnauthorized () throws KustvaktException {
         ClientResponse response = resource().path("group").path("list")
                 .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
                 .get(ClientResponse.class);
@@ -99,37 +102,192 @@
         assertEquals(Status.UNAUTHORIZED.getStatusCode(), response.getStatus());
         assertEquals(StatusCodes.AUTHORIZATION_FAILED,
                 node.at("/errors/0/0").asInt());
-        assertEquals("Operation is not permitted for user: guest",
+        assertEquals("Unauthorized operation for user: guest",
                 node.at("/errors/0/1").asText());
     }
 
+    @Test
+    public void testCreateUserGroup () throws UniformInterfaceException,
+            ClientHandlerException, KustvaktException {
 
-//    @Test
-//    public void testInviteMember () {
-//
-//    }
-//
-//    @Test
-//    public void testInviteDeletedMember () {
-//
-//    }
-//    
-//    @Test
-//    public void testDeletePendingMember () {
-//
-//    }
+        UserGroupJson json = new UserGroupJson();
+        json.setName("new user group");
+        json.setMembers(new String[] { "marlin", "nemo" });
 
-    
+        ClientResponse response = resource().path("group").path("create")
+                .type(MediaType.APPLICATION_JSON)
+                .header(Attributes.AUTHORIZATION,
+                        handler.createBasicAuthorizationHeaderValue(username,
+                                "pass"))
+                .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32").entity(json)
+                .post(ClientResponse.class);
+
+        assertEquals(Status.OK.getStatusCode(), response.getStatus());
+
+        // list user group
+        response = resource().path("group").path("list")
+                .header(Attributes.AUTHORIZATION,
+                        handler.createBasicAuthorizationHeaderValue(username,
+                                "pass"))
+                .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
+                .get(ClientResponse.class);
+
+        String entity = response.getEntity(String.class);
+        //        System.out.println(entity);
+        JsonNode node = JsonUtils.readTree(entity);
+        assertEquals(1, node.size());
+        node = node.get(0);
+        assertEquals("new user group", node.get("name").asText());
+        String groupId = node.get("id").asText();
+
+        assertEquals(username, node.get("owner").asText());
+        assertEquals(3, node.get("members").size());
+        assertEquals(username, node.at("/members/0/userId").asText());
+        assertEquals(GroupMemberStatus.ACTIVE.name(),
+                node.at("/members/0/status").asText());
+        assertEquals(PredefinedRole.USER_GROUP_ADMIN.name(),
+                node.at("/members/0/roles/0").asText());
+        assertEquals(PredefinedRole.VC_ACCESS_ADMIN.name(),
+                node.at("/members/0/roles/1").asText());
+
+        assertEquals("marlin", node.at("/members/1/userId").asText());
+        assertEquals(GroupMemberStatus.PENDING.name(),
+                node.at("/members/1/status").asText());
+        assertEquals(PredefinedRole.USER_GROUP_MEMBER.name(),
+                node.at("/members/1/roles/0").asText());
+        assertEquals(PredefinedRole.VC_ACCESS_MEMBER.name(),
+                node.at("/members/1/roles/1").asText());
+
+        testDeleteGroupMemberUnauthorized(groupId);
+        testDeleteGroupMember(groupId);
+        testDeleteGroup(groupId);
+    }
+
+    private void testDeleteGroupMemberUnauthorized (String groupId)
+            throws UniformInterfaceException, ClientHandlerException,
+            KustvaktException {
+        // nemo is a group member
+        ClientResponse response = resource().path("group").path("member")
+                .path("delete").queryParam("memberId", "marlin")
+                .queryParam("groupId", groupId)
+                .header(Attributes.AUTHORIZATION,
+                        handler.createBasicAuthorizationHeaderValue("nemo",
+                                "pass"))
+                .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
+                .delete(ClientResponse.class);
+
+        String entity = response.getEntity(String.class);
+//        System.out.println(entity);
+        JsonNode node = JsonUtils.readTree(entity);
+        assertEquals(Status.UNAUTHORIZED.getStatusCode(), response.getStatus());
+        assertEquals(StatusCodes.AUTHORIZATION_FAILED,
+                node.at("/errors/0/0").asInt());
+        assertEquals("Unauthorized operation for user: nemo",
+                node.at("/errors/0/1").asText());
+    }
+
+    private void testDeleteGroupMember (String groupId)
+            throws UniformInterfaceException, ClientHandlerException,
+            KustvaktException {
+        // delete marlin from group
+        ClientResponse response = resource().path("group").path("member")
+                .path("delete").queryParam("memberId", "marlin")
+                .queryParam("groupId", groupId)
+                .header(Attributes.AUTHORIZATION,
+                        handler.createBasicAuthorizationHeaderValue(username,
+                                "pass"))
+                .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
+                .delete(ClientResponse.class);
+
+        // check group member
+        response = resource().path("group").path("list")
+                .header(Attributes.AUTHORIZATION,
+                        handler.createBasicAuthorizationHeaderValue(username,
+                                "pass"))
+                .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
+                .get(ClientResponse.class);
+        String entity = response.getEntity(String.class);
+        JsonNode node = JsonUtils.readTree(entity);
+        node = node.get(0);
+        assertEquals(2, node.get("members").size());
+        assertEquals("nemo", node.at("/members/1/userId").asText());
+        assertEquals(GroupMemberStatus.PENDING.name(),
+                node.at("/members/1/status").asText());
+
+    }
+
+    private void testDeleteGroup (String groupId)
+            throws UniformInterfaceException, ClientHandlerException,
+            KustvaktException {
+        //delete group
+        ClientResponse response = resource().path("group").path("delete")
+                .queryParam("groupId", groupId)
+                .header(Attributes.AUTHORIZATION,
+                        handler.createBasicAuthorizationHeaderValue(username,
+                                "pass"))
+                .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
+                .delete(ClientResponse.class);
+
+        assertEquals(Status.OK.getStatusCode(), response.getStatus());
+
+        // check group
+        response = resource().path("group").path("list")
+                .header(Attributes.AUTHORIZATION,
+                        handler.createBasicAuthorizationHeaderValue(username,
+                                "pass"))
+                .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
+                .get(ClientResponse.class);
+        String entity = response.getEntity(String.class);
+        assertEquals("[]", entity);
+    }
+
+    @Test
+    public void testDeleteGroupOwner () throws UniformInterfaceException,
+            ClientHandlerException, KustvaktException {
+        // delete marlin from marlin group
+        // dory is a VCA in marlin group
+        ClientResponse response = resource().path("group").path("member")
+                .path("delete").queryParam("memberId", "marlin")
+                .queryParam("groupId", "1")
+                .header(Attributes.AUTHORIZATION,
+                        handler.createBasicAuthorizationHeaderValue("dory",
+                                "pass"))
+                .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
+                .delete(ClientResponse.class);
+
+        String entity = response.getEntity(String.class);
+        //        System.out.println(entity);
+        JsonNode node = JsonUtils.readTree(entity);
+        assertEquals(Status.BAD_REQUEST.getStatusCode(), response.getStatus());
+        assertEquals(StatusCodes.NOT_ALLOWED, node.at("/errors/0/0").asInt());
+        assertEquals("Operation 'delete group owner'is not allowed.",
+                node.at("/errors/0/1").asText());
+
+    }
+
+    //    @Test
+    //    public void testInviteMember () {
+    //
+    //    }
+    //
+    //    @Test
+    //    public void testInviteDeletedMember () {
+    //
+    //    }
+    //    
+    //    @Test
+    //    public void testDeletePendingMember () {
+    //
+    //    }
+
+
     // marlin has GroupMemberStatus.PENDING in dory group
     @Test
-    public void testSubscribeUnsubscribeMarlinToDoryGroup ()
-            throws KustvaktException {
+    public void testSubscribeMarlinToDoryGroup () throws KustvaktException {
         MultivaluedMap<String, String> form = new MultivaluedMapImpl();
         form.add("groupId", "2");
 
-        ClientResponse response;
-        String entity;
-        response = resource().path("group").path("subscribe")
+        ClientResponse response = resource().path("group").path("subscribe")
                 .type(MediaType.APPLICATION_FORM_URLENCODED)
                 .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
                 .header(Attributes.AUTHORIZATION,
@@ -140,71 +298,84 @@
         assertEquals(Status.OK.getStatusCode(), response.getStatus());
 
         // retrieve marlin group
-        response = resource().path("group").path("list")
-                .header(Attributes.AUTHORIZATION,
-                        handler.createBasicAuthorizationHeaderValue("marlin",
-                                "pass"))
-                .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
-                .get(ClientResponse.class);
-        entity = response.getEntity(String.class);
-
-        assertEquals(Status.OK.getStatusCode(), response.getStatus());
-
-        JsonNode node = JsonUtils.readTree(entity);
+        JsonNode node = retrieveMarlinGroups();
+        System.out.println(node);
         assertEquals(2, node.size());
 
-        JsonNode group;
-        for (int i = 0; i < node.size(); i++) {
-            group = node.get(i);
-            if (group.at("/id").asInt() == 2) {
-                assertEquals("dory group", group.at("/name").asText());
-                assertEquals("dory", group.at("/owner").asText());
-                // group members are not allowed to see other members
-                assertEquals(0, group.at("/members").size());
-            }
-        }
+        JsonNode group = node.get(1);
+        assertEquals(2, group.at("/id").asInt());
+        assertEquals("dory group", group.at("/name").asText());
+        assertEquals("dory", group.at("/owner").asText());
+        // group members are not allowed to see other members
+        assertEquals(0, group.at("/members").size());
+        assertEquals(GroupMemberStatus.ACTIVE.name(),
+                group.at("/userMemberStatus").asText());
 
-        // unsubscribe
-        response = resource().path("group").path("unsubscribe")
+        // unsubscribe marlin from dory group
+        testUnsubscribe(form);
+
+        // invite marlin to dory group to set back the GroupMemberStatus.PENDING
+        testInviteMember();
+    }
+
+    private JsonNode retrieveMarlinGroups () throws UniformInterfaceException,
+            ClientHandlerException, KustvaktException {
+        ClientResponse response = resource().path("group").path("list")
+                .header(Attributes.AUTHORIZATION,
+                        handler.createBasicAuthorizationHeaderValue("marlin",
+                                "pass"))
+                .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
+                .get(ClientResponse.class);
+        String entity = response.getEntity(String.class);
+
+        assertEquals(Status.OK.getStatusCode(), response.getStatus());
+
+        return JsonUtils.readTree(entity);
+    }
+
+    private void testUnsubscribe (MultivaluedMap<String, String> form)
+            throws UniformInterfaceException, ClientHandlerException,
+            KustvaktException {
+        ClientResponse response = resource().path("group").path("unsubscribe")
                 .type(MediaType.APPLICATION_FORM_URLENCODED)
                 .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
                 .header(Attributes.AUTHORIZATION,
                         handler.createBasicAuthorizationHeaderValue("marlin",
                                 "pass"))
                 .entity(form).post(ClientResponse.class);
-        entity = response.getEntity(String.class);
-
-        // retrieve marlin group
-        response = resource().path("group").path("list")
-                .header(Attributes.AUTHORIZATION,
-                        handler.createBasicAuthorizationHeaderValue("marlin",
-                                "pass"))
-                .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
-                .get(ClientResponse.class);
-        entity = response.getEntity(String.class);
 
         assertEquals(Status.OK.getStatusCode(), response.getStatus());
 
-        node = JsonUtils.readTree(entity);
+        JsonNode node = retrieveMarlinGroups();
         assertEquals(1, node.size());
+    }
 
-        // add marlin to dory group again to set back the GroupMemberStatus.PENDING
-        ArrayList<String> members = new ArrayList<String>();
-        members.add("marlin");
+    private void testInviteMember () throws UniformInterfaceException,
+            ClientHandlerException, KustvaktException {
+        String[] members = new String[] { "marlin" };
 
         UserGroupJson userGroup = new UserGroupJson();
         userGroup.setMembers(members);
         // dory group
         userGroup.setId(2);
 
-        response = resource().path("group").path("add")
-                .type(MediaType.APPLICATION_JSON)
+        ClientResponse response = resource().path("group").path("member")
+                .path("invite").type(MediaType.APPLICATION_JSON)
                 .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
                 .header(Attributes.AUTHORIZATION,
                         handler.createBasicAuthorizationHeaderValue("dory",
                                 "pass"))
                 .entity(userGroup).post(ClientResponse.class);
-        entity = response.getEntity(String.class);
+
+        assertEquals(Status.OK.getStatusCode(), response.getStatus());
+
+        // check member
+        JsonNode node = retrieveMarlinGroups();
+        assertEquals(2, node.size());
+        JsonNode group = node.get(1);
+        assertEquals(GroupMemberStatus.PENDING.name(),
+                group.at("/userMemberStatus").asText());
+
     }
 
     // pearl has GroupMemberStatus.DELETED in dory group
diff --git a/full/src/test/java/de/ids_mannheim/korap/web/controller/VirtualCorpusControllerTest.java b/full/src/test/java/de/ids_mannheim/korap/web/controller/VirtualCorpusControllerTest.java
index 2b68543..6207cb2 100644
--- a/full/src/test/java/de/ids_mannheim/korap/web/controller/VirtualCorpusControllerTest.java
+++ b/full/src/test/java/de/ids_mannheim/korap/web/controller/VirtualCorpusControllerTest.java
@@ -9,15 +9,13 @@
 import java.io.InputStreamReader;
 import java.util.List;
 import java.util.Map.Entry;
+import java.util.Set;
 
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.MultivaluedMap;
 
-import java.util.Set;
-
 import org.apache.http.entity.ContentType;
 import org.eclipse.jetty.http.HttpHeaders;
-import org.junit.Ignore;
 import org.junit.Test;
 import org.springframework.beans.factory.annotation.Autowired;
 
@@ -25,8 +23,8 @@
 import com.sun.jersey.api.client.ClientHandlerException;
 import com.sun.jersey.api.client.ClientResponse;
 import com.sun.jersey.api.client.ClientResponse.Status;
-import com.sun.jersey.core.util.MultivaluedMapImpl;
 import com.sun.jersey.api.client.UniformInterfaceException;
+import com.sun.jersey.core.util.MultivaluedMapImpl;
 import com.sun.jersey.spi.container.ContainerRequest;
 
 import de.ids_mannheim.korap.authentication.http.HttpAuthorizationHandler;
@@ -243,7 +241,7 @@
         assertEquals(Status.UNAUTHORIZED.getStatusCode(), response.getStatus());
         assertEquals(StatusCodes.AUTHORIZATION_FAILED,
                 node.at("/errors/0/0").asInt());
-        assertEquals("Operation is not permitted for user: guest",
+        assertEquals("Unauthorized operation for user: guest",
                 node.at("/errors/0/1").asText());
 
         checkWWWAuthenticateHeader(response);
@@ -276,7 +274,7 @@
                 .get(ClientResponse.class);
         entity = response.getEntity(String.class);
         assertEquals(Status.OK.getStatusCode(), response.getStatus());
-                System.out.println(entity);
+//                System.out.println(entity);
         JsonNode node = JsonUtils.readTree(entity);
         assertEquals(2, node.size());
         assertEquals("new vc", node.get(1).get("name").asText());
@@ -402,7 +400,7 @@
         JsonNode node = JsonUtils.readTree(entity);
         assertEquals(StatusCodes.AUTHORIZATION_FAILED,
                 node.at("/errors/0/0").asInt());
-        assertEquals("Operation is not permitted for user: guest",
+        assertEquals("Unauthorized operation for user: guest",
                 node.at("/errors/0/1").asText());
 
         checkWWWAuthenticateHeader(response);
@@ -663,10 +661,9 @@
                         handler.createBasicAuthorizationHeaderValue("dory",
                                 "pass"))
                 .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
-                .header(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON)
                 .get(ClientResponse.class);
         String entity = response.getEntity(String.class);
-        //        System.out.println(entity);
+//                System.out.println(entity);
         JsonNode node = JsonUtils.readTree(entity);
         assertEquals(1, node.at("/0/accessId").asInt());
         assertEquals(2, node.at("/0/vcId").asInt());
@@ -684,7 +681,6 @@
                         handler.createBasicAuthorizationHeaderValue("nemo",
                                 "pass"))
                 .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
-                .header(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON)
                 .get(ClientResponse.class);
         String entity = response.getEntity(String.class);
         assertEquals("[]", entity);
@@ -698,8 +694,6 @@
                                 handler.createBasicAuthorizationHeaderValue(
                                         "VirtualCorpusControllerTest", "pass"))
                         .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
-                        .header(HttpHeaders.CONTENT_TYPE,
-                                ContentType.APPLICATION_JSON)
                         .get(ClientResponse.class);
         String entity = response.getEntity(String.class);
         JsonNode node = JsonUtils.readTree(entity);
@@ -717,7 +711,6 @@
                         handler.createBasicAuthorizationHeaderValue("dory",
                                 "pass"))
                 .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
-                .header(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON)
                 .get(ClientResponse.class);
         String entity = response.getEntity(String.class);
         //        System.out.println(entity);
@@ -727,7 +720,7 @@
         assertEquals("group VC", node.at("/0/vcName").asText());
         assertEquals(2, node.at("/0/userGroupId").asInt());
         assertEquals("dory group", node.at("/0/userGroupName").asText());
-    }
+    }   
 
 
     @Test