http://git-wip-us.apache.org/repos/asf/ambari/blob/30415a18/ambari-server/src/main/java/org/apache/ambari/server/ldap/LdapModule.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/ldap/LdapModule.java b/ambari-server/src/main/java/org/apache/ambari/server/ldap/LdapModule.java new file mode 100644 index 0000000..089da1d --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/ldap/LdapModule.java @@ -0,0 +1,82 @@ +/* + * 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.ldap; + +import org.apache.ambari.server.ldap.domain.AmbariLdapConfiguration; +import org.apache.ambari.server.ldap.domain.AmbariLdapConfigurationFactory; +import org.apache.ambari.server.ldap.service.AmbariLdapConfigurationProvider; +import org.apache.ambari.server.ldap.service.AmbariLdapFacade; +import org.apache.ambari.server.ldap.service.AttributeDetector; +import org.apache.ambari.server.ldap.service.LdapAttributeDetectionService; +import org.apache.ambari.server.ldap.service.LdapConfigurationService; +import org.apache.ambari.server.ldap.service.LdapConnectionConfigService; +import org.apache.ambari.server.ldap.service.LdapFacade; +import org.apache.ambari.server.ldap.service.ads.DefaultLdapAttributeDetectionService; +import org.apache.ambari.server.ldap.service.ads.DefaultLdapConfigurationService; +import org.apache.ambari.server.ldap.service.ads.DefaultLdapConnectionConfigService; +import org.apache.ambari.server.ldap.service.ads.detectors.AttributeDetectorFactory; +import org.apache.ambari.server.ldap.service.ads.detectors.GroupMemberAttrDetector; +import org.apache.ambari.server.ldap.service.ads.detectors.GroupNameAttrDetector; +import org.apache.ambari.server.ldap.service.ads.detectors.GroupObjectClassDetector; +import org.apache.ambari.server.ldap.service.ads.detectors.UserGroupMemberAttrDetector; +import org.apache.ambari.server.ldap.service.ads.detectors.UserNameAttrDetector; +import org.apache.ambari.server.ldap.service.ads.detectors.UserObjectClassDetector; + +import com.google.inject.AbstractModule; +import com.google.inject.assistedinject.FactoryModuleBuilder; +import com.google.inject.multibindings.Multibinder; +import com.google.inject.name.Names; + +/** + * GUICE configuration module for setting up LDAP related infrastructure. + */ +public class LdapModule extends AbstractModule { + + public static final String USER_ATTRIBUTES_DETECTORS = "UserAttributesDetectors"; + public static final String GROUP_ATTRIBUTES_DETECTORS = "GroupAttributesDetectors"; + + @Override + protected void configure() { + bind(LdapFacade.class).to(AmbariLdapFacade.class); + bind(LdapConfigurationService.class).to(DefaultLdapConfigurationService.class); + bind(LdapAttributeDetectionService.class).to(DefaultLdapAttributeDetectionService.class); + bind(LdapConnectionConfigService.class).to(DefaultLdapConnectionConfigService.class); + + // this binding requires the JPA module! + bind(AmbariLdapConfiguration.class).toProvider(AmbariLdapConfigurationProvider.class); + + bind(AttributeDetectorFactory.class); + + install(new FactoryModuleBuilder().build(AmbariLdapConfigurationFactory.class)); + + // binding the set of user attributes detector + Multibinder<AttributeDetector> userAttributeDetectorBinder = Multibinder.newSetBinder(binder(), AttributeDetector.class, + Names.named(USER_ATTRIBUTES_DETECTORS)); + userAttributeDetectorBinder.addBinding().to(UserObjectClassDetector.class); + userAttributeDetectorBinder.addBinding().to(UserNameAttrDetector.class); + userAttributeDetectorBinder.addBinding().to(UserGroupMemberAttrDetector.class); + + + // binding the set of group attributes detector + Multibinder<AttributeDetector> groupAttributeDetectorBinder = Multibinder.newSetBinder(binder(), AttributeDetector.class, + Names.named(GROUP_ATTRIBUTES_DETECTORS)); + groupAttributeDetectorBinder.addBinding().to(GroupObjectClassDetector.class); + groupAttributeDetectorBinder.addBinding().to(GroupNameAttrDetector.class); + groupAttributeDetectorBinder.addBinding().to(GroupMemberAttrDetector.class); + + } + +}
http://git-wip-us.apache.org/repos/asf/ambari/blob/30415a18/ambari-server/src/main/java/org/apache/ambari/server/ldap/domain/AmbariLdapConfigKeys.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/ldap/domain/AmbariLdapConfigKeys.java b/ambari-server/src/main/java/org/apache/ambari/server/ldap/domain/AmbariLdapConfigKeys.java new file mode 100644 index 0000000..da655ad --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/ldap/domain/AmbariLdapConfigKeys.java @@ -0,0 +1,83 @@ +/* + * 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.ldap.domain; + +/** + * Constants representing supported LDAP related property names + * // todo extend this with validation information, description, defaults maybe + */ +public enum AmbariLdapConfigKeys { + + 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"), + + 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"), + + BIND_DN("ambari.ldap.connectivity.bind_dn"), + BIND_PASSWORD("ambari.ldap.connectivity.bind_password"), + + ATTR_DETECTION("ambari.ldap.attributes.detection"), // manual | auto + + DN_ATTRIBUTE("ambari.ldap.attributes.dn_attr"), + + 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"), + + 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"), + + 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"), + + 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"), + + 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 + + private String propertyName; + + AmbariLdapConfigKeys(String propName) { + this.propertyName = propName; + } + + public String key() { + return this.propertyName; + } + + public static AmbariLdapConfigKeys fromKeyStr(String keyStr) { + for (AmbariLdapConfigKeys key : values()) { + if (key.key().equals(keyStr)) { + return key; + } + } + + throw new IllegalStateException("invalid konfiguration key found!"); + + } +} http://git-wip-us.apache.org/repos/asf/ambari/blob/30415a18/ambari-server/src/main/java/org/apache/ambari/server/ldap/domain/AmbariLdapConfiguration.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/ldap/domain/AmbariLdapConfiguration.java b/ambari-server/src/main/java/org/apache/ambari/server/ldap/domain/AmbariLdapConfiguration.java new file mode 100644 index 0000000..8b26cd3 --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/ldap/domain/AmbariLdapConfiguration.java @@ -0,0 +1,199 @@ +/* + * 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.ldap.domain; + +import java.util.Map; + +import javax.inject.Inject; + +import org.apache.commons.lang3.builder.EqualsBuilder; +import org.apache.commons.lang3.builder.HashCodeBuilder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.inject.assistedinject.Assisted; + +/** + * This class is an immutable representation of all the LDAP related configurationMap entries. + */ +public class AmbariLdapConfiguration { + + private static final Logger LOGGER = LoggerFactory.getLogger(AmbariLdapConfiguration.class); + + private final Map<String, Object> configurationMap; + + private Object configValue(AmbariLdapConfigKeys ambariLdapConfigKeys) { + Object value = null; + if (configurationMap.containsKey(ambariLdapConfigKeys.key())) { + value = configurationMap.get(ambariLdapConfigKeys.key()); + } else { + LOGGER.warn("Ldap configuration property [{}] hasn't been set", ambariLdapConfigKeys.key()); + } + return value; + } + + public void setValueFor(AmbariLdapConfigKeys ambariLdapConfigKeys, Object value) { + configurationMap.put(ambariLdapConfigKeys.key(), value); + } + + // intentionally package private, instances to be created through the factory + @Inject + AmbariLdapConfiguration(@Assisted Map<String, Object> configuration) { + this.configurationMap = configuration; + } + + public boolean ldapEnabled() { + return Boolean.valueOf((String) configValue(AmbariLdapConfigKeys.LDAP_ENABLED)); + } + + public String serverHost() { + return (String) configValue(AmbariLdapConfigKeys.SERVER_HOST); + } + + public int serverPort() { + return Integer.valueOf((String) configValue(AmbariLdapConfigKeys.SERVER_PORT)); + } + + public boolean useSSL() { + return Boolean.valueOf((String) configValue(AmbariLdapConfigKeys.USE_SSL)); + } + + public String trustStore() { + return (String) configValue(AmbariLdapConfigKeys.TRUST_STORE); + } + + public String trustStoreType() { + return (String) configValue(AmbariLdapConfigKeys.TRUST_STORE_TYPE); + } + + public String trustStorePath() { + return (String) configValue(AmbariLdapConfigKeys.TRUST_STORE_PATH); + } + + public String trustStorePassword() { + return (String) configValue(AmbariLdapConfigKeys.TRUST_STORE_PASSWORD); + } + + public boolean anonymousBind() { + return Boolean.valueOf((String) configValue(AmbariLdapConfigKeys.ANONYMOUS_BIND)); + } + + public String bindDn() { + return (String) configValue(AmbariLdapConfigKeys.BIND_DN); + } + + public String bindPassword() { + return (String) configValue(AmbariLdapConfigKeys.BIND_PASSWORD); + } + + public String attributeDetection() { + return (String) configValue(AmbariLdapConfigKeys.ATTR_DETECTION); + } + + public String dnAttribute() { + return (String) configValue(AmbariLdapConfigKeys.DN_ATTRIBUTE); + } + + public String userObjectClass() { + return (String) configValue(AmbariLdapConfigKeys.USER_OBJECT_CLASS); + } + + public String userNameAttribute() { + return (String) configValue(AmbariLdapConfigKeys.USER_NAME_ATTRIBUTE); + } + + public String userSearchBase() { + return (String) configValue(AmbariLdapConfigKeys.USER_SEARCH_BASE); + } + + public String groupObjectClass() { + return (String) configValue(AmbariLdapConfigKeys.GROUP_OBJECT_CLASS); + } + + public String groupNameAttribute() { + return (String) configValue(AmbariLdapConfigKeys.GROUP_NAME_ATTRIBUTE); + } + + public String groupMemberAttribute() { + return (String) configValue(AmbariLdapConfigKeys.GROUP_MEMBER_ATTRIBUTE); + } + + public String groupSearchBase() { + return (String) configValue(AmbariLdapConfigKeys.GROUP_SEARCH_BASE); + } + + public String userSearchFilter() { + return (String) configValue(AmbariLdapConfigKeys.USER_SEARCH_FILTER); + } + + public String userMemberReplacePattern() { + return (String) configValue(AmbariLdapConfigKeys.USER_MEMBER_REPLACE_PATTERN); + } + + public String userMemberFilter() { + return (String) configValue(AmbariLdapConfigKeys.USER_MEMBER_FILTER); + } + + public String groupSearchFilter() { + return (String) configValue(AmbariLdapConfigKeys.GROUP_SEARCH_FILTER); + } + + public String groupMemberReplacePattern() { + return (String) configValue(AmbariLdapConfigKeys.GROUP_MEMBER_REPLACE_PATTERN); + } + + public String groupMemberFilter() { + return (String) configValue(AmbariLdapConfigKeys.GROUP_MEMBER_FILTER); + } + + public boolean forceLowerCaseUserNames() { + return Boolean.valueOf((String) configValue(AmbariLdapConfigKeys.FORCE_LOWERCASE_USERNAMES)); + } + + public boolean paginationEnabled() { + return Boolean.valueOf((String) configValue(AmbariLdapConfigKeys.PAGINATION_ENABLED)); + } + + public String referralHandling() { + return (String) configValue(AmbariLdapConfigKeys.REFERRAL_HANDLING); + } + + + @Override + public String toString() { + return configurationMap.toString(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + + if (o == null || getClass() != o.getClass()) return false; + + AmbariLdapConfiguration that = (AmbariLdapConfiguration) o; + + return new EqualsBuilder() + .append(configurationMap, that.configurationMap) + .isEquals(); + } + + @Override + public int hashCode() { + return new HashCodeBuilder(17, 37) + .append(configurationMap) + .toHashCode(); + } +} http://git-wip-us.apache.org/repos/asf/ambari/blob/30415a18/ambari-server/src/main/java/org/apache/ambari/server/ldap/domain/AmbariLdapConfigurationFactory.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/ldap/domain/AmbariLdapConfigurationFactory.java b/ambari-server/src/main/java/org/apache/ambari/server/ldap/domain/AmbariLdapConfigurationFactory.java new file mode 100644 index 0000000..2b9f24b --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/ldap/domain/AmbariLdapConfigurationFactory.java @@ -0,0 +1,34 @@ +/* + * 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.ldap.domain; + +import java.util.Map; + +/** + * Factory interface for AmbariLdapConfiguration instances. + * It's registered as a factory in the GUICE context (so no implementations required) + * + * To be extended with other factory methods upon needs. + */ +public interface AmbariLdapConfigurationFactory { + + /** + * Creates an AmbariLdapConfiguration instance with the provided map of configuration settings. + * + * @param configuration a map where keys are the configuration properties and values are the configuration values + * @return an AmbariLdapConfiguration instance + */ + AmbariLdapConfiguration createLdapConfiguration(Map<String, Object> configuration); +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/ambari/blob/30415a18/ambari-server/src/main/java/org/apache/ambari/server/ldap/service/AmbariLdapConfigurationProvider.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/ldap/service/AmbariLdapConfigurationProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/ldap/service/AmbariLdapConfigurationProvider.java new file mode 100644 index 0000000..c88d420 --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/ldap/service/AmbariLdapConfigurationProvider.java @@ -0,0 +1,120 @@ +/* + * 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.ldap.service; + +import java.util.Map; +import java.util.Set; + +import javax.inject.Inject; +import javax.inject.Provider; +import javax.inject.Singleton; + +import org.apache.ambari.server.events.AmbariLdapConfigChangedEvent; +import org.apache.ambari.server.events.publishers.AmbariEventPublisher; +import org.apache.ambari.server.ldap.domain.AmbariLdapConfiguration; +import org.apache.ambari.server.ldap.domain.AmbariLdapConfigurationFactory; +import org.apache.ambari.server.orm.dao.AmbariConfigurationDAO; +import org.apache.ambari.server.orm.entities.AmbariConfigurationEntity; +import org.apache.ambari.server.security.authorization.AmbariLdapAuthenticationProvider; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.eventbus.Subscribe; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + +/** + * Provider implementation for LDAP configurations. + * It needs to be registered in the related GUICE module as a provider. + * It's responsible for managing LDAP configurations in the application. + * Whenever requested, this provider returns an AmbariLdapConfiguration which is always in sync with the persisted LDAP + * configuration resource. + * + * The provider receives notifications on CRUD operations related to the persisted resource and reloads the cached + * configuration instance accordingly. + */ +@Singleton +public class AmbariLdapConfigurationProvider implements Provider<AmbariLdapConfiguration> { + + private static final Logger LOGGER = LoggerFactory.getLogger(AmbariLdapAuthenticationProvider.class); + private AmbariLdapConfiguration instance; + + @Inject + private AmbariEventPublisher publisher; + + @Inject + private Provider<AmbariConfigurationDAO> ambariConfigurationDAOProvider; + + @Inject + private AmbariLdapConfigurationFactory ldapConfigurationFactory; + + private Gson gson = new GsonBuilder().create(); + + @Inject + public AmbariLdapConfigurationProvider() { + } + + @Inject + void register() { + publisher.register(this); + } + + @Override + public AmbariLdapConfiguration get() { + return instance != null ? instance : loadInstance(null); + } + + /** + * Loads the AmbariLdapConfiguration from the database. + * + * @param configurationId the configuration id + * @return the AmbariLdapConfiguration instance + */ + private AmbariLdapConfiguration loadInstance(Long configurationId) { + AmbariConfigurationEntity configEntity = null; + + LOGGER.info("Loading LDAP configuration ..."); + if (null == configurationId) { + + LOGGER.debug("Initial loading of the ldap configuration ..."); + configEntity = ambariConfigurationDAOProvider.get().getLdapConfiguration(); + + } else { + + LOGGER.debug("Reloading configuration based on the provied id: {}", configurationId); + configEntity = ambariConfigurationDAOProvider.get().findByPK(configurationId); + + } + + if (configEntity != null) { + Set propertyMaps = gson.fromJson(configEntity.getConfigurationBaseEntity().getConfigurationData(), Set.class); + instance = ldapConfigurationFactory.createLdapConfiguration((Map<String, Object>) propertyMaps.iterator().next()); + } + + LOGGER.info("Loaded LDAP configuration instance: [ {} ]", instance); + + return instance; + } + + // On changing the configuration, the provider gets updated with the fresh value + @Subscribe + public void ambariLdapConfigChanged(AmbariLdapConfigChangedEvent event) { + LOGGER.info("LDAP config changed event received: {}", event); + loadInstance(event.getConfigurationId()); + LOGGER.info("Refreshed LDAP config instance."); + } + + +} http://git-wip-us.apache.org/repos/asf/ambari/blob/30415a18/ambari-server/src/main/java/org/apache/ambari/server/ldap/service/AmbariLdapException.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/ldap/service/AmbariLdapException.java b/ambari-server/src/main/java/org/apache/ambari/server/ldap/service/AmbariLdapException.java new file mode 100644 index 0000000..cb38acc --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/ldap/service/AmbariLdapException.java @@ -0,0 +1,33 @@ +/* + * 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.ldap.service; + +public class AmbariLdapException extends Exception { + public AmbariLdapException() { + super(); + } + + public AmbariLdapException(String message) { + super(message); + } + + public AmbariLdapException(String message, Throwable cause) { + super(message, cause); + } + + public AmbariLdapException(Throwable cause) { + super(cause); + } +} http://git-wip-us.apache.org/repos/asf/ambari/blob/30415a18/ambari-server/src/main/java/org/apache/ambari/server/ldap/service/AmbariLdapFacade.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/ldap/service/AmbariLdapFacade.java b/ambari-server/src/main/java/org/apache/ambari/server/ldap/service/AmbariLdapFacade.java new file mode 100644 index 0000000..0118840 --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/ldap/service/AmbariLdapFacade.java @@ -0,0 +1,140 @@ +/* + * 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.ldap.service; + +import java.util.Map; +import java.util.Set; + +import javax.inject.Inject; +import javax.inject.Singleton; + +import org.apache.ambari.server.ldap.domain.AmbariLdapConfiguration; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@Singleton +public class AmbariLdapFacade implements LdapFacade { + + private static final Logger LOGGER = LoggerFactory.getLogger(AmbariLdapFacade.class); + + /** + * Additional parameters expected to be provided along with the configuration + */ + public enum Parameters { + TEST_USER_NAME("ambari.ldap.test.user.name"), + TEST_USER_PASSWORD("ambari.ldap.test.user.password"); + + private String parameterKey; + + Parameters(String parameterKey) { + this.parameterKey = parameterKey; + } + + public String getParameterKey() { + return parameterKey; + } + + } + + @Inject + private LdapConfigurationService ldapConfigurationService; + + @Inject + private LdapAttributeDetectionService ldapAttributeDetectionService; + + @Inject + public AmbariLdapFacade() { + } + + @Override + public void checkConnection(AmbariLdapConfiguration ambariLdapConfiguration) throws AmbariLdapException { + try { + + ldapConfigurationService.checkConnection(ambariLdapConfiguration); + LOGGER.info("Validating LDAP connection related configuration: SUCCESS"); + + } catch (Exception e) { + + LOGGER.error("Validating LDAP connection configuration failed", e); + throw new AmbariLdapException(e); + + } + + } + + + @Override + public AmbariLdapConfiguration detectAttributes(AmbariLdapConfiguration ambariLdapConfiguration) throws AmbariLdapException { + LOGGER.info("Detecting LDAP configuration attributes ..."); + + try { + LOGGER.info("Detecting user attributes ...."); + // decorate the configuration with detected user attributes + ambariLdapConfiguration = ldapAttributeDetectionService.detectLdapUserAttributes(ambariLdapConfiguration); + + LOGGER.info("Detecting group attributes ...."); + // decorate the configuration with detected group attributes + ambariLdapConfiguration = ldapAttributeDetectionService.detectLdapGroupAttributes(ambariLdapConfiguration); + + LOGGER.info("Attribute detection finished."); + return ambariLdapConfiguration; + + } catch (Exception e) { + + LOGGER.error("Error during LDAP attribute detection", e); + throw new AmbariLdapException(e); + + } + } + + @Override + public Set<String> checkLdapAttributes(Map<String, Object> parameters, AmbariLdapConfiguration ldapConfiguration) throws AmbariLdapException { + String userName = getTestUserNameFromParameters(parameters); + String testUserPass = getTestUserPasswordFromParameters(parameters); + + if (null == userName) { + throw new IllegalArgumentException("No test user available for testing LDAP attributes"); + } + + LOGGER.info("Testing LDAP user attributes with test user: {}", userName); + String userDn = ldapConfigurationService.checkUserAttributes(userName, testUserPass, ldapConfiguration); + + // todo handle the case where group membership is stored in the user rather than the group + LOGGER.info("Testing LDAP group attributes with test user dn: {}", userDn); + Set<String> groups = ldapConfigurationService.checkGroupAttributes(userDn, ldapConfiguration); + + return groups; + } + + + private String getTestUserNameFromParameters(Map<String, Object> parameters) { + return (String) parameterValue(parameters, Parameters.TEST_USER_NAME); + } + + private String getTestUserPasswordFromParameters(Map<String, Object> parameters) { + return (String) parameterValue(parameters, Parameters.TEST_USER_PASSWORD); + } + + private Object parameterValue(Map<String, Object> parameters, Parameters parameter) { + Object value = null; + if (parameters.containsKey(parameter.getParameterKey())) { + value = parameters.get(parameter.getParameterKey()); + } else { + LOGGER.warn("Parameter [{}] is missing from parameters", parameter.getParameterKey()); + } + return value; + } +} http://git-wip-us.apache.org/repos/asf/ambari/blob/30415a18/ambari-server/src/main/java/org/apache/ambari/server/ldap/service/AttributeDetector.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/ldap/service/AttributeDetector.java b/ambari-server/src/main/java/org/apache/ambari/server/ldap/service/AttributeDetector.java new file mode 100644 index 0000000..f39a1fd --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/ldap/service/AttributeDetector.java @@ -0,0 +1,41 @@ +/* + * 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.ldap.service; + +import java.util.Map; + +/** + * Operations for detecting LDAP related settings. + * The basis for the attribute or value detection is a set of entries returned by a search operation. + * Individual attribute detector implementations are responsible for detecting a specific set of attributes or values + */ +public interface AttributeDetector<T> { + + /** + * Collects potential attribute names or values from a set of result entries. + * + * @param entry a result entry returned by a search operation + */ + void collect(T entry); + + /** + * Implements the decision based on which the "best" possible attribute or value is selected. + * + * @return a map of the form <property-key, detected-value> + */ + Map<String, String> detect(); + + +} http://git-wip-us.apache.org/repos/asf/ambari/blob/30415a18/ambari-server/src/main/java/org/apache/ambari/server/ldap/service/LdapAttributeDetectionService.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/ldap/service/LdapAttributeDetectionService.java b/ambari-server/src/main/java/org/apache/ambari/server/ldap/service/LdapAttributeDetectionService.java new file mode 100644 index 0000000..c08a2e0 --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/ldap/service/LdapAttributeDetectionService.java @@ -0,0 +1,40 @@ +/* + * 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.ldap.service; + +import org.apache.ambari.server.ldap.domain.AmbariLdapConfiguration; + +/** + * Contract defining operations to detect user and group attributes. + */ +public interface LdapAttributeDetectionService { + + /** + * Decorates the passed in configuration with the detected ldap user attribute values + * + * @param ambariLdapConfiguration configuration instance holding connection details + * @return the configuration decorated with user related attributes + */ + AmbariLdapConfiguration detectLdapUserAttributes(AmbariLdapConfiguration ambariLdapConfiguration) throws AmbariLdapException; + + /** + * Decorates the passed in configuration with the detected ldap group attribute values + * + * @param ambariLdapConfiguration configuration instance holding connection details + * @return the configuration decorated with group related attributes + */ + AmbariLdapConfiguration detectLdapGroupAttributes(AmbariLdapConfiguration ambariLdapConfiguration) throws AmbariLdapException; +} + http://git-wip-us.apache.org/repos/asf/ambari/blob/30415a18/ambari-server/src/main/java/org/apache/ambari/server/ldap/service/LdapConfigurationService.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/ldap/service/LdapConfigurationService.java b/ambari-server/src/main/java/org/apache/ambari/server/ldap/service/LdapConfigurationService.java new file mode 100644 index 0000000..4b82aa2 --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/ldap/service/LdapConfigurationService.java @@ -0,0 +1,60 @@ +/* + * 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.ldap.service; + +import java.util.Set; + +import org.apache.ambari.server.AmbariException; +import org.apache.ambari.server.ldap.domain.AmbariLdapConfiguration; + +/** + * Collection of operations for validating ldap configuration. + * It's intended to decouple implementations using different libraries. + */ +public interface LdapConfigurationService { + + /** + * Tests the connection based on the provided configuration. + * + * @param configuration the ambari ldap configuration instance + * @throws AmbariLdapException if the connection is not possible + */ + void checkConnection(AmbariLdapConfiguration configuration) throws AmbariLdapException; + + + /** + * Implements LDAP user related configuration settings validation logic. + * Implementers communicate with the LDAP server (search, bind) to validate attributes in the provided configuration + * instance + * + * @param testUserName the test username + * @param testPassword the test password + * @param configuration the available ldap configuration + * @return The DN of the found user entry + * @throws AmbariException if the connection couldn't be estabilisheds + */ + String checkUserAttributes(String testUserName, String testPassword, AmbariLdapConfiguration configuration) throws AmbariLdapException; + + /** + * Checks whether the group related LDAP attributes in the configuration are correct. + * + * @param userDn + * @param ambariLdapConfiguration + * @return + * @throws AmbariLdapException + */ + Set<String> checkGroupAttributes(String userDn, AmbariLdapConfiguration ambariLdapConfiguration) throws AmbariLdapException; + +} http://git-wip-us.apache.org/repos/asf/ambari/blob/30415a18/ambari-server/src/main/java/org/apache/ambari/server/ldap/service/LdapConnectionConfigService.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/ldap/service/LdapConnectionConfigService.java b/ambari-server/src/main/java/org/apache/ambari/server/ldap/service/LdapConnectionConfigService.java new file mode 100644 index 0000000..a882075 --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/ldap/service/LdapConnectionConfigService.java @@ -0,0 +1,36 @@ +/* + * 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.ldap.service; + +import org.apache.ambari.server.ldap.domain.AmbariLdapConfiguration; +import org.apache.directory.ldap.client.api.LdapConnectionConfig; + +/** + * Contract for creating connection configuration instances. + * Implementers are in charge for implementing any required custom logic based on the ambari configuration properties. + * (Eg.: using custom key stores etc...) + */ +public interface LdapConnectionConfigService { + + /** + * Creates and sets up an ldap connection configuration instance based on the provided ambari ldap configuration instance. + * + * @param ambariLdapConfiguration instance holding configuration values + * @return a set up ldap connection configuration instance + * @throws AmbariLdapException if an error occurs while setting up the connection configuration + */ + LdapConnectionConfig createLdapConnectionConfig(AmbariLdapConfiguration ambariLdapConfiguration) throws AmbariLdapException; + +} http://git-wip-us.apache.org/repos/asf/ambari/blob/30415a18/ambari-server/src/main/java/org/apache/ambari/server/ldap/service/LdapFacade.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/ldap/service/LdapFacade.java b/ambari-server/src/main/java/org/apache/ambari/server/ldap/service/LdapFacade.java new file mode 100644 index 0000000..ef84d1b --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/ldap/service/LdapFacade.java @@ -0,0 +1,58 @@ +/* + * 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.ldap.service; + +import java.util.Map; +import java.util.Set; + +import org.apache.ambari.server.ldap.domain.AmbariLdapConfiguration; + +/** + * The contract defining all the operations required by the application when communicating with an arbitrary LDAP server. + * This interface is intended to decouple LDAP specific details from the application. + * + * Any operation that requires interaction with an LDAP server from within Ambari should go through this interface. + * (LDAP) + */ +public interface LdapFacade { + + /** + * Tests the connection to the LDAP server based on the provided configuration. + * + * @param ambariLdapConfiguration the available ldap related configuration + * @throws AmbariLdapException if the connection fails or other problems occur during the operation + */ + void checkConnection(AmbariLdapConfiguration ambariLdapConfiguration) throws AmbariLdapException; + + + /** + * Runs the user and group attribute detection algorithms. + * The method is not intended to be used as a coniguration factory, the returned instance may not be suitable for use. + * + * @param ambariLdapConfiguration partially filled configuration instance to be extended with detected properties + * @return a configuration instance, with properties filled with potentially correct values + * @throws AmbariLdapException + */ + AmbariLdapConfiguration detectAttributes(AmbariLdapConfiguration ambariLdapConfiguration) throws AmbariLdapException; + + /** + * Checks user and group related LDAP configuration attributes in the configuration object with the help of the provided parameters + * + * @param parameters a map of property name and value pairs holding information to facilitate checking the attributes + * @param ambariLdapConfiguration configutration instance with available attributes + * @throws AmbariLdapException if the attribute checking fails + */ + Set<String> checkLdapAttributes(Map<String, Object> parameters, AmbariLdapConfiguration ambariLdapConfiguration) throws AmbariLdapException; +} http://git-wip-us.apache.org/repos/asf/ambari/blob/30415a18/ambari-server/src/main/java/org/apache/ambari/server/ldap/service/ads/DefaultLdapAttributeDetectionService.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/ldap/service/ads/DefaultLdapAttributeDetectionService.java b/ambari-server/src/main/java/org/apache/ambari/server/ldap/service/ads/DefaultLdapAttributeDetectionService.java new file mode 100644 index 0000000..a9a9b53 --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/ldap/service/ads/DefaultLdapAttributeDetectionService.java @@ -0,0 +1,200 @@ +/* + * 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.ldap.service.ads; + +import java.util.List; +import java.util.Map; + +import javax.inject.Inject; +import javax.inject.Singleton; + +import org.apache.ambari.server.ldap.domain.AmbariLdapConfigKeys; +import org.apache.ambari.server.ldap.domain.AmbariLdapConfiguration; +import org.apache.ambari.server.ldap.service.AmbariLdapException; +import org.apache.ambari.server.ldap.service.AttributeDetector; +import org.apache.ambari.server.ldap.service.LdapAttributeDetectionService; +import org.apache.ambari.server.ldap.service.ads.detectors.AttributeDetectorFactory; +import org.apache.directory.api.ldap.model.entry.Entry; +import org.apache.directory.api.ldap.model.exception.LdapException; +import org.apache.directory.api.ldap.model.message.SearchRequest; +import org.apache.directory.api.ldap.model.message.SearchScope; +import org.apache.directory.api.util.Strings; +import org.apache.directory.ldap.client.api.search.FilterBuilder; +import org.apache.directory.ldap.client.template.EntryMapper; +import org.apache.directory.ldap.client.template.LdapConnectionTemplate; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Service implementation that performs user and group attribute detection based on a sample set of entries returned by + * an ldap search operation. A accuracy of detected values may depend on the size of the sample result set + */ +@Singleton +public class DefaultLdapAttributeDetectionService implements LdapAttributeDetectionService { + + private static final Logger LOG = LoggerFactory.getLogger(DefaultLdapAttributeDetectionService.class); + + /** + * The maximum size of the entry set the detection is performed on + */ + private static final int SAMPLE_RESULT_SIZE = 50; + + @Inject + private AttributeDetectorFactory attributeDetectorFactory; + + @Inject + private LdapConnectionTemplateFactory ldapConnectionTemplateFactory; + + @Inject + public DefaultLdapAttributeDetectionService() { + } + + @Override + public AmbariLdapConfiguration detectLdapUserAttributes(AmbariLdapConfiguration ambariLdapConfiguration) throws AmbariLdapException { + LOG.info("Detecting LDAP user attributes ..."); + + // perform a search using the user search base + if (Strings.isEmpty(ambariLdapConfiguration.userSearchBase())) { + LOG.warn("No user search base provided"); + return ambariLdapConfiguration; + } + + try { + + LdapConnectionTemplate ldapConnectionTemplate = ldapConnectionTemplateFactory.create(ambariLdapConfiguration); + AttributeDetector<Entry> userAttributeDetector = attributeDetectorFactory.userAttributDetector(); + + SearchRequest searchRequest = assembleUserSearchRequest(ldapConnectionTemplate, ambariLdapConfiguration); + + // do the search + List<Entry> entries = ldapConnectionTemplate.search(searchRequest, getEntryMapper()); + + for (Entry entry : entries) { + LOG.info("Collecting user attribute information from the sample entry with dn: [{}]", entry.getDn()); + userAttributeDetector.collect(entry); + } + + // select attributes based on the collected information + Map<String, String> detectedUserAttributes = userAttributeDetector.detect(); + + // setting the attributes into the configuration + setDetectedAttributes(ambariLdapConfiguration, detectedUserAttributes); + + LOG.info("Decorated ambari ldap config : [{}]", ambariLdapConfiguration); + + } catch (Exception e) { + + LOG.error("Ldap operation failed while detecting user attributes", e); + throw new AmbariLdapException(e); + + } + + return ambariLdapConfiguration; + } + + + @Override + public AmbariLdapConfiguration detectLdapGroupAttributes(AmbariLdapConfiguration ambariLdapConfiguration) throws AmbariLdapException { + LOG.info("Detecting LDAP group attributes ..."); + + // perform a search using the user search base + if (Strings.isEmpty(ambariLdapConfiguration.groupSearchBase())) { + LOG.warn("No group search base provided"); + return ambariLdapConfiguration; + } + + try { + + LdapConnectionTemplate ldapConnectionTemplate = ldapConnectionTemplateFactory.create(ambariLdapConfiguration); + AttributeDetector<Entry> groupAttributeDetector = attributeDetectorFactory.groupAttributeDetector(); + + SearchRequest searchRequest = assembleGroupSearchRequest(ldapConnectionTemplate, ambariLdapConfiguration); + + // do the search + List<Entry> groupEntries = ldapConnectionTemplate.search(searchRequest, getEntryMapper()); + + for (Entry groupEntry : groupEntries) { + + LOG.info("Collecting group attribute information from the sample entry with dn: [{}]", groupEntry.getDn()); + groupAttributeDetector.collect(groupEntry); + + } + + // select attributes based on the collected information + Map<String, String> detectedGroupAttributes = groupAttributeDetector.detect(); + + // setting the attributes into the configuration + setDetectedAttributes(ambariLdapConfiguration, detectedGroupAttributes); + + LOG.info("Decorated ambari ldap config : [{}]", ambariLdapConfiguration); + + } catch (Exception e) { + + LOG.error("Ldap operation failed while detecting group attributes", e); + throw new AmbariLdapException(e); + + } + + return ambariLdapConfiguration; + } + + private void setDetectedAttributes(AmbariLdapConfiguration ambariLdapConfiguration, Map<String, String> detectedAttributes) { + + for (Map.Entry<String, String> detecteMapEntry : detectedAttributes.entrySet()) { + LOG.info("Setting detected configuration value: [{}] - > [{}]", detecteMapEntry.getKey(), detecteMapEntry.getValue()); + ambariLdapConfiguration.setValueFor(AmbariLdapConfigKeys.fromKeyStr(detecteMapEntry.getKey()), detecteMapEntry.getValue()); + } + + } + + private SearchRequest assembleUserSearchRequest(LdapConnectionTemplate ldapConnectionTemplate, AmbariLdapConfiguration ambariLdapConfiguration) throws AmbariLdapException { + try { + + SearchRequest req = ldapConnectionTemplate.newSearchRequest(ambariLdapConfiguration.userSearchBase(), + FilterBuilder.present(ambariLdapConfiguration.dnAttribute()).toString(), SearchScope.SUBTREE); + req.setSizeLimit(SAMPLE_RESULT_SIZE); + + return req; + + } catch (Exception e) { + LOG.error("Could not assemble ldap search request", e); + throw new AmbariLdapException(e); + } + } + + private SearchRequest assembleGroupSearchRequest(LdapConnectionTemplate ldapConnectionTemplate, AmbariLdapConfiguration ambariLdapConfiguration) throws AmbariLdapException { + try { + + SearchRequest req = ldapConnectionTemplate.newSearchRequest(ambariLdapConfiguration.groupSearchBase(), + FilterBuilder.present(ambariLdapConfiguration.dnAttribute()).toString(), SearchScope.SUBTREE); + req.setSizeLimit(SAMPLE_RESULT_SIZE); + + return req; + + } catch (Exception e) { + LOG.error("Could not assemble ldap search request", e); + throw new AmbariLdapException(e); + } + } + + public EntryMapper<Entry> getEntryMapper() { + return new EntryMapper<Entry>() { + @Override + public Entry map(Entry entry) throws LdapException { + return entry; + } + }; + } +} http://git-wip-us.apache.org/repos/asf/ambari/blob/30415a18/ambari-server/src/main/java/org/apache/ambari/server/ldap/service/ads/DefaultLdapConfigurationService.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/ldap/service/ads/DefaultLdapConfigurationService.java b/ambari-server/src/main/java/org/apache/ambari/server/ldap/service/ads/DefaultLdapConfigurationService.java new file mode 100644 index 0000000..3f6995c --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/ldap/service/ads/DefaultLdapConfigurationService.java @@ -0,0 +1,213 @@ +/* + * 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.ldap.service.ads; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import javax.inject.Inject; +import javax.inject.Singleton; + +import org.apache.ambari.server.ldap.domain.AmbariLdapConfiguration; +import org.apache.ambari.server.ldap.service.AmbariLdapException; +import org.apache.ambari.server.ldap.service.LdapConfigurationService; +import org.apache.directory.api.ldap.model.constants.SchemaConstants; +import org.apache.directory.api.ldap.model.entry.Entry; +import org.apache.directory.api.ldap.model.exception.LdapException; +import org.apache.directory.api.ldap.model.message.SearchRequest; +import org.apache.directory.api.ldap.model.message.SearchScope; +import org.apache.directory.api.ldap.model.name.Dn; +import org.apache.directory.ldap.client.api.LdapConnection; +import org.apache.directory.ldap.client.api.search.FilterBuilder; +import org.apache.directory.ldap.client.template.ConnectionCallback; +import org.apache.directory.ldap.client.template.EntryMapper; +import org.apache.directory.ldap.client.template.LdapConnectionTemplate; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.collect.Lists; + +/** + * Implementation of the validation logic using the Apache Directory API. + */ +@Singleton +public class DefaultLdapConfigurationService implements LdapConfigurationService { + + private static final Logger LOGGER = LoggerFactory.getLogger(DefaultLdapConfigurationService.class); + + @Inject + private LdapConnectionTemplateFactory ldapConnectionTemplateFactory; + + @Inject + public DefaultLdapConfigurationService() { + } + + + @Override + public void checkConnection(AmbariLdapConfiguration ambariLdapConfiguration) throws AmbariLdapException { + LOGGER.info("Trying to connect to the LDAP server using provided configuration..."); + LdapConnectionTemplate ldapConnectionTemplate = ldapConnectionTemplateFactory.create(ambariLdapConfiguration); + + // check if the connection from the connection pool of the template is connected + Boolean isConnected = ldapConnectionTemplate.execute(new ConnectionCallback<Boolean>() { + @Override + public Boolean doWithConnection(LdapConnection connection) throws LdapException { + return connection.isConnected() && connection.isAuthenticated(); + } + }); + + if (!isConnected) { + LOGGER.error("Could not connect to the LDAP server"); + throw new AmbariLdapException("Could not connect to the LDAP server. Configuration: " + ambariLdapConfiguration); + } + + LOGGER.info("Successfully conencted to the LDAP."); + + } + + /** + * Checks the user attributes provided in the configuration instance by issuing a search for a (known) test user in the LDAP. + * Attributes are considered correct if there is at least one entry found. + * + * Invalid attributes are signaled by throwing an exception. + * + * @param testUserName the test username + * @param testPassword the test password + * @param ambariLdapConfiguration the available LDAP configuration to be validated + * @return the DN of the test user + * @throws AmbariLdapException if an error occurs + */ + @Override + public String checkUserAttributes(String testUserName, String testPassword, AmbariLdapConfiguration ambariLdapConfiguration) throws AmbariLdapException { + String userDn; + try { + LOGGER.info("Checking user attributes for user [{}] ...", testUserName); + + // set up a filter based on the provided attributes + String filter = FilterBuilder.and( + FilterBuilder.equal(SchemaConstants.OBJECT_CLASS_AT, ambariLdapConfiguration.userObjectClass()), + FilterBuilder.equal(ambariLdapConfiguration.userNameAttribute(), testUserName)) + .toString(); + + LOGGER.info("Searching for the user: [{}] using the search filter: [{}]", testUserName, filter); + userDn = ldapConnectionTemplateFactory.create(ambariLdapConfiguration).searchFirst(new Dn(ambariLdapConfiguration.userSearchBase()), filter, SearchScope.SUBTREE, getUserDnNameEntryMapper(ambariLdapConfiguration)); + + if (null == userDn) { + LOGGER.info("Could not find user based on the provided configuration. User attributes are not complete "); + throw new AmbariLdapException("User attribute configuration incomplete"); + } + LOGGER.info("Attribute validation succeeded. Filter: [{}]", filter); + + + } catch (Exception e) { + + LOGGER.error("User attributes validation failed.", e); + throw new AmbariLdapException(e.getMessage(), e); + + } + return userDn; + } + + /** + * Checks whether the provided group related settings are correct. + * + * @param userDn a user DN to check + * @param ambariLdapConfiguration the available LDAP configuration to be validated + * @return + * @throws AmbariLdapException + */ + @Override + public Set<String> checkGroupAttributes(String userDn, AmbariLdapConfiguration ambariLdapConfiguration) throws AmbariLdapException { + List<String> groups = Lists.newArrayList(); + try { + LOGGER.info("Checking group attributes for user dn: [{}] ...", userDn); + + // set up a filter based on the provided attributes + String filter = FilterBuilder.and( + FilterBuilder.equal(SchemaConstants.OBJECT_CLASS_AT, ambariLdapConfiguration.groupObjectClass()), + FilterBuilder.equal(ambariLdapConfiguration.groupMemberAttribute(), userDn) + ).toString(); + + LOGGER.info("Searching for the groups the user dn: [{}] is member of using the search filter: [{}]", userDn, filter); + LdapConnectionTemplate ldapConnectionTemplate = ldapConnectionTemplateFactory.create(ambariLdapConfiguration); + + // assemble a search request + SearchRequest searchRequest = ldapConnectionTemplate.newSearchRequest(new Dn(ambariLdapConfiguration.groupSearchBase()), filter, SearchScope.SUBTREE); + // attributes to be returned + searchRequest.addAttributes(ambariLdapConfiguration.groupMemberAttribute(), ambariLdapConfiguration.groupNameAttribute()); + + // perform the search + groups = ldapConnectionTemplate.search(searchRequest, getGroupNameEntryMapper(ambariLdapConfiguration)); + + if (groups == null || groups.isEmpty()) { + LOGGER.info("No groups found for the user dn. Group attributes configuration is incomplete"); + throw new AmbariLdapException("Group attribute ldap configuration is incomplete"); + } + + LOGGER.info("Group attribute configuration check succeeded."); + + } catch (Exception e) { + + LOGGER.error("User attributes validation failed.", e); + throw new AmbariLdapException(e.getMessage(), e); + + } + + return new HashSet<>(groups); + } + + + /** + * Entry mapper for handling user search results. + * + * @param ambariLdapConfiguration ambari ldap configuration values + * @return user dn entry mapper instance + */ + private EntryMapper<String> getGroupNameEntryMapper(AmbariLdapConfiguration ambariLdapConfiguration) { + + EntryMapper<String> entryMapper = new EntryMapper<String>() { + @Override + public String map(Entry entry) throws LdapException { + return entry.get(ambariLdapConfiguration.groupNameAttribute()).get().getString(); + } + }; + + return entryMapper; + } + + /** + * Entry mapper for handling group searches. + * + * @param ambariLdapConfiguration ambari ldap configuration values + * @return + */ + private EntryMapper<String> getUserDnNameEntryMapper(AmbariLdapConfiguration ambariLdapConfiguration) { + + EntryMapper<String> entryMapper = new EntryMapper<String>() { + @Override + public String map(Entry entry) throws LdapException { + return entry.getDn().getNormName(); + } + }; + + return entryMapper; + } + + +} + + + http://git-wip-us.apache.org/repos/asf/ambari/blob/30415a18/ambari-server/src/main/java/org/apache/ambari/server/ldap/service/ads/DefaultLdapConnectionConfigService.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/ldap/service/ads/DefaultLdapConnectionConfigService.java b/ambari-server/src/main/java/org/apache/ambari/server/ldap/service/ads/DefaultLdapConnectionConfigService.java new file mode 100644 index 0000000..9afcf51 --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/ldap/service/ads/DefaultLdapConnectionConfigService.java @@ -0,0 +1,113 @@ +/* + * 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.ldap.service.ads; + +import static javax.net.ssl.TrustManagerFactory.getDefaultAlgorithm; + +import java.io.FileInputStream; +import java.security.KeyStore; + +import javax.inject.Inject; +import javax.inject.Singleton; +import javax.net.ssl.TrustManager; +import javax.net.ssl.TrustManagerFactory; + +import org.apache.ambari.server.ldap.domain.AmbariLdapConfiguration; +import org.apache.ambari.server.ldap.service.AmbariLdapException; +import org.apache.ambari.server.ldap.service.LdapConnectionConfigService; +import org.apache.directory.api.util.Strings; +import org.apache.directory.ldap.client.api.LdapConnectionConfig; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@Singleton +public class DefaultLdapConnectionConfigService implements LdapConnectionConfigService { + + private static Logger LOG = LoggerFactory.getLogger(DefaultLdapConnectionConfigService.class); + + @Inject + public DefaultLdapConnectionConfigService() { + } + + @Override + public LdapConnectionConfig createLdapConnectionConfig(AmbariLdapConfiguration ambariLdapConfiguration) throws AmbariLdapException { + + LOG.debug("Assembling ldap connection config based on: {}", ambariLdapConfiguration); + + LdapConnectionConfig config = new LdapConnectionConfig(); + config.setLdapHost(ambariLdapConfiguration.serverHost()); + config.setLdapPort(ambariLdapConfiguration.serverPort()); + config.setName(ambariLdapConfiguration.bindDn()); + config.setCredentials(ambariLdapConfiguration.bindPassword()); + config.setUseSsl(ambariLdapConfiguration.useSSL()); + + if ("custom".equals(ambariLdapConfiguration.trustStore())) { + LOG.info("Using custom trust manager configuration"); + config.setTrustManagers(trustManagers(ambariLdapConfiguration)); + } + + return config; + } + + + /** + * Configure the trust managers to use the custom keystore. + * + * @param ambariLdapConfiguration congiguration instance holding current values + * @return the array of trust managers + * @throws AmbariLdapException if an error occurs while setting up the connection + */ + private TrustManager[] trustManagers(AmbariLdapConfiguration ambariLdapConfiguration) throws AmbariLdapException { + try { + + TrustManagerFactory tmFactory = TrustManagerFactory.getInstance(getDefaultAlgorithm()); + tmFactory.init(keyStore(ambariLdapConfiguration)); + return tmFactory.getTrustManagers(); + + } catch (Exception e) { + + LOG.error("Failed to initialize trust managers", e); + throw new AmbariLdapException(e); + + } + + } + + private KeyStore keyStore(AmbariLdapConfiguration ambariLdapConfiguration) throws AmbariLdapException { + + // validating configuration settings + if (Strings.isEmpty(ambariLdapConfiguration.trustStoreType())) { + throw new AmbariLdapException("Key Store Type must be specified"); + } + + if (Strings.isEmpty(ambariLdapConfiguration.trustStorePath())) { + throw new AmbariLdapException("Key Store Path must be specified"); + } + + try { + + KeyStore ks = KeyStore.getInstance(ambariLdapConfiguration.trustStoreType()); + FileInputStream fis = new FileInputStream(ambariLdapConfiguration.trustStorePath()); + ks.load(fis, ambariLdapConfiguration.trustStorePassword().toCharArray()); + return ks; + + } catch (Exception e) { + + LOG.error("Failed to create keystore", e); + throw new AmbariLdapException(e); + + } + } +} http://git-wip-us.apache.org/repos/asf/ambari/blob/30415a18/ambari-server/src/main/java/org/apache/ambari/server/ldap/service/ads/LdapConnectionTemplateFactory.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/ldap/service/ads/LdapConnectionTemplateFactory.java b/ambari-server/src/main/java/org/apache/ambari/server/ldap/service/ads/LdapConnectionTemplateFactory.java new file mode 100644 index 0000000..8467af0 --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/ldap/service/ads/LdapConnectionTemplateFactory.java @@ -0,0 +1,111 @@ +/* + * 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.ldap.service.ads; + +import javax.inject.Inject; +import javax.inject.Provider; +import javax.inject.Singleton; + +import org.apache.ambari.server.events.AmbariLdapConfigChangedEvent; +import org.apache.ambari.server.ldap.domain.AmbariLdapConfiguration; +import org.apache.ambari.server.ldap.service.AmbariLdapException; +import org.apache.ambari.server.ldap.service.LdapConnectionConfigService; +import org.apache.directory.ldap.client.api.DefaultLdapConnectionFactory; +import org.apache.directory.ldap.client.api.LdapConnectionConfig; +import org.apache.directory.ldap.client.api.LdapConnectionFactory; +import org.apache.directory.ldap.client.api.LdapConnectionPool; +import org.apache.directory.ldap.client.api.ValidatingPoolableLdapConnectionFactory; +import org.apache.directory.ldap.client.template.LdapConnectionTemplate; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.eventbus.Subscribe; + +/** + * Factory for creating LdapConnectionTemplate instances. + * Depending on the usage context, the instance can be constructed based on the provided configuration or based on the persisted settings. + */ +@Singleton +public class LdapConnectionTemplateFactory { + + private static final Logger LOG = LoggerFactory.getLogger(LdapConnectionTemplateFactory.class); + + // Inject the persisted configuration (when available) check the provider implementation for details. + @Inject + private Provider<AmbariLdapConfiguration> ambariLdapConfigurationProvider; + + + @Inject + private LdapConnectionConfigService ldapConnectionConfigService; + + // cached instance that only changes when the underlying configuration changes. + private LdapConnectionTemplate ldapConnectionTemplateInstance; + + + @Inject + public LdapConnectionTemplateFactory() { + } + + /** + * Creates a new instance based on the provided configuration. Use this factory method whle operating with ambari configuration not yet persisted. + * + * @param ambariLdapConfiguration ambari ldap configuration instance + * @return an instance of LdapConnectionTemplate + */ + public LdapConnectionTemplate create(AmbariLdapConfiguration ambariLdapConfiguration) throws AmbariLdapException { + LOG.info("Constructing new instance based on the provided ambari ldap configuration: {}", ambariLdapConfiguration); + + // create the connection config + LdapConnectionConfig ldapConnectionConfig = ldapConnectionConfigService.createLdapConnectionConfig(ambariLdapConfiguration); + + // create the connection factory + LdapConnectionFactory ldapConnectionFactory = new DefaultLdapConnectionFactory(ldapConnectionConfig); + + // create the connection pool + LdapConnectionPool ldapConnectionPool = new LdapConnectionPool(new ValidatingPoolableLdapConnectionFactory(ldapConnectionFactory)); + + LdapConnectionTemplate template = new LdapConnectionTemplate(ldapConnectionPool); + LOG.info("Ldap connection template instance: {}", template); + + return template; + + } + + /** + * Loads the persisted LDAP configuration. + * + * @return theh persisted + */ + public LdapConnectionTemplate load() throws AmbariLdapException { + + if (null == ldapConnectionTemplateInstance) { + ldapConnectionTemplateInstance = create(ambariLdapConfigurationProvider.get()); + } + return ldapConnectionTemplateInstance; + } + + /** + * The returned connection template instance is recreated whenever the ambari ldap configuration changes + * + * @param event + * @throws AmbariLdapException + */ + @Subscribe + public void onConfigChange(AmbariLdapConfigChangedEvent event) throws AmbariLdapException { + ldapConnectionTemplateInstance = create(ambariLdapConfigurationProvider.get()); + } + + +} http://git-wip-us.apache.org/repos/asf/ambari/blob/30415a18/ambari-server/src/main/java/org/apache/ambari/server/ldap/service/ads/detectors/AttributeDetectorFactory.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/ldap/service/ads/detectors/AttributeDetectorFactory.java b/ambari-server/src/main/java/org/apache/ambari/server/ldap/service/ads/detectors/AttributeDetectorFactory.java new file mode 100644 index 0000000..eba0bd9 --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/ldap/service/ads/detectors/AttributeDetectorFactory.java @@ -0,0 +1,75 @@ +/* + * 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.ldap.service.ads.detectors; + +import java.util.Set; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.apache.ambari.server.ldap.service.AttributeDetector; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Factory for attribute detector chains. + */ +@Singleton +public class AttributeDetectorFactory { + + private static final Logger LOG = LoggerFactory.getLogger(AttributeDetectorFactory.class); + private static final String USER_ATTRIBUTES_DETECTORS = "UserAttributesDetectors"; + private static final String GROUP_ATTRIBUTES_DETECTORS = "GroupAttributesDetectors"; + /** + * The set of group attribute detectors, configured by GUICE (check the relevant guice module implementation) + */ + @Inject + @Named(GROUP_ATTRIBUTES_DETECTORS) + Set<AttributeDetector> groupAttributeDetectors; + /** + * The set of user attribute detectors, configured by GUICE (check the relevant guice module implementation) + */ + @Inject + @Named(USER_ATTRIBUTES_DETECTORS) + private Set<AttributeDetector> userAttributeDetectors; + + @Inject + public AttributeDetectorFactory() { + } + + /** + * Creates a chained attribute detector instance with user attribute detectors + * + * @return the constructed ChainedAttributeDetector instance + */ + public ChainedAttributeDetector userAttributDetector() { + LOG.info("Creating instance with user attribute detectors: [{}]", userAttributeDetectors); + return new ChainedAttributeDetector(userAttributeDetectors); + } + + /** + * Creates a chained attribute detector instance with user attribute detectors + * + * @return the constructed ChainedAttributeDetector instance + */ + + public ChainedAttributeDetector groupAttributeDetector() { + LOG.info("Creating instance with group attribute detectors: [{}]", groupAttributeDetectors); + return new ChainedAttributeDetector(groupAttributeDetectors); + } + + +} http://git-wip-us.apache.org/repos/asf/ambari/blob/30415a18/ambari-server/src/main/java/org/apache/ambari/server/ldap/service/ads/detectors/ChainedAttributeDetector.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/ldap/service/ads/detectors/ChainedAttributeDetector.java b/ambari-server/src/main/java/org/apache/ambari/server/ldap/service/ads/detectors/ChainedAttributeDetector.java new file mode 100644 index 0000000..094922b --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/ldap/service/ads/detectors/ChainedAttributeDetector.java @@ -0,0 +1,73 @@ +/* + * 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.ldap.service.ads.detectors; + +import java.util.Map; +import java.util.Set; + +import javax.inject.Inject; +import javax.inject.Singleton; + +import org.apache.ambari.server.ldap.service.AttributeDetector; +import org.apache.directory.api.ldap.model.entry.Entry; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.collect.Maps; + +/** + * Attribute detector implementation that performs the attribute detection on a configured set of attribute detectors. + * (it implements the composite design pattern) + */ +@Singleton +public class ChainedAttributeDetector implements AttributeDetector<Entry> { + + private static final Logger LOG = LoggerFactory.getLogger(ChainedAttributeDetector.class); + + /** + * The set of detectors this instance delegates to + */ + private final Set<AttributeDetector> detectors; + + @Inject + public ChainedAttributeDetector(Set<AttributeDetector> detectors) { + this.detectors = detectors; + } + + @Override + public void collect(Entry entry) { + for (AttributeDetector detector : detectors) { + LOG.info("Collecting information for the detector: [{}]", detector); + detector.collect(entry); + } + } + + @Override + public Map<String, String> detect() { + Map<String, String> detectedAttributes = Maps.newHashMap(); + for (AttributeDetector detector : detectors) { + LOG.info("Detecting ldap configuration value using the detector: [{}]", detector); + detectedAttributes.putAll(detector.detect()); + } + return detectedAttributes; + } + + @Override + public String toString() { + return "ChainedAttributeDetector{" + + "detectors=" + detectors + + '}'; + } +} http://git-wip-us.apache.org/repos/asf/ambari/blob/30415a18/ambari-server/src/main/java/org/apache/ambari/server/ldap/service/ads/detectors/GroupMemberAttrDetector.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/ldap/service/ads/detectors/GroupMemberAttrDetector.java b/ambari-server/src/main/java/org/apache/ambari/server/ldap/service/ads/detectors/GroupMemberAttrDetector.java new file mode 100644 index 0000000..8c34ef8 --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/ldap/service/ads/detectors/GroupMemberAttrDetector.java @@ -0,0 +1,65 @@ +/* + * 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.ldap.service.ads.detectors; + +import javax.inject.Inject; + +import org.apache.ambari.server.ldap.domain.AmbariLdapConfigKeys; +import org.apache.directory.api.ldap.model.entry.Entry; + +public class GroupMemberAttrDetector extends OccurrenceAndWeightBasedDetector { + + enum GroupMemberAttr { + + MEMBER("member", 1), + MEMBER_UID("memberUid", 1), + UNIQUE_MEMBER("uniqueMember", 1); + + private String attrName; + private Integer weight; + + GroupMemberAttr(String attr, Integer weght) { + this.attrName = attr; + this.weight = weght; + } + + Integer weight() { + return this.weight; + } + + String attrName() { + return this.attrName; + } + + } + + @Inject + public GroupMemberAttrDetector() { + for (GroupMemberAttr groupMemberAttr : GroupMemberAttr.values()) { + occurrenceMap().put(groupMemberAttr.attrName(), 0); + weightsMap().put(groupMemberAttr.attrName(), groupMemberAttr.weight()); + } + } + + @Override + protected boolean applies(Entry entry, String attribute) { + return entry.containsAttribute(attribute); + } + + @Override + public String detectedProperty() { + return AmbariLdapConfigKeys.GROUP_MEMBER_ATTRIBUTE.key(); + } +} http://git-wip-us.apache.org/repos/asf/ambari/blob/30415a18/ambari-server/src/main/java/org/apache/ambari/server/ldap/service/ads/detectors/GroupNameAttrDetector.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/ldap/service/ads/detectors/GroupNameAttrDetector.java b/ambari-server/src/main/java/org/apache/ambari/server/ldap/service/ads/detectors/GroupNameAttrDetector.java new file mode 100644 index 0000000..0315ef2 --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/ldap/service/ads/detectors/GroupNameAttrDetector.java @@ -0,0 +1,70 @@ +/* + * 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.ldap.service.ads.detectors; + +import javax.inject.Inject; + +import org.apache.ambari.server.ldap.domain.AmbariLdapConfigKeys; +import org.apache.directory.api.ldap.model.entry.Entry; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class GroupNameAttrDetector extends OccurrenceAndWeightBasedDetector { + private static final Logger LOGGER = LoggerFactory.getLogger(UserNameAttrDetector.class); + + private enum GroupNameAttr { + + DISTINGUISHED_NAME("distinguishedName", 1), + + CN("cn", 1); + + private String attrName; + private Integer weight; + + GroupNameAttr(String attr, Integer weght) { + this.attrName = attr; + this.weight = weght; + } + + Integer weight() { + return this.weight; + } + + String attrName() { + return this.attrName; + } + + } + + @Inject + public GroupNameAttrDetector() { + + for (GroupNameAttr groupNameAttr : GroupNameAttr.values()) { + occurrenceMap().put(groupNameAttr.attrName(), 0); + weightsMap().put(groupNameAttr.attrName(), groupNameAttr.weight()); + } + } + + + @Override + protected boolean applies(Entry entry, String attribute) { + return entry.containsAttribute(attribute); + } + + @Override + public String detectedProperty() { + return AmbariLdapConfigKeys.GROUP_NAME_ATTRIBUTE.key(); + } +} http://git-wip-us.apache.org/repos/asf/ambari/blob/30415a18/ambari-server/src/main/java/org/apache/ambari/server/ldap/service/ads/detectors/GroupObjectClassDetector.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/ldap/service/ads/detectors/GroupObjectClassDetector.java b/ambari-server/src/main/java/org/apache/ambari/server/ldap/service/ads/detectors/GroupObjectClassDetector.java new file mode 100644 index 0000000..b681134 --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/ldap/service/ads/detectors/GroupObjectClassDetector.java @@ -0,0 +1,73 @@ +/* + * 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.ldap.service.ads.detectors; + +import javax.inject.Inject; + +import org.apache.ambari.server.ldap.domain.AmbariLdapConfigKeys; +import org.apache.directory.api.ldap.model.entry.Entry; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class GroupObjectClassDetector extends OccurrenceAndWeightBasedDetector { + + private static final Logger LOGGER = LoggerFactory.getLogger(GroupObjectClassDetector.class); + + private enum ObjectClassValue { + + GROUP("group", 1), + + GROUP_OF_NAMES("groupOfNames", 1), + + POSIX_GROUP("posixGroup", 1), + + GROUP_OF_UNIQUE_NAMES("groupOfUniqueNames", 1); + + private String ocVal; + private Integer weight; + + ObjectClassValue(String attr, Integer weght) { + this.ocVal = attr; + this.weight = weght; + } + + Integer weight() { + return this.weight; + } + + String ocVal() { + return this.ocVal; + } + + } + + @Inject + public GroupObjectClassDetector() { + for (ObjectClassValue ocVal : ObjectClassValue.values()) { + occurrenceMap().put(ocVal.ocVal(), 0); + weightsMap().put(ocVal.ocVal(), ocVal.weight()); + } + } + + @Override + protected boolean applies(Entry entry, String attribute) { + return entry.hasObjectClass(attribute); + } + + @Override + public String detectedProperty() { + return AmbariLdapConfigKeys.GROUP_OBJECT_CLASS.key(); + } +} http://git-wip-us.apache.org/repos/asf/ambari/blob/30415a18/ambari-server/src/main/java/org/apache/ambari/server/ldap/service/ads/detectors/OccurrenceAndWeightBasedDetector.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/ldap/service/ads/detectors/OccurrenceAndWeightBasedDetector.java b/ambari-server/src/main/java/org/apache/ambari/server/ldap/service/ads/detectors/OccurrenceAndWeightBasedDetector.java new file mode 100644 index 0000000..6ce7ca6 --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/ldap/service/ads/detectors/OccurrenceAndWeightBasedDetector.java @@ -0,0 +1,143 @@ +/* + * 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.ldap.service.ads.detectors; + +import java.util.Map; + +import org.apache.ambari.server.ldap.service.AttributeDetector; +import org.apache.directory.api.ldap.model.entry.Entry; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.collect.Maps; + +/** + * Attribute detector implementation that detects attributes considering their count of occurrence in a sample set of entries. + * When multiple values are checked these values can be assigned a weight, that represents it's importance. + */ +public abstract class OccurrenceAndWeightBasedDetector implements AttributeDetector<Entry> { + + private static final Logger LOGGER = LoggerFactory.getLogger(OccurrenceAndWeightBasedDetector.class); + + /** + * A map in which the keys are the attributes that are checked in an entry and the values are the number the key occurs + * in the sample entry set. + */ + private Map<String, Integer> occurrenceMap = Maps.newHashMap(); + + /** + * A map in which the keys are the attributes that are checked in an entry and the values are the weight of the attribute. + */ + private Map<String, Integer> weightsMap = Maps.newHashMap(); + + protected Map<String, Integer> occurrenceMap() { + return occurrenceMap; + } + + protected Map<String, Integer> weightsMap() { + return weightsMap; + } + + + /** + * Checks whether the provided atribute is present in the entry.s + * + * @param entry the entry being procesed + * @param attribute the attribute being detected + * @return true if the attribute is present, false otherwise + */ + protected abstract boolean applies(Entry entry, String attribute); + + /** + * The configuration key being detected. + * + * @return the key as a string + */ + public abstract String detectedProperty(); + + /** + * Calculates the attribute value based on the two maps. + * + * @return a map with a single element, the key is the configuration key, the value is the detected attribute value + */ + @Override + public Map<String, String> detect() { + LOGGER.info("Calculating the most probable attribute/value ..."); + Map<String, String> detectedMap = Maps.newHashMap(); + + Map.Entry<String, Integer> selectedEntry = null; + + for (Map.Entry<String, Integer> entry : occurrenceMap().entrySet()) { + if (selectedEntry == null) { + + selectedEntry = entry; + LOGGER.debug("Initial attribute / value entry: {}", selectedEntry); + continue; + + } + + if (selectedEntry.getValue() < entry.getValue()) { + + LOGGER.info("Changing potential attribute / value entry from : [{}] to: [{}]", selectedEntry, entry); + selectedEntry = entry; + + } + } + + // check whether the selected entry is valid (has occured in the sample result set) + String detectedVal = "N/A"; + + if (selectedEntry.getValue() > 0) { + detectedVal = selectedEntry.getKey(); + } else { + LOGGER.warn("Unable to detect attribute or attribute value"); + } + + LOGGER.info("Detected attribute or value: [{}]", detectedVal); + detectedMap.put(detectedProperty(), detectedVal); + return detectedMap; + } + + + /** + * Collects the information about the attribute to be detected from the provided entry. + * + * @param entry a result entry returned by a search operation + */ + @Override + public void collect(Entry entry) { + LOGGER.info("Collecting ldap attributes/values form entry with dn: [{}]", entry.getDn()); + + for (String attributeValue : occurrenceMap().keySet()) { + if (applies(entry, attributeValue)) { + + Integer cnt = occurrenceMap().get(attributeValue).intValue(); + if (weightsMap().containsKey(attributeValue)) { + cnt = cnt + weightsMap().get(attributeValue); + } else { + cnt = cnt + 1; + } + occurrenceMap().put(attributeValue, cnt); + + LOGGER.info("Collected potential name attr: {}, count: {}", attributeValue, cnt); + + } else { + LOGGER.info("The result entry doesn't contain the attribute: [{}]", attributeValue); + } + } + } + + +}