Use admin filter instead of OAuth2 ADMIN scope

Change-Id: I11717a2ef898f25b14f1a490d4122e7076363d15
diff --git a/full/Changes b/full/Changes
index e4c7a6c..04f388e 100644
--- a/full/Changes
+++ b/full/Changes
@@ -13,6 +13,9 @@
 - Fixed content-type in error responses by changing it to application/json
 2023-02-06
 - Allow admin access using admin token for the clean token API
+2023-02-10
+- Use admin filter instead of OAuth2 ADMIN scope  
+
 
 # version 0.69.1
 
diff --git a/full/src/main/java/de/ids_mannheim/korap/service/QueryService.java b/full/src/main/java/de/ids_mannheim/korap/service/QueryService.java
index c3081ee..981cb41 100644
--- a/full/src/main/java/de/ids_mannheim/korap/service/QueryService.java
+++ b/full/src/main/java/de/ids_mannheim/korap/service/QueryService.java
@@ -134,21 +134,14 @@
         return createQueryDtos(list, queryType);
     }
 
-    public List<QueryDto> listQueryByType (String username, String createdBy,
-            ResourceType type, QueryType queryType) throws KustvaktException {
+    public List<QueryDto> listQueryByType (String createdBy, ResourceType type,
+            QueryType queryType) throws KustvaktException {
 
-        boolean isAdmin = adminDao.isAdmin(username);
+        List<QueryDO> virtualCorpora =
+                queryDao.retrieveQueryByType(type, createdBy, queryType);
+        Collections.sort(virtualCorpora);
+        return createQueryDtos(virtualCorpora, queryType);
 
-        if (isAdmin) {
-            List<QueryDO> virtualCorpora =
-                    queryDao.retrieveQueryByType(type, createdBy, queryType);
-            Collections.sort(virtualCorpora);
-            return createQueryDtos(virtualCorpora, queryType);
-        }
-        else {
-            throw new KustvaktException(StatusCodes.AUTHORIZATION_FAILED,
-                    "Unauthorized operation for user: " + username, username);
-        }
     }
 
     private ArrayList<QueryDto> createQueryDtos (List<QueryDO> queryList,
@@ -631,10 +624,10 @@
         
         ParameterChecker.checkStringValue(fieldName, "fieldName");
         
-        if (!adminDao.isAdmin(username)) {
-            throw new KustvaktException(StatusCodes.AUTHORIZATION_FAILED,
-                    "Unauthorized operation for user: " + username, username);
-        }
+//        if (!adminDao.isAdmin(username)) {
+//            throw new KustvaktException(StatusCodes.AUTHORIZATION_FAILED,
+//                    "Unauthorized operation for user: " + username, username);
+//        }
         
         if (fieldName.equals("tokens") || fieldName.equals("base")) {
             throw new KustvaktException(StatusCodes.NOT_ALLOWED,
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 47d2c0c..51aa1c0 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
@@ -144,33 +144,22 @@
     }
 
     public List<UserGroupDto> retrieveUserGroupByStatus (String username,
-            String contextUsername, UserGroupStatus status)
-            throws KustvaktException {
+            UserGroupStatus status) throws KustvaktException {
 
-        boolean isAdmin = adminDao.isAdmin(contextUsername);
+        List<UserGroup> userGroups =
+                userGroupDao.retrieveGroupByStatus(username, status);
+        Collections.sort(userGroups);
+        ArrayList<UserGroupDto> dtos = new ArrayList<>(userGroups.size());
 
-        if (isAdmin) {
-            List<UserGroup> userGroups =
-                    userGroupDao.retrieveGroupByStatus(username, status);
-            Collections.sort(userGroups);
-            ArrayList<UserGroupDto> dtos = new ArrayList<>(userGroups.size());
-
-            List<UserGroupMember> members;
-            UserGroupDto groupDto;
-            for (UserGroup group : userGroups) {
-                members = groupMemberDao.retrieveMemberByGroupId(group.getId(),
-                        true);
-                groupDto = converter.createUserGroupDto(group, members, null,
-                        null);
-                dtos.add(groupDto);
-            }
-            return dtos;
+        List<UserGroupMember> members;
+        UserGroupDto groupDto;
+        for (UserGroup group : userGroups) {
+            members =
+                    groupMemberDao.retrieveMemberByGroupId(group.getId(), true);
+            groupDto = converter.createUserGroupDto(group, members, null, null);
+            dtos.add(groupDto);
         }
-        else {
-            throw new KustvaktException(StatusCodes.AUTHORIZATION_FAILED,
-                    "Unauthorized operation for user: " + contextUsername,
-                    contextUsername);
-        }
+        return dtos;
     }
 
     public List<UserGroupMember> retrieveQueryAccessAdmins (UserGroup userGroup)
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/controller/UserGroupAdminController.java b/full/src/main/java/de/ids_mannheim/korap/web/controller/UserGroupAdminController.java
new file mode 100644
index 0000000..c881cb9
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/web/controller/UserGroupAdminController.java
@@ -0,0 +1,64 @@
+package de.ids_mannheim.korap.web.controller;
+
+import java.util.List;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.FormParam;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Controller;
+
+import de.ids_mannheim.korap.constant.UserGroupStatus;
+import de.ids_mannheim.korap.dto.UserGroupDto;
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.service.UserGroupService;
+import de.ids_mannheim.korap.web.KustvaktResponseHandler;
+import de.ids_mannheim.korap.web.filter.APIVersionFilter;
+import de.ids_mannheim.korap.web.filter.AdminFilter;
+import de.ids_mannheim.korap.web.utils.ResourceFilters;
+
+@Controller
+@Path("{version}/group/admin")
+@Produces(MediaType.APPLICATION_JSON + ";charset=utf-8")
+@ResourceFilters({ APIVersionFilter.class, AdminFilter.class })
+public class UserGroupAdminController {
+
+    @Autowired
+    private KustvaktResponseHandler kustvaktResponseHandler;
+    @Autowired
+    private UserGroupService service;
+    
+    /**
+     * Lists user-groups for system-admin purposes. If username is
+     * specified, lists user-groups of the given user, otherwise list
+     * user-groups of all users. If status specified, list only
+     * user-groups with the given status, otherwise list user-groups
+     * regardless of their status.
+     * 
+     * @param securityContext
+     * @param username
+     *            a username
+     * @param status
+     *            {@link UserGroupStatus}
+     * @return a list of user-groups
+     */
+    @POST
+    @Path("list")
+    @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
+    public List<UserGroupDto> listUserGroupBySystemAdmin (
+            @FormParam("username") String username,
+            @FormParam("status") UserGroupStatus status) {
+        try {
+            return service.retrieveUserGroupByStatus(username,
+                    status);
+        }
+        catch (KustvaktException e) {
+            throw kustvaktResponseHandler.throwit(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 2d053fa..cf16395 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
@@ -11,7 +11,6 @@
 import javax.ws.rs.Path;
 import javax.ws.rs.PathParam;
 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;
@@ -21,8 +20,6 @@
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Controller;
 
-import de.ids_mannheim.korap.web.utils.ResourceFilters;
-
 import de.ids_mannheim.korap.constant.OAuth2Scope;
 import de.ids_mannheim.korap.constant.UserGroupStatus;
 import de.ids_mannheim.korap.dto.UserGroupDto;
@@ -32,9 +29,11 @@
 import de.ids_mannheim.korap.service.UserGroupService;
 import de.ids_mannheim.korap.web.KustvaktResponseHandler;
 import de.ids_mannheim.korap.web.filter.APIVersionFilter;
+import de.ids_mannheim.korap.web.filter.AdminFilter;
 import de.ids_mannheim.korap.web.filter.AuthenticationFilter;
 import de.ids_mannheim.korap.web.filter.BlockingFilter;
 import de.ids_mannheim.korap.web.filter.PiwikFilter;
+import de.ids_mannheim.korap.web.utils.ResourceFilters;
 
 /**
  * UserGroupController defines web APIs related to user groups,
@@ -88,38 +87,7 @@
         }
     }
 
-    /**
-     * Lists user-groups for system-admin purposes. If username is
-     * specified, lists user-groups of the given user, otherwise list
-     * user-groups of all users. If status specified, list only
-     * user-groups with the given status, otherwise list user-groups
-     * regardless of their status.
-     * 
-     * @param securityContext
-     * @param username
-     *            a username
-     * @param status
-     *            {@link UserGroupStatus}
-     * @return a list of user-groups
-     */
-    @GET
-    @Path("list/system-admin")
-    public List<UserGroupDto> getUserGroupBySystemAdmin (
-            @Context SecurityContext securityContext,
-            @QueryParam("username") String username,
-            @QueryParam("status") UserGroupStatus status) {
-        TokenContext context =
-                (TokenContext) securityContext.getUserPrincipal();
-        try {
-            scopeService.verifyScope(context, OAuth2Scope.ADMIN);
-            return service.retrieveUserGroupByStatus(username,
-                    context.getUsername(), status);
-        }
-        catch (KustvaktException e) {
-            throw kustvaktResponseHandler.throwit(e);
-        }
-    }
-
+    
     /**
      * Retrieves a specific user-group. Only system admins are
      * allowed.
@@ -131,13 +99,13 @@
      */
     @GET
     @Path("@{groupName}")
+    @ResourceFilters({ APIVersionFilter.class, AdminFilter.class })
     public UserGroupDto retrieveUserGroup (
             @Context SecurityContext securityContext,
             @PathParam("groupName") String groupName) {
         TokenContext context =
                 (TokenContext) securityContext.getUserPrincipal();
         try {
-            scopeService.verifyScope(context, OAuth2Scope.ADMIN);
             return service.searchByName(context.getUsername(), groupName);
         }
         catch (KustvaktException e) {
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/controller/VirtualCorpusAdminController.java b/full/src/main/java/de/ids_mannheim/korap/web/controller/VirtualCorpusAdminController.java
new file mode 100644
index 0000000..d9fb208
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/web/controller/VirtualCorpusAdminController.java
@@ -0,0 +1,65 @@
+package de.ids_mannheim.korap.web.controller;
+
+import java.util.List;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.FormParam;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Controller;
+
+import de.ids_mannheim.korap.constant.QueryType;
+import de.ids_mannheim.korap.constant.ResourceType;
+import de.ids_mannheim.korap.dto.QueryDto;
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.service.QueryService;
+import de.ids_mannheim.korap.web.KustvaktResponseHandler;
+import de.ids_mannheim.korap.web.filter.APIVersionFilter;
+import de.ids_mannheim.korap.web.filter.AdminFilter;
+import de.ids_mannheim.korap.web.utils.ResourceFilters;
+
+@Controller
+@Path("{version}/vc/admin")
+@Produces(MediaType.APPLICATION_JSON + ";charset=utf-8")
+@ResourceFilters({ APIVersionFilter.class, AdminFilter.class })
+public class VirtualCorpusAdminController {
+
+    @Autowired
+    private KustvaktResponseHandler kustvaktResponseHandler;
+    @Autowired
+    private QueryService service;
+    
+    /**
+     * Lists virtual corpora by creator and type. This is a controller
+     * for system admin requiring valid system admin authentication.
+     * 
+     * If type is not specified, retrieves virtual corpora of all
+     * types. If createdBy is not specified, retrieves virtual corpora
+     * of all users.
+     * 
+     * @param securityContext
+     * @param createdBy
+     *            username of virtual corpus creator (optional)
+     * @param type
+     *            {@link ResourceType}
+     * @return a list of virtual corpora
+     */
+    @POST
+    @Path("list")
+    @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
+    public List<QueryDto> listVCByType (
+            @FormParam("createdBy") String createdBy,
+            @FormParam("type") ResourceType type) {
+        try {
+            return service.listQueryByType(createdBy, type,
+                    QueryType.VIRTUAL_CORPUS);
+        }
+        catch (KustvaktException e) {
+            throw kustvaktResponseHandler.throwit(e);
+        }
+    }
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/controller/VirtualCorpusController.java b/full/src/main/java/de/ids_mannheim/korap/web/controller/VirtualCorpusController.java
index 19e4b2b..20be6dc 100644
--- a/full/src/main/java/de/ids_mannheim/korap/web/controller/VirtualCorpusController.java
+++ b/full/src/main/java/de/ids_mannheim/korap/web/controller/VirtualCorpusController.java
@@ -21,11 +21,9 @@
 import org.springframework.stereotype.Controller;
 
 import com.fasterxml.jackson.databind.JsonNode;
-import de.ids_mannheim.korap.web.utils.ResourceFilters;
 
 import de.ids_mannheim.korap.constant.OAuth2Scope;
 import de.ids_mannheim.korap.constant.QueryType;
-import de.ids_mannheim.korap.constant.ResourceType;
 import de.ids_mannheim.korap.dto.QueryAccessDto;
 import de.ids_mannheim.korap.dto.QueryDto;
 import de.ids_mannheim.korap.exceptions.KustvaktException;
@@ -35,11 +33,13 @@
 import de.ids_mannheim.korap.utils.ParameterChecker;
 import de.ids_mannheim.korap.web.KustvaktResponseHandler;
 import de.ids_mannheim.korap.web.filter.APIVersionFilter;
+import de.ids_mannheim.korap.web.filter.AdminFilter;
 import de.ids_mannheim.korap.web.filter.AuthenticationFilter;
 import de.ids_mannheim.korap.web.filter.BlockingFilter;
 import de.ids_mannheim.korap.web.filter.DemoUserFilter;
 import de.ids_mannheim.korap.web.filter.PiwikFilter;
 import de.ids_mannheim.korap.web.input.QueryJson;
+import de.ids_mannheim.korap.web.utils.ResourceFilters;
 
 /**
  * VirtualCorpusController defines web APIs related to virtual corpus
@@ -81,8 +81,8 @@
      * VC name cannot be updated.
      * 
      * The VC creator must be the same as the authenticated username,
-     * except for admins. Admins can create or update system VC as
-     * well as VC for any users.
+     * except for system admins. System admins can create or update 
+     * system VC and any VC for any users.
      * 
      * 
      * @param securityContext
@@ -127,6 +127,8 @@
      * Returns the virtual corpus with the given name and creator.
      * This web-service is also available for guests.
      * 
+     * System admin can retrieve private or project vc of any users.
+     * 
      * @param securityContext
      * @param createdBy
      *            vc creator
@@ -172,8 +174,19 @@
         }
     }
     
+    /** Retrieves field values of a virtual corpus, e.g. corpus sigle.
+     * 
+     * This service is restricted to system admin only.
+     * 
+     * @param securityContext
+     * @param createdBy
+     * @param vcName
+     * @param fieldName
+     * @return
+     */
     @GET
     @Path("/field/~{createdBy}/{vcName}")
+    @ResourceFilters({ APIVersionFilter.class, AdminFilter.class })
     public JsonNode retrieveVCField (
             @Context SecurityContext securityContext,
             @PathParam("createdBy") String createdBy,
@@ -182,7 +195,6 @@
         TokenContext context =
                 (TokenContext) securityContext.getUserPrincipal();
         try {
-            scopeService.verifyScope(context, OAuth2Scope.ADMIN);
             return service.retrieveFieldValues(context.getUsername(), vcName,
                     createdBy, QueryType.VIRTUAL_CORPUS, fieldName);
         }
@@ -263,40 +275,7 @@
         }
     }
     
-    /**
-     * Lists virtual corpora by creator and type. This is a controller
-     * for system admin requiring valid system admin authentication.
-     * 
-     * If type is not specified, retrieves virtual corpora of all
-     * types. If createdBy is not specified, retrieves virtual corpora
-     * of all users.
-     * 
-     * @param securityContext
-     * @param createdBy
-     *            username of virtual corpus creator (optional)
-     * @param type
-     *            {@link ResourceType}
-     * @return a list of virtual corpora
-     */
-    @GET
-    @Path("list/system-admin")
-    public List<QueryDto> listVCByType (
-            @Context SecurityContext securityContext,
-            @QueryParam("createdBy") String createdBy,
-            @QueryParam("type") ResourceType type) {
-        TokenContext context =
-                (TokenContext) securityContext.getUserPrincipal();
-        try {
-            scopeService.verifyScope(context, OAuth2Scope.ADMIN);
-            return service.listQueryByType(context.getUsername(), createdBy, type,
-                    QueryType.VIRTUAL_CORPUS);
-        }
-        catch (KustvaktException e) {
-            throw kustvaktResponseHandler.throwit(e);
-        }
-    }
-
-
+   
     /**
      * Only the VC owner and system admins can delete VC. VCA admins
      * can delete VC-accesses e.g. of project VC, but not the VC
diff --git a/full/src/test/java/de/ids_mannheim/korap/web/controller/UserGroupControllerAdminTest.java b/full/src/test/java/de/ids_mannheim/korap/web/controller/UserGroupControllerAdminTest.java
index 07db0a2..354f6f9 100644
--- a/full/src/test/java/de/ids_mannheim/korap/web/controller/UserGroupControllerAdminTest.java
+++ b/full/src/test/java/de/ids_mannheim/korap/web/controller/UserGroupControllerAdminTest.java
@@ -3,6 +3,7 @@
 import static org.junit.Assert.assertEquals;
 
 import javax.ws.rs.core.Form;
+import javax.ws.rs.core.MediaType;
 
 import org.junit.Test;
 
@@ -19,6 +20,8 @@
 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.service.UserGroupService;
 import de.ids_mannheim.korap.utils.JsonUtils;
 
 /**
@@ -27,8 +30,8 @@
  */
 public class UserGroupControllerAdminTest extends SpringJerseyTest {
 
-    private String adminUser = "admin";
-    private String testUser = "UserGroupControllerAdminTest";
+    private String sysAdminUser = "admin";
+    private String testUser = "group-admin";
 
     private JsonNode listGroup (String username)
             throws ProcessingException,
@@ -47,33 +50,81 @@
     }
 
     @Test
-    public void testListDoryGroups () throws KustvaktException {
+    public void testListUserGroupsUsingAdminToken () throws KustvaktException {
+        Form f = new Form();
+        f.param("username", "dory");
+        f.param("token", "secret");
+        
         Response response = target().path(API_VERSION).path("group")
-                .path("list").path("system-admin")
-                .queryParam("username", "dory")
+                .path("admin").path("list")
                 .request()
-                .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
-                        .createBasicAuthorizationHeaderValue(adminUser, "pass"))
-                .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
-                .get();
+                .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED)
+                .post(Entity.form(f));
 
         assertEquals(Status.OK.getStatusCode(), response.getStatus());
         String entity = response.readEntity(String.class);
-        // System.out.println(entity);
         JsonNode node = JsonUtils.readTree(entity);
         assertEquals(3, node.size());
     }
+    
+    
+    /**
+     * Cannot use admin token
+     * see
+     * {@link UserGroupService#retrieveUserGroupByStatus(String, 
+     * String, de.ids_mannheim.korap.constant.UserGroupStatus)}
+     * 
+     * @throws KustvaktException
+     */
+//    @Test
+//    public void testListUserGroupsWithAdminToken () throws KustvaktException {
+//        Response response = target().path(API_VERSION).path("group")
+//                .path("list").path("system-admin")
+//                .queryParam("username", "dory")
+//                .queryParam("token", "secret")
+//                .request()
+//                .get();
+//
+//        assertEquals(Status.OK.getStatusCode(), response.getStatus());
+//        String entity = response.readEntity(String.class);
+//        JsonNode node = JsonUtils.readTree(entity);
+//        assertEquals(3, node.size());
+//    }
+    
+    
+    @Test
+    public void testListUserGroupsUnauthorized () throws KustvaktException {
+        Form f = new Form();
+        f.param("username", "dory");
+        
+        Response response = target().path(API_VERSION).path("group")
+                .path("admin").path("list")
+                .request()
+                .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
+                        .createBasicAuthorizationHeaderValue(testUser, "pass"))
+                .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED)
+                .post(Entity.form(f));
+
+        assertEquals(Status.UNAUTHORIZED.getStatusCode(), response.getStatus());
+        String entity = response.readEntity(String.class);
+        JsonNode node = JsonUtils.readTree(entity);
+        assertEquals(StatusCodes.AUTHORIZATION_FAILED, node.at("/errors/0/0").asInt());
+    }
 
     @Test
-    public void testListDoryActiveGroups () throws KustvaktException {
+    public void testListUserGroupsWithStatus () throws KustvaktException {
+        Form f = new Form();
+        f.param("username", "dory");
+        f.param("status", "ACTIVE");
+        
         Response response = target().path(API_VERSION).path("group")
-                .path("list").path("system-admin")
+                .path("admin").path("list")
                 .queryParam("username", "dory").queryParam("status", "ACTIVE")
                 .request()
                 .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
-                        .createBasicAuthorizationHeaderValue(adminUser, "pass"))
-                .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
-                .get();
+                        .createBasicAuthorizationHeaderValue(sysAdminUser, "pass"))
+                .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED)
+                .post(Entity.form(f));
 
         assertEquals(Status.OK.getStatusCode(), response.getStatus());
         String entity = response.readEntity(String.class);
@@ -89,7 +140,7 @@
         Response response = target().path(API_VERSION).path("group")
                 .request()
                 .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
-                        .createBasicAuthorizationHeaderValue(adminUser, "pass"))
+                        .createBasicAuthorizationHeaderValue(sysAdminUser, "pass"))
                 .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
                 .get();
 
@@ -102,12 +153,12 @@
     public void testListByStatusAll () throws
             ProcessingException, KustvaktException {
         Response response = target().path(API_VERSION).path("group")
-                .path("list").path("system-admin")
+                .path("admin").path("list")
                 .request()
                 .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
-                        .createBasicAuthorizationHeaderValue(adminUser, "pass"))
-                .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
-                .get();
+                        .createBasicAuthorizationHeaderValue(sysAdminUser, "pass"))
+                .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED)
+                .post(null);
 
         assertEquals(Status.OK.getStatusCode(), response.getStatus());
         String entity = response.readEntity(String.class);
@@ -125,14 +176,18 @@
     @Test
     public void testListByStatusHidden () throws
             ProcessingException, KustvaktException {
+        Form f = new Form();
+        f.param("status", "HIDDEN");
+        
         Response response = target().path(API_VERSION).path("group")
-                .path("list").path("system-admin")
+                .path("admin").path("list")
                 .queryParam("status", "HIDDEN")
                 .request()
                 .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
-                        .createBasicAuthorizationHeaderValue(adminUser, "pass"))
+                        .createBasicAuthorizationHeaderValue(sysAdminUser, "pass"))
                 .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
-                .get();
+                .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED)
+                .post(Entity.form(f));
 
         assertEquals(Status.OK.getStatusCode(), response.getStatus());
         String entity = response.readEntity(String.class);
