Added maximum limit to custom refresh token expiry

Change-Id: Ie92d181b1941df867bb5377c2f2f6bf61ed56825
diff --git a/core/Changes b/core/Changes
index 57aa6ca..6a157f5 100644
--- a/core/Changes
+++ b/core/Changes
@@ -1,5 +1,9 @@
 # version 0.68
 
+ - Added OAuth2 scope: INSTALL_USER_CLIENT
+ - Added status codes
+ 
+
 # version 0.67.1
 
 # version 0.67
diff --git a/core/src/main/java/de/ids_mannheim/korap/exceptions/StatusCodes.java b/core/src/main/java/de/ids_mannheim/korap/exceptions/StatusCodes.java
index ac77657..33d85d7 100644
--- a/core/src/main/java/de/ids_mannheim/korap/exceptions/StatusCodes.java
+++ b/core/src/main/java/de/ids_mannheim/korap/exceptions/StatusCodes.java
@@ -147,6 +147,8 @@
     public static final int ID_TOKEN_SIGNING_FAILED = 1814;
     public static final int USER_REAUTHENTICATION_REQUIRED = 1815;
     
+    public static final int INVALID_REFRESH_TOKEN_EXPIRY = 1816;
+    
     /**
      * 1850 Plugins
      */
diff --git a/full/Changes b/full/Changes
index aa7b5f2..4f7de42 100644
--- a/full/Changes
+++ b/full/Changes
@@ -11,6 +11,9 @@
  - Handled super client id in plugin installation
  - Deprecated UserClientDto and uses ClientinfoDto instead
  - Updated redirect URI error message for coherence
+2022-05-27
+ - Added maximum limit to custom refresh token expiry
+ 
  
 # version 0.67.1
 
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 6961092..66ba01f 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
@@ -78,9 +78,20 @@
         else {
             client.setPermitted(true);
         }
-        if (refreshTokenExpiry <= 0 && type.equals(OAuth2ClientType.CONFIDENTIAL)) {
-           refreshTokenExpiry = config.getRefreshTokenLongExpiry();
+        if (refreshTokenExpiry <= 0) {
+            if (type.equals(OAuth2ClientType.CONFIDENTIAL)){
+                refreshTokenExpiry = config.getRefreshTokenLongExpiry();
+            }
         }
+        else if (type.equals(OAuth2ClientType.PUBLIC)){
+            throw new KustvaktException(StatusCodes.INVALID_REFRESH_TOKEN_EXPIRY,
+                    "Custom refresh token expiry is only applicable for confidential clients");
+        }
+        else if (refreshTokenExpiry > 31536000 ){
+            throw new KustvaktException(StatusCodes.INVALID_REFRESH_TOKEN_EXPIRY,
+                    "Maximum refresh token expiry is 31536000 seconds (1 year)");
+        }
+        
         client.setRefreshTokenExpiry(refreshTokenExpiry);
         entityManager.persist(client);
     }
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 7ea1485..fc1e24c 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
@@ -237,6 +237,55 @@
     }
     
     @Test
+    public void testRegisterPublicClientWithRefreshTokenExpiry ()
+            throws UniformInterfaceException, ClientHandlerException,
+            KustvaktException {
+        OAuth2ClientJson clientJson =
+                createOAuth2ClientJson("OAuth2PublicClient",
+                        OAuth2ClientType.PUBLIC, "A public test client.");
+        clientJson.setRefreshTokenExpiry(31535000);
+        ClientResponse response = registerClient(username, clientJson);
+        JsonNode node = JsonUtils.readTree(response.getEntity(String.class));
+        assertEquals("invalid_request", node.at("/error").asText());
+        assertEquals(Status.BAD_REQUEST.getStatusCode(), response.getStatus());
+    }
+    
+    @Test
+    public void testRegisterConfidentialClientWithRefreshTokenExpiry ()
+            throws UniformInterfaceException, ClientHandlerException,
+            KustvaktException {
+        int expiry = 31535000;
+        OAuth2ClientJson clientJson =
+                createOAuth2ClientJson("OAuth2 Confidential Client",
+                        OAuth2ClientType.CONFIDENTIAL, "A confidential client.");
+        clientJson.setRefreshTokenExpiry(expiry);
+        ClientResponse response = registerClient(username, clientJson);
+        JsonNode node = JsonUtils.readTree(response.getEntity(String.class));
+        String clientId = node.at("/client_id").asText();
+        JsonNode clientInfo = retrieveClientInfo(clientId, username);
+        assertEquals(expiry, clientInfo.at("/refresh_token_expiry").asInt());
+        
+        deregisterConfidentialClient(username, clientId);
+    }
+    
+    @Test
+    public void testRegisterConfidentialClientWithInvalidRefreshTokenExpiry ()
+            throws UniformInterfaceException, ClientHandlerException,
+            KustvaktException {
+        OAuth2ClientJson clientJson = createOAuth2ClientJson(
+                "OAuth2 Confidential Client", OAuth2ClientType.CONFIDENTIAL,
+                "A confidential client.");
+        clientJson.setRefreshTokenExpiry(31537000);
+        ClientResponse response = registerClient(username, clientJson);
+        JsonNode node = JsonUtils.readTree(response.getEntity(String.class));
+        assertEquals(
+                "Maximum refresh token expiry is 31536000 seconds (1 year)",
+                node.at("/error_description").asText());
+        assertEquals("invalid_request", node.at("/error").asText());
+        assertEquals(Status.BAD_REQUEST.getStatusCode(), response.getStatus());
+    }
+    
+    @Test
     public void testRegisterClientInvalidURL ()
             throws UniformInterfaceException, ClientHandlerException,
             KustvaktException {