Handled redundant plugin installation

Change-Id: Idbc585f8e063be8021eef95353c0143cac694e39
diff --git a/full/Changes b/full/Changes
index 18bb5ef..3dd1d7f 100644
--- a/full/Changes
+++ b/full/Changes
@@ -7,7 +7,8 @@
    list API response)
  - Added installed_plugins table
  - Added a new API: install plugin 
-
+ - Handled redundant plugin installation
+ 
 
 # version 0.67.1
 
diff --git a/full/src/main/java/de/ids_mannheim/korap/authentication/OAuth2Authentication.java b/full/src/main/java/de/ids_mannheim/korap/authentication/OAuth2Authentication.java
index 2b1ea1e..14dbd42 100644
--- a/full/src/main/java/de/ids_mannheim/korap/authentication/OAuth2Authentication.java
+++ b/full/src/main/java/de/ids_mannheim/korap/authentication/OAuth2Authentication.java
@@ -9,6 +9,7 @@
 import de.ids_mannheim.korap.constant.TokenType;
 import de.ids_mannheim.korap.exceptions.KustvaktException;
 import de.ids_mannheim.korap.exceptions.StatusCodes;
+import de.ids_mannheim.korap.oauth2.constant.OAuth2Error;
 import de.ids_mannheim.korap.oauth2.dao.AccessTokenDao;
 import de.ids_mannheim.korap.oauth2.entity.AccessToken;
 import de.ids_mannheim.korap.oauth2.service.OAuth2ScopeServiceImpl;
@@ -35,7 +36,7 @@
         AccessToken accessToken = accessDao.retrieveAccessToken(authToken);
         if (accessToken.isRevoked()) {
             throw new KustvaktException(StatusCodes.INVALID_ACCESS_TOKEN,
-                    "Access token is invalid");
+                    "Access token is invalid", OAuth2Error.INVALID_TOKEN);
         }
 
         String scopes = scopeService
diff --git a/full/src/main/java/de/ids_mannheim/korap/oauth2/dao/InstalledPluginDao.java b/full/src/main/java/de/ids_mannheim/korap/oauth2/dao/InstalledPluginDao.java
index 8cd5368..284c0fd 100644
--- a/full/src/main/java/de/ids_mannheim/korap/oauth2/dao/InstalledPluginDao.java
+++ b/full/src/main/java/de/ids_mannheim/korap/oauth2/dao/InstalledPluginDao.java
@@ -4,15 +4,24 @@
 import java.time.ZonedDateTime;
 
 import javax.persistence.EntityManager;
+import javax.persistence.NoResultException;
 import javax.persistence.PersistenceContext;
+import javax.persistence.Query;
+import javax.persistence.criteria.CriteriaBuilder;
+import javax.persistence.criteria.CriteriaQuery;
+import javax.persistence.criteria.Join;
+import javax.persistence.criteria.Root;
 
 import org.springframework.stereotype.Repository;
 import org.springframework.transaction.annotation.Transactional;
 
 import de.ids_mannheim.korap.config.Attributes;
 import de.ids_mannheim.korap.entity.InstalledPlugin;
+import de.ids_mannheim.korap.entity.InstalledPlugin_;
 import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.exceptions.StatusCodes;
 import de.ids_mannheim.korap.oauth2.entity.OAuth2Client;
+import de.ids_mannheim.korap.oauth2.entity.OAuth2Client_;
 import de.ids_mannheim.korap.utils.ParameterChecker;
 
 @Repository
@@ -34,4 +43,31 @@
         entityManager.persist(p);
         return p;
     }
+
+    public InstalledPlugin retrieveInstalledPlugin (String clientId,
+            String installedBy) throws KustvaktException {
+        ParameterChecker.checkStringValue(clientId, "client_id");
+        ParameterChecker.checkStringValue(installedBy, "installedBy");
+
+        CriteriaBuilder builder = entityManager.getCriteriaBuilder();
+        CriteriaQuery<InstalledPlugin> query =
+                builder.createQuery(InstalledPlugin.class);
+
+        Root<InstalledPlugin> root = query.from(InstalledPlugin.class);
+        Join<InstalledPlugin, OAuth2Client> client =
+                root.join(InstalledPlugin_.client);
+        query.select(root);
+        query.where(builder.and(
+                builder.equal(root.get(InstalledPlugin_.INSTALLED_BY),
+                        installedBy),
+                builder.equal(client.get(OAuth2Client_.id), clientId)));
+
+        Query q = entityManager.createQuery(query);
+        try {
+            return (InstalledPlugin) q.getSingleResult();
+        }
+        catch (NoResultException e) {
+            throw new KustvaktException(StatusCodes.NO_RESOURCE_FOUND);
+        }
+    }
 }
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 acaaa88..9e319f8 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
@@ -25,9 +25,9 @@
 import de.ids_mannheim.korap.oauth2.constant.OAuth2Error;
 import de.ids_mannheim.korap.oauth2.dao.AccessTokenDao;
 import de.ids_mannheim.korap.oauth2.dao.AuthorizationDao;
