Introduce filter_by and deprecate authorized_only (close #579)

in the OAuth2ClientList web-service.

Also cleaned up dory access and refresh tokens for confidentialClientId
(see OAuth2TestBase)

Change-Id: I47e2416c0a6ce9fc3f500f5f30e3ca021f984142
diff --git a/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2AccessTokenTest.java b/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2AccessTokenTest.java
index 6b5627d..6aa2924 100644
--- a/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2AccessTokenTest.java
+++ b/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2AccessTokenTest.java
@@ -6,16 +6,9 @@
 
 import java.io.IOException;
 
-import jakarta.ws.rs.client.Entity;
-import jakarta.ws.rs.core.Form;
-import jakarta.ws.rs.core.Response;
-import jakarta.ws.rs.core.Response.Status;
-
-import org.apache.http.entity.ContentType;
 import org.junit.jupiter.api.Test;
+
 import com.fasterxml.jackson.databind.JsonNode;
-import com.google.common.net.HttpHeaders;
-import com.nimbusds.oauth2.sdk.GrantType;
 
 import de.ids_mannheim.korap.authentication.http.HttpAuthorizationHandler;
 import de.ids_mannheim.korap.config.Attributes;
@@ -24,6 +17,8 @@
 import de.ids_mannheim.korap.exceptions.KustvaktException;
 import de.ids_mannheim.korap.exceptions.StatusCodes;
 import de.ids_mannheim.korap.utils.JsonUtils;
+import jakarta.ws.rs.core.Response;
+import jakarta.ws.rs.core.Response.Status;
 
 public class OAuth2AccessTokenTest extends OAuth2TestBase {
 
@@ -38,7 +33,7 @@
                 .createBasicAuthorizationHeaderValue(confidentialClientId,
                         clientSecret);
     }
-
+    
     @Test
     public void testScopeWithSuperClient () throws KustvaktException {
         Response response = requestTokenWithDoryPassword(superClientId,
@@ -65,6 +60,8 @@
                 confidentialClientId, clientSecret, code);
         JsonNode node = JsonUtils.readTree(response.readEntity(String.class));
         String token = node.at("/access_token").asText();
+        String refreshToken = node.at("/refresh_token").asText();
+        
         assertTrue(node.at("/scope").asText()
                 .contains(OAuth2Scope.VC_INFO.toString()));
         // test list vc using the token
@@ -73,6 +70,11 @@
         assertEquals(Status.OK.getStatusCode(), response.getStatus());
         node = JsonUtils.readTree(response.readEntity(String.class));
         assertEquals(4, node.size());
+        
+        revokeToken(token, confidentialClientId, clientSecret,
+                ACCESS_TOKEN_TYPE);
+        revokeToken(refreshToken, confidentialClientId, clientSecret,
+                REFRESH_TOKEN_TYPE);
     }
 
     @Test
@@ -84,9 +86,15 @@
         assertEquals(Status.OK.getStatusCode(), response.getStatus());
         JsonNode node = JsonUtils.readTree(response.readEntity(String.class));
         String accessToken = node.at("/access_token").asText();
+        String refreshToken = node.at("/refresh_token").asText();
         testScopeNotAuthorized(accessToken);
         testScopeNotAuthorize2(accessToken);
         testSearchWithOAuth2Token(accessToken);
+        
+        revokeToken(accessToken, confidentialClientId, clientSecret,
+                ACCESS_TOKEN_TYPE);
+        revokeToken(refreshToken, confidentialClientId, clientSecret,
+                REFRESH_TOKEN_TYPE);
     }
 
     private void testScopeNotAuthorized (String accessToken)
@@ -140,17 +148,14 @@
         JsonNode node = requestTokenWithAuthorizationCodeAndHeader(
                 confidentialClientId, code, clientAuthHeader);
         String accessToken = node.at("/access_token").asText();
-        Form form = new Form();
-        form.param("token", accessToken);
-        form.param("client_id", confidentialClientId);
-        form.param("client_secret", "secret");
-        Response response = target().path(API_VERSION).path("oauth2")
-                .path("revoke").request()
-                .header(HttpHeaders.CONTENT_TYPE,
-                        ContentType.APPLICATION_FORM_URLENCODED)
-                .post(Entity.form(form));
-        assertEquals(Status.OK.getStatusCode(), response.getStatus());
+        String refreshToken = node.at("/refresh_token").asText();
+        
+        revokeToken(accessToken, confidentialClientId, clientSecret,
+                ACCESS_TOKEN_TYPE);
         testSearchWithRevokedAccessToken(accessToken);
