This is an automated email from the ASF dual-hosted git repository. krisztiankasa 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 1edde0c AMBARI-24516 - Default value for LDAP type (#2181) 1edde0c is described below commit 1edde0cfd7b91a76b306688252d41914979942e9 Author: kasakrisz <33458261+kasakr...@users.noreply.github.com> AuthorDate: Wed Aug 29 09:11:05 2018 +0200 AMBARI-24516 - Default value for LDAP type (#2181) --- ambari-server/src/main/python/ambari-server.py | 43 +++-- .../src/main/python/ambari_server/setupSecurity.py | 192 +++++++++++++-------- ambari-server/src/test/python/TestAmbariServer.py | 113 ++++++++++-- 3 files changed, 247 insertions(+), 101 deletions(-) diff --git a/ambari-server/src/main/python/ambari-server.py b/ambari-server/src/main/python/ambari-server.py index 6a05013..c1fc6eb 100755 --- a/ambari-server/src/main/python/ambari-server.py +++ b/ambari-server/src/main/python/ambari-server.py @@ -18,48 +18,46 @@ See the License for the specific language governing permissions and limitations under the License. ''' +import logging +import logging.config +import logging.handlers import optparse -import sys import os import signal -import logging -import logging.handlers -import logging.config - -from optparse import OptionValueError +import sys from ambari_commons.exceptions import FatalException, NonFatalException from ambari_commons.logging_utils import set_verbose, set_silent, \ print_info_msg, print_warning_msg, print_error_msg, set_debug_mode_from_options from ambari_commons.os_check import OSConst from ambari_commons.os_family_impl import OsFamilyFuncImpl, OsFamilyImpl from ambari_commons.os_utils import remove_file +from optparse import OptionValueError + from ambari_server.BackupRestore import main as BackupRestore_main +from ambari_server.checkDatabase import check_database +from ambari_server.dbCleanup import db_purge from ambari_server.dbConfiguration import DATABASE_NAMES, LINUX_DBMS_KEYS_LIST +from ambari_server.enableStack import enable_stack_version +from ambari_server.hostUpdate import update_host_names +from ambari_server.kerberos_setup import setup_kerberos from ambari_server.serverConfiguration import configDefaults, get_ambari_properties, PID_NAME -from ambari_server.serverUtils import is_server_runing, refresh_stack_hash, wait_for_server_to_stop from ambari_server.serverSetup import reset, setup, setup_jce_policy from ambari_server.serverUpgrade import upgrade, set_current +from ambari_server.serverUtils import is_server_runing, refresh_stack_hash, wait_for_server_to_stop +from ambari_server.setupActions import BACKUP_ACTION, LDAP_SETUP_ACTION, LDAP_SYNC_ACTION, PSTART_ACTION, \ + REFRESH_STACK_HASH_ACTION, RESET_ACTION, RESTORE_ACTION, UPDATE_HOST_NAMES_ACTION, CHECK_DATABASE_ACTION, \ + SETUP_ACTION, SETUP_SECURITY_ACTION, RESTART_ACTION, START_ACTION, STATUS_ACTION, STOP_ACTION, UPGRADE_ACTION, \ + SETUP_JCE_ACTION, SET_CURRENT_ACTION, ENABLE_STACK_ACTION, SETUP_SSO_ACTION, \ + DB_PURGE_ACTION, INSTALL_MPACK_ACTION, UNINSTALL_MPACK_ACTION, UPGRADE_MPACK_ACTION, PAM_SETUP_ACTION, \ + MIGRATE_LDAP_PAM_ACTION, KERBEROS_SETUP_ACTION from ambari_server.setupHttps import setup_https, setup_truststore from ambari_server.setupMpacks import install_mpack, uninstall_mpack, upgrade_mpack, STACK_DEFINITIONS_RESOURCE_NAME, \ SERVICE_DEFINITIONS_RESOURCE_NAME, MPACKS_RESOURCE_NAME +from ambari_server.setupSecurity import setup_ldap, sync_ldap, setup_master_key, setup_ambari_krb5_jaas, setup_pam, \ + migrate_ldap_pam, LDAP_TYPES from ambari_server.setupSso import setup_sso -from ambari_server.dbCleanup import db_purge -from ambari_server.hostUpdate import update_host_names -from ambari_server.checkDatabase import check_database -from ambari_server.enableStack import enable_stack_version - -from ambari_server.setupActions import BACKUP_ACTION, LDAP_SETUP_ACTION, LDAP_SYNC_ACTION, PSTART_ACTION, \ - REFRESH_STACK_HASH_ACTION, RESET_ACTION, RESTORE_ACTION, UPDATE_HOST_NAMES_ACTION, CHECK_DATABASE_ACTION, \ - SETUP_ACTION, SETUP_SECURITY_ACTION,START_ACTION, STATUS_ACTION, STOP_ACTION, RESTART_ACTION, UPGRADE_ACTION, \ - SETUP_JCE_ACTION, SET_CURRENT_ACTION, START_ACTION, STATUS_ACTION, STOP_ACTION, UPGRADE_ACTION, \ - SETUP_JCE_ACTION, SET_CURRENT_ACTION, ENABLE_STACK_ACTION, SETUP_SSO_ACTION, \ - DB_PURGE_ACTION, INSTALL_MPACK_ACTION, UNINSTALL_MPACK_ACTION, UPGRADE_MPACK_ACTION, PAM_SETUP_ACTION, MIGRATE_LDAP_PAM_ACTION, KERBEROS_SETUP_ACTION -from ambari_server.setupSecurity import setup_ldap, sync_ldap, setup_master_key, setup_ambari_krb5_jaas, setup_pam, migrate_ldap_pam from ambari_server.userInput import get_validated_string_input -from ambari_server.kerberos_setup import setup_kerberos - from ambari_server_main import server_process_main -from ambari_server.ambariPath import AmbariPath logger = logging.getLogger() @@ -552,6 +550,7 @@ def init_ldap_setup_parser_options(parser): parser.add_option('--ldap-secondary-host', action="callback", callback=check_ldap_url_options, type='str', default=None, help="Secondary Host for LDAP (must not be used together with --ldap-secondary-url)", dest="ldap_secondary_host") parser.add_option('--ldap-secondary-port', action="callback", callback=check_ldap_url_options, type='int', default=None, help="Secondary Port for LDAP (must not be used together with --ldap-secondary-url)", dest="ldap_secondary_port") parser.add_option('--ldap-ssl', default=None, help="Use SSL [true/false] for LDAP", dest="ldap_ssl") + parser.add_option('--ldap-use-generic-defaults', action="store_true", default=None, help="Disables ldap type query and generic defaults will be offered for non existent properties".format("/".join(LDAP_TYPES)), dest="ldap_use_generic_defaults") parser.add_option('--ldap-user-class', default=None, help="User Attribute Object Class for LDAP", dest="ldap_user_class") parser.add_option('--ldap-user-attr', default=None, help="User Attribute Name for LDAP", dest="ldap_user_attr") parser.add_option('--ldap-group-class', default=None, help="Group Attribute Object Class for LDAP", dest="ldap_group_class") diff --git a/ambari-server/src/main/python/ambari_server/setupSecurity.py b/ambari-server/src/main/python/ambari_server/setupSecurity.py index b35b097..e3a9f8a 100644 --- a/ambari-server/src/main/python/ambari_server/setupSecurity.py +++ b/ambari-server/src/main/python/ambari_server/setupSecurity.py @@ -59,12 +59,13 @@ 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 from contextlib import closing +from urllib2 import HTTPError logger = logging.getLogger(__name__) LDAP_AD="AD" LDAP_IPA="IPA" -LDAP_GENERIC="Generic LDAP" +LDAP_GENERIC="Generic" LDAP_TYPES = [LDAP_AD, LDAP_IPA, LDAP_GENERIC] @@ -287,8 +288,12 @@ class LdapSyncOptions: def no_ldap_sync_options_set(self): return not self.ldap_sync_all and not self.ldap_sync_existing and self.ldap_sync_users is None and self.ldap_sync_groups is None -def getLdapPropertyFromDB(properties, admin_login, admin_password, property_name): - ldapProperty = None +def get_ldap_property_from_db(properties, admin_login, admin_password, property_name): + ldap_properties_from_db = get_ldap_properties_from_db(properties, admin_login, admin_password) + return ldap_properties_from_db[property_name] if ldap_properties_from_db else None + +def get_ldap_properties_from_db(properties, admin_login, admin_password): + ldap_properties = None url = get_ambari_server_api_base(properties) + SETUP_LDAP_CONFIG_URL admin_auth = base64.encodestring('%s:%s' % (admin_login, admin_password)).replace('\n', '') request = urllib2.Request(url) @@ -298,10 +303,10 @@ def getLdapPropertyFromDB(properties, admin_login, admin_password, property_name request_in_progress = True sys.stdout.write('\nFetching LDAP configuration from DB') - numOfTries = 0 + num_of_tries = 0 while request_in_progress: - numOfTries += 1 - if (numOfTries == 60): + num_of_tries += 1 + if num_of_tries == 60: raise FatalException(1, "Could not fetch LDAP configuration within a minute; giving up!") sys.stdout.write('.') sys.stdout.flush() @@ -315,22 +320,26 @@ def getLdapPropertyFromDB(properties, admin_login, admin_password, property_name raise FatalException(1, err) else: response_body = json.loads(response.read()) - ldapProperties = response_body['Configuration']['properties'] - ldapProperty = ldapProperties[property_name] - if not ldapProperty: + ldap_properties = response_body['Configuration']['properties'] + if not ldap_properties: time.sleep(1) else: request_in_progress = False + except HTTPError as e: + if e.code == 404: + sys.stdout.write(' No configuration.') + return None + err = 'Error while fetching LDAP configuration. Error details: %s' % e + raise FatalException(1, err) except Exception as e: - request_in_progress = False err = 'Error while fetching LDAP configuration. Error details: %s' % e raise FatalException(1, err) - return ldapProperty + return ldap_properties def is_ldap_enabled(properties, admin_login, admin_password): - ldapEnabled = getLdapPropertyFromDB(properties, admin_login, admin_password, IS_LDAP_CONFIGURED) - return ldapEnabled if ldapEnabled != None else 'false' + ldap_enabled = get_ldap_property_from_db(properties, admin_login, admin_password, IS_LDAP_CONFIGURED) + return ldap_enabled if ldap_enabled is not None else 'false' # @@ -638,63 +647,98 @@ def setup_ambari_krb5_jaas(options): class LdapPropTemplate: - def __init__(self, properties, i_option, i_prop_name, i_prop_val_pattern, i_prompt_regex, i_allow_empty_prompt, i_prop_name_default=None): + def __init__(self, properties, i_option, i_prop_name, i_prop_val_pattern, i_prompt_regex, i_allow_empty_prompt, i_prop_default=None): self.prop_name = i_prop_name self.option = i_option - self.ldap_prop_value = get_value_from_properties(properties, i_prop_name, i_prop_name_default) - self.ldap_prop_val_prompt = format_prop_val_prompt(i_prop_val_pattern, self.ldap_prop_value) + stored_value = get_value_from_properties(properties, i_prop_name) + self.default_value = LdapDefault(stored_value) if stored_value else i_prop_default + self.prompt_pattern = i_prop_val_pattern self.prompt_regex = i_prompt_regex self.allow_empty_prompt = i_allow_empty_prompt + def get_default_value(self, ldap_type): + return self.default_value.get_default_value(ldap_type) if self.default_value else None + + def get_prompt_text(self, ldap_type): + default_value = self.get_default_value(ldap_type) + return format_prop_val_prompt(self.prompt_pattern, default_value) + + def get_input(self, ldap_type): + default_value = self.get_default_value(ldap_type) + return get_validated_string_input(self.get_prompt_text(ldap_type), + default_value, self.prompt_regex, + "Invalid characters in the input!", False, self.allow_empty_prompt, + answer = self.option) + + def should_query_ldap_type(self): + return not self.allow_empty_prompt and not self.option and self.default_value and self.default_value.depends_on_ldap_type() + +class LdapDefault: + def __init__(self, value): + self.default_value = value + + def get_default_value(self, ldap_type): + return self.default_value + + def depends_on_ldap_type(self): + return False + + +class LdapDefaultMap(LdapDefault): + def __init__(self, value_map): + LdapDefault.__init__(self, None) + self.default_value_map = value_map + + def get_default_value(self, ldap_type): + return self.default_value_map[ldap_type] if self.default_value_map and ldap_type in self.default_value_map else None + + def depends_on_ldap_type(self): + return True + def format_prop_val_prompt(prop_prompt_pattern, prop_default_value): default_value = get_prompt_default(prop_default_value) return prop_prompt_pattern.format((" " + default_value) if default_value is not None and default_value != "" else "") @OsFamilyFuncImpl(OSConst.WINSRV_FAMILY) -def init_ldap_properties_list_reqd(properties, options, ldap_type): +def init_ldap_properties_list_reqd(properties, options): # python2.x dict is not ordered ldap_properties = [ - LdapPropTemplate(properties, options.ldap_primary_host, "ambari.ldap.connectivity.server.host", "Primary LDAP Host{0}: ", REGEX_HOSTNAME, False, get_default_prop_value(ldap_type, {LDAP_IPA:'ipa.ambari.apache.org', LDAP_GENERIC:'ldap.ambari.apache.org'})), - LdapPropTemplate(properties, options.ldap_primary_port, "ambari.ldap.connectivity.server.port", "Primary LDAP Port{0}: ", REGEX_PORT, False, get_default_prop_value(ldap_type, {LDAP_IPA:'636', LDAP_GENERIC:'389'})), + LdapPropTemplate(properties, options.ldap_primary_host, "ambari.ldap.connectivity.server.host", "Primary LDAP Host{0}: ", REGEX_HOSTNAME, False, LdapDefaultMap({LDAP_IPA:'ipa.ambari.apache.org', LDAP_GENERIC:'ldap.ambari.apache.org'})), + LdapPropTemplate(properties, options.ldap_primary_port, "ambari.ldap.connectivity.server.port", "Primary LDAP Port{0}: ", REGEX_PORT, False, LdapDefaultMap({LDAP_IPA:'636', LDAP_GENERIC:'389'})), LdapPropTemplate(properties, options.ldap_secondary_host, "ambari.ldap.connectivity.secondary.server.host", "Secondary LDAP Host <Optional>{0}: ", REGEX_HOSTNAME, True), LdapPropTemplate(properties, options.ldap_secondary_port, "ambari.ldap.connectivity.secondary.server.port", "Secondary LDAP Port <Optional>{0}: ", REGEX_PORT, True), - LdapPropTemplate(properties, options.ldap_ssl, "ambari.ldap.connectivity.use_ssl", "Use SSL [true/false]{0}: ", REGEX_TRUE_FALSE, False, get_default_prop_value(ldap_type, {LDAP_AD:'false', LDAP_IPA:'true', LDAP_GENERIC:'false'})), - LdapPropTemplate(properties, options.ldap_user_attr, "ambari.ldap.attributes.user.name_attr", "User ID attribute{0}: ", REGEX_ANYTHING, False, get_default_prop_value(ldap_type, {LDAP_AD:'sAMAccountName', LDAP_IPA:'uid', LDAP_GENERIC:'uid'})), - LdapPropTemplate(properties, options.ldap_base_dn, "ambari.ldap.attributes.user.search_base", "Search Base{0}: ", REGEX_ANYTHING, False, "dc=ambari,dc=apache,dc=org"), - LdapPropTemplate(properties, options.ldap_referral, "ambari.ldap.advanced.referrals", "Referral method [follow/ignore]{0}: ", REGEX_REFERRAL, True, "follow"), - LdapPropTemplate(properties, options.ldap_bind_anonym, "ambari.ldap.connectivity.anonymous_bind" "Bind anonymously [true/false]{0}: ", REGEX_TRUE_FALSE, False, "false") + LdapPropTemplate(properties, options.ldap_ssl, "ambari.ldap.connectivity.use_ssl", "Use SSL [true/false]{0}: ", REGEX_TRUE_FALSE, False, LdapDefaultMap({LDAP_AD:'false', LDAP_IPA:'true', LDAP_GENERIC:'false'})), + LdapPropTemplate(properties, options.ldap_user_attr, "ambari.ldap.attributes.user.name_attr", "User ID attribute{0}: ", REGEX_ANYTHING, False, LdapDefaultMap({LDAP_AD:'sAMAccountName', LDAP_IPA:'uid', LDAP_GENERIC:'uid'})), + LdapPropTemplate(properties, options.ldap_base_dn, "ambari.ldap.attributes.user.search_base", "Search Base{0}: ", REGEX_ANYTHING, False, LdapDefault("dc=ambari,dc=apache,dc=org")), + LdapPropTemplate(properties, options.ldap_referral, "ambari.ldap.advanced.referrals", "Referral method [follow/ignore]{0}: ", REGEX_REFERRAL, True, LdapDefault("follow")), + LdapPropTemplate(properties, options.ldap_bind_anonym, "ambari.ldap.connectivity.anonymous_bind" "Bind anonymously [true/false]{0}: ", REGEX_TRUE_FALSE, False, LdapDefault("false")) ] return ldap_properties @OsFamilyFuncImpl(OsFamilyImpl.DEFAULT) -def init_ldap_properties_list_reqd(properties, options, ldap_type): +def init_ldap_properties_list_reqd(properties, options): ldap_properties = [ - LdapPropTemplate(properties, options.ldap_primary_host, "ambari.ldap.connectivity.server.host", "Primary LDAP Host{0}: ", REGEX_HOSTNAME, False, get_default_prop_value(ldap_type, {LDAP_IPA:'ipa.ambari.apache.org', LDAP_GENERIC:'ldap.ambari.apache.org'})), - LdapPropTemplate(properties, options.ldap_primary_port, "ambari.ldap.connectivity.server.port", "Primary LDAP Port{0}: ", REGEX_PORT, False, get_default_prop_value(ldap_type, {LDAP_IPA:'636', LDAP_GENERIC:'389'})), + LdapPropTemplate(properties, options.ldap_primary_host, "ambari.ldap.connectivity.server.host", "Primary LDAP Host{0}: ", REGEX_HOSTNAME, False, LdapDefaultMap({LDAP_IPA:'ipa.ambari.apache.org', LDAP_GENERIC:'ldap.ambari.apache.org'})), + LdapPropTemplate(properties, options.ldap_primary_port, "ambari.ldap.connectivity.server.port", "Primary LDAP Port{0}: ", REGEX_PORT, False, LdapDefaultMap({LDAP_IPA:'636', LDAP_GENERIC:'389'})), LdapPropTemplate(properties, options.ldap_secondary_host, "ambari.ldap.connectivity.secondary.server.host", "Secondary LDAP Host <Optional>{0}: ", REGEX_HOSTNAME, True), LdapPropTemplate(properties, options.ldap_secondary_port, "ambari.ldap.connectivity.secondary.server.port", "Secondary LDAP Port <Optional>{0}: ", REGEX_PORT, True), - LdapPropTemplate(properties, options.ldap_ssl, "ambari.ldap.connectivity.use_ssl", "Use SSL [true/false]{0}: ", REGEX_TRUE_FALSE, False, get_default_prop_value(ldap_type, {LDAP_AD:'false', LDAP_IPA:'true', LDAP_GENERIC:'false'})), - LdapPropTemplate(properties, options.ldap_user_class, "ambari.ldap.attributes.user.object_class", "User object class{0}: ", REGEX_ANYTHING, False, get_default_prop_value(ldap_type, {LDAP_AD:'user', LDAP_IPA:'posixUser', LDAP_GENERIC:'posixUser'})), - LdapPropTemplate(properties, options.ldap_user_attr, "ambari.ldap.attributes.user.name_attr", "User ID attribute{0}: ", REGEX_ANYTHING, False, get_default_prop_value(ldap_type, {LDAP_AD:'sAMAccountName', LDAP_IPA:'uid', LDAP_GENERIC:'uid'})), - LdapPropTemplate(properties, options.ldap_group_class, "ambari.ldap.attributes.group.object_class", "Group object class{0}: ", REGEX_ANYTHING, False, get_default_prop_value(ldap_type, {LDAP_AD:'group', LDAP_IPA:'posixGroup', LDAP_GENERIC:'posixGroup'})), - LdapPropTemplate(properties, options.ldap_group_attr, "ambari.ldap.attributes.group.name_attr", "Group name attribute{0}: ", REGEX_ANYTHING, False, "cn"), - LdapPropTemplate(properties, options.ldap_member_attr, "ambari.ldap.attributes.group.member_attr", "Group member attribute{0}: ", REGEX_ANYTHING, False, get_default_prop_value(ldap_type, {LDAP_AD:'member', LDAP_IPA:'memberUid', LDAP_GENERIC:'memberUid'})), - LdapPropTemplate(properties, options.ldap_dn, "ambari.ldap.attributes.dn_attr", "Distinguished name attribute{0}: ", REGEX_ANYTHING, False, get_default_prop_value(ldap_type, {LDAP_AD:'distinguishedName', LDAP_IPA:'dn', LDAP_GENERIC:'dn'})), - LdapPropTemplate(properties, options.ldap_base_dn, "ambari.ldap.attributes.user.search_base", "Search Base{0}: ", REGEX_ANYTHING, False, "dc=ambari,dc=apache,dc=org"), - LdapPropTemplate(properties, options.ldap_referral, "ambari.ldap.advanced.referrals", "Referral method [follow/ignore]{0}: ", REGEX_REFERRAL, True, "follow"), - LdapPropTemplate(properties, options.ldap_bind_anonym, "ambari.ldap.connectivity.anonymous_bind", "Bind anonymously [true/false]{0}: ", REGEX_TRUE_FALSE, False, "false"), - LdapPropTemplate(properties, options.ldap_sync_username_collisions_behavior, "ambari.ldap.advanced.collision_behavior", "Handling behavior for username collisions [convert/skip] for LDAP sync{0}: ", REGEX_SKIP_CONVERT, False, "skip"), + LdapPropTemplate(properties, options.ldap_ssl, "ambari.ldap.connectivity.use_ssl", "Use SSL [true/false]{0}: ", REGEX_TRUE_FALSE, False, LdapDefaultMap({LDAP_AD:'false', LDAP_IPA:'true', LDAP_GENERIC:'false'})), + LdapPropTemplate(properties, options.ldap_user_class, "ambari.ldap.attributes.user.object_class", "User object class{0}: ", REGEX_ANYTHING, False, LdapDefaultMap({LDAP_AD:'user', LDAP_IPA:'posixAccount', LDAP_GENERIC:'posixUser'})), + LdapPropTemplate(properties, options.ldap_user_attr, "ambari.ldap.attributes.user.name_attr", "User ID attribute{0}: ", REGEX_ANYTHING, False, LdapDefaultMap({LDAP_AD:'sAMAccountName', LDAP_IPA:'uid', LDAP_GENERIC:'uid'})), + LdapPropTemplate(properties, options.ldap_group_class, "ambari.ldap.attributes.group.object_class", "Group object class{0}: ", REGEX_ANYTHING, False, LdapDefaultMap({LDAP_AD:'group', LDAP_IPA:'posixGroup', LDAP_GENERIC:'posixGroup'})), + LdapPropTemplate(properties, options.ldap_group_attr, "ambari.ldap.attributes.group.name_attr", "Group name attribute{0}: ", REGEX_ANYTHING, False, LdapDefault("cn")), + LdapPropTemplate(properties, options.ldap_member_attr, "ambari.ldap.attributes.group.member_attr", "Group member attribute{0}: ", REGEX_ANYTHING, False, LdapDefaultMap({LDAP_AD:'member', LDAP_IPA:'memberUid', LDAP_GENERIC:'memberUid'})), + LdapPropTemplate(properties, options.ldap_dn, "ambari.ldap.attributes.dn_attr", "Distinguished name attribute{0}: ", REGEX_ANYTHING, False, LdapDefaultMap({LDAP_AD:'distinguishedName', LDAP_IPA:'dn', LDAP_GENERIC:'dn'})), + LdapPropTemplate(properties, options.ldap_base_dn, "ambari.ldap.attributes.user.search_base", "Search Base{0}: ", REGEX_ANYTHING, False, LdapDefaultMap({LDAP_AD:'dc=ambari,dc=apache,dc=org', LDAP_IPA:'cn=accounts,dc=ambari,dc=apache,dc=org', LDAP_GENERIC:'dc=ambari,dc=apache,dc=org'})), + LdapPropTemplate(properties, options.ldap_referral, "ambari.ldap.advanced.referrals", "Referral method [follow/ignore]{0}: ", REGEX_REFERRAL, True, LdapDefault("follow")), + LdapPropTemplate(properties, options.ldap_bind_anonym, "ambari.ldap.connectivity.anonymous_bind", "Bind anonymously [true/false]{0}: ", REGEX_TRUE_FALSE, False, LdapDefault("false")), + LdapPropTemplate(properties, options.ldap_sync_username_collisions_behavior, "ambari.ldap.advanced.collision_behavior", "Handling behavior for username collisions [convert/skip] for LDAP sync{0}: ", REGEX_SKIP_CONVERT, False, LdapDefault("skip")), LdapPropTemplate(properties, options.ldap_force_lowercase_usernames, "ambari.ldap.advanced.force_lowercase_usernames", "Force lower-case user names [true/false]{0}:", REGEX_TRUE_FALSE, True), LdapPropTemplate(properties, options.ldap_pagination_enabled, "ambari.ldap.advanced.pagination_enabled", "Results from LDAP are paginated when requested [true/false]{0}:", REGEX_TRUE_FALSE, True) ] return ldap_properties -def get_default_prop_value(ldap_type, default_value_map): - return default_value_map[ldap_type] if ldap_type in default_value_map else None - - -def update_ldap_configuration(options, properties, ldap_property_value_map): - admin_login, admin_password = get_ambari_admin_username_password_pair(options) +def update_ldap_configuration(admin_login, admin_password, properties, ldap_property_value_map): request_data = { "Configuration": { "category": "ldap-configuration", @@ -705,9 +749,15 @@ def update_ldap_configuration(options, properties, ldap_property_value_map): request_data['Configuration']['properties'] = ldap_property_value_map perform_changes_via_rest_api(properties, admin_login, admin_password, SETUP_LDAP_CONFIG_URL, 'PUT', request_data) +def should_query_ldap_type(ldap_property_list_reqd): + for ldap_prop in ldap_property_list_reqd: + if ldap_prop.should_query_ldap_type(): + return True + return False + def query_ldap_type(): - return get_validated_string_input("Please select the type of LDAP you want to use ({}):".format(", ".join(LDAP_TYPES)), - None, + return get_validated_string_input("Please select the type of LDAP you want to use [{}]({}):".format("/".join(LDAP_TYPES), LDAP_GENERIC), + LDAP_GENERIC, REGEX_LDAP_TYPE, "Please enter one of the followings '{}'!".format("', '".join(LDAP_TYPES)), False, @@ -735,6 +785,12 @@ def setup_ldap(options): err = "Currently '" + current_client_security + "' configured. Can not setup LDAP." raise FatalException(1, err) + admin_login, admin_password = get_ambari_admin_username_password_pair(options) + ldap_properties = get_ldap_properties_from_db(properties, admin_login, admin_password) + if ldap_properties: + properties.update(ldap_properties) + sys.stdout.write('\n') + isSecure = get_is_secure(properties) if options.ldap_url: @@ -745,9 +801,13 @@ def setup_ldap(options): options.ldap_secondary_host = options.ldap_secondary_url.split(':')[0] options.ldap_secondary_port = options.ldap_secondary_url.split(':')[1] - ldap_type = query_ldap_type() - ldap_property_list_reqd = init_ldap_properties_list_reqd(properties, options, ldap_type) + ldap_property_list_reqd = init_ldap_properties_list_reqd(properties, options) + ldap_bind_dn_template = LdapPropTemplate(properties, options.ldap_manager_dn, LDAP_MGR_USERNAME_PROPERTY, "Bind DN{0}: ", REGEX_ANYTHING, False, LdapDefaultMap({ + LDAP_AD:'cn=ldapbind,dc=ambari,dc=apache,dc=org', + LDAP_IPA:'uid=ldapbind,cn=users,cn=accounts,dc=ambari,dc=apache,dc=org', + LDAP_GENERIC:'uid=ldapbind,cn=users,dc=ambari,dc=apache,dc=org'})) + ldap_type = LDAP_GENERIC if options.ldap_use_generic_defaults or not should_query_ldap_type(ldap_property_list_reqd) else query_ldap_type() ldap_property_list_opt = [LDAP_MGR_USERNAME_PROPERTY, LDAP_MGR_PASSWORD_PROPERTY, @@ -758,20 +818,16 @@ def setup_ldap(options): ldap_property_list_passwords=[LDAP_MGR_PASSWORD_PROPERTY, SSL_TRUSTSTORE_PASSWORD_PROPERTY] - ldap_mgr_dn_default = get_default_prop_value(ldap_type, { - LDAP_AD:'cn=ldapbind,dc=ambari,dc=apache,dc=org', - LDAP_IPA:'uid=ldapbind,cn=users,cn=accounts,dc=ambari,dc=apache,dc=org', - LDAP_GENERIC:'uid=ldapbind,cn=users,dc=ambari,dc=apache,dc=org'}) - SSL_TRUSTSTORE_TYPE_DEFAULT = get_value_from_properties(properties, SSL_TRUSTSTORE_TYPE_PROPERTY, "jks") - SSL_TRUSTSTORE_PATH_DEFAULT = get_value_from_properties(properties, SSL_TRUSTSTORE_PATH_PROPERTY) + ssl_truststore_type_default = get_value_from_properties(properties, SSL_TRUSTSTORE_TYPE_PROPERTY, "jks") + ssl_truststore_path_default = get_value_from_properties(properties, SSL_TRUSTSTORE_PATH_PROPERTY) + disable_endpoint_identification_default = get_value_from_properties(properties, LDAP_DISABLE_ENDPOINT_IDENTIFICATION, "False") ldap_property_value_map = {} ldap_property_values_in_ambari_properties = {} for ldap_prop in ldap_property_list_reqd: - input = get_validated_string_input(ldap_prop.ldap_prop_val_prompt, ldap_prop.ldap_prop_value, ldap_prop.prompt_regex, - "Invalid characters in the input!", False, ldap_prop.allow_empty_prompt, - answer = ldap_prop.option) + input = ldap_prop.get_input(ldap_type) + if input is not None and input != "": ldap_property_value_map[ldap_prop.prop_name] = input @@ -780,10 +836,7 @@ def setup_ldap(options): mgr_password = None # Ask for manager credentials only if bindAnonymously is false if not anonymous: - username = get_validated_string_input( - format_prop_val_prompt("Bind DN{0}: ", get_value_from_properties(properties, LDAP_MGR_USERNAME_PROPERTY, ldap_mgr_dn_default)), - ldap_mgr_dn_default, ".*", - "Invalid characters in the input!", False, False, answer = options.ldap_manager_dn) + username = ldap_bind_dn_template.get_input(ldap_type) ldap_property_value_map[LDAP_MGR_USERNAME_PROPERTY] = username mgr_password = configure_ldap_password(options) ldap_property_value_map[LDAP_MGR_PASSWORD_PROPERTY] = mgr_password @@ -792,12 +845,13 @@ def setup_ldap(options): ts_password = None if ldaps: - disable_endpoint_identification = get_validated_string_input("Disable endpoint identification during SSL handshake [true/false] (false): ", "false", + disable_endpoint_identification = get_validated_string_input("Disable endpoint identification during SSL handshake [true/false] ({0}): ".format(disable_endpoint_identification_default), + disable_endpoint_identification_default, REGEX_TRUE_FALSE, "Invalid characters in the input!", False, allowEmpty=True, answer=options.ldap_sync_disable_endpoint_identification) ldap_property_value_map[LDAP_DISABLE_ENDPOINT_IDENTIFICATION] = disable_endpoint_identification truststore_default = "n" - truststore_set = bool(SSL_TRUSTSTORE_PATH_DEFAULT) + truststore_set = bool(ssl_truststore_path_default) if truststore_set: truststore_default = "y" custom_trust_store = True if options.trust_store_path is not None and options.trust_store_path else False @@ -806,12 +860,12 @@ def setup_ldap(options): format(truststore_default), truststore_set) if custom_trust_store: - ts_type = get_validated_string_input("TrustStore type [jks/jceks/pkcs12] {0}:".format(get_prompt_default(SSL_TRUSTSTORE_TYPE_DEFAULT)), - SSL_TRUSTSTORE_TYPE_DEFAULT, "^(jks|jceks|pkcs12)?$", "Wrong type", False, answer=options.trust_store_type) + ts_type = get_validated_string_input("TrustStore type [jks/jceks/pkcs12] {0}:".format(get_prompt_default(ssl_truststore_type_default)), + ssl_truststore_type_default, "^(jks|jceks|pkcs12)?$", "Wrong type", False, answer=options.trust_store_type) ts_path = None while True: - ts_path = get_validated_string_input("Path to TrustStore file {0}:".format(get_prompt_default(SSL_TRUSTSTORE_PATH_DEFAULT)), - SSL_TRUSTSTORE_PATH_DEFAULT, ".*", False, False, answer = options.trust_store_path) + ts_path = get_validated_string_input(format_prop_val_prompt("Path to TrustStore file{0}: ", ssl_truststore_path_default), + ssl_truststore_path_default, ".*", False, False, answer = options.trust_store_path) if os.path.exists(ts_path): break else: @@ -842,7 +896,7 @@ def setup_ldap(options): print '=' * 20 for property in ldap_property_list_reqd: if ldap_property_value_map.has_key(property.prop_name): - print("%s %s" % (property.ldap_prop_val_prompt, ldap_property_value_map[property.prop_name])) + print("%s %s" % (property.get_prompt_text(ldap_type), ldap_property_value_map[property.prop_name])) for property in ldap_property_list_opt: if ldap_property_value_map.has_key(property): @@ -882,7 +936,7 @@ def setup_ldap(options): ldap_property_value_map[IS_LDAP_CONFIGURED] = "true" #Saving LDAP configuration in Ambari DB using the REST API - update_ldap_configuration(options, properties, ldap_property_value_map) + update_ldap_configuration(admin_login, admin_password, properties, ldap_property_value_map) #The only properties we want to write out in Ambari.properties are the client.security type being LDAP and the custom Truststore related properties (if any) ldap_property_values_in_ambari_properties[CLIENT_SECURITY] = 'ldap' diff --git a/ambari-server/src/test/python/TestAmbariServer.py b/ambari-server/src/test/python/TestAmbariServer.py index 3b9adf6..affea57 100644 --- a/ambari-server/src/test/python/TestAmbariServer.py +++ b/ambari-server/src/test/python/TestAmbariServer.py @@ -109,7 +109,7 @@ with patch.object(platform, "linux_distribution", return_value = MagicMock(retur SRVR_ONE_WAY_SSL_PORT_PROPERTY, SRVR_TWO_WAY_SSL_PORT_PROPERTY from ambari_server.setupSecurity import adjust_directory_permissions, get_alias_string, get_ldap_event_spec_names, sync_ldap, \ configure_ldap_password, setup_ldap, REGEX_HOSTNAME_PORT, REGEX_TRUE_FALSE, REGEX_ANYTHING, setup_master_key, \ - setup_ambari_krb5_jaas + setup_ambari_krb5_jaas, LDAP_GENERIC, should_query_ldap_type, LdapPropTemplate, LdapDefault, LdapDefaultMap from ambari_server.userInput import get_YN_input, get_choice_string_input, get_validated_string_input, \ read_password from ambari_server_main import get_ulimit_open_files, ULIMIT_OPEN_FILES_KEY, ULIMIT_OPEN_FILES_DEFAULT @@ -7131,8 +7131,8 @@ class TestAmbariServer(TestCase): @patch("ambari_server.setupSecurity.get_ambari_properties") @patch("ambari_server.setupSecurity.logger") @patch("ambari_server.setupSecurity.is_server_runing") - @patch("ambari_server.setupSecurity.query_ldap_type") - def test_setup_ldap_invalid_input(self, query_ldap_type_method, is_server_runing_method, logger_mock, get_ambari_properties_method, + @patch("ambari_server.setupSecurity.get_ldap_properties_from_db") + def test_setup_ldap_invalid_input(self, get_ldap_properties_from_db_method, is_server_runing_method, logger_mock, get_ambari_properties_method, search_file_message, update_properties_method, get_YN_input_method, @@ -7142,7 +7142,7 @@ class TestAmbariServer(TestCase): sys.stdout = out is_server_runing_method.return_value = (True, 0) search_file_message.return_value = "filepath" - query_ldap_type_method.return_value = 'Generic LDAP' + get_ldap_properties_from_db_method.return_value = None properties = Properties(); properties.process_pair(SECURITY_MASTER_KEY_LOCATION, "filepath") @@ -7151,7 +7151,7 @@ class TestAmbariServer(TestCase): properties.process_pair(CLIENT_API_PORT_PROPERTY, '8080') get_ambari_properties_method.return_value = properties - raw_input_mock.side_effect = ['a', '3', 'b', 'b', 'hody', 'b', '2', 'false', 'user', 'uid', 'group', 'cn', 'member', 'dn', 'base', 'follow', 'true', 'skip', 'false', 'false', 'admin'] + raw_input_mock.side_effect = [LDAP_GENERIC, 'a', '3', 'b', 'b', 'hody', 'b', '2', 'false', 'user', 'uid', 'group', 'cn', 'member', 'dn', 'base', 'follow', 'true', 'skip', 'false', 'false', 'admin'] get_password_mock.side_effect = ['admin'] set_silent(False) get_YN_input_method.return_value = True @@ -7184,7 +7184,7 @@ class TestAmbariServer(TestCase): self.assertEqual(1, get_password_mock.call_count) raw_input_mock.reset_mock() - raw_input_mock.side_effect = ['a', '3', '', '', 'b', '2', 'false', 'user', 'uid', 'group', 'cn', 'member', 'dn', 'base', 'follow', 'true', 'skip', 'false', 'false', 'admin'] + raw_input_mock.side_effect = [LDAP_GENERIC, 'a', '3', '', '', 'b', '2', 'false', 'user', 'uid', 'group', 'cn', 'member', 'dn', 'base', 'follow', 'true', 'skip', 'false', 'false', 'admin'] get_password_mock.reset_mock() get_password_mock.side_effect = ['admin'] @@ -7298,7 +7298,9 @@ class TestAmbariServer(TestCase): @patch("os.path.exists") @patch("ambari_server.setupSecurity.logger") @patch("ambari_server.setupSecurity.is_server_runing") - def test_setup_ldap(self, is_server_runing_method, logger_mock, exists_method, read_password_method, get_ambari_properties_method, + @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, search_file_message, get_validated_string_input_method, configure_ldap_password_method, update_properties_method, @@ -7312,6 +7314,9 @@ class TestAmbariServer(TestCase): options.ambari_admin_password = 'admin' is_server_runing_method.return_value = (True, 0) + get_ldap_properties_from_db_method.return_value = None + query_ldap_type_method.return_value = LDAP_GENERIC + search_file_message.return_value = "filepath" properties = Properties(); @@ -7453,13 +7458,15 @@ class TestAmbariServer(TestCase): @patch("ambari_server.setupSecurity.get_validated_string_input") @patch("ambari_server.setupSecurity.get_ambari_properties") @patch("ambari_server.setupSecurity.is_server_runing") - def test_setup_ldap_primary_host_and_port_with_ldap_url_option(self, is_server_runing_method, get_ambari_properties_method, + @patch("ambari_server.setupSecurity.get_ldap_properties_from_db") + def test_setup_ldap_primary_host_and_port_with_ldap_url_option(self, get_ldap_properties_from_db_method, is_server_runing_method, get_ambari_properties_method, get_validated_string_input_method, read_password_method, get_YN_input_method, urlopen_method): out = StringIO.StringIO() sys.stdout = out is_server_runing_method.return_value = (True, 0) + get_ldap_properties_from_db_method.return_value = None def yn_input_side_effect(*args, **kwargs): if 'TrustStore' in args[0]: @@ -7494,6 +7501,7 @@ class TestAmbariServer(TestCase): options = self._create_empty_options_mock() options.ambari_admin_username = 'admin' options.ambari_admin_password = 'admin' + options.ldap_type = LDAP_GENERIC options.ldap_url = "a:1" setup_ldap(options) @@ -7518,13 +7526,15 @@ class TestAmbariServer(TestCase): @patch("ambari_server.setupSecurity.get_validated_string_input") @patch("ambari_server.setupSecurity.get_ambari_properties") @patch("ambari_server.setupSecurity.is_server_runing") - def test_setup_ldap_with_ambari_admin_username_and_password_options(self, is_server_runing_method, get_ambari_properties_method, + @patch("ambari_server.setupSecurity.get_ldap_properties_from_db") + def test_setup_ldap_with_ambari_admin_username_and_password_options(self, get_ldap_properties_from_db_method, is_server_runing_method, get_ambari_properties_method, get_validated_string_input_method, get_YN_input_method, urlopen_method): out = StringIO.StringIO() sys.stdout = out is_server_runing_method.return_value = (True, 0) + get_ldap_properties_from_db_method.return_value = None def yn_input_side_effect(*args, **kwargs): return False if 'TrustStore' in args[0] else True @@ -7558,6 +7568,7 @@ class TestAmbariServer(TestCase): options = self._create_empty_options_mock() options.ambari_admin_username = 'admin' options.ambari_admin_password = 'admin' + options.ldap_type = LDAP_GENERIC setup_ldap(options) @@ -7572,12 +7583,16 @@ class TestAmbariServer(TestCase): @patch("ambari_server.setupSecurity.get_validated_string_input") @patch("ambari_server.setupSecurity.get_ambari_properties") @patch("ambari_server.setupSecurity.is_server_runing") - def test_setup_ldap_enforcement_cli_option(self, is_server_runing_method, get_ambari_properties_method, + @patch("ambari_server.setupSecurity.query_ldap_type") + @patch("ambari_server.setupSecurity.get_ldap_properties_from_db") + def test_setup_ldap_enforcement_cli_option(self, get_ldap_properties_from_db_method, query_ldap_type_method, is_server_runing_method, get_ambari_properties_method, get_validated_string_input_method, get_YN_input_method, urlopen_method): out = StringIO.StringIO() sys.stdout = out is_server_runing_method.return_value = (True, 0) + query_ldap_type_method.return_value = LDAP_GENERIC + get_ldap_properties_from_db_method.return_value = None def yn_input_side_effect(*args, **kwargs): if 'do you wish to use LDAP instead' in args[0]: @@ -7622,6 +7637,84 @@ class TestAmbariServer(TestCase): sys.stdout = sys.__stdout__ pass + def test_should_query_ldap_type_returns_false_if_no_prop_requires_ldap_type(self): + prop_template0 = MagicMock() + prop_template0.should_query_ldap_type.return_value = False + prop_template1 = MagicMock() + prop_template1.should_query_ldap_type.return_value = False + ldap_properties = [prop_template0, prop_template1] + self.assertFalse(should_query_ldap_type(ldap_properties)) + pass + + def test_should_query_ldap_type_returns_true_if_any_prop_requires_ldap_type(self): + prop_template0 = MagicMock() + prop_template0.should_query_ldap_type.return_value = False + prop_template1 = MagicMock() + prop_template1.should_query_ldap_type.return_value = True + ldap_properties = [prop_template0, prop_template1] + self.assertTrue(should_query_ldap_type(ldap_properties)) + pass + + def test_LdapPropTemplate_should_query_ldap_type_returns_false_if_empty_prompt_is_allowed(self): + prop_template = LdapPropTemplate(Properties(), None, "any.prop", "Any prop:", REGEX_ANYTHING, True) + self.assertFalse(prop_template.should_query_ldap_type()) + pass + + def test_LdapPropTemplate_should_query_ldap_type_returns_false_if_option_value_is_present(self): + prop_template = LdapPropTemplate(Properties(), 'value', "any.prop", "Any prop:", REGEX_ANYTHING, False) + self.assertFalse(prop_template.should_query_ldap_type()) + pass + + def test_LdapPropTemplate_should_query_ldap_type_returns_false_if_property_value_is_present(self): + properties = Properties() + properties.process_pair("a.prop", "value") + prop_template = LdapPropTemplate(properties, None, "a.prop", "Any prop:", REGEX_ANYTHING, False, LdapDefaultMap({'key' : 'default_value'})) + self.assertFalse(prop_template.should_query_ldap_type()) + pass + + def test_LdapPropTemplate_should_query_ldap_type_returns_false_if_default_is_not_depend_on_ldap_type(self): + prop_template = LdapPropTemplate(Properties(), None, "a.prop", "Any prop:", REGEX_ANYTHING, False, LdapDefault('default_value')) + self.assertFalse(prop_template.should_query_ldap_type()) + pass + + def test_LdapPropTemplate_should_query_ldap_type_returns_true_if_property_value_is_depends_on_ldap_type_and_no_option_and_property_is_given(self): + prop_template = LdapPropTemplate(Properties(), None, "a.prop", "Any prop:", REGEX_ANYTHING, False, LdapDefaultMap({'key' : 'default_value'})) + self.assertTrue(prop_template.should_query_ldap_type()) + pass + + def test_LdapPropTemplate_get_default_value_returns_stored_property_value_if_presents(self): + properties = Properties() + properties.process_pair("a.prop", "value") + prop_template = LdapPropTemplate(properties, None, "a.prop", "Any prop:", REGEX_ANYTHING, False, LdapDefaultMap('default_value')) + self.assertEquals(prop_template.get_default_value('any_type'), "value") + pass + + def test_LdapPropTemplate_get_default_value_returns_default_value_if_stored_property_value_not_exists(self): + prop_template = LdapPropTemplate(Properties(), None, "a.prop", "Any prop:", REGEX_ANYTHING, False, LdapDefault('default_value')) + self.assertEquals(prop_template.get_default_value('any_type'), "default_value") + pass + + def test_LdapPropTemplate_get_default_value_returns_none_if_stored_property_value_not_exists_and_no_default_value_was_specified(self): + prop_template = LdapPropTemplate(Properties(), None, "a.prop", "Any prop:", REGEX_ANYTHING, False) + self.assertEquals(prop_template.get_default_value('any_type'), None) + pass + + def test_LdapDefaultMap_get_default_value_returns_none_if_default_map_is_none(self): + default_map = LdapDefaultMap(None) + self.assertEquals(default_map.get_default_value('any_type'), None) + pass + + def test_LdapDefaultMap_get_default_value_returns_none_if_key_not_present(self): + default_map = LdapDefaultMap({LDAP_GENERIC : 'value'}) + self.assertEquals(default_map.get_default_value('non_existent_type'), None) + pass + + def test_LdapDefaultMap_get_default_value_returns_key_value_if_key_presents(self): + default_map = LdapDefaultMap({LDAP_GENERIC : 'value'}) + self.assertEquals(default_map.get_default_value(LDAP_GENERIC), 'value') + pass + + @patch("urllib2.urlopen") @patch("ambari_server.setupSecurity.get_validated_string_input") @patch("ambari_server.setupSecurity.get_ambari_properties")