You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@qpid.apache.org by gm...@apache.org on 2018/06/05 18:35:56 UTC

qpid-dispatch git commit: DISPATCH-1013 - Enable vhost policies to be defined on router config

Repository: qpid-dispatch
Updated Branches:
  refs/heads/master 9dcf20ca0 -> a62be32f1


DISPATCH-1013 - Enable vhost policies to be defined on router config


Project: http://git-wip-us.apache.org/repos/asf/qpid-dispatch/repo
Commit: http://git-wip-us.apache.org/repos/asf/qpid-dispatch/commit/a62be32f
Tree: http://git-wip-us.apache.org/repos/asf/qpid-dispatch/tree/a62be32f
Diff: http://git-wip-us.apache.org/repos/asf/qpid-dispatch/diff/a62be32f

Branch: refs/heads/master
Commit: a62be32f143054ad2b10573abc2004b9752a3fe0
Parents: 9dcf20c
Author: Fernando Giorgetti <fg...@redhat.com>
Authored: Fri May 25 17:02:52 2018 -0300
Committer: Fernando Giorgetti <fg...@redhat.com>
Committed: Tue Jun 5 15:21:49 2018 -0300

----------------------------------------------------------------------
 .../qpid_dispatch_internal/management/config.py | 18 ++++-
 .../policy/policy_local.py                      |  4 +-
 tests/system_test.py                            | 18 ++++-
 tests/system_tests_policy.py                    | 76 +++++++++++++++++++-
 4 files changed, 107 insertions(+), 9 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/a62be32f/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 ee76ce6..8cf1940 100644
--- a/python/qpid_dispatch_internal/management/config.py
+++ b/python/qpid_dispatch_internal/management/config.py
@@ -41,6 +41,9 @@ from qpid_dispatch_internal.compat import PY_TEXT_TYPE
 class Config(object):
     """Load config entities from qdrouterd.conf and validated against L{QdSchema}."""
 
+    # static property to control depth level while reading the entities
+    child_level = 0
+
     def __init__(self, filename=None, schema=QdSchema(), raw_json=False):
         self.schema = schema
         self.config_types = [et for et in dict_itervalues(schema.entity_types)
@@ -68,9 +71,10 @@ class Config(object):
     @staticmethod
     def _parse(lines):
         """Parse config file format into a section list"""
-        begin = re.compile(r'([\w-]+)[ \t]*{') # WORD {
-        end = re.compile(r'}')                 # }
-        attr = re.compile(r'([\w-]+)[ \t]*:[ \t]*(.+)') # WORD1: VALUE
+        begin = re.compile(r'([\w-]+)[ \t]*{[ \t]*($|#)')             # WORD {
+        end = re.compile(r'^}')                                       # }
+        attr = re.compile(r'([\w-]+)[ \t]*:[ \t]*(.+)')               # WORD1: VALUE
+        child = re.compile(r'([\$]*[\w-]+)[ \t]*:[ \t]*{[ \t]*($|#)') # WORD: {
 
         # The 'pattern:' and 'bindingKey:' attributes in the schema are special
         # snowflakes. They allow '#' characters in their value, so they cannot
@@ -85,6 +89,14 @@ class Config(object):
                 return ""
             if line.split(':')[0].strip() in special_snowflakes:
                 line = re.sub(hash_ok, r'"\1": "\2",', line)
+            elif child.search(line):
+                line = line.split('#')[0].strip()
+                line = re.sub(child, r'"\1": {', line)
+                Config.child_level += 1
+            elif end.search(line) and Config.child_level > 0:
+                line = line.split('#')[0].strip()
+                line = re.sub(end, r'},', line)
+                Config.child_level -= 1
             else:
                 line = line.split('#')[0].strip()
                 line = re.sub(begin, r'["\1", {', line)

http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/a62be32f/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 d3d35f4..10bd6c4 100644
--- a/python/qpid_dispatch_internal/policy/policy_local.py
+++ b/python/qpid_dispatch_internal/policy/policy_local.py
@@ -268,7 +268,7 @@ class PolicyCompiler(object):
                     errors.append("Policy vhost '%s' user group '%s' option '%s' has error '%s'." %
                                   (vhostname, usergroup, key, cerror[0]))
                     return False
-                policy_out[key] = val
+                policy_out[key] = int(val)
             elif key == PolicyKeys.KW_REMOTE_HOSTS:
                 # Conection groups are lists of IP addresses that need to be
                 # converted into binary structures for comparisons.
@@ -280,6 +280,8 @@ class PolicyCompiler(object):
                          PolicyKeys.KW_ALLOW_DYNAMIC_SRC,
                          PolicyKeys.KW_ALLOW_USERID_PROXY
                          ]:
+                if type(val) in [unicode, str] and val.lower() in ['true', 'false']:
+                    val = True if val == 'true' else False
                 if not type(val) is bool:
                     errors.append("Policy vhost '%s' user group '%s' option '%s' has illegal boolean value '%s'." %
                                   (vhostname, usergroup, key, val))

