From e2d728042f8c552b981bf622f877ef3b847590c5 Mon Sep 17 00:00:00 2001 From: Fabio Sinibaldi Date: Mon, 28 Mar 2022 16:35:30 +0200 Subject: [PATCH] Default 3Phase Manager --- .../cms/plugins/AbstractLifeCycleManager.java | 6 - .../cms/plugins/LifecycleManager.java | 6 +- .../faults/InsufficientPrivileges.java | 22 ++ .../AbstractLifeCycleManager.java | 120 +++++++ .../{ => implementations}/AbstractPlugin.java | 7 +- .../implementations/Default3PhaseManager.java | 323 ++++++++++++++++++ .../cms/plugins/model/StepAccess.java | 29 ++ .../plugins/reports/EventExecutionReport.java | 2 +- .../plugins/ConcessioniLifeCycleManager.java | 287 +--------------- .../lifecycle/LifecycleInformation.java | 10 + .../common/model/useCaseDescriptor/Field.java | 2 +- .../common/utils/tests/GCubeTest.java | 2 +- .../handlers/SimpleLifeCycleManager.java | 79 ----- .../engine/mongo/ProfiledMongoManager.java | 42 ++- .../service/profiledDocuments/RolesTests.java | 42 +++ .../test/resources/profiles/rolesTesting.json | 20 ++ .../cms/sdi/plugins/SDIAbstractPlugin.java | 3 +- .../sdi/plugins/SDIMaterializerPlugin.java | 2 +- 18 files changed, 612 insertions(+), 392 deletions(-) delete mode 100644 cms-plugin-framework/src/main/java/org/gcube/application/cms/plugins/AbstractLifeCycleManager.java create mode 100644 cms-plugin-framework/src/main/java/org/gcube/application/cms/plugins/faults/InsufficientPrivileges.java create mode 100644 cms-plugin-framework/src/main/java/org/gcube/application/cms/plugins/implementations/AbstractLifeCycleManager.java rename cms-plugin-framework/src/main/java/org/gcube/application/cms/plugins/{ => implementations}/AbstractPlugin.java (85%) create mode 100644 cms-plugin-framework/src/main/java/org/gcube/application/cms/plugins/implementations/Default3PhaseManager.java create mode 100644 cms-plugin-framework/src/main/java/org/gcube/application/cms/plugins/model/StepAccess.java delete mode 100644 geoportal-service/src/main/java/org/gcube/application/geoportal/service/engine/handlers/SimpleLifeCycleManager.java create mode 100644 geoportal-service/src/test/java/org/gcube/application/geoportal/service/profiledDocuments/RolesTests.java create mode 100644 geoportal-service/src/test/resources/profiles/rolesTesting.json diff --git a/cms-plugin-framework/src/main/java/org/gcube/application/cms/plugins/AbstractLifeCycleManager.java b/cms-plugin-framework/src/main/java/org/gcube/application/cms/plugins/AbstractLifeCycleManager.java deleted file mode 100644 index b004032..0000000 --- a/cms-plugin-framework/src/main/java/org/gcube/application/cms/plugins/AbstractLifeCycleManager.java +++ /dev/null @@ -1,6 +0,0 @@ -package org.gcube.application.cms.plugins; - -public abstract class AbstractLifeCycleManager { - - -} diff --git a/cms-plugin-framework/src/main/java/org/gcube/application/cms/plugins/LifecycleManager.java b/cms-plugin-framework/src/main/java/org/gcube/application/cms/plugins/LifecycleManager.java index f00a141..6317e06 100644 --- a/cms-plugin-framework/src/main/java/org/gcube/application/cms/plugins/LifecycleManager.java +++ b/cms-plugin-framework/src/main/java/org/gcube/application/cms/plugins/LifecycleManager.java @@ -1,8 +1,6 @@ package org.gcube.application.cms.plugins; -import org.gcube.application.cms.plugins.faults.EventException; -import org.gcube.application.cms.plugins.faults.InvalidPluginRequestException; -import org.gcube.application.cms.plugins.faults.StepException; +import org.gcube.application.cms.plugins.faults.*; import org.gcube.application.cms.plugins.reports.EventExecutionReport; import org.gcube.application.cms.plugins.reports.StepExecutionReport; import org.gcube.application.cms.plugins.requests.BaseRequest; @@ -17,7 +15,7 @@ public interface LifecycleManager extends InitializablePlugin{ // Lifecycle operations - public StepExecutionReport performStep(StepExecutionRequest request) throws StepException, InvalidPluginRequestException; + public StepExecutionReport performStep(StepExecutionRequest request) throws StepException, InvalidPluginRequestException, InvalidProfileException, ConfigurationException, InsufficientPrivileges; public Configuration getCurrentConfiguration(BaseRequest request) throws ConfigurationException; diff --git a/cms-plugin-framework/src/main/java/org/gcube/application/cms/plugins/faults/InsufficientPrivileges.java b/cms-plugin-framework/src/main/java/org/gcube/application/cms/plugins/faults/InsufficientPrivileges.java new file mode 100644 index 0000000..c33a332 --- /dev/null +++ b/cms-plugin-framework/src/main/java/org/gcube/application/cms/plugins/faults/InsufficientPrivileges.java @@ -0,0 +1,22 @@ +package org.gcube.application.cms.plugins.faults; + +public class InsufficientPrivileges extends Exception { + public InsufficientPrivileges() { + } + + public InsufficientPrivileges(String message) { + super(message); + } + + public InsufficientPrivileges(String message, Throwable cause) { + super(message, cause); + } + + public InsufficientPrivileges(Throwable cause) { + super(cause); + } + + public InsufficientPrivileges(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } +} diff --git a/cms-plugin-framework/src/main/java/org/gcube/application/cms/plugins/implementations/AbstractLifeCycleManager.java b/cms-plugin-framework/src/main/java/org/gcube/application/cms/plugins/implementations/AbstractLifeCycleManager.java new file mode 100644 index 0000000..ef17385 --- /dev/null +++ b/cms-plugin-framework/src/main/java/org/gcube/application/cms/plugins/implementations/AbstractLifeCycleManager.java @@ -0,0 +1,120 @@ +package org.gcube.application.cms.plugins.implementations; + +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; +import org.gcube.application.cms.plugins.LifecycleManager; +import org.gcube.application.cms.plugins.PluginManagerInterface; +import org.gcube.application.cms.plugins.faults.*; +import org.gcube.application.cms.plugins.model.StepAccess; +import org.gcube.application.cms.plugins.reports.EventExecutionReport; +import org.gcube.application.cms.plugins.reports.InitializationReport; +import org.gcube.application.cms.plugins.reports.Report; +import org.gcube.application.cms.plugins.reports.StepExecutionReport; +import org.gcube.application.cms.plugins.requests.BaseRequest; +import org.gcube.application.cms.plugins.requests.EventExecutionRequest; +import org.gcube.application.cms.plugins.requests.StepExecutionRequest; +import org.gcube.application.cms.serialization.Serialization; +import org.gcube.application.geoportal.common.model.configuration.Configuration; +import org.gcube.application.geoportal.common.model.document.accounting.User; +import org.gcube.application.geoportal.common.model.document.lifecycle.LifecycleInformation; +import org.gcube.application.geoportal.common.model.document.lifecycle.TriggeredEvents; +import org.gcube.application.geoportal.common.model.rest.ConfigurationException; +import org.gcube.application.geoportal.common.model.useCaseDescriptor.HandlerDeclaration; +import org.gcube.application.geoportal.common.utils.ContextUtils; + +import java.util.List; + +@Slf4j +public abstract class AbstractLifeCycleManager extends AbstractPlugin implements LifecycleManager { + + @Setter + protected PluginManagerInterface pluginManager; + + + @Override + public StepExecutionReport performStep(StepExecutionRequest request) throws StepException, InvalidPluginRequestException, InvalidProfileException, ConfigurationException, InsufficientPrivileges { + log.info("Serving Request {}",request); + StepExecutionReport report=new StepExecutionReport(request); + report.setStatus(Report.Status.OK); + + if(!canInvokeStep(request.getStep(),request.getCaller(), + getConfigurationFromProfile(request.getUseCaseDescriptor()))) + throw new InsufficientPrivileges("User is not allowed to invoke "+request.getStep()); + + + LifecycleInformation info=report.getToSetLifecycleInformation(); + info.setLastOperationStatus(LifecycleInformation.Status.OK); + info.setLastInvokedStep(request.getStep()); + + return report; + } + + + protected static boolean canInvokeStep(String stepID, User u, HandlerDeclaration config) throws ConfigurationException { + log.debug("Checking if {} can access STEP {}",u,stepID); + log.trace("Config is {}",config); + List l =config.getConfiguration().get("step_access", List.class); + if(l==null|| l.isEmpty()) throw new ConfigurationException("Missing Role management in UCD"); + for (Object o : l) { + StepAccess a= Serialization.convert(o,StepAccess.class); + if(a.getStepId().equals(stepID)){ + // found step ID + log.trace("Found Step ID declaration {} ",a); + for (String s : a.getRoles()) { + if (u.getRoles().contains(s)) + return true; + } + } + } + return false; + } + + + @Override + public InitializationReport initInContext() throws InitializationException { + InitializationReport report = new InitializationReport(); + try{ + report.setStatus(Report.Status.OK); + } catch (Throwable e) { + log.error("Unable to initialize plugins in {} ", ContextUtils.getCurrentScope(),e); + report.setStatus(Report.Status.WARNING); + report.putMessage("Unable to initialize plugins in "+ ContextUtils.getCurrentScope()+" : "+e.getMessage()); + } + return report; + } + + @Override + public InitializationReport init() throws InitializationException { + InitializationReport report = new InitializationReport(); + try{ + report.setStatus(Report.Status.OK); + + } catch (Throwable e) { + log.error("Unable to initialize plugins ",e); + report.setStatus(Report.Status.WARNING); + report.putMessage("Unable to initialize plugins : "+e.getMessage()); + } + return report; + } + + @Override + public void shutdown() throws ShutDownException {} + + @Override + public Configuration getCurrentConfiguration(BaseRequest request) throws ConfigurationException { + return new Configuration(); + } + + @Override + public EventExecutionReport onEvent(EventExecutionRequest request) throws EventException, InvalidPluginRequestException { + log.info("Executing Event {}",request); + EventExecutionReport report=new EventExecutionReport(request); + + TriggeredEvents info=new TriggeredEvents(); + info.setEvent(request.getEvent()); + info.setLastOperationStatus(LifecycleInformation.Status.OK); + report.setStatus(Report.Status.OK); + report.getToSetLifecycleInformation().addEventReport(info); + return report; + } +} diff --git a/cms-plugin-framework/src/main/java/org/gcube/application/cms/plugins/AbstractPlugin.java b/cms-plugin-framework/src/main/java/org/gcube/application/cms/plugins/implementations/AbstractPlugin.java similarity index 85% rename from cms-plugin-framework/src/main/java/org/gcube/application/cms/plugins/AbstractPlugin.java rename to cms-plugin-framework/src/main/java/org/gcube/application/cms/plugins/implementations/AbstractPlugin.java index 5f9a640..c0fe6d7 100644 --- a/cms-plugin-framework/src/main/java/org/gcube/application/cms/plugins/AbstractPlugin.java +++ b/cms-plugin-framework/src/main/java/org/gcube/application/cms/plugins/implementations/AbstractPlugin.java @@ -1,5 +1,6 @@ -package org.gcube.application.cms.plugins; +package org.gcube.application.cms.plugins.implementations; +import org.gcube.application.cms.plugins.Plugin; import org.gcube.application.cms.plugins.faults.InvalidProfileException; import org.gcube.application.geoportal.common.model.useCaseDescriptor.HandlerDeclaration; import org.gcube.application.geoportal.common.model.useCaseDescriptor.UseCaseDescriptor; @@ -7,7 +8,7 @@ import org.gcube.application.geoportal.common.model.useCaseDescriptor.UseCaseDes import java.util.List; import java.util.Map; -public abstract class AbstractPlugin implements Plugin{ +public abstract class AbstractPlugin implements Plugin { @@ -24,4 +25,6 @@ public abstract class AbstractPlugin implements Plugin{ else throw new InvalidProfileException("No Configuration found for "+getDescriptor().getId()+" in "+p.getId()); } + + } diff --git a/cms-plugin-framework/src/main/java/org/gcube/application/cms/plugins/implementations/Default3PhaseManager.java b/cms-plugin-framework/src/main/java/org/gcube/application/cms/plugins/implementations/Default3PhaseManager.java new file mode 100644 index 0000000..380f8da --- /dev/null +++ b/cms-plugin-framework/src/main/java/org/gcube/application/cms/plugins/implementations/Default3PhaseManager.java @@ -0,0 +1,323 @@ +package org.gcube.application.cms.plugins.implementations; + +import com.sun.xml.internal.xsom.impl.scd.Step; +import lombok.extern.slf4j.Slf4j; +import org.bson.Document; +import org.gcube.application.cms.plugins.IndexerPluginInterface; +import org.gcube.application.cms.plugins.MaterializationPlugin; +import org.gcube.application.cms.plugins.implementations.AbstractLifeCycleManager; +import org.gcube.application.cms.plugins.LifecycleManager; +import org.gcube.application.cms.plugins.faults.*; +import org.gcube.application.cms.plugins.model.PluginDescriptor; +import org.gcube.application.cms.plugins.model.StepAccess; +import org.gcube.application.cms.plugins.reports.*; +import org.gcube.application.cms.plugins.requests.*; +import org.gcube.application.cms.plugins.model.ComparableVersion; +import org.gcube.application.cms.serialization.Serialization; +import org.gcube.application.geoportal.common.model.configuration.Configuration; +import org.gcube.application.geoportal.common.model.configuration.Index; +import org.gcube.application.geoportal.common.model.document.accounting.User; +import org.gcube.application.geoportal.common.model.document.lifecycle.LifecycleInformation; +import org.gcube.application.geoportal.common.model.document.lifecycle.TriggeredEvents; +import org.gcube.application.geoportal.common.model.rest.ConfigurationException; +import org.gcube.application.geoportal.common.model.useCaseDescriptor.HandlerDeclaration; +import org.gcube.application.geoportal.common.utils.Files; + +import java.util.ArrayList; +import java.util.List; + +@Slf4j +public class Default3PhaseManager extends AbstractLifeCycleManager implements LifecycleManager { + + private static class Phases { + public static final String PENDING_APPROVAL="Pending Approval"; + public static final String PUBLISHED="PUBLISHED"; + + } + + + private static class STEPS{ + public static final String SUBMIT="SUBMIT-FOR-REVIEW"; + public static final String REJECT="REJECT-DRAFT"; + public static final String APPROVE="APPROVE-SUBMITTED"; + } + + private static class PARAMETERS{ + public static final String NOTES="notes"; + } + + + protected PluginDescriptor DESCRIPTOR=new PluginDescriptor("DEFAULT-3PHASE", PluginDescriptor.BaseTypes.LIFECYCLE_MANAGER); + + public Default3PhaseManager() { + DESCRIPTOR.setDescription("Default 3-phase lifecycle manager. This plugin supports a simple moderated publication lifecycle."); + DESCRIPTOR.setVersion(new ComparableVersion("1.0.0")); + } + + protected static boolean canInvokeStep(String stepID, User u, HandlerDeclaration config) throws ConfigurationException { + List l =config.getConfiguration().get("step_access", List.class); + if(l==null|| l.isEmpty()) throw new ConfigurationException("Missing Role management in UCD"); + boolean existingRule=false; + for (Object o : l) { + StepAccess a= Serialization.convert(o, StepAccess.class); + if(a.getStepId().equals(stepID)) { + existingRule = true; + log.trace("Found role {}",a); + for (String role : a.getRoles()) + if (u.getRoles().contains(role)) + return true; + } + } + if(!existingRule) throw new ConfigurationException("Missing step "+stepID+" access definition"); + // nothing matches + return false; + } + + + @Override + public Configuration getCurrentConfiguration(BaseRequest req) throws ConfigurationException { + Configuration toReturn = super.getCurrentConfiguration(req); + toReturn.setIndexes(new ArrayList<>()); + + IndexerPluginInterface indexerPlugin; + indexerPlugin = (IndexerPluginInterface) pluginManager.getById("SDI-Indexer-Plugin"); + BaseRequest indexRequest = new BaseRequest(req.getUseCaseDescriptor(),req.getCaller(),req.getContext()); + + // Info on Public index + try { + indexRequest.setCallParameters(getPublicIndexParams(req)); + Index publicIndex = indexerPlugin.getIndex(indexRequest); + publicIndex.put("flag", "public"); + toReturn.getIndexes().add(publicIndex); + }catch(ConfigurationException e){ + toReturn.addErrorMessage("Unable to gather information on public GIS Centroids Index : "+e.getMessage()); + log.error("Unable to gather information on public GIS Centroids Index",e); + } + // Info on internal_index + try { + indexRequest.setCallParameters(getInternalIndexParams(req)); + Index internalIndex = indexerPlugin.getIndex(indexRequest); + internalIndex.put("flag", "internal"); + toReturn.getIndexes().add(internalIndex); + }catch(ConfigurationException e){ + toReturn.addErrorMessage("Unable to gather information on internal GIS Centroids Index : "+e.getMessage()); + log.error("Unable to gather information on internal GIS Centroids Index",e); + } + return toReturn; + } + + + private Document getInternalIndexParams(BaseRequest req){ + Document callParameters = new Document(); + + callParameters.put("workspace", Files.fixFilename(req.getUseCaseDescriptor().getId()+"_internal_"+req.getContext().getName())); + callParameters.put("indexName",Files.fixFilename(req.getUseCaseDescriptor().getId()+"_internal_"+req.getContext().getName()+"_centroids")); + return callParameters; + } + + private Document getPublicIndexParams(BaseRequest req){ + Document callParameters = new Document(); + callParameters.put("workspace",Files.fixFilename(req.getUseCaseDescriptor().getId()+req.getContext().getName())); + callParameters.put("indexName",Files.fixFilename(req.getUseCaseDescriptor().getId()+req.getContext().getName()+"_centroids")); + return callParameters; + } + + + @Override + public EventExecutionReport onEvent(EventExecutionRequest request) throws EventException, InvalidPluginRequestException { + EventExecutionReport report=super.onEvent(request); + TriggeredEvents info = report.getToSetLifecycleInformation().getLastEvent(); + try { + switch(request.getEvent()){ + case EventExecutionRequest.Events.ON_INIT_DOCUMENT: + // Set Defaults as for on update + case EventExecutionRequest.Events.ON_UPDATE_DOCUMENT: { + log.debug("Setting default values.."); + report=setDefault(report); + break; + } + case EventExecutionRequest.Events.ON_DELETE_DOCUMENT: { + //DELETE ALL + break; + } + default: throw new EventException("Unexpected Event "+request.getEvent()); + } + }catch (EventException e){ + throw e; + }catch (Throwable t){ + log.error("Unable to execute on event "+request.getEvent(),t); + String msg = "Unable to execute on event "+request.getEvent()+". Error was "+t.getMessage(); + info.setLastOperationStatus(LifecycleInformation.Status.ERROR); + info.addErrorMessage(msg); + report.setStatus(Report.Status.ERROR); + report.putMessage(msg); + } + return report; + } + + + + @Override + public StepExecutionReport performStep(StepExecutionRequest request) throws StepException, InvalidPluginRequestException, InvalidProfileException, ConfigurationException, InsufficientPrivileges { + StepExecutionReport report = super.performStep(request); + LifecycleInformation info=report.getToSetLifecycleInformation(); + try { + if(!canInvokeStep(request.getStep(),request.getCaller(),getConfigurationFromProfile(request.getUseCaseDescriptor()))) + throw new InsufficientPrivileges("Insufficient privileges for executing step "+request.getStep()); + + MaterializationPlugin plugin; + IndexerPluginInterface indexerPlugin; + plugin= (MaterializationPlugin) pluginManager.getById("SDI-Default-Materializer"); + indexerPlugin = (IndexerPluginInterface) pluginManager.getById("SDI-Indexer-Plugin"); + + + + switch (request.getStep()) { + case STEPS.SUBMIT:{ + //TODO validation + + if(!request.getDocument().getLifecycleInformation().getPhase().equals(LifecycleInformation.DRAFT_PHASE)) + throw new StepException("Document is not in "+LifecycleInformation.DRAFT_PHASE+" phase"); + //Materialize layers + MaterializationRequest matReq = new MaterializationRequest(request.getUseCaseDescriptor(),request.getCaller(), request.getContext(), request.getDocument()); + + matReq.setDocument(request.getDocument()); + matReq.setUseCaseDescriptor(request.getUseCaseDescriptor()); + Document params = new Document(); + String workspace = request.getUseCaseDescriptor().getId() + request.getContext().getId(); + params.put("workspace", Files.fixFilename(workspace)); + + + matReq.setCallParameters(params); + MaterializationReport matRep = plugin.materialize(matReq); + + + report.setResultingDocument(matRep.getResultingDocument()); + + + switch(matRep.getStatus()){ + case OK : { + index(report,indexerPlugin); + //TODO Optional Notifications + break; + } + case ERROR : { + info.setLastOperationStatus(LifecycleInformation.Status.ERROR); + matRep.getMessages().forEach(s -> info.addErrorMessage(s)); + break; + } + case WARNING : { + info.setLastOperationStatus(LifecycleInformation.Status.WARNING); + matRep.getMessages().forEach(s -> info.addWarningMessage(s)); + break; + } + } + + } + break; + case STEPS.REJECT:{ + if(!request.getDocument().getLifecycleInformation().getPhase().equals(Phases.PENDING_APPROVAL)) + throw new StepException("Document is not in "+Phases.PENDING_APPROVAL+" phase"); + // TODO OPTIONAL Notification + + info.setPhase(Phases.PENDING_APPROVAL); + if(request.getCallParameters()!=null&&request.getCallParameters().containsKey(PARAMETERS.NOTES)) + info.setNotes(request.getCallParameters().getString(PARAMETERS.NOTES)); + break; + } + + case STEPS.APPROVE:{ + // Index-published + if(!request.getDocument().getLifecycleInformation().getPhase() + .equals(Phases.PENDING_APPROVAL)) + throw new StepException("Document is not in "+Phases.PENDING_APPROVAL+" phase"); + + index(report,indexerPlugin); + break; + } + + default: + throw new StepException("Invalid Step " + request.getStep()); + } + }catch (StepException e){ + throw e; + }catch (Throwable t){ + log.error("Unable to perform step "+request.getStep(),t); + String msg = "Unable to execute Step "+request.getStep()+". Error was "+t.getMessage(); + report.setStatus(Report.Status.ERROR); + report.putMessage(msg); + info.setLastOperationStatus(LifecycleInformation.Status.ERROR); + info.addErrorMessage(msg); + } + return report; + } + + + + private IndexDocumentReport index(StepExecutionReport report,IndexerPluginInterface indexer) throws InvalidPluginRequestException { + StepExecutionRequest request = report.getTheRequest(); + LifecycleInformation info=report.getToSetLifecycleInformation(); + IndexDocumentRequest indexRequest = new IndexDocumentRequest( + request.getUseCaseDescriptor(),request.getCaller(), request.getContext(),request.getDocument()); + + + indexRequest.setCallParameters(new Document()); + if(request.getStep().equals(STEPS.APPROVE)) { + // public index + indexRequest.getCallParameters().putAll(getPublicIndexParams(request)); + } + else { + // private index + indexRequest.getCallParameters().putAll(getPublicIndexParams(request)); + } + IndexDocumentReport indexReport = indexer.index(indexRequest); + + + switch(indexReport.getStatus()){ + case OK : { + if(request.getStep().equals(STEPS.APPROVE)) { + // public index + info.setPhase(Phases.PUBLISHED); + } + else { + // private index + info.setPhase(Phases.PENDING_APPROVAL); + } + report.setToSetSpatialReference(indexReport.getToSetSpatialReference()); + break; + } + case ERROR : { + info.setLastOperationStatus(LifecycleInformation.Status.ERROR); + indexReport.getMessages().forEach(s -> info.addErrorMessage(s)); + break; + } + case WARNING : { + info.setLastOperationStatus(LifecycleInformation.Status.WARNING); + indexReport.getMessages().forEach(s -> info.addWarningMessage(s)); + break; + } + } + + return indexReport; + + } + + + + + /** + * Override this method for programmatic default values management + * + * @param currentReport + * @return + */ + public EventExecutionReport setDefault(EventExecutionReport currentReport){ + // Default implementation is no op + return currentReport; + } + + @Override + public PluginDescriptor getDescriptor() { + return DESCRIPTOR; + } +} diff --git a/cms-plugin-framework/src/main/java/org/gcube/application/cms/plugins/model/StepAccess.java b/cms-plugin-framework/src/main/java/org/gcube/application/cms/plugins/model/StepAccess.java new file mode 100644 index 0000000..6b3dc88 --- /dev/null +++ b/cms-plugin-framework/src/main/java/org/gcube/application/cms/plugins/model/StepAccess.java @@ -0,0 +1,29 @@ +package org.gcube.application.cms.plugins.model; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; + +import javax.xml.bind.annotation.XmlRootElement; +import java.util.List; + +@XmlRootElement +@AllArgsConstructor +@Getter +@Setter +@ToString(callSuper = true) +public class StepAccess { + + public static final String STEP="STEP"; + public static final String ROLES="roles"; + + + @JsonProperty(STEP) + private String stepId; + + @JsonProperty(ROLES) + private List roles; +} diff --git a/cms-plugin-framework/src/main/java/org/gcube/application/cms/plugins/reports/EventExecutionReport.java b/cms-plugin-framework/src/main/java/org/gcube/application/cms/plugins/reports/EventExecutionReport.java index 8680642..018ab64 100644 --- a/cms-plugin-framework/src/main/java/org/gcube/application/cms/plugins/reports/EventExecutionReport.java +++ b/cms-plugin-framework/src/main/java/org/gcube/application/cms/plugins/reports/EventExecutionReport.java @@ -16,6 +16,6 @@ public class EventExecutionReport extends DocumentHandlingReport info.addErrorMessage(s)); - break; - } - case WARNING : { - info.setLastOperationStatus(LifecycleInformation.Status.WARNING); - matRep.getMessages().forEach(s -> info.addWarningMessage(s)); - break; - } - } - - } - break; - case "REJECT-DRAFT":{ - // Notification - // Set - break; - } - - case "APPROVE DRAFT":{ - // Index-published - - if(!request.getDocument().getLifecycleInformation().getPhase() - .equals(Phases.PENDING_APPROVAL)) - throw new StepException("Document is not in "+Phases.PENDING_APPROVAL+" phase"); - - IndexDocumentRequest indexRequest = new IndexDocumentRequest(request.getUseCaseDescriptor(),request.getCaller(), request.getContext(),request.getDocument()); - - Document callParameters = getPublicIndexParams(request); - indexRequest.setCallParameters(callParameters); - - IndexDocumentReport indexReport = indexerPlugin.index(indexRequest); - - - - switch(indexReport.getStatus()){ - case OK : { - info.setPhase("PUBLISHED"); - report.setToSetSpatialReference(indexReport.getToSetSpatialReference()); - break; - } - case ERROR : { - info.setLastOperationStatus(LifecycleInformation.Status.ERROR); - indexReport.getMessages().forEach(s -> info.addErrorMessage(s)); - break; - } - case WARNING : { - info.setLastOperationStatus(LifecycleInformation.Status.WARNING); - indexReport.getMessages().forEach(s -> info.addWarningMessage(s)); - break; - } - } - break; - } - - default: - throw new StepException("Invalid Step " + request.getStep()); - } - }catch (StepException e){ - throw e; - }catch (Throwable t){ - log.error("Unable to perform step "+request.getStep(),t); - String msg = "Unable to execute Step "+request.getStep()+". Error was "+t.getMessage(); - report.setStatus(Report.Status.ERROR); - report.putMessage(msg); - info.setLastOperationStatus(LifecycleInformation.Status.ERROR); - info.addErrorMessage(msg); - } - return report; - } - - - @Override - public EventExecutionReport onEvent(EventExecutionRequest request) throws EventException, InvalidPluginRequestException { - log.info("Executing Event {}",request); - EventExecutionReport report=new EventExecutionReport(request); - - TriggeredEvents info=new TriggeredEvents(); - info.setEvent(request.getEvent()); - info.setLastOperationStatus(LifecycleInformation.Status.OK); - report.setStatus(Report.Status.OK); - - try { - switch(request.getEvent()){ - case EventExecutionRequest.Events.ON_INIT_DOCUMENT: - // Set Defaults as for on update - case EventExecutionRequest.Events.ON_UPDATE_DOCUMENT: { - log.debug("Setting default values.."); - // TODO implement defaults for concessione - // report.setResult(setDefaults(request.getDocument())); - break; - } - case EventExecutionRequest.Events.ON_DELETE_DOCUMENT: { - //DELETE ALL - break; - } - default: throw new EventException("Unexpected Event "+request.getEvent()); - } - }catch (EventException e){ - throw e; - }catch (Throwable t){ - log.error("Unable to execute on event "+request.getEvent(),t); - String msg = "Unable to execute on event "+request.getEvent()+". Error was "+t.getMessage(); - info.setLastOperationStatus(LifecycleInformation.Status.ERROR); - info.addErrorMessage(msg); - report.setStatus(Report.Status.ERROR); - report.putMessage(msg); - }finally{ - report.getToSetLifecycleInformation().addEventReport(info); - } - return report; - } - - - - @Override - public Configuration getCurrentConfiguration(BaseRequest req) throws ConfigurationException { - Configuration toReturn = new Configuration(); - toReturn.setIndexes(new ArrayList<>()); - - IndexerPluginInterface indexerPlugin; - indexerPlugin = (IndexerPluginInterface) pluginManager.getById("SDI-Indexer-Plugin"); - BaseRequest indexRequest = new BaseRequest(req.getUseCaseDescriptor(),req.getCaller(),req.getContext()); - - // Info on Public index - try { - indexRequest.setCallParameters(getPublicIndexParams(req)); - Index publicIndex = indexerPlugin.getIndex(indexRequest); - publicIndex.put("flag", "public"); - toReturn.getIndexes().add(publicIndex); - }catch(ConfigurationException e){ - toReturn.addErrorMessage("Unable to gather information on public GIS Centroids Index : "+e.getMessage()); - log.error("Unable to gather information on public GIS Centroids Index",e); - } - // Info on internal_index - try { - indexRequest.setCallParameters(getInternalIndexParams(req)); - Index internalIndex = indexerPlugin.getIndex(indexRequest); - internalIndex.put("flag", "internal"); - toReturn.getIndexes().add(internalIndex); - }catch(ConfigurationException e){ - toReturn.addErrorMessage("Unable to gather information on internal GIS Centroids Index : "+e.getMessage()); - log.error("Unable to gather information on internal GIS Centroids Index",e); - } - return toReturn; - } - - @Override - public PluginDescriptor getDescriptor() { - return DESCRIPTOR; - } - - - private Document getInternalIndexParams(BaseRequest req){ - Document callParameters = new Document(); - - callParameters.put("workspace",Files.fixFilename(req.getUseCaseDescriptor().getId()+"_internal_"+req.getContext().getName())); - callParameters.put("indexName",Files.fixFilename(req.getUseCaseDescriptor().getId()+"_internal_"+req.getContext().getName()+"_centroids")); - return callParameters; - } - - private Document getPublicIndexParams(BaseRequest req){ - Document callParameters = new Document(); - callParameters.put("workspace",Files.fixFilename(req.getUseCaseDescriptor().getId()+req.getContext().getName())); - callParameters.put("indexName",Files.fixFilename(req.getUseCaseDescriptor().getId()+req.getContext().getName()+"_centroids")); - return callParameters; - } // STATIC ROUTINES private static final Project setDefaults(Project document){ ProfiledConcessione c=Serialization.convert(document,ProfiledConcessione.class); - - - - Document doc=c.getTheDocument(); + Document doc=c.getTheDocument(); doc.putIfAbsent(ProfiledConcessione.SOGGETTO,new String[]{"Research Excavation","Archaeology"}); doc.putIfAbsent(ProfiledConcessione.DSCRIZIONE_CONTENUTO,"Relazione di fine scavo e relativo abstract; selezione di immagini rappresentative;" diff --git a/geoportal-common/src/main/java/org/gcube/application/geoportal/common/model/document/lifecycle/LifecycleInformation.java b/geoportal-common/src/main/java/org/gcube/application/geoportal/common/model/document/lifecycle/LifecycleInformation.java index 1648666..0931c94 100644 --- a/geoportal-common/src/main/java/org/gcube/application/geoportal/common/model/document/lifecycle/LifecycleInformation.java +++ b/geoportal-common/src/main/java/org/gcube/application/geoportal/common/model/document/lifecycle/LifecycleInformation.java @@ -21,6 +21,7 @@ public class LifecycleInformation { public static final String ERROR_MESSAGES="_errorMessages"; public static final String WARNING_MESSAGES="_warningMessages"; public static final String TRIGGERED_EVENTS="_triggeredEvents"; + public static final String NOTES="_notes"; public static enum Status{ OK,ERROR,WARNING @@ -38,6 +39,15 @@ public class LifecycleInformation { private List warningMessages; @JsonProperty(TRIGGERED_EVENTS) private List triggeredEvents; + @JsonProperty(NOTES) + private String notes; + + + @JsonIgnore + public TriggeredEvents getLastEvent(){ + if(triggeredEvents==null || triggeredEvents.isEmpty()) return null; + return triggeredEvents.get(triggeredEvents.size()-1); + } @JsonIgnore public LifecycleInformation addErrorMessage(String msg){ diff --git a/geoportal-common/src/main/java/org/gcube/application/geoportal/common/model/useCaseDescriptor/Field.java b/geoportal-common/src/main/java/org/gcube/application/geoportal/common/model/useCaseDescriptor/Field.java index bcfa0cb..580b83e 100644 --- a/geoportal-common/src/main/java/org/gcube/application/geoportal/common/model/useCaseDescriptor/Field.java +++ b/geoportal-common/src/main/java/org/gcube/application/geoportal/common/model/useCaseDescriptor/Field.java @@ -15,7 +15,7 @@ import java.util.Map; @Getter @Setter @ToString(callSuper = true) -public class Field extends Document { +public class Field extends Document { public static final String TYPE="_type"; public static final String CHILDREN="_children"; diff --git a/geoportal-common/src/main/java/org/gcube/application/geoportal/common/utils/tests/GCubeTest.java b/geoportal-common/src/main/java/org/gcube/application/geoportal/common/utils/tests/GCubeTest.java index 7923f80..bd9c3c5 100644 --- a/geoportal-common/src/main/java/org/gcube/application/geoportal/common/utils/tests/GCubeTest.java +++ b/geoportal-common/src/main/java/org/gcube/application/geoportal/common/utils/tests/GCubeTest.java @@ -12,7 +12,7 @@ public class GCubeTest { // testContext = "/pred4s/preprod/preVRE"; - // testContext = "/gcube/devsec/devVRE"; + testContext = "/gcube/devsec/devVRE"; System.out.println("TEST CONTEXT = "+testContext); diff --git a/geoportal-service/src/main/java/org/gcube/application/geoportal/service/engine/handlers/SimpleLifeCycleManager.java b/geoportal-service/src/main/java/org/gcube/application/geoportal/service/engine/handlers/SimpleLifeCycleManager.java deleted file mode 100644 index b176836..0000000 --- a/geoportal-service/src/main/java/org/gcube/application/geoportal/service/engine/handlers/SimpleLifeCycleManager.java +++ /dev/null @@ -1,79 +0,0 @@ -package org.gcube.application.geoportal.service.engine.handlers; - -import lombok.extern.slf4j.Slf4j; -import org.gcube.application.cms.plugins.LifecycleManager; -import org.gcube.application.cms.plugins.PluginManagerInterface; -import org.gcube.application.cms.plugins.faults.EventException; -import org.gcube.application.cms.plugins.faults.InitializationException; -import org.gcube.application.cms.plugins.faults.ShutDownException; -import org.gcube.application.cms.plugins.faults.StepException; -import org.gcube.application.cms.plugins.model.PluginDescriptor; -import org.gcube.application.cms.plugins.reports.EventExecutionReport; -import org.gcube.application.cms.plugins.reports.StepExecutionReport; -import org.gcube.application.cms.plugins.reports.InitializationReport; -import org.gcube.application.cms.plugins.requests.BaseRequest; -import org.gcube.application.cms.plugins.requests.EventExecutionRequest; -import org.gcube.application.cms.plugins.requests.StepExecutionRequest; -import org.gcube.application.cms.plugins.model.ComparableVersion; -import org.gcube.application.geoportal.common.model.configuration.Configuration; -import org.gcube.application.geoportal.common.model.rest.ConfigurationException; -import org.gcube.application.geoportal.common.model.useCaseDescriptor.UseCaseDescriptor; - -@Slf4j -public class SimpleLifeCycleManager implements LifecycleManager { - - - private static final PluginDescriptor DESCRIPTOR=new PluginDescriptor("SIMPLE-LIFECYCLE", "Simple Lifecycle manager"); - static { - DESCRIPTOR.setDescription("Simple Lifecycle Management. This plugin supports a simple publication lifecycle."); - DESCRIPTOR.setVersion(new ComparableVersion("1.0.0")); - } - - @Override - public InitializationReport initInContext() throws InitializationException { - return null; - } - - @Override - public InitializationReport init() throws InitializationException { - return null; - } - - @Override - public void shutdown() throws ShutDownException { - - } - - @Override - public StepExecutionReport performStep(StepExecutionRequest request) throws StepException { - log.info("Received Request "); - - switch(request.getStep()){ - default : throw new StepException("Invalid Step "+request.getStep()); - } - - - //throw new StepException("Still to implement"); - } - - @Override - public Configuration getCurrentConfiguration(BaseRequest ucd) throws ConfigurationException { - return null; - } - - @Override - public EventExecutionReport onEvent(EventExecutionRequest request) throws EventException { - return null; - } - - @Override - public void setPluginManager(PluginManagerInterface manager) { - - } - - - @Override - public PluginDescriptor getDescriptor() { - return null; - } -} diff --git a/geoportal-service/src/main/java/org/gcube/application/geoportal/service/engine/mongo/ProfiledMongoManager.java b/geoportal-service/src/main/java/org/gcube/application/geoportal/service/engine/mongo/ProfiledMongoManager.java index be175ab..1cce640 100644 --- a/geoportal-service/src/main/java/org/gcube/application/geoportal/service/engine/mongo/ProfiledMongoManager.java +++ b/geoportal-service/src/main/java/org/gcube/application/geoportal/service/engine/mongo/ProfiledMongoManager.java @@ -6,6 +6,7 @@ import com.mongodb.client.model.FindOneAndReplaceOptions; import com.mongodb.client.model.FindOneAndUpdateOptions; import com.mongodb.client.model.ReturnDocument; import com.vdurmont.semver4j.Semver; +import jdk.nashorn.internal.runtime.regexp.joni.Config; import lombok.Getter; import lombok.extern.slf4j.Slf4j; import org.apache.commons.io.IOUtils; @@ -13,6 +14,7 @@ import org.bson.Document; import org.bson.types.ObjectId; import org.gcube.application.cms.plugins.LifecycleManager; import org.gcube.application.cms.plugins.faults.EventException; +import org.gcube.application.cms.plugins.faults.InsufficientPrivileges; import org.gcube.application.cms.plugins.faults.StepException; import org.gcube.application.cms.plugins.model.PluginDescriptor; import org.gcube.application.cms.plugins.reports.DocumentHandlingReport; @@ -405,21 +407,23 @@ public class ProfiledMongoManager extends MongoManager implements MongoManagerI< @Override public Project performStep(String id, String step, Document options) throws StepException, JsonProcessingException, ProjectLockedException, ProjectNotFoundException, InvalidLockException, InvalidUserRoleException, UnauthorizedAccess { Project document = lock(id,"Step "+step+" execution"); - try{ + try { User u = UserUtils.getCurrent().asInfo().getUser(); - final DataAccessPolicy policy = useCaseDescriptor.getMatching(u); - log.info("Registering Fileset for {} [{}] , policy for {} is {} ",id,useCaseDescriptor.getId(),u,policy); + final DataAccessPolicy policy = useCaseDescriptor.getMatching(u); + log.info("Registering Fileset for {} [{}] , policy for {} is {} ", id, useCaseDescriptor.getId(), u, policy); // NB cannot check ownership on returned values, must specify filter - if(policy == null) { + if (policy == null) { log.warn("No policy found for {}. Returning empty ", u); throw new InvalidUserRoleException("No policy defined for current user roles " + u.getRoles()); } - if(!policy.canWrite(document,u)) throw new UnauthorizedAccess("No edit rights on project "+id); + if (!policy.canWrite(document, u)) throw new UnauthorizedAccess("No edit rights on project " + id); document.getLifecycleInformation().cleanState(); document = step(document, step, options); + }catch (InsufficientPrivileges | ConfigurationException e){ + throw e; } catch(Throwable t){ log.error("[UseCaseDescriptor {} ] ERROR Invoking Step {} on document {}" , useCaseDescriptor.getId(),step,id,t); LifecycleInformation info = new LifecycleInformation(); @@ -649,20 +653,20 @@ public class ProfiledMongoManager extends MongoManager implements MongoManagerI< } - private Project step(Project theDocument, String step, Document callParameters){ - try{ - log.info("[UseCaseDescriptor {}] Invoking Step {} on {}" , useCaseDescriptor.getId(),step,getManager().getDescriptor()); - AccountingInfo user= UserUtils.getCurrent().asInfo(); + private Project step(Project theDocument, String step, Document callParameters) throws InsufficientPrivileges, ConfigurationException { + try { + log.info("[UseCaseDescriptor {}] Invoking Step {} on {}", useCaseDescriptor.getId(), step, getManager().getDescriptor()); + AccountingInfo user = UserUtils.getCurrent().asInfo(); - StepExecutionRequest request=new StepExecutionRequest(useCaseDescriptor,user.getUser(),user.getContext(),theDocument,step); + StepExecutionRequest request = new StepExecutionRequest(useCaseDescriptor, user.getUser(), user.getContext(), theDocument, step); - log.debug("Requesting Step Execution {}",request); - StepExecutionReport report= getManager().performStep(request); - Project toReturn = report.prepareResult(); + log.debug("Requesting Step Execution {}", request); + StepExecutionReport report = getManager().performStep(request); + Project toReturn = report.prepareResult(); - // EVENTS - if(report.getToTriggerEvents()!=null) { + // EVENTS + if (report.getToTriggerEvents() != null) { Iterator eventIT = report.getToTriggerEvents().listIterator(); while (!toReturn.getLifecycleInformation().getLastOperationStatus().equals(LifecycleInformation.Status.ERROR) && eventIT.hasNext()) { @@ -672,8 +676,8 @@ public class ProfiledMongoManager extends MongoManager implements MongoManagerI< } } - // STEPS - if(report.getCascadeSteps()!=null) { + // STEPS + if (report.getCascadeSteps() != null) { Iterator stepIT = report.getCascadeSteps().listIterator(); while (!toReturn.getLifecycleInformation().getLastOperationStatus().equals(LifecycleInformation.Status.ERROR) && stepIT.hasNext()) { @@ -682,7 +686,9 @@ public class ProfiledMongoManager extends MongoManager implements MongoManagerI< toReturn = step(toReturn, triggerRequest.getStep(), triggerRequest.getCallParameters()); } } - return report.prepareResult(); + return report.prepareResult(); + }catch (InsufficientPrivileges | ConfigurationException e){ + throw e; }catch(Throwable t){ log.error("Unable to perform step "+step,t); theDocument.getLifecycleInformation().addErrorMessage("Unable to perform step "+step+" cause : "+ t.getMessage()); diff --git a/geoportal-service/src/test/java/org/gcube/application/geoportal/service/profiledDocuments/RolesTests.java b/geoportal-service/src/test/java/org/gcube/application/geoportal/service/profiledDocuments/RolesTests.java new file mode 100644 index 0000000..81aa564 --- /dev/null +++ b/geoportal-service/src/test/java/org/gcube/application/geoportal/service/profiledDocuments/RolesTests.java @@ -0,0 +1,42 @@ +package org.gcube.application.geoportal.service.profiledDocuments; + +import org.gcube.application.geoportal.common.model.useCaseDescriptor.DataAccessPolicy; +import org.gcube.application.geoportal.common.rest.InterfaceConstants; +import org.junit.Test; + +import javax.ws.rs.client.WebTarget; + +public class RolesTests extends AbstractProfiledDocumentsTests{ + + + @Override + protected WebTarget baseTarget() { + String testProfileId="rolesTesting"; + return target(InterfaceConstants.Methods.PROJECTS).path(testProfileId); + } + + + @Test + private void testRoles(){ + // Insert for each + } + + + private void test(String role, DataAccessPolicy.Policy expected){ + // Collect behaviour + // try access not owned + // check against expected.getRead() + // try access owned + // check against expected.getRead() + // try access any + // check against expected.getRead() + + + // Try create + // check against expected.getWrite() + // Try edit owned + // check against expected.getWrite() + // Try edit not owned + // check against expected.getWrite() + } +} diff --git a/geoportal-service/src/test/resources/profiles/rolesTesting.json b/geoportal-service/src/test/resources/profiles/rolesTesting.json new file mode 100644 index 0000000..1242e9a --- /dev/null +++ b/geoportal-service/src/test/resources/profiles/rolesTesting.json @@ -0,0 +1,20 @@ +{ + "_id" : "rolesTesting", + "_version" : "1.0.0", + "_name" : "Roles Testing", + "_description" : "Simple uC for roles management testing", + "_schema" : {}, + "_creationInfo": { + "_user" : { + "_username": "fabio.sinibaldi" + } + }, + "_data_access_policy" : [ + {"_policy" : {"_read" : "own", "_write" : "own"}, "_roles":[]}, + {"_policy" : {"_read" : "any", "_write" : "none"}, "_roles":["Guest"], + "_enforcer": {"_filter" : {"_lifecycleInformation._phase" : {"$eq" : "Published"}}}}, + {"_policy" : {"_read" : "any", "_write" : "none"}, "_roles":["Admin"]}, + {"_policy" : {"_read" : "any", "_write" : "any"}, "_roles":["Data-Manager"]} + ], + "_handlers" : [] +} \ No newline at end of file diff --git a/sdi-plugins/src/main/java/org/gcube/application/cms/sdi/plugins/SDIAbstractPlugin.java b/sdi-plugins/src/main/java/org/gcube/application/cms/sdi/plugins/SDIAbstractPlugin.java index c38d803..49739e5 100644 --- a/sdi-plugins/src/main/java/org/gcube/application/cms/sdi/plugins/SDIAbstractPlugin.java +++ b/sdi-plugins/src/main/java/org/gcube/application/cms/sdi/plugins/SDIAbstractPlugin.java @@ -5,7 +5,7 @@ import lombok.extern.slf4j.Slf4j; import org.gcube.application.cms.caches.AbstractScopedMap; import org.gcube.application.cms.implementations.ISInterface; import org.gcube.application.cms.implementations.ImplementationProvider; -import org.gcube.application.cms.plugins.AbstractPlugin; +import org.gcube.application.cms.plugins.implementations.AbstractPlugin; import org.gcube.application.cms.plugins.InitializablePlugin; import org.gcube.application.cms.plugins.faults.InitializationException; import org.gcube.application.cms.plugins.faults.ShutDownException; @@ -15,7 +15,6 @@ import org.gcube.application.cms.sdi.engine.PostgisIndexer; import org.gcube.application.cms.sdi.engine.SDIManagerWrapper; import org.gcube.application.geoportal.common.model.rest.ConfigurationException; import org.gcube.application.geoportal.common.model.rest.DatabaseConnection; -import org.gcube.application.geoportal.common.utils.ContextUtils; import java.time.Duration; import java.time.temporal.ChronoUnit; diff --git a/sdi-plugins/src/main/java/org/gcube/application/cms/sdi/plugins/SDIMaterializerPlugin.java b/sdi-plugins/src/main/java/org/gcube/application/cms/sdi/plugins/SDIMaterializerPlugin.java index 3f4c3e7..0dd01b4 100644 --- a/sdi-plugins/src/main/java/org/gcube/application/cms/sdi/plugins/SDIMaterializerPlugin.java +++ b/sdi-plugins/src/main/java/org/gcube/application/cms/sdi/plugins/SDIMaterializerPlugin.java @@ -4,7 +4,7 @@ import lombok.Data; import lombok.Synchronized; import lombok.extern.slf4j.Slf4j; import org.bson.Document; -import org.gcube.application.cms.plugins.AbstractPlugin; +import org.gcube.application.cms.plugins.implementations.AbstractPlugin; import org.gcube.application.cms.plugins.faults.InvalidPluginRequestException; import org.gcube.application.cms.sdi.engine.SDIManagerWrapper; import org.gcube.application.cms.sdi.faults.SDIInteractionException;