Package net.ex337.scriptus.config

Source Code of net.ex337.scriptus.config.ScriptusConfig$ScriptusConfigPropertySource

package net.ex337.scriptus.config;

import static net.ex337.scriptus.CryptUtils.hash;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.charset.Charset;
import java.security.Key;
import java.security.SecureRandom;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.TimeUnit;

import javax.annotation.PostConstruct;
import javax.crypto.spec.SecretKeySpec;

import net.ex337.scriptus.CryptUtils;
import net.ex337.scriptus.exceptions.ScriptusRuntimeException;

import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.PropertySource;

/**
* Acts as the interface to the configuration store.
*
* The configuration is loaded by default from ~/.scriptus/config.properties.
*
* If the system property scriptus.config is supplied, then this property is
* taken as a URL (file-system, relative or absolute, or HTTP etc.) from which
* to load the properties file.
*
* The configuration includes what datastore and transport to use.
*
* If no configuration file is found, we default to using the in-memory
* datastore and the command-line transport.
*
* The properties in the config file can, with one exception, be modified via
* the "/settings" page. The one exception is the boolean setting
* "disableOpenID", which has to be set manually by editing the file itself, for
* reasons of security. This setting is useful when debugging scripts offline,
* where openID authentication is impossible.
*
* @author ian
*
*/
public class ScriptusConfig implements ApplicationContextInitializer<ConfigurableApplicationContext> {

    private static final String DB_PARAMETERS = "dbParameters";
    private static final String DB_NAME = "dbName";
    private static final String DB_USERNAME = "dbUsername";
    private static final String DB_PASSWORD = "dbPassword";
    private static final String DB_PORT = "dbPort";
    private static final String DB_HOST = "dbHost";

    private static final String HASH_ALGO = "SHA-256";

    private static final String SYMETRIC_CIPHER = "AES";

    public static final String SCRIPTUS_CONFIG_SYSVAR = "scriptus.config";

    public static enum TransportType {
        Twitter(true), Personal(false), Dummy(false), Facebook(true),

        ;

        private boolean isPublic;

        private TransportType(boolean isPublic) {
            this.isPublic = isPublic;
        }

        public boolean isPublic() {
            return isPublic;
        }
    };

    public static enum DatastoreType {
        PostgreSQL, Embedded, Memory
    };

    public static final String DURATION_FORMAT = "([0-9]+)[\\ ,]*([smhdwMqyDC])";

    public static final String DATE_FORMAT = "yyyy-MM-dd HH:mm";

    public static final String CHARSET_STR = "UTF-8";

    public static final Charset CHARSET = Charset.forName(ScriptusConfig.CHARSET_STR);

    public static final String SCRIPTUS_DIR = System.getProperty("user.home") + "/.scriptus";

    private int DEFAULT_TIMEOUT_LENGTH = 24;
    private TimeUnit WAIT_SLEEP_UNIT = TimeUnit.HOURS;

    // also defines on what multiple of the time unit to execute, e.g.
    // 10minutes = execution at 00, 10, 20,
    private int SCHEDULER_POLL_INTERVAL = 1;
    private TimeUnit SCHEDULER_TIME_UNIT = TimeUnit.MINUTES;

    private String twitterConsumerKey = "";
    private String twitterConsumerSecret = "";

    private String facebookAppKey="";
    private String facebookAppSecret="";

    private TransportType transportType = TransportType.Dummy;

    private DatastoreType datastoreType = DatastoreType.Embedded;

    private byte[] salt;
    private transient Map<String, byte[]> keys = new HashMap<String, byte[]>();
    private transient String lastKey;
   
    private String dbHost, dbPort, dbUsername, dbPassword, dbName, dbParameters;

    public static boolean FORCE_CLEAN_INSTALL = false;

    /**
     * Set to true during init() iff no config file exists at specified (or
     * default) location.
     */
    private boolean cleanInstall;

    private boolean disableOpenID;

    private String defaultConfigLocation = SCRIPTUS_DIR + "/config.properties";;

    private String configLocation = defaultConfigLocation;

    @PostConstruct
    public void init() throws IOException {

        /*
         * is there a system property? if so use it is there a
         * ~/.scriptus/config.properties? if so use it use a default
         */
        File scriptusDir = new File(SCRIPTUS_DIR);

        // if( ! scriptusDir.exists() && ! scriptusDir.mkdir()) {
        // throw new IOException("Unable to create directory "+SCRIPTUS_DIR);
        // }

        Properties props = getProperties();
       
        if(props == null){
            cleanInstall = true;
        } else {
            load(props);
        }

        if (cleanInstall || FORCE_CLEAN_INSTALL) {

            //transportType = TransportType.CommandLine;

            //datastoreType = DatastoreType.Memory;

            SecureRandom r = new SecureRandom();

            salt = new byte[32];
            byte[] firstKey = new byte[32];

            r.nextBytes(salt);
            r.nextBytes(firstKey);

            lastKey = new SimpleDateFormat("yyyyMMddHHmm").format(new Date());

            keys.put(lastKey, firstKey);

        }

        /*
         * Try not to touch the local filesystem unless we know we're going to
         * need to.
         */
        // put DB initialisatoin here
        if (configLocation.equals(defaultConfigLocation)) {

            if (!scriptusDir.exists()) {
                scriptusDir.mkdir();
            }

        }

        /*
         * save config file with salt & key. But if we're forcing, we don't want
         * to save a new config file (every test-case)
         */
        if (cleanInstall && !FORCE_CLEAN_INSTALL) {
            save();
        }

    }

