Restrict registration info for plugins (solves #572)
Only available for owners & admins.
Added token scopes for DemoUser.
Change-Id: I74e6e2a3c3dc477dbd0402155048299ed6a78a48
diff --git a/.gitignore b/.gitignore
index f79fa55..dbb295d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -59,4 +59,5 @@
/data/
/old-jars/
/data-backup
-/notes
\ No newline at end of file
+/notes
+/docker
\ No newline at end of file
diff --git a/Changes b/Changes
index 691a5d7..a7d713a 100644
--- a/Changes
+++ b/Changes
@@ -6,6 +6,7 @@
- Updated tests using the old match info web-services (#757)
- Added deprecation warning for the old matchInfo service (#757)
- Fixed empty named VC path in configuration (solves #754)
+- Restrict registration info for plugins (solves #572)
# version 0.73.1
diff --git a/src/main/java/de/ids_mannheim/korap/oauth2/dto/OAuth2ClientInfoDto.java b/src/main/java/de/ids_mannheim/korap/oauth2/dto/OAuth2ClientInfoDto.java
index d50d79b..6f45900 100644
--- a/src/main/java/de/ids_mannheim/korap/oauth2/dto/OAuth2ClientInfoDto.java
+++ b/src/main/java/de/ids_mannheim/korap/oauth2/dto/OAuth2ClientInfoDto.java
@@ -40,6 +40,7 @@
@JsonProperty("registered_by")
private String registeredBy;
@JsonProperty("refresh_token_expiry")
+ @JsonInclude(Include.NON_DEFAULT)
private int refreshTokenExpiry; // in seconds
@JsonProperty("permitted")
@@ -47,28 +48,41 @@
private JsonNode source;
public OAuth2ClientInfoDto (OAuth2Client client) throws KustvaktException {
+ this(client,true);
+ }
+
+ public OAuth2ClientInfoDto (OAuth2Client client, boolean showAllInfo) 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.setSuper(client.isSuper());
this.setPermitted(client.isPermitted());
- this.setRegisteredBy(client.getRegisteredBy());
-
+
String source = client.getSource();
- if (source != null && !source.isEmpty()) {
- this.source = JsonUtils.readTree(source);
+
+ if (showAllInfo) {
+ this.setSuper(client.isSuper());
+ this.setRedirect_uri(client.getRedirectURI());
+ this.setRegisteredBy(client.getRegisteredBy());
+ ZonedDateTime registrationDate = client.getRegistrationDate();
+ if (registrationDate != null) {
+ this.setRegistrationDate(registrationDate.toString());
+ }
+ if (client.getType().equals(OAuth2ClientType.CONFIDENTIAL)) {
+ this.setRefreshTokenExpiry(client.getRefreshTokenExpiry());
+ }
+
+ if (source != null && !source.isEmpty()) {
+ this.source = JsonUtils.readTree(source);
+ }
}
- if (client.getType().equals(OAuth2ClientType.CONFIDENTIAL)) {
- this.setRefreshTokenExpiry(client.getRefreshTokenExpiry());
- }
- ZonedDateTime registrationDate = client.getRegistrationDate();
- if (registrationDate != null) {
- this.setRegistrationDate(registrationDate.toString());
- }
+ else { //plugins
+ if (source != null && !source.isEmpty()) {
+ this.source = JsonUtils.readTree(source);
+ }
+ }
}
public boolean isSuper () {
diff --git a/src/main/java/de/ids_mannheim/korap/oauth2/entity/OAuth2Client.java b/src/main/java/de/ids_mannheim/korap/oauth2/entity/OAuth2Client.java
index 5f29815..16d6879 100644
--- a/src/main/java/de/ids_mannheim/korap/oauth2/entity/OAuth2Client.java
+++ b/src/main/java/de/ids_mannheim/korap/oauth2/entity/OAuth2Client.java
@@ -49,6 +49,7 @@
private String description;
private String url;
+ // Source determines if a client is a plugin
private String source;
@Column(name = "is_permitted")
private boolean isPermitted;
diff --git a/src/main/java/de/ids_mannheim/korap/oauth2/service/OAuth2ClientService.java b/src/main/java/de/ids_mannheim/korap/oauth2/service/OAuth2ClientService.java
index 3781458..d6feae2 100644
--- a/src/main/java/de/ids_mannheim/korap/oauth2/service/OAuth2ClientService.java
+++ b/src/main/java/de/ids_mannheim/korap/oauth2/service/OAuth2ClientService.java
@@ -341,17 +341,27 @@
return clientDao.retrieveClientById(clientId);
}
- public OAuth2ClientInfoDto retrieveClientInfo (String clientId)
+ public OAuth2ClientInfoDto retrieveClientInfo (String clientId, String username)
throws KustvaktException {
OAuth2Client client = clientDao.retrieveClientById(clientId);
- // if (adminDao.isAdmin(username)
- // || username.equals(client.getRegisteredBy())) {
- return new OAuth2ClientInfoDto(client);
- // }
- // else {
- // throw new KustvaktException(StatusCodes.AUTHORIZATION_FAILED,
- // "Unauthorized operation for user: " + username, username);
- // }
+
+ // all client info is only available to the owner/admin
+ if (adminDao.isAdmin(username)
+ || username.equals(client.getRegisteredBy())) {
+ return new OAuth2ClientInfoDto(client);
+ }
+ // plugin info is available for all users inclusive guest
+ else if (isPlugin(client)) {
+ return new OAuth2ClientInfoDto(client, false);
+ }
+ else {
+ throw new KustvaktException(StatusCodes.AUTHORIZATION_FAILED,
+ "Unauthorized operation for user: " + username, username);
+ }
+ }
+
+ public boolean isPlugin (OAuth2Client client) {
+ return (client.getSource() != null && !client.getSource().isEmpty());
}
public OAuth2Client retrieveClient (String clientId)
@@ -376,7 +386,7 @@
}
Collections.sort(uniqueClients);
- return createClientDtos(uniqueClients);
+ return createClientDtos(uniqueClients,username);
}
public List<OAuth2ClientInfoDto> listUserRegisteredClients (String username)
@@ -384,15 +394,15 @@
List<OAuth2Client> userClients = clientDao
.retrieveUserRegisteredClients(username);
Collections.sort(userClients);
- return createClientDtos(userClients);
+ return createClientDtos(userClients,username);
}
- public List<OAuth2ClientInfoDto> listPlugins (boolean isPermitted)
+ public List<OAuth2ClientInfoDto> listPlugins (boolean isPermitted, String username)
throws KustvaktException {
List<OAuth2Client> plugins = clientDao.retrievePlugins(isPermitted);
Collections.sort(plugins);
- return createClientDtos(plugins);
+ return createClientDtos(plugins,username);
}
public List<InstalledPluginDto> listInstalledPlugins (String superClientId,
@@ -452,12 +462,18 @@
}
private List<OAuth2ClientInfoDto> createClientDtos (
- List<OAuth2Client> userClients) throws KustvaktException {
+ List<OAuth2Client> userClients, String username) throws KustvaktException {
List<OAuth2ClientInfoDto> dtoList = new ArrayList<>(userClients.size());
+ boolean showAllInfo;
for (OAuth2Client uc : userClients) {
- if (uc.isSuper())
+ showAllInfo = false;
+ if (uc.isSuper()) {
continue;
- OAuth2ClientInfoDto dto = new OAuth2ClientInfoDto(uc);
+ }
+ if (uc.getRegisteredBy().equals(username)) {
+ showAllInfo = true;
+ }
+ OAuth2ClientInfoDto dto = new OAuth2ClientInfoDto(uc, showAllInfo);
dtoList.add(dto);
}
return dtoList;
diff --git a/src/main/java/de/ids_mannheim/korap/web/controller/OAuthClientController.java b/src/main/java/de/ids_mannheim/korap/web/controller/OAuthClientController.java
index b530e19..ee2d72f 100644
--- a/src/main/java/de/ids_mannheim/korap/web/controller/OAuthClientController.java
+++ b/src/main/java/de/ids_mannheim/korap/web/controller/OAuthClientController.java
@@ -16,6 +16,8 @@
import de.ids_mannheim.korap.web.filter.APIVersionFilter;
import de.ids_mannheim.korap.web.filter.AuthenticationFilter;
import de.ids_mannheim.korap.web.filter.BlockingFilter;
+import de.ids_mannheim.korap.web.filter.DemoFilter;
+import de.ids_mannheim.korap.web.filter.DemoUserFilter;
import de.ids_mannheim.korap.web.input.OAuth2ClientJson;
import de.ids_mannheim.korap.web.utils.ResourceFilters;
import jakarta.ws.rs.Consumes;
@@ -158,17 +160,34 @@
}
}
+ /**
+ * Returns information about the given client id. Only super
+ * clients are allowed to use this service.
+ *
+ * @param securityContext
+ * @param clientId
+ * plugin or OAuth2 client id
+ * @param superClientId
+ * @param superClientSecret
+ * @return OAuth2 client/plugin information
+ */
@POST
@Path("{client_id}")
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
- @ResourceFilters({ APIVersionFilter.class })
+ @ResourceFilters({ APIVersionFilter.class, AuthenticationFilter.class,
+ DemoUserFilter.class })
public OAuth2ClientInfoDto retrieveClientInfo (
+ @Context SecurityContext securityContext,
@PathParam("client_id") String clientId,
@FormParam("super_client_id") String superClientId,
@FormParam("super_client_secret") String superClientSecret) {
+ TokenContext context = (TokenContext) securityContext
+ .getUserPrincipal();
try {
clientService.verifySuperClient(superClientId, superClientSecret);
- return clientService.retrieveClientInfo(clientId);
+ scopeService.verifyScope(context, OAuth2Scope.CLIENT_INFO);
+ return clientService.retrieveClientInfo(clientId,
+ context.getUsername());
}
catch (KustvaktException e) {
throw responseHandler.throwit(e);
diff --git a/src/main/java/de/ids_mannheim/korap/web/controller/PluginController.java b/src/main/java/de/ids_mannheim/korap/web/controller/PluginController.java
index 0afda45..2d41dc6 100644
--- a/src/main/java/de/ids_mannheim/korap/web/controller/PluginController.java
+++ b/src/main/java/de/ids_mannheim/korap/web/controller/PluginController.java
@@ -56,7 +56,7 @@
OAuth2Scope.LIST_USER_CLIENT);
clientService.verifySuperClient(superClientId, superClientSecret);
- return clientService.listPlugins(permittedOnly);
+ return clientService.listPlugins(permittedOnly, tokenContext.getUsername());
}
catch (KustvaktException e) {
throw responseHandler.throwit(e);
diff --git a/src/main/java/de/ids_mannheim/korap/web/filter/DemoUserFilter.java b/src/main/java/de/ids_mannheim/korap/web/filter/DemoUserFilter.java
index 300c3da..2bf4b69 100644
--- a/src/main/java/de/ids_mannheim/korap/web/filter/DemoUserFilter.java
+++ b/src/main/java/de/ids_mannheim/korap/web/filter/DemoUserFilter.java
@@ -1,12 +1,16 @@
package de.ids_mannheim.korap.web.filter;
import java.security.Principal;
+import java.util.HashSet;
+import java.util.Set;
import org.glassfish.jersey.server.ContainerRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
+import de.ids_mannheim.korap.config.Attributes;
import de.ids_mannheim.korap.config.KustvaktConfiguration;
+import de.ids_mannheim.korap.constant.OAuth2Scope;
import de.ids_mannheim.korap.constant.TokenType;
import de.ids_mannheim.korap.security.context.KustvaktContext;
import de.ids_mannheim.korap.security.context.TokenContext;
@@ -39,7 +43,7 @@
String authentication = request
.getHeaderString(ContainerRequest.AUTHORIZATION);
- // means that this is the public service
+ // means that this is a public service
if (authentication == null || authentication.isEmpty()) {
Principal pr = null;
SecurityContext securityContext = request.getSecurityContext();
@@ -58,9 +62,16 @@
c.setUsername(demo.getUsername());
c.setHostAddress(host);
c.setUserAgent(agent);
- c.setExpirationTime(
- TimeUtils.plusSeconds(config.getShortTokenTTL()).getMillis());
+// c.setExpirationTime(
+// TimeUtils.plusSeconds(config.getShortTokenTTL()).getMillis());
c.setTokenType(TokenType.BASIC);
+
+ Set<String> scopeSet = new HashSet<>();
+ scopeSet.add(OAuth2Scope.SEARCH.toString());
+ scopeSet.add(OAuth2Scope.CLIENT_INFO.toString());
+ scopeSet.add(OAuth2Scope.MATCH_INFO.toString());
+ String scopes = String.join(" ", scopeSet);
+ c.addContextParameter(Attributes.SCOPE, scopes);
return c;
}
}
diff --git a/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2ClientControllerTest.java b/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2ClientControllerTest.java
index 5f24cd6..6c8652d 100644
--- a/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2ClientControllerTest.java
+++ b/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2ClientControllerTest.java
@@ -8,13 +8,9 @@
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
-import java.util.List;
-import java.util.Map.Entry;
-import java.util.Set;
import org.apache.commons.io.IOUtils;
import org.apache.http.entity.ContentType;
-import org.glassfish.jersey.server.ContainerRequest;
import org.junit.jupiter.api.Test;
import com.fasterxml.jackson.databind.JsonNode;
@@ -63,15 +59,15 @@
@Test
public void testRetrieveClientInfo () throws KustvaktException {
- // public client
+ // public client plugin
JsonNode clientInfo = retrieveClientInfo(publicClientId, "system");
assertEquals(publicClientId, clientInfo.at("/client_id").asText());
assertEquals(clientInfo.at("/client_name").asText(),
"public client plugin with redirect uri");
assertNotNull(clientInfo.at("/client_description"));
assertNotNull(clientInfo.at("/client_url"));
- assertEquals(clientInfo.at("/client_type").asText(), "PUBLIC");
- assertEquals(clientInfo.at("/registered_by").asText(), "system");
+ assertEquals("PUBLIC", clientInfo.at("/client_type").asText());
+ assertEquals("system", clientInfo.at("/registered_by").asText());
// confidential client
clientInfo = retrieveClientInfo(confidentialClientId, "system");
assertEquals(confidentialClientId,
@@ -81,15 +77,15 @@
assertNotNull(clientInfo.at("/client_url"));
assertNotNull(clientInfo.at("/redirect_uri"));
assertEquals(false, clientInfo.at("/super").asBoolean());
- assertEquals(clientInfo.at("/client_type").asText(), "CONFIDENTIAL");
+ assertEquals("CONFIDENTIAL", clientInfo.at("/client_type").asText());
// super client
clientInfo = retrieveClientInfo(superClientId, "system");
assertEquals(superClientId, clientInfo.at("/client_id").asText());
- assertEquals(clientInfo.at("/client_name").asText(),
- "super confidential client");
+ assertEquals("super confidential client",
+ clientInfo.at("/client_name").asText());
assertNotNull(clientInfo.at("/client_url"));
assertNotNull(clientInfo.at("/redirect_uri"));
- assertEquals(clientInfo.at("/client_type").asText(), "CONFIDENTIAL");
+ assertEquals("CONFIDENTIAL", clientInfo.at("/client_type").asText());
assertTrue(clientInfo.at("/super").asBoolean());
}
diff --git a/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2PluginTest.java b/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2PluginTest.java
index 90597e2..d72787f 100644
--- a/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2PluginTest.java
+++ b/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2PluginTest.java
@@ -91,17 +91,12 @@
assertEquals(Status.BAD_REQUEST.getStatusCode(), response.getStatus());
assertEquals(OAuth2Error.INVALID_REQUEST, node.at("/error").asText());
assertFalse(node.at("/error_description").isMissingNode());
- // assertEquals(Status.OK.getStatusCode(), response.getStatus());
- // String clientId = node.at("/client_id").asText();
- // assertTrue(node.at("/client_secret").isMissingNode());
- //
- // deregisterClient(username, clientId);
}
private void testRetrievePluginInfo (String clientId,
int refreshTokenExpiry)
throws ProcessingException, KustvaktException {
- JsonNode clientInfo = retrieveClientInfo(clientId, username);
+ JsonNode clientInfo = retrieveClientInfo(clientId, "other-user");
assertEquals(clientId, clientInfo.at("/client_id").asText());
assertEquals(clientInfo.at("/client_name").asText(), "Plugin");
assertEquals(OAuth2ClientType.CONFIDENTIAL.name(),
@@ -109,10 +104,10 @@
assertNotNull(clientInfo.at("/client_description").asText());
assertNotNull(clientInfo.at("/source").asText());
assertFalse(clientInfo.at("/permitted").asBoolean());
- assertEquals(username, clientInfo.at("/registered_by").asText());
- assertNotNull(clientInfo.at("/registration_date"));
- assertEquals(refreshTokenExpiry,
- clientInfo.at("/refresh_token_expiry").asInt());
+ assertTrue(clientInfo.at("/registered_by").isMissingNode());
+ assertTrue(clientInfo.at("/registration_date").isMissingNode());
+ assertTrue(clientInfo.at("/refresh_token_expiry").isMissingNode());
+
}
private void testListUserRegisteredPlugins (String username,
@@ -264,10 +259,10 @@
assertFalse(node.at("/0/client_description").isMissingNode());
assertFalse(node.at("/0/client_type").isMissingNode());
assertFalse(node.at("/0/permitted").isMissingNode());
- assertFalse(node.at("/0/registration_date").isMissingNode());
+ assertTrue(node.at("/0/registration_date").isMissingNode());
assertFalse(node.at("/0/source").isMissingNode());
- assertFalse(node.at("/0/refresh_token_expiry").isMissingNode());
- // assertTrue(node.at("/1/refresh_token_expiry").isMissingNode());
+ assertTrue(node.at("/0/refresh_token_expiry").isMissingNode());
+ assertTrue(node.at("/1/client_redirect_uri").isMissingNode());
}
private JsonNode listPlugins (boolean permitted_only)
diff --git a/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2TestBase.java b/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2TestBase.java
index b27fd0f..bd57686 100644
--- a/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2TestBase.java
+++ b/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2TestBase.java
@@ -349,8 +349,8 @@
Response response = target().path(API_VERSION).path("oauth2")
.path("client").path(clientId).request()
- // .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
- // .createBasicAuthorizationHeaderValue(username, "pass"))
+ .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
+ .createBasicAuthorizationHeaderValue(username, "pass"))
.header(HttpHeaders.CONTENT_TYPE,
ContentType.APPLICATION_FORM_URLENCODED)
.post(Entity.form(form));