Pārlūkot izejas kodu

AMBARI-24894. Sensitive service configuration values should be decrypted when processing the Ambari agent command script, if enabled (amagyar) (#2613)

Attila Magyar 7 gadi atpakaļ
vecāks
revīzija
f8a019926f
55 mainītis faili ar 1947 papildinājumiem un 262 dzēšanām
  1. 3 1
      ambari-agent/pom.xml
  2. 2 1
      ambari-agent/src/main/python/ambari_agent/Constants.py
  3. 6 0
      ambari-agent/src/main/python/ambari_agent/CustomServiceOrchestrator.py
  4. 3 3
      ambari-agent/src/main/python/ambari_agent/HeartbeatThread.py
  5. 43 0
      ambari-agent/src/main/python/ambari_agent/listeners/EncryptionKeyListener.py
  6. 2 1
      ambari-agent/src/main/python/ambari_agent/listeners/__init__.py
  7. 12 0
      ambari-agent/src/packages/tarball/all.xml
  8. 27 0
      ambari-agent/src/test/python/resource_management/TestEncryption.py
  9. 86 0
      ambari-common/src/main/python/ambari_pbkdf2/README.txt
  10. 0 0
      ambari-common/src/main/python/ambari_pbkdf2/__init__.py
  11. 297 0
      ambari-common/src/main/python/ambari_pbkdf2/pbkdf2.py
  12. 22 0
      ambari-common/src/main/python/ambari_pyaes/LICENSE.txt
  13. 363 0
      ambari-common/src/main/python/ambari_pyaes/README.md
  14. 53 0
      ambari-common/src/main/python/ambari_pyaes/__init__.py
  15. 113 0
      ambari-common/src/main/python/ambari_pyaes/aes.py
  16. 227 0
      ambari-common/src/main/python/ambari_pyaes/blockfeeder.py
  17. 60 0
      ambari-common/src/main/python/ambari_pyaes/util.py
  18. 47 0
      ambari-common/src/main/python/resource_management/core/encryption.py
  19. 1 1
      ambari-common/src/main/python/resource_management/core/utils.py
  20. 5 3
      ambari-common/src/main/python/resource_management/libraries/script/config_dictionary.py
  21. 2 0
      ambari-server/pom.xml
  22. 8 0
      ambari-server/src/main/assemblies/server.xml
  23. 88 0
      ambari-server/src/main/java/org/apache/ambari/server/agent/AgentEncryptionKey.java
  24. 16 17
      ambari-server/src/main/java/org/apache/ambari/server/agent/HeartBeatHandler.java
  25. 9 3
      ambari-server/src/main/java/org/apache/ambari/server/agent/stomp/AgentConfigsHolder.java
  26. 4 1
      ambari-server/src/main/java/org/apache/ambari/server/agent/stomp/AgentDataHolder.java
  27. 9 1
      ambari-server/src/main/java/org/apache/ambari/server/controller/ControllerModule.java
  28. 3 1
      ambari-server/src/main/java/org/apache/ambari/server/events/DefaultMessageEmitter.java
  29. 52 0
      ambari-server/src/main/java/org/apache/ambari/server/events/EncryptionKeyUpdateEvent.java
  30. 2 1
      ambari-server/src/main/java/org/apache/ambari/server/events/STOMPEvent.java
  31. 33 19
      ambari-server/src/main/java/org/apache/ambari/server/security/encryption/AESEncryptionService.java
  32. 26 37
      ambari-server/src/main/java/org/apache/ambari/server/security/encryption/AESEncryptor.java
  33. 84 0
      ambari-server/src/main/java/org/apache/ambari/server/security/encryption/AgentConfigUpdateEncryptor.java
  34. 11 80
      ambari-server/src/main/java/org/apache/ambari/server/security/encryption/ConfigPropertiesEncryptor.java
  35. 13 21
      ambari-server/src/main/java/org/apache/ambari/server/security/encryption/EncryptionService.java
  36. 13 2
      ambari-server/src/main/java/org/apache/ambari/server/security/encryption/Encryptor.java
  37. 115 0
      ambari-server/src/main/java/org/apache/ambari/server/security/encryption/PropertiesEncryptor.java
  38. 15 18
      ambari-server/src/main/java/org/apache/ambari/server/state/ConfigImpl.java
  39. 3 2
      ambari-server/src/test/java/org/apache/ambari/server/agent/AgentResourceTest.java
  40. 2 1
      ambari-server/src/test/java/org/apache/ambari/server/agent/HeartbeatProcessorTest.java
  41. 2 1
      ambari-server/src/test/java/org/apache/ambari/server/agent/HeartbeatTestHelper.java
  42. 18 35
      ambari-server/src/test/java/org/apache/ambari/server/agent/TestHeartbeatHandler.java
  43. 7 7
      ambari-server/src/test/java/org/apache/ambari/server/agent/TestHeartbeatMonitor.java
  44. 2 1
      ambari-server/src/test/java/org/apache/ambari/server/agent/stomp/AgentDataHolderTest.java
  45. 0 1
      ambari-server/src/test/java/org/apache/ambari/server/controller/KerberosHelperTest.java
  46. 5 0
      ambari-server/src/test/java/org/apache/ambari/server/controller/internal/PreUpgradeCheckResourceProviderTest.java
  47. 5 0
      ambari-server/src/test/java/org/apache/ambari/server/controller/internal/UserAuthorizationResourceProviderTest.java
  48. 5 0
      ambari-server/src/test/java/org/apache/ambari/server/controller/internal/UserResourceProviderTest.java
  49. 5 0
      ambari-server/src/test/java/org/apache/ambari/server/orm/InMemoryDefaultTestModule.java
  50. 5 0
      ambari-server/src/test/java/org/apache/ambari/server/serveraction/kerberos/AbstractPrepareKerberosServerActionTest.java
  51. 4 0
      ambari-server/src/test/java/org/apache/ambari/server/serveraction/upgrades/PreconfigureKerberosActionTest.java
  52. 2 1
      ambari-server/src/test/java/org/apache/ambari/server/state/host/HostTest.java
  53. 3 1
      ambari-server/src/test/java/org/apache/ambari/server/testutils/PartialNiceMockBinder.java
  54. 3 0
      pom.xml
  55. 1 1
      start-build-env.sh

+ 3 - 1
ambari-agent/pom.xml

@@ -42,6 +42,8 @@
     <simplejson.install.dir>/usr/lib/ambari-agent/lib/ambari_simplejson</simplejson.install.dir>
     <stomp.install.dir>/usr/lib/ambari-agent/lib/ambari_stomp</stomp.install.dir>
     <ws4py.install.dir>/usr/lib/ambari-agent/lib/ambari_ws4py</ws4py.install.dir>
+    <pbkdf2.install.dir>/usr/lib/ambari-agent/lib/ambari_pbkdf2</pbkdf2.install.dir>
+    <pyaes.install.dir>/usr/lib/ambari-agent/lib/ambari_pyaes</pyaes.install.dir>
     <lib.dir>/usr/lib/ambari-agent/lib</lib.dir>
     <deb.architecture>amd64</deb.architecture>
     <ambari.server.module>../ambari-server</ambari.server.module>
@@ -698,7 +700,7 @@
       <executable.shell>sh</executable.shell>
       <fileextension.shell>sh</fileextension.shell>
       <fileextension.dot.shell-default></fileextension.dot.shell-default>
-      <path.python.1>${project.basedir}/../ambari-common/src/main/python:${project.basedir}/../ambari-agent/src/main/python:${project.basedir}/../ambari-common/src/main/python/ambari_jinja2:${project.basedir}/../ambari-common/src/main/python/ambari_commons:${project.basedir}/../ambari-common/src/test/python:${project.basedir}/src/main/python:${project.basedir}/src/main/python/ambari_agent:${project.basedir}/src/main/python/resource_management:${project.basedir}/src/test/python:${project.basedir}/src/test/python/ambari_agent:${project.basedir}/src/test/python/resource_management:${project.basedir}/../ambari-server/src/test/python:${project.basedir}/../ambari-server/src/main/resources/common-services/HDFS/2.1.0.2.0/package/files</path.python.1>
+      <path.python.1>${project.basedir}/../ambari-common/src/main/python:${project.basedir}/../ambari-agent/src/main/python:${project.basedir}/../ambari-common/src/main/python/ambari_jinja2:${project.basedir}/../ambari-agent/src/main/python:${project.basedir}/../ambari-common/src/main/python/ambari_commons:${project.basedir}/../ambari-common/src/test/python:${project.basedir}/src/main/python:${project.basedir}/src/main/python/ambari_agent:${project.basedir}/src/main/python/resource_management:${project.basedir}/src/test/python:${project.basedir}/src/test/python/ambari_agent:${project.basedir}/src/test/python/resource_management:${project.basedir}/../ambari-server/src/test/python:${project.basedir}/../ambari-server/src/main/resources/common-services/HDFS/2.1.0.2.0/package/files</path.python.1>
      </properties>
     </profile>
     <profile>

+ 2 - 1
ambari-agent/src/main/python/ambari_agent/Constants.py

@@ -27,8 +27,9 @@ METADATA_TOPIC = '/events/metadata'
 TOPOLOGIES_TOPIC = '/events/topologies'
 SERVER_RESPONSES_TOPIC = '/user/'
 AGENT_ACTIONS_TOPIC = '/user/agent_actions'
+ENCRYPTION_KEY_TOPIC = '/events/encryption_key'
 
-PRE_REGISTRATION_TOPICS_TO_SUBSCRIBE = [SERVER_RESPONSES_TOPIC, AGENT_ACTIONS_TOPIC]
+PRE_REGISTRATION_TOPICS_TO_SUBSCRIBE = [SERVER_RESPONSES_TOPIC, AGENT_ACTIONS_TOPIC, ENCRYPTION_KEY_TOPIC]
 POST_REGISTRATION_TOPICS_TO_SUBSCRIBE = [COMMANDS_TOPIC]
 
 AGENT_RESPONSES_TOPIC = '/reports/responses'

+ 6 - 0
ambari-agent/src/main/python/ambari_agent/CustomServiceOrchestrator.py

@@ -34,6 +34,7 @@ from ambari_commons import shell, subprocess32
 from ambari_commons.constants import AGENT_TMP_DIR
 from resource_management.libraries.functions.log_process_information import log_process_information
 from resource_management.core.utils import PasswordString
+from resource_management.core.encryption import ensure_decrypted
 
 from ambari_agent.models.commands import AgentCommand
 from ambari_agent.Utils import Utils
@@ -111,6 +112,7 @@ class CustomServiceOrchestrator(object):
 
     # save count (not boolean) for parallel execution cases
     self.commands_for_component_in_progress = defaultdict(lambda:defaultdict(lambda:0))
+    self.encryption_key = None
 
   def map_task_to_process(self, task_id, processId):
     with self.commands_in_progress_lock:
@@ -297,6 +299,7 @@ class CustomServiceOrchestrator(object):
       logger.info('provider_path={0}'.format(provider_path))
       for alias, pwd in credentials.items():
         logger.debug("config={0}".format(config))
+        pwd = ensure_decrypted(pwd, self.encryption_key)
         protected_pwd = PasswordString(pwd)
         # Generate the JCEKS file
         cmd = (java_bin, '-cp', cs_lib_path, self.credential_shell_cmd, 'create',
@@ -406,6 +409,9 @@ class CustomServiceOrchestrator(object):
 
         raise AgentException("Background commands are supported without hooks only")
 
+      if self.encryption_key:
+        os.environ['AGENT_ENCRYPTION_KEY'] = self.encryption_key
+
       python_executor = self.get_py_executor(forced_command_name)
       backup_log_files = command_name not in self.DONT_BACKUP_LOGS_FOR_COMMANDS
       try:

+ 3 - 3
ambari-agent/src/main/python/ambari_agent/HeartbeatThread.py

@@ -19,7 +19,6 @@ limitations under the License.
 '''
 
 import logging
-import ambari_stomp
 import threading
 from socket import error as socket_error
 
@@ -28,7 +27,6 @@ from ambari_agent.Register import Register
 from ambari_agent.Utils import BlockingDictionary
 from ambari_agent.Utils import Utils
 from ambari_agent.ComponentVersionReporter import ComponentVersionReporter
-from ambari_agent.listeners.ServerResponsesListener import ServerResponsesListener
 from ambari_agent.listeners.TopologyEventListener import TopologyEventListener
 from ambari_agent.listeners.ConfigurationEventListener import ConfigurationEventListener
 from ambari_agent.listeners.AgentActionsListener import AgentActionsListener
@@ -36,6 +34,7 @@ from ambari_agent.listeners.MetadataEventListener import MetadataEventListener
 from ambari_agent.listeners.CommandsEventListener import CommandsEventListener
 from ambari_agent.listeners.HostLevelParamsEventListener import HostLevelParamsEventListener
 from ambari_agent.listeners.AlertDefinitionsEventListener import AlertDefinitionsEventListener
+from ambari_agent.listeners.EncryptionKeyListener import EncryptionKeyListener
 from ambari_agent import security
 from ambari_stomp.adapter.websocket import ConnectionIsAlreadyClosed
 
@@ -64,11 +63,12 @@ class HeartbeatThread(threading.Thread):
     self.metadata_events_listener = MetadataEventListener(initializer_module)
     self.topology_events_listener = TopologyEventListener(initializer_module)
     self.configuration_events_listener = ConfigurationEventListener(initializer_module)
+    self.encryption_key_events_listener = EncryptionKeyListener(initializer_module)
     self.host_level_params_events_listener = HostLevelParamsEventListener(initializer_module)
     self.alert_definitions_events_listener = AlertDefinitionsEventListener(initializer_module)
     self.agent_actions_events_listener = AgentActionsListener(initializer_module)
     self.component_status_executor = initializer_module.component_status_executor
-    self.listeners = [self.server_responses_listener, self.commands_events_listener, self.metadata_events_listener, self.topology_events_listener, self.configuration_events_listener, self.host_level_params_events_listener, self.alert_definitions_events_listener, self.agent_actions_events_listener]
+    self.listeners = [self.server_responses_listener, self.commands_events_listener, self.metadata_events_listener, self.topology_events_listener, self.configuration_events_listener, self.host_level_params_events_listener, self.alert_definitions_events_listener, self.agent_actions_events_listener, self.encryption_key_events_listener]
 
     self.post_registration_requests = [
     (Constants.TOPOLOGY_REQUEST_ENDPOINT, initializer_module.topology_cache, self.topology_events_listener, Constants.TOPOLOGIES_TOPIC),

+ 43 - 0
ambari-agent/src/main/python/ambari_agent/listeners/EncryptionKeyListener.py

@@ -0,0 +1,43 @@
+#!/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 logging
+
+from ambari_agent.listeners import EventListener
+from ambari_agent import Constants
+
+logger = logging.getLogger(__name__)
+
+class EncryptionKeyListener(EventListener):
+  """
+  Listener of Constants.ENCRYPTION_KEY_TOPIC events from server.
+  """
+
+  def __init__(self, initializer_module):
+    super(EncryptionKeyListener, self).__init__(initializer_module)
+
+  def on_event(self, headers, message):
+    logger.info("EncryptionKey received")
+    self.initializer_module.customServiceOrchestrator.encryption_key = message['encryptionKey']
+
+  def get_handled_path(self):
+    return Constants.ENCRYPTION_KEY_TOPIC
+
+

+ 2 - 1
ambari-agent/src/main/python/ambari_agent/listeners/__init__.py

@@ -79,7 +79,8 @@ class EventListener(ambari_stomp.ConnectionListener):
         self.report_status_to_sender(headers, message, ex)
         return
 
-      logger.info("Event from server at {0}{1}".format(destination, self.get_log_message(headers, copy.deepcopy(message_json))))
+      if destination != Constants.ENCRYPTION_KEY_TOPIC:
+        logger.info("Event from server at {0}{1}".format(destination, self.get_log_message(headers, copy.deepcopy(message_json))))
 
       if not self.enabled:
         with self.event_queue_lock:

+ 12 - 0
ambari-agent/src/packages/tarball/all.xml

@@ -87,6 +87,18 @@
       <directory>${project.basedir}/../ambari-common/src/main/python/ambari_ws4py</directory>
       <outputDirectory>${ws4py.install.dir}</outputDirectory>
     </fileSet>
+    <fileSet>
+      <directoryMode>755</directoryMode>
+      <fileMode>755</fileMode>
+      <directory>${project.basedir}/../ambari-common/src/main/python/ambari_pbkdf2</directory>
+      <outputDirectory>${pbkdf2.install.dir}</outputDirectory>
+    </fileSet>
+    <fileSet>
+      <directoryMode>755</directoryMode>
+      <fileMode>755</fileMode>
+      <directory>${project.basedir}/../ambari-common/src/main/python/ambari_pyaes</directory>
+      <outputDirectory>${pyaes.install.dir}</outputDirectory>
+    </fileSet>
     <fileSet>
       <directoryMode>755</directoryMode>
       <fileMode>755</fileMode>

+ 27 - 0
ambari-agent/src/test/python/resource_management/TestEncryption.py

@@ -0,0 +1,27 @@
+'''
+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.
+'''
+
+from unittest import TestCase
+from resource_management.core.encryption import ensure_decrypted
+
+class TestUtils(TestCase):
+
+  def test_attr_to_bitmask(self):
+    encypted_value = '${enc=aes256_hex, value=616639333036363938646230613262383a3a32313537386561376136326362656436656135626165313664613265316336663a3a6361633666333432653532393863313364393064626133653562353663663235}'
+    encyption_key = 'i%r041K%1VC!C5 K=('
+    self.assertEquals('mysecret', ensure_decrypted(encypted_value, encyption_key))

+ 86 - 0
ambari-common/src/main/python/ambari_pbkdf2/README.txt

@@ -0,0 +1,86 @@
+Python PKCS#5 v2.0 PBKDF2 Module
+--------------------------------
+
+This module implements the password-based key derivation function, PBKDF2,
+specified in `RSA PKCS#5 v2.0 <http://www.rsa.com/rsalabs/node.asp?id=2127>`_.
+
+Example PBKDF2 usage
+====================
+
+::
+
+ from pbkdf2 import PBKDF2
+ from Crypto.Cipher import AES
+ import os
+
+ salt = os.urandom(8)    # 64-bit salt
+ key = PBKDF2("This passphrase is a secret.", salt).read(32) # 256-bit key
+ iv = os.urandom(16)     # 128-bit IV
+ cipher = AES.new(key, AES.MODE_CBC, iv)
+ # ...
+
+Example crypt() usage
+=====================
+
+::
+
+ from pbkdf2 import crypt
+ pwhash = crypt("secret")
+ alleged_pw = raw_input("Enter password: ")
+ if pwhash == crypt(alleged_pw, pwhash):
+     print "Password good"
+ else:
+     print "Invalid password"
+
+Example crypt() output
+======================
+
+::
+
+ >>> from pbkdf2 import crypt
+ >>> crypt("secret")
+ '$p5k2$$hi46RA73$aGBpfPOgOrgZLaHGweSQzJ5FLz4BsQVs'
+ >>> crypt("secret", "XXXXXXXX")
+ '$p5k2$$XXXXXXXX$L9mVVdq7upotdvtGvXTDTez3FIu3z0uG'
+ >>> crypt("secret", "XXXXXXXX", 400)  # 400 iterations (the default for crypt)
+ '$p5k2$$XXXXXXXX$L9mVVdq7upotdvtGvXTDTez3FIu3z0uG'
+ >>> crypt("spam", iterations=400)
+ '$p5k2$$FRsH3HJB$SgRWDNmB2LukCy0OTal6LYLHZVgtOi7s'
+ >>> crypt("spam", iterations=1000)    # 1000 iterations
+ '$p5k2$3e8$H0NX9mT/$wk/sE8vv6OMKuMaqazCJYDSUhWY9YB2J'
+
+
+Resources
+=========
+
+Homepage
+    https://www.dlitz.net/software/python-pbkdf2/
+
+Source Code
+    https://github.com/dlitz/python-pbkdf2/
+
+PyPI package name
+    `pbkdf2 <http://pypi.python.org/pypi/pbkdf2>`_
+
+License
+=======
+Copyright (C) 2007-2011 Dwayne C. Litzenberger <dlitz@dlitz.net>
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

+ 0 - 0
ambari-common/src/main/python/ambari_pbkdf2/__init__.py


+ 297 - 0
ambari-common/src/main/python/ambari_pbkdf2/pbkdf2.py

@@ -0,0 +1,297 @@
+#!/usr/bin/python
+# -*- coding: ascii -*-
+###########################################################################
+# pbkdf2 - PKCS#5 v2.0 Password-Based Key Derivation
+#
+# Copyright (C) 2007-2011 Dwayne C. Litzenberger <dlitz@dlitz.net>
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+# Country of origin: Canada
+#
+###########################################################################
+# Sample PBKDF2 usage:
+#   from Crypto.Cipher import AES
+#   from pbkdf2 import PBKDF2
+#   import os
+#
+#   salt = os.urandom(8)    # 64-bit salt
+#   key = PBKDF2("This passphrase is a secret.", salt).read(32) # 256-bit key
+#   iv = os.urandom(16)     # 128-bit IV
+#   cipher = AES.new(key, AES.MODE_CBC, iv)
+#     ...
+#
+# Sample crypt() usage:
+#   from pbkdf2 import crypt
+#   pwhash = crypt("secret")
+#   alleged_pw = raw_input("Enter password: ")
+#   if pwhash == crypt(alleged_pw, pwhash):
+#       print "Password good"
+#   else:
+#       print "Invalid password"
+#
+###########################################################################
+
+__version__ = "1.3"
+__all__ = ['PBKDF2', 'crypt']
+
+from struct import pack
+from random import randint
+import string
+import sys
+
+try:
+    # Use PyCrypto (if available).
+    from Crypto.Hash import HMAC, SHA as SHA1
+except ImportError:
+    # PyCrypto not available.  Use the Python standard library.
+    import hmac as HMAC
+    try:
+        from hashlib import sha1 as SHA1
+    except ImportError:
+        # hashlib not available.  Use the old sha module.
+        import sha as SHA1
+
+#
+# Python 2.1 thru 3.2 compatibility
+#
+
+if sys.version_info[0] == 2:
+    _0xffffffffL = long(1) << 32
+    def isunicode(s):
+        return isinstance(s, unicode)
+    def isbytes(s):
+        return isinstance(s, str)
+    def isinteger(n):
+        return isinstance(n, (int, long))
+    def b(s):
+        return s
+    def binxor(a, b):
+        return "".join([chr(ord(x) ^ ord(y)) for (x, y) in zip(a, b)])
+    def b64encode(data, chars="+/"):
+        tt = string.maketrans("+/", chars)
+        return data.encode('base64').replace("\n", "").translate(tt)
+    from binascii import b2a_hex
+else:
+    _0xffffffffL = 0xffffffff
+    def isunicode(s):
+        return isinstance(s, str)
+    def isbytes(s):
+        return isinstance(s, bytes)
+    def isinteger(n):
+        return isinstance(n, int)
+    def callable(obj):
+        return hasattr(obj, '__call__')
+    def b(s):
+       return s.encode("latin-1")
+    def binxor(a, b):
+        return bytes([x ^ y for (x, y) in zip(a, b)])
+    from base64 import b64encode as _b64encode
+    def b64encode(data, chars="+/"):
+        if isunicode(chars):
+            return _b64encode(data, chars.encode('utf-8')).decode('utf-8')
+        else:
+            return _b64encode(data, chars)
+    from binascii import b2a_hex as _b2a_hex
+    def b2a_hex(s):
+        return _b2a_hex(s).decode('us-ascii')
+    xrange = range
+
+class PBKDF2(object):
+    """PBKDF2.py : PKCS#5 v2.0 Password-Based Key Derivation
+
+    This implementation takes a passphrase and a salt (and optionally an
+    iteration count, a digest module, and a MAC module) and provides a
+    file-like object from which an arbitrarily-sized key can be read.
+
+    If the passphrase and/or salt are unicode objects, they are encoded as
+    UTF-8 before they are processed.
+
+    The idea behind PBKDF2 is to derive a cryptographic key from a
+    passphrase and a salt.
+
+    PBKDF2 may also be used as a strong salted password hash.  The
+    'crypt' function is provided for that purpose.
+
+    Remember: Keys generated using PBKDF2 are only as strong as the
+    passphrases they are derived from.
+    """
+
+    def __init__(self, passphrase, salt, iterations=1000,
+                 digestmodule=SHA1, macmodule=HMAC):
+        self.__macmodule = macmodule
+        self.__digestmodule = digestmodule
+        self._setup(passphrase, salt, iterations, self._pseudorandom)
+
+    def _pseudorandom(self, key, msg):
+        """Pseudorandom function.  e.g. HMAC-SHA1"""
+        return self.__macmodule.new(key=key, msg=msg,
+            digestmod=self.__digestmodule).digest()
+
+    def read(self, bytes):
+        """Read the specified number of key bytes."""
+        if self.closed:
+            raise ValueError("file-like object is closed")
+
+        size = len(self.__buf)
+        blocks = [self.__buf]
+        i = self.__blockNum
+        while size < bytes:
+            i += 1
+            if i > _0xffffffffL or i < 1:
+                # We could return "" here, but
+                raise OverflowError("derived key too long")
+            block = self.__f(i)
+            blocks.append(block)
+            size += len(block)
+        buf = b("").join(blocks)
+        retval = buf[:bytes]
+        self.__buf = buf[bytes:]
+        self.__blockNum = i
+        return retval
+
+    def __f(self, i):
+        # i must fit within 32 bits
+        assert 1 <= i <= _0xffffffffL
+        U = self.__prf(self.__passphrase, self.__salt + pack("!L", i))
+        result = U
+        for j in xrange(2, 1+self.__iterations):
+            U = self.__prf(self.__passphrase, U)
+            result = binxor(result, U)
+        return result
+
+    def hexread(self, octets):
+        """Read the specified number of octets. Return them as hexadecimal.
+
+        Note that len(obj.hexread(n)) == 2*n.
+        """
+        return b2a_hex(self.read(octets))
+
+    def _setup(self, passphrase, salt, iterations, prf):
+        # Sanity checks:
+
+        # passphrase and salt must be str or unicode (in the latter
+        # case, we convert to UTF-8)
+        if isunicode(passphrase):
+            passphrase = passphrase.encode("UTF-8")
+        elif not isbytes(passphrase):
+            raise TypeError("passphrase must be str or unicode")
+        if isunicode(salt):
+            salt = salt.encode("UTF-8")
+        elif not isbytes(salt):
+            raise TypeError("salt must be str or unicode")
+
+        # iterations must be an integer >= 1
+        if not isinteger(iterations):
+            raise TypeError("iterations must be an integer")
+        if iterations < 1:
+            raise ValueError("iterations must be at least 1")
+
+        # prf must be callable
+        if not callable(prf):
+            raise TypeError("prf must be callable")
+
+        self.__passphrase = passphrase
+        self.__salt = salt
+        self.__iterations = iterations
+        self.__prf = prf
+        self.__blockNum = 0
+        self.__buf = b("")
+        self.closed = False
+
+    def close(self):
+        """Close the stream."""
+        if not self.closed:
+            del self.__passphrase
+            del self.__salt
+            del self.__iterations
+            del self.__prf
+            del self.__blockNum
+            del self.__buf
+            self.closed = True
+
+def crypt(word, salt=None, iterations=None):
+    """PBKDF2-based unix crypt(3) replacement.
+
+    The number of iterations specified in the salt overrides the 'iterations'
+    parameter.
+
+    The effective hash length is 192 bits.
+    """
+
+    # Generate a (pseudo-)random salt if the user hasn't provided one.
+    if salt is None:
+        salt = _makesalt()
+
+    # salt must be a string or the us-ascii subset of unicode
+    if isunicode(salt):
+        salt = salt.encode('us-ascii').decode('us-ascii')
+    elif isbytes(salt):
+        salt = salt.decode('us-ascii')
+    else:
+        raise TypeError("salt must be a string")
+
+    # word must be a string or unicode (in the latter case, we convert to UTF-8)
+    if isunicode(word):
+        word = word.encode("UTF-8")
+    elif not isbytes(word):
+        raise TypeError("word must be a string or unicode")
+
+    # Try to extract the real salt and iteration count from the salt
+    if salt.startswith("$p5k2$"):
+        (iterations, salt, dummy) = salt.split("$")[2:5]
+        if iterations == "":
+            iterations = 400
+        else:
+            converted = int(iterations, 16)
+            if iterations != "%x" % converted:  # lowercase hex, minimum digits
+                raise ValueError("Invalid salt")
+            iterations = converted
+            if not (iterations >= 1):
+                raise ValueError("Invalid salt")
+
+    # Make sure the salt matches the allowed character set
+    allowed = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789./"
+    for ch in salt:
+        if ch not in allowed:
+            raise ValueError("Illegal character %r in salt" % (ch,))
+
+    if iterations is None or iterations == 400:
+        iterations = 400
+        salt = "$p5k2$$" + salt
+    else:
+        salt = "$p5k2$%x$%s" % (iterations, salt)
+    rawhash = PBKDF2(word, salt, iterations).read(24)
+    return salt + "$" + b64encode(rawhash, "./")
+
+# Add crypt as a static method of the PBKDF2 class
+# This makes it easier to do "from PBKDF2 import PBKDF2" and still use
+# crypt.
+PBKDF2.crypt = staticmethod(crypt)
+
+def _makesalt():
+    """Return a 48-bit pseudorandom salt for crypt().
+
+    This function is not suitable for generating cryptographic secrets.
+    """
+    binarysalt = b("").join([pack("@H", randint(0, 0xffff)) for i in range(3)])
+    return b64encode(binarysalt, "./")
+
+# vim:set ts=4 sw=4 sts=4 expandtab:

+ 22 - 0
ambari-common/src/main/python/ambari_pyaes/LICENSE.txt

@@ -0,0 +1,22 @@
+The MIT License (MIT)
+
+Copyright (c) 2014 Richard Moore
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+

+ 363 - 0
ambari-common/src/main/python/ambari_pyaes/README.md

@@ -0,0 +1,363 @@
+pyaes
+=====
+
+A pure-Python implmentation of the AES block cipher algorithm and the common modes of operation (CBC, CFB, CTR, ECB and OFB).
+
+
+Features
+--------
+
+* Supports all AES key sizes
+* Supports all AES common modes
+* Pure-Python (no external dependancies)
+* BlockFeeder API allows streams to easily be encrypted and decrypted
+* Python 2.x and 3.x support (make sure you pass in bytes(), not strings for Python 3)
+
+
+API
+---
+
+All keys may be 128 bits (16 bytes), 192 bits (24 bytes) or 256 bits (32 bytes) long.
+
+To generate a random key use:
+```python
+import os
+
+# 128 bit, 192 bit and 256 bit keys
+key_128 = os.urandom(16)
+key_192 = os.urandom(24)
+key_256 = os.urandom(32)
+```
+
+To generate keys from simple-to-remember passwords, consider using a _password-based key-derivation function_ such as [scrypt](https://github.com/ricmoo/pyscrypt).
+
+
+### Common Modes of Operation
+
+There are many modes of operations, each with various pros and cons. In general though, the **CBC** and **CTR** modes are recommended. The **ECB is NOT recommended.**, and is included primarilty for completeness.
+
+Each of the following examples assumes the following key:
+```python
+import pyaes
+
+# A 256 bit (32 byte) key
+key = "This_key_for_demo_purposes_only!"
+
+# For some modes of operation we need a random initialization vector
+# of 16 bytes
+iv = "InitializationVe"
+```
+
+
+#### Counter Mode of Operation (recommended)
+
+```python
+aes = pyaes.AESModeOfOperationCTR(key)
+plaintext = "Text may be any length you wish, no padding is required"
+ciphertext = aes.encrypt(plaintext)
+
+# '''\xb6\x99\x10=\xa4\x96\x88\xd1\x89\x1co\xe6\x1d\xef;\x11\x03\xe3\xee
+#    \xa9V?wY\xbfe\xcdO\xe3\xdf\x9dV\x19\xe5\x8dk\x9fh\xb87>\xdb\xa3\xd6
+#    \x86\xf4\xbd\xb0\x97\xf1\t\x02\xe9 \xed'''
+print repr(ciphertext)
+
+# The counter mode of operation maintains state, so decryption requires
+# a new instance be created
+aes = pyaes.AESModeOfOperationCTR(key)
+decrypted = aes.decrypt(ciphertext)
+
+# True
+print decrypted == plaintext
+
+# To use a custom initial value
+counter = pyaes.Counter(initial_value = 100)
+aes = pyaes.AESModeOfOperationCTR(key, counter = counter)
+ciphertext = aes.encrypt(plaintext)
+
+# '''WZ\x844\x02\xbfoY\x1f\x12\xa6\xce\x03\x82Ei)\xf6\x97mX\x86\xe3\x9d
+#    _1\xdd\xbd\x87\xb5\xccEM_4\x01$\xa6\x81\x0b\xd5\x04\xd7Al\x07\xe5
+#    \xb2\x0e\\\x0f\x00\x13,\x07'''
+print repr(ciphertext)
+```
+
+
+#### Cipher-Block Chaining (recommended)
+
+```python
+aes = pyaes.AESModeOfOperationCBC(key, iv = iv)
+plaintext = "TextMustBe16Byte"
+ciphertext = aes.encrypt(plaintext)
+
+# '\xd6:\x18\xe6\xb1\xb3\xc3\xdc\x87\xdf\xa7|\x08{k\xb6'
+print repr(ciphertext)
+
+
+# The cipher-block chaining mode of operation maintains state, so 
+# decryption requires a new instance be created
+aes = pyaes.AESModeOfOperationCBC(key, iv = iv)
+decrypted = aes.decrypt(ciphertext)
+
+# True
+print decrypted == plaintext
+```
+
+
+#### Cipher Feedback
+
+```python
+# Each block into the mode of operation must be a multiple of the segment
+# size. For this example we choose 8 bytes.
+aes = pyaes.AESModeOfOperationCFB(key, iv = iv, segment_size = 8)
+plaintext =  "TextMustBeAMultipleOfSegmentSize"
+ciphertext = aes.encrypt(plaintext)
+
+# '''v\xa9\xc1w"\x8aL\x93\xcb\xdf\xa0/\xf8Y\x0b\x8d\x88i\xcb\x85rmp
+#    \x85\xfe\xafM\x0c)\xd5\xeb\xaf'''
+print repr(ciphertext)
+
+
+# The cipher-block chaining mode of operation maintains state, so 
+# decryption requires a new instance be created
+aes = pyaes.AESModeOfOperationCFB(key, iv = iv, segment_size = 8)
+decrypted = aes.decrypt(ciphertext)
+
+# True
+print decrypted == plaintext
+```
+
+
+#### Output Feedback Mode of Operation
+
+```python
+aes = pyaes.AESModeOfOperationOFB(key, iv = iv)
+plaintext = "Text may be any length you wish, no padding is required"
+ciphertext = aes.encrypt(plaintext)
+
+# '''v\xa9\xc1wO\x92^\x9e\rR\x1e\xf7\xb1\xa2\x9d"l1\xc7\xe7\x9d\x87(\xc26s
+#    \xdd8\xc8@\xb6\xd9!\xf5\x0cM\xaa\x9b\xc4\xedLD\xe4\xb9\xd8\xdf\x9e\xac
+#    \xa1\xb8\xea\x0f\x8ev\xb5'''
+print repr(ciphertext)
+
+# The counter mode of operation maintains state, so decryption requires
+# a new instance be created
+aes = pyaes.AESModeOfOperationOFB(key, iv = iv)
+decrypted = aes.decrypt(ciphertext)
+
+# True
+print decrypted == plaintext
+```
+
+
+#### Electronic Codebook (NOT recommended)
+
+```python
+aes = pyaes.AESModeOfOperationECB(key)
+plaintext = "TextMustBe16Byte"
+ciphertext = aes.encrypt(plaintext)
+
+# 'L6\x95\x85\xe4\xd9\xf1\x8a\xfb\xe5\x94X\x80|\x19\xc3'
+print repr(ciphertext)
+
+# Since there is no state stored in this mode of operation, it
+# is not necessary to create a new aes object for decryption.
+#aes = pyaes.AESModeOfOperationECB(key)
+decrypted = aes.decrypt(ciphertext)
+
+# True
+print decrypted == plaintext
+```
+
+
+### BlockFeeder
+
+Since most of the modes of operations require data in specific block-sized or segment-sized blocks, it can be difficult when working with large arbitrary streams or strings of data.
+
+The BlockFeeder class is meant to make life easier for you, by buffering bytes across multiple calls and returning bytes as they are available, as well as padding or stripping the output when finished, if necessary.
+
+```python
+import pyaes
+
+# Any mode of operation can be used; for this example CBC
+key = "This_key_for_demo_purposes_only!"
+iv = "InitializationVe"
+
+ciphertext = ''
+
+# We can encrypt one line at a time, regardles of length
+encrypter = pyaes.Encrypter(pyaes.AESModeOfOperationCBC(key, iv))
+for line in file('/etc/passwd'):
+    ciphertext += encrypter.feed(line)
+
+# Make a final call to flush any remaining bytes and add paddin
+ciphertext += encrypter.feed()
+
+# We can decrypt the cipher text in chunks (here we split it in half)
+decrypter = pyaes.Decrypter(pyaes.AESModeOfOperationCBC(key, iv))
+decrypted = decrypter.feed(ciphertext[:len(ciphertext) / 2])
+decrypted += decrypter.feed(ciphertext[len(ciphertext) / 2:])
+
+# Again, make a final call to flush any remaining bytes and strip padding
+decrypted += decrypter.feed()
+
+print file('/etc/passwd').read() == decrypted
+```
+
+### Stream Feeder
+
+This is meant to make it even easier to encrypt and decrypt streams and large files.
+
+```python
+import pyaes
+
+# Any mode of operation can be used; for this example CTR
+key = "This_key_for_demo_purposes_only!"
+
+# Create the mode of operation to encrypt with
+mode = pyaes.AESModeOfOperationCTR(key)
+
+# The input and output files
+file_in = file('/etc/passwd')
+file_out = file('/tmp/encrypted.bin', 'wb')
+
+# Encrypt the data as a stream, the file is read in 8kb chunks, be default
+pyaes.encrypt_stream(mode, file_in, file_out)
+
+# Close the files
+file_in.close()
+file_out.close()
+```
+
+Decrypting is identical, except you would use `pyaes.decrypt_stream`, and the encrypted file would be the `file_in` and target for decryption the `file_out`.
+
+### AES block cipher
+
+Generally you should use one of the modes of operation above. This may however be useful for experimenting with a custom mode of operation or dealing with encrypted blocks.
+
+The block cipher requires exactly one block of data to encrypt or decrypt, and each block should be an array with each element an integer representation of a byte.
+
+```python
+import pyaes
+
+# 16 byte block of plain text
+plaintext = "Hello World!!!!!"
+plaintext_bytes = [ ord(c) for c in plaintext ]
+
+# 32 byte key (256 bit)
+key = "This_key_for_demo_purposes_only!"
+
+# Our AES instance
+aes = pyaes.AES(key)
+
+# Encrypt!
+ciphertext = aes.encrypt(plaintext_bytes)
+
+# [55, 250, 182, 25, 185, 208, 186, 95, 206, 115, 50, 115, 108, 58, 174, 115]
+print repr(ciphertext)
+
+# Decrypt!
+decrypted = aes.decrypt(ciphertext)
+
+# True
+print decrypted == plaintext_bytes
+```
+
+What is a key?
+--------------
+
+This seems to be a point of confusion for many people new to using encryption. You can think of the key as the *"password"*. However, these algorithms require the *"password"* to be a specific length.
+
+With AES, there are three possible key lengths, 16-bytes, 24-bytes or 32-bytes. When you create an AES object, the key size is automatically detected, so it is important to pass in a key of the correct length.
+
+Often, you wish to provide a password of arbitrary length, for example, something easy to remember or write down. In these cases, you must come up with a way to transform the password into a key, of a specific length. A **Password-Based Key Derivation Function** (PBKDF) is an algorithm designed for this exact purpose.
+
+Here is an example, using the popular (possibly obsolete?) *crypt* PBKDF:
+
+```
+# See: https://www.dlitz.net/software/python-pbkdf2/
+import pbkdf2
+
+password = "HelloWorld"
+
+# The crypt PBKDF returns a 48-byte string
+key = pbkdf2.crypt(password)
+
+# A 16-byte, 24-byte and 32-byte key, respectively
+key_16 = key[:16]
+key_24 = key[:24]
+key_32 = key[:32]
+```
+
+The [scrypt](https://github.com/ricmoo/pyscrypt) PBKDF is intentionally slow, to make it more difficult to brute-force guess a password:
+
+```
+# See: https://github.com/ricmoo/pyscrypt
+import pyscrypt
+
+password = "HelloWorld"
+
+# Salt is required, and prevents Rainbow Table attacks
+salt = "SeaSalt"
+
+# N, r, and p are parameters to specify how difficult it should be to
+# generate a key; bigger numbers take longer and more memory
+N = 1024
+r = 1
+p = 1
+
+# A 16-byte, 24-byte and 32-byte key, respectively; the scrypt algorithm takes
+# a 6-th parameter, indicating key length
+key_16 = pyscrypt.hash(password, salt, N, r, p, 16)
+key_24 = pyscrypt.hash(password, salt, N, r, p, 24)
+key_32 = pyscrypt.hash(password, salt, N, r, p, 32)
+```
+
+Another possibility, is to use a hashing function, such as SHA256 to hash the password, but this method may be vulnerable to [Rainbow Attacks](http://en.wikipedia.org/wiki/Rainbow_table), unless you use a [salt](http://en.wikipedia.org/wiki/Salt_(cryptography)).
+
+```python
+import hashlib
+
+password = "HelloWorld"
+
+# The SHA256 hash algorithm returns a 32-byte string
+hashed = hashlib.sha256(password).digest()
+
+# A 16-byte, 24-byte and 32-byte key, respectively
+key_16 = hashed[:16]
+key_24 = hashed[:24]
+key_32 = hashed
+```
+
+
+
+
+Performance
+-----------
+
+There is a test case provided in _/tests/test-aes.py_ which does some basic performance testing (its primary purpose is moreso as a regression test).
+
+Based on that test, in **CPython**, this library is about 30x slower than [PyCrypto](https://www.dlitz.net/software/pycrypto/) for CBC, ECB and OFB; about 80x slower for CFB; and 300x slower for CTR.
+
+Based on that same test, in **Pypy**, this library is about 4x slower than [PyCrypto](https://www.dlitz.net/software/pycrypto/) for CBC, ECB and OFB; about 12x slower for CFB; and 19x slower for CTR.
+
+The PyCrypto documentation makes reference to the counter call being responsible for the speed problems of the counter (CTR) mode of operation, which is why they use a specially optimized counter. I will investigate this problem further in the future.
+
+
+FAQ
+---
+
+#### Why do this?
+
+The short answer, *why not?*
+
+The longer answer, is for my [pyscrypt](https://github.com/ricmoo/pyscrypt) library. I required a pure-Python AES implementation that supported 256-bit keys with the counter (CTR) mode of operation. After searching, I found several implementations, but all were missing CTR or only supported 128 bit keys. After all the work of learning AES inside and out to implement the library, it was only a marginal amount of extra work to library-ify a more general solution. So, *why not?*
+
+#### How do I get a question I have added?
+
+E-mail me at pyaes@ricmoo.com with any questions, suggestions, comments, et cetera.
+
+
+#### Can I give you my money?
+
+Umm... Ok? :-)
+
+_Bitcoin_  - `18UDs4qV1shu2CgTS2tKojhCtM69kpnWg9`

+ 53 - 0
ambari-common/src/main/python/ambari_pyaes/__init__.py

@@ -0,0 +1,53 @@
+# The MIT License (MIT)
+#
+# Copyright (c) 2014 Richard Moore
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+
+# This is a pure-Python implementation of the AES algorithm and AES common
+# modes of operation.
+
+# See: https://en.wikipedia.org/wiki/Advanced_Encryption_Standard
+# See: https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation
+
+
+# Supported key sizes:
+#   128-bit
+#   192-bit
+#   256-bit
+
+
+# Supported modes of operation:
+#   ECB - Electronic Codebook
+#   CBC - Cipher-Block Chaining
+#   CFB - Cipher Feedback
+#   OFB - Output Feedback
+#   CTR - Counter
+
+# See the README.md for API details and general information.
+
+# Also useful, PyCrypto, a crypto library implemented in C with Python bindings:
+# https://www.dlitz.net/software/pycrypto/
+
+
+VERSION = [1, 3, 0]
+
+from .aes import AES, AESModeOfOperationCTR, AESModeOfOperationCBC, AESModeOfOperationCFB, AESModeOfOperationECB, AESModeOfOperationOFB, AESModesOfOperation, Counter
+from .blockfeeder import decrypt_stream, Decrypter, encrypt_stream, Encrypter
+from .blockfeeder import PADDING_NONE, PADDING_DEFAULT

Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 113 - 0
ambari-common/src/main/python/ambari_pyaes/aes.py


+ 227 - 0
ambari-common/src/main/python/ambari_pyaes/blockfeeder.py

@@ -0,0 +1,227 @@
+# The MIT License (MIT)
+#
+# Copyright (c) 2014 Richard Moore
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+
+
+from .aes import AESBlockModeOfOperation, AESSegmentModeOfOperation, AESStreamModeOfOperation
+from .util import append_PKCS7_padding, strip_PKCS7_padding, to_bufferable
+
+
+# First we inject three functions to each of the modes of operations
+#
+#    _can_consume(size)
+#       - Given a size, determine how many bytes could be consumed in
+#         a single call to either the decrypt or encrypt method
+#
+#    _final_encrypt(data, padding = PADDING_DEFAULT)
+#       - call and return encrypt on this (last) chunk of data,
+#         padding as necessary; this will always be at least 16
+#         bytes unless the total incoming input was less than 16
+#         bytes
+#
+#    _final_decrypt(data, padding = PADDING_DEFAULT)
+#       - same as _final_encrypt except for decrypt, for
+#         stripping off padding
+#
+
+PADDING_NONE       = 'none'
+PADDING_DEFAULT    = 'default'
+
+# @TODO: Ciphertext stealing and explicit PKCS#7
+# PADDING_CIPHERTEXT_STEALING
+# PADDING_PKCS7
+
+# ECB and CBC are block-only ciphers
+
+def _block_can_consume(self, size):
+    if size >= 16: return 16
+    return 0
+
+# After padding, we may have more than one block
+def _block_final_encrypt(self, data, padding = PADDING_DEFAULT):
+    if padding == PADDING_DEFAULT:
+        data = append_PKCS7_padding(data)
+
+    elif padding == PADDING_NONE:
+        if len(data) != 16:
+            raise Exception('invalid data length for final block')
+    else:
+        raise Exception('invalid padding option')
+
+    if len(data) == 32:
+        return self.encrypt(data[:16]) + self.encrypt(data[16:])
+
+    return self.encrypt(data)
+
+
+def _block_final_decrypt(self, data, padding = PADDING_DEFAULT):
+    if padding == PADDING_DEFAULT:
+        return strip_PKCS7_padding(self.decrypt(data))
+
+    if padding == PADDING_NONE:
+        if len(data) != 16:
+            raise Exception('invalid data length for final block')
+        return self.decrypt(data)
+
+    raise Exception('invalid padding option')
+
+AESBlockModeOfOperation._can_consume = _block_can_consume
+AESBlockModeOfOperation._final_encrypt = _block_final_encrypt
+AESBlockModeOfOperation._final_decrypt = _block_final_decrypt
+
+
+
+# CFB is a segment cipher
+
+def _segment_can_consume(self, size):
+    return self.segment_bytes * int(size // self.segment_bytes)
+
+# CFB can handle a non-segment-sized block at the end using the remaining cipherblock
+def _segment_final_encrypt(self, data, padding = PADDING_DEFAULT):
+    if padding != PADDING_DEFAULT:
+        raise Exception('invalid padding option')
+
+    faux_padding = (chr(0) * (self.segment_bytes - (len(data) % self.segment_bytes)))
+    padded = data + to_bufferable(faux_padding)
+    return self.encrypt(padded)[:len(data)]
+
+# CFB can handle a non-segment-sized block at the end using the remaining cipherblock
+def _segment_final_decrypt(self, data, padding = PADDING_DEFAULT):
+    if padding != PADDING_DEFAULT:
+        raise Exception('invalid padding option')
+
+    faux_padding = (chr(0) * (self.segment_bytes - (len(data) % self.segment_bytes)))
+    padded = data + to_bufferable(faux_padding)
+    return self.decrypt(padded)[:len(data)]
+
+AESSegmentModeOfOperation._can_consume = _segment_can_consume
+AESSegmentModeOfOperation._final_encrypt = _segment_final_encrypt
+AESSegmentModeOfOperation._final_decrypt = _segment_final_decrypt
+
+
+
+# OFB and CTR are stream ciphers
+
+def _stream_can_consume(self, size):
+    return size
+
+def _stream_final_encrypt(self, data, padding = PADDING_DEFAULT):
+    if padding not in [PADDING_NONE, PADDING_DEFAULT]:
+        raise Exception('invalid padding option')
+
+    return self.encrypt(data)
+
+def _stream_final_decrypt(self, data, padding = PADDING_DEFAULT):
+    if padding not in [PADDING_NONE, PADDING_DEFAULT]:
+        raise Exception('invalid padding option')
+
+    return self.decrypt(data)
+
+AESStreamModeOfOperation._can_consume = _stream_can_consume
+AESStreamModeOfOperation._final_encrypt = _stream_final_encrypt
+AESStreamModeOfOperation._final_decrypt = _stream_final_decrypt
+
+
+
+class BlockFeeder(object):
+    '''The super-class for objects to handle chunking a stream of bytes
+       into the appropriate block size for the underlying mode of operation
+       and applying (or stripping) padding, as necessary.'''
+
+    def __init__(self, mode, feed, final, padding = PADDING_DEFAULT):
+        self._mode = mode
+        self._feed = feed
+        self._final = final
+        self._buffer = to_bufferable("")
+        self._padding = padding
+
+    def feed(self, data = None):
+        '''Provide bytes to encrypt (or decrypt), returning any bytes
+           possible from this or any previous calls to feed.
+
+           Call with None or an empty string to flush the mode of
+           operation and return any final bytes; no further calls to
+           feed may be made.'''
+
+        if self._buffer is None:
+            raise ValueError('already finished feeder')
+
+        # Finalize; process the spare bytes we were keeping
+        if not data:
+            result = self._final(self._buffer, self._padding)
+            self._buffer = None
+            return result
+
+        self._buffer += to_bufferable(data)
+
+        # We keep 16 bytes around so we can determine padding
+        result = to_bufferable('')
+        while len(self._buffer) > 16:
+            can_consume = self._mode._can_consume(len(self._buffer) - 16)
+            if can_consume == 0: break
+            result += self._feed(self._buffer[:can_consume])
+            self._buffer = self._buffer[can_consume:]
+
+        return result
+
+
+class Encrypter(BlockFeeder):
+    'Accepts bytes of plaintext and returns encrypted ciphertext.'
+
+    def __init__(self, mode, padding = PADDING_DEFAULT):
+        BlockFeeder.__init__(self, mode, mode.encrypt, mode._final_encrypt, padding)
+
+
+class Decrypter(BlockFeeder):
+    'Accepts bytes of ciphertext and returns decrypted plaintext.'
+
+    def __init__(self, mode, padding = PADDING_DEFAULT):
+        BlockFeeder.__init__(self, mode, mode.decrypt, mode._final_decrypt, padding)
+
+
+# 8kb blocks
+BLOCK_SIZE = (1 << 13)
+
+def _feed_stream(feeder, in_stream, out_stream, block_size = BLOCK_SIZE):
+    'Uses feeder to read and convert from in_stream and write to out_stream.'
+
+    while True:
+        chunk = in_stream.read(block_size)
+        if not chunk:
+            break
+        converted = feeder.feed(chunk)
+        out_stream.write(converted)
+    converted = feeder.feed()
+    out_stream.write(converted)
+
+
+def encrypt_stream(mode, in_stream, out_stream, block_size = BLOCK_SIZE, padding = PADDING_DEFAULT):
+    'Encrypts a stream of bytes from in_stream to out_stream using mode.'
+
+    encrypter = Encrypter(mode, padding = padding)
+    _feed_stream(encrypter, in_stream, out_stream, block_size)
+
+
+def decrypt_stream(mode, in_stream, out_stream, block_size = BLOCK_SIZE, padding = PADDING_DEFAULT):
+    'Decrypts a stream of bytes from in_stream to out_stream using mode.'
+
+    decrypter = Decrypter(mode, padding = padding)
+    _feed_stream(decrypter, in_stream, out_stream, block_size)

+ 60 - 0
ambari-common/src/main/python/ambari_pyaes/util.py

@@ -0,0 +1,60 @@
+# The MIT License (MIT)
+#
+# Copyright (c) 2014 Richard Moore
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+
+# Why to_bufferable?
+# Python 3 is very different from Python 2.x when it comes to strings of text
+# and strings of bytes; in Python 3, strings of bytes do not exist, instead to
+# represent arbitrary binary data, we must use the "bytes" object. This method
+# ensures the object behaves as we need it to.
+
+def to_bufferable(binary):
+    return binary
+
+def _get_byte(c):
+    return ord(c)
+
+try:
+    xrange
+except:
+
+    def to_bufferable(binary):
+        if isinstance(binary, bytes):
+            return binary
+        return bytes(ord(b) for b in binary)
+
+    def _get_byte(c):
+        return c
+
+def append_PKCS7_padding(data):
+    pad = 16 - (len(data) % 16)
+    return data + to_bufferable(chr(pad) * pad)
+
+def strip_PKCS7_padding(data):
+    if len(data) % 16 != 0:
+        raise ValueError("invalid length")
+
+    pad = _get_byte(data[-1])
+
+    if pad > 16:
+        raise ValueError("invalid padding byte")
+
+    return data[:-pad]

+ 47 - 0
ambari-common/src/main/python/resource_management/core/encryption.py

@@ -0,0 +1,47 @@
+#!/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.
+
+Ambari Agent
+
+"""
+import os
+import ambari_pyaes
+from ambari_pbkdf2.pbkdf2 import PBKDF2
+
+def ensure_decrypted(value, encryption_key=None):
+  if is_encrypted(value):
+    return decrypt(encrypted_value(value), agent_encryption_key() if encryption_key is None else encryption_key)
+  else:
+    return value
+
+def decrypt(encrypted_value, encryption_key):
+  salt, iv, data = [each.decode('hex') for each in encrypted_value.decode('hex').split('::')]
+  key = PBKDF2(encryption_key, salt, iterations=65536).read(16)
+  aes = ambari_pyaes.AESModeOfOperationCBC(key, iv=iv)
+  return ambari_pyaes.util.strip_PKCS7_padding(aes.decrypt(data))
+
+def is_encrypted(value):
+  return isinstance(value, basestring) and value.startswith('${enc=aes256_hex, value=') # XXX: ideally it shouldn't be hardcoded but currently only one enc type is supported
+
+def encrypted_value(value):
+  return value.split('value=')[1][:-1]
+
+def agent_encryption_key():
+  if 'AGENT_ENCRYPTION_KEY' not in os.environ:
+    raise RuntimeError('Missing encryption key: AGENT_ENCRYPTION_KEY is not defined at environment.')
+  return os.environ['AGENT_ENCRYPTION_KEY']

+ 1 - 1
ambari-common/src/main/python/resource_management/core/utils.py

@@ -139,7 +139,7 @@ class PasswordString(unicode):
     self.value = value
     
   def __str__(self):
-    return value
+    return self.value
   
   def __repr__(self):
     return PASSWORDS_HIDE_STRING

+ 5 - 3
ambari-common/src/main/python/resource_management/libraries/script/config_dictionary.py

@@ -18,6 +18,7 @@ See the License for the specific language governing permissions and
 limitations under the License.
 '''
 from resource_management.core.exceptions import Fail
+from resource_management.core.encryption import ensure_decrypted
 
 IMMUTABLE_MESSAGE = """Configuration dictionary is immutable!
 
@@ -52,8 +53,9 @@ class ConfigDictionary(dict):
       value = super(ConfigDictionary, self).__getitem__(name)
     except KeyError:
       return UnknownConfiguration(name)
-      
-    
+
+    value = ensure_decrypted(value)
+
     if value == "true":
       value = True
     elif value == "false":
@@ -76,4 +78,4 @@ class UnknownConfiguration():
     """
     Allow [] 
     """
-    return self
+    return self

+ 2 - 0
ambari-server/pom.xml

@@ -34,6 +34,8 @@
     <resource_management.install.dir>/usr/lib/ambari-server/lib/resource_management</resource_management.install.dir>
     <jinja.install.dir>/usr/lib/ambari-server/lib/ambari_jinja2</jinja.install.dir>
     <simplejson.install.dir>/usr/lib/ambari-server/lib/ambari_simplejson</simplejson.install.dir>
+    <pbkdf2.install.dir>/usr/lib/ambari-server/lib/ambari_pbkdf2</pbkdf2.install.dir>
+    <pyaes.install.dir>/usr/lib/ambari-server/lib/ambari_pyaes</pyaes.install.dir>
     <swagger.spec.dir>${basedir}/docs/api/generated/</swagger.spec.dir>
     <swagger.generated.resources.dir>${project.build.directory}/generated-sources/swagger/</swagger.generated.resources.dir>
     <ambari-web-dir>${basedir}/../ambari-web/public</ambari-web-dir>

+ 8 - 0
ambari-server/src/main/assemblies/server.xml

@@ -62,6 +62,14 @@
       <directory>${project.basedir}/../ambari-common/src/main/python/ambari_simplejson</directory>
       <outputDirectory>${simplejson.install.dir}</outputDirectory>
     </fileSet>
+    <fileSet>
+      <directory>${project.basedir}/../ambari-common/src/main/python/ambari_pbkdf2</directory>
+      <outputDirectory>${pbkdf2.install.dir}</outputDirectory>
+    </fileSet>
+    <fileSet>
+      <directory>${project.basedir}/../ambari-common/src/main/python/ambari_pyaes</directory>
+      <outputDirectory>${pyaes.install.dir}</outputDirectory>
+    </fileSet>
     <fileSet>
       <fileMode>700</fileMode>
       <directory>src/main/resources/db</directory>

+ 88 - 0
ambari-server/src/main/java/org/apache/ambari/server/agent/AgentEncryptionKey.java

@@ -0,0 +1,88 @@
+/*
+ *
+ *  * 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.
+ *
+ */
+
+package org.apache.ambari.server.agent;
+
+import java.util.Arrays;
+
+import org.apache.ambari.server.AmbariException;
+import org.apache.ambari.server.AmbariRuntimeException;
+import org.apache.ambari.server.security.SecurePasswordHelper;
+import org.apache.ambari.server.security.credential.Credential;
+import org.apache.ambari.server.security.encryption.CredentialStoreService;
+import org.apache.ambari.server.security.encryption.CredentialStoreType;
+
+/**
+ * The encryption key which is used to encrypt sensitive data in agent config updates.
+ * This key is shared with the agent so that it can decrypt the encrypted values.
+ */
+public class AgentEncryptionKey implements Credential {
+  private static final String ALIAS = "agent.encryption.key";
+  private static final SecurePasswordHelper securePasswordHelper = new SecurePasswordHelper();
+  private final char[] key;
+
+  public static AgentEncryptionKey random() {
+    return new AgentEncryptionKey(securePasswordHelper.createSecurePassword().toCharArray());
+  }
+
+  public static AgentEncryptionKey loadFrom(CredentialStoreService credentialStoreService, boolean createIfNotFound) {
+    try {
+      if (!credentialStoreService.containsCredential(null, AgentEncryptionKey.ALIAS)) {
+        if (createIfNotFound) {
+          AgentEncryptionKey encryptionKey = AgentEncryptionKey.random();
+          encryptionKey.saveToCredentialStore(credentialStoreService);
+          return loadKey(credentialStoreService); // load it again because after saving the key is cleared
+        } else {
+          throw new AmbariRuntimeException("AgentEncryptionKey with alias: " + ALIAS + " doesn't exist in credential store.");
+        }
+      } else {
+        return loadKey(credentialStoreService);
+      }
+    } catch (AmbariException e) {
+      throw new AmbariRuntimeException("Cannot load agent encryption key: " + e.getMessage(), e);
+    }
+  }
+
+  private static AgentEncryptionKey loadKey(CredentialStoreService credentialStoreService) throws AmbariException {
+    return new AgentEncryptionKey(credentialStoreService.getCredential(null, ALIAS).toValue());
+  }
+
+  public AgentEncryptionKey(char[] key) {
+    this.key = Arrays.copyOf(key, key.length);
+  }
+
+  @Override
+  public String toString() {
+    return new String(key);
+  }
+
+  @Override
+  public char[] toValue() {
+    return key;
+  }
+
+  public void saveToCredentialStore(CredentialStoreService credentialStoreService) {
+    try {
+      credentialStoreService.setCredential(null, ALIAS, this, CredentialStoreType.PERSISTED);
+    } catch (AmbariException e) {
+      throw new AmbariRuntimeException("Cannot save agent encryption key: " + e.getMessage(), e);
+    }
+  }
+}

+ 16 - 17
ambari-server/src/main/java/org/apache/ambari/server/agent/HeartBeatHandler.java

@@ -23,6 +23,8 @@ import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.regex.Pattern;
 
+import javax.inject.Named;
+
 import org.apache.ambari.server.AmbariException;
 import org.apache.ambari.server.HostNotFoundException;
 import org.apache.ambari.server.actionmanager.ActionManager;
@@ -31,20 +33,21 @@ import org.apache.ambari.server.agent.stomp.dto.HostStatusReport;
 import org.apache.ambari.server.api.services.AmbariMetaInfo;
 import org.apache.ambari.server.configuration.Configuration;
 import org.apache.ambari.server.events.AgentActionEvent;
+import org.apache.ambari.server.events.AgentConfigsUpdateEvent;
+import org.apache.ambari.server.events.EncryptionKeyUpdateEvent;
 import org.apache.ambari.server.events.HostRegisteredEvent;
 import org.apache.ambari.server.events.publishers.AmbariEventPublisher;
 import org.apache.ambari.server.events.publishers.STOMPUpdatePublisher;
+import org.apache.ambari.server.security.encryption.Encryptor;
 import org.apache.ambari.server.state.AgentVersion;
 import org.apache.ambari.server.state.Cluster;
 import org.apache.ambari.server.state.Clusters;
 import org.apache.ambari.server.state.ComponentInfo;
-import org.apache.ambari.server.state.ConfigHelper;
 import org.apache.ambari.server.state.Host;
 import org.apache.ambari.server.state.HostState;
 import org.apache.ambari.server.state.ServiceComponent;
 import org.apache.ambari.server.state.ServiceComponentHost;
 import org.apache.ambari.server.state.StackId;
-import org.apache.ambari.server.state.alert.AlertDefinitionHash;
 import org.apache.ambari.server.state.alert.AlertHelper;
 import org.apache.ambari.server.state.fsm.InvalidStateTransitionException;
 import org.apache.ambari.server.state.host.HostHealthyHeartbeatEvent;
@@ -73,7 +76,7 @@ public class HeartBeatHandler {
 
   private static final Pattern DOT_PATTERN = Pattern.compile("\\.");
   private final Clusters clusterFsm;
-  private final ActionManager actionManager;
+  private final Encryptor<AgentConfigsUpdateEvent> encryptor;
   private HeartbeatMonitor heartbeatMonitor;
   private HeartbeatProcessor heartbeatProcessor;
 
@@ -83,15 +86,6 @@ public class HeartBeatHandler {
   @Inject
   private AmbariMetaInfo ambariMetaInfo;
 
-  @Inject
-  private ConfigHelper configHelper;
-
-  @Inject
-  private AlertDefinitionHash alertDefinitionHash;
-
-  @Inject
-  private RecoveryConfigHelper recoveryConfigHelper;
-
   @Inject
   private STOMPUpdatePublisher STOMPUpdatePublisher;
 
@@ -109,12 +103,12 @@ public class HeartBeatHandler {
   private Map<String, HeartBeatResponse> hostResponses = new ConcurrentHashMap<>();
 
   @Inject
-  public HeartBeatHandler(Clusters fsm, ActionManager am,
+  public HeartBeatHandler(Clusters fsm, ActionManager am, @Named("AgentConfigEncryptor") Encryptor<AgentConfigsUpdateEvent> encryptor,
                           Injector injector) {
-    clusterFsm = fsm;
-    actionManager = am;
-    heartbeatMonitor = new HeartbeatMonitor(fsm, am, 60000, injector);
-    heartbeatProcessor = new HeartbeatProcessor(fsm, am, heartbeatMonitor, injector); //TODO modify to match pattern
+    this.clusterFsm = fsm;
+    this.encryptor = encryptor;
+    this.heartbeatMonitor = new HeartbeatMonitor(fsm, am, 60000, injector);
+    this.heartbeatProcessor = new HeartbeatProcessor(fsm, am, heartbeatMonitor, injector); //TODO modify to match pattern
     injector.injectMembers(this);
   }
 
@@ -361,6 +355,11 @@ public class HeartBeatHandler {
     HostRegisteredEvent event = new HostRegisteredEvent(hostname, hostObject.getHostId());
     ambariEventPublisher.publish(event);
 
+    if (config.shouldEncryptSensitiveData()) {
+      EncryptionKeyUpdateEvent encryptionKeyUpdateEvent = new EncryptionKeyUpdateEvent(encryptor.getEncryptionKey());
+      STOMPUpdatePublisher.publish(encryptionKeyUpdateEvent);
+    }
+
     RegistrationResponse response = new RegistrationResponse();
 
     Long requestId = 0L;

+ 9 - 3
ambari-server/src/main/java/org/apache/ambari/server/agent/stomp/AgentConfigsHolder.java

@@ -16,12 +16,14 @@
  * limitations under the License.
  */
 package org.apache.ambari.server.agent.stomp;
+
 import java.util.List;
 import java.util.stream.Collectors;
 
 import org.apache.ambari.server.AmbariException;
 import org.apache.ambari.server.events.AgentConfigsUpdateEvent;
 import org.apache.ambari.server.events.publishers.AmbariEventPublisher;
+import org.apache.ambari.server.security.encryption.Encryptor;
 import org.apache.ambari.server.state.Clusters;
 import org.apache.ambari.server.state.ConfigHelper;
 import org.apache.ambari.server.state.Host;
@@ -33,10 +35,12 @@ import org.slf4j.LoggerFactory;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
+import com.google.inject.name.Named;
 
 @Singleton
 public class AgentConfigsHolder extends AgentHostDataHolder<AgentConfigsUpdateEvent> {
   public static final Logger LOG = LoggerFactory.getLogger(AgentConfigsHolder.class);
+  private final Encryptor<AgentConfigsUpdateEvent> encryptor;
 
   @Inject
   private ConfigHelper configHelper;
@@ -45,7 +49,8 @@ public class AgentConfigsHolder extends AgentHostDataHolder<AgentConfigsUpdateEv
   private Provider<Clusters> clusters;
 
   @Inject
-  public AgentConfigsHolder(AmbariEventPublisher ambariEventPublisher) {
+  public AgentConfigsHolder(AmbariEventPublisher ambariEventPublisher, @Named("AgentConfigEncryptor") Encryptor<AgentConfigsUpdateEvent> encryptor) {
+    this.encryptor = encryptor;
     ambariEventPublisher.register(this);
   }
 
@@ -59,7 +64,7 @@ public class AgentConfigsHolder extends AgentHostDataHolder<AgentConfigsUpdateEv
   }
 
   @Override
-  protected AgentConfigsUpdateEvent handleUpdate(AgentConfigsUpdateEvent current, AgentConfigsUpdateEvent update) throws AmbariException {
+  protected AgentConfigsUpdateEvent handleUpdate(AgentConfigsUpdateEvent current, AgentConfigsUpdateEvent update) {
     return update;
   }
 
@@ -90,7 +95,8 @@ public class AgentConfigsHolder extends AgentHostDataHolder<AgentConfigsUpdateEv
 
   @Override
   protected void regenerateDataIdentifiers(AgentConfigsUpdateEvent data) {
-    data.setHash(getHash(data));
+    data.setHash(getHash(data, encryptor.getEncryptionKey()));
+    encryptor.encryptSensitiveData(data);
     data.setTimestamp(System.currentTimeMillis());
   }
 

+ 4 - 1
ambari-server/src/main/java/org/apache/ambari/server/agent/stomp/AgentDataHolder.java

@@ -37,7 +37,6 @@ import com.fasterxml.jackson.databind.ObjectMapper;
  * @param <T> event with hash to control version
  */
 public abstract class AgentDataHolder<T extends Hashable> {
-  private final String salt = "";
   protected final ReentrantLock updateLock = new ReentrantLock();
   private final static ObjectMapper MAPPER = new ObjectMapper();
   static {
@@ -56,6 +55,10 @@ public abstract class AgentDataHolder<T extends Hashable> {
   }
 
   protected String getHash(T data) {
+    return getHash(data, "");
+  }
+
+  protected String getHash(T data, String salt) {
     String json = null;
     try {
       json = MAPPER.writeValueAsString(data);

+ 9 - 1
ambari-server/src/main/java/org/apache/ambari/server/controller/ControllerModule.java

@@ -85,6 +85,7 @@ import org.apache.ambari.server.controller.metrics.timeline.cache.TimelineMetric
 import org.apache.ambari.server.controller.metrics.timeline.cache.TimelineMetricCacheProvider;
 import org.apache.ambari.server.controller.spi.ResourceProvider;
 import org.apache.ambari.server.controller.utilities.KerberosChecker;
+import org.apache.ambari.server.events.AgentConfigsUpdateEvent;
 import org.apache.ambari.server.events.AmbariEvent;
 import org.apache.ambari.server.hooks.AmbariEventFactory;
 import org.apache.ambari.server.hooks.HookContext;
@@ -115,6 +116,7 @@ import org.apache.ambari.server.security.authorization.AuthorizationHelper;
 import org.apache.ambari.server.security.authorization.internal.InternalAuthenticationInterceptor;
 import org.apache.ambari.server.security.authorization.internal.RunWithInternalSecurityContext;
 import org.apache.ambari.server.security.encryption.AESEncryptionService;
+import org.apache.ambari.server.security.encryption.AgentConfigUpdateEncryptor;
 import org.apache.ambari.server.security.encryption.ConfigPropertiesEncryptor;
 import org.apache.ambari.server.security.encryption.CredentialStoreService;
 import org.apache.ambari.server.security.encryption.CredentialStoreServiceImpl;
@@ -336,7 +338,13 @@ public class ControllerModule extends AbstractModule {
     bind(CredentialStoreService.class).to(CredentialStoreServiceImpl.class);
     bind(EncryptionService.class).to(AESEncryptionService.class);
     //to support different Encryptor implementation we have to annotate them by their name and use them as @Named injects
-    bind(new TypeLiteral<Encryptor<Config>>() {}).annotatedWith(Names.named("ConfigPropertiesEncryptor")).to(ConfigPropertiesEncryptor.class);
+    if (configuration.shouldEncryptSensitiveData()) {
+      bind(new TypeLiteral<Encryptor<Config>>() {}).annotatedWith(Names.named("ConfigPropertiesEncryptor")).to(ConfigPropertiesEncryptor.class);
+      bind(new TypeLiteral<Encryptor<AgentConfigsUpdateEvent>>() {}).annotatedWith(Names.named("AgentConfigEncryptor")).to(AgentConfigUpdateEncryptor.class);
+    } else {
+      bind(new TypeLiteral<Encryptor<Config>>() {}).annotatedWith(Names.named("ConfigPropertiesEncryptor")).toInstance(Encryptor.NONE);
+      bind(new TypeLiteral<Encryptor<AgentConfigsUpdateEvent>>() {}).annotatedWith(Names.named("AgentConfigEncryptor")).toInstance(Encryptor.NONE);
+    }
 
     bind(Configuration.class).toInstance(configuration);
     bind(OsFamily.class).toInstance(os_family);

+ 3 - 1
ambari-server/src/main/java/org/apache/ambari/server/events/DefaultMessageEmitter.java

@@ -51,6 +51,7 @@ public class DefaultMessageEmitter extends MessageEmitter {
         put(STOMPEvent.Type.UI_ALERT_DEFINITIONS, "/events/alert_definitions");
         put(STOMPEvent.Type.UPGRADE, "/events/upgrade");
         put(STOMPEvent.Type.AGENT_ACTIONS, "/agent_actions");
+        put(STOMPEvent.Type.ENCRYPTION_KEY_UPDATE, "/events/encryption_key");
   }});
   public static final Set<STOMPEvent.Type> DEFAULT_AGENT_EVENT_TYPES =
       Collections.unmodifiableSet(new HashSet<STOMPEvent.Type>(Arrays.asList(
@@ -60,7 +61,8 @@ public class DefaultMessageEmitter extends MessageEmitter {
         STOMPEvent.Type.AGENT_CONFIGS,
         STOMPEvent.Type.COMMAND,
         STOMPEvent.Type.ALERT_DEFINITIONS,
-        STOMPEvent.Type.AGENT_ACTIONS
+        STOMPEvent.Type.AGENT_ACTIONS,
+        STOMPEvent.Type.ENCRYPTION_KEY_UPDATE
   )));
   public static final Set<STOMPEvent.Type> DEFAULT_API_EVENT_TYPES =
       Collections.unmodifiableSet(new HashSet<STOMPEvent.Type>(Arrays.asList(

+ 52 - 0
ambari-server/src/main/java/org/apache/ambari/server/events/EncryptionKeyUpdateEvent.java

@@ -0,0 +1,52 @@
+/*
+ *
+ *  * 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.
+ *
+ */
+
+package org.apache.ambari.server.events;
+
+import java.util.Objects;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+
+@JsonInclude(JsonInclude.Include.NON_NULL)
+public class EncryptionKeyUpdateEvent extends STOMPEvent {
+  private final String encryptionKey;
+
+  public EncryptionKeyUpdateEvent(String encryptionKey) {
+    super(Type.ENCRYPTION_KEY_UPDATE);
+    this.encryptionKey = encryptionKey;
+  }
+
+  public String getEncryptionKey() {
+    return encryptionKey;
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+    EncryptionKeyUpdateEvent that = (EncryptionKeyUpdateEvent) o;
+    return Objects.equals(encryptionKey, that.encryptionKey);
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(encryptionKey);
+  }
+}

+ 2 - 1
ambari-server/src/main/java/org/apache/ambari/server/events/STOMPEvent.java

@@ -61,7 +61,8 @@ public abstract class STOMPEvent {
     ALERT_DEFINITIONS("alert_definitions"),
     UPGRADE("events.upgrade"),
     COMMAND("events.commands"),
-    AGENT_ACTIONS("events.agentactions");
+    AGENT_ACTIONS("events.agentactions"),
+    ENCRYPTION_KEY_UPDATE("events.encryption_key_update");
 
     /**
      * Is used to collect info about event appearing frequency.

+ 33 - 19
ambari-server/src/main/java/org/apache/ambari/server/security/encryption/AESEncryptionService.java

@@ -17,6 +17,8 @@
  */
 package org.apache.ambari.server.security.encryption;
 
+import java.io.IOException;
+import java.io.UncheckedIOException;
 import java.io.UnsupportedEncodingException;
 import java.nio.charset.StandardCharsets;
 
@@ -24,6 +26,7 @@ import javax.inject.Inject;
 
 import org.apache.ambari.server.configuration.Configuration;
 import org.apache.ambari.server.utils.TextEncoding;
+import org.apache.commons.codec.DecoderException;
 import org.apache.commons.codec.binary.Base64;
 import org.apache.commons.codec.binary.Hex;
 
@@ -45,24 +48,28 @@ public class AESEncryptionService implements EncryptionService {
   private Configuration configuration;
 
   @Override
-  public String encrypt(String toBeEncrypted) throws Exception {
+  public String encrypt(String toBeEncrypted) {
     return encrypt(toBeEncrypted, TextEncoding.BASE_64);
   }
 
   @Override
-  public String encrypt(String toBeEncrypted, TextEncoding textEncoding) throws Exception {
+  public String encrypt(String toBeEncrypted, TextEncoding textEncoding) {
     return encrypt(toBeEncrypted, getAmbariMasterKey(), textEncoding);
   }
 
   @Override
-  public String encrypt(String toBeEncrypted, String key) throws Exception {
+  public String encrypt(String toBeEncrypted, String key) {
     return encrypt(toBeEncrypted, key, TextEncoding.BASE_64);
   }
 
   @Override
-  public String encrypt(String toBeEncrypted, String key, TextEncoding textEncoding) throws Exception {
-    final EncryptionResult encryptionResult = getAesEncryptor(key).encrypt(toBeEncrypted);
-    return TextEncoding.BASE_64 == textEncoding ? encodeEncryptionResultBase64(encryptionResult) : encodeEncryptionResultBinHex(encryptionResult);
+  public String encrypt(String toBeEncrypted, String key, TextEncoding textEncoding) {
+    try {
+      final EncryptionResult encryptionResult = getAesEncryptor(key).encrypt(toBeEncrypted);
+      return TextEncoding.BASE_64 == textEncoding ? encodeEncryptionResultBase64(encryptionResult) : encodeEncryptionResultBinHex(encryptionResult);
+    } catch (IOException e) {
+      throw new UncheckedIOException(e);
+    }
   }
 
   private AESEncryptor getAesEncryptor(String key) {
@@ -75,7 +82,8 @@ public class AESEncryptionService implements EncryptionService {
     return aesEncryptor;
   }
 
-  private final String getAmbariMasterKey() {
+  @Override
+  public final String getAmbariMasterKey() {
     initEnvironmentMasterKeyService();
     return String.valueOf(environmentMasterKeyService.getMasterSecret());
   }
@@ -100,32 +108,38 @@ public class AESEncryptionService implements EncryptionService {
   }
 
   @Override
-  public String decrypt(String toBeDecrypted) throws Exception {
+  public String decrypt(String toBeDecrypted) {
     return decrypt(toBeDecrypted, TextEncoding.BASE_64);
   }
 
   @Override
-  public String decrypt(String toBeDecrypted, TextEncoding textEncoding) throws Exception {
+  public String decrypt(String toBeDecrypted, TextEncoding textEncoding) {
     return decrypt(toBeDecrypted, getAmbariMasterKey(), textEncoding);
   }
 
   @Override
-  public String decrypt(String toBeDecrypted, String key) throws Exception {
+  public String decrypt(String toBeDecrypted, String key) {
     return decrypt(toBeDecrypted, key, TextEncoding.BASE_64);
   }
 
   @Override
-  public String decrypt(String toBeDecrypted, String key, TextEncoding textEncoding) throws Exception {
-    final byte[] decodedValue = TextEncoding.BASE_64 == textEncoding ? Base64.decodeBase64(toBeDecrypted) : Hex.decodeHex(toBeDecrypted.toCharArray());
-    final String decodedText = new String(decodedValue, UTF_8_CHARSET);
-    final String[] decodedParts = decodedText.split(ENCODED_TEXT_FIELD_DELIMITER);
-    final AESEncryptor aes = getAesEncryptor(key);
-    if (TextEncoding.BASE_64 == textEncoding) {
-      return new String(aes.decrypt(Base64.decodeBase64(decodedParts[0]), Base64.decodeBase64(decodedParts[1]), Base64.decodeBase64(decodedParts[2])), UTF_8_CHARSET);
-    } else {
-      return new String(
+  public String decrypt(String toBeDecrypted, String key, TextEncoding textEncoding) {
+    try {
+      final byte[] decodedValue = TextEncoding.BASE_64 == textEncoding ? Base64.decodeBase64(toBeDecrypted) : Hex.decodeHex(toBeDecrypted.toCharArray());
+      final String decodedText = new String(decodedValue, UTF_8_CHARSET);
+      final String[] decodedParts = decodedText.split(ENCODED_TEXT_FIELD_DELIMITER);
+      final AESEncryptor aes = getAesEncryptor(key);
+      if (TextEncoding.BASE_64 == textEncoding) {
+        return new String(aes.decrypt(Base64.decodeBase64(decodedParts[0]), Base64.decodeBase64(decodedParts[1]), Base64.decodeBase64(decodedParts[2])), UTF_8_CHARSET);
+      } else {
+        return new String(
           aes.decrypt(Hex.decodeHex(decodedParts[0].toCharArray()), Hex.decodeHex(decodedParts[1].toCharArray()), Hex.decodeHex(decodedParts[2].toCharArray())),
           UTF_8_CHARSET);
+      }
+    } catch (IOException e) {
+      throw new UncheckedIOException(e);
+    } catch (DecoderException e) {
+      throw new RuntimeException(e);
     }
   }
 }

+ 26 - 37
ambari-server/src/main/java/org/apache/ambari/server/security/encryption/AESEncryptor.java

@@ -17,6 +17,9 @@
  */
 package org.apache.ambari.server.security.encryption;
 
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.security.GeneralSecurityException;
 import java.security.InvalidAlgorithmParameterException;
 import java.security.InvalidKeyException;
 import java.security.NoSuchAlgorithmException;
@@ -63,21 +66,6 @@ public class AESEncryptor {
     }
   }
 
-  AESEncryptor(SecretKey secret) {
-    try {
-      this.secret = new SecretKeySpec (secret.getEncoded(), "AES");
-
-      ecipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
-      ecipher.init(Cipher.ENCRYPT_MODE, secret);
-
-      dcipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
-      byte[] iv = ecipher.getParameters().getParameterSpec(IvParameterSpec.class).getIV();
-      dcipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(iv));
-    } catch (NoSuchAlgorithmException | InvalidAlgorithmParameterException | InvalidParameterSpecException | InvalidKeyException | NoSuchPaddingException e) {
-      e.printStackTrace();
-    }
-  }
-
   public SecretKey getKeyFromPassword(String passPhrase) {
     return getKeyFromPassword(passPhrase, salt);
   }
@@ -96,32 +84,33 @@ public class AESEncryptor {
     return key;
   }
 
-  public EncryptionResult encrypt(String encrypt) throws Exception {
-    byte[] bytes = encrypt.getBytes("UTF8");
-    EncryptionResult atom = encrypt(bytes);
-    return atom;
-  }
-
-  public EncryptionResult encrypt(byte[] plain) throws Exception {
-    EncryptionResult atom = new EncryptionResult(salt, ecipher.getParameters().getParameterSpec(IvParameterSpec.class).getIV(), ecipher.doFinal(plain));
-    return atom;
+  public EncryptionResult encrypt(String encrypt) {
+    try {
+      byte[] bytes = encrypt.getBytes("UTF8");
+      EncryptionResult atom = encrypt(bytes);
+      return atom;
+    } catch (IOException e) {
+      throw new UncheckedIOException(e);
+    }
   }
 
-  public String decrypt(String salt, String iv, String cipher) throws Exception {
-    byte[] decrypted = decrypt(salt.getBytes("UTF8"), iv.getBytes("UTF8"), cipher.getBytes("UTF8"));
-    return new String(decrypted, "UTF8");
+  public EncryptionResult encrypt(byte[] plain) {
+    try {
+      return new EncryptionResult(salt, ecipher.getParameters().getParameterSpec(IvParameterSpec.class).getIV(), ecipher.doFinal(plain));
+    } catch (GeneralSecurityException e) {
+      throw new RuntimeException(e);
+    }
   }
 
-  public byte[] decrypt(byte[] salt, byte[] iv, byte[] encrypt) throws Exception {
-    SecretKey tmp = getKeyFromPassword(new String(passPhrase), salt);
-    secret = new SecretKeySpec(tmp.getEncoded(), "AES");
-
-    dcipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(iv));
-    return dcipher.doFinal(encrypt);
-  }
+  public byte[] decrypt(byte[] salt, byte[] iv, byte[] encrypt) {
+    try {
+      SecretKey tmp = getKeyFromPassword(new String(passPhrase), salt);
+      secret = new SecretKeySpec(tmp.getEncoded(), "AES");
 
-  public byte[] decrypt(byte[] encrypt) throws Exception {
-    dcipher.init(Cipher.DECRYPT_MODE, secret);
-    return dcipher.doFinal(encrypt);
+      dcipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(iv));
+      return dcipher.doFinal(encrypt);
+    } catch (GeneralSecurityException e) {
+      throw new RuntimeException(e);
+    }
   }
 }

+ 84 - 0
ambari-server/src/main/java/org/apache/ambari/server/security/encryption/AgentConfigUpdateEncryptor.java

@@ -0,0 +1,84 @@
+/*
+ * 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
+ * <p/>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p/>
+ * 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.
+ */
+
+package org.apache.ambari.server.security.encryption;
+
+import java.util.Map;
+import java.util.SortedMap;
+
+import org.apache.ambari.server.AmbariException;
+import org.apache.ambari.server.AmbariRuntimeException;
+import org.apache.ambari.server.agent.AgentEncryptionKey;
+import org.apache.ambari.server.agent.stomp.dto.ClusterConfigs;
+import org.apache.ambari.server.events.AgentConfigsUpdateEvent;
+import org.apache.ambari.server.state.Cluster;
+import org.apache.ambari.server.state.Clusters;
+
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import com.google.inject.Singleton;
+
+/**
+ * This encryptor encrypts the sensitive data in config updates sent to ambari-agent.
+ * Decryption happens on the agent side.
+ */
+@Singleton
+public class AgentConfigUpdateEncryptor extends PropertiesEncryptor implements Encryptor<AgentConfigsUpdateEvent> {
+  private final AgentEncryptionKey encryptionKey;
+  private final Provider<Clusters> clusters;
+
+  @Inject
+  public AgentConfigUpdateEncryptor(EncryptionService encryptionService, CredentialStoreService credentialStore, Provider<Clusters> clusters) {
+    super(encryptionService);
+    this.encryptionKey = AgentEncryptionKey.loadFrom(credentialStore, true);
+    this.clusters = clusters;
+  }
+
+  @Override
+  public void encryptSensitiveData(AgentConfigsUpdateEvent event) {
+    for (Map.Entry<String, ClusterConfigs> each : event.getClustersConfigs().entrySet()) {
+      Cluster cluster = getCluster(Long.parseLong(each.getKey()));
+      ClusterConfigs clusterConfigs = each.getValue();
+      for (Map.Entry<String, SortedMap<String, String>> clusterConfig : clusterConfigs.getConfigurations().entrySet()) {
+        encrypt(
+          clusterConfig.getValue(),
+          cluster,
+          clusterConfig.getKey(),
+          encryptionKey.toString());
+      }
+    }
+  }
+
+  @Override
+  public void decryptSensitiveData(AgentConfigsUpdateEvent event) {
+    throw new UnsupportedOperationException("Not supported"); // Decryption happens on the agent side, this is not needed right now
+  }
+
+  private Cluster getCluster(long clusterId) throws AmbariRuntimeException {
+    try {
+      return clusters.get().getCluster(clusterId);
+    } catch (AmbariException e) {
+      throw new AmbariRuntimeException("Cannot load cluster: " + clusterId, e);
+    }
+  }
+
+  @Override
+  public String getEncryptionKey() {
+    return encryptionKey.toString();
+  }
+}

+ 11 - 80
ambari-server/src/main/java/org/apache/ambari/server/security/encryption/ConfigPropertiesEncryptor.java

@@ -17,20 +17,9 @@
  */
 package org.apache.ambari.server.security.encryption;
 
-import java.util.HashMap;
-import java.util.HashSet;
 import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
 
-import org.apache.ambari.server.AmbariException;
-import org.apache.ambari.server.AmbariRuntimeException;
-import org.apache.ambari.server.state.Cluster;
 import org.apache.ambari.server.state.Config;
-import org.apache.ambari.server.state.PropertyInfo.PropertyType;
-import org.apache.ambari.server.state.StackId;
-import org.apache.ambari.server.utils.TextEncoding;
-import org.apache.commons.collections.CollectionUtils;
 
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
@@ -41,87 +30,29 @@ import com.google.inject.Singleton;
  */
 
 @Singleton
-public class ConfigPropertiesEncryptor implements Encryptor<Config> {
-
-  private static final String ENCRYPTED_PROPERTY_PREFIX = "${enc=aes256_hex, value=";
-  private static final String ENCRYPTED_PROPERTY_SCHEME = ENCRYPTED_PROPERTY_PREFIX + "%s}";
-
-  private final EncryptionService encryptionService;
-  private final Map<Long, Map<StackId, Map<String, Set<String>>>> clusterPasswordProperties = new ConcurrentHashMap<>(); //Map<clusterId, <Map<stackId, Map<configType, Set<passwordPropertyKeys>>>>;
+public class ConfigPropertiesEncryptor extends PropertiesEncryptor implements Encryptor<Config> {
 
   @Inject
   public ConfigPropertiesEncryptor(EncryptionService encryptionService) {
-    this.encryptionService = encryptionService;
+    super(encryptionService);
   }
 
   @Override
   public void encryptSensitiveData(Config config) {
-    try {
-      final Map<String, String> configProperties = config.getProperties();
-      if (configProperties != null) {
-        final Set<String> passwordProperties = getPasswordProperties(config.getCluster(), config.getType());
-        if (CollectionUtils.isNotEmpty(passwordProperties)) {
-          final Map<String, String> encryptedProperties = new HashMap<>(configProperties);
-          for (Map.Entry<String, String> property : configProperties.entrySet()) {
-            if (passwordProperties.contains(property.getKey()) && !isEncryptedPassword(property.getValue())) {
-              encryptedProperties.put(property.getKey(), encryptAndDecoratePropertyValue(property.getValue()));
-            }
-          }
-          config.setProperties(encryptedProperties);
-        }
-      }
-    } catch (Exception e) {
-      throw new AmbariRuntimeException("Error while encrypting sensitive data", e);
-    }
-  }
-
-  private boolean isEncryptedPassword(String password) {
-    return password != null && password.startsWith(ENCRYPTED_PROPERTY_PREFIX); // assuming previous encryption by this class
-  }
-
-  private Set<String> getPasswordProperties(Cluster cluster, String configType) throws AmbariException {
-    //in case of normal configuration change on the UI - or via the API - the current and desired stacks are equal
-    //in case of an upgrade they are different; in this case we want to get password properties from the desired stack
-    if (cluster.getCurrentStackVersion().equals(cluster.getDesiredStackVersion())) {
-      return getPasswordProperties(cluster, cluster.getCurrentStackVersion(), configType);
-    } else {
-      return getPasswordProperties(cluster, cluster.getDesiredStackVersion(), configType);
-    }
-  }
-
-  private Set<String> getPasswordProperties(Cluster cluster, StackId stackId, String configType) {
-    final long clusterId = cluster.getClusterId();
-    clusterPasswordProperties.computeIfAbsent(clusterId, v -> new ConcurrentHashMap<>()).computeIfAbsent(stackId, v -> new ConcurrentHashMap<>())
-        .computeIfAbsent(configType, v -> cluster.getConfigPropertiesTypes(configType, stackId).getOrDefault(PropertyType.PASSWORD, new HashSet<>()));
-    return clusterPasswordProperties.get(clusterId).get(stackId).getOrDefault(configType, new HashSet<>());
-  }
-
-  private String encryptAndDecoratePropertyValue(String propertyValue) throws Exception {
-    final String encrypted = encryptionService.encrypt(propertyValue, TextEncoding.BIN_HEX);
-    return String.format(ENCRYPTED_PROPERTY_SCHEME, encrypted);
+    Map<String, String> properties = config.getProperties();
+    encrypt(properties, config.getCluster(), config.getType());
+    config.setProperties(properties);
   }
 
   @Override
   public void decryptSensitiveData(Config config) {
-    final Map<String, String> configProperties = config.getProperties();
-    if (configProperties != null) {
-      final Map<String, String> decryptedProperties = new HashMap<>(configProperties);
-      for (Map.Entry<String, String> property : configProperties.entrySet()) {
-        if (isEncryptedPassword(property.getValue())) {
-          decryptedProperties.put(property.getKey(), decryptProperty(property.getValue()));
-        }
-      }
-      config.setProperties(decryptedProperties);
-    }
+    Map<String, String> properties = config.getProperties();
+    decrypt(properties);
+    config.setProperties(properties);
   }
 
-  private String decryptProperty(String property) {
-    try {
-      // sample value: ${enc=aes256_hex, value=5248...303d}
-      final String encrypted = property.substring(ENCRYPTED_PROPERTY_PREFIX.length(), property.indexOf('}'));
-      return encryptionService.decrypt(encrypted, TextEncoding.BIN_HEX);
-    } catch (Exception e) {
-      throw new AmbariRuntimeException("Error while decrypting property", e);
-    }
+  @Override
+  public String getEncryptionKey() {
+    return encryptionService.getAmbariMasterKey();
   }
 }

+ 13 - 21
ambari-server/src/main/java/org/apache/ambari/server/security/encryption/EncryptionService.java

@@ -48,7 +48,7 @@ public interface EncryptionService {
    * @throws Exception
    *           in case any error happened during the encryption process
    */
-  String encrypt(String toBeEncrypted) throws Exception;
+  String encrypt(String toBeEncrypted);
 
   /**
    * Encrypts the given text using Ambari's master key found in the environment.
@@ -59,10 +59,8 @@ public interface EncryptionService {
    * @param textEncoding
    *          the text encoding which the encrypted text is encoded with
    * @return the String representation of the encrypted text
-   * @throws Exception
-   *           in case any error happened during the encryption process
    */
-  String encrypt(String toBeEncrypted, TextEncoding textEncoding) throws Exception;
+  String encrypt(String toBeEncrypted, TextEncoding textEncoding);
 
   /**
    * Encrypts the given text using the given key. The returned value will be
@@ -73,10 +71,9 @@ public interface EncryptionService {
    * @param key
    *          the key to be used for encryption
    * @return the String representation of the encrypted text
-   * @throws Exception
    *           in case any error happened during the encryption process
    */
-  String encrypt(String toBeEncrypted, String key) throws Exception;
+  String encrypt(String toBeEncrypted, String key);
 
   /**
    * Encrypts the given text using the given key.The returned value will be
@@ -89,10 +86,13 @@ public interface EncryptionService {
    * @param textEncoding
    *          the text encoding which the encrypted text is encoded with
    * @return the String representation of the encrypted text
-   * @throws Exception
-   *           in case any error happened during the encryption process
    */
-  String encrypt(String toBeEncrypted, String key, TextEncoding textEncoding) throws Exception;
+  String encrypt(String toBeEncrypted, String key, TextEncoding textEncoding);
+
+  /**
+   * @return the default encryption key used by this encryption service
+   */
+  String getAmbariMasterKey();
 
   /**
    * Decrypts the given text (must be encoded with BASE_64 encoding) using
@@ -101,10 +101,8 @@ public interface EncryptionService {
    * @param toBeDecrypted
    *          the text to be decrypted
    * @return the String representation of the decrypted text
-   * @throws Exception
-   *           in case any error happened during the decryption process
    */
-  public String decrypt(String toBeDecrypted) throws Exception;
+  String decrypt(String toBeDecrypted);
 
   /**
    * Decrypts the given text (must be encoded with the given text encoding) using
@@ -115,10 +113,8 @@ public interface EncryptionService {
    * @param textEncoding
    *          the text encoding which <code>toBeDecrypted</code> is encoded with
    * @return the String representation of the decrypted text
-   * @throws Exception
-   *           in case any error happened during the decryption process
    */
-  public String decrypt(String toBeDecrypted, TextEncoding textEncoding) throws Exception;
+  String decrypt(String toBeDecrypted, TextEncoding textEncoding);
 
   /**
    * Decrypts the given text (must be encoded with BASE_64 encoding) using the
@@ -129,10 +125,8 @@ public interface EncryptionService {
    * @param key
    *          the key to be used for decryption
    * @return the String representation of the decrypted text
-   * @throws Exception
-   *           in case any error happened during the decryption process
    */
-  public String decrypt(String toBeDecrypted, String key) throws Exception;
+  String decrypt(String toBeDecrypted, String key);
 
   /**
    * Decrypts the given text (must be encoded with the given text encoding) using
@@ -145,9 +139,7 @@ public interface EncryptionService {
    * @param textEncoding
    *          the text encoding which <code>toBeDecrypted</code> is encoded with
    * @return the String representation of the decrypted text
-   * @throws Exception
-   *           in case any error happened during the decryption process
    */
-  public String decrypt(String toBeDecrypted, String key, TextEncoding textEncoding) throws Exception;
+  String decrypt(String toBeDecrypted, String key, TextEncoding textEncoding);
 
 }

+ 13 - 2
ambari-server/src/main/java/org/apache/ambari/server/security/encryption/Encryptor.java

@@ -27,7 +27,6 @@ public interface Encryptor<T> {
    * 
    * @param encryptible
    *          to be encrypted
-   * @return the encrypted value
    */
   void encryptSensitiveData(T encryptible);
 
@@ -36,8 +35,20 @@ public interface Encryptor<T> {
    * 
    * @param decryptible
    *          to be decrypted
-   * @return the decrypted value
    */
   void decryptSensitiveData(T decryptible);
 
+  /**
+   * @return the default encryption key used by this encryptor
+   */
+  String getEncryptionKey();
+
+  Encryptor NONE = new Encryptor() {
+    @Override
+    public void encryptSensitiveData(Object data) { }
+    @Override
+    public void decryptSensitiveData(Object decryptible) { }
+    @Override
+    public String getEncryptionKey() { return ""; }
+  };
 }

+ 115 - 0
ambari-server/src/main/java/org/apache/ambari/server/security/encryption/PropertiesEncryptor.java

@@ -0,0 +1,115 @@
+/*
+ *
+ *  * 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.
+ *
+ */
+
+package org.apache.ambari.server.security.encryption;
+
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.Function;
+
+import org.apache.ambari.server.state.Cluster;
+import org.apache.ambari.server.state.PropertyInfo;
+import org.apache.ambari.server.state.StackId;
+import org.apache.ambari.server.utils.TextEncoding;
+import org.apache.commons.collections.CollectionUtils;
+
+/**
+ * A common base class for various encryptor implementations
+ */
+public class PropertiesEncryptor {
+  private static final String ENCRYPTED_PROPERTY_PREFIX = "${enc=aes256_hex, value=";
+  private static final String ENCRYPTED_PROPERTY_SCHEME = ENCRYPTED_PROPERTY_PREFIX + "%s}";
+  private final Map<Long, Map<StackId, Map<String, Set<String>>>> clusterPasswordProperties = new ConcurrentHashMap<>(); //Map<clusterId, <Map<stackId, Map<configType, Set<passwordPropertyKeys>>>>;
+  protected final EncryptionService encryptionService;
+
+  public PropertiesEncryptor(EncryptionService encryptionService) {
+    this.encryptionService = encryptionService;
+  }
+
+  protected void encrypt(Map<String, String> configProperties, Cluster cluster, String configType, String encryptionKey) {
+    encrypt(configProperties, cluster, configType, value -> encryptAndDecoratePropertyValue(value, encryptionKey));
+  }
+
+  protected void encrypt(Map<String, String> configProperties, Cluster cluster, String configType) {
+    encrypt(configProperties, cluster, configType, value -> encryptAndDecoratePropertyValue(value));
+  }
+
+  protected void encrypt(Map<String, String> configProperties, Cluster cluster, String configType, Function<String,String> encryption) {
+    final Set<String> passwordProperties = getPasswordProperties(cluster, configType);
+    if (CollectionUtils.isNotEmpty(passwordProperties)) {
+      for (Map.Entry<String, String> property : configProperties.entrySet()) {
+        if (shouldEncrypt(property, passwordProperties)) {
+          configProperties.put(property.getKey(), encryption.apply(property.getValue()));
+        }
+      }
+    }
+  }
+
+  private boolean shouldEncrypt(Map.Entry<String, String> property, Set<String> passwordProperties) {
+    return passwordProperties.contains(property.getKey()) && !isEncryptedPassword(property.getValue());
+  }
+
+  private boolean isEncryptedPassword(String password) {
+    return password != null && password.startsWith(ENCRYPTED_PROPERTY_PREFIX); // assuming previous encryption by this class
+  }
+
+  private Set<String> getPasswordProperties(Cluster cluster, String configType) {
+    //in case of normal configuration change on the UI - or via the API - the current and desired stacks are equal
+    //in case of an upgrade they are different; in this case we want to get password properties from the desired stack
+    if (cluster.getCurrentStackVersion().equals(cluster.getDesiredStackVersion())) {
+      return getPasswordProperties(cluster, cluster.getCurrentStackVersion(), configType);
+    } else {
+      return getPasswordProperties(cluster, cluster.getDesiredStackVersion(), configType);
+    }
+  }
+
+  private Set<String> getPasswordProperties(Cluster cluster, StackId stackId, String configType) {
+    final long clusterId = cluster.getClusterId();
+    clusterPasswordProperties.computeIfAbsent(clusterId, v -> new ConcurrentHashMap<>()).computeIfAbsent(stackId, v -> new ConcurrentHashMap<>())
+        .computeIfAbsent(configType, v -> cluster.getConfigPropertiesTypes(configType, stackId).getOrDefault(PropertyInfo.PropertyType.PASSWORD, new HashSet<>()));
+    return clusterPasswordProperties.get(clusterId).get(stackId).getOrDefault(configType, new HashSet<>());
+  }
+
+  private String encryptAndDecoratePropertyValue(String propertyValue) {
+    final String encrypted = encryptionService.encrypt(propertyValue, TextEncoding.BIN_HEX);
+    return String.format(ENCRYPTED_PROPERTY_SCHEME, encrypted);
+  }
+
+  private String encryptAndDecoratePropertyValue(String propertyValue, String encryptionKey) {
+    final String encrypted = encryptionService.encrypt(propertyValue, encryptionKey, TextEncoding.BIN_HEX);
+    return String.format(ENCRYPTED_PROPERTY_SCHEME, encrypted);
+  }
+
+  protected void decrypt(Map<String, String> configProperties) {
+    for (Map.Entry<String, String> property : configProperties.entrySet()) {
+      if (isEncryptedPassword(property.getValue())) {
+        configProperties.put(property.getKey(), decryptProperty(property.getValue()));
+      }
+    }
+  }
+
+  private String decryptProperty(String property) {
+    // sample value: ${enc=aes256_hex, value=5248...303d}
+    final String encrypted = property.substring(ENCRYPTED_PROPERTY_PREFIX.length(), property.indexOf('}'));
+    return encryptionService.decrypt(encrypted, TextEncoding.BIN_HEX);
+  }
+}

+ 15 - 18
ambari-server/src/main/java/org/apache/ambari/server/state/ConfigImpl.java

@@ -27,9 +27,7 @@ import java.util.concurrent.ConcurrentMap;
 import java.util.concurrent.locks.ReadWriteLock;
 
 import javax.annotation.Nullable;
-import javax.inject.Named;
 
-import org.apache.ambari.server.configuration.Configuration;
 import org.apache.ambari.server.events.ClusterConfigChangedEvent;
 import org.apache.ambari.server.events.publishers.AmbariEventPublisher;
 import org.apache.ambari.server.logging.LockFactory;
@@ -50,6 +48,7 @@ import com.google.gson.JsonSyntaxException;
 import com.google.inject.Inject;
 import com.google.inject.assistedinject.Assisted;
 import com.google.inject.assistedinject.AssistedInject;
+import com.google.inject.name.Named;
 import com.google.inject.persist.Transactional;
 
 public class ConfigImpl implements Config {
@@ -104,34 +103,32 @@ public class ConfigImpl implements Config {
 
   @AssistedInject
   ConfigImpl(@Assisted Cluster cluster, @Assisted("type") String type,
-      @Assisted("tag") @Nullable String tag,
-      @Assisted Map<String, String> properties,
-      @Assisted @Nullable Map<String, Map<String, String>> propertiesAttributes,
-      ClusterDAO clusterDAO, StackDAO stackDAO,
-      Gson gson, AmbariEventPublisher eventPublisher, LockFactory lockFactory,
-      Configuration serverConfiguration, @Named("ConfigPropertiesEncryptor") Encryptor<Config> configPropertiesEncryptor) {
+             @Assisted("tag") @Nullable String tag,
+             @Assisted Map<String, String> properties,
+             @Assisted @Nullable Map<String, Map<String, String>> propertiesAttributes,
+             ClusterDAO clusterDAO, StackDAO stackDAO,
+             Gson gson, AmbariEventPublisher eventPublisher, LockFactory lockFactory,
+             @Named("ConfigPropertiesEncryptor") Encryptor<Config> configPropertiesEncryptor) {
     this(cluster.getDesiredStackVersion(), cluster, type, tag, properties, propertiesAttributes,
-        clusterDAO, stackDAO, gson, eventPublisher, lockFactory, serverConfiguration, configPropertiesEncryptor);
+        clusterDAO, stackDAO, gson, eventPublisher, lockFactory, configPropertiesEncryptor);
   }
 
 
   @AssistedInject
   ConfigImpl(@Assisted @Nullable StackId stackId, @Assisted Cluster cluster, @Assisted("type") String type,
-      @Assisted("tag") @Nullable String tag,
-      @Assisted Map<String, String> properties,
-      @Assisted @Nullable Map<String, Map<String, String>> propertiesAttributes,
-      ClusterDAO clusterDAO, StackDAO stackDAO,
-      Gson gson, AmbariEventPublisher eventPublisher, LockFactory lockFactory,
-      Configuration serverConfiguration, @Named("ConfigPropertiesEncryptor") Encryptor<Config> configPropertiesEncryptor) {
+             @Assisted("tag") @Nullable String tag,
+             @Assisted Map<String, String> properties,
+             @Assisted @Nullable Map<String, Map<String, String>> propertiesAttributes,
+             ClusterDAO clusterDAO, StackDAO stackDAO,
+             Gson gson, AmbariEventPublisher eventPublisher, LockFactory lockFactory,
+             @Named("ConfigPropertiesEncryptor") Encryptor<Config> configPropertiesEncryptor) {
 
     propertyLock = lockFactory.newReadWriteLock(PROPERTY_LOCK_LABEL);
 
     this.cluster = cluster;
     this.type = type;
     this.properties = properties;
-    if (serverConfiguration.shouldEncryptSensitiveData()) {
-      configPropertiesEncryptor.encryptSensitiveData(this);
-    }
+    configPropertiesEncryptor.encryptSensitiveData(this);
 
     // only set this if it's non-null
     this.propertiesAttributes = null == propertiesAttributes ? null

+ 3 - 2
ambari-server/src/test/java/org/apache/ambari/server/agent/AgentResourceTest.java

@@ -40,6 +40,7 @@ import org.apache.ambari.server.controller.AbstractRootServiceResponseFactory;
 import org.apache.ambari.server.controller.AmbariManagementController;
 import org.apache.ambari.server.controller.KerberosHelper;
 import org.apache.ambari.server.controller.RootServiceResponseFactory;
+import org.apache.ambari.server.events.AgentConfigsUpdateEvent;
 import org.apache.ambari.server.events.AmbariEvent;
 import org.apache.ambari.server.events.publishers.AmbariEventPublisher;
 import org.apache.ambari.server.hooks.AmbariEventFactory;
@@ -60,7 +61,6 @@ import org.apache.ambari.server.scheduler.ExecutionSchedulerImpl;
 import org.apache.ambari.server.security.SecurityHelper;
 import org.apache.ambari.server.security.SecurityHelperImpl;
 import org.apache.ambari.server.security.encryption.AESEncryptionService;
-import org.apache.ambari.server.security.encryption.ConfigPropertiesEncryptor;
 import org.apache.ambari.server.security.encryption.CredentialStoreService;
 import org.apache.ambari.server.security.encryption.CredentialStoreServiceImpl;
 import org.apache.ambari.server.security.encryption.EncryptionService;
@@ -358,7 +358,8 @@ public class AgentResourceTest extends RandomPortJerseyTest {
       bind(KerberosHelper.class).toInstance(createNiceMock(KerberosHelper.class));
       bind(MpackManagerFactory.class).toInstance(createNiceMock(MpackManagerFactory.class));
       bind(EncryptionService.class).to(AESEncryptionService.class);
-      bind(new TypeLiteral<Encryptor<Config>>() {}).annotatedWith(Names.named("ConfigPropertiesEncryptor")).to(ConfigPropertiesEncryptor.class);
+      bind(new TypeLiteral<Encryptor<AgentConfigsUpdateEvent>>() {}).annotatedWith(Names.named("AgentConfigEncryptor")).toInstance(Encryptor.NONE);
+      bind(new TypeLiteral<Encryptor<Config>>() {}).annotatedWith(Names.named("ConfigPropertiesEncryptor")).toInstance(Encryptor.NONE);
     }
 
     private void installDependencies() {

+ 2 - 1
ambari-server/src/test/java/org/apache/ambari/server/agent/HeartbeatProcessorTest.java

@@ -68,6 +68,7 @@ import org.apache.ambari.server.orm.dao.HostDAO;
 import org.apache.ambari.server.orm.dao.RepositoryVersionDAO;
 import org.apache.ambari.server.orm.entities.HostEntity;
 import org.apache.ambari.server.orm.entities.RepositoryVersionEntity;
+import org.apache.ambari.server.security.encryption.Encryptor;
 import org.apache.ambari.server.state.Alert;
 import org.apache.ambari.server.state.AlertState;
 import org.apache.ambari.server.state.Cluster;
@@ -1046,7 +1047,7 @@ public class HeartbeatProcessorTest {
     hostObject.setIPv6("ipv6");
     hostObject.setOsType(DummyOsType);
 
-    HeartBeatHandler handler = new HeartBeatHandler(fsm, am, injector);
+    HeartBeatHandler handler = new HeartBeatHandler(fsm, am, Encryptor.NONE, injector);
     Register reg = new Register();
     HostInfo hi = new HostInfo();
     hi.setHostName(DummyHostname1);

+ 2 - 1
ambari-server/src/test/java/org/apache/ambari/server/agent/HeartbeatTestHelper.java

@@ -57,6 +57,7 @@ import org.apache.ambari.server.orm.entities.ResourceEntity;
 import org.apache.ambari.server.orm.entities.ResourceTypeEntity;
 import org.apache.ambari.server.orm.entities.StackEntity;
 import org.apache.ambari.server.security.authorization.ResourceType;
+import org.apache.ambari.server.security.encryption.Encryptor;
 import org.apache.ambari.server.state.Cluster;
 import org.apache.ambari.server.state.Clusters;
 import org.apache.ambari.server.state.Config;
@@ -126,7 +127,7 @@ public class HeartbeatTestHelper {
 
   public HeartBeatHandler getHeartBeatHandler(ActionManager am)
       throws InvalidStateTransitionException, AmbariException {
-    HeartBeatHandler handler = new HeartBeatHandler(clusters, am, injector);
+    HeartBeatHandler handler = new HeartBeatHandler(clusters, am, Encryptor.NONE, injector);
     Register reg = new Register();
     HostInfo hi = new HostInfo();
     hi.setHostName(DummyHostname1);

+ 18 - 35
ambari-server/src/test/java/org/apache/ambari/server/agent/TestHeartbeatHandler.java

@@ -86,6 +86,7 @@ import org.apache.ambari.server.orm.GuiceJpaInitializer;
 import org.apache.ambari.server.orm.InMemoryDefaultTestModule;
 import org.apache.ambari.server.orm.OrmTestHelper;
 import org.apache.ambari.server.orm.entities.RepositoryVersionEntity;
+import org.apache.ambari.server.security.encryption.Encryptor;
 import org.apache.ambari.server.serveraction.kerberos.KerberosIdentityDataFileWriter;
 import org.apache.ambari.server.serveraction.kerberos.KerberosServerAction;
 import org.apache.ambari.server.serveraction.kerberos.stageutils.KerberosKeytabController;
@@ -203,7 +204,6 @@ public class TestHeartbeatHandler {
     Collection<Host> hosts = cluster.getHosts();
     assertEquals(hosts.size(), 1);
 
-    Clusters fsm = clusters;
     Host hostObject = hosts.iterator().next();
     hostObject.setIPv4("ipv4");
     hostObject.setIPv6("ipv6");
@@ -211,7 +211,7 @@ public class TestHeartbeatHandler {
 
     String hostname = hostObject.getHostName();
 
-    HeartBeatHandler handler = new HeartBeatHandler(fsm, am, injector);
+    HeartBeatHandler handler = createHeartBeatHandler();
     Register reg = new Register();
     HostInfo hi = new HostInfo();
     hi.setHostName(hostname);
@@ -361,9 +361,7 @@ public class TestHeartbeatHandler {
       InvalidStateTransitionException {
     ActionManager am = actionManagerTestHelper.getMockActionManager();
     replay(am);
-    Clusters fsm = clusters;
-    HeartBeatHandler handler = new HeartBeatHandler(fsm, am,
-        injector);
+    HeartBeatHandler handler = createHeartBeatHandler();
     clusters.addHost(DummyHostname1);
     Host hostObject = clusters.getHost(DummyHostname1);
     hostObject.setIPv4("ipv4");
@@ -387,6 +385,10 @@ public class TestHeartbeatHandler {
         hostObject.getLastRegistrationTime());
   }
 
+  private HeartBeatHandler createHeartBeatHandler() {
+    return new HeartBeatHandler(clusters, actionManagerTestHelper.getMockActionManager(), Encryptor.NONE, injector);
+  }
+
   @Test
   @Ignore
   // TODO should be rewritten after STOMP protocol implementation.
@@ -409,9 +411,7 @@ public class TestHeartbeatHandler {
     // timestamp invalidation (RecoveryConfigHelper.handleServiceComponentInstalledEvent())
     ActionManager am = actionManagerTestHelper.getMockActionManager();
     replay(am);
-    Clusters fsm = clusters;
-    HeartBeatHandler handler = new HeartBeatHandler(fsm, am,
-        injector);
+    HeartBeatHandler handler = createHeartBeatHandler();
     handler.start();
 
     Host hostObject = clusters.getHost(DummyHostname1);
@@ -457,9 +457,7 @@ public class TestHeartbeatHandler {
       throws Exception, InvalidStateTransitionException {
     ActionManager am = actionManagerTestHelper.getMockActionManager();
     replay(am);
-    Clusters fsm = clusters;
-    HeartBeatHandler handler = new HeartBeatHandler(fsm, am,
-            injector);
+    HeartBeatHandler handler = createHeartBeatHandler();
     Cluster cluster = heartbeatTestHelper.getDummyCluster();
     Service hdfs = addService(cluster, HDFS);
 
@@ -508,9 +506,7 @@ public class TestHeartbeatHandler {
       InvalidStateTransitionException {
     ActionManager am = actionManagerTestHelper.getMockActionManager();
     replay(am);
-    Clusters fsm = clusters;
-    HeartBeatHandler handler = new HeartBeatHandler(fsm, am,
-                                                    injector);
+    HeartBeatHandler handler = createHeartBeatHandler();
     clusters.addHost(DummyHostname1);
     Host hostObject = clusters.getHost(DummyHostname1);
     hostObject.setIPv4("ipv4");
@@ -541,9 +537,7 @@ public class TestHeartbeatHandler {
 
     ActionManager am = actionManagerTestHelper.getMockActionManager();
     replay(am);
-    Clusters fsm = clusters;
-    HeartBeatHandler handler = new HeartBeatHandler(fsm, am,
-        injector);
+    HeartBeatHandler handler = createHeartBeatHandler();
     clusters.addHost(DummyHostname1);
     Host hostObject = clusters.getHost(DummyHostname1);
     hostObject.setIPv4("ipv4");
@@ -582,9 +576,7 @@ public class TestHeartbeatHandler {
   public void testRegistrationPublicHostname() throws Exception, InvalidStateTransitionException {
     ActionManager am = actionManagerTestHelper.getMockActionManager();
     replay(am);
-    Clusters fsm = clusters;
-    HeartBeatHandler handler = new HeartBeatHandler(fsm, am,
-        injector);
+    HeartBeatHandler handler = createHeartBeatHandler();
     clusters.addHost(DummyHostname1);
     Host hostObject = clusters.getHost(DummyHostname1);
     hostObject.setIPv4("ipv4");
@@ -615,9 +607,7 @@ public class TestHeartbeatHandler {
       InvalidStateTransitionException {
     ActionManager am = actionManagerTestHelper.getMockActionManager();
     replay(am);
-    Clusters fsm = clusters;
-    HeartBeatHandler handler = new HeartBeatHandler(fsm, am,
-        injector);
+    HeartBeatHandler handler = createHeartBeatHandler();
     clusters.addHost(DummyHostname1);
     Host hostObject = clusters.getHost(DummyHostname1);
     hostObject.setIPv4("ipv4");
@@ -644,9 +634,7 @@ public class TestHeartbeatHandler {
 
     ActionManager am = actionManagerTestHelper.getMockActionManager();
     replay(am);
-    Clusters fsm = clusters;
-    HeartBeatHandler handler = new HeartBeatHandler(fsm, am,
-            injector);
+    HeartBeatHandler handler = createHeartBeatHandler();
     clusters.addHost(DummyHostname1);
     Host hostObject = clusters.getHost(DummyHostname1);
     hostObject.setIPv4("ipv4");
@@ -678,8 +666,7 @@ public class TestHeartbeatHandler {
     hostObject.setIPv4("ipv4");
     hostObject.setIPv6("ipv6");
 
-    HeartBeatHandler handler = new HeartBeatHandler(fsm, am,
-        injector);
+    HeartBeatHandler handler = createHeartBeatHandler();
     Register reg = new Register();
     HostInfo hi = new HostInfo();
     hi.setHostName(DummyHostname1);
@@ -765,9 +752,7 @@ public class TestHeartbeatHandler {
 
     ActionManager am = actionManagerTestHelper.getMockActionManager();
     replay(am);
-    Clusters fsm = clusters;
-    HeartBeatHandler handler = new HeartBeatHandler(fsm, am,
-        injector);
+    HeartBeatHandler handler = createHeartBeatHandler();
     handler.setHeartbeatMonitor(hm);
     clusters.addHost(DummyHostname1);
     Host hostObject = clusters.getHost(DummyHostname1);
@@ -969,8 +954,6 @@ public class TestHeartbeatHandler {
 
   @Test
   public void testRecoveryStatusReports() throws Exception {
-    Clusters fsm = clusters;
-
     Cluster cluster = heartbeatTestHelper.getDummyCluster();
     Host hostObject = clusters.getHost(DummyHostname1);
     Service hdfs = addService(cluster, HDFS);
@@ -989,7 +972,7 @@ public class TestHeartbeatHandler {
           add(command);
         }}).anyTimes();
     replay(am);
-    HeartBeatHandler handler = new HeartBeatHandler(fsm, am, injector);
+    HeartBeatHandler handler = createHeartBeatHandler();
 
     Register reg = new Register();
     HostInfo hi = new HostInfo();
@@ -1070,7 +1053,7 @@ public class TestHeartbeatHandler {
               add(command);
             }}).anyTimes();
     replay(am);
-    HeartBeatHandler handler = new HeartBeatHandler(fsm, am, injector);
+    HeartBeatHandler handler = createHeartBeatHandler();
     HeartbeatProcessor heartbeatProcessor = handler.getHeartbeatProcessor();
 
     Register reg = new Register();

+ 7 - 7
ambari-server/src/test/java/org/apache/ambari/server/agent/TestHeartbeatMonitor.java

@@ -41,6 +41,7 @@ import org.apache.ambari.server.orm.GuiceJpaInitializer;
 import org.apache.ambari.server.orm.InMemoryDefaultTestModule;
 import org.apache.ambari.server.orm.OrmTestHelper;
 import org.apache.ambari.server.orm.entities.RepositoryVersionEntity;
+import org.apache.ambari.server.security.encryption.Encryptor;
 import org.apache.ambari.server.state.Cluster;
 import org.apache.ambari.server.state.Clusters;
 import org.apache.ambari.server.state.Config;
@@ -118,7 +119,7 @@ public class TestHeartbeatMonitor {
     fsm.addHost(hostname);
     ActionManager am = mock(ActionManager.class);
     HeartbeatMonitor hm = new HeartbeatMonitor(fsm, am, 10, injector);
-    HeartBeatHandler handler = new HeartBeatHandler(fsm, am, injector);
+    HeartBeatHandler handler = new HeartBeatHandler(fsm, am, Encryptor.NONE, injector);
     Register reg = new Register();
     reg.setHostname(hostname);
     reg.setResponseId(12);
@@ -186,7 +187,7 @@ public class TestHeartbeatMonitor {
     ActionManager am = mock(ActionManager.class);
     HeartbeatMonitor hm = new HeartbeatMonitor(clusters, am,
       heartbeatMonitorWakeupIntervalMS, injector);
-    HeartBeatHandler handler = new HeartBeatHandler(clusters, am, injector);
+    HeartBeatHandler handler = new HeartBeatHandler(clusters, am, Encryptor.NONE, injector);
     Register reg = new Register();
     reg.setHostname(hostname1);
     reg.setResponseId(12);
@@ -305,7 +306,7 @@ public class TestHeartbeatMonitor {
     ActionManager am = mock(ActionManager.class);
     HeartbeatMonitor hm = new HeartbeatMonitor(clusters, am,
       heartbeatMonitorWakeupIntervalMS, injector);
-    HeartBeatHandler handler = new HeartBeatHandler(clusters, am, injector);
+    HeartBeatHandler handler = new HeartBeatHandler(clusters, am, Encryptor.NONE, injector);
     Register reg = new Register();
     reg.setHostname(hostname1);
     reg.setResponseId(12);
@@ -400,8 +401,7 @@ public class TestHeartbeatMonitor {
     ActionManager am = mock(ActionManager.class);
     HeartbeatMonitor hm = new HeartbeatMonitor(clusters, am,
       heartbeatMonitorWakeupIntervalMS, injector);
-    HeartBeatHandler handler = new HeartBeatHandler(clusters, am,
-        injector);
+    HeartBeatHandler handler = new HeartBeatHandler(clusters, am, Encryptor.NONE, injector);
     Register reg = new Register();
     reg.setHostname(hostname1);
     reg.setResponseId(12);
@@ -476,7 +476,7 @@ public class TestHeartbeatMonitor {
 
     ActionManager am = mock(ActionManager.class);
     HeartbeatMonitor hm = new HeartbeatMonitor(clusters, am, 10, injector);
-    HeartBeatHandler handler = new HeartBeatHandler(clusters, am, injector);
+    HeartBeatHandler handler = new HeartBeatHandler(clusters, am, Encryptor.NONE, injector);
 
     Register reg = new Register();
     reg.setHostname(hostname1);
@@ -598,7 +598,7 @@ public class TestHeartbeatMonitor {
     ActionManager am = mock(ActionManager.class);
     HeartbeatMonitor hm = new HeartbeatMonitor(clusters, am,
       heartbeatMonitorWakeupIntervalMS, injector);
-    HeartBeatHandler handler = new HeartBeatHandler(clusters, am, injector);
+    HeartBeatHandler handler = new HeartBeatHandler(clusters, am, Encryptor.NONE, injector);
     Register reg = new Register();
     reg.setHostname(hostname1);
     reg.setResponseId(12);

+ 2 - 1
ambari-server/src/test/java/org/apache/ambari/server/agent/stomp/AgentDataHolderTest.java

@@ -25,6 +25,7 @@ import org.apache.ambari.server.events.AgentConfigsUpdateEvent;
 import org.apache.ambari.server.events.MetadataUpdateEvent;
 import org.apache.ambari.server.events.UpdateEventType;
 import org.apache.ambari.server.events.publishers.AmbariEventPublisher;
+import org.apache.ambari.server.security.encryption.Encryptor;
 import org.apache.commons.collections.MapUtils;
 import org.junit.Test;
 
@@ -33,7 +34,7 @@ public class AgentDataHolderTest {
   @Test
   public void testGetHashWithTimestamp() {
     AmbariEventPublisher ambariEventPublisher = createNiceMock(AmbariEventPublisher.class);
-    AgentConfigsHolder agentConfigsHolder = new AgentConfigsHolder(ambariEventPublisher);
+    AgentConfigsHolder agentConfigsHolder = new AgentConfigsHolder(ambariEventPublisher, Encryptor.NONE);
 
     AgentConfigsUpdateEvent event1 = new AgentConfigsUpdateEvent(null, null);
     event1.setHash("01");

+ 0 - 1
ambari-server/src/test/java/org/apache/ambari/server/controller/KerberosHelperTest.java

@@ -277,7 +277,6 @@ public class KerberosHelperTest extends EasyMockSupport {
         bind(RoleCommandOrderProvider.class).to(CachedRoleCommandOrderProvider.class);
         bind(HostRoleCommandFactory.class).to(HostRoleCommandFactoryImpl.class);
         bind(MpackManagerFactory.class).toInstance(createNiceMock(MpackManagerFactory.class));
-
         requestStaticInjection(KerberosChecker.class);
       }
     });

+ 5 - 0
ambari-server/src/test/java/org/apache/ambari/server/controller/internal/PreUpgradeCheckResourceProviderTest.java

@@ -53,6 +53,7 @@ import org.apache.ambari.server.controller.spi.Resource;
 import org.apache.ambari.server.controller.spi.ResourceProvider;
 import org.apache.ambari.server.controller.utilities.PredicateBuilder;
 import org.apache.ambari.server.controller.utilities.PropertyHelper;
+import org.apache.ambari.server.events.AgentConfigsUpdateEvent;
 import org.apache.ambari.server.hooks.HookContextFactory;
 import org.apache.ambari.server.hooks.HookService;
 import org.apache.ambari.server.metadata.RoleCommandOrderProvider;
@@ -65,6 +66,7 @@ import org.apache.ambari.server.orm.entities.RepositoryVersionEntity;
 import org.apache.ambari.server.sample.checks.SampleServiceCheck;
 import org.apache.ambari.server.scheduler.ExecutionScheduler;
 import org.apache.ambari.server.security.encryption.CredentialStoreService;
+import org.apache.ambari.server.security.encryption.Encryptor;
 import org.apache.ambari.server.serveraction.kerberos.KerberosConfigDataFileWriterFactory;
 import org.apache.ambari.server.serveraction.kerberos.KerberosIdentityDataFileWriterFactory;
 import org.apache.ambari.server.stack.StackManagerFactory;
@@ -109,6 +111,8 @@ import com.google.inject.AbstractModule;
 import com.google.inject.Guice;
 import com.google.inject.Injector;
 import com.google.inject.Provider;
+import com.google.inject.TypeLiteral;
+import com.google.inject.name.Names;
 
 /**
  * PreUpgradeCheckResourceProvider tests.
@@ -322,6 +326,7 @@ public class PreUpgradeCheckResourceProviderTest extends EasyMockSupport {
         bind(MpackManagerFactory.class).toInstance(createNiceMock(MpackManagerFactory.class));
         Provider<EntityManager> entityManagerProvider = createNiceMock(Provider.class);
         bind(EntityManager.class).toProvider(entityManagerProvider);
+        bind(new TypeLiteral<Encryptor<AgentConfigsUpdateEvent>>() {}).annotatedWith(Names.named("AgentConfigEncryptor")).toInstance(Encryptor.NONE);
 
         requestStaticInjection(PreUpgradeCheckResourceProvider.class);
       }

+ 5 - 0
ambari-server/src/test/java/org/apache/ambari/server/controller/internal/UserAuthorizationResourceProviderTest.java

@@ -48,6 +48,7 @@ import org.apache.ambari.server.controller.spi.ResourceProvider;
 import org.apache.ambari.server.controller.spi.SystemException;
 import org.apache.ambari.server.controller.utilities.PredicateBuilder;
 import org.apache.ambari.server.controller.utilities.PropertyHelper;
+import org.apache.ambari.server.events.AgentConfigsUpdateEvent;
 import org.apache.ambari.server.hooks.HookContextFactory;
 import org.apache.ambari.server.hooks.HookService;
 import org.apache.ambari.server.metadata.CachedRoleCommandOrderProvider;
@@ -67,6 +68,7 @@ import org.apache.ambari.server.security.authorization.AuthorizationException;
 import org.apache.ambari.server.security.authorization.Users;
 import org.apache.ambari.server.security.encryption.CredentialStoreService;
 import org.apache.ambari.server.security.encryption.CredentialStoreServiceImpl;
+import org.apache.ambari.server.security.encryption.Encryptor;
 import org.apache.ambari.server.stack.StackManagerFactory;
 import org.apache.ambari.server.stack.upgrade.orchestrate.UpgradeContextFactory;
 import org.apache.ambari.server.stageplanner.RoleGraphFactory;
@@ -93,7 +95,9 @@ import org.springframework.security.crypto.password.PasswordEncoder;
 import com.google.inject.AbstractModule;
 import com.google.inject.Guice;
 import com.google.inject.Injector;
+import com.google.inject.TypeLiteral;
 import com.google.inject.assistedinject.FactoryModuleBuilder;
+import com.google.inject.name.Names;
 
 
 /**
@@ -430,6 +434,7 @@ public class UserAuthorizationResourceProviderTest extends EasyMockSupport {
         bind(HostRoleCommandFactory.class).to(HostRoleCommandFactoryImpl.class);
         bind(PersistedState.class).to(PersistedStateImpl.class);
         bind(MpackManagerFactory.class).toInstance(createNiceMock(MpackManagerFactory.class));
+        bind(new TypeLiteral<Encryptor<AgentConfigsUpdateEvent>>() {}).annotatedWith(Names.named("AgentConfigEncryptor")).toInstance(Encryptor.NONE);
       }
     });
   }

+ 5 - 0
ambari-server/src/test/java/org/apache/ambari/server/controller/internal/UserResourceProviderTest.java

@@ -58,6 +58,7 @@ import org.apache.ambari.server.controller.spi.Resource;
 import org.apache.ambari.server.controller.spi.ResourceProvider;
 import org.apache.ambari.server.controller.utilities.PredicateBuilder;
 import org.apache.ambari.server.controller.utilities.PropertyHelper;
+import org.apache.ambari.server.events.AgentConfigsUpdateEvent;
 import org.apache.ambari.server.events.publishers.AmbariEventPublisher;
 import org.apache.ambari.server.hooks.HookContext;
 import org.apache.ambari.server.hooks.HookContextFactory;
@@ -90,6 +91,7 @@ import org.apache.ambari.server.security.authorization.AuthorizationException;
 import org.apache.ambari.server.security.authorization.UserAuthenticationType;
 import org.apache.ambari.server.security.encryption.CredentialStoreService;
 import org.apache.ambari.server.security.encryption.CredentialStoreServiceImpl;
+import org.apache.ambari.server.security.encryption.Encryptor;
 import org.apache.ambari.server.stack.StackManagerFactory;
 import org.apache.ambari.server.stack.upgrade.orchestrate.UpgradeContextFactory;
 import org.apache.ambari.server.stageplanner.RoleGraphFactory;
@@ -122,7 +124,9 @@ import com.google.inject.AbstractModule;
 import com.google.inject.Guice;
 import com.google.inject.Injector;
 import com.google.inject.Provider;
+import com.google.inject.TypeLiteral;
 import com.google.inject.assistedinject.FactoryModuleBuilder;
+import com.google.inject.name.Names;
 
 /**
  * UserResourceProvider tests.
@@ -432,6 +436,7 @@ public class UserResourceProviderTest extends EasyMockSupport {
         bind(ResourceDAO.class).toInstance(createMock(ResourceDAO.class));
         bind(PrincipalTypeDAO.class).toInstance(createMock(PrincipalTypeDAO.class));
         bind(MpackManagerFactory.class).toInstance(createNiceMock(MpackManagerFactory.class));
+        bind(new TypeLiteral<Encryptor<AgentConfigsUpdateEvent>>() {}).annotatedWith(Names.named("AgentConfigEncryptor")).toInstance(Encryptor.NONE);
       }
     });
   }

+ 5 - 0
ambari-server/src/test/java/org/apache/ambari/server/orm/InMemoryDefaultTestModule.java

@@ -26,10 +26,12 @@ import java.util.concurrent.atomic.AtomicReference;
 import org.apache.ambari.server.audit.AuditLogger;
 import org.apache.ambari.server.configuration.Configuration;
 import org.apache.ambari.server.controller.ControllerModule;
+import org.apache.ambari.server.events.AgentConfigsUpdateEvent;
 import org.apache.ambari.server.ldap.LdapModule;
 import org.apache.ambari.server.mpack.MpackManager;
 import org.apache.ambari.server.mpack.MpackManagerFactory;
 import org.apache.ambari.server.mpack.MpackManagerMock;
+import org.apache.ambari.server.security.encryption.Encryptor;
 import org.apache.ambari.server.stack.StackManager;
 import org.apache.ambari.server.stack.StackManagerFactory;
 import org.apache.ambari.server.stack.StackManagerMock;
@@ -37,7 +39,9 @@ import org.easymock.EasyMock;
 import org.springframework.beans.factory.config.BeanDefinition;
 
 import com.google.inject.AbstractModule;
+import com.google.inject.TypeLiteral;
 import com.google.inject.assistedinject.FactoryModuleBuilder;
+import com.google.inject.name.Names;
 import com.google.inject.util.Modules;
 
 public class InMemoryDefaultTestModule extends AbstractModule {
@@ -132,6 +136,7 @@ public class InMemoryDefaultTestModule extends AbstractModule {
       AuditLogger al = EasyMock.createNiceMock(AuditLogger.class);
       EasyMock.expect(al.isEnabled()).andReturn(false).anyTimes();
       bind(AuditLogger.class).toInstance(al);
+      bind(new TypeLiteral<Encryptor<AgentConfigsUpdateEvent>>() {}).annotatedWith(Names.named("AgentConfigEncryptor")).toInstance(Encryptor.NONE);
     } catch (Exception e) {
       throw new RuntimeException(e);
     }

+ 5 - 0
ambari-server/src/test/java/org/apache/ambari/server/serveraction/kerberos/AbstractPrepareKerberosServerActionTest.java

@@ -52,6 +52,7 @@ import org.apache.ambari.server.controller.AmbariManagementController;
 import org.apache.ambari.server.controller.KerberosHelper;
 import org.apache.ambari.server.controller.KerberosHelperImpl;
 import org.apache.ambari.server.controller.UpdateConfigurationPolicy;
+import org.apache.ambari.server.events.AgentConfigsUpdateEvent;
 import org.apache.ambari.server.hooks.HookContextFactory;
 import org.apache.ambari.server.hooks.HookService;
 import org.apache.ambari.server.metadata.RoleCommandOrderProvider;
@@ -59,6 +60,7 @@ import org.apache.ambari.server.mpack.MpackManagerFactory;
 import org.apache.ambari.server.orm.dao.HostRoleCommandDAO;
 import org.apache.ambari.server.scheduler.ExecutionScheduler;
 import org.apache.ambari.server.security.encryption.CredentialStoreService;
+import org.apache.ambari.server.security.encryption.Encryptor;
 import org.apache.ambari.server.stack.StackManagerFactory;
 import org.apache.ambari.server.stageplanner.RoleGraphFactory;
 import org.apache.ambari.server.state.Cluster;
@@ -91,6 +93,8 @@ import com.google.inject.AbstractModule;
 import com.google.inject.Guice;
 import com.google.inject.Injector;
 import com.google.inject.Provider;
+import com.google.inject.TypeLiteral;
+import com.google.inject.name.Names;
 
 public class AbstractPrepareKerberosServerActionTest extends EasyMockSupport {
   private static final String KERBEROS_DESCRIPTOR_JSON = "" +
@@ -227,6 +231,7 @@ public class AbstractPrepareKerberosServerActionTest extends EasyMockSupport {
         Provider<EntityManager> entityManagerProvider = createNiceMock(Provider.class);
         bind(EntityManager.class).toProvider(entityManagerProvider);
         bind(MpackManagerFactory.class).toInstance(createNiceMock(MpackManagerFactory.class));
+        bind(new TypeLiteral<Encryptor<AgentConfigsUpdateEvent>>() {}).annotatedWith(Names.named("AgentConfigEncryptor")).toInstance(Encryptor.NONE);
       }
     });
 

+ 4 - 0
ambari-server/src/test/java/org/apache/ambari/server/serveraction/upgrades/PreconfigureKerberosActionTest.java

@@ -71,6 +71,7 @@ import org.apache.ambari.server.controller.AmbariManagementController;
 import org.apache.ambari.server.controller.KerberosHelper;
 import org.apache.ambari.server.controller.KerberosHelperImpl;
 import org.apache.ambari.server.controller.RootServiceResponseFactory;
+import org.apache.ambari.server.events.AgentConfigsUpdateEvent;
 import org.apache.ambari.server.events.AmbariEvent;
 import org.apache.ambari.server.hooks.AmbariEventFactory;
 import org.apache.ambari.server.hooks.HookContext;
@@ -95,6 +96,7 @@ import org.apache.ambari.server.orm.entities.UpgradeEntity;
 import org.apache.ambari.server.scheduler.ExecutionScheduler;
 import org.apache.ambari.server.scheduler.ExecutionSchedulerImpl;
 import org.apache.ambari.server.security.encryption.CredentialStoreService;
+import org.apache.ambari.server.security.encryption.Encryptor;
 import org.apache.ambari.server.stack.StackManagerFactory;
 import org.apache.ambari.server.stack.upgrade.Direction;
 import org.apache.ambari.server.stack.upgrade.orchestrate.UpgradeContext;
@@ -152,6 +154,7 @@ import com.google.gson.reflect.TypeToken;
 import com.google.inject.AbstractModule;
 import com.google.inject.Guice;
 import com.google.inject.Injector;
+import com.google.inject.TypeLiteral;
 import com.google.inject.assistedinject.FactoryModuleBuilder;
 import com.google.inject.name.Names;
 
@@ -654,6 +657,7 @@ public class PreconfigureKerberosActionTest extends EasyMockSupport {
         bind(HostDAO.class).toInstance(createMock(HostDAO.class));
         bind(ExecutionScheduler.class).to(ExecutionSchedulerImpl.class);
         bind(ActionDBAccessor.class).to(ActionDBAccessorImpl.class);
+        bind(new TypeLiteral<Encryptor<AgentConfigsUpdateEvent>>() {}).annotatedWith(Names.named("AgentConfigEncryptor")).toInstance(Encryptor.NONE);
 
         install(new FactoryModuleBuilder().implement(HookContext.class, PostUserCreationHookContext.class)
             .build(HookContextFactory.class));

+ 2 - 1
ambari-server/src/test/java/org/apache/ambari/server/state/host/HostTest.java

@@ -45,6 +45,7 @@ import org.apache.ambari.server.orm.OrmTestHelper;
 import org.apache.ambari.server.orm.dao.HostDAO;
 import org.apache.ambari.server.orm.entities.HostEntity;
 import org.apache.ambari.server.orm.entities.HostStateEntity;
+import org.apache.ambari.server.security.encryption.Encryptor;
 import org.apache.ambari.server.state.AgentVersion;
 import org.apache.ambari.server.state.Cluster;
 import org.apache.ambari.server.state.Clusters;
@@ -132,7 +133,7 @@ public class HostTest {
     Injector injector = mock(Injector.class);
     doNothing().when(injector).injectMembers(any());
     when(injector.getInstance(AmbariEventPublisher.class)).thenReturn(mock(AmbariEventPublisher.class));
-    HeartBeatHandler handler = new HeartBeatHandler(clusters, manager, injector);
+    HeartBeatHandler handler = new HeartBeatHandler(clusters, manager, Encryptor.NONE, injector);
     String os = handler.getOsType("RedHat", "6.1");
     Assert.assertEquals("redhat6", os);
     os = handler.getOsType("RedHat", "6");

+ 3 - 1
ambari-server/src/test/java/org/apache/ambari/server/testutils/PartialNiceMockBinder.java

@@ -37,6 +37,7 @@ import org.apache.ambari.server.controller.AbstractRootServiceResponseFactory;
 import org.apache.ambari.server.controller.AmbariManagementController;
 import org.apache.ambari.server.controller.KerberosHelper;
 import org.apache.ambari.server.controller.RootServiceResponseFactory;
+import org.apache.ambari.server.events.AgentConfigsUpdateEvent;
 import org.apache.ambari.server.events.AmbariEvent;
 import org.apache.ambari.server.hooks.AmbariEventFactory;
 import org.apache.ambari.server.hooks.HookContext;
@@ -213,7 +214,8 @@ public class PartialNiceMockBinder implements Module {
     public Builder addPasswordEncryptorBindings() {
       configurers.add((Binder binder) -> {
         binder.bind(EncryptionService.class).toInstance(easyMockSupport.createNiceMock(EncryptionService.class));
-        binder.bind(new TypeLiteral<Encryptor<Config>>() {}).annotatedWith(Names.named("ConfigPropertiesEncryptor")).toInstance(easyMockSupport.createNiceMock(Encryptor.class));
+        binder.bind(new TypeLiteral<Encryptor<Config>>() {}).annotatedWith(Names.named("ConfigPropertiesEncryptor")).toInstance(Encryptor.NONE);
+        binder.bind(new TypeLiteral<Encryptor<AgentConfigsUpdateEvent>>() {}).annotatedWith(Names.named("AgentConfigEncryptor")).toInstance(Encryptor.NONE);
       });
       return this;
     }

+ 3 - 0
pom.xml

@@ -319,6 +319,9 @@
             <!--Jinja2 library (BSD license)-->
             <exclude>ambari-common/src/main/python/ambari_jinja2/**</exclude>
             <exclude>ambari-common/src/main/python/jinja2/**</exclude>
+            <exclude>ambari-common/src/main/python/ambari_pyaes/**</exclude>
+            <exclude>ambari-common/src/main/python/ambari_pbkdf2/**</exclude>
+            <exclude>**/.metadata_never_index</exclude>
             <!--Simplejson library (MIT license)-->
             <exclude>ambari-common/src/main/python/ambari_simplejson/**</exclude>
             <!--Subprocess32 library (PSF license)-->

+ 1 - 1
start-build-env.sh

@@ -28,7 +28,7 @@ cd "$(dirname "$0")"
 : ${AMBARI_DIR:=$(pwd -P)}
 
 # Maven version
-: ${MAVEN_VERSION:=3.3.9}
+: ${MAVEN_VERSION:=3.6.0}
 
 docker build -t ambari-build-base:${BUILD_OS} dev-support/docker/${BUILD_OS}
 docker build -t ambari-build:${BUILD_OS} --build-arg BUILD_OS="${BUILD_OS}" --build-arg MAVEN_VERSION="${MAVEN_VERSION}" dev-support/docker/common

Daži faili netika attēloti, jo izmaiņu fails ir pārāk liels