From 72164da17068fc1d0d060f13ba879dbe64f9bf4e Mon Sep 17 00:00:00 2001 From: Luca Frosini Date: Fri, 14 May 2021 15:34:11 +0200 Subject: [PATCH] Implementing feature --- .../gcat/persistence/ckan/CKANPackage.java | 247 +++++++++++++++--- .../persistence/ckan/CKANPackageTest.java | 126 ++++++++- 2 files changed, 338 insertions(+), 35 deletions(-) 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 d118a4a..a15a8dd 100644 --- a/src/main/java/org/gcube/gcat/persistence/ckan/CKANPackage.java +++ b/src/main/java/org/gcube/gcat/persistence/ckan/CKANPackage.java @@ -17,6 +17,7 @@ import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.core.Response.Status; +import org.apache.http.MethodNotSupportedException; import org.gcube.com.fasterxml.jackson.databind.JsonNode; import org.gcube.com.fasterxml.jackson.databind.node.ArrayNode; import org.gcube.com.fasterxml.jackson.databind.node.ObjectNode; @@ -319,6 +320,24 @@ public class CKANPackage extends CKAN { return count; } + protected CMItemStatus getRequestedCMItemStatus() { + CMItemStatus cmItemStatus = null; + + try { + MultivaluedMap queryParameters = uriInfo.getQueryParameters(); + + if(queryParameters.containsKey(GCatConstants.CM_ITEM_STATUS_QUERY_PARAMETER)) { + String cmItemStatusString = queryParameters.getFirst(GCatConstants.CM_ITEM_STATUS_QUERY_PARAMETER); + cmItemStatus = CMItemStatus.getCMItemStatusFromValue(cmItemStatusString); + } + }catch (Exception e) { + cmItemStatus = null; + } + + return cmItemStatus; + } + + @Override public String list(int limit, int offset) { Map parameters = new HashMap<>(); @@ -349,13 +368,51 @@ public class CKANPackage extends CKAN { String q = parameters.get(GCatConstants.Q_KEY); StringBuffer stringBuffer = new StringBuffer(); + stringBuffer.append("("); stringBuffer.append(CM_STATUS_QUERY_FILTER_KEY); stringBuffer.append(":"); - // Default se non viene richiesto esplicitamente lo status o per i ruoli che possono solo vedere approved - stringBuffer.append(CMItemStatus.APPROVED.getValue()); + CMItemStatus cmItemStatus = getRequestedCMItemStatus(); - parameters.put(GCatConstants.Q_KEY, String.format("%s AND %s", q, stringBuffer.toString())); + if(!ckanUser.getPortalUser().isCatalogueModerator()) { + + switch (ckanUser.getRole()) { + case ADMIN: + break; + + case EDITOR: + if(cmItemStatus!=null && cmItemStatus!=CMItemStatus.APPROVED) { + q = String.format("%s AND %s:%s", q, AUTHOR_EMAIL_KEY, ckanUser.getPortalUser().getEMail()); + }else{ + cmItemStatus = CMItemStatus.APPROVED; + } + break; + + case MEMBER: + if(cmItemStatus!=null && cmItemStatus!=CMItemStatus.APPROVED) { + throw new ForbiddenException("You are only authorized to list " + CMItemStatus.APPROVED.getValue() + " items"); + } + cmItemStatus = CMItemStatus.APPROVED; + break; + + default: + break; + } + + + } + + if(cmItemStatus!=null) { + stringBuffer.append(cmItemStatus.getValue()); + if(cmItemStatus == CMItemStatus.APPROVED) { + stringBuffer.append(" OR (*:* -"); + stringBuffer.append(CM_STATUS_QUERY_FILTER_KEY); + stringBuffer.append(":[* TO *])"); + } + stringBuffer.append(")"); + q = String.format("%s AND %s", q, stringBuffer.toString()); + parameters.put(GCatConstants.Q_KEY, q); + } parameters.put(INCLUDE_PRIVATE_KEY, String.valueOf(true)); } @@ -518,24 +575,24 @@ public class CKANPackage extends CKAN { protected void sendSocialPost(String title, String catalogueItemURL) { try { - boolean socialPost = false; + boolean makePost = false; try { MultivaluedMap queryParameters = uriInfo.getQueryParameters(); - if(queryParameters.containsKey(GCatConstants.SOCIAL_POST_PARAMETER)) { - socialPost = Boolean.parseBoolean(queryParameters.getFirst(GCatConstants.SOCIAL_POST_PARAMETER)); + if(queryParameters.containsKey(GCatConstants.SOCIAL_POST_QUERY_PARAMETER)) { + makePost = Boolean.parseBoolean(queryParameters.getFirst(GCatConstants.SOCIAL_POST_QUERY_PARAMETER)); } } catch(Exception e) { - socialPost = false; + makePost = false; } - if(socialPost) { + if(makePost) { ArrayNode arrayNode = (ArrayNode) result.get(TAGS_KEY); - SocialPost socialService = new SocialPost(); - socialService.setItemID(itemID); - socialService.setItemURL(catalogueItemURL); - socialService.setTags(arrayNode); - socialService.setItemTitle(title); - socialService.start(); + SocialPost socialPost = new SocialPost(); + socialPost.setItemID(itemID); + socialPost.setItemURL(catalogueItemURL); + socialPost.setTags(arrayNode); + socialPost.setItemTitle(title); + socialPost.start(); } else { logger.info("The request explicitly disabled the Social Post."); } @@ -553,7 +610,7 @@ public class CKANPackage extends CKAN { if(result.has(EXTRAS_KEY)) { ArrayNode extras = (ArrayNode) result.get(EXTRAS_KEY); for(JsonNode extra : extras) { - if(extra.has(EXTRAS_KEY_KEY) && extra.get(EXTRAS_KEY_KEY).asText().compareTo(GCatConstants.CM_ITEM_STATUS) == 0) { + if(extra.has(EXTRAS_KEY_KEY) && extra.get(EXTRAS_KEY_KEY).asText().compareTo(GCatConstants.SYSTEM_CM_ITEM_STATUS) == 0) { cmItemStatusString = extra.get(EXTRAS_VALUE_KEY).asText(); found = true; break; @@ -565,13 +622,17 @@ public class CKANPackage extends CKAN { if(!found) { // TODO can be used to fix an item published before activating the moderation. - // The item is considered ad approved and the item representation must be updateds + // The item is considered ad approved and the item representation must be updated return cmItemStatus; } return cmItemStatus; } + protected boolean isModerationEnabled() { + return ckanInstance.isModerationEnabled(); + } + @Override public String read() { try { @@ -579,7 +640,7 @@ public class CKANPackage extends CKAN { result = mapper.readTree(ret); result = cleanResult(result); - if(ckanInstance.isModerationEnabled()) { + if(isModerationEnabled()) { CMItemStatus cmItemStatus = getCMItemStatus(); @@ -613,25 +674,80 @@ public class CKANPackage extends CKAN { } + protected void setToPending(JsonNode jsonNode) { + addExtraField(jsonNode, GCatConstants.SYSTEM_CM_ITEM_STATUS, CMItemStatus.PENDING.getValue()); + + CMItemVisibility cmItemVisibility = CMItemVisibility.PUBLIC; + + if(jsonNode.has(PRIVATE_KEY)) { + boolean privatePackage = jsonNode.get(PRIVATE_KEY).asBoolean(); + if(privatePackage) { + cmItemVisibility = CMItemVisibility.RESTRICTED; + } + } + addExtraField(jsonNode, GCatConstants.SYSTEM_CM_ITEM_VISIBILITY, cmItemVisibility.getValue()); + + ((ObjectNode) jsonNode).put(PRIVATE_KEY, true); + ((ObjectNode) jsonNode).put(SEARCHABLE_KEY, false); + } + + protected void setToApproved(JsonNode jsonNode) { + ArrayNode extras = (ArrayNode) jsonNode.get(EXTRAS_KEY); + + boolean approvedSet = false; + CMItemVisibility cmItemVisibility = null; + + for(JsonNode extra : extras) { + if(extra.has(EXTRAS_KEY_KEY) && extra.get(EXTRAS_KEY_KEY).asText().compareTo(GCatConstants.SYSTEM_CM_ITEM_STATUS) == 0) { + ((ObjectNode) extra).put(EXTRAS_VALUE_KEY, CMItemStatus.APPROVED.getValue()); + approvedSet = true; + } + + if(extra.has(EXTRAS_KEY_KEY) && extra.get(EXTRAS_KEY_KEY).asText().compareTo(GCatConstants.SYSTEM_CM_ITEM_VISIBILITY) == 0) { + cmItemVisibility = CMItemVisibility.getCMItemStatusFromValue(extra.get(EXTRAS_VALUE_KEY).asText()); + } + } + + + + if(!approvedSet) { + addExtraField(jsonNode, GCatConstants.SYSTEM_CM_ITEM_STATUS, CMItemStatus.APPROVED.getValue()); + } + + if(cmItemVisibility==null) { + cmItemVisibility = CMItemVisibility.PUBLIC; + addExtraField(jsonNode, GCatConstants.SYSTEM_CM_ITEM_VISIBILITY, cmItemVisibility.getValue()); + } + + + ((ObjectNode) jsonNode).put(PRIVATE_KEY, cmItemVisibility == CMItemVisibility.RESTRICTED ? true :false); + ((ObjectNode) jsonNode).put(SEARCHABLE_KEY, true); + } + + // see https://docs.ckan.org/en/latest/api/#ckan.logic.action.create.package_create @Override public String create(String json) { try { logger.debug("Going to create Item {}", json); - if(ckanInstance.isModerationEnabled()) { - // TODO - + if(ckanUser.getRole().ordinal() < Role.EDITOR.ordinal()) { + StringBuffer stringBuffer = new StringBuffer(); + stringBuffer.append("Only "); + stringBuffer.append(Role.EDITOR.getPortalRole()); + stringBuffer.append(" and "); + stringBuffer.append(Role.ADMIN.getPortalRole()); + stringBuffer.append(" are entitled to create items. "); + stringBuffer.append("Please contact the VRE Manager to request your grant."); + throw new ForbiddenException(stringBuffer.toString()); } JsonNode jsonNode = validateJson(json); - if(ckanInstance.isModerationEnabled()) { - addExtraField(jsonNode, GCatConstants.CM_ITEM_STATUS, CMItemStatus.APPROVED.getValue()); - addExtraField(jsonNode, GCatConstants.CM_ITEM_VISIBILITY, CMItemVisibility.PUBLIC.getValue()); + if(isModerationEnabled()) { + setToPending(jsonNode); } - ArrayNode resourcesToBeCreated = mapper.createArrayNode(); if(jsonNode.has(RESOURCES_KEY)) { resourcesToBeCreated = (ArrayNode) jsonNode.get(RESOURCES_KEY); @@ -671,19 +787,64 @@ public class CKANPackage extends CKAN { } } + protected JsonNode checkModerationUpdate(JsonNode jsonNode){ + PortalUser portalUser = ckanUser.getPortalUser(); + + if(isModerationEnabled()) { + CMItemStatus cmItemStatus = getCMItemStatus(); + + boolean setToPending = true; + + switch (cmItemStatus) { + case APPROVED: + if(ckanUser.getRole() != Role.ADMIN) { + throw new ForbiddenException("Only " + Role.ADMIN.getPortalRole() + "s are entitled to update an " + cmItemStatus.getValue() + " item"); + } + setToPending = false; + break; + + case PENDING: + if(result.get(AUTHOR_EMAIL_KEY).asText().compareTo(portalUser.getEMail())==0) { + break; + } + if(portalUser.isCatalogueModerator()) { + break; + } + throw new ForbiddenException("You are not entitled to update a " + cmItemStatus.getValue() + " item"); + + case REJECTED: + if(result.get(AUTHOR_EMAIL_KEY).asText().compareTo(portalUser.getEMail())==0) { + break; + } + if(ckanUser.getRole() == Role.ADMIN || portalUser.isCatalogueModerator()) { + break; + } + throw new ForbiddenException("You are not entitled to update a " + cmItemStatus.getValue() + " item"); + + default: + break; + } + + if(setToPending) { + setToPending(jsonNode); + } + + } + + return jsonNode; + } + // see https://docs.ckan.org/en/latest/api/#ckan.logic.action.update.package_update @Override public String update(String json) { try { - if(ckanInstance.isModerationEnabled()) { - // TODO - } - JsonNode jsonNode = validateJson(json); read(); this.itemID = result.get(ID_KEY).asText(); + jsonNode = checkModerationUpdate(jsonNode); + Map originalResources = new HashMap<>(); ArrayNode originalResourcesarrayNode = (ArrayNode) result.get(RESOURCES_KEY); if(originalResources != null) { @@ -748,21 +909,39 @@ public class CKANPackage extends CKAN { } } + public String approve() { + try { + read(); + + if(isModerationEnabled()) { + setToApproved(result); + } else { + throw new MethodNotSupportedException("The approve operation is available only in moderation mode"); + } + + result = cleanResult(result); + + return getAsString(result); + }catch(WebApplicationException e) { + throw e; + } catch(Exception e) { + throw new InternalServerErrorException(e); + } + } + // see https://docs.ckan.org/en/latest/api/#ckan.logic.action.patch.package_patch @Override public String patch(String json) { try { - if(ckanInstance.isModerationEnabled()) { - // TODO - } + read(); JsonNode jsonNode = checkBaseInformation(json, true); - read(); - this.itemID = result.get(ID_KEY).asText(); ((ObjectNode)jsonNode).put(ID_KEY, this.itemID); + jsonNode = checkModerationUpdate(jsonNode); + Map originalResources = new HashMap<>(); ArrayNode originalResourcesarrayNode = (ArrayNode) result.get(RESOURCES_KEY); if(originalResources != null) { @@ -830,7 +1009,7 @@ public class CKANPackage extends CKAN { @Override protected void delete() { - if(ckanInstance.isModerationEnabled()) { + if(isModerationEnabled()) { // TODO } super.delete(); 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 9bc8f41..60757b2 100644 --- a/src/test/java/org/gcube/gcat/persistence/ckan/CKANPackageTest.java +++ b/src/test/java/org/gcube/gcat/persistence/ckan/CKANPackageTest.java @@ -1,6 +1,7 @@ package org.gcube.gcat.persistence.ckan; import java.io.StringWriter; +import java.net.URI; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -10,12 +11,16 @@ import java.util.Set; import javax.ws.rs.ForbiddenException; import javax.ws.rs.core.MultivaluedHashMap; import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.core.PathSegment; +import javax.ws.rs.core.UriBuilder; +import javax.ws.rs.core.UriInfo; 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.CMItemStatus; import org.gcube.gcat.api.GCatConstants; import org.gcube.gcat.utils.ContextUtility; import org.gcube.informationsystem.publisher.RegistryPublisher; @@ -56,11 +61,130 @@ public class CKANPackageTest extends ContextTest { public void list() throws Exception { ContextTest.setContextByName("/gcube/devsec/devVRE"); CKANPackage ckanPackage = new CKANPackage(); + + UriInfo uriInfo = new UriInfo() { + + @Override + public URI resolve(URI uri) { + // TODO Auto-generated method stub + return null; + } + + @Override + public URI relativize(URI uri) { + // TODO Auto-generated method stub + return null; + } + + @Override + public UriBuilder getRequestUriBuilder() { + // TODO Auto-generated method stub + return null; + } + + @Override + public URI getRequestUri() { + // TODO Auto-generated method stub + return null; + } + + @Override + public MultivaluedMap getQueryParameters(boolean decode) { + return null; + } + + @Override + public MultivaluedMap getQueryParameters() { + MultivaluedMap mvm = new MultivaluedHashMap(); + mvm.add(GCatConstants.CM_ITEM_STATUS_QUERY_PARAMETER, CMItemStatus.PENDING.getValue()); + return mvm; + } + + @Override + public List getPathSegments(boolean decode) { + // TODO Auto-generated method stub + return null; + } + + @Override + public List getPathSegments() { + // TODO Auto-generated method stub + return null; + } + + @Override + public MultivaluedMap getPathParameters(boolean decode) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MultivaluedMap getPathParameters() { + // TODO Auto-generated method stub + return null; + } + + @Override + public String getPath(boolean decode) { + // TODO Auto-generated method stub + return null; + } + + @Override + public String getPath() { + // TODO Auto-generated method stub + return null; + } + + @Override + public List getMatchedURIs(boolean decode) { + // TODO Auto-generated method stub + return null; + } + + @Override + public List getMatchedURIs() { + // TODO Auto-generated method stub + return null; + } + + @Override + public List getMatchedResources() { + // TODO Auto-generated method stub + return null; + } + + @Override + public UriBuilder getBaseUriBuilder() { + // TODO Auto-generated method stub + return null; + } + + @Override + public URI getBaseUri() { + // TODO Auto-generated method stub + return null; + } + + @Override + public UriBuilder getAbsolutePathBuilder() { + // TODO Auto-generated method stub + return null; + } + + @Override + public URI getAbsolutePath() { + // TODO Auto-generated method stub + return null; + } + }; + ckanPackage.setUriInfo(uriInfo); + ObjectMapper mapper = new ObjectMapper(); String ret = ckanPackage.list(10, 0); JsonNode gotList = mapper.readTree(ret); Assert.assertTrue(gotList instanceof ArrayNode); - logger.debug("List :\n{}", mapper.writeValueAsString(gotList)); + logger.debug("List:\n{}", mapper.writeValueAsString(gotList)); } @Test