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 2012/08/20 22:09:44 UTC

svn commit: r1375195 - in /qpid/trunk/qpid/cpp/src: qpid/acl/AclData.cpp qpid/acl/AclData.h qpid/acl/AclReader.cpp qpid/broker/AclModule.h tests/acl.py

Author: chug
Date: Mon Aug 20 20:09:43 2012
New Revision: 1375195

URL: http://svn.apache.org/viewvc?rev=1375195&view=rev
Log:
QPID-4230 (review 6645) Username substition keywords in Acl file.


Modified:
    qpid/trunk/qpid/cpp/src/qpid/acl/AclData.cpp
    qpid/trunk/qpid/cpp/src/qpid/acl/AclData.h
    qpid/trunk/qpid/cpp/src/qpid/acl/AclReader.cpp
    qpid/trunk/qpid/cpp/src/qpid/broker/AclModule.h
    qpid/trunk/qpid/cpp/src/tests/acl.py

Modified: qpid/trunk/qpid/cpp/src/qpid/acl/AclData.cpp
URL: http://svn.apache.org/viewvc/qpid/trunk/qpid/cpp/src/qpid/acl/AclData.cpp?rev=1375195&r1=1375194&r2=1375195&view=diff
==============================================================================
--- qpid/trunk/qpid/cpp/src/qpid/acl/AclData.cpp (original)
+++ qpid/trunk/qpid/cpp/src/qpid/acl/AclData.cpp Mon Aug 20 20:09:43 2012
@@ -25,6 +25,13 @@ namespace qpid {
 namespace acl {
 
     //
+    // Instantiate the substitution keyword string
+    //
+    const std::string AclData::USER_SUBSTITUTION_KEYWORD       = "${user}";
+    const std::string AclData::DOMAIN_SUBSTITUTION_KEYWORD     = "${domain}";
+    const std::string AclData::USERDOMAIN_SUBSTITUTION_KEYWORD = "${userdomain}";
+
+    //
     // constructor
     //
     AclData::AclData():
@@ -147,7 +154,17 @@ namespace acl {
                         // the calling args and not in the param map.
                         if (rulePropMapItr->first == acl::SPECPROP_NAME)
                         {
-                            if (matchProp(rulePropMapItr->second, name))
+                            // substitute user name into object name
+                            bool result;
+                            if (rsItr->ruleHasUserSub[PROP_NAME]) {
+                                std::string sName(rulePropMapItr->second);
+                                substituteUserId(sName, id);
+                                result = matchProp(sName, name);
+                            } else {
+                                result = matchProp(rulePropMapItr->second, name);
+                            }
+
+                            if (result)
                             {
                                 QPID_LOG(debug, "ACL: lookup name '" << name
                                     << "' matched with rule name '"
@@ -222,7 +239,20 @@ namespace acl {
                                         break;
 
                                     default:
-                                        if (matchProp(rulePropMapItr->second, lookupParamItr->second))
+                                        bool result;
+                                        if ((SPECPROP_ALTERNATE  == rulePropMapItr->first && rsItr->ruleHasUserSub[PROP_ALTERNATE])  ||
+                                            (SPECPROP_ROUTINGKEY == rulePropMapItr->first && rsItr->ruleHasUserSub[PROP_ROUTINGKEY]) ||
+                                            (SPECPROP_QUEUENAME  == rulePropMapItr->first && rsItr->ruleHasUserSub[PROP_QUEUENAME]))
+                                        {
+                                            // These properties are allowed to have username substitution
+                                            std::string sName(rulePropMapItr->second);
+                                            substituteUserId(sName, id);
+                                            result = matchProp(sName, lookupParamItr->second);
+                                        } else {
+                                            result = matchProp(rulePropMapItr->second, lookupParamItr->second);
+                                        }
+
+                                        if (result)
                                         {
                                             QPID_LOG(debug, "ACL: the pair("
                                                 << AclHelper::getPropertyStr(lookupParamItr->first)
@@ -346,7 +376,18 @@ namespace acl {
                     bool match =true;
                     if (rsItr->pubExchNameInRule)
                     {
-                        if (matchProp(rsItr->pubExchName, name))
+                        // substitute user name into object name
+                        bool result;
+
+                        if (rsItr->ruleHasUserSub[PROP_NAME]) {
+                            std::string sName(rsItr->pubExchName);
+                            substituteUserId(sName, id);
+                            result = matchProp(sName, name);
+                        } else {
+                            result = matchProp(rsItr->pubExchName, name);
+                        }
+
+                        if (result)
                         {
                             QPID_LOG(debug, "ACL: Rule: " << rsItr->rawRuleNum << " lookup exchange name '"
                                 << name << "' matched with rule name '"
@@ -364,18 +405,40 @@ namespace acl {
 
                     if (match && rsItr->pubRoutingKeyInRule)
                     {
-                        if (rsItr->matchRoutingKey(routingKey))
+                        if ((routingKey.find(USER_SUBSTITUTION_KEYWORD, 0)       != std::string::npos) ||
+                            (routingKey.find(DOMAIN_SUBSTITUTION_KEYWORD, 0)     != std::string::npos) ||
+                            (routingKey.find(USERDOMAIN_SUBSTITUTION_KEYWORD, 0) != std::string::npos))
                         {
-                            QPID_LOG(debug, "ACL: Rule: " << rsItr->rawRuleNum << " lookup key name '"
-                                << routingKey << "' matched with rule routing key '"
-                                << rsItr->pubRoutingKey << "'");
+                            // The user is not allowed to present a routing key with the substitution key in it
+                            QPID_LOG(debug, "ACL: Rule: " << rsItr->rawRuleNum <<
+                                " User-specified routing key has substitution wildcard:" << routingKey
+                                << ". Rule match prohibited.");
+                            match = false;
                         }
                         else
                         {
-                            QPID_LOG(debug, "ACL: Rule: " << rsItr->rawRuleNum << " lookup key name '"
-                                << routingKey << "' did not match with rule routing key '"
-                                << rsItr->pubRoutingKey << "'");
-                            match = false;
+                            bool result;
+                            if (rsItr->ruleHasUserSub[PROP_ROUTINGKEY]) {
+                                std::string sKey(routingKey);
+                                substituteKeywords(sKey, id);
+                                result = rsItr->matchRoutingKey(sKey);
+                            } else {
+                                result = rsItr->matchRoutingKey(routingKey);
+                            }
+
+                            if (result)
+                            {
+                                QPID_LOG(debug, "ACL: Rule: " << rsItr->rawRuleNum << " lookup key name '"
+                                    << routingKey << "' matched with rule routing key '"
+                                    << rsItr->pubRoutingKey << "'");
+                            }
+                            else
+                            {
+                                QPID_LOG(debug, "ACL: Rule: " << rsItr->rawRuleNum << " lookup key name '"
+                                    << routingKey << "' did not match with rule routing key '"
+                                    << rsItr->pubRoutingKey << "'");
+                                match = false;
+                            }
                         }
                     }
 
@@ -501,4 +564,102 @@ namespace acl {
         return true;
     }
 
+    const std::string DOMAIN_SEPARATOR("@");
+    const std::string PERIOD(".");
+    const std::string UNDERSCORE("_");
+    //
+    // substituteString
+    //   Given a name string from an Acl rule, substitute the replacement into it
+    //   wherever the placeholder directs.
+    //
+    void AclData::substituteString(std::string& targetString,
+                                   const std::string& placeholder,
+                                   const std::string& replacement)
+    {
+        assert (!placeholder.empty());
+        if (placeholder.empty())
+            return;
+        size_t start_pos(0);
+        while((start_pos = targetString.find(placeholder, start_pos)) != std::string::npos)
+        {
+            targetString.replace(start_pos, placeholder.length(), replacement);
+            start_pos += replacement.length();
+        }
+    }
+
+
+    //
+    // normalizeUserId
+    //   Given a name string return it in a form usable as topic keys:
+    //     change "@" and "." to "_".
+    //
+    std::string AclData::normalizeUserId(const std::string& userId)
+    {
+        std::string normalId(userId);
+        substituteString(normalId, DOMAIN_SEPARATOR, UNDERSCORE);
+        substituteString(normalId, PERIOD,           UNDERSCORE);
+        return normalId;
+    }
+
+
+    //
+    // substituteUserId
+    //   Given an Acl rule and an authenticated userId
+    //   do the keyword substitutions on the rule.
+    //
+    void AclData::AclData::substituteUserId(std::string& ruleString,
+                                            const std::string& userId)
+    {
+        size_t locDomSeparator(0);
+        std::string user("");
+        std::string domain("");
+        std::string userdomain = normalizeUserId(userId);
+
+        locDomSeparator = userId.find(DOMAIN_SEPARATOR);
+        if (std::string::npos == locDomSeparator) {
+            // "@" not found. There's just a user name
+            user   = normalizeUserId(userId);
+        } else {
+            // "@" found, split the names. Domain may be blank.
+            user   = normalizeUserId(userId.substr(0,locDomSeparator));
+            domain = normalizeUserId(userId.substr(locDomSeparator+1));
+        }
+
+        substituteString(ruleString, USER_SUBSTITUTION_KEYWORD,       user);
+        substituteString(ruleString, DOMAIN_SUBSTITUTION_KEYWORD,     domain);
+        substituteString(ruleString, USERDOMAIN_SUBSTITUTION_KEYWORD, userdomain);
+    }
+
+
+    //
+    // substituteKeywords
+    //   Given an Acl rule and an authenticated userId
+    //   do reverse keyword substitutions on the rule.
+    //   That is, replace the normalized name in the rule string with
+    //   the keyword that represents it. This stragegy is used for
+    //   topic key lookups where the keyword string proper is in the
+    //   topic key search tree.
+    //
+    void AclData::AclData::substituteKeywords(std::string& ruleString,
+                                              const std::string& userId)
+    {
+        size_t locDomSeparator(0);
+        std::string user("");
+        std::string domain("");
+        std::string userdomain = normalizeUserId(userId);
+
+        locDomSeparator = userId.find(DOMAIN_SEPARATOR);
+        if (std::string::npos == locDomSeparator) {
+            // "@" not found. There's just a user name
+            user   = normalizeUserId(userId);
+        } else {
+            // "@" found, split the names
+            user   = normalizeUserId(userId.substr(0,locDomSeparator));
+            domain = normalizeUserId(userId.substr(locDomSeparator+1));
+        }
+        std::string oRule(ruleString);
+        substituteString(ruleString, userdomain, USERDOMAIN_SUBSTITUTION_KEYWORD);
+        substituteString(ruleString, user,       USER_SUBSTITUTION_KEYWORD);
+        substituteString(ruleString, domain,     DOMAIN_SUBSTITUTION_KEYWORD);
+    }
 }}

Modified: qpid/trunk/qpid/cpp/src/qpid/acl/AclData.h
URL: http://svn.apache.org/viewvc/qpid/trunk/qpid/cpp/src/qpid/acl/AclData.h?rev=1375195&r1=1375194&r2=1375195&view=diff
==============================================================================
--- qpid/trunk/qpid/cpp/src/qpid/acl/AclData.h (original)
+++ qpid/trunk/qpid/cpp/src/qpid/acl/AclData.h Mon Aug 20 20:09:43 2012
@@ -62,6 +62,7 @@ public:
         boost::shared_ptr<topicTester> pTTest;
         bool                  pubExchNameInRule;
         std::string           pubExchName;
+        std::vector<bool>     ruleHasUserSub;
 
         Rule (int ruleNum, qpid::acl::AclResult res, specPropertyMap& p) :
             rawRuleNum(ruleNum),
@@ -71,7 +72,8 @@ public:
             pubRoutingKey(),
             pTTest(boost::shared_ptr<topicTester>(new topicTester())),
             pubExchNameInRule(false),
-            pubExchName()
+            pubExchName(),
+            ruleHasUserSub(PROPERTYSIZE, false)
             {}
 
 
@@ -132,6 +134,17 @@ public:
 
     bool matchProp(const std::string & src, const std::string& src1);
     void clear ();
+    static const std::string USER_SUBSTITUTION_KEYWORD;
+    static const std::string DOMAIN_SUBSTITUTION_KEYWORD;
+    static const std::string USERDOMAIN_SUBSTITUTION_KEYWORD;
+    void substituteString(std::string& targetString,
+                          const std::string& placeholder,
+                          const std::string& replacement);
+    std::string normalizeUserId(const std::string& userId);
+    void substituteUserId(std::string& ruleString,
+                          const std::string& userId);
+    void substituteKeywords(std::string& ruleString,
+                            const std::string& userId);
 
     AclData();
     virtual ~AclData();

Modified: qpid/trunk/qpid/cpp/src/qpid/acl/AclReader.cpp
URL: http://svn.apache.org/viewvc/qpid/trunk/qpid/cpp/src/qpid/acl/AclReader.cpp?rev=1375195&r1=1375194&r2=1375195&view=diff
==============================================================================
--- qpid/trunk/qpid/cpp/src/qpid/acl/AclReader.cpp (original)
+++ qpid/trunk/qpid/cpp/src/qpid/acl/AclReader.cpp Mon Aug 20 20:09:43 2012
@@ -103,6 +103,15 @@ namespace acl {
             } else {
                 AclData::Rule rule(cnt, (*i)->res, (*i)->props);
 
+                // Record which properties have the user substitution string
+                for (pmCitr pItr=rule.props.begin(); pItr!=rule.props.end(); pItr++) {
+                    if ((pItr->second.find(AclData::USER_SUBSTITUTION_KEYWORD, 0)       != std::string::npos) ||
+                        (pItr->second.find(AclData::DOMAIN_SUBSTITUTION_KEYWORD, 0)     != std::string::npos) ||
+                        (pItr->second.find(AclData::USERDOMAIN_SUBSTITUTION_KEYWORD, 0) != std::string::npos)) {
+                        rule.ruleHasUserSub[pItr->first] = true;
+                    }
+                }
+
                 // Action -> Object -> map<user -> set<Rule> >
                 std::ostringstream actionstr;
                 for (int acnt = ((*i)->actionAll ? 0 : (*i)->action);

Modified: qpid/trunk/qpid/cpp/src/qpid/broker/AclModule.h
URL: http://svn.apache.org/viewvc/qpid/trunk/qpid/cpp/src/qpid/broker/AclModule.h?rev=1375195&r1=1375194&r2=1375195&view=diff
==============================================================================
--- qpid/trunk/qpid/cpp/src/qpid/broker/AclModule.h (original)
+++ qpid/trunk/qpid/cpp/src/qpid/broker/AclModule.h Mon Aug 20 20:09:43 2012
@@ -78,7 +78,9 @@ namespace acl {
         PROP_SCHEMACLASS,
         PROP_POLICYTYPE,
         PROP_MAXQUEUESIZE,
-        PROP_MAXQUEUECOUNT };
+        PROP_MAXQUEUECOUNT,
+        PROPERTYSIZE           // PROPERTYSIZE must be last in list
+    };
 
     // Property used in ACL spec file
     // Note for properties common to file processing/rule storage and to

Modified: qpid/trunk/qpid/cpp/src/tests/acl.py
URL: http://svn.apache.org/viewvc/qpid/trunk/qpid/cpp/src/tests/acl.py?rev=1375195&r1=1375194&r2=1375195&view=diff
==============================================================================
--- qpid/trunk/qpid/cpp/src/tests/acl.py (original)
+++ qpid/trunk/qpid/cpp/src/tests/acl.py Mon Aug 20 20:09:43 2012
@@ -1704,6 +1704,546 @@ class ACLTests(TestBase010):
             result = None
 
 
+   #=====================================
+   # User name substitution
+   #=====================================
+
+    def test_user_name_substitution(self):
+        """
+        Test name substitution internals, limits, and edge cases.
+        """
+        aclf = self.get_acl_file()
+        aclf.write('# begin hack alert: allow anonymous to access the lookup debug functions\n')
+        aclf.write('acl allow-log anonymous create  queue\n')
+        aclf.write('acl allow-log anonymous all     exchange name=qmf.*\n')
+        aclf.write('acl allow-log anonymous all     exchange name=amq.direct\n')
+        aclf.write('acl allow-log anonymous all     exchange name=qpid.management\n')
+        aclf.write('acl allow-log anonymous access  method   name=*\n')
+        aclf.write('# end hack alert\n')
+        aclf.write('acl allow all create queue    name=tmp-${userdomain}\n')
+        aclf.write('acl allow all create queue    name=${userdomain}-tmp\n')
+        aclf.write('acl allow all create queue    name=tmp-${userdomain}-tmp\n')
+        aclf.write('acl allow all create queue    name=tmp-${userdomain}-tmp-${userdomain}\n')
+        aclf.write('acl allow all create  queue    name=temp0-${userdomain}\n')
+        aclf.write('acl allow all access  queue    name=temp0-${userdomain}\n')
+        aclf.write('acl allow all purge   queue    name=temp0-${userdomain}\n')
+        aclf.write('acl allow all consume queue    name=temp0-${userdomain}\n')
+        aclf.write('acl allow all delete  queue    name=temp0-${userdomain}\n')
+        aclf.write('acl allow all create  exchange name=temp0-${userdomain}\n')
+        aclf.write('acl allow all access  exchange name=temp0-${userdomain}\n')
+        aclf.write('acl allow all bind    exchange name=temp0-${userdomain}\n')
+        aclf.write('acl allow all unbind  exchange name=temp0-${userdomain}\n')
+        aclf.write('acl allow all delete  exchange name=temp0-${userdomain}\n')
+        aclf.write('acl allow all publish exchange name=temp0-${userdomain}\n')
+
+        aclf.write('acl allow all   publish exchange name=X routingkey=${userdomain}.cd.e\n')
+        aclf.write('acl allow all   publish exchange name=X routingkey=a.*.${userdomain}\n')
+        aclf.write('acl allow all   publish exchange name=X routingkey=b.#.${userdomain}\n')
+        aclf.write('acl allow all   publish exchange name=X routingkey=*.${userdomain}.#.y\n')
+
+        aclf.write('acl allow all   create  queue    name=user-${user}\n')
+        aclf.write('acl allow all   publish exchange name=U routingkey=${user}.cd.e\n')
+        aclf.write('acl allow all   publish exchange name=U routingkey=a.*.${user}\n')
+        aclf.write('acl allow all   publish exchange name=U routingkey=b.#.${user}\n')
+        aclf.write('acl allow all   publish exchange name=U routingkey=*.${user}.#.y\n')
+
+        aclf.write('acl allow all   create  queue    name=domain-${domain}\n')
+        aclf.write('acl allow all   publish exchange name=D routingkey=${domain}.cd.e\n')
+        aclf.write('acl allow all   publish exchange name=D routingkey=a.*.${domain}\n')
+        aclf.write('acl allow all   publish exchange name=D routingkey=b.#.${domain}\n')
+        aclf.write('acl allow all   publish exchange name=D routingkey=*.${domain}.#.y\n')
+
+        # Resolving ${user}_${domain} into ${userdomain} works for everything but routing keys
+        aclf.write('acl allow all   create  queue    name=mixed-OK-${user}_${domain}\n')
+        # For routing keys ${user}_${domain} will be parsed into ${userdomain}.
+        # Routing keys not be found when the rule specifies ${user}_${domain}.
+        aclf.write('acl allow all   publish exchange name=NOGO routingkey=${user}_${domain}.cd.e\n')
+        # This works since it is does not conflict with ${userdomain}
+        aclf.write('acl allow all   publish exchange name=OK   routingkey=${user}___${domain}.cd.e\n')
+
+        aclf.write('acl deny-log all all\n')
+        aclf.close()
+
+        result = self.reload_acl()
+        if (result):
+            self.fail(result)
+
+        self.Lookup("alice@QPID",   "create", "queue", "tmp-alice_QPID",              {}, "allow")
+        self.Lookup("bob@QPID",     "create", "queue", "bob_QPID-tmp",                {}, "allow")
+        self.Lookup("charlie@QPID", "create", "queue", "tmp-charlie_QPID-tmp",        {}, "allow")
+        self.Lookup("dave@QPID",    "create", "queue", "tmp-dave_QPID-tmp-dave_QPID", {}, "allow")
+        self.Lookup("ed@BIG.COM",   "create", "queue", "tmp-ed_BIG_COM",              {}, "allow")
+        self.Lookup("c.e.r@BIG.GER.COM", "create", "queue", "tmp-c_e_r_BIG_GER_COM",  {}, "allow")
+        self.Lookup("c@",           "create", "queue", "tmp-c_",                      {}, "allow")
+        self.Lookup("someuser",     "create", "queue", "tmp-someuser",                {}, "allow")
+
+        self.Lookup("alice@QPID",   "create", "queue", "tmp-${user}",                 {}, "deny-log")
+
+        self.Lookup("bob@QPID",     "create", "exchange", "temp0-bob_QPID", {}, "allow")
+        self.Lookup("bob@QPID",     "access", "exchange", "temp0-bob_QPID", {}, "allow")
+        self.Lookup("bob@QPID",     "bind",   "exchange", "temp0-bob_QPID", {}, "allow")
+        self.Lookup("bob@QPID",     "unbind", "exchange", "temp0-bob_QPID", {}, "allow")
+        self.Lookup("bob@QPID",     "delete", "exchange", "temp0-bob_QPID", {}, "allow")
+        self.LookupPublish("bob@QPID", "temp0-bob_QPID", "x", "allow")
+
+        self.Lookup("bob@QPID",     "create",  "queue", "temp0-bob_QPID", {}, "allow")
+        self.Lookup("bob@QPID",     "access",  "queue", "temp0-bob_QPID", {}, "allow")
+        self.Lookup("bob@QPID",     "purge",   "queue", "temp0-bob_QPID", {}, "allow")
+        self.Lookup("bob@QPID",     "consume", "queue", "temp0-bob_QPID", {}, "allow")
+        self.Lookup("bob@QPID",     "delete",  "queue", "temp0-bob_QPID", {}, "allow")
+
+        self.Lookup("alice@QPID",   "access",  "queue", "temp0-bob_QPID", {}, "deny-log")
+
+        #                                  aclKey: "${userdomain}.cd.e"
+        self.LookupPublish("uPlain1@COMPANY", "X", "uPlain1_COMPANY.cd.e",   "allow")
+        #                                  aclKey: "a.*.${userdomain}"
+        self.LookupPublish("uStar1@COMPANY", "X", "a.xx.uStar1_COMPANY",   "allow")
+        self.LookupPublish("uStar1@COMPANY", "X", "a.b",                   "deny-log")
+        #                                  aclKey: "b.#.${userdomain}"
+        self.LookupPublish("uHash1@COMPANY", "X", "b.uHash1_COMPANY",         "allow")
+        self.LookupPublish("uHash1@COMPANY", "X", "b.x.uHash1_COMPANY",       "allow")
+        self.LookupPublish("uHash1@COMPANY", "X", "b..x.y.zz.uHash1_COMPANY", "allow")
+        self.LookupPublish("uHash1@COMPANY", "X", "b.uHash1_COMPANY.",        "deny-log")
+        self.LookupPublish("uHash1@COMPANY", "X", "q.x.uHash1_COMPANY",       "deny-log")
+        #                                  aclKey: "*.${userdomain}.#.y"
+        self.LookupPublish("uMixed1@COMPANY", "X", "a.uMixed1_COMPANY.y",          "allow")
+        self.LookupPublish("uMixed1@COMPANY", "X", "a.uMixed1_COMPANY.p.qq.y",     "allow")
+        self.LookupPublish("uMixed1@COMPANY", "X", "a.a.uMixed1_COMPANY.y",        "deny-log")
+        self.LookupPublish("uMixed1@COMPANY", "X", "aa.uMixed1_COMPANY.b.c",       "deny-log")
+        self.LookupPublish("uMixed1@COMPANY.COM", "X", "a.uMixed1_COMPANY_COM.y",  "allow")
+
+
+        self.Lookup("bob@QPID",     "create", "queue", "user-bob",                {}, "allow")
+        #                                  aclKey: "${user}.cd.e"
+        self.LookupPublish("uPlain1@COMPANY", "U", "uPlain1.cd.e",   "allow")
+        #                                  aclKey: "a.*.${user}"
+        self.LookupPublish("uStar1@COMPANY", "U", "a.xx.uStar1",   "allow")
+        self.LookupPublish("uStar1@COMPANY", "U", "a.b",                   "deny-log")
+        #                                  aclKey: "b.#.${user}"
+        self.LookupPublish("uHash1@COMPANY", "U", "b.uHash1",         "allow")
+        self.LookupPublish("uHash1@COMPANY", "U", "b.x.uHash1",       "allow")
+        self.LookupPublish("uHash1@COMPANY", "U", "b..x.y.zz.uHash1", "allow")
+        self.LookupPublish("uHash1@COMPANY", "U", "b.uHash1.",        "deny-log")
+        self.LookupPublish("uHash1@COMPANY", "U", "q.x.uHash1",       "deny-log")
+        #                                  aclKey: "*.${user}.#.y"
+        self.LookupPublish("uMixed1@COMPANY",     "U", "a.uMixed1.y",          "allow")
+        self.LookupPublish("uMixed1@COMPANY",     "U", "a.uMixed1.p.qq.y",     "allow")
+        self.LookupPublish("uMixed1@COMPANY",     "U", "a.a.uMixed1.y",        "deny-log")
+        self.LookupPublish("uMixed1@COMPANY",     "U", "aa.uMixed1.b.c",       "deny-log")
+        self.LookupPublish("uMixed1@COMPANY.COM", "U", "a.uMixed1.y",          "allow")
+
+
+        self.Lookup("bob@QPID",     "create", "queue", "domain-QPID",                {}, "allow")
+        #                                  aclKey: "${domain}.cd.e"
+        self.LookupPublish("uPlain1@COMPANY", "D", "COMPANY.cd.e",         "allow")
+        #                                  aclKey: "a.*.${domain}"
+        self.LookupPublish("uStar1@COMPANY", "D", "a.xx.COMPANY",          "allow")
+        self.LookupPublish("uStar1@COMPANY", "D", "a.b",                   "deny-log")
+        #                                  aclKey: "b.#.${domain}"
+        self.LookupPublish("uHash1@COMPANY", "D", "b.COMPANY",             "allow")
+        self.LookupPublish("uHash1@COMPANY", "D", "b.x.COMPANY",           "allow")
+        self.LookupPublish("uHash1@COMPANY", "D", "b..x.y.zz.COMPANY",     "allow")
+        self.LookupPublish("uHash1@COMPANY", "D", "b.COMPANY.",            "deny-log")
+        self.LookupPublish("uHash1@COMPANY", "D", "q.x.COMPANY",           "deny-log")
+        #                                  aclKey: "*.${domain}.#.y"
+        self.LookupPublish("uMixed1@COMPANY", "D", "a.COMPANY.y",          "allow")
+        self.LookupPublish("uMixed1@COMPANY", "D", "a.COMPANY.p.qq.y",     "allow")
+        self.LookupPublish("uMixed1@COMPANY", "D", "a.a.COMPANY.y",        "deny-log")
+        self.LookupPublish("uMixed1@COMPANY", "D", "aa.COMPANY.b.c",       "deny-log")
+        self.LookupPublish("uMixed1@COMPANY.COM", "D", "a.COMPANY_COM.y",  "allow")
+
+        self.Lookup("uPlain1@COMPANY", "create", "queue", "mixed-OK-uPlain1_COMPANY", {}, "allow")
+        self.LookupPublish("uPlain1@COMPANY", "NOGO", "uPlain1_COMPANY.cd.e",             "deny-log")
+        self.LookupPublish("uPlain1@COMPANY", "OK",   "uPlain1___COMPANY.cd.e",           "allow")
+
+
+   #=====================================
+   # User name substitution details
+   #=====================================
+   #  User name substitution allows for three flavors of keyword in the Acl file.
+   #  Given a user name of bob.user@QPID.COM the keywords are normalized and resolve as follows:
+   #   ${userdomain} - bob_user_QPID_COM
+   #   ${user}       - bob_user
+   #   ${domain}     - QPID_COM
+   #
+   # The following substitution tests are very similar but differ in the flavor of keyword used
+   # in the rules. The tests results using the different keywords differ slightly in how permissive
+   # the rules become.
+   #   ${userdomain} limits access to one authenticated user
+   #   ${user}       limits access to a user name regardless of user's domain
+   #   ${domain}     limits access to a domain regardless of user name
+   #
+
+    def test_user_name_substitution_userdomain(self):
+        """
+        Test a setup where users can create, bind, and publish to a main exchange and queue.
+        Allow access to a single alternate exchange and queue.
+        """
+        aclf = self.get_acl_file()
+        aclf.write('# begin hack alert: allow anonymous to access the lookup debug functions\n')
+        aclf.write('acl allow-log anonymous create  queue\n')
+        aclf.write('acl allow-log anonymous all     exchange name=qmf.*\n')
+        aclf.write('acl allow-log anonymous all     exchange name=amq.direct\n')
+        aclf.write('acl allow-log anonymous all     exchange name=qpid.management\n')
+        aclf.write('acl allow-log anonymous access  method   name=*\n')
+        aclf.write('# end hack alert\n')
+        # Create primary queue and exchange:
+        #   allow predefined alternate
+        #   deny  any other alternate
+        #   allow no alternate
+        aclf.write('acl allow all create  queue    name=${userdomain}-work alternate=${userdomain}-work2\n')
+        aclf.write('acl deny  all create  queue    name=${userdomain}-work alternate=*\n')
+        aclf.write('acl allow all create  queue    name=${userdomain}-work\n')
+        aclf.write('acl allow all create  exchange name=${userdomain}-work alternate=${userdomain}-work2\n')
+        aclf.write('acl deny  all create  exchange name=${userdomain}-work alternate=*\n')
+        aclf.write('acl allow all create  exchange name=${userdomain}-work\n')
+        # Create backup queue and exchange
+        #   Deny any alternate
+        aclf.write('acl deny  all create  queue    name=${userdomain}-work2 alternate=*\n')
+        aclf.write('acl allow all create  queue    name=${userdomain}-work2\n')
+        aclf.write('acl deny  all create  exchange name=${userdomain}-work2 alternate=*\n')
+        aclf.write('acl allow all create  exchange name=${userdomain}-work2\n')
+        # Bind/unbind primary exchange
+        #  Use only predefined routingkey and queuename
+        aclf.write('acl allow all bind    exchange name=${userdomain}-work routingkey=${userdomain} queuename=${userdomain}-work\n')
+        aclf.write('acl allow all unbind  exchange name=${userdomain}-work routingkey=${userdomain} queuename=${userdomain}-work\n')
+        # Bind/unbind backup exchange
+        #  Use only predefined routingkey and queuename
+        aclf.write('acl allow all bind    exchange name=${userdomain}-work2 routingkey=${userdomain} queuename=${userdomain}-work2\n')
+        aclf.write('acl allow all unbind  exchange name=${userdomain}-work2 routingkey=${userdomain} queuename=${userdomain}-work2\n')
+        # Access primary exchange
+        #  Use only predefined routingkey and queuename
+        aclf.write('acl allow all access  exchange name=${userdomain}-work routingkey=${userdomain} queuename=${userdomain}-work\n')
+        # Access backup exchange
+        #  Use only predefined routingkey and queuename
+        aclf.write('acl allow all access  exchange name=${userdomain}-work2 routingkey=${userdomain} queuename=${userdomain}-work2\n')
+        # Publish primary exchange
+        #  Use only predefined routingkey
+        aclf.write('acl allow all publish exchange name=${userdomain}-work routingkey=${userdomain}\n')
+        # Publish backup exchange
+        #  Use only predefined routingkey
+        aclf.write('acl allow all publish exchange name=${userdomain}-work2 routingkey=${userdomain}\n')
+        # deny mode
+        aclf.write('acl deny all all\n')
+        aclf.close()
+
+        result = self.reload_acl()
+        if (result):
+            self.fail(result)
+
+        # create queues
+        self.Lookup("bob@QPID",     "create", "queue", "bob_QPID-work",    {},                             "allow")
+        self.Lookup("bob@QPID",     "create", "queue", "bob_QPID-work2",   {},                             "allow")
+        self.Lookup("bob@QPID",     "create", "queue", "joe_QPID-work",    {},                             "deny")
+        self.Lookup("bob@QPID",     "create", "queue", "joe_QPID-work2",   {},                             "deny")
+        self.Lookup("bob@QPID",     "create", "queue", "bob_QPID-work3",   {},                             "deny")
+        self.Lookup("bob@QPID",     "create", "queue", "bob_QPID-work",    {"alternate":"bob_QPID-work2"}, "allow")
+        self.Lookup("bob@QPID",     "create", "queue", "bob_QPID-work",    {"alternate":"joe_QPID-work2"}, "deny")
+        self.Lookup("bob@QPID",     "create", "queue", "bob_QPID-work2",   {"alternate":"someexchange"},   "deny")
+        # create exchanges
+        self.Lookup("bob@QPID",     "create", "exchange", "bob_QPID-work", {},                             "allow")
+        self.Lookup("bob@QPID",     "create", "exchange", "bob_QPID-work2",{},                             "allow")
+        self.Lookup("bob@QPID",     "create", "exchange", "joe_QPID-work", {},                             "deny")
+        self.Lookup("bob@QPID",     "create", "exchange", "joe_QPID-work2",{},                             "deny")
+        self.Lookup("bob@QPID",     "create", "exchange", "bob_QPID-work3",{},                             "deny")
+        self.Lookup("bob@QPID",     "create", "exchange", "bob_QPID-work", {"alternate":"bob_QPID-work2"}, "allow")
+        self.Lookup("bob@QPID",     "create", "exchange", "bob_QPID-work2",{"alternate":"someexchange"},   "deny")
+        # bind/unbind/access
+        self.Lookup("bob@QPID", "bind", "exchange", "bob_QPID-work", {},                                                     "deny")
+        self.Lookup("bob@QPID", "bind", "exchange", "bob_QPID-work", {"routingkey":"bob_QPID"},                              "deny")
+        self.Lookup("bob@QPID", "bind", "exchange", "bob_QPID-work", {                         "queuename":"bob_QPID-work"}, "deny")
+        self.Lookup("bob@QPID", "bind", "exchange", "bob_QPID-work", {"routingkey":"bob_QPID", "queuename":"bob_QPID-work"}, "allow")
+        self.Lookup("bob@QPID", "bind", "exchange", "joe_QPID-work", {"routingkey":"bob_QPID", "queuename":"bob_QPID-work"}, "deny")
+        self.Lookup("bob@QPID", "bind", "exchange", "bob_QPID-work", {"routingkey":"joe_QPID", "queuename":"bob_QPID-work"}, "deny")
+        self.Lookup("bob@QPID", "bind", "exchange", "bob_QPID-work", {"routingkey":"bob_QPID", "queuename":"joe_QPID-work"}, "deny")
+
+        self.Lookup("bob@QPID", "bind", "exchange", "bob_QPID-work2", {},                                                      "deny")
+        self.Lookup("bob@QPID", "bind", "exchange", "bob_QPID-work2", {"routingkey":"bob_QPID"},                               "deny")
+        self.Lookup("bob@QPID", "bind", "exchange", "bob_QPID-work2", {                         "queuename":"bob_QPID-work2"}, "deny")
+        self.Lookup("bob@QPID", "bind", "exchange", "bob_QPID-work2", {"routingkey":"bob_QPID", "queuename":"bob_QPID-work2"}, "allow")
+        self.Lookup("bob@QPID", "bind", "exchange", "joe_QPID-work2", {"routingkey":"bob_QPID", "queuename":"bob_QPID-work2"}, "deny")
+        self.Lookup("bob@QPID", "bind", "exchange", "bob_QPID-work2", {"routingkey":"joe_QPID", "queuename":"bob_QPID-work2"}, "deny")
+        self.Lookup("bob@QPID", "bind", "exchange", "bob_QPID-work2", {"routingkey":"bob_QPID", "queuename":"joe_QPID-work2"}, "deny")
+
+        self.Lookup("bob@QPID", "unbind", "exchange", "bob_QPID-work", {},                                                     "deny")
+        self.Lookup("bob@QPID", "unbind", "exchange", "bob_QPID-work", {"routingkey":"bob_QPID"},                              "deny")
+        self.Lookup("bob@QPID", "unbind", "exchange", "bob_QPID-work", {                         "queuename":"bob_QPID-work"}, "deny")
+        self.Lookup("bob@QPID", "unbind", "exchange", "bob_QPID-work", {"routingkey":"bob_QPID", "queuename":"bob_QPID-work"}, "allow")
+        self.Lookup("bob@QPID", "unbind", "exchange", "joe_QPID-work", {"routingkey":"bob_QPID", "queuename":"bob_QPID-work"}, "deny")
+        self.Lookup("bob@QPID", "unbind", "exchange", "bob_QPID-work", {"routingkey":"joe_QPID", "queuename":"bob_QPID-work"}, "deny")
+        self.Lookup("bob@QPID", "unbind", "exchange", "bob_QPID-work", {"routingkey":"bob_QPID", "queuename":"joe_QPID-work"}, "deny")
+
+        self.Lookup("bob@QPID", "unbind", "exchange", "bob_QPID-work2", {},                                                      "deny")
+        self.Lookup("bob@QPID", "unbind", "exchange", "bob_QPID-work2", {"routingkey":"bob_QPID"},                               "deny")
+        self.Lookup("bob@QPID", "unbind", "exchange", "bob_QPID-work2", {                         "queuename":"bob_QPID-work2"}, "deny")
+        self.Lookup("bob@QPID", "unbind", "exchange", "bob_QPID-work2", {"routingkey":"bob_QPID", "queuename":"bob_QPID-work2"}, "allow")
+        self.Lookup("bob@QPID", "unbind", "exchange", "joe_QPID-work2", {"routingkey":"bob_QPID", "queuename":"bob_QPID-work2"}, "deny")
+        self.Lookup("bob@QPID", "unbind", "exchange", "bob_QPID-work2", {"routingkey":"joe_QPID", "queuename":"bob_QPID-work2"}, "deny")
+        self.Lookup("bob@QPID", "unbind", "exchange", "bob_QPID-work2", {"routingkey":"bob_QPID", "queuename":"joe_QPID-work2"}, "deny")
+
+        self.Lookup("bob@QPID", "access", "exchange", "bob_QPID-work", {},                                                     "deny")
+        self.Lookup("bob@QPID", "access", "exchange", "bob_QPID-work", {"routingkey":"bob_QPID"},                              "deny")
+        self.Lookup("bob@QPID", "access", "exchange", "bob_QPID-work", {                         "queuename":"bob_QPID-work"}, "deny")
+        self.Lookup("bob@QPID", "access", "exchange", "bob_QPID-work", {"routingkey":"bob_QPID", "queuename":"bob_QPID-work"}, "allow")
+        self.Lookup("bob@QPID", "access", "exchange", "joe_QPID-work", {"routingkey":"bob_QPID", "queuename":"bob_QPID-work"}, "deny")
+        self.Lookup("bob@QPID", "access", "exchange", "bob_QPID-work", {"routingkey":"joe_QPID", "queuename":"bob_QPID-work"}, "deny")
+        self.Lookup("bob@QPID", "access", "exchange", "bob_QPID-work", {"routingkey":"bob_QPID", "queuename":"joe_QPID-work"}, "deny")
+
+        self.Lookup("bob@QPID", "access", "exchange", "bob_QPID-work2", {},                                                      "deny")
+        self.Lookup("bob@QPID", "access", "exchange", "bob_QPID-work2", {"routingkey":"bob_QPID"},                               "deny")
+        self.Lookup("bob@QPID", "access", "exchange", "bob_QPID-work2", {                         "queuename":"bob_QPID-work2"}, "deny")
+        self.Lookup("bob@QPID", "access", "exchange", "bob_QPID-work2", {"routingkey":"bob_QPID", "queuename":"bob_QPID-work2"}, "allow")
+        self.Lookup("bob@QPID", "access", "exchange", "joe_QPID-work2", {"routingkey":"bob_QPID", "queuename":"bob_QPID-work2"}, "deny")
+        self.Lookup("bob@QPID", "access", "exchange", "bob_QPID-work2", {"routingkey":"joe_QPID", "queuename":"bob_QPID-work2"}, "deny")
+        self.Lookup("bob@QPID", "access", "exchange", "bob_QPID-work2", {"routingkey":"bob_QPID", "queuename":"joe_QPID-work2"}, "deny")
+        # publish
+        self.LookupPublish("bob@QPID", "bob_QPID-work",  "bob_QPID",        "allow")
+        self.LookupPublish("bob@QPID", "bob_QPID-work2", "bob_QPID",        "allow")
+        self.LookupPublish("bob@QPID", "joe_QPID-work",  "bob_QPID",        "deny")
+        self.LookupPublish("bob@QPID", "joe_QPID-work2", "bob_QPID",        "deny")
+        self.LookupPublish("bob@QPID", "bob_QPID-work",  "joe_QPID",        "deny")
+        self.LookupPublish("bob@QPID", "bob_QPID-work2", "joe_QPID",        "deny")
+
+
+    def test_user_name_substitution_user(self):
+        """
+        Test a setup where users can create, bind, and publish to a main exchange and queue.
+        Allow access to a single backup exchange and queue.
+        """
+        aclf = self.get_acl_file()
+        aclf.write('# begin hack alert: allow anonymous to access the lookup debug functions\n')
+        aclf.write('acl allow-log anonymous create  queue\n')
+        aclf.write('acl allow-log anonymous all     exchange name=qmf.*\n')
+        aclf.write('acl allow-log anonymous all     exchange name=amq.direct\n')
+        aclf.write('acl allow-log anonymous all     exchange name=qpid.management\n')
+        aclf.write('acl allow-log anonymous access  method   name=*\n')
+        aclf.write('# end hack alert\n')
+        # Create primary queue and exchange
+        #   allow predefined alternate
+        #   deny  any other alternate
+        #   allow no alternate
+        aclf.write('acl allow all create  queue    name=${user}-work alternate=${user}-work2\n')
+        aclf.write('acl deny  all create  queue    name=${user}-work alternate=*\n')
+        aclf.write('acl allow all create  queue    name=${user}-work\n')
+        aclf.write('acl allow all create  exchange name=${user}-work alternate=${user}-work2\n')
+        aclf.write('acl deny  all create  exchange name=${user}-work alternate=*\n')
+        aclf.write('acl allow all create  exchange name=${user}-work\n')
+        # Create backup queue and exchange
+        #   Deny any alternate
+        aclf.write('acl deny  all create  queue    name=${user}-work2 alternate=*\n')
+        aclf.write('acl allow all create  queue    name=${user}-work2\n')
+        aclf.write('acl deny  all create  exchange name=${user}-work2 alternate=*\n')
+        aclf.write('acl allow all create  exchange name=${user}-work2\n')
+        # Bind/unbind primary exchange
+        #  Use only predefined routingkey and queuename
+        aclf.write('acl allow all bind    exchange name=${user}-work routingkey=${user} queuename=${user}-work\n')
+        aclf.write('acl allow all unbind  exchange name=${user}-work routingkey=${user} queuename=${user}-work\n')
+        # Bind/unbind backup exchange
+        #  Use only predefined routingkey and queuename
+        aclf.write('acl allow all bind    exchange name=${user}-work2 routingkey=${user} queuename=${user}-work2\n')
+        aclf.write('acl allow all unbind  exchange name=${user}-work2 routingkey=${user} queuename=${user}-work2\n')
+        # Access primary exchange
+        #  Use only predefined routingkey and queuename
+        aclf.write('acl allow all access  exchange name=${user}-work routingkey=${user} queuename=${user}-work\n')
+        # Access backup exchange
+        #  Use only predefined routingkey and queuename
+        aclf.write('acl allow all access  exchange name=${user}-work2 routingkey=${user} queuename=${user}-work2\n')
+        # Publish primary exchange
+        #  Use only predefined routingkey
+        aclf.write('acl allow all publish exchange name=${user}-work routingkey=${user}\n')
+        # Publish backup exchange
+        #  Use only predefined routingkey
+        aclf.write('acl allow all publish exchange name=${user}-work2 routingkey=${user}\n')
+        # deny mode
+        aclf.write('acl deny all all\n')
+        aclf.close()
+
+        result = self.reload_acl()
+        if (result):
+            self.fail(result)
+
+        # create queues
+        self.Lookup("bob@QPID",     "create", "queue", "bob-work",    {},                          "allow")
+        self.Lookup("bob@QPID",     "create", "queue", "bob-work2",   {},                          "allow")
+        self.Lookup("bob@QPID",     "create", "queue", "joe-work",    {},                          "deny")
+        self.Lookup("bob@QPID",     "create", "queue", "joe-work2",   {},                          "deny")
+        self.Lookup("bob@QPID",     "create", "queue", "bob-work3",   {},                          "deny")
+        self.Lookup("bob@QPID",     "create", "queue", "bob-work",    {"alternate":"bob-work2"},   "allow")
+        self.Lookup("bob@QPID",     "create", "queue", "bob-work",    {"alternate":"joe-work2"},   "deny")
+        self.Lookup("bob@QPID",     "create", "queue", "bob-work2",   {"alternate":"someexchange"},"deny")
+        # create exchanges
+        self.Lookup("bob@QPID",     "create", "exchange", "bob-work", {},                          "allow")
+        self.Lookup("bob@QPID",     "create", "exchange", "bob-work2",{},                          "allow")
+        self.Lookup("bob@QPID",     "create", "exchange", "joe-work", {},                          "deny")
+        self.Lookup("bob@QPID",     "create", "exchange", "joe-work2",{},                          "deny")
+        self.Lookup("bob@QPID",     "create", "exchange", "bob-work3",{},                          "deny")
+        self.Lookup("bob@QPID",     "create", "exchange", "bob-work", {"alternate":"bob-work2"},   "allow")
+        self.Lookup("bob@QPID",     "create", "exchange", "bob-work2",{"alternate":"someexchange"},"deny")
+        # bind/unbind/access
+        self.Lookup("bob@QPID", "bind", "exchange", "bob-work", {},                                           "deny")
+        self.Lookup("bob@QPID", "bind", "exchange", "bob-work", {"routingkey":"bob"},                         "deny")
+        self.Lookup("bob@QPID", "bind", "exchange", "bob-work", {                    "queuename":"bob-work"}, "deny")
+        self.Lookup("bob@QPID", "bind", "exchange", "bob-work", {"routingkey":"bob", "queuename":"bob-work"}, "allow")
+        self.Lookup("bob@QPID", "bind", "exchange", "joe-work", {"routingkey":"bob", "queuename":"bob-work"}, "deny")
+        self.Lookup("bob@QPID", "bind", "exchange", "bob-work", {"routingkey":"joe", "queuename":"bob-work"}, "deny")
+        self.Lookup("bob@QPID", "bind", "exchange", "bob-work", {"routingkey":"bob", "queuename":"joe-work"}, "deny")
+
+        self.Lookup("bob@QPID", "bind", "exchange", "bob-work2", {},                                            "deny")
+        self.Lookup("bob@QPID", "bind", "exchange", "bob-work2", {"routingkey":"bob"},                          "deny")
+        self.Lookup("bob@QPID", "bind", "exchange", "bob-work2", {                    "queuename":"bob-work2"}, "deny")
+        self.Lookup("bob@QPID", "bind", "exchange", "bob-work2", {"routingkey":"bob", "queuename":"bob-work2"}, "allow")
+        self.Lookup("bob@QPID", "bind", "exchange", "joe-work2", {"routingkey":"bob", "queuename":"bob-work2"}, "deny")
+        self.Lookup("bob@QPID", "bind", "exchange", "bob-work2", {"routingkey":"joe", "queuename":"bob-work2"}, "deny")
+        self.Lookup("bob@QPID", "bind", "exchange", "bob-work2", {"routingkey":"bob", "queuename":"joe-work2"}, "deny")
+
+        self.Lookup("bob@QPID", "unbind", "exchange", "bob-work", {},                                           "deny")
+        self.Lookup("bob@QPID", "unbind", "exchange", "bob-work", {"routingkey":"bob"},                         "deny")
+        self.Lookup("bob@QPID", "unbind", "exchange", "bob-work", {                    "queuename":"bob-work"}, "deny")
+        self.Lookup("bob@QPID", "unbind", "exchange", "bob-work", {"routingkey":"bob", "queuename":"bob-work"}, "allow")
+        self.Lookup("bob@QPID", "unbind", "exchange", "joe-work", {"routingkey":"bob", "queuename":"bob-work"}, "deny")
+        self.Lookup("bob@QPID", "unbind", "exchange", "bob-work", {"routingkey":"joe", "queuename":"bob-work"}, "deny")
+        self.Lookup("bob@QPID", "unbind", "exchange", "bob-work", {"routingkey":"bob", "queuename":"joe-work"}, "deny")
+
+        self.Lookup("bob@QPID", "unbind", "exchange", "bob-work2", {},                                            "deny")
+        self.Lookup("bob@QPID", "unbind", "exchange", "bob-work2", {"routingkey":"bob"},                          "deny")
+        self.Lookup("bob@QPID", "unbind", "exchange", "bob-work2", {                    "queuename":"bob-work2"}, "deny")
+        self.Lookup("bob@QPID", "unbind", "exchange", "bob-work2", {"routingkey":"bob", "queuename":"bob-work2"}, "allow")
+        self.Lookup("bob@QPID", "unbind", "exchange", "joe-work2", {"routingkey":"bob", "queuename":"bob-work2"}, "deny")
+        self.Lookup("bob@QPID", "unbind", "exchange", "bob-work2", {"routingkey":"joe", "queuename":"bob-work2"}, "deny")
+        self.Lookup("bob@QPID", "unbind", "exchange", "bob-work2", {"routingkey":"bob", "queuename":"joe-work2"}, "deny")
+
+        self.Lookup("bob@QPID", "access", "exchange", "bob-work", {},                                           "deny")
+        self.Lookup("bob@QPID", "access", "exchange", "bob-work", {"routingkey":"bob"},                         "deny")
+        self.Lookup("bob@QPID", "access", "exchange", "bob-work", {                    "queuename":"bob-work"}, "deny")
+        self.Lookup("bob@QPID", "access", "exchange", "bob-work", {"routingkey":"bob", "queuename":"bob-work"}, "allow")
+        self.Lookup("bob@QPID", "access", "exchange", "joe-work", {"routingkey":"bob", "queuename":"bob-work"}, "deny")
+        self.Lookup("bob@QPID", "access", "exchange", "bob-work", {"routingkey":"joe", "queuename":"bob-work"}, "deny")
+        self.Lookup("bob@QPID", "access", "exchange", "bob-work", {"routingkey":"bob", "queuename":"joe-work"}, "deny")
+
+        self.Lookup("bob@QPID", "access", "exchange", "bob-work2", {},                                            "deny")
+        self.Lookup("bob@QPID", "access", "exchange", "bob-work2", {"routingkey":"bob"},                          "deny")
+        self.Lookup("bob@QPID", "access", "exchange", "bob-work2", {                    "queuename":"bob-work2"}, "deny")
+        self.Lookup("bob@QPID", "access", "exchange", "bob-work2", {"routingkey":"bob", "queuename":"bob-work2"}, "allow")
+        self.Lookup("bob@QPID", "access", "exchange", "joe-work2", {"routingkey":"bob", "queuename":"bob-work2"}, "deny")
+        self.Lookup("bob@QPID", "access", "exchange", "bob-work2", {"routingkey":"joe", "queuename":"bob-work2"}, "deny")
+        self.Lookup("bob@QPID", "access", "exchange", "bob-work2", {"routingkey":"bob", "queuename":"joe-work2"}, "deny")
+        # publish
+        self.LookupPublish("bob@QPID", "bob-work",  "bob",        "allow")
+        self.LookupPublish("bob@QPID", "bob-work2", "bob",        "allow")
+        self.LookupPublish("bob@QPID", "joe-work",  "bob",        "deny")
+        self.LookupPublish("bob@QPID", "joe-work2", "bob",        "deny")
+        self.LookupPublish("bob@QPID", "bob-work",  "joe",        "deny")
+        self.LookupPublish("bob@QPID", "bob-work2", "joe",        "deny")
+
+
+    def test_user_name_substitution_domain(self):
+        """
+        Test a setup where users can create, bind, and publish to a main exchange and queue.
+        Allow access to a single backup exchange and queue.
+        """
+        aclf = self.get_acl_file()
+        aclf.write('# begin hack alert: allow anonymous to access the lookup debug functions\n')
+        aclf.write('acl allow-log anonymous create  queue\n')
+        aclf.write('acl allow-log anonymous all     exchange name=qmf.*\n')
+        aclf.write('acl allow-log anonymous all     exchange name=amq.direct\n')
+        aclf.write('acl allow-log anonymous all     exchange name=qpid.management\n')
+        aclf.write('acl allow-log anonymous access  method   name=*\n')
+        aclf.write('# end hack alert\n')
+        # Create primary queue and exchange
+        #   allow predefined alternate
+        #   deny  any other alternate
+        #   allow no alternate
+        aclf.write('acl allow all create  queue    name=${domain}-work alternate=${domain}-work2\n')
+        aclf.write('acl deny  all create  queue    name=${domain}-work alternate=*\n')
+        aclf.write('acl allow all create  queue    name=${domain}-work\n')
+        aclf.write('acl allow all create  exchange name=${domain}-work alternate=${domain}-work2\n')
+        aclf.write('acl deny  all create  exchange name=${domain}-work alternate=*\n')
+        aclf.write('acl allow all create  exchange name=${domain}-work\n')
+        # Create backup queue and exchange
+        #   Deny any alternate
+        aclf.write('acl deny  all create  queue    name=${domain}-work2 alternate=*\n')
+        aclf.write('acl allow all create  queue    name=${domain}-work2\n')
+        aclf.write('acl deny  all create  exchange name=${domain}-work2 alternate=*\n')
+        aclf.write('acl allow all create  exchange name=${domain}-work2\n')
+        # Bind/unbind primary exchange
+        #  Use only predefined routingkey and queuename
+        aclf.write('acl allow all bind    exchange name=${domain}-work routingkey=${domain} queuename=${domain}-work\n')
+        aclf.write('acl allow all unbind  exchange name=${domain}-work routingkey=${domain} queuename=${domain}-work\n')
+        # Bind/unbind backup exchange
+        #  Use only predefined routingkey and queuename
+        aclf.write('acl allow all bind    exchange name=${domain}-work2 routingkey=${domain} queuename=${domain}-work2\n')
+        aclf.write('acl allow all unbind  exchange name=${domain}-work2 routingkey=${domain} queuename=${domain}-work2\n')
+        # Access primary exchange
+        #  Use only predefined routingkey and queuename
+        aclf.write('acl allow all access  exchange name=${domain}-work routingkey=${domain} queuename=${domain}-work\n')
+        # Access backup exchange
+        #  Use only predefined routingkey and queuename
+        aclf.write('acl allow all access  exchange name=${domain}-work2 routingkey=${domain} queuename=${domain}-work2\n')
+        # Publish primary exchange
+        #  Use only predefined routingkey
+        aclf.write('acl allow all publish exchange name=${domain}-work routingkey=${domain}\n')
+        # Publish backup exchange
+        #  Use only predefined routingkey
+        aclf.write('acl allow all publish exchange name=${domain}-work2 routingkey=${domain}\n')
+        # deny mode
+        aclf.write('acl deny all all\n')
+        aclf.close()
+
+        result = self.reload_acl()
+        if (result):
+            self.fail(result)
+
+        # create queues
+        self.Lookup("bob@QPID",     "create", "queue", "QPID-work",    {},                            "allow")
+        self.Lookup("bob@QPID",     "create", "queue", "QPID-work2",   {},                            "allow")
+        self.Lookup("bob@QPID",     "create", "queue", "QPID-work3",   {},                            "deny")
+        self.Lookup("bob@QPID",     "create", "queue", "QPID-work",    {"alternate":"QPID-work2"},    "allow")
+        self.Lookup("bob@QPID",     "create", "queue", "QPID-work",    {"alternate":"bob_QPID-work2"},"deny")
+        self.Lookup("bob@QPID",     "create", "queue", "QPID-work",    {"alternate":"joe_QPID-work2"},"deny")
+        self.Lookup("bob@QPID",     "create", "queue", "QPID-work2",   {"alternate":"someexchange"},  "deny")
+        # create exchanges
+        self.Lookup("bob@QPID",     "create", "exchange", "QPID-work", {},                           "allow")
+        self.Lookup("bob@QPID",     "create", "exchange", "QPID-work2",{},                           "allow")
+        self.Lookup("bob@QPID",     "create", "exchange", "QPID-work3",{},                           "deny")
+        self.Lookup("bob@QPID",     "create", "exchange", "QPID-work", {"alternate":"QPID-work2"},   "allow")
+        self.Lookup("bob@QPID",     "create", "exchange", "QPID-work2",{"alternate":"someexchange"}, "deny")
+        # bind/unbind/access
+        self.Lookup("bob@QPID", "bind", "exchange", "QPID-work", {},                                             "deny")
+        self.Lookup("bob@QPID", "bind", "exchange", "QPID-work", {"routingkey":"QPID"},                          "deny")
+        self.Lookup("bob@QPID", "bind", "exchange", "QPID-work", {                     "queuename":"QPID-work"}, "deny")
+        self.Lookup("bob@QPID", "bind", "exchange", "QPID-work", {"routingkey":"QPID", "queuename":"QPID-work"}, "allow")
+
+        self.Lookup("bob@QPID", "bind", "exchange", "QPID-work2", {},                                              "deny")
+        self.Lookup("bob@QPID", "bind", "exchange", "QPID-work2", {"routingkey":"QPID"},                           "deny")
+        self.Lookup("bob@QPID", "bind", "exchange", "QPID-work2", {                     "queuename":"QPID-work2"}, "deny")
+        self.Lookup("bob@QPID", "bind", "exchange", "QPID-work2", {"routingkey":"QPID", "queuename":"QPID-work2"}, "allow")
+
+        self.Lookup("bob@QPID", "unbind", "exchange", "QPID-work", {},                                             "deny")
+        self.Lookup("bob@QPID", "unbind", "exchange", "QPID-work", {"routingkey":"QPID"},                          "deny")
+        self.Lookup("bob@QPID", "unbind", "exchange", "QPID-work", {                     "queuename":"QPID-work"}, "deny")
+        self.Lookup("bob@QPID", "unbind", "exchange", "QPID-work", {"routingkey":"QPID", "queuename":"QPID-work"}, "allow")
+
+        self.Lookup("bob@QPID", "unbind", "exchange", "QPID-work2", {},                                              "deny")
+        self.Lookup("bob@QPID", "unbind", "exchange", "QPID-work2", {"routingkey":"QPID"},                           "deny")
+        self.Lookup("bob@QPID", "unbind", "exchange", "QPID-work2", {                     "queuename":"QPID-work2"}, "deny")
+        self.Lookup("bob@QPID", "unbind", "exchange", "QPID-work2", {"routingkey":"QPID", "queuename":"QPID-work2"}, "allow")
+
+        self.Lookup("bob@QPID", "access", "exchange", "QPID-work", {},                                             "deny")
+        self.Lookup("bob@QPID", "access", "exchange", "QPID-work", {"routingkey":"QPID"},                          "deny")
+        self.Lookup("bob@QPID", "access", "exchange", "QPID-work", {                     "queuename":"QPID-work"}, "deny")
+        self.Lookup("bob@QPID", "access", "exchange", "QPID-work", {"routingkey":"QPID", "queuename":"QPID-work"}, "allow")
+
+        self.Lookup("bob@QPID", "access", "exchange", "QPID-work2", {},                                              "deny")
+        self.Lookup("bob@QPID", "access", "exchange", "QPID-work2", {"routingkey":"QPID"},                           "deny")
+        self.Lookup("bob@QPID", "access", "exchange", "QPID-work2", {                     "queuename":"QPID-work2"}, "deny")
+        self.Lookup("bob@QPID", "access", "exchange", "QPID-work2", {"routingkey":"QPID", "queuename":"QPID-work2"}, "allow")
+        # publish
+        self.LookupPublish("bob@QPID", "QPID-work",  "QPID",        "allow")
+        self.LookupPublish("bob@QPID", "QPID-work2", "QPID",        "allow")
+        self.LookupPublish("joe@QPID", "QPID-work",  "QPID",        "allow")
+        self.LookupPublish("joe@QPID", "QPID-work2", "QPID",        "allow")
+
+
 class BrokerAdmin:
     def __init__(self, broker, username=None, password=None):
         self.connection = qpid.messaging.Connection(broker)



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