@@ -142,7 +197,7 @@
     }
 
     @Test
-    public void testUserGroup () throws
+    public void testUserGroupAdmin () throws
             ProcessingException, KustvaktException {
 
         String groupName = "admin-test-group";
@@ -205,7 +260,7 @@
                 .request()
                 .header(Attributes.AUTHORIZATION,
                         HttpAuthorizationHandler
-                                .createBasicAuthorizationHeaderValue(adminUser,
+                                .createBasicAuthorizationHeaderValue(sysAdminUser,
                                         "password"))
                 .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
                 .post(Entity.form(form));
@@ -237,7 +292,7 @@
                 .request()
                 .header(Attributes.AUTHORIZATION,
                         HttpAuthorizationHandler
-                                .createBasicAuthorizationHeaderValue(adminUser,
+                                .createBasicAuthorizationHeaderValue(sysAdminUser,
                                         "password"))
                 .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
                 .post(Entity.form(form));
@@ -262,7 +317,7 @@
                 .path("@" + groupName)
                 .request()
                 .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
-                        .createBasicAuthorizationHeaderValue(adminUser, "pass"))
+                        .createBasicAuthorizationHeaderValue(sysAdminUser, "pass"))
                 .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
                 .get();
 
@@ -281,7 +336,7 @@
                 .path("@" + groupName)
                 .request()
                 .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
-                        .createBasicAuthorizationHeaderValue(adminUser, "pass"))
+                        .createBasicAuthorizationHeaderValue(sysAdminUser, "pass"))
                 .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
                 .delete();
 
