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 2019/01/30 13:53:43 UTC

[ambari] branch branch-2.7 updated: AMBARI-24986. Using Ambari server's CLI to enable/disable Trusted Proxy support (#2798)

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

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


The following commit(s) were added to refs/heads/branch-2.7 by this push:
     new 0b6320d  AMBARI-24986. Using Ambari server's CLI to enable/disable Trusted Proxy support (#2798)
0b6320d is described below

commit 0b6320d5ca07200e98e1497aced92dc8029766eb
Author: Sandor Molnar <sm...@apache.org>
AuthorDate: Wed Jan 30 14:53:37 2019 +0100

    AMBARI-24986. Using Ambari server's CLI to enable/disable Trusted Proxy support (#2798)
---
 ambari-server/sbin/ambari-server                   |   6 +-
 ambari-server/src/main/python/ambari-server.py     |  24 +-
 .../src/main/python/ambari_server/serverUtils.py   |  11 +-
 .../src/main/python/ambari_server/setupActions.py  |   1 +
 .../src/main/python/ambari_server/setupSso.py      |   9 +-
 .../main/python/ambari_server/setupTrustedProxy.py | 172 +++++++++++
 .../src/test/python/TestSetupTrustedProxy.py       | 323 +++++++++++++++++++++
 7 files changed, 534 insertions(+), 12 deletions(-)

diff --git a/ambari-server/sbin/ambari-server b/ambari-server/sbin/ambari-server
index 3289ff8..57975c2 100755
--- a/ambari-server/sbin/ambari-server
+++ b/ambari-server/sbin/ambari-server
@@ -184,6 +184,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" "$@"
@@ -206,7 +210,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 8cfa147..7cb7a39 100755
--- a/ambari-server/src/main/python/ambari-server.py
+++ b/ambari-server/src/main/python/ambari-server.py
@@ -49,13 +49,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, 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
 
@@ -588,11 +589,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")
@@ -806,7 +821,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
 
@@ -838,7 +854,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
 
@@ -870,6 +887,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/serverUtils.py b/ambari-server/src/main/python/ambari_server/serverUtils.py
index 7d2370e..07072e6 100644
--- a/ambari-server/src/main/python/ambari_server/serverUtils.py
+++ b/ambari-server/src/main/python/ambari_server/serverUtils.py
@@ -294,4 +294,13 @@ 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 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/main/python/ambari_server/setupActions.py b/ambari-server/src/main/python/ambari_server/setupActions.py
index 61d20af..7e5b677 100644
--- a/ambari-server/src/main/python/ambari_server/setupActions.py
+++ b/ambari-server/src/main/python/ambari_server/setupActions.py
@@ -49,3 +49,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/setupSso.py b/ambari-server/src/main/python/ambari_server/setupSso.py
index 8b3a041..4ee77b9 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_value_from_dictionary, get_boolean_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
 
@@ -319,10 +320,4 @@ 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/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