Updated OAuth2Client list API

Added redirect_uri, registration_date, permitted, and source to
OAuth2UserClientDto

Change-Id: Id2614f1c2d7a408ddc7cda1ccfa9ed038a89adf8
diff --git a/full/Changes b/full/Changes
index 4902952..cad48b8 100644
--- a/full/Changes
+++ b/full/Changes
@@ -9,7 +9,9 @@
    to the oauth2_client database table, and updated the OAuth2 client 
    registration mechanism.
  - Added authorization request with GET and deprecated that with POST.
-
+2022-04-13
+ - Updated OAuth2Client list API (added redirect_uri, registration_date, 
+   permitted, source to OAuth2UserClientDto).
 
 # version 0.65.2
 
diff --git a/full/src/main/java/de/ids_mannheim/korap/oauth2/dto/OAuth2UserClientDto.java b/full/src/main/java/de/ids_mannheim/korap/oauth2/dto/OAuth2UserClientDto.java
index cdef1fc..59a3c56 100644
--- a/full/src/main/java/de/ids_mannheim/korap/oauth2/dto/OAuth2UserClientDto.java
+++ b/full/src/main/java/de/ids_mannheim/korap/oauth2/dto/OAuth2UserClientDto.java
@@ -1,14 +1,22 @@
 package de.ids_mannheim.korap.oauth2.dto;
 
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonInclude.Include;
 import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.databind.JsonNode;
 
+import de.ids_mannheim.korap.exceptions.KustvaktException;
 import de.ids_mannheim.korap.oauth2.constant.OAuth2ClientType;
+import de.ids_mannheim.korap.oauth2.entity.OAuth2Client;
+import de.ids_mannheim.korap.utils.JsonUtils;
 
-/** Lists authorized OAuth2 clients of a user
+/**
+ * Lists OAuth2 clients of a user
  * 
  * @author margaretha
  *
  */
+@JsonInclude(Include.NON_EMPTY)
 public class OAuth2UserClientDto {
     @JsonProperty("client_id")
     private String clientId;
@@ -20,35 +28,98 @@
     private String description;
     @JsonProperty("client_url")
     private String url;
+    @JsonProperty("client_redirect_uri")
+    private String redirect_uri;
+    @JsonProperty("registration_date")
+    private String registrationDate;
+    
+    private boolean permitted;
+    private JsonNode source;
+
+    public OAuth2UserClientDto (OAuth2Client client) throws KustvaktException {
+        this.setClientId(client.getId());
+        this.setClientName(client.getName());
+        this.setDescription(client.getDescription());
+        this.setClientType(client.getType());
+        this.setUrl(client.getUrl());
+        this.setClientType(client.getType());
+        this.setRedirect_uri(client.getRedirectURI());
+        this.setRegistrationDate(client.getRegistrationDate().toString());
+        this.setPermitted(client.isPermitted());
+        
+        String source = client.getSource();
+        if (source!=null) {
+            this.setSource(JsonUtils.readTree(client.getSource()));
+        }
+    }
     
     public String getClientName () {
         return clientName;
     }
+
     public void setClientName (String clientName) {
         this.clientName = clientName;
     }
+
     public String getClientId () {
         return clientId;
     }
+
     public void setClientId (String clientId) {
         this.clientId = clientId;
     }
+
     public String getDescription () {
         return description;
     }
+
     public void setDescription (String description) {
         this.description = description;
     }
+
     public String getUrl () {
         return url;
     }
+
     public void setUrl (String url) {
         this.url = url;
     }
+
     public OAuth2ClientType getClientType () {
         return clientType;
     }
+
     public void setClientType (OAuth2ClientType clientType) {
         this.clientType = clientType;
     }
+
+    public String getRedirect_uri () {
+        return redirect_uri;
+    }
+
+    public void setRedirect_uri (String redirect_uri) {
+        this.redirect_uri = redirect_uri;
+    }
+
+    public boolean isPermitted () {
+        return permitted;
+    }
+
+    public void setPermitted (boolean permitted) {
+        this.permitted = permitted;
+    }
+
+    public JsonNode getSource () {
+        return source;
+    }
+    public void setSource (JsonNode source) {
+        this.source = source;
+    }
+    
+    public String getRegistrationDate () {
+        return registrationDate;
+    }
+    public void setRegistrationDate (String registrationDate) {
+        this.registrationDate = registrationDate;
+    }
 }
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 d714111..f013023 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
@@ -13,8 +13,6 @@
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
-import com.fasterxml.jackson.databind.JsonNode;
-
 import de.ids_mannheim.korap.config.FullConfiguration;
 import de.ids_mannheim.korap.dao.AdminDao;
 import de.ids_mannheim.korap.encryption.RandomCodeGenerator;
