Author: chug Date: Sun Jul 15 10:17:26 2012 New Revision: 1361678 URL: http://svn.apache.org/viewvc?rev=1361678&view=rev Log: QPID-3892 C++ broker add routing key wildcard support to Acl 'publish exchange' lookups. Although this patch does not address the original issue's regex request it provides the desired functionality in a more comprehensive manner.
* Acl publish exchange rules may specify routing keys using the topic exchange syntax with '*' and '#' wildcard match tokens. * Acl lookups hook in to the broker's topic exchange key match code to perform the wildcard match. * Acl rules written using the old Acl wildcard syntax (with a single trailing '*') will continue to work the same as before. 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/acl/AclReader.h qpid/trunk/qpid/cpp/src/qpid/acl/AclValidator.cpp qpid/trunk/qpid/cpp/src/qpid/acl/AclValidator.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=1361678&r1=1361677&r2=1361678&view=diff ============================================================================== --- qpid/trunk/qpid/cpp/src/qpid/acl/AclData.cpp (original) +++ qpid/trunk/qpid/cpp/src/qpid/acl/AclData.cpp Sun Jul 15 10:17:26 2012 @@ -305,7 +305,9 @@ namespace acl { // lookup // // The ACL main business logic function of matching rules and declaring - // an allow or deny result. + // an allow or deny result. This lookup is the fastpath per-message + // lookup to verify if a user is allowed to publish to an exchange with + // a given key. // AclResult AclData::lookup( const std::string& id, @@ -331,7 +333,8 @@ namespace acl { if (itrRule != actionList[action][objType]->end() ) { - //loop the vector + // Found a rule list for this user-action-object set. + // Search the rule list for a matching rule. ruleSetItr rsItr = itrRule->second.end(); for (int cnt = itrRule->second.size(); cnt != 0; cnt--) { @@ -339,56 +342,46 @@ namespace acl { QPID_LOG(debug, "ACL: checking rule " << rsItr->toString()); - // loop the names looking for match + // Search on exchange name and routing key only if specfied in rule. bool match =true; - for (specPropertyMapItr pMItr = rsItr->props.begin(); - (pMItr != rsItr->props.end()) && match; - pMItr++) + if (rsItr->pubExchNameInRule) { - //match name is exists first - switch (pMItr->first) + if (matchProp(rsItr->pubExchName, name)) { - case acl::SPECPROP_NAME: - if (matchProp(pMItr->second, name)) - { - QPID_LOG(debug, "ACL: lookup exchange name '" - << name << "' matched with rule name '" - << pMItr->second << "'"); - - } - else - { - match= false; - QPID_LOG(debug, "ACL: lookup exchange name '" - << name << "' did not match with rule name '" - << pMItr->second << "'"); - } - break; - - case acl::SPECPROP_ROUTINGKEY: - if (matchProp(pMItr->second, routingKey)) - { - QPID_LOG(debug, "ACL: lookup key name '" - << routingKey << "' matched with rule routing key '" - << pMItr->second << "'"); - } - else - { - match= false; - QPID_LOG(debug, "ACL: lookup key name '" - << routingKey << "' did not match with rule routing key '" - << pMItr->second << "'"); - } - break; - - default: - // Don't care - break; - }; + QPID_LOG(debug, "ACL: Rule: " << rsItr->rawRuleNum << " lookup exchange name '" + << name << "' matched with rule name '" + << rsItr->pubExchName << "'"); + + } + else + { + match= false; + QPID_LOG(debug, "ACL: Rule: " << rsItr->rawRuleNum << " lookup exchange name '" + << name << "' did not match with rule name '" + << rsItr->pubExchName << "'"); + } + } + + if (match && rsItr->pubRoutingKeyInRule) + { + if (rsItr->matchRoutingKey(routingKey)) + { + 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; + } } + if (match){ aclresult = rsItr->ruleMode; - QPID_LOG(debug,"ACL: Successful match, the decision is:" + QPID_LOG(debug,"ACL: Rule: " << rsItr->rawRuleNum << " Successful match, the decision is:" << AclHelper::getAclResultStr(aclresult)); return aclresult; } 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=1361678&r1=1361677&r2=1361678&view=diff ============================================================================== --- qpid/trunk/qpid/cpp/src/qpid/acl/AclData.h (original) +++ qpid/trunk/qpid/cpp/src/qpid/acl/AclData.h Sun Jul 15 10:17:26 2012 @@ -21,6 +21,9 @@ */ #include "qpid/broker/AclModule.h" +#include "AclTopicMatch.h" +#include "qpid/log/Statement.h" +#include "boost/shared_ptr.hpp" #include <vector> #include <sstream> @@ -48,18 +51,29 @@ public: // A single ACL file entry may create many rule entries in // many ruleset vectors. // - struct rule { + struct Rule { + typedef broker::TopicExchange::TopicExchangeTester topicTester; int rawRuleNum; // rule number in ACL file qpid::acl::AclResult ruleMode; // combined allow/deny log/nolog specPropertyMap props; // + bool pubRoutingKeyInRule; + std::string pubRoutingKey; + boost::shared_ptr<topicTester> pTTest; + bool pubExchNameInRule; + std::string pubExchName; - - rule (int ruleNum, qpid::acl::AclResult res, specPropertyMap& p) : + Rule (int ruleNum, qpid::acl::AclResult res, specPropertyMap& p) : rawRuleNum(ruleNum), ruleMode(res), - props(p) - {}; + props(p), + pubRoutingKeyInRule(false), + pubRoutingKey(), + pTTest(boost::shared_ptr<topicTester>(new topicTester())), + pubExchNameInRule(false), + pubExchName() + {} + std::string toString () const { std::ostringstream ruleStr; @@ -76,9 +90,21 @@ public: ruleStr << " }]"; return ruleStr.str(); } + + void addTopicTest(const std::string& pattern) { + pTTest->addBindingKey(broker::TopicExchange::normalize(pattern)); + } + + // Topic Exchange tester + // return true if any bindings match 'pattern' + bool matchRoutingKey(const std::string& pattern) const + { + topicTester::BindingVec bv; + return pTTest->findMatches(pattern, bv); + } }; - typedef std::vector<rule> ruleSet; + typedef std::vector<Rule> ruleSet; typedef ruleSet::const_iterator ruleSetItr; typedef std::map<std::string, ruleSet > actionObject; // user typedef actionObject::iterator actObjItr; 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=1361678&r1=1361677&r2=1361678&view=diff ============================================================================== --- qpid/trunk/qpid/cpp/src/qpid/acl/AclReader.cpp (original) +++ qpid/trunk/qpid/cpp/src/qpid/acl/AclReader.cpp Sun Jul 15 10:17:26 2012 @@ -101,7 +101,7 @@ namespace acl { << AclHelper::getAclResultStr(d->decisionMode)); foundmode = true; } else { - AclData::rule rule(cnt, (*i)->res, (*i)->props); + AclData::Rule rule(cnt, (*i)->res, (*i)->props); // Action -> Object -> map<user -> set<Rule> > std::ostringstream actionstr; @@ -110,8 +110,27 @@ namespace acl { (*i)->actionAll ? acnt++ : acnt = acl::ACTIONSIZE) { if (acnt == acl::ACT_PUBLISH) + { d->transferAcl = true; // we have transfer ACL - + // For Publish the only object should be Exchange + // and the only property should be routingkey. + // Go through the rule properties and find the name and the key. + // If found then place them specially for the lookup engine. + for (pmCitr pItr=(*i)->props.begin(); pItr!=(*i)->props.end(); pItr++) { + if (acl::SPECPROP_ROUTINGKEY == pItr->first) + { + rule.pubRoutingKeyInRule = true; + rule.pubRoutingKey = (std::string)pItr->second; + rule.addTopicTest(rule.pubRoutingKey); + break; + } + if (acl::SPECPROP_NAME == pItr->first) + { + rule.pubExchNameInRule = true; + rule.pubExchName = pItr->second; + } + } + } actionstr << AclHelper::getActionStr((Action) acnt) << ","; //find the Action, create if not exist @@ -285,7 +304,7 @@ namespace acl { if (ws) { ret = true; } else { - errorStream << ACL_FORMAT_ERR_LOG_PREFIX << "Line : " << lineNumber + errorStream << ACL_FORMAT_ERR_LOG_PREFIX << "Line : " << lineNumber << ", Non-continuation line must start with \"group\" or \"acl\"."; ret = false; } @@ -330,7 +349,7 @@ namespace acl { } else { const unsigned minimumSize = (cont ? 2 : 3); if (toksSize < minimumSize) { - errorStream << ACL_FORMAT_ERR_LOG_PREFIX << "Line : " << lineNumber + errorStream << ACL_FORMAT_ERR_LOG_PREFIX << "Line : " << lineNumber << ", Insufficient tokens for group definition."; return false; } @@ -479,7 +498,7 @@ namespace acl { nvPair propNvp = splitNameValuePair(toks[i]); if (propNvp.second.size() == 0) { errorStream << ACL_FORMAT_ERR_LOG_PREFIX << "Line : " << lineNumber - <<", Badly formed property name-value pair \"" + <<", Badly formed property name-value pair \"" << propNvp.first << "\". (Must be name=value)"; return false; } Modified: qpid/trunk/qpid/cpp/src/qpid/acl/AclReader.h URL: http://svn.apache.org/viewvc/qpid/trunk/qpid/cpp/src/qpid/acl/AclReader.h?rev=1361678&r1=1361677&r2=1361678&view=diff ============================================================================== --- qpid/trunk/qpid/cpp/src/qpid/acl/AclReader.h (original) +++ qpid/trunk/qpid/cpp/src/qpid/acl/AclReader.h Sun Jul 15 10:17:26 2012 @@ -26,6 +26,7 @@ #include <string> #include <vector> #include <sstream> +#include <memory> #include "qpid/acl/AclData.h" #include "qpid/broker/AclModule.h" Modified: qpid/trunk/qpid/cpp/src/qpid/acl/AclValidator.cpp URL: http://svn.apache.org/viewvc/qpid/trunk/qpid/cpp/src/qpid/acl/AclValidator.cpp?rev=1361678&r1=1361677&r2=1361678&view=diff ============================================================================== --- qpid/trunk/qpid/cpp/src/qpid/acl/AclValidator.cpp (original) +++ qpid/trunk/qpid/cpp/src/qpid/acl/AclValidator.cpp Sun Jul 15 10:17:26 2012 @@ -131,7 +131,7 @@ namespace acl { boost::bind(&AclValidator::validateRule, this, _1)); } - void AclValidator::validateRule(qpid::acl::AclData::rule& rule){ + void AclValidator::validateRule(qpid::acl::AclData::Rule& rule){ std::for_each(rule.props.begin(), rule.props.end(), boost::bind(&AclValidator::validateProperty, this, _1)); Modified: qpid/trunk/qpid/cpp/src/qpid/acl/AclValidator.h URL: http://svn.apache.org/viewvc/qpid/trunk/qpid/cpp/src/qpid/acl/AclValidator.h?rev=1361678&r1=1361677&r2=1361678&view=diff ============================================================================== --- qpid/trunk/qpid/cpp/src/qpid/acl/AclValidator.h (original) +++ qpid/trunk/qpid/cpp/src/qpid/acl/AclValidator.h Sun Jul 15 10:17:26 2012 @@ -71,7 +71,7 @@ class AclValidator { public: void validateRuleSet(std::pair<const std::string, qpid::acl::AclData::ruleSet>& rules); - void validateRule(qpid::acl::AclData::rule& rule); + void validateRule(qpid::acl::AclData::Rule& rule); void validateProperty(std::pair<const qpid::acl::SpecProperty, std::string>& prop); void validate(boost::shared_ptr<AclData> d); AclValidator(); Modified: qpid/trunk/qpid/cpp/src/tests/acl.py URL: http://svn.apache.org/viewvc/qpid/trunk/qpid/cpp/src/tests/acl.py?rev=1361678&r1=1361677&r2=1361678&view=diff ============================================================================== --- qpid/trunk/qpid/cpp/src/tests/acl.py (original) +++ qpid/trunk/qpid/cpp/src/tests/acl.py Sun Jul 15 10:17:26 2012 @@ -310,7 +310,7 @@ class ACLTests(TestBase010): self.fail("ACL should allow queue create request"); self.fail("Error during queue create request"); - + def test_user_realm(self): """ @@ -1537,6 +1537,124 @@ class ACLTests(TestBase010): #===================================== + # QMF Topic Exchange tests + #===================================== + + def test_qmf_topic_exchange_tests(self): + """ + Test using QMF method hooks into ACL logic + """ + 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-log uPlain1@COMPANY publish exchange name=X routingkey=ab.cd.e\n') + aclf.write('acl allow-log uPlain2@COMPANY publish exchange name=X routingkey=.\n') + aclf.write('acl allow-log uStar1@COMPANY publish exchange name=X routingkey=a.*.b\n') + aclf.write('acl allow-log uStar2@COMPANY publish exchange name=X routingkey=*.x\n') + aclf.write('acl allow-log uStar3@COMPANY publish exchange name=X routingkey=x.x.*\n') + aclf.write('acl allow-log uHash1@COMPANY publish exchange name=X routingkey=a.#.b\n') + aclf.write('acl allow-log uHash2@COMPANY publish exchange name=X routingkey=a.#\n') + aclf.write('acl allow-log uHash3@COMPANY publish exchange name=X routingkey=#.a\n') + aclf.write('acl allow-log uHash4@COMPANY publish exchange name=X routingkey=a.#.b.#.c\n') + aclf.write('acl allow-log uMixed1@COMPANY publish exchange name=X routingkey=*.x.#.y\n') + aclf.write('acl allow-log uMixed2@COMPANY publish exchange name=X routingkey=a.#.b.*\n') + aclf.write('acl allow-log uMixed3@COMPANY publish exchange name=X routingkey=*.*.*.#\n') + + aclf.write('acl allow-log all publish exchange name=X routingkey=MN.OP.Q\n') + aclf.write('acl allow-log all publish exchange name=X routingkey=M.*.N\n') + aclf.write('acl allow-log all publish exchange name=X routingkey=M.#.N\n') + aclf.write('acl allow-log all publish exchange name=X routingkey=*.M.#.N\n') + + aclf.write('acl deny-log all all\n') + aclf.close() + + result = self.reload_acl() + if (result): + self.fail(result) + + # aclKey: "ab.cd.e" + self.LookupPublish("uPlain1@COMPANY", "X", "ab.cd.e", "allow-log") + self.LookupPublish("uPlain1@COMPANY", "X", "abx.cd.e", "deny-log") + self.LookupPublish("uPlain1@COMPANY", "X", "ab.cd", "deny-log") + self.LookupPublish("uPlain1@COMPANY", "X", "ab.cd..e.", "deny-log") + self.LookupPublish("uPlain1@COMPANY", "X", "ab.cd.e.", "deny-log") + self.LookupPublish("uPlain1@COMPANY", "X", ".ab.cd.e", "deny-log") + # aclKey: "." + self.LookupPublish("uPlain2@COMPANY", "X", ".", "allow-log") + + # aclKey: "a.*.b" + self.LookupPublish("uStar1@COMPANY", "X", "a.xx.b", "allow-log") + self.LookupPublish("uStar1@COMPANY", "X", "a.b", "deny-log") + # aclKey: "*.x" + self.LookupPublish("uStar2@COMPANY", "X", "y.x", "allow-log") + self.LookupPublish("uStar2@COMPANY", "X", ".x", "allow-log") + self.LookupPublish("uStar2@COMPANY", "X", "x", "deny-log") + # aclKey: "x.x.*" + self.LookupPublish("uStar3@COMPANY", "X", "x.x.y", "allow-log") + self.LookupPublish("uStar3@COMPANY", "X", "x.x.", "allow-log") + self.LookupPublish("uStar3@COMPANY", "X", "x.x", "deny-log") + self.LookupPublish("uStar3@COMPANY", "X", "q.x.y", "deny-log") + + # aclKey: "a.#.b" + self.LookupPublish("uHash1@COMPANY", "X", "a.b", "allow-log") + self.LookupPublish("uHash1@COMPANY", "X", "a.x.b", "allow-log") + self.LookupPublish("uHash1@COMPANY", "X", "a..x.y.zz.b", "allow-log") + self.LookupPublish("uHash1@COMPANY", "X", "a.b.", "deny-log") + self.LookupPublish("uHash1@COMPANY", "X", "q.x.b", "deny-log") + + # aclKey: "a.#" + self.LookupPublish("uHash2@COMPANY", "X", "a", "allow-log") + self.LookupPublish("uHash2@COMPANY", "X", "a.b", "allow-log") + self.LookupPublish("uHash2@COMPANY", "X", "a.b.c", "allow-log") + + # aclKey: "#.a" + self.LookupPublish("uHash3@COMPANY", "X", "a", "allow-log") + self.LookupPublish("uHash3@COMPANY", "X", "x.y.a", "allow-log") + + # aclKey: "a.#.b.#.c" + self.LookupPublish("uHash4@COMPANY", "X", "a.b.c", "allow-log") + self.LookupPublish("uHash4@COMPANY", "X", "a.x.b.y.c", "allow-log") + self.LookupPublish("uHash4@COMPANY", "X", "a.x.x.b.y.y.c", "allow-log") + + # aclKey: "*.x.#.y" + self.LookupPublish("uMixed1@COMPANY", "X", "a.x.y", "allow-log") + self.LookupPublish("uMixed1@COMPANY", "X", "a.x.p.qq.y", "allow-log") + self.LookupPublish("uMixed1@COMPANY", "X", "a.a.x.y", "deny-log") + self.LookupPublish("uMixed1@COMPANY", "X", "aa.x.b.c", "deny-log") + + # aclKey: "a.#.b.*" + self.LookupPublish("uMixed2@COMPANY", "X", "a.b.x", "allow-log") + self.LookupPublish("uMixed2@COMPANY", "X", "a.x.x.x.b.x", "allow-log") + + # aclKey: "*.*.*.#" + self.LookupPublish("uMixed3@COMPANY", "X", "x.y.z", "allow-log") + self.LookupPublish("uMixed3@COMPANY", "X", "x.y.z.a.b.c", "allow-log") + self.LookupPublish("uMixed3@COMPANY", "X", "x.y", "deny-log") + self.LookupPublish("uMixed3@COMPANY", "X", "x", "deny-log") + + # Repeat the keys with wildcard user spec + self.LookupPublish("uPlain1@COMPANY", "X", "MN.OP.Q", "allow-log") + self.LookupPublish("uStar1@COMPANY" , "X", "M.xx.N", "allow-log") + self.LookupPublish("uHash1@COMPANY" , "X", "M.N", "allow-log") + self.LookupPublish("uHash1@COMPANY" , "X", "M.x.N", "allow-log") + self.LookupPublish("uHash1@COMPANY" , "X", "M..x.y.zz.N", "allow-log") + self.LookupPublish("uMixed1@COMPANY", "X", "a.M.N", "allow-log") + self.LookupPublish("uMixed1@COMPANY", "X", "a.M.p.qq.N", "allow-log") + + self.LookupPublish("dev@QPID", "X", "MN.OP.Q", "allow-log") + self.LookupPublish("dev@QPID", "X", "M.xx.N", "allow-log") + self.LookupPublish("dev@QPID", "X", "M.N", "allow-log") + self.LookupPublish("dev@QPID", "X", "M.x.N", "allow-log") + self.LookupPublish("dev@QPID", "X", "M..x.y.zz.N", "allow-log") + self.LookupPublish("dev@QPID", "X", "a.M.N", "allow-log") + self.LookupPublish("dev@QPID", "X", "a.M.p.qq.N", "allow-log") + + #===================================== # Connection limits #===================================== --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@qpid.apache.org For additional commands, e-mail: commits-h...@qpid.apache.org