changeset 1245:484de0a847c4

Add some support for 'system' users Reviewed-by: jerboaa Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2013-September/008153.html PR1333
author Omair Majid <omajid@redhat.com>
date Thu, 05 Sep 2013 15:58:29 -0400
parents 9ad2d74ff780
children bac262a2c98f
files config/src/main/java/com/redhat/thermostat/shared/config/Configuration.java config/src/test/java/com/redhat/thermostat/shared/config/ConfigurationTest.java
diffstat 2 files changed, 211 insertions(+), 43 deletions(-) [+]
line wrap: on
line diff
--- a/config/src/main/java/com/redhat/thermostat/shared/config/Configuration.java	Thu Sep 05 12:36:53 2013 -0400
+++ b/config/src/main/java/com/redhat/thermostat/shared/config/Configuration.java	Thu Sep 05 15:58:29 2013 -0400
@@ -38,9 +38,6 @@
 
 import java.io.File;
 
-import com.redhat.thermostat.shared.locale.LocaleResources;
-import com.redhat.thermostat.shared.locale.Translate;
-
 /**
  * Contains locations to various files and directories used by thermostat
  * components.
@@ -56,19 +53,40 @@
  * <p>
  * The directories are split according to functionality, along the lines of
  * Filesystem Hierarchy Standard (FHS).
+ * <p>
+ * The behaviour of this class is affected by the following environment
+ * variables:
+ * <dl>
+ *   <di>{@code THERMOSTAT_HOME}</di>
+ *   <dd>Specifies the location thermostat uses for it's read-only data,
+ *     such as jars.</dd>
+ *   <di>{@code THERMOSTAT_SYSTEM_USER}</di>
+ *   <dd>If set, indicates that thermostat is running as a system user.
+ *     In this mode, it reads configuration from system paths and places
+ *     data in system writeable locations.</dd>
+ *   <di>{@code USER_THERMOSTAT_HOME}</di>
+ *   <dd>The meaning of this varies depending on whether thermostat is
+ *     running as a system user or not. In normal mode, this controls
+ *     where thermostat places it's data and where configuration files
+ *     are searched for. In system mode, this is treated as a prefix
+ *     writing system data.</dd>
+ * </dl>
  */
 public class Configuration {
 
     // Note: these paths are used by the integration tests too. Please update
     // them whenever you change this class.
 
+    // environment variables (also system properties for convenience):
     private static final String THERMOSTAT_HOME = "THERMOSTAT_HOME";
     private static final String USER_THERMOSTAT_HOME = "USER_THERMOSTAT_HOME";
+    private static final String THERMOSTAT_SYSTEM_USER = "THERMOSTAT_SYSTEM_USER";
+
+
     private static final String THERMOSTAT_USER_DIR = ".thermostat";
-    private static final Translate<LocaleResources> t = LocaleResources.createLocalizer();
 
     private final String home;
-    private final File userHome;
+    private final UserDirectories userDirectories;
 
     private boolean printOsgiInfo = false;
 
@@ -79,27 +97,26 @@
         if (home == null) {
             home = System.getenv(THERMOSTAT_HOME);
         }
-        
+
         if (home == null) {
             throw new InvalidConfigurationException(THERMOSTAT_HOME + " not defined...");
         }
         this.home = home;
 
-        // allow this to be specified also as a special property, meant for tests
-        String userHome = System.getProperty(USER_THERMOSTAT_HOME);
-        if (userHome == null) {
-            userHome = System.getenv(USER_THERMOSTAT_HOME);
+        String systemUser = System.getProperty(THERMOSTAT_SYSTEM_USER);
+        if (systemUser == null) {
+            systemUser = System.getenv(THERMOSTAT_SYSTEM_USER);
         }
-        if (userHome == null) {
-            userHome = System.getProperty("user.home") + File.separatorChar + THERMOSTAT_USER_DIR;
+
+        if (systemUser != null) {
+            userDirectories = new SystemUserDirectories();
+        } else {
+            userDirectories = new UnprivilegedUserDirectories();
         }
-        this.userHome = new File(userHome);
     }
 
     /*
      * Overall hierarchy
-     *
-     * TODO: these might have to be different for 'root' or 'thermostat' users
      */
 
     public File getSystemThermostatHome() throws InvalidConfigurationException {
@@ -107,7 +124,7 @@
     }
 
     public File getUserThermostatHome() throws InvalidConfigurationException {
-        return userHome;
+        return userDirectories.getSystemRoot();
     }
 
     public File getSystemPluginRoot() throws InvalidConfigurationException {
@@ -117,7 +134,7 @@
     public File getSystemLibRoot() throws InvalidConfigurationException {
         return new File(home, "libs");
     }
-    
+
     public File getSystemNativeLibsRoot() throws InvalidConfigurationException {
         return new File(getSystemLibRoot(), "native");
     }
@@ -127,32 +144,32 @@
     }
 
     public File getUserConfigurationDirectory() throws InvalidConfigurationException {
-        return new File(getUserThermostatHome(), "etc");
+        return userDirectories.getUserConfigurationDirectory();
     }
 
     /** A location that contains data that is persisted */
     public File getUserPersistentDataDirectory() throws InvalidConfigurationException {
-        File dataDir = new File(getUserThermostatHome(), "data");
-        return dataDir;
+        return userDirectories.getUserPersistentDataDirectory();
     }
 
-    /** Contains data that is only useful for the duration that thermostat is running */
+    /**
+     * Contains data that is only useful for the duration that thermostat is
+     * running
+     */
     public File getUserRuntimeDataDirectory() throws InvalidConfigurationException {
-        File runDir = new File(getUserThermostatHome(), "run");
-        return runDir;
+        return userDirectories.getUserRuntimeDataDirectory();
     }
 
     public File getUserLogDirectory() throws InvalidConfigurationException {
-        File logDir = new File(getUserThermostatHome(), "logs");
-        return logDir;
+        return userDirectories.getUserLogDirectory();
+
     }
 
     public File getUserCacheDirectory() throws InvalidConfigurationException {
-        File cacheDir = new File(getUserThermostatHome(), "cache");
-        return cacheDir;
+        return userDirectories.getUserCacheDirectory();
     }
 
-    /* Specific files and directories */
+    /* Specific files and directories. All these methods should use the directories defined above */
 
     public File getUserStorageDirectory() throws InvalidConfigurationException {
         return new File(getUserPersistentDataDirectory(), "db");
@@ -204,7 +221,7 @@
 
     // TODO add logging files here (see LoggingUtils)
     // TODO add ssl.properties file here (see SSLConfiguration)
-    
+
     public boolean getPrintOSGiInfo() {
         return printOsgiInfo;
     }
@@ -213,5 +230,121 @@
         printOsgiInfo = newValue;
     }
 
+    private interface UserDirectories {
+
+        public File getSystemRoot();
+
+        public File getUserConfigurationDirectory();
+
+        public File getUserPersistentDataDirectory();
+
+        public File getUserRuntimeDataDirectory();
+
+        public File getUserLogDirectory();
+
+        public File getUserCacheDirectory();
+
+    }
+
+    /*
+     * We need two different implementations because the paths are different. We
+     * can't get clean paths by simply changing the prefix.
+     *
+     * user path:   $USER/.thermostat/{etc,log,...}
+     * system path: /{etc,var/log,var/lib}/thermostat
+     *
+     * Notice how 'thermostat' comes first in one set of paths and later in the second set.
+     */
+
+    private static class UnprivilegedUserDirectories implements UserDirectories {
+
+        private File userHome;
+
+        public UnprivilegedUserDirectories() {
+            // allow this to be specified also as a special property, meant for tests
+            String userHome = System.getProperty(USER_THERMOSTAT_HOME);
+            if (userHome == null) {
+                userHome = System.getenv(USER_THERMOSTAT_HOME);
+            }
+            if (userHome == null) {
+                userHome = System.getProperty("user.home") + File.separatorChar + THERMOSTAT_USER_DIR;
+            }
+            this.userHome = new File(userHome);
+        }
+
+        public File getSystemRoot() throws InvalidConfigurationException {
+            return userHome;
+        }
+
+
+        public File getUserConfigurationDirectory() throws InvalidConfigurationException {
+            return new File(getSystemRoot(), "etc");
+        }
+
+        public File getUserPersistentDataDirectory() throws InvalidConfigurationException {
+            File dataDir = new File(getSystemRoot(), "data");
+            return dataDir;
+        }
+
+        public File getUserRuntimeDataDirectory() throws InvalidConfigurationException {
+            File runDir = new File(getSystemRoot(), "run");
+            return runDir;
+        }
+
+        public File getUserLogDirectory() throws InvalidConfigurationException {
+            File logDir = new File(getSystemRoot(), "logs");
+            return logDir;
+        }
+
+        public File getUserCacheDirectory() throws InvalidConfigurationException {
+            File cacheDir = new File(getSystemRoot(), "cache");
+            return cacheDir;
+        }
+    }
+
+    private static class SystemUserDirectories implements UserDirectories {
+
+        private File prefix;
+
+        public SystemUserDirectories() {
+            // allow this to be specified also as a special property, meant for tests
+            String userHome = System.getProperty(USER_THERMOSTAT_HOME);
+            if (userHome == null) {
+                userHome = System.getenv(USER_THERMOSTAT_HOME);
+            }
+            if (userHome == null) {
+                userHome = "/";
+            }
+            this.prefix = new File(userHome);
+        }
+
+        public File getSystemRoot() throws InvalidConfigurationException {
+            return prefix;
+        }
+
+
+        public File getUserConfigurationDirectory() throws InvalidConfigurationException {
+            return new File(getSystemRoot(), "etc/thermostat");
+        }
+
+        public File getUserPersistentDataDirectory() throws InvalidConfigurationException {
+            File dataDir = new File(getSystemRoot(), "var/lib/thermostat");
+            return dataDir;
+        }
+
+        public File getUserRuntimeDataDirectory() throws InvalidConfigurationException {
+            File runDir = new File(getSystemRoot(), "var/run/thermostat");
+            return runDir;
+        }
+
+        public File getUserLogDirectory() throws InvalidConfigurationException {
+            File logDir = new File(getSystemRoot(), "var/log/thermostat");
+            return logDir;
+        }
+
+        public File getUserCacheDirectory() throws InvalidConfigurationException {
+            File cacheDir = new File(getSystemRoot(), "var/cache/thermostat");
+            return cacheDir;
+        }
+    }
 }
-
--- a/config/src/test/java/com/redhat/thermostat/shared/config/ConfigurationTest.java	Thu Sep 05 12:36:53 2013 -0400
+++ b/config/src/test/java/com/redhat/thermostat/shared/config/ConfigurationTest.java	Thu Sep 05 15:58:29 2013 -0400
@@ -40,29 +40,27 @@
 
 import java.io.File;
 import java.io.IOException;
-import java.util.Properties;
 
 import junit.framework.Assert;
 
 import org.junit.After;
+import org.junit.Before;
 import org.junit.Test;
 
-import com.redhat.thermostat.shared.config.Configuration;
-import com.redhat.thermostat.shared.config.InvalidConfigurationException;
+public class ConfigurationTest {
 
-public class ConfigurationTest {
+    @Before
+    public void setUp() {
+        System.clearProperty("THERMOSTAT_HOME");
+        System.clearProperty("USER_THERMOSTAT_HOME");
+        System.clearProperty("THERMOSTAT_SYSTEM_USER");
+    }
     
     @After
     public void tearDown() {
-        // remove THERMOSTAT_HOME system property for a clean slate
-        Properties props = System.getProperties();
-        Properties newProps = new Properties();
-        for (Object key : props.keySet()) {
-            if (!key.equals("THERMOSTAT_HOME")) {
-                newProps.put(key, props.get(key));
-            }
-        }
-        System.setProperties(newProps);
+        System.clearProperty("THERMOSTAT_HOME");
+        System.clearProperty("USER_THERMOSTAT_HOME");
+        System.clearProperty("THERMOSTAT_SYSTEM_USER");
     }
 
     @Test
@@ -107,6 +105,43 @@
     }
 
     @Test
+    public void testPrivilegedUserLocations() throws InvalidConfigurationException, IOException {
+        String thermostatHome = "/tmp";
+        System.setProperty("THERMOSTAT_HOME", thermostatHome);
+        System.setProperty("THERMOSTAT_SYSTEM_USER", "");
+        Configuration config = new Configuration();
+
+        // the paths are unix specific, but so are the paths in Configuration
+
+        Assert.assertEquals("/etc/thermostat/agent.properties", config.getUserAgentConfigurationFile().getCanonicalPath());
+        Assert.assertEquals("/etc/thermostat/agent.auth", config.getUserAgentAuthConfigFile().getCanonicalPath());
+        Assert.assertEquals("/etc/thermostat/db.properties", config.getUserStorageConfigurationFile().getCanonicalPath());
+
+        Assert.assertEquals("/var/lib/thermostat/db", config.getUserStorageDirectory().getCanonicalPath());
+        Assert.assertEquals("/var/run/thermostat/db.pid", config.getUserStoragePidFile().getAbsolutePath());
+        Assert.assertEquals("/var/log/thermostat/db.log", config.getUserStorageLogFile().getCanonicalPath());
+    }
+
+    @Test
+    public void testPrivilegedUserLocationsWithPrefix() throws InvalidConfigurationException, IOException {
+        String thermostatHome = "/tmp";
+        String prefix = "/opt/custom/prefix";
+        System.setProperty("THERMOSTAT_HOME", thermostatHome);
+        System.setProperty("USER_THERMOSTAT_HOME", prefix);
+        System.setProperty("THERMOSTAT_SYSTEM_USER", "");
+        Configuration config = new Configuration();
+
+        // the paths are unix specific, but so are the paths in Configuration
+
+        Assert.assertEquals(prefix + "/etc/thermostat/agent.properties", config.getUserAgentConfigurationFile().getCanonicalPath());
+        Assert.assertEquals(prefix + "/etc/thermostat/agent.auth", config.getUserAgentAuthConfigFile().getCanonicalPath());
+        Assert.assertEquals(prefix + "/etc/thermostat/db.properties", config.getUserStorageConfigurationFile().getCanonicalPath());
+
+        Assert.assertEquals(prefix + "/var/lib/thermostat/db", config.getUserStorageDirectory().getCanonicalPath());
+        Assert.assertEquals(prefix + "/var/run/thermostat/db.pid", config.getUserStoragePidFile().getAbsolutePath());
+        Assert.assertEquals(prefix + "/var/log/thermostat/db.log", config.getUserStorageLogFile().getCanonicalPath());
+    }
+    @Test
     public void instantiationThrowsException() {
         try {
             new Configuration();