Added access token scope join table and updated fix for #21.

Change-Id: Ice8941e62f01c1e8472415737632a8ca5cf61129
diff --git a/full/Changes b/full/Changes
index 9445c94..9182b80 100644
--- a/full/Changes
+++ b/full/Changes
@@ -1,5 +1,5 @@
 version 0.60.2
-26/04/2018
+02/05/2018
 	- implemented OAuth2 client registration (margaretha)
 	- implemented OAuth2 client authentication (margaretha)
 	- changed virtual corpus search to retrieval (margaretha)
@@ -17,6 +17,8 @@
 	- implemented OAuth2 request access token with authorization code grant (margaretha)
 	- added setting default scopes in the config file (margaretha)
 	- fixed loading spring config multiple times in the test suite (margaretha)
+	- added SQLite created_date trigger for access token (margaretha)  
+	- added a join table for access token scopes (margaretha)
 	
 version 0.60.1
 28/03/2018
diff --git a/full/src/main/java/de/ids_mannheim/korap/oauth2/dao/AccessScopeDao.java b/full/src/main/java/de/ids_mannheim/korap/oauth2/dao/AccessScopeDao.java
index 29ab54f..a402c33 100644
--- a/full/src/main/java/de/ids_mannheim/korap/oauth2/dao/AccessScopeDao.java
+++ b/full/src/main/java/de/ids_mannheim/korap/oauth2/dao/AccessScopeDao.java
@@ -40,7 +40,6 @@
             if (!existingScopes.contains(newScope)) {
                 entityManager.persist(newScope);
             }
-            // else skip
         }
 
     }
diff --git a/full/src/main/java/de/ids_mannheim/korap/oauth2/dao/AccessTokenDao.java b/full/src/main/java/de/ids_mannheim/korap/oauth2/dao/AccessTokenDao.java
index 1e3866f..1834c66 100644
--- a/full/src/main/java/de/ids_mannheim/korap/oauth2/dao/AccessTokenDao.java
+++ b/full/src/main/java/de/ids_mannheim/korap/oauth2/dao/AccessTokenDao.java
@@ -20,6 +20,7 @@
         AccessToken accessToken = new AccessToken();
         accessToken.setAuthorization(authorization);
         accessToken.setToken(token);
+        accessToken.setScopes(authorization.getScopes());
         entityManager.persist(accessToken);
     }
 }
diff --git a/full/src/main/java/de/ids_mannheim/korap/oauth2/dao/AuthorizationDao.java b/full/src/main/java/de/ids_mannheim/korap/oauth2/dao/AuthorizationDao.java
index 0486420..f6f505f 100644
--- a/full/src/main/java/de/ids_mannheim/korap/oauth2/dao/AuthorizationDao.java
+++ b/full/src/main/java/de/ids_mannheim/korap/oauth2/dao/AuthorizationDao.java
@@ -7,7 +7,6 @@
 import javax.persistence.Query;
 import javax.persistence.criteria.CriteriaBuilder;
 import javax.persistence.criteria.CriteriaQuery;
-import javax.persistence.criteria.Expression;
 import javax.persistence.criteria.Predicate;
 import javax.persistence.criteria.Root;
 
@@ -60,7 +59,8 @@
         return (Authorization) q.getSingleResult();
     }
 
-    public void updateAuthorization (Authorization authorization) {
-        entityManager.merge(authorization);
+    public Authorization updateAuthorization (Authorization authorization) {
+        authorization = entityManager.merge(authorization);
+        return authorization;
     }
 }
diff --git a/full/src/main/java/de/ids_mannheim/korap/oauth2/entity/AccessScope.java b/full/src/main/java/de/ids_mannheim/korap/oauth2/entity/AccessScope.java
index 29c94eb..cca86ac 100644
--- a/full/src/main/java/de/ids_mannheim/korap/oauth2/entity/AccessScope.java
+++ b/full/src/main/java/de/ids_mannheim/korap/oauth2/entity/AccessScope.java
@@ -27,7 +27,10 @@
     }
 
     @ManyToMany(mappedBy = "scopes", fetch = FetchType.LAZY)
