You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@qpid.apache.org by ch...@apache.org on 2018/06/01 20:37:33 UTC

[1/4] qpid-dispatch git commit: DISPATCH-1011: Describe user name substitution changes in doc

Repository: qpid-dispatch
Updated Branches:
  refs/heads/master 561f210f4 -> c38f99e2a


DISPATCH-1011: Describe user name substitution changes in doc


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

Branch: refs/heads/master
Commit: 2ccb9e7e0095e92ef09540577c4bf1a5fb7c4622
Parents: 3803dc8
Author: Chuck Rolke <cr...@redhat.com>
Authored: Thu May 31 14:02:18 2018 -0400
Committer: Chuck Rolke <cr...@redhat.com>
Committed: Fri Jun 1 15:44:07 2018 -0400

----------------------------------------------------------------------
 doc/book/policy.adoc | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/2ccb9e7e/doc/book/policy.adoc
----------------------------------------------------------------------
diff --git a/doc/book/policy.adoc b/doc/book/policy.adoc
index 888611c..a4eb99b 100644
--- a/doc/book/policy.adoc
+++ b/doc/book/policy.adoc
@@ -279,12 +279,19 @@ a * character, a # character, or a sequence of characters that do not
 include /, *, or #. The * token matches any single token. The # token 
 matches zero or more tokens.
 
+The user name substitution token may be used in a sourcePattern or in a
+targetPattern subject to the following restrictions:
+
+* The user name substitution token must be the first or last token in the rule clause.
+* The user name substitution token must stand alone within its delimited field.
+  It may not be concatenated with literal text prefixes or suffixes.
+
 For each rule definition multiple patterns may be specified in a comma
 separated list.
 
 [options="nowrap"]
 ----
-    sourcePattern: tmp_${user}, temp/#, ${user}-home/*
+    sourcePattern: tmp.${user}, temp/#, ${user}.home/*
 ----
 
 [NOTE]


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


[4/4] qpid-dispatch git commit: DISPATCH-1011: Fix merge errors found by tox; delete usused defs

Posted by ch...@apache.org.
DISPATCH-1011: Fix merge errors found by tox; delete usused defs

This closes #311


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

Branch: refs/heads/master
Commit: c38f99e2aa9836b326cc8d1e66048281e0ef0829
Parents: 729ada0
Author: Chuck Rolke <cr...@redhat.com>
Authored: Fri Jun 1 16:35:20 2018 -0400
Committer: Chuck Rolke <cr...@redhat.com>
Committed: Fri Jun 1 16:35:20 2018 -0400

----------------------------------------------------------------------
 src/policy.c                 |  5 ++---
 tests/system_tests_policy.py | 10 ++++++----
 2 files changed, 8 insertions(+), 7 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/c38f99e2/src/policy.c
----------------------------------------------------------------------
diff --git a/src/policy.c b/src/policy.c
index 43891da..33bd709 100644
--- a/src/policy.c
+++ b/src/policy.c
@@ -581,11 +581,10 @@ bool is_token_sep(char testc)
 //
 // Size of 'easy' temporary copy of allowed input string
 #define QPALN_SIZE 1024
-// Wildcard character
+// Wildcard character at end of source/target name strings
 #define QPALN_WILDCARD '*'
-#define QPALN_USERBUFSIZE 300
 
