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/27 09:26:59 UTC

[ambari] branch trunk updated: AMBARI-24951. Using Ambari CLI to specify which services should be setup for LDAP integration (#2652)

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 4d245a1  AMBARI-24951. Using Ambari CLI to specify which services should be setup for LDAP integration (#2652)
4d245a1 is described below

commit 4d245a1196bbcf6e2ca9b77901bf3942f3ac60f8
Author: Sandor Molnar <sm...@apache.org>
AuthorDate: Tue Nov 27 10:26:53 2018 +0100

    AMBARI-24951. Using Ambari CLI to specify which services should be setup for LDAP integration (#2652)
---
 ambari-server/src/main/python/ambari-server.py     |  4 ++
 .../src/main/python/ambari_server/serverUtils.py   | 34 +++++++++-
 .../src/main/python/ambari_server/setupSecurity.py | 78 ++++++++++++++++++++--
 .../src/main/python/ambari_server/setupSso.py      | 37 +---------
 ambari-server/src/test/python/TestAmbariServer.py  | 39 +++++++++--
 ambari-server/src/test/python/TestSetupSso.py      |  5 +-
 6 files changed, 149 insertions(+), 48 deletions(-)

diff --git a/ambari-server/src/main/python/ambari-server.py b/ambari-server/src/main/python/ambari-server.py
index 4cf7325..e5a943f 100755
--- a/ambari-server/src/main/python/ambari-server.py
+++ b/ambari-server/src/main/python/ambari-server.py
@@ -581,6 +581,10 @@ def init_ldap_setup_parser_options(parser):
   parser.add_option('--truststore-path', default=None, help="Path of TrustStore", dest="trust_store_path")
   parser.add_option('--truststore-password', default=None, help="Password for TrustStore", dest="trust_store_password")
   parser.add_option('--truststore-reconfigure', action="store_true", default=None, help="Force to reconfigure TrustStore if exits", dest="trust_store_reconfigure")
+  parser.add_option('--ldap-enabled-ambari', default=None, help="Indicates whether to enable/disable LDAP authentication for Ambari, itself", dest='ldap_enabled_ambari')
+  parser.add_option('--ldap-manage-services', default=None, help="Indicates whether Ambari should manage the LDAP configurations for specified services", dest='ldap_manage_services')
+  parser.add_option('--ldap-enabled-services', default=None, help="A comma separated list of services that are expected to be configured for LDAP (you are allowed to use '*' to indicate ALL services)", dest='ldap_enabled_services')
+
 
 @OsFamilyFuncImpl(OsFamilyImpl.DEFAULT)
 def init_setup_sso_options(parser):
diff --git a/ambari-server/src/main/python/ambari_server/serverUtils.py b/ambari-server/src/main/python/ambari_server/serverUtils.py
index 7d2370e..f37e515 100644
--- a/ambari-server/src/main/python/ambari_server/serverUtils.py
+++ b/ambari-server/src/main/python/ambari_server/serverUtils.py
@@ -294,4 +294,36 @@ def is_api_ssl_enabled(properties):
   if api_ssl_prop is not None:
     ssl_enabled = api_ssl_prop.lower() == "true"
 
-  return ssl_enabled
\ No newline at end of file
+  return ssl_enabled
+
+def eligible(service_info, is_sso_integration):
+  if  is_sso_integration:
+    return service_info['sso_integration_supported'] and (not service_info['sso_integration_requires_kerberos'] or service_info['kerberos_enabled'])
+  else:
+    return service_info['ldap_integration_supported']
+
+def get_eligible_services(properties, admin_login, admin_password, cluster_name, entry_point, service_qualifier):
+  print_info_msg("Fetching %s enabled services" % service_qualifier)
+
+  safe_cluster_name = urllib2.quote(cluster_name)
+
+  response_code, json_data = get_json_via_rest_api(properties, admin_login, admin_password, entry_point % safe_cluster_name)
+
+  services = []
+
+  if json_data and 'items' in json_data:
+    services = [item['ServiceInfo']['service_name'] for item in json_data['items'] if eligible(item['ServiceInfo'], 'SSO' ==  service_qualifier)]
+
+    if len(services) > 0:
+      print_info_msg('Found %s enabled services: %s' % (service_qualifier, ', '.join(services)))
+    else:
+      print_info_msg('No %s enabled services were found' % service_qualifier)
+
+  return services
+
+def get_value_from_dictionary(properties, key, default_value=None):
+  return properties[key] if properties and key in properties else default_value
+
+def get_boolean_from_dictionary(properties, key, default_value=False):
+  value = get_value_from_dictionary(properties, key, None)
+  return 'true' == value.lower() if value else default_value
\ No newline at end of file
diff --git a/ambari-server/src/main/python/ambari_server/setupSecurity.py b/ambari-server/src/main/python/ambari_server/setupSecurity.py
index f6d0a3e..e281c16 100644
--- a/ambari-server/src/main/python/ambari_server/setupSecurity.py
+++ b/ambari-server/src/main/python/ambari_server/setupSecurity.py
@@ -54,7 +54,8 @@ from ambari_server.serverConfiguration import configDefaults, parse_properties_f
   get_resources_location, SECURITY_MASTER_KEY_LOCATION, SETUP_OR_UPGRADE_MSG, \
   CHECK_AMBARI_KRB_JAAS_CONFIGURATION_PROPERTY
 from ambari_server.serverUtils import is_server_runing, get_ambari_server_api_base, \
-  get_ambari_admin_username_password_pair, perform_changes_via_rest_api, get_ssl_context
+  get_ambari_admin_username_password_pair, perform_changes_via_rest_api, get_ssl_context, get_cluster_name, \
+  get_eligible_services, get_boolean_from_dictionary, get_value_from_dictionary
 from ambari_server.setupActions import SETUP_ACTION, LDAP_SETUP_ACTION
 from ambari_server.userInput import get_validated_string_input, get_prompt_default, read_password, get_YN_input, \
   quit_if_has_answer
@@ -89,7 +90,6 @@ SETUP_LDAP_CONFIG_URL = 'services/AMBARI/components/AMBARI_SERVER/configurations
 
 PAM_CONFIG_FILE = 'pam.configuration'
 
-IS_LDAP_CONFIGURED = "ambari.ldap.authentication.enabled"
 LDAP_MGR_USERNAME_PROPERTY = "ambari.ldap.connectivity.bind_dn"
 LDAP_MGR_PASSWORD_FILENAME = "ldap-password.dat"
 LDAP_ANONYMOUS_BIND="ambari.ldap.connectivity.anonymous_bind"
@@ -97,6 +97,12 @@ LDAP_USE_SSL="ambari.ldap.connectivity.use_ssl"
 LDAP_DISABLE_ENDPOINT_IDENTIFICATION = "ambari.ldap.advanced.disable_endpoint_identification"
 NO_AUTH_METHOD_CONFIGURED = "no auth method"
 
+AMBARI_LDAP_AUTH_ENABLED = "ambari.ldap.authentication.enabled"
+LDAP_MANAGE_SERVICES = "ambari.ldap.manage_services"
+LDAP_ENABLED_SERVICES = "ambari.ldap.enabled_services"
+WILDCARD_FOR_ALL_SERVICES = "*"
+FETCH_SERVICES_FOR_LDAP_ENTRYPOINT = "clusters/%s/services?ServiceInfo/ldap_integration_supported=true&fields=ServiceInfo/*"
+
 def read_master_key(isReset=False, options = None):
   passwordPattern = ".*"
   passwordPrompt = "Please provide master key for locking the credential store: "
@@ -338,7 +344,7 @@ def get_ldap_properties_from_db(properties, admin_login, admin_password):
   return ldap_properties
 
 def is_ldap_enabled(properties, admin_login, admin_password):
-  ldap_enabled = get_ldap_property_from_db(properties, admin_login, admin_password, IS_LDAP_CONFIGURED)
+  ldap_enabled = get_ldap_property_from_db(properties, admin_login, admin_password, AMBARI_LDAP_AUTH_ENABLED)
   return ldap_enabled if ldap_enabled is not None else 'false'
 
 
@@ -814,7 +820,9 @@ def setup_ldap(options):
                             LDAP_DISABLE_ENDPOINT_IDENTIFICATION,
                             SSL_TRUSTSTORE_TYPE_PROPERTY,
                             SSL_TRUSTSTORE_PATH_PROPERTY,
-                            SSL_TRUSTSTORE_PASSWORD_PROPERTY]
+                            SSL_TRUSTSTORE_PASSWORD_PROPERTY,
+                            LDAP_MANAGE_SERVICES,
+                            LDAP_ENABLED_SERVICES]
 
   ldap_property_list_passwords=[LDAP_MGR_PASSWORD_PROPERTY, SSL_TRUSTSTORE_PASSWORD_PROPERTY]
 
@@ -891,6 +899,9 @@ def setup_ldap(options):
         pass
       pass
 
+  populate_ambari_requires_ldap(options, ldap_property_value_map)
+  populate_service_management(options, ldap_property_value_map, properties, admin_login, admin_password)
+
   print '=' * 20
   print 'Review Settings'
   print '=' * 20
@@ -934,7 +945,6 @@ def setup_ldap(options):
 
     print 'Saving LDAP properties...'
 
-    ldap_property_value_map[IS_LDAP_CONFIGURED] = "true"
     #Saving LDAP configuration in Ambari DB using the REST API
     update_ldap_configuration(admin_login, admin_password, properties, ldap_property_value_map)
 
@@ -1127,3 +1137,61 @@ def migrate_ldap_pam(args):
   else:
     print_info_msg('LDAP to PAM migration completed')
   return retcode
+
+
+def populate_ambari_requires_ldap(options, properties):
+  if options.ldap_enabled_ambari is None:
+    enabled = get_boolean_from_dictionary(properties, AMBARI_LDAP_AUTH_ENABLED, False)
+    enabled = get_YN_input("Use LDAP authentication for Ambari [y/n] ({0})? ".format('y' if enabled else 'n'), enabled)
+  else:
+    enabled = 'true' == options.ldap_enabled_ambari
+
+  properties[AMBARI_LDAP_AUTH_ENABLED] = 'true' if enabled else 'false'
+
+
+def populate_service_management(options, properties, ambari_properties, admin_login, admin_password):
+  services = ""
+  if options.ldap_enabled_services is None:
+    if options.ldap_manage_services is None:
+      manage_services = get_boolean_from_dictionary(properties, LDAP_MANAGE_SERVICES, False)
+      manage_services = get_YN_input("Manage LDAP configurations for eligible services [y/n] ({0})? ".format('y' if manage_services else 'n'), manage_services)
+    else:
+      manage_services = 'true' == options.ldap_manage_services
+      stored_manage_services = get_boolean_from_dictionary(properties, LDAP_MANAGE_SERVICES, False)
+      print("Manage LDAP configurations for eligible services [y/n] ({0})? {1}".format('y' if stored_manage_services else 'n', 'y' if manage_services else 'n'))
+
+    if manage_services:
+      enabled_services = get_value_from_dictionary(properties, LDAP_ENABLED_SERVICES, "").upper().split(',')
+
+      all = WILDCARD_FOR_ALL_SERVICES in enabled_services
+      configure_for_all_services = get_YN_input(" Manage LDAP for all services [y/n] ({0})? ".format('y' if all else 'n'), all)
+      if configure_for_all_services:
+        services = WILDCARD_FOR_ALL_SERVICES
+      else:
+        cluster_name = get_cluster_name(ambari_properties, admin_login, admin_password)
+
+        if cluster_name:
+          eligible_services = get_eligible_services(ambari_properties, admin_login, admin_password, cluster_name, FETCH_SERVICES_FOR_LDAP_ENTRYPOINT, 'LDAP')
+
+          if eligible_services and len(eligible_services) > 0:
+            service_list = []
+
+            for service in eligible_services:
+              enabled = service.upper() in enabled_services
+              question = "   Manage LDAP for {0} [y/n] ({1})? ".format(service, 'y' if enabled else 'n')
+              if get_YN_input(question, enabled):
+                service_list.append(service)
+
+            services = ','.join(service_list)
+          else:
+            print ("   There are no eligible services installed.")
+  else:
+    if options.ldap_manage_services:
+      manage_services = 'true' == options.ldap_manage_services
+    else:
+      manage_services = True
+
+    services = options.ldap_enabled_services.upper() if options.ldap_enabled_services else ""
+
+  properties[LDAP_MANAGE_SERVICES] = 'true' if manage_services else 'false'
+  properties[LDAP_ENABLED_SERVICES] = services
\ No newline at end of file
diff --git a/ambari-server/src/main/python/ambari_server/setupSso.py b/ambari-server/src/main/python/ambari_server/setupSso.py
index 8b3a041..d65f579 100644
--- a/ambari-server/src/main/python/ambari_server/setupSso.py
+++ b/ambari-server/src/main/python/ambari_server/setupSso.py
@@ -26,7 +26,8 @@ from ambari_commons.logging_utils import get_silent, print_info_msg
 
 from ambari_server.serverConfiguration import get_ambari_properties
 from ambari_server.serverUtils import is_server_runing, get_ambari_admin_username_password_pair, \
-  get_cluster_name, perform_changes_via_rest_api, get_json_via_rest_api
+  get_cluster_name, perform_changes_via_rest_api, get_json_via_rest_api, get_eligible_services, \
+  get_boolean_from_dictionary, get_value_from_dictionary
 from ambari_server.setupSecurity import REGEX_TRUE_FALSE
 from ambari_server.userInput import get_validated_string_input, get_YN_input, get_multi_line_input
 
@@ -134,31 +135,6 @@ def populate_ambari_requires_sso(options, properties):
 
   properties[AMBARI_SSO_AUTH_ENABLED] = 'true' if enabled else 'false'
 
-def eligible(service_info):
-  return service_info['sso_integration_supported'] \
-         and (not service_info['sso_integration_requires_kerberos'] or service_info['kerberos_enabled'])
-
-def get_eligible_services(properties, admin_login, admin_password, cluster_name):
-  print_info_msg("Fetching SSO enabled services")
-
-  safe_cluster_name = urllib2.quote(cluster_name)
-
-  response_code, json_data = get_json_via_rest_api(properties, admin_login, admin_password,
-                                                   FETCH_SERVICES_FOR_SSO_ENTRYPOINT % safe_cluster_name)
-
-  services = []
-
-  if json_data and 'items' in json_data:
-    services = [item['ServiceInfo']['service_name'] for item in json_data['items'] if eligible(item['ServiceInfo'])]
-
-    if len(services) > 0:
-      print_info_msg('Found SSO enabled services: %s' % ', '.join(services))
-    else:
-      print_info_msg('No SSO enabled services were found')
-
-  return services
-
-
 def populate_service_management(options, properties, ambari_properties, admin_login, admin_password):
   if not options.sso_enabled_services:
     if not options.sso_manage_services:
@@ -183,7 +159,7 @@ def populate_service_management(options, properties, ambari_properties, admin_lo
         cluster_name = get_cluster_name(ambari_properties, admin_login, admin_password)
 
         if cluster_name:
-          eligible_services = get_eligible_services(ambari_properties, admin_login, admin_password, cluster_name)
+          eligible_services = get_eligible_services(ambari_properties, admin_login, admin_password, cluster_name, FETCH_SERVICES_FOR_SSO_ENTRYPOINT, 'SSO')
 
           if eligible_services and len(eligible_services) > 0:
             service_list = []
@@ -319,10 +295,3 @@ def ensure_complete_cert(cert_string):
 
   return cert_string
 
-def get_value_from_dictionary(properties, key, default_value=None):
-  return properties[key] if properties and key in properties else default_value
-
-def get_boolean_from_dictionary(properties, key, default_value=False):
-  value = get_value_from_dictionary(properties, key, None)
-  return 'true' == value.lower() if value else default_value
-
diff --git a/ambari-server/src/test/python/TestAmbariServer.py b/ambari-server/src/test/python/TestAmbariServer.py
index 9c4e704..4e5db52 100644
--- a/ambari-server/src/test/python/TestAmbariServer.py
+++ b/ambari-server/src/test/python/TestAmbariServer.py
@@ -7047,7 +7047,9 @@ class TestAmbariServer(TestCase):
         "ambari.ldap.attributes.user.search_base": "uid",
         "ambari.ldap.connectivity.anonymous_bind": "true",
         "ambari.ldap.advanced.referrals": "follow",
-        "ambari.ldap.authentication.enabled": "true"
+        "ambari.ldap.authentication.enabled": "true",
+        "ambari.ldap.manage_services": "true",
+        "ambari.ldap.enabled_services":"*"
       }
     return ldap_properties_map
 
@@ -7073,7 +7075,9 @@ class TestAmbariServer(TestCase):
         "ambari.ldap.advanced.collision_behavior": "skip",
         "ambari.ldap.advanced.force_lowercase_usernames": "false",
         "ambari.ldap.advanced.pagination_enabled": "false",
-        "ambari.ldap.authentication.enabled": "true"
+        "ambari.ldap.authentication.enabled": "true",
+        "ambari.ldap.manage_services": "true",
+        "ambari.ldap.enabled_services":"*"
       }
     return ldap_properties_map
 
