From edbf4cd1d0bf6c0689fe70c0f13fa7729f004456 Mon Sep 17 00:00:00 2001 From: Luca Frosini Date: Mon, 22 Jun 2020 15:39:37 +0200 Subject: [PATCH] Fixing the implementation of the feature --- pom.xml | 2 +- .../gcat/persistence/ckan/CKANPackage.java | 138 +++++++++++++----- .../persistence/ckan/CKANPackageTest.java | 95 +++++++++++- 3 files changed, 196 insertions(+), 39 deletions(-) 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(""); + 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); }