diff --git a/scm-clients/scm-cli-client/src/main/java/sonia/scm/cli/config/ScmClientConfig.java b/scm-clients/scm-cli-client/src/main/java/sonia/scm/cli/config/ScmClientConfig.java index c49558b11a..63e861e209 100644 --- a/scm-clients/scm-cli-client/src/main/java/sonia/scm/cli/config/ScmClientConfig.java +++ b/scm-clients/scm-cli-client/src/main/java/sonia/scm/cli/config/ScmClientConfig.java @@ -38,8 +38,11 @@ package sonia.scm.cli.config; import java.util.HashMap; import java.util.Map; +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlTransient; import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; /** @@ -47,6 +50,7 @@ import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; * @author Sebastian Sdorra */ @XmlRootElement(name = "client-config") +@XmlAccessorType(XmlAccessType.FIELD) public class ScmClientConfig { @@ -62,7 +66,7 @@ public class ScmClientConfig * Constructs ... * */ - public ScmClientConfig() + private ScmClientConfig() { this.serverConfigMap = new HashMap(); } @@ -101,9 +105,17 @@ public class ScmClientConfig */ private static ScmClientConfig load() { + ScmClientConfigFileHandler fileHandler = new ScmClientConfigFileHandler(); + ScmClientConfig config = fileHandler.read(); - // TODO load config - return new ScmClientConfig(); + if (config == null) + { + config = new ScmClientConfig(); + } + + config.setFileHandler(fileHandler); + + return config; } /** @@ -112,8 +124,7 @@ public class ScmClientConfig */ public void store() { - - // TODO + fileHandler.write(this); } //~--- get methods ---------------------------------------------------------- @@ -133,6 +144,7 @@ public class ScmClientConfig if (config == null) { config = new ServerConfig(); + serverConfigMap.put(name, config); } return config; @@ -149,9 +161,27 @@ public class ScmClientConfig return getConfig(DEFAULT_NAME); } + //~--- set methods ---------------------------------------------------------- + + /** + * Method description + * + * + * @param fileHandler + */ + private void setFileHandler(ScmClientConfigFileHandler fileHandler) + { + this.fileHandler = fileHandler; + } + //~--- fields --------------------------------------------------------------- /** Field description */ + @XmlTransient + private ScmClientConfigFileHandler fileHandler; + + /** Field description */ + @XmlElement(name = "server-config") @XmlJavaTypeAdapter(XmlConfigAdapter.class) private Map serverConfigMap; } diff --git a/scm-clients/scm-cli-client/src/main/java/sonia/scm/cli/config/ScmClientConfigFileHandler.java b/scm-clients/scm-cli-client/src/main/java/sonia/scm/cli/config/ScmClientConfigFileHandler.java new file mode 100644 index 0000000000..d615a64b79 --- /dev/null +++ b/scm-clients/scm-cli-client/src/main/java/sonia/scm/cli/config/ScmClientConfigFileHandler.java @@ -0,0 +1,289 @@ +/** + * Copyright (c) 2010, Sebastian Sdorra + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of SCM-Manager; nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * http://bitbucket.org/sdorra/scm-manager + * + */ + + + +package sonia.scm.cli.config; + +//~--- non-JDK imports -------------------------------------------------------- + +import sonia.scm.util.IOUtil; +import sonia.scm.util.Util; + +//~--- JDK imports ------------------------------------------------------------ + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.io.OutputStream; + +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.spec.InvalidKeySpecException; + +import java.util.UUID; +import java.util.prefs.Preferences; + +import javax.crypto.Cipher; +import javax.crypto.CipherInputStream; +import javax.crypto.CipherOutputStream; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.SecretKey; +import javax.crypto.SecretKeyFactory; +import javax.crypto.spec.PBEKeySpec; +import javax.crypto.spec.PBEParameterSpec; + +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBException; +import javax.xml.bind.Marshaller; +import javax.xml.bind.Unmarshaller; + +/** + * + * @author Sebastian Sdorra + */ +public class ScmClientConfigFileHandler +{ + + /** Field description */ + public static final String DEFAULT_CONFIG_NAME = ".scm-cli-config.enc.xml"; + + /** Field description */ + public static final String ENV_CONFIG_FILE = "SCM_CLI_CONFIG"; + + /** Field description */ + public static final String PREF_SECRET_KEY = "scm.client.key"; + + /** Field description */ + public static final String SALT = "AE16347F"; + + /** Field description */ + public static final int SPEC_ITERATION = 12; + + /** Field description */ + private static final String CIPHER_NAME = "PBEWithMD5AndDES"; + + //~--- constructors --------------------------------------------------------- + + /** + * Constructs ... + * + */ + public ScmClientConfigFileHandler() + { + Preferences prefs = + Preferences.userNodeForPackage(ScmClientConfigFileHandler.class); + + key = prefs.get(PREF_SECRET_KEY, null); + + if (Util.isEmpty(key)) + { + key = createNewKey(); + prefs.put(PREF_SECRET_KEY, key); + } + + try + { + context = JAXBContext.newInstance(ScmClientConfig.class); + } + catch (JAXBException ex) + { + throw new ScmConfigException( + "could not create JAXBContext for ScmClientConfig", ex); + } + } + + //~--- methods -------------------------------------------------------------- + + /** + * Method description + * + * + * @return + */ + public ScmClientConfig read() + { + ScmClientConfig config = null; + File configFile = getConfigFile(); + + if (configFile.exists()) + { + InputStream input = null; + + try + { + Cipher c = createCipher(Cipher.DECRYPT_MODE); + + input = new CipherInputStream(new FileInputStream(configFile), c); + + Unmarshaller um = context.createUnmarshaller(); + + config = (ScmClientConfig) um.unmarshal(input); + } + catch (Exception ex) + { + throw new ScmConfigException("could not read config file", ex); + } + finally + { + IOUtil.close(input); + } + } + + return config; + } + + /** + * Method description + * + * + * @param config + */ + public void write(ScmClientConfig config) + { + File configFile = getConfigFile(); + OutputStream output = null; + + try + { + Cipher c = createCipher(Cipher.ENCRYPT_MODE); + + output = new CipherOutputStream(new FileOutputStream(configFile), c); + + Marshaller m = context.createMarshaller(); + + m.marshal(config, output); + } + catch (Exception ex) + { + throw new ScmConfigException("could not write config file", ex); + } + finally + { + IOUtil.close(output); + } + } + + /** + * Method description + * + * + * @param mode + * + * @return + * + * + * @throws InvalidAlgorithmParameterException + * @throws InvalidKeyException + * @throws InvalidKeySpecException + * @throws NoSuchAlgorithmException + * @throws NoSuchPaddingException + */ + private Cipher createCipher(int mode) + throws NoSuchAlgorithmException, NoSuchPaddingException, + InvalidKeySpecException, InvalidKeyException, + InvalidAlgorithmParameterException + { + SecretKey sk = createSecretKey(); + Cipher cipher = Cipher.getInstance(CIPHER_NAME); + PBEParameterSpec spec = new PBEParameterSpec(SALT.getBytes(), + SPEC_ITERATION); + + cipher.init(mode, sk, spec); + + return cipher; + } + + /** + * Method description + * + * + * @return + */ + private String createNewKey() + { + return UUID.randomUUID().toString(); + } + + /** + * Method description + * + * + * @return + * + * @throws InvalidKeySpecException + * @throws NoSuchAlgorithmException + */ + private SecretKey createSecretKey() + throws NoSuchAlgorithmException, InvalidKeySpecException + { + PBEKeySpec keySpec = new PBEKeySpec(key.toCharArray()); + SecretKeyFactory factory = SecretKeyFactory.getInstance(CIPHER_NAME); + + return factory.generateSecret(keySpec); + } + + //~--- get methods ---------------------------------------------------------- + + /** + * Method description + * + * + * @return + */ + private File getConfigFile() + { + File configFile = null; + String configPath = System.getenv(ENV_CONFIG_FILE); + + if (Util.isEmpty(configPath)) + { + configFile = new File(System.getProperty("user.home"), + DEFAULT_CONFIG_NAME); + } + else + { + configFile = new File(configPath); + } + + return configFile; + } + + //~--- fields --------------------------------------------------------------- + + /** Field description */ + private JAXBContext context; + + /** Field description */ + private String key; +} diff --git a/scm-clients/scm-cli-client/src/main/java/sonia/scm/cli/config/ScmConfigException.java b/scm-clients/scm-cli-client/src/main/java/sonia/scm/cli/config/ScmConfigException.java new file mode 100644 index 0000000000..33fbef093e --- /dev/null +++ b/scm-clients/scm-cli-client/src/main/java/sonia/scm/cli/config/ScmConfigException.java @@ -0,0 +1,90 @@ +/** + * Copyright (c) 2010, Sebastian Sdorra + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of SCM-Manager; nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * http://bitbucket.org/sdorra/scm-manager + * + */ + + + +package sonia.scm.cli.config; + +/** + * + * @author Sebastian Sdorra + */ +public class ScmConfigException extends RuntimeException +{ + + /** Field description */ + private static final long serialVersionUID = -4226165375815233654L; + + //~--- constructors --------------------------------------------------------- + + /** + * Constructs ... + * + */ + public ScmConfigException() + { + super(); + } + + /** + * Constructs ... + * + * + * @param message + */ + public ScmConfigException(String message) + { + super(message); + } + + /** + * Constructs ... + * + * + * @param cause + */ + public ScmConfigException(Throwable cause) + { + super(cause); + } + + /** + * Constructs ... + * + * + * @param message + * @param cause + */ + public ScmConfigException(String message, Throwable cause) + { + super(message, cause); + } +} diff --git a/scm-clients/scm-cli-client/src/main/java/sonia/scm/cli/config/XmlConfigAdapter.java b/scm-clients/scm-cli-client/src/main/java/sonia/scm/cli/config/XmlConfigAdapter.java index 16ee15ed3d..fcf109d879 100644 --- a/scm-clients/scm-cli-client/src/main/java/sonia/scm/cli/config/XmlConfigAdapter.java +++ b/scm-clients/scm-cli-client/src/main/java/sonia/scm/cli/config/XmlConfigAdapter.java @@ -47,7 +47,7 @@ import javax.xml.bind.annotation.adapters.XmlAdapter; * @author Sebastian Sdorra */ public class XmlConfigAdapter - extends XmlAdapter, Map> + extends XmlAdapter> { /** @@ -61,8 +61,7 @@ public class XmlConfigAdapter * @throws Exception */ @Override - public Set marshal(Map map) - throws Exception + public XmlConfigSet marshal(Map map) throws Exception { Set set = new HashSet(); @@ -71,7 +70,7 @@ public class XmlConfigAdapter set.add(new XmlConfigElement(e.getKey(), e.getValue())); } - return set; + return new XmlConfigSet(set); } /** @@ -85,8 +84,7 @@ public class XmlConfigAdapter * @throws Exception */ @Override - public Map unmarshal(Set set) - throws Exception + public Map unmarshal(XmlConfigSet set) throws Exception { Map map = new HashMap(); diff --git a/scm-clients/scm-cli-client/src/main/java/sonia/scm/cli/config/XmlConfigElement.java b/scm-clients/scm-cli-client/src/main/java/sonia/scm/cli/config/XmlConfigElement.java index 94c07fb3b7..f4e3eb2c04 100644 --- a/scm-clients/scm-cli-client/src/main/java/sonia/scm/cli/config/XmlConfigElement.java +++ b/scm-clients/scm-cli-client/src/main/java/sonia/scm/cli/config/XmlConfigElement.java @@ -38,11 +38,13 @@ package sonia.scm.cli.config; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; /** * * @author Sebastian Sdorra */ +@XmlRootElement(name = "server") @XmlAccessorType(XmlAccessType.FIELD) public class XmlConfigElement { diff --git a/scm-clients/scm-cli-client/src/main/java/sonia/scm/cli/config/XmlConfigSet.java b/scm-clients/scm-cli-client/src/main/java/sonia/scm/cli/config/XmlConfigSet.java new file mode 100644 index 0000000000..9bd3370775 --- /dev/null +++ b/scm-clients/scm-cli-client/src/main/java/sonia/scm/cli/config/XmlConfigSet.java @@ -0,0 +1,117 @@ +/** + * Copyright (c) 2010, Sebastian Sdorra + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of SCM-Manager; nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * http://bitbucket.org/sdorra/scm-manager + * + */ + + + +package sonia.scm.cli.config; + +//~--- JDK imports ------------------------------------------------------------ + +import java.util.Iterator; +import java.util.Set; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; + +/** + * + * @author Sebastian Sdorra + */ +@XmlRootElement(name = "server-config") +@XmlAccessorType(XmlAccessType.FIELD) +public class XmlConfigSet implements Iterable +{ + + /** + * Constructs ... + * + */ + public XmlConfigSet() {} + + /** + * Constructs ... + * + * + * @param configSet + */ + public XmlConfigSet(Set configSet) + { + this.configSet = configSet; + } + + //~--- methods -------------------------------------------------------------- + + /** + * Method description + * + * + * @return + */ + @Override + public Iterator iterator() + { + return configSet.iterator(); + } + + //~--- get methods ---------------------------------------------------------- + + /** + * Method description + * + * + * @return + */ + public Set getConfigSet() + { + return configSet; + } + + //~--- set methods ---------------------------------------------------------- + + /** + * Method description + * + * + * @param configSet + */ + public void setConfigSet(Set configSet) + { + this.configSet = configSet; + } + + //~--- fields --------------------------------------------------------------- + + /** Field description */ + @XmlElement(name = "server") + private Set configSet; +}