@@ -7092,7 +7096,9 @@ class TestAmbariServer(TestCase):
         "ambari.ldap.advanced.force_lowercase_usernames": "false",
         "ambari.ldap.advanced.pagination_enabled": "false",
         "ambari.ldap.advanced.referrals": "follow",
-        "ambari.ldap.authentication.enabled": "true"
+        "ambari.ldap.authentication.enabled": "true",
+        "ambari.ldap.manage_services": "true",
+        "ambari.ldap.enabled_services":"*"
       }
     return ldap_properties_map
 
@@ -7116,7 +7122,9 @@ class TestAmbariServer(TestCase):
         "ambari.ldap.advanced.collision_behavior": "skip",
         "ambari.ldap.advanced.force_lowercase_usernames": "false",
         "ambari.ldap.advanced.pagination_enabled": "false",
-        "ambari.ldap.authentication.enabled": "true"
+        "ambari.ldap.authentication.enabled": "true",
+        "ambari.ldap.manage_services": "true",
+        "ambari.ldap.enabled_services":"*"
       }
     return ldap_properties_map
 
@@ -7280,7 +7288,9 @@ class TestAmbariServer(TestCase):
         "ambari.ldap.attributes.dn_attr": "test",
         "ambari.ldap.advanced.referrals": "test",
         LDAP_MGR_PASSWORD_PROPERTY: "ldap-password.dat",
