diff options
author | Julien Lancelot <julien.lancelot@sonarsource.com> | 2014-04-18 13:27:05 +0200 |
---|---|---|
committer | Julien Lancelot <julien.lancelot@sonarsource.com> | 2014-04-18 13:27:05 +0200 |
commit | c41842fc961af612d03ab6f21d47fd05c2c070d8 (patch) | |
tree | 56b50a4f62508f7653ba5180c73a1f8ca79f5434 /sonar-application | |
parent | b6083abec8c95e5bb7c68fb83d09fe40d154e23d (diff) | |
parent | eebc6d31b2750c1aae890e716c2c37854543980f (diff) | |
download | sonarqube-c41842fc961af612d03ab6f21d47fd05c2c070d8.tar.gz sonarqube-c41842fc961af612d03ab6f21d47fd05c2c070d8.zip |
Merge branch medium-tests
Diffstat (limited to 'sonar-application')
20 files changed, 692 insertions, 48 deletions
diff --git a/sonar-application/assembly.xml b/sonar-application/assembly.xml index d99cf5fedb4..8010c915144 100644 --- a/sonar-application/assembly.xml +++ b/sonar-application/assembly.xml @@ -13,13 +13,20 @@ <excludes> <exclude>org.codehaus.sonar:sonar-server</exclude> <exclude>mysql:mysql-connector-java</exclude> - <exclude>com.h2database:h2</exclude> <exclude>postgresql:postgresql</exclude> <exclude>net.sourceforge.jtds:jtds</exclude> <exclude>org.codehaus.sonar.plugins:*</exclude> <exclude>org.codehaus.sonar-plugins.*:*</exclude> + <exclude>org.codehaus.sonar:sonar-batch-maven-compat</exclude> </excludes> </dependencySet> + <dependencySet> + <outputDirectory>lib/batch</outputDirectory> + <useTransitiveDependencies>false</useTransitiveDependencies> + <includes> + <include>org.codehaus.sonar:sonar-batch-maven-compat</include> + </includes> + </dependencySet> <!-- Plugins --> <dependencySet> <outputDirectory>lib/core-plugins</outputDirectory> @@ -53,14 +60,6 @@ <scope>runtime</scope> </dependencySet> <dependencySet> - <outputDirectory>extensions/jdbc-driver/h2/</outputDirectory> - <includes> - <include>com.h2database:h2</include> - </includes> - <unpack>false</unpack> - <scope>runtime</scope> - </dependencySet> - <dependencySet> <outputDirectory>extensions/jdbc-driver/postgresql/</outputDirectory> <includes> <include>postgresql:postgresql</include> diff --git a/sonar-application/pom.xml b/sonar-application/pom.xml index 3bceb25828e..83058a4ead5 100644 --- a/sonar-application/pom.xml +++ b/sonar-application/pom.xml @@ -17,15 +17,27 @@ <dependencies> <dependency> + <groupId>commons-codec</groupId> + <artifactId>commons-codec</artifactId> + </dependency> + <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> </dependency> <dependency> + <groupId>commons-lang</groupId> + <artifactId>commons-lang</artifactId> + </dependency> + <dependency> <groupId>com.google.code.findbugs</groupId> <artifactId>jsr305</artifactId> <scope>provided</scope> </dependency> <dependency> + <groupId>com.google.guava</groupId> + <artifactId>guava</artifactId> + </dependency> + <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> </dependency> @@ -46,6 +58,12 @@ <artifactId>logback-core</artifactId> </dependency> <dependency> + <groupId>${pom.groupId}</groupId> + <artifactId>sonar-batch-maven-compat</artifactId> + <version>${pom.version}</version> + <scope>runtime</scope> + </dependency> + <dependency> <groupId>org.apache.tomcat.embed</groupId> <artifactId>tomcat-embed-core</artifactId> </dependency> @@ -75,11 +93,6 @@ <scope>runtime</scope> </dependency> <dependency> - <groupId>com.h2database</groupId> - <artifactId>h2</artifactId> - <scope>runtime</scope> - </dependency> - <dependency> <groupId>net.sourceforge.jtds</groupId> <artifactId>jtds</artifactId> <scope>runtime</scope> @@ -134,13 +147,6 @@ </dependency> <dependency> <groupId>org.codehaus.sonar.plugins</groupId> - <artifactId>sonar-l10n-en-plugin</artifactId> - <version>${project.version}</version> - <type>sonar-plugin</type> - <scope>runtime</scope> - </dependency> - <dependency> - <groupId>org.codehaus.sonar.plugins</groupId> <artifactId>sonar-email-notifications-plugin</artifactId> <version>${project.version}</version> <type>sonar-plugin</type> @@ -159,13 +165,6 @@ <scope>runtime</scope> </dependency> <dependency> - <groupId>org.codehaus.sonar.plugins</groupId> - <artifactId>sonar-maven-batch-plugin</artifactId> - <version>${project.version}</version> - <type>sonar-plugin</type> - <scope>runtime</scope> - </dependency> - <dependency> <groupId>org.sonatype.jsw-binaries</groupId> <artifactId>jsw-binaries</artifactId> <version>3.2.3.6</version> @@ -201,12 +200,6 @@ <artifactId>http-request</artifactId> <scope>test</scope> </dependency> - <dependency> - <groupId>com.google.guava</groupId> - <artifactId>guava</artifactId> - <scope>test</scope> - </dependency> - </dependencies> <build> @@ -259,8 +252,8 @@ <configuration> <rules> <requireFilesSize> - <minsize>55000000</minsize> - <maxsize>75000000</maxsize> + <minsize>80000000</minsize> + <maxsize>88000000</maxsize> <files> <file>${project.build.directory}/sonarqube-${project.version}.zip</file> </files> diff --git a/sonar-application/src/main/assembly/conf/sonar.properties b/sonar-application/src/main/assembly/conf/sonar.properties index 6c058aa3227..65954622bb9 100644 --- a/sonar-application/src/main/assembly/conf/sonar.properties +++ b/sonar-application/src/main/assembly/conf/sonar.properties @@ -82,7 +82,7 @@ sonar.jdbc.timeBetweenEvictionRunsMillis=30000 # Web context. When set, it must start with forward slash (for example /sonarqube). # The default value is root context (empty value). -#sonar.web.context= +#sonar.web.context=/ # TCP port for incoming HTTP connections. Disabled when value is -1. #sonar.web.port=9000 diff --git a/sonar-application/src/main/assembly/conf/wrapper.conf b/sonar-application/src/main/assembly/conf/wrapper.conf index 9353b41cd83..d53aa251978 100644 --- a/sonar-application/src/main/assembly/conf/wrapper.conf +++ b/sonar-application/src/main/assembly/conf/wrapper.conf @@ -33,11 +33,10 @@ wrapper.java.mainclass=org.tanukisoftware.wrapper.WrapperSimpleApp # needed starting from 1 wrapper.java.classpath.1=../../lib/*.jar wrapper.java.classpath.2=../../conf -wrapper.java.classpath.3=../../extensions/jdbc-driver/h2/*.jar -wrapper.java.classpath.4=../../extensions/jdbc-driver/mysql/*.jar -wrapper.java.classpath.5=../../extensions/jdbc-driver/oracle/*.jar -wrapper.java.classpath.6=../../extensions/jdbc-driver/postgresql/*.jar -wrapper.java.classpath.7=../../extensions/jdbc-driver/mssql/*.jar +wrapper.java.classpath.3=../../extensions/jdbc-driver/mysql/*.jar +wrapper.java.classpath.4=../../extensions/jdbc-driver/oracle/*.jar +wrapper.java.classpath.5=../../extensions/jdbc-driver/postgresql/*.jar +wrapper.java.classpath.6=../../extensions/jdbc-driver/mssql/*.jar # Java Library Path (location of Wrapper.DLL or libwrapper.so) wrapper.java.library.path.1=./lib diff --git a/sonar-application/src/main/java/org/sonar/application/AesCipher.java b/sonar-application/src/main/java/org/sonar/application/AesCipher.java new file mode 100644 index 00000000000..e778b0ebc18 --- /dev/null +++ b/sonar-application/src/main/java/org/sonar/application/AesCipher.java @@ -0,0 +1,138 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package org.sonar.application; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Throwables; +import org.apache.commons.codec.binary.Base64; +import org.apache.commons.io.FileUtils; +import org.apache.commons.lang.StringUtils; + +import javax.annotation.Nullable; +import javax.crypto.KeyGenerator; +import javax.crypto.SecretKey; +import javax.crypto.spec.SecretKeySpec; + +import java.io.File; +import java.io.IOException; +import java.security.Key; +import java.security.SecureRandom; + +final class AesCipher extends Cipher { + + // Can't be increased because of Java 6 policy files : + // https://confluence.terena.org/display/~visser/No+256+bit+ciphers+for+Java+apps + // http://java.sun.com/javase/6/webnotes/install/jre/README + public static final int KEY_SIZE_IN_BITS = 128; + + private static final String CRYPTO_KEY = "AES"; + + /** + * Duplication from CoreProperties.ENCRYPTION_SECRET_KEY_PATH + */ + static final String ENCRYPTION_SECRET_KEY_PATH = "sonar.secretKeyPath"; + + private String pathToSecretKey; + + AesCipher(@Nullable String pathToSecretKey) { + this.pathToSecretKey = pathToSecretKey; + } + + @Override + String encrypt(String clearText) { + try { + javax.crypto.Cipher cipher = javax.crypto.Cipher.getInstance(CRYPTO_KEY); + cipher.init(javax.crypto.Cipher.ENCRYPT_MODE, loadSecretFile()); + return new String(Base64.encodeBase64(cipher.doFinal(clearText.getBytes("UTF-8")))); + } catch (Exception e) { + throw Throwables.propagate(e); + } + } + + @Override + String decrypt(String encryptedText) { + try { + javax.crypto.Cipher cipher = javax.crypto.Cipher.getInstance(CRYPTO_KEY); + cipher.init(javax.crypto.Cipher.DECRYPT_MODE, loadSecretFile()); + byte[] cipherData = cipher.doFinal(Base64.decodeBase64(StringUtils.trim(encryptedText))); + return new String(cipherData); + } catch (Exception e) { + throw Throwables.propagate(e); + } + } + + /** + * This method checks the existence of the file, but not the validity of the contained key. + */ + boolean hasSecretKey() { + String path = getPathToSecretKey(); + if (StringUtils.isNotBlank(path)) { + File file = new File(path); + return file.exists() && file.isFile(); + } + return false; + } + + private Key loadSecretFile() throws IOException { + String path = getPathToSecretKey(); + return loadSecretFileFromFile(path); + } + + @VisibleForTesting + Key loadSecretFileFromFile(@Nullable String path) throws IOException { + if (StringUtils.isBlank(path)) { + throw new IllegalStateException("Secret key not found. Please set the property " + ENCRYPTION_SECRET_KEY_PATH); + } + File file = new File(path); + if (!file.exists() || !file.isFile()) { + throw new IllegalStateException("The property " + ENCRYPTION_SECRET_KEY_PATH + " does not link to a valid file: " + path); + } + String s = FileUtils.readFileToString(file); + if (StringUtils.isBlank(s)) { + throw new IllegalStateException("No secret key in the file: " + path); + } + return new SecretKeySpec(Base64.decodeBase64(StringUtils.trim(s)), CRYPTO_KEY); + } + + String generateRandomSecretKey() { + try { + KeyGenerator keyGen = KeyGenerator.getInstance(CRYPTO_KEY); + keyGen.init(KEY_SIZE_IN_BITS, new SecureRandom()); + SecretKey secretKey = keyGen.generateKey(); + return new String(Base64.encodeBase64(secretKey.getEncoded())); + + } catch (Exception e) { + throw new IllegalStateException("Fail to generate secret key", e); + } + } + + @VisibleForTesting + String getPathToSecretKey() { + if (StringUtils.isBlank(pathToSecretKey)) { + pathToSecretKey = new File(FileUtils.getUserDirectoryPath(), ".sonar/sonar-secret.txt").getPath(); + } + return pathToSecretKey; + } + + public void setPathToSecretKey(@Nullable String pathToSecretKey) { + this.pathToSecretKey = pathToSecretKey; + } +} diff --git a/sonar-application/src/main/java/org/sonar/application/Base64Cipher.java b/sonar-application/src/main/java/org/sonar/application/Base64Cipher.java new file mode 100644 index 00000000000..5abbeb85ac2 --- /dev/null +++ b/sonar-application/src/main/java/org/sonar/application/Base64Cipher.java @@ -0,0 +1,35 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package org.sonar.application; + +import org.apache.commons.codec.binary.Base64; + +final class Base64Cipher extends Cipher { + @Override + String encrypt(String clearText) { + return new String(Base64.encodeBase64(clearText.getBytes())); + } + + @Override + String decrypt(String encryptedText) { + return new String(Base64.decodeBase64(encryptedText)); + } +} diff --git a/sonar-application/src/main/java/org/sonar/application/Cipher.java b/sonar-application/src/main/java/org/sonar/application/Cipher.java new file mode 100644 index 00000000000..44abfbb3176 --- /dev/null +++ b/sonar-application/src/main/java/org/sonar/application/Cipher.java @@ -0,0 +1,26 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package org.sonar.application; + +abstract class Cipher { + abstract String encrypt(String clearText); + abstract String decrypt(String encryptedText); +} diff --git a/sonar-application/src/main/java/org/sonar/application/ConfigurationUtils.java b/sonar-application/src/main/java/org/sonar/application/ConfigurationUtils.java new file mode 100644 index 00000000000..a5a563a168c --- /dev/null +++ b/sonar-application/src/main/java/org/sonar/application/ConfigurationUtils.java @@ -0,0 +1,49 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.application; + +import org.apache.commons.lang.text.StrSubstitutor; + +import java.util.Enumeration; +import java.util.Map; +import java.util.Properties; + +public final class ConfigurationUtils { + + private ConfigurationUtils() { + // Utility class + } + + static Properties interpolateEnvVariables(Properties properties) { + return interpolateVariables(properties, System.getenv()); + } + + static Properties interpolateVariables(Properties properties, Map<String, String> variables) { + Properties result = new Properties(); + Enumeration keys = properties.keys(); + while (keys.hasMoreElements()) { + String key = (String) keys.nextElement(); + String value = (String) properties.get(key); + String interpolatedValue = StrSubstitutor.replace(value, variables, "${env:", "}"); + result.setProperty(key, interpolatedValue); + } + return result; + } +} diff --git a/sonar-application/src/main/java/org/sonar/application/EmbeddedTomcat.java b/sonar-application/src/main/java/org/sonar/application/EmbeddedTomcat.java index ddb1efad898..a677f8bcf11 100644 --- a/sonar-application/src/main/java/org/sonar/application/EmbeddedTomcat.java +++ b/sonar-application/src/main/java/org/sonar/application/EmbeddedTomcat.java @@ -65,6 +65,7 @@ class EmbeddedTomcat { tomcat.getHost().setDeployOnStartup(true); Props props = Props.create(env); + Logging.configure(tomcat, env, props); Connectors.configure(tomcat, props); Webapp.configure(tomcat, env, props); diff --git a/sonar-application/src/main/java/org/sonar/application/Encryption.java b/sonar-application/src/main/java/org/sonar/application/Encryption.java new file mode 100644 index 00000000000..60e732fc716 --- /dev/null +++ b/sonar-application/src/main/java/org/sonar/application/Encryption.java @@ -0,0 +1,66 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package org.sonar.application; + +import com.google.common.collect.ImmutableMap; + +import javax.annotation.Nullable; + +import java.util.Locale; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * @since 3.0 + */ +public final class Encryption { + + private static final String BASE64_ALGORITHM = "b64"; + + private static final String AES_ALGORITHM = "aes"; + private final AesCipher aesCipher; + + private final Map<String, Cipher> ciphers; + private static final Pattern ENCRYPTED_PATTERN = Pattern.compile("\\{(.*?)\\}(.*)"); + + public Encryption(@Nullable String pathToSecretKey) { + aesCipher = new AesCipher(pathToSecretKey); + ciphers = ImmutableMap.of( + BASE64_ALGORITHM, new Base64Cipher(), + AES_ALGORITHM, aesCipher); + } + public boolean isEncrypted(String value) { + return value.indexOf('{') == 0 && value.indexOf('}') > 1; + } + + public String decrypt(String encryptedText) { + Matcher matcher = ENCRYPTED_PATTERN.matcher(encryptedText); + if (matcher.matches()) { + Cipher cipher = ciphers.get(matcher.group(1).toLowerCase(Locale.ENGLISH)); + if (cipher != null) { + return cipher.decrypt(matcher.group(2)); + } + } + return encryptedText; + } + +} diff --git a/sonar-application/src/main/java/org/sonar/application/Props.java b/sonar-application/src/main/java/org/sonar/application/Props.java index 7b33d918cc6..dbe24636d0e 100644 --- a/sonar-application/src/main/java/org/sonar/application/Props.java +++ b/sonar-application/src/main/java/org/sonar/application/Props.java @@ -22,16 +22,14 @@ package org.sonar.application; import org.apache.commons.io.IOUtils; import javax.annotation.Nullable; + import java.io.File; -import java.io.FileNotFoundException; import java.io.FileReader; -import java.io.IOException; +import java.util.Map; import java.util.Properties; -/** - * TODO support env substitution and encryption - */ class Props { + private final Properties props; Props(Properties props) { @@ -80,8 +78,18 @@ class Props { FileReader reader = null; try { reader = new FileReader(propsFile); + + // order is important : the last override the first p.load(reader); + p.putAll(System.getenv()); p.putAll(System.getProperties()); + + p = ConfigurationUtils.interpolateEnvVariables(p); + p = decrypt(p); + + // Set all properties as system properties to pass them to PlatformServletContextListener + System.setProperties(p); + return new Props(p); } catch (Exception e) { @@ -91,4 +99,19 @@ class Props { IOUtils.closeQuietly(reader); } } + + static Properties decrypt(Properties properties) { + Encryption encryption = new Encryption(properties.getProperty(AesCipher.ENCRYPTION_SECRET_KEY_PATH)); + Properties result = new Properties(); + + for (Map.Entry<Object, Object> entry : properties.entrySet()) { + String key = (String) entry.getKey(); + String value = (String) entry.getValue(); + if (encryption.isEncrypted(value)) { + value = encryption.decrypt(value); + } + result.setProperty(key, value); + } + return result; + } } diff --git a/sonar-application/src/test/java/org/sonar/application/AesCipherTest.java b/sonar-application/src/test/java/org/sonar/application/AesCipherTest.java new file mode 100644 index 00000000000..9f097093105 --- /dev/null +++ b/sonar-application/src/test/java/org/sonar/application/AesCipherTest.java @@ -0,0 +1,186 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package org.sonar.application; + +import com.google.common.io.Resources; +import org.apache.commons.codec.binary.Base64; +import org.apache.commons.lang.StringUtils; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +import javax.crypto.BadPaddingException; + +import java.io.File; +import java.security.InvalidKeyException; +import java.security.Key; + +import static org.fest.assertions.Assertions.assertThat; +import static org.fest.assertions.Fail.fail; + + +public class AesCipherTest { + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + @Test + public void generateRandomSecretKey() { + AesCipher cipher = new AesCipher(null); + + String key = cipher.generateRandomSecretKey(); + + assertThat(StringUtils.isNotBlank(key)).isTrue(); + assertThat(Base64.isArrayByteBase64(key.getBytes())).isTrue(); + } + + @Test + public void encrypt() throws Exception { + AesCipher cipher = new AesCipher(pathToSecretKey()); + + String encryptedText = cipher.encrypt("this is a secret"); + + assertThat(StringUtils.isNotBlank(encryptedText)).isTrue(); + assertThat(Base64.isArrayByteBase64(encryptedText.getBytes())).isTrue(); + } + + @Test + public void encrypt_bad_key() throws Exception { + thrown.expect(RuntimeException.class); + thrown.expectMessage("Invalid AES key"); + + AesCipher cipher = new AesCipher(getPath("bad_secret_key.txt")); + + cipher.encrypt("this is a secret"); + } + + @Test + public void decrypt() throws Exception { + AesCipher cipher = new AesCipher(pathToSecretKey()); + + // the following value has been encrypted with the key /org/sonar/api/config/AesCipherTest/aes_secret_key.txt + String clearText = cipher.decrypt("9mx5Zq4JVyjeChTcVjEide4kWCwusFl7P2dSVXtg9IY="); + + assertThat(clearText).isEqualTo("this is a secret"); + } + + @Test + public void decrypt_bad_key() throws Exception { + AesCipher cipher = new AesCipher(getPath("bad_secret_key.txt")); + + try { + cipher.decrypt("9mx5Zq4JVyjeChTcVjEide4kWCwusFl7P2dSVXtg9IY="); + fail(); + + } catch (RuntimeException e) { + assertThat(e.getCause()).isInstanceOf(InvalidKeyException.class); + } + } + + @Test + public void decrypt_other_key() throws Exception { + AesCipher cipher = new AesCipher(getPath("other_secret_key.txt")); + + try { + // text encrypted with another key + cipher.decrypt("9mx5Zq4JVyjeChTcVjEide4kWCwusFl7P2dSVXtg9IY="); + fail(); + + } catch (RuntimeException e) { + assertThat(e.getCause()).isInstanceOf(BadPaddingException.class); + } + } + + @Test + public void encryptThenDecrypt() throws Exception { + AesCipher cipher = new AesCipher(pathToSecretKey()); + + assertThat(cipher.decrypt(cipher.encrypt("foo"))).isEqualTo("foo"); + } + + @Test + public void testDefaultPathToSecretKey() { + AesCipher cipher = new AesCipher(null); + + String path = cipher.getPathToSecretKey(); + + assertThat(StringUtils.isNotBlank(path)).isTrue(); + assertThat(new File(path).getName()).isEqualTo("sonar-secret.txt"); + } + + @Test + public void loadSecretKeyFromFile() throws Exception { + AesCipher cipher = new AesCipher(null); + Key secretKey = cipher.loadSecretFileFromFile(pathToSecretKey()); + assertThat(secretKey.getAlgorithm()).isEqualTo("AES"); + assertThat(secretKey.getEncoded().length).isGreaterThan(10); + } + + @Test + public void loadSecretKeyFromFile_trim_content() throws Exception { + String path = getPath("non_trimmed_secret_key.txt"); + AesCipher cipher = new AesCipher(null); + + Key secretKey = cipher.loadSecretFileFromFile(path); + + assertThat(secretKey.getAlgorithm()).isEqualTo("AES"); + assertThat(secretKey.getEncoded().length).isGreaterThan(10); + } + + @Test + public void loadSecretKeyFromFile_file_does_not_exist() throws Exception { + thrown.expect(IllegalStateException.class); + + AesCipher cipher = new AesCipher(null); + cipher.loadSecretFileFromFile("/file/does/not/exist"); + } + + @Test + public void loadSecretKeyFromFile_no_property() throws Exception { + thrown.expect(IllegalStateException.class); + + AesCipher cipher = new AesCipher(null); + cipher.loadSecretFileFromFile(null); + } + + @Test + public void hasSecretKey() throws Exception { + AesCipher cipher = new AesCipher(pathToSecretKey()); + + assertThat(cipher.hasSecretKey()).isTrue(); + } + + @Test + public void doesNotHaveSecretKey() throws Exception { + AesCipher cipher = new AesCipher("/my/twitter/id/is/SimonBrandhof"); + + assertThat(cipher.hasSecretKey()).isFalse(); + } + + private static String getPath(String file){ + return Resources.getResource(AesCipherTest.class, "AesCipherTest/" + file).getPath(); + } + + private static String pathToSecretKey() throws Exception { + return getPath("aes_secret_key.txt"); + } + +} diff --git a/sonar-application/src/test/java/org/sonar/application/ConfigurationUtilsTest.java b/sonar-application/src/test/java/org/sonar/application/ConfigurationUtilsTest.java new file mode 100644 index 00000000000..29a722b9fad --- /dev/null +++ b/sonar-application/src/test/java/org/sonar/application/ConfigurationUtilsTest.java @@ -0,0 +1,55 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.application; + +import com.google.common.collect.Maps; +import org.junit.Test; + +import java.util.Map; +import java.util.Properties; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; + +public class ConfigurationUtilsTest { + @Test + public void shouldInterpolateVariables() { + Properties input = new Properties(); + input.setProperty("hello", "world"); + input.setProperty("url", "${env:SONAR_JDBC_URL}"); + input.setProperty("do_not_change", "${SONAR_JDBC_URL}"); + Map<String, String> variables = Maps.newHashMap(); + variables.put("SONAR_JDBC_URL", "jdbc:h2:mem"); + + Properties output = ConfigurationUtils.interpolateVariables(input, variables); + + assertThat(output.size(), is(3)); + assertThat(output.getProperty("hello"), is("world")); + assertThat(output.getProperty("url"), is("jdbc:h2:mem")); + assertThat(output.getProperty("do_not_change"), is("${SONAR_JDBC_URL}")); + + // input is not changed + assertThat(input.size(), is(3)); + assertThat(input.getProperty("hello"), is("world")); + assertThat(input.getProperty("url"), is("${env:SONAR_JDBC_URL}")); + assertThat(input.getProperty("do_not_change"), is("${SONAR_JDBC_URL}")); + } + +} diff --git a/sonar-application/src/test/java/org/sonar/application/EncryptionTest.java b/sonar-application/src/test/java/org/sonar/application/EncryptionTest.java new file mode 100644 index 00000000000..85b3c568a35 --- /dev/null +++ b/sonar-application/src/test/java/org/sonar/application/EncryptionTest.java @@ -0,0 +1,59 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package org.sonar.application; + +import org.junit.Test; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; + +public class EncryptionTest { + + @Test + public void isEncrypted() { + Encryption encryption = new Encryption(null); + assertThat(encryption.isEncrypted("{aes}ADASDASAD"), is(true)); + assertThat(encryption.isEncrypted("{b64}ADASDASAD"), is(true)); + assertThat(encryption.isEncrypted("{abc}ADASDASAD"), is(true)); + + assertThat(encryption.isEncrypted("{}"), is(false)); + assertThat(encryption.isEncrypted("{foo"), is(false)); + assertThat(encryption.isEncrypted("foo{aes}"), is(false)); + } + + @Test + public void decrypt() { + Encryption encryption = new Encryption(null); + assertThat(encryption.decrypt("{b64}Zm9v"), is("foo")); + } + + @Test + public void decrypt_unknown_algorithm() { + Encryption encryption = new Encryption(null); + assertThat(encryption.decrypt("{xxx}Zm9v"), is("{xxx}Zm9v")); + } + + @Test + public void decrypt_uncrypted_text() { + Encryption encryption = new Encryption(null); + assertThat(encryption.decrypt("foo"), is("foo")); + } +} diff --git a/sonar-application/src/test/java/org/sonar/application/PropsTest.java b/sonar-application/src/test/java/org/sonar/application/PropsTest.java index 92091740301..46ca5e8dd26 100644 --- a/sonar-application/src/test/java/org/sonar/application/PropsTest.java +++ b/sonar-application/src/test/java/org/sonar/application/PropsTest.java @@ -19,6 +19,7 @@ */ package org.sonar.application; +import com.google.common.io.Resources; import org.apache.commons.io.FilenameUtils; import org.junit.Test; @@ -31,6 +32,7 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; public class PropsTest { + @Test public void of() throws Exception { Properties p = new Properties(); @@ -99,8 +101,10 @@ public class PropsTest { @Test public void load_file_and_system_properties() throws Exception { + System.setProperty("hello", "bar"); + Env env = mock(Env.class); - File propsFile = new File(getClass().getResource("/org/sonar/application/PropsTest/sonar.properties").toURI()); + File propsFile = new File(Resources.getResource(getClass(), "PropsTest/sonar.properties").getFile()); when(env.file("conf/sonar.properties")).thenReturn(propsFile); Props props = Props.create(env); @@ -109,7 +113,11 @@ public class PropsTest { assertThat(props.of("java.version")).isNotNull(); // system properties override file properties + assertThat(props.of("hello")).isEqualTo("bar"); assertThat(props.of("java.io.tmpdir")).isNotEmpty().isNotEqualTo("/should/be/overridden"); + + assertThat(System.getProperty("foo")).isEqualTo("bar"); + assertThat(System.getProperty("hello")).isEqualTo("bar"); } @Test diff --git a/sonar-application/src/test/resources/org/sonar/application/AesCipherTest/aes_secret_key.txt b/sonar-application/src/test/resources/org/sonar/application/AesCipherTest/aes_secret_key.txt new file mode 100644 index 00000000000..65b98c522da --- /dev/null +++ b/sonar-application/src/test/resources/org/sonar/application/AesCipherTest/aes_secret_key.txt @@ -0,0 +1 @@ +0PZz+G+f8mjr3sPn4+AhHg==
\ No newline at end of file diff --git a/sonar-application/src/test/resources/org/sonar/application/AesCipherTest/bad_secret_key.txt b/sonar-application/src/test/resources/org/sonar/application/AesCipherTest/bad_secret_key.txt new file mode 100644 index 00000000000..b33e179e5c8 --- /dev/null +++ b/sonar-application/src/test/resources/org/sonar/application/AesCipherTest/bad_secret_key.txt @@ -0,0 +1 @@ +badbadbad==
\ No newline at end of file diff --git a/sonar-application/src/test/resources/org/sonar/application/AesCipherTest/non_trimmed_secret_key.txt b/sonar-application/src/test/resources/org/sonar/application/AesCipherTest/non_trimmed_secret_key.txt new file mode 100644 index 00000000000..ab83e4adc03 --- /dev/null +++ b/sonar-application/src/test/resources/org/sonar/application/AesCipherTest/non_trimmed_secret_key.txt @@ -0,0 +1,3 @@ + + 0PZz+G+f8mjr3sPn4+AhHg== + diff --git a/sonar-application/src/test/resources/org/sonar/application/AesCipherTest/other_secret_key.txt b/sonar-application/src/test/resources/org/sonar/application/AesCipherTest/other_secret_key.txt new file mode 100644 index 00000000000..23f5ecf5104 --- /dev/null +++ b/sonar-application/src/test/resources/org/sonar/application/AesCipherTest/other_secret_key.txt @@ -0,0 +1 @@ +IBxEUxZ41c8XTxyaah1Qlg==
\ No newline at end of file diff --git a/sonar-application/src/test/resources/org/sonar/application/PropsTest/sonar.properties b/sonar-application/src/test/resources/org/sonar/application/PropsTest/sonar.properties index b73be15411b..5c06e58a32e 100644 --- a/sonar-application/src/test/resources/org/sonar/application/PropsTest/sonar.properties +++ b/sonar-application/src/test/resources/org/sonar/application/PropsTest/sonar.properties @@ -1,2 +1,3 @@ +hello: world foo=bar java.io.tmpdir=/should/be/overridden |