Repository: qpid-dispatch Updated Branches: refs/heads/master 2138da722 -> 590d79ef9
DISPATCH-331: Define a default policy for when Open hostname policy is absent or is to be ignored Project: http://git-wip-us.apache.org/repos/asf/qpid-dispatch/repo Commit: http://git-wip-us.apache.org/repos/asf/qpid-dispatch/commit/590d79ef Tree: http://git-wip-us.apache.org/repos/asf/qpid-dispatch/tree/590d79ef Diff: http://git-wip-us.apache.org/repos/asf/qpid-dispatch/diff/590d79ef Branch: refs/heads/master Commit: 590d79ef9e3472c5fc1376e52b3574eedc442fb9 Parents: 2138da7 Author: Chuck Rolke <[email protected]> Authored: Wed May 11 17:19:23 2016 -0400 Committer: Chuck Rolke <[email protected]> Committed: Wed May 11 17:19:23 2016 -0400 ---------------------------------------------------------------------- python/qpid_dispatch/management/qdrouter.json | 14 ++++++ .../qpid_dispatch_internal/management/config.py | 5 ++ .../policy/policy_local.py | 51 +++++++++++++++++--- .../policy/policy_manager.py | 12 +++++ tests/router_policy_test.py | 44 +++++++++++++++++ 5 files changed, 118 insertions(+), 8 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/590d79ef/python/qpid_dispatch/management/qdrouter.json ---------------------------------------------------------------------- diff --git a/python/qpid_dispatch/management/qdrouter.json b/python/qpid_dispatch/management/qdrouter.json index e977438..9eafdbf 100644 --- a/python/qpid_dispatch/management/qdrouter.json +++ b/python/qpid_dispatch/management/qdrouter.json @@ -1328,6 +1328,20 @@ "required": false, "create": true }, + "defaultApplication": { + "type": "string", + "default": "", + "description": "Application policyRuleset to use for connections with no open.hostname or a hostname that does not match any existing policy. For users that don't wish to use open.hostname or any multi-tennancy feature, this default policy can be the only policy in effect for the network.", + "required": false, + "create": true + }, + "defaultApplicationEnabled": { + "type": "boolean", + "default": false, + "description": "Enable defaultApplication policy fallback logic.", + "required": false, + "create": true + }, "connectionsProcessed": {"type": "integer", "graph": true}, "connectionsDenied": {"type": "integer", "graph": true}, "connectionsCurrent": {"type": "integer", "graph": true} http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/590d79ef/python/qpid_dispatch_internal/management/config.py ---------------------------------------------------------------------- diff --git a/python/qpid_dispatch_internal/management/config.py b/python/qpid_dispatch_internal/management/config.py index 6a5e741..c95d1f3 100644 --- a/python/qpid_dispatch_internal/management/config.py +++ b/python/qpid_dispatch_internal/management/config.py @@ -183,6 +183,8 @@ def configure_dispatch(dispatch, lib_handle, filename): from qpid_dispatch_internal.display_name.display_name import DisplayNameService displayname_service = DisplayNameService("$displayname") policyFolder = config.by_type('policy')[0]['policyFolder'] + policyDefaultApplication = config.by_type('policy')[0]['defaultApplication'] + policyDefaultApplicationEnabled = config.by_type('policy')[0]['defaultApplicationEnabled'] # Remaining configuration for t in "fixedAddress", "listener", "connector", "waypoint", "linkRoutePattern", \ "router.config.address", "router.config.linkRoute", "router.config.autoLink", \ @@ -207,3 +209,6 @@ def configure_dispatch(dispatch, lib_handle, filename): pconfig = PolicyConfig(os.path.join(apath, i)) for a in pconfig.by_type("policyRuleset"): agent.configure(a) + + # Set policy default application after all rulesets loaded + agent.policy.set_default_application(policyDefaultApplication, policyDefaultApplicationEnabled) http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/590d79ef/python/qpid_dispatch_internal/policy/policy_local.py ---------------------------------------------------------------------- diff --git a/python/qpid_dispatch_internal/policy/policy_local.py b/python/qpid_dispatch_internal/policy/policy_local.py index b690c52..cb62896 100644 --- a/python/qpid_dispatch_internal/policy/policy_local.py +++ b/python/qpid_dispatch_internal/policy/policy_local.py @@ -486,6 +486,16 @@ class PolicyLocal(object): # Entries created as connection AMQP Opens arrive # Entries destroyed as sockets closed self._connections = {} + + # _default_application is a string + # holds the name of the policyRuleset to use when the + # open.hostname is not found in the rulesetdb + self._default_application = "" + + # _default_application_enabled is a boolean + # controls default application fallback logic + self._default_application_enabled = False + # # Service interfaces # @@ -549,11 +559,27 @@ class PolicyLocal(object): """ return self.rulesetdb.keys() + def set_default_application(self, name, enabled): + """ + Set the default application name and control its enablement. + Raise PolicyError if the named application is enabled but absent. + @param name: the name of the default application + @param enabled: default application ruleset logic is active + @return: none + """ + self._default_application = name + self._default_application_enabled = enabled + if enabled: + if not name in self.policy_db_get_names(): + raise PolicyError("Policy fallback defaultApplication '%s' does not exist" % name) + self._manager.log_info("Policy fallback defaultApplication is enabled: '%s'" % name) + else: + self._manager.log_info("Policy fallback defaultApplication is disabled") # # Runtime query interface # - def lookup_user(self, user, host, app, conn_name, conn_id): + def lookup_user(self, user, host, app_in, conn_name, conn_id): """ Lookup function called from C. Determine if a user on host accessing app through AMQP Open is allowed @@ -562,19 +588,24 @@ class PolicyLocal(object): returns true then it has registered and counted the connection. @param[in] user connection authId @param[in] host connection remote host numeric IP address as string - @param[in] app application user is accessing + @param[in] app_in application user is accessing @param[in] conn_name connection name used for tracking reports @param[in] conn_id internal connection id @return settings user-group name if allowed; "" if not allowed """ try: - if not app in self.rulesetdb: - self._manager.log_info( - "DENY AMQP Open for user '%s', host '%s', application '%s': " - "No policy defined for application" % (user, host, app)) - return "" + app = app_in + if not app_in in self.rulesetdb: + if self._default_application_enabled: + app = self._default_application + else: + self._manager.log_info( + "DENY AMQP Open for user '%s', host '%s', application '%s': " + "No policy defined for application" % (user, host, app)) + return "" ruleset = self.rulesetdb[app] + if not app in self.statsdb: msg = ( "DENY AMQP Open for user '%s', host '%s', application '%s': " @@ -641,7 +672,7 @@ class PolicyLocal(object): # return failure return "" - def lookup_settings(self, appname, name, upolicy): + def lookup_settings(self, appname_in, name, upolicy): """ Given a settings name, return the aggregated policy blob. @param[in] appname: application user is accessing @@ -652,6 +683,10 @@ class PolicyLocal(object): # Note: the upolicy output is a non-nested dict with settings of interest """ try: + appname = appname_in + if not appname in self.rulesetdb and self._default_application_enabled: + appname = self._default_application + if not appname in self.rulesetdb: self._manager.log_info( "lookup_settings fail for application '%s', user group '%s': " http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/590d79ef/python/qpid_dispatch_internal/policy/policy_manager.py ---------------------------------------------------------------------- diff --git a/python/qpid_dispatch_internal/policy/policy_manager.py b/python/qpid_dispatch_internal/policy/policy_manager.py index a2823c8..7cb72fb 100644 --- a/python/qpid_dispatch_internal/policy/policy_manager.py +++ b/python/qpid_dispatch_internal/policy/policy_manager.py @@ -78,6 +78,18 @@ class PolicyManager(object): self._policy_local.create_ruleset(attributes) # + # Management interface to set the default application + # + def set_default_application(self, name, enabled): + """ + Set default application + @param name: + @param enabled + @return: + """ + self._policy_local.set_default_application(name, enabled) + + # # Runtime query interface # def lookup_user(self, user, host, app, conn_name, conn_id): http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/590d79ef/tests/router_policy_test.py ---------------------------------------------------------------------- diff --git a/tests/router_policy_test.py b/tests/router_policy_test.py index 9b37567..c2359f2 100644 --- a/tests/router_policy_test.py +++ b/tests/router_policy_test.py @@ -190,6 +190,50 @@ class PolicyFile(TestCase): PolicyFile.policy.lookup_settings('photoserver', 'unknown', upolicy) ) +class PolicyFileApplicationFallback(TestCase): + manager = MockPolicyManager() + policy = PolicyLocal(manager) + policy.test_load_config() + + def test_bad_app_fallback(self): + # Show that with no fallback the user cannot connect + self.assertTrue( + self.policy.lookup_user('zeke', '192.168.100.5', 'galleria', "connid", 5) == '') + + # Enable the fallback defaultApplication and show the same user can now connect + self.policy.set_default_application('photoserver', True) + settingsname = self.policy.lookup_user('zeke', '192.168.100.5', 'galleria', "connid", 5) + self.assertTrue(settingsname == 'test') + + # Show that the fallback settings are returned + upolicy = {} + self.assertTrue( + self.policy.lookup_settings('phony*app*name', settingsname, upolicy) + ) + self.assertTrue(upolicy['maxFrameSize'] == 444444) + self.assertTrue(upolicy['maxMessageSize'] == 444444) + self.assertTrue(upolicy['maxSessionWindow'] == 444444) + self.assertTrue(upolicy['maxSessions'] == 4) + self.assertTrue(upolicy['maxSenders'] == 44) + self.assertTrue(upolicy['maxReceivers'] == 44) + self.assertTrue(upolicy['allowAnonymousSender']) + self.assertTrue(upolicy['allowDynamicSrc']) + self.assertTrue(upolicy['targets'] == 'private') + self.assertTrue(upolicy['sources'] == 'private') + + # Disable fallback and show failure again + self.policy.set_default_application('', False) + self.assertTrue( + self.policy.lookup_user('zeke', '192.168.100.5', 'galleria', "connid", 5) == '') + + # Configuration will not allow default application to point to bogus app ruleset + was_allowed = True + try: + self.policy.set_default_application('foobar', True) + except: + was_allowed = False + self.assertTrue(was_allowed == False) + class PolicyAppConnectionMgrTests(TestCase): def test_policy_app_conn_mgr_fail_by_total(self): --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
