diff --git a/CHANGELOG.md b/CHANGELOG.md index 4fe1004e5630c82056ec851f7ceeb141635c9003..4d8a87996586347cb32457bfbb06e524f380098b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,5 @@ # Changelog + All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), @@ -6,17 +7,36 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] -### Added (for new features) +### Added + +- Scripting is simplified by adding a `home` directory, of which a copy is + created for each called script and set as the `HOME` environment variable. + +### Changed + +- + + +### Deprecated -### Changed (for changes in existing functionality) +- -### Deprecated (for soon-to-be removed features) -### Removed (for now removed features) +### Fixed -### Fixed (for any bug fixes) +- #46 - Server-side scripting failed as an unprivileged user because the was no + writable home directory. - NaN Double Values (see #41) + + ### Security (in case of vulnerabilities) +## [0.1.0] - 2018-10-09 + +Tag `v0.1` - Commit 3b17b49 + +### Added + +- everything diff --git a/conf/core/server.conf b/conf/core/server.conf index 8e71c561705fda384a2a87b2b45c3aa8ccfb8ea4..00b3b1a33dbab19f9931424cc91b5b11f9d137e3 100644 --- a/conf/core/server.conf +++ b/conf/core/server.conf @@ -3,6 +3,7 @@ SERVER_OWNER= SERVER_NAME=CaosDB Server SERVER_SIDE_SCRIPTING_BIN_DIR=./scripting/bin/ SERVER_SIDE_SCRIPTING_WORKING_DIR=./scripting/working/ +SERVER_SIDE_SCRIPTING_HOME_DIR=./scripting/home/ FILE_SYSTEM_ROOT=./CaosDBFileSystem/FileSystemRoot/ DROP_OFF_BOX=./CaosDBFileSystem/DropOffBox/ TMP_FILES=./CaosDBFileSystem/TMP/ diff --git a/scripting/home/readme.md b/scripting/home/readme.md new file mode 100644 index 0000000000000000000000000000000000000000..9c9744a55cec41f33929a3953d9c582fa97f46e0 --- /dev/null +++ b/scripting/home/readme.md @@ -0,0 +1,3 @@ +# The `home` directory # +This directory will be copied to a temporary directy for each server-side +scripting invocation and set as the `HOME` environment variable. diff --git a/src/main/java/caosdb/server/ServerProperties.java b/src/main/java/caosdb/server/ServerProperties.java index 1f90b069df76760864385f6f090e123387bcbd0a..929f845d8023dc9d81c62294a5318442fcfb52ea 100644 --- a/src/main/java/caosdb/server/ServerProperties.java +++ b/src/main/java/caosdb/server/ServerProperties.java @@ -119,6 +119,7 @@ public class ServerProperties extends Properties { public static final String KEY_SERVER_OWNER = "SERVER_OWNER"; public static final String KEY_SERVER_SIDE_SCRIPTING_BIN_DIR = "SERVER_SIDE_SCRIPTING_BIN_DIR"; + public static final String KEY_SERVER_SIDE_SCRIPTING_HOME_DIR = "SERVER_SIDE_SCRIPTING_HOME_DIR"; public static final String KEY_SERVER_SIDE_SCRIPTING_WORKING_DIR = "SERVER_SIDE_SCRIPTING_WORKING_DIR"; diff --git a/src/main/java/caosdb/server/scripting/ServerSideScriptingCaller.java b/src/main/java/caosdb/server/scripting/ServerSideScriptingCaller.java index 7af6ba1ca5ac15868f991e6a2a8c7e85b2847bd7..6fa5d1a479df47c20d9d3a5d3a7134e1c0898ad1 100644 --- a/src/main/java/caosdb/server/scripting/ServerSideScriptingCaller.java +++ b/src/main/java/caosdb/server/scripting/ServerSideScriptingCaller.java @@ -25,7 +25,9 @@ package caosdb.server.scripting; import caosdb.server.CaosDBException; +import caosdb.server.CaosDBServer; import caosdb.server.FileSystem; +import caosdb.server.ServerProperties; import caosdb.server.entity.FileProperties; import caosdb.server.entity.Message; import caosdb.server.utils.ServerMessages; @@ -54,6 +56,7 @@ public class ServerSideScriptingCaller { private ScriptingUtils utils; private List<FileProperties> files; private File workingDir; + private File tmpHome; private File sharedDir; private Object authToken; private Map<String, String> env; @@ -110,6 +113,7 @@ public class ServerSideScriptingCaller { this.authToken = authToken; this.env = env; this.workingDir = workingDir; + this.tmpHome = (new File(getTmpWorkingDir(), "temphome")).getAbsoluteFile(); this.stdOutFile = utils.getStdOutFile(workingDir); this.stdErrFile = utils.getStdErrFile(workingDir); } @@ -122,6 +126,7 @@ public class ServerSideScriptingCaller { createWorkingDir(); putFilesInWorkingDir(files); createSharedDir(); + createTmpHomeDir(); updateEnvironment(); } catch (final Exception e) { e.printStackTrace(); @@ -171,17 +176,41 @@ public class ServerSideScriptingCaller { } // create working dir - getTmpWorkingDir().mkdirs(); + if (!getTmpWorkingDir().mkdirs()) { + throw new Exception("Creating the temporary working dir failed."); + } } - /** Creates a temporary directory for shareing by the script and sets `sharedDir` accordingly. */ + /** + * Create a writable "home like" directory, needed for some Python libs, e.g. caosdb-pylib. + * + * <p>The temporary working directory must exist when this method is called. + */ + void createTmpHomeDir() throws Exception, IOException { + if (!getTmpWorkingDir().exists()) { + throw new Exception( + "The temp working dir (" + + getTmpWorkingDir().toString() + + ") must exist before the home dir can be created."); + } + if (getTmpHomeDir().exists()) { + throw new Exception("The home directory must be non-existing when the caller is invoked."); + } + File srcDir = + new File( + CaosDBServer.getServerProperty(ServerProperties.KEY_SERVER_SIDE_SCRIPTING_HOME_DIR)); + FileUtils.copyDirectory(srcDir, this.tmpHome); + } + + /** Creates a temporary directory for sharing by the script and sets `sharedDir` accordingly. */ void createSharedDir() throws Exception { sharedDir = new File(FileSystem.assertDir(null)); } /** Sets environment variables for the script, according to the current state of the caller. */ - void updateEnvironment() { + void updateEnvironment() throws Exception { env.put("SHARED_DIR", sharedDir.toString()); + env.put("HOME", tmpHome.toString()); } void cleanup() { @@ -263,6 +292,10 @@ public class ServerSideScriptingCaller { return this.workingDir; } + File getTmpHomeDir() { + return this.tmpHome; + } + File getSharedDir() { return this.sharedDir; } diff --git a/src/test/java/caosdb/server/scripting/TestServerSideScriptingCaller.java b/src/test/java/caosdb/server/scripting/TestServerSideScriptingCaller.java index 668d75fbc9271babc8df20a872f661d3bd0a9646..e66c646a4e5604f2b7df2dac0af689aa8797160a 100644 --- a/src/test/java/caosdb/server/scripting/TestServerSideScriptingCaller.java +++ b/src/test/java/caosdb/server/scripting/TestServerSideScriptingCaller.java @@ -492,6 +492,9 @@ public class TestServerSideScriptingCaller extends CaosDBTestClass { public void putFilesInWorkingDir(final Collection<FileProperties> files) throws FileNotFoundException, CaosDBException {} + @Override + public void createTmpHomeDir() throws Exception {} + @Override public int callScript() throws IOException { throw new IOException(); @@ -609,4 +612,82 @@ public class TestServerSideScriptingCaller extends CaosDBTestClass { null, -1, null, "authToken", emptyEnv, new ScriptingUtils(), this.pwd); caller.cleanup(); } + +/** + * Does the order of directory creation matter? + */ + @Test + public void testDirectoriesInWrongOrder() throws Message { + final String[] cmd = {testExecutable.getAbsolutePath()}; + final ServerSideScriptingCaller caller = + new ServerSideScriptingCaller( + cmd, -1, null, null, emptyEnv, new ScriptingUtils(), this.pwd); + this.exception.expect( + new BaseMatcher<Exception>() { + + @Override + public boolean matches(final Object item) { + return item == ServerMessages.SERVER_SIDE_SCRIPT_ERROR; + } + + @Override + public void describeTo(final Description description) { + description.appendValue(ServerMessages.SERVER_SIDE_SCRIPT_ERROR); + } + }); + try { + caller.createTmpHomeDir(); + caller.createWorkingDir(); + } catch (final Exception e) { + e.printStackTrace(); + throw ServerMessages.SERVER_SIDE_SCRIPT_ERROR; + } + caller.cleanup(); + } + + /** + * Is the new home directory created correctly? + */ + @Test + public void testWorkingDirCreation() throws Exception { + final String[] cmd = {testExecutable.getAbsolutePath()}; + final ServerSideScriptingCaller caller = + new ServerSideScriptingCaller( + cmd, -1, null, null, emptyEnv, new ScriptingUtils(), this.pwd); + caller.createWorkingDir(); + caller.createTmpHomeDir(); + assertTrue(caller.getTmpHomeDir().exists()); + caller.cleanup(); + } + + /** + * Does copying files over to the new home directory work? + */ + @Test + public void testWorkingDirCopying() throws Exception { + final String[] cmd = {testExecutable.getAbsolutePath()}; + final ServerSideScriptingCaller caller = + new ServerSideScriptingCaller( + cmd, -1, null, null, emptyEnv, new ScriptingUtils(), this.pwd); + caller.createWorkingDir(); + // Create source folder in working directory + String scriptingHomeDir = CaosDBServer.getServerProperty("SERVER_SIDE_SCRIPTING_HOME_DIR"); + File tempHomeDirSrc = new File(caller.getTmpWorkingDir(), "home_source"); + CaosDBServer.setProperty("SERVER_SIDE_SCRIPTING_HOME_DIR", tempHomeDirSrc.toString()); + tempHomeDirSrc.mkdir(); + String testFileName = "testfile.txt"; + String testContent = "sloikdfgu89w4t78930tvgyum9q238456t27ht2"; + File testFile = new File(tempHomeDirSrc, testFileName); + // testFile.createNewFile(); + FileUtils.write(testFile, testContent, "UTF-8"); + // Attempt to copy the source to the new home + caller.createTmpHomeDir(); + // Compare source and new home + File expectedFile = new File(caller.getTmpHomeDir(), testFileName); + assertTrue(expectedFile.exists()); + String expectedFileContent = FileUtils.readFileToString(expectedFile, "UTF-8"); + assertEquals(expectedFileContent, testContent); + caller.cleanup(); + CaosDBServer.setProperty("SERVER_SIDE_SCRIPTING_HOME_DIR", scriptingHomeDir); + } }