From a55dd9873bf5e79873a839f11d9fced303ca727b Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Tue, 17 Apr 2018 22:00:54 +0200 Subject: [PATCH] #979 split implementation of ScmClientConfigFileHandler in order to create new more secure implementation --- .../scm/cli/config/CipherStreamHandler.java | 60 +++++ .../java/sonia/scm/cli/config/KeyStore.java | 57 ++++ .../sonia/scm/cli/config/PrefsKeyStore.java | 64 +++++ .../sonia/scm/cli/config/ScmClientConfig.java | 2 +- .../config/ScmClientConfigFileHandler.java | 246 ++++-------------- .../cli/config/WeakCipherStreamHandler.java | 109 ++++++++ .../ScmClientConfigFileHandlerTest.java | 98 +++++++ 7 files changed, 439 insertions(+), 197 deletions(-) create mode 100644 scm-clients/scm-cli-client/src/main/java/sonia/scm/cli/config/CipherStreamHandler.java create mode 100644 scm-clients/scm-cli-client/src/main/java/sonia/scm/cli/config/KeyStore.java create mode 100644 scm-clients/scm-cli-client/src/main/java/sonia/scm/cli/config/PrefsKeyStore.java create mode 100644 scm-clients/scm-cli-client/src/main/java/sonia/scm/cli/config/WeakCipherStreamHandler.java create mode 100644 scm-clients/scm-cli-client/src/test/java/sonia/scm/cli/config/ScmClientConfigFileHandlerTest.java diff --git a/scm-clients/scm-cli-client/src/main/java/sonia/scm/cli/config/CipherStreamHandler.java b/scm-clients/scm-cli-client/src/main/java/sonia/scm/cli/config/CipherStreamHandler.java new file mode 100644 index 0000000000..e687988be3 --- /dev/null +++ b/scm-clients/scm-cli-client/src/main/java/sonia/scm/cli/config/CipherStreamHandler.java @@ -0,0 +1,60 @@ +/** + * 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; + +import java.io.InputStream; +import java.io.OutputStream; + +/** + * The CipherStreamHandler is able to encrypt and decrypt streams. + */ +public interface CipherStreamHandler { + + /** + * Decrypts the given input stream. + * + * @param inputStream encrypted input stream + * + * @return raw input stream + */ + InputStream decrypt(InputStream inputStream); + + /** + * Encrypts the given output stream. + * + * @param outputStream raw output stream + * + * @return encrypting output stream + */ + OutputStream encrypt(OutputStream outputStream); +} diff --git a/scm-clients/scm-cli-client/src/main/java/sonia/scm/cli/config/KeyStore.java b/scm-clients/scm-cli-client/src/main/java/sonia/scm/cli/config/KeyStore.java new file mode 100644 index 0000000000..39874b499c --- /dev/null +++ b/scm-clients/scm-cli-client/src/main/java/sonia/scm/cli/config/KeyStore.java @@ -0,0 +1,57 @@ +/** + * 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; + +/** + * KeyStore is able to read and write keys. + */ +public interface KeyStore { + + /** + * Writes the given secret key to the store. + * + * @param secretKey secret key to write + */ + void set(String secretKey); + + /** + * Reads the secret key from the store. The method returns {@code null} if no secret key was stored. + * + * @return secret key of {@code null} + */ + String get(); + + /** + * Removes the secret key from store. + */ + void remove(); +} diff --git a/scm-clients/scm-cli-client/src/main/java/sonia/scm/cli/config/PrefsKeyStore.java b/scm-clients/scm-cli-client/src/main/java/sonia/scm/cli/config/PrefsKeyStore.java new file mode 100644 index 0000000000..26a7e67b05 --- /dev/null +++ b/scm-clients/scm-cli-client/src/main/java/sonia/scm/cli/config/PrefsKeyStore.java @@ -0,0 +1,64 @@ +/** + * 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; + +import java.util.prefs.Preferences; + +/** + * KeyStore implementation with uses {@link Preferences}. + */ +public class PrefsKeyStore implements KeyStore { + + private static final String PREF_SECRET_KEY = "scm.client.key"; + + private final Preferences preferences; + + public PrefsKeyStore() { + // we use ScmClientConfigFileHandler as base for backward compatibility + preferences = Preferences.userNodeForPackage(ScmClientConfigFileHandler.class); + } + + @Override + public void set(String secretKey) { + preferences.put(PREF_SECRET_KEY, secretKey); + } + + @Override + public String get() { + return preferences.get(PREF_SECRET_KEY, null); + } + + @Override + public void remove() { + preferences.remove(PREF_SECRET_KEY); + } +} 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 9edb2b382a..7bec710974 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 @@ -66,7 +66,7 @@ public class ScmClientConfig * Constructs ... * */ - private ScmClientConfig() + ScmClientConfig() { this.serverConfigMap = new HashMap(); } 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 index 5cc2c46978..bbb0fd85c1 100644 --- 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 @@ -35,38 +35,17 @@ package sonia.scm.cli.config; //~--- non-JDK imports -------------------------------------------------------- -import sonia.scm.util.IOUtil; +import sonia.scm.security.KeyGenerator; +import sonia.scm.security.UUIDKeyGenerator; 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; +import java.io.*; + +//~--- JDK imports ------------------------------------------------------------ /** * @@ -81,62 +60,66 @@ public class ScmClientConfigFileHandler /** 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 --------------------------------------------------------- + private final KeyStore keyStore; + private final KeyGenerator keyGenerator; + private final File file; + private final JAXBContext context; + + private final CipherStreamHandler cipherStreamHandler; + + /** * Constructs ... * */ - public ScmClientConfigFileHandler() - { - prefs = Preferences.userNodeForPackage(ScmClientConfigFileHandler.class); - key = prefs.get(PREF_SECRET_KEY, null); + public ScmClientConfigFileHandler() { + this(new PrefsKeyStore(), new UUIDKeyGenerator(), getDefaultConfigFile()); + } - if (Util.isEmpty(key)) - { - key = createNewKey(); - prefs.put(PREF_SECRET_KEY, key); + ScmClientConfigFileHandler(KeyStore keyStore, KeyGenerator keyGenerator, File file) { + this.keyStore = keyStore; + this.keyGenerator = keyGenerator; + this.file = file; + + String key = keyStore.get(); + + if (Util.isEmpty(key)) { + key = keyGenerator.createKey(); + keyStore.set(key); } - try - { + cipherStreamHandler = new WeakCipherStreamHandler(key.toCharArray()); + + try { context = JAXBContext.newInstance(ScmClientConfig.class); - } - catch (JAXBException ex) - { - throw new ScmConfigException( - "could not create JAXBContext for ScmClientConfig", ex); + } catch (JAXBException ex) { + throw new ScmConfigException("could not create JAXBContext for ScmClientConfig", ex); } } //~--- methods -------------------------------------------------------------- + private static File getDefaultConfigFile() { + String configPath = System.getenv(ENV_CONFIG_FILE); + + if (Util.isNotEmpty(configPath)){ + return new File(configPath); + } + return new File(System.getProperty("user.home"), DEFAULT_CONFIG_NAME); + } + /** * Method description * */ - public void delete() - { - File configFile = getConfigFile(); - - if (configFile.exists() &&!configFile.delete()) - { + public void delete() { + if (file.exists() &&!file.delete()) { throw new ScmConfigException("could not delete config file"); } - prefs.remove(PREF_SECRET_KEY); + keyStore.remove(); } /** @@ -145,33 +128,16 @@ public class ScmClientConfigFileHandler * * @return */ - public ScmClientConfig read() - { + 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); + if (file.exists()) { + try (InputStream input = cipherStreamHandler.decrypt(new FileInputStream(file))) { Unmarshaller um = context.createUnmarshaller(); - config = (ScmClientConfig) um.unmarshal(input); - } - catch (Exception ex) - { + } catch (IOException | JAXBException ex) { throw new ScmConfigException("could not read config file", ex); } - finally - { - IOUtil.close(input); - } } return config; @@ -183,124 +149,12 @@ public class ScmClientConfigFileHandler * * @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); - + public void write(ScmClientConfig config) { + try (OutputStream output = cipherStreamHandler.encrypt(new FileOutputStream(file))) { Marshaller m = context.createMarshaller(); - m.marshal(config, output); - } - catch (Exception ex) - { + } catch (IOException | JAXBException 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; - - /** Field description */ - private Preferences prefs; } diff --git a/scm-clients/scm-cli-client/src/main/java/sonia/scm/cli/config/WeakCipherStreamHandler.java b/scm-clients/scm-cli-client/src/main/java/sonia/scm/cli/config/WeakCipherStreamHandler.java new file mode 100644 index 0000000000..0a867d5b30 --- /dev/null +++ b/scm-clients/scm-cli-client/src/main/java/sonia/scm/cli/config/WeakCipherStreamHandler.java @@ -0,0 +1,109 @@ +/** + * 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; + +import javax.crypto.*; +import javax.crypto.spec.PBEKeySpec; +import javax.crypto.spec.PBEParameterSpec; +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; + +/** + * Weak implementation of {@link CipherStreamHandler}. + * + * @see Issue 978 + * @see Issue 979 + */ +public class WeakCipherStreamHandler implements CipherStreamHandler { + + private static final String SALT = "AE16347F"; + private static final int SPEC_ITERATION = 12; + private static final String CIPHER_NAME = "PBEWithMD5AndDES"; + + private final char[] secretKey; + + /** + * Creates a new handler with the given secret key. + * + * @param secretKey secret key + */ + public WeakCipherStreamHandler(char[] secretKey) { + this.secretKey = secretKey; + } + + @Override + public InputStream decrypt(InputStream inputStream) { + try { + Cipher c = createCipher(Cipher.DECRYPT_MODE); + return new CipherInputStream(inputStream, c); + } catch (Exception ex) { + throw new ScmConfigException("could not encrypt output stream", ex); + } + } + + @Override + public OutputStream encrypt(OutputStream outputStream) { + try { + Cipher c = createCipher(Cipher.ENCRYPT_MODE); + return new CipherOutputStream(outputStream, c); + } catch (Exception ex) { + throw new ScmConfigException("could not encrypt output stream", ex); + } + } + + 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; + } + + private SecretKey createSecretKey() + throws NoSuchAlgorithmException, InvalidKeySpecException + { + PBEKeySpec keySpec = new PBEKeySpec(secretKey); + SecretKeyFactory factory = SecretKeyFactory.getInstance(CIPHER_NAME); + + return factory.generateSecret(keySpec); + } +} diff --git a/scm-clients/scm-cli-client/src/test/java/sonia/scm/cli/config/ScmClientConfigFileHandlerTest.java b/scm-clients/scm-cli-client/src/test/java/sonia/scm/cli/config/ScmClientConfigFileHandlerTest.java new file mode 100644 index 0000000000..ec598fd2d4 --- /dev/null +++ b/scm-clients/scm-cli-client/src/test/java/sonia/scm/cli/config/ScmClientConfigFileHandlerTest.java @@ -0,0 +1,98 @@ +/** + * 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; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import sonia.scm.security.UUIDKeyGenerator; + +import java.io.File; +import java.io.IOException; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class ScmClientConfigFileHandlerTest { + + @Rule + public TemporaryFolder temporaryFolder = new TemporaryFolder(); + + @Test + public void testClientConfigFileHandler() throws IOException { + File configFile = temporaryFolder.newFile(); + + ScmClientConfigFileHandler handler = new ScmClientConfigFileHandler( + new InMemoryKeyStore(), new UUIDKeyGenerator(), configFile + ); + + ScmClientConfig config = new ScmClientConfig(); + ServerConfig defaultConfig = config.getDefaultConfig(); + defaultConfig.setServerUrl("http://localhost:8080/scm"); + defaultConfig.setUsername("scmadmin"); + defaultConfig.setPassword("admin123"); + handler.write(config); + + assertTrue(configFile.exists()); + + config = handler.read(); + defaultConfig = config.getDefaultConfig(); + assertEquals("http://localhost:8080/scm", defaultConfig.getServerUrl()); + assertEquals("scmadmin", defaultConfig.getUsername()); + assertEquals("admin123", defaultConfig.getPassword()); + + handler.delete(); + + assertFalse(configFile.exists()); + } + + private static class InMemoryKeyStore implements KeyStore { + + private String secretKey; + + @Override + public void set(String secretKey) { + this.secretKey = secretKey; + } + + @Override + public String get() { + return secretKey; + } + + @Override + public void remove() { + this.secretKey = null; + } + } +}