You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by sm...@apache.org on 2018/11/06 18:08:51 UTC

[ambari] branch trunk updated: AMBARI-24861. New wrapper class on Python side for LDAP-related data for use in service advisors (#2579)

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

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


The following commit(s) were added to refs/heads/trunk by this push:
     new 66a3bf9  AMBARI-24861. New wrapper class on Python side for LDAP-related data for use in service advisors (#2579)
66a3bf9 is described below

commit 66a3bf93adbecdda74b1ea76cde4f39c49b9beaa
Author: Sandor Molnar <sm...@apache.org>
AuthorDate: Tue Nov 6 19:08:46 2018 +0100

    AMBARI-24861. New wrapper class on Python side for LDAP-related data for use in service advisors (#2579)
---
 .../ldap/domain/AmbariLdapConfiguration.java       |   4 +
 .../main/resources/stacks/ambari_configuration.py  | 225 +++++++++++++++++++++
 ...configuration.py => TestAmbariConfiguration.py} | 126 +++++++++++-
 3 files changed, 354 insertions(+), 1 deletion(-)

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
index fe99a35..c55f337 100644
--- 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
@@ -34,6 +34,10 @@ import org.slf4j.LoggerFactory;
 /**
  * This class is an immutable representation of all the LDAP related
  * configurationMap entries.
+ * <p>
+ * <strong>IMPORTANT: </strong>in case you declare any new LDAP related property
+ * please do it in the Python class
+ * <code>stacks.ambari_configuration.AmbariLDAPConfiguration</code> too.
  */
 public class AmbariLdapConfiguration {
 
diff --git a/ambari-server/src/main/resources/stacks/ambari_configuration.py b/ambari-server/src/main/resources/stacks/ambari_configuration.py
index d327c44..9104c32 100644
--- a/ambari-server/src/main/resources/stacks/ambari_configuration.py
+++ b/ambari-server/src/main/resources/stacks/ambari_configuration.py
@@ -107,6 +107,19 @@ class AmbariConfiguration(object):
     """
     return AmbariSSODetails(self.get_ambari_sso_configuration())
 
+  def get_ambari_ldap_configuration(self):
+    """
+    Safely gets a dictionary of properties for the "ldap-configuration" category.
+
+    :return: a dictionary or None, if "ldap-configuration" is not available
+    """
+    return self.get_ambari_server_configuration_category("ldap-configuration")
+
+  def get_ambari_ldap_details(self):
+    """
+    :return: instance of AmbariLDAPConfiguration that may be used to configure a service for LDAP integration
+    """
+    return AmbariLDAPConfiguration(self.get_ambari_ldap_configuration())
 
 class AmbariSSODetails(object):
   """
@@ -262,3 +275,215 @@ class AmbariSSODetails(object):
         public_cert = public_cert.replace('\n', '')
 
     return public_cert
+
+
+class AmbariLDAPConfiguration:
+  """
+  AmbariLDAPConfiguration encapsulates the LDAP configuration data specified in the ambari-server-configuration data.
+  The public API of class mirrors the following Java class's public API (except for trust store related API and getLdapServerProperties which we do not need in Pyton side):
+  org.apache.ambari.server.ldap.domain.AmbariLdapConfiguration
+  """
+
+  def __init__(self, ldap_properties):
+    self.ldap_properties = ldap_properties
+
+  def is_ldap_enabled(self):
+    return "true" == _get_from_dictionary(self.ldap_properties, 'ambari.ldap.authentication.enabled')
+
+  def get_server_host(self):
+    '''
+    :return: The LDAP URL host used for connecting to an LDAP server when authenticating users or None if ldap-configuration/ambari.ldap.connectivity.server.host is not specified
+    '''
+    return _get_from_dictionary(self.ldap_properties, 'ambari.ldap.connectivity.server.host')
+
+  def get_server_port(self):
+    '''
+    :return: The LDAP URL port (as an integer) used for connecting to an LDAP server when authenticating users or None if ldap-configuration/ambari.ldap.connectivity.server.port is not specified
+    '''
+    ldap_server_port = _get_from_dictionary(self.ldap_properties, 'ambari.ldap.connectivity.server.port')
+    return int(ldap_server_port) if ldap_server_port is not None else None
+
+  def get_server_url(self):
+    '''
+    :return: The LDAP URL (host:port) used for connecting to an LDAP server when authenticating users
+    '''
+    ldap_host = self.get_server_host()
+    ldap_port = self.get_server_port()
+    return None if ldap_host is None or ldap_port is None else '{}:{}'.format(ldap_host,ldap_port)
+
+  def get_secondary_server_host(self):
+    '''
+    :return: A second LDAP URL host to use as a backup when authenticating users or None if ldap-configuration/ambari.ldap.connectivity.secondary.server.host is not specified
+    '''
+    return _get_from_dictionary(self.ldap_properties, 'ambari.ldap.connectivity.secondary.server.host')
+
+  def get_secondary_server_port(self):
+    '''
+    :return: A second LDAP URL port (as an integer) to use as a backup when authenticating users or None if ldap-configuration/ambari.ldap.connectivity.secondary.server.port is not specified
+    '''
+    ldap_server_secondary_port = _get_from_dictionary(self.ldap_properties, 'ambari.ldap.connectivity.secondary.server.port')
+    return int(ldap_server_secondary_port) if ldap_server_secondary_port is not None else None
+
+  def get_secondary_server_url(self):
+    '''
+    :return: A second LDAP URL (host:port) used for connecting to an LDAP server when authenticating users
+    '''
+    ldap_host = self.get_secondary_server_host()
+    ldap_port = self.get_secondary_server_port()
+    return None if ldap_host is None or ldap_port is None else '{}:{}'.format(ldap_host,ldap_port)
+
+  def is_use_ssl(self):
+    '''
+    :return: Whether to use LDAP over SSL (LDAPS).
+    '''
+    return "true" == _get_from_dictionary(self.ldap_properties, 'ambari.ldap.connectivity.use_ssl')
+
+  def is_anonymous_bind(self):
+    '''
+    :return: Whether LDAP requests can connect anonymously or if a managed user is required to connect
+    '''
+    return "true" == _get_from_dictionary(self.ldap_properties, 'ambari.ldap.connectivity.anonymous_bind')
+
+  def get_bind_dn(self):
+    '''
+    :return: The DN of the manager account to use when binding to LDAP if anonymous binding is disabled or None if ldap-configuration/ is not specified
+    '''
+    return _get_from_dictionary(self.ldap_properties, 'ambari.ldap.connectivity.bind_dn')
+
+  def get_bind_password(self):
+    '''
+    :return: The password for the manager account used to bind to LDAP if anonymous binding is disabled
+    '''
+    return _get_from_dictionary(self.ldap_properties, 'ambari.ldap.connectivity.bind_password')
+
+  def get_dn_attribute(self):
+    '''
+    :return: The attribute used for determining what the distinguished name property is or None if ldap-configuration/ambari.ldap.attributes.dn_attr is not specified
+    '''
+    return _get_from_dictionary(self.ldap_properties, 'ambari.ldap.attributes.dn_attr')
+
+  def get_user_object_class(self):
+    '''
+    :return: The class to which user objects in LDAP belong or None if ldap-configuration/ambari.ldap.attributes.user.object_class is not specified
+    '''
+    return _get_from_dictionary(self.ldap_properties, 'ambari.ldap.attributes.user.object_class')
+
+  def get_user_name_attribute(self):
+    '''
+    :return: The attribute used for determining the user name, such as 'uid' or None if ldap-configuration/ambari.ldap.attributes.user.name_attr is not specified
+    '''
+    return _get_from_dictionary(self.ldap_properties, 'ambari.ldap.attributes.user.name_attr')
+
+  def get_user_search_base(self):
+    '''
+    :return: The base DN to use when filtering LDAP users and groups. This is only used when LDAP authentication is enabled or None if ldap-configuration/ambari.ldap.attributes.user.search_base is not specified
+    '''
+    return _get_from_dictionary(self.ldap_properties, 'ambari.ldap.attributes.user.search_base')
+
+  def get_group_object_class(self):
+    '''
+    :return: The LDAP object class value that defines groups in the directory service or None if ldap-configuration/ambari.ldap.attributes.group.object_class is not specified
+    '''
+    return _get_from_dictionary(self.ldap_properties, 'ambari.ldap.attributes.group.object_class')
+
+  def get_group_name_attribute(self):
+    '''
+    :return: The attribute used to determine the group name in LDAP  or None if ldap-configuration/ambari.ldap.attributes.group.name_attr is not specified
+    '''
+    return _get_from_dictionary(self.ldap_properties, 'ambari.ldap.attributes.group.name_attr')
+
+  def get_group_member_attribute(self):
+    '''
+    :return: The LDAP attribute which identifies group membership  or None if ldap-configuration/ambari.ldap.attributes.group.member_attr is not specified
+    '''
+    return _get_from_dictionary(self.ldap_properties, 'ambari.ldap.attributes.group.member_attr')
+
+  def get_group_search_base(self):
+    '''
+    :return: The base DN to use when filtering LDAP users and groups or None if ldap-configuration/ambari.ldap.attributes.group.search_base is not specified
+    '''
+    return _get_from_dictionary(self.ldap_properties, 'ambari.ldap.attributes.group.search_base')
+
+  def get_group_mapping_rules(self):
+    '''
+    :return: A comma-separate list of groups which would give a user administrative access to Ambari when syncing from LDAP or None if ldap-configuration/ambari.ldap.advanced.group_mapping_rules is not specified
+    '''
+    return _get_from_dictionary(self.ldap_properties, 'ambari.ldap.advanced.group_mapping_rules')
+
+  def get_user_search_filter(self):
+    '''
+    :return: A filter used to lookup a user in LDAP based on the Ambari user name or None if ldap-configuration/ambari.ldap.advanced.user_search_filter is not specified
+    '''
+    return _get_from_dictionary(self.ldap_properties, 'ambari.ldap.advanced.user_search_filter')
+
+  def get_user_member_replace_pattern(self):
+    '''
+    :return: The regex pattern to use when replacing the user member attribute ID value with a placeholder or None if ldap-configuration/ambari.ldap.advanced.user_member_replace_pattern is not specified
+    '''
+    return _get_from_dictionary(self.ldap_properties, 'ambari.ldap.advanced.user_member_replace_pattern')
+
+  def get_user_member_filter(self):
+    '''
+    :return: The filter to use for syncing user members of a group from LDAP or None if ldap-configuration/ambari.ldap.advanced.user_member_filter is not specified
+    '''
+    return _get_from_dictionary(self.ldap_properties, 'ambari.ldap.advanced.user_member_filter')
+
+  def get_group_search_filter(self):
+    '''
+    :return: The DN to use when searching for LDAP groups or None if ldap-configuration/ambari.ldap.advanced.group_search_filter is not specified
+    '''
+    return _get_from_dictionary(self.ldap_properties, 'ambari.ldap.advanced.group_search_filter')
+
+  def get_group_member_replace_pattern(self):
+    '''
+    :return: The regex pattern to use when replacing the group member attribute ID value with a placeholder or None if ldap-configuration/ambari.ldap.advanced.group_member_replace_pattern is not specified
+    '''
+    return _get_from_dictionary(self.ldap_properties, 'ambari.ldap.advanced.group_member_replace_pattern')
+
+  def get_group_member_filter(self):
+    '''
+    :return: The F=filter to use for syncing group members of a group from LDAP or None if ldap-configuration/ambari.ldap.advanced.group_member_filter is not specified
+    '''
+    return _get_from_dictionary(self.ldap_properties, 'ambari.ldap.advanced.group_member_filter')
+
+  def is_force_lower_case_user_names(self):
+    '''
+    :return: Whether to force the ldap user name to be lowercase or leave as-is
+    '''
+    return "true" == _get_from_dictionary(self.ldap_properties, 'ambari.ldap.advanced.force_lowercase_usernames')
+
+  def is_pagination_enabled(self):
+    '''
+    :return: Whether results from LDAP are paginated when requested
+    '''
+    return "true" == _get_from_dictionary(self.ldap_properties, 'ambari.ldap.advanced.pagination_enabled')
+
+  def is_follow_referral_handling(self):
+    '''
+    :return: Whether to follow LDAP referrals to other URLs when the LDAP controller doesn't have the requested object
+    '''
+    return "true" == _get_from_dictionary(self.ldap_properties, 'ambari.ldap.advanced.referrals')
+
+  def is_disable_endpoint_identification(self):
+    '''
+    :return: Whether to disable endpoint identification (hostname verification) during SSL handshake while updating from LDAP
+    '''
+    return "true" == _get_from_dictionary(self.ldap_properties, 'ambari.ldap.advanced.disable_endpoint_identification')
+
+  def is_ldap_alternate_user_search_enabled(self):
+    '''
+    :return: Whether a secondary (alternate) LDAP user search filer is used if the primary filter fails to find a user
+    '''
+    return "true" == _get_from_dictionary(self.ldap_properties, 'ambari.ldap.advanced.alternate_user_search_enabled')
+
+  def get_alternate_user_search_filter(self):
+    '''
+    :return: An alternate LDAP user search filter which can be used if 'ldap_alternate_user_search_enabled' is enabled and the primary filter fails to find a user
+    '''
+    return _get_from_dictionary(self.ldap_properties, 'ambari.ldap.advanced.alternate_user_search_filter')
+
+  def get_sync_collision_handling_behavior(self):
+    '''
+    :return: How to handle username collision while updating from LDAP or None if ldap-configuration/ambari.ldap.advanced.collision_behavior is not specified
+    '''
+    return _get_from_dictionary(self.ldap_properties, 'ambari.ldap.advanced.collision_behavior')
diff --git a/ambari-server/src/test/python/stacks/test_ambari_configuration.py b/ambari-server/src/test/python/TestAmbariConfiguration.py
similarity index 61%
rename from ambari-server/src/test/python/stacks/test_ambari_configuration.py
rename to ambari-server/src/test/python/TestAmbariConfiguration.py
index 5894e49..58062e1 100644
--- a/ambari-server/src/test/python/stacks/test_ambari_configuration.py
+++ b/ambari-server/src/test/python/TestAmbariConfiguration.py
@@ -26,7 +26,7 @@ class TestAmbariConfiguration(TestCase):
     import imp
     self.test_directory = os.path.dirname(os.path.abspath(__file__))
 
-    relative_path = '../../../main/resources/stacks/ambari_configuration.py'
+    relative_path = '../../main/resources/stacks/ambari_configuration.py'
     ambari_configuration_path = os.path.abspath(os.path.join(self.test_directory, relative_path))
     class_name = 'AmbariConfiguration'
 
@@ -41,6 +41,7 @@ class TestAmbariConfiguration(TestCase):
     ambari_configuration = self.ambari_configuration_class('{}')
     self.assertIsNone(ambari_configuration.get_ambari_server_configuration())
     self.assertIsNone(ambari_configuration.get_ambari_sso_configuration())
+    self.assertIsNone(ambari_configuration.get_ambari_ldap_configuration())
 
   def testMissingSSOConfiguration(self):
     services_json = {
@@ -280,3 +281,126 @@ class TestAmbariConfiguration(TestCase):
                       '................................................................'
                       'dXRpbmcxFzAVBgNVBAMTDmNsb3VkYnJlYWstcmdsMSUwIwYJKoZIhvcNAQkBFhZy',
                       ambari_sso_details.get_sso_provider_certificate(False, True))
+
+  def testMissingLDAPConfiguration(self):
+    services_json = {
+      "ambari-server-configuration": {
+      }
+    }
+
+    ambari_configuration = self.ambari_configuration_class(services_json)
+    self.assertIsNone(ambari_configuration.get_ambari_ldap_configuration())
+
+    ambari_ldap_details = ambari_configuration.get_ambari_ldap_details()
+    self.assertIsNotNone(ambari_ldap_details)
+    self.assertFalse(ambari_ldap_details.is_ldap_enabled())
+    self.assertIsNone(ambari_ldap_details.get_server_host())
+    self.assertIsNone(ambari_ldap_details.get_server_port())
+    self.assertIsNone(ambari_ldap_details.get_server_url())
+    self.assertIsNone(ambari_ldap_details.get_secondary_server_host())
+    self.assertIsNone(ambari_ldap_details.get_secondary_server_port())
+    self.assertIsNone(ambari_ldap_details.get_secondary_server_url())
+    self.assertFalse(ambari_ldap_details.is_use_ssl())
+    self.assertFalse(ambari_ldap_details.is_anonymous_bind())
+    self.assertIsNone(ambari_ldap_details.get_bind_dn())
+    self.assertIsNone(ambari_ldap_details.get_bind_password())
+    self.assertIsNone(ambari_ldap_details.get_dn_attribute())
+    self.assertIsNone(ambari_ldap_details.get_user_object_class())
+    self.assertIsNone(ambari_ldap_details.get_user_name_attribute())
+    self.assertIsNone(ambari_ldap_details.get_user_search_base())
+    self.assertIsNone(ambari_ldap_details.get_group_object_class())
+    self.assertIsNone(ambari_ldap_details.get_group_name_attribute())
+    self.assertIsNone(ambari_ldap_details.get_group_member_attribute())
+    self.assertIsNone(ambari_ldap_details.get_group_search_base())
+    self.assertIsNone(ambari_ldap_details.get_group_mapping_rules())
+    self.assertIsNone(ambari_ldap_details.get_user_search_filter())
+    self.assertIsNone(ambari_ldap_details.get_user_member_replace_pattern())
+    self.assertIsNone(ambari_ldap_details.get_user_member_filter())
+    self.assertIsNone(ambari_ldap_details.get_group_search_filter())
+    self.assertIsNone(ambari_ldap_details.get_group_member_replace_pattern())
+    self.assertIsNone(ambari_ldap_details.get_group_member_filter())
+    self.assertFalse(ambari_ldap_details.is_force_lower_case_user_names())
+    self.assertFalse(ambari_ldap_details.is_pagination_enabled())
+    self.assertFalse(ambari_ldap_details.is_follow_referral_handling())
+    self.assertFalse(ambari_ldap_details.is_disable_endpoint_identification())
+    self.assertFalse(ambari_ldap_details.is_ldap_alternate_user_search_enabled())
+    self.assertIsNone(ambari_ldap_details.get_alternate_user_search_filter())
+    self.assertIsNone(ambari_ldap_details.get_sync_collision_handling_behavior())
+
+  def testNotEmtpyLDAPConfiguration(self):
+    services_json = {
+        "ambari-server-configuration": {
+          "ldap-configuration": {
+            "ambari.ldap.authentication.enabled" : "true",
+            "ambari.ldap.connectivity.server.host" : "host1",
+            "ambari.ldap.connectivity.server.port" : "336",
+            "ambari.ldap.connectivity.secondary.server.host" : "host2",
+            "ambari.ldap.connectivity.secondary.server.port" : "337",
+            "ambari.ldap.connectivity.use_ssl" : "true",
+            "ambari.ldap.connectivity.anonymous_bind" : "true",
+            "ambari.ldap.connectivity.bind_dn" : "bind_dn",
+            "ambari.ldap.connectivity.bind_password" : "bind_password",
+            "ambari.ldap.attributes.dn_attr" : "dn_attr",
+            "ambari.ldap.attributes.user.object_class" : "user.object_class",
+            "ambari.ldap.attributes.user.name_attr" : "user.name_attr",
+            "ambari.ldap.attributes.user.search_base" : "user.search_base",
+            "ambari.ldap.attributes.group.object_class" : "group.object_class",
+            "ambari.ldap.attributes.group.name_attr" : "group.name_attr",
+            "ambari.ldap.attributes.group.member_attr" : "group.member_attr",
+            "ambari.ldap.attributes.group.search_base" : "group.search_base",
+            "ambari.ldap.advanced.group_mapping_rules" : "group_mapping_rules",
+            "ambari.ldap.advanced.user_search_filter" : "user_search_filter",
+            "ambari.ldap.advanced.user_member_replace_pattern" : "user_member_replace_pattern",
+            "ambari.ldap.advanced.user_member_filter" : "user_member_filter",
+            "ambari.ldap.advanced.group_search_filter" : "group_search_filter",
+            "ambari.ldap.advanced.group_member_replace_pattern" : "group_member_replace_pattern",
+            "ambari.ldap.advanced.group_member_filter" : "group_member_filter",
+            "ambari.ldap.advanced.force_lowercase_usernames" : "true",
+            "ambari.ldap.advanced.pagination_enabled" : "true",
+            "ambari.ldap.advanced.referrals" : "true",
+            "ambari.ldap.advanced.disable_endpoint_identification" : "true",
+            "ambari.ldap.advanced.alternate_user_search_enabled" : "true",
+            "ambari.ldap.advanced.alternate_user_search_filter" : "alternate_user_search_filter",
+            "ambari.ldap.advanced.collision_behavior" : "collision_behavior"
+        }
+      }
+    }
+
+    ambari_configuration = self.ambari_configuration_class(services_json)
+    self.assertIsNotNone(ambari_configuration.get_ambari_ldap_configuration())
+    ambari_ldap_details = ambari_configuration.get_ambari_ldap_details()
+    self.assertIsNotNone(ambari_ldap_details)
+
+    self.assertTrue(ambari_ldap_details.is_ldap_enabled())
+    self.assertEquals(ambari_ldap_details.get_server_host(), "host1")
+    self.assertEquals(ambari_ldap_details.get_server_port(), 336)
+    self.assertEquals(ambari_ldap_details.get_server_url(), "host1:336")
+    self.assertEquals(ambari_ldap_details.get_secondary_server_host(), "host2")
+    self.assertEquals(ambari_ldap_details.get_secondary_server_port(), 337)
+    self.assertEquals(ambari_ldap_details.get_secondary_server_url(), "host2:337")
+    self.assertTrue(ambari_ldap_details.is_use_ssl())
+    self.assertTrue(ambari_ldap_details.is_anonymous_bind())
+    self.assertEquals(ambari_ldap_details.get_bind_dn(), "bind_dn")
+    self.assertEquals(ambari_ldap_details.get_bind_password(), "bind_password")
+    self.assertEquals(ambari_ldap_details.get_dn_attribute(), "dn_attr")
+    self.assertEquals(ambari_ldap_details.get_user_object_class(), "user.object_class")
+    self.assertEquals(ambari_ldap_details.get_user_name_attribute(), "user.name_attr")
+    self.assertEquals(ambari_ldap_details.get_user_search_base(), "user.search_base")
+    self.assertEquals(ambari_ldap_details.get_group_object_class(), "group.object_class")
+    self.assertEquals(ambari_ldap_details.get_group_name_attribute(), "group.name_attr")
+    self.assertEquals(ambari_ldap_details.get_group_member_attribute(), "group.member_attr")
+    self.assertEquals(ambari_ldap_details.get_group_search_base(), "group.search_base")
+    self.assertEquals(ambari_ldap_details.get_group_mapping_rules(), "group_mapping_rules")
+    self.assertEquals(ambari_ldap_details.get_user_search_filter(), "user_search_filter")
+    self.assertEquals(ambari_ldap_details.get_user_member_replace_pattern(), "user_member_replace_pattern")
+    self.assertEquals(ambari_ldap_details.get_user_member_filter(), "user_member_filter")
+    self.assertEquals(ambari_ldap_details.get_group_search_filter(), "group_search_filter")
+    self.assertEquals(ambari_ldap_details.get_group_member_replace_pattern(), "group_member_replace_pattern")
+    self.assertEquals(ambari_ldap_details.get_group_member_filter(), "group_member_filter")
+    self.assertTrue(ambari_ldap_details.is_force_lower_case_user_names())
+    self.assertTrue(ambari_ldap_details.is_pagination_enabled())
+    self.assertTrue(ambari_ldap_details.is_follow_referral_handling())
+    self.assertTrue(ambari_ldap_details.is_disable_endpoint_identification())
+    self.assertTrue(ambari_ldap_details.is_ldap_alternate_user_search_enabled())
+    self.assertEquals(ambari_ldap_details.get_alternate_user_search_filter(), "alternate_user_search_filter")
+    self.assertEquals(ambari_ldap_details.get_sync_collision_handling_behavior(), "collision_behavior")