    private Properties getProperties() throws IOException {

        Properties props = new Properties();

        File localConfig;

        if (System.getProperty(SCRIPTUS_CONFIG_SYSVAR) != null) {

            configLocation = System.getProperty(SCRIPTUS_CONFIG_SYSVAR);

            /*
             * We need to figure out if we can load from this location, because
             * if we're running for the first time and the config file doesn't
             * exist then we might end up in a sticky situation.
             */
            InputStream configStream = null;

            try {

                configStream = new URL(configLocation).openStream();

            } catch (MalformedURLException mfe) {

                configStream = Thread.currentThread().getContextClassLoader().getResourceAsStream(configLocation);

            }

            if (configStream == null)
                try {
                    configStream = new FileInputStream(configLocation);
                } catch (FileNotFoundException fnfe) {
                    // do nothing...
                }

            boolean canLoadConfig = (configStream != null);

            if (canLoadConfig) {

                props.load(configStream);

            } else {

                return null;

            }

        } else if ((localConfig = new File(configLocation)).exists()) {

            InputStream fin = new FileInputStream(localConfig);
            props.load(fin);
            fin.close();

        } else {

            return null;

        }
       
        return props;

    }

    private void load(Properties props) {
        twitterConsumerKey = props.getProperty("twitterConsumerKey");
        twitterConsumerSecret = props.getProperty("twitterConsumerSecret");
        facebookAppKey = props.getProperty("facebookAppKey");
        facebookAppSecret = props.getProperty("facebookAppSecret");
        // twitterAccessToken = props.getProperty("twitterAccessToken");
        // twitterAccessTokenSecret =
        // props.getProperty("twitterAccessTokenSecret");
        datastoreType = DatastoreType.valueOf(props.getProperty("datastore"));
        transportType = TransportType.valueOf(props.getProperty("transport"));
        disableOpenID = Boolean.parseBoolean(props.getProperty("disableOpenID"));

        dbHost = props.getProperty(DB_HOST);
        dbPort = props.getProperty(DB_PORT);
        dbUsername = props.getProperty(DB_USERNAME);
        dbPassword = props.getProperty(DB_PASSWORD);
        dbName = props.getProperty(DB_NAME);
        dbParameters = props.getProperty(DB_PARAMETERS);
       
        String[] keyIdList = StringUtils.split(props.getProperty("transportKeys"), ",");

        if (keyIdList == null) {
            return;
        }

        for (String k : keyIdList) {
            this.keys.put(k, CryptUtils.fromHex(props.getProperty("transportKey." + k)));

            if (lastKey == null || k.compareTo(lastKey) > 0) {
                this.lastKey = k;
            }
        }

        String saltHex = props.getProperty("transportKeys.salt");

        if (saltHex == null) {
            return;
        }

        this.salt = CryptUtils.fromHex(saltHex);

    }

    public void save() throws IOException {

        Properties props = dumpConfigToProperties();

        /*
         * not written out automatically - (a) no option in GUI, - (b)
         * dangerous! so has to be done manually
         */
        // props.put("disableOpenID", Boolean.toString(disableOpenID));

        FileOutputStream fout = new FileOutputStream(new File(configLocation));

        props.store(fout, "Scriptus configuration file");

        fout.close();
    }

    public Properties dumpConfigToProperties() {

        Properties props = new Properties();

        props.put("twitterConsumerKey", twitterConsumerKey);
        props.put("twitterConsumerSecret", twitterConsumerSecret);
        props.put("facebookAppKey", facebookAppKey);
        props.put("facebookAppSecret", facebookAppSecret);
        props.put("transport", transportType.toString());
        props.put("datastore", datastoreType.toString());

        List<String> keys = new ArrayList<String>();

        props.put("transportKeys.salt", CryptUtils.toHex(this.salt));

        for (Map.Entry<String, byte[]> e : this.keys.entrySet()) {
            keys.add(e.getKey());
            props.put("transportKey." + e.getKey(), CryptUtils.toHex(e.getValue()));
        }

        props.put("transportKeys", StringUtils.join(keys, ','));

        return props;

    }