@@ -59,6 +57,10 @@
  */
 @Service
 public class OAuth2ClientService {
+    
+//    public static final UrlValidator redirectURIValidator =
+//            new UrlValidator(new String[] { "http", "https" },
+//                    UrlValidator.NO_FRAGMENTS + UrlValidator.ALLOW_LOCAL_URLS);
 
     @Autowired
     private OAuth2ClientDao clientDao;
@@ -337,15 +339,8 @@
         return clientDao.retrieveClientById(clientId);
     }
 
-    public List<OAuth2UserClientDto> listUserAuthorizedClients (String username,
-            String superClientId, String superClientSecret) throws KustvaktException {
-        OAuth2Client superClient = authenticateClient(superClientId, superClientSecret);
-        if (!superClient.isSuper()) {
-            throw new KustvaktException(StatusCodes.CLIENT_AUTHORIZATION_FAILED,
-                    "Only super client is allowed to list user authorized clients.",
-                    OAuth2Error.UNAUTHORIZED_CLIENT);
-        }
-                
+    public List<OAuth2UserClientDto> listUserAuthorizedClients (String username)
+            throws KustvaktException {
         List<OAuth2Client> userClients =
                 clientDao.retrieveUserAuthorizedClients(username);
         userClients.addAll(clientDao.retrieveClientsByAccessTokens(username));
@@ -364,30 +359,20 @@
         return createClientDtos(uniqueClients);
     }
     
-    public List<OAuth2UserClientDto> listUserRegisteredClients (String username,
-            String clientId, String clientSecret) throws KustvaktException {
-        OAuth2Client client = authenticateClient(clientId, clientSecret);
-        if (!client.isSuper()) {
-            throw new KustvaktException(StatusCodes.CLIENT_AUTHORIZATION_FAILED,
-                    "Only super client is allowed to list user registered clients.",
-                    OAuth2Error.UNAUTHORIZED_CLIENT);
-        }
+    public List<OAuth2UserClientDto> listUserRegisteredClients (String username)
+            throws KustvaktException {
         List<OAuth2Client> userClients =
                 clientDao.retrieveUserRegisteredClients(username);
         Collections.sort(userClients);
         return createClientDtos(userClients);
     }
     
-    private List<OAuth2UserClientDto> createClientDtos (List<OAuth2Client> userClients) {
+    private List<OAuth2UserClientDto> createClientDtos (
+            List<OAuth2Client> userClients) throws KustvaktException {
         List<OAuth2UserClientDto> dtoList = new ArrayList<>(userClients.size());
         for (OAuth2Client uc : userClients) {
             if (uc.isSuper()) continue;
-            OAuth2UserClientDto dto = new OAuth2UserClientDto();
-            dto.setClientId(uc.getId());
-            dto.setClientName(uc.getName());
-            dto.setDescription(uc.getDescription());
-            dto.setUrl(uc.getUrl());
-            dto.setClientType(uc.getType());
+            OAuth2UserClientDto dto = new OAuth2UserClientDto(uc);
             dtoList.add(dto);
         }
         return dtoList;
@@ -396,4 +381,14 @@
     public boolean isPublicClient (OAuth2Client oAuth2Client) {
         return oAuth2Client.getType().equals(OAuth2ClientType.PUBLIC);
     }
+
+    public void verifySuperClient (String clientId, String clientSecret)
+            throws KustvaktException {
+        OAuth2Client client = authenticateClient(clientId, clientSecret);
+        if (!client.isSuper()) {
+            throw new KustvaktException(StatusCodes.CLIENT_AUTHORIZATION_FAILED,
+                    "Only super client is allowed to list user registered clients.",
+                    OAuth2Error.UNAUTHORIZED_CLIENT);
+        }
+    }
 }
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 05208f3..63499cf 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
@@ -204,7 +204,6 @@
      */
     @POST
     @Path("/list")
-    @ResourceFilters({ AuthenticationFilter.class, BlockingFilter.class })
     @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
     @Produces(MediaType.APPLICATION_JSON + ";charset=utf-8")
     public List<OAuth2UserClientDto> listUserAuthorizedClients (
@@ -219,13 +218,13 @@
         try {
             scopeService.verifyScope(tokenContext,
                     OAuth2Scope.LIST_USER_CLIENT);
-            if(authorizedOnly){
-                return clientService.listUserAuthorizedClients(username,
-                        superClientId, superClientSecret);
+
+            clientService.verifySuperClient(superClientId, superClientSecret);
+            if (authorizedOnly) {
+                return clientService.listUserAuthorizedClients(username);
             }
             else {
-                return clientService.listUserRegisteredClients(username,
-                        superClientId, superClientSecret);                
+                return clientService.listUserRegisteredClients(username);
             }
         }
         catch (KustvaktException e) {
diff --git a/full/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2ClientControllerTest.java b/full/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2ClientControllerTest.java
index 1cf6c9c..f4b595d 100644
--- a/full/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2ClientControllerTest.java
+++ b/full/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2ClientControllerTest.java
@@ -107,6 +107,31 @@
         assertEquals("CONFIDENTIAL", clientInfo.at("/type").asText());
         assertTrue(clientInfo.at("/is_super").asBoolean());
     }
+    
+    @Test
+    public void testRegisteredPublicClient () throws KustvaktException {
+        String clientName = "OAuth2DoryClient";
+        OAuth2ClientJson json = createOAuth2ClientJson(clientName,
+                OAuth2ClientType.PUBLIC, "Dory's client.");
+
+        registerClient("dory", json);
+
+        MultivaluedMap<String, String> form = new MultivaluedMapImpl();
+        form.add("super_client_id", superClientId);
+        form.add("super_client_secret", clientSecret);
+
+        JsonNode node = testListUserRegisteredClients("dory");
+        
+        assertEquals(1, node.size());
+        assertEquals(clientName, node.at("/0/client_name").asText());
+        assertEquals(OAuth2ClientType.PUBLIC.name(),
+                node.at("/0/client_type").asText());
+        assertTrue(node.at("/0/permitted").asBoolean());
+        assertFalse(node.at("/0/registration_date").isMissingNode());
+        
+        String clientId = node.at("/0/client_id").asText();
+        testDeregisterPublicClient(clientId, "dory");
+    }
 
     @Test
     public void testRegisterConfidentialClient () throws KustvaktException {
@@ -154,6 +179,9 @@
         
         assertFalse(clientInfo.at("/permitted").asBoolean());
         assertNotNull(clientInfo.at("/source"));
+        
+        testListUserRegisteredClients(username);
+        deregisterConfidentialClient(username, clientId);
     }
 
     @Test
@@ -643,6 +671,30 @@
         testRequestTokenWithRevokedRefreshToken(confidentialClientId,
                 clientSecret, refreshToken);
     }
+    
+    private JsonNode testListUserRegisteredClients (String username)
+            throws UniformInterfaceException, ClientHandlerException,
+            KustvaktException {
+        MultivaluedMap<String, String> form = new MultivaluedMapImpl();
+        form.add("super_client_id", superClientId);
+        form.add("super_client_secret", clientSecret);
+
+        ClientResponse response = resource().path(API_VERSION).path("oauth2")
+                .path("client").path("list")
+                .header(Attributes.AUTHORIZATION,
+                        HttpAuthorizationHandler
+                                .createBasicAuthorizationHeaderValue(username,
+                                        "password"))
+                .header(HttpHeaders.CONTENT_TYPE,
+                        ContentType.APPLICATION_FORM_URLENCODED)
+                .entity(form).post(ClientResponse.class);
+
+        assertEquals(Status.OK.getStatusCode(), response.getStatus());
+
+        String entity = response.getEntity(String.class);
+//        System.out.println(entity);
+        return JsonUtils.readTree(entity);
+    }
 
     private void testRevokeAllTokenViaSuperClient (String clientId,
             String userAuthHeader, String accessToken)
@@ -674,37 +726,4 @@
         assertEquals("Access token is invalid",
                 node.at("/errors/0/1").asText());
     }
-
-    @Test
-    public void testListRegisteredClients () throws KustvaktException {
-
-        String clientName = "OAuth2DoryClient";
-        OAuth2ClientJson json = createOAuth2ClientJson(clientName,
-                OAuth2ClientType.PUBLIC, "Dory's client.");
-
-        registerClient("dory", json);
-
-        MultivaluedMap<String, String> form = new MultivaluedMapImpl();
-        form.add("super_client_id", superClientId);
-        form.add("super_client_secret", clientSecret);
-
-        ClientResponse response = resource().path(API_VERSION).path("oauth2")
-                .path("client").path("list")
-                .header(Attributes.AUTHORIZATION, userAuthHeader)
-                .header(HttpHeaders.CONTENT_TYPE,
-                        ContentType.APPLICATION_FORM_URLENCODED)
-                .entity(form).post(ClientResponse.class);
-
-        assertEquals(Status.OK.getStatusCode(), response.getStatus());
-
-        String entity = response.getEntity(String.class);
-        JsonNode node = JsonUtils.readTree(entity);
-
-        assertEquals(1, node.size());
-        assertEquals(clientName, node.at("/0/client_name").asText());
-        assertEquals(OAuth2ClientType.PUBLIC.name(),
-                node.at("/0/client_type").asText());
-        String clientId = node.at("/0/client_id").asText();
-        testDeregisterPublicClient(clientId, "dory");
-    }
 }