ambari-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From rle...@apache.org
Subject ambari git commit: AMBARI-22571. Handle passwords/sensitive data in Ambari configuration properties (Sandor Molnar via rlevas)
Date Tue, 19 Dec 2017 04:38:55 GMT
Repository: ambari
Updated Branches:
  refs/heads/trunk afe469c98 -> be0e87bef


AMBARI-22571. Handle passwords/sensitive data in Ambari configuration properties (Sandor Molnar via rlevas)


Project: http://git-wip-us.apache.org/repos/asf/ambari/repo
Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/be0e87be
Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/be0e87be
Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/be0e87be

Branch: refs/heads/trunk
Commit: be0e87bef52458955eddf1b824e6c5ddc6f31de6
Parents: afe469c
Author: Sandor Molnar <smolnar@hortonworks.com>
Authored: Mon Dec 18 23:38:29 2017 -0500
Committer: Robert Levas <rlevas@hortonworks.com>
Committed: Mon Dec 18 23:38:29 2017 -0500

----------------------------------------------------------------------
 ambari-server/pom.xml                           |   2 +-
 .../RootServiceComponentConfiguration.java      |  73 +++++++
 .../server/configuration/Configuration.java     |  11 +-
 .../ConfigurationPropertyType.java              |  22 +++
 .../AmbariServerConfigurationHandler.java       | 102 ++++++++--
 .../AmbariServerConfigurationUtils.java         |  78 ++++++++
 .../AmbariServerLDAPConfigurationHandler.java   |   9 +-
 ...ootServiceComponentConfigurationHandler.java |   7 +-
 ...eComponentConfigurationResourceProvider.java |  18 +-
 .../domain/AmbariLdapConfigurationKeys.java     |  77 ++++----
 ...ponentConfigurationResourceProviderTest.java | 192 +++++++++++++------
 11 files changed, 473 insertions(+), 118 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/be0e87be/ambari-server/pom.xml
----------------------------------------------------------------------
diff --git a/ambari-server/pom.xml b/ambari-server/pom.xml
index 2ecf435..5a0afb7 100644
--- a/ambari-server/pom.xml
+++ b/ambari-server/pom.xml
@@ -1169,7 +1169,7 @@
     <dependency>
       <groupId>commons-io</groupId>
       <artifactId>commons-io</artifactId>
-      <version>2.4</version>
+      <version>2.5</version>
     </dependency>
     <dependency>
       <groupId>org.apache.commons</groupId>