+        
+        revokeToken(refreshToken, confidentialClientId, clientSecret,
+                REFRESH_TOKEN_TYPE);
     }
 
     @Test
@@ -161,7 +166,7 @@
                 publicClientId, "", code);
         JsonNode node = JsonUtils.readTree(response.readEntity(String.class));
         String accessToken = node.at("/access_token").asText();
-        testRevokeTokenViaSuperClient(accessToken, userAuthHeader);
+        revokeTokenViaSuperClient(accessToken, userAuthHeader);
         testSearchWithRevokedAccessToken(accessToken);
     }
 
@@ -174,23 +179,18 @@
                 confidentialClientId, code, clientAuthHeader);
         String accessToken = node.at("/access_token").asText();
         String refreshToken = node.at("/refresh_token").asText();
-        Form form = new Form();
-        form.param("grant_type", GrantType.REFRESH_TOKEN.toString());
-        form.param("client_id", confidentialClientId);
-        form.param("client_secret", "secret");
-        form.param("refresh_token", refreshToken);
-        Response response = target().path(API_VERSION).path("oauth2")
-                .path("token").request()
-                .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
-                .header(HttpHeaders.CONTENT_TYPE,
-                        ContentType.APPLICATION_FORM_URLENCODED)
-                .post(Entity.form(form));
-        String entity = response.readEntity(String.class);
-        assertEquals(Status.OK.getStatusCode(), response.getStatus());
-        node = JsonUtils.readTree(entity);
-        assertNotNull(node.at("/access_token").asText());
-        assertTrue(!refreshToken.equals(node.at("/refresh_token").asText()));
+
+        node = requestTokenWithRefreshToken(confidentialClientId, clientSecret,
+                refreshToken);
+        String newAccessToken = node.at("/access_token").asText();
+        String newRefreshToken = node.at("/refresh_token").asText();
+        assertTrue(!refreshToken.equals(newRefreshToken));
+
         testSearchWithRevokedAccessToken(accessToken);
+        revokeToken(newAccessToken, confidentialClientId, clientSecret,
+                ACCESS_TOKEN_TYPE);
+        revokeToken(newRefreshToken, confidentialClientId, clientSecret,
+                REFRESH_TOKEN_TYPE);
     }
 
     @Test
@@ -201,6 +201,7 @@
         JsonNode node = requestTokenWithAuthorizationCodeAndHeader(
                 confidentialClientId, code, clientAuthHeader);
         String userAuthToken = node.at("/access_token").asText();
+        String refreshToken = node.at("/refresh_token").asText();
         Response response = requestAuthorizationCode("code",
                 confidentialClientId, "", "search", "",
                 "Bearer " + userAuthToken);
@@ -210,6 +211,11 @@
                 node.at("/errors/0/0").asInt());
         assertEquals(node.at("/errors/0/1").asText(),
                 "Scope authorize is not authorized");
+
+        revokeToken(userAuthToken, confidentialClientId, clientSecret,
+                ACCESS_TOKEN_TYPE);
+        revokeToken(refreshToken, confidentialClientId, clientSecret,
+                REFRESH_TOKEN_TYPE);
     }
 
     @Test
@@ -227,5 +233,7 @@
         String code = requestAuthorizationCode(superClientId,
                 "Bearer " + userAuthToken);
         assertNotNull(code);
+        
+        revokeTokenViaSuperClient(userAuthToken, userAuthHeader);
     }
 }
diff --git a/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2AdminControllerTest.java b/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2AdminControllerTest.java
index fabc267..f5a8a61 100644
--- a/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2AdminControllerTest.java
+++ b/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2AdminControllerTest.java
@@ -114,7 +114,7 @@
         String entity = response.readEntity(String.class);
         JsonNode node = JsonUtils.readTree(entity);
         String accessToken = node.at("/access_token").asText();
