Merge "Added columns to the oauth2_client table and updated client registration"
diff --git a/full/Changes b/full/Changes
index f10e02c..5117c88 100644
--- a/full/Changes
+++ b/full/Changes
@@ -4,6 +4,10 @@
- Updated query and user-group name pattern.
2022-04-08
- Added redirect_uri to client info API.
+2022-04-11
+ - Added registration_date, refresh_token_expiry, source and is_permitted
+ to the oauth2_client database table, and updated the OAuth2 client
+ registration mechanism.
# version 0.65.2
diff --git a/full/src/main/java/de/ids_mannheim/korap/config/FullConfiguration.java b/full/src/main/java/de/ids_mannheim/korap/config/FullConfiguration.java
index 969d4b6..32f98f8 100644
--- a/full/src/main/java/de/ids_mannheim/korap/config/FullConfiguration.java
+++ b/full/src/main/java/de/ids_mannheim/korap/config/FullConfiguration.java
@@ -79,6 +79,7 @@
private int accessTokenLongExpiry;
private int accessTokenExpiry;
+ private int refreshTokenLongExpiry;
private int refreshTokenExpiry;
private int authorizationCodeExpiry;
@@ -270,6 +271,8 @@
setAccessTokenLongExpiry(TimeUtils.convertTimeToSeconds(
properties.getProperty("oauth2.access.token.long.expiry", "365D")));
+ setRefreshTokenLongExpiry(TimeUtils.convertTimeToSeconds(
+ properties.getProperty("oauth2.refresh.token.long.expiry", "365D")));
}
private void setMailConfiguration (Properties properties) {
@@ -663,4 +666,12 @@
public void setAccessTokenLongExpiry (int accessTokenLongExpiry) {
this.accessTokenLongExpiry = accessTokenLongExpiry;
}
+
+ public int getRefreshTokenLongExpiry () {
+ return refreshTokenLongExpiry;
+ }
+
+ public void setRefreshTokenLongExpiry (int refreshTokenLongExpiry) {
+ this.refreshTokenLongExpiry = refreshTokenLongExpiry;
+ }
}
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 dff99d6..989deb2 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
@@ -15,10 +15,14 @@
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
+import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
+import com.fasterxml.jackson.databind.JsonNode;
+
import de.ids_mannheim.korap.config.Attributes;
+import de.ids_mannheim.korap.config.FullConfiguration;
import de.ids_mannheim.korap.exceptions.KustvaktException;
import de.ids_mannheim.korap.exceptions.StatusCodes;
import de.ids_mannheim.korap.oauth2.constant.OAuth2ClientType;
@@ -42,10 +46,13 @@
@PersistenceContext
private EntityManager entityManager;
+ @Autowired
+ private FullConfiguration config;
public void registerClient (String id, String secretHashcode, String name,
OAuth2ClientType type, String url, String redirectURI,
- String registeredBy, String description) throws KustvaktException {
+ String registeredBy, String description, int refreshTokenExpiry,
+ JsonNode source) throws KustvaktException {
ParameterChecker.checkStringValue(id, "client_id");
ParameterChecker.checkStringValue(name, "client_name");
ParameterChecker.checkObjectValue(type, "client_type");
@@ -63,7 +70,17 @@
client.setUrl(url);
client.setRedirectURI(redirectURI);
client.setRegisteredBy(registeredBy);
+ client.setRegistrationDate(ZonedDateTime.now());
client.setDescription(description);
+ if (source !=null && !source.isNull()) {
+ client.setSource(source.toString());
+ }
+ else {
+ client.setPermitted(true);
+ }
+ if (refreshTokenExpiry <= 0) {
+ refreshTokenExpiry = config.getRefreshTokenLongExpiry();
+ }
entityManager.persist(client);
}
diff --git a/full/src/main/java/de/ids_mannheim/korap/oauth2/dto/OAuth2ClientInfoDto.java b/full/src/main/java/de/ids_mannheim/korap/oauth2/dto/OAuth2ClientInfoDto.java
index 3f22288..04dc175 100644
--- a/full/src/main/java/de/ids_mannheim/korap/oauth2/dto/OAuth2ClientInfoDto.java
+++ b/full/src/main/java/de/ids_mannheim/korap/oauth2/dto/OAuth2ClientInfoDto.java
@@ -1,11 +1,14 @@
package de.ids_mannheim.korap.oauth2.dto;
import com.fasterxml.jackson.annotation.JsonInclude;
-import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.annotation.JsonProperty;
+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;
/** Describes information about an OAuth2 client.
*
@@ -24,9 +27,15 @@
private String redirect_uri;
@JsonProperty("registered_by")
private String registeredBy;
+ @JsonProperty("registration_date")
+ private String registrationDate;
private OAuth2ClientType type;
+
+ @JsonProperty("permitted")
+ private boolean isPermitted;
+ private JsonNode source;
- public OAuth2ClientInfoDto (OAuth2Client client) {
+ public OAuth2ClientInfoDto (OAuth2Client client) throws KustvaktException {
this.id = client.getId();
this.name = client.getName();
this.description = client.getDescription();
@@ -34,7 +43,12 @@
this.url = client.getUrl();
this.registeredBy = client.getRegisteredBy();
this.redirect_uri = client.getRedirectURI();
-
+ this.registrationDate = client.getRegistrationDate().toString();
+ this.isPermitted = client.isPermitted();
+ String source = client.getSource();
+ if (source != null) {
+ this.source = JsonUtils.readTree(source);
+ }
if (client.isSuper()) {
this.isSuper = "true";
}
@@ -103,5 +117,26 @@
public void setRedirect_uri (String redirect_uri) {
this.redirect_uri = redirect_uri;
}
+
+ public String getRegistrationDate () {
+ return registrationDate;
+ }
+ public void setRegistrationDate (String registrationDate) {
+ this.registrationDate = registrationDate;
+ }
+
+ public JsonNode getSource () {
+ return source;
+ }
+ public void setSource (JsonNode source) {
+ this.source = source;
+ }
+
+ public boolean isPermitted () {
+ return isPermitted;
+ }
+ public void setPermitted (boolean isPermitted) {
+ this.isPermitted = isPermitted;
+ }
}
diff --git a/full/src/main/java/de/ids_mannheim/korap/oauth2/entity/OAuth2Client.java b/full/src/main/java/de/ids_mannheim/korap/oauth2/entity/OAuth2Client.java
index fb5cbbb..b7d1031 100644
--- a/full/src/main/java/de/ids_mannheim/korap/oauth2/entity/OAuth2Client.java
+++ b/full/src/main/java/de/ids_mannheim/korap/oauth2/entity/OAuth2Client.java
@@ -1,5 +1,6 @@
package de.ids_mannheim.korap.oauth2.entity;
+import java.time.ZonedDateTime;
import java.util.List;
import javax.persistence.Column;
@@ -33,12 +34,20 @@
private boolean isSuper;
@Column(name = "redirect_uri")
private String redirectURI;
- @Column(name = "registered_by")
+ @Column(name = "registered_by", updatable = false)
private String registeredBy;
+ @Column(name = "registration_date", updatable = false)
+ private ZonedDateTime registrationDate;
+
+ @Column(name = "refresh_token_expiry")
+ private int refresTokenExpiry;
private String description;
-
private String url;
+ private String source;
+ @Column(name = "is_permitted")
+ private boolean isPermitted;
+
@OneToMany(fetch = FetchType.LAZY, mappedBy = "client")
private List<RefreshToken> refreshTokens;
@@ -113,6 +122,13 @@
public void setRegisteredBy (String registeredBy) {
this.registeredBy = registeredBy;
}
+
+ public ZonedDateTime getRegistrationDate () {
+ return registrationDate;
+ }
+ public void setRegistrationDate (ZonedDateTime registrationDate) {
+ this.registrationDate = registrationDate;
+ }
public String getDescription () {
return description;
@@ -130,4 +146,19 @@
this.url = url;
}
+ public String getSource () {
+ return source;
+ }
+
+ public void setSource (String source) {
+ this.source = source;
+ }
+
+ public boolean isPermitted () {
+ return isPermitted;
+ }
+
+ public void setPermitted (boolean isPermitted) {
+ this.isPermitted = isPermitted;
+ }
}
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 505140c..d714111 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,6 +13,8 @@
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;
@@ -128,10 +130,13 @@
String id = codeGenerator.createRandomCode();
id = codeGenerator.filterRandomCode(id);
+
try {
clientDao.registerClient(id, secretHashcode, clientJson.getName(),
clientJson.getType(), url, redirectURI, registeredBy,
- clientJson.getDescription());
+ clientJson.getDescription(),
+ clientJson.getRefreshTokenExpiry(),
+ clientJson.getSource());
}
catch (KustvaktException e) {
throw new KustvaktException(e.getStatusCode(),
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/input/OAuth2ClientJson.java b/full/src/main/java/de/ids_mannheim/korap/web/input/OAuth2ClientJson.java
index b11ac63..7180eda 100644
--- a/full/src/main/java/de/ids_mannheim/korap/web/input/OAuth2ClientJson.java
+++ b/full/src/main/java/de/ids_mannheim/korap/web/input/OAuth2ClientJson.java
@@ -1,6 +1,7 @@
package de.ids_mannheim.korap.web.input;
import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.databind.JsonNode;
import de.ids_mannheim.korap.oauth2.constant.OAuth2ClientType;
@@ -11,6 +12,8 @@
* To accommodate desktop applications such as R, url and redirectURI
* are not compulsory.
*
+ * Source is json description of a plugin.
+ *
* @author margaretha
*
*/
@@ -27,7 +30,11 @@
// the user to after they have authorized a client.
@JsonProperty("redirect_uri")
private String redirectURI;
+ // Default 365 days
+ private int refreshTokenExpiry; // in seconds
+ // plugins
+ private JsonNode source;
public String getName () {
return name;
@@ -68,4 +75,20 @@
public void setDescription (String description) {
this.description = description;
}
+
+ public int getRefreshTokenExpiry () {
+ return refreshTokenExpiry;
+ }
+
+ public void setRefreshTokenExpiry (int refreshTokenExpiry) {
+ this.refreshTokenExpiry = refreshTokenExpiry;
+ }
+
+ public JsonNode getSource () {
+ return source;
+ }
+
+ public void setSource (JsonNode source2) {
+ this.source = source2;
+ }
}
diff --git a/full/src/main/resources/db/sqlite/V1.11__plugin.sql b/full/src/main/resources/db/sqlite/V1.11__plugin.sql
new file mode 100644
index 0000000..94b90ca
--- /dev/null
+++ b/full/src/main/resources/db/sqlite/V1.11__plugin.sql
@@ -0,0 +1,22 @@
+ALTER TABLE oauth2_client
+ ADD COLUMN registration_date TIMESTAMP NOT NULL;
+
+-- default 365 days in seconds
+ALTER TABLE oauth2_client
+ ADD COLUMN refresh_token_expiry INTEGER DEFAULT 31536000;
+
+ALTER TABLE oauth2_client
+ ADD COLUMN source BLOB DEFAULT NULL;
+
+ALTER TABLE oauth2_client
+ ADD COLUMN is_permitted BOOLEAN DEFAULT FALSE;
+
+--CREATE TABLE IF NOT EXISTS user_installed_client (
+-- id INTEGER PRIMARY KEY AUTOINCREMENT,
+-- installed_by VARCHAR(100) NOT NULL,
+-- installed_date TIMESTAMP NOT NULL,
+-- client_id VARCHAR(100) NOT NULL,
+-- FOREIGN KEY (client_id)
+-- REFERENCES oauth2_client(id)
+-- ON DELETE CASCADE
+--);
\ No newline at end of file
diff --git a/full/src/main/resources/db/sqlite/V1.9__query_alteration.sql b/full/src/main/resources/db/sqlite/V1.9__query_alteration.sql
index 0d3bf8c..02cf486 100644
--- a/full/src/main/resources/db/sqlite/V1.9__query_alteration.sql
+++ b/full/src/main/resources/db/sqlite/V1.9__query_alteration.sql
@@ -23,7 +23,6 @@
ON query(name,created_by);
-
ALTER TABLE virtual_corpus_access
RENAME COLUMN virtual_corpus_id TO query_id;
@@ -31,8 +30,6 @@
RENAME TO query_access;
-
-
DROP TABLE IF EXISTS query_reference;
DROP INDEX IF EXISTS query_reference_owner_index;
diff --git a/full/src/main/resources/db/test/V3.5__insert_oauth2_clients.sql b/full/src/main/resources/db/test/V3.5__insert_oauth2_clients.sql
index 858d238..043426d 100644
--- a/full/src/main/resources/db/test/V3.5__insert_oauth2_clients.sql
+++ b/full/src/main/resources/db/test/V3.5__insert_oauth2_clients.sql
@@ -2,50 +2,55 @@
-- plain secret value is "secret"
INSERT INTO oauth2_client(id,name,secret,type,super,
- redirect_uri,registered_by, description, url)
+ redirect_uri,registered_by, description, url, registration_date,
+ is_permitted)
VALUES ("fCBbQkAyYzI4NzUxMg","super confidential client",
"$2a$08$vi1FbuN3p6GcI1tSxMAoeuIYL8Yw3j6A8wJthaN8ZboVnrQaTwLPq",
"CONFIDENTIAL", 1,
"https://korap.ids-mannheim.de/confidential/redirect", "system",
"This is a test super confidential client.",
- "http://korap.ids-mannheim.de/confidential");
+ "http://korap.ids-mannheim.de/confidential", CURRENT_TIMESTAMP, 1);
-- plain secret value is "secret"
INSERT INTO oauth2_client(id,name,secret,type,super,
- redirect_uri,registered_by, description,url)
+ redirect_uri,registered_by, description,url,registration_date,
+ is_permitted)
VALUES ("9aHsGW6QflV13ixNpez","non super confidential client",
"$2a$08$vi1FbuN3p6GcI1tSxMAoeuIYL8Yw3j6A8wJthaN8ZboVnrQaTwLPq",
"CONFIDENTIAL", 0,
"https://third.party.com/confidential/redirect", "system",
"This is a test nonsuper confidential client.",
- "http://third.party.com/confidential");
+ "http://third.party.com/confidential", CURRENT_TIMESTAMP,1);
INSERT INTO oauth2_client(id,name,secret,type,super,
- redirect_uri,registered_by, description,url)
+ redirect_uri,registered_by, description,url, registration_date,
+ is_permitted)
VALUES ("52atrL0ajex_3_5imd9Mgw","confidential client 2",
"$2a$08$vi1FbuN3p6GcI1tSxMAoeuIYL8Yw3j6A8wJthaN8ZboVnrQaTwLPq",
"CONFIDENTIAL", 0,
"https://example.client.de/redirect", "system",
"This is a test nonsuper confidential client.",
- "http://example.client.de");
+ "http://example.client.de", CURRENT_TIMESTAMP, 1);
INSERT INTO oauth2_client(id,name,secret,type,super,
- redirect_uri, registered_by, description, url)
+ redirect_uri, registered_by, description, url, registration_date,
+ is_permitted)
VALUES ("8bIDtZnH6NvRkW2Fq","third party client",null,
"PUBLIC", 0,
"https://third.party.client.com/redirect","system",
"This is a test public client.",
- "http://third.party.client.com");
+ "http://third.party.client.com", CURRENT_TIMESTAMP,1);
INSERT INTO oauth2_client(id,name,secret,type,super,
- redirect_uri, registered_by, description,url)
+ redirect_uri, registered_by, description, url, registration_date,
+ is_permitted)
VALUES ("nW5qM63Rb2a7KdT9L","test public client",null,
"PUBLIC", 0,
"https://korap.ids-mannheim.de/public/redirect","system",
"This is a test public client.",
- "http://korap.ids-mannheim.de/public");
+ "http://korap.ids-mannheim.de/public", CURRENT_TIMESTAMP, 1);
INSERT INTO oauth2_access_token(token,user_id,created_date,
diff --git a/full/src/main/resources/kustvakt.conf b/full/src/main/resources/kustvakt.conf
index daf0e9a..5d822ae 100644
--- a/full/src/main/resources/kustvakt.conf
+++ b/full/src/main/resources/kustvakt.conf
@@ -5,10 +5,10 @@
krill.index.commit.log = log/krill.commit.log
krill.index.commit.auto = 500
krill.index.relations.max = 100
-## Directory path of virtual corpora to cache
+# Directory path of virtual corpora to cache
#krill.namedVC = vc
-## LDAP
+# LDAP
ldap.config = file-path-to-ldap-config
# Kustvakt
@@ -17,20 +17,20 @@
# multiple versions separated by space
# supported.api.version = v1.0
-## server
+# server
server.port=8089
server.host=localhost
-## mail settings
+# mail settings
mail.enabled = false
mail.receiver = test@localhost
mail.sender = noreply@ids-mannheim.de
mail.address.retrieval = test
-## mail.templates
+# mail.templates
template.group.invitation = notification.vm
-## default foundries for specific layers
+# default foundries for specific layers
default.foundry.partOfSpeech = tt
default.foundry.lemma = tt
default.foundry.orthography = opennlp
@@ -39,32 +39,33 @@
default.foundry.morphology = marmot
default.foundry.surface = base
-## delete configuration (default hard)
+# delete configuration (default hard)
# delete.auto.group = hard
delete.group = soft
delete.group.member = soft
-## availability regex
-## only support |
+# availability regex (only support | )
availability.regex.free = CC-BY.*
availability.regex.public = ACA.*|QAO.NC
availability.regex.all = QAO.*
-## options referring to the security module!
+# options referring to the security module!
-## OAuth
-### (see de.ids_mannheim.korap.constant.AuthenticationMethod for possible
-### oauth.password.authentication values)
+# OAuth
+# (see de.ids_mannheim.korap.constant.AuthenticationMethod for possible
+# oauth.password.authentication values)
oauth.password.authentication = TEST
-### used to determine native client, currently not used
-oauth2.native.client.host = korap.ids-mannheim.de
+# used to determine native client, currently not used
+# oauth2.native.client.host = korap.ids-mannheim.de
oauth2.max.attempts = 1
# expiry in seconds (S), minutes (M), hours (H), days (D)
oauth2.access.token.expiry = 1D
-oauth2.access.token.long.expiry = 365D
oauth2.refresh.token.expiry = 90D
+# default 365D
+# oauth2.access.token.long.expiry = 365D
+# oauth2.refresh.token.long.expiry = 365D
oauth2.authorization.code.expiry = 10M
-# -- scopes separated by space
+# scopes separated by space
oauth2.default.scopes = search match_info
oauth2.client.credentials.scopes = client_info
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 a9f1095..ec73796 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
@@ -118,13 +118,43 @@
String clientSecret = node.at("/client_secret").asText();
assertNotNull(clientId);
assertNotNull(clientSecret);
-
assertFalse(clientId.contains("a"));
testConfidentialClientInfo(clientId, username);
testResetConfidentialClientSecret(clientId, clientSecret);
deregisterConfidentialClient(username, clientId);
}
+
+ @Test
+ public void testRegisterPlugin () throws UniformInterfaceException,
+ ClientHandlerException, KustvaktException {
+ JsonNode source = JsonUtils.readTree("{ \"plugin\" : \"source\"}");
+
+ OAuth2ClientJson json = new OAuth2ClientJson();
+ json.setName("Plugin");
+ json.setType(OAuth2ClientType.CONFIDENTIAL);
+ json.setDescription("This is a plugin test client.");
+ json.setSource(source);
+
+ ClientResponse response = registerClient(username, json);
+ assertEquals(Status.OK.getStatusCode(), response.getStatus());
+ JsonNode node = JsonUtils.readTree(response.getEntity(String.class));
+ String clientId = node.at("/client_id").asText();
+ String clientSecret = node.at("/client_secret").asText();
+ assertNotNull(clientId);
+ assertNotNull(clientSecret);
+
+ JsonNode clientInfo = retrieveClientInfo(clientId, username);
+ assertEquals(clientId, clientInfo.at("/id").asText());
+ assertEquals("Plugin", clientInfo.at("/name").asText());
+ assertEquals(OAuth2ClientType.CONFIDENTIAL.name(),
+ clientInfo.at("/type").asText());
+ assertEquals(username, clientInfo.at("/registered_by").asText());
+ assertNotNull(clientInfo.at("/registration_date"));
+
+ assertFalse(clientInfo.at("/permitted").asBoolean());
+ assertNotNull(clientInfo.at("/source"));
+ }
@Test
public void testRegisterClientNameTooShort ()
diff --git a/full/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2TestBase.java b/full/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2TestBase.java
index 9eb287d..1574505 100644
--- a/full/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2TestBase.java
+++ b/full/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2TestBase.java
@@ -2,6 +2,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
import java.net.URI;
@@ -260,6 +261,10 @@
assertEquals(clientURL, clientInfo.at("/url").asText());
assertEquals(clientRedirectUri, clientInfo.at("/redirect_uri").asText());
assertNotNull(clientInfo.at("/description"));
+ assertNotNull(clientInfo.at("/registration_date"));
+ assertTrue(clientInfo.at("/permitted").asBoolean());
+ assertTrue(clientInfo.at("/source").isMissingNode());
+
}
protected void deregisterConfidentialClient (String username, String clientId)