http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/a62be32f/tests/system_test.py
----------------------------------------------------------------------
diff --git a/tests/system_test.py b/tests/system_test.py
index 37056b6..e4e1109 100755
--- a/tests/system_test.py
+++ b/tests/system_test.py
@@ -308,10 +308,22 @@ class Qdrouterd(Process):
 
         def __str__(self):
             """Generate config file content. Calls default() first."""
-            def props(p):
-                return "".join(["    %s: %s\n"%(k, v) for k, v in dict_iteritems(p)])
+            def tabs(level):
+                return "    " * level
+
+            def sub_elem(l, level):
+                return "".join(["%s%s: {\n%s%s}\n" % (tabs(level), n, props(p, level + 1), tabs(level)) for n, p in l])
+
+            def child(v, level):
+                return "{\n%s%s}" % (sub_elem(v, level), tabs(level - 1))
+
+            def props(p, level):
+                return "".join(
+                    ["%s%s: %s\n" % (tabs(level), k, v if not isinstance(v, list) else child(v, level + 1)) for k, v in
+                     dict_iteritems(p)])
+
             self.defaults()
-            return "".join(["%s {\n%s}\n"%(n, props(p)) for n, p in self])
+            return "".join(["%s {\n%s}\n"%(n, props(p, 1)) for n, p in self])
 
     def __init__(self, name=None, config=Config(), pyinclude=None, wait=True, perform_teardown=True):
         """

http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/a62be32f/tests/system_tests_policy.py
----------------------------------------------------------------------
diff --git a/tests/system_tests_policy.py b/tests/system_tests_policy.py
index 07ed91e..9fdd43b 100644
--- a/tests/system_tests_policy.py
+++ b/tests/system_tests_policy.py
@@ -26,8 +26,8 @@ import unittest as unittest
 import os, json
 from system_test import TestCase, Qdrouterd, main_module, Process, TIMEOUT, DIR
 from subprocess import PIPE, STDOUT
-from proton import ConnectionException
-from proton.utils import BlockingConnection, LinkDetached
+from proton import ConnectionException, Timeout
+from proton.utils import BlockingConnection, LinkDetached, SyncRequestResponse
 from qpid_dispatch_internal.policy.policy_util import is_ipv6_enabled
 from qpid_dispatch_internal.compat import dict_iteritems
 
@@ -905,5 +905,77 @@ class PolicyHostamePatternTest(TestCase):
         self.assertFalse("222222" in qdm_out)
 
 
+class VhostPolicyFromRouterConfig(TestCase):
+    """
+    Verify that connections beyond the vhost limit are denied.
+    Differently than global maxConnections, opening a connection
+    does not raise a ConnectionException, but when an attempt to
+    create a sync request and response client is made after limit
+    is reached, the connection times out.
+    """
+    @classmethod
+    def setUpClass(cls):
+        """Start the router"""
+        super(VhostPolicyFromRouterConfig, cls).setUpClass()
+        config = Qdrouterd.Config([
+            ('router', {'mode': 'standalone', 'id': 'QDR.Policy'}),
+            ('listener', {'port': cls.tester.get_port()}),
+            ('policy', {'maxConnections': 100, 'enableVhostPolicy': 'true'}),
+            ('vhost', {
+                'hostname': '0.0.0.0', 'maxConnections': 2,
+                'allowUnknownUser': 'true',
+                'groups': [(
+                    '$default', {
+                        'users': '*', 'remoteHosts': '*',
+                        'sources': '*', 'targets': '*',
+                        'allowDynamicSource': 'true'
+                    }
+                ), (
+                    'anonymous', {
+                        'users': 'anonymous', 'remoteHosts': '*',
+                        'sources': '*', 'targets': '*',
+                        'allowDynamicSource': 'true',
+                        'allowAnonymousSender': 'true'
+                    }
+                )]
+            })
+        ])
+
+        cls.router = cls.tester.qdrouterd('vhost-conn-limit-router', config, wait=True)
+
+    def address(self):
+        return self.router.addresses[0]
+
+    def test_verify_vhost_maximum_connections(self):
+        addr = "%s/$management" % self.address()
+        timeout = 5
+
+        # two connections should be ok
+        denied = False
+        try:
+            bc1 = SyncRequestResponse(BlockingConnection(addr, timeout=timeout))
+            bc2 = SyncRequestResponse(BlockingConnection(addr, timeout=timeout))
+        except ConnectionException:
+            denied = True
+        except Timeout:
+            denied = True
+
+        self.assertFalse(denied)  # assert connections were opened
+
+        # third connection should be denied
+        denied = False
+        try:
+            bc3 = SyncRequestResponse(BlockingConnection(addr, timeout=timeout))
+        except ConnectionException:
+            denied = True
+        except Timeout:
+            denied = True
+
+        self.assertTrue(denied)  # assert if connection that should not open did open
+
+        bc1.connection.close()
+        bc2.connection.close()
+
+
 if __name__ == '__main__':
     unittest.main(main_module())


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org