diff --git a/kmip/core/exceptions.py b/kmip/core/exceptions.py index eb228de..dbc1d8b 100644 --- a/kmip/core/exceptions.py +++ b/kmip/core/exceptions.py @@ -311,6 +311,12 @@ class InvalidPrimitiveLength(Exception): pass +class ShutdownError(Exception): + """ + An error generated when a problem occurs with shutting down the server. + """ + + class StreamNotEmptyError(Exception): def __init__(self, cls, extra): super(StreamNotEmptyError, self).__init__() diff --git a/kmip/services/server/engine.py b/kmip/services/server/engine.py index 99bcfb9..fcda637 100644 --- a/kmip/services/server/engine.py +++ b/kmip/services/server/engine.py @@ -15,7 +15,6 @@ import copy import logging -import os import six import sqlalchemy @@ -42,8 +41,6 @@ from kmip.core.messages import payloads from kmip.core import misc -from kmip.core import policy as operation_policy - from kmip.pie import factory from kmip.pie import objects from kmip.pie import sqltypes @@ -77,7 +74,7 @@ class KmipEngine(object): * Cryptographic usage mask enforcement per object type """ - def __init__(self, policy_path=None): + def __init__(self, policies=None): """ Create a KmipEngine. @@ -124,69 +121,9 @@ class KmipEngine(object): } self._attribute_policy = policy.AttributePolicy(self._protocol_version) - self._operation_policies = copy.deepcopy(operation_policy.policies) - self._load_operation_policies(policy_path) - + self._operation_policies = policies self._client_identity = [None, None] - def _load_operation_policies(self, policy_path): - if (policy_path is None) or (not os.path.isdir(policy_path)): - self._logger.warning( - "The specified operation policy directory{0} is not " - "valid. No user-defined policies will be loaded.".format( - " (" + policy_path + ")" if policy_path else '' - ) - ) - return dict() - else: - self._logger.info( - "Loading user-defined operation policy files from: {0}".format( - policy_path - ) - ) - - for filename in os.listdir(policy_path): - file_path = os.path.join(policy_path, filename) - if os.path.isfile(file_path): - self._logger.info( - "Loading user-defined operation policies " - "from file: {0}".format(file_path) - ) - - try: - policies = operation_policy.read_policy_from_file( - file_path - ) - except ValueError as e: - self._logger.error( - "A failure occurred while loading policies." - ) - self._logger.exception(e) - continue - - reserved_policies = ['default', 'public'] - for policy_name in six.iterkeys(policies): - if policy_name in reserved_policies: - self._logger.warning( - "Loaded policy '{0}' overwrites a reserved " - "policy and will be thrown out.".format( - policy_name - ) - ) - elif policy_name in six.iterkeys( - self._operation_policies - ): - self._logger.warning( - "Loaded policy '{0}' overwrites a " - "preexisting policy and will be thrown " - "out.".format(policy_name) - ) - else: - self._operation_policies.update([( - policy_name, - policies.get(policy_name) - )]) - def _get_enum_string(self, e): return ''.join([x.capitalize() for x in e.name.split('_')]) @@ -982,6 +919,8 @@ class KmipEngine(object): object_type.unique_identifier == uid ).one() + # TODO (peter-hamilton) Add debug log with policy contents? + # Determine if the request should be carried out under the object's # operation policy. If not, feign ignorance of the object. is_allowed = self._is_allowed_by_operation_policy( diff --git a/kmip/services/server/monitor.py b/kmip/services/server/monitor.py index 5239765..a3cd6b4 100644 --- a/kmip/services/server/monitor.py +++ b/kmip/services/server/monitor.py @@ -35,7 +35,7 @@ class PolicyDirectoryMonitor(multiprocessing.Process): A file monitor that tracks modifications made within the policy directory. """ - def __init__(self, policy_directory, policy_store): + def __init__(self, policy_directory, policy_store, live_monitoring=True): """ Set up the file monitor with the policy directory to track. @@ -46,11 +46,15 @@ class PolicyDirectoryMonitor(multiprocessing.Process): multiprocessing resource manager. Used to store and share the policy information across server processes and threads. Required. + live_monitoring (boolean): A boolean indicating whether or not + live monitoring should continue indefinitely. Optional, + defaults to True. """ super(PolicyDirectoryMonitor, self).__init__() self.halt_trigger = multiprocessing.Event() self.policy_directory = policy_directory + self.live_monitoring = live_monitoring self.file_timestamps = None self.policy_cache = None @@ -71,69 +75,77 @@ class PolicyDirectoryMonitor(multiprocessing.Process): def stop(self): self.halt_trigger.set() + def scan_policies(self): + """ + Scan the policy directory for policy data. + """ + policy_files = get_json_files(self.policy_directory) + for f in set(policy_files) - set(self.policy_files): + self.file_timestamps[f] = 0 + for f in set(self.policy_files) - set(policy_files): + self.logger.info("Removing policies for file: {}".format(f)) + self.file_timestamps.pop(f, None) + for p in self.policy_cache.keys(): + self.disassociate_policy_and_file(p, f) + for p in [k for k, v in self.policy_map.items() if v == f]: + self.restore_or_delete_policy(p) + self.policy_files = policy_files + + for f in sorted(self.file_timestamps.keys()): + t = os.path.getmtime(f) + if t > self.file_timestamps[f]: + self.logger.info("Loading policies for file: {}".format(f)) + self.file_timestamps[f] = t + old_p = [k for k, v in self.policy_map.items() if v == f] + try: + new_p = operation_policy.read_policy_from_file(f) + except ValueError: + self.logger.error("Failure loading file: {}".format(f)) + self.logger.debug("", exc_info=True) + continue + for p in new_p.keys(): + self.logger.info("Loading policy: {}".format(p)) + if p in self.reserved_policies: + self.logger.warning( + "Policy '{}' overwrites a reserved policy and " + "will be thrown out.".format(p) + ) + continue + if p in sorted(self.policy_store.keys()): + self.logger.debug( + "Policy '{}' overwrites an existing " + "policy.".format(p) + ) + if f != self.policy_map.get(p): + self.policy_cache.get(p).append( + ( + time.time(), + self.policy_map.get(p), + self.policy_store.get(p) + ) + ) + else: + self.policy_cache[p] = [] + self.policy_store[p] = new_p.get(p) + self.policy_map[p] = f + for p in set(old_p) - set(new_p.keys()): + self.disassociate_policy_and_file(p, f) + self.restore_or_delete_policy(p) + def run(self): """ Start monitoring operation policy files. """ self.initialize_tracking_structures() - self.logger.info("Starting up the operation policy file monitor.") - while not self.halt_trigger.is_set(): - time.sleep(1) - - policy_files = get_json_files(self.policy_directory) - for f in set(policy_files) - set(self.policy_files): - self.file_timestamps[f] = 0 - for f in set(self.policy_files) - set(policy_files): - self.logger.info("Removing policies for file: {}".format(f)) - self.file_timestamps.pop(f, None) - for p in self.policy_cache.keys(): - self.disassociate_policy_and_file(p, f) - for p in [k for k, v in self.policy_map.items() if v == f]: - self.restore_or_delete_policy(p) - self.policy_files = policy_files - - for f in sorted(self.file_timestamps.keys()): - t = os.path.getmtime(f) - if t > self.file_timestamps[f]: - self.logger.info("Loading policies for file: {}".format(f)) - self.file_timestamps[f] = t - old_p = [k for k, v in self.policy_map.items() if v == f] - try: - new_p = operation_policy.read_policy_from_file(f) - except ValueError: - self.logger.error("Failure loading file: {}".format(f)) - self.logger.debug("", exc_info=True) - continue - for p in new_p.keys(): - self.logger.info("Loading policy: {}".format(p)) - if p in self.reserved_policies: - self.logger.warning( - "Policy '{}' overwrites a reserved policy and " - "will be thrown out.".format(p) - ) - continue - if p in sorted(self.policy_store.keys()): - self.logger.debug( - "Policy '{}' overwrites an existing " - "policy.".format(p) - ) - if f != self.policy_map.get(p): - self.policy_cache.get(p).append( - ( - time.time(), - self.policy_map.get(p), - self.policy_store.get(p) - ) - ) - else: - self.policy_cache[p] = [] - self.policy_store[p] = new_p.get(p) - self.policy_map[p] = f - for p in set(old_p) - set(new_p.keys()): - self.disassociate_policy_and_file(p, f) - self.restore_or_delete_policy(p) - self.logger.info("Stopping the operation policy file monitor.") + if self.live_monitoring: + self.logger.info("Starting up the operation policy file monitor.") + while not self.halt_trigger.is_set(): + time.sleep(1) + self.scan_policies() + self.logger.info("Stopping the operation policy file monitor.") + else: + self.scan_policies() def initialize_tracking_structures(self): self.file_timestamps = {} @@ -142,7 +154,8 @@ class PolicyDirectoryMonitor(multiprocessing.Process): self.policy_map = {} for k in self.policy_store.keys(): - self.policy_store.pop(k, None) + if k not in self.reserved_policies: + self.policy_store.pop(k, None) def disassociate_policy_and_file(self, policy, file_name): c = self.policy_cache.get(policy, []) diff --git a/kmip/services/server/server.py b/kmip/services/server/server.py index 6647b6c..c99572a 100644 --- a/kmip/services/server/server.py +++ b/kmip/services/server/server.py @@ -13,20 +13,25 @@ # License for the specific language governing permissions and limitations # under the License. +import copy import logging import logging.handlers as handlers +import multiprocessing import optparse import os import signal +import six import socket import ssl import sys import threading from kmip.core import exceptions +from kmip.core import policy as operation_policy from kmip.services import auth from kmip.services.server import config from kmip.services.server import engine +from kmip.services.server import monitor from kmip.services.server import session @@ -53,7 +58,8 @@ class KmipServer(object): policy_path=None, enable_tls_client_auth=None, tls_cipher_suites=None, - logging_level=None + logging_level=None, + live_policies=False ): """ Create a KmipServer. @@ -113,6 +119,10 @@ class KmipServer(object): level for the server. All log messages logged at this level or higher in criticality will be logged. All log messages lower in criticality will not be logged. Optional, defaults to None. + live_policies (boolean): A boolean indicating if the operation + policy directory should be actively monitored to autoload any + policy changes while the server is running. Optional, defaults + to False. """ self._logger = logging.getLogger('kmip.server') self._setup_logging(log_path) @@ -131,6 +141,8 @@ class KmipServer(object): tls_cipher_suites, logging_level ) + self.live_policies = live_policies + self.policies = {} self._logger.setLevel(self.config.settings.get('logging_level')) @@ -140,9 +152,6 @@ class KmipServer(object): else: self.auth_suite = auth.BasicAuthenticationSuite(cipher_suites) - self._engine = engine.KmipEngine( - self.config.settings.get('policy_path') - ) self._session_id = 1 self._is_serving = False @@ -223,6 +232,29 @@ class KmipServer(object): NetworkingError: Raised if the TLS socket cannot be bound to the network address. """ + self.manager = multiprocessing.Manager() + self.policies = self.manager.dict() + policies = copy.deepcopy(operation_policy.policies) + for policy_name, policy_set in six.iteritems(policies): + self.policies[policy_name] = policy_set + + self.policy_monitor = monitor.PolicyDirectoryMonitor( + self.config.settings.get('policy_path'), + self.policies, + self.live_policies + ) + + def interrupt_handler(trigger, frame): + self.policy_monitor.stop() + signal.signal(signal.SIGINT, interrupt_handler) + signal.signal(signal.SIGTERM, interrupt_handler) + + self.policy_monitor.start() + + self._engine = engine.KmipEngine( + policies=self.policies + ) + self._logger.info("Starting server socket handler.") # Create a TCP stream socket and configure it for immediate reuse. @@ -328,6 +360,16 @@ class KmipServer(object): "Server failed to shutdown socket handler." ) + if hasattr(self, "policy_monitor"): + try: + self.policy_monitor.stop() + self.policy_monitor.join() + except Exception as e: + self._logger.exception(e) + raise exceptions.ShutdownError( + "Server failed to clean up the policy monitor." + ) + def serve(self): """ Serve client connections. @@ -595,6 +637,8 @@ def main(args=None): if opts.logging_level: kwargs['logging_level'] = opts.logging_level + kwargs['live_policies'] = True + # Create and start the server. s = KmipServer(**kwargs) with s: diff --git a/kmip/tests/unit/services/server/test_engine.py b/kmip/tests/unit/services/server/test_engine.py index ded97a8..425cba9 100644 --- a/kmip/tests/unit/services/server/test_engine.py +++ b/kmip/tests/unit/services/server/test_engine.py @@ -148,179 +148,6 @@ class TestKmipEngine(testtools.TestCase): } create_engine_mock.assert_called_once_with(*args, **fargs) - def test_load_operation_policies(self): - """ - Test that the KmipEngine can correctly load operation policies. - """ - e = engine.KmipEngine() - e._logger = mock.MagicMock() - - policy_file = tempfile.NamedTemporaryFile( - dir=self.temp_dir - ) - with open(policy_file.name, 'w') as f: - f.write( - '{"test": ' - '{"default": {"CERTIFICATE": {"LOCATE": "ALLOW_ALL"}}}}' - ) - - self.assertEqual(2, len(e._operation_policies)) - - e._load_operation_policies(self.temp_dir) - e._logger.info.assert_any_call( - "Loading user-defined operation policy files from: {0}".format( - self.temp_dir - ) - ) - e._logger.info.assert_any_call( - "Loading user-defined operation policies from file: {0}".format( - policy_file.name - ) - ) - - self.assertEqual(3, len(e._operation_policies)) - self.assertIn('test', e._operation_policies.keys()) - - test_policy = { - 'default': { - enums.ObjectType.CERTIFICATE: { - enums.Operation.LOCATE: enums.Policy.ALLOW_ALL - } - } - } - - self.assertEqual(test_policy, e._operation_policies.get('test')) - - def test_load_operation_policies_with_file_read_error(self): - """ - Test that the KmipEngine can correctly handle load errors. - """ - e = engine.KmipEngine() - e._logger = mock.MagicMock() - - policy_file = tempfile.NamedTemporaryFile( - dir=self.temp_dir - ) - with open(policy_file.name, 'w') as f: - f.write( - '{"test": {"INVALID": {"LOCATE": "ALLOW_ALL"}}}' - ) - - self.assertEqual(2, len(e._operation_policies)) - - e._load_operation_policies(self.temp_dir) - e._logger.info.assert_any_call( - "Loading user-defined operation policy files from: {0}".format( - self.temp_dir - ) - ) - e._logger.info.assert_any_call( - "Loading user-defined operation policies from file: {0}".format( - policy_file.name - ) - ) - e._logger.error.assert_called_once_with( - "A failure occurred while loading policies." - ) - e._logger.exception.assert_called_once() - - self.assertEqual(2, len(e._operation_policies)) - - def test_load_operation_policies_with_reserved(self): - """ - Test that the KmipEngine can correctly load operation policies, even - when a policy attempts to overwrite a reserved one. - """ - e = engine.KmipEngine() - e._logger = mock.MagicMock() - - policy_file = tempfile.NamedTemporaryFile( - dir=self.temp_dir - ) - with open(policy_file.name, 'w') as f: - f.write( - '{"public": {"CERTIFICATE": {"LOCATE": "ALLOW_ALL"}}}' - ) - - self.assertEqual(2, len(e._operation_policies)) - - e._load_operation_policies(self.temp_dir) - e._logger.info.assert_any_call( - "Loading user-defined operation policy files from: {0}".format( - self.temp_dir - ) - ) - e._logger.info.assert_any_call( - "Loading user-defined operation policies from file: {0}".format( - policy_file.name - ) - ) - e._logger.warning.assert_called_once_with( - "Loaded policy 'public' overwrites a reserved policy and will " - "be thrown out." - ) - - self.assertEqual(2, len(e._operation_policies)) - - def test_load_operation_policies_with_duplicate(self): - """ - Test that the KmipEngine can correctly load operation policies, even - when a policy is defined multiple times. - """ - self.skip('Refactor') - e = engine.KmipEngine() - e._logger = mock.MagicMock() - - policy_file_a = tempfile.NamedTemporaryFile( - dir=self.temp_dir - ) - with open(policy_file_a.name, 'w') as f: - f.write( - '{"test": {"CERTIFICATE": {"LOCATE": "ALLOW_ALL"}}}' - ) - - policy_file_b = tempfile.NamedTemporaryFile( - dir=self.temp_dir - ) - with open(policy_file_b.name, 'w') as f: - f.write( - '{"test": {"CERTIFICATE": {"LOCATE": "ALLOW_ALL"}}}' - ) - - self.assertEqual(2, len(e._operation_policies)) - - e._load_operation_policies(self.temp_dir) - e._logger.info.assert_any_call( - "Loading user-defined operation policy files from: {0}".format( - self.temp_dir - ) - ) - e._logger.info.assert_any_call( - "Loading user-defined operation policies from file: {0}".format( - policy_file_a.name - ) - ) - e._logger.info.assert_any_call( - "Loading user-defined operation policies from file: {0}".format( - policy_file_b.name - ) - ) - e._logger.warning.assert_called_once_with( - "Loaded policy 'test' overwrites a preexisting policy and will " - "be thrown out." - ) - - self.assertEqual(3, len(e._operation_policies)) - self.assertIn('test', e._operation_policies.keys()) - - test_policy = { - enums.ObjectType.CERTIFICATE: { - enums.Operation.LOCATE: enums.Policy.ALLOW_ALL - } - } - - self.assertEqual(test_policy, e._operation_policies.get('test')) - def test_version_operation_match(self): """ Test that a valid response is generated when trying to invoke an @@ -957,6 +784,7 @@ class TestKmipEngine(testtools.TestCase): e._data_store = self.engine e._data_store_session_factory = self.session_factory e._data_session = e._data_store_session_factory() + e._is_allowed_by_operation_policy = mock.Mock(return_value=True) e._logger = mock.MagicMock() obj_a = pie_objects.OpaqueObject(b'', enums.OpaqueDataType.NONE) @@ -981,6 +809,7 @@ class TestKmipEngine(testtools.TestCase): e._data_store = self.engine e._data_store_session_factory = self.session_factory e._data_session = e._data_store_session_factory() + e._is_allowed_by_operation_policy = mock.Mock(return_value=True) e._logger = mock.MagicMock() args = ('1', ) @@ -1007,6 +836,7 @@ class TestKmipEngine(testtools.TestCase): e._data_store = self.engine e._data_store_session_factory = self.session_factory e._data_session = e._data_store_session_factory() + e._is_allowed_by_operation_policy = mock.Mock(return_value=True) test_exception = exc.MultipleResultsFound() e._data_session.query = mock.MagicMock(side_effect=test_exception) e._logger = mock.MagicMock() @@ -1033,6 +863,7 @@ class TestKmipEngine(testtools.TestCase): e._data_store = self.engine e._data_store_session_factory = self.session_factory e._data_session = e._data_store_session_factory() + e._is_allowed_by_operation_policy = mock.Mock(return_value=True) e._logger = mock.MagicMock() obj_a = pie_objects.OpaqueObject(b'', enums.OpaqueDataType.NONE) @@ -1419,6 +1250,7 @@ class TestKmipEngine(testtools.TestCase): e._data_store = self.engine e._data_store_session_factory = self.session_factory e._data_session = e._data_store_session_factory() + e._is_allowed_by_operation_policy = mock.Mock(return_value=True) e._logger = mock.MagicMock() symmetric_key = pie_objects.SymmetricKey( @@ -1481,6 +1313,7 @@ class TestKmipEngine(testtools.TestCase): e._data_store = self.engine e._data_store_session_factory = self.session_factory e._data_session = e._data_store_session_factory() + e._is_allowed_by_operation_policy = mock.Mock(return_value=True) e._logger = mock.MagicMock() symmetric_key = pie_objects.SymmetricKey( @@ -2196,6 +2029,7 @@ class TestKmipEngine(testtools.TestCase): Test that the lookup for a non-existent policy is handled correctly. """ e = engine.KmipEngine() + e._operation_policies = {} e._logger = mock.MagicMock() result = e.get_relevant_policy_section('invalid') @@ -2524,6 +2358,7 @@ class TestKmipEngine(testtools.TestCase): e._data_store = self.engine e._data_store_session_factory = self.session_factory e._data_session = e._data_store_session_factory() + e._is_allowed_by_operation_policy = mock.Mock(return_value=False) e._logger = mock.MagicMock() e._client_identity = 'test' @@ -3677,6 +3512,7 @@ class TestKmipEngine(testtools.TestCase): e._data_store = self.engine e._data_store_session_factory = self.session_factory e._data_session = e._data_store_session_factory() + e._is_allowed_by_operation_policy = mock.Mock(return_value=True) e._logger = mock.MagicMock() e._cryptography_engine.logger = mock.MagicMock() @@ -3851,6 +3687,7 @@ class TestKmipEngine(testtools.TestCase): e._data_store = self.engine e._data_store_session_factory = self.session_factory e._data_session = e._data_store_session_factory() + e._is_allowed_by_operation_policy = mock.Mock(return_value=True) e._logger = mock.MagicMock() e._cryptography_engine.logger = mock.MagicMock() @@ -3968,6 +3805,7 @@ class TestKmipEngine(testtools.TestCase): e._data_store = self.engine e._data_store_session_factory = self.session_factory e._data_session = e._data_store_session_factory() + e._is_allowed_by_operation_policy = mock.Mock(return_value=True) e._logger = mock.MagicMock() e._cryptography_engine.logger = mock.MagicMock() @@ -4003,6 +3841,7 @@ class TestKmipEngine(testtools.TestCase): e._data_store = self.engine e._data_store_session_factory = self.session_factory e._data_session = e._data_store_session_factory() + e._is_allowed_by_operation_policy = mock.Mock(return_value=True) e._logger = mock.MagicMock() e._cryptography_engine.logger = mock.MagicMock() @@ -4042,6 +3881,7 @@ class TestKmipEngine(testtools.TestCase): e._data_store = self.engine e._data_store_session_factory = self.session_factory e._data_session = e._data_store_session_factory() + e._is_allowed_by_operation_policy = mock.Mock(return_value=True) e._logger = mock.MagicMock() e._cryptography_engine.logger = mock.MagicMock() @@ -4162,6 +4002,7 @@ class TestKmipEngine(testtools.TestCase): e._data_store = self.engine e._data_store_session_factory = self.session_factory e._data_session = e._data_store_session_factory() + e._is_allowed_by_operation_policy = mock.Mock(return_value=True) e._logger = mock.MagicMock() e._cryptography_engine.logger = mock.MagicMock() @@ -4219,6 +4060,7 @@ class TestKmipEngine(testtools.TestCase): e._data_store = self.engine e._data_store_session_factory = self.session_factory e._data_session = e._data_store_session_factory() + e._is_allowed_by_operation_policy = mock.Mock(return_value=True) e._logger = mock.MagicMock() e._cryptography_engine.logger = mock.MagicMock() @@ -4280,6 +4122,7 @@ class TestKmipEngine(testtools.TestCase): e._data_store = self.engine e._data_store_session_factory = self.session_factory e._data_session = e._data_store_session_factory() + e._is_allowed_by_operation_policy = mock.Mock(return_value=True) e._logger = mock.MagicMock() e._cryptography_engine.logger = mock.MagicMock() @@ -4337,6 +4180,7 @@ class TestKmipEngine(testtools.TestCase): e._data_store = self.engine e._data_store_session_factory = self.session_factory e._data_session = e._data_store_session_factory() + e._is_allowed_by_operation_policy = mock.Mock(return_value=True) e._logger = mock.MagicMock() e._cryptography_engine.logger = mock.MagicMock() @@ -4402,6 +4246,7 @@ class TestKmipEngine(testtools.TestCase): e._data_store = self.engine e._data_store_session_factory = self.session_factory e._data_session = e._data_store_session_factory() + e._is_allowed_by_operation_policy = mock.Mock(return_value=True) e._logger = mock.MagicMock() obj_a = pie_objects.OpaqueObject(b'', enums.OpaqueDataType.NONE) @@ -4485,6 +4330,7 @@ class TestKmipEngine(testtools.TestCase): e._data_store = self.engine e._data_store_session_factory = self.session_factory e._data_session = e._data_store_session_factory() + e._is_allowed_by_operation_policy = mock.Mock(return_value=True) e._logger = mock.MagicMock() key = (b'\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' @@ -4580,6 +4426,7 @@ class TestKmipEngine(testtools.TestCase): e._data_store = self.engine e._data_store_session_factory = self.session_factory e._data_session = e._data_store_session_factory() + e._is_allowed_by_operation_policy = mock.Mock(return_value=True) e._logger = mock.MagicMock() obj_a = pie_objects.OpaqueObject(b'', enums.OpaqueDataType.NONE) @@ -4686,6 +4533,7 @@ class TestKmipEngine(testtools.TestCase): e._data_store = self.engine e._data_store_session_factory = self.session_factory e._data_session = e._data_store_session_factory() + e._is_allowed_by_operation_policy = mock.Mock(return_value=True) e._logger = mock.MagicMock() obj_a = pie_objects.SymmetricKey( @@ -4793,6 +4641,7 @@ class TestKmipEngine(testtools.TestCase): e._data_store = self.engine e._data_store_session_factory = self.session_factory e._data_session = e._data_store_session_factory() + e._is_allowed_by_operation_policy = mock.Mock(return_value=False) e._logger = mock.MagicMock() e._client_identity = 'test' @@ -4824,6 +4673,7 @@ class TestKmipEngine(testtools.TestCase): e._data_store = self.engine e._data_store_session_factory = self.session_factory e._data_session = e._data_store_session_factory() + e._is_allowed_by_operation_policy = mock.Mock(return_value=True) e._logger = mock.MagicMock() e._cryptography_engine.logger = mock.MagicMock() @@ -4937,6 +4787,7 @@ class TestKmipEngine(testtools.TestCase): e._data_store = self.engine e._data_store_session_factory = self.session_factory e._data_session = e._data_store_session_factory() + e._is_allowed_by_operation_policy = mock.Mock(return_value=True) e._logger = mock.MagicMock() e._cryptography_engine.logger = mock.MagicMock() @@ -5003,6 +4854,7 @@ class TestKmipEngine(testtools.TestCase): e._data_store = self.engine e._data_store_session_factory = self.session_factory e._data_session = e._data_store_session_factory() + e._is_allowed_by_operation_policy = mock.Mock(return_value=True) e._logger = mock.MagicMock() e._cryptography_engine.logger = mock.MagicMock() @@ -5067,6 +4919,7 @@ class TestKmipEngine(testtools.TestCase): e._data_store = self.engine e._data_store_session_factory = self.session_factory e._data_session = e._data_store_session_factory() + e._is_allowed_by_operation_policy = mock.Mock(return_value=True) e._logger = mock.MagicMock() e._cryptography_engine.logger = mock.MagicMock() @@ -5111,6 +4964,7 @@ class TestKmipEngine(testtools.TestCase): e._data_store = self.engine e._data_store_session_factory = self.session_factory e._data_session = e._data_store_session_factory() + e._is_allowed_by_operation_policy = mock.Mock(return_value=True) e._logger = mock.MagicMock() e._cryptography_engine.logger = mock.MagicMock() @@ -5162,6 +5016,7 @@ class TestKmipEngine(testtools.TestCase): e._data_store = self.engine e._data_store_session_factory = self.session_factory e._data_session = e._data_store_session_factory() + e._is_allowed_by_operation_policy = mock.Mock(return_value=True) e._logger = mock.MagicMock() e._cryptography_engine.logger = mock.MagicMock() @@ -5226,6 +5081,7 @@ class TestKmipEngine(testtools.TestCase): e._data_store = self.engine e._data_store_session_factory = self.session_factory e._data_session = e._data_store_session_factory() + e._is_allowed_by_operation_policy = mock.Mock(return_value=True) e._logger = mock.MagicMock() e._cryptography_engine.logger = mock.MagicMock() @@ -5290,6 +5146,7 @@ class TestKmipEngine(testtools.TestCase): e._data_store = self.engine e._data_store_session_factory = self.session_factory e._data_session = e._data_store_session_factory() + e._is_allowed_by_operation_policy = mock.Mock(return_value=True) e._logger = mock.MagicMock() e._cryptography_engine.logger = mock.MagicMock() @@ -5355,6 +5212,7 @@ class TestKmipEngine(testtools.TestCase): e._data_store = self.engine e._data_store_session_factory = self.session_factory e._data_session = e._data_store_session_factory() + e._is_allowed_by_operation_policy = mock.Mock(return_value=True) e._logger = mock.MagicMock() e._cryptography_engine.logger = mock.MagicMock() @@ -5420,6 +5278,7 @@ class TestKmipEngine(testtools.TestCase): e._data_store = self.engine e._data_store_session_factory = self.session_factory e._data_session = e._data_store_session_factory() + e._is_allowed_by_operation_policy = mock.Mock(return_value=True) e._logger = mock.MagicMock() e._cryptography_engine.logger = mock.MagicMock() @@ -5485,6 +5344,7 @@ class TestKmipEngine(testtools.TestCase): e._data_store = self.engine e._data_store_session_factory = self.session_factory e._data_session = e._data_store_session_factory() + e._is_allowed_by_operation_policy = mock.Mock(return_value=True) e._logger = mock.MagicMock() secret = pie_objects.SymmetricKey( @@ -5541,6 +5401,7 @@ class TestKmipEngine(testtools.TestCase): e._data_store = self.engine e._data_store_session_factory = self.session_factory e._data_session = e._data_store_session_factory() + e._is_allowed_by_operation_policy = mock.Mock(return_value=True) e._logger = mock.MagicMock() secret = pie_objects.SymmetricKey( @@ -5624,6 +5485,7 @@ class TestKmipEngine(testtools.TestCase): e._data_store = self.engine e._data_store_session_factory = self.session_factory e._data_session = e._data_store_session_factory() + e._is_allowed_by_operation_policy = mock.Mock(return_value=False) e._logger = mock.MagicMock() e._client_identity = 'test' @@ -5657,6 +5519,7 @@ class TestKmipEngine(testtools.TestCase): e._data_store = self.engine e._data_store_session_factory = self.session_factory e._data_session = e._data_store_session_factory() + e._is_allowed_by_operation_policy = mock.Mock(return_value=True) e._logger = mock.MagicMock() secret = pie_objects.SymmetricKey( @@ -5734,6 +5597,7 @@ class TestKmipEngine(testtools.TestCase): e._data_store = self.engine e._data_store_session_factory = self.session_factory e._data_session = e._data_store_session_factory() + e._is_allowed_by_operation_policy = mock.Mock(return_value=True) e._logger = mock.MagicMock() secret = pie_objects.SymmetricKey( @@ -5810,6 +5674,7 @@ class TestKmipEngine(testtools.TestCase): e._data_store = self.engine e._data_store_session_factory = self.session_factory e._data_session = e._data_store_session_factory() + e._is_allowed_by_operation_policy = mock.Mock(return_value=False) e._logger = mock.MagicMock() e._client_identity = 'test' @@ -5843,6 +5708,7 @@ class TestKmipEngine(testtools.TestCase): e._data_store = self.engine e._data_store_session_factory = self.session_factory e._data_session = e._data_store_session_factory() + e._is_allowed_by_operation_policy = mock.Mock(return_value=True) e._logger = mock.MagicMock() managed_object = pie_objects.SymmetricKey( @@ -5913,6 +5779,7 @@ class TestKmipEngine(testtools.TestCase): e._data_store = self.engine e._data_store_session_factory = self.session_factory e._data_session = e._data_store_session_factory() + e._is_allowed_by_operation_policy = mock.Mock(return_value=True) e._logger = mock.MagicMock() managed_object = pie_objects.OpaqueObject( @@ -5953,6 +5820,7 @@ class TestKmipEngine(testtools.TestCase): e._data_store = self.engine e._data_store_session_factory = self.session_factory e._data_session = e._data_store_session_factory() + e._is_allowed_by_operation_policy = mock.Mock(return_value=True) e._logger = mock.MagicMock() managed_object = pie_objects.SymmetricKey( @@ -5989,6 +5857,7 @@ class TestKmipEngine(testtools.TestCase): e._data_store = self.engine e._data_store_session_factory = self.session_factory e._data_session = e._data_store_session_factory() + e._is_allowed_by_operation_policy = mock.Mock(return_value=False) e._logger = mock.MagicMock() e._client_identity = 'test' @@ -6021,6 +5890,7 @@ class TestKmipEngine(testtools.TestCase): e._data_store = self.engine e._data_store_session_factory = self.session_factory e._data_session = e._data_store_session_factory() + e._is_allowed_by_operation_policy = mock.Mock(return_value=True) e._logger = mock.MagicMock() managed_object = pie_objects.SymmetricKey( @@ -6169,6 +6039,7 @@ class TestKmipEngine(testtools.TestCase): e._data_store = self.engine e._data_store_session_factory = self.session_factory e._data_session = e._data_store_session_factory() + e._is_allowed_by_operation_policy = mock.Mock(return_value=True) e._logger = mock.MagicMock() managed_object = pie_objects.SymmetricKey( @@ -6211,6 +6082,7 @@ class TestKmipEngine(testtools.TestCase): e._data_store = self.engine e._data_store_session_factory = self.session_factory e._data_session = e._data_store_session_factory() + e._is_allowed_by_operation_policy = mock.Mock(return_value=True) e._logger = mock.MagicMock() managed_object = pie_objects.SymmetricKey( @@ -6255,6 +6127,7 @@ class TestKmipEngine(testtools.TestCase): e._data_store = self.engine e._data_store_session_factory = self.session_factory e._data_session = e._data_store_session_factory() + e._is_allowed_by_operation_policy = mock.Mock(return_value=True) e._logger = mock.MagicMock() managed_object = pie_objects.OpaqueObject( @@ -6299,6 +6172,7 @@ class TestKmipEngine(testtools.TestCase): e._data_store = self.engine e._data_store_session_factory = self.session_factory e._data_session = e._data_store_session_factory() + e._is_allowed_by_operation_policy = mock.Mock(return_value=False) e._logger = mock.MagicMock() e._client_identity = 'test' @@ -6337,6 +6211,7 @@ class TestKmipEngine(testtools.TestCase): e._data_store = self.engine e._data_store_session_factory = self.session_factory e._data_session = e._data_store_session_factory() + e._is_allowed_by_operation_policy = mock.Mock(return_value=True) e._logger = mock.MagicMock() obj_a = pie_objects.OpaqueObject(b'', enums.OpaqueDataType.NONE) @@ -6445,6 +6320,7 @@ class TestKmipEngine(testtools.TestCase): e._data_store = self.engine e._data_store_session_factory = self.session_factory e._data_session = e._data_store_session_factory() + e._is_allowed_by_operation_policy = mock.Mock(return_value=False) e._logger = mock.MagicMock() e._client_identity = 'test' @@ -6479,6 +6355,7 @@ class TestKmipEngine(testtools.TestCase): e._data_store = self.engine e._data_store_session_factory = self.session_factory e._data_session = e._data_store_session_factory() + e._is_allowed_by_operation_policy = mock.Mock(return_value=True) e._logger = mock.MagicMock() key = (b'\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' @@ -6871,6 +6748,7 @@ class TestKmipEngine(testtools.TestCase): e._data_store = self.engine e._data_store_session_factory = self.session_factory e._data_session = e._data_store_session_factory() + e._is_allowed_by_operation_policy = mock.Mock(return_value=True) e._logger = mock.MagicMock() e._cryptography_engine.logger = mock.MagicMock() @@ -6937,6 +6815,7 @@ class TestKmipEngine(testtools.TestCase): e._data_store = self.engine e._data_store_session_factory = self.session_factory e._data_session = e._data_store_session_factory() + e._is_allowed_by_operation_policy = mock.Mock(return_value=True) e._logger = mock.MagicMock() e._cryptography_engine.logger = mock.MagicMock() @@ -6999,6 +6878,7 @@ class TestKmipEngine(testtools.TestCase): e._data_store = self.engine e._data_store_session_factory = self.session_factory e._data_session = e._data_store_session_factory() + e._is_allowed_by_operation_policy = mock.Mock(return_value=True) e._logger = mock.MagicMock() e._cryptography_engine.logger = mock.MagicMock() @@ -7051,6 +6931,7 @@ class TestKmipEngine(testtools.TestCase): e._data_store = self.engine e._data_store_session_factory = self.session_factory e._data_session = e._data_store_session_factory() + e._is_allowed_by_operation_policy = mock.Mock(return_value=True) e._logger = mock.MagicMock() e._cryptography_engine.logger = mock.MagicMock() @@ -7102,6 +6983,7 @@ class TestKmipEngine(testtools.TestCase): e._data_store = self.engine e._data_store_session_factory = self.session_factory e._data_session = e._data_store_session_factory() + e._is_allowed_by_operation_policy = mock.Mock(return_value=True) e._logger = mock.MagicMock() e._cryptography_engine.logger = mock.MagicMock() @@ -7158,6 +7040,7 @@ class TestKmipEngine(testtools.TestCase): e._data_store = self.engine e._data_store_session_factory = self.session_factory e._data_session = e._data_store_session_factory() + e._is_allowed_by_operation_policy = mock.Mock(return_value=True) e._logger = mock.MagicMock() e._cryptography_engine.logger = mock.MagicMock() @@ -7217,6 +7100,7 @@ class TestKmipEngine(testtools.TestCase): e._data_store = self.engine e._data_store_session_factory = self.session_factory e._data_session = e._data_store_session_factory() + e._is_allowed_by_operation_policy = mock.Mock(return_value=True) e._logger = mock.MagicMock() e._cryptography_engine.logger = mock.MagicMock() @@ -7286,6 +7170,7 @@ class TestKmipEngine(testtools.TestCase): e._data_store = self.engine e._data_store_session_factory = self.session_factory e._data_session = e._data_store_session_factory() + e._is_allowed_by_operation_policy = mock.Mock(return_value=True) e._logger = mock.MagicMock() e._cryptography_engine.logger = mock.MagicMock() @@ -7338,6 +7223,7 @@ class TestKmipEngine(testtools.TestCase): e._data_store = self.engine e._data_store_session_factory = self.session_factory e._data_session = e._data_store_session_factory() + e._is_allowed_by_operation_policy = mock.Mock(return_value=True) e._logger = mock.MagicMock() e._cryptography_engine.logger = mock.MagicMock() @@ -7389,6 +7275,7 @@ class TestKmipEngine(testtools.TestCase): e._data_store = self.engine e._data_store_session_factory = self.session_factory e._data_session = e._data_store_session_factory() + e._is_allowed_by_operation_policy = mock.Mock(return_value=True) e._logger = mock.MagicMock() e._cryptography_engine.logger = mock.MagicMock() @@ -7445,6 +7332,7 @@ class TestKmipEngine(testtools.TestCase): e._data_store = self.engine e._data_store_session_factory = self.session_factory e._data_session = e._data_store_session_factory() + e._is_allowed_by_operation_policy = mock.Mock(return_value=True) e._logger = mock.MagicMock() e._cryptography_engine.logger = mock.MagicMock() @@ -7501,6 +7389,7 @@ class TestKmipEngine(testtools.TestCase): e._data_store = self.engine e._data_store_session_factory = self.session_factory e._data_session = e._data_store_session_factory() + e._is_allowed_by_operation_policy = mock.Mock(return_value=True) e._logger = mock.MagicMock() e._cryptography_engine.logger = mock.MagicMock() @@ -7637,6 +7526,7 @@ class TestKmipEngine(testtools.TestCase): e._data_store = self.engine e._data_store_session_factory = self.session_factory e._data_session = e._data_store_session_factory() + e._is_allowed_by_operation_policy = mock.Mock(return_value=True) e._logger = mock.MagicMock() e._cryptography_engine.logger = mock.MagicMock() @@ -7690,6 +7580,7 @@ class TestKmipEngine(testtools.TestCase): e._data_store = self.engine e._data_store_session_factory = self.session_factory e._data_session = e._data_store_session_factory() + e._is_allowed_by_operation_policy = mock.Mock(return_value=True) e._logger = mock.MagicMock() e._cryptography_engine.logger = mock.MagicMock() @@ -7732,6 +7623,7 @@ class TestKmipEngine(testtools.TestCase): e._data_store = self.engine e._data_store_session_factory = self.session_factory e._data_session = e._data_store_session_factory() + e._is_allowed_by_operation_policy = mock.Mock(return_value=True) e._logger = mock.MagicMock() e._cryptography_engine.logger = mock.MagicMock() @@ -7790,6 +7682,7 @@ class TestKmipEngine(testtools.TestCase): e._data_store = self.engine e._data_store_session_factory = self.session_factory e._data_session = e._data_store_session_factory() + e._is_allowed_by_operation_policy = mock.Mock(return_value=True) e._logger = mock.MagicMock() e._cryptography_engine.logger = mock.MagicMock() @@ -7847,6 +7740,7 @@ class TestKmipEngine(testtools.TestCase): e._data_store = self.engine e._data_store_session_factory = self.session_factory e._data_session = e._data_store_session_factory() + e._is_allowed_by_operation_policy = mock.Mock(return_value=True) e._logger = mock.MagicMock() e._cryptography_engine.logger = mock.MagicMock() @@ -7915,6 +7809,7 @@ class TestKmipEngine(testtools.TestCase): e._data_store = self.engine e._data_store_session_factory = self.session_factory e._data_session = e._data_store_session_factory() + e._is_allowed_by_operation_policy = mock.Mock(return_value=True) e._logger = mock.MagicMock() key = (b'\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' @@ -7992,6 +7887,7 @@ class TestKmipEngine(testtools.TestCase): e._data_store = self.engine e._data_store_session_factory = self.session_factory e._data_session = e._data_store_session_factory() + e._is_allowed_by_operation_policy = mock.Mock(return_value=True) e._logger = mock.MagicMock() e._cryptography_engine.logger = mock.MagicMock() @@ -8040,6 +7936,7 @@ class TestKmipEngine(testtools.TestCase): e._data_store = self.engine e._data_store_session_factory = self.session_factory e._data_session = e._data_store_session_factory() + e._is_allowed_by_operation_policy = mock.Mock(return_value=True) e._logger = mock.MagicMock() e._cryptography_engine.logger = mock.MagicMock() @@ -8089,6 +7986,7 @@ class TestKmipEngine(testtools.TestCase): e._data_store = self.engine e._data_store_session_factory = self.session_factory e._data_session = e._data_store_session_factory() + e._is_allowed_by_operation_policy = mock.Mock(return_value=True) e._logger = mock.MagicMock() attribute_factory = factory.AttributeFactory() @@ -8214,6 +8112,7 @@ class TestKmipEngine(testtools.TestCase): e._data_store = self.engine e._data_store_session_factory = self.session_factory e._data_session = e._data_store_session_factory() + e._is_allowed_by_operation_policy = mock.Mock(return_value=True) e._logger = mock.MagicMock() attribute_factory = factory.AttributeFactory() @@ -8424,6 +8323,7 @@ class TestKmipEngine(testtools.TestCase): e._data_store = self.engine e._data_store_session_factory = self.session_factory e._data_session = e._data_store_session_factory() + e._is_allowed_by_operation_policy = mock.Mock(return_value=True) e._logger = mock.MagicMock() attribute_factory = factory.AttributeFactory() @@ -8565,6 +8465,7 @@ class TestKmipEngine(testtools.TestCase): e._data_store = self.engine e._data_store_session_factory = self.session_factory e._data_session = e._data_store_session_factory() + e._is_allowed_by_operation_policy = mock.Mock(return_value=True) e._logger = mock.MagicMock() attribute_factory = factory.AttributeFactory() @@ -8773,6 +8674,7 @@ class TestKmipEngine(testtools.TestCase): e._data_store = self.engine e._data_store_session_factory = self.session_factory e._data_session = e._data_store_session_factory() + e._is_allowed_by_operation_policy = mock.Mock(return_value=True) e._logger = mock.MagicMock() e._cryptography_engine.logger = mock.MagicMock() @@ -8866,6 +8768,7 @@ class TestKmipEngine(testtools.TestCase): e._data_store = self.engine e._data_store_session_factory = self.session_factory e._data_session = e._data_store_session_factory() + e._is_allowed_by_operation_policy = mock.Mock(return_value=True) e._logger = mock.MagicMock() e._cryptography_engine.logger = mock.MagicMock() @@ -8946,6 +8849,7 @@ class TestKmipEngine(testtools.TestCase): e._data_store = self.engine e._data_store_session_factory = self.session_factory e._data_session = e._data_store_session_factory() + e._is_allowed_by_operation_policy = mock.Mock(return_value=True) e._logger = mock.MagicMock() e._cryptography_engine.logger = mock.MagicMock() @@ -8990,6 +8894,7 @@ class TestKmipEngine(testtools.TestCase): e._data_store = self.engine e._data_store_session_factory = self.session_factory e._data_session = e._data_store_session_factory() + e._is_allowed_by_operation_policy = mock.Mock(return_value=True) e._logger = mock.MagicMock() e._cryptography_engine.logger = mock.MagicMock() @@ -9077,6 +8982,7 @@ class TestKmipEngine(testtools.TestCase): e._data_store = self.engine e._data_store_session_factory = self.session_factory e._data_session = e._data_store_session_factory() + e._is_allowed_by_operation_policy = mock.Mock(return_value=True) e._logger = mock.MagicMock() e._cryptography_engine.logger = mock.MagicMock() diff --git a/kmip/tests/unit/services/server/test_monitor.py b/kmip/tests/unit/services/server/test_monitor.py index 23aba0b..7f4c435 100644 --- a/kmip/tests/unit/services/server/test_monitor.py +++ b/kmip/tests/unit/services/server/test_monitor.py @@ -493,33 +493,6 @@ class TestPolicyDirectoryMonitor(testtools.TestCase): }, cache[0][2] ) - # self.assertEqual( - # { - # 'policy_A': [], - # 'policy_B': [ - # ( - # 1480043060.870089, - # os.path.join(self.tmp_dir, "policy_2.json"), - # { - # 'groups': { - # 'group_B': { - # enums.ObjectType.SYMMETRIC_KEY: { - # enums.Operation.GET: - # enums.Policy.ALLOW_ALL, - # enums.Operation.LOCATE: - # enums.Policy.ALLOW_ALL, - # enums.Operation.DESTROY: - # enums.Policy.ALLOW_ALL - # } - # } - # } - # } - # ) - # ], - # 'policy_C': [] - # }, - # m.policy_cache - # ) self.assertEqual(3, len(m.policy_store.keys())) self.assertEqual( @@ -1888,6 +1861,114 @@ class TestPolicyDirectoryMonitor(testtools.TestCase): m.policy_store.get("policy_D", None) ) + def test_run_without_live_monitoring(self): + """ + Test that the PolicyDirectoryMonitor can load policy files and track + them properly even when operating in a one-shot scanning mode. + """ + m = monitor.PolicyDirectoryMonitor( + self.tmp_dir, + multiprocessing.Manager().dict(), + live_monitoring=False + ) + m.logger = mock.MagicMock(logging.Logger) + m.halt_trigger = mock.MagicMock(multiprocessing.synchronize.Event) + m.halt_trigger.is_set.side_effect = [False, True] + + write_file(self.tmp_dir, "policy_1.json", POLICY_1) + write_file(self.tmp_dir, "policy_2.json", POLICY_2) + + self.assertEqual({}, m.file_timestamps) + self.assertEqual({}, m.policy_cache) + self.assertEqual([], m.policy_files) + self.assertEqual({}, m.policy_map) + self.assertEqual([], m.policy_store.keys()) + + m.run() + + m.logger.info.assert_any_call( + "Loading policies for file: {}".format( + os.path.join(self.tmp_dir, "policy_1.json") + ) + ) + m.logger.info.assert_any_call("Loading policy: policy_A") + m.logger.info.assert_any_call( + "Loading policies for file: {}".format( + os.path.join(self.tmp_dir, "policy_2.json") + ) + ) + m.logger.info.assert_any_call("Loading policy: policy_B") + m.logger.info.assert_any_call("Loading policy: policy_C") + + self.assertEqual(2, len(m.policy_files)) + path = os.path.join(self.tmp_dir, "policy_1.json") + self.assertEqual( + os.path.getmtime(path), + m.file_timestamps.get(path, None) + ) + self.assertIn(path, m.policy_files) + self.assertEqual(path, m.policy_map.get("policy_A", None)) + + path = os.path.join(self.tmp_dir, "policy_2.json") + self.assertEqual( + os.path.getmtime(path), + m.file_timestamps.get(path, None) + ) + self.assertIn(path, m.policy_files) + self.assertEqual(path, m.policy_map.get("policy_B", None)) + self.assertEqual(path, m.policy_map.get("policy_C", None)) + + self.assertEqual( + { + "policy_A": [], + "policy_B": [], + "policy_C": [] + }, + m.policy_cache + ) + + self.assertEqual(3, len(m.policy_store.keys())) + self.assertEqual( + { + "groups": { + "group_A": { + enums.ObjectType.SYMMETRIC_KEY: { + enums.Operation.GET: enums.Policy.ALLOW_ALL, + enums.Operation.DESTROY: enums.Policy.ALLOW_ALL + } + } + } + }, + m.policy_store.get("policy_A", None) + ) + self.assertEqual( + { + "groups": { + "group_B": { + enums.ObjectType.SYMMETRIC_KEY: { + enums.Operation.GET: enums.Policy.ALLOW_ALL, + enums.Operation.LOCATE: enums.Policy.ALLOW_ALL, + enums.Operation.DESTROY: enums.Policy.ALLOW_ALL + } + } + } + }, + m.policy_store.get("policy_B", None) + ) + self.assertEqual( + { + "groups": { + "group_C": { + enums.ObjectType.SYMMETRIC_KEY: { + enums.Operation.GET: enums.Policy.ALLOW_ALL, + enums.Operation.DESTROY: enums.Policy.DISALLOW_ALL + } + } + } + }, + m.policy_store.get("policy_C", None) + ) + def test_initialize_tracking_structures(self): """ Test that the PolicyDirectoryMonitor can correctly initialize/reset the @@ -1903,6 +1984,7 @@ class TestPolicyDirectoryMonitor(testtools.TestCase): m.policy_files = ["a", "b"] m.policy_map["a"] = "b" m.policy_store["a"] = {"c": 2} + m.policy_store["default"] = {"c": 3} m.initialize_tracking_structures() @@ -1910,7 +1992,8 @@ class TestPolicyDirectoryMonitor(testtools.TestCase): self.assertEqual({}, m.policy_cache) self.assertEqual([], m.policy_files) self.assertEqual({}, m.policy_map) - self.assertEqual([], m.policy_store.keys()) + self.assertEqual(["default"], m.policy_store.keys()) + self.assertEqual({"c": 3}, m.policy_store.get("default")) def test_disassociate_policy_and_file(self): """ diff --git a/kmip/tests/unit/services/server/test_server.py b/kmip/tests/unit/services/server/test_server.py index 991f17c..e576d06 100644 --- a/kmip/tests/unit/services/server/test_server.py +++ b/kmip/tests/unit/services/server/test_server.py @@ -51,7 +51,6 @@ class TestKmipServer(testtools.TestCase): self.assertTrue(logging_mock.called) self.assertIsInstance(s.auth_suite, auth.BasicAuthenticationSuite) - self.assertIsNotNone(s._engine) self.assertEqual(1, s._session_id) self.assertFalse(s._is_serving) @@ -167,13 +166,27 @@ class TestKmipServer(testtools.TestCase): self.assertEqual('TLS1.2', s.config.settings.get('auth_suite')) self.assertIsNotNone(s.auth_suite) + @mock.patch('multiprocessing.Manager') + @mock.patch('kmip.services.server.monitor.PolicyDirectoryMonitor') @mock.patch('kmip.services.server.engine.KmipEngine') @mock.patch('kmip.services.server.server.KmipServer._setup_logging') - def test_start(self, logging_mock, engine_mock): + def test_start(self, + logging_mock, + engine_mock, + monitor_mock, + manager_mock): """ Test that starting the KmipServer either runs as expected or generates the expected error. """ + monitor_instance_mock = mock.MagicMock() + monitor_mock.return_value = monitor_instance_mock + + dict_mock = mock.MagicMock() + manager_instance_mock = mock.MagicMock() + manager_instance_mock.dict.return_value = dict_mock + manager_mock.return_value = manager_instance_mock + a_mock = mock.MagicMock() b_mock = mock.MagicMock() @@ -196,7 +209,18 @@ class TestKmipServer(testtools.TestCase): socket_mock.return_value = a_mock ssl_mock.return_value = b_mock + manager_mock.assert_not_called() + monitor_mock.assert_not_called() + s.start() + + manager_mock.assert_called_once_with() + monitor_mock.assert_called_once_with( + None, + dict_mock, + False + ) + self.assertIsNotNone(s._engine) s._logger.info.assert_any_call( "Starting server socket handler." ) @@ -223,8 +247,20 @@ class TestKmipServer(testtools.TestCase): "127.0.0.1:5696" ) + monitor_instance_mock.stop.assert_not_called() + handler = signal.getsignal(signal.SIGINT) + handler(None, None) + monitor_instance_mock.stop.assert_called_once_with() + monitor_instance_mock.stop.reset_mock() + monitor_instance_mock.stop.assert_not_called() + handler = signal.getsignal(signal.SIGTERM) + handler(None, None) + monitor_instance_mock.stop.assert_called_once_with() + self.assertTrue(s._is_serving) + manager_mock.reset_mock() + monitor_mock.reset_mock() a_mock.reset_mock() b_mock.reset_mock() @@ -237,6 +273,9 @@ class TestKmipServer(testtools.TestCase): test_exception = Exception() b_mock.bind.side_effect = test_exception + manager_mock.assert_not_called() + monitor_mock.assert_not_called() + regex = ( "Server failed to bind socket handler to 127.0.0.1:5696" ) @@ -245,6 +284,13 @@ class TestKmipServer(testtools.TestCase): regex, s.start ) + + manager_mock.assert_called_once_with() + monitor_mock.assert_called_once_with( + None, + dict_mock, + False + ) s._logger.info.assert_any_call( "Starting server socket handler." ) @@ -368,6 +414,50 @@ class TestKmipServer(testtools.TestCase): s._socket.close.assert_called_once_with() s._logger.exception(test_exception) + @mock.patch('kmip.services.server.engine.KmipEngine') + @mock.patch('kmip.services.server.server.KmipServer._setup_logging') + def test_stop_with_monitor_shutdown_error(self, logging_mock, engine_mock): + """ + Test that the right calls and log messages are triggered when stopping + the server results in an error while shutting down the policy monitor. + """ + s = server.KmipServer( + hostname='127.0.0.1', + port=5696, + config_path=None, + policy_path=None + ) + s._logger = mock.MagicMock() + s._socket = mock.MagicMock() + s.policy_monitor = mock.MagicMock() + test_exception = Exception() + s.policy_monitor.join.side_effect = test_exception + + # Test the expected behavior for a normal server stop sequence + thread_mock = mock.MagicMock() + thread_mock.join = mock.MagicMock() + thread_mock.is_alive = mock.MagicMock(return_value=False) + thread_mock.name = 'TestThread' + + regex = "Server failed to clean up the policy monitor." + self.assertRaisesRegexp( + exceptions.ShutdownError, + regex, + s.stop + ) + s._logger.info.assert_any_call( + "Cleaning up remaining connection threads." + ) + s._logger.info.assert_any_call( + "Shutting down server socket handler." + ) + s._socket.shutdown.assert_called_once_with(socket.SHUT_RDWR) + s._socket.close.assert_called_once_with() + + s.policy_monitor.stop.assert_called_once_with() + s.policy_monitor.join.assert_called_once_with() + s._logger.exception(test_exception) + @mock.patch('kmip.services.server.engine.KmipEngine') @mock.patch('kmip.services.server.server.KmipServer._setup_logging') def test_serve(self, logging_mock, engine_mock): @@ -468,6 +558,7 @@ class TestKmipServer(testtools.TestCase): policy_path=None ) s._logger = mock.MagicMock() + s._engine = engine_mock # Test that the right calls and log messages are made when # starting a new session.