-        "ambari.ldap.authentication.enabled": "true"
+        "ambari.ldap.authentication.enabled": "true",
+        "ambari.ldap.manage_services": "true",
+        "ambari.ldap.enabled_services":"ZOOKEEPER",
       }
     return ldap_properties_map
 
@@ -7301,7 +7311,10 @@ class TestAmbariServer(TestCase):
   @patch("ambari_server.setupSecurity.is_server_runing")
   @patch("ambari_server.setupSecurity.query_ldap_type")
   @patch("ambari_server.setupSecurity.get_ldap_properties_from_db")
-  def test_setup_ldap(self, get_ldap_properties_from_db_method, query_ldap_type_method, is_server_runing_method, logger_mock, exists_method, read_password_method, get_ambari_properties_method,
+  @patch("ambari_server.setupSecurity.get_eligible_services")
+  @patch("ambari_server.setupSecurity.get_cluster_name")
+  def test_setup_ldap(self, get_cluster_name_method, get_eligible_services_method,
+                      get_ldap_properties_from_db_method, query_ldap_type_method, is_server_runing_method, logger_mock, exists_method, read_password_method, get_ambari_properties_method,
                       search_file_message,
                       get_validated_string_input_method,
                       configure_ldap_password_method, update_properties_method,
@@ -7334,6 +7347,10 @@ class TestAmbariServer(TestCase):
     def yn_input_side_effect(*args, **kwargs):
       if 'TrustStore' in args[0]:
         return False
+      if 'for all services' in args[0]:
+        return False
+      if 'Manage LDAP for HDFS' in args[0]: #note that LDAP is enabled for HDFS (see below) but we say we do not want it to be managed by Ambari
+        return False
       else:
         return True
 
@@ -7355,6 +7372,10 @@ class TestAmbariServer(TestCase):
 
     get_validated_string_input_method.side_effect = valid_input_side_effect
 
+    get_cluster_name_method.return_value = "cluster1"
+    eligible_services = ["HDFS", "ZOOKEEPER"]
+    get_eligible_services_method.return_value = eligible_services
+
     response = MagicMock()
     response.getcode.return_value = 200
     urlopen_mock.return_value = response
@@ -7415,7 +7436,7 @@ class TestAmbariServer(TestCase):
     get_validated_string_input_method.side_effect = input_enable_ssl
     read_password_method.return_value = "password"
     get_YN_input_method.reset_mock()
-    get_YN_input_method.side_effect = [True, True, True]
+    get_YN_input_method.side_effect = [True, True, True, False, True]
     update_properties_method.reset_mock()
 
     options.ldap_primary_host = None
@@ -8781,6 +8802,10 @@ class TestAmbariServer(TestCase):
 
   def _create_empty_options_mock(self):
     options = MagicMock()
+    options.ldap_enabled = None
+    options.ldap_enabled_ambari = None
+    options.ldap_manage_services = None
+    options.ldap_enabled_services = None
     options.ldap_url = None
     options.ldap_primary_host = None
     options.ldap_primary_port = None
diff --git a/ambari-server/src/test/python/TestSetupSso.py b/ambari-server/src/test/python/TestSetupSso.py
index 802c058..b708587 100644
--- a/ambari-server/src/test/python/TestSetupSso.py
+++ b/ambari-server/src/test/python/TestSetupSso.py
@@ -482,6 +482,7 @@ class TestSetupSso(unittest.TestCase):
     self.assertEqual(ssoProperties[SSO_ENABLED_SERVICES], "*")
 
 
+  @patch("urllib2.urlopen")
   @patch("ambari_server.setupSso.perform_changes_via_rest_api")
   @patch("ambari_server.setupSso.get_cluster_name")
   @patch("ambari_server.setupSso.get_YN_input")
@@ -497,7 +498,8 @@ class TestSetupSso(unittest.TestCase):
                                                              get_ambari_properties_mock,
                                                              get_YN_input_mock,
                                                              get_cluster_name_mock,
-                                                             perform_changes_via_rest_api_mock):
+                                                             perform_changes_via_rest_api_mock,
+                                                             urlopen_mock):
     out = StringIO.StringIO()
     sys.stdout = out
 
@@ -582,6 +584,7 @@ class TestSetupSso(unittest.TestCase):
     response = MagicMock()
     response.getcode.return_value = 200
     response.read.return_value = eligible_services
+    urlopen_mock.return_value = response
 
     options = self._create_empty_options_mock()
     options.sso_enabled = 'true'