Removed duplicate clients in user client lists.

Change-Id: If9806e5a2f98153b1428498f74c5bded989b7d70
diff --git a/full/src/main/java/de/ids_mannheim/korap/oauth2/dao/OAuth2ClientDao.java b/full/src/main/java/de/ids_mannheim/korap/oauth2/dao/OAuth2ClientDao.java
index 444659e..7bde510 100644
--- a/full/src/main/java/de/ids_mannheim/korap/oauth2/dao/OAuth2ClientDao.java
+++ b/full/src/main/java/de/ids_mannheim/korap/oauth2/dao/OAuth2ClientDao.java
@@ -11,7 +11,7 @@
 import javax.persistence.TypedQuery;
 import javax.persistence.criteria.CriteriaBuilder;
 import javax.persistence.criteria.CriteriaQuery;
-import javax.persistence.criteria.ListJoin;
+import javax.persistence.criteria.Join;
 import javax.persistence.criteria.Predicate;
 import javax.persistence.criteria.Root;
 
@@ -116,7 +116,7 @@
                 builder.createQuery(OAuth2Client.class);
 
         Root<OAuth2Client> client = query.from(OAuth2Client.class);
-        ListJoin<OAuth2Client, RefreshToken> refreshToken =
+        Join<OAuth2Client, RefreshToken> refreshToken =
                 client.join(OAuth2Client_.refreshTokens);
         Predicate condition = builder.and(
                 builder.equal(refreshToken.get(RefreshToken_.userId), username),
@@ -128,6 +128,7 @@
                                 .now(ZoneId.of(Attributes.DEFAULT_TIME_ZONE))));
         query.select(client);
         query.where(condition);
+        query.distinct(true);
         TypedQuery<OAuth2Client> q = entityManager.createQuery(query);
         return q.getResultList();
     }
diff --git a/full/src/main/java/de/ids_mannheim/korap/oauth2/service/OAuth2ClientService.java b/full/src/main/java/de/ids_mannheim/korap/oauth2/service/OAuth2ClientService.java
index 23b8267..f1e4de0 100644
--- a/full/src/main/java/de/ids_mannheim/korap/oauth2/service/OAuth2ClientService.java
+++ b/full/src/main/java/de/ids_mannheim/korap/oauth2/service/OAuth2ClientService.java
@@ -346,11 +346,6 @@
         return clientDao.retrieveClientById(clientId);
     }
 
-    public List<OAuth2Client> retrieveUserClients (String username)
-            throws KustvaktException {
-        return clientDao.retrieveUserClients(username);
-    }
-
     public List<OAuth2UserClientDto> listUserClients (String username,
             String clientId, String clientSecret) throws KustvaktException {
         OAuth2Client client = authenticateClient(clientId, clientSecret);
@@ -359,9 +354,10 @@
                     "Only super client is allowed to list user clients.",
                     OAuth2Error.UNAUTHORIZED_CLIENT);
         }
-        List<OAuth2Client> userClients = retrieveUserClients(username);
+        List<OAuth2Client> userClients =
+                clientDao.retrieveUserClients(username);
         Collections.sort(userClients);
-        
+
         List<OAuth2UserClientDto> dtoList = new ArrayList<>(userClients.size());
         for (OAuth2Client uc : userClients) {
             if (uc.isSuper()) continue;
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/controller/OAuthClientController.java b/full/src/main/java/de/ids_mannheim/korap/web/controller/OAuthClientController.java
index 02b190f..38e7474 100644
--- a/full/src/main/java/de/ids_mannheim/korap/web/controller/OAuthClientController.java
+++ b/full/src/main/java/de/ids_mannheim/korap/web/controller/OAuthClientController.java
@@ -229,10 +229,12 @@
     }
 
     /**
-     * Lists user clients having refresh tokens. This service is not
-     * part of the OAuth2 specification. It is intended to facilitate
-     * users revoking any suspicious and misused access or refresh
-     * tokens.
+     * Lists user clients having active refresh tokens (not revoked,
+     * not expired), except super clients.
+     * 
+     * This service is not part of the OAuth2 specification. It is
+     * intended to facilitate users revoking any suspicious and
+     * misused access or refresh tokens.
      * 
      * Only super clients are allowed to use this service. It requires
      * user and client authentications.