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