-#define MIN(a,b) (((a)<(b))?(a):(b))\
+#define MIN(a,b) (((a)<(b))?(a):(b))
 
 /**
  * Given a username and a list of allowed link names 

http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/c38f99e2/tests/system_tests_policy.py
----------------------------------------------------------------------
diff --git a/tests/system_tests_policy.py b/tests/system_tests_policy.py
index ab02e6e..07ed91e 100644
--- a/tests/system_tests_policy.py
+++ b/tests/system_tests_policy.py
@@ -812,9 +812,10 @@ class PolicyLinkNamePatternTest(TestCase):
         exception = False
         try:
             qdm_out = self.run_qdmanage('create --type=vhost --name=DISPATCH-1993-3 --stdin', input=self.disallowed_source_pattern1())
-        except Exception, e:
+        except Exception as e:
             exception = True
-            self.assertTrue("InternalServerErrorStatus: PolicyError: \"Policy 'DISPATCH-1993-3' is invalid:" in e.message)
+            self.assertTrue("InternalServerErrorStatus: PolicyError:" in e.message)
+            self.assertTrue("Policy 'DISPATCH-1993-3' is invalid:" in e.message)
         self.assertTrue(exception)
 
         # attempt another create that should be rejected - name subst must be prefix or suffix
@@ -822,9 +823,10 @@ class PolicyLinkNamePatternTest(TestCase):
         exception = False
         try:
             qdm_out = self.run_qdmanage('create --type=vhost --name=DISPATCH-1993-3 --stdin', input=self.disallowed_source_pattern2())
-        except Exception, e:
+        except Exception as e:
             exception = True
-            self.assertTrue("InternalServerErrorStatus: PolicyError: \"Policy 'DISPATCH-1993-3' is invalid:" in e.message)
+            self.assertTrue("InternalServerErrorStatus: PolicyError:" in e.message)
+            self.assertTrue("Policy 'DISPATCH-1993-3' is invalid:" in e.message)
         self.assertTrue(exception)
 
 


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


[3/4] qpid-dispatch git commit: DISPATCH-1011: Parse tree exports separators. Improve self tests.

Posted by ch...@apache.org.
DISPATCH-1011: Parse tree exports separators. Improve self tests.

Test sourcePattern suffixes found latent paste error in code.
More through test cases to hit more conditional code paths.


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

Branch: refs/heads/master
Commit: 729ada00b00de840935ffd35efa55e61f11b4a39
Parents: 2ccb9e7
Author: Chuck Rolke <cr...@redhat.com>
Authored: Thu May 31 15:52:39 2018 -0400
Committer: Chuck Rolke <cr...@redhat.com>
Committed: Fri Jun 1 15:47:01 2018 -0400

----------------------------------------------------------------------
 .../policy/policy_local.py                      |  2 -
 src/parse_tree.c                                |  5 ++
 src/parse_tree.h                                |  3 +-
 tests/policy_test.c                             | 33 +++++++--
 tests/system_tests_policy.py                    | 74 ++++++++++++++++++++
 5 files changed, 109 insertions(+), 8 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/729ada00/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 eaf25cb..d3d35f4 100644
--- a/python/qpid_dispatch_internal/policy/policy_local.py
+++ b/python/qpid_dispatch_internal/policy/policy_local.py
@@ -355,8 +355,6 @@ class PolicyCompiler(object):
                                 eVal.append(v)
                                 eVal.append('')
                     policy_out[key] = ','.join(eVal)
-                    print "Val: ", val               # hack alert
-                    print "eVal:", eVal              # hack alert
 
                 if key == PolicyKeys.KW_SOURCES:
                     user_sources = True

http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/729ada00/src/parse_tree.c
----------------------------------------------------------------------
diff --git a/src/parse_tree.c b/src/parse_tree.c
index c02b74a..189f316 100644
--- a/src/parse_tree.c
+++ b/src/parse_tree.c
@@ -91,6 +91,11 @@ static void token_iterator_next(token_iterator_t *t)
 }
 
 
+const char address_token_sep[] = "./";
+const char *qd_parse_address_token_sep() {
+    return address_token_sep;
+}
+
 static bool token_iterator_done(const token_iterator_t *t)
 {
     return t->token.begin == t->terminator;

http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/729ada00/src/parse_tree.h
----------------------------------------------------------------------
diff --git a/src/parse_tree.h b/src/parse_tree.h
index 8a06fc9..26613a3 100644
--- a/src/parse_tree.h
+++ b/src/parse_tree.h
@@ -28,8 +28,6 @@
 
 typedef struct qd_parse_node qd_parse_tree_t;
 
-//extern const char * const QD_PARSE_TREE_TOKEN_SEP;
-
 // Pattern matching algorithms
 // ADDRESS - configured address prefix/pattern matching
 //    token separators: '.' or '/'
@@ -54,6 +52,7 @@ typedef enum {
 qd_parse_tree_t *qd_parse_tree_new(qd_parse_tree_type_t type);
 void qd_parse_tree_free(qd_parse_tree_t *tree);
 qd_parse_tree_type_t qd_parse_tree_type(const qd_parse_tree_t *tree);
+const char *qd_parse_address_token_sep();
 
 // verify the pattern is in a legal format for the given tree's match algorithm
 bool qd_parse_tree_validate_pattern(const qd_parse_tree_t *tree,

http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/729ada00/tests/policy_test.c
----------------------------------------------------------------------
diff --git a/tests/policy_test.c b/tests/policy_test.c
index 377e474..3dbd478 100644
--- a/tests/policy_test.c
+++ b/tests/policy_test.c
@@ -98,19 +98,44 @@ static char *test_link_name_tree_lookup(void *context)
     qd_parse_tree_add_pattern_str(node, "${user}.xyz", payload);
 
     if (!_qd_policy_approve_link_name_tree("chuck", "p,,.xyz", "chuck.xyz", node))
-        return "proposed link 'chuck.xyz' should tree-match allowed links with ${user} but does not";
+        return "proposed link 'chuck.xyz' should tree-match allow links with ${user} but does not";
 
     if (_qd_policy_approve_link_name_tree("chuck", "p,,.xyz", "chuck.xyz.ynot", node))
-        return "proposed link 'chuck.xyz.ynot' should not tree-match allowed links with ${user} but does";
+        return "proposed link 'chuck.xyz.ynot' should not tree-match allow links with ${user} but does";
 
     qd_parse_tree_add_pattern_str(node, "${user}.#", payload);
 
     if (!_qd_policy_approve_link_name_tree("motronic", "p,,.#", "motronic", node))
-        return "proposed link 'motronic' should tree-match allowed links with ${user} but does not";
+        return "proposed link 'motronic' should tree-match allow links with ${user} but does not";
 
     if (!_qd_policy_approve_link_name_tree("motronic", "p,,.#", "motronic.stubs.wobbler", node))
-        return "proposed link 'motronic.stubs.wobbler' should tree-match allowed links with ${user} but does not";
+        return "proposed link 'motronic.stubs.wobbler' should tree-match allow links with ${user} but does not";
 
+    qd_parse_tree_t *node2 = qd_parse_tree_new(QD_PARSE_TREE_ADDRESS);
+    qd_parse_tree_add_pattern_str(node2, "abc.${user}", payload);
+
+    if (!_qd_policy_approve_link_name_tree("chuck", "s,abc.,", "abc.chuck", node2))
+        return "proposed link 'abc.chuck' should tree-match allow links with ${user} but does not";
+
+    if (_qd_policy_approve_link_name_tree("chuck", "s,abc.,", "abc.ynot.chuck", node2))
+        return "proposed link 'abc.ynot.chuck' should not tree-match allow links with ${user} but does";
+
+    if (_qd_policy_approve_link_name_tree("chuck", "s,abc.,", "abc.achuck", node2))
+        return "proposed link 'abc.achuck' should not tree-match allow links with ${user} but does";
+
+    if (_qd_policy_approve_link_name_tree("chuckginormous", "s,abc.,", "abc.chuck", node2))
+        return "proposed link 'abc.chuck' should not tree-match allow links with ${user} but does";
+
+    qd_parse_tree_t *node3 = qd_parse_tree_new(QD_PARSE_TREE_ADDRESS);
+    qd_parse_tree_add_pattern_str(node3, "${user}", payload);
+
+    if (!_qd_policy_approve_link_name_tree("chuck", "p,,", "chuck", node3))
+        return "proposed link 'chuck' should tree-match allow links with ${user} but does not";
+
+    qd_parse_tree_free(node);
+    qd_parse_tree_free(node2);
+    qd_parse_tree_free(node3);
+    
     return 0;
 }
 

http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/729ada00/tests/system_tests_policy.py
----------------------------------------------------------------------
diff --git a/tests/system_tests_policy.py b/tests/system_tests_policy.py
index 5ab99d5..ab02e6e 100644
--- a/tests/system_tests_policy.py
+++ b/tests/system_tests_policy.py
@@ -724,6 +724,60 @@ class PolicyLinkNamePatternTest(TestCase):
 }
 """
 
+    def disallowed_source_pattern1(self):
+        return """
+{
+    "id": "DISPATCH-1993-3",
+    "maxConnections": 3,
+    "maxConnectionsPerHost": 3,
+    "maxConnectionsPerUser": 3,
+    "allowUnknownUser": true,
+    "groups": {
+        "$default": {
+            "allowAnonymousSender": true,
+            "maxReceivers": 99,
+            "users": "*",
+            "maxSessionWindow": 1000000,
+            "maxFrameSize": 222222,
+            "sourcePattern": "public, private, $management, abc-${user}.xyz",
+            "maxMessageSize": 222222,
+            "allowDynamicSource": true,
+            "remoteHosts": "*",
+            "maxSessions": 2,
+            "targetPattern": "public, private, $management",
+            "maxSenders": 22
+        }
+    }
+}
+"""
+
+    def disallowed_source_pattern2(self):
+        return """
+{
+    "id": "DISPATCH-1993-3",
+    "maxConnections": 3,
+    "maxConnectionsPerHost": 3,
+    "maxConnectionsPerUser": 3,
+    "allowUnknownUser": true,
+    "groups": {
+        "$default": {
+            "allowAnonymousSender": true,
+            "maxReceivers": 99,
+            "users": "*",
+            "maxSessionWindow": 1000000,
+            "maxFrameSize": 222222,
+            "sourcePattern": "public, private, $management, abc/${user}.xyz",
+            "maxMessageSize": 222222,
+            "allowDynamicSource": true,
+            "remoteHosts": "*",
+            "maxSessions": 2,
+            "targetPattern": "public, private, $management",
+            "maxSenders": 22
+        }
+    }
+}
+"""
+
     def test_link_name_parse_tree_patterns(self):
         # update to replace source/target match patterns
         qdm_out = "<not written>"
@@ -753,6 +807,26 @@ class PolicyLinkNamePatternTest(TestCase):
             self.assertTrue("InternalServerErrorStatus: PolicyError: Policy 'DISPATCH-1993-3' is invalid:" in str(e))
         self.assertTrue(exception)
 
+        # attempt another create that should be rejected - name subst must whole token
+        qdm_out = "<not written>"
+        exception = False
+        try:
+            qdm_out = self.run_qdmanage('create --type=vhost --name=DISPATCH-1993-3 --stdin', input=self.disallowed_source_pattern1())
+        except Exception, e:
+            exception = True
+            self.assertTrue("InternalServerErrorStatus: PolicyError: \"Policy 'DISPATCH-1993-3' is invalid:" in e.message)
+        self.assertTrue(exception)
+
+        # attempt another create that should be rejected - name subst must be prefix or suffix
+        qdm_out = "<not written>"
+        exception = False
+        try:
+            qdm_out = self.run_qdmanage('create --type=vhost --name=DISPATCH-1993-3 --stdin', input=self.disallowed_source_pattern2())
+        except Exception, e:
+            exception = True
+            self.assertTrue("InternalServerErrorStatus: PolicyError: \"Policy 'DISPATCH-1993-3' is invalid:" in e.message)
+        self.assertTrue(exception)
+
 
 class PolicyHostamePatternTest(TestCase):
     """


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


[2/4] qpid-dispatch git commit: DISPATCH-1011: Improve user name substitution token logic and code

Posted by ch...@apache.org.
DISPATCH-1011: Improve user name substitution token logic and code

Remove code flagged by Coverity.
Add scheme that specifies precisely where user name substitution goes.


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

Branch: refs/heads/master
Commit: 3803dc8cbd722847bbfd7238cd0dcd5d2728cb0b
Parents: 561f210
Author: Chuck Rolke <cr...@redhat.com>
Authored: Thu May 31 13:35:49 2018 -0400
Committer: Chuck Rolke <cr...@redhat.com>
Committed: Fri Jun 1 15:44:07 2018 -0400

----------------------------------------------------------------------
 .../policy/policy_local.py                      |  66 ++-
 src/policy.c                                    | 486 +++++++++++++++----
 src/policy.h                                    |  11 +
 src/policy_internal.h                           |   3 +-
 src/remote_sasl.c                               |   4 +-
 tests/policy_test.c                             |  90 +++-
 tests/router_policy_test.py                     |   8 +-
 7 files changed, 543 insertions(+), 125 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/3803dc8c/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 b55e86e..eaf25cb 100644
--- a/python/qpid_dispatch_internal/policy/policy_local.py
+++ b/python/qpid_dispatch_internal/policy/policy_local.py
@@ -98,6 +98,16 @@ class PolicyKeys(object):
     # policy stats controlled by C code but referenced by settings
     KW_CSTATS                   = "denialCounts"
 
+    # Username subsitituion token in link source and target names and patterns
+    KC_TOKEN_USER               = "${user}"
+
+    # Link target/source name wildcard tuple keys
+    KC_TUPLE_ABSENT             = 'a'
+    KC_TUPLE_PREFIX             = 'p'
+    KC_TUPLE_SUFFIX             = 's'
+    KC_TUPLE_EMBED              = 'e'
+    KC_TUPLE_WILDCARD           = '*'
+
 #
 #
 class PolicyCompiler(object):
@@ -293,8 +303,60 @@ class PolicyCompiler(object):
                                   (vhostname, usergroup, key, val, type(val)))
                 # deduplicate address lists
                 val = list(set(val))
-                # output result is CSV string with no white space between values: 'abc,def,mytarget'
-                policy_out[key] = ','.join(val)
+                # val is CSV string with no white space between values: 'abc,def,mytarget,tmp-${user}'
+                if key == PolicyKeys.KW_USERS:
+                    # user name list items are literal strings and need no special handling
+                    policy_out[key] = ','.join(val)
+                else:
+                    # source and target names get special handling for the '${user}' substitution token
+                    # The literal string is translated to a (key, prefix, suffix) set of three strings.
+                    # C code does not have to search for the username token and knows with authority
+                    # how to construct match strings.
+                    # A wildcard is also signaled.
+                    utoken = PolicyKeys.KC_TOKEN_USER
+                    eVal = []
+                    for v in val:
+                        vcount = v.count(utoken)
+                        if vcount > 1:
+                            errors.append("Policy vhost '%s' user group '%s' policy key '%s' item '%s' contains multiple user subtitution tokens" %
+                                          (vhostname, usergroup, key, v))
+                            return False
+                        elif vcount == 1:
+                            # a single token is present as a prefix, suffix, or embedded
+                            # construct cChar, S1, S2 encodings to be added to eVal description
+                            if v.startswith(utoken):
+                                # prefix
+                                eVal.append(PolicyKeys.KC_TUPLE_PREFIX)
+                                eVal.append('')
+                                eVal.append(v)
+                            elif v.endswith(utoken):
+                                # suffix
+                                eVal.append(PolicyKeys.KC_TUPLE_SUFFIX)
+                                eVal.append(v)
+                                eVal.append('')
+                            else:
+                                # embedded
+                                if key in [PolicyKeys.KW_SOURCE_PATTERN,
+                                           PolicyKeys.KW_TARGET_PATTERN]:
+                                    errors.append("Policy vhost '%s' user group '%s' policy key '%s' item '%s' may contain match pattern '%s' as a prefix or a suffix only." %
+                                          (vhostname, usergroup, key, v, utoken))
+                                    return False
+                                eVal.append(PolicyKeys.KC_TUPLE_EMBED)
+                                eVal.append(v[0:v.find(utoken)])
+                                eVal.append(v[v.find(utoken) + len(utoken):])
+                        else:
+                            # ${user} token is absent
+                            if v == PolicyKeys.KC_TUPLE_WILDCARD:
+                                eVal.append(PolicyKeys.KC_TUPLE_WILDCARD)
+                                eVal.append('')
+                                eVal.append('')
+                            else:
+                                eVal.append(PolicyKeys.KC_TUPLE_ABSENT)
+                                eVal.append(v)
+                                eVal.append('')
+                    policy_out[key] = ','.join(eVal)
+                    print "Val: ", val               # hack alert
+                    print "eVal:", eVal              # hack alert
 
                 if key == PolicyKeys.KW_SOURCES:
                     user_sources = True

http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/3803dc8c/src/policy.c
----------------------------------------------------------------------
diff --git a/src/policy.c b/src/policy.c
index 31d31da..43891da 100644
--- a/src/policy.c
+++ b/src/policy.c
@@ -55,6 +55,17 @@ static char* SESSION_DISALLOWED            = "session disallowed by local policy
 static char* LINK_DISALLOWED               = "link disallowed by local policy";
 
 //
+// username substitution key shared with configuration files and python code
+// substitution triplet keys shared with python code 
+//
+static const char * const user_subst_key        = "${user}";
+static const char * const user_subst_i_absent   = "a";
+static const char * const user_subst_i_prefix   = "p";
+static const char * const user_subst_i_embed    = "e";
+static const char * const user_subst_i_suffix   = "s";
+static const char * const user_subst_i_wildcard = "*";
+
+//
 // Policy configuration/statistics management interface
 //
 struct qd_policy_t {
@@ -258,6 +269,8 @@ static const char* QPALN_COMMA_SEP =",";
 // receiver links, return a parse_tree
 //
 //  @param config_spec CSV string with link name match patterns
+//     The patterns consist of ('key', 'prefix', 'suffix') triplets describing
+//     the match pattern.
 //  @return pointer to parse tree
 //
 qd_parse_tree_t * qd_policy_parse_tree(const char *config_spec)
@@ -270,18 +283,53 @@ qd_parse_tree_t * qd_policy_parse_tree(const char *config_spec)
     if (!tree)
         return NULL;
 
-    // Add CSV's values to the tree.
-    // Note that tree's pattern is unused. This code uses a dummy '1'.
+    // make a writable, disposable copy of the csv string
     char * dup = strdup(config_spec);
+    if (!dup)
+        return NULL;
     char * dupend = dup + strlen(dup);
+
     char * pch = dup;
     while (pch < dupend) {
-        size_t vsize = strcspn(pch, QPALN_COMMA_SEP);
-        if (vsize > 0) {
-            pch[vsize] = '\0';
-            qd_parse_tree_add_pattern_str(tree, pch, (void *)1);
-        }
-        pch += vsize + 1;
+        // the tuple strings
+        char  *pChar, *pS1, *pS2;
+        size_t sChar,  sS1,  sS2;
+
+        // extract control field
+        sChar = strcspn(pch, QPALN_COMMA_SEP);
+        if (sChar != 1) { assert(false); break;}
+        pChar = pch;
+        pChar[sChar] = '\0';
+        pch += sChar + 1;
+        if (pch >= dupend) { assert(false); break; }
+
+        // extract prefix field S1
+        sS1 = strcspn(pch, QPALN_COMMA_SEP);
+        pS1 = pch;
+        pS1[sS1] = '\0';
+        pch += sS1 + 1;
+        if (pch > dupend) { assert(false); break; }
+
+        // extract suffix field S2
+        sS2 = strcspn(pch, QPALN_COMMA_SEP);
+        pS2 = pch;
+        pch += sS2 + 1;
+        pS2[sS2] = '\0';
+
+        size_t sName = sS1 + strlen(user_subst_key) + sS2 + 1; // large enough to handle any case
+        char *pName = (char *)malloc(sName);
+
+        if (!strcmp(pChar, user_subst_i_absent))
+            snprintf(pName, sName, "%s", pS1);
+        else if (!strcmp(pChar, user_subst_i_prefix))
+            snprintf(pName, sName, "%s%s", user_subst_key, pS2);
+        else if (!strcmp(pChar, user_subst_i_embed))
+            snprintf(pName, sName, "%s%s%s", pS1, user_subst_key, pS2);
+        else
+            snprintf(pName, sName, "%s%s", pS1, user_subst_key);
+        qd_parse_tree_add_pattern_str(tree, pName, (void *)1);
+
+        free(pName);
     }
     free(dup);
     return tree;
@@ -516,45 +564,16 @@ void _qd_policy_deny_amqp_receiver_link(pn_link_t *pn_link, qd_connection_t *qd_
 }
 
 
-//
-//
-#define MIN(a,b) (((a)<(b))?(a):(b))
-
-// substitute "${user}" in place of uname in proposed
-char * _qd_policy_link_user_name_subst(const char *uname, const char *proposed, char *obuf, int osize)
+/**
+ * Given a char return true if it is a parse_tree token separater
+ */
+bool is_token_sep(char testc)
 {
-    if (strlen(uname) == 0)
-        return NULL;
-
-    const char duser[] = "${user}";
-    char *retptr = obuf;
-    const char *wiptr = proposed;
-    const char *findptr = strstr(proposed, uname);
-    if (findptr == NULL) {
-        return NULL;
+    for (const char *ptr = qd_parse_address_token_sep(); *ptr != '\0'; ptr++) {
+        if (*ptr == testc)
+            return true;
     }
-
-    // Copy leading before match
-    int segsize = findptr - wiptr;
-    int copysize = MIN(osize, segsize);
-    if (copysize)
-        strncpy(obuf, wiptr, copysize);
-    wiptr += copysize;
-    osize -= copysize;
-    obuf  += copysize;
-
-    // Copy the substitution string
-    segsize = sizeof(duser) - 1;
-    copysize = MIN(osize, segsize);
-    if (copysize)
-        strncpy(obuf, duser, copysize);
-    wiptr += strlen(uname);
-    osize -= copysize;
-    obuf  += copysize;
-
-    // Copy trailing after match
-    strncpy(obuf, wiptr, osize);
-    return retptr;
+    return false;
 }
 
 
@@ -562,17 +581,30 @@ char * _qd_policy_link_user_name_subst(const char *uname, const char *proposed,
 //
 // Size of 'easy' temporary copy of allowed input string
 #define QPALN_SIZE 1024
-// Size of user-name-substituted proposed string.
-#define QPALN_USERBUFSIZE 300
 // Wildcard character
 #define QPALN_WILDCARD '*'
+#define QPALN_USERBUFSIZE 300
 
+#define MIN(a,b) (((a)<(b))?(a):(b))\
+
+/**
+ * Given a username and a list of allowed link names 
+ * decide if the proposed link name is approved.
+ * @param[in] username the user name
+ * @param[in] allowed csv of (key, prefix, suffix) tuples
+ * @param[in] proposed the link source/target name to be approved
+ * @return true if the user is allowed to open this link source/target name
+ * 
+ * Concrete example
+ * user: 'bob', allowed (from spec): 'A,B,tmp-${user},C', proposed: 'tmp-bob'
+ * note that allowed above is now a tuple and not simple string fron the spec.
+ */
 bool _qd_policy_approve_link_name(const char *username, const char *allowed, const char *proposed)
 {
     // Verify string sizes are usable
     size_t p_len = strlen(proposed);
     if (p_len == 0) {
-        // degenerate case of blank name being opened. will never match anything.
+        // degenerate case of blank proposed name being opened. will never match anything.
         return false;
     }
     size_t a_len = strlen(allowed);
@@ -581,72 +613,252 @@ bool _qd_policy_approve_link_name(const char *username, const char *allowed, con
         return false;
     }
 
-    // Create a temporary writable copy of incoming allowed list
-    char t_allow[QPALN_SIZE + 1]; // temporary buffer for normal allow lists
-    int buflen = sizeof(t_allow);
-    char * pa = t_allow;
-    if (a_len >= buflen) {
-        buflen = a_len + 1;
-        pa = (char *)malloc(buflen); // malloc a buffer for larger allow lists
-        if (!pa)
-            return false;
+    size_t username_len = strlen(username);
+
+    // make a writable, disposable copy of the csv string
+    char * dup = strdup(allowed);
+    if (!dup) {
+        return false;
+    }
+    char * dupend = dup + strlen(dup);
+    char * pch = dup;
+
+    // get a scratch buffer for writing temporary match strings
+    char * pName = (char *)malloc(QPALN_SIZE);
+    if (!pName) {
+        free(dup);
+        return false;
     }
-    strcpy(pa, allowed);        /* We know we have allocated enoough space */
-    pa[a_len] = 0;
-    // Do reverse user substitution into proposed
-    char substbuf[QPALN_USERBUFSIZE];
-    char * prop2 = _qd_policy_link_user_name_subst(username, proposed, substbuf, QPALN_USERBUFSIZE);
-    char *toknext = 0;
-    char *tok = strtok_r(pa, QPALN_COMMA_SEP, &toknext);
-    assert (tok);
+    size_t pName_sz = QPALN_SIZE;
+
     bool result = false;
-    while (tok != NULL) {
-        if (*tok == QPALN_WILDCARD) {
-            result = true;
-            break;
-        }
-        int matchlen = p_len;
-        int len = strlen(tok);
-        if (tok[len-1] == QPALN_WILDCARD) {
-            matchlen = len - 1;
-            assert(len > 0);
+
+    while (pch < dupend) {
+        // the tuple strings
+        char  *pChar, *pS1, *pS2;
+        size_t sChar,  sS1,  sS2;
+
+        // extract control field
+        sChar = strcspn(pch, QPALN_COMMA_SEP);
+        if (sChar != 1) { assert(false); break;}
+        pChar = pch;
+        pChar[sChar] = '\0';
+        pch += sChar + 1;
+        if (pch >= dupend) { assert(false); break; }
+
+        // extract prefix field S1
+        sS1 = strcspn(pch, QPALN_COMMA_SEP);
+        pS1 = pch;
+        pS1[sS1] = '\0';
+        pch += sS1 + 1;
+        if (pch > dupend) { assert(false); break; }
+
+        // extract suffix field S2
+        sS2 = strcspn(pch, QPALN_COMMA_SEP);
+        pS2 = pch;
+        pch += sS2 + 1;
+        pS2[sS2] = '\0';
+
+        // compute size of generated string and make sure
+        // temporary buffer is big enough to hold it.
+        size_t sName = sS1 + username_len + sS2 + 1;
+        if (sName > pName_sz) {
+            size_t newSize = sName + QPALN_SIZE;
+            char * newPtr = (char *)realloc(pName, newSize);
+            if (!newPtr) {
+                break;
+            }
+            pName = newPtr;
+            pName_sz = newSize;
         }
-        if (strncmp(tok, proposed, matchlen) == 0) {
+
+        // if wildcard then check no more
+        if (*pChar == *user_subst_i_wildcard) {
             result = true;
             break;
         }
-        if (prop2 && strncmp(tok, prop2, matchlen) == 0) {
-            result = true;
+        // From the rule clause construct what the rule is allowing
+        // given the user name associated with this request.
+        int snpN;
+        if (*pChar == *user_subst_i_absent)
+            snpN = snprintf(pName, sName, "%s", pS1);
+        else if (*pChar == *user_subst_i_prefix)
+            snpN = snprintf(pName, sName, "%s%s", username, pS2);
+        else if (*pChar == *user_subst_i_embed)
+            snpN = snprintf(pName, sName, "%s%s%s", pS1, username, pS2);
+        else if (*pChar == *user_subst_i_suffix)
+            snpN = snprintf(pName, sName, "%s%s", pS1, username);
+        else {
+            assert(false);
             break;
         }
-        tok = strtok_r(NULL, QPALN_COMMA_SEP, &toknext);
-    }
-    if (pa != t_allow) {
-        free(pa);
+
+        size_t rule_len = MIN(snpN, sName); 
+        if (pName[rule_len-1] != QPALN_WILDCARD) {
+            // Rule clauses that do not end with wildcard 
+            // must match entire proposed name string.
+            // pName=tmp-bob-5, proposed can be only 'tmp-bob-5'
+            result = strcmp(proposed, pName) == 0;
+        } else {
+            // Rule clauses that end with wildcard
+            // must match only as many characters as the cluase without the '*'.
+            // pName=tmp*, will match proposed 'tmp', 'tmp-xxx', 'tmp-bob', ...
+            result = strncmp(proposed, pName, rule_len - 1) == 0;
+        }
+        if (result)
+            break;
     }
+    free(pName);
+    free(dup);
+
     return result;
 }
 
 
-bool _qd_policy_approve_link_name_tree(const char *username, qd_parse_tree_t *tree, const char *proposed)
+bool _qd_policy_approve_link_name_tree(const char *username, const char *allowed, const char *proposed,
+                                       qd_parse_tree_t *tree)
 {
     // Verify string sizes are usable
-    size_t p_len = strlen(proposed);
-    if (p_len == 0) {
-        // degenerate case of blank name being opened. will never match anything.
+    size_t proposed_len = strlen(proposed);
+    if (proposed_len == 0) {
+        // degenerate case of blank proposed name being opened. will never match anything.
+        return false;
+    }
+    size_t a_len = strlen(allowed);
+    if (a_len == 0) {
+        // no names in 'allowed'.
         return false;
     }
-    void * unused_payload = 0;
 
-    if (qd_parse_tree_retrieve_match_str(tree, proposed, &unused_payload))
-        return true;
+    // Regardless of how many rule clauses are specified only three match
+    // patterns must be checked: no user subst, prefix subst, and suffix subst.
+    bool need_check_nosubst = true;
+    bool need_check_prefix  = true;
+    bool need_check_suffix  = true;
 
-    // Do reverse user substitution into proposed
-    char substbuf[QPALN_USERBUFSIZE];
-    char * prop2 = _qd_policy_link_user_name_subst(username, proposed, substbuf, QPALN_USERBUFSIZE);
-    if (prop2 && qd_parse_tree_retrieve_match_str(tree, prop2, &unused_payload))
-        return true;
-    return false;
+    size_t username_len = strlen(username);
+    size_t usersubst_len = strlen(user_subst_key);
+
+    // make a writable, disposable copy of the csv string
+    char * dup = strdup(allowed);
+    if (!dup) {
+        return false;
+    }
+    char * dupend = dup + strlen(dup);
+    char * pch = dup;
+
+    // get a scratch buffer for writing temporary match strings
+    char * pName = (char *)malloc(QPALN_SIZE);
+    if (!pName) {
+        free(dup);
+        return false;
+    }
+    size_t pName_sz = QPALN_SIZE;
+
+    bool result = false;
+
+    while (pch < dupend) {
+        // the tuple strings
+        char  *pChar, *pS1, *pS2;
+        size_t sChar,  sS1,  sS2;
+
+        // extract control field
+        sChar = strcspn(pch, QPALN_COMMA_SEP);
+        if (sChar != 1) { assert(false); break;}
+        pChar = pch;
+        pChar[sChar] = '\0';
+        pch += sChar + 1;
+        if (pch >= dupend) { assert(false); break; }
+
+        // extract prefix field S1
+        sS1 = strcspn(pch, QPALN_COMMA_SEP);
+        pS1 = pch;
+        pS1[sS1] = '\0';
+        pch += sS1 + 1;
+        if (pch > dupend) { assert(false); break; }
+
+        // extract suffix field S2
+        sS2 = strcspn(pch, QPALN_COMMA_SEP);
+        pS2 = pch;
+        pch += sS2 + 1;
+        pS2[sS2] = '\0';
+
+        // compute size of generated string and make sure
+        // temporary buffer is big enough to hold it.
+        size_t sName = proposed_len + usersubst_len + 1;
+        if (sName > pName_sz) {
+            size_t newSize = sName + QPALN_SIZE;
+            char * newPtr = (char *)realloc(pName, newSize);
+            if (!newPtr) {
+                break;
+            }
+            pName = newPtr;
+            pName_sz = newSize;
+        }
+
+        // From the rule clause construct what the rule is allowing
+        // given the user name associated with this request.
+        if (*pChar == *user_subst_i_absent && need_check_nosubst) {
+            need_check_nosubst = false;
+            // Substitution spec is absent. The search string is the literal
+            // S1 in the rule.
+            snprintf(pName, sName, "%s", proposed);
+        }
+        else if (*pChar == *user_subst_i_prefix && need_check_prefix) {
+            need_check_prefix = false;
+            // Substitution spec is prefix.
+            if (strncmp(proposed, username, username_len) != 0)
+                continue; // Denied. Proposed does not have username prefix.
+            // Check that username is not part of a larger token.
+            if (username_len == proposed_len) {
+                // If username is the whole link name then allow if lookup is ok
+            } else {
+                // Proposed is longer than username. Make sure that proposed
+                // is delimited after user name.
+                if (!is_token_sep(proposed[username_len])) {
+                    continue; // denied. proposed has username prefix it it not a delimited user name
+                }
+            }
+            snprintf(pName, sName, "%s%s", user_subst_key, proposed + username_len);
+        }
+        else if (*pChar == *user_subst_i_embed) {
+            assert(false); // not supported
+        }
+        else if (*pChar == *user_subst_i_suffix && need_check_suffix) {
+            need_check_suffix = false;
+            // Check that link name has username suffix
+            if (username_len > proposed_len) {
+                continue; // denied. proposed name is too short to hold username
+            } else {
+                //---
+                // if (username_len == proposed_len) { ... }
+                // unreachable code. substitution-only rule clause is handled by prefix
+                //---
+                if (!is_token_sep(proposed[proposed_len - username_len - 1])) {
+                    continue; // denied. proposed suffix it it not a delimited user name
+                }
+                if (strncmp(&proposed[proposed_len - username_len], username, username_len) != 0) {
+                    continue; // denied. username is not the suffix
+                }
+            }
+            pName[0] = '\0';
+            strncat(pName, proposed, proposed_len - username_len);
+            strcat(pName, user_subst_key);
+        }
+        else {
+            assert(false);
+            break;
+        }
+
+        void * unused_payload = 0;
+        result = qd_parse_tree_retrieve_match_str(tree, pName, &unused_payload);
+        if (result)
+            break;
+    }
+    free(pName);
+    free(dup);
+
+    return result;
 }
 
 
@@ -828,13 +1040,13 @@ bool qd_policy_approve_link_name(const char *username,
 {
     if (isReceiver) {
         if (settings->sourceParseTree) {
-            return _qd_policy_approve_link_name_tree(username, settings->sourceParseTree, proposed);
+            return _qd_policy_approve_link_name_tree(username, settings->sourcePattern, proposed, settings->sourceParseTree);
         } else if (settings->sources) {
             return _qd_policy_approve_link_name(username, settings->sources, proposed);
         }
     } else {
         if (settings->targetParseTree) {
-            return _qd_policy_approve_link_name_tree(username, settings->targetParseTree, proposed);
+            return _qd_policy_approve_link_name_tree(username, settings->targetPattern, proposed, settings->targetParseTree);
         } else if (settings->targets) {
             return _qd_policy_approve_link_name(username, settings->targets, proposed);
         }
@@ -889,3 +1101,83 @@ char * qd_policy_host_pattern_lookup(qd_policy_t *policy, char *hostPattern)
            hostPattern, (payload ? (char *)payload : "null"));
     return payload;
 }
+
+
+// Convert naked CSV allow list into parsed settings 3-tuple
+// Note that this logic is also present in python compile_app_settings.
+char * qd_policy_compile_allowed_csv(char * csv)
+{
+    size_t csv_len = strlen(csv);
+    size_t usersubst_len = strlen(user_subst_key);
+
+    size_t n_commas = 0;
+    char * pch = strchr(csv, *QPALN_COMMA_SEP);
+    while (pch != NULL) {
+        n_commas++;
+        pch = strchr(pch + 1, *QPALN_COMMA_SEP);
+    }
+
+    size_t result_size = csv_len + 3 * (n_commas + 1) + 1; // each token gets ctrl char and 2 commas
+    char * result = (char *)malloc(result_size);
+    if (!result)
+        return NULL;
+    result[0] = '\0';
+
+    char * dup = strdup(csv);
+    if (!dup) {
+        free(result);
+        return NULL;
+    }
+    char * dupend = dup + csv_len;
+
+    size_t tok_size = 0;
+    char * sep = "";
+    for (pch = dup; pch < dupend; pch += tok_size + 1) {
+        // isolate token
+        char * pcomma = strchr(pch, *QPALN_COMMA_SEP);
+        if (!pcomma) pcomma = dupend;
+        *pcomma = '\0';
+        tok_size = pcomma - pch;
+
+        strcat(result, sep);
+        sep = ",";
+        char * psubst = strstr(pch, user_subst_key);
+        if (psubst) {
+            // substitute token is present
+            if (psubst == pch) {
+                // token is a prefix
+                strcat(result, user_subst_i_prefix);
+                strcat(result, ",,");
+                strcat(result, pch + usersubst_len);
+            } else if (psubst == pch + tok_size - usersubst_len) {
+                // token is a suffix
+                strcat(result, user_subst_i_suffix);
+                strcat(result, ",");
+                strncat(result, pch, tok_size - usersubst_len);
+                strcat(result, ",");
+            } else {
+                // token is embedded
+                strcat(result, user_subst_i_embed);
+                strcat(result, ",");
+                strncat(result, pch, psubst - pch);
+                strcat(result, ",");
+                strncat(result, psubst + usersubst_len, tok_size - (psubst - pch) - usersubst_len);
+            }
+        } else {
+            // substitute token is absent
+            if (strcmp(pch, user_subst_i_wildcard) == 0) {
+                // token is wildcard
+                strcat(result, user_subst_i_wildcard);
+                strcat(result, ",,");
+            } else {
+                // token is ordinary string
+                strcat(result, user_subst_i_absent);
+                strcat(result, ",");
+                strcat(result, pch);
+                strcat(result, ",");
+            }
+        }
+    }
+    free(dup);
+    return result;
+}

http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/3803dc8c/src/policy.h
----------------------------------------------------------------------
diff --git a/src/policy.h b/src/policy.h
index 98500ec..c24ad6b 100644
--- a/src/policy.h
+++ b/src/policy.h
@@ -202,4 +202,15 @@ void qd_policy_host_pattern_remove(qd_policy_t *policy, char *hostPattern);
  * @return the name of the ruleset whose hostname pattern matched this actual hostname
  */
 char * qd_policy_host_pattern_lookup(qd_policy_t *policy, char *hostPattern);
+
+/**
+ * Compile raw CSV spec of allowed sources/targets and return
+ * the string of tuples used by policy runtime.
+ * The returned string is allocated here and freed by the caller.
+ * This function does no error checking or logging.
+ *
+ * @param[in] csv the CSV allowed list
+ * @return the ruleset string to be used in policy settings.
+ */
+char * qd_policy_compile_allowed_csv(char * csv);
 #endif

http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/3803dc8c/src/policy_internal.h
----------------------------------------------------------------------
diff --git a/src/policy_internal.h b/src/policy_internal.h
index 100338f..c075a75 100644
--- a/src/policy_internal.h
+++ b/src/policy_internal.h
@@ -110,6 +110,7 @@ bool _qd_policy_approve_link_name(const char *username, const char *allowed, con
  * @param[in] username authenticated user name
  * @param[in] allowed policy settings source/target string in packed CSV form.
  * @param[in] proposed the link target name to be approved
+ * @param[in] tree the parse tree for this source/target names
  */
-bool _qd_policy_approve_link_name_tree(const char *username, qd_parse_tree_t *tree, const char *proposed);
+bool _qd_policy_approve_link_name_tree(const char *username, const char *allowed, const char *proposed, qd_parse_tree_t *tree);
 #endif

http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/3803dc8c/src/remote_sasl.c
----------------------------------------------------------------------
diff --git a/src/remote_sasl.c b/src/remote_sasl.c
index fd9da26..239891c 100644
--- a/src/remote_sasl.c
+++ b/src/remote_sasl.c
@@ -249,10 +249,10 @@ static void set_policy_settings(pn_connection_t* conn, permissions_t* permission
         ZERO(qd_conn->policy_settings->denialCounts);
 
         if (permissions->targets.start && permissions->targets.capacity) {
-            qd_conn->policy_settings->targets = strdup(permissions->targets.start);
+            qd_conn->policy_settings->targets = qd_policy_compile_allowed_csv(permissions->targets.start);
         }
         if (permissions->sources.start && permissions->sources.capacity) {
-            qd_conn->policy_settings->sources = strdup(permissions->sources.start);
+            qd_conn->policy_settings->sources = qd_policy_compile_allowed_csv(permissions->sources.start);
         }
         qd_conn->policy_settings->allowDynamicSource = true;
         qd_conn->policy_settings->allowAnonymousSender = true;

http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/3803dc8c/tests/policy_test.c
----------------------------------------------------------------------
diff --git a/tests/policy_test.c b/tests/policy_test.c
index ca73f32..377e474 100644
--- a/tests/policy_test.c
+++ b/tests/policy_test.c
@@ -26,6 +26,11 @@
 
 static char *test_link_name_lookup(void *context)
 {
+    // DISPATCH-1011: approval specifications are now CSV concatenated 3-tuples:
+    // (user-subst-code, prefix, suffix)
+    // codes are 'a'bsent, 'p'refix, 's'uffix, 'e'mbedded, '*'wildcard
+    // OLD: 'joe', NEW: 'a,joe,'
+
     // Degenerate blank names
     if (_qd_policy_approve_link_name("a", "a", ""))
 	return "blank proposed name not rejected";
@@ -33,31 +38,34 @@ static char *test_link_name_lookup(void *context)
 	return "blank allowed list not rejected";
 
     // Easy matches
-    if (!_qd_policy_approve_link_name("", "joe", "joe"))
+    if (!_qd_policy_approve_link_name("", "a,joe,", "joe"))
         return "proposed link 'joe' should match allowed links 'joe' but does not";
-    if (_qd_policy_approve_link_name("", "joe", "joey"))
+    if (_qd_policy_approve_link_name("", "a,joe,", "joey"))
         return "proposed link 'joey' should not match allowed links 'joe' but does";
 
     // Wildcard matches
-    if (!_qd_policy_approve_link_name("", "joe*", "joey"))
+    if (!_qd_policy_approve_link_name("", "a,joe*,", "joey"))
         return "proposed link 'joey' should match allowed links 'joe*' but does not";
-    if (!_qd_policy_approve_link_name("", "joe*", "joezzzZZZ"))
+    if (!_qd_policy_approve_link_name("", "a,joe*,", "joezzzZZZ"))
         return "proposed link 'joezzzZZZ' should match allowed links 'joe*' but does not";
-    if (!_qd_policy_approve_link_name("", "joe,*", "joey"))
+    if (!_qd_policy_approve_link_name("", "a,joe,,*,,", "joey"))
         return "proposed link 'joey' should match allowed links 'joe,*' but does not";
 
     // Deeper match
-    if (!_qd_policy_approve_link_name("", "no1,no2,no3,yes,no4", "yes"))
+    if (!_qd_policy_approve_link_name("", "a,no1,,a,no2,,a,no3,,a,yes,,a,no4,", "yes"))
         return "proposed link 'yes' should match allowed links 'no1,no2,no3,yes,no4' but does not";
 
     // Deeeper match - triggers malloc/free internal handler
-    char * bufp = (char *)malloc(512 * 5 + 6);
+#define BIG_N 512
+    char * bufp = (char *)malloc(BIG_N * 8 + 6);
+    if (!bufp)
+        return "failed to allocate buffer for large link name test";
     char * wp = bufp;
     int i;
-    for (i=0; i<512; i++) {
-        wp += sprintf(wp, "n%03d,", i);
+    for (i=0; i<BIG_N; i++) {
+        wp += sprintf(wp, "a,n%03d,,", i);
     }
-    sprintf(wp, "yes");
+    sprintf(wp, "a,yes,");
     if (!_qd_policy_approve_link_name("", bufp, "yes")) {
         free(bufp);
         return "proposed link 'yes' should match allowed large list but does not";
@@ -65,15 +73,17 @@ static char *test_link_name_lookup(void *context)
     free(bufp);
 
     // Substitute a user name
-    if (!_qd_policy_approve_link_name("chuck", "ab${user}xyz", "abchuckxyz"))
+    if (!_qd_policy_approve_link_name("chuck", "e,ab,xyz", "abchuckxyz"))
         return "proposed link 'abchuckxyz' should match allowed links with ${user} but does not";
-    if (!_qd_policy_approve_link_name("chuck", "${user}xyz", "chuckxyz"))
+    if (!_qd_policy_approve_link_name("chuck", "p,,xyz", "chuckxyz"))
         return "proposed link 'chuckxyz' should match allowed links with ${user} but does not";
-    if (!_qd_policy_approve_link_name("chuck", "ab${user}", "abchuck"))
+    if (!_qd_policy_approve_link_name("chuck", "s,ab,", "abchuck"))
         return "proposed link 'abchuck' should match allowed links with ${user} but does not";
+//    if (!_qd_policy_approve_link_name("em", "temp-${user}", "temp-em"))
+//        return "proposed link 'temp-em' should match allowed links with ${user} but does not";
 
     // Combine user name and wildcard
-    if (!_qd_policy_approve_link_name("chuck", "ab${user}*", "abchuckzyxw"))
+    if (!_qd_policy_approve_link_name("chuck", "e,ab,*", "abchuckzyxw"))
         return "proposed link 'abchuckzyxw' should match allowed links with ${user}* but does not";
 
     return 0;
@@ -85,23 +95,64 @@ static char *test_link_name_tree_lookup(void *context)
     qd_parse_tree_t *node = qd_parse_tree_new(QD_PARSE_TREE_ADDRESS);
     void *payload = (void*)1;
 
-    qd_parse_tree_add_pattern_str(node, "ab${user}xyz", payload);
+    qd_parse_tree_add_pattern_str(node, "${user}.xyz", payload);
+
+    if (!_qd_policy_approve_link_name_tree("chuck", "p,,.xyz", "chuck.xyz", node))
+        return "proposed link 'chuck.xyz' should tree-match allowed links with ${user} but does not";
 
-    if (!_qd_policy_approve_link_name_tree("chuck", node, "abchuckxyz"))
-        return "proposed link 'abchuckxyz' should tree-match allowed links with ${user} but does not";
+    if (_qd_policy_approve_link_name_tree("chuck", "p,,.xyz", "chuck.xyz.ynot", node))
+        return "proposed link 'chuck.xyz.ynot' should not tree-match allowed links with ${user} but does";
 
     qd_parse_tree_add_pattern_str(node, "${user}.#", payload);
 
-    if (!_qd_policy_approve_link_name_tree("motronic", node, "motronic"))
+    if (!_qd_policy_approve_link_name_tree("motronic", "p,,.#", "motronic", node))
         return "proposed link 'motronic' should tree-match allowed links with ${user} but does not";
 
-    if (!_qd_policy_approve_link_name_tree("motronic", node, "motronic.stubs.wobbler"))
+    if (!_qd_policy_approve_link_name_tree("motronic", "p,,.#", "motronic.stubs.wobbler", node))
         return "proposed link 'motronic.stubs.wobbler' should tree-match allowed links with ${user} but does not";
 
     return 0;
 }
 
 
+static char *test_link_name_csv_parser(void *context)
+{
+    char * result;
+
+    result = qd_policy_compile_allowed_csv("ttt");
+    if (!!strcmp(result, "a,ttt,"))
+        return "simple csv with no subst failed";
+    free(result);
+
+    result = qd_policy_compile_allowed_csv("ttt,uuu,vvvv");
+    if (!!strcmp(result, "a,ttt,,a,uuu,,a,vvvv,"))
+        return "moderate csv with no subst failed";
+    free(result);
+
+    result = qd_policy_compile_allowed_csv("*");
+    if (!!strcmp(result, "*,,"))
+        return "wildcard csv failed";
+    free(result);
+
+    result = qd_policy_compile_allowed_csv("${user}-temp");
+    if (!!strcmp(result, "p,,-temp"))
+        return "csv with prefix subst failed";
+    free(result);
+
+    result = qd_policy_compile_allowed_csv("temp-${user}");
+    if (!!strcmp(result, "s,temp-,"))
+        return "csv with suffix subst failed";
+    free(result);
+
+    result = qd_policy_compile_allowed_csv("temp-${user}-home");
+    if (!!strcmp(result, "e,temp-,-home"))
+        return "csv with embedded subst failed";
+    free(result);
+
+    return 0;
+}
+
+
 int policy_tests(void)
 {
     int result = 0;
@@ -109,6 +160,7 @@ int policy_tests(void)
 
     TEST_CASE(test_link_name_lookup, 0);
     TEST_CASE(test_link_name_tree_lookup, 0);
+    TEST_CASE(test_link_name_csv_parser, 0);
 
     return result;
 }

http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/3803dc8c/tests/router_policy_test.py
----------------------------------------------------------------------
diff --git a/tests/router_policy_test.py b/tests/router_policy_test.py
index a437668..a8331a0 100644
--- a/tests/router_policy_test.py
+++ b/tests/router_policy_test.py
@@ -165,8 +165,8 @@ class PolicyFile(TestCase):
         self.assertTrue(upolicy['maxReceivers']             == 44)
         self.assertTrue(upolicy['allowAnonymousSender'])
         self.assertTrue(upolicy['allowDynamicSource'])
-        self.assertTrue(upolicy['targets'] == 'private')
-        self.assertTrue(upolicy['sources'] == 'private')
+        self.assertTrue(upolicy['targets'] == 'a,private,')
+        self.assertTrue(upolicy['sources'] == 'a,private,')
 
     def test_policy1_test_zeke_bad_IP(self):
         self.assertTrue(
@@ -225,8 +225,8 @@ class PolicyFileApplicationFallback(TestCase):
         self.assertTrue(upolicy['maxReceivers']             == 44)
         self.assertTrue(upolicy['allowAnonymousSender'])
         self.assertTrue(upolicy['allowDynamicSource'])
-        self.assertTrue(upolicy['targets'] == 'private')
-        self.assertTrue(upolicy['sources'] == 'private')
+        self.assertTrue(upolicy['targets'] == 'a,private,')
+        self.assertTrue(upolicy['sources'] == 'a,private,')
 
         # Disable fallback and show failure again
         self.policy.set_default_vhost('')


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