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.GET; 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.resources.discovery.client.api.DiscoveryClient; import org.gcube.resources.discovery.client.queries.api.SimpleQuery; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * The WekeoResolver provides a method to wrap the basic authentication from * D4Science account to WekeoBroker service. * * See more at * https://gcube.wiki.gcube-system.org/gcube/URI_Resolver#Wekeo_Resolver * * @author Francesco Mangiacrapa at ISTI-CNR francesco.mangiacrapa@isti.cnr.it * * Mar 24, 2022 */ @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; } } /** * Index. * * @param req the req * @return the input stream * @throws WebApplicationException the web application exception */ @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(); } } } }