-    private List<Authorization> authorizationCodes;
+    private List<Authorization> authorizations;
+    
+    @ManyToMany(mappedBy = "scopes", fetch = FetchType.LAZY)
+    private List<AccessToken> accessTokens;
 
     @Override
     public String toString () {
diff --git a/full/src/main/java/de/ids_mannheim/korap/oauth2/entity/AccessToken.java b/full/src/main/java/de/ids_mannheim/korap/oauth2/entity/AccessToken.java
index 5d36723..145f798 100644
--- a/full/src/main/java/de/ids_mannheim/korap/oauth2/entity/AccessToken.java
+++ b/full/src/main/java/de/ids_mannheim/korap/oauth2/entity/AccessToken.java
@@ -1,6 +1,7 @@
 package de.ids_mannheim.korap.oauth2.entity;
 
 import java.time.ZonedDateTime;
+import java.util.Set;
 
 import javax.persistence.Column;
 import javax.persistence.Entity;
@@ -9,8 +10,11 @@
 import javax.persistence.GenerationType;
 import javax.persistence.Id;
 import javax.persistence.JoinColumn;
+import javax.persistence.JoinTable;
+import javax.persistence.ManyToMany;
 import javax.persistence.OneToOne;
 import javax.persistence.Table;
+import javax.persistence.UniqueConstraint;
 
 import lombok.Getter;
 import lombok.Setter;
@@ -32,8 +36,18 @@
     @Column(name = "total_attempts")
     private int totalAttempts;
     
-    @OneToOne(fetch=FetchType.EAGER)
+    @OneToOne(fetch=FetchType.LAZY)
     @JoinColumn(name="authorization_id")
     private Authorization authorization;
     
+    @ManyToMany(fetch = FetchType.EAGER)
+    @JoinTable(name = "oauth2_access_token_scope",
+            joinColumns = @JoinColumn(name = "token_id",
+                    referencedColumnName = "id"),
+            inverseJoinColumns = @JoinColumn(name = "scope_id",
+                    referencedColumnName = "id"),
+            uniqueConstraints = @UniqueConstraint(
+                    columnNames = { "token_id", "scope_id" }))
+    private Set<AccessScope> scopes;
+    
 }
diff --git a/full/src/main/java/de/ids_mannheim/korap/oauth2/entity/Authorization.java b/full/src/main/java/de/ids_mannheim/korap/oauth2/entity/Authorization.java
index 4113f48..207512f 100644
--- a/full/src/main/java/de/ids_mannheim/korap/oauth2/entity/Authorization.java
+++ b/full/src/main/java/de/ids_mannheim/korap/oauth2/entity/Authorization.java
@@ -33,7 +33,7 @@
     private String userId;
     @Column(name = "redirect_uri")
     private String redirectURI;
-    @Column(name = "created_date")
+    @Column(name = "created_date", updatable=false)
     private ZonedDateTime createdDate;
     @Column(name = "is_revoked")
     private boolean isRevoked;
diff --git a/full/src/main/java/de/ids_mannheim/korap/oauth2/service/OAuth2AuthorizationService.java b/full/src/main/java/de/ids_mannheim/korap/oauth2/service/OAuth2AuthorizationService.java
index 8b654a5..5c1d02b 100644
--- a/full/src/main/java/de/ids_mannheim/korap/oauth2/service/OAuth2AuthorizationService.java
+++ b/full/src/main/java/de/ids_mannheim/korap/oauth2/service/OAuth2AuthorizationService.java
@@ -167,7 +167,7 @@
     }
 
 
