diff --git a/pom.xml b/pom.xml
index 72cdacf..9aebd19 100644
--- a/pom.xml
+++ b/pom.xml
@@ -12,7 +12,7 @@
org.gcube.data-publishing
gcat
war
- 1.4.3
+ 1.5.0-SNAPSHOT
gCube Catalogue (gCat) Service
This service allows any client to publish on the gCube Catalogue.
diff --git a/src/main/java/org/gcube/gcat/persistence/ckan/CKANPackage.java b/src/main/java/org/gcube/gcat/persistence/ckan/CKANPackage.java
index 1342823..d2479f7 100644
--- a/src/main/java/org/gcube/gcat/persistence/ckan/CKANPackage.java
+++ b/src/main/java/org/gcube/gcat/persistence/ckan/CKANPackage.java
@@ -2,13 +2,16 @@ package org.gcube.gcat.persistence.ckan;
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.ws.rs.BadRequestException;
import javax.ws.rs.DELETE;
+import javax.ws.rs.ForbiddenException;
import javax.ws.rs.GET;
import javax.ws.rs.HEAD;
import javax.ws.rs.InternalServerErrorException;
@@ -19,6 +22,7 @@ import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response.Status;
+import org.gcube.common.resources.gcore.GenericResource;
import org.gcube.common.scope.impl.ScopeBean;
import org.gcube.common.scope.impl.ScopeBean.Type;
import org.gcube.gcat.annotation.PURGE;
@@ -28,8 +32,12 @@ import org.gcube.gcat.profile.MetadataUtility;
import org.gcube.gcat.social.SocialPost;
import org.gcube.gcat.utils.ContextUtility;
import org.gcube.gcat.utils.URIResolver;
+import org.gcube.resources.discovery.client.api.DiscoveryClient;
+import org.gcube.resources.discovery.client.queries.api.SimpleQuery;
+import org.gcube.resources.discovery.icclient.ICFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.w3c.dom.NodeList;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
@@ -75,6 +83,11 @@ public class CKANPackage extends CKAN {
ORGANIZATION_REGEX_PATTERN = Pattern.compile(ORGANIZATION_REGEX);
}
+ public static final String GENERIC_RESOURCE_SECONDARY_TYPE_FOR_ORGANIZATIONS = "ApplicationProfile";
+ public static final String GENERIC_RESOURCE_NAME_FOR_ORGANIZATIONS = "Supported CKAN Organizations";
+ public static final String GENERIC_RESOURCE_TAG_NAME = "CKANOrganization";
+ public static final String GENERIC_RESOURCE_TAG_NAME_PLURAL = "CKANOrganizations";
+
protected static final String LICENSE_KEY = "license_id";
protected static final String EXTRAS_ITEM_URL_KEY = "Item URL";
@@ -107,6 +120,12 @@ public class CKANPackage extends CKAN {
protected String itemID;
+ protected ScopeBean currentScopeBean;
+ protected String currentContext;
+
+ protected String currentOrganizationName;
+ protected Set supportedOrganizations;
+
public CKANPackage() {
super();
LIST = ITEM_LIST;
@@ -117,55 +136,103 @@ public class CKANPackage extends CKAN {
DELETE = ITEM_DELETE;
PURGE = ITEM_PURGE;
managedResources = new ArrayList();
+ getCurrentContext();
+ getSupportedOrganizationsFromIS();
+ }
+
+ protected Set getSupportedOrganizationsFromIS() {
+ if(supportedOrganizations == null) {
+
+ supportedOrganizations = new HashSet<>();
+
+ SimpleQuery query = ICFactory.queryFor(GenericResource.class);
+ query.addCondition(String.format("$resource/Profile/SecondaryType/text() eq '%s'",
+ GENERIC_RESOURCE_SECONDARY_TYPE_FOR_ORGANIZATIONS));
+ query.addCondition(
+ String.format("$resource/Profile/Name/text() eq '%s'", GENERIC_RESOURCE_NAME_FOR_ORGANIZATIONS));
+
+ DiscoveryClient client = ICFactory.clientFor(GenericResource.class);
+ List resources = client.submit(query);
+
+ if(resources == null || resources.size() == 0) {
+ logger.info(
+ "{} with SecondaryType {} and Name %s not found. Item will be only be created in {} CKAN organization",
+ GenericResource.class.getSimpleName(), GENERIC_RESOURCE_SECONDARY_TYPE_FOR_ORGANIZATIONS,
+ GENERIC_RESOURCE_NAME_FOR_ORGANIZATIONS, getOrganizationName());
+ supportedOrganizations.add(getOrganizationName());
+ } else {
+ GenericResource genericResource = resources.get(0);
+ NodeList nodeList = genericResource.profile().body().getChildNodes();
+ if(nodeList != null && nodeList.getLength() > 0) {
+ for(int i = 0; i < nodeList.getLength(); i++) {
+ NodeList nl = nodeList.item(i).getChildNodes();
+ String organization = nl.item(0).getNodeValue();
+ supportedOrganizations.add(organization);
+ }
+ }
+ }
+
+ }
+
+ logger.debug("Supported CKAN Organization for current Context ({}) are {}", getCurrentContext(),
+ supportedOrganizations);
+ return supportedOrganizations;
}
/*
* Return the CKAN organization name using the current context name
*/
- protected String getOrganizationName(ScopeBean scopeBean) {
+ protected static String getOrganizationName(ScopeBean scopeBean) {
String contextName = scopeBean.name();
return contextName.toLowerCase().replace(" ", "_");
}
- protected String getOrganizationName() {
- ScopeBean scopeBean = new ScopeBean(ContextUtility.getCurrentContext());
- return getOrganizationName(scopeBean);
+ protected String getCurrentContext() {
+ if(currentContext == null) {
+ currentContext = ContextUtility.getCurrentContext();
+ currentScopeBean = new ScopeBean(getCurrentContext());
+ }
+ return currentContext;
}
- protected CKANOrganization checkGotOrganization(String gotOrganization) {
+ protected String getOrganizationName() {
+ if(currentOrganizationName == null) {
+ this.currentOrganizationName = CKANPackage.getOrganizationName(currentScopeBean);
+ }
+ return currentOrganizationName;
+ }
+
+ protected CKANOrganization checkGotOrganization(String gotOrganization) throws ForbiddenException {
+ // TODO Check if it is allowed to publish in such organization by reading an IS resource
+
+ if(!supportedOrganizations.contains(gotOrganization)) {
+ String error = String.format(
+ "IS Configuration does not allow to publish in %s organizations. Allowed organization are: %s",
+ gotOrganization, supportedOrganizations);
+ throw new ForbiddenException(error);
+ }
+
CKANOrganization ckanOrganization = new CKANOrganization();
ckanOrganization.setName(gotOrganization);
ckanOrganization.read();
- // TODO Check if the organization exists
- // TODO Check if it is allowed to publish in such organization by reading an IS resource
-
return ckanOrganization;
}
- protected List getPublishingOrganization(ObjectNode objectNode) {
- // owner organization must be specified if the token belongs to a VRE
- ScopeBean scopeBean = new ScopeBean(ContextUtility.getCurrentContext());
-
- List gotOrganizations = new ArrayList<>();
+ protected CKANOrganization getPublishingOrganization(ObjectNode objectNode) throws ForbiddenException {
+ CKANOrganization ckanOrganization = null;
if(objectNode.has(OWNER_ORG_KEY)) {
- JsonNode jsonNode = objectNode.get(OWNER_ORG_KEY);
- if(jsonNode.isArray()) {
- ArrayNode arrayNode = (ArrayNode) jsonNode;
- for(int i=0; i ckanOrganizations = getPublishingOrganization(objectNode);
-
- for(CKANOrganization ckanOrganization : ckanOrganizations) {
- ckanUser.addUserToOrganization(ckanOrganization.getName());
- }
-
+ CKANOrganization ckanOrganization = getPublishingOrganization(objectNode);
+ ckanUser.addUserToOrganization(ckanOrganization.getName());
return objectNode;
}
@@ -322,6 +385,8 @@ public class CKANPackage extends CKAN {
q = queryParameters.getFirst(GCatConstants.Q_KEY);
}
+
+ // TODO check against feature #19365
/*
* List is filter per organization only is the request arriving in a VRE.
* If the request arrive form a VO or from ROOT the request return the item in all organizations
@@ -484,10 +549,8 @@ public class CKANPackage extends CKAN {
((ObjectNode) jsonNode).remove(RESOURCES_KEY);
}
- ScopeBean scopeBean = new ScopeBean(ContextUtility.getCurrentContext());
-
String catalogueItemURL = "";
- if(scopeBean.is(Type.VRE)) {
+ if(currentScopeBean.is(Type.VRE)) {
catalogueItemURL = addItemURLViaResolver(jsonNode);
}
@@ -503,7 +566,7 @@ public class CKANPackage extends CKAN {
// Actions performed after a package has been correctly created on ckan.
String title = result.get(TITLE_KEY).asText();
- if(scopeBean.is(Type.VRE)) {
+ if(currentScopeBean.is(Type.VRE)) {
sendSocialPost(title, catalogueItemURL);
}
@@ -552,7 +615,8 @@ public class CKANPackage extends CKAN {
"The content contains a resource with id " + resourceId + " which does not exists");
}
}
- if(originalResources.get(resourceId)!= null && (!originalResources.get(resourceId).getPreviousRepresentation().equals(resourceNode))) {
+ if(originalResources.get(resourceId) != null
+ && (!originalResources.get(resourceId).getPreviousRepresentation().equals(resourceNode))) {
resourceNode = ckanResource.createOrUpdate(resourceNode);
}
resourcesToBeSend.add(resourceNode);
diff --git a/src/test/java/org/gcube/gcat/persistence/ckan/CKANPackageTest.java b/src/test/java/org/gcube/gcat/persistence/ckan/CKANPackageTest.java
index 213d3a9..7f28a2c 100644
--- a/src/test/java/org/gcube/gcat/persistence/ckan/CKANPackageTest.java
+++ b/src/test/java/org/gcube/gcat/persistence/ckan/CKANPackageTest.java
@@ -1,14 +1,24 @@
package org.gcube.gcat.persistence.ckan;
+import java.io.StringWriter;
+import java.util.ArrayList;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
+import java.util.Set;
import javax.ws.rs.BadRequestException;
import javax.ws.rs.core.MultivaluedHashMap;
import javax.ws.rs.core.MultivaluedMap;
+import org.gcube.common.authorization.client.exceptions.ObjectNotFound;
+import org.gcube.common.resources.gcore.GenericResource;
+import org.gcube.common.resources.gcore.Resources;
+import org.gcube.common.scope.impl.ScopeBean;
import org.gcube.gcat.ContextTest;
import org.gcube.gcat.api.GCatConstants;
+import org.gcube.informationsystem.publisher.RegistryPublisher;
+import org.gcube.informationsystem.publisher.RegistryPublisherFactory;
import org.junit.Assert;
import org.junit.Test;
import org.slf4j.Logger;
@@ -73,6 +83,89 @@ public class CKANPackageTest extends ContextTest {
}
+ protected GenericResource instantiateGenericResource(String secondaryType, String name, String xml) throws Exception {
+ GenericResource genericResource = new GenericResource();
+ org.gcube.common.resources.gcore.GenericResource.Profile profile = genericResource.newProfile();
+ profile.type(secondaryType);
+ profile.name(name);
+ profile.description("This resource is read by gCat and define the list of CKAN organizations where a client is allowed to publish for the current context");
+ profile.newBody(xml);
+ StringWriter stringWriter = new StringWriter();
+ Resources.marshal(genericResource, stringWriter);
+ logger.debug("The generated {} is\n{}", GenericResource.class.getSimpleName(), stringWriter.toString());
+ return genericResource;
+ }
+
+ protected void createGenericResource(String xml) throws Exception {
+ GenericResource genericResource = instantiateGenericResource(
+ CKANPackage.GENERIC_RESOURCE_SECONDARY_TYPE_FOR_ORGANIZATIONS,
+ CKANPackage.GENERIC_RESOURCE_NAME_FOR_ORGANIZATIONS, xml);
+ RegistryPublisher registryPublisher = RegistryPublisherFactory.create();
+ genericResource = registryPublisher.create(genericResource);
+ StringWriter stringWriter = new StringWriter();
+ Resources.marshal(genericResource, stringWriter);
+ logger.trace("The {} with ID {} has been created \n{}", GenericResource.class.getSimpleName(),
+ genericResource.id(), stringWriter.toString());
+ }
+
+ protected String createXMLFragment(List organizations) throws Exception {
+ if(organizations==null || organizations.size()<1) {
+ throw new Exception("Unable to create the XML fragment for the generic resource with empty organization list");
+ }
+ StringWriter stringWriter = new StringWriter();
+ stringWriter.append("<");
+ stringWriter.append(CKANPackage.GENERIC_RESOURCE_TAG_NAME_PLURAL);
+ stringWriter.append(">");
+ for(String organizationName : organizations) {
+ stringWriter.append("<");
+ stringWriter.append(CKANPackage.GENERIC_RESOURCE_TAG_NAME);
+ stringWriter.append(">");
+ stringWriter.append(organizationName);
+ stringWriter.append("");
+ stringWriter.append(CKANPackage.GENERIC_RESOURCE_TAG_NAME);
+ stringWriter.append(">");
+ }
+ stringWriter.append("");
+ stringWriter.append(CKANPackage.GENERIC_RESOURCE_TAG_NAME_PLURAL);
+ stringWriter.append(">");
+ return stringWriter.toString();
+ }
+
+ protected void createGenericResourceForSupportedOrganizations(List organizations) throws Exception {
+ String xml = createXMLFragment(organizations);
+ createGenericResource(xml);
+ }
+
+ // @Test
+ public void createGenericResourceForSupportedOrganizationsByName() throws Exception {
+ List organizations = new ArrayList<>();
+ organizations.add("nextnext");
+ organizations.add("devvre");
+ createGenericResourceForSupportedOrganizations(organizations);
+ }
+
+ // @Test
+ public void createGenericResourceForSupportedOrganizationsByScopeBean() throws Exception {
+ ContextTest.setContextByName("/gcube/devNext/NextNext");
+
+ List scopeBeans = new ArrayList<>();
+ scopeBeans.add(new ScopeBean("/gcube/devNext/NextNext"));
+ scopeBeans.add(new ScopeBean("/gcube/devsec/devVRE"));
+ List organizations = new ArrayList<>();
+ for(ScopeBean scopeBean : scopeBeans) {
+ organizations.add(CKANPackage.getOrganizationName(scopeBean));
+ }
+ createGenericResourceForSupportedOrganizations(organizations);
+ }
+
+ @Test
+ public void testGetSupportedOrganizationsFromIS() throws ObjectNotFound, Exception {
+ ContextTest.setContextByName("/gcube/devNext/NextNext");
+ CKANPackage ckanPackage = new CKANPackage();
+ Set organizations = ckanPackage.getSupportedOrganizationsFromIS();
+ Assert.assertTrue(organizations.size()>0);
+ }
+
@Test(expected=BadRequestException.class)
public void checkParameter() throws Exception {
@@ -185,7 +278,7 @@ public class CKANPackageTest extends ContextTest {
@Test
public void create() throws Exception {
- //ContextTest.setContextByName("/gcube/devNext");
+ ContextTest.setContextByName("/gcube/devNext/NextNext");
ObjectMapper mapper = new ObjectMapper();
createPackage(mapper);
}