package org.gcube.gcat.rest; import java.util.Iterator; import javax.ws.rs.BadRequestException; import javax.ws.rs.Consumes; import javax.ws.rs.DELETE; import javax.ws.rs.DefaultValue; import javax.ws.rs.ForbiddenException; import javax.ws.rs.GET; import javax.ws.rs.InternalServerErrorException; //import javax.ws.rs.NotAuthorizedException; import javax.ws.rs.POST; import javax.ws.rs.PUT; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.ResponseBuilder; import javax.ws.rs.core.Response.Status; import javax.xml.ws.WebServiceException; import org.gcube.com.fasterxml.jackson.databind.ObjectMapper; import org.gcube.com.fasterxml.jackson.databind.node.ObjectNode; //import org.gcube.common.authorization.control.annotations.AuthorizationControl; import org.gcube.common.authorization.utils.manager.SecretManagerProvider; import org.gcube.gcat.annotation.PATCH; import org.gcube.gcat.annotation.PURGE; import org.gcube.gcat.api.GCatConstants; import org.gcube.gcat.api.roles.Role; import org.gcube.gcat.configuration.CatalogueConfigurationFactory; import org.gcube.gcat.configuration.ServiceCatalogueConfiguration; import org.gcube.gcat.persistence.ckan.CKANUser; import org.gcube.gcat.persistence.ckan.CKANUserCache; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.webcohesion.enunciate.metadata.rs.ResponseCode; import com.webcohesion.enunciate.metadata.rs.StatusCodes; /** * @author Luca Frosini (ISTI - CNR) */ @Path(Configuration.CONFIGURATIONS) public class Configuration extends BaseREST implements org.gcube.gcat.api.interfaces.Configuration { private static Logger logger = LoggerFactory.getLogger(Configuration.class); public static final String CONTEXT_FULLNAME_PARAMETER = "CONTEXT_FULLNAME_PARAMETER"; protected String checkContext(String context) throws WebServiceException { if(context==null || context.compareTo("")==0) { throw new BadRequestException("Please provide a valid context as path parameter"); } String c = SecretManagerProvider.instance.get().getContext(); if(context.compareTo(Configuration.CURRENT_CONTEXT_PATH_PARAMETER)==0) { return c; } if(context.compareTo(c)!=0) { throw new BadRequestException("Context provided as path parameter (i.e. " + context + ") does not match with token request context (i.e. " + c + ")"); } return c; } protected String checkContext(String context, ServiceCatalogueConfiguration catalogueConfiguration) { String c = checkContext(context); if(c.compareTo(catalogueConfiguration.getContext())!=0) { throw new BadRequestException("Context provided in the configuration (i.e. " + catalogueConfiguration.getContext() + ") does not match with token request context (i.e. " + c + ")"); } return c; } protected void checkRole(Role required) { CKANUser ckanUser = CKANUserCache.getCurrrentCKANUser(); if(ckanUser.getRole().ordinal() < required.ordinal()) { throw new ForbiddenException("To perform such a request you must have " + required.getPortalRole() + " role"); } } private String createOrUpdate(ServiceCatalogueConfiguration catalogueConfiguration) throws WebServiceException { try { ServiceCatalogueConfiguration gotCatalogueConfiguration = CatalogueConfigurationFactory.createOrUpdate(catalogueConfiguration); String configuration = gotCatalogueConfiguration.toJsonString(); logger.debug("The new configuration in context {} is {}", catalogueConfiguration.getContext(), configuration); return configuration; }catch (WebApplicationException e) { throw e; }catch (Exception e) { throw new InternalServerErrorException(e); } } @POST @Consumes(GCatConstants.APPLICATION_JSON_CHARSET_UTF_8) @Produces(GCatConstants.APPLICATION_JSON_CHARSET_UTF_8) @Override // @AuthorizationControl(allowedRoles={Role.CATALOGUE_MANAGER}, exception=NotAuthorizedException.class) @StatusCodes ({ @ResponseCode(code = 201, condition = "Catalogue configuration successfully created."), @ResponseCode(code = 401, condition = "Only Catalogue-Managers can create catalogue configuration."), @ResponseCode(code = 500, condition = "Error while persisting catalogue configuration."), }) /** * Creates the catalogue configuration for the current context using the json provided as parameter * @param json the configuration representation * * { * "id": "584b8503-a490-4a89-8372-e21830fa716c", * "context": "/gcube/devsec/devVRE", * "defaultOrganization": "devvre", * "supportedOrganizations": [ "devvre" ], * "ckanURL": "https://ckan-d-d4s.d4science.org", * "solrURL": "https://ckan-d-d4s.d4science.org/solr/", * "socialPostEnabled": false, * "notificationToUsersEnabled": true, * "moderationEnabled": true * } * * @return the created configuration * * { * "id": "584b8503-a490-4a89-8372-e21830fa716c", * "context": "/gcube/devsec/devVRE", * "defaultOrganization": "devvre", * "supportedOrganizations": [ "devvre" ], * "ckanURL": "https://ckan-d-d4s.d4science.org", * "solrURL": "https://ckan-d-d4s.d4science.org/solr/", * "socialPostEnabled": false, * "notificationToUsersEnabled": true, * "moderationEnabled": true * } * * @throws WebServiceException */ public Response create(String json) throws WebServiceException { try { ServiceCatalogueConfiguration catalogueConfiguration = ServiceCatalogueConfiguration.getServiceCatalogueConfiguration(json); checkContext(CURRENT_CONTEXT_PATH_PARAMETER, catalogueConfiguration); String ret = createOrUpdate(catalogueConfiguration); ResponseBuilder responseBuilder = Response.status(Status.CREATED); if(ret!=null) { responseBuilder.entity(ret).type(GCatConstants.APPLICATION_JSON_CHARSET_UTF_8); } return responseBuilder.build(); }catch (WebApplicationException e) { throw e; }catch (Exception e) { throw new InternalServerErrorException(e); } } @GET @Path("/{" + CONTEXT_FULLNAME_PARAMETER + "}") @Produces(GCatConstants.APPLICATION_JSON_CHARSET_UTF_8) // @AuthorizationControl(allowedRoles={Role.CATALOGUE_EDITOR, Role.CATALOGUE_ADMIN, Role.CATALOGUE_MANAGER}, exception=NotAuthorizedException.class) @StatusCodes ({ @ResponseCode(code = 200, condition = "Catalogue configuration successfully read."), @ResponseCode(code = 401, condition = "Only User with role Catalogue-Editors or above can read a catalogue configuration."), @ResponseCode(code = 500, condition = "Error while reading catalogue configuration."), }) /** * @param context * @return * @throws WebServiceException */ public Response read(@PathParam(CONTEXT_FULLNAME_PARAMETER) String context) throws WebServiceException { try { checkContext(context); return read(); }catch (WebApplicationException e) { throw e; }catch (Exception e) { throw new InternalServerErrorException(e); } } @Override public Response read() throws WebServiceException { try { ServiceCatalogueConfiguration catalogueConfiguration = CatalogueConfigurationFactory.getInstance(); String configuration = catalogueConfiguration.toJsonString(); logger.debug("Configuration in context {} is {}", catalogueConfiguration.getContext(), configuration); ResponseBuilder responseBuilder = Response.status(Status.OK); if(configuration!=null) { responseBuilder.entity(configuration).type(GCatConstants.APPLICATION_JSON_CHARSET_UTF_8); } return responseBuilder.build(); }catch (WebApplicationException e) { throw e; }catch (Exception e) { throw new InternalServerErrorException(e); } } @PUT @Path("/{" + CONTEXT_FULLNAME_PARAMETER + "}") @Consumes(GCatConstants.APPLICATION_JSON_CHARSET_UTF_8) @Produces(GCatConstants.APPLICATION_JSON_CHARSET_UTF_8) // @AuthorizationControl(allowedRoles={Role.CATALOGUE_MANAGER}, exception=NotAuthorizedException.class) @StatusCodes ({ @ResponseCode(code = 200, condition = "Catalogue configuration successfully created/updated."), @ResponseCode(code = 401, condition = "Only Catalogue-Managers can create/update catalogue configuration."), @ResponseCode(code = 500, condition = "Error while creating/updating catalogue configuration."), }) public String createOrUpdate(@PathParam(CONTEXT_FULLNAME_PARAMETER) String context, String json) throws WebServiceException { try { ServiceCatalogueConfiguration catalogueConfiguration = ServiceCatalogueConfiguration.getServiceCatalogueConfiguration(json); checkContext(context, catalogueConfiguration); return createOrUpdate(catalogueConfiguration); }catch (WebApplicationException e) { throw e; }catch (Exception e) { throw new InternalServerErrorException(e); } } @Override public Response update(String json) throws WebServiceException { try { ServiceCatalogueConfiguration catalogueConfiguration = ServiceCatalogueConfiguration.getServiceCatalogueConfiguration(json); checkContext(CURRENT_CONTEXT_PATH_PARAMETER); catalogueConfiguration = CatalogueConfigurationFactory.createOrUpdate(catalogueConfiguration); String configuration = catalogueConfiguration.toJsonString(); logger.debug("Configuration in context {} has been updated to {}", catalogueConfiguration.getContext(), configuration); ResponseBuilder responseBuilder = Response.status(Status.OK); if(configuration!=null) { responseBuilder.entity(configuration).type(GCatConstants.APPLICATION_JSON_CHARSET_UTF_8); } return responseBuilder.build(); }catch (WebApplicationException e) { throw e; }catch (Exception e) { throw new InternalServerErrorException(e); } } @PATCH @Path("/{" + CONTEXT_FULLNAME_PARAMETER + "}") @Consumes(GCatConstants.APPLICATION_JSON_CHARSET_UTF_8) @Produces(GCatConstants.APPLICATION_JSON_CHARSET_UTF_8) // @AuthorizationControl(allowedRoles={Role.CATALOGUE_MANAGER}, exception=NotAuthorizedException.class) @StatusCodes ({ @ResponseCode(code = 200, condition = "Catalogue configuration successfully updated."), @ResponseCode(code = 401, condition = "Only Catalogue-Managers can update catalogue configuration."), @ResponseCode(code = 500, condition = "Error while updating catalogue configuration."), }) public Response patch(@PathParam(CONTEXT_FULLNAME_PARAMETER) String context, String json) throws WebServiceException { try { checkContext(context); return patch(json); }catch (WebApplicationException e) { throw e; }catch (Exception e) { throw new InternalServerErrorException(e); } } @Override public Response patch(String json) throws WebServiceException { try { ServiceCatalogueConfiguration catalogueConfiguration = CatalogueConfigurationFactory.getInstance(); ObjectMapper mapper = new ObjectMapper(); ObjectNode node = (ObjectNode) mapper.readTree(json); if(node.has(ServiceCatalogueConfiguration.CONTEXT_KEY)) { String context = node.get(ServiceCatalogueConfiguration.CONTEXT_KEY).asText(); String c = SecretManagerProvider.instance.get().getContext(); if(c.compareTo(context)!=0) { throw new BadRequestException("Context provided in the configuration (i.e. " + catalogueConfiguration.getContext() + ") does not match with token request context (i.e. " + c + ")"); } node.remove(CURRENT_CONTEXT_PATH_PARAMETER); } ObjectNode configuration = mapper.valueToTree(catalogueConfiguration); Iterator fieldNames = node.fieldNames(); while(fieldNames.hasNext()) { String fieldName = fieldNames.next(); configuration.set(fieldName, node.get(fieldName)); } ServiceCatalogueConfiguration newCatalogueConfiguration = ServiceCatalogueConfiguration.getServiceCatalogueConfiguration(configuration); newCatalogueConfiguration = CatalogueConfigurationFactory.createOrUpdate(newCatalogueConfiguration); String ret = newCatalogueConfiguration.toJsonString(); logger.debug("Configuration in context {} has been patched to {}", catalogueConfiguration.getContext(), ret); ResponseBuilder responseBuilder = Response.status(Status.OK); if(ret!=null) { responseBuilder.entity(ret).type(GCatConstants.APPLICATION_JSON_CHARSET_UTF_8); } return responseBuilder.build(); }catch (WebApplicationException e) { throw e; }catch (Exception e) { throw new InternalServerErrorException(e); } } @DELETE @Path("/{" + CONTEXT_FULLNAME_PARAMETER + "}") // @AuthorizationControl(allowedRoles={Role.CATALOGUE_MANAGER}, exception=NotAuthorizedException.class) @StatusCodes ({ @ResponseCode(code = 200, condition = "Catalogue configuration successfully deleted."), @ResponseCode(code = 401, condition = "Only Catalogue-Managers can delete catalogue configuration."), @ResponseCode(code = 500, condition = "Error while deleting catalogue configuration."), }) public Response delete(@PathParam(CONTEXT_FULLNAME_PARAMETER) String context, @QueryParam(GCatConstants.PURGE_QUERY_PARAMETER) @DefaultValue("false") Boolean purge) throws WebServiceException { try { checkContext(context); if(purge) { return purge(); }else { return delete(); } }catch (WebApplicationException e) { throw e; }catch (Exception e) { throw new InternalServerErrorException(e); } } // Remove the configuration from cache and force reload @Override public Response delete() throws WebServiceException { try { CatalogueConfigurationFactory.renew(); return Response.status(Status.NO_CONTENT).build(); }catch (WebApplicationException e) { throw e; }catch (Exception e) { throw new InternalServerErrorException(e); } } @PURGE @Path("/{" + CONTEXT_FULLNAME_PARAMETER + "}") // @AuthorizationControl(allowedRoles={Role.CATALOGUE_MANAGER}, exception=NotAuthorizedException.class) @StatusCodes ({ @ResponseCode(code = 200, condition = "Catalogue configuration successfully deleted."), @ResponseCode(code = 401, condition = "Only Catalogue-Managers can delete catalogue configuration."), @ResponseCode(code = 500, condition = "Error while deleting catalogue configuration."), }) public Response purge(@PathParam(CONTEXT_FULLNAME_PARAMETER) String context) throws WebServiceException { try { checkContext(context); return purge(); }catch (WebApplicationException e) { throw e; }catch (Exception e) { throw new InternalServerErrorException(e); } } // Remove the configuration from cache and from IS @Override public Response purge() throws WebServiceException { try { CatalogueConfigurationFactory.purge(); return Response.status(Status.NO_CONTENT).build(); }catch (WebApplicationException e) { throw e; }catch (Exception e) { throw new InternalServerErrorException(e); } } }