-        testRevokeToken(accessToken, publicClientId, null, ACCESS_TOKEN_TYPE);
+        revokeToken(accessToken, publicClientId, null, ACCESS_TOKEN_TYPE);
         int accessTokensAfter = accessDao.retrieveInvalidAccessTokens().size();
         assertEquals(accessTokensAfter, accessTokensBefore + 1);
         target().path(API_VERSION).path("admin").path("oauth2").path("token")
diff --git a/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2AuthorizationPostTest.java b/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2AuthorizationPostTest.java
index 8372acc..9225220 100644
--- a/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2AuthorizationPostTest.java
+++ b/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2AuthorizationPostTest.java
@@ -78,10 +78,15 @@
                 confidentialClientId, clientSecret, code);
         String entity = response.readEntity(String.class);
         JsonNode node = JsonUtils.readTree(entity);
-        assertNotNull(node.at("/access_token").asText());
-        assertNotNull(node.at("/refresh_token").asText());
+        String token = node.at("/access_token").asText();
+        String refreshToken = node.at("/refresh_token").asText();
         assertEquals(TokenType.BEARER.displayName(),
                 node.at("/token_type").asText());
         assertNotNull(node.at("/expires_in").asText());
+        
+        revokeToken(token, confidentialClientId, clientSecret,
+                ACCESS_TOKEN_TYPE);
+        revokeToken(refreshToken, confidentialClientId, clientSecret,
+                REFRESH_TOKEN_TYPE);
     }
 }
diff --git a/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2ClientControllerTest.java b/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2ClientControllerTest.java
index 20179fb..fd74696 100644
--- a/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2ClientControllerTest.java
+++ b/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2ClientControllerTest.java
@@ -102,6 +102,7 @@
         assertFalse(clientId.contains("a"));
         testListConfidentialClient(username, clientId);
         testConfidentialClientInfo(clientId, username);
+//        testListAllUserClients(username);
         testResetConfidentialClientSecret(clientId, clientSecret);
         deregisterClient(username, clientId);
     }
@@ -292,7 +293,7 @@
         assertNotNull(clientId);
         assertTrue(node.at("/client_secret").isMissingNode());
         