http://git-wip-us.apache.org/repos/asf/ambari/blob/be0e87be/ambari-server/src/main/java/org/apache/ambari/server/api/services/RootServiceComponentConfiguration.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/api/services/RootServiceComponentConfiguration.java b/ambari-server/src/main/java/org/apache/ambari/server/api/services/RootServiceComponentConfiguration.java
new file mode 100644
index 0000000..cd31c75
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/api/services/RootServiceComponentConfiguration.java
@@ -0,0 +1,73 @@
+/*
+ * 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
+ *
+ *     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 org.apache.ambari.server.api.services;
+
+import java.util.Map;
+import java.util.TreeMap;
+
+import org.apache.commons.lang3.builder.EqualsBuilder;
+import org.apache.commons.lang3.builder.HashCodeBuilder;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+
+public class RootServiceComponentConfiguration {
+
+  private final Map<String, String> properties;
+  private final Map<String, String> propertyTypes;
+
+  public RootServiceComponentConfiguration() {
+    this(new TreeMap<>(), new TreeMap<>());
+  }
+
+  public RootServiceComponentConfiguration(Map<String, String> properties, Map<String, String> propertyTypes) {
+    this.properties = properties == null ? new TreeMap<>() : properties;
+    this.propertyTypes = propertyTypes == null ? new TreeMap<>() : propertyTypes;
+  }
+
+  public void addProperty(String propertyName, String propertyValue) {
+    properties.put(propertyName, propertyValue);
+  }
+
+  public void addPropertyType(String propertyName, String propertyType) {
+    propertyTypes.put(propertyName, propertyType);
+  }
+
+  public Map<String, String> getProperties() {
+    return properties;
+  }
+
+  public Map<String, String> getPropertyTypes() {
+    return propertyTypes;
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    return EqualsBuilder.reflectionEquals(this, obj);
+  }
+
+  @Override
+  public int hashCode() {
+    return HashCodeBuilder.reflectionHashCode(3, 19, this);
+  }
+
+  @Override
+  public String toString() {
+    return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE);
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/be0e87be/ambari-server/src/main/java/org/apache/ambari/server/configuration/Configuration.java
----------------------------------------------------------------------
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 ea44a90..7003c98 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
@@ -2783,6 +2783,9 @@ public class Configuration {
   public static final ConfigurationProperty<String> DISPATCH_PROPERTY_SCRIPT_DIRECTORY = new ConfigurationProperty<>(
           "notification.dispatch.alert.script.directory",AmbariPath.getPath("/var/lib/ambari-server/resources/scripts"));
 
+  @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<Boolean>("security.passwords.encryption.enabled", false);
+
 
   private static final Logger LOG = LoggerFactory.getLogger(
     Configuration.class);
@@ -5693,6 +5696,13 @@ public class Configuration {
   }
 
   /**
+   * @return  whether security password encryption is enabled or not (defaults to {@code false})
+   */
+  public boolean isSecurityPasswordEncryptionEnabled() {
+    return Boolean.parseBoolean(getProperty(SECURITY_PASSWORD_ENCRYPTON_ENABLED));
+  }
+
+  /**
    * Generates a markdown table which includes:
    * <ul>
    * <li>Property key name</li>
@@ -5869,7 +5879,6 @@ public class Configuration {
     private ConfigurationProperty(String key, T defaultValue) {
       m_key = key;
       m_defaultValue = defaultValue;
-
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/ambari/blob/be0e87be/ambari-server/src/main/java/org/apache/ambari/server/configuration/ConfigurationPropertyType.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/configuration/ConfigurationPropertyType.java b/ambari-server/src/main/java/org/apache/ambari/server/configuration/ConfigurationPropertyType.java
new file mode 100644
index 0000000..2e61c19
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/configuration/ConfigurationPropertyType.java
@@ -0,0 +1,22 @@
+/*
+ * 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 org.apache.ambari.server.configuration;
+
+/**
+ * Constants representing types for AMBARI-level properties that are being stored in the DB
+ */
+public enum ConfigurationPropertyType {
+  PLAINTEXT, PASSWORD;
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/be0e87be/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AmbariServerConfigurationHandler.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AmbariServerConfigurationHandler.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AmbariServerConfigurationHandler.java
index 34285d6..f3ff2dd 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AmbariServerConfigurationHandler.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AmbariServerConfigurationHandler.java
@@ -18,23 +18,33 @@
 
 package org.apache.ambari.server.controller.internal;
 
+import java.io.File;
+import java.io.IOException;
+import java.nio.charset.Charset;
 import java.util.HashMap;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
-import java.util.TreeMap;
 
+import org.apache.ambari.server.AmbariException;
 import org.apache.ambari.server.StaticallyInject;
+import org.apache.ambari.server.api.services.RootServiceComponentConfiguration;
+import org.apache.ambari.server.configuration.Configuration;
 import org.apache.ambari.server.controller.spi.NoSuchResourceException;
 import org.apache.ambari.server.controller.spi.SystemException;
 import org.apache.ambari.server.events.AmbariConfigurationChangedEvent;
 import org.apache.ambari.server.events.publishers.AmbariEventPublisher;
 import org.apache.ambari.server.orm.dao.AmbariConfigurationDAO;
 import org.apache.ambari.server.orm.entities.AmbariConfigurationEntity;
+import org.apache.ambari.server.security.encryption.CredentialProvider;
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.lang3.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import com.google.inject.Inject;
 
+
 /**
  * AmbariServerConfigurationHandler handles Ambari server specific configuration properties.
  */
@@ -48,11 +58,15 @@ class AmbariServerConfigurationHandler extends RootServiceComponentConfiguration
   @Inject
   private static AmbariEventPublisher publisher;
 
+  @Inject
+  private static Configuration ambariConfiguration;
+
+  private CredentialProvider credentialProvider;
 
   @Override
-  public Map<String, Map<String, String>> getConfigurations(String categoryName)
+  public Map<String, RootServiceComponentConfiguration> getConfigurations(String categoryName)
       throws NoSuchResourceException {
-    Map<String, Map<String, String>> configurations = null;
+    Map<String, RootServiceComponentConfiguration> configurations = null;
 
     List<AmbariConfigurationEntity> entities = (categoryName == null)
         ? ambariConfigurationDAO.findAll()
@@ -60,17 +74,18 @@ class AmbariServerConfigurationHandler extends RootServiceComponentConfiguration
 
     if (entities != null) {
       configurations = new HashMap<>();
-
       for (AmbariConfigurationEntity entity : entities) {
         String category = entity.getCategoryName();
-        Map<String, String> properties = configurations.get(category);
-
-        if (properties == null) {
-          properties = new TreeMap<>();
-          configurations.put(category, properties);
+        RootServiceComponentConfiguration configuration = configurations.get(category);
+        if (configuration == null) {
+          configuration = new RootServiceComponentConfiguration();
+          configurations.put(category, configuration);
         }
 
-        properties.put(entity.getPropertyName(), entity.getPropertyValue());
+        configuration.addProperty(entity.getPropertyName(), entity.getPropertyValue());
+        if (categoryName != null) {
+          configuration.addPropertyType(entity.getPropertyName(), AmbariServerConfigurationUtils.getConfigurationPropertyTypeName(categoryName, entity.getPropertyName()));
+        }
       }
     }
 
@@ -94,13 +109,76 @@ class AmbariServerConfigurationHandler extends RootServiceComponentConfiguration
   }
 
   @Override
-  public void updateCategory(String categoryName, Map<String, String> properties, boolean removePropertiesIfNotSpecified) {
-    if (ambariConfigurationDAO.reconcileCategory(categoryName, properties, removePropertiesIfNotSpecified)) {
+  public void updateCategory(String categoryName, Map<String, String> properties, boolean removePropertiesIfNotSpecified) throws AmbariException {
+    boolean toBePublished = false;
+    final Iterator<Map.Entry<String, String>> propertiesIterator = properties.entrySet().iterator();
+    while (propertiesIterator.hasNext()) {
+      Map.Entry<String, String> property = propertiesIterator.next();
+      if (AmbariServerConfigurationUtils.isPassword(categoryName, property.getKey())) {
+        if (updatePasswordIfNeeded(categoryName, property.getKey(), property.getValue())) {
+          toBePublished = true;
+        }
+        propertiesIterator.remove(); //we do not need to change the any PASSWORD type configuration going forward
+      }
+    }
+
+    if (!properties.isEmpty()) {
+      toBePublished = ambariConfigurationDAO.reconcileCategory(categoryName, properties, removePropertiesIfNotSpecified) || toBePublished;
+    }
+
+    if (toBePublished) {
       // notify subscribers about the configuration changes
       publisher.publish(new AmbariConfigurationChangedEvent(categoryName));
     }
   }
 
+  private boolean updatePasswordIfNeeded(String categoryName, String propertyName, String newPassword) throws AmbariException {
+    if (newPassword != null) {
+      final String passwordFileOrCredentailStoreAlias = fetchPasswordFileNameOrCredentialStoreAlias(categoryName, propertyName);
+      if (!newPassword.equals(passwordFileOrCredentailStoreAlias)) { //we only need to do anything if the user-supplied password is a 'real' password
+        if (ambariConfiguration.isSecurityPasswordEncryptionEnabled()) {
+          getCredentialProvider().addAliasToCredentialStore(passwordFileOrCredentailStoreAlias, newPassword);
+        } else {
+          savePasswordInFile(passwordFileOrCredentailStoreAlias, newPassword);
+        }
+        return true;
+      }
+    }
+    return false;
+  }
+
+  /*
+   * If the configuration element is actually a PASSWORD type element then we either have a password file name stored in the DB
+   * or - in case security password encryption is enabled - a Credential Store alias.
+   */
+  private String fetchPasswordFileNameOrCredentialStoreAlias(String categoryName, String propertyName) {
+    for (AmbariConfigurationEntity entity : ambariConfigurationDAO.findByCategory(categoryName)) {
+      if (entity.getPropertyName().equals(propertyName)) {
+        return entity.getPropertyValue();
+      }
+    }
+
+    return null;
+  }
+
+  private CredentialProvider getCredentialProvider() throws AmbariException {
+    if (credentialProvider == null) {
+      credentialProvider = new CredentialProvider(null, ambariConfiguration.getMasterKeyLocation(),
+          ambariConfiguration.isMasterKeyPersisted(), ambariConfiguration.getMasterKeyStoreLocation());
+    }
+    return credentialProvider;
+  }
+
+  private void savePasswordInFile(String passwordFileName, String newPassword) throws AmbariException {
+    try {
+      if (StringUtils.isNotBlank(passwordFileName)) {
+        FileUtils.writeStringToFile(new File(passwordFileName), newPassword, Charset.defaultCharset());
+      }
+    } catch (IOException e) {
+      throw new AmbariException("Error while updating password file [" + passwordFileName + "]", e);
+    }
+  }
+
   @Override
   public OperationResult performOperation(String categoryName, Map<String, String> properties,
                                           boolean mergeExistingProperties, String operation,

http://git-wip-us.apache.org/repos/asf/ambari/blob/be0e87be/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AmbariServerConfigurationUtils.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AmbariServerConfigurationUtils.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AmbariServerConfigurationUtils.java
new file mode 100644
index 0000000..ac05f93
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AmbariServerConfigurationUtils.java
@@ -0,0 +1,78 @@
+/*
+ * 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
+ *
+ *     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 org.apache.ambari.server.controller.internal;
+
+import org.apache.ambari.server.configuration.ConfigurationPropertyType;
+import org.apache.ambari.server.ldap.domain.AmbariLdapConfigurationKeys;
+
+/**
+ * Provides useful utility methods for AMBARI-level configuration related tasks.
+ */
+public class AmbariServerConfigurationUtils {
+
+  /**
+   * @param category
+   *          the name of the category
+   * @param propertyName
+   *          the name of the property
+   * @return the type of the given category/property if such category/property
+   *         exists; {@code null} otherwise
+   * @throws IllegalStateException
+   *           if there is no property found with the given name
+   */
+  public static ConfigurationPropertyType getConfigurationPropertyType(String category, String propertyName) {
+    if (AmbariServerConfigurationCategory.LDAP_CONFIGURATION.getCategoryName().equals(category)) {
+      return AmbariLdapConfigurationKeys.fromKeyStr(propertyName).getConfigurationPropertyType();
+    }
+    return null;
+  }
+
+  /**
+   * @param category
+   *          the name of the category
+   * @param propertyName
+   *          the name of the property
+   * @return the String representation of the type if such category/property
+   *         exists; {@code null} otherwise * @throws IllegalStateException if
+   *         there is no property found with the given name
+   */
+  public static String getConfigurationPropertyTypeName(String category, String propertyName) {
+    final ConfigurationPropertyType configurationPropertyType = getConfigurationPropertyType(category, propertyName);
+    return configurationPropertyType == null ? null : configurationPropertyType.name();
+  }
+
+  /**
+   * Indicates whether the given property's type is
+   * 
+   * {@link ConfigurationPropertyType.PASSWORD}
+   *
+   * @param category
+   *          the name of the category
+   * @param propertyName
+   *          the name of the property
+   * @return {@code true} in case the given property's type is
+   *         {@link ConfigurationPropertyType.PASSWORD}; {@code false} otherwise
+   * @throws IllegalStateException
+   *           if there is no property found with the given name
+   */
+  public static boolean isPassword(String category, String propertyName) {
+    return ConfigurationPropertyType.PASSWORD.equals(getConfigurationPropertyType(category, propertyName));
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/be0e87be/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AmbariServerLDAPConfigurationHandler.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AmbariServerLDAPConfigurationHandler.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AmbariServerLDAPConfigurationHandler.java
index 6f16c49..c4dca80 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AmbariServerLDAPConfigurationHandler.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AmbariServerLDAPConfigurationHandler.java
@@ -24,6 +24,7 @@ import java.util.Map;
 import java.util.Set;
 
 import org.apache.ambari.server.StaticallyInject;
+import org.apache.ambari.server.api.services.RootServiceComponentConfiguration;
 import org.apache.ambari.server.controller.spi.NoSuchResourceException;
 import org.apache.ambari.server.controller.spi.SystemException;
 import org.apache.ambari.server.ldap.domain.AmbariLdapConfiguration;
@@ -44,7 +45,7 @@ class AmbariServerLDAPConfigurationHandler extends AmbariServerConfigurationHand
 
   @Inject
   private static LdapFacade ldapFacade;
-
+  
   @Override
   public OperationResult performOperation(String categoryName, Map<String, String> properties,
                                           boolean mergeExistingProperties, String operation, Map<String, Object> operationParameters) throws SystemException {
@@ -64,12 +65,12 @@ class AmbariServerLDAPConfigurationHandler extends AmbariServerConfigurationHand
     // to retrieve if. If one does not exist, that is ok.
     if (mergeExistingProperties) {
       try {
-        Map<String, Map<String, String>> _configurations = getConfigurations(categoryName);
+        Map<String, RootServiceComponentConfiguration> _configurations = getConfigurations(categoryName);
         if (_configurations != null) {
-          Map<String, String> _ldapProperties = _configurations.get(categoryName);
+          RootServiceComponentConfiguration _ldapProperties = _configurations.get(categoryName);
 
           if (_ldapProperties != null) {
-            ldapConfigurationProperties.putAll(_ldapProperties);
+            ldapConfigurationProperties.putAll(_ldapProperties.getProperties());
           }
         }
       } catch (NoSuchResourceException e) {

http://git-wip-us.apache.org/repos/asf/ambari/blob/be0e87be/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/RootServiceComponentConfigurationHandler.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/RootServiceComponentConfigurationHandler.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/RootServiceComponentConfigurationHandler.java
index 3b58ce1..e9e4c9e 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/RootServiceComponentConfigurationHandler.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/RootServiceComponentConfigurationHandler.java
@@ -20,6 +20,8 @@ package org.apache.ambari.server.controller.internal;
 
 import java.util.Map;
 
+import org.apache.ambari.server.AmbariException;
+import org.apache.ambari.server.api.services.RootServiceComponentConfiguration;
 import org.apache.ambari.server.controller.spi.NoSuchResourceException;
 import org.apache.ambari.server.controller.spi.SystemException;
 
@@ -35,7 +37,7 @@ abstract class RootServiceComponentConfigurationHandler {
    * @return a map of category names to properties (name/value pairs).
    * @throws NoSuchResourceException if the requested data is not found
    */
-  public abstract Map<String, Map<String, String>> getConfigurations(String categoryName) throws NoSuchResourceException;
+  public abstract Map<String, RootServiceComponentConfiguration> getConfigurations(String categoryName) throws NoSuchResourceException;
 
   /**
    * Delete the requested configuration.
@@ -58,8 +60,9 @@ abstract class RootServiceComponentConfigurationHandler {
    * @param properties                     a map of properties to set
    * @param removePropertiesIfNotSpecified <code>true</code> to ensure the set of properties are only those that have be explicitly specified;
    *                                       <code>false</code> to update the set of existing properties with the specified set of properties, adding missing properties but not removing any properties
+   * @throws AmbariException in case an error occurred while updating category's properties
    */
-  public abstract void updateCategory(String categoryName, Map<String, String> properties, boolean removePropertiesIfNotSpecified);
+  public abstract void updateCategory(String categoryName, Map<String, String> properties, boolean removePropertiesIfNotSpecified) throws AmbariException;
 
   /**
    * Preform some operation on the set of data for a category.

http://git-wip-us.apache.org/repos/asf/ambari/blob/be0e87be/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/RootServiceComponentConfigurationResourceProvider.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/RootServiceComponentConfigurationResourceProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/RootServiceComponentConfigurationResourceProvider.java
index 74f8a4d..543cf94 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/RootServiceComponentConfigurationResourceProvider.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/RootServiceComponentConfigurationResourceProvider.java
@@ -26,6 +26,7 @@ import java.util.Map;
 import java.util.Set;
 
 import org.apache.ambari.server.AmbariException;
+import org.apache.ambari.server.api.services.RootServiceComponentConfiguration;
 import org.apache.ambari.server.api.services.RootServiceComponentConfigurationService;
 import org.apache.ambari.server.controller.spi.NoSuchParentResourceException;
 import org.apache.ambari.server.controller.spi.NoSuchResourceException;
@@ -50,6 +51,7 @@ public class RootServiceComponentConfigurationResourceProvider extends AbstractA
 
   public static final String CONFIGURATION_CATEGORY_PROPERTY_ID = PropertyHelper.getPropertyId(RESOURCE_KEY, "category");
   public static final String CONFIGURATION_PROPERTIES_PROPERTY_ID = PropertyHelper.getPropertyId(RESOURCE_KEY, "properties");
+  public static final String CONFIGURATION_PROPERTY_TYPES_PROPERTY_ID = PropertyHelper.getPropertyId(RESOURCE_KEY, "property_types");
   public static final String CONFIGURATION_COMPONENT_NAME_PROPERTY_ID = PropertyHelper.getPropertyId(RESOURCE_KEY, "component_name");
   public static final String CONFIGURATION_SERVICE_NAME_PROPERTY_ID = PropertyHelper.getPropertyId(RESOURCE_KEY, "service_name");
 
@@ -65,6 +67,7 @@ public class RootServiceComponentConfigurationResourceProvider extends AbstractA
     set.add(CONFIGURATION_COMPONENT_NAME_PROPERTY_ID);
     set.add(CONFIGURATION_CATEGORY_PROPERTY_ID);
     set.add(CONFIGURATION_PROPERTIES_PROPERTY_ID);
+    set.add(CONFIGURATION_PROPERTY_TYPES_PROPERTY_ID);
 
     PROPERTIES = Collections.unmodifiableSet(set);
 
@@ -202,12 +205,13 @@ public class RootServiceComponentConfigurationResourceProvider extends AbstractA
     return getRequestStatus(null, null, operationStatusMetadata);
   }
 
-  private Resource toResource(String serviceName, String componentName, String categoryName, Map<String, String> properties, Set<String> requestedIds) {
+  private Resource toResource(String serviceName, String componentName, String categoryName, Map<String, String> properties, Map<String, String> propertyTypes, Set<String> requestedIds) {
     Resource resource = new ResourceImpl(Resource.Type.RootServiceComponentConfiguration);
     setResourceProperty(resource, CONFIGURATION_SERVICE_NAME_PROPERTY_ID, serviceName, requestedIds);
     setResourceProperty(resource, CONFIGURATION_COMPONENT_NAME_PROPERTY_ID, componentName, requestedIds);
     setResourceProperty(resource, CONFIGURATION_CATEGORY_PROPERTY_ID, categoryName, requestedIds);
     setResourceProperty(resource, CONFIGURATION_PROPERTIES_PROPERTY_ID, properties, requestedIds);
+    setResourceProperty(resource, CONFIGURATION_PROPERTY_TYPES_PROPERTY_ID, propertyTypes, requestedIds);
     return resource;
   }
 
@@ -238,7 +242,11 @@ public class RootServiceComponentConfigurationResourceProvider extends AbstractA
 
         RootServiceComponentConfigurationHandler handler = rootServiceComponentConfigurationHandlerFactory.getInstance(requestDetails.serviceName, requestDetails.componentName, requestDetails.categoryName);
         if (handler != null) {
-          handler.updateCategory(requestDetails.categoryName, requestDetails.properties, removePropertiesIfNotSpecified);
+          try {
+            handler.updateCategory(requestDetails.categoryName, requestDetails.properties, removePropertiesIfNotSpecified);
+          } catch (AmbariException e) {
+            throw new SystemException(e.getMessage(), e.getCause());
+          }
         } else {
           throw new SystemException(String.format("Configurations may not be updated for the %s component of the root service, %s", requestDetails.serviceName, requestDetails.componentName));
         }
@@ -390,11 +398,11 @@ public class RootServiceComponentConfigurationResourceProvider extends AbstractA
     RootServiceComponentConfigurationHandler handler = rootServiceComponentConfigurationHandlerFactory.getInstance(serviceName, componentName, categoryName);
 
     if (handler != null) {
-      Map<String, Map<String, String>> configurations = handler.getConfigurations(categoryName);
+      Map<String, RootServiceComponentConfiguration> configurations = handler.getConfigurations(categoryName);
 
       if (configurations != null) {
-        for (Map.Entry<String, Map<String, String>> entry : configurations.entrySet()) {
-          resources.add(toResource(serviceName, componentName, entry.getKey(), entry.getValue(), requestedIds));
+        for (Map.Entry<String, RootServiceComponentConfiguration> entry : configurations.entrySet()) {
+          resources.add(toResource(serviceName, componentName, entry.getKey(), entry.getValue().getProperties(), entry.getValue().getPropertyTypes(), requestedIds));
         }
       }
     }

http://git-wip-us.apache.org/repos/asf/ambari/blob/be0e87be/ambari-server/src/main/java/org/apache/ambari/server/ldap/domain/AmbariLdapConfigurationKeys.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/ldap/domain/AmbariLdapConfigurationKeys.java b/ambari-server/src/main/java/org/apache/ambari/server/ldap/domain/AmbariLdapConfigurationKeys.java
index 2e1c36b..b7a713b 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/ldap/domain/AmbariLdapConfigurationKeys.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/ldap/domain/AmbariLdapConfigurationKeys.java
@@ -14,62 +14,73 @@
 
 package org.apache.ambari.server.ldap.domain;
 
+import static org.apache.ambari.server.configuration.ConfigurationPropertyType.PASSWORD;
+import static org.apache.ambari.server.configuration.ConfigurationPropertyType.PLAINTEXT;
+
+import org.apache.ambari.server.configuration.ConfigurationPropertyType;
+
 /**
  * Constants representing supported LDAP related property names
- * // todo extend this with validation information, description, defaults maybe
+ * // TODO: extend this with validation information, description, defaults maybe
  */
 public enum AmbariLdapConfigurationKeys {
 
-  LDAP_ENABLED("ambari.ldap.authentication.enabled"),
-  SERVER_HOST("ambari.ldap.connectivity.server.host"),
-  SERVER_PORT("ambari.ldap.connectivity.server.port"),
-  USE_SSL("ambari.ldap.connectivity.use_ssl"),
+  LDAP_ENABLED("ambari.ldap.authentication.enabled", PLAINTEXT),
+  SERVER_HOST("ambari.ldap.connectivity.server.host", PLAINTEXT),
+  SERVER_PORT("ambari.ldap.connectivity.server.port", PLAINTEXT),
+  USE_SSL("ambari.ldap.connectivity.use_ssl", PLAINTEXT),
 
-  TRUST_STORE("ambari.ldap.connectivity.trust_store"),
-  TRUST_STORE_TYPE("ambari.ldap.connectivity.trust_store.type"),
-  TRUST_STORE_PATH("ambari.ldap.connectivity.trust_store.path"),
-  TRUST_STORE_PASSWORD("ambari.ldap.connectivity.trust_store.password"),
-  ANONYMOUS_BIND("ambari.ldap.connectivity.anonymous_bind"),
+  TRUST_STORE("ambari.ldap.connectivity.trust_store", PLAINTEXT),
+  TRUST_STORE_TYPE("ambari.ldap.connectivity.trust_store.type", PLAINTEXT),
+  TRUST_STORE_PATH("ambari.ldap.connectivity.trust_store.path", PLAINTEXT),
+  TRUST_STORE_PASSWORD("ambari.ldap.connectivity.trust_store.password", PASSWORD),
+  ANONYMOUS_BIND("ambari.ldap.connectivity.anonymous_bind", PLAINTEXT),
 
-  BIND_DN("ambari.ldap.connectivity.bind_dn"),
-  BIND_PASSWORD("ambari.ldap.connectivity.bind_password"),
+  BIND_DN("ambari.ldap.connectivity.bind_dn", PLAINTEXT),
+  BIND_PASSWORD("ambari.ldap.connectivity.bind_password", PASSWORD),
 
-  ATTR_DETECTION("ambari.ldap.attributes.detection"), // manual | auto
+  ATTR_DETECTION("ambari.ldap.attributes.detection", PLAINTEXT), // manual | auto
 
-  DN_ATTRIBUTE("ambari.ldap.attributes.dn_attr"),
+  DN_ATTRIBUTE("ambari.ldap.attributes.dn_attr", PLAINTEXT),
 
-  USER_OBJECT_CLASS("ambari.ldap.attributes.user.object_class"),
-  USER_NAME_ATTRIBUTE("ambari.ldap.attributes.user.name_attr"),
-  USER_GROUP_MEMBER_ATTRIBUTE("ambari.ldap.attributes.user.group_member_attr"),
-  USER_SEARCH_BASE("ambari.ldap.attributes.user.search_base"),
+  USER_OBJECT_CLASS("ambari.ldap.attributes.user.object_class", PLAINTEXT),
+  USER_NAME_ATTRIBUTE("ambari.ldap.attributes.user.name_attr", PLAINTEXT),
+  USER_GROUP_MEMBER_ATTRIBUTE("ambari.ldap.attributes.user.group_member_attr", PLAINTEXT),
+  USER_SEARCH_BASE("ambari.ldap.attributes.user.search_base", PLAINTEXT),
 
-  GROUP_OBJECT_CLASS("ambari.ldap.attributes.group.object_class"),
-  GROUP_NAME_ATTRIBUTE("ambari.ldap.attributes.group.name_attr"),
-  GROUP_MEMBER_ATTRIBUTE("ambari.ldap.attributes.group.member_attr"),
-  GROUP_SEARCH_BASE("ambari.ldap.attributes.group.search_base"),
+  GROUP_OBJECT_CLASS("ambari.ldap.attributes.group.object_class", PLAINTEXT),
+  GROUP_NAME_ATTRIBUTE("ambari.ldap.attributes.group.name_attr", PLAINTEXT),
+  GROUP_MEMBER_ATTRIBUTE("ambari.ldap.attributes.group.member_attr", PLAINTEXT),
+  GROUP_SEARCH_BASE("ambari.ldap.attributes.group.search_base", PLAINTEXT),
 
-  USER_SEARCH_FILTER("ambari.ldap.advanced.user_search_filter"),
-  USER_MEMBER_REPLACE_PATTERN("ambari.ldap.advanced.user_member_replace_pattern"),
-  USER_MEMBER_FILTER("ambari.ldap.advanced.user_member_filter"),
+  USER_SEARCH_FILTER("ambari.ldap.advanced.user_search_filter", PLAINTEXT),
+  USER_MEMBER_REPLACE_PATTERN("ambari.ldap.advanced.user_member_replace_pattern", PLAINTEXT),
+  USER_MEMBER_FILTER("ambari.ldap.advanced.user_member_filter", PLAINTEXT),
 
-  GROUP_SEARCH_FILTER("ambari.ldap.advanced.group_search_filter"),
-  GROUP_MEMBER_REPLACE_PATTERN("ambari.ldap.advanced.group_member_replace_pattern"),
-  GROUP_MEMBER_FILTER("ambari.ldap.advanced.group_member_filter"),
+  GROUP_SEARCH_FILTER("ambari.ldap.advanced.group_search_filter", PLAINTEXT),
+  GROUP_MEMBER_REPLACE_PATTERN("ambari.ldap.advanced.group_member_replace_pattern", PLAINTEXT),
+  GROUP_MEMBER_FILTER("ambari.ldap.advanced.group_member_filter", PLAINTEXT),
 
-  FORCE_LOWERCASE_USERNAMES("ambari.ldap.advanced.force_lowercase_usernames"),
-  REFERRAL_HANDLING("ambari.ldap.advanced.referrals"), // folow
-  PAGINATION_ENABLED("ambari.ldap.advanced.pagination_enabled"); // true | false
+  FORCE_LOWERCASE_USERNAMES("ambari.ldap.advanced.force_lowercase_usernames", PLAINTEXT),
+  REFERRAL_HANDLING("ambari.ldap.advanced.referrals", PLAINTEXT), // folow
+  PAGINATION_ENABLED("ambari.ldap.advanced.pagination_enabled", PLAINTEXT); // true | false
 
   private String propertyName;
+  private ConfigurationPropertyType configurationPropertyType;
 
-  AmbariLdapConfigurationKeys(String propName) {
+  AmbariLdapConfigurationKeys(String propName, ConfigurationPropertyType configurationPropertyType) {
     this.propertyName = propName;
+    this.configurationPropertyType = configurationPropertyType;
   }
 
   public String key() {
     return this.propertyName;
   }
 
+  public ConfigurationPropertyType getConfigurationPropertyType() {
+    return configurationPropertyType;
+  }
+
   public static AmbariLdapConfigurationKeys fromKeyStr(String keyStr) {
     for (AmbariLdapConfigurationKeys key : values()) {
       if (key.key().equals(keyStr)) {
@@ -77,7 +88,7 @@ public enum AmbariLdapConfigurationKeys {
       }
     }
 
-    throw new IllegalStateException("invalid konfiguration key found!");
+    throw new IllegalStateException("invalid configuration key found!");
 
   }
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/be0e87be/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/RootServiceComponentConfigurationResourceProviderTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/RootServiceComponentConfigurationResourceProviderTest.java b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/RootServiceComponentConfigurationResourceProviderTest.java
index 13c644a..bd0ff1d 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/RootServiceComponentConfigurationResourceProviderTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/RootServiceComponentConfigurationResourceProviderTest.java
@@ -25,6 +25,8 @@ import static org.easymock.EasyMock.expect;
 import static org.easymock.EasyMock.expectLastCall;
 import static org.easymock.EasyMock.newCapture;
 
+import java.io.File;
+import java.nio.charset.Charset;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -36,6 +38,7 @@ import java.util.TreeMap;
 import javax.persistence.EntityManager;
 
 import org.apache.ambari.server.api.services.RootServiceComponentConfigurationService;
+import org.apache.ambari.server.configuration.Configuration;
 import org.apache.ambari.server.controller.RootComponent;
 import org.apache.ambari.server.controller.RootService;
 import org.apache.ambari.server.controller.predicate.AndPredicate;
@@ -47,14 +50,23 @@ import org.apache.ambari.server.controller.spi.SystemException;
 import org.apache.ambari.server.controller.utilities.PredicateBuilder;
 import org.apache.ambari.server.events.AmbariConfigurationChangedEvent;
 import org.apache.ambari.server.events.publishers.AmbariEventPublisher;
+import org.apache.ambari.server.ldap.domain.AmbariLdapConfigurationKeys;
 import org.apache.ambari.server.orm.dao.AmbariConfigurationDAO;
 import org.apache.ambari.server.orm.entities.AmbariConfigurationEntity;
 import org.apache.ambari.server.security.TestAuthenticationFactory;
 import org.apache.ambari.server.security.authorization.AuthorizationException;
+import org.apache.ambari.server.security.encryption.CredentialProvider;
+import org.apache.ambari.server.state.stack.OsFamily;
+import org.apache.commons.io.FileUtils;
 import org.easymock.Capture;
 import org.easymock.EasyMockSupport;
 import org.junit.After;
+import org.junit.Before;
 import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.powermock.api.easymock.PowerMock;
+import org.powermock.core.classloader.annotations.PrepareForTest;
+import org.powermock.modules.junit4.PowerMockRunner;
 import org.springframework.security.core.Authentication;
 import org.springframework.security.core.context.SecurityContextHolder;
 
@@ -64,11 +76,40 @@ import com.google.inject.Injector;
 
 import edu.emory.mathcs.backport.java.util.Collections;
 import junit.framework.Assert;
+import junit.framework.AssertionFailedError;
 
+@RunWith(PowerMockRunner.class)
+@PrepareForTest({FileUtils.class, AmbariServerConfigurationHandler.class})
 public class RootServiceComponentConfigurationResourceProviderTest extends EasyMockSupport {
 
   private static final String CATEGORY_NAME_1 = "test-category-1";
   private static final String CATEGORY_NAME_2 = "test-category-2";
+  private static final String LDAP_CONFIG_CATEGORY = AmbariServerConfigurationCategory.LDAP_CONFIGURATION.getCategoryName();
+
+  private Injector injector;
+  private Predicate predicate;
+  private ResourceProvider resourceProvider;
+  private RootServiceComponentConfigurationHandlerFactory factory;
+  private Request request;
+  private AmbariConfigurationDAO dao;
+  private Configuration configuration;
+  private AmbariEventPublisher publisher;
+  private Map<String, String> properties = new HashMap<>();
+  private Set<Map<String, Object>> propertySets = new HashSet<>();
+
+  @Before
+  public void init() throws Exception {
+    injector = createInjector();
+    resourceProvider = injector.getInstance(RootServiceComponentConfigurationResourceProvider.class);
+    predicate = createPredicate(RootService.AMBARI.name(), RootComponent.AMBARI_SERVER.name(), CATEGORY_NAME_1);
+    request = createMock(Request.class);
+    dao = injector.getInstance(AmbariConfigurationDAO.class);
+    configuration = injector.getInstance(Configuration.class);
+    factory = injector.getInstance(RootServiceComponentConfigurationHandlerFactory.class);
+    publisher = injector.getInstance(AmbariEventPublisher.class);
+    properties = new HashMap<>();
+    propertySets = new HashSet<>();
+  }
 
   @After
   public void clearAuthentication() {
@@ -126,21 +167,14 @@ public class RootServiceComponentConfigurationResourceProviderTest extends EasyM
   }
 
   private void testCreateResources(Authentication authentication, String opDirective) throws Exception {
-    Injector injector = createInjector();
-
-    ResourceProvider resourceProvider = injector.getInstance(RootServiceComponentConfigurationResourceProvider.class);
-
-    Set<Map<String, Object>> propertySets = new HashSet<>();
-
-    Map<String, String> properties1 = new HashMap<>();
-    properties1.put("property1a", "value1");
-    properties1.put("property2a", "value2");
-    propertySets.add(toRequestProperties(CATEGORY_NAME_1, properties1));
+    properties.put(Configuration.AMBARI_PYTHON_WRAP.getKey(), "value1");
+    properties.put(Configuration.AMBARI_DISPLAY_URL.getKey(), "value2");
+    propertySets.add(toRequestProperties(CATEGORY_NAME_1, properties));
 
     Map<String, String> properties2 = new HashMap<>();
     if (opDirective == null) {
-      properties2.put("property1b", "value1");
-      properties2.put("property2b", "value2");
+      properties2.put(Configuration.SSL_TRUSTSTORE_TYPE.getKey(), "value1");
+      properties2.put(Configuration.SSL_TRUSTSTORE_PATH.getKey(), "value2");
       propertySets.add(toRequestProperties(CATEGORY_NAME_2, properties2));
     }
 
@@ -151,7 +185,6 @@ public class RootServiceComponentConfigurationResourceProviderTest extends EasyM
       requestInfoProperties = Collections.singletonMap(RootServiceComponentConfigurationService.DIRECTIVE_OPERATION, opDirective);
     }
 
-    Request request = createMock(Request.class);
     expect(request.getProperties()).andReturn(propertySets).once();
     expect(request.getRequestInfoProperties()).andReturn(requestInfoProperties).once();
 
@@ -159,7 +192,6 @@ public class RootServiceComponentConfigurationResourceProviderTest extends EasyM
     Capture<Map<String, String>> capturedProperties2 = newCapture();
 
     if (opDirective == null) {
-      AmbariConfigurationDAO dao = injector.getInstance(AmbariConfigurationDAO.class);
       expect(dao.reconcileCategory(eq(CATEGORY_NAME_1), capture(capturedProperties1), eq(true)))
           .andReturn(true)
           .once();
@@ -168,12 +200,10 @@ public class RootServiceComponentConfigurationResourceProviderTest extends EasyM
           .once();
 
 
-      AmbariEventPublisher publisher = injector.getInstance(AmbariEventPublisher.class);
       publisher.publish(anyObject(AmbariConfigurationChangedEvent.class));
       expectLastCall().times(2);
     }
 
-    RootServiceComponentConfigurationHandlerFactory factory = injector.getInstance(RootServiceComponentConfigurationHandlerFactory.class);
     expect(factory.getInstance(RootService.AMBARI.name(), RootComponent.AMBARI_SERVER.name(), CATEGORY_NAME_1))
         .andReturn(new AmbariServerConfigurationHandler())
         .once();
@@ -205,7 +235,7 @@ public class RootServiceComponentConfigurationResourceProviderTest extends EasyM
     verifyAll();
 
     if (opDirective == null) {
-      validateCapturedProperties(properties1, capturedProperties1);
+      validateCapturedProperties(properties, capturedProperties1);
       validateCapturedProperties(properties2, capturedProperties2);
     } else {
       Assert.assertFalse(capturedProperties1.hasCaptured());
@@ -239,22 +269,11 @@ public class RootServiceComponentConfigurationResourceProviderTest extends EasyM
   }
 
   private void testDeleteResources(Authentication authentication) throws Exception {
-    Injector injector = createInjector();
-
-    ResourceProvider resourceProvider = injector.getInstance(RootServiceComponentConfigurationResourceProvider.class);
-
-    Predicate predicate = createPredicate(RootService.AMBARI.name(), RootComponent.AMBARI_SERVER.name(), CATEGORY_NAME_1);
-
-    Request request = createMock(Request.class);
-
-    AmbariConfigurationDAO dao = injector.getInstance(AmbariConfigurationDAO.class);
     expect(dao.removeByCategory(CATEGORY_NAME_1)).andReturn(1).once();
 
-    AmbariEventPublisher publisher = injector.getInstance(AmbariEventPublisher.class);
     publisher.publish(anyObject(AmbariConfigurationChangedEvent.class));
     expectLastCall().once();
 
-    RootServiceComponentConfigurationHandlerFactory factory = injector.getInstance(RootServiceComponentConfigurationHandlerFactory.class);
     expect(factory.getInstance(RootService.AMBARI.name(), RootComponent.AMBARI_SERVER.name(), CATEGORY_NAME_1))
         .andReturn(new AmbariServerConfigurationHandler())
         .once();
@@ -294,25 +313,15 @@ public class RootServiceComponentConfigurationResourceProviderTest extends EasyM
   }
 
   private void testGetResources(Authentication authentication) throws Exception {
-    Injector injector = createInjector();
-
-    ResourceProvider resourceProvider = injector.getInstance(RootServiceComponentConfigurationResourceProvider.class);
-
-    Predicate predicate = createPredicate(RootService.AMBARI.name(), RootComponent.AMBARI_SERVER.name(), CATEGORY_NAME_1);
-
-    Request request = createMock(Request.class);
     expect(request.getPropertyIds()).andReturn(null).anyTimes();
 
-    Map<String, String> properties = new HashMap<>();
-    properties.put("property1a", "value1");
-    properties.put("property2a", "value2");
+    properties.put(AmbariLdapConfigurationKeys.ANONYMOUS_BIND.key(), "value1");
+    properties.put(AmbariLdapConfigurationKeys.GROUP_MEMBER_ATTRIBUTE.key(), "value2");
 
-    AmbariConfigurationDAO dao = injector.getInstance(AmbariConfigurationDAO.class);
     expect(dao.findByCategory(CATEGORY_NAME_1)).andReturn(createEntities(CATEGORY_NAME_1, properties)).once();
 
-    RootServiceComponentConfigurationHandlerFactory factory = injector.getInstance(RootServiceComponentConfigurationHandlerFactory.class);
     expect(factory.getInstance(RootService.AMBARI.name(), RootComponent.AMBARI_SERVER.name(), CATEGORY_NAME_1))
-        .andReturn(new AmbariServerConfigurationHandler())
+        .andReturn(new AmbariServerLDAPConfigurationHandler())
         .once();
 
     replayAll();
@@ -330,16 +339,19 @@ public class RootServiceComponentConfigurationResourceProviderTest extends EasyM
     Assert.assertEquals(Resource.Type.RootServiceComponentConfiguration, resource.getType());
 
     Map<String, Map<String, Object>> propertiesMap = resource.getPropertiesMap();
-    Assert.assertEquals(2, propertiesMap.size());
+    Assert.assertEquals(3, propertiesMap.size());
 
     Assert.assertEquals(CATEGORY_NAME_1, propertiesMap.get(RootServiceComponentConfigurationResourceProvider.RESOURCE_KEY).get("category"));
 
-    Map<String, Object> retrievedProperties = propertiesMap.get(RootServiceComponentConfigurationResourceProvider.RESOURCE_KEY + "/properties");
+    Map<String, Object> retrievedProperties = propertiesMap.get(RootServiceComponentConfigurationResourceProvider.CONFIGURATION_PROPERTIES_PROPERTY_ID);
     Assert.assertEquals(2, retrievedProperties.size());
 
     for (Map.Entry<String, String> entry : properties.entrySet()) {
       Assert.assertEquals(entry.getValue(), retrievedProperties.get(entry.getKey()));
     }
+
+    Map<String, Object> retrievedPropertyTypes = propertiesMap.get(RootServiceComponentConfigurationResourceProvider.CONFIGURATION_PROPERTY_TYPES_PROPERTY_ID);
+    Assert.assertEquals(2, retrievedPropertyTypes.size());
   }
 
   @Test
@@ -393,18 +405,9 @@ public class RootServiceComponentConfigurationResourceProviderTest extends EasyM
   }
 
   private void testUpdateResources(Authentication authentication, String opDirective) throws Exception {
-    Injector injector = createInjector();
-
-    ResourceProvider resourceProvider = injector.getInstance(RootServiceComponentConfigurationResourceProvider.class);
-
-    Predicate predicate = createPredicate(RootService.AMBARI.name(), RootComponent.AMBARI_SERVER.name(), CATEGORY_NAME_1);
-
-    Set<Map<String, Object>> propertySets = new HashSet<>();
-
-    Map<String, String> properties1 = new HashMap<>();
-    properties1.put("property1a", "value1");
-    properties1.put("property2a", "value2");
-    propertySets.add(toRequestProperties(CATEGORY_NAME_1, properties1));
+    properties.put(Configuration.AMBARI_DISPLAY_URL.getKey(), "value1");
+    properties.put(Configuration.BOOTSTRAP_MASTER_HOSTNAME.getKey(), "value2");
+    propertySets.add(toRequestProperties(CATEGORY_NAME_1, properties));
 
     Map<String, String> requestInfoProperties;
     if (opDirective == null) {
@@ -413,24 +416,20 @@ public class RootServiceComponentConfigurationResourceProviderTest extends EasyM
       requestInfoProperties = Collections.singletonMap(RootServiceComponentConfigurationService.DIRECTIVE_OPERATION, opDirective);
     }
 
-    Request request = createMock(Request.class);
     expect(request.getProperties()).andReturn(propertySets).once();
     expect(request.getRequestInfoProperties()).andReturn(requestInfoProperties).once();
 
     Capture<Map<String, String>> capturedProperties1 = newCapture();
 
     if (opDirective == null) {
-      AmbariConfigurationDAO dao = injector.getInstance(AmbariConfigurationDAO.class);
       expect(dao.reconcileCategory(eq(CATEGORY_NAME_1), capture(capturedProperties1), eq(false)))
           .andReturn(true)
           .once();
 
-      AmbariEventPublisher publisher = injector.getInstance(AmbariEventPublisher.class);
       publisher.publish(anyObject(AmbariConfigurationChangedEvent.class));
       expectLastCall().times(1);
     }
 
-    RootServiceComponentConfigurationHandlerFactory factory = injector.getInstance(RootServiceComponentConfigurationHandlerFactory.class);
     expect(factory.getInstance(RootService.AMBARI.name(), RootComponent.AMBARI_SERVER.name(), CATEGORY_NAME_1))
         .andReturn(new AmbariServerConfigurationHandler())
         .once();
@@ -458,12 +457,83 @@ public class RootServiceComponentConfigurationResourceProviderTest extends EasyM
     verifyAll();
 
     if (opDirective == null) {
-      validateCapturedProperties(properties1, capturedProperties1);
+      validateCapturedProperties(properties, capturedProperties1);
     } else {
       Assert.assertFalse(capturedProperties1.hasCaptured());
     }
   }
 
+  @Test
+  public void shouldNotUpdatePasswordIfItHasNotBeenChanged() throws Exception {
+    SecurityContextHolder.getContext().setAuthentication(TestAuthenticationFactory.createAdministrator());
+    properties.put(AmbariLdapConfigurationKeys.BIND_PASSWORD.key(), "passwd");
+    propertySets.add(toRequestProperties(LDAP_CONFIG_CATEGORY, properties));
+    setupBasicExpectations(properties);
+    expect(configuration.isSecurityPasswordEncryptionEnabled()).andThrow(new AssertionFailedError()).anyTimes(); //this call should never have never been hit
+
+    replayAll();
+    resourceProvider.updateResources(request, predicate);
+    verifyAll();
+  }
+
+  @Test
+  public void shouldUpdatePasswordFileIfSecurityPasswordEncryptionIsDisabled() throws Exception {
+    SecurityContextHolder.getContext().setAuthentication(TestAuthenticationFactory.createAdministrator());
+    properties.put(AmbariLdapConfigurationKeys.BIND_PASSWORD.key(), "newPasswd");
+    propertySets.add(toRequestProperties(LDAP_CONFIG_CATEGORY, properties));
+    Map<String, String> expectedProperties = new HashMap<>();
+    expectedProperties.put(AmbariLdapConfigurationKeys.BIND_PASSWORD.key(), "currentPasswd");
+    setupBasicExpectations(expectedProperties);
+    expect(configuration.isSecurityPasswordEncryptionEnabled()).andReturn(false).once();
+    PowerMock.mockStatic(FileUtils.class);
+    FileUtils.writeStringToFile(new File("currentPasswd"), "newPasswd", Charset.defaultCharset());
+    PowerMock.expectLastCall().once();
+    PowerMock.replay(FileUtils.class);
+    publisher.publish(anyObject(AmbariConfigurationChangedEvent.class));
+    expectLastCall().once();
+
+    replayAll();
+    resourceProvider.updateResources(request, predicate);
+    verifyAll();
+  }
+
+  @Test
+  public void shouldUpdatePasswordInCredentialStoreIfSecurityPasswordEncryptionIsEnabled() throws Exception {
+    SecurityContextHolder.getContext().setAuthentication(TestAuthenticationFactory.createAdministrator());
+    properties.put(AmbariLdapConfigurationKeys.BIND_PASSWORD.key(), "newPasswd");
+    propertySets.add(toRequestProperties(LDAP_CONFIG_CATEGORY, properties));
+    Map<String, String> expectedProperties = new HashMap<>();
+    expectedProperties.put(AmbariLdapConfigurationKeys.BIND_PASSWORD.key(), "currentPasswd");
+    setupBasicExpectations(expectedProperties);
+    expect(configuration.isSecurityPasswordEncryptionEnabled()).andReturn(true).once();
+
+    File masterKeyLocation = createNiceMock(File.class);
+    File masterKeyStoreLocation = createNiceMock(File.class);
+    expect(configuration.getMasterKeyLocation()).andReturn(masterKeyLocation).once();
+    expect(configuration.isMasterKeyPersisted()).andReturn(false).once();
+    expect(configuration.getMasterKeyStoreLocation()).andReturn(masterKeyStoreLocation).once();
+    CredentialProvider credentialProvider = PowerMock.createMock(CredentialProvider.class);
+    PowerMock.expectNew(CredentialProvider.class, null, (String) null, masterKeyLocation, false, masterKeyStoreLocation).andReturn(credentialProvider);
+    credentialProvider.addAliasToCredentialStore("currentPasswd", "newPasswd");
+    PowerMock.expectLastCall().once();
+    PowerMock.replay(credentialProvider, CredentialProvider.class);
+
+    publisher.publish(anyObject(AmbariConfigurationChangedEvent.class));
+    expectLastCall().once();
+
+    replayAll();
+    resourceProvider.updateResources(request, predicate);
+    verifyAll();
+    PowerMock.verify(credentialProvider, CredentialProvider.class);
+  }
+
+  private void setupBasicExpectations(Map<String, String> expectedProperties) {
+    expect(request.getProperties()).andReturn(propertySets).once();
+    expect(request.getRequestInfoProperties()).andReturn(new HashMap<>());
+    expect(dao.findByCategory(LDAP_CONFIG_CATEGORY)).andReturn(createEntities(AmbariServerConfigurationCategory.LDAP_CONFIGURATION.getCategoryName(), expectedProperties)).once();
+    expect(factory.getInstance(RootService.AMBARI.name(), RootComponent.AMBARI_SERVER.name(), LDAP_CONFIG_CATEGORY)).andReturn(new AmbariServerLDAPConfigurationHandler()).once();
+  }
+
   private Predicate createPredicate(String serviceName, String componentName, String categoryName) {
     Predicate predicateService = new PredicateBuilder()
         .property(CONFIGURATION_SERVICE_NAME_PROPERTY_ID)
@@ -521,6 +591,8 @@ public class RootServiceComponentConfigurationResourceProviderTest extends EasyM
     return Guice.createInjector(new AbstractModule() {
       @Override
       protected void configure() {
+        bind(OsFamily.class).toInstance(createNiceMock(OsFamily.class));
+        bind(Configuration.class).toInstance(createNiceMock(Configuration.class));
         bind(EntityManager.class).toInstance(createNiceMock(EntityManager.class));
         bind(AmbariConfigurationDAO.class).toInstance(createMock(AmbariConfigurationDAO.class));
         bind(AmbariEventPublisher.class).toInstance(createMock(AmbariEventPublisher.class));


Mime
View raw message