-    public void verifyAuthorization (String code, String clientId,
+    public Authorization verifyAuthorization (String code, String clientId,
             String redirectURI) throws KustvaktException {
         Authorization authorization =
                 authorizationDao.retrieveAuthorizationCode(code, clientId);
@@ -194,7 +194,9 @@
         }
 
         authorization.setRevoked(true);
-        authorizationDao.updateAuthorization(authorization);
+        authorization = authorizationDao.updateAuthorization(authorization);
+        
+        return authorization;
     }
 
     public void addTotalAttempts (Authorization authorization) {
diff --git a/full/src/main/java/de/ids_mannheim/korap/oauth2/service/OAuth2TokenService.java b/full/src/main/java/de/ids_mannheim/korap/oauth2/service/OAuth2TokenService.java
index 067ad81..a2d1ba5 100644
--- a/full/src/main/java/de/ids_mannheim/korap/oauth2/service/OAuth2TokenService.java
+++ b/full/src/main/java/de/ids_mannheim/korap/oauth2/service/OAuth2TokenService.java
@@ -96,10 +96,9 @@
             throws KustvaktException, OAuthSystemException {
 
         clientService.authenticateClient(clientId, clientSecret);
-        authorizationService.verifyAuthorization(authorizationCode, clientId,
-                redirectURI);
-
-        return createsAccessTokenResponse();
+        Authorization authorization = authorizationService
+                .verifyAuthorization(authorizationCode, clientId, redirectURI);
+        return createsAccessTokenResponse(authorization);
     }
 
 
diff --git a/full/src/main/resources/db/new-sqlite/V1.1__create_virtual_corpus_tables.sql b/full/src/main/resources/db/new-sqlite/V1.1__create_virtual_corpus_tables.sql
index d2702f7..462e85a 100644
--- a/full/src/main/resources/db/new-sqlite/V1.1__create_virtual_corpus_tables.sql
+++ b/full/src/main/resources/db/new-sqlite/V1.1__create_virtual_corpus_tables.sql
@@ -1,14 +1,14 @@
 CREATE TABLE IF NOT EXISTS role (
   id INTEGER PRIMARY KEY AUTOINCREMENT,
-  name varchar(100) NOT NULL
+  name VARCHAR(100) NOT NULL
 );
 
 CREATE UNIQUE INDEX role_index on role(name);
 
 CREATE TABLE IF NOT EXISTS privilege (
   id INTEGER PRIMARY KEY AUTOINCREMENT,
-  name varchar(20) NOT NULL,
-  role_id int NOT NULL,
+  name VARCHAR(20) NOT NULL,
+  role_id INTEGER NOT NULL,
   FOREIGN KEY (role_id) 
   	REFERENCES role (id)
   	ON DELETE CASCADE
@@ -19,10 +19,10 @@
 
 CREATE TABLE IF NOT EXISTS user_group (
   id INTEGER PRIMARY KEY AUTOINCREMENT,
-  name varchar(100) NOT NULL,
-  status varchar(100) NOT NULL,
-  created_by varchar(100) NOT NULL,
-  deleted_by varchar(100) DEFAULT NULL
+  name VARCHAR(100) NOT NULL,
+  status VARCHAR(100) NOT NULL,
+  created_by VARCHAR(100) NOT NULL,
+  deleted_by VARCHAR(100) DEFAULT NULL
 );
 
 CREATE INDEX user_group_index ON user_group(status);
@@ -30,11 +30,11 @@
 
 CREATE TABLE IF NOT EXISTS user_group_member (
   id INTEGER PRIMARY KEY AUTOINCREMENT,
-  user_id varchar(100) NOT NULL,
-  group_id int(11) NOT NULL,
-  status varchar(100) NOT NULL,
-  created_by varchar(100) NOT NULL,
-  deleted_by varchar(100) DEFAULT NULL,
+  user_id VARCHAR(100) NOT NULL,
+  group_id INTEGER NOT NULL,
+  status VARCHAR(100) NOT NULL,
+  created_by VARCHAR(100) NOT NULL,
+  deleted_by VARCHAR(100) DEFAULT NULL,
 -- interprets now as localtime and save it as UTC
   status_date timestamp DEFAULT (datetime('now','localtime')),
   FOREIGN KEY (group_id) 
@@ -49,8 +49,8 @@
 
 CREATE TABLE IF NOT EXISTS group_member_role (
   id INTEGER PRIMARY KEY AUTOINCREMENT,
-  group_member_id int(11) NOT NULL,
-  role_id varchar(100) NOT NULL,
+  group_member_id INTEGER NOT NULL,
+  role_id INTEGER NOT NULL,
   FOREIGN KEY (group_member_id)
   	REFERENCES user_group_member (id)
   	ON DELETE CASCADE,
@@ -65,14 +65,14 @@
 
 CREATE TABLE IF NOT EXISTS virtual_corpus (
   id INTEGER PRIMARY KEY AUTOINCREMENT,
-  name varchar(255) NOT NULL,
-  type varchar(100) NOT NULL,
-  required_access varchar(100) NOT NULL,
-  created_by varchar(100) NOT NULL,
-  description varchar(255) DEFAULT NULL,
-  status varchar(100) DEFAULT NULL,
+  name VARCHAR(255) NOT NULL,
+  type VARCHAR(100) NOT NULL,
+  required_access VARCHAR(100) NOT NULL,
+  created_by VARCHAR(100) NOT NULL,
+  description VARCHAR(255) DEFAULT NULL,
+  status VARCHAR(100) DEFAULT NULL,
   corpus_query TEXT NOT NULL,
-  definition varchar(255) DEFAULT NULL
+  definition VARCHAR(255) DEFAULT NULL
 );
 
 CREATE INDEX virtual_corpus_owner_index ON virtual_corpus(created_by);
@@ -80,12 +80,12 @@
 
 CREATE TABLE IF NOT EXISTS virtual_corpus_access (
   id INTEGER PRIMARY KEY AUTOINCREMENT,
-  virtual_corpus_id int(11) NOT NULL,
-  user_group_id int(11) NOT NULL,
-  status varchar(100) NOT NULL,
-  created_by varchar(100) NOT NULL,
-  approved_by varchar(100) DEFAULT NULL,
-  deleted_by varchar(100) DEFAULT NULL,
+  virtual_corpus_id INTEGER NOT NULL,
+  user_group_id INTEGER NOT NULL,
+  status VARCHAR(100) NOT NULL,
+  created_by VARCHAR(100) NOT NULL,
+  approved_by VARCHAR(100) DEFAULT NULL,
+  deleted_by VARCHAR(100) DEFAULT NULL,
   FOREIGN KEY (user_group_id) 
   	REFERENCES user_group (id)
   	ON DELETE CASCADE,
diff --git a/full/src/main/resources/db/new-sqlite/V1.3__create_admin_table.sql b/full/src/main/resources/db/new-sqlite/V1.3__create_admin_table.sql
index 3f4f32f..f13861c 100644
--- a/full/src/main/resources/db/new-sqlite/V1.3__create_admin_table.sql
+++ b/full/src/main/resources/db/new-sqlite/V1.3__create_admin_table.sql
@@ -1,5 +1,5 @@
 CREATE TABLE IF NOT EXISTS admin(
-	id INTEGER PRIMARY KEY AUTOINCREMENT,
+	id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
 	user_id varchar(100) NOT NULL
 );
 
diff --git a/full/src/main/resources/db/new-sqlite/V1.4__oauth2_tables.sql b/full/src/main/resources/db/new-sqlite/V1.4__oauth2_tables.sql
index a76e745..ff1e130 100644
--- a/full/src/main/resources/db/new-sqlite/V1.4__oauth2_tables.sql
+++ b/full/src/main/resources/db/new-sqlite/V1.4__oauth2_tables.sql
@@ -3,15 +3,15 @@
 -- oauth2 db tables
 CREATE TABLE IF NOT EXISTS oauth2_client (
 	id VARCHAR(100) PRIMARY KEY NOT NULL,
-	name VARCHAR(200) NOT NULL,
-	secret VARCHAR(200),
-	type VARCHAR(200) NOT NULL,
+	name VARCHAR(255) NOT NULL,
+	secret VARCHAR(255),
+	type VARCHAR(255) NOT NULL,
 	native BOOLEAN DEFAULT FALSE,
 	url TEXT NOT NULL,
 	url_hashcode INTEGER NOT NULL,
 	redirect_uri TEXT NOT NULL,
 	registered_by VARCHAR(100) NOT NULL,
-	description VARCHAR(250) NOT NULL
+	description VARCHAR(255) NOT NULL
 );
 
 CREATE UNIQUE INDEX client_id_index on oauth2_client(id);
@@ -19,7 +19,7 @@
 
 CREATE TABLE IF NOT EXISTS oauth2_authorization (
 	id INTEGER PRIMARY KEY AUTOINCREMENT,
-	code VARCHAR(250) NOT NULL,
+	code VARCHAR(255) NOT NULL,
 	client_id VARCHAR(100) NOT NULL,
 	user_id VARCHAR(100) NOT NULL,
 	redirect_uri TEXT DEFAULT NULL,
@@ -33,13 +33,13 @@
 CREATE UNIQUE INDEX authorization_index on oauth2_authorization(code, client_id);
 
 CREATE TABLE IF NOT EXISTS oauth2_access_scope (
-	id VARCHAR(200) PRIMARY KEY NOT NULL
+	id VARCHAR(255) PRIMARY KEY NOT NULL
 );
 
 CREATE TABLE IF NOT EXISTS oauth2_authorization_scope (
 	id INTEGER PRIMARY KEY AUTOINCREMENT,
 	authorization_id INTEGER NOT NULL,
-	scope_id VARCHAR(200) NOT NULL,
+	scope_id VARCHAR(100) NOT NULL,
 	FOREIGN KEY (authorization_id)
 	   REFERENCES oauth2_authorization(id),
 	FOREIGN KEY (scope_id)
@@ -49,16 +49,9 @@
 CREATE UNIQUE INDEX authorization_scope_index on 
 	oauth2_authorization_scope(authorization_id, scope_id);
 
-CREATE TRIGGER insert_created_date AFTER INSERT ON oauth2_authorization
-     BEGIN
-      UPDATE oauth2_authorization
-      SET created_date = DATETIME('now', 'localtime')  
-      WHERE rowid = new.rowid;
-     END;
-     
 CREATE TABLE IF NOT EXISTS oauth2_access_token (
 	id INTEGER PRIMARY KEY AUTOINCREMENT,
-	token VARCHAR(300) NOT NULL,
+	token VARCHAR(255) NOT NULL,
 	authorization_id INTEGER DEFAULT NULL,
 	created_date timestamp DEFAULT (datetime('now','localtime')),
 	is_revoked BOOLEAN DEFAULT 0,
@@ -67,3 +60,8 @@
 	   REFERENCES oauth2_authorization(id)
 );
 
+CREATE TABLE oauth2_access_token_scope (
+	token_id INTEGER NOT NULL, 
+	scope_id VARCHAR(100) NOT NULL, 
+	primary key (token_id, scope_id)
+);
diff --git a/full/src/main/resources/db/new-sqlite/V1.5__oauth2_triggers.sql b/full/src/main/resources/db/new-sqlite/V1.5__oauth2_triggers.sql
new file mode 100644
index 0000000..d62e4df
--- /dev/null
+++ b/full/src/main/resources/db/new-sqlite/V1.5__oauth2_triggers.sql
@@ -0,0 +1,14 @@
+CREATE TRIGGER insert_authorization_date AFTER INSERT ON oauth2_authorization
+     BEGIN
+      UPDATE oauth2_authorization
+      SET created_date = DATETIME('now', 'localtime')  
+      WHERE rowid = new.rowid;
+     END;
+
+CREATE TRIGGER insert_access_token_date AFTER INSERT ON oauth2_access_token
+     BEGIN
+      UPDATE oauth2_access_token
+      SET created_date = DATETIME('now', 'localtime')  
+      WHERE rowid = new.rowid;
+     END;
+     
\ No newline at end of file
diff --git a/full/src/main/resources/db/new-sqlite/V1__initial_version.sql b/full/src/main/resources/db/new-sqlite/V1__initial_version.sql
index 2f8002c..af81b54 100644
--- a/full/src/main/resources/db/new-sqlite/V1__initial_version.sql
+++ b/full/src/main/resources/db/new-sqlite/V1__initial_version.sql
@@ -12,7 +12,7 @@
 	id INTEGER PRIMARY KEY AUTOINCREMENT,
 	annotation1 INTEGER NOT NULL,
 	annotation2 INTEGER NOT NULL,
-	description VARCHAR(300) NOT NULL,
+	description VARCHAR(255) NOT NULL,
 	FOREIGN KEY (annotation1)
 		REFERENCES annotation (id)
 		ON DELETE CASCADE,
diff --git a/full/src/test/java/de/ids_mannheim/korap/config/SpringJerseyTest.java b/full/src/test/java/de/ids_mannheim/korap/config/SpringJerseyTest.java
index 5c821e8..619b795 100644
--- a/full/src/test/java/de/ids_mannheim/korap/config/SpringJerseyTest.java
+++ b/full/src/test/java/de/ids_mannheim/korap/config/SpringJerseyTest.java
@@ -1,13 +1,17 @@
 package de.ids_mannheim.korap.config;
 
+import java.io.IOException;
 import java.util.concurrent.ThreadLocalRandom;
 
 import org.junit.runner.RunWith;
+import org.springframework.beans.BeansException;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.context.ApplicationContext;
+import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
+import org.springframework.beans.factory.support.DefaultListableBeanFactory;
+import org.springframework.context.support.GenericApplicationContext;
 import org.springframework.test.context.ContextConfiguration;
 import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
-import org.springframework.web.context.WebApplicationContext;
+import org.springframework.web.context.support.AbstractRefreshableWebApplicationContext;
 
 import com.sun.jersey.spi.spring.container.servlet.SpringServlet;
 import com.sun.jersey.test.framework.AppDescriptor;
@@ -22,7 +26,7 @@
 public abstract class SpringJerseyTest extends JerseyTest {
 
     @Autowired
-    protected ApplicationContext applicationContext;
+    protected GenericApplicationContext applicationContext;
 
     private static String[] classPackages =
             new String[] { "de.ids_mannheim.korap.web.controller",
@@ -37,16 +41,39 @@
     }
 
     @Override
-    protected AppDescriptor configure () {
+    public void setUp () throws Exception {
 
         StaticContextLoaderListener.applicationContext =
-                (WebApplicationContext) applicationContext;
+                new AbstractRefreshableWebApplicationContext() {
 
+                    ConfigurableListableBeanFactory existingBeanFactory =
+                            applicationContext.getBeanFactory();
+
+                    @Override
+                    protected void loadBeanDefinitions (
+                            DefaultListableBeanFactory beanFactory)
+                            throws BeansException, IOException {
+
+                        String[] beanDefinitionNames =
+                                existingBeanFactory.getBeanDefinitionNames();
+                        for (String beanName : beanDefinitionNames) {
+                            beanFactory.registerBeanDefinition(beanName,
+                                    existingBeanFactory
+                                            .getBeanDefinition(beanName));
+                        }
+                    }
+                };
+
+        super.setUp();
+    }
+
+    @Override
+    protected AppDescriptor configure () {
         return new WebAppDescriptor.Builder(classPackages)
                 .servletClass(SpringServlet.class)
                 .contextListenerClass(StaticContextLoaderListener.class)
-                .contextParam("contextConfigLocation",
-                        "classpath:test-config.xml")
+                //                .contextParam("contextConfigLocation",
+                //                        "classpath:test-config.xml")
                 .build();
     }
 
diff --git a/full/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2ControllerTest.java b/full/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2ControllerTest.java
index 92461ad..ca4d0ee 100644
--- a/full/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2ControllerTest.java
+++ b/full/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2ControllerTest.java
@@ -145,7 +145,8 @@
         authForm.add("client_id", "fCBbQkAyYzI4NzUxMg");
         authForm.add("username", "dory");
         authForm.add("password", "password");
-//        form.add("scope", "read");
+        authForm.add("scope", "read_username");
+        
         ClientResponse response = requestAuthorizationConfidentialClient(authForm);
         URI redirectUri = response.getLocation();
         String code = redirectUri.getQuery().split("=")[1];