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/297e5b9f Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/297e5b9f Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/297e5b9f Branch: refs/heads/branch-feature-AMBARI-20859 Commit: 297e5b9f81e64dcd46f736f3f10609ecbc1cd7d6 Parents: 85ef611 Author: Sandor Molnar <[email protected]> Authored: Mon Dec 18 23:38:29 2017 -0500 Committer: Robert Levas <[email protected]> Committed: Fri Jan 5 17:54:17 2018 -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/297e5b9f/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/297e5b9f/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/297e5b9f/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 2c203c9..9cc7ad0 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 @@ -2774,6 +2774,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); + /** * 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 @@ -5723,6 +5726,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> @@ -5899,7 +5909,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/297e5b9f/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/297e5b9f/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/297e5b9f/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/297e5b9f/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/297e5b9f/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/297e5b9f/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/297e5b9f/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/297e5b9f/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));