-        node = listUserClients(username);
+        node = listUserClients(username,"owned_only");
         assertFalse(node.at("/0/client_redirect_uri").isMissingNode());
         assertFalse(node.at("/0/registration_date").isMissingNode());
         assertEquals(username,
@@ -508,7 +509,10 @@
     private void testListAuthorizedClients (String userAuthHeader)
             throws KustvaktException {
         Form form = getSuperClientForm();
-        form.param("authorized_only", "true");
+        // deprecated, use filter_by = authorized_only instead
+//        form.param("authorized_only", "true");
+        
+        form.param("filter_by", "authorized_only");
         Response response = target().path(API_VERSION).path("oauth2")
                 .path("client").path("list").request()
                 .header(Attributes.AUTHORIZATION, userAuthHeader)
@@ -535,7 +539,7 @@
         OAuth2ClientJson json = createOAuth2ClientJson(clientName,
                 OAuth2ClientType.PUBLIC, "Dory's client.");
         registerClient("dory", json);
-        JsonNode node = listUserClients("dory");
+        JsonNode node = listUserClients("dory","owned_only");
         assertEquals(1, node.size());
         assertEquals(clientName, node.at("/0/client_name").asText());
         assertEquals(OAuth2ClientType.PUBLIC.name(),
@@ -544,12 +548,16 @@
         assertFalse(node.at("/0/registration_date").isMissingNode());
         assertTrue(node.at("/refresh_token_expiry").isMissingNode());
         String clientId = node.at("/0/client_id").asText();
+        
+//        testListAllUserClients("dory");
         testDeregisterPublicClient(clientId, "dory");
     }
 
     private void testListConfidentialClient (String username, String clientId)
             throws ProcessingException, KustvaktException {
-        JsonNode node = listUserClients(username);
+        // means authorized_only = false
+        // this is deprecated, filter_by = owned_only should be use instead.
+        JsonNode node = listUserClients(username,"");
         assertEquals(1, node.size());
         assertEquals(clientId, node.at("/0/client_id").asText());
         assertEquals(node.at("/0/client_name").asText(), "OAuth2ClientTest");
@@ -566,8 +574,27 @@
         assertTrue(node.at("/0/source").isMissingNode());
     }
 
+    // not ready until the behavior for (filterBy=null || filterBy.isEmpty) is set
+    private void testListAllUserClients (String username) throws KustvaktException {
+        // authorize
+        String userAuthHeader = HttpAuthorizationHandler
+                .createBasicAuthorizationHeaderValue(username, "password");
+        String code = requestAuthorizationCode(confidentialClientId, userAuthHeader);
+        Response response = requestTokenWithAuthorizationCodeAndForm(
+                confidentialClientId, this.clientSecret, code);
+        assertEquals(Status.OK.getStatusCode(), response.getStatus());
+        JsonNode node = JsonUtils.readTree(response.readEntity(String.class));
+        String accessToken = node.at("/access_token").asText();
+        
+        node = listUserClients(username,null);
+        assertEquals(2, node.size());
+        
+        testRevokeAllTokenViaSuperClient(confidentialClientId, userAuthHeader,
+                accessToken);
+    }
+    
     @Test
-    public void testListUserClients () throws KustvaktException {
+    public void testListAuthorizedUserClients () throws KustvaktException {
         String username = "pearl";
         String password = "pwd";
         userAuthHeader = HttpAuthorizationHandler
diff --git a/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2ControllerTest.java b/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2ControllerTest.java
index ab2eb27..dfbb987 100644
--- a/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2ControllerTest.java
+++ b/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2ControllerTest.java
@@ -57,7 +57,7 @@
         assertEquals(TokenType.BEARER.displayName(),
                 node.at("/token_type").asText());
         assertEquals(31536000, node.at("/expires_in").asInt());
-        testRevokeToken(accessToken, publicClientId, null, ACCESS_TOKEN_TYPE);
+        revokeToken(accessToken, publicClientId, null, ACCESS_TOKEN_TYPE);
         assertTrue(node.at("/refresh_token").isMissingNode());
     }
 
@@ -487,7 +487,7 @@
         assertTrue(!newRefreshToken.equals(refreshToken));
         testRequestTokenWithRevokedRefreshToken(clientId, clientSecret,
                 refreshToken);
-        testRevokeToken(newRefreshToken, clientId, clientSecret,
+        revokeToken(newRefreshToken, clientId, clientSecret,
                 REFRESH_TOKEN_TYPE);
         testRequestTokenWithRevokedRefreshToken(clientId, clientSecret,
                 newRefreshToken);
@@ -654,24 +654,24 @@
         node = requestTokenList(userAuthHeader, REFRESH_TOKEN_TYPE,
                 confidentialClientId);
         assertEquals(2, node.size());
-        testRevokeToken(refreshToken1, superClientId, clientSecret,
+        revokeToken(refreshToken1, superClientId, clientSecret,
                 REFRESH_TOKEN_TYPE);
-        testRevokeToken(node.at("/0/token").asText(), confidentialClientId,
+        revokeToken(node.at("/0/token").asText(), confidentialClientId,
                 clientSecret, REFRESH_TOKEN_TYPE);
-        testRevokeToken(node.at("/1/token").asText(), confidentialClientId2,
+        revokeToken(node.at("/1/token").asText(), confidentialClientId2,
                 clientSecret, REFRESH_TOKEN_TYPE);
         node = requestTokenList(userAuthHeader, REFRESH_TOKEN_TYPE);
         assertEquals(1, node.size());
-        testRevokeTokenViaSuperClient(node.at("/0/token").asText(),
+        revokeTokenViaSuperClient(node.at("/0/token").asText(),
                 userAuthHeader);
         node = requestTokenList(userAuthHeader, REFRESH_TOKEN_TYPE);
         assertEquals(0, node.size());
         // try revoking a token belonging to another user
         // should not return any errors
-        testRevokeTokenViaSuperClient(refreshToken5, userAuthHeader);
+        revokeTokenViaSuperClient(refreshToken5, userAuthHeader);
         node = requestTokenList(darlaAuthHeader, REFRESH_TOKEN_TYPE);
         assertEquals(1, node.size());
-        testRevokeTokenViaSuperClient(refreshToken5, darlaAuthHeader);
+        revokeTokenViaSuperClient(refreshToken5, darlaAuthHeader);
         node = requestTokenList(darlaAuthHeader, REFRESH_TOKEN_TYPE);
         assertEquals(0, node.size());
     }
@@ -702,7 +702,7 @@
         // list refresh tokens
         node = requestTokenList(userAuthHeader, REFRESH_TOKEN_TYPE);
         assertEquals(0, node.size());
-        testRevokeTokenViaSuperClient(accessToken1, userAuthHeader);
+        revokeTokenViaSuperClient(accessToken1, userAuthHeader);
         node = requestTokenList(userAuthHeader, ACCESS_TOKEN_TYPE);
         // System.out.println(node);
         assertEquals(1, node.size());
@@ -715,7 +715,7 @@
         assertNotNull(node.at("/0/client_name").asText());
         assertNotNull(node.at("/0/client_description").asText());
         assertNotNull(node.at("/0/client_url").asText());
-        testRevokeTokenViaSuperClient(accessToken2, userAuthHeader);
+        revokeTokenViaSuperClient(accessToken2, userAuthHeader);
         node = requestTokenList(userAuthHeader, ACCESS_TOKEN_TYPE);
         assertEquals(0, node.size());
     }
diff --git a/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2PluginTest.java b/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2PluginTest.java
index a8c2139..e150d5d 100644
--- a/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2PluginTest.java
+++ b/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2PluginTest.java
@@ -113,7 +113,7 @@
     private void testListUserRegisteredPlugins (String username,
             String clientId, String clientName, int refreshTokenExpiry)
             throws ProcessingException, KustvaktException {
-        JsonNode node = listUserClients(username);
+        JsonNode node = listUserClients(username, "owned_only");
         assertEquals(1, node.size());
         assertEquals(clientId, node.at("/0/client_id").asText());
         assertEquals(clientName, node.at("/0/client_name").asText());
diff --git a/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2TestBase.java b/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2TestBase.java
index 4ce03d1..8a20196 100644
--- a/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2TestBase.java
+++ b/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2TestBase.java
@@ -60,7 +60,7 @@
 public abstract class OAuth2TestBase extends SpringJerseyTest {
 
     @Autowired
-    private AccessTokenDao tokenDao;
+    protected AccessTokenDao tokenDao;
     @Autowired
     private OAuth2ClientDao clientDao;
     @Autowired
@@ -255,6 +255,24 @@
         return requestToken(form);
     }
 
+    protected JsonNode requestTokenWithRefreshToken (String clientId,
+            String clientSecret, String refreshToken) throws KustvaktException {
+        Form form = new Form();
+        form.param("grant_type", GrantType.REFRESH_TOKEN.toString());
+        form.param("client_id", clientId);
+        form.param("client_secret", clientSecret);
+        form.param("refresh_token", refreshToken);
+        Response response = target().path(API_VERSION).path("oauth2")
+                .path("token").request()
+                .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
+                .header(HttpHeaders.CONTENT_TYPE,
+                        ContentType.APPLICATION_FORM_URLENCODED)
+                .post(Entity.form(form));
+        String entity = response.readEntity(String.class);
+        assertEquals(Status.OK.getStatusCode(), response.getStatus());
+        return JsonUtils.readTree(entity);
+    }
+    
     protected void testRequestTokenWithRevokedRefreshToken (String clientId,
             String clientSecret, String refreshToken) throws KustvaktException {
         Form form = new Form();
@@ -280,6 +298,44 @@
         assertEquals("Refresh token has been revoked",
                 node.at("/error_description").asText());
     }
+    
+    protected void revokeTokenViaSuperClient (String token,
+            String userAuthHeader) {
+        Form form = new Form();
+        form.param("token", token);
+        form.param("super_client_id", superClientId);
+        form.param("super_client_secret", clientSecret);
+
+        Response response = target().path(API_VERSION).path("oauth2")
+                .path("revoke").path("super").request()
+                .header(HttpHeaders.CONTENT_TYPE,
+                        ContentType.APPLICATION_FORM_URLENCODED)
+                .header(Attributes.AUTHORIZATION, userAuthHeader)
+                .post(Entity.form(form));
+
+        assertEquals(Status.OK.getStatusCode(), response.getStatus());
+        assertEquals("SUCCESS", response.readEntity(String.class));
+    }
+
+    protected void revokeToken (String token, String clientId,
+            String clientSecret, String tokenType) {
+        Form form = new Form();
+        form.param("token_type", tokenType);
+        form.param("token", token);
+        form.param("client_id", clientId);
+        if (clientSecret != null) {
+            form.param("client_secret", clientSecret);
+        }
+
+        Response response = target().path(API_VERSION).path("oauth2")
+                .path("revoke").request()
+                .header(HttpHeaders.CONTENT_TYPE,
+                        ContentType.APPLICATION_FORM_URLENCODED)
+                .post(Entity.form(form));
+
+        assertEquals(Status.OK.getStatusCode(), response.getStatus());
+        assertEquals("SUCCESS", response.readEntity(String.class));
+    }
 
     protected Response registerClient (String username, OAuth2ClientJson json)
             throws ProcessingException, KustvaktException {
@@ -392,55 +448,21 @@
                 node.at("/errors/0/1").asText());
     }
 
-    protected void testRevokeTokenViaSuperClient (String token,
-            String userAuthHeader) {
-        Form form = new Form();
-        form.param("token", token);
-        form.param("super_client_id", superClientId);
-        form.param("super_client_secret", clientSecret);
-
-        Response response = target().path(API_VERSION).path("oauth2")
-                .path("revoke").path("super").request()
-                .header(HttpHeaders.CONTENT_TYPE,
-                        ContentType.APPLICATION_FORM_URLENCODED)
-                .header(Attributes.AUTHORIZATION, userAuthHeader)
-                .post(Entity.form(form));
-
-        assertEquals(Status.OK.getStatusCode(), response.getStatus());
-        assertEquals("SUCCESS", response.readEntity(String.class));
-    }
-
-    protected void testRevokeToken (String token, String clientId,
-            String clientSecret, String tokenType) {
-        Form form = new Form();
-        form.param("token_type", tokenType);
-        form.param("token", token);
-        form.param("client_id", clientId);
-        if (clientSecret != null) {
-            form.param("client_secret", clientSecret);
-        }
-
-        Response response = target().path(API_VERSION).path("oauth2")
-                .path("revoke").request()
-                .header(HttpHeaders.CONTENT_TYPE,
-                        ContentType.APPLICATION_FORM_URLENCODED)
-                .post(Entity.form(form));
-
-        assertEquals(Status.OK.getStatusCode(), response.getStatus());
-        assertEquals("SUCCESS", response.readEntity(String.class));
-    }
-
-    protected JsonNode listUserClients (String username)
+    protected JsonNode listUserClients (String username, String filterBy)
             throws ProcessingException, KustvaktException {
         Form form = getSuperClientForm();
+        
+        if (filterBy != null) {
+            form.param("filter_by", filterBy);
+        }
         Response response = target().path(API_VERSION).path("oauth2")
-                .path("client").path("list").request()
-                .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
-                        .createBasicAuthorizationHeaderValue(username, "pwd"))
-                .header(HttpHeaders.CONTENT_TYPE,
-                        ContentType.APPLICATION_FORM_URLENCODED)
-                .post(Entity.form(form));
-
+            .path("client").path("list").request()
+            .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
+                    .createBasicAuthorizationHeaderValue(username, "pwd"))
+            .header(HttpHeaders.CONTENT_TYPE,
+                    ContentType.APPLICATION_FORM_URLENCODED)
+            .post(Entity.form(form));
+        
         assertEquals(Status.OK.getStatusCode(), response.getStatus());
 
         String entity = response.readEntity(String.class);
diff --git a/src/test/resources/test-config.xml b/src/test/resources/test-config.xml
index f79f967..cff5100 100644
--- a/src/test/resources/test-config.xml
+++ b/src/test/resources/test-config.xml
@@ -40,11 +40,15 @@
 			<array>
 				<value>classpath:test-jdbc.properties</value>
 				<value>file:./test-jdbc.properties</value>
+				<value>file:./data/test-jdbc.properties</value>
 				<value>classpath:properties/mail.properties</value>
 				<value>file:./mail.properties</value>
+				<value>file:./data/mail.properties</value>
 				<value>classpath:test-hibernate.properties</value>
+				<value>file:./data/test-hibernate.properties</value>
 				<value>file:./kustvakt-test.conf</value>
 				<value>classpath:kustvakt-test.conf</value>
+				<value>file:./data/kustvakt-test.conf</value>
 			</array>
 		</property>
 	</bean>