+import de.ids_mannheim.korap.oauth2.dao.InstalledPluginDao;
 import de.ids_mannheim.korap.oauth2.dao.OAuth2ClientDao;
 import de.ids_mannheim.korap.oauth2.dao.RefreshTokenDao;
-import de.ids_mannheim.korap.oauth2.dao.InstalledPluginDao;
 import de.ids_mannheim.korap.oauth2.dto.OAuth2ClientDto;
 import de.ids_mannheim.korap.oauth2.dto.OAuth2ClientInfoDto;
 import de.ids_mannheim.korap.oauth2.dto.OAuth2UserClientDto;
@@ -400,8 +400,14 @@
         OAuth2Client client = clientDao.retrieveClientById(clientId);
         if (!client.isPermitted()) {
             throw new KustvaktException(StatusCodes.PLUGIN_NOT_PERMITTED,
-                    "Plugin is not permitted");
+                    "Plugin is not permitted", clientId);
         }
+        
+        if (isPluginInstalled(clientId,installedBy)) {
+            throw new KustvaktException(StatusCodes.PLUGIN_HAS_BEEN_INSTALLED,
+                    "Plugin has been installed", clientId);
+        }
+        
         InstalledPlugin plugin =
                 pluginDao.storeUserPlugin(client, installedBy);
         
@@ -409,6 +415,16 @@
         return dto;
     }
 
+    private boolean isPluginInstalled (String clientId, String installedBy) {
+        try {
+            pluginDao.retrieveInstalledPlugin(clientId, installedBy);
+        }
+        catch (KustvaktException e) {
+            return false;
+        }
+        return true;
+    }
+
     private List<OAuth2UserClientDto> createClientDtos (
             List<OAuth2Client> userClients) throws KustvaktException {
         List<OAuth2UserClientDto> dtoList = new ArrayList<>(userClients.size());
@@ -433,6 +449,4 @@
                     OAuth2Error.UNAUTHORIZED_CLIENT);
         }
     }
-
-    
 }
diff --git a/full/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2PluginTest.java b/full/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2PluginTest.java
index 83472e5..55f7dbb 100644
--- a/full/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2PluginTest.java
+++ b/full/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2PluginTest.java
@@ -50,7 +50,8 @@
         String clientSecret = node.at("/client_secret").asText();
         assertNotNull(clientId);
         assertNotNull(clientSecret);
-
+        
+        testInstallPluginNotPermitted(clientId);
         testRetrievePluginInfo(clientId);
 
         node = listPlugins(false);
@@ -177,7 +178,7 @@
 
         String entity = response.getEntity(String.class);
         JsonNode node = JsonUtils.readTree(entity);
-
+        
         assertEquals(Status.UNAUTHORIZED.getStatusCode(), response.getStatus());
         assertEquals(StatusCodes.INVALID_ACCESS_TOKEN,
                 node.at("/errors/0/0").asInt());
@@ -256,8 +257,35 @@
         assertFalse(node.at("/installed_date").isMissingNode());
         
         assertEquals(Status.OK.getStatusCode(), response.getStatus());
+        
+        testInstallPluginRedundant(form);
+    }
+    
+    private void testInstallPluginRedundant (
+            MultivaluedMap<String, String> form)
+            throws UniformInterfaceException, ClientHandlerException,
+            KustvaktException {
+        ClientResponse response = installPlugin(form);
+        String entity = response.getEntity(String.class);
+        JsonNode node = JsonUtils.readTree(entity);
+        assertEquals(StatusCodes.PLUGIN_HAS_BEEN_INSTALLED,
+                node.at("/errors/0/0").asInt());
+        assertEquals(Status.BAD_REQUEST.getStatusCode(), response.getStatus());
     }
 
+    private void testInstallPluginNotPermitted (String clientId)
+            throws UniformInterfaceException, ClientHandlerException,
+            KustvaktException {
+        MultivaluedMap<String, String> form = getSuperClientForm();
+        form.add("client_id", clientId);
+        ClientResponse response = installPlugin(form);
+        String entity = response.getEntity(String.class);
+        JsonNode node = JsonUtils.readTree(entity);
+        assertEquals(StatusCodes.PLUGIN_NOT_PERMITTED,
+                node.at("/errors/0/0").asInt());
+        assertEquals(Status.BAD_REQUEST.getStatusCode(), response.getStatus());
+    }
+    
     @Test
     public void testInstallPluginMissingClientId () throws UniformInterfaceException,
             ClientHandlerException, KustvaktException {