diff --git a/.classpath b/.classpath index e43402f..63c80a6 100644 --- a/.classpath +++ b/.classpath @@ -15,11 +15,13 @@ + + @@ -30,6 +32,7 @@ + diff --git a/.project b/.project index 446545d..4bd607c 100644 --- a/.project +++ b/.project @@ -5,11 +5,21 @@ + + org.eclipse.wst.common.project.facet.core.builder + + + org.eclipse.jdt.core.javabuilder + + org.eclipse.wst.validation.validationbuilder + + + org.eclipse.m2e.core.maven2Builder @@ -17,7 +27,11 @@ + org.eclipse.jem.workbench.JavaEMFNature + org.eclipse.wst.common.modulecore.ModuleCoreNature org.eclipse.jdt.core.javanature org.eclipse.m2e.core.maven2Nature + org.eclipse.wst.common.project.facet.core.nature + org.eclipse.wst.jsdt.core.jsNature diff --git a/distro/changelog.xml b/distro/changelog.xml index cfc9a9c..fd19851 100644 --- a/distro/changelog.xml +++ b/distro/changelog.xml @@ -1,5 +1,8 @@ - + + integration with storageHub + + add user script is executed also when configfile.csv is not present diff --git a/distro/web.xml b/distro/web.xml index e7c1bee..ef29b85 100644 --- a/distro/web.xml +++ b/distro/web.xml @@ -11,6 +11,10 @@ addUserScript /usr/local/bin/rusersadd + + unmountScript + /usr/local/bin/rconnector_unmount + storedKeyPath /var/lib/rstudio-server/secure-cookie-key diff --git a/pom.xml b/pom.xml index 612d26e..b765d74 100644 --- a/pom.xml +++ b/pom.xml @@ -1,9 +1,10 @@ - 4.0.0 org.gcube.data.analysis r-connector - 2.1.3-SNAPSHOT + 2.1.4-SNAPSHOT RConnector makes R available in the infrastructure @@ -64,7 +65,12 @@ tabulardata-client-library [2.0.0-SNAPSHOT, 3.0.0-SNAPSHOT) - + + org.gcube.data-access + sh-fuse-integration + 1.0.0-SNAPSHOT + + javax.ws.rs diff --git a/src/main/java/org/gcube/data/analysis/rconnector/ConfigFileWriter.java b/src/main/java/org/gcube/data/analysis/rconnector/ConfigFileWriter.java index 4d7bae1..9c6660f 100644 --- a/src/main/java/org/gcube/data/analysis/rconnector/ConfigFileWriter.java +++ b/src/main/java/org/gcube/data/analysis/rconnector/ConfigFileWriter.java @@ -1,13 +1,13 @@ package org.gcube.data.analysis.rconnector; -import java.io.BufferedReader; import java.io.File; import java.io.FileWriter; -import java.io.IOException; -import java.io.InputStreamReader; import javax.inject.Singleton; +import org.gcube.common.authorization.library.provider.SecurityTokenProvider; +import org.gcube.common.scope.api.ScopeProvider; + import lombok.extern.slf4j.Slf4j; @@ -21,38 +21,10 @@ public class ConfigFileWriter { usersHome=usersHome+"/"; File userDir = new File(usersHome+login); File configFile= new File(userDir, fileName); - executeCommandLine(scriptToExecute,login); + Utils.executeCommandLine(scriptToExecute,login, SecurityTokenProvider.instance.get(), ScopeProvider.instance.get()); return writeFile(info, configFile); } - private String executeCommandLine(String cmd, String login){ - log.debug("executing command "+cmd+" "+login); - Process process = null; - String lastline = ""; - try { - - ProcessBuilder pb = new ProcessBuilder(cmd, login); - process = pb.start(); - - try { - process.waitFor(); - } catch (InterruptedException e) { - log.warn("interrupt Exception on process",e); - } - - BufferedReader br = new BufferedReader(new InputStreamReader(process.getErrorStream())); - String line = br.readLine(); - while (line!=null){ - lastline += line; - line = br.readLine(); - } - } catch (IOException e) { - log.error("error executing command line",e); - } - log.debug("command result line is "+lastline); - return lastline; - } - private boolean writeFile(Info info, File pathToFile){ log.debug("writing config file"); StringBuffer sb = new StringBuffer(); diff --git a/src/main/java/org/gcube/data/analysis/rconnector/DisconnectResource.java b/src/main/java/org/gcube/data/analysis/rconnector/DisconnectResource.java index 86b11e9..3d7791b 100644 --- a/src/main/java/org/gcube/data/analysis/rconnector/DisconnectResource.java +++ b/src/main/java/org/gcube/data/analysis/rconnector/DisconnectResource.java @@ -1,5 +1,6 @@ package org.gcube.data.analysis.rconnector; +import javax.inject.Inject; import javax.servlet.ServletContext; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; @@ -10,21 +11,39 @@ import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; +import org.gcube.common.authorization.library.provider.AuthorizationProvider; + import lombok.extern.slf4j.Slf4j; @Path("disconnect") @Slf4j public class DisconnectResource { + @Context ServletContext context; + @Inject + InfoRetriever infoRetriever; + @GET @Produces(MediaType.TEXT_HTML) public Response disconnect(@Context HttpServletRequest req) { log.info("disconnect called "); String rStudioServerAddress = context==null?"":context.getInitParameter("rStudioAddress"); - log.info(rStudioServerAddress); + String scriptToExecute = context.getInitParameter("unmountScript"); + + String login = null; + for (Cookie cookie : req.getCookies()) + if (cookie.getName().equals("user-id")) + login = cookie.getValue(); + + if (login==null) + return Response.serverError().build(); + try{ + + Utils.executeCommandLine(scriptToExecute, login); + boolean found = false; for (Cookie cookie : req.getCookies()) if (cookie.getName().equals("user-id")){ @@ -39,8 +58,8 @@ public class DisconnectResource { ).build(); else return Response.ok(context.getClassLoader().getResourceAsStream("inactivesession.html")).build(); }catch(Exception e){ - e.printStackTrace(); - return null; + log.error("erorr disconnecting from Rstudio",e); + return Response.serverError().build(); } } diff --git a/src/main/java/org/gcube/data/analysis/rconnector/InfoRetriever.java b/src/main/java/org/gcube/data/analysis/rconnector/InfoRetriever.java index d5a610e..00033c1 100644 --- a/src/main/java/org/gcube/data/analysis/rconnector/InfoRetriever.java +++ b/src/main/java/org/gcube/data/analysis/rconnector/InfoRetriever.java @@ -10,8 +10,6 @@ import java.util.regex.Pattern; import javax.inject.Inject; import javax.inject.Singleton; -import lombok.extern.slf4j.Slf4j; - import org.gcube.common.authorization.library.provider.SecurityTokenProvider; import org.gcube.common.database.DatabaseEndpointIdentifier; import org.gcube.common.database.DatabaseProvider; @@ -25,6 +23,8 @@ import org.gcube.data.analysis.tabulardata.model.metadata.common.NamesMetadata; import org.gcube.data.analysis.tabulardata.model.metadata.table.DatasetViewTableMetadata; import org.gcube.data.analysis.tabulardata.model.table.Table; +import lombok.extern.slf4j.Slf4j; + @Slf4j @Singleton public class InfoRetriever { @@ -32,7 +32,6 @@ public class InfoRetriever { @Inject DatabaseProvider databaseProvider; - @SuppressWarnings("unchecked") public Info retrieve(String userName, Long tabularResourceId){ try { diff --git a/src/main/java/org/gcube/data/analysis/rconnector/Resource.java b/src/main/java/org/gcube/data/analysis/rconnector/Resource.java index 057ea1a..b4e7301 100644 --- a/src/main/java/org/gcube/data/analysis/rconnector/Resource.java +++ b/src/main/java/org/gcube/data/analysis/rconnector/Resource.java @@ -1,9 +1,11 @@ package org.gcube.data.analysis.rconnector; import java.io.File; +import java.io.IOException; import java.net.URI; import java.net.URLEncoder; import java.nio.file.Files; +import java.nio.file.Paths; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Locale; @@ -20,11 +22,12 @@ import javax.ws.rs.core.NewCookie; import javax.ws.rs.core.Response; import javax.xml.bind.DatatypeConverter; -import lombok.extern.slf4j.Slf4j; - import org.gcube.common.authorization.library.provider.AuthorizationProvider; import org.gcube.common.authorization.library.provider.SecurityTokenProvider; import org.gcube.common.scope.api.ScopeProvider; +import org.gcube.data.access.storagehub.fs.StorageHubFS; + +import lombok.extern.slf4j.Slf4j; @Path("connect/") @Slf4j @@ -35,85 +38,89 @@ public class Resource { @Inject InfoRetriever infoRetriever; - + @Context ServletContext context; - - + + @Path("/{trId}") @GET public Response connect(@PathParam("trId") Long tabularResourceId) { log.info("connect called with user {} and trID {} in scope {}",AuthorizationProvider.instance.get().getClient().getId(), tabularResourceId, ScopeProvider.instance.get()); + + String login = AuthorizationProvider.instance.get().getClient().getId(); String usersHome = context.getInitParameter("usersHome"); String filename = context.getInitParameter("filename"); String scriptToExecute = context.getInitParameter("addUserScript"); - if (ScopeProvider.instance.get()==null || AuthorizationProvider.instance.get().getClient().getId() == null || tabularResourceId ==null ) return Response.serverError().build(); + if (ScopeProvider.instance.get()==null || login == null || tabularResourceId ==null ) return Response.serverError().build(); Info info; try{ - info = infoRetriever.retrieve(AuthorizationProvider.instance.get().getClient().getId(), tabularResourceId); + info = infoRetriever.retrieve(login, tabularResourceId); log.debug("retrieved info: "+info); }catch(Exception e){ log.error("error connecting to r",e); return Response.serverError().build(); } - if (!writer.write(info, AuthorizationProvider.instance.get().getClient().getId(), usersHome, filename, scriptToExecute)) return Response.serverError().build(); - - return createResponse(AuthorizationProvider.instance.get().getClient().getId()); + if (!writer.write(info, login, usersHome, filename, scriptToExecute)) return Response.serverError().build(); + + return createResponse(login); } - + @GET public Response connect() { log.info("connect called with user {} in scope {}",AuthorizationProvider.instance.get().getClient().getId(), ScopeProvider.instance.get()); + String login = AuthorizationProvider.instance.get().getClient().getId(); String usersHome = context.getInitParameter("usersHome"); String filename = context.getInitParameter("filename"); String scriptToExecute = context.getInitParameter("addUserScript"); - if (AuthorizationProvider.instance.get().getClient().getId() == null ) return Response.serverError().build(); + if (login == null ) return Response.serverError().build(); Info info = new Info(); - info.setUsername(AuthorizationProvider.instance.get().getClient().getId()); + info.setUsername(login); info.setToken(SecurityTokenProvider.instance.get()); - if (!writer.write(info, AuthorizationProvider.instance.get().getClient().getId(), usersHome, filename, scriptToExecute)) return Response.serverError().build(); + if (!writer.write(info, login, usersHome, filename, scriptToExecute)) return Response.serverError().build(); + return createResponse(AuthorizationProvider.instance.get().getClient().getId()); } - + private Response createResponse(String userName){ try{ String keyFilePath = context.getInitParameter("storedKeyPath"); String rStudioServerAddress = context.getInitParameter("rStudioAddress"); - + log.debug("key file path: "+keyFilePath); log.debug("rstudio server address: "+rStudioServerAddress); - + Calendar now = Calendar.getInstance(); now.add(Calendar.YEAR, 10); now.add(Calendar.DAY_OF_YEAR, -1); SimpleDateFormat sdf = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z", Locale.ENGLISH); String format = sdf.format(now.getTime()); - + File keyFile = new File(keyFilePath); if (!keyFile.exists()) return Response.serverError().build(); - + byte[] keyByte = Files.readAllBytes(keyFile.toPath()); - + SecretKeySpec keySpec = new SecretKeySpec(keyByte, - "HmacSHA256"); + "HmacSHA256"); Mac mac = Mac.getInstance("HmacSHA256"); mac.init(keySpec); byte[] result = mac.doFinal((userName+format).getBytes()); String encoded = URLEncoder.encode(DatatypeConverter.printBase64Binary(result)); - + String cookieValue = userName+"|"+URLEncoder.encode(format).replaceAll("\\+", "%20")+"|"+encoded; NewCookie cookie = new NewCookie("user-id", cookieValue, "/", rStudioServerAddress, "", -1, false, true ); - - + + return Response.seeOther(new URI("http://"+rStudioServerAddress)) .cookie(cookie).build(); }catch(Exception e){ log.error("error creating response", e); return Response.serverError().build();} } - + } diff --git a/src/main/java/org/gcube/data/analysis/rconnector/Utils.java b/src/main/java/org/gcube/data/analysis/rconnector/Utils.java new file mode 100644 index 0000000..c18a86e --- /dev/null +++ b/src/main/java/org/gcube/data/analysis/rconnector/Utils.java @@ -0,0 +1,43 @@ +package org.gcube.data.analysis.rconnector; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.Arrays; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class Utils { + + private static Logger log = LoggerFactory.getLogger(Utils.class); + + protected static String executeCommandLine(String ... args){ + log.debug("executing command {}",Arrays.asList(args)); + Process process = null; + String lastline = ""; + try { + + ProcessBuilder pb = new ProcessBuilder(args); + process = pb.start(); + + try { + process.waitFor(); + } catch (InterruptedException e) { + log.warn("interrupt Exception on process",e); + } + + BufferedReader br = new BufferedReader(new InputStreamReader(process.getErrorStream())); + String line = br.readLine(); + while (line!=null){ + lastline += line; + line = br.readLine(); + } + } catch (IOException e) { + log.error("error executing command line",e); + } + log.debug("command result line is {} ",lastline); + return lastline; + } + +} diff --git a/src/main/resources/META-INF/beans.xml b/src/main/resources/META-INF/beans.xml index 7c7e8db..e10a85a 100644 --- a/src/main/resources/META-INF/beans.xml +++ b/src/main/resources/META-INF/beans.xml @@ -1,5 +1,3 @@ - + + \ No newline at end of file