    public String getTwitterConsumerKey() {
        return twitterConsumerKey;
    }

    public void setTwitterConsumerKey(String twitterConsumerKey) {
        this.twitterConsumerKey = twitterConsumerKey;
    }

    public String getTwitterConsumerSecret() {
        return twitterConsumerSecret;
    }

    public void setTwitterConsumerSecret(String twitterConsumerSecret) {
        this.twitterConsumerSecret = twitterConsumerSecret;
    }

    public DatastoreType getDatastoreType() {
        return datastoreType;
    }

    public void setDatastoreType(DatastoreType datastoreType) {
        this.datastoreType = datastoreType;
    }

    public boolean isCleanInstall() {
        if (FORCE_CLEAN_INSTALL) {
            return true;
        }
        return cleanInstall;
    }

    public String getConfigLocation() {
        return configLocation;
    }

    public boolean getDisableOpenID() {
        return disableOpenID;
    }

    public void setDisableOpenID(boolean offlineMode) {
        this.disableOpenID = offlineMode;
    }

    public int getDefaultTimeoutLength() {
        return DEFAULT_TIMEOUT_LENGTH;
    }

    public int getSchedulerPollInterval() {
        return SCHEDULER_POLL_INTERVAL;
    }

    public TimeUnit getWaitSleepUnit() {
        return WAIT_SLEEP_UNIT;
    }

    public TimeUnit getSchedulerTimeUnit() {
        return SCHEDULER_TIME_UNIT;
    }

    public String decrypt(byte[] ciphertext, String keyId) {

        byte[] keymat = hash(HASH_ALGO, ArrayUtils.addAll(hash(HASH_ALGO, getKey(keyId)), getSalt()));

        Key key = new SecretKeySpec(keymat, 0, 16, SYMETRIC_CIPHER);

        return new String(CryptUtils.decrypt(SYMETRIC_CIPHER, ciphertext, key), CHARSET);
    }

    private byte[] getSalt() {
        return salt;
    }

    private byte[] getKey(String keyId) {
        byte[] key = keys.get(keyId);
        if (key == null) {
            throw new ScriptusRuntimeException("key with ID " + keyId + " not found");
        }
        return Arrays.copyOf(key, key.length);
    }

    public String getLatestKeyId() {
        return lastKey;
    }

    public byte[] encrypt(String plaintext, String keyId) {

        byte[] keymat = hash(HASH_ALGO, ArrayUtils.addAll(hash(HASH_ALGO, getKey(keyId)), getSalt()));

        Key key = new SecretKeySpec(keymat, 0, 16, SYMETRIC_CIPHER);

        return CryptUtils.encrypt(SYMETRIC_CIPHER, plaintext.getBytes(CHARSET), key);
    }

    @Override
    public void initialize(ConfigurableApplicationContext c) {
       
       
        /*
         * also load config here to configure bean container itself
         */
       
        Properties r;
        try {
            r = getProperties();
        } catch (IOException e) {
            throw new ScriptusRuntimeException(e);
        }
       
        if(r != null){
            load(r);
        }
       
        /*
        <property name="url" value="jdbc:postgresql://${dbServer}:${dbPort}/${dbName}?${dbParameters}"/>
        <property name="username" value="${dbUsername}"/>
        <property name="password" value="${dbUsername}"/>
         */

        c.getEnvironment().getPropertySources().addFirst(new ScriptusConfigPropertySource("Scriptus config", this));
    }
   
    public class ScriptusConfigPropertySource extends PropertySource<ScriptusConfig>{
       
        public ScriptusConfigPropertySource(String name, ScriptusConfig source) {
            super(name, source);
        }

        @Override
        public Object getProperty(String name) {
            if(DatastoreType.class.getSimpleName().equals(name)){
                return getDatastoreType().toString();
            }
            if(DB_HOST.equals(name)){
                return dbHost;
            } else if(DB_PORT.equals(name)){
                return dbPort;
            } else if(DB_PASSWORD.equals(name)){
                return dbPassword;
            } else if(DB_USERNAME.equals(name)){
                return dbUsername;
            } else if(DB_NAME.equals(name)){
                return dbName;
            } else if(DB_PARAMETERS.equals(name)){
                return dbParameters == null ? "" : dbParameters;
            }
           
            return null;
        }
       
    }

    public String getFacebookAppKey() {
        return facebookAppKey;
    }

    public void setFacebookAppKey(String facebookAppKey) {
        this.facebookAppKey = facebookAppKey;
    }

    public String getFacebookAppSecret() {
        return facebookAppSecret;
    }

    public void setFacebookAppSecret(String facebookAppSecret) {
        this.facebookAppSecret = facebookAppSecret;
    }

}
TOP

Related Classes of net.ex337.scriptus.config.ScriptusConfig$ScriptusConfigPropertySource

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.