This is an automated email from the ASF dual-hosted git repository.

smolnar pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/ambari.git


The following commit(s) were added to refs/heads/trunk by this push:
     new 9c61584  AMBARI-24742. Encrypting/decrypting PASSWORD type properties 
when inserting them into the DB/using them (#2458)
9c61584 is described below

commit 9c61584bba46820f506b8cecd480da912df2fa90
Author: Sandor Molnar <smol...@apache.org>
AuthorDate: Fri Oct 19 17:52:36 2018 +0200

    AMBARI-24742. Encrypting/decrypting PASSWORD type properties when inserting 
them into the DB/using them (#2458)
---
 .../ambari/server/configuration/Configuration.java |  11 ++
 .../ambari/server/controller/ControllerModule.java |   5 +
 .../security/encryption/AESEncryptionService.java  |  19 ++-
 .../encryption/ConfigPropertiesEncryptor.java      | 127 +++++++++++++++++++++
 .../server/security/encryption/Encryptor.java      |  43 +++++++
 .../org/apache/ambari/server/state/Cluster.java    |  10 +-
 .../org/apache/ambari/server/state/Config.java     |   5 +
 .../org/apache/ambari/server/state/ConfigImpl.java |  30 +++--
 .../ambari/server/state/cluster/ClusterImpl.java   |   9 +-
 .../ambari/server/agent/AgentResourceTest.java     |   7 ++
 .../KerberosAdminPersistedCredentialCheckTest.java |   2 +-
 .../server/controller/KerberosHelperTest.java      |   2 +-
 .../internal/HostResourceProviderTest.java         |   2 +-
 .../server/testutils/PartialNiceMockBinder.java    |  12 ++
 .../ambari/server/update/HostUpdateHelperTest.java |   3 +-
 .../server/upgrade/UpgradeCatalog251Test.java      |   3 +-
 .../server/upgrade/UpgradeCatalog252Test.java      |   3 +-
 .../server/upgrade/UpgradeCatalog260Test.java      |   4 +-
 .../server/upgrade/UpgradeCatalog270Test.java      |   2 +-
 19 files changed, 279 insertions(+), 20 deletions(-)

diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/configuration/Configuration.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/configuration/Configuration.java
index d644c08..63c1777 100644
--- 
a/ambari-server/src/main/java/org/apache/ambari/server/configuration/Configuration.java
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/configuration/Configuration.java
@@ -2572,6 +2572,9 @@ public class Configuration {
   @Markdown(description = "Whether security password encryption is enabled or 
not. In case it is we store passwords in their own file(s); otherwise we store 
passwords in the Ambari credential store.")
   public static final ConfigurationProperty<Boolean> 
SECURITY_PASSWORD_ENCRYPTON_ENABLED = new 
ConfigurationProperty<>("security.passwords.encryption.enabled", false);
 
+  @Markdown(description="Whether to encrypt sensitive data (at rest) on 
service level configuration.")
+  public static final ConfigurationProperty<Boolean> 
SECURITY_SENSITIVE_DATA_ENCRYPTON_ENABLED = new 
ConfigurationProperty<>("security.server.encrypt_sensitive_data", false);
+
   /**
    * The maximum number of authentication attempts permitted to a local user. 
Once the number of failures reaches this limit the user will be locked out. 0 
indicates unlimited failures
    */
@@ -5518,6 +5521,14 @@ public class Configuration {
     return 
Boolean.parseBoolean(getProperty(SECURITY_PASSWORD_ENCRYPTON_ENABLED));
   }
 
+  public boolean isSensitiveDataEncryptionEnabled() {
+    return 
Boolean.parseBoolean(getProperty(SECURITY_SENSITIVE_DATA_ENCRYPTON_ENABLED));
+  }
+
+  public boolean shouldEncryptSensitiveData() {
+    return isSecurityPasswordEncryptionEnabled() && 
isSensitiveDataEncryptionEnabled();
+  }
+
   /**
    * @return default value of number of tasks to run in parallel during 
upgrades
    */
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/controller/ControllerModule.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/controller/ControllerModule.java
index 176e709..2e60deb 100644
--- 
a/ambari-server/src/main/java/org/apache/ambari/server/controller/ControllerModule.java
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/controller/ControllerModule.java
@@ -115,9 +115,11 @@ import 
org.apache.ambari.server.security.authorization.AuthorizationHelper;
 import 
org.apache.ambari.server.security.authorization.internal.InternalAuthenticationInterceptor;
 import 
org.apache.ambari.server.security.authorization.internal.RunWithInternalSecurityContext;
 import org.apache.ambari.server.security.encryption.AESEncryptionService;
+import org.apache.ambari.server.security.encryption.ConfigPropertiesEncryptor;
 import org.apache.ambari.server.security.encryption.CredentialStoreService;
 import org.apache.ambari.server.security.encryption.CredentialStoreServiceImpl;
 import org.apache.ambari.server.security.encryption.EncryptionService;
+import org.apache.ambari.server.security.encryption.Encryptor;
 import 
org.apache.ambari.server.serveraction.kerberos.KerberosOperationHandlerFactory;
 import org.apache.ambari.server.serveraction.users.CollectionPersisterService;
 import 
org.apache.ambari.server.serveraction.users.CollectionPersisterServiceFactory;
@@ -179,6 +181,7 @@ import com.google.gson.GsonBuilder;
 import com.google.inject.AbstractModule;
 import com.google.inject.Scopes;
 import com.google.inject.Singleton;
+import com.google.inject.TypeLiteral;
 import com.google.inject.assistedinject.FactoryModuleBuilder;
 import com.google.inject.name.Names;
 import com.google.inject.persist.PersistModule;
@@ -332,6 +335,8 @@ public class ControllerModule extends AbstractModule {
 
     bind(CredentialStoreService.class).to(CredentialStoreServiceImpl.class);
     bind(EncryptionService.class).to(AESEncryptionService.class);
+    //to support different Encryptor implementation we have to annotate them 
by their name and use them as @Named injects
+    bind(new TypeLiteral<Encryptor<Config>>() 
{}).annotatedWith(Names.named("ConfigPropertiesEncryptor")).to(ConfigPropertiesEncryptor.class);
 
     bind(Configuration.class).toInstance(configuration);
     bind(OsFamily.class).toInstance(os_family);
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/security/encryption/AESEncryptionService.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/security/encryption/AESEncryptionService.java
index ffb92ba..25b3fc7 100644
--- 
a/ambari-server/src/main/java/org/apache/ambari/server/security/encryption/AESEncryptionService.java
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/security/encryption/AESEncryptionService.java
@@ -24,6 +24,8 @@ import org.apache.ambari.server.utils.TextEncoding;
 import org.apache.commons.codec.binary.Base64;
 import org.apache.commons.codec.binary.Hex;
 
+import com.google.common.cache.Cache;
+import com.google.common.cache.CacheBuilder;
 import com.google.inject.Singleton;
 
 @Singleton
@@ -32,6 +34,8 @@ public class AESEncryptionService implements 
EncryptionService {
   private static final String ENCODED_TEXT_FIELD_DELIMITER = "::";
   private static final String UTF_8_CHARSET = StandardCharsets.UTF_8.name();
 
+  private final Cache<String, AESEncryptor> aesEncryptorCache = 
CacheBuilder.newBuilder().build();
+
   private MasterKeyService environmentMasterKeyService;
 
   @Override
@@ -51,11 +55,20 @@ public class AESEncryptionService implements 
EncryptionService {
 
   @Override
   public String encrypt(String toBeEncrypted, String key, TextEncoding 
textEncoding) throws Exception {
-    final AESEncryptor aes = new AESEncryptor(key);
-    final EncryptionResult encryptionResult = aes.encrypt(toBeEncrypted);
+    final EncryptionResult encryptionResult = 
getAesEncryptor(key).encrypt(toBeEncrypted);
     return TextEncoding.BASE_64 == textEncoding ? 
encodeEncryptionResultBase64(encryptionResult) : 
encodeEncryptionResultBinHex(encryptionResult);
   }
 
+  private AESEncryptor getAesEncryptor(String key) {
+    AESEncryptor aesEncryptor = aesEncryptorCache.getIfPresent(key);
+    if (aesEncryptor == null) {
+      aesEncryptor = new AESEncryptor(key);
+      aesEncryptorCache.put(key, aesEncryptor);
+    }
+
+    return aesEncryptor;
+  }
+
   private final String getAmbariMasterKey() {
     initEnvironmentMasterKeyService();
     return String.valueOf(environmentMasterKeyService.getMasterSecret());
@@ -100,7 +113,7 @@ public class AESEncryptionService implements 
EncryptionService {
     final byte[] decodedValue = TextEncoding.BASE_64 == textEncoding ? 
Base64.decodeBase64(toBeDecrypted) : Hex.decodeHex(toBeDecrypted.toCharArray());
     final String decodedText = new String(decodedValue, UTF_8_CHARSET);
     final String[] decodedParts = 
decodedText.split(ENCODED_TEXT_FIELD_DELIMITER);
-    final AESEncryptor aes = new AESEncryptor(key);
+    final AESEncryptor aes = getAesEncryptor(key);
     if (TextEncoding.BASE_64 == textEncoding) {
       return new String(aes.decrypt(Base64.decodeBase64(decodedParts[0]), 
Base64.decodeBase64(decodedParts[1]), Base64.decodeBase64(decodedParts[2])), 
UTF_8_CHARSET);
     } else {
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/security/encryption/ConfigPropertiesEncryptor.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/security/encryption/ConfigPropertiesEncryptor.java
new file mode 100644
index 0000000..68710f4
--- /dev/null
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/security/encryption/ConfigPropertiesEncryptor.java
@@ -0,0 +1,127 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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
+ * <p/>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p/>
+ * 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 org.apache.ambari.server.security.encryption;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.apache.ambari.server.AmbariException;
+import org.apache.ambari.server.AmbariRuntimeException;
+import org.apache.ambari.server.state.Cluster;
+import org.apache.ambari.server.state.Config;
+import org.apache.ambari.server.state.PropertyInfo.PropertyType;
+import org.apache.ambari.server.state.StackId;
+import org.apache.ambari.server.utils.TextEncoding;
+import org.apache.commons.collections.CollectionUtils;
+
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+
+/**
+ * {@link Encryptor} implementation for encrypting/decrypting PASSWORD type
+ * properties in {@link Config}'s properties
+ */
+
+@Singleton
+public class ConfigPropertiesEncryptor implements Encryptor<Config> {
+
+  private static final String ENCRYPTED_PROPERTY_PREFIX = "${enc=aes256_hex, 
value=";
+  private static final String ENCRYPTED_PROPERTY_SCHEME = 
ENCRYPTED_PROPERTY_PREFIX + "%s}";
+
+  private final EncryptionService encryptionService;
+  private final Map<Long, Map<StackId, Map<String, Set<String>>>> 
clusterPasswordProperties = new ConcurrentHashMap<>(); //Map<clusterId, 
<Map<stackId, Map<configType, Set<passwordPropertyKeys>>>>;
+
+  @Inject
+  public ConfigPropertiesEncryptor(EncryptionService encryptionService) {
+    this.encryptionService = encryptionService;
+  }
+
+  @Override
+  public void encryptSensitiveData(Config config) {
+    try {
+      final Map<String, String> configProperties = config.getProperties();
+      if (configProperties != null) {
+        final Set<String> passwordProperties = 
getPasswordProperties(config.getCluster(), config.getType());
+        if (CollectionUtils.isNotEmpty(passwordProperties)) {
+          final Map<String, String> encryptedProperties = new 
HashMap<>(configProperties);
+          for (Map.Entry<String, String> property : 
configProperties.entrySet()) {
+            if (passwordProperties.contains(property.getKey()) && 
!isEncryptedPassword(property.getValue())) {
+              encryptedProperties.put(property.getKey(), 
encryptAndDecoratePropertyValue(property.getValue()));
+            }
+          }
+          config.setProperties(encryptedProperties);
+        }
+      }
+    } catch (Exception e) {
+      throw new AmbariRuntimeException("Error while encrypting sensitive 
data", e);
+    }
+  }
+
+  private boolean isEncryptedPassword(String password) {
+    return password != null && password.startsWith(ENCRYPTED_PROPERTY_PREFIX); 
// assuming previous encryption by this class
+  }
+
+  private Set<String> getPasswordProperties(Cluster cluster, String 
configType) throws AmbariException {
+    //in case of normal configuration change on the UI - or via the API - the 
current and desired stacks are equal
+    //in case of an upgrade they are different; in this case we want to get 
password properties from the desired stack
+    if 
(cluster.getCurrentStackVersion().equals(cluster.getDesiredStackVersion())) {
+      return getPasswordProperties(cluster, cluster.getCurrentStackVersion(), 
configType);
+    } else {
+      return getPasswordProperties(cluster, cluster.getDesiredStackVersion(), 
configType);
+    }
+  }
+
+  private Set<String> getPasswordProperties(Cluster cluster, StackId stackId, 
String configType) {
+    final long clusterId = cluster.getClusterId();
+    clusterPasswordProperties.computeIfAbsent(clusterId, v -> new 
ConcurrentHashMap<>()).computeIfAbsent(stackId, v -> new ConcurrentHashMap<>())
+        .computeIfAbsent(configType, v -> 
cluster.getConfigPropertiesTypes(configType, 
stackId).getOrDefault(PropertyType.PASSWORD, new HashSet<>()));
+    return 
clusterPasswordProperties.get(clusterId).get(stackId).getOrDefault(configType, 
new HashSet<>());
+  }
+
+  private String encryptAndDecoratePropertyValue(String propertyValue) throws 
Exception {
+    final String encrypted = encryptionService.encrypt(propertyValue, 
TextEncoding.BIN_HEX);
+    return String.format(ENCRYPTED_PROPERTY_SCHEME, encrypted);
+  }
+
+  @Override
+  public void decryptSensitiveData(Config config) {
+    final Map<String, String> configProperties = config.getProperties();
+    if (configProperties != null) {
+      final Map<String, String> decryptedProperties = new 
HashMap<>(configProperties);
+      for (Map.Entry<String, String> property : configProperties.entrySet()) {
+        if (isEncryptedPassword(property.getValue())) {
+          decryptedProperties.put(property.getKey(), 
decryptProperty(property.getValue()));
+        }
+      }
+      config.setProperties(decryptedProperties);
+    }
+  }
+
+  private String decryptProperty(String property) {
+    try {
+      // sample value: ${enc=aes256_hex, value=5248...303d}
+      final String encrypted = 
property.substring(ENCRYPTED_PROPERTY_PREFIX.length(), property.indexOf('}'));
+      return encryptionService.decrypt(encrypted, TextEncoding.BIN_HEX);
+    } catch (Exception e) {
+      throw new AmbariRuntimeException("Error while decrypting property", e);
+    }
+  }
+}
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/security/encryption/Encryptor.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/security/encryption/Encryptor.java
new file mode 100644
index 0000000..4fad723
--- /dev/null
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/security/encryption/Encryptor.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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
+ * <p/>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p/>
+ * 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 org.apache.ambari.server.security.encryption;
+
+/**
+ * Defines a generic contract on encrypting/decrypting sensitive data
+ */
+public interface Encryptor<T> {
+
+  /**
+   * Encrypts the given encryptible object
+   * 
+   * @param encryptible
+   *          to be encrypted
+   * @return the encrypted value
+   */
+  void encryptSensitiveData(T encryptible);
+
+  /**
+   * Decrypts the given decryptible object
+   * 
+   * @param decryptible
+   *          to be decrypted
+   * @return the decrypted value
+   */
+  void decryptSensitiveData(T decryptible);
+
+}
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/state/Cluster.java 
b/ambari-server/src/main/java/org/apache/ambari/server/state/Cluster.java
index c201310..a150796 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/Cluster.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/Cluster.java
@@ -268,13 +268,21 @@ public interface Cluster {
   Map<String, Config> getConfigsByType(String configType);
 
   /**
-   * Gets all properties types that mach the specified type.
+   * Gets all properties types that matches the specified type for the current 
stack.
    * @param configType the config type to return
    * @return properties types for given config type
    */
   Map<PropertyInfo.PropertyType, Set<String>> getConfigPropertiesTypes(String 
configType);
 
   /**
+   * Gets all properties types that matches the specified type for the given 
stack.
+   * @param configType the config type to return
+   * @param stackId the stack to scan properties for
+   * @return properties types for given config type
+   */
+  Map<PropertyInfo.PropertyType, Set<String>> getConfigPropertiesTypes(String 
configType, StackId stackId);
+
+  /**
    * Gets the specific config that matches the specified type and tag.  This 
not
    * necessarily a DESIRED configuration that applies to a cluster.
    * @param configType  the config type to find
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/state/Config.java 
b/ambari-server/src/main/java/org/apache/ambari/server/state/Config.java
index c1a8dc1..d687e84 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/Config.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/Config.java
@@ -97,4 +97,9 @@ public interface Config {
    * Persist the configuration.
    */
   void save();
+
+  /**
+   * @return the cluster where this config belongs to
+   */
+  Cluster getCluster();
 }
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/state/ConfigImpl.java 
b/ambari-server/src/main/java/org/apache/ambari/server/state/ConfigImpl.java
index b3ff843..e04a6bb 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/ConfigImpl.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/ConfigImpl.java
@@ -27,7 +27,9 @@ import java.util.concurrent.ConcurrentMap;
 import java.util.concurrent.locks.ReadWriteLock;
 
 import javax.annotation.Nullable;
+import javax.inject.Named;
 
+import org.apache.ambari.server.configuration.Configuration;
 import org.apache.ambari.server.events.ClusterConfigChangedEvent;
 import org.apache.ambari.server.events.publishers.AmbariEventPublisher;
 import org.apache.ambari.server.logging.LockFactory;
@@ -37,11 +39,13 @@ import org.apache.ambari.server.orm.dao.StackDAO;
 import org.apache.ambari.server.orm.entities.ClusterConfigEntity;
 import org.apache.ambari.server.orm.entities.ClusterEntity;
 import org.apache.ambari.server.orm.entities.StackEntity;
+import org.apache.ambari.server.security.encryption.Encryptor;
 import org.apache.commons.lang.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
 import com.google.gson.JsonSyntaxException;
 import com.google.inject.Inject;
 import com.google.inject.assistedinject.Assisted;
@@ -104,9 +108,10 @@ public class ConfigImpl implements Config {
       @Assisted Map<String, String> properties,
       @Assisted @Nullable Map<String, Map<String, String>> 
propertiesAttributes,
       ClusterDAO clusterDAO, StackDAO stackDAO,
-      Gson gson, AmbariEventPublisher eventPublisher, LockFactory lockFactory) 
{
+      Gson gson, AmbariEventPublisher eventPublisher, LockFactory lockFactory,
+      Configuration serverConfiguration, @Named("ConfigPropertiesEncryptor") 
Encryptor<Config> configPropertiesEncryptor) {
     this(cluster.getDesiredStackVersion(), cluster, type, tag, properties, 
propertiesAttributes,
-        clusterDAO, stackDAO, gson, eventPublisher, lockFactory);
+        clusterDAO, stackDAO, gson, eventPublisher, lockFactory, 
serverConfiguration, configPropertiesEncryptor);
   }
 
 
@@ -116,20 +121,24 @@ public class ConfigImpl implements Config {
       @Assisted Map<String, String> properties,
       @Assisted @Nullable Map<String, Map<String, String>> 
propertiesAttributes,
       ClusterDAO clusterDAO, StackDAO stackDAO,
-      Gson gson, AmbariEventPublisher eventPublisher, LockFactory lockFactory) 
{
+      Gson gson, AmbariEventPublisher eventPublisher, LockFactory lockFactory,
+      Configuration serverConfiguration, @Named("ConfigPropertiesEncryptor") 
Encryptor<Config> configPropertiesEncryptor) {
 
     propertyLock = lockFactory.newReadWriteLock(PROPERTY_LOCK_LABEL);
 
     this.cluster = cluster;
     this.type = type;
     this.properties = properties;
+    if (serverConfiguration.shouldEncryptSensitiveData()) {
+      configPropertiesEncryptor.encryptSensitiveData(this);
+    }
 
     // only set this if it's non-null
     this.propertiesAttributes = null == propertiesAttributes ? null
         : new HashMap<>(propertiesAttributes);
 
     this.clusterDAO = clusterDAO;
-    this.gson = gson;
+    this.gson = new GsonBuilder().disableHtmlEscaping().create();
     this.eventPublisher = eventPublisher;
     version = cluster.getNextConfigVersion(type);
 
@@ -148,10 +157,10 @@ public class ConfigImpl implements Config {
     entity.setTag(this.tag);
     entity.setTimestamp(System.currentTimeMillis());
     entity.setStack(stackEntity);
-    entity.setData(gson.toJson(properties));
+    entity.setData(this.gson.toJson(this.properties));
 
     if (null != propertiesAttributes) {
-      entity.setAttributes(gson.toJson(propertiesAttributes));
+      entity.setAttributes(this.gson.toJson(propertiesAttributes));
     }
 
     // when creating a brand new config without a backing entity, use the
@@ -161,12 +170,13 @@ public class ConfigImpl implements Config {
     persist(entity);
 
     configId = entity.getConfigId();
+    configPropertiesEncryptor.decryptSensitiveData(this);
   }
 
   @AssistedInject
   ConfigImpl(@Assisted Cluster cluster, @Assisted ClusterConfigEntity entity,
       ClusterDAO clusterDAO, Gson gson, AmbariEventPublisher eventPublisher,
-      LockFactory lockFactory) {
+      LockFactory lockFactory,  @Named("ConfigPropertiesEncryptor") 
Encryptor<Config> configPropertiesEncryptor) {
     propertyLock = lockFactory.newReadWriteLock(PROPERTY_LOCK_LABEL);
 
     this.cluster = cluster;
@@ -194,6 +204,7 @@ public class ConfigImpl implements Config {
       }
 
       properties = deserializedProperties;
+      configPropertiesEncryptor.decryptSensitiveData(this);
     } catch (JsonSyntaxException e) {
       LOG.error("Malformed configuration JSON stored in the database for 
{}/{}", entity.getType(),
           entity.getTag());
@@ -324,6 +335,11 @@ public class ConfigImpl implements Config {
   }
 
   @Override
+  public Cluster getCluster() {
+    return cluster;
+  }
+
+  @Override
   public List<Long> getServiceConfigVersions() {
     return 
serviceConfigDAO.getServiceConfigVersionsByConfig(cluster.getClusterId(), type, 
version);
   }
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/state/cluster/ClusterImpl.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/state/cluster/ClusterImpl.java
index eb1176e..4ca2caf 100644
--- 
a/ambari-server/src/main/java/org/apache/ambari/server/state/cluster/ClusterImpl.java
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/state/cluster/ClusterImpl.java
@@ -2548,8 +2548,15 @@ public class ClusterImpl implements Cluster {
    */
   @Override
   public Map<PropertyInfo.PropertyType, Set<String>> 
getConfigPropertiesTypes(String configType){
+    return getConfigPropertiesTypes(configType, getCurrentStackVersion());
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public Map<PropertyInfo.PropertyType, Set<String>> 
getConfigPropertiesTypes(String configType, StackId stackId) {
     try {
-      StackId stackId = getCurrentStackVersion();
       StackInfo stackInfo = ambariMetaInfo.getStack(stackId.getStackName(), 
stackId.getStackVersion());
       return stackInfo.getConfigPropertiesTypes(configType);
     } catch (AmbariException ignored) {
diff --git 
a/ambari-server/src/test/java/org/apache/ambari/server/agent/AgentResourceTest.java
 
b/ambari-server/src/test/java/org/apache/ambari/server/agent/AgentResourceTest.java
index 37d5166..d992d4a 100644
--- 
a/ambari-server/src/test/java/org/apache/ambari/server/agent/AgentResourceTest.java
+++ 
b/ambari-server/src/test/java/org/apache/ambari/server/agent/AgentResourceTest.java
@@ -59,8 +59,12 @@ import org.apache.ambari.server.scheduler.ExecutionScheduler;
 import org.apache.ambari.server.scheduler.ExecutionSchedulerImpl;
 import org.apache.ambari.server.security.SecurityHelper;
 import org.apache.ambari.server.security.SecurityHelperImpl;
+import org.apache.ambari.server.security.encryption.AESEncryptionService;
+import org.apache.ambari.server.security.encryption.ConfigPropertiesEncryptor;
 import org.apache.ambari.server.security.encryption.CredentialStoreService;
 import org.apache.ambari.server.security.encryption.CredentialStoreServiceImpl;
+import org.apache.ambari.server.security.encryption.EncryptionService;
+import org.apache.ambari.server.security.encryption.Encryptor;
 import org.apache.ambari.server.stack.StackManagerFactory;
 import 
org.apache.ambari.server.stack.upgrade.orchestrate.UpgradeContextFactory;
 import org.apache.ambari.server.stageplanner.RoleGraphFactory;
@@ -107,6 +111,7 @@ import com.google.gson.GsonBuilder;
 import com.google.inject.AbstractModule;
 import com.google.inject.Guice;
 import com.google.inject.Injector;
+import com.google.inject.TypeLiteral;
 import com.google.inject.assistedinject.FactoryModuleBuilder;
 import com.google.inject.name.Names;
 import com.sun.jersey.api.client.Client;
@@ -352,6 +357,8 @@ public class AgentResourceTest extends RandomPortJerseyTest 
{
       
bind(AmbariManagementController.class).toInstance(createNiceMock(AmbariManagementController.class));
       
bind(KerberosHelper.class).toInstance(createNiceMock(KerberosHelper.class));
       
bind(MpackManagerFactory.class).toInstance(createNiceMock(MpackManagerFactory.class));
+      bind(EncryptionService.class).to(AESEncryptionService.class);
+      bind(new TypeLiteral<Encryptor<Config>>() 
{}).annotatedWith(Names.named("ConfigPropertiesEncryptor")).to(ConfigPropertiesEncryptor.class);
     }
 
     private void installDependencies() {
diff --git 
a/ambari-server/src/test/java/org/apache/ambari/server/checks/KerberosAdminPersistedCredentialCheckTest.java
 
b/ambari-server/src/test/java/org/apache/ambari/server/checks/KerberosAdminPersistedCredentialCheckTest.java
index edea0a7..ff3be49 100644
--- 
a/ambari-server/src/test/java/org/apache/ambari/server/checks/KerberosAdminPersistedCredentialCheckTest.java
+++ 
b/ambari-server/src/test/java/org/apache/ambari/server/checks/KerberosAdminPersistedCredentialCheckTest.java
@@ -217,7 +217,7 @@ public class KerberosAdminPersistedCredentialCheckTest 
extends EasyMockSupport {
       @Override
       protected void configure() {
         
PartialNiceMockBinder.newBuilder().addActionDBAccessorConfigsBindings().addFactoriesInstallBinding()
-            .build().configure(binder());
+            .addPasswordEncryptorBindings().build().configure(binder());
 
         
bind(ExecutionScheduler.class).toInstance(createNiceMock(ExecutionSchedulerImpl.class));
         
bind(EntityManager.class).toInstance(createNiceMock(EntityManager.class));
diff --git 
a/ambari-server/src/test/java/org/apache/ambari/server/controller/KerberosHelperTest.java
 
b/ambari-server/src/test/java/org/apache/ambari/server/controller/KerberosHelperTest.java
index 584eff9..c229eb2 100644
--- 
a/ambari-server/src/test/java/org/apache/ambari/server/controller/KerberosHelperTest.java
+++ 
b/ambari-server/src/test/java/org/apache/ambari/server/controller/KerberosHelperTest.java
@@ -238,7 +238,7 @@ public class KerberosHelperTest extends EasyMockSupport {
       @Override
       protected void configure() {
         
PartialNiceMockBinder.newBuilder().addActionDBAccessorConfigsBindings().addFactoriesInstallBinding()
-            .build().configure(binder());
+            .addPasswordEncryptorBindings().build().configure(binder());
 
         bind(ActionDBAccessor.class).to(ActionDBAccessorImpl.class);
         bind(ExecutionScheduler.class).to(ExecutionSchedulerImpl.class);
diff --git 
a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/HostResourceProviderTest.java
 
b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/HostResourceProviderTest.java
index 3659019..125a9e9 100644
--- 
a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/HostResourceProviderTest.java
+++ 
b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/HostResourceProviderTest.java
@@ -1403,7 +1403,7 @@ public class HostResourceProviderTest extends 
EasyMockSupport {
     Injector injector = Guice.createInjector(new AbstractModule() {
       @Override
       protected void configure() {
-        
PartialNiceMockBinder.newBuilder().addConfigsBindings().addFactoriesInstallBinding().build().configure(binder());
+        
PartialNiceMockBinder.newBuilder().addConfigsBindings().addFactoriesInstallBinding().addPasswordEncryptorBindings().build().configure(binder());
 
         
bind(EntityManager.class).toInstance(createNiceMock(EntityManager.class));
         bind(DBAccessor.class).toInstance(createNiceMock(DBAccessor.class));
diff --git 
a/ambari-server/src/test/java/org/apache/ambari/server/testutils/PartialNiceMockBinder.java
 
b/ambari-server/src/test/java/org/apache/ambari/server/testutils/PartialNiceMockBinder.java
index d87a335..94d64ac 100644
--- 
a/ambari-server/src/test/java/org/apache/ambari/server/testutils/PartialNiceMockBinder.java
+++ 
b/ambari-server/src/test/java/org/apache/ambari/server/testutils/PartialNiceMockBinder.java
@@ -55,6 +55,8 @@ import org.apache.ambari.server.orm.dao.HostRoleCommandDAO;
 import org.apache.ambari.server.scheduler.ExecutionScheduler;
 import org.apache.ambari.server.scheduler.ExecutionSchedulerImpl;
 import org.apache.ambari.server.security.encryption.CredentialStoreService;
+import org.apache.ambari.server.security.encryption.EncryptionService;
+import org.apache.ambari.server.security.encryption.Encryptor;
 import org.apache.ambari.server.stack.StackManagerFactory;
 import 
org.apache.ambari.server.stack.upgrade.orchestrate.UpgradeContextFactory;
 import org.apache.ambari.server.stageplanner.RoleGraphFactory;
@@ -90,6 +92,7 @@ import 
org.springframework.security.crypto.password.StandardPasswordEncoder;
 
 import com.google.inject.Binder;
 import com.google.inject.Module;
+import com.google.inject.TypeLiteral;
 import com.google.inject.assistedinject.FactoryModuleBuilder;
 import com.google.inject.name.Names;
 import com.google.inject.persist.UnitOfWork;
@@ -153,6 +156,7 @@ public class PartialNiceMockBinder implements Module {
       });
       addConfigsBindings();
       addFactoriesInstallBinding();
+      addPasswordEncryptorBindings();
       return this;
     }
 
@@ -206,6 +210,14 @@ public class PartialNiceMockBinder implements Module {
       return this;
     }
 
+    public Builder addPasswordEncryptorBindings() {
+      configurers.add((Binder binder) -> {
+        
binder.bind(EncryptionService.class).toInstance(easyMockSupport.createNiceMock(EncryptionService.class));
+        binder.bind(new TypeLiteral<Encryptor<Config>>() 
{}).annotatedWith(Names.named("ConfigPropertiesEncryptor")).toInstance(easyMockSupport.createNiceMock(Encryptor.class));
+      });
+      return this;
+    }
+
     public Builder addHostRoleCommandsConfigsBindings() {
       configurers.add((Binder binder) -> {
           
binder.bindConstant().annotatedWith(Names.named(HostRoleCommandDAO.HRC_STATUS_SUMMARY_CACHE_ENABLED)).to(true);
diff --git 
a/ambari-server/src/test/java/org/apache/ambari/server/update/HostUpdateHelperTest.java
 
b/ambari-server/src/test/java/org/apache/ambari/server/update/HostUpdateHelperTest.java
index 9c438c9..028f888 100644
--- 
a/ambari-server/src/test/java/org/apache/ambari/server/update/HostUpdateHelperTest.java
+++ 
b/ambari-server/src/test/java/org/apache/ambari/server/update/HostUpdateHelperTest.java
@@ -495,7 +495,8 @@ public class HostUpdateHelperTest {
       @Override
       protected void configure() {
 
-        
PartialNiceMockBinder.newBuilder().addConfigsBindings().addFactoriesInstallBinding().build().configure(binder());
+        
PartialNiceMockBinder.newBuilder().addConfigsBindings().addFactoriesInstallBinding().addPasswordEncryptorBindings()
+        .build().configure(binder());
 
         bind(DBAccessor.class).toInstance(dbAccessor);
         bind(EntityManager.class).toInstance(entityManager);
diff --git 
a/ambari-server/src/test/java/org/apache/ambari/server/upgrade/UpgradeCatalog251Test.java
 
b/ambari-server/src/test/java/org/apache/ambari/server/upgrade/UpgradeCatalog251Test.java
index 6ecea16..03437fa 100644
--- 
a/ambari-server/src/test/java/org/apache/ambari/server/upgrade/UpgradeCatalog251Test.java
+++ 
b/ambari-server/src/test/java/org/apache/ambari/server/upgrade/UpgradeCatalog251Test.java
@@ -292,7 +292,8 @@ public class UpgradeCatalog251Test {
     Module module = new Module() {
       @Override
       public void configure(Binder binder) {
-        
PartialNiceMockBinder.newBuilder().addConfigsBindings().addFactoriesInstallBinding().build().configure(binder);
+        
PartialNiceMockBinder.newBuilder().addConfigsBindings().addFactoriesInstallBinding()
+        .addPasswordEncryptorBindings().build().configure(binder);
 
         binder.bind(DBAccessor.class).toInstance(dbAccessor);
         binder.bind(OsFamily.class).toInstance(osFamily);
diff --git 
a/ambari-server/src/test/java/org/apache/ambari/server/upgrade/UpgradeCatalog252Test.java
 
b/ambari-server/src/test/java/org/apache/ambari/server/upgrade/UpgradeCatalog252Test.java
index 1d5168c..7dfee87 100644
--- 
a/ambari-server/src/test/java/org/apache/ambari/server/upgrade/UpgradeCatalog252Test.java
+++ 
b/ambari-server/src/test/java/org/apache/ambari/server/upgrade/UpgradeCatalog252Test.java
@@ -440,7 +440,8 @@ public class UpgradeCatalog252Test {
     Module module = new Module() {
       @Override
       public void configure(Binder binder) {
-        
PartialNiceMockBinder.newBuilder().addConfigsBindings().addFactoriesInstallBinding().build().configure(binder);
+        
PartialNiceMockBinder.newBuilder().addConfigsBindings().addFactoriesInstallBinding()
+        .addPasswordEncryptorBindings().build().configure(binder);
 
         binder.bind(DBAccessor.class).toInstance(dbAccessor);
         binder.bind(OsFamily.class).toInstance(osFamily);
diff --git 
a/ambari-server/src/test/java/org/apache/ambari/server/upgrade/UpgradeCatalog260Test.java
 
b/ambari-server/src/test/java/org/apache/ambari/server/upgrade/UpgradeCatalog260Test.java
index ba09e97..02a45c2 100644
--- 
a/ambari-server/src/test/java/org/apache/ambari/server/upgrade/UpgradeCatalog260Test.java
+++ 
b/ambari-server/src/test/java/org/apache/ambari/server/upgrade/UpgradeCatalog260Test.java
@@ -1025,7 +1025,8 @@ public class UpgradeCatalog260Test {
     final Injector mockInjector = Guice.createInjector(new AbstractModule() {
       @Override
       protected void configure() {
-        
PartialNiceMockBinder.newBuilder().addConfigsBindings().addFactoriesInstallBinding().build().configure(binder());
+        
PartialNiceMockBinder.newBuilder().addConfigsBindings().addFactoriesInstallBinding()
+        .addPasswordEncryptorBindings().build().configure(binder());
 
         
bind(EntityManager.class).toInstance(createNiceMock(EntityManager.class));
         bind(AmbariManagementController.class).toInstance(controller);
@@ -1083,6 +1084,7 @@ public class UpgradeCatalog260Test {
     return Guice.createInjector(new Module() {
       @Override
       public void configure(Binder binder) {
+        
PartialNiceMockBinder.newBuilder().addPasswordEncryptorBindings().build().configure(binder);
         
binder.bindConstant().annotatedWith(Names.named("actionTimeout")).to(600000L);
         
binder.bindConstant().annotatedWith(Names.named("schedulerSleeptime")).to(1L);
         
binder.bindConstant().annotatedWith(Names.named(HostRoleCommandDAO.HRC_STATUS_SUMMARY_CACHE_ENABLED)).to(true);
diff --git 
a/ambari-server/src/test/java/org/apache/ambari/server/upgrade/UpgradeCatalog270Test.java
 
b/ambari-server/src/test/java/org/apache/ambari/server/upgrade/UpgradeCatalog270Test.java
index c2096d5..6fa317b 100644
--- 
a/ambari-server/src/test/java/org/apache/ambari/server/upgrade/UpgradeCatalog270Test.java
+++ 
b/ambari-server/src/test/java/org/apache/ambari/server/upgrade/UpgradeCatalog270Test.java
@@ -600,7 +600,7 @@ public class UpgradeCatalog270Test {
     Module module = new AbstractModule() {
       @Override
       public void configure() {
-        
PartialNiceMockBinder.newBuilder().addConfigsBindings().addFactoriesInstallBinding().build().configure(binder());
+        
PartialNiceMockBinder.newBuilder().addConfigsBindings().addPasswordEncryptorBindings().addFactoriesInstallBinding().build().configure(binder());
 
         bind(DBAccessor.class).toInstance(dbAccessor);
         bind(OsFamily.class).toInstance(osFamily);

Reply via email to