@@ -300,7 +355,7 @@
                 .path("@" + groupName).path("~marlin")
                 .request()
                 .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
-                        .createBasicAuthorizationHeaderValue(adminUser, "pass"))
+                        .createBasicAuthorizationHeaderValue(sysAdminUser, "pass"))
                 .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
                 .delete();
 
@@ -326,7 +381,7 @@
                 .request()
                 .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
                 .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
-                        .createBasicAuthorizationHeaderValue(adminUser, "pass"))
+                        .createBasicAuthorizationHeaderValue(sysAdminUser, "pass"))
                 .post(Entity.form(form));
 
         assertEquals(Status.OK.getStatusCode(), response.getStatus());
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 f30baa9..853d5cc 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
@@ -5,6 +5,7 @@
 import java.util.Set;
 
 import javax.ws.rs.core.Form;
+import javax.ws.rs.core.MediaType;
 
 import org.junit.Test;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -380,17 +381,19 @@
 
         assertEquals(Status.OK.getStatusCode(), response.getStatus());
 
+        Form f = new Form();
+        f.param("username", username);
+        f.param("status", "DELETED");
         // EM: this is so complicated because the group retrieval are not allowed 
         // for delete groups
         // check group
-        response = target().path(API_VERSION).path("group").path("list")
-                .path("system-admin").queryParam("username", username)
-                .queryParam("status", "DELETED")
+        response = target().path(API_VERSION).path("group")
+                .path("admin").path("list")
                 .request()
                 .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
                         .createBasicAuthorizationHeaderValue(admin, "pass"))
