/* * Copyright 2017 ThoughtWorks, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.thoughtworks.go.domain.config; import java.lang.reflect.Method; import java.util.HashMap; import javax.annotation.PostConstruct; import com.thoughtworks.go.config.ConfigSaveValidationContext; import com.thoughtworks.go.security.GoCipher; import com.thoughtworks.go.plugin.access.packagematerial.PackageConfiguration; import com.thoughtworks.go.plugin.access.packagematerial.PackageConfigurations; import org.bouncycastle.crypto.InvalidCipherTextException; import org.junit.Before; import org.junit.Test; import org.springframework.util.ReflectionUtils; import static org.hamcrest.core.Is.is; import static org.hamcrest.core.IsNull.notNullValue; import static org.hamcrest.core.IsNull.nullValue; import static org.junit.Assert.assertThat; import static org.mockito.Matchers.anyString; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; public class ConfigurationPropertyTest { private GoCipher cipher; @Before public void setUp() throws Exception { cipher = mock(GoCipher.class); } @Test public void shouldCheckForConfigurationPropertyEquality() { ConfigurationValue configurationValue = new ConfigurationValue(); ConfigurationKey configurationKey = new ConfigurationKey(); ConfigurationProperty configurationProperty = new ConfigurationProperty(configurationKey, configurationValue, null, null); assertThat(configurationProperty, is(new ConfigurationProperty(configurationKey, configurationValue, null, null))); } @Test public void shouldGetPropertyForFingerprint() { ConfigurationValue configurationValue = new ConfigurationValue("value"); ConfigurationKey configurationKey = new ConfigurationKey("key"); ConfigurationProperty configurationProperty = new ConfigurationProperty(configurationKey, configurationValue, null, null); assertThat(configurationProperty.forFingerprint(), is("key=value")); } @Test public void shouldGetKeyValuePairForFingerPrintString() { ConfigurationValue configurationValue = new ConfigurationValue("value"); ConfigurationKey configurationKey = new ConfigurationKey("key"); ConfigurationProperty configurationProperty = new ConfigurationProperty(configurationKey, configurationValue, null, null); assertThat(configurationProperty.forFingerprint(), is("key=value")); } @Test public void shouldGetEncryptValueWhenConstructedAsSecure() throws InvalidCipherTextException { GoCipher goCipher = mock(GoCipher.class); String encryptedText = "encryptedValue"; when(goCipher.encrypt("secureValue")).thenReturn(encryptedText); ConfigurationProperty property = new ConfigurationProperty(new ConfigurationKey("secureKey"), new ConfigurationValue("secureValue"), new EncryptedConfigurationValue("old-encrypted-text"), goCipher); property.handleSecureValueConfiguration(true); assertThat(property.isSecure(), is(true)); assertThat(property.getEncryptedValue(), is(encryptedText)); assertThat(property.getConfigurationKey().getName(), is("secureKey")); assertThat(property.getConfigurationValue(), is(nullValue())); } @Test public void shouldNotEncryptWhenWhenConstructedAsNotSecure() throws InvalidCipherTextException { GoCipher goCipher = mock(GoCipher.class); ConfigurationProperty property = new ConfigurationProperty(new ConfigurationKey("secureKey"), new ConfigurationValue("secureValue"), null, goCipher); property.handleSecureValueConfiguration(false); assertThat(property.isSecure(), is(false)); assertThat(property.getConfigurationKey().getName(), is("secureKey")); assertThat(property.getConfigurationValue().getValue(), is("secureValue")); assertThat(property.getEncryptedConfigurationValue(), is(nullValue())); } @Test public void shouldNotClearEncryptedValueWhenWhenNewValueNotProvided() throws InvalidCipherTextException { GoCipher goCipher = mock(GoCipher.class); ConfigurationProperty property = new ConfigurationProperty(new ConfigurationKey("secureKey"), null, new EncryptedConfigurationValue("secureValue"), goCipher); property.handleSecureValueConfiguration(true); assertThat(property.isSecure(), is(true)); assertThat(property.getConfigurationKey().getName(), is("secureKey")); assertThat(property.getConfigurationValue(), is(nullValue())); assertThat(property.getEncryptedConfigurationValue(), is(notNullValue())); assertThat(property.getEncryptedValue(), is("secureValue")); } @Test public void shouldSetEmptyEncryptedValueWhenValueIsEmptyAndSecure() throws Exception { GoCipher goCipher = mock(GoCipher.class); ConfigurationProperty property = new ConfigurationProperty(new ConfigurationKey("secureKey"), new ConfigurationValue(""), new EncryptedConfigurationValue("old"), goCipher); property.handleSecureValueConfiguration(true); assertThat(property.getEncryptedValue(), is("")); verify(cipher, never()).decrypt(anyString()); } @Test public void shouldFailValidationIfAPropertyDoesNotHaveValue() { ConfigurationProperty property = new ConfigurationProperty(new ConfigurationKey("secureKey"), null, new EncryptedConfigurationValue("invalid-encrypted-value"), new GoCipher()); property.validate(ConfigSaveValidationContext.forChain(property)); assertThat(property.errors().isEmpty(), is(false)); assertThat(property.errors().getAllOn(ConfigurationProperty.ENCRYPTED_VALUE).contains( "Encrypted value for property with key 'secureKey' is invalid. This usually happens when the cipher text is modified to have an invalid value."), is(true)); } @Test public void shouldPassValidationIfBothNameAndValueAreProvided() throws InvalidCipherTextException { GoCipher cipher = mock(GoCipher.class); ConfigurationProperty property = new ConfigurationProperty(new ConfigurationKey("name"), new ConfigurationValue("value"), null, cipher); property.validate(ConfigSaveValidationContext.forChain(property)); assertThat(property.errors().isEmpty(), is(true)); } @Test public void shouldPassValidationIfBothNameAndEncryptedValueAreProvidedForSecureProperty() throws InvalidCipherTextException { String encrypted = "encrypted"; String decrypted = "decrypted"; when(cipher.decrypt(encrypted)).thenReturn(decrypted); ConfigurationProperty property = new ConfigurationProperty(new ConfigurationKey("name"), null, new EncryptedConfigurationValue(encrypted), cipher); property.validate(ConfigSaveValidationContext.forChain(property)); assertThat(property.errors().isEmpty(), is(true)); } @Test public void shouldSetConfigAttributesForNonSecureProperty() throws Exception { ConfigurationProperty configurationProperty = new ConfigurationProperty(); HashMap attributes = new HashMap(); HashMap keyMap = new HashMap(); keyMap.put("name", "fooKey"); attributes.put(ConfigurationProperty.CONFIGURATION_KEY, keyMap); HashMap valueMap = new HashMap(); valueMap.put("value", "fooValue"); attributes.put(ConfigurationProperty.CONFIGURATION_VALUE, valueMap); PackageConfigurations metadata = new PackageConfigurations(); metadata.addConfiguration(new PackageConfiguration("fooKey", null)); attributes.put(Configuration.METADATA, metadata); configurationProperty.setConfigAttributes(attributes,null); assertThat(configurationProperty.getConfigurationKey().getName(), is("fooKey")); assertThat(configurationProperty.getConfigurationValue().getValue(), is("fooValue")); } @Test public void shouldInitializeConfigValueToBlankWhenBothValueAndEncryptedValueIsNull() throws Exception { ConfigurationProperty configurationProperty = new ConfigurationProperty(new ConfigurationKey("key"), (ConfigurationValue) null); configurationProperty.initialize(); assertThat(configurationProperty.getConfigurationKey().getName(), is("key")); assertThat(configurationProperty.getConfigurationValue(), is(notNullValue())); assertThat(configurationProperty.getConfigurationValue().getValue(), is("")); assertThat(configurationProperty.getEncryptedConfigurationValue(), is(nullValue())); Method initializeMethod = ReflectionUtils.findMethod(ConfigurationProperty.class, "initialize"); assertThat(initializeMethod.getAnnotation(PostConstruct.class), is(notNullValue())); } @Test public void shouldSetConfigAttributesForSecurePropertyWhenUserChangesIt() throws Exception { ConfigurationProperty configurationProperty = new ConfigurationProperty(); HashMap attributes = new HashMap(); HashMap keyMap = new HashMap(); final String secureKey = "fooKey"; keyMap.put("name", secureKey); attributes.put(ConfigurationProperty.CONFIGURATION_KEY, keyMap); HashMap valueMap = new HashMap(); valueMap.put("value", "fooValue"); attributes.put(ConfigurationProperty.CONFIGURATION_VALUE, valueMap); attributes.put(ConfigurationProperty.IS_CHANGED, "0"); configurationProperty.setConfigAttributes(attributes,new SecureKeyInfoProvider() { @Override public boolean isSecure(String key) { return secureKey.equals(key); } }); String encryptedValue = new GoCipher().encrypt("fooValue"); assertThat(configurationProperty.getConfigurationKey().getName(), is(secureKey)); assertThat(configurationProperty.getConfigurationValue(), is(nullValue())); assertThat(configurationProperty.getEncryptedValue(), is(encryptedValue)); } @Test public void shouldSetConfigAttributesForSecurePropertyWhenUserDoesNotChangeIt() throws Exception { ConfigurationProperty configurationProperty = new ConfigurationProperty(); HashMap attributes = new HashMap(); HashMap keyMap = new HashMap(); final String secureKey = "fooKey"; keyMap.put("name", secureKey); attributes.put(ConfigurationProperty.CONFIGURATION_KEY, keyMap); HashMap valueMap = new HashMap(); valueMap.put("value", "fooValue"); attributes.put(ConfigurationProperty.CONFIGURATION_VALUE, valueMap); HashMap encryptedValueMap = new HashMap(); encryptedValueMap.put("value", "encryptedValue"); attributes.put(ConfigurationProperty.ENCRYPTED_VALUE, encryptedValueMap); configurationProperty.setConfigAttributes(attributes,new SecureKeyInfoProvider() { @Override public boolean isSecure(String key) { return secureKey.equals(key); } }); assertThat(configurationProperty.getConfigurationKey().getName(), is(secureKey)); assertThat(configurationProperty.getConfigurationValue(), is(nullValue())); assertThat(configurationProperty.getEncryptedValue(), is("encryptedValue")); } @Test public void shouldSetConfigAttributesWhenMetadataIsNotPassedInMap() throws Exception { ConfigurationProperty configurationProperty = new ConfigurationProperty(); HashMap attributes = new HashMap(); HashMap keyMap = new HashMap(); keyMap.put("name", "fooKey"); attributes.put(ConfigurationProperty.CONFIGURATION_KEY, keyMap); HashMap valueMap = new HashMap(); valueMap.put("value", "fooValue"); attributes.put(ConfigurationProperty.CONFIGURATION_VALUE, valueMap); configurationProperty.setConfigAttributes(attributes,null); assertThat(configurationProperty.getConfigurationKey().getName(), is("fooKey")); assertThat(configurationProperty.getConfigurationValue().getValue(), is("fooValue")); assertThat(configurationProperty.getEncryptedConfigurationValue(), is(nullValue())); } @Test public void shouldGetValueForSecureProperty() throws Exception { when(cipher.decrypt("encrypted-value")).thenReturn("decrypted-value"); ConfigurationProperty configurationProperty = new ConfigurationProperty(new ConfigurationKey("key"), null, new EncryptedConfigurationValue("encrypted-value"), cipher); assertThat(configurationProperty.getValue(), is("decrypted-value")); } @Test public void shouldGetEmptyValueWhenSecurePropertyValueIsNullOrEmpty() throws Exception { assertThat(new ConfigurationProperty(new ConfigurationKey("key"), null, new EncryptedConfigurationValue(""), cipher).getValue(), is("")); assertThat(new ConfigurationProperty(new ConfigurationKey("key"), null, new EncryptedConfigurationValue(null), cipher).getValue(), is("")); verify(cipher, never()).decrypt(anyString()); } @Test public void shouldGetValueForNonSecureProperty() throws Exception { ConfigurationProperty configurationProperty = new ConfigurationProperty(new ConfigurationKey("key"), new ConfigurationValue("value"), null, cipher); assertThat(configurationProperty.getValue(), is("value")); } @Test public void shouldGetNullValueForPropertyWhenValueIsNull() throws Exception { ConfigurationProperty configurationProperty = new ConfigurationProperty(new ConfigurationKey("key"), null, null, cipher); assertThat(configurationProperty.getValue(), is(nullValue())); } @Test public void shouldCheckIfSecureValueFieldHasNoErrors() throws Exception { EncryptedConfigurationValue encryptedValue = new EncryptedConfigurationValue("encrypted-value"); assertThat(new ConfigurationProperty(new ConfigurationKey("key"), null, encryptedValue, cipher).doesNotHaveErrorsAgainstConfigurationValue(), is(true)); encryptedValue.addError("value", "some-error"); assertThat(new ConfigurationProperty(new ConfigurationKey("key"), null, encryptedValue, cipher).doesNotHaveErrorsAgainstConfigurationValue(), is(false)); } @Test public void shouldCheckIfNonSecureValueFieldHasNoErrors() throws Exception { ConfigurationValue configurationValue = new ConfigurationValue("encrypted-value"); assertThat(new ConfigurationProperty(new ConfigurationKey("key"), configurationValue, null, cipher).doesNotHaveErrorsAgainstConfigurationValue(), is(true)); configurationValue.addError("value", "some-error"); assertThat(new ConfigurationProperty(new ConfigurationKey("key"), configurationValue, null, cipher).doesNotHaveErrorsAgainstConfigurationValue(), is(false)); } @Test public void shouldValidateKeyUniqueness(){ ConfigurationProperty property = new ConfigurationProperty(new ConfigurationKey("key"), new ConfigurationValue()); HashMap<String, ConfigurationProperty> map = new HashMap<>(); ConfigurationProperty original = new ConfigurationProperty(new ConfigurationKey("key"), new ConfigurationValue()); map.put("key", original); property.validateKeyUniqueness(map, "Repo"); assertThat(property.errors().isEmpty(), is(false)); assertThat(property.errors().getAllOn(ConfigurationProperty.CONFIGURATION_KEY).contains("Duplicate key 'key' found for Repo"), is(true)); assertThat(original.errors().isEmpty(), is(false)); assertThat(original.errors().getAllOn(ConfigurationProperty.CONFIGURATION_KEY).contains("Duplicate key 'key' found for Repo"), is(true)); } @Test public void shouldGetMaskedStringIfConfigurationPropertyIsSecure() throws Exception { assertThat(new ConfigurationProperty(new ConfigurationKey("key"), new EncryptedConfigurationValue("value")).getDisplayValue(), is("****")); assertThat(new ConfigurationProperty(new ConfigurationKey("key"), new ConfigurationValue("value")).getDisplayValue(), is("value")); } }