diff --git a/.classpath b/.classpath index 27073a0..045159b 100644 --- a/.classpath +++ b/.classpath @@ -29,6 +29,5 @@ - diff --git a/CHANGELOG.md b/CHANGELOG.md index c4df5c5..aea3bc4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,11 +4,22 @@ All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [v2-5-0-SNAPSHOT] - 2021-04-08 + +**New features** + +[#20993] Supported new resource "Wekeo Interface" - gettoken. + +**Bug fixes** + +[#21093] StorageHubResolver HEAD request does not support Content-Length + + ## [v2-4-1] - 2021-01-13 **Bug Fixes** -[Task #19942] Fixing master build fails +[#19942] Fixing master build fails ## [v2-4-0] [r4-24-0]- 2020-06-18 diff --git a/pom.xml b/pom.xml index 62eeb9c..d3b3f49 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ org.gcube.data.transfer uri-resolver - 2.4.1 + 2.5.0-SNAPSHOT war The URI Resolver is an HTTP URI resolver implemented as an REST service which gives access trough HTTP to different gcube Resolvers and gCube Applications. @@ -92,14 +92,17 @@ - - org.gcube.common - gxHTTP - compile - + + + + + - + + com.google.guava + guava + 18.0 + org.gcube.core @@ -137,7 +140,6 @@ compile - javax.enterprise @@ -170,6 +172,11 @@ compile + + org.projectlombok + lombok + 1.14.8 + @@ -180,13 +187,25 @@ compile + + + + + + + + + + + + + commons-lang commons-lang 2.6 - org.w3c diff --git a/src/main/java/org/gcube/datatransfer/resolver/gis/MetadataConverter.java b/src/main/java/org/gcube/datatransfer/resolver/gis/MetadataConverter.java index a61188d..b3cb4d6 100644 --- a/src/main/java/org/gcube/datatransfer/resolver/gis/MetadataConverter.java +++ b/src/main/java/org/gcube/datatransfer/resolver/gis/MetadataConverter.java @@ -189,23 +189,4 @@ public class MetadataConverter { return gisLI; } - - /* - public static void main(String[] args) throws Exception { - -// String geoserver = "http://www.fao.org/figis/a/wms/?service=WMS&version=1.1.0&request=GetMap&layers=area:FAO_AREAS&styles=Species_prob, puppa&bbox=-180.0,-88.0,180.0,90.0000000694&width=667&height=330&srs=EPSG:4326&format=image%2Fpng"; -// System.out.println(MetadataConverter.getGeoserverBaseUri(geoserver)); - - String user ="admin"; - String pwd = "admin"; - boolean authenticate = true; -// String uuid ="177e1c3c-4a22-4ad9-b015-bfc443d16cb8"; - String uuid ="fao-species-map-bep"; -// String uuid ="fao-species-map-bon"; //FAO - String geoNetworkUrl ="http://geoserver-dev2.d4science-ii.research-infrastructures.eu/geonetwork"; - GeonetworkInstance geonetowrkInstance = new GeonetworkInstance(geoNetworkUrl, user, pwd, authenticate); - String onLineResource = getWMSOnLineResource(geonetowrkInstance, uuid); - System.out.println(onLineResource); - - }*/ } diff --git a/src/main/java/org/gcube/datatransfer/resolver/services/StorageHubResolver.java b/src/main/java/org/gcube/datatransfer/resolver/services/StorageHubResolver.java index 481168f..da5711c 100644 --- a/src/main/java/org/gcube/datatransfer/resolver/services/StorageHubResolver.java +++ b/src/main/java/org/gcube/datatransfer/resolver/services/StorageHubResolver.java @@ -71,7 +71,7 @@ public class StorageHubResolver { ItemManagerClient client = AbstractPlugin.item().build(); StreamDescriptor descriptor = client.resolvePublicLink(id); - ResponseBuilder response = Response.noContent(); + ResponseBuilder response = Response.ok(); response = new StorageHubMetadataResponseBuilder(req, response).fillMetadata(descriptor, id, CONTENT_DISPOSITION_VALUE.attachment); return response.build(); diff --git a/src/main/java/org/gcube/datatransfer/resolver/services/WekeoResolver.java b/src/main/java/org/gcube/datatransfer/resolver/services/WekeoResolver.java new file mode 100644 index 0000000..d6de8e2 --- /dev/null +++ b/src/main/java/org/gcube/datatransfer/resolver/services/WekeoResolver.java @@ -0,0 +1,277 @@ +package org.gcube.datatransfer.resolver.services; + +import static org.gcube.resources.discovery.icclient.ICFactory.clientFor; +import static org.gcube.resources.discovery.icclient.ICFactory.queryFor; + +import java.io.File; +import java.io.FileInputStream; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.Collection; +import java.util.List; + +import javax.servlet.http.HttpServletRequest; +import javax.ws.rs.Consumes; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; + +import org.apache.commons.codec.binary.Base64; +import org.gcube.common.authorization.library.provider.SecurityTokenProvider; +import org.gcube.common.encryption.StringEncrypter; +import org.gcube.common.resources.gcore.ServiceEndpoint; +import org.gcube.common.resources.gcore.ServiceEndpoint.AccessPoint; +import org.gcube.common.scope.api.ScopeProvider; +import org.gcube.datatransfer.resolver.requesthandler.RequestHandler; +import org.gcube.datatransfer.resolver.services.error.ExceptionManager; +import org.gcube.datatransfer.resolver.services.exceptions.NotFoundException; +import org.gcube.datatransfer.resolver.util.Util; +import org.gcube.resources.discovery.client.api.DiscoveryClient; +import org.gcube.resources.discovery.client.queries.api.SimpleQuery; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + + +/** + * The Class WekeoResolver. + * + * @author Francesco Mangiacrapa at ISTI-CNR francesco.mangiacrapa@isti.cnr.it + * + * Mar 30, 2021 + */ +@Path("wekeo") +public class WekeoResolver { + + private static Logger logger = LoggerFactory.getLogger(WekeoResolver.class); + + private final static String RUNTIME_WKEO_RESOURCE_NAME = "WekeoDataBroker"; + private final static String CATEGORY_WEKEO_TYPE = "OnlineService"; + + private static String helpURI = "https://wiki.gcube-system.org/gcube/URI_Resolver#Wekeo_Resolver"; + + + + /** + * Gets the token. + * + * @param req the req + * @return the token + * @throws WebApplicationException the web application exception + */ + @GET + @Path("/gettoken") + @Produces(MediaType.APPLICATION_JSON) + public Response getToken(@Context HttpServletRequest req) throws WebApplicationException{ + + logger.info(this.getClass().getSimpleName()+" getToken starts..."); + + try { + + String contextToken = SecurityTokenProvider.instance.get(); + String scope = ScopeProvider.instance.get(); + logger.info("ScopeProvider has scope: "+scope); + + String appToken = req.getServletContext().getInitParameter(RequestHandler.ROOT_APP_TOKEN); + + if(contextToken.compareTo(appToken)==0){ + logger.error("Token not passed, SecurityTokenProvider contains the root app token: "+appToken.substring(0,10)+"..."); + throw ExceptionManager.unauthorizedException(req, "You are not authorized. You must pass a token of VRE", this.getClass(), helpURI); + } + + StringBuilder wekeoResponse = new StringBuilder(); + AccessPoint wekeoAccessPoint = WekeoResolver.readWekeoServiceEndpoint(req, scope); + + if (wekeoAccessPoint != null) { + + String wekeoUsername = wekeoAccessPoint.username(); + String wekeoAddress = wekeoAccessPoint.address(); + String wekeoPwd = wekeoAccessPoint.password(); + + // printing the access point found + if (logger.isDebugEnabled()) { + String msg = String.format("Found the username '%s' and the address '%s' to perform the request", + wekeoUsername, wekeoAddress); + logger.debug(msg); + } + + logger.info("The pwd is: "+wekeoPwd); + // decrypting the pwd + if (wekeoPwd != null) { + wekeoPwd = StringEncrypter.getEncrypter().decrypt(wekeoPwd); + logger.info("Decrypted pwd registered into Access Point '" + wekeoAccessPoint.name() + "' is: " + + wekeoPwd.substring(0,wekeoPwd.length()/2)+"..."); + } + + if (wekeoUsername != null && wekeoPwd != null & wekeoAddress != null) { + HttpURLConnection connection = null; + InputStream content = null; + InputStreamReader in = null; + try { + + String authString = wekeoUsername + ":" + wekeoPwd; + byte[] authEncBytes = Base64.encodeBase64(authString.getBytes()); + String authStringEnc = new String(authEncBytes); + logger.debug("Base64 encoded auth string: " + authStringEnc); + + logger.info("Performing the request to: "+wekeoAddress); + URL url = new URL(wekeoAddress); + connection = (HttpURLConnection) url.openConnection(); + connection.setRequestMethod("GET"); + connection.setDoOutput(true); + connection.setRequestProperty("Authorization", "Basic " + authStringEnc); + + content = (InputStream) connection.getInputStream(); + in = new InputStreamReader(content); + + logger.info("the response code is: "+connection.getResponseCode()); + + int numCharsRead; + char[] charArray = new char[1024]; + StringBuffer sb = new StringBuffer(); + logger.debug("reading the response..."); + while ((numCharsRead = in.read(charArray)) > 0) { + sb.append(charArray, 0, numCharsRead); + } + wekeoResponse.append(sb.toString()); + //System.out.println(wekeoResponse); + + } catch (Exception e) { + logger.error(e.getMessage(), e); + String error = String.format("Error on performing request to %s", wekeoAddress); + throw new Exception(error); + } finally { + try { + if (content!= null && in != null) { + in.close(); + content.close(); + } + }catch (Exception e) { + //silent + } + } + + } else { + String error = String.format( + "I cannot read the configurations (adress, username,password) from %s in the scope %s", + RUNTIME_WKEO_RESOURCE_NAME, scope); + throw new Exception(error); + } + } + + // to be sure + if (wekeoResponse.length() == 0) { + String error = String + .format("Sorry an error occured on getting the access token from Wekeo. Please, retry the request"); + throw new Exception(error); + } + + String theResponse = wekeoResponse.toString(); + logger.info("returning: \n"+theResponse); + + return Response.ok(theResponse).build(); + }catch (Exception e) { + //ALREADY MANAGED AS WebApplicationException + logger.error("Exception:", e); + throw (WebApplicationException) e; + } + } + + @GET + @Produces({MediaType.TEXT_HTML}) + @Path("") + public InputStream index(@Context HttpServletRequest req) throws WebApplicationException{ + + String indexFile = "/WEB-INF/jsp/wekeo.jsp"; + + try{ + logger.info(this.getClass().getSimpleName() +" index called"); + String realPath = req.getServletContext().getRealPath(indexFile); + return new FileInputStream(new File(realPath)); + }catch (Exception e) { + + if(!(e instanceof WebApplicationException)){ + //UNEXPECTED EXCEPTION managing it as WebApplicationException + String error = "wekeo.jsp not found. Please, contact the support!"; + throw ExceptionManager.internalErrorException(req, error, this.getClass(), null); + } + //ALREADY MANAGED AS WebApplicationException + logger.error("Exception:", e); + throw (WebApplicationException) e; + } + } + + /** + * Reads the wekeo endpoint information from IS. {The SE name is: @link WekeoResolver#RUNTIME_WKEO_RESOURCE_NAME} + * + * @param req the req + * @param scope the scope + * @return the string + */ + public static AccessPoint readWekeoServiceEndpoint(HttpServletRequest req, String scope){ + + String callerScope = null; + try{ + callerScope = ScopeProvider.instance.get(); + ScopeProvider.instance.set(scope); + logger.info("Searching SE "+RUNTIME_WKEO_RESOURCE_NAME+" configurations in the scope: "+ScopeProvider.instance.get()); + + SimpleQuery query = queryFor(ServiceEndpoint.class); + query.addCondition("$resource/Profile/Name/text() eq '"+ RUNTIME_WKEO_RESOURCE_NAME +"'"); + query.addCondition("$resource/Profile/Category/text() eq '"+ CATEGORY_WEKEO_TYPE +"'"); + + DiscoveryClient client = clientFor(ServiceEndpoint.class); + List toReturn = client.submit(query); + + logger.info("The query returned "+toReturn.size()+ " ServiceEndpoint/s"); + + if(toReturn.size()==0){ + String errorMessage = String.format("Missing the RR with Name '%s' and Category '%s' in the scope '%s'. Please contact the support.",RUNTIME_WKEO_RESOURCE_NAME,CATEGORY_WEKEO_TYPE,ScopeProvider.instance.get()); + logger.error(errorMessage); + throw ExceptionManager.notFoundException(req, errorMessage, WekeoResolver.class, helpURI); + + } + + String wekeoUsername = null; + String wekeoPwd = null; + + ServiceEndpoint se = toReturn.get(0); + Collection theAccessPoints = se.profile().accessPoints().asCollection(); + for (AccessPoint accessPoint : theAccessPoints) { + wekeoUsername = accessPoint.username(); + wekeoPwd = accessPoint.password(); + if(wekeoUsername!=null && wekeoPwd!=null) { + logger.info("returning the access point with name: "+accessPoint.name()); + return accessPoint; + } + } + + return null; + + }catch(Exception e){ + + if(e instanceof NotFoundException) + throw e; + + String errorMessage = "Error occurred on reading the "+RUNTIME_WKEO_RESOURCE_NAME+" SE registered in the scope: "+ScopeProvider.instance.get(); + logger.error(errorMessage, e); + throw ExceptionManager.internalErrorException(req, errorMessage, WekeoResolver.class, helpURI); + + }finally{ + if(callerScope!=null){ + logger.info("Setting to the callerScope scope: "+callerScope); + ScopeProvider.instance.set(callerScope); + }else{ + logger.info("Reset scope"); + ScopeProvider.instance.reset(); + } + } + + } +} diff --git a/src/main/java/org/gcube/datatransfer/resolver/shub/StorageHubMetadataResponseBuilder.java b/src/main/java/org/gcube/datatransfer/resolver/shub/StorageHubMetadataResponseBuilder.java index c05d2a4..da67910 100644 --- a/src/main/java/org/gcube/datatransfer/resolver/shub/StorageHubMetadataResponseBuilder.java +++ b/src/main/java/org/gcube/datatransfer/resolver/shub/StorageHubMetadataResponseBuilder.java @@ -58,8 +58,10 @@ public class StorageHubMetadataResponseBuilder { responseBuilder.header("Content-Location", contentLocation); //Managing "Content-Type" - if (streamDescriptor.getContentType()!= null && !streamDescriptor.getContentType().isEmpty()) + if (streamDescriptor.getContentType()!= null && !streamDescriptor.getContentType().isEmpty()) { + responseBuilder.header("Content-Type", streamDescriptor.getContentType()); + } //Managing "Content-Lenght" if(streamDescriptor.getContentLenght()>0) { diff --git a/src/main/java/org/gcube/datatransfer/resolver/util/HttpRequestUtil.java b/src/main/java/org/gcube/datatransfer/resolver/util/HttpRequestUtil.java index 936431c..a652d6a 100644 --- a/src/main/java/org/gcube/datatransfer/resolver/util/HttpRequestUtil.java +++ b/src/main/java/org/gcube/datatransfer/resolver/util/HttpRequestUtil.java @@ -120,14 +120,4 @@ public class HttpRequestUtil { return false; } - - /** - * The main method. - * - * @param args the arguments - * @throws Exception the exception - */ - public static void main(String[] args) throws Exception { - System.out.println(HttpRequestUtil.urlExists("http://geoserver2.d4science.research-infrastructures.eu/geoserver/wms", true)); - } } diff --git a/src/main/webapp/WEB-INF/gcube-app.xml b/src/main/webapp/WEB-INF/gcube-app.xml index 2a9b9cc..dd44721 100644 --- a/src/main/webapp/WEB-INF/gcube-app.xml +++ b/src/main/webapp/WEB-INF/gcube-app.xml @@ -5,4 +5,5 @@ URIResolver RESTful /analytics/create/* /knime/create/* + /wekeo/gettoken/* \ No newline at end of file diff --git a/src/main/webapp/WEB-INF/jsp/cr7huevos.jsp b/src/main/webapp/WEB-INF/jsp/cr7huevos.jsp deleted file mode 100644 index 3c2d1cc..0000000 --- a/src/main/webapp/WEB-INF/jsp/cr7huevos.jsp +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - - -
- -
- - \ No newline at end of file diff --git a/src/main/webapp/WEB-INF/jsp/index.jsp b/src/main/webapp/WEB-INF/jsp/index.jsp index 6d889f9..c65dc2e 100644 --- a/src/main/webapp/WEB-INF/jsp/index.jsp +++ b/src/main/webapp/WEB-INF/jsp/index.jsp @@ -1,6 +1,8 @@ - + + + + + + +
+ +
The Wekeo Resolver
+
+ Available Operations: +

+ # authentication operator + + (gCube AuthN required) +

+
+ +
+ + \ No newline at end of file diff --git a/src/test/java/GeonetworkQueryTest.java b/src/test/java/GeonetworkQueryTest.java deleted file mode 100644 index 90518b0..0000000 --- a/src/test/java/GeonetworkQueryTest.java +++ /dev/null @@ -1,231 +0,0 @@ -import org.gcube.common.encryption.StringEncrypter; -import org.gcube.common.scope.api.ScopeProvider; -import org.gcube.spatial.data.geonetwork.GeoNetwork; -import org.gcube.spatial.data.geonetwork.GeoNetworkPublisher; -import org.gcube.spatial.data.geonetwork.GeoNetworkReader; -import org.gcube.spatial.data.geonetwork.LoginLevel; -import org.gcube.spatial.data.geonetwork.configuration.Configuration; -import org.gcube.spatial.data.geonetwork.model.Account; -import org.gcube.spatial.data.geonetwork.model.Account.Type; -import org.opengis.metadata.Metadata; - -import it.geosolutions.geonetwork.util.GNSearchRequest; -import it.geosolutions.geonetwork.util.GNSearchResponse; - -/** - * - * @author Francesco Mangiacrapa francesco.mangiacrapa@isti.cnr.it - * Aug 31, 2016 - */ -public class GeonetworkQueryTest { - - private static final int MAX = 10; - - private static final String UUID = "8a878105-ef06-4b1f-843f-120fc525b22b"; - - //private String[] scopesProd = {"/gcube/devsec/devVRE"}; - - //private String[] scopesProd = {"/d4science.research-infrastructures.eu/gCubeApps/SIASPA"}; - - //private String[] scopesProd = {"/d4science.research-infrastructures.eu/gCubeApps/fisheriesandecosystematmii"}; - - //private String[] scopesProd = {"/d4science.research-infrastructures.eu/D4Research"}; - - //private String[] scopesProd = {"/d4science.research-infrastructures.eu"}; - - //private String[] scopesProd = {"/d4science.research-infrastructures.eu/gCubeApps/FAO_TunaAtlas"}; - - //private String[] scopesProd = {"/d4science.research-infrastructures.eu/D4Research/Blue-Datathon"}; - private String[] scopesProd = {"/d4science.research-infrastructures.eu/gCubeApps/RPrototypingLab"}; - - private LoginLevel loginLevel = LoginLevel.CKAN; - - private Type accountType = Type.SCOPE; - - private String textToSearch = "geo_fea"; - - //@Test - public void getCount() throws Exception{ - try{ - for(String scope:scopesProd){ - ScopeProvider.instance.set(scope); - GeoNetworkPublisher reader=GeoNetwork.get(); - - Configuration config = reader.getConfiguration(); - Account account=config.getScopeConfiguration().getAccounts().get(accountType); - - //System.out.println("User: "+account.getUser()+", Pwd: "+account.getPassword()); - System.out.println("Admin: "+config.getAdminAccount().getUser()+", Pwd: "+config.getAdminAccount().getPassword()); - - try{ - String decryptedPassword = StringEncrypter.getEncrypter().decrypt(account.getPassword()); - System.out.println("Decrypted Password: "+decryptedPassword); - }catch(Exception e){ - System.out.println("ignoring exception during pwd decrypting"); - } - - - // req.addParam("keyword", "Thredds"); - final GNSearchRequest req=new GNSearchRequest(); -// req.addParam(GNSearchRequest.Param.any,"Thredds"); - GNSearchResponse resp = reader.query(req); - int publicCount=resp.getCount(); - reader.login(loginLevel); - int totalCount=reader.query(req).getCount(); - System.out.println("SCOPE "+scope+" found "+totalCount+" (public : "+publicCount+", private :"+(totalCount-publicCount)+")"); - if(totalCount==0) - return; - /*try{ - int last = totalCount>MAX?totalCount:MAX; - for(int i=0; iMAX?totalCount:MAX; -// -// for(int i=0; iMAX?totalCount:MAX; +// for(int i=0; iMAX?totalCount:MAX; +//// +//// for(int i=0; i 0) { + sb.append(charArray, 0, numCharsRead); + } + wekeoResponse.append(sb.toString()); + //System.out.println(wekeoResponse); + + } catch (Exception e) { + logger.error(e.getMessage(), e); + String error = String.format("Error on performing request to %s", wekeoAddress); + throw new Exception(error); + } finally { + try { + if (connection != null) { + connection.disconnect(); + } + }catch (Exception e) { + + } + } + + } else { + String error = String.format( + "I cannot read the configurations (adress, username,password) from %s in the scope %s", + RUNTIME_WKEO_RESOURCE_NAME, scope); + throw new Exception(error); + } + } + + // to be sure + if (wekeoResponse.length() == 0) { + String error = String + .format("Sorry an error occured on getting the access token from Wekeo. Please, retry the request"); + throw new Exception(error); + } + + logger.info("returning: \n"+wekeoResponse.toString()); + + } catch (Exception e) { + e.printStackTrace(); + logger.error(e.getMessage(), e); + } + } +} diff --git a/src/test/java/rest/TestResolvers.java b/src/test/java/rest/TestResolvers.java index baf0ea1..2b510cb 100644 --- a/src/test/java/rest/TestResolvers.java +++ b/src/test/java/rest/TestResolvers.java @@ -15,10 +15,19 @@ import java.nio.file.StandardCopyOption; import java.util.List; import java.util.Map; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.ResponseBuilder; + import org.apache.commons.io.FilenameUtils; import org.apache.commons.io.IOUtils; import org.apache.http.HttpStatus; +import org.gcube.common.storagehub.client.StreamDescriptor; +import org.gcube.common.storagehub.client.plugins.AbstractPlugin; +import org.gcube.common.storagehub.client.proxies.ItemManagerClient; import org.gcube.datatransfer.resolver.applicationprofile.ApplicationProfileReader; +import org.gcube.datatransfer.resolver.services.StorageHubResolver; +import org.gcube.datatransfer.resolver.services.error.ExceptionManager; +import org.gcube.datatransfer.resolver.shub.StorageHubMetadataResponseBuilder; import org.gcube.datatransfer.resolver.util.HTTPCallsUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -81,7 +90,6 @@ public class TestResolvers { } } - /** * Storage hub test. * diff --git a/src/test/resources/.gitignore b/src/test/resources/.gitignore index c04ed84..91f56a4 100644 --- a/src/test/resources/.gitignore +++ b/src/test/resources/.gitignore @@ -11,3 +11,4 @@ /gcube.gcubekey /preprod.gcubekey /pred4s.gcubekey +/log4j.properties