-                .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
-                .get();
+                .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED)
+                .post(Entity.form(f));
         
         assertEquals(Status.OK.getStatusCode(), response.getStatus());
         String entity = response.readEntity(String.class);
diff --git a/full/src/test/java/de/ids_mannheim/korap/web/controller/VCReferenceTest.java b/full/src/test/java/de/ids_mannheim/korap/web/controller/VCReferenceTest.java
index 128d6a5..baeed7c 100644
--- a/full/src/test/java/de/ids_mannheim/korap/web/controller/VCReferenceTest.java
+++ b/full/src/test/java/de/ids_mannheim/korap/web/controller/VCReferenceTest.java
@@ -12,6 +12,10 @@
 
 import com.fasterxml.jackson.databind.JsonNode;
 import com.google.common.net.HttpHeaders;
+
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.Form;
+import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.Response.Status;
 
@@ -220,15 +224,17 @@
         JsonNode node = JsonUtils.readTree(ent);
         assertTrue(node.at("/matches").size() > 0);
         
+        Form f = new Form();
+        f.param("status", "HIDDEN");
+        
         // check dory in the hidden group of the vc
         response = target().path(API_VERSION).path("group")
-                .path("list").path("system-admin")
-                .queryParam("status", "HIDDEN")
+                .path("admin").path("list")
                 .request()
                 .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
                         .createBasicAuthorizationHeaderValue("admin", "pass"))
-                .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
-                .get();
+                .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED)
+                .post(Entity.form(f));
 
         assertEquals(Status.OK.getStatusCode(), response.getStatus());
         String entity = response.readEntity(String.class);
diff --git a/full/src/test/java/de/ids_mannheim/korap/web/controller/VirtualCorpusControllerAdminTest.java b/full/src/test/java/de/ids_mannheim/korap/web/controller/VirtualCorpusControllerAdminTest.java
index 24caf0b..484a7d1 100644
--- a/full/src/test/java/de/ids_mannheim/korap/web/controller/VirtualCorpusControllerAdminTest.java
+++ b/full/src/test/java/de/ids_mannheim/korap/web/controller/VirtualCorpusControllerAdminTest.java
@@ -2,22 +2,24 @@
 
 import static org.junit.Assert.assertEquals;
 
+import javax.ws.rs.ProcessingException;
+import javax.ws.rs.client.Entity;
 import javax.ws.rs.core.Form;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
 
 import org.apache.http.entity.ContentType;
 import org.junit.Test;
 
 import com.fasterxml.jackson.databind.JsonNode;
 import com.google.common.net.HttpHeaders;
-import javax.ws.rs.ProcessingException;
-import javax.ws.rs.core.Response;
-import javax.ws.rs.core.Response.Status;
-import javax.ws.rs.client.Entity;
 
 import de.ids_mannheim.korap.authentication.http.HttpAuthorizationHandler;
 import de.ids_mannheim.korap.config.Attributes;
 import de.ids_mannheim.korap.constant.ResourceType;
 import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.exceptions.StatusCodes;
 import de.ids_mannheim.korap.utils.JsonUtils;
 
 /**
@@ -27,10 +29,23 @@
 public class VirtualCorpusControllerAdminTest extends VirtualCorpusTestBase {
 
     private String admin = "admin";
-    private String username = "VirtualCorpusControllerAdminTest";
+    private String testUser = "VirtualCorpusControllerAdminTest";
 
+    
+    private void testResponseUnauthorized (Response response) throws KustvaktException {
+        assertEquals(Status.UNAUTHORIZED.getStatusCode(), response.getStatus());
+
+        String entity = response.readEntity(String.class);
+        JsonNode node = JsonUtils.readTree(entity);
+
+        assertEquals(StatusCodes.AUTHORIZATION_FAILED,
+                node.at("/errors/0/0").asInt());
+        assertEquals("Unauthorized operation for user: " + testUser,
+                node.at("/errors/0/1").asText());
+    }
+    
     @Test
-    public void testSearchPrivateVC () throws
+    public void testRetrievePrivateVC () throws
             ProcessingException, KustvaktException {
         Response response = target().path(API_VERSION).path("vc")
                 .path("~dory").path("dory-vc")
@@ -48,9 +63,21 @@
         assertEquals(1, node.at("/id").asInt());
         assertEquals("dory-vc", node.at("/name").asText());
     }
+    
+    @Test
+    public void testRetrievePrivateVCUnauthorized ()
+            throws ProcessingException, KustvaktException {
+        Response response = target().path(API_VERSION).path("vc").path("~dory")
+                .path("dory-vc").request()
+                .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
+                        .createBasicAuthorizationHeaderValue(testUser, "pass"))
+                .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32").get();
+
+        testResponseUnauthorized(response);
+    }
 
     @Test
-    public void testSearchProjectVC () throws
+    public void testRetrieveProjectVC () throws
             ProcessingException, KustvaktException {
 
         Response response = target().path(API_VERSION).path("vc")
@@ -70,8 +97,23 @@
                 node.at("/type").asText());
     }
 
+    
     @Test
-    public void testListDoryVC () throws
+    public void testRetrieveProjectVCUnauthorized () throws
+            ProcessingException, KustvaktException {
+
+        Response response = target().path(API_VERSION).path("vc")
+                .path("~dory").path("group-vc")
+                .request()
+                .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
+                        .createBasicAuthorizationHeaderValue(testUser, "pass"))
+                .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
+                .get();
+        testResponseUnauthorized(response);
+    }
+
+    @Test
+    public void testListUserVC () throws
             ProcessingException, KustvaktException {
         Response response = target().path(API_VERSION).path("vc")
                 .queryParam("username", "dory")
@@ -87,17 +129,40 @@
         JsonNode node = JsonUtils.readTree(entity);
         assertEquals(4, node.size());
     }
-
-    private JsonNode testListSystemVC () throws
-            ProcessingException, KustvaktException {
+    
+    private JsonNode testAdminListVC (String username)
+            throws ProcessingException,
+            KustvaktException {
+        Form f = new Form();
+        f.param("createdBy", username);
+        
         Response response = target().path(API_VERSION).path("vc")
-                .path("list").path("system-admin").queryParam("type", "SYSTEM")
-                .queryParam("createdBy", "system")
+                .path("admin").path("list")
                 .request()
-                .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
                 .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
                         .createBasicAuthorizationHeaderValue(admin, "pass"))
-                .get();
+                .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED)
+                .post(Entity.form(f));
+
+        assertEquals(Status.OK.getStatusCode(), response.getStatus());
+
+        String entity = response.readEntity(String.class);
+        return JsonUtils.readTree(entity);
+    }
+
+    private JsonNode testAdminListVC_UsingAdminToken (String username, ResourceType type)
+            throws ProcessingException, KustvaktException {
+        Form f = new Form();
+        f.param("createdBy", username);
+        f.param("type", type.toString());
+        f.param("token", "secret");
+        
+        Response response = target().path(API_VERSION).path("vc").path("admin")
+                .path("list").request()
+                .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
+                        .createBasicAuthorizationHeaderValue(admin, "pass"))
+                .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED)
+                .post(Entity.form(f));
 
         assertEquals(Status.OK.getStatusCode(), response.getStatus());
 
@@ -121,7 +186,7 @@
 
         assertEquals(Status.CREATED.getStatusCode(), response.getStatus());
 
-        JsonNode node = testListSystemVC();
+        JsonNode node = testAdminListVC_UsingAdminToken("system",ResourceType.SYSTEM);
         assertEquals(2, node.size());
 
         testDeleteSystemVC(admin, "new-system-vc");
@@ -140,12 +205,12 @@
 
         assertEquals(Status.OK.getStatusCode(), response.getStatus());
 
-        JsonNode node = testListSystemVC();
+        JsonNode node = testAdminListVC_UsingAdminToken("system",ResourceType.SYSTEM);
         assertEquals(1, node.size());
     }
 
     @Test
-    public void testPrivateVC () throws
+    public void testCreatePrivateVC () throws
             ProcessingException, KustvaktException {
         String json = "{\"type\": \"PRIVATE\""
                 + ",\"queryType\": \"VIRTUAL_CORPUS\""
@@ -153,41 +218,24 @@
 
         String vcName = "new-vc";
         Response response = target().path(API_VERSION).path("vc")
-                .path("~"+username).path(vcName)
+                .path("~"+testUser).path(vcName)
                 .request()
                 .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
-                        .createBasicAuthorizationHeaderValue(username, "pass"))
+                        .createBasicAuthorizationHeaderValue(admin, "pass"))
                 .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
                 .header(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON)
                 .put(Entity.json(json));
 
         assertEquals(Status.CREATED.getStatusCode(), response.getStatus());
 
-        JsonNode node = testListUserVC(username);
+        JsonNode node = testAdminListVC(testUser);
         assertEquals(1, node.size());
 
-        testEditPrivateVC(username, vcName);
-        testDeletePrivateVC(username, vcName);
+        testEditPrivateVC(testUser, vcName);
+        testDeletePrivateVC(testUser, vcName);
     }
 
-    private JsonNode testListUserVC (String username)
-            throws ProcessingException,
-            KustvaktException {
-        Response response = target().path(API_VERSION).path("vc")
-                .path("list").path("system-admin")
-                .queryParam("createdBy", username)
-                .request()
-                .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
-                .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
-                        .createBasicAuthorizationHeaderValue(admin, "pass"))
-                .get();
-
-        assertEquals(Status.OK.getStatusCode(), response.getStatus());
-
-        String entity = response.readEntity(String.class);
-        return JsonUtils.readTree(entity);
-    }
-
+    
     private void testEditPrivateVC (String vcCreator, String vcName)
             throws ProcessingException,
             KustvaktException {
@@ -205,7 +253,7 @@
 
         assertEquals(Status.NO_CONTENT.getStatusCode(), response.getStatus());
 
-        JsonNode node = testListUserVC(username);
+        JsonNode node = testAdminListVC(testUser);
         assertEquals("edited vc", node.at("/0/description").asText());
     }
 
@@ -222,7 +270,7 @@
 
         assertEquals(Status.OK.getStatusCode(), response.getStatus());
 
-        JsonNode node = testListUserVC(vcCreator);
+        JsonNode node = testAdminListVC(vcCreator);
         assertEquals(0, node.size());
     }
 
@@ -274,13 +322,22 @@
         String vcName = "marlin-vc";
         String groupName = "marlin-group";
 
+        JsonNode node2 = testAdminListVC_UsingAdminToken(vcCreator,ResourceType.PROJECT);
+        assertEquals(0, node2.size());
+        
         testCreateVCAccess(vcCreator, vcName, groupName);
         JsonNode node = testlistAccessByGroup(groupName);
 
         String accessId = node.at("/accessId").asText();
         testDeleteVCAccess(accessId);
 
+        node2 = testAdminListVC_UsingAdminToken(vcCreator,ResourceType.PROJECT);
+        assertEquals(1, node2.size());
+        
         testEditVCType(admin, vcCreator, vcName, ResourceType.PRIVATE);
+        
+        node2 = testAdminListVC_UsingAdminToken(vcCreator,ResourceType.PROJECT);
+        assertEquals(0, node2.size());
     }
 
     private void testCreateVCAccess (String vcCreator, String vcName,
@@ -308,7 +365,7 @@
                 .path("access").path(accessId)
                 .request()
                 .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
-                        .createBasicAuthorizationHeaderValue("dory", "pass"))
+                        .createBasicAuthorizationHeaderValue(admin, "pass"))
                 .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
                 .delete();
 
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 c91cec8..c764225 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
@@ -13,18 +13,20 @@
 import java.util.Map.Entry;
 import java.util.Set;
 
+import javax.ws.rs.ProcessingException;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.Form;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+
 import org.apache.http.HttpStatus;
 import org.apache.http.entity.ContentType;
+import org.glassfish.jersey.server.ContainerRequest;
 import org.junit.Test;
 
 import com.fasterxml.jackson.databind.JsonNode;
 import com.google.common.net.HttpHeaders;
-import javax.ws.rs.ProcessingException;
-import javax.ws.rs.core.Response;
-import javax.ws.rs.core.Form;
-import javax.ws.rs.core.Response.Status;
-import javax.ws.rs.client.Entity;
-import org.glassfish.jersey.server.ContainerRequest;
 
 import de.ids_mannheim.korap.authentication.http.HttpAuthorizationHandler;
 import de.ids_mannheim.korap.config.Attributes;
@@ -221,15 +223,16 @@
         assertEquals(ResourceType.PUBLISHED.displayName(),
                 node.at("/type").asText());
 
+        Form f = new Form();
+        f.param("status", "HIDDEN");
         // check gill in the hidden group of the vc
         Response response = target().path(API_VERSION).path("group")
-                .path("list").path("system-admin")
-                .queryParam("status", "HIDDEN")
+                .path("admin").path("list")
                 .request()
                 .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
                         .createBasicAuthorizationHeaderValue("admin", "pass"))
-                .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
-                .get();
+                .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED)
+                .post(Entity.form(f));
 
         assertEquals(Status.OK.getStatusCode(), response.getStatus());
         String entity = response.readEntity(String.class);