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 02e7b56 AMBARI-24986. Using Ambari server's CLI to enable/disable Trusted Proxy support (#2683) 02e7b56 is described below commit 02e7b56aff1acee1f1acb5647be48f7566bd89d0 Author: Sandor Molnar <smol...@apache.org> AuthorDate: Wed Dec 5 07:49:29 2018 +0100 AMBARI-24986. Using Ambari server's CLI to enable/disable Trusted Proxy support (#2683) --- ambari-server/sbin/ambari-server | 6 +- ambari-server/src/main/python/ambari-server.py | 25 +- .../src/main/python/ambari_server/setupActions.py | 1 + .../main/python/ambari_server/setupTrustedProxy.py | 172 +++++++++++ .../src/test/python/TestSetupTrustedProxy.py | 323 +++++++++++++++++++++ 5 files changed, 523 insertions(+), 4 deletions(-) diff --git a/ambari-server/sbin/ambari-server b/ambari-server/sbin/ambari-server index 4d0c334..761a15d 100755 --- a/ambari-server/sbin/ambari-server +++ b/ambari-server/sbin/ambari-server @@ -188,6 +188,10 @@ case "${1:-}" in echo -e "Setting up SSO authentication properties..." $PYTHON "$AMBARI_PYTHON_EXECUTABLE" "$@" ;; + setup-trusted-proxy) + echo -e "Setting up Trusted Proxy support..." + $PYTHON "$AMBARI_PYTHON_EXECUTABLE" "$@" + ;; db-purge-history) echo -e "Purge database history..." $PYTHON "$AMBARI_PYTHON_EXECUTABLE" "$@" @@ -210,7 +214,7 @@ case "${1:-}" in ;; *) echo "Usage: $AMBARI_EXECUTABLE - {start|stop|reset|restart|upgrade|status|upgradestack|setup|setup-jce|setup-ldap|sync-ldap|set-current|setup-security|refresh-stack-hash|backup|restore|update-host-names|check-database|enable-stack|setup-sso|db-purge-history|install-mpack|uninstall-mpack|upgrade-mpack|setup-kerberos|setup-pam|migrate-ldap-pam} [options] + {start|stop|reset|restart|upgrade|status|upgradestack|setup|setup-jce|setup-ldap|sync-ldap|set-current|setup-security|refresh-stack-hash|backup|restore|update-host-names|check-database|enable-stack|setup-sso|setup-trusted-proxy|db-purge-history|install-mpack|uninstall-mpack|upgrade-mpack|setup-kerberos|setup-pam|migrate-ldap-pam} [options] Use $AMBARI_PYTHON_EXECUTABLE <action> --help to get details on options available. Or, simply invoke ambari-server.py --help to print the options." exit 1 diff --git a/ambari-server/src/main/python/ambari-server.py b/ambari-server/src/main/python/ambari-server.py index fccd758..65a8aaa 100755 --- a/ambari-server/src/main/python/ambari-server.py +++ b/ambari-server/src/main/python/ambari-server.py @@ -18,6 +18,7 @@ See the License for the specific language governing permissions and limitations under the License. ''' +import textwrap import logging import logging.config import logging.handlers @@ -49,13 +50,14 @@ from ambari_server.setupActions import BACKUP_ACTION, LDAP_SETUP_ACTION, LDAP_SY SETUP_ACTION, SETUP_SECURITY_ACTION, RESTART_ACTION, START_ACTION, STATUS_ACTION, STOP_ACTION, UPGRADE_ACTION, \ SETUP_JCE_ACTION, SETUP_JDBC_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 + MIGRATE_LDAP_PAM_ACTION, KERBEROS_SETUP_ACTION, SETUP_TPROXY_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.setupTrustedProxy import setup_trusted_proxy from ambari_server.userInput import get_validated_string_input from ambari_server_main import server_process_main @@ -600,11 +602,25 @@ def init_setup_sso_options(parser): parser.add_option('--ambari-admin-username', default=None, help="Ambari administrator username for accessing Ambari's REST API", dest="ambari_admin_username") parser.add_option('--ambari-admin-password', default=None, help="Ambari administrator password for accessing Ambari's REST API", dest="ambari_admin_password") + @OsFamilyFuncImpl(OsFamilyImpl.DEFAULT) def init_pam_setup_parser_options(parser): parser.add_option('--pam-config-file', default=None, help="Path to the PAM configuration file", dest="pam_config_file") parser.add_option('--pam-auto-create-groups', default=None, help="Automatically create groups for authenticated users [true/false]", dest="pam_auto_create_groups") + +@OsFamilyFuncImpl(OsFamilyImpl.DEFAULT) +def init_tproxy_setup_parser_options(parser): + parser.add_option('--ambari-admin-username', default=None, help="Ambari administrator username for accessing Ambari's REST API", dest="ambari_admin_username") + parser.add_option('--ambari-admin-password', default=None, help="Ambari administrator password for accessing Ambari's REST API", dest="ambari_admin_password") + parser.add_option('--tproxy-enabled', default=None, help="Indicates whether to enable/disable Trusted Proxy Support", dest="tproxy_enabled") + parser.add_option('--tproxy-configuration-file-path', default=None, + help="The path where the Trusted Proxy configuration is located. The content is expected to be in JSON format." \ + "Sample configuration:[{\"proxyuser\": \"knox\", \"hosts\": \"host1\", \"users\": \"user1, user2\", \"groups\": \"group1\"}]", + dest="tproxy_configuration_file_path" + ) + + @OsFamilyFuncImpl(OsFamilyImpl.DEFAULT) def init_set_current_parser_options(parser): parser.add_option('--cluster-name', default=None, help="Cluster name", dest="cluster_name") @@ -818,7 +834,8 @@ def create_user_action_map(args, options): SETUP_SSO_ACTION: UserActionRestart(setup_sso, options), INSTALL_MPACK_ACTION: UserAction(install_mpack, options), UNINSTALL_MPACK_ACTION: UserAction(uninstall_mpack, options), - UPGRADE_MPACK_ACTION: UserAction(upgrade_mpack, options) + UPGRADE_MPACK_ACTION: UserAction(upgrade_mpack, options), + SETUP_TPROXY_ACTION: UserAction(setup_trusted_proxy, options) } return action_map @@ -851,7 +868,8 @@ def create_user_action_map(args, options): UPGRADE_MPACK_ACTION: UserAction(upgrade_mpack, options), PAM_SETUP_ACTION: UserAction(setup_pam, options), MIGRATE_LDAP_PAM_ACTION: UserAction(migrate_ldap_pam, options), - KERBEROS_SETUP_ACTION: UserAction(setup_kerberos, options) + KERBEROS_SETUP_ACTION: UserAction(setup_kerberos, options), + SETUP_TPROXY_ACTION: UserAction(setup_trusted_proxy, options) } return action_map @@ -884,6 +902,7 @@ def init_action_parser(action, parser): UPGRADE_MPACK_ACTION: init_upgrade_mpack_parser_options, PAM_SETUP_ACTION: init_pam_setup_parser_options, KERBEROS_SETUP_ACTION: init_kerberos_setup_parser_options, + SETUP_TPROXY_ACTION: init_tproxy_setup_parser_options } parser.add_option("-v", "--verbose", action="store_true", dest="verbose", default=False, diff --git a/ambari-server/src/main/python/ambari_server/setupActions.py b/ambari-server/src/main/python/ambari_server/setupActions.py index 5917a83..e75c70a 100644 --- a/ambari-server/src/main/python/ambari_server/setupActions.py +++ b/ambari-server/src/main/python/ambari_server/setupActions.py @@ -50,3 +50,4 @@ UPGRADE_MPACK_ACTION = "upgrade-mpack" PAM_SETUP_ACTION = "setup-pam" MIGRATE_LDAP_PAM_ACTION = "migrate-ldap-pam" KERBEROS_SETUP_ACTION = "setup-kerberos" +SETUP_TPROXY_ACTION = "setup-trusted-proxy" diff --git a/ambari-server/src/main/python/ambari_server/setupTrustedProxy.py b/ambari-server/src/main/python/ambari_server/setupTrustedProxy.py new file mode 100644 index 0000000..11f1e7a --- /dev/null +++ b/ambari-server/src/main/python/ambari_server/setupTrustedProxy.py @@ -0,0 +1,172 @@ +#!/usr/bin/env python + +''' +Licensed to the Apache Software Foundation (ASF) under one +or more contributor license agreements. See the NOTICE file +distributed with this work for additional information +regarding copyright ownership. The ASF licenses this file +to you under the Apache License, Version 2.0 (the +"License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +''' + +import ambari_simplejson as json +import httplib +import os +import re +import urllib2 + +from ambari_commons.exceptions import FatalException, NonFatalException +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_value_from_dictionary +from ambari_server.setupSecurity import REGEX_TRUE_FALSE +from ambari_server.userInput import get_validated_string_input, get_YN_input + + +TPROXY_SUPPORT_ENABLED = "ambari.tproxy.authentication.enabled" +PROXYUSER_HOSTS = "ambari.tproxy.proxyuser.{}.hosts" +PROXYUSER_USERS = "ambari.tproxy.proxyuser.{}.users" +PROXYUSER_GROUPS = "ambari.tproxy.proxyuser.{}.groups" + +TPROXY_CONFIG_API_ENTRYPOINT = 'services/AMBARI/components/AMBARI_SERVER/configurations/tproxy-configuration' + +REGEX_ANYTHING = ".*" +WILDCARD_FOR_ALL = "*" + + +def get_trusted_proxy_properties(ambari_properties, admin_login, admin_password): + print_info_msg("Fetching Trusted Proxy configuration from DB") + + try: + response_code, json_data = get_json_via_rest_api(ambari_properties, admin_login, admin_password, TPROXY_CONFIG_API_ENTRYPOINT) + except urllib2.HTTPError as http_error: + if http_error.code == httplib.NOT_FOUND: + # This means that there is no Trusted Proxy configuration in the database yet -> we can not fetch the properties; but this is NOT an error + json_data = None + else: + raise http_error + + return json_data.get('Configuration', {}).get('properties', {}) if json_data else {} + + +def populate_tproxy_configuration_property(properties, tproxy_user_name, property_name, question_text_qualifier): + resolved_property_name = property_name.format(tproxy_user_name) + resolved_property_value = get_value_from_dictionary(properties, resolved_property_name, WILDCARD_FOR_ALL) + resolved_property_value = get_validated_string_input("Allowed {0} for {1} ({2})? ".format(question_text_qualifier, tproxy_user_name, resolved_property_value), resolved_property_value, REGEX_ANYTHING, "Invalid input", False) + properties[resolved_property_name] = resolved_property_value + + +def add_new_trusted_proxy_config(properties): + tproxy_user_name = get_validated_string_input("The proxy user's (local) username? ", None, REGEX_ANYTHING, "Invalid Trusted Proxy User Name", False, allowEmpty=False) + populate_tproxy_configuration_property(properties, tproxy_user_name, PROXYUSER_HOSTS, "hosts") + populate_tproxy_configuration_property(properties, tproxy_user_name, PROXYUSER_USERS, "users") + populate_tproxy_configuration_property(properties, tproxy_user_name, PROXYUSER_GROUPS, "groups") + return get_YN_input("Add another proxy user [y/n] (n)? ", False) + + +def parse_trusted_configuration_file(tproxy_configuration_file_path, properties): + with open(tproxy_configuration_file_path) as tproxy_configuration_file: + tproxy_configurations = json.loads(tproxy_configuration_file.read()) + + if tproxy_configurations: + for tproxy_configuration in tproxy_configurations: + tproxy_user_name = tproxy_configuration['proxyuser'] + properties[PROXYUSER_HOSTS.format(tproxy_user_name)] = tproxy_configuration['hosts'] + properties[PROXYUSER_USERS.format(tproxy_user_name)] = tproxy_configuration['users'] + properties[PROXYUSER_GROUPS.format(tproxy_user_name)] = tproxy_configuration['groups'] + + +def update_tproxy_conf(ambari_properties, tproxy_configuration_properties, admin_login, admin_password): + request_data = { + "Configuration": { + "category": "tproxy-configuration", + "properties": { + } + } + } + request_data['Configuration']['properties'] = tproxy_configuration_properties + perform_changes_via_rest_api(ambari_properties, admin_login, admin_password, TPROXY_CONFIG_API_ENTRYPOINT, 'PUT', request_data) + + +def remove_tproxy_conf(ambari_properties, admin_login, admin_password): + perform_changes_via_rest_api(ambari_properties, admin_login, admin_password, TPROXY_CONFIG_API_ENTRYPOINT, 'DELETE') + + +def validate_options(options): + errors = [] + if options.tproxy_enabled and not re.match(REGEX_TRUE_FALSE, options.tproxy_enabled): + errors.append("--tproxy-enabled should be to either 'true' or 'false'") + + if options.tproxy_configuration_file_path and options.tproxy_configuration_file_path is not None: + if not os.path.isfile(options.tproxy_configuration_file_path): + errors.append("--tproxy-configuration-file-path is set to a non-existing file: {}".format(options.tproxy_configuration_file_path)) + + if len(errors) > 0: + error_msg = "The following errors occurred while processing your request: {0}" + raise FatalException(1, error_msg.format(str(errors))) + + +def setup_trusted_proxy(options): + print_info_msg("Setup Trusted Proxy") + + server_status, pid = is_server_runing() + if not server_status: + err = 'Ambari Server is not running.' + raise FatalException(1, err) + + if not get_silent(): + validate_options(options) + + ambari_properties = get_ambari_properties() + + admin_login, admin_password = get_ambari_admin_username_password_pair(options) + properties = get_trusted_proxy_properties(ambari_properties, admin_login, admin_password) + + if not options.tproxy_enabled: + tproxy_support_enabled = get_value_from_dictionary(properties, TPROXY_SUPPORT_ENABLED) + + if tproxy_support_enabled: + if 'true' == tproxy_support_enabled: + tproxy_status = "enabled" + else: + tproxy_status = "disabled" + else: + tproxy_status = "not configured" + print_info_msg("\nTrusted Proxy support is currently %s\n" % tproxy_status) + + if tproxy_status == "enabled": + enable_tproxy = not get_YN_input("Do you want to disable Trusted Proxy support [y/n] (n)? ", False) + elif get_YN_input("Do you want to configure Trusted Proxy Support [y/n] (y)? ", True): + enable_tproxy = True + else: + return False + else: + enable_tproxy = options.tproxy_enabled == 'true' + + if enable_tproxy: + properties[TPROXY_SUPPORT_ENABLED] = "true" + if not options.tproxy_configuration_file_path: + add_new_trusted_proxy = add_new_trusted_proxy_config(properties) + while add_new_trusted_proxy: + add_new_trusted_proxy = add_new_trusted_proxy_config(properties) + else: + parse_trusted_configuration_file(options.tproxy_configuration_file_path, properties) + + update_tproxy_conf(ambari_properties, properties, admin_login, admin_password) + else: + remove_tproxy_conf(ambari_properties, admin_login, admin_password) + + else: + warning = "setup-trusted-proxy is not enabled in silent mode." + raise NonFatalException(warning) + pass \ No newline at end of file diff --git a/ambari-server/src/test/python/TestSetupTrustedProxy.py b/ambari-server/src/test/python/TestSetupTrustedProxy.py new file mode 100644 index 0000000..7c02f49 --- /dev/null +++ b/ambari-server/src/test/python/TestSetupTrustedProxy.py @@ -0,0 +1,323 @@ +''' +Licensed to the Apache Software Foundation (ASF) under one +or more contributor license agreements. See the NOTICE file +distributed with this work for additional information +regarding copyright ownership. The ASF licenses this file +to you under the Apache License, Version 2.0 (the +"License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +''' + +import os +import platform +import sys +import unittest +import StringIO + +from mock.mock import patch, MagicMock + +from only_for_platform import os_distro_value +from ambari_commons import os_utils +from urllib2 import HTTPError + +import shutil + +# Mock classes for reading from a file +class MagicFile(object): + def __init__(self, data): + self.data = data + + def read(self): + return self.data + + def __exit__(self, exc_type, exc_val, exc_tb): + pass + + def __enter__(self): + return self +pass + +project_dir = os.path.join(os.path.abspath(os.path.dirname(__file__)),os.path.normpath("../../../../")) +shutil.copyfile(project_dir+"/ambari-server/conf/unix/ambari.properties", "/tmp/ambari.properties") + +# We have to use this import HACK because the filename contains a dash +_search_file = os_utils.search_file + +def search_file_proxy(filename, searchpatch, pathsep=os.pathsep): + global _search_file + if "ambari.properties" in filename: + return "/tmp/ambari.properties" + return _search_file(filename, searchpatch, pathsep) + +os_utils.search_file = search_file_proxy + +with patch.object(platform, "linux_distribution", return_value = MagicMock(return_value=('Redhat', '7.4', 'Final'))): + with patch("os.path.isdir", return_value = MagicMock(return_value=True)): + with patch("os.access", return_value = MagicMock(return_value=True)): + with patch.object(os_utils, "parse_log4j_file", return_value={'ambari.log.dir': '/var/log/ambari-server'}): + with patch("platform.linux_distribution", return_value = os_distro_value): + with patch("os.symlink"): + with patch("glob.glob", return_value = ['/etc/init.d/postgresql-9.3']): + _ambari_server_ = __import__('ambari-server') + with patch("__builtin__.open"): + from ambari_commons.exceptions import FatalException, NonFatalException + from ambari_server.properties import Properties + from ambari_server.setupTrustedProxy import setup_trusted_proxy, TPROXY_SUPPORT_ENABLED, PROXYUSER_HOSTS, PROXYUSER_USERS, PROXYUSER_GROUPS + +class TestSetupTrustedProxy(unittest.TestCase): + + @patch("ambari_server.setupTrustedProxy.is_server_runing") + def test_tproxy_setup_should_fail_if_server_is_not_running(self, is_server_runing_mock): + out = StringIO.StringIO() + sys.stdout = out + + is_server_runing_mock.return_value = (False, 0) + options = self._create_empty_options_mock() + + try: + setup_trusted_proxy(options) + self.fail("Should fail with non-fatal exception") + except FatalException as e: + self.assertTrue("Ambari Server is not running" in e.reason) + pass + + sys.stdout = sys.__stdout__ + pass + + + @patch("ambari_server.setupTrustedProxy.get_silent") + @patch("ambari_server.setupTrustedProxy.is_server_runing") + def test_silent_mode_is_not_allowed(self, is_server_runing_mock, get_silent_mock): + out = StringIO.StringIO() + sys.stdout = out + + is_server_runing_mock.return_value = (True, 0) + get_silent_mock.return_value = True + options = self._create_empty_options_mock() + + try: + setup_trusted_proxy(options) + self.fail("Should fail with fatal exception") + except NonFatalException as e: + self.assertTrue("setup-trusted-proxy is not enabled in silent mode." in e.reason) + pass + + sys.stdout = sys.__stdout__ + pass + + + @patch("ambari_server.setupTrustedProxy.get_silent") + @patch("ambari_server.setupTrustedProxy.is_server_runing") + def test_invalid_tproxy_enabled_cli_option_should_result_in_error(self, is_server_runing_mock, get_silent_mock): + out = StringIO.StringIO() + sys.stdout = out + + is_server_runing_mock.return_value = (True, 0) + get_silent_mock.return_value = False + options = self._create_empty_options_mock() + options.tproxy_enabled = 'not_true_or_false' + + try: + setup_trusted_proxy(options) + self.fail("Should fail with fatal exception") + except FatalException as e: + self.assertTrue("--tproxy-enabled should be to either 'true' or 'false'" in e.reason) + pass + + sys.stdout = sys.__stdout__ + pass + + + @patch("ambari_server.setupTrustedProxy.perform_changes_via_rest_api") + @patch("ambari_server.setupTrustedProxy.get_YN_input") + @patch("ambari_server.setupTrustedProxy.get_validated_string_input") + @patch("ambari_server.setupTrustedProxy.get_ambari_properties") + @patch("ambari_server.setupTrustedProxy.get_silent") + @patch("ambari_server.setupTrustedProxy.is_server_runing") + @patch("ambari_server.setupTrustedProxy.get_json_via_rest_api") + def test_tproxy_is_enabled_for_two_proxy_users(self, get_json_via_rest_api_mock, is_server_runing_mock, get_silent_mock, + get_ambari_properties_mock, get_validated_string_input_mock, get_YN_input_mock, perform_changes_via_rest_api_mock): + out = StringIO.StringIO() + sys.stdout = out + + get_json_via_rest_api_mock.return_value = (200, {}) + + is_server_runing_mock.return_value = (True, 0) + get_silent_mock.return_value = False + get_ambari_properties_mock.return_value = Properties() + + user_name1 = 'knox' + hosts1 = 'knox_hosts' + users1 = 'knox_users' + groups1 = 'knox_groups' + + user_name2 = 'admin' + hosts2 = 'admin_hosts' + users2 = 'admin_users' + groups2 = 'admin_groups' + get_validated_string_input_mock.side_effect = [user_name1, hosts1, users1, groups1, user_name2, hosts2, users2, groups2] + + get_YN_input_mock.side_effect = [True, False] #answer 'True' for the first time when asking for a new proxy user addition and then 'False' (indicating we do not want to add more proxy users) + + options = self._create_empty_options_mock() + options.tproxy_enabled = 'true' + + setup_trusted_proxy(options) + + self.assertTrue(perform_changes_via_rest_api_mock.called) + requestCall = perform_changes_via_rest_api_mock.call_args_list[0] + args, kwargs = requestCall + requestData = args[5] + self.assertTrue(isinstance(requestData, dict)) + tproxyProperties = requestData['Configuration']['properties'] + self.assertEqual(tproxyProperties[TPROXY_SUPPORT_ENABLED], 'true') + + self.assertEqual(tproxyProperties[PROXYUSER_HOSTS.format(user_name1)], hosts1) + self.assertEqual(tproxyProperties[PROXYUSER_USERS.format(user_name1)], users1) + self.assertEqual(tproxyProperties[PROXYUSER_GROUPS.format(user_name1)], groups1) + + self.assertEqual(tproxyProperties[PROXYUSER_HOSTS.format(user_name2)], hosts2) + self.assertEqual(tproxyProperties[PROXYUSER_USERS.format(user_name2)], users2) + self.assertEqual(tproxyProperties[PROXYUSER_GROUPS.format(user_name2)], groups2) + + sys.stdout = sys.__stdout__ + pass + + + @patch("ambari_server.setupTrustedProxy.perform_changes_via_rest_api") + @patch("ambari_server.setupTrustedProxy.get_ambari_properties") + @patch("ambari_server.setupTrustedProxy.get_silent") + @patch("ambari_server.setupTrustedProxy.is_server_runing") + @patch("ambari_server.setupTrustedProxy.get_json_via_rest_api") + def test_disabling_tproxy_support(self, get_json_via_rest_api_mock, is_server_runing_mock, get_silent_mock, get_ambari_properties_mock, perform_changes_via_rest_api_mock): + out = StringIO.StringIO() + sys.stdout = out + + get_json_via_rest_api_mock.return_value = (200, {}) + + is_server_runing_mock.return_value = (True, 0) + get_silent_mock.return_value = False + + properties = Properties() + get_ambari_properties_mock.return_value = properties + + options = self._create_empty_options_mock() + options.tproxy_enabled = 'false' + + setup_trusted_proxy(options) + + self.assertTrue(perform_changes_via_rest_api_mock.called) + requestCall = perform_changes_via_rest_api_mock.call_args_list[0] + args, kwargs = requestCall + requestMethod = args[4] + self.assertTrue(isinstance(requestMethod, str)) + self.assertEqual(requestMethod, "DELETE") + + sys.stdout = sys.__stdout__ + pass + + @patch("ambari_server.setupTrustedProxy.get_silent") + @patch("ambari_server.setupTrustedProxy.is_server_runing") + @patch("os.path.isfile") + def test_enable_tproxy_support_using_configuration_file_path_from_command_line_should_fail_if_file_does_not_exist(self, isfile_mock, is_server_runing_mock, get_silent_mock): + out = StringIO.StringIO() + sys.stdout = out + + is_server_runing_mock.return_value = (True, 0) + get_silent_mock.return_value = False + isfile_mock.return_value = False + + options = self._create_empty_options_mock() + options.tproxy_enabled = 'true' + options.tproxy_configuration_file_path = 'samplePath' + + try: + setup_trusted_proxy(options) + self.fail("Should fail with fatal exception") + except FatalException as e: + self.assertTrue("--tproxy-configuration-file-path is set to a non-existing file" in e.reason) + pass + + sys.stdout = sys.__stdout__ + pass + + + @patch("ambari_server.setupTrustedProxy.perform_changes_via_rest_api") + @patch("ambari_server.setupTrustedProxy.get_ambari_properties") + @patch("ambari_server.setupTrustedProxy.get_silent") + @patch("ambari_server.setupTrustedProxy.is_server_runing") + @patch("ambari_server.setupTrustedProxy.get_json_via_rest_api") + @patch("os.path.isfile") + @patch('__builtin__.open') + def test_enable_tproxy_support_using_configuration_file_path_from_command_line(self, open_mock, isfile_mock, get_json_via_rest_api_mock, is_server_runing_mock, get_silent_mock, get_ambari_properties_mock, perform_changes_via_rest_api_mock): + out = StringIO.StringIO() + sys.stdout = out + + get_json_via_rest_api_mock.return_value = (200, {}) + + is_server_runing_mock.return_value = (True, 0) + get_silent_mock.return_value = False + + properties = Properties() + get_ambari_properties_mock.return_value = properties + + isfile_mock.return_value = True + + tproxy_configurations = "["\ + " {"\ + " \"proxyuser\" : \"knox\"," \ + " \"hosts\" : \"host1\"," \ + " \"users\" : \"user1\"," \ + " \"groups\" : \"group1\"" \ + " }," \ + " {"\ + " \"proxyuser\": \"admin\"," \ + " \"hosts\" : \"host2\"," \ + " \"users\" : \"user2\"," \ + " \"groups\" : \"group2\"" \ + " }" \ + "]" + mock_file = MagicFile(tproxy_configurations) + open_mock.side_effect = [mock_file] + + options = self._create_empty_options_mock() + options.tproxy_enabled = 'true' + options.tproxy_configuration_file_path = 'samplePath' + + setup_trusted_proxy(options) + + self.assertTrue(perform_changes_via_rest_api_mock.called) + requestCall = perform_changes_via_rest_api_mock.call_args_list[0] + args, kwargs = requestCall + requestData = args[5] + self.assertTrue(isinstance(requestData, dict)) + tproxyProperties = requestData['Configuration']['properties'] + self.assertEqual(tproxyProperties[TPROXY_SUPPORT_ENABLED], 'true') + + user_name1="knox" + self.assertEqual(tproxyProperties[PROXYUSER_HOSTS.format(user_name1)], "host1") + self.assertEqual(tproxyProperties[PROXYUSER_USERS.format(user_name1)], "user1") + self.assertEqual(tproxyProperties[PROXYUSER_GROUPS.format(user_name1)], "group1") + + user_name2="admin" + self.assertEqual(tproxyProperties[PROXYUSER_HOSTS.format(user_name2)], "host2") + self.assertEqual(tproxyProperties[PROXYUSER_USERS.format(user_name2)], "user2") + self.assertEqual(tproxyProperties[PROXYUSER_GROUPS.format(user_name2)], "group2") + + sys.stdout = sys.__stdout__ + pass + + + def _create_empty_options_mock(self): + options = MagicMock() + options.tproxy_enabled = None + options.tproxy